# AGENTS.md — Universalisos Safety-critical type-1 hypervisor (ARMv7 primary, AArch64/RISC-V in progress) implementing PikeOS 5.0 patterns. ## Critical Build Gotchas - **Default build target is RISC-V**, not ARM. The Makefile defaults to `ARCH=riscv PLATFORM=polarfire`. - **To build the working ARMv7 target**, you must explicitly pass: ```bash cd kernel make ARCH=armv7 PLATFORM=qemu-arm-virt ``` - Output lands at `build///universalisos.elf`, not `kernel.elf`. - `make clean` only cleans the current `ARCH/PLATFORM` combo. Use `make clean-all` to wipe all build trees. - The old commands `make -C kernel/arch/arm` and the driver script `.claude/skills/run-universalisos/driver.sh` reference the **obsolete** pre-Bao build system and are broken. Ignore them. ## Verified Boot Command (ARMv7) ```bash cd kernel qemu-system-arm -M virt -cpu cortex-a15 -m 512M \ -nographic -kernel build/armv7/qemu-arm-virt/universalisos.elf ``` Press `Ctrl+A` then `X` to exit QEMU. ## Verified Boot Command (AArch64) ```bash cd kernel qemu-system-aarch64 -M virt,gic-version=3,virtualization=on -cpu cortex-a53 -m 512M \ -smp 4 -nographic -kernel build/aarch64/qemu-aarch64-virt/universalisos.elf ``` > **Gotcha:** `virtualization=on` is mandatory. Without it QEMU hands the `-kernel` > payload EL1 (not EL2); the EL2 hypervisor then traps on its first EL2 > system-register write (`msr vbar_el2, …`) and hangs silently with **no UART output** > and no logged guest error. `boot.S` now halts cleanly (`wfe` loop) if entered below > EL2 instead of falling into the EL2 register writes. ## Verified Boot Command (RISC-V / Icicle Kit) Bare-metal smoke test (BSP only, `-bios none`): ```bash cd kernel make ARCH=riscv PLATFORM=polarfire # default arch/platform # or: make run-qemu qemu-system-riscv64 -machine microchip-icicle-kit -smp 5 -m 2G \ -nographic -bios none -kernel build/riscv/polarfire/universalisos.elf ``` Firmware / HSS-payload boot (releases all 5 harts into S-mode, SBI live — this is the tear-de-silicio `polarstar-fpga` flow retargeted at UniversalisOS): ```bash cd kernel # 1. Build the S-mode-entry image (byte @0x80200000 = _start_firmware). make ARCH=riscv PLATFORM=polarfire UOS_BOOT=firmware bin # 2. Wrap it as an HSS payload (needs Microchip's hss-payload-generator). hss-payload-generator -c config/icicle_hss_payload.yaml \ build/riscv/polarfire/universalisos.bin build/riscv/polarfire/payload.bin # (or just: make hss-payload — it prints the exact command if the tool is absent) # 3. Boot it as the machine BIOS, all harts released at 0x80200000 in S-mode. make run-qemu-firmware FIRMWARE=build/riscv/polarfire/payload.bin # equivalent: qemu-system-riscv64 -machine microchip-icicle-kit -smp 5 -m 2G -nographic \ -bios build/riscv/polarfire/payload.bin \ -device loader,file=build/riscv/polarfire/universalisos.elf,addr=0x80200000 ``` > **Why two images.** `-bios none` enters in M-mode and runs the `_start` > trampoline (delegates + `mret` to S-mode, leaves `__uos_has_sbi=0`). The > firmware image puts `_start_firmware` at `0x80200000`; it sets > `__uos_has_sbi=1` so the timer goes through SBI `set_timer` and secondary > bring-up goes through SBI HSM — the only way to get preemptive ticks and > live SMP on the Icicle Kit, since the CLINT `mtimecmp` is M-mode-only and QEMU > holds harts 0/2/3/4 in reset under `-bios none`. `riscv_cpu_wake_secondary()` > is a deliberate no-op when `__uos_has_sbi==0`. > OpenSBI generic firmware still does **not** chain on `microchip-icicle-kit` > (MROM overlap at `0x20220000`); the HSS payload is the working firmware path. ## Architecture Reality Check - **ARMv7 + qemu-arm-virt**: Boots, produces UART output, runs scheduler/IPC/HM demos end-to-end. This is the target you should test against. - **AArch64 + qemu-aarch64-virt**: EL2 hypervisor track. Builds clean (0 linker warnings) and boots to a full banner at EL2 — but **only** when QEMU is run with `virtualization=on` (see gotcha above). MP0 bring-up reaches the scheduler, `eret` into the first task works, and **timer-driven preemption is live** (the former U3 open item is resolved): the HV tick uses the EL2 **physical** timer CNTHP on **PPI 26** (`timer.cpp` → `gicv3_irq_enable(QEMU_TIMER_EL2_PHYS_PPI)`), `el2_irq_handler` (el2_trap.cpp) acks INTID 26 / re-arms CNTHP_CVAL / EOIs / runs `sched_tick()`, and `el2_irq_entry` (exceptions.S) performs the deferred preemptive switch via `g_need_reschedule` + the cur/next trap-frame pointers. Verified by boot: A/B tasks interleave in runs of 1–3 ticks at ~50/50. (PPI 27 enabled on MP1 is the *guest's* virtual timer via the vGIC — intentional, not the HV tick.) - **RISC-V + polarfire (Icicle Kit)**: Self-contained S-mode port under `kernel/src/arch/riscv/`. Builds clean (`make ARCH=riscv PLATFORM=polarfire`) and boots to banner + `Partition 0/1 says hello!` for `-smp 2..5` (QEMU rejects `-smp 1`: min 2 CPUs; `-bios none` is BSP-only regardless of `-smp`). Two boot modes: `-bios none` (M-mode trampoline, BSP only — verifiable today) and the HSS-payload firmware path (`UOS_BOOT=firmware` + `config/icicle_hss_payload.yaml`, releases all 5 harts + SBI timer/IPI/HSM — needs Microchip `hss-payload-generator`). Timer is gated on `__uos_has_sbi`: SBI `set_timer` under firmware, direct CLINT under `-bios none`. `-bios none` is BSP-only by construction (secondaries held in reset; `riscv_cpu_wake_secondary()` is a no-op when `__uos_has_sbi==0`); the HSS path is the intended route to real SMP + preemptive ticks. When making changes, **build and boot ARMv7** to verify. Do not assume changes to shared headers compile on all three arches unless you test each. ## Code Conventions - Use `uos_` prefix for new APIs (not PikeOS `p4_`/`UOS_PART_`). - MMU is **enabled** (PikeOS-style flat 4GB 1MB-section identity map, all RAM cacheable, I/O strongly-ordered). Device MMIO reads/writes work. - No dynamic memory allocation; static/compile-time only. - No exceptions, no RTTI (`-fno-exceptions -fno-rtti`). - Freestanding environment: provide `__aeabi_uidiv`/`__aeabi_uldivmod` in `aeabi_runtime.cpp` when needed. ## Known Hardware/Emulation Quirks - **QEMU 32-bit ARM virt PCIe ECAM deadlocks**: Reads at `0x3f000000` hang inside QEMU's device model (independent of MMU state). The PCI core ships with a safe framework transport as default; ECAM transport is ready for real hardware or AArch64 virt. - **SPI demo is intentionally NOT called at boot**: A write to `spi_controllers` (last 32 bytes of BSS, at the BSS/SVC-stack boundary) triggers a fault due to a latent kernel memory/exception issue. The SPI driver itself is compiled-in and correct. ## Project Boundaries - `kernel/src/arch/armv7/` — ARMv7-specific boot, exceptions, context switch, UART, MMU walk, adspace, scheduling asm. - `kernel/src/arch/aarch64/` — AArch64 EL2 hypervisor (separate, self-contained). - `kernel/src/arch/riscv/` — RISC-V port (separate, self-contained; does NOT pull ARM-centric core files). - `kernel/src/core/` — Shared hypervisor core: scheduler, IPC, memory, device framework, guest boot, UOS API, capability MDB, health monitoring. **ARMv7 compiles all of these**; AArch64/RISC-V are self-contained and do not pull from `core/`. - `kernel/src/platform/drivers/` — Shared device drivers (block, GPIO, I2C, SPI, network, PCI, USB, fbcon, timer, UART). Platforms opt in via `drv-objs-y`. - `kernel/src/platform//` — Board/platform specifics (linker script, config, optional driver substitutions via `cpu-skip-y`). ## Primary Reference Documents - `UNIVERSALISOS_VS_PIKEOS_5.0.md` — Roadmap, feature-by-feature gap analysis, and implementation priorities. - `kernel/README.md` — **Stale**: claims "Stage 1" is current. Ignore stage labels; treat this as historical context only. ## Context Isolation Rule Do **not** mix Universalisos work with other PortugalFuturista projects (DocSpace, Aurelio Web, mycelium, etc.). When working in this repo, focus **only** on the hypervisor implementation. ## Verification Workflow There is no unit-test framework, linter, or typechecker. Verification is: 1. `make ARCH=armv7 PLATFORM=qemu-arm-virt` — must compile clean. 2. Boot in QEMU — must reach UART banner and complete demo sequence (scheduler slots, IPC, shared memory, health monitoring, preemption). 3. Check for unexpected hangs or missing demo sections in output. If a change touches shared headers, verify it does not break `ARCH=aarch64` or `ARCH=riscv` builds as well.