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.
12 KiB
UniversalisOS ESP32 — Master Implementation Plan
Status: MCUboot + U-Boot chain working (2026-07-16) Target: ESP32-D0WDQ6-V3, Xtensa LX6, 4MB flash
1. Current State
1.1 Working
- ROM → MCUboot @ 0x1000 (image validation, OTA slots)
- MCUboot flash MMU:
cache_flash_mmu_set→ IROM @ 0x40800000 (rc=0) - MCUboot → U-Boot handoff: jumps to
_start, printsX\nW\nY\nE\n[UOS2\n - U-Boot banner: "U-Boot 2025.04..." appears
- DRAM loading via D-bus (0x3FFAF000+)
- U-Boot IROM linker script at 0x40800000+
- Wrapper tool for ELF→MCUboot image format
1.2 Broken / Incomplete
- U-Boot console garbled after banner — can't interact
- IROM data loads (l32r) fail — D-bus has no path to I-bus addresses
- Exception vectors discarded in U-Boot linker — faults go to ROM
- U-Boot init functions reset cache/MMU — IROM mapping destroyed after board_early_init_f
- No DRAM loader for secondary images (UOS apps)
- No partition table parser in U-Boot
2. Architecture: Three-Layer Boot Chain
Flash Layout (4MB ESP32):
┌─────────────┬───────┬──────────────────────────────────┐
│ 0x0000 │ 4KB │ (reserved) │
│ 0x1000 │ 30KB │ MCUboot (1st stage bootloader) │
│ 0x8000 │ 4KB │ Partition Table (32B × 96) │
│ 0x9000 │ 128KB │ OTA Data (boot state) │
│ 0x20000 │ 1.3MB │ U-Boot (primary slot) │
│ 0x170000 │ 1.5MB │ UOS App (secondary slot / app) │
│ 0x300000 │ 1MB │ UOS Data / NVS / scratch │
└─────────────┴───────┴──────────────────────────────────┘
Boot Flow:
ROM → MCUboot → U-Boot → UOS microkernel → FreeRTOS/Apps
2.1 Layer 1: MCUboot (Bootloader)
Responsibilities:
- Image validation (SHA256 via imgtool TLV)
- Flash MMU mapping for U-Boot IROM code
- DRAM loading for U-Boot data
- OTA slot management (primary/secondary swap)
- Flash encryption / secure boot (future)
Current status: ✅ Working. Needs:
- Fix IROM bus mask in DPORT register (done, but U-Boot resets it)
- Add DROM mapping alongside IROM (for l32r data loads)
- Partition table reading (currently uses hardcoded slot offsets)
2.2 Layer 2: U-Boot (Hardware Init + OS Loader)
U-Boot's job on ESP32 (mirrors STM32/ARM U-Boot):
| Function | ARM U-Boot (STM32) | ESP32 U-Boot (target) |
|---|---|---|
| Console | UART via DM_SERIAL | ✅ DM_SERIAL enabled, 🔴 garbled |
| DRAM init | UCLASS_RAM driver | 🔴 Need SRAM sizing |
| Clock init | rcc_clock_setup() | 🔴 Need PLL/APB config |
| Flash driver | SPI NOR via DM_SPI_FLASH | 🔴 Need esp_flash driver |
| Partition table | GPT/DOS parser | 🔴 Need ESP32 partition table parser |
| Environment | ENV_IS_IN_SPI_FLASH | 🔴 Need ESP32 env backend |
| Network | ETH/PHY driver | N/A (ESP32 has WiFi) |
| Boot commands | bootm, bootelf, go | ✅ go works (jumps) |
| Exception vectors | ARM vectors at 0x00 | 🔴 Discarded — need IROM vectors |
| Cache control | icache_enable/dcache | 🔴 Need cache_hal wrappers |
| Timer | ARM systick | 🔴 Need CCOUNT driver |
| Watchdog | IWDG driver | 🔴 Need ESP32 WDT driver |
| GPIO | DM_GPIO driver | 🔴 Need ESP32 GPIO driver |
| Device Tree | FDT blob | 🔴 No DTS for ESP32 |
2.3 Layer 3: UOS Microkernel (RTOS)
UOS runs as a U-Boot application (loaded via bootelf/bootm):
UOS provides:
- Preemptive scheduler (tick via CCOMPARE0)
- IPC (message queues, semaphores)
- Memory protection (MPU-based, Tier 1)
- Health monitoring (heartbeat, watchdog)
- Device framework (UART, SPI, I2C, GPIO)
- Capability MDB (security model)
Current UOS ESP32 port has:
- UART0 init (polled TX)
- Timer init (CCOMPARE0, tick via CCOUNT)
- Interrupt controller (INTC_CPU_INT_ENA)
- Startup sequence (esp32_app_entry)
- Stack in DRAM, vectors in IRAM (old layout)
- Exception handlers (user/kernel/double/NMI)
3. Implementation Plan — 5 Phases
Phase 1: Stabilize U-Boot (Weeks 1-2)
Goal: U-Boot console working, can type commands.
| Task | Effort | Depends on |
|---|---|---|
1.1 Implement cache_ll_l1_enable_bus from esp-hal-3rdparty HAL |
1d | — |
| 1.2 Fix U-Boot init sequence: NO cache/MMU reset during boot | 1d | 1.1 |
| 1.3 Fix IROM data loads: add DROM mapping for U-Boot IROM region | 1d | 1.1 |
| 1.4 Fix U-Boot DM_SERIAL: proper UART0 config matching MCUboot | 1d | — |
| 1.5 Implement exception vectors in IROM linker (xtensa_vectors.S) | 2d | 1.1 |
| 1.6 Fix U-Boot env to RAM (ENV_IS_NOWHERE → ENV_IS_IN_SPI_FLASH) | 1d | 1.2 |
| 1.7 Implement CCOUNT timer for U-Boot delay/get_timer | 1d | — |
Deliverable: U-Boot prompt working, help shows commands.
Phase 2: U-Boot Hardware Drivers (Weeks 3-4)
Goal: U-Boot can access flash, load images, manage partitions.
| Task | Effort | Depends on |
|---|---|---|
| 2.1 Port ESP32 partition table parser from gen_esp32part.py to C | 2d | — |
2.2 Implement esp_flash_read via SPI1 ROM functions |
1d | 1.1 |
| 2.3 Implement DM_SPI_FLASH driver for ESP32 | 2d | 2.2 |
| 2.4 Implement ENV in SPI flash backend (CONFIG_ENV_IS_IN_SPI_FLASH) | 2d | 2.2, 2.1 |
| 2.5 Implement ESP32 WDT driver (disable/feed/config) | 1d | — |
| 2.6 Implement basic GPIO driver | 1d | — |
| 2.7 SDRAM sizing: detect SRAM0/SRAM1/SRAM2 at runtime | 1d | — |
| 2.8 PLL/clock init: read from ROM defaults, expose APB freq | 1d | — |
Deliverable: U-Boot can fatload/ext4load from flash, setenv/saveenv, reset.
Phase 3: U-Boot OS Loader (Week 5)
Goal: U-Boot can load and boot UOS microkernel images.
| Task | Effort | Depends on |
|---|---|---|
3.1 Implement bootelf for Xtensa ELF loading |
2d | 2.2 |
| 3.2 Flash MMU mapping in U-Boot (add pages for UOS app) | 1d | 1.1 |
| 3.3 DRAM loading for UOS .data/.bss | 1d | 2.2 |
| 3.4 Auto-boot: CONFIG_BOOTCOMMAND → load UOS from partition | 1d | 3.1, 2.1 |
| 3.5 Boot script support (boot.scr or bootcmd) | 1d | 2.4 |
Deliverable: U-Boot boots UOS microkernel automatically.
Phase 4: UOS Microkernel ESP32 Port (Weeks 6-8)
Goal: UOS microkernel running on ESP32, loaded by U-Boot.
| Task | Effort | Depends on |
|---|---|---|
| 4.1 Update UOS linker to IROM (0x40800000+) + DRAM (0x3FFB0000+) | 1d | — |
| 4.2 Port ESP32 exception vectors from esp-hal-3rdparty xtensa_vectors.S | 3d | — |
| 4.3 Implement UOS port layer (uos_port.h API): | ||
| 4.3.1 — tick_init (CCOMPARE0) | 1d | — |
| 4.3.2 — context_switch (save/restore a0-a15, PS, EPC) | 2d | — |
| 4.3.3 — task_stack_init | 1d | — |
| 4.3.4 — critical_enter/exit (rsil) | 0.5d | — |
| 4.3.5 — idle (waiti) | 0.5d | — |
| 4.4 ESP32 UART driver (polled + interrupt) | 2d | 4.3 |
| 4.5 ESP32 GPIO driver | 1d | 4.3 |
| 4.6 ESP32 timer/WDT driver | 1d | 4.3 |
| 4.7 Test: two tasks printing messages via UOS scheduler | 1d | 4.3 |
| 4.8 Test: IPC between two tasks (message queue) | 1d | 4.7 |
Deliverable: UOS scheduler running, tasks printing, IPC working.
Phase 5: FreeRTOS/App Compatibility (Weeks 9-10)
Goal: Run existing ESP-IDF/FreeRTOS apps on UOS.
| Task | Effort | Depends on |
|---|---|---|
| 5.1 FreeRTOS-to-UOS syscall shim (xTaskCreate→uos_task_create, etc.) | 3d | 4.3 |
| 5.2 ESP-IDF peripheral driver shim (spi_flash, gpio, uart, i2c) | 3d | 4.4-4.6 |
| 5.3 WiFi/BT stack integration (if desired) | 10d+ | Phase 4 |
| 5.4 OTA update via MCUboot from UOS | 2d | 2.1 |
| 5.5 Test: ESP-IDF blinky/hello_world on UOS | 1d | 5.1 |
4. Key Technical Decisions
4.1 IROM vs IRAM for Code
ESP32 D-bus cannot write to I-bus addresses (0x40000000+). All executable code MUST be flash-MMU-mapped to IROM (0x40800000+), never memcpy'd to IRAM. Data (.data/.bss) lives in DRAM (0x3FFxxxxx) and is loaded via memcpy.
4.2 Flash MMU Architecture
The ESP32 flash MMU maps 64KB-aligned flash pages to IROM/DROM virtual addresses:
- IROM0 entries (192-255): I-bus for instruction fetch
- DROM0 entries (0-63): D-bus for data loads
For l32r (PC-relative data load) to work from IROM code, a DROM entry must map the same flash page to a DROM virtual address. The compiler generates l32r even with -mtext-section-literals. Solution: map each IROM page ALSO as a DROM page at a corresponding 0x3F4xxxxx address. The linker must ensure literals are referenced via DROM, not IROM.
Alternative: Use only movi.n + slli sequences (no l32r) for early boot code. U-Boot startup does this. For general C code, use the DROM mapping approach.
4.3 Cache Bus Masks
DPORT_PRO_CACHE_CTRL1_REG (0x3FF00044) bits:
- Bit 0: DROM0 mask (1=masked)
- Bit 1: DROM1 mask
- Bit 2: IROM0 mask ← Must be CLEAR
- Bit 3: IROM1 mask ← Must be CLEAR
MCUboot clears these before jumping to U-Boot. U-Boot's init sequence must PRESERVE them.
4.4 Exception Vectors at VECBASE
VECBASE = 0x40800000 (IROM0 base). Vector offsets:
- 0x040: UserException
- 0x100: KernelException
- 0x180: Level2 interrupt
- 0x1C0: Level3 interrupt
- 0x200: Level4 interrupt
- 0x240: Level5 interrupt
- 0x280: Level6 interrupt
- 0x2C0: NMI
- 0x3C0: DoubleException
Must use Espressif's xtensa_vectors.S (from esp-hal-3rdparty) or equivalent. Currently U-Boot discards these in the linker script.
4.5 ESP32 Partition Table Format
[esp_partition_info_t] × N (N ≤ 96)
Each 32 bytes:
uint16 magic = 0x50AA
uint8 type (APP=0, DATA=1, BOOT=2)
uint8 subtype (FACTORY=0, OTA_0=0x10, ...)
uint32 offset (flash byte offset)
uint32 size (flash byte size)
char label[16]
uint32 flags
Terminator: magic = 0xFFFF
U-Boot needs a parser for this. The structure is simple enough to implement in ~200 lines of C.
5. Files to Create / Modify
U-Boot (universalis-uboot/)
New files:
arch/xtensa/cpu/cache.c — cache_hal wrapper
arch/xtensa/cpu/ccompare_timer.c — CCOUNT-based timer
arch/xtensa/lib/board.c — board_init_f/r
board/espressif/esp32/partition.c — partition table parser
drivers/mtd/spi/esp32_flash.c — SPI flash driver
drivers/serial/esp32_uart.c — UART driver
drivers/gpio/esp32_gpio.c — GPIO driver
drivers/watchdog/esp32_wdt.c — WDT driver
include/configs/esp32.h — update CFG_* macros
arch/xtensa/dts/esp32.dts — Device Tree
Modified:
board/espressif/esp32/u-boot.lds — add vector sections (DROM mapped)
arch/xtensa/include/asm/arch-esp32/core.h — add XCHAL_* from official core-isa.h
configs/esp32_defconfig — enable DM_SPI_FLASH, ENV_IN_SPI_FLASH, etc.
board/espressif/esp32/esp32.c — full board_init
UOS Microkernel (universalisos/microkernel/)
New files:
ports/esp32/uos_port_asm.S — context switch in asm
ports/esp32/uos_port_tick.c — CCOMPARE0 tick init
ports/esp32/uos_port_uart.c — UART driver
ports/esp32/uos_port_gpio.c — GPIO driver
ports/esp32/uos_port_wdt.c — WDT driver
ports/esp32/uos_vectors.S — exception handlers (from xtensa_vectors.S)
Modified:
ports/esp32/linker_flash.ld — IROM 0x40800000 + DRAM layout
ports/esp32/startup.S — VECBASE, vectors from IROM
ports/esp32/esp32_integration.c — full HW init
6. Immediate Next Actions
-
Fix U-Boot console (Phase 1.4): Verify DM_SERIAL UART0 config. The garbled output after banner suggests baud rate mismatch or FIFO corruption between U-Boot init and console driver.
-
Implement ESP32 cache_ll wrapper (Phase 1.1-1.2): Copy
cache_ll_l1_enable_busfromesp-hal-3rdparty/components/hal/esp32/include/hal/cache_ll.hinto U-Boot. -
Preserve MMU state (Phase 1.2): Audit U-Boot init sequence for any Cache_Read_Disable/Cache_Flush calls that would reset the MMU. Disable them for ESP32.
-
Fix IROM data loads (Phase 1.3): Map DROM entry alongside IROM entry in MCUboot.