Unikernels and embedded targets
Freestanding Unikernels (x86_64, AArch64, RISC-V64) and bare-metal microcontrollers (ESP32, RISC-V).
NURL compiles to target-agnostic LLVM IR or C, making it straightforward to run without a host operating system — either as a bootable Unikernel appliance or directly on bare-metal microcontrollers.
Unikernels (x86_64, AArch64, RISC-V64)
unikernel/ turns an ordinary .nu program into a self-contained, bootable kernel image with no Linux kernel or host userland underneath:
./unikernel/build.sh app.nu # x86_64 PVH ELF (QEMU / KVM / Firecracker)
./unikernel/build.sh --arch aarch64 app.nu # ARM64 QEMU virt image
./unikernel/build.sh --arch riscv64 app.nu # RISC-V64 QEMU virt image
./unikernel/build.sh --iso app.nu # bootable BIOS + UEFI hybrid .isoWhat is inside the unikernel target:
nolibc+runtime_bare.c— a freestanding C library (including a from-scratchlibmandmalloc), cooperative fiber scheduler, and hardware entropy driver (RDRAND/RNDR/ VirtIO-RNG).- Sub-5 ms boot — enters directly via the PVH ELF note (
x86_64) orvirtboot stub (AArch64/RISC-V64), with optional SMP multi-vCPU wake (APIC/PSCI/SBI HSM). - VirtIO-Net + sans-IO network stack —
unikernel/net/sockets.nuimplements the standard NURL socket ABI (tcp_*,udp_*,dns_*, TLS 1.3, HTTP/1.1, HTTP/2, MCP) in pure NURL overstdlib/net/andunikernel/drivers/virtionet.nu. - VirtIO-Blk + FAT12/16/32 & initfs — read/write persistent block storage (
unikernel/drivers/virtioblk.nu,stdlib/fs/fat32.nu) plus baked-in read-only filesystems.
See unikernel/README.md for QEMU, Firecracker, and Kubernetes (unikernel/k8s/) deployment guides.
Memory-mapped I/O
stdlib/hal/mmio.nu
provides the register-level primitives used to build hardware drivers. Each primitive reads or
writes a 32-bit word at a fixed physical address:
( mmio_read32 i addr ) → i // load *(u32*)addr
( mmio_write32 i addr i32 val ) → v // store *(u32*)addr = val
( mmio_set32 i addr i32 mask ) → v // *(u32*)addr |= mask
( mmio_clear32 i addr i32 mask ) → v // *(u32*)addr &= ~maskBecause arbitrary physical addresses bypass memory safety, mmio_* functions are declared raw and must be called from an unsafe @ driver function. They compile to LLVM's load volatile / store volatile, so the optimizer never hoists, reorders, or coalesces an MMIO access.
ESP32
stdlib/hal/esp32.nu
is a register-level HAL for the classic ESP32 (Xtensa). You build it
entirely on mmio.nu. There is no ESP-IDF. There is no FFI beyond the
memory-mapped registers themselves. Register addresses are cross-checked
against Espressif's technical reference manual.
( esp32_gpio_enable_output i mask ) → v // drive these pins as outputs
( esp32_gpio_set i mask ) → v // set pins high
( esp32_gpio_clear i mask ) → v // set pins low
( esp32_uart_putc i ch ) → v // blocking write, UART0
( esp32_uart_getc ) → i // blocking read, UART0Both CPU families on the chip are covered, through two different toolchains:
| Chip family | ISA | Toolchain |
|---|---|---|
| ESP32 / S2 / S3 (classic) | Xtensa LX6/LX7 | esp-clang + ESP-IDF |
| ESP32-C3 / C6 / H2 | RISC-V (RV32IMC) | stock LLVM llc |
The Xtensa path has been flashed to real ESP32 silicon. It is verified
over serial. A GPIO blink uses only NURL's register writes. A call
crosses from C into NURL and back. examples/esp32/ has both paths.
idf-blink/ is a complete ESP-IDF project. All GPIO access is NURL. C
supplies only app_main. blink.nu targets the RISC-V chips directly
through stock LLVM. It needs no vendor fork.
Next
- Platforms — the rest of NURL's cross-compile targets.
- Build steps, prerequisites, and both toolchains in full are in
examples/esp32/README.md.
Last updated on