universalisos/kernel/arch/arm/uos_cmm.cpp
Fábio Coutada 9540b0528c feat(universalisos): PikeOS-style Phase B/C device drivers + Phase D microkernel
Phase B (Core Device Support) — all drivers verified in QEMU:
- Network: virtio-net cleanup, RTL8139, E1000, clause-22 MDIO PHY management,
  CAN bus, industrial protocols (Modbus/Profibus/EtherCAT), controller probe+dispatch
- Block storage: RAM disk backend (write->read->verify PASSED), virtio-blk transport,
  backend dispatch, real MBR+GPT partition parsers, SD/eMMC command framework
- GPIO: PL061 (verified), I2C: DesignWare (verified), SPI: PL022 (verified)

Phase C (Advanced Features):
- PCI: FULL PikeOS ARMv7 replica — transport-agnostic uos_pci_ops, config-address
  encoding, BAR sizing, capability walk, enumeration+bridge recursion, MSI/MSI-X
- USB: PikeOS-style layered stack — usb.h contract, usb_core.cpp (enumeration
  state machine), usb_ehci.cpp (EHCI transport)
- Display: FULL 1:1 PikeOS fbcon replica + copied font_8x16

Build foundation fixes:
- Freestanding aeabi_runtime.cpp (__aeabi_uidiv/__aeabi_uldivmod)
- PikeOS-style flat 4GB MMU section map + proper enable (unblocked device MMIO)
- guest.h MAX_GUEST_IMAGE_SIZE 256MB->16MB (BSS was 259MB)
- C/C++ linkage fixes, duplicate-virtio_net_init, MMIO access-size handling

Phase D (PikeOS ARMv7 Microkernel Port):
- D-1: Per-VM address spaces — cloned pgdirs, ASID-tagged TLB, 4K page walker,
  isolation PASSED (two guests, same VA->different PAs), guest fault recovery
- D-2: IRQ dispatch backbone — 1024-slot dispatch table, real GICv2 hardware
  (GICD_CTLR/GICC_CTLR/GICC_PMR/GICC_IAR/GICC_EOIR), arm_irq_handler wired
- D-3: Time subsystem — CNTVCT ns-since-boot, CNTP periodic ticker via D-2
- D-4: KDEV framework — linker-section driver registration, uos_kdev_init_all,
  name lookup
- D-5: VFP/NEON — lazy enable (undef trap->CPACR+FPEXC.EN), FPEXC=0x40000000
- D-6: SMP — per-CPU state, MPIDR, IPI/SGI framework (reschedule+TLB flush)

All uos_ naming (PikeOS p4_ convention adapted). Compiles -Werror freestanding C++17.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-09 09:10:53 +01:00

