KVM Hello World 2 - Using rust-vmm Crates¶
A second iteration of the KVM hello world prototype that uses the vm-memory
crate from the rust-vmm project for simplified and safer guest memory
management.
Motivation¶
The original helloworld prototype demonstrated that KVM-based bare-metal execution works. This prototype demonstrates how rust-vmm crates can simplify VMM development:
- Safer memory operations: No manual pointer arithmetic
- Automatic cleanup: Memory freed when dropped
- Type safety:
GuestAddressprevents address confusion - Bounds checking:
write_objvalidates writes stay in bounds
These improvements become critical as complexity increases with virtio devices.
Architecture¶
┌─────────────────────────────────────────────────────────┐
│ VMM (Host Process) │
│ - Uses vm-memory for guest memory management │
│ - GuestMemoryMmap for automatic mmap allocation │
│ - GuestAddress for type-safe address handling │
│ - write_obj/write_slice for safe memory writes │
└─────────────────────────────────────────────────────────┘
│
│ KVM ioctls
▼
┌─────────────────────────────────────────────────────────┐
│ Guest (Bare-Metal Binary) │
│ - Unchanged from helloworld │
│ - Same no_std bare-metal code │
│ - Guest is unaware of VMM implementation details │
└─────────────────────────────────────────────────────────┘
Key Improvements¶
Guest Memory Allocation¶
Before (helloworld):
// Manual allocation requiring unsafe
let layout = std::alloc::Layout::from_size_align(size, 4096)?;
let ptr = unsafe { std::alloc::alloc_zeroed(layout) };
if ptr.is_null() {
return Err("Failed to allocate guest memory".into());
}
// ... must manually track for cleanup
After (helloworld2):
// Automatic allocation with vm-memory
let regions = vec![(GuestAddress(0), size as usize)];
let guest_mem = GuestMemoryMmap::<()>::from_ranges(®ions)?;
// Cleanup is automatic when guest_mem is dropped
Memory Writes¶
Before (helloworld):
fn setup_gdt(guest_mem: *mut u8) {
let gdt = unsafe { guest_mem.add(GDT_BASE as usize) as *mut u64 };
unsafe {
ptr::write(gdt.add(0), 0u64);
ptr::write(gdt.add(1), 0x00AF_9A00_0000_FFFFu64);
ptr::write(gdt.add(2), 0x00CF_9200_0000_FFFFu64);
}
}
After (helloworld2):
fn setup_gdt(guest_mem: &GuestMemoryMmap) -> Result<(), ...> {
guest_mem.write_obj(0u64, GuestAddress(GDT_BASE))?;
guest_mem.write_obj(0x00AF_9A00_0000_FFFFu64, GuestAddress(GDT_BASE + 8))?;
guest_mem.write_obj(0x00CF_9200_0000_FFFFu64, GuestAddress(GDT_BASE + 16))?;
Ok(())
}
Loading Guest Code¶
Before (helloworld):
fn load_guest_code(guest_mem: *mut u8, code: &[u8]) {
unsafe {
let dest = guest_mem.add(GUEST_CODE_BASE as usize);
ptr::copy_nonoverlapping(code.as_ptr(), dest, code.len());
}
}
After (helloworld2):
// One-liner with bounds checking
guest_mem.write_slice(&guest_code, GuestAddress(GUEST_CODE_BASE))?;
Dependencies¶
| Crate | Version | Purpose |
|---|---|---|
kvm-ioctls |
0.19 | Safe KVM API wrappers |
kvm-bindings |
0.10 | KVM FFI bindings |
vm-memory |
0.15 | Guest memory abstraction |
Code Comparison¶
| Metric | helloworld | helloworld2 | Change |
|---|---|---|---|
| VMM lines | 325 | ~230 | -29% |
| Unsafe blocks | 8 | 1 | -87% |
| Manual ptr ops | Yes | No | Eliminated |
| Auto cleanup | No | Yes | Added |
| Error handling | Panics | Results | Improved |
Memory Layout¶
Unchanged from helloworld:
| Address Range | Purpose |
|---|---|
| 0x1000-0x1FFF | GDT (4KB) |
| 0x2000-0x5FFF | Page tables (16KB) |
| 0x10000-0x1FFFF | Guest code (64KB) |
| 0x20000-0x2FFFF | Stack (64KB) |
Building¶
Prerequisites¶
Same as helloworld:
- Linux with KVM support (
/dev/kvm) - Rust nightly toolchain
rust-srccomponentcargo-binutils(providesrust-objcopy)
Using DevContainer (Recommended)¶
The prototype includes a devcontainer with all dependencies:
cd prototypes/helloworld2
# Using devcontainer CLI
devcontainer up --workspace-folder .
devcontainer exec --workspace-folder . ./build.sh
# Or using Docker directly
docker build -t helloworld2-dev .devcontainer/
docker run --rm -it --device=/dev/kvm \
-v "$(pwd)":/workspace -w /workspace \
helloworld2-dev ./build.sh
Manual Build¶
cd prototypes/helloworld2
# Install Rust nightly with required components
rustup install nightly
rustup component add rust-src --toolchain nightly
rustup component add llvm-tools-preview
cargo install cargo-binutils
# Build
./build.sh
Running¶
Expected Output¶
Loaded guest binary: 99 bytes from guest.bin
KVM API version: 12
Created VM
Allocated 2097152 bytes of guest memory via vm-memory
Configured memory region
Set up GDT at 0x1000
Set up page tables at 0x2000
Loaded guest code at 0x10000
Created vCPU
Configured special registers for long mode
Configured general registers (RIP=0x10000, RSP=0x2fff8)
--- Starting guest execution ---
Hello from KVM guest!
--- Guest executed HLT ---
Guest completed successfully!
Why This Matters¶
The vm-memory crate is just the beginning. rust-vmm provides:
| Crate | Estimated Code Reduction |
|---|---|
| vm-memory | ~30% for memory management |
| virtio-queue | ~50% for virtqueue handling |
| virtio-blk | ~70% for block device |
| Overall | ~72% for full virtio-block |
As noted in the comparison document, using rust-vmm crates reduces a complete virtio-block implementation from ~1600 lines to ~450 lines.
Next Steps¶
Future prototypes will build on this foundation:
- helloworld3: Add virtio-queue for virtqueue implementation
- virtio-blk-prototype: Full block device with rust-vmm crates
- image-conversion: Actual disk image transcoding