Skip to content

Commit d6255a2

Browse files
committed
feat(dstack-mr): generate QEMU ACPI tables in Rust
1 parent 5d7d966 commit d6255a2

52 files changed

Lines changed: 1643 additions & 1116 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/docker-build-check.yml

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,6 @@ jobs:
8888
DSTACK_REV=${{ env.DSTACK_REV }}
8989
DSTACK_SRC_URL=${{ env.DSTACK_SRC_URL }}
9090
91-
- name: Verify pinned packages (qemu stage)
92-
run: |
93-
dstack/build/shared/verify-pinned-packages.sh dstack-kms-check:latest \
94-
dstack/kms/dstack-app/builder/shared/qemu-pinned-packages.txt
95-
9691
- name: Build kms-builder target
9792
run: |
9893
docker buildx build \
@@ -163,21 +158,3 @@ jobs:
163158
run: |
164159
dstack/build/shared/verify-pinned-packages.sh verifier-builder-check:latest \
165160
dstack/verifier/builder/shared/builder-pinned-packages.txt
166-
167-
- name: Build acpi-builder target
168-
run: |
169-
docker buildx build \
170-
--load \
171-
--target acpi-builder \
172-
--tag verifier-acpi-check:latest \
173-
--provenance=false \
174-
--file dstack/verifier/builder/Dockerfile \
175-
--build-context build-shared=dstack/build/shared \
176-
--build-arg "DSTACK_REV=${DSTACK_REV}" \
177-
--build-arg "DSTACK_SRC_URL=${DSTACK_SRC_URL}" \
178-
dstack/verifier
179-
180-
- name: Verify qemu pinned packages
181-
run: |
182-
dstack/build/shared/verify-pinned-packages.sh verifier-acpi-check:latest \
183-
dstack/verifier/builder/shared/qemu-pinned-packages.txt

REUSE.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ path = [
4848
"sdk/go/go.sum",
4949
"sdk/go/ratls/go.sum",
5050
"dstack/kms/dstack-app/builder/shared/builder-pinned-packages.txt",
51-
"dstack/kms/dstack-app/builder/shared/qemu-pinned-packages.txt",
5251
"dstack/gateway/dstack-app/builder/shared/builder-pinned-packages.txt",
5352
"dstack/gateway/dstack-app/builder/shared/pinned-packages.txt",
5453
]
@@ -261,3 +260,8 @@ path = [
261260
]
262261
SPDX-FileCopyrightText = "NONE"
263262
SPDX-License-Identifier = "CC0-1.0"
263+
264+
[[annotations]]
265+
path = "dstack/crates/qemu-acpi/fixtures/*.bin"
266+
SPDX-FileCopyrightText = "NONE"
267+
SPDX-License-Identifier = "CC0-1.0"

docs/tutorials/attestation-verification.md

Lines changed: 2 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -246,46 +246,9 @@ To verify attestation, you need to independently calculate what the measurements
246246
cat /var/lib/dstack/images/dstack-0.5.7/metadata.json | jq .
247247
```
248248

249-
### Build dstack-acpi-tables (required dependency)
249+
### ACPI generation
250250

251-
`dstack-mr` internally runs a tool called `dstack-acpi-tables` to generate ACPI tables for RTMR0 calculation. This is a custom-patched QEMU binary compiled with `-DDUMP_ACPI_TABLES`. You need to build it once:
252-
253-
```bash
254-
# Install QEMU build dependencies
255-
sudo apt-get update
256-
sudo apt-get install -y git libslirp-dev python3-pip ninja-build \
257-
pkg-config libglib2.0-dev build-essential flex bison
258-
259-
# Clone the custom QEMU fork
260-
cd ~/dstack
261-
git clone https://github.com/kvinwang/qemu-tdx.git --depth 1 \
262-
--branch dstack-qemu-9.2.1 --single-branch
263-
264-
# Configure with ACPI table dumping enabled
265-
cd qemu-tdx
266-
export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)
267-
export CFLAGS="-DDUMP_ACPI_TABLES -Wno-builtin-macro-redefined -D__DATE__=\"\" -D__TIME__=\"\" -D__TIMESTAMP__=\"\""
268-
export LDFLAGS="-Wl,--build-id=none"
269-
mkdir build && cd build
270-
../configure --target-list=x86_64-softmmu --disable-werror
271-
272-
# Build (this takes several minutes)
273-
ninja
274-
275-
# Install the binary
276-
strip qemu-system-x86_64
277-
sudo install -m 755 qemu-system-x86_64 /usr/local/bin/dstack-acpi-tables
278-
279-
# Install required QEMU data files
280-
sudo install -d /usr/local/share/qemu
281-
sudo install -m 644 ../pc-bios/efi-virtio.rom /usr/local/share/qemu/
282-
sudo install -m 644 ../pc-bios/kvmvapic.bin /usr/local/share/qemu/
283-
sudo install -m 644 ../pc-bios/linuxboot_dma.bin /usr/local/share/qemu/
284-
285-
# Clean up source (optional)
286-
cd ~/dstack
287-
rm -rf qemu-tdx
288-
```
251+
`dstack-mr` generates QEMU-compatible ACPI measurement data in process. No custom QEMU binary or runtime helper is required.
289252

290253
### Build the measurement calculator
291254

docs/tutorials/kms-build-configuration.md

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -371,16 +371,11 @@ The Dockerfile bakes all configuration into the image for reliable CVM deploymen
371371
```bash
372372
cat > Dockerfile << 'EOF'
373373
# KMS Docker Image for CVM Deployment
374-
# Extract dstack-acpi-tables and QEMU BIOS files from the official builder image.
375-
# These are required for OS image verification (computing expected TDX measurements).
376-
FROM dstacktee/dstack-kms@sha256:11ac59f524a22462ccd2152219b0bec48a28ceb734e32500152d4abefab7a62a AS official
377-
378374
FROM ubuntu:24.04
379375
380376
# Install runtime dependencies
381-
# libglib2.0-0t64, libpixman-1-0, and libslirp0 are required by dstack-acpi-tables (QEMU binary)
382377
RUN apt-get update && \
383-
apt-get install -y ca-certificates curl libglib2.0-0t64 libpixman-1-0 libslirp0 && \
378+
apt-get install -y ca-certificates curl && \
384379
rm -rf /var/lib/apt/lists/*
385380
386381
# Install Node.js 20.x for auth-eth
@@ -391,10 +386,6 @@ RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
391386
# Create directories
392387
RUN mkdir -p /etc/kms/certs /etc/kms/images /var/run/kms /var/log/kms
393388
394-
# Copy dstack-acpi-tables from official image (needed for OS image verification)
395-
COPY --from=official /usr/local/bin/dstack-acpi-tables /usr/local/bin/dstack-acpi-tables
396-
COPY --from=official /usr/local/share/qemu /usr/local/share/qemu
397-
398389
# Copy KMS binary
399390
COPY dstack-kms /usr/local/bin/dstack-kms
400391
RUN chmod 755 /usr/local/bin/dstack-kms

dstack/Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dstack/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ members = [
7070
"crates/api-auth",
7171
"crates/build-info",
7272
"crates/mock-attestation",
73+
"crates/qemu-acpi",
7374
]
7475
# Vendored third-party crates are path dependencies but deliberately not members:
7576
# `--all-features` applies to members, and ktls declares its `ring` and
@@ -90,6 +91,7 @@ dstack-cli-core = { path = "crates/dstack-cli-core" }
9091
dstack-volume = { path = "crates/dstack-volume" }
9192
dstack-api-auth = { path = "crates/api-auth" }
9293
dstack-build-info = { path = "crates/build-info" }
94+
qemu-acpi = { path = "crates/qemu-acpi" }
9395
cc-eventlog = { path = "cc-eventlog" }
9496
supervisor = { path = "supervisor" }
9597
supervisor-client = { path = "supervisor/client" }

dstack/build/shared/config-qemu.sh

Lines changed: 0 additions & 28 deletions
This file was deleted.

dstack/crates/qemu-acpi/Cargo.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# SPDX-FileCopyrightText: © 2026 Phala Network <dstack@phala.network>
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
[package]
5+
name = "qemu-acpi"
6+
version = "0.1.0"
7+
edition = "2021"
8+
license = "Apache-2.0"
9+
description = "Pure Rust generator for QEMU-compatible ACPI measurement blobs"
10+
11+
[dependencies]
12+
thiserror.workspace = true
13+
14+
[dev-dependencies]
15+
hex = { workspace = true, features = ["alloc"] }
16+
sha2.workspace = true
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// SPDX-FileCopyrightText: © 2026 Phala Network <dstack@phala.network>
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use std::error::Error;
5+
use std::str::FromStr;
6+
7+
use qemu_acpi::{build, MachineConfig, QemuVersion};
8+
9+
fn optional<T: FromStr>(args: &mut impl Iterator<Item = String>, default: T) -> Result<T, String> {
10+
match args.next() {
11+
Some(value) => value.parse().map_err(|_| format!("invalid value: {value}")),
12+
None => Ok(default),
13+
}
14+
}
15+
16+
fn main() -> Result<(), Box<dyn Error>> {
17+
let mut args = std::env::args().skip(1);
18+
let nics: u32 = optional(&mut args, 1)?;
19+
let cpus: u32 = optional(&mut args, 1)?;
20+
let version = QemuVersion::from_str(&optional::<String>(&mut args, "11.1.0".into())?)
21+
.map_err(|error| format!("invalid QEMU version: {error}"))?;
22+
let gpus = optional(&mut args, 0)?;
23+
let nvswitches = optional(&mut args, 0)?;
24+
let hugepages = optional::<u8>(&mut args, 0)? == 1;
25+
let root_verity = optional::<u8>(&mut args, 1)? != 0;
26+
let hotplug_off = optional::<u8>(&mut args, 0)? == 1;
27+
let smm = optional::<u8>(&mut args, 0)? == 1;
28+
let hole = optional::<u64>(&mut args, 0)?;
29+
let memory_size = optional(&mut args, 2u64 << 30)?;
30+
let blobs = build(&MachineConfig {
31+
qemu_version: version,
32+
cpu_count: cpus,
33+
memory_size,
34+
pic: false,
35+
smm,
36+
hugepages,
37+
num_gpus: gpus,
38+
num_nvswitches: nvswitches,
39+
num_nics: nics,
40+
num_verity_volumes: 0,
41+
hotplug_off,
42+
root_verity,
43+
pci_hole64_size: (hole != 0).then_some(hole),
44+
})?;
45+
std::fs::write("/tmp/rust.bin", blobs.tables)?;
46+
Ok(())
47+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# QEMU ACPI compatibility fixtures
2+
3+
These binary fixtures are machine-generated `etc/acpi/tables` blobs produced by
4+
the dstack QEMU ACPI compatibility build. They capture QEMU's externally
5+
observable ACPI ABI; they do not contain QEMU source code.
6+
7+
The trimmed `*-base.bin` files contain only the used ACPI table bytes. The
8+
runtime generator pads the result using QEMU's 128 KiB allocation granularity.
9+
`qemu-11.1-q35-one-nic.bin` is an untrimmed byte-for-byte regression fixture.
10+
11+
Fixture names identify the QEMU compatibility version and Q35 topology used to
12+
generate them. Regenerate and compare fixtures with a QEMU build containing the
13+
dstack `DUMP_ACPI_TABLES` compatibility patch; never regenerate them from the
14+
Rust implementation itself.

0 commit comments

Comments
 (0)