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-util is not vendored anymore, all device emulation is in vmm. 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 ideally fd and tokei installed (all in the dev container).
  • Cargo available (inside tools/devtool shell if 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):

CrateRole
vmmThe core VMM library: machine model, vCPU/KVM state, all device emulation, most subsystems. The big merged crate — you live here.
firecrackerThe firecracker binary + the HTTP API server (src/firecracker/src/api_server/). Drives vmm.
jailerThe jailer binary: the isolation barrier, then execs firecracker.
seccompilerCompiles JSON seccomp filters → BPF. (rust-vmm's external seccompiler originated here.)
cpu-template-helperCreate/inspect/verify CPU templates.
snapshot-editorInspect/edit snapshot files.
rebase-snapRebase a diff-snapshot's memory onto a base.
acpi-tablesBuild guest ACPI tables.
utilsInternal shared utilities.
clippy-tracing, log-instrument, log-instrument-macrosDev/CI tooling, not shipped in the VMM.

Warning: Do not trust this table over your checkout. If rg 'members' Cargo.toml shows a member not listed here, or omits one, the repo moved — your rg output 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"):

SubsystemFind it withRole
The running microVM objectrg -n 'struct Vmm' src/vmm/src/Vmm — owned by the VMM thread; holds devices, vCPUs, memory.
Pre-boot configurationrg -n 'struct VmResources' src/vmm/src/resources.rsVmResources — aggregated config before InstanceStart.
The control-plane command enumrg -n 'enum VmmAction' src/vmm/src/rpc_interface.rsVmmAction — what the API thread sends the VMM thread.
Build/boot entry pointsrg -n 'fn build_microvm_for_boot|fn build_and_boot' src/vmm/src/builder.rsThe functions that assemble and start a microVM.
Device placement/dispatchrg -n 'MMIODeviceManager|PortIODeviceManager|ACPIDeviceManager' src/vmm/src/device_manager/The device managers that own the bus.
virtio devicesls src/vmm/src/devices/virtio/block/, net/, vsock/, balloon/, rng/, …
vCPU state & run looprg -n 'fn run|KVM_RUN|VcpuExit' src/vmm/src/vstate/vcpu/The vCPU thread's KVM_RUN loop.
Guest memoryrg -n 'GuestMemoryMmap|fn create_guest_memory' src/vmm/src/vstate/Guest RAM, registered with KVM.
Snapshot/restorerg -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 wiringrg -n 'mod seccomp|fn apply_filter' src/vmm/src/Where compiled BPF filters are installed.

Note: The HTTP API server is in the firecracker binary, not in vmm. 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 a VmmAction, and sends it over a channel to the VMM thread. The boundary between firecracker and vmm is 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
PathContainsWhy 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/devtoolThe Docker-wrapped build/test/lint harness.Every gate you run.
CHANGELOG.md, CONTRIBUTING.md, SPECIFICATION.mdProcess + 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:

  1. firecracker → vmm: the binary depends on the library; not the other way around.
  2. vmm → rust-vmm: kvm-ioctls, kvm-bindings, vm-memory, linux-loader, vm-superio, event-manager, vmm-sys-util, vm-fdt come from outside the workspace (verify with rg -n 'kvm-ioctls|vm-memory|vmm-sys-util' Cargo.toml src/vmm/Cargo.toml).
  3. vmm-sys-util is not vendored. It used to be a directory in the repo; now it is a crates.io dependency. cargo tree -i vmm-sys-util proves 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 own rg/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 the vmm_config validate() functions with a single command.
  • You can state, with cargo tree evidence, that firecracker → vmm, that the API server is in firecracker, and that vmm-sys-util is 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

  1. Diff the crate list against history. git log --oneline -- Cargo.toml | head and inspect a commit that changed members. Find the refactor that merged crates into vmm; read its message.
  2. Reverse-map a rust-vmm crate. Pick vm-memory; cargo tree -i vm-memory and then rg -n 'use vm_memory' src/vmm/src/ | head to see how vmm actually uses it.
  3. Count the device model. find src/vmm/src/devices -name '*.rs' | wc -l and list the virtio device directories. How many device types does Firecracker implement? Cross-check against the minimal-device-model deep dive.
  4. Locate the seccomp filters. ls resources/seccomp/ and rg -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:

  1. Which workspace member holds the HTTP API server — vmm or firecracker? Prove it with one command.
  2. Where does all device emulation live, and what is the path to the virtio devices?
  3. Give the rg command that locates the VmmAction enum and the one that locates the builder's boot entry point.
  4. Is vmm-sys-util a workspace member or an external dependency today? What command answers this?
  5. Where are integration tests — and why is it not cargo test that runs the real suite?
  6. Name three modules now inside src/vmm/src/ that were historically separate crates.
  7. Using only rg, find the validate() functions in vmm_config — where would an error-message good-first-issue most likely land?

Next: Lab 2.2 — Prepare a PR Using Firecracker Practices.