- 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
22 KiB
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
// 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.
// 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)
// kernel/ports/<arch>/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
// 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)
// 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)
@ 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_twith static allocation (8 tasks max)- Cooperative round-robin scheduler
- PendSV context switch (Cortex-M0: save R4-R11, no FPU)
uos_tick_delay()via SysTickuos_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/unlockwith priority inheritanceuos_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 translationriscv64/— 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
-
Static allocation for Tier 0: No malloc, no heap. All kernel objects statically allocated at compile time via
UOS_STATIC_TASK()macro. -
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.
-
Bitmap priority scheduler: O(1) using CLZ instruction (available on all ARM Cortex). 32 priority levels. Round-robin within same priority.
-
f9-kernel MPU pattern for Tier 1: Flexible pages (power-of-2, aligned) mapped to MPU regions. LRU eviction when 8 regions insufficient.
-
Object registry for Tier 1+: All kernel objects linked in a list for debug/monitoring. Tier 0 skips this (no spare RAM).
-
Personality shells are compile-time:
#define UOS_PERSONALITY_FREERTOSselects the FreeRTOS wrapper. Multiple personalities can coexist (each gets its own namespace). -
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)