Rust bindings (ddi-sys) and safe driver layer (ddi) for the illumos kernel DDI/DKI
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Till Wegmüller e82e75d406
Some checks failed
ci / kmod-illumos (push) Successful in 12m48s
ci / check-hosted (push) Failing after 3h22m25s
Merge pull request 'private: module_loaded ignores a module left in memory by a failed _init (G7)' (#9) from g7-module-busy into main
2026-10-05 16:35:10 +00:00
.cargo M0: design document and skeleton for Rust illumos DDI crates 2026-09-20 20:35:37 +02:00
.forgejo/workflows ddi::private::virtio: the virtio framework's protocol as types 2026-09-29 23:33:38 +02:00
crates private: module_loaded: the exclusion rests on mod_lock 2026-10-03 19:33:56 +02:00
docs private: module_loaded ignores a module stuck after a failed _init 2026-10-03 19:24:48 +02:00
examples private: module_loaded looks a module up by name under mod_lock 2026-10-03 18:28:59 +02:00
targets M0: design document and skeleton for Rust illumos DDI crates 2026-09-20 20:35:37 +02:00
xtask illumos-kernel-private-virtio-sys: bind the virtio framework at gate cda5b9a49f 2026-09-29 23:33:38 +02:00
.gitignore M0: design document and skeleton for Rust illumos DDI crates 2026-09-20 20:35:37 +02:00
Cargo.lock illumos-kernel-private-virtio-sys: bind the virtio framework at gate cda5b9a49f 2026-09-29 23:33:38 +02:00
Cargo.toml illumos-kernel-private-virtio-sys: bind the virtio framework at gate cda5b9a49f 2026-09-29 23:33:38 +02:00
README.md private: module_loaded looks a module up by name under mod_lock 2026-10-03 18:28:59 +02:00
rust-toolchain.toml M0: design document and skeleton for Rust illumos DDI crates 2026-09-20 20:35:37 +02:00
rustfmt.toml Add Solstice CI workflow and rustfmt config; apply rustfmt 2026-09-20 22:16:23 +02:00

illumos-ddi

Rust for illumos kernel drivers, built on the documented Driver Developer Interface (the DDI/DKI in section 9 of the manual) rather than on a private pile of extern "C" declarations.

The project is hosted at https://code.aopc.cloud/illumos/illumos-ddi.

Where things stand

On 20 September 2026 the example driver rnull was compiled for the kernel target, linked with the illumos linker as a kernel module, loaded with add_drv on an OpenIndiana Hipster 2026.04 kernel, exercised from userland and unloaded again with rem_drv.

On 26 September 2026 the binding surface became complete. Every function that section 9F of the manual documents and the kernel exports is declared, in the core crate or in one of eight subsystem crates, and every structure layout and every symbol was checked against the running kernel. The safe layer now covers what a leaf driver needs: properties, locks of every kind, timers and task queues, kernel statistics, register and PCI configuration access, interrupts, DMA, name-value lists and layered devices. A second example driver, rselftest, runs 49 checks of the safe layer inside the running kernel, and all of them pass.

Every kernel panic traced to misuse of the DDI becomes a type rule of the safe layer, with a runtime guard, regression tests and an entry in the "Panics prevented" table of docs/DESIGN.md section 5.12. The first is the own-node rule: DMA handles, interrupts, register mappings and PCI configuration handles are allocated from the driver's OwnNode, which only the attach glue creates. Minor nodes, property updates and LDI identifiers, which the kernel ties to the node and its driver, need it too.

Two things are not there yet. The hardware parts of the safe layer have only been checked against the gate source and the compiler, because testing them needs a device to bind to; porting pchtemp is the next step. And safe layers for the subsystem frameworks, such as MAC for network drivers, are still to be written on top of their raw bindings.

What is in the repository

The repository is a Cargo workspace.

crates/ddi-sys holds the raw bindings for everything that resolves from the base kernel: #[repr(C)] structures, opaque handles, constants and extern "C" prototypes. Every item corresponds to a section 9 manual page, and its doc comment names the page and quotes its interface level. The crate contains no logic.

The subsystem crates follow the same rules for frameworks that live in their own kernel modules. ddi-mac-sys binds the GLDv3 network driver interface, ddi-scsa-sys the SCSI target and host adapter interfaces, ddi-usba-sys the USB client and host controller interfaces, ddi-neti-sys the network information and packet hook interfaces, and ddi-ksensor-sys the kernel sensors. ddi-pcmcia-sys, ddi-gld-sys and ddi-ktest-sys cover PC Card services, the older GLDv2 network interface and the kernel test framework. Each crate records the module it needs, so the link step can add the -N flag.

crates/illumos-kernel-private-sys is the private tier: kernel interfaces without a manual page, today the file descriptor functions (falloc, setf, getf, ...) and the specfs and vnode calls a driver needs to hand one of its own minor numbers to user space as a file descriptor from an ioctl. The declarations were checked against one gate commit, and the crate is empty unless the feature naming it, unstable-gate-cda5b9a49f, is enabled; each item cites the gate source line instead of a manual page. The safe wrapper is ddi::private::fd, behind the ddi feature of the same name. The crate also declares the module records of sys/modctl.h (modctl_t, the list modules and its lock mod_lock), so that a module can tell whether another one is loaded without loading it: ddi::private::module_loaded. crates/illumos-kernel-private-virtio-sys binds the gate's virtio framework (misc/virtio, io/virtio/virtio.h) under the same rules and feature; its safe wrapper, ddi::private::virtio behind the ddi feature unstable-gate-cda5b9a49f-virtio, turns the framework's initialisation protocol into types (setup, negotiated, running), hands queue completions to a task queue, and keeps every buffer a descriptor chain names alive until the device returns it. Section 4 of docs/DESIGN.md gives the rules, section 5.13 the virtio rules.

crates/ddi is the layer a driver actually programs against. Its types carry the rules that the manual pages state in prose. A Sleepable token proves that the current context may block, and interrupt handlers receive an Atomic token instead. Errno, DdiError and a few family-specific error types keep the return conventions of the DDI apart. Locks own their data. Handles release what they own when dropped, and callbacks keep their state alive until the kernel can no longer call them. The module! macro produces the module linkage and the entry-point glue from a Driver implementation.

examples/rnull is a pseudo character device that discards writes, returns end of file on reads, and answers three ioctls. It is the template for a new driver. examples/rselftest is the in-kernel test of the safe layer; built with its private-gate feature it also tests ddi::private::fd and ddi::private::module_loaded.

xtask builds a driver for the kernel target and links it with ld -ztype=kmod, collecting module dependencies from the crate graph. It also holds the two checks that keep the bindings honest: lint-man compares every doc comment with its manual page and measures coverage, and kernel-check compares every layout and symbol with a running kernel.

docs/DESIGN.md explains the reasoning behind all of this. Read it before changing the crate boundaries. docs/ddi-inventory.md maps the kernel's headers, manual pages and linker mapfiles to the crates.

Building

Type checking and linting work on any host with stable Rust and the hosted illumos target installed:

rustup target add x86_64-unknown-illumos
cargo check --workspace --target x86_64-unknown-illumos
cargo clippy --workspace --target x86_64-unknown-illumos -- -D warnings

Building an actual kernel module needs a nightly toolchain, because core and alloc have to be rebuilt without SSE for the kernel target. The compile step runs on any host. The link step needs the illumos linker and therefore runs on an illumos machine.

cargo xtask build rnull      # nightly-2026-04-16, -Zbuild-std, targets/x86_64-illumos-kmod.json
cargo xtask link rnull       # on illumos: ld -ztype=kmod, -N flags from crate metadata
cargo xtask deps rnull       # show the -N list and mapfiles the crate graph implies

The two checks need an illumos gate checkout and a running illumos kernel respectively:

cargo xtask lint-man --gate ~/illumos-gate --coverage
cargo xtask kernel-check --emit | ssh root@host mdb -k | cargo xtask kernel-check --verify -

The result of the link is a kernel module file. Installing it is the usual illumos procedure: copy the module to /kernel/drv/amd64/, copy the .conf file to /kernel/drv/, and run add_drv. The design document lists the exact commands that were used for the first load.

Continuous integration

.forgejo/workflows/ci.yml runs on Solstice CI. One job type-checks, lints, checks formatting and builds the documentation on a hosted Linux image against the illumos target. A second job runs natively on an illumos image, installs the pinned nightly, builds rnull and rselftest for the kernel target and links them as kernel modules. Neither job loads a module, since that needs root on a kernel that is allowed to panic; loading is done by hand on a test VM, as the design document describes.

Licence

MPL-2.0.