universalisos/XTENSA_AUDIT.md
Fábio Coutada 059f96c948 docs: add safety-critical evaluation and implementation plans
- HARD_REALTIME_EVALUATION.md: full HRT audit
- MICROKERNEL_*.md: complete architecture targets and implementation plan
- PIKEOS_3LAYER_REPLICATION_PLAN.md: 3-layer replication strategy
- PIKEOS_POSIX_AUDIT.md: POSIX compliance audit
- RTOS_AUDIT.md: RTOS comparison
- XTENSA_AUDIT.md: Xtensa ISA audit
- BIBLIOGRAPHY_SAFETY_CRITICAL_HYPERVISOR.md: references
2026-07-15 15:32:05 +01:00

11 KiB

Xtensa/ESP32 Integration Audit — UniversalisOS

ESP-IDF Boot Sequence (Complete)

┌─────────────────────────────────────────────────────────────┐
│ 1. ROM Bootloader (0x40000400)                              │
│    - Reads flash header                                     │
│    - Loads 2nd stage bootloader to IRAM                     │
│    - Jumps to 2nd stage entry (0x40080644)                  │
└─────────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────┐
│ 2. 2nd Stage Bootloader (0x40080400)                        │
│    - Configures cache, clocks, flash                        │
│    - Reads partition table                                  │
│    - Finds "factory" app at offset 0x10000                  │
│    - Loads app segments to IRAM/DRAM                        │
│    - Jumps to app entry (0x40080000)                        │
└─────────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────┐
│ 3. start_cpu0() — Entry Point                               │
│    - do_core_init() → core init handlers                    │
│    - __libc_init_array() → C constructors                   │
│    - do_secondary_init() → secondary handlers               │
│    - esp_startup_start_app() → FreeRTOS                     │
└─────────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────┐
│ 4. esp_startup_start_app()                                   │
│    - esp_int_wdt_init() → interrupt watchdog                │
│    - esp_crosscore_int_init() → cross-core interrupts       │
│    - xTaskCreatePinnedToCore(main_task) → create main task  │
│    - vTaskStartScheduler() → start FreeRTOS                 │
└─────────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────┐
│ 5. xPortStartScheduler() — FreeRTOS Port                    │
│    - portDISABLE_INTERRUPTS()                               │
│    - _xt_coproc_init() → co-processor setup                 │
│    - vPortSetupTimer() → configure CCOUNT or SYSTIMER       │
│    - port_xSchedulerRunning[core] = 1                       │
│    - xthal_window_spill() → clear window registers          │
│    - _frxt_dispatch → first context switch                  │
└─────────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────┐
│ 6. First Task Execution                                     │
│    - main_task() runs                                       │
│    - User's main() is called                                │
└─────────────────────────────────────────────────────────────┘

Xtensa Context Frame (XtExcFrame)

From xtensa_context.h:

STRUCT_FIELD(long, 4, XT_STK_EXIT,     exit)    /* exit point for dispatch */
STRUCT_FIELD(long, 4, XT_STK_PC,       pc)      /* return PC */
STRUCT_FIELD(long, 4, XT_STK_PS,       ps)      /* return PS */
STRUCT_FIELD(long, 4, XT_STK_A0,       a0)
STRUCT_FIELD(long, 4, XT_STK_A1,       a1)      /* stack pointer */
STRUCT_FIELD(long, 4, XT_STK_A2,       a2)
STRUCT_FIELD(long, 4, XT_STK_A3,       a3)
STRUCT_FIELD(long, 4, XT_STK_A4,       a4)
STRUCT_FIELD(long, 4, XT_STK_A5,       a5)
STRUCT_FIELD(long, 4, XT_STK_A6,       a6)
STRUCT_FIELD(long, 4, XT_STK_A7,       a7)
STRUCT_FIELD(long, 4, XT_STK_A8,       a8)
STRUCT_FIELD(long, 4, XT_STK_A9,       a9)
STRUCT_FIELD(long, 4, XT_STK_A10,      a10)
STRUCT_FIELD(long, 4, XT_STK_A11,      a11)
STRUCT_FIELD(long, 4, XT_STK_A12,      a12)
STRUCT_FIELD(long, 4, XT_STK_A13,      a13)
STRUCT_FIELD(long, 4, XT_STK_A14,      a14)
STRUCT_FIELD(long, 4, XT_STK_A15,      a15)
STRUCT_FIELD(long, 4, XT_STK_SAR,      sar)
STRUCT_FIELD(long, 4, XT_STK_EXCCAUSE, exccause)
STRUCT_FIELD(long, 4, XT_STK_EXCVADDR, excvaddr)
/* For CALL0 ABI: exit = _xt_user_exit, PS = PS_UM | PS_EXCM */

Stack Frame Layout (for CALL0 ABI)

