Load the Debian 13 VM hardware from QEMU config files
Move the Debian launcher's QEMU arguments into qemu/debian13.ini, which the launcher renders with the per-run file paths and passes to QEMU with -readconfig, followed by qemu/debian13-<arch>.ini for the machine type and arm64-only devices. Memory, CPU count, SSH port and accelerator are now set in the config files, replacing VM_MEMORY, VM_CPUS and VM_SSH_PORT; VM_QEMU_CONFIG selects a different shared config file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YKxXLyiL7XJ3a9e8HmR2my
This commit is contained in:
1 parent
280abbd2de
commit
fe8b6f93ff
5 files changed
+141
-47
No files matched your search
@@ -22,8 +22,18 @@ background while still showing the graphical window:
|
||||
The launcher uses KVM when `/dev/kvm` is accessible, forwards host port `2222`
|
||||
to guest port `22`, and creates `.vm/debian13/disk.qcow2` as a 20G writable overlay with the
|
||||
original qcow2 image as its backing file. The base image therefore stays
|
||||
unchanged. Resource settings can be overridden with environment variables;
|
||||
run `./run-debian13.py --help` for the full list.
|
||||
unchanged. Run `./run-debian13.py --help` for the supported environment
|
||||
variables.
|
||||
|
||||
The Debian VM hardware (accelerator, 8G memory, 8 CPUs, drives, network) is
|
||||
defined in the QEMU config file `qemu/debian13.ini`; the machine type and
|
||||
architecture-specific devices are in `qemu/debian13-amd64.ini` and
|
||||
`qemu/debian13-arm64.ini`. Edit these files to change the settings. The
|
||||
launcher fills in the `${...}` file path placeholders of `qemu/debian13.ini`,
|
||||
writes the result to `.vm/debian13/qemu.ini` and starts QEMU with
|
||||
`-readconfig` for that file followed by the one for the guest architecture.
|
||||
QEMU arguments given after `--` override the config files, for example
|
||||
`./run-debian13.py -- -m 4G -smp 4`.
|
||||
|
||||
The launchers accept `--state-dir DIR` to keep
|
||||
the VM disk, seed ISO, UEFI variables, PID file and console log in `DIR`
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# amd64 part of the Debian 13 QEMU configuration; read after debian13.ini.
|
||||
|
||||
[machine]
|
||||
type = "q35"
|
||||
@@ -0,0 +1,17 @@
|
||||
# arm64 part of the Debian 13 QEMU configuration; read after debian13.ini.
|
||||
|
||||
[machine]
|
||||
type = "virt"
|
||||
|
||||
# Unlike q35, the "virt" machine has no default GPU or USB input devices.
|
||||
[device "gpu0"]
|
||||
driver = "virtio-gpu-pci"
|
||||
|
||||
[device "usb0"]
|
||||
driver = "qemu-xhci"
|
||||
|
||||
[device "kbd0"]
|
||||
driver = "usb-kbd"
|
||||
|
||||
[device "tablet0"]
|
||||
driver = "usb-tablet"
|
||||
@@ -0,0 +1,69 @@
|
||||
# QEMU configuration for the Debian 13 virtual machine, shared by all
|
||||
# architectures. The machine type and architecture-specific devices are in
|
||||
# debian13-<arch>.ini, which QEMU reads after this file.
|
||||
#
|
||||
# run-debian13.py fills in the file path placeholders below, writes the result
|
||||
# to <state dir>/qemu.ini and passes it to QEMU with -readconfig. Options that
|
||||
# have no -readconfig equivalent (-cpu, -display, daemon mode) are still added
|
||||
# by the launcher.
|
||||
|
||||
[name]
|
||||
guest = "debian13"
|
||||
|
||||
# Accelerators are tried in order: KVM on Linux, HVF on macOS, else emulation.
|
||||
[accel]
|
||||
accel = "kvm"
|
||||
|
||||
[accel]
|
||||
accel = "hvf"
|
||||
|
||||
[accel]
|
||||
accel = "tcg"
|
||||
|
||||
[memory]
|
||||
size = "8G"
|
||||
|
||||
[smp-opts]
|
||||
cpus = "8"
|
||||
|
||||
# UEFI firmware: read-only code and the per-VM variable store.
|
||||
[drive "pflash0"]
|
||||
if = "pflash"
|
||||
format = "raw"
|
||||
readonly = "on"
|
||||
file = "${uefi_code}"
|
||||
|
||||
[drive "pflash1"]
|
||||
if = "pflash"
|
||||
format = "raw"
|
||||
file = "${uefi_vars}"
|
||||
|
||||
# Writable qcow2 overlay on top of the shared base image.
|
||||
[drive "disk0"]
|
||||
if = "none"
|
||||
format = "qcow2"
|
||||
file = "${disk_image}"
|
||||
|
||||
[device "disk0-dev"]
|
||||
driver = "virtio-blk-pci"
|
||||
drive = "disk0"
|
||||
|
||||
# cloud-init seed ISO.
|
||||
[drive "seed0"]
|
||||
if = "none"
|
||||
format = "raw"
|
||||
readonly = "on"
|
||||
file = "${seed_image}"
|
||||
|
||||
[device "seed0-dev"]
|
||||
driver = "virtio-blk-pci"
|
||||
drive = "seed0"
|
||||
|
||||
# User-mode networking with guest SSH forwarded to the host.
|
||||
[netdev "net0"]
|
||||
type = "user"
|
||||
hostfwd = "tcp::2222-:22"
|
||||
|
||||
[device "nic0"]
|
||||
driver = "virtio-net-pci"
|
||||
netdev = "net0"
|
||||
+39
-45
@@ -4,6 +4,7 @@
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
import string
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
@@ -53,9 +54,6 @@ else:
|
||||
|
||||
state_dir = Path(env("VM_STATE_DIR") or script_dir / ".vm" / "debian13")
|
||||
disk_size = env("VM_DISK_SIZE") or "20G"
|
||||
memory = env("VM_MEMORY") or "8G"
|
||||
cpus = env("VM_CPUS") or "8"
|
||||
ssh_port = env("VM_SSH_PORT") or "2222"
|
||||
|
||||
|
||||
def normalize_arch(arch):
|
||||
@@ -68,12 +66,9 @@ def normalize_arch(arch):
|
||||
|
||||
native_arch = normalize_arch(platform.machine())
|
||||
host_arch = normalize_arch(env("VM_IMAGE_ARCH") or native_arch)
|
||||
if host_arch == "arm64":
|
||||
qemu_binary = "qemu-system-aarch64"
|
||||
machine_type = "virt"
|
||||
else:
|
||||
qemu_binary = "qemu-system-x86_64"
|
||||
machine_type = "q35"
|
||||
qemu_binary = "qemu-system-aarch64" if host_arch == "arm64" else "qemu-system-x86_64"
|
||||
qemu_config_template = Path(env("VM_QEMU_CONFIG") or script_dir / "qemu" / "debian13.ini")
|
||||
qemu_arch_config = script_dir / "qemu" / f"debian13-{host_arch}.ini"
|
||||
|
||||
|
||||
def pick_firmware(override, *candidates):
|
||||
@@ -125,7 +120,10 @@ Usage: {Path(sys.argv[0]).name} [--daemon] [--state-dir DIR] [-- QEMU_ARGUMENT .
|
||||
|
||||
Start the Debian 13 virtual machine. A writable overlay is created in the state
|
||||
directory (default: .vm/debian13/), so the downloaded base image is never
|
||||
modified.
|
||||
modified. The VM hardware (memory, CPUs, SSH port forward, ...) is defined in
|
||||
qemu/debian13.ini, which is rendered to qemu.ini in the state directory and
|
||||
loaded with QEMU's -readconfig, followed by qemu/debian13-<arch>.ini. Later
|
||||
QEMU arguments override them, e.g. `-- -m 4G -smp 4`.
|
||||
|
||||
Options:
|
||||
--daemon Run in the background while keeping the graphical window
|
||||
@@ -134,9 +132,6 @@ Options:
|
||||
-h, --help Show this help
|
||||
|
||||
Environment variables:
|
||||
VM_MEMORY Guest memory (default: 8G)
|
||||
VM_CPUS Virtual CPU count (default: 8)
|
||||
VM_SSH_PORT Host port forwarded to guest SSH (default: 2222)
|
||||
VM_BASE_IMAGE Base qcow2 image path (default: resolved by shared image helper)
|
||||
VM_SEED_IMAGE cloud-init seed ISO path (default: alongside the VM disk)
|
||||
VM_STATE_DIR Runtime state directory (default: .vm/debian13)
|
||||
@@ -146,6 +141,8 @@ Environment variables:
|
||||
VM_UEFI_CODE UEFI read-only firmware path (auto-detected for the selected architecture)
|
||||
VM_UEFI_VARS UEFI variable-store template path (auto-detected for the selected architecture)
|
||||
VM_DISPLAY QEMU display backend (default: cocoa on macOS, gtk elsewhere)
|
||||
VM_QEMU_CONFIG QEMU config template rendered into the state directory and
|
||||
passed to -readconfig (default: qemu/debian13.ini)
|
||||
""")
|
||||
|
||||
|
||||
@@ -176,6 +173,7 @@ seed_image = Path(env("VM_SEED_IMAGE") or disk_image.parent / "seed.iso")
|
||||
uefi_vars = state_dir / "OVMF_VARS_4M.fd"
|
||||
pid_file = state_dir / "qemu.pid"
|
||||
log_file = state_dir / "console.log"
|
||||
qemu_config = state_dir / "qemu.ini"
|
||||
|
||||
for command_name in (qemu_binary, "qemu-img"):
|
||||
if shutil.which(command_name) is None:
|
||||
@@ -232,42 +230,38 @@ if not disk_image.is_file():
|
||||
is_macos = platform.system() == "Darwin"
|
||||
display_backend = env("VM_DISPLAY") or ("cocoa" if is_macos else "gtk")
|
||||
|
||||
# Most of the VM definition lives in the QEMU config template; fill in the
|
||||
# file paths and hand the rendered file to QEMU with -readconfig.
|
||||
config_values = {
|
||||
"uefi_code": uefi_code_flash,
|
||||
"uefi_vars": uefi_vars,
|
||||
"disk_image": disk_image,
|
||||
"seed_image": seed_image,
|
||||
}
|
||||
for key, value in config_values.items():
|
||||
# QEMU's config file parser has no escape syntax for quoted values.
|
||||
if any(char in str(value) for char in '"\n'):
|
||||
die(f"{key} must not contain double quotes or newlines: {value}")
|
||||
for config_file in (qemu_config_template, qemu_arch_config):
|
||||
if not config_file.is_file():
|
||||
die(f"QEMU config file was not found: {config_file}")
|
||||
try:
|
||||
qemu_config_text = string.Template(qemu_config_template.read_text()).substitute(
|
||||
{key: str(value) for key, value in config_values.items()})
|
||||
except (KeyError, ValueError) as error:
|
||||
die(f"invalid placeholder in {qemu_config_template}: {error}")
|
||||
qemu_config.write_text(qemu_config_text)
|
||||
|
||||
qemu_args = [
|
||||
"-name", "debian13",
|
||||
"-machine", machine_type,
|
||||
"-readconfig", str(qemu_config),
|
||||
"-readconfig", str(qemu_arch_config),
|
||||
# "max" is the host CPU under KVM/HVF and the fullest model under TCG.
|
||||
"-cpu", "max",
|
||||
"-display", display_backend,
|
||||
"-m", memory,
|
||||
"-smp", cpus,
|
||||
"-drive", f"if=pflash,format=raw,readonly=on,file={uefi_code_flash}",
|
||||
"-drive", f"if=pflash,format=raw,file={uefi_vars}",
|
||||
"-drive", f"file={disk_image},if=none,id=disk0,format=qcow2",
|
||||
"-device", "virtio-blk-pci,drive=disk0",
|
||||
"-drive", f"file={seed_image},if=none,id=seed0,format=raw,readonly=on",
|
||||
"-device", "virtio-blk-pci,drive=seed0",
|
||||
"-nic", f"user,model=virtio-net-pci,hostfwd=tcp::{ssh_port}-:22",
|
||||
]
|
||||
|
||||
if machine_type == "virt":
|
||||
# Unlike q35, the "virt" machine has no default GPU or USB input devices.
|
||||
qemu_args += [
|
||||
"-device", "virtio-gpu-pci",
|
||||
"-device", "qemu-xhci",
|
||||
"-device", "usb-kbd",
|
||||
"-device", "usb-tablet",
|
||||
]
|
||||
|
||||
if is_macos and host_arch == native_arch:
|
||||
qemu_args += ["-accel", "hvf", "-cpu", "host"]
|
||||
accelerator = "HVF"
|
||||
elif host_arch == native_arch and os.access("/dev/kvm", os.R_OK | os.W_OK):
|
||||
qemu_args += ["-enable-kvm", "-cpu", "host"]
|
||||
accelerator = "KVM"
|
||||
else:
|
||||
qemu_args += ["-accel", "tcg", "-cpu", "max"]
|
||||
accelerator = "TCG"
|
||||
|
||||
print(f"Starting {host_arch} VM with {accelerator} acceleration; "
|
||||
f"SSH is forwarded to localhost:{ssh_port}.", flush=True)
|
||||
print(f"Starting {host_arch} VM from {qemu_config_template} "
|
||||
f"and {qemu_arch_config}.", flush=True)
|
||||
|
||||
if daemonize:
|
||||
log_file.write_text("")
|
||||
|
||||
Reference in new issue
Block a user