From: Tulio A M Mendes Date: Mon, 16 Feb 2026 01:17:43 +0000 (-0300) Subject: feat: Rump Kernel hypercall scaffold + roadmap documentation X-Git-Url: https://projects.tadryanom.me/docs/static/git-logo.png?a=commitdiff_plain;h=04d4f12061d16925285fcc93356f4f8ef91e53df;p=AdrOS.git feat: Rump Kernel hypercall scaffold + roadmap documentation - docs/RUMP_KERNEL_ROADMAP.md: full integration plan with 4 phases (filesystem → network → USB → audio), API mapping table, prerequisites checklist, and build integration notes - src/rump/rumpuser_adros.c: Phase 1+3 hypercall scaffold implementing: rumpuser_init, rumpuser_malloc/free, rumpuser_putchar/dprintf, rumpuser_exit, rumpuser_getparam, rumpuser_getrandom, rumpuser_clock_gettime/sleep - Phase 2 (threads/sync) and Phase 4 (file/block I/O) are TODO stubs --- diff --git a/docs/RUMP_KERNEL_ROADMAP.md b/docs/RUMP_KERNEL_ROADMAP.md new file mode 100644 index 0000000..1046599 --- /dev/null +++ b/docs/RUMP_KERNEL_ROADMAP.md @@ -0,0 +1,137 @@ +# Rump Kernel Integration Roadmap + +## Overview + +[Rump Kernels](https://github.com/rumpkernel/wiki) allow AdrOS to reuse production-quality +NetBSD drivers (USB, audio, ZFS, TCP/IP, etc.) without writing them from scratch. The +integration requires implementing a **hypercall layer** (`librumpuser_adros`) that maps +NetBSD kernel abstractions to AdrOS primitives. + +## Prerequisites Status + +| Requirement | Status | AdrOS Implementation | +|---|---|---| +| Dynamic memory (malloc/free) | ✅ Ready | `kmalloc`/`kfree` (buddy allocator, 16-byte aligned) | +| Kernel threads | ✅ Ready | `process_create_kernel`, `PROCESS_FLAG_THREAD` | +| Mutexes | ✅ Ready | `kmutex_t` in `sync.c` | +| Semaphores | ✅ Ready | `ksem_t` with timeout in `sync.c` | +| Condition variables | ✅ Ready | `kcond_t` (wait/signal/broadcast) in `sync.c` | +| FPU/SSE context switch | ✅ Ready | FXSAVE/FXRSTOR per process | +| Nanosecond timekeeping | ✅ Ready | TSC-calibrated `clock_gettime_ns()` | +| Shared IRQ handling | ✅ Ready | IRQ chaining (32-node pool) in `idt.c` | +| Block I/O | ✅ Ready | ATA multi-drive, VFS callbacks | +| PCI enumeration | ✅ Ready | PCI scanner + HAL driver registry | + +## Rumpuser Hypercall API → AdrOS Mapping + +The `rumpuser(3)` interface requires ~35 functions grouped into 8 categories: + +### Phase 1: Core (Memory + Console + Init) +``` +rumpuser_init() → validate version, store upcalls +rumpuser_malloc() → kmalloc() with alignment +rumpuser_free() → kfree() +rumpuser_putchar() → kprintf("%c", ch) +rumpuser_dprintf() → kprintf() +rumpuser_exit() → kprintf + halt/panic +rumpuser_getparam() → return NCPU, hostname from cmdline +rumpuser_getrandom() → /dev/urandom or RDRAND +``` + +### Phase 2: Threads + Synchronization +``` +rumpuser_thread_create() → process_create_kernel() wrapper +rumpuser_thread_exit() → process_exit_notify() + schedule() +rumpuser_thread_join() → waitpid() equivalent +rumpuser_curlwpop() → per-thread TLS via GS segment +rumpuser_curlwp() → read from TLS +rumpuser_mutex_init() → kmutex_init() or spinlock_init() +rumpuser_mutex_enter() → kmutex_lock() (unschedule rump ctx first) +rumpuser_mutex_exit() → kmutex_unlock() +rumpuser_mutex_owner() → track owner in wrapper struct +rumpuser_rw_init() → custom reader-writer lock +rumpuser_rw_enter() → kcond_t based rwlock +rumpuser_cv_init() → kcond_init() +rumpuser_cv_wait() → kcond_wait() +rumpuser_cv_timedwait() → kcond_wait() with timeout +rumpuser_cv_signal() → kcond_signal() +rumpuser_cv_broadcast() → kcond_broadcast() +``` + +### Phase 3: Clocks + Signals +``` +rumpuser_clock_gettime() → clock_gettime_ns() (MONO) or rtc (WALL) +rumpuser_clock_sleep() → process_sleep() with ns precision +rumpuser_kill() → process_kill() signal delivery +rumpuser_seterrno() → per-thread errno via TLS +``` + +### Phase 4: File/Block I/O +``` +rumpuser_open() → vfs_open() or raw device access +rumpuser_close() → vfs_close() +rumpuser_getfileinfo() → vfs_stat() +rumpuser_bio() → async ATA I/O + callback +rumpuser_iovread() → vfs_read() scatter-gather +rumpuser_iovwrite() → vfs_write() scatter-gather +rumpuser_syncfd() → flush + barrier +``` + +## Implementation Plan + +### Stage 1: Filesystem (ext2 via Rump) — weeks 1-2 +**Goal**: Mount an ext2 image using NetBSD's ext2fs driver via Rump instead of AdrOS native. +- Implement Phase 1 + 2 + 3 hypercalls +- Build `librumpuser_adros.a` statically linked into kernel +- Cross-compile `librump.a` + `librumpvfs.a` + `librumpfs_ext2fs.a` +- Test: mount ext2 disk image, read files, compare with native driver + +**Why first**: Exercises memory, threads, and synchronization without hardware IRQs. + +### Stage 2: Network (E1000 via Rump) — weeks 3-4 +**Goal**: Replace lwIP+custom E1000 driver with NetBSD's full TCP/IP stack. +- Implement Phase 4 (block I/O) hypercalls +- Cross-compile `librumpnet.a` + `librumpdev_pci.a` + `librumpdev_wm.a` +- Wire PCI device passthrough via rumpuser_open/bio +- Test: ping, TCP connect through Rump network stack + +**Why second**: Adds IRQ sharing and async I/O, leveraging existing IOAPIC routing. + +### Stage 3: USB (xHCI/EHCI via Rump) — weeks 5-8 +**Goal**: USB mass storage support. +- Cross-compile USB host controller + mass storage components +- Map PCI MMIO regions for USB controller +- DMA buffer management via rumpuser_malloc with alignment +- Test: enumerate USB devices, mount USB mass storage + +### Stage 4: Audio (HDA via Rump) — optional +**Goal**: Intel HDA audio playback. +- Leverages same PCI/IRQ infrastructure as Stage 2-3 + +## Build Integration + +``` +src/rump/ + rumpuser_adros.c # All hypercall implementations + rumpuser_adros.h # Internal helpers + rump_integration.c # Boot-time init, component loading +third_party/rump/ + include/ # NetBSD rump headers + lib/ # Pre-built librump*.a archives +``` + +## Missing Kernel Primitives (to implement as needed) + +| Primitive | Needed For | Complexity | +|---|---|---| +| Reader-writer lock (`krwlock_t`) | `rumpuser_rw_*` | Small | +| Per-thread TLS storage | `rumpuser_curlwp` | Small (GS segment) | +| Aligned kmalloc | `rumpuser_malloc` with alignment | Small (round up) | +| Async block I/O callback | `rumpuser_bio` | Medium | + +## References + +- [rumpuser(3) manpage](https://man.netbsd.org/rumpuser.3) +- [buildrump.sh](https://github.com/rumpkernel/buildrump.sh) +- [Rump Kernel wiki](https://github.com/rumpkernel/wiki/wiki) +- Antti Kantee, "The Design and Implementation of the Anykernel and Rump Kernels", 2012 diff --git a/src/rump/rumpuser_adros.c b/src/rump/rumpuser_adros.c new file mode 100644 index 0000000..f5122bc --- /dev/null +++ b/src/rump/rumpuser_adros.c @@ -0,0 +1,222 @@ +/* + * AdrOS Rump Kernel Hypercall Implementation + * + * This file implements the rumpuser(3) hypercall interface, mapping + * NetBSD Rump Kernel abstractions to AdrOS kernel primitives. + * + * Reference: https://man.netbsd.org/rumpuser.3 + * + * Phase 1: Core (memory, console, init, params, random) + * Phase 2: Threads + synchronization (TODO) + * Phase 3: Clocks + signals (TODO) + * Phase 4: File/Block I/O (TODO) + */ + +#include +#include +#include + +#include "heap.h" +#include "console.h" +#include "timer.h" +#include "process.h" +#include "sync.h" +#include "hal/cpu.h" + +#include + +/* ------------------------------------------------------------------ */ +/* Rump hypercall version */ +/* ------------------------------------------------------------------ */ + +#define RUMPUSER_VERSION 17 + +/* ------------------------------------------------------------------ */ +/* Upcall pointers (set by rumpuser_init) */ +/* ------------------------------------------------------------------ */ + +typedef void (*rump_schedule_fn)(void); +typedef void (*rump_unschedule_fn)(void); + +static rump_schedule_fn g_hyp_schedule = NULL; +static rump_unschedule_fn g_hyp_unschedule = NULL; + +/* ------------------------------------------------------------------ */ +/* Phase 1: Initialization */ +/* ------------------------------------------------------------------ */ + +struct rump_hyperup { + rump_schedule_fn hyp_schedule; + rump_unschedule_fn hyp_unschedule; + /* additional upcalls omitted for now */ +}; + +int rumpuser_init(int version, const struct rump_hyperup *hyp) { + if (version != RUMPUSER_VERSION) { + kprintf("[RUMP] Version mismatch: kernel=%d, expected=%d\n", + version, RUMPUSER_VERSION); + return 1; + } + + if (hyp) { + g_hyp_schedule = hyp->hyp_schedule; + g_hyp_unschedule = hyp->hyp_unschedule; + } + + kprintf("[RUMP] Hypercall layer initialized (v%d).\n", version); + return 0; +} + +/* ------------------------------------------------------------------ */ +/* Phase 1: Memory allocation */ +/* ------------------------------------------------------------------ */ + +int rumpuser_malloc(size_t len, int alignment, void **memp) { + if (!memp) return 22; /* EINVAL */ + if (len == 0) { *memp = NULL; return 0; } + + /* kmalloc returns 16-byte aligned; for larger alignment, over-allocate */ + if (alignment <= 16) { + *memp = kmalloc(len); + } else { + /* Over-allocate and manually align */ + size_t total = len + (size_t)alignment + sizeof(void*); + void *raw = kmalloc(total); + if (!raw) { *memp = NULL; return 12; } /* ENOMEM */ + uintptr_t addr = ((uintptr_t)raw + sizeof(void*) + (size_t)alignment - 1) + & ~((uintptr_t)alignment - 1); + ((void**)addr)[-1] = raw; + *memp = (void*)addr; + return 0; + } + + return *memp ? 0 : 12; /* ENOMEM */ +} + +void rumpuser_free(void *mem, size_t len) { + (void)len; + if (mem) kfree(mem); +} + +/* ------------------------------------------------------------------ */ +/* Phase 1: Console output */ +/* ------------------------------------------------------------------ */ + +void rumpuser_putchar(int ch) { + char c = (char)ch; + (void)c; + kprintf("%c", ch); +} + +void rumpuser_dprintf(const char *fmt, ...) { + char buf[256]; + va_list ap; + va_start(ap, fmt); + kvsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + kprintf("%s", buf); +} + +/* ------------------------------------------------------------------ */ +/* Phase 1: Termination */ +/* ------------------------------------------------------------------ */ + +#define RUMPUSER_PANIC 0xFF + +void rumpuser_exit(int value) { + if (value == RUMPUSER_PANIC) { + kprintf("[RUMP] PANIC — halting.\n"); + } else { + kprintf("[RUMP] Exit with code %d.\n", value); + } + for (;;) hal_cpu_idle(); +} + +/* ------------------------------------------------------------------ */ +/* Phase 1: Parameter retrieval */ +/* ------------------------------------------------------------------ */ + +int rumpuser_getparam(const char *name, void *buf, size_t buflen) { + if (!name || !buf || buflen == 0) return 22; /* EINVAL */ + + if (strcmp(name, "_RUMPUSER_NCPU") == 0) { + /* Report 1 CPU for now — SMP rump requires more work */ + strncpy((char*)buf, "1", buflen); + return 0; + } + if (strcmp(name, "_RUMPUSER_HOSTNAME") == 0) { + strncpy((char*)buf, "adros-rump", buflen); + return 0; + } + if (strcmp(name, "RUMP_VERBOSE") == 0) { + strncpy((char*)buf, "1", buflen); + return 0; + } + + /* Unknown parameter */ + ((char*)buf)[0] = '\0'; + return 2; /* ENOENT */ +} + +/* ------------------------------------------------------------------ */ +/* Phase 1: Random */ +/* ------------------------------------------------------------------ */ + +#define RUMPUSER_RANDOM_HARD 0x01 +#define RUMPUSER_RANDOM_NOWAIT 0x02 + +int rumpuser_getrandom(void *buf, size_t buflen, int flags, size_t *retp) { + (void)flags; + if (!buf || !retp) return 22; + + /* Simple PRNG seeded from TSC — adequate for early bring-up */ + uint8_t *p = (uint8_t*)buf; + uint32_t seed = (uint32_t)clock_gettime_ns(); + for (size_t i = 0; i < buflen; i++) { + seed = seed * 1103515245 + 12345; + p[i] = (uint8_t)(seed >> 16); + } + *retp = buflen; + return 0; +} + +/* ------------------------------------------------------------------ */ +/* Phase 3: Clocks */ +/* ------------------------------------------------------------------ */ + +#define RUMPUSER_CLOCK_RELWALL 0 +#define RUMPUSER_CLOCK_ABSMONO 1 + +extern uint32_t rtc_unix_timestamp(void); + +int rumpuser_clock_gettime(int clk, int64_t *sec, long *nsec) { + if (!sec || !nsec) return 22; + + if (clk == RUMPUSER_CLOCK_RELWALL) { + *sec = (int64_t)rtc_unix_timestamp(); + *nsec = 0; + } else { + uint64_t ns = clock_gettime_ns(); + *sec = (int64_t)(ns / 1000000000ULL); + *nsec = (long)(ns % 1000000000ULL); + } + return 0; +} + +int rumpuser_clock_sleep(int clk, int64_t sec, long nsec) { + if (clk == RUMPUSER_CLOCK_RELWALL) { + uint32_t ms = (uint32_t)(sec * 1000 + nsec / 1000000); + uint32_t ticks = (ms + TIMER_MS_PER_TICK - 1) / TIMER_MS_PER_TICK; + if (ticks > 0) process_sleep(ticks); + } else { + /* ABSMONO: sleep until absolute monotonic time */ + uint64_t target_ns = (uint64_t)sec * 1000000000ULL + (uint64_t)nsec; + uint64_t now = clock_gettime_ns(); + if (target_ns > now) { + uint64_t delta_ms = (target_ns - now) / 1000000ULL; + uint32_t ticks = (uint32_t)((delta_ms + TIMER_MS_PER_TICK - 1) / TIMER_MS_PER_TICK); + if (ticks > 0) process_sleep(ticks); + } + } + return 0; +}