universalisos/kernel/arch/arm/uos_adspace.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

94 lines
3.3 KiB
C++

/*
* Universalisos per-VM address-space management — implementation
*
* Faithful port of PikeOS arch/arm/src/adspace.c (148 lines), `uos_` naming.
* Only PikeOS-isms removed: sched_preempt_point() -> no-op, assert() -> check,
* KERN_TO_PHYS/TTB_FLAGS from uos_armmmu_v6.h + uos_adspace.h.
*
* Author: PortugalFuturista Hypervisor Development Team (adapted from SYSGO PikeOS)
*/
#include "uos_adspace.h"
/* PikeOS calls sched_preempt_point() in a few spots; until the D-3 scheduler
* tick exists this is a no-op. */
static inline void uos_preempt_point(void) { }
/**
* Switch current address space (PikeOS p4arch_adspace_set_active).
*
* Sequence: DSB (Errata 754322) -> ASID 0 (neutral during switch) -> new
* TTBR0 (physical base of the task's user pgdir) -> invalidate BTAC ->
* DSB -> set the task's ASID to tag new TLB entries.
*/
void uos_arch_adspace_set_active(uos_taskinfo_t *td, uos_taskinfo_t *prev) {
(void)prev;
if (td == nullptr || td->adspace.pgdir == nullptr) {
return; /* kernel/idle task: no TTBR0 swap (stays on TTBR1) */
}
uos_arm_dsb(); /* Errata 754322 */
uos_arm_set_asid(0);
uos_arm_set_ttbr0(UOS_KERN_TO_PHYS((uint32_t)(uintptr_t)&td->adspace.pgdir[0]) | UOS_TTB_FLAGS);
uos_arm_inval_btac();
uos_arm_tlb_inval_all(); /* flush stale global kernel TLB entries */
uos_arm_dsb(); /* Errata 754322 */
uos_arm_set_asid(td->taskno);
}
/** Set up the address space of the idle/kernel task (runs on TTBR1 only). */
void uos_arch_adspace_register_kernel(uos_taskinfo_t *td) {
td->adspace.pgdir = nullptr;
uos_arch_adspace_register_user(td);
}
/** Initialise a new address space. The pgdir is assigned later (register_user). */
int uos_arch_adspace_init(uos_taskinfo_t *td) {
td->adspace.pgdir = nullptr;
uos_preempt_point();
return 0; /* UOS_E_OK */
}
/**
* Register a new address space: assign the task's half-size user pgdir out of
* the global pool allocated by uos_arm_init_mmu (kglobal.mach.user_pgdirs).
*/
void uos_arch_adspace_register_user(uos_taskinfo_t *td) {
uos_task_mmuctxt_t *adspace = &td->adspace;
/* PikeOS: assert(adspace->pgdir == NULL); */
if (adspace->pgdir != nullptr) {
return; /* already registered */
}
/* TTBCR=0: TTBR0 covers all 4 GB, so each guest pgdir is full-size
* (4096 entries = 16 KiB), cloned from the kernel flat map. */
adspace->pgdir = uos_user_pgdirs + td->taskno * UOS_PD_ENTRIES;
}
/** Unused callback (PikeOS leaves this empty). */
void uos_arch_adspace_free(uos_taskinfo_t *td) {
(void)td;
}
/**
* Unmap a task's complete user address space: walk every 4K-pgdir slot in
* [USR_BASE, USR_END), free any L2 page table behind it, then flush the task's
* ASID-tagged TLB entries.
*/
void uos_arch_adspace_unmap_user(uos_taskinfo_t *td) {
uos_task_mmuctxt_t *adspace = &td->adspace;
uint32_t vaddr = UOS_MEM_USR_BASE;
while (vaddr < UOS_MEM_USR_END) {
uos_preempt_point();
if (adspace->pgdir != nullptr) {
uint32_t pdent = adspace->pgdir[UOS_PD_INDEX_4K(vaddr)];
if ((pdent & UOS_PD_PT) != 0u) {
uos_arm_free_pt(td, vaddr); /* free the L2 table */
}
}
vaddr += UOS_PT_ENTRIES_4K * UOS_PAGESIZE;
}
uos_arm_tlb_inval_asid(td->taskno);
}