Lab 2.1: Navigate the Firecracker Repository
Background
Before you change a line of Firecracker you have to be able to find the line. The codebase went
through a large refactor that merged many once-separate crates into one big vmm crate, so blog
posts and old GitHub comments routinely point at paths that no longer exist. This lab is a guided
tour of the src/ workspace and the vmm internals, done the only way that survives a refactor:
with rg, find, fd, and cargo against your own checkout. You will produce a module map —
your own one-page index of where each subsystem lives — and a picture of the crate dependency graph.
This is a navigate-it lab. You will not write VMM code. You will write down, in your own words and with the commands that prove it, where everything is. That map is the substrate for every later lab.
Why This Lab Matters for Contributors
- A maintainer's first question on a vague PR is "did you look at how the existing code does it?" You cannot answer that without knowing where the existing code is.
- Old line numbers and crate names are traps. The habit you build here — locate by role with a runnable command, never by remembered line number — is the anti-staleness discipline this whole curriculum is built on.
- The crate boundaries encode the architecture: the API server is not in
vmm,vmm-sys-utilis not vendored anymore, all device emulation is invmm. Knowing the boundaries tells you which crate a change belongs in.
Prerequisites
- Lab 1.1 complete — you have a checkout that builds.
ripgrep(rg),find, and ideallyfdandtokeiinstalled (all in the dev container).- Cargo available (inside
tools/devtool shellif you don't have a host toolchain):
# Confirm you are at the repo root and tooling is present.
ls Cargo.toml src/ tools/devtool
rg --version && find . -maxdepth 0 && (fd --version || echo "fd optional") && (tokei --version || echo "tokei optional")
Step-by-Step Tasks
Step 1: See the Workspace Shape
Firecracker is a Cargo workspace: a top-level Cargo.toml lists member crates under src/. Read
the member list from the manifest, not from memory:
# The authoritative list of workspace members.
rg -n 'members|"src/' Cargo.toml
# Cross-check against the directory tree.
ls src/
You should see these members (roles are stable even though paths drift — verify on your branch):
| Crate | Role |
|---|---|
vmm | The core VMM library: machine model, vCPU/KVM state, all device emulation, most subsystems. The big merged crate — you live here. |
firecracker | The firecracker binary + the HTTP API server (src/firecracker/src/api_server/). Drives vmm. |
jailer | The jailer binary: the isolation barrier, then execs firecracker. |
seccompiler | Compiles JSON seccomp filters → BPF. (rust-vmm's external seccompiler originated here.) |
cpu-template-helper | Create/inspect/verify CPU templates. |
snapshot-editor | Inspect/edit snapshot files. |
rebase-snap | Rebase a diff-snapshot's memory onto a base. |
acpi-tables | Build guest ACPI tables. |
utils | Internal shared utilities. |
clippy-tracing, log-instrument, log-instrument-macros | Dev/CI tooling, not shipped in the VMM. |
Warning: Do not trust this table over your checkout. If
rg 'members' Cargo.tomlshows a member not listed here, or omits one, the repo moved — yourrgoutput wins. That is the entire point of this lab.
ASCII view of how the binaries relate to the library:
┌──────────────────────────────────────────────┐
user ───►│ jailer (binary) ── isolation barrier ──┐ │
└──────────────────────────────────────────│────┘
▼ exec
┌──────────────────────────────────────────────┐
API ───►│ firecracker (binary) │
(UDS) │ • HTTP API server (src/firecracker/src/ │
│ api_server/) │
│ • drives ──────────────┐ │
└───────────────────────────│──────────────────┘
▼ links
┌──────────────────────────────────────────────┐
│ vmm (library crate) │
│ Vmm, Vcpu, devices/, vstate/, builder.rs… │
└──────────────────────────────────────────────┘
│ uses (external rust-vmm deps)
▼
kvm-ioctls · kvm-bindings · vm-memory · linux-loader · vm-superio · event-manager · vmm-sys-util
Step 2: Measure the Crates
Get a sense of weight so you know where the substance is. tokei gives lines per crate; fall back to
find | wc -l if it isn't installed:
# Lines of Rust per workspace member (where the mass is).
tokei src/ --type Rust 2>/dev/null || find src -name '*.rs' | xargs wc -l | tail -n 1
# How many .rs files in each crate — a quick weight proxy.
for d in src/*/; do printf "%-22s %s\n" "$d" "$(find "$d" -name '*.rs' | wc -l)"; done
Expect vmm to dwarf everything else. That is the merged crate, and it is where your reading time
goes.
Step 3: Map the vmm Internals
This is the core of the map. Everything below is a module inside src/vmm/src/ — historically
several were separate crates. Locate each by role; do not memorize a path:
# The top-level modules inside the vmm crate.
ls src/vmm/src/
# Device emulation — all of it lives here.
ls src/vmm/src/devices/ src/vmm/src/devices/virtio/
# vCPU / VM / KVM state.
ls src/vmm/src/vstate/
# Per-architecture code.
ls src/vmm/src/arch/
Fill in this map as you go (a ? means "run the command and write down what you find"):
| Subsystem | Find it with | Role |
|---|---|---|
| The running microVM object | rg -n 'struct Vmm' src/vmm/src/ | Vmm — owned by the VMM thread; holds devices, vCPUs, memory. |
| Pre-boot configuration | rg -n 'struct VmResources' src/vmm/src/resources.rs | VmResources — aggregated config before InstanceStart. |
| The control-plane command enum | rg -n 'enum VmmAction' src/vmm/src/rpc_interface.rs | VmmAction — what the API thread sends the VMM thread. |
| Build/boot entry points | rg -n 'fn build_microvm_for_boot|fn build_and_boot' src/vmm/src/builder.rs | The functions that assemble and start a microVM. |
| Device placement/dispatch | rg -n 'MMIODeviceManager|PortIODeviceManager|ACPIDeviceManager' src/vmm/src/device_manager/ | The device managers that own the bus. |
| virtio devices | ls src/vmm/src/devices/virtio/ | block/, net/, vsock/, balloon/, rng/, … |
| vCPU state & run loop | rg -n 'fn run|KVM_RUN|VcpuExit' src/vmm/src/vstate/vcpu/ | The vCPU thread's KVM_RUN loop. |
| Guest memory | rg -n 'GuestMemoryMmap|fn create_guest_memory' src/vmm/src/vstate/ | Guest RAM, registered with KVM. |
| Snapshot/restore | rg -n 'trait Persist|fn save_state|fn restore' src/vmm/src/persist.rs src/vmm/src/snapshot/ | Per-device Persist, snapshot file layout. |
| Config types (your Level-2 turf) | ls src/vmm/src/vmm_config/ | boot_source, drive, machine_config, net, … and their validate(). |
| seccomp wiring | rg -n 'mod seccomp|fn apply_filter' src/vmm/src/ | Where compiled BPF filters are installed. |
Note: The HTTP API server is in the
firecrackerbinary, not invmm. This trips up everyone. Prove it to yourself:ls src/firecracker/src/api_server/ rg -n 'struct ApiServer|fn run_with_api|fn run_without_api' src/firecracker/src/The API thread parses a request into a
ParsedRequest, turns it into aVmmAction, and sends it over a channel to the VMM thread. The boundary betweenfirecrackerandvmmis that channel. You trace it in Lab 3.1.
Step 4: Tour the Non-src Tree
The repo is more than code. Know where the rest lives:
ls tests/ # the pytest integration suite — NOT cargo test
ls resources/ # seccomp JSON, guest kernel configs
ls docs/ # getting-started, jailer, seccomp, snapshotting, prod-host-setup
ls tools/ # devtool and friends
| Path | Contains | Why you care |
|---|---|---|
tests/ | Python integration tests (pytest), the real suite. | New functionality needs a test here (Lab 2.3). |
resources/seccomp/ | The per-arch seccomp filter JSON. | Level 9 / security work. |
resources/guest_configs/ | Guest kernel .config files. | Boot/kernel work (Level 6). |
docs/ | User and operator docs. | Doc-fix good-first-issues live here. |
tools/devtool | The Docker-wrapped build/test/lint harness. | Every gate you run. |
CHANGELOG.md, CONTRIBUTING.md, SPECIFICATION.md | Process + spec. | Required reading for Level 2. |
# Confirm the docs you'll cite in later labs exist.
ls docs/getting-started.md docs/jailer.md docs/seccomp.md docs/snapshotting* docs/prod-host-setup.md 2>/dev/null
Step 5: Read the Crate Dependency Graph
The workspace members depend on each other and on external rust-vmm crates. See it with cargo tree:
# How the firecracker binary's dependencies fan out (depth-limited for sanity).
cargo tree -p firecracker --depth 2
# What vmm pulls in directly — note the rust-vmm crates.
cargo tree -p vmm --depth 1
# Find every place the workspace consumes a specific external crate.
cargo tree -i kvm-ioctls # reverse: who depends on kvm-ioctls?
cargo tree -i vmm-sys-util # confirm it's an EXTERNAL dep now, not a member
Confirm three structural facts and write them in your map:
firecracker→vmm: the binary depends on the library; not the other way around.vmm→ rust-vmm:kvm-ioctls,kvm-bindings,vm-memory,linux-loader,vm-superio,event-manager,vmm-sys-util,vm-fdtcome from outside the workspace (verify withrg -n 'kvm-ioctls|vm-memory|vmm-sys-util' Cargo.toml src/vmm/Cargo.toml).vmm-sys-utilis not vendored. It used to be a directory in the repo; now it is a crates.io dependency.cargo tree -i vmm-sys-utilproves it resolves to an external version.
flowchart LR
fc["firecracker (bin)"] --> vmm["vmm (lib)"]
jailer["jailer (bin)"]
vmm --> kvm["kvm-ioctls / kvm-bindings"]
vmm --> mem["vm-memory"]
vmm --> loader["linux-loader"]
vmm --> superio["vm-superio"]
vmm --> em["event-manager"]
vmm --> sysutil["vmm-sys-util"]
vmm --> acpi["acpi-tables (member)"]
vmm --> utils["utils (member)"]
seccompiler["seccompiler (member)"]
Step 6: Produce the Module Map (the deliverable)
Write a one-page MODULE_MAP.md for yourself (do not PR it). For each subsystem give: the role,
the locating command, and one sentence on what's there. Minimum entries:
- vmm crate root rg -n 'struct Vmm' src/vmm/src/lib.rs
- VmmAction enum rg -n 'enum VmmAction' src/vmm/src/rpc_interface.rs
- VmResources rg -n 'struct VmResources' src/vmm/src/resources.rs
- builder entry points rg -n 'fn build_microvm_for_boot' src/vmm/src/builder.rs
- device managers ls src/vmm/src/device_manager/
- virtio devices ls src/vmm/src/devices/virtio/
- vCPU run loop rg -n 'KVM_RUN|VcpuExit' src/vmm/src/vstate/vcpu/
- guest memory rg -n 'GuestMemoryMmap' src/vmm/src/vstate/
- config + validate() ls src/vmm/src/vmm_config/ ; rg -n 'fn validate' src/vmm/src/vmm_config/
- API server (in firecracker!) ls src/firecracker/src/api_server/
- pytest suite ls tests/
- seccomp filters ls resources/seccomp/
Implementation Requirements
-
You can name every
src/workspace member and its role from your ownrg/ls, not this page. -
A
MODULE_MAP.md(local, not committed) with a locating command for each subsystem above. -
You can locate any of
Vmm,VmmAction,VmResources, the builder entry points, the device managers, the vCPU run loop, and thevmm_configvalidate()functions with a single command. -
You can state, with
cargo treeevidence, thatfirecracker → vmm, that the API server is infirecracker, and thatvmm-sys-utilis an external dependency.
Troubleshooting
cargo tree fails: "no such command" or a toolchain error
Run it inside the dev container, which has the pinned toolchain:
tools/devtool shell --privileged
# then, inside:
cargo tree -p firecracker --depth 2
rg finds a struct in many files
That is normal — narrow by directory (the "Find it with" column already scopes each search). If a
definition is what you want, add the keyword: rg -n 'struct Vmm\b' or rg -n 'enum VmmAction\b'.
A path in this lab doesn't exist on your branch
Expected, and the lesson. The repo refactors. Use rg/fd to find where the role moved:
fd -e rs builder # find a file by name fragment
rg -n 'build_microvm' # find the function by role, wherever it now lives
tools/devtool shell complains about Docker
Docker must be running and your user able to talk to it (docker ps). This is the same prerequisite
as Lab 1.1.
Expected Output
A correct cargo tree -i vmm-sys-util shows it resolving from crates.io (an external version),
proving it is no longer a vendored workspace member:
vmm-sys-util v0.x.y
└── vmm v...
└── firecracker v...
A MODULE_MAP.md whose every line is a command you can paste and that lands on the right code on
your checkout. If a line cites a fixed line number, you did it wrong — rewrite it as an rg by role.
Stretch Goals
- Diff the crate list against history.
git log --oneline -- Cargo.toml | headand inspect a commit that changedmembers. Find the refactor that merged crates intovmm; read its message. - Reverse-map a rust-vmm crate. Pick
vm-memory;cargo tree -i vm-memoryand thenrg -n 'use vm_memory' src/vmm/src/ | headto see howvmmactually uses it. - Count the device model.
find src/vmm/src/devices -name '*.rs' | wc -land list the virtio device directories. How many device types does Firecracker implement? Cross-check against the minimal-device-model deep dive. - Locate the seccomp filters.
ls resources/seccomp/andrg -n 'vcpu|api|vmm' resources/seccomp/*.json | head— note the three per-thread filter categories you will study in Level 9.
Validation / Self-check
You are done when you can answer these without notes:
- Which workspace member holds the HTTP API server —
vmmorfirecracker? Prove it with one command. - Where does all device emulation live, and what is the path to the virtio devices?
- Give the
rgcommand that locates theVmmActionenum and the one that locates the builder's boot entry point. - Is
vmm-sys-utila workspace member or an external dependency today? What command answers this? - Where are integration tests — and why is it not
cargo testthat runs the real suite? - Name three modules now inside
src/vmm/src/that were historically separate crates. - Using only
rg, find thevalidate()functions invmm_config— where would an error-message good-first-issue most likely land?