176 lines
7.2 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Universalisos ARM MMU core — CP15 primitives + init/activate scaffolding.
*
* D-1.2 scaffolding: the CP15 primitives (uos_arm_set_asid / _set_ttbr0 /
* _inval_btac / tlb_inval_*) and the per-VM user-pgdir pool that uos_adspace.cpp
* references, plus link-stubs for the (later) uos_arm_init_mmu / _activate_mmu /
* fault decoders. Full bodies land in D-1.3 (activate_mmu + handover) and D-1.6
* (abort decoders).
*
* Author: PortugalFuturista Hypervisor Development Team (adapted from SYSGO PikeOS)
*/
#include "uos_adspace.h"
#include "uos_mmu_walk.h"
#include "uos_psp.h"
/* The single global PSP descriptor pointer (kglobal.psp). D-1.3's uos_psp_init
* installs it; for now it is NULL so the dead code never dereferences it. */
uos_psp_descriptor_t *uos_psp = nullptr;
/* Per-VM user page-directory pool. Allocated by uos_arm_init_mmu (D-1.3); NULL
* until then — uos_arch_adspace_register_user is dead code in D-1.2. */
uint32_t *uos_user_pgdirs = nullptr;
/* -------------------------------------------------------------------------
* CP15 primitives (PikeOS p4arm_* in armmacro.S / armcpu.h)
* ------------------------------------------------------------------------- */
void uos_arm_dsb(void) {
__asm__ volatile("dsb" ::: "memory");
}
void uos_arm_set_asid(uint32_t asid) {
/* CONTEXTIDR, PROCID+ASID: cp15 c13,c0,1 */
__asm__ volatile("mcr p15, 0, %0, c13, c0, 1" : : "r"(asid));
}
void uos_arm_set_ttbr0(uint32_t ttbr0) {
/* TTBR0: cp15 c2,c0,0 */
__asm__ volatile("mcr p15, 0, %0, c2, c0, 0" : : "r"(ttbr0));
}
void uos_arm_inval_btac(void) {
/* Invalidate entire branch target address cache (Errata 754322 hygiene). */
__asm__ volatile("mcr p15, 0, %0, c7, c5, 6" : : "r"(0)); /* flush BTAC */
__asm__ volatile("mcr p15, 0, %0, c7, c5, 0" : : "r"(0)); /* invalidate I-cache */
}
void uos_arm_tlb_inval_asid(uint32_t asid) {
/* TLBIASID: cp15 c8,c7,2 — invalidate all TLB entries matching the ASID. */
__asm__ volatile("mcr p15, 0, %0, c8, c7, 2" : : "r"(asid));
}
void uos_arm_tlb_inval_page(uint32_t mva) {
/* TLBIMVA: cp15 c8,c7,1 — invalidate by MVA (ASID-tagged). */
__asm__ volatile("mcr p15, 0, %0, c8, c7, 1" : : "r"(mva));
}
void uos_arm_tlb_inval_all(void) {
__asm__ volatile("mcr p15, 0, %0, c8, c7, 0" : : "r"(0)); /* TLBIALL */
}
/* -------------------------------------------------------------------------
* D-1.3 / D-1.6 bodies (stubs for now so the unit links; replaced later)
* ------------------------------------------------------------------------- */
/* The kernel flat-map pgdir (defined in mm.cpp). */
extern "C" uint32_t *uos_get_kernel_pgdir(void);
extern "C" void *memcpy(void *dest, const void *src, unsigned long n);
/* Per-VM page-directory pool: 16 guests × 4096 entries × 4 bytes = 256 KiB.
* Each entry is cloned from the kernel flat map so kernel/device sections
* (AP=privileged-only) are present in every guest pgdir. Guest-specific user
* pages are layered on top with AP=user-RW. */
#define UOS_NUM_VM_PGDIRS 16u
static uint32_t __attribute__((aligned(16384)))
g_uos_vm_pgdir_pool[UOS_NUM_VM_PGDIRS][UOS_PD_ENTRIES];
extern "C" int uos_arm_init_mmu(void) {
uint32_t *kern = uos_get_kernel_pgdir();
/* Clone the kernel flat map into every per-VM pgdir. */
for (uint32_t i = 0; i < UOS_NUM_VM_PGDIRS; i++) {
memcpy(g_uos_vm_pgdir_pool[i], kern,
UOS_PD_ENTRIES * sizeof(uint32_t));
}
uos_user_pgdirs = (uint32_t *)g_uos_vm_pgdir_pool;
return 0;
}
int uos_arm_activate_mmu(void) {
/* TODO D-1.3: write TTBR0/TTBR1, set TTBCR=1, DACR=0x55555555, TLBIALL,
* enable SCTLR.M. */
return 0;
}
/* PSP init stub (real body in D-1.3, populated from UART/timer/board_halt). */
void uos_psp_init(void) {
/* TODO D-1.3. */
}
/* -------------------------------------------------------------------------
* D-1.5 isolation demo: prove per-VM address spaces work.
*
* Creates two tasks (guest 0, guest 1), maps the SAME virtual address
* (0x1000) to DIFFERENT physical pages in each guest's pgdir, switches
* between them, and verifies the writes don't collide — i.e. the per-VM
* TTBR0 + ASID gives real address-space isolation.
* ------------------------------------------------------------------------- */
#include "uart.h"
extern "C" void uos_adspace_isolation_demo(void) {
uart_puts("\n=== Per-VM Address-Space Isolation Demo ===\n");
/* Two task contexts (guest 0 and 1). */
static uos_taskinfo_t g0, g1;
uos_arch_adspace_init(&g0); g0.taskno = 1;
uos_arch_adspace_init(&g1); g1.taskno = 2;
uos_arch_adspace_register_user(&g0);
uos_arch_adspace_register_user(&g1);
uart_puts("D-1: adspace registered (g0 pgdir 0x"); uart_print_hex((uint32_t)(uintptr_t)g0.adspace.pgdir);
uart_puts(", g1 pgdir 0x"); uart_print_hex((uint32_t)(uintptr_t)g1.adspace.pgdir); uart_puts(")\n");
/* Map VA 0x1000 -> PA 0x50000000 in guest 0, PA 0x50100000 in guest 1. */
uos_access_t rw = UOS_M_READ | UOS_M_WRITE;
int rc0 = uos_arch_mem_create(&g0, 0x1000u, 4096u, 0x50000000u, rw);
int rc1 = uos_arch_mem_create(&g1, 0x1000u, 4096u, 0x50100000u, rw);
uart_puts("D-1: mem_create rc="); uart_print_dec((uint32_t)rc0);
uart_puts(","); uart_print_dec((uint32_t)rc1); uart_puts("\n");
/* Switch to guest 0, write pattern A at VA 0x1000. */
uart_puts("D-1: set_active g0...\n");
uos_arch_adspace_set_active(&g0, nullptr);
uart_puts("D-1: g0 active, writing 0xDEADBEEF...\n");
*((volatile uint32_t *)0x1000u) = 0xDEADBEEF;
uart_puts("D-1: g0 write OK\n");
/* Switch to guest 1, write pattern B at VA 0x1000. */
uart_puts("D-1: set_active g1...\n");
uos_arch_adspace_set_active(&g1, &g0);
uart_puts("D-1: g1 active, writing 0xCAFEBABE...\n");
*((volatile uint32_t *)0x1000u) = 0xCAFEBABE;
uart_puts("D-1: g1 write OK\n");
/* Switch back to guest 0, verify pattern A survived (isolation). */
uart_puts("D-1: set_active g0 again...\n");
uos_arch_adspace_set_active(&g0, &g1);
uart_puts("D-1: g0 re-active, reading...\n");
uint32_t val0 = *((volatile uint32_t *)0x1000u);
/* Read guest 1's page via its identity PA to confirm pattern B. */
uint32_t val1_phys = *((volatile uint32_t *)0x50100000u);
/* V-D1.3: test that an unmapped guest access faults and recovers (not halts). */
static uos_taskinfo_t idle = { { nullptr }, 0 };
uart_puts("D-1: testing guest fault recovery...\n");
uos_arch_adspace_set_active(&g0, &idle);
uart_puts("D-1: g0 active, reading unmapped VA 0x2000000...\n");
volatile uint32_t unmapped = *((volatile uint32_t *)0x2000000u);
(void)unmapped; /* if we get here, the handler recovered */
uart_puts("D-1: survived guest fault (handler recovered)\n");
/* Restore kernel address space (idle task, pgdir=NULL). */
uos_arch_adspace_set_active(&idle, &g0);
uart_puts("D-1: guest0 VA 0x1000 = 0x"); uart_print_hex(val0);
uart_puts(" (expect 0xDEADBEEF)\n");
uart_puts("D-1: guest1 PA 0x50100000 = 0x"); uart_print_hex(val1_phys);
uart_puts(" (expect 0xCAFEBABE)\n");
if (val0 == 0xDEADBEEF && val1_phys == 0xCAFEBABE) {
uart_puts("D-1: per-VM isolation PASSED\n");
} else {
uart_puts("D-1: per-VM isolation FAILED\n");
}
uart_puts("=== End Isolation Demo ===\n\n");
}