Skip to content

vmstate parser fails on Azure nested virtualization (Firecracker v1.12.0, AMD EPYC) #4

@cchen7

Description

@cchen7

Environment

  • Host: Azure Standard_D8ads_v6 (AMD EPYC 9V74, 8 vCPU)
  • Host OS: Ubuntu 24.04.4, Kernel 6.17.0-1008-azure
  • Nested virtualization: KVM-in-Hyper-V (Azure)
  • Firecracker: v1.12.0
  • ZeroBoot: commit 99d86c8

Problem

zeroboot test-exec and zeroboot bench fail with:

Error: cannot detect vmstate layout: IOAPIC base address 0xFEC00000 not found

Template creation (zeroboot template) succeeds normally — the issue is in vmstate.rs::detect_offset_shift() during snapshot restore.

Root Cause

The detect_offset_shift() function assumes a single global shift between reference offsets and actual offsets in the vmstate file. In our environment, the vmstate has two different shifts:

  • IOAPIC region: shift = +4 (from reference 0x0591 to actual 0x058d)
  • CPU registers (LAPIC, EFER, XSAVE, etc.): shift = -764

This happens because Firecracker's versionize format has variable-length sections between IOAPIC and the CPU state block. The current code finds IOAPIC at the correct offset but then validates by checking EFER at REF_EFER - shift, which points to the wrong location (offset 0x2AF1 instead of actual 0x2DF1).

Offset analysis

Field       Reference    Actual     Shift
IOAPIC      0x0591       0x058d     +4
LAPIC       0x2541       0x283d     -764
REGS        0x2955       0x2c51     -764
EFER        0x2AF5       0x2df1     -764
XCRS        0x2B75       0x2e71     -764
XSAVE       0x2D0D       0x3009     -764

Workaround

We patched vmstate.rs with a dual-anchor approach:

  1. detect_offset_shift() — accepts IOAPIC shift even when EFER validation fails
  2. New detect_cpu_shift() — independently locates EFER by searching for value 0xD01/0x501 and validating CR0 at a known relative offset (-40 bytes, with PG+PE bits set)
  3. parse_vmstate() — uses ioapic_shift for IOAPIC redirect table and cpu_shift for all CPU register fields

After patching, fork time is 0.39ms p50 (1000 iterations) on Azure nested virtualization.

Suggested Fix

The detect_offset_shift() function should either:

  1. Use multiple anchor points (IOAPIC + EFER independently) instead of assuming a single global shift, or
  2. Search for each field type independently using known value patterns (IOAPIC base 0xFEC00000, EFER 0xD01/0x501, LAPIC version 0x50014, XSAVE FCW 0x037F)

Happy to submit a PR if you'd prefer.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions