universalisos/docs/esp32-master-plan.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

12 KiB
Raw Permalink Blame History

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, prints X\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

  1. 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.

  2. Implement ESP32 cache_ll wrapper (Phase 1.1-1.2): Copy cache_ll_l1_enable_bus from esp-hal-3rdparty/components/hal/esp32/include/hal/cache_ll.h into U-Boot.

  3. 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.

  4. Fix IROM data loads (Phase 1.3): Map DROM entry alongside IROM entry in MCUboot.