# 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 [architecture split](architecture.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/architecture//`. ## 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 [architecture.md](architecture.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/architecture/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/architecture/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 truly x86-specific bits are the ELF machine check (`.X86_64`), the SysV calling convention for the kernel jump, the `hlt` park on failure — and, the substantial one, the bootstrap page tables: `buildBootstrapTables` builds x86-64 4-level tables (PML4/PDPT/PD index shifts, x86 PTE bits, 2 MiB leaves) and hands the kernel a CR3. Page-table formats are per-architecture (see above), so an `aarch64` loader keeps the protocol code but rewrites that builder in the aarch64 translation-table format. Even so, an `aarch64`-UEFI target (QEMU `virt` + AAVMF) reuses most of 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 - [architecture.md](architecture.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.