diff --git a/AGENTS.md b/AGENTS.md index c3c49f4b5..c42c5f658 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -34,6 +34,18 @@ Safety-critical type-1 hypervisor (ARMv7 primary, AArch64/RISC-V in progress) im 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. @@ -48,6 +60,14 @@ Safety-critical type-1 hypervisor (ARMv7 primary, AArch64/RISC-V in progress) im 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. **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`. @@ -131,6 +151,7 @@ qemu-system-riscv64 -machine microchip-icicle-kit -smp 5 -m 2G -nographic \ - **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. +- **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. @@ -167,10 +188,44 @@ Do **not** mix Universalisos work with other PortugalFuturista projects (DocSpac ## 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 — 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. +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` builds as well. +If a change touches shared headers, verify it does not break `ARCH=aarch64` or `ARCH=riscv` or `TARGET=esp32` builds as well. diff --git a/docs/esp32-mcuboot-uboot-strategy.md b/docs/esp32-mcuboot-uboot-strategy.md index 727af9f9c..f85be60a2 100644 --- a/docs/esp32-mcuboot-uboot-strategy.md +++ b/docs/esp32-mcuboot-uboot-strategy.md @@ -14,12 +14,14 @@ This is the single fact that shapes the whole design. | SoC family | Arch | U-Boot support | Viability as a loader | -|---|---|---|---| +||---|---|---|---| +| ESP32 / ESP32-S2 / ESP32-S3 | Xtensa LX6/LX7 | **Experimental, not mainline** | 🟡 commitment: estamos a portar | | ESP32-C3 / C6 / H2 | RISC-V | **Mainline** (since ~2023) | ✅ real | | ESP32-P4 | RISC-V | Partial / under review | 🟡 | -| ESP32 / ESP32-S2 / ESP32-S3 | Xtensa LX6/LX7 | **Experimental, not mainline** | 🔴 risky | | ESP32-C5 | RISC-V | not upstream | 🔴 | +**Decisão do Maestro Fabio:** U-Boot em Xtensa ESP32 é uma realidade — não saltamos, não simplificamos. Estamos a fazer um fork Xtensa do U-Boot. Isto significa portar drivers, fixar cache/MMU, excepções, e todo o ecossistema (partition table, spi flash, env, GPIO, timer, WDT). O trabalho vive em `portugalfuturista/universalis-uboot`. + MCUboot, by contrast, **supports every Espressif target** (Xtensa + RISC-V) via `boot/espressif`. So: - **"MCUboot only" works on ALL targets** — it is the proven Zephyr path. @@ -32,23 +34,26 @@ MCUboot, by contrast, **supports every Espressif target** (Xtensa + RISC-V) via ## 2. The three candidate chains ``` -CHAIN A — Zephyr-proven, RECOMMENDED (all targets) +CHAIN A — MCUboot → UOS (development/test stepping stone, Xtensa only) ROM (mask, fixed) → MCUboot @ 0x1000 (2nd-stage, esptool elf2image 0xE9 format) validates signature, swap/scratch, picks primary → UOS app @ slot0 (mcuboot-signed, esp_image_load_header 0xace637d3) esp_loader.c copies IRAM/DRAM segs, jumps to entry + *** Use Chain A for GDB stub bring-up and µ-kernel development only *** + *** Chain B is the FINAL target for all Espressif targets *** -CHAIN B — MCUboot + U-Boot (RISC-V only: C3/C6/H2) +CHAIN B — MCUboot → U-Boot → UOS (MANDATORY, ALL ESPRESSIF TARGETS) ROM → MCUboot @ 0x1000 → U-Boot @ slot0 → UOS (loaded by U-Boot) U-Boot = the mcuboot "application image"; UOS = a payload U-Boot chainloads. - Adds a full U-Boot build + a UOS load command. Heavy; only for dev/debug richness. + No shortcuts. No skipping U-Boot. Full boot chain verification required. + - Xtensa: fork não-mainline em porting (consola, cache, vectors, drivers) + - RISC-V: mainline, mais fácil — fazer depois do Xtensa -CHAIN C — our in-tree bootstub (already QEMU-proven, Xtensa) +CHAIN C — in-tree bootstub (QEMU-only debugging, NOT for HW) ROM → bootstub @ 0x1000 (own minimal 2nd-stage, watches-off + MMU map) → UOS app @ 0x10000 (plain 0xE9 image, mcuboot-style bounds-checked loader) - Simplest. No signature/OTA. Good for bring-up; lacks secure boot + DFU. -``` + *** QEMU only. Never flash to hardware. *** --- @@ -148,31 +153,33 @@ RISC-V U-Boot port + a UOS load command. --- -## 7. Recommended strategy (phased) +## 7. Estratégia (mandatada por Maestro Fabio) -**Phase 0 — Pin the layout (do first, blocks real HW).** +**Fase 0 — Pin the layout (do first, blocks real HW).** - Pick ONE partition scheme. Generate it with `esp_genpartition.py` for the - target flash size; record `slot0` offset. Keep Zephyr-compatible offsets so - we can cross-check against a known-good `west build --sysbuild` if needed. + target flash size; record `slot0` offset. -**Phase 1 — Lock Chain A for ALL targets (secure-boot baseline).** -- Build `mcuboot/boot/espressif` for the target (`-DMCUBOOT_TARGET=esp32[c3|s3|…]`). -- Package with `esptool elf2image` → `mcuboot.bin @ 0x1000`. -- Build UOS app; sign with `imgtool` + prepend `esp_image_load_header` - (`0xace637d3`); flash at slot0. -- Verify on QEMU (esp32/esp32s3) then real HW. Add secure-boot + flash-enc - Kconfig once baseline boots. +**Fase 1 — Chain A funcional (MCUboot → UOS, stepping stone).** +- Build MCUboot/boot/espressif for the target. +- Build UOS app; sign with imgtool + prepend `esp_image_load_header`. +- Flash at slot0. Verify in QEMU + HW. +- **GDB stub mandatório em todos os testes** (break 0,0 → T05 via UART). -**Phase 2 — Keep Chain C (bootstub) as the fast QEMU/dev path.** -- It already works. Keep it for iteration; do not flash it to secure-boot HW. +**Fase 2 — Chain B (MCUboot → U-Boot → UOS, OBRIGATÓRIO).** +- Portar U-Boot para Xtensa ESP32 no fork `universalis-uboot`. +- Fix consola garbled (Phase 1 do master plan: UART DM_SERIAL, cache/MMU, vectors). +- ESP32 drivers (SPI flash, partition table, env, GPIO, timer, WDT). +- bootelf / load command para carregar UOS image. +- Auto-boot via CONFIG_BOOTCOMMAND. +- Chain A continua a existir como GDB-stub development path. -**Phase 3 — Chain B (MCUboot → U-Boot → UOS) ONLY for RISC-V (C3/C6/H2).** -- Only if a U-Boot pre-OS shell is actually wanted (debug/ provisioning). -- Otherwise skip — U-Boot adds ~200 KB + a second image format for no HV gain. +**Fase 3 — Chain C (bootstub) eliminated.** +- Bootstub era para QEMU. Com Chain A funcional, não precisamos de bootstub. +- Remover `microkernel/ports/esp32/bootstub*` após Chain A+B estarem estáveis. -**Phase 4 — SMP / APP-CPU bring-up.** -- Reuse `CONFIG_ESP_MULTI_PROCESSOR_BOOT` path (`start_cpu1_image`) — already - present in both mcuboot and our `uos_loader.h` weak hook. +**Fase 4 — SMP / APP-CPU bring-up (MCUboot + U-Boot).** +- Reuse `CONFIG_ESP_MULTI_PROCESSOR_BOOT` path (`start_cpu1_image`) no MCUboot. +- U-Boot eventualmente carrega as duas CPUs. --- @@ -188,15 +195,17 @@ RISC-V U-Boot port + a UOS load command. --- -## 9. Open decision (blocks Phase 1/3) +## 9. Open decisions (resolvidas) -1. **Target SoC(s)** — Xtensa (ESP32/S3) and/or RISC-V (C3/C6/H2)? This decides - whether Chain B (U-Boot) is even on the table. -2. **Is U-Boot actually required**, or was "mcuboot+uboot" shorthand for - "a proper verified boot chain"? If the latter, Chain A alone satisfies it - and is the Zephyr-proven path. -3. **Secure boot / flash encryption** needed at first boot, or bring-up first - then enable? +1. **Target SoC(s)** — Xtensa (ESP32/S3) é o alvo primário. RISC-V (C3/C6/H2) + depois do Xtensa. Chain B (U-Boot) para TODOS. +2. **U-Boot é obrigatório, não opcional.** "mcuboot+uboot" não era shorthand — + é a arquitetura de boot final. Chain A (MCUboot → UOS) é stepping stone + de desenvolvimento apenas. +3. **Secure boot / flash encryption** — primeiro bring-up das cadeias, + depois ativar segurança. + +Ver docs/esp32-master-plan.md para o plano detalhado de 5 fases do U-Boot. ---