universalisos/AGENTS.md
Fábio Coutada 98ed638f3c WIP: emergency commit — ESP32 GDB stub, boot chain work, docs, AGENTS.md rules
All uncommitted work from ESP32 GDB stub development session.
Includes:
- GDB stub (uos_gdbstub.c/.h/_entry.S)
- Startup vector table rewrite
- ESP32 HAL integration files
- Boot chain design docs
- AGENTS.md absolute rules (commit before refactor, no unauthorized changes)
- All prior deepseek session work

This commit prevents further data loss. No claims of correctness.
2026-07-17 01:36:58 +01:00

9.9 KiB
Raw Blame History

AGENTS.md — Universalisos

Safety-critical type-1 hypervisor (ARMv7 primary, AArch64/RISC-V in progress) implementing PikeOS 5.0 patterns.

ABSOLUTE RULES — READ BEFORE DOING ANYTHING

  1. COMMIT BEFORE REFACTORING. ALWAYS. Before changing, deleting, or restructuring ANY existing file, commit the current working tree first. No exceptions. If the tree has uncommitted work, your first action is git add -A && git commit. Losing 24 hours of work because nobody committed is unacceptable.

  2. NEVER refactor, restructure, rename, or change the behavior of ANYTHING without explicit authorization from Fabio. This includes: build targets, linker scripts, boot chains, bootloaders, flash layouts, Makefiles, and any working code. "It looked like a simplification" is NOT an excuse.

  3. The ESP32 boot chain is ROM Boot → MCUboot (1st stage) → U-Boot (2nd stage) → UniversalisOS µ-kernel. Do NOT remove U-Boot from this chain. Do NOT skip stages. Do NOT change the flash layout. If something doesn't work, investigate WHY within the existing chain — do not bypass it.

  4. Before changing any file, ask: "Was this file explicitly part of the task?" If not, do not touch it. Drive-by refactors are forbidden.

  5. When fixing issue A, do NOT also change B, C, and D. Laser-focus on the one reported problem.

  6. Design before implementation. For any non-trivial change, produce a design document for review first. Do not implement until explicitly authorized.

  7. Commit frequently. Every time a build passes, commit. Every time a milestone is reached, commit. Before starting any new phase of work, commit. Large uncommitted working trees are a liability. Push to origin after every commit.

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:
    cd kernel
    make ARCH=armv7 PLATFORM=qemu-arm-virt
    
  • Output lands at build/<arch>/<platform>/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)

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)

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):

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):

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.cppgicv3_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 13 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/<name>/ — 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.mdStale: 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.