]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
feat: Rump Kernel hypercall scaffold + roadmap documentation
authorTulio A M Mendes <[email protected]>
Mon, 16 Feb 2026 01:17:43 +0000 (22:17 -0300)
committerTulio A M Mendes <[email protected]>
Mon, 16 Feb 2026 01:17:43 +0000 (22:17 -0300)
- 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

docs/RUMP_KERNEL_ROADMAP.md [new file with mode: 0644]
src/rump/rumpuser_adros.c [new file with mode: 0644]

diff --git a/docs/RUMP_KERNEL_ROADMAP.md b/docs/RUMP_KERNEL_ROADMAP.md
new file mode 100644 (file)
index 0000000..1046599
--- /dev/null
@@ -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 (file)
index 0000000..f5122bc
--- /dev/null
@@ -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 <stdint.h>
+#include <stddef.h>
+#include <stdarg.h>
+
+#include "heap.h"
+#include "console.h"
+#include "timer.h"
+#include "process.h"
+#include "sync.h"
+#include "hal/cpu.h"
+
+#include <string.h>
+
+/* ------------------------------------------------------------------ */
+/* 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;
+}