A minimal Docker + QEMU environment for safely developing and testing Linux kernel modules.
Author: Federico Roux [rouxfederico@gmail.com]
This project provides an isolated sandbox for learning kernel driver development without risking or your host system. It uses Docker for building modules and QEMU for testing them in a safe, virtualized environment.
graph TD
A[Host Machine] --> B[Docker Container]
B --> C[Build Tools]
B --> D[QEMU VM]
C --> E[Kernel Modules .ko]
E --> F[Bundled in initramfs]
F --> D
D --> G[Safe Testing Environment]
style A fill:#e1f5ff
style B fill:#fff4e1
style D fill:#ffe1e1
style G fill:#e1ffe1
Key Features:
- ✅ Safe testing - Crashes won't affect your host
- ✅ Fast builds - Native compilation in container
- ✅ Minimal setup - Single Dockerfile, works out of the box
- ✅ Version matched - Module builds for exact QEMU kernel
- Docker and Docker Compose
- ~1GB disk space
- (Optional)
/dev/kvmfor faster QEMU
# 1. Build the Docker image (first time only, ~2-3 minutes)
docker compose build
# 2. Start container
docker compose run --rm ldd-sandbox bash
# 3. Build a module (inside container)
make all
# or: cd 01-hello-world && make
# 4. Test in QEMU (inside container)
./run-qemu.sh
# 5. Inside QEMU VM
insmod /modules/hello.ko
dmesg | tail
rmmod hello
# 6. Exit QEMU
# Press: Ctrl+A, then XsequenceDiagram
participant Host
participant Container
participant QEMU
Host->>Host: Edit code with IDE
Host->>Container: docker compose run
Container->>Container: make all
Note over Container: Build .ko modules
Container->>Container: ./run-qemu.sh
Note over Container: Bundle modules into initramfs
Container->>QEMU: Boot VM with modules
QEMU->>QEMU: insmod hello.ko
QEMU->>QEMU: Test module
QEMU->>QEMU: rmmod hello
QEMU-->>Container: Ctrl+A X (exit)
Container-->>Host: Make changes, repeat
- Edit code on host with your favorite editor
- Build in container:
make all - Test in QEMU:
./run-qemu.sh - Iterate - changes on host are immediately visible in container
# Build all modules
make all
# Build specific module
make example ex=01-hello-world
# Clean build artifacts
make cleanThe run-qemu.sh script automatically:
- Finds the container's kernel
- Bundles your built
.komodules into initramfs - Boots QEMU with modules ready to test
# Inside container
./run-qemu.sh
# Inside QEMU
ls /modules/ # See available modules
insmod /modules/hello.ko # Load module
dmesg | tail # See kernel messages
lsmod # List loaded modules
rmmod hello # Unload moduleExit QEMU: Press Ctrl+A, then X
- What are Device Drivers?
Device drivers are kernel modules that act as a bridge between hardware devices and the operating system. They translate generic OS commands into device-specific operations.
- Types of Device Drivers
- Character Devices: Accessed as streams of bytes (e.g., serial ports, keyboards)
- Block Devices: Accessed in fixed-size blocks (e.g., hard drives, USB sticks)
- Network Devices: Handle network packets (e.g., Ethernet cards)
- Essential Concepts
- Kernel Space vs User Space: Drivers run in kernel space with full hardware access
- Kernel Modules: Loadable pieces of kernel code (.ko files)
- Major/Minor Numbers: Identify devices (major = driver, minor = specific device)
# Get a shell in the container
docker compose run --rm ldd-sandbox bash
# Run specific command
docker compose run --rm ldd-sandbox make all# Inside QEMU
dmesg # All kernel messages
dmesg | tail # Recent messages
dmesg | grep hello # Filter messages
dmesg -c # Clear message buffer# Inside container (after building, requires extra steps, check individual README.md)
modinfo 01-hello-world/hello.ko
# Shows: version, description, author, license, parametersAnother option is using objdump:
objdump -d -j .modinfo 01-hello-world/build/hello.ko# Inside QEMU - Check if module loaded
lsmod | grep hello
# See module details
cat /proc/modules | grep hello
# Check for errors
dmesg | grep -i error- This is expected - the minimal QEMU environment doesn't have 9p filesystem support
- Modules are bundled into initramfs instead (automatic)
- This is normal and can be ignored
- BTF is for advanced debugging/eBPF, not needed for basic drivers
- Make sure you're building inside the container
- Kernel headers version must match the kernel in
/boot
- Make sure
/dev/kvmis available on your host - Check that
devices: - /dev/kvmis incompose.yml
stateDiagram-v2
[*] --> Built: make
Built --> Loaded: insmod
Loaded --> Running: module_init()
Running --> Unloading: rmmod
Unloading --> Cleaned: module_exit()
Cleaned --> [*]
Running --> Running: module functions
note right of Built
.ko file created
end note
note right of Running
Module active in kernel
Can handle requests
end note
flowchart TB
%% --- User Space ---
subgraph USERSPACE["user space"]
APPS["apps"]
end
%% This subgraph is only to create a wide bar
subgraph SYSCALL["-"]
SYSCALL2["syscall interface"]
end
APPS --> SYSCALL
%% --- Kernel subsystems (5 columns) ---
subgraph KERNELSUB["kernel subsystems"]
PROCESS["process mgmt"]
MEM["mem mgmt"]
FS["fs"]
DEV["dev ctrl"]
NET["networking"]
end
SYSCALL --> PROCESS
SYSCALL --> MEM
SYSCALL --> FS
SYSCALL --> DEV
SYSCALL --> NET
%% --- Kernel services ---
subgraph KSERV["kernel services"]
CONCUR["concurrency\nmultitasking"]
VMEM["virtual memory"]
VFS["VFS, files\nand dirs"]
TTY["Tlys dev access"]
CONN["connectivity"]
end
PROCESS --> CONCUR
MEM --> VMEM
FS --> VFS
DEV --> TTY
NET --> CONN
%% --- Hardware space ---
subgraph HWS["hardware space"]
CPU["CPU\n(arch dep code)"]
HMEM["memory\n(mem mgmt)"]
DISKS["disks\n(block devices)"]
CONS["consoles\n(char devices)"]
NETIF["network interfaces\n(IF drivers)"]
end
CONCUR --> CPU
VMEM --> HMEM
VFS --> DISKS
TTY --> CONS
CONN --> NETIF
- Create a new directory:
mkdir 02-my-driver - Add source in
02-my-driver/src/ - Create
02-my-driver/Makefile(copy from01-hello-world) - Update root
Makefile: add02-my-drivertoSUBDIRS - Build and test!
See LICENSE file.
This is a learning project. Feel free to add more example drivers and submit pull requests!