danos/docs/arm.md

121 lines
6.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ARM targets (`arm` and `aarch64`)
danos aims to run on Raspberry Pi hardware eventually. "ARM" isn't one target,
though — the Pis span **two different CPU architectures** (32-bit `arm` and 64-bit
`aarch64`) and (stock) a different boot protocol from x86-64's UEFI. **danos targets
`aarch64` only** (see the decision below); the `arm`/`aarch64` distinction still
matters for understanding why. This page maps the landscape so the
[arch split](arch.md) and build system can be planned for it.
## `arm` vs `aarch64` — 32-bit vs 64-bit
- **`arm`** = **32-bit** ARM (the *AArch32* state, A32/T32 instruction sets).
ARMv7 and earlier, plus the 32-bit compatibility mode of newer cores. 16 × 32-bit
registers.
- **`aarch64`** = **64-bit** ARM (the *AArch64* state, A64 instruction set), from
**ARMv8-A** on. Also called **arm64**. 31 × 64-bit registers, a fixed 32-bit
instruction width, a redesigned exception model — *not* a widening of A32, a clean
new ISA.
They are as different from each other as either is from x86-64: separate registers,
page-table formats, and calling conventions. Each needs its own `system/kernel/arch/<name>/`.
## The Raspberry Pi models
| Model | SoC | Core | Architecture | danos target |
|-------|-----|------|--------------|--------------|
| Pi Zero / Zero W | BCM2835 | ARM1176JZF-S | ARMv6, 32-bit only | `arm` (not planned) |
| **Pi Zero 2 W** ← target | BCM2710 | Cortex-A53 | ARMv8-A, 64-bit | **`aarch64`** |
| **Pi 3 / 3B+** | BCM2837 | Cortex-A53 | ARMv8-A, 64-bit | **`aarch64`** |
| **Pi 4** | BCM2711 | Cortex-A72 | ARMv8-A, 64-bit | **`aarch64`** |
| **Pi 5** | BCM2712 | Cortex-A76 | ARMv8.2-A, 64-bit | **`aarch64`** |
> **Decision: `aarch64` only.** The target small board is a **Pi Zero 2 W** (BCM2710,
> Cortex-A53) — which is **`aarch64`**, *not* the original Zero W's 32-bit ARMv6. So
> every ARM board danos targets (Zero 2 W and Pi 3-5) is `aarch64`, and the 32-bit
> `arm`/ARMv6 backend is **not planned** — one ARM CPU port, not two. The original
> Zero W (ARMv6) would only re-enter scope if that specific older board were ever
> needed; the row above is kept only to explain the distinction.
## Booting: UEFI is not x86-only
The boot protocol is a **separate axis** from the CPU (see [arch.md](arch.md)):
- **UEFI** exists for ARM too — ARM servers require it (SBSA/SBBR), QEMU boots it
with **AAVMF** (the AArch64 build of the same EDK2 firmware as x86's OVMF), and
the Pi can even run it with community UEFI firmware. Under UEFI the handoff is the
*same* as x86-64: system table, boot services, memory map, GOP framebuffer — so
the loader logic largely carries over.
- **Device tree / firmware boot** — the **stock** Raspberry Pi firmware (VideoCore
bootloader) is *not* UEFI: it loads the kernel and jumps to it with a **device-tree
blob (DTB)** pointer. Both the Zero W and stock Pi 3-5 boot this way.
Note that even under UEFI on ARM, the OS still gets its hardware description from
**ACPI or a device tree** (often the DTB passed via a UEFI configuration table). So
"UEFI on ARM" doesn't remove the device tree — UEFI gives you memory + framebuffer;
the DTB/ACPI tells you what devices exist.
## What danos needs, layer by layer
- **One CPU arch module: `system/kernel/arch/aarch64/`** — covering the Zero 2 W and Pi 3-5,
providing the same `arch` interface as x86_64: `halt`, context switch,
interrupt/exception vectors, page tables, a UART, a timer. No `system/kernel/arch/arm/` is
planned (see the decision above), so there's a single ARM backend to write.
- **A device-tree boot path.** Since stock Pis boot via DTB, danos needs an entry
that parses the DTB's `/memory` and `/reserved-memory` into the neutral
[`MemoryMap`](memory-map.md) — the same neutral handoff `efi.zig` produces, just
from a different source. This is where keeping boot-protocol knowledge on the
loader side (as we did for the UEFI memory-map classification) pays off.
- **The UEFI loader mostly carries over.** `boot/efi.zig` is largely
boot-*protocol* code (`std.os.uefi` protocol calls), not x86 code. Its only truly
x86-specific bits are the ELF machine check (`.X86_64`) and the SysV calling
convention for the kernel jump. So an `aarch64`-UEFI target (QEMU `virt` + AAVMF)
can reuse it — which makes **aarch64-UEFI the easiest second target**, easier than
the device-tree Pi.
## Pi hardware quirks (for when we port)
The Pi is not a "standard" ARM platform — expect Broadcom-specific peripherals:
- **Peripheral base moves per SoC**: `0x2000_0000` (BCM2835, Zero W),
`0x3F00_0000` (BCM2837, Pi 3), `0xFE00_0000` (BCM2711, Pi 4), different again on
Pi 5. Everything below is an offset from it.
- **UART**: a **PL011** (at base + `0x20_1000`) plus a mini-UART; on some boards the
PL011 is wired to Bluetooth, so which one is the console varies. This is the
`aarch64`/`arm` equivalent of our x86 [COM1 serial](testing.md).
- **Interrupt controller**: *not* a standard ARM GIC on the older parts — the Zero W
and Pi 3 use Broadcom's own ARMCTRL controller (Pi 3 adds a per-core "local"
controller for timers/mailboxes). The **Pi 4 and 5 do have a GIC-400**. So the
interrupt backend differs even within the `aarch64` Pis.
- **Timer**: the ARM generic timer (`CNTPCT`/`CNTFRQ`) on ARMv8, or the BCM system
timer — the counterpart to our calibrated LAPIC/TSC clock.
## Building each (intended)
`build.zig` currently pins the kernel to `x86_64`; supporting these means selecting
the target and `arch` module together (e.g. a `-Darch=` option). The Zig target
queries would be roughly:
- **Pi Zero W**: `.cpu_arch = .arm`, `.cpu_model = arm1176jzf_s`, `.os_tag = .freestanding`
- **Pi 3**: `.cpu_arch = .aarch64`, `.cpu_model = cortex_a53`, `.os_tag = .freestanding`
- **Pi 4**: `.cpu_arch = .aarch64`, `.cpu_model = cortex_a72`
- **Pi 5**: `.cpu_arch = .aarch64`, `.cpu_model = cortex_a76`
## Testing in QEMU
Two routes, mirroring how we test x86-64 with OVMF:
- **Board emulation**: `qemu-system-aarch64 -machine raspi3b` (and `raspi4b` on
recent QEMU) for the Pi 3/4; `qemu-system-arm -machine raspi0`/`raspi1ap` for the
ARMv6 Zero-class board — closest to real hardware, device-tree boot.
- **Generic aarch64-UEFI**: `qemu-system-aarch64 -machine virt` + AAVMF — the
cleanest way to bring up the `aarch64` kernel via the reused UEFI loader before
tackling Pi-specific boards. A future `run-aarch64` build step would use this.
## Related
- [arch.md](arch.md) — the arch-module boundary these targets plug into, and the
CPU-arch vs boot-protocol "two axes".
- [efi.md](efi.md) — the UEFI loader that carries over to aarch64-UEFI.
- [vision.md](vision.md) — why isolated, portable-across-architectures is the goal.