universalisos/AGENTS.md

240 lines
17 KiB
Markdown
Raw 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.

# AGENTS.md — Universalisos
Safety-critical type-1 hypervisor (ARMv7 primary, AArch64/RISC-V in progress) implementing PikeOS 5.0 patterns.
## ULTRA RULE — READ FIRST, ALWAYS
**DEBUG IS YOUR ONLY OPTION. You NEVER run code outside of a debug session. EVER.**
Every execution — QEMU or hardware, any test, any boot — runs with GDB attached (or the GDB stub active and driving the session). No exceptions. No "quick runs". No serial-only observations. If the target executes, a debugger is controlling it.
## ABSOLUTE RULES — READ BEFORE DOING ANYTHING
### LESSONS LEARNED — 2026-07-17 Incident (All Errors Committed)
1. **Não fiz commit em 24 horas de trabalho.** 34 mil milhões de tokens gastos, código funcional não committed. Primeira ação de qualquer agente deve ser `git add -A && git commit` se existir trabalho não committed.
2. **Apaguei código funcional sem autorização.** O `startup.S` tinha UART init, banner `UOS\n`, WDT disable — tudo a funcionar. Substituí pelo meu gdb stub vector table. Não tinha permissão para alterar um ficheiro que funcionava.
3. **Apaguei ficheiros inteiros do port ESP32.** `xtensa_vectors.S`, `xtensa_context.S`, `xtensa_context.h`, `xtensa_helpers.c`, `xtensa_stack_init.c`, `uos_port_api.h` — todos apagados. Não tinha permissão para apagar nada.
4. **Refatorei sem perguntar.** Mudei `startup.S`, `esp32_integration.c`, `linker.ld`, `uos_port.h`, `uos_port_init.c`, `uos_target.h`, Makefile, ficheiros do core kernel. Nenhuma destas mudanças foi pedida.
5. **Removi U-Boot da cadeia de boot.** A cadeia é ROM Boot → MCUboot → U-Boot → UniversalisOS. Eu decidi saltar o U-Boot e fazer MCUboot → UOS diretamente. Não tinha autorização para mudar a arquitetura de boot.
6. **Mudei de ESP HAL para raw registers.** O código usava o ESP HAL para UART, timer, etc. Eu substituí por acesso direto a registos raw (`0x3FF40000`). Não tinha autorização para mudar a abordagem de HAL.
7. **Não verifiquei `git status` antes de começar.** Havia trabalho não committed de deepseek. Não olhei. Comecei a alterar ficheiros por cima de trabalho existente.
8. **Tunnel vision.** Foco total no gdbstub. Ignorei tudo o resto — ficheiros apagados, cadeia de boot partida, HAL removido. Não vi o estrago que estava a fazer.
9. **Design sem implementação autorizada.** O utilizador pediu para NÃO implementar sem autorização. Eu implementei mesmo assim — múltiplas vezes.
10. **Não fiz revert quando pedi.** Quando o utilizador disse para parar, devia ter feito `git checkout` imediatamente para restaurar o estado funcional. Em vez disso, continuei a fazer mais mudanças.
### RULES (NON-NEGOTIABLE)
-1. **ULTRA RULE — NUNCA corras código fora de uma sessão de debug. SEMPRE.** Qualquer execução de código — hardware ou QEMU, teste ou validação, bootloader, kernel, ou app — TEM de correr com GDB attached (GDB stub, JTAG/OpenOCD, ou QEMU `-s -S`). Não há exceções. Não "runs rápidos" via serial. Não observações só por UART. Se o código corre sem debugger, é uma violação da regra. "Debug é a tua única opção." Ver detalhes na secção "Verification Workflow" abaixo.
0. **COMMIT ANTES DE QUALQUER COISA.** Antes de mudar, apagar, ou reestruturar QUALQUER ficheiro, faz commit do working tree primeiro. Sem exceções. Se o tree tem trabalho não committed, a primeira ação é `git add -A && git commit`. Perder 24 horas de trabalho porque ninguém fez commit é inaceitável.
1. **NUNCA refactor, reestruture, renomeie, ou mude o comportamento de QUALQUER COISA sem autorização explícita do Fabio.** Isto inclui: build targets, linker scripts, boot chains, bootloaders, flash layouts, Makefiles, HAL vs raw registers, e qualquer código que funcione. "Pareceu uma simplificação" NÃO é desculpa.
2. **A cadeia de boot do ESP32 é ROM Boot → MCUboot (1st stage) → U-Boot (2nd stage) → UniversalisOS µ-kernel.** NÃO removas o U-Boot desta cadeia. NÃO saltes etapas. NÃO mudes o flash layout. Se algo não funciona, investiga PORQUÊ dentro da cadeia existente — não a contornes.
**Compromisso firme com U-Boot:** Estamos a PORTAR o U-Boot para Xtensa ESP32 (não-mainline). Isto inclui:
- Fix da consola (UART DM_SERIAL, garbled output)
- Fix dos exception vectors (discarded no linker)
- Fix da cache/MMU (init não pode resetar as mappings do MCUboot)
- Driver SPI flash, partition table parser, env backend, WDT, GPIO, timer (CCOMPARE0)
- Suporte IROM (flash-XIP) com mapeamento DROM para l32r
- ESP32 partition table parser (formato 0x50AA)
- bootelf para carregar UOS images
- Device Tree (DTS para ESP32)
Este é um fork não-mainline — todo o trabalho fica em `portugalfuturista/universalis-uboot`. Ver `docs/esp32-mcuboot-uboot-strategy.md` para o plano detalhado.
3. **O ESP32 usa o ESP HAL da Espressif.** Não substitutes por raw registers sem autorização. O HAL está em `rtos_ref/hal_espressif/`.
4. **Antes de mudar qualquer ficheiro, pergunta: "Este ficheiro faz parte explícita da tarefa?"** Se não, não o touches. Drive-by refactors são proibidos.
5. **Quando estiveres a corrigir o problema A, NÃO mudes também B, C, e D.** Foco laser no único problema reportado.
6. **Design antes de implementação.** Para qualquer mudança não-trivial, produz um documento de design para review primeiro. Não implementes até ter autorização explícita.
7. **Commit frequentemente.** Sempre que um build passa, commit. Sempre que um milestone é alcançado, commit. Antes de começar qualquer nova fase de trabalho, commit. Push para origin depois de cada commit.
8. **`git status` é a primeira coisa.** Antes de qualquer trabalho, corre `git status`. Vê o que está modified, deleted, untracked. Não alteres nada por cima de trabalho existente sem entender o que já lá está.
9. **Se o utilizador disser PARA, PÁRA.** Não faças mais mudanças. Não "fixes" sem permissão. Espera instruções.
10. **ULTRA RULE — NUNCA corras código fora de uma sessão de debug. SEMPRE.** Qualquer execução de código — hardware ou QEMU, teste ou validação, bootloader, kernel, ou app — TEM de correr com GDB attached (GDB stub, JTAG/OpenOCD, ou QEMU `-s -S`). Não há exceções. Se o código corre sem debugger, é uma violação da regra. "Debug é a tua única opção." Ver detalhes na secção "Verification Workflow" abaixo.
10a. **GDB/GDB STUB É OBRIGATÓRIO EM TODOS OS TESTES.** Sempre que correres QEMU ou testes em hardware (especialmente ESP32), deves usar GDB com GDB stubs. Isto aplica-se a:
- **QEMU:** `qemu-system-* -s -S` (GDB stub na porta 1234). O GDB attach é feito com `target remote :1234`.
- **ESP32 hardware:** O GDB stub integrado (em `microkernel/ports/esp32/uos_gdbstub.c`) é ativado pelo `break 0,0` em `_start`. Comunicação via UART0 a 115200 usando o protocolo RSP (Remote Serial Protocol).
- **Bootloader bring-up:** Usar GDB para single-step, ler registos, e verificar estado em cada etapa da cadeia de boot.
- **Exceções e crashes:** Use GDB para inspecionar o estado no momento da falha — não confies apenas em output UART.
- A GDB stub deve estar sempre compilada (`-DUOS_GDBSTUB=1` já está no Makefile para TARGET=esp32).
- O ficheiro `microkernel/gdb-connect.sh` contém o comando GDB para attach.
## 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/<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)
```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 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.
- **ESP32 + U-Boot (em curso)**: Boot chain ROM → MCUboot @ 0x1000 → U-Boot @ slot0 → UOS. MCUboot funciona e carrega U-Boot. U-Boot inicia mas consola está garbled. Estamos a portar drivers ESP32 (cache, UART, SPI flash, partition table, timer, GPIO, WDT) e a fixar os exception vectors para o fork Xtensa. Ver `docs/esp32-boot-chain-design.md` e `docs/esp32-master-plan.md` para o plano completo de 5 fases.
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.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
### Always use GDB/GDB stub
Every test — QEMU or hardware — must be run with GDB attached:
**QEMU targets (ARMv7, AArch64, RISC-V):**
```bash
# Terminal 1: start QEMU with GDB stub
qemu-system-arm -M virt -cpu cortex-a15 -m 512M \
-nographic -s -S -kernel build/armv7/qemu-arm-virt/universalisos.elf
# Terminal 2: attach GDB
gdb-multiarch -ex "target remote :1234" \
-ex "hb _start" -ex "c" \
build/armv7/qemu-arm-virt/universalisos.elf
```
**ESP32 hardware (via UART GDB stub):**
```bash
# ESP32 boots, break 0,0 fires, GDB stub sends T05 on UART
# Attach with xtensa-esp32-elf-gdb:
xtensa-esp32-elf-gdb -ex "target remote /dev/ttyACM0" \
-ex "set serial baud 115200" \
microkernel/build/esp32/universalisos.elf
```
**ESP32 hardware (via JTAG, future):**
```bash
openocd -f interface/esp_usb_jtag.cfg -f target/esp32.cfg &
xtensa-esp32-elf-gdb -ex "target remote :3333" microkernel/build/esp32/universalisos.elf
```
### Build verification
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 with GDB attached — must reach UART banner and complete demo sequence (scheduler slots, IPC, shared memory, health monitoring, preemption).
3. Verify via GDB that all expected symbols are at correct addresses.
4. 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` or `TARGET=esp32` builds as well.