Skip to content
Closed
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
22 changes: 21 additions & 1 deletion sev_verify/guest_vsock.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import re
import shlex
import socket
import subprocess
import time
from pathlib import Path
from typing import TYPE_CHECKING, Any
Expand Down Expand Up @@ -142,9 +143,15 @@ def wait_for_guest(
*,
timeout: float | None = None,
poll_interval: float = 2.0,
process: subprocess.Popen | None = None,
) -> None:
"""
Block until the guest vsock agent responds or timeout is reached.

Pass ``process`` (the QEMU handle) to stop waiting the moment QEMU exits.
Without it, a guest that never starts — or one the firmware refuses to
launch — costs the full boot timeout and is then reported as an
indistinguishable "agent not ready", discarding the reason QEMU gave.
"""
boot_timeout = timeout if timeout is not None else profile.vsock_boot_timeout
deadline = time.monotonic() + boot_timeout
Expand All @@ -156,6 +163,11 @@ def wait_for_guest(
return
except GuestVsockError as exc:
last_error = str(exc)
if process is not None and process.poll() is not None:
raise GuestVsockError(
f"QEMU exited with code {process.returncode} before the "
f"guest became ready"
) from exc
time.sleep(poll_interval)

raise GuestVsockError(
Expand All @@ -169,12 +181,20 @@ def check_guest_ready(
*,
timeout: float | None = None,
poll_interval: float = 2.0,
process: subprocess.Popen | None = None,
) -> tuple[bool, str]:
"""
Return whether the guest vsock agent responds.

``process`` is forwarded to :func:`wait_for_guest`; see its docstring.
"""
try:
wait_for_guest(profile, timeout=timeout, poll_interval=poll_interval)
wait_for_guest(
profile,
timeout=timeout,
poll_interval=poll_interval,
process=process,
)
except GuestVsockError as exc:
return False, str(exc)

Expand Down
12 changes: 11 additions & 1 deletion sev_verify/vm_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,20 @@ def vm_launch(
# Import here to avoid circular import with guest_vsock.
from .guest_vsock import check_guest_ready

booted, boot_error = check_guest_ready(self)
# Passing the process lets the wait abort as soon as QEMU dies,
# instead of polling vsock until the boot timeout expires against
# a VM that no longer exists.
booted, boot_error = check_guest_ready(self, process=process)
if not booted:
ok = False
message = f"Guest did not boot: {boot_error}"
if process.poll() is not None:
# QEMU's own diagnosis is far more useful than "agent not
# ready" — for a rejected launch it names the firmware
# error, e.g. "SNP_LAUNCH_FINISH ... 'Bad measurement'".
stderr_tail = _read_guest_errors(self.guest_error_log).strip()
if stderr_tail:
message = f"{message}\nQEMU stderr:\n{stderr_tail}"
elif message == "VM launch verified":
message = "VM launched and guest booted"

Expand Down
Loading