HIGH ADDRESS
┌─────────────────────────┐
│ Extra Storage (CPSA)    │  ← XT_STK_EXTRA
├─────────────────────────┤
│ XtExcFrame (interrupt   │  ← Task's SP points here
│ frame on stack)         │
│   [+0]  exit = _xt_user_exit
│   [+4]  pc = task entry
│   [+8]  ps = PS_UM | PS_EXCM
│   [+12] a0 = 0
│   [+16] a1 = SP + XT_STK_FRMSZ
│   [+20] a2 = pvParameters
│   ...
│   [+76] a15
│   [+80] sar
│   [+84] exccause
│   [+88] excvaddr
├─────────────────────────┤
│ Base Save Area          │
├─────────────────────────┤
│ Task Stack              │
└─────────────────────────┘
LOW ADDRESS

What We Got Wrong

1. Stack Frame Layout

ESP-IDF uses XtExcFrame structure with exit field pointing to _xt_user_exit

  • frame->exit = _xt_user_exit (exception exit dispatcher)
  • frame->pc = task entry point
  • frame->ps = PS_UM | PS_EXCM (user mode, EXCM disabled)
  • frame->a1 = SP + XT_STK_FRMSZ (top of stack frame)
  • frame->a2 = pvParameters (task argument for CALL0)

Our code uses simple 16-word frame without exit field

2. Interrupt Handler Flow

ESP-IDF flow:

Hardware interrupt → _xt_int_enter → saves context → 
calls C handler → _xt_int_exit → checks for reschedule →
restores context → rfe

Our code:

Hardware interrupt → saves context → calls handler →
restores context → rfe

Missing: _xt_context_save/restore, interrupt nesting tracking, reschedule check

3. First Dispatch

ESP-IDF: xPortStartScheduler()_frxt_dispatch (assembly)

  • Sets port_xSchedulerRunning[core] = 1
  • Calls _frxt_dispatch which does the first context switch

Our code: uos_sched_start()uos_port_dispatch_first() (C function)

  • Simpler but missing: window spill, co-processor init, interrupt nesting

4. Timer Configuration

ESP-IDF uses two options:

  1. CCOUNT (core timer)CONFIG_FREERTOS_SYSTICK_USES_CCOUNT
    • Uses internal timer 0 or 1
    • _frxt_tick_timer_init() sets up CCOMPARE0
    • Interrupt at level 1
  2. SYSTIMERCONFIG_FREERTOS_SYSTICK_USES_SYSTIMER
    • Uses SYSTIMER peripheral
    • SysTickIsrHandler() handles the interrupt
    • Periodic mode with alarm

Our code: Direct CCOUNT manipulation

  • Missing: interrupt allocation via esp_intr_alloc()
  • Missing: proper timer interrupt handler

UniversalisOS Integration Strategy

Replace Points

ESP-IDF Component Our Equivalent Status
app_startup.c esp32_integration.c Created
startup.c startup.S ⚠️ Needs fix
port.c uos_port_context.S ⚠️ Needs fix
portasm.S esp32_vectors.S ⚠️ Needs fix
port_systick.c esp32_timer.c Not created

What Needs to Change

  1. Stack frame must include exit field — This is how FreeRTOS returns from exception
  2. Interrupt handlers need _xt_context_save/restore — Save/restore all registers properly
  3. Timer must use esp_intr_alloc() — Proper interrupt allocation
  4. First dispatch must use _frxt_dispatch — Assembly context switch, not C function
  5. VECBASE must be set early — Before any interrupts fire

Key Functions to Implement

/* Our integration layer needs: */
void esp32_app_entry(void) {
    /* 1. Set VECBASE (must be first!) */
    /* 2. Disable WDT */
    /* 3. Configure UART for debug */
    /* 4. Init interrupt controller */
    /* 5. Init timer (CCOUNT or SYSTIMER) */
    /* 6. Call uos_init() */
    /* 7. Create tasks */
    /* 8. Start scheduler */
}

/* Context switch needs: */
void _frxt_dispatch(void);  /* First dispatch — assembly */
void _frxt_int_enter(void); /* Interrupt entry — assembly */
void _frxt_int_exit(void);  /* Interrupt exit — assembly */

/* Timer needs: */
void vPortSetupTimer(void); /* Configure CCOUNT/SYSTIMER */
void _frxt_tick_timer_init(void); /* CCOUNT timer init */

Files to Study Further

  1. components/esp_system/startup.c — Boot sequence Studied
  2. components/freertos/app_startup.c — FreeRTOS startup Studied
  3. components/freertos/port_systick.c — Timer config Studied
  4. components/freertos/FreeRTOS-Kernel-SMP/portable/xtensa/port.c — Port layer Studied
  5. components/freertos/FreeRTOS-Kernel-SMP/portable/xtensa/portasm.S — Context switch Studied
  6. components/xtensa/include/xtensa_context.h — Context frame Studied
  7. components/xtensa/ — Xtensa HAL and runtime
  8. components/bootloader/ — 2nd stage bootloader

Next Steps

  1. Fix stack frame to include exit field
  2. Implement proper interrupt handlers with _xt_context_save/restore
  3. Implement _frxt_dispatch for first context switch
  4. Implement vPortSetupTimer for proper timer configuration
  5. Test on ESP32 QEMU with flash image approach