Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 43 additions & 4 deletions misc/AMDSEV/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ If `--katana` is not provided, `build.sh` prompts for confirmation (`y/N`) befor
| `build-kernel.sh` | Downloads and extracts Ubuntu kernel (`vmlinuz`) |
| `build-initrd.sh` | Creates minimal initrd with busybox, SEV-SNP modules, and katana |
| `build-config` | Pinned versions and checksums for reproducible builds |
| `start-vm.sh` | Starts a TEE VM with SEV-SNP enabled using QEMU |
| `start-vm.sh` | Starts a TEE VM with SEV-SNP and launches Katana asynchronously |

## SNP Tools

Expand Down Expand Up @@ -69,6 +69,9 @@ cargo build -p snp-tools

## Running

The QEMU command below boots the VM but does not automatically start Katana.
Katana must be started asynchronously via the control channel.

```sh
qemu-system-x86_64 \
# Use KVM hardware virtualization (required for SEV-SNP)
Expand All @@ -93,11 +96,42 @@ qemu-system-x86_64 \
# Initial ramdisk containing katana (measured when kernel-hashes=on)
-initrd output/qemu/initrd.img \
# Kernel command line (measured when kernel-hashes=on)
# katana.args passes arguments to katana via init script
-append "console=ttyS0 katana.args=--http.addr,0.0.0.0,--http.port,5050,--tee.provider,sev-snp" \
-append "console=ttyS0" \
# Katana control channel (used to start Katana asynchronously after boot)
-device virtio-serial-pci,id=virtio-serial0 \
-chardev socket,id=katanactl,path=/tmp/katana-control.sock,server=on,wait=off \
-device virtserialport,chardev=katanactl,name=org.katana.control.0 \
..
```

### Start Katana via Control Channel

In the QEMU example above, this line defines the host-side control channel endpoint:

```sh
-chardev socket,id=katanactl,path=/tmp/katana-control.sock,server=on,wait=off
```

The `path=/tmp/katana-control.sock` value is the Unix socket file on the host.
That socket is connected to the guest virtio-serial port:

```sh
-device virtserialport,chardev=katanactl,name=org.katana.control.0
```

So writes to that Unix socket become control commands inside the VM (`start`, `status`).

Example:

```sh
# Start Katana with comma-separated CLI args
printf 'start --http.addr,0.0.0.0,--http.port,5050,--tee.provider,sev-snp\n' \
| socat - UNIX-CONNECT:/tmp/katana-control.sock

# Check launcher status
printf 'status\n' | socat - UNIX-CONNECT:/tmp/katana-control.sock
```

## Running the VM

The `start-vm.sh` script provides an easy way to launch a TEE VM with SEV-SNP enabled:
Expand All @@ -108,11 +142,16 @@ sudo ./misc/AMDSEV/start-vm.sh

# Or specify a custom boot components directory
sudo ./misc/AMDSEV/start-vm.sh /path/to/boot-components

# Or customize Katana runtime flags (comma-separated)
sudo ./misc/AMDSEV/start-vm.sh --katana-args "--http.addr,0.0.0.0,--http.port,5050,--tee.provider,sev-snp,--dev"
```

The script:
- Starts QEMU with SEV-SNP confidential computing enabled
- Uses direct kernel boot with kernel-hashes=on for attestation
- Keeps kernel cmdline stable (`console=ttyS0`) for deterministic measurement
- Starts Katana asynchronously via virtio-serial control channel
- Forwards RPC port 5050 to host port 15051
- Outputs serial log to a temp file and follows it

Expand All @@ -129,7 +168,7 @@ cargo build -p snp-tools
--ovmf output/qemu/OVMF.fd \
--kernel output/qemu/vmlinuz \
--initrd output/qemu/initrd.img \
--append "console=ttyS0 katana.args=--http.addr,0.0.0.0,--http.port,5050,--tee.provider,sev-snp" \
--append "console=ttyS0" \
--vcpus 1 \
--cpu epyc-v4 \
--vmm qemu \
Expand Down
132 changes: 109 additions & 23 deletions misc/AMDSEV/build-initrd.sh
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,9 @@ log() { echo "[init] $*"; }
KATANA_PID=""
KATANA_DB_DIR="/mnt/data/katana-db"
SHUTTING_DOWN=0
KATANA_EXIT_CODE="never"
CONTROL_PORT_NAME="org.katana.control.0"
CONTROL_PORT_LINK="/dev/virtio-ports/org.katana.control.0"

fatal_boot() {
log "ERROR: $*"
Expand All @@ -303,6 +306,90 @@ fatal_boot() {
done
}

refresh_katana_state() {
if [ -n "$KATANA_PID" ] && ! kill -0 "$KATANA_PID" 2>/dev/null; then
if wait "$KATANA_PID"; then
KATANA_EXIT_CODE=0
else
KATANA_EXIT_CODE=$?
fi
log "Katana exited with code $KATANA_EXIT_CODE"
KATANA_PID=""
fi
}

respond_control() {
printf '%s\n' "$1" >&3 2>/dev/null || true
}

resolve_control_port() {
mkdir -p /dev/virtio-ports
for name_file in /sys/class/virtio-ports/*/name; do
[ -f "$name_file" ] || continue

PORT_NAME_VALUE="$(cat "$name_file" 2>/dev/null || true)"
if [ "$PORT_NAME_VALUE" != "$CONTROL_PORT_NAME" ]; then
continue
fi

PORT_DIR="${name_file%/name}"
PORT_DEV="/dev/${PORT_DIR##*/}"
if [ -e "$PORT_DEV" ]; then
ln -sf "$PORT_DEV" "$CONTROL_PORT_LINK"
echo "$CONTROL_PORT_LINK"
return 0
fi
done
return 1
}

handle_control_command() {
RAW_CMD="$1"
CMD="${RAW_CMD%% *}"
CMD_PAYLOAD=""
if [ "$CMD" != "$RAW_CMD" ]; then
CMD_PAYLOAD="${RAW_CMD#* }"
fi

case "$CMD" in
start)
refresh_katana_state
if [ -n "$KATANA_PID" ] && kill -0 "$KATANA_PID" 2>/dev/null; then
respond_control "err already-running pid=$KATANA_PID"
return 0
fi

KATANA_ARGS=""
if [ -n "$CMD_PAYLOAD" ]; then
KATANA_ARGS="$(echo "$CMD_PAYLOAD" | tr ',' ' ')"
fi

log "Starting katana asynchronously..."
# shellcheck disable=SC2086
/bin/katana --db-dir="$KATANA_DB_DIR" $KATANA_ARGS &
KATANA_PID=$!
KATANA_EXIT_CODE="running"
respond_control "ok started pid=$KATANA_PID"
;;

status)
refresh_katana_state
if [ -n "$KATANA_PID" ] && kill -0 "$KATANA_PID" 2>/dev/null; then
respond_control "running pid=$KATANA_PID"
else
respond_control "stopped exit=$KATANA_EXIT_CODE"
fi
;;

"")
;;

*)
respond_control "err unknown-command"
;;
esac
}

shutdown_handler() {
if [ "$SHUTTING_DOWN" -eq 1 ]; then
return 0
Expand Down Expand Up @@ -404,15 +491,6 @@ else
log "WARNING: eth0 interface not found; skipping static network setup"
fi

# Parse katana args from cmdline
CMDLINE="$(cat /proc/cmdline 2>/dev/null || true)"
KATANA_ARGS=""
for tok in $CMDLINE; do
case "$tok" in
katana.args=*) KATANA_ARGS="$(echo "${tok#katana.args=}" | tr ',' ' ')" ;;
esac
done

# Require persistent storage at /dev/sda
if [ ! -b /dev/sda ]; then
fatal_boot "required storage device /dev/sda not found"
Expand All @@ -426,22 +504,30 @@ fi
mkdir -p "$KATANA_DB_DIR"
log "Storage mounted at /mnt/data"

log "Starting katana..."
# shellcheck disable=SC2086
/bin/katana --db-dir="$KATANA_DB_DIR" $KATANA_ARGS &
KATANA_PID=$!
log "Katana started with PID $KATANA_PID"

if wait "$KATANA_PID"; then
EXIT_CODE=0
else
EXIT_CODE=$?
fi
log "Katana exited with code $EXIT_CODE"
# Start async control loop for Katana startup/status commands.
log "Waiting for control channel ($CONTROL_PORT_NAME)..."
CONTROL_PORT=""
while [ -z "$CONTROL_PORT" ]; do
CONTROL_PORT="$(resolve_control_port || true)"
[ -n "$CONTROL_PORT" ] || sleep 1
done
log "Control channel ready: $CONTROL_PORT"

# PID 1 must stay alive unless explicitly powered off.
while true; do
sleep 60
refresh_katana_state

if ! exec 3<>"$CONTROL_PORT"; then
log "WARNING: failed to open control channel, retrying..."
sleep 1
continue
fi

while IFS= read -r CONTROL_CMD <&3; do
handle_control_command "$CONTROL_CMD"
done

exec 3>&- 3<&-
sleep 1
done
INIT_EOF

Expand Down
Loading