# UniversalisOS Microkernel — Implementation Plan **Date:** 2026-07-14 **Status:** ACTIVE — Implementation Plan **Scope:** Universal microkernel for ALL architectures, from AVR to Xeon --- ## Executive Summary UniversalisOS microkernel is a **single codebase** that compiles for every architecture from 8-bit AVR to 64-bit x86_64 with VT-x. It uses **compile-time tier selection** to scale from a 2KB cooperative task switcher (Cortex-M0, no MPU) to a full type-1 hypervisor with hardware virtualization (x86_64 VT-x). **Core design**: uos_* naming throughout. Agnostic API shell. Personality wrappers for FreeRTOS, ThreadX, Zephyr, POSIX, CMSIS-RTOS v2. **Primary references**: f9-kernel (MPU isolation), ThreadX (port breadth), ChibiOS (port contract), NuttX (no-MMU strategy), RTEMS (context switch HAL). --- ## Architecture: The Tier System ``` Tier 0: Bare Metal (no MPU, no MMU) Targets: Cortex-M0/M0+, AVR, basic RISC-V (RV32I), 8051 RAM: 4-16KB Kernel: ~2KB Features: Cooperative scheduling, static alloc, no isolation Context switch: Direct register save/restore (PendSV/manual) Tier 1: MPU-Based (Memory Protection Unit) Targets: Cortex-M3/M4/M7/M23/M33/M55, Cortex-R4/R5/R7, ESP32 RAM: 64-512KB Kernel: ~8KB Features: Preemptive, MPU isolation, stack canary, priority scheduling Context switch: PendSV (Cortex-M) / ECALL (RISC-V) / Syscall (Xtensa) Tier 2: MMU-Based (Full Virtual Memory) Targets: Cortex-A5/A7/A8/A9/A53/A72/A76, RISC-V Sv39+, x86 protected RAM: 1MB-4GB Kernel: ~32KB Features: Full type-1 hypervisor, partition isolation, guest OS boot Context switch: SVC/HVC (ARM) / ECALL (RISC-V) / INT (x86) Tier 3: Hardware Virtualization Targets: Cortex-A with VE, RISC-V H-extension, x86 VT-x/AMD-V RAM: 4GB+ Kernel: ~64KB Features: Hardware-assisted, nested page tables, device passthrough Context switch: VM entry/exit (hardware-managed) ``` ### Compile-Time Selection ```c // kernel/include/uos_config.h #if defined(__ARM_ARCH_6M__) // Cortex-M0/M0+ #define UOS_TIER 0 #define UOS_HAS_MPU 0 #define UOS_HAS_MMU 0 #define UOS_MAX_TASKS 8 #define UOS_TICK_RATE_HZ 100 #elif defined(__ARM_ARCH_7M__) // Cortex-M3/M4/M7 #define UOS_TIER 1 #define UOS_HAS_MPU 1 #define UOS_HAS_MMU 0 #define UOS_MAX_TASKS 32 #define UOS_MPU_REGIONS 8 #elif defined(__ARM_ARCH_7A__) // Cortex-A #define UOS_TIER 2 #define UOS_HAS_MPU 0 #define UOS_HAS_MMU 1 #define UOS_MAX_TASKS 128 #elif defined(__riscv) #if __riscv_xlen == 32 #define UOS_TIER 1 // PMP-based #define UOS_HAS_PMP 1 #else #define UOS_TIER 2 // Sv39 #define UOS_HAS_MMU 1 #endif #elif defined(__x86_64__) #define UOS_TIER 3 #define UOS_HAS_VT_X 1 #define UOS_HAS_MMU 1 #endif ``` --- ## Universal Kernel API (`uos_*`) Every function, every type, every constant uses the `uos_` prefix. This is the identity. ```c // kernel/include/uos_api.h — THE universal API /* === Types === */ typedef uint32_t uos_tick_t; typedef uint8_t uos_prio_t; typedef int32_t uos_status_t; typedef uint32_t uos_flags_t; typedef uint32_t uos_size_t; typedef uint32_t uos_count_t; /* Opaque kernel objects (Tier 1+: heap-allocated; Tier 0: static) */ typedef struct uos_task uos_task_t; typedef struct uos_sem uos_sem_t; typedef struct uos_mutex uos_mutex_t; typedef struct uos_queue uos_queue_t; typedef struct uos_event uos_event_t; typedef struct uos_timer uos_timer_t; /* Status codes */ #define UOS_OK 0 #define UOS_ERR_TIMEOUT -1 #define UOS_ERR_NOMEM -2 #define UOS_ERR_PARAM -3 #define UOS_ERR_STATE -4 #define UOS_ERR_WOULDBLOCK -5 #define UOS_ERR_ISR -6 /* Special timeout values */ #define UOS_WAIT_FOREVER 0xFFFFFFFF #define UOS_NO_WAIT 0 /* === Task Management === */ uos_task_t* uos_task_create(const char* name, uos_prio_t prio, void (*entry)(void*), void* arg, void* stack, uos_size_t stack_size); uos_status_t uos_task_delete(uos_task_t* task); uos_status_t uos_task_yield(void); uos_status_t uos_task_suspend(uos_task_t* task); uos_status_t uos_task_resume(uos_task_t* task); uos_task_t* uos_task_self(void); uos_prio_t uos_task_get_priority(uos_task_t* task); uos_status_t uos_task_set_priority(uos_task_t* task, uos_prio_t prio); /* === Scheduling === */ uos_status_t uos_sched_start(void); /* never returns */ uos_status_t uos_sched_stop(void); /* === Semaphores === */ uos_status_t uos_sem_init(uos_sem_t* sem, uos_count_t count); uos_status_t uos_sem_destroy(uos_sem_t* sem); uos_status_t uos_sem_wait(uos_sem_t* sem, uos_tick_t timeout); uos_status_t uos_sem_post(uos_sem_t* sem); uos_status_t uos_sem_post_from_isr(uos_sem_t* sem); /* === Mutexes === */ uos_status_t uos_mutex_init(uos_mutex_t* mutex, bool recursive); uos_status_t uos_mutex_destroy(uos_mutex_t* mutex); uos_status_t uos_mutex_lock(uos_mutex_t* mutex, uos_tick_t timeout); uos_status_t uos_mutex_unlock(uos_mutex_t* mutex); /* === Message Queues === */ uos_queue_t* uos_queue_create(uos_size_t msg_size, uos_count_t max_msgs); uos_status_t uos_queue_delete(uos_queue_t* queue); uos_status_t uos_queue_send(uos_queue_t* queue, const void* msg, uos_tick_t timeout); uos_status_t uos_queue_receive(uos_queue_t* queue, void* msg, uos_tick_t timeout); uos_status_t uos_queue_send_from_isr(uos_queue_t* queue, const void* msg); /* === Event Flags === */ uos_status_t uos_event_init(uos_event_t* event); uos_status_t uos_event_set(uos_event_t* event, uos_flags_t flags); uos_status_t uos_event_clear(uos_event_t* event, uos_flags_t flags); uos_status_t uos_event_wait(uos_event_t* event, uos_flags_t flags, uos_flags_t* actual, uos_tick_t timeout); /* === Timers === */ uos_timer_t* uos_timer_create(const char* name, uos_tick_t period, void (*callback)(void*), void* arg, bool periodic); uos_status_t uos_timer_start(uos_timer_t* timer); uos_status_t uos_timer_stop(uos_timer_t* timer); uos_status_t uos_timer_delete(uos_timer_t* timer); /* === Memory (Tier 1+ only; Tier 0 uses static alloc) === */ void* uos_mem_alloc(uos_size_t size); void* uos_mem_aligned_alloc(uos_size_t align, uos_size_t size); void uos_mem_free(void* ptr); /* === Time === */ uos_tick_t uos_tick_get(void); uos_status_t uos_tick_delay(uos_tick_t ticks); uos_status_t uos_tick_delay_until(uos_tick_t* prev, uos_tick_t increment); uos_tick_t uos_ms_to_ticks(uint32_t ms); uos_tick_t uos_us_to_ticks(uint32_t us); /* === Interrupt Management (Tier 1+ === */ typedef void (*uos_isr_t)(void* arg); uos_status_t uos_irq_attach(uint32_t irq, uos_isr_t handler, void* arg); uos_status_t uos_irq_enable(uint32_t irq); uos_status_t uos_irq_disable(uint32_t irq); ``` --- ## Port Layer (4 files per architecture) Inspired by ThreadX (70+ ports) + ChibiOS (`chcore.h` contract). ### Port Directory Structure ``` kernel/ports/ ├── armv6m/ ← Cortex-M0/M0+ (Tier 0) │ ├── uos_port.h ← Port defines, critical section macros │ ├── uos_port_init.c ← NVIC setup, SysTick config │ ├── uos_port_context.S ← PendSV handler: save/restore R4-R11 │ └── uos_port_dispatch.S ← First task launch: load SP, restore context │ ├── armv7m/ ← Cortex-M3/M4/M7 (Tier 1) │ ├── uos_port.h ← MPU register access, CLZ instruction │ ├── uos_port_init.c ← NVIC priority config, MPU setup │ ├── uos_port_context.S ← PendSV with lazy FPU stacking │ └── uos_port_dispatch.S ← First task with MPU region setup │ ├── armv8m/ ← Cortex-M23/M33/M55 (Tier 1, TrustZone) │ ├── uos_port.h ← TrustZone SAU/IDAU defines │ ├── uos_port_init.c ← Secure/Non-secure partition setup │ ├── uos_port_context.S ← Secure context save (8 additional regs) │ └── uos_port_dispatch.S ← Non-secure task launch via SG instruction │ ├── armv7a/ ← Cortex-A (Tier 2) │ ├── uos_port.h ← CP15 register access, cache ops │ ├── uos_port_init.c ← MMU setup, GIC init, page table config │ ├── uos_port_context.S ← SVC/IRQ handlers: save full register set │ └── uos_port_dispatch.S ← First task with MMU context switch │ ├── armv8a/ ← Cortex-A53/A72 (Tier 2/3) │ ├── uos_port.h ← EL2/EL1 system register access │ ├── uos_port_init.c ← VBAR_EL2, HCR_EL2, VTTBR setup │ ├── uos_port_context.S ← EL2 exception vectors, context save │ └── uos_port_dispatch.S ← ERET into first guest task │ ├── riscv32/ ← RV32IMC (Tier 1, PMP) │ ├── uos_port.h ← CSR access macros, PMP defines │ ├── uos_port_init.c ← PMP configuration, CLINT timer setup │ ├── uos_port_context.S ← ECALL/trap handler: save caller-saved regs │ └── uos_port_dispatch.S ← MRET into first task │ ├── riscv64/ ← RV64GC (Tier 2, Sv39) │ ├── uos_port.h ← Sv39 page table defines │ ├── uos_port_init.c ← SATP setup, PLIC init │ ├── uos_port_context.S ← Trap handler with full context save │ └── uos_port_dispatch.S ← SRET into first task │ ├── x86/ ← 32-bit protected mode (Tier 2) │ ├── uos_port.h ← GDT/TSS/IDT defines │ ├── uos_port_init.c ← GDT setup, IDT init, PIT timer │ ├── uos_port_context.S ← INT handler: pusha/popa context save │ └── uos_port_dispatch.S ← IRET to first task │ ├── x86_64/ ← 64-bit long mode (Tier 3) │ ├── uos_port.h ← VMX/EPT defines, MSR access │ ├── uos_port_init.c ← GDT/IDT, VMX init, HPET timer │ ├── uos_port_context.S ← SYSCALL handler: swapgs, full save │ └── uos_port_dispatch.S ← SYSRET/IRETQ to first task │ ├── xtensa/ ← ESP32/ESP32-S3 (Tier 1) │ ├── uos_port.h ← Windowed register defines, EXCSAVE │ ├── uos_port_init.c ← Interrupt matrix, timer config │ ├── uos_port_context.S ← Level-1 interrupt handler context save │ └── uos_port_dispatch.S ← First task via RFI │ └── mips32/ ← PIC32 (Tier 1) ├── uos_port.h ← CP0 register access ├── uos_port_init.c ← EBASE setup, timer config ├── uos_port_context.S ← General exception handler └── uos_port_dispatch.S ← ERET to first task ``` ### Port Contract (what each port MUST export) ```c // kernel/ports//uos_port.h — REQUIRED exports /* Critical section */ uos_status_t uos_port_critical_enter(void); /* return previous state */ void uos_port_critical_exit(uos_status_t prev); /* Context switch trigger */ void uos_port_yield(void); /* PendSV/ECALL/SVC/syscall */ /* First task dispatch (never returns) */ void uos_port_dispatch_first(void) __attribute__((noreturn)); /* Tick timer */ void uos_port_tick_init(uint32_t freq_hz); uint32_t uos_port_tick_get(void); /* MPU/MMU (Tier 1+) */ #if UOS_HAS_MPU void uos_port_mpu_set_region(uint32_t idx, uint32_t base, uint32_t size, uint32_t attrs); void uos_port_mpu_enable(void); void uos_port_mpu_disable(void); #endif #if UOS_HAS_MMU void uos_port_mmu_set_table(uint32_t table_phys); void uos_port_mmu_invalidate_tlb(void); #endif /* Architecture-specific constants */ #define UOS_PORT_STACK_ALIGN 8 /* or 4 for Cortex-M0, 16 for AArch64 */ #define UOS_PORT_MIN_STACK_SIZE 256 /* or 128 for Tier 0 */ ``` --- ## Personality Shell Architecture ``` kernel/personality/ ├── freertos/ ← FreeRTOS API → uos_* wrapper │ ├── FreeRTOS.h ← Redirects to uos_* internals │ ├── task.h ← xTaskCreate → uos_task_create │ ├── semphr.h ← xSemaphoreCreateCounting → uos_sem_init │ ├── queue.h ← xQueueCreate → uos_queue_create │ ├── timers.h ← xTimerCreate → uos_timer_create │ └── portable/ ← FreeRTOS port layer (maps to uos_port_*) │ ├── threadx/ ← ThreadX API → uos_* wrapper │ ├── tx_api.h ← tx_thread_create → uos_task_create │ ├── tx_thread.h ← ThreadX thread types → uos_task_t │ └── tx_port.h ← Maps ThreadX port macros to uos_port_* │ ├── zephyr/ ← Zephyr API → uos_* wrapper │ ├── kernel.h ← k_thread_create → uos_task_create │ └── zephyr/ ← Zephyr kernel object mapping │ ├── posix/ ← POSIX PSE51 → uos_* wrapper │ ├── pthread.h ← pthread_create → uos_task_create │ ├── semaphore.h ← sem_init → uos_sem_init │ ├── mqueue.h ← mq_open → uos_queue_create │ └── signal.h ← signal handling (Tier 2+) │ ├── cmsis_rtos2/ ← CMSIS-RTOS v2 → uos_* wrapper │ ├── cmsis_os2.h ← osThreadNew → uos_task_create │ └── cmsis_os.h ← osThreadCreate (v1 compat) │ └── arduino/ ← Arduino API → uos_* wrapper └── Arduino.h ← setup()/loop() → uos_task ``` ### How a Personality Shell Works ```c // Example: FreeRTOS personality // kernel/personality/freertos/task.h #include "uos_api.h" #define xTaskCreate(entry, name, stack, arg, prio, handle) \ uos_task_create(name, (uos_prio_t)(prio), (void(*)(void*))(entry), \ (void*)(arg), NULL, (uos_size_t)(stack)) #define vTaskDelete(task) uos_task_delete((uos_task_t*)(task)) #define vTaskDelay(ticks) uos_tick_delay((uos_tick_t)(ticks)) #define xTaskGetCurrentTaskHandle() ((TaskHandle_t)uos_task_self()) ``` --- ## Kernel Object System (inspired by RT-Thread) ```c // kernel/include/uos_object.h — Base kernel object typedef enum { UOS_OBJ_TASK = 0x01, UOS_OBJ_SEM = 0x02, UOS_OBJ_MUTEX = 0x03, UOS_OBJ_QUEUE = 0x04, UOS_OBJ_EVENT = 0x05, UOS_OBJ_TIMER = 0x06, } uos_obj_type_t; typedef struct uos_object { uos_obj_type_t type; const char* name; uint8_t flags; /* static/dynamic, allocated/free */ #if UOS_TIER >= 1 struct uos_object* next; /* linked list for object registry */ #endif } uos_object_t; /* All kernel objects embed uos_object_t as first member */ struct uos_task { uos_object_t obj; /* must be first */ uos_prio_t priority; uint8_t state; void* stack_ptr; uos_size_t stack_size; /* ... arch-specific context at end ... */ }; ``` --- ## Context Switch: Cortex-M Example (Tier 0/1) ```asm @ kernel/ports/armv7m/uos_port_context.S @ PendSV handler — the heart of Cortex-M context switching .global PendSV_Handler .type PendSV_Handler, %function PendSV_Handler: @ Save context of current task mrs r0, psp @ Get process stack pointer tst lr, #0x10 @ Check if FPU context needs saving it eq vstmdbeq r0!, {s16-s31} @ Save FPU regs if used stmdb r0!, {r4-r11, lr} @ Save core regs + EXC_RETURN @ Load uos_task_current pointer ldr r1, =uos_task_current ldr r2, [r1] str r0, [r2] @ Save SP to current task's TCB @ Load next task ldr r2, =uos_task_next ldr r2, [r2] str r2, [r1] @ Update current = next ldr r0, [r2] @ Load next task's SP @ Restore context of next task ldmia r0!, {r4-r11, lr} @ Restore core regs + EXC_RETURN tst lr, #0x10 it eq vldmiaeq r0!, {s16-s31} @ Restore FPU regs if needed msr psp, r0 @ Set process stack pointer bx lr @ Return to next task (hardware restores R0-R3, PC, LR, xPSR) ``` --- ## Implementation Phases ### Phase 1: Core Microkernel (Tier 0 — Cortex-M0) — Weeks 1-4 - `uos_task_t` with static allocation (8 tasks max) - Cooperative round-robin scheduler - PendSV context switch (Cortex-M0: save R4-R11, no FPU) - `uos_tick_delay()` via SysTick - `uos_sem_init/wait/post` (counting semaphore) - `uos_sched_start()` — launch first task - **Test**: STM32F0 (real hardware) + QEMU cortex-m0 ### Phase 2: Preemptive + MPU (Tier 1 — Cortex-M4) — Weeks 5-8 - Preemptive priority scheduler (32 levels, bitmap) - MPU region management (8 regions per task) - `uos_mutex_init/lock/unlock` with priority inheritance - `uos_queue_create/send/receive` (message passing) - `uos_event_init/set/wait` (event flags) - `uos_timer_create/start/stop` (periodic + one-shot) - Tickless idle for power efficiency - **Test**: STM32F4 (real hardware) + QEMU cortex-m3 ### Phase 3: Additional Ports — Weeks 9-12 - `armv8m/` — Cortex-M33 with TrustZone (Secure/Non-secure) - `riscv32/` — RV32IMC with PMP (f9-kernel pattern) - `xtensa/` — ESP32 (windowed registers, level-1 interrupt) - `armv7a/` — Cortex-A with MMU (Tier 2) - **Test**: nRF5340, ESP32, HiFive1, QEMU arm-virt ### Phase 4: Personality Shells — Weeks 13-16 - FreeRTOS personality: `xTaskCreate`, `xSemaphoreCreate`, `xQueueCreate` - ThreadX personality: `tx_thread_create`, `tx_mutex_get`, `tx_queue_send` - POSIX personality: `pthread_create`, `sem_wait`, `mq_send` - CMSIS-RTOS v2: `osThreadNew`, `osSemaphoreAcquire` - **Test**: Run existing FreeRTOS/ThreadX apps unchanged on UniversalisOS ### Phase 5: Hypervisor Extensions (Tier 2/3) — Weeks 17-24 - `armv8a/` — AArch64 EL2 with vGIC, Stage-2 translation - `riscv64/` — Sv39 with H-extension (G-stage page tables) - `x86_64/` — VT-x with EPT, VMCS management - Guest OS boot (Linux, FreeRTOS, bare-metal) - Device passthrough (PCI, UART, timer) - **Test**: QEMU aarch64-virt, QEMU riscv64-virt, QEMU x86_64 --- ## Target Hardware Matrix (Priority Order) | P0 | STM32F4 (Cortex-M4), QEMU all arches, x86_64 (existing) | | P1 | nRF52/53 (Cortex-M4/M33), ESP32 (Xtensa), STM32H7 (M7) | | P2 | i.MX RT1060 (M7), HiFive1 (RISC-V), PIC32 (MIPS32) | | P3 | STM32MP1 (A7+M4), i.MX 8M (A53), RISC-V boards | --- ## File Structure (Target) ``` kernel/ ├── include/ │ ├── uos_api.h ← Universal kernel API │ ├── uos_object.h ← Kernel object base type │ ├── uos_config.h ← Tier/arch detection │ ├── uos_types.h ← uos_tick_t, uos_prio_t, uos_status_t │ └── uos_compiler.h ← Compiler abstraction (__attribute__, etc.) ├── src/ │ ├── core/ │ │ ├── uos_task.c ← Task management (universal) │ │ ├── uos_sched.c ← Scheduler (universal, tier-scaled) │ │ ├── uos_sem.c ← Semaphore (universal) │ │ ├── uos_mutex.c ← Mutex with priority inheritance │ │ ├── uos_queue.c ← Message queue │ │ ├── uos_event.c ← Event flags │ │ ├── uos_timer.c ← Software timers │ │ ├── uos_mem.c ← Memory allocator (tier-scaled) │ │ ├── uos_tick.c ← Tick management │ │ ├── uos_irq.c ← Interrupt dispatch │ │ └── uos_idle.c ← Idle task (WFI/WFE/HALT) │ ├── port/ ← 11 architecture ports (4 files each) │ └── personality/ ← 6 RTOS personality shells ├── platform/ ← Board-specific BSP (pinmux, clock, UART) └── test/ ← Kernel test suite ``` --- ## Key Design Decisions 1. **Static allocation for Tier 0**: No malloc, no heap. All kernel objects statically allocated at compile time via `UOS_STATIC_TASK()` macro. 2. **PendSV for all Cortex-M**: Hardware saves R0-R3, LR, PC, xPSR on exception entry. Software saves R4-R11 (and optionally S16-S31). This is the most efficient context switch for Cortex-M. 3. **Bitmap priority scheduler**: O(1) using CLZ instruction (available on all ARM Cortex). 32 priority levels. Round-robin within same priority. 4. **f9-kernel MPU pattern for Tier 1**: Flexible pages (power-of-2, aligned) mapped to MPU regions. LRU eviction when 8 regions insufficient. 5. **Object registry for Tier 1+**: All kernel objects linked in a list for debug/monitoring. Tier 0 skips this (no spare RAM). 6. **Personality shells are compile-time**: `#define UOS_PERSONALITY_FREERTOS` selects the FreeRTOS wrapper. Multiple personalities can coexist (each gets its own namespace). 7. **Port contract is 4 files**: Every architecture port provides exactly `uos_port.h`, `uos_port_init.c`, `uos_port_context.S`, `uos_port_dispatch.S`. No exceptions. --- ## Success Criteria ### Phase 1 (Week 4) - [ ] Cooperative scheduling on Cortex-M0 (QEMU + STM32F0) - [ ] 8 tasks running with semaphores and delays - [ ] Context switch < 5μs on 48MHz Cortex-M0 - [ ] Kernel binary < 2KB ### Phase 2 (Week 8) - [ ] Preemptive scheduling on Cortex-M4 (QEMU + STM32F4) - [ ] MPU isolation: task A cannot access task B's memory - [ ] Mutex with priority inheritance - [ ] Message queues, event flags, software timers - [ ] Context switch < 2μs on 168MHz Cortex-M4 ### Phase 3 (Week 12) - [ ] ESP32 port boots and runs tasks - [ ] RISC-V PMP port boots on HiFive1 - [ ] TrustZone Secure/Non-secure split on Cortex-M33 ### Phase 4 (Week 16) - [ ] FreeRTOS app compiles and runs unchanged on UniversalisOS - [ ] ThreadX app compiles and runs unchanged on UniversalisOS - [ ] POSIX pthread app compiles and runs on UniversalisOS ### Phase 5 (Week 24) - [ ] Linux boots as guest on AArch64 Tier 3 - [ ] FreeRTOS runs as guest inside UniversalisOS hypervisor on x86_64 - [ ] Same kernel binary runs on Cortex-M0 AND Cortex-A72 (different tiers)