~vdupras/duskbsd

NetBSD fork with no userland
Abandoned in favor of the "kernel module" approach
Make the i386/MINE config compile again
ata: wd_readsector() was ported!

refs

master
browse  log 

clone

read-only
https://git.sr.ht/~vdupras/duskbsd
read/write
git@git.sr.ht:~vdupras/duskbsd

You can also use your local clone with git send-email.

#DuskBSD

Abandoned: this approach of "DuskBSD" has been abandoned in favor of a "kernel module" approach. This repository is no longer updated.

This is a fork of the NetBSD kernel with no userland and drastic simplifications of the kernel to account for requirements that aren't part of Dusk OS design goals, that is: memory protection, security, processes, concurrency, foolproof-ness.

Instead of launching "init", it loads a raw binary (no ELF) in memory from rootfs "/kexec" and runs this in kernel memory.

To allow this binary to have access to the kernel's drivers, it passes around to this opaque binary a structure with enough addresses to go around merrily.

Read Dusk OS's NetBSD page for a rationale.

#Status and Changelog

The simplification of the kernel is ongoing, here's what has been done so far:

  • Remove all arches except i386. For the refactoring ahead, this code is noise and I'll re-add arches as needed.
  • Remove rump, nfs, netsmb
  • Remove modules
  • Begin dismantling the authorization system
  • Remove Veriexec subsystem
  • Remove per-UID ressource "accounting"
  • Mount root FS as read/write directly (rather than mounting it as read-only and have /sbin/init remount it later).
  • In dev/wscons/wskbd.c, function wskbd_input() is borked to cut off link between keyboard and wsdisplay, and instead send these keypresses in a buffer when is then fetched by Dusk's (key?) routine.
  • vnodes are never released.
  • Remove VFS' FHTOVP VPTOSH SNAPSHOT EXTATTRCTL
  • Remove post-mountroot hooks: they're used for firmware loading which we don't do.
  • Remove any kind of backward compatibility.
  • Remove all syscalls.
  • Begin dismantling all very complex mechanisms on which syscalls rested, such as execve, fork, etc.
  • Remove swapping.
  • Remove VFS synching thread (syncs in Dusk are all explicit).

Not all configurations work, as many parts have been gutted. You can see an example of a configuration that is known to work in arch/i386/conf/MINE.

#Usage

Build this kernel like you would a regular NetBSD kernel and copy it to something like /duskbsd.

Then, build a payload to execute. It has to be raw i386 code. Write this to /kexec.

Then, at boot time, drop to boot prompt and write something like boot duskbsd. Once the kernel is done initializing, it will load /kexec in memory and execute it in kernel mode.

For a convenient way to work on this kernel through QEMU, see DuskBSD Workbench.

#Exposing the kernel API

Running a binary, right, but how to access kernel drivers and other goodies?

Through the "Kernel API" struct that is passed to kexec. Right before jumping to the kecxec payload, the kernel will push the address of that API to ESP.

You can see the struct in kern/kexec.c:KexecAPI.

Another problem is the predictability of the address at which the kexec program is ran. It's much more useful to have a predictable org we can rely on so that we can reference addresses inside the program through tomething else than relative jumps.

The mechanism through which this is done is in a comment called "DuskBSD fixed addressing" in kern/kexec.c.

Here's an example NASM listing that would result in the kexec payload to print X to the console and then wait for a key press, then print the pressed key, then loop forever:

ORG 0xc0430000 ; KEXECADDR
BITS 32
pop eax
pop eax
mov ebx, [eax]
mov [emit], ebx
mov ebx, [eax+4]
mov [key], ebx
push 0x58 ; 'X'
call [emit]
pop eax ; 'X'
call [key]
push eax ; typed key
call [emit]
forever: jmp forever
emit: dd 0	; void emit(int c)
key: dd 0	; int key()

#System V ABI

A little reminder that NetBSD uses the System V ABI, which is necessary to keep in mind when we write the kexec payload. On i386, that means:

  • Function arguments are pushed to the stack before it's called, first argument pushed last. The return address is of course at the top of the stack when the function is executed.
  • It's the caller's responsibility to pop arguments that were previously pushed.
  • Function result goes in eax.
  • ebx, esi, edi, ebp, esp are preserved by the callee, the rest are not.
  • Stack is 32-bit aligned at all times.