summaryrefslogtreecommitdiffstats
path: root/recipes-containers/vcontainer/files
diff options
context:
space:
mode:
authorBruce Ashfield <bruce.ashfield@gmail.com>2026-02-19 19:07:17 +0000
committerBruce Ashfield <bruce.ashfield@gmail.com>2026-02-26 01:05:01 +0000
commit5c84f660f7f717765b5662fd69c52cacf0ab64d9 (patch)
tree6bf52fd570442bc2885684f832c28e42870af3d8 /recipes-containers/vcontainer/files
parent9377aede3157a3e7b702dc389c15f27523b673e7 (diff)
downloadmeta-virtualization-5c84f660f7f717765b5662fd69c52cacf0ab64d9.tar.gz
vcontainer: add QEMU hypervisor backend and register in recipes
Add vrunner-backend-qemu.sh implementing the hypervisor interface for QEMU (arch setup, KVM detection, disk/network/9p options, VM lifecycle, QMP control). Register backend scripts in vcontainer-native and vcontainer-tarball recipes so they are available in both build-time and standalone tarball contexts. Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
Diffstat (limited to 'recipes-containers/vcontainer/files')
-rw-r--r--recipes-containers/vcontainer/files/vrunner-backend-qemu.sh260
1 files changed, 260 insertions, 0 deletions
diff --git a/recipes-containers/vcontainer/files/vrunner-backend-qemu.sh b/recipes-containers/vcontainer/files/vrunner-backend-qemu.sh
new file mode 100644
index 00000000..87054876
--- /dev/null
+++ b/recipes-containers/vcontainer/files/vrunner-backend-qemu.sh
@@ -0,0 +1,260 @@
1#!/bin/bash
2# SPDX-FileCopyrightText: Copyright (C) 2025 Bruce Ashfield
3#
4# SPDX-License-Identifier: GPL-2.0-only
5#
6# vrunner-backend-qemu.sh
7# QEMU hypervisor backend for vrunner.sh
8#
9# This backend implements the hypervisor interface for QEMU.
10# It is sourced by vrunner.sh when VCONTAINER_HYPERVISOR=qemu.
11#
12# Backend interface functions:
13# hv_setup_arch() - Set arch-specific QEMU command and machine type
14# hv_check_accel() - Detect KVM acceleration
15# hv_build_disk_opts() - Build QEMU disk drive options
16# hv_build_network_opts() - Build QEMU network options
17# hv_build_9p_opts() - Build QEMU virtio-9p options
18# hv_build_daemon_opts() - Build QEMU daemon mode options (serial, QMP)
19# hv_build_vm_cmd() - Assemble final QEMU command line
20# hv_start_vm_background() - Start QEMU in background, capture PID
21# hv_start_vm_foreground() - Start QEMU in foreground (interactive)
22# hv_is_vm_running() - Check if QEMU process is alive
23# hv_wait_vm_exit() - Wait for QEMU to exit
24# hv_stop_vm() - Graceful shutdown via QMP
25# hv_destroy_vm() - Force kill QEMU process
26# hv_get_vm_id() - Return QEMU PID
27# hv_setup_port_forwards() - Port forwards via QEMU hostfwd (built into netdev)
28# hv_cleanup_port_forwards() - No-op for QEMU (forwards die with process)
29# hv_idle_shutdown() - Send QMP quit for idle timeout
30# hv_get_console_device() - Return arch-specific console device name
31
32# ============================================================================
33# Architecture Setup
34# ============================================================================
35
36hv_setup_arch() {
37 case "$TARGET_ARCH" in
38 aarch64)
39 KERNEL_IMAGE="$BLOB_DIR/aarch64/Image"
40 INITRAMFS="$BLOB_DIR/aarch64/initramfs.cpio.gz"
41 ROOTFS_IMG="$BLOB_DIR/aarch64/rootfs.img"
42 HV_CMD="qemu-system-aarch64"
43 HV_MACHINE="-M virt -cpu cortex-a57"
44 HV_CONSOLE="ttyAMA0"
45 ;;
46 x86_64)
47 KERNEL_IMAGE="$BLOB_DIR/x86_64/bzImage"
48 INITRAMFS="$BLOB_DIR/x86_64/initramfs.cpio.gz"
49 ROOTFS_IMG="$BLOB_DIR/x86_64/rootfs.img"
50 HV_CMD="qemu-system-x86_64"
51 HV_MACHINE="-M q35 -cpu Skylake-Client"
52 HV_CONSOLE="ttyS0"
53 ;;
54 *)
55 log "ERROR" "Unsupported architecture: $TARGET_ARCH"
56 exit 1
57 ;;
58 esac
59}
60
61hv_check_accel() {
62 USE_KVM="false"
63 if [ "$DISABLE_KVM" = "true" ]; then
64 log "DEBUG" "KVM disabled by --no-kvm flag"
65 return
66 fi
67
68 HOST_ARCH=$(uname -m)
69 if [ "$HOST_ARCH" = "$TARGET_ARCH" ] || \
70 { [ "$HOST_ARCH" = "x86_64" ] && [ "$TARGET_ARCH" = "x86_64" ]; }; then
71 if [ -w /dev/kvm ]; then
72 USE_KVM="true"
73 case "$TARGET_ARCH" in
74 x86_64) HV_MACHINE="-M q35 -cpu host" ;;
75 aarch64) HV_MACHINE="-M virt -cpu host" ;;
76 esac
77 log "INFO" "KVM acceleration enabled"
78 else
79 log "DEBUG" "KVM not available (no write access to /dev/kvm)"
80 fi
81 fi
82}
83
84hv_find_command() {
85 if ! command -v "$HV_CMD" >/dev/null 2>&1; then
86 for path in \
87 "${STAGING_BINDIR_NATIVE:-}" \
88 "/usr/bin"; do
89 if [ -n "$path" ] && [ -x "$path/$HV_CMD" ]; then
90 HV_CMD="$path/$HV_CMD"
91 break
92 fi
93 done
94 fi
95
96 if ! command -v "$HV_CMD" >/dev/null 2>&1 && [ ! -x "$HV_CMD" ]; then
97 log "ERROR" "QEMU not found: $HV_CMD"
98 exit 1
99 fi
100 log "DEBUG" "Using QEMU: $HV_CMD"
101}
102
103hv_get_console_device() {
104 echo "$HV_CONSOLE"
105}
106
107# ============================================================================
108# VM Configuration Building
109# ============================================================================
110
111hv_build_disk_opts() {
112 # Rootfs (read-only)
113 HV_DISK_OPTS="-drive file=$ROOTFS_IMG,if=virtio,format=raw,readonly=on"
114
115 # Input disk (if any)
116 if [ -n "$DISK_OPTS" ]; then
117 HV_DISK_OPTS="$HV_DISK_OPTS $DISK_OPTS"
118 fi
119
120 # State disk (if any)
121 if [ -n "$STATE_DISK_OPTS" ]; then
122 HV_DISK_OPTS="$HV_DISK_OPTS $STATE_DISK_OPTS"
123 fi
124}
125
126hv_build_network_opts() {
127 HV_NET_OPTS=""
128 if [ "$NETWORK" = "true" ]; then
129 NETDEV_OPTS="user,id=net0"
130
131 # Add port forwards
132 for pf in "${PORT_FORWARDS[@]}"; do
133 HOST_PORT="${pf%%:*}"
134 CONTAINER_PART="${pf#*:}"
135 CONTAINER_PORT="${CONTAINER_PART%%/*}"
136 if [[ "$CONTAINER_PART" == */* ]]; then
137 PROTOCOL="${CONTAINER_PART##*/}"
138 else
139 PROTOCOL="tcp"
140 fi
141 NETDEV_OPTS="$NETDEV_OPTS,hostfwd=$PROTOCOL::$HOST_PORT-:$HOST_PORT"
142 log "INFO" "Port forward: host:$HOST_PORT -> VM:$HOST_PORT (Docker maps to container:$CONTAINER_PORT)"
143 done
144
145 HV_NET_OPTS="-netdev $NETDEV_OPTS -device virtio-net-pci,netdev=net0"
146 else
147 HV_NET_OPTS="-nic none"
148 fi
149}
150
151hv_build_9p_opts() {
152 local share_dir="$1"
153 local share_tag="$2"
154 local extra_opts="${3:-}"
155 HV_OPTS="$HV_OPTS -virtfs local,path=$share_dir,mount_tag=$share_tag,security_model=none${extra_opts:+,$extra_opts},id=$share_tag"
156}
157
158hv_build_daemon_opts() {
159 HV_DAEMON_OPTS=""
160
161 # virtio-serial for command channel
162 HV_DAEMON_OPTS="$HV_DAEMON_OPTS -chardev socket,id=vdkr,path=$DAEMON_SOCKET,server=on,wait=off"
163 HV_DAEMON_OPTS="$HV_DAEMON_OPTS -device virtio-serial-pci"
164 HV_DAEMON_OPTS="$HV_DAEMON_OPTS -device virtserialport,chardev=vdkr,name=vdkr"
165
166 # QMP socket for dynamic control
167 QMP_SOCKET="$DAEMON_SOCKET_DIR/qmp.sock"
168 HV_DAEMON_OPTS="$HV_DAEMON_OPTS -qmp unix:$QMP_SOCKET,server,nowait"
169}
170
171hv_build_vm_cmd() {
172 HV_OPTS="$HV_MACHINE -nographic -smp 2 -m 2048 -no-reboot"
173 if [ "$USE_KVM" = "true" ]; then
174 HV_OPTS="$HV_OPTS -enable-kvm"
175 fi
176 HV_OPTS="$HV_OPTS -kernel $KERNEL_IMAGE"
177 HV_OPTS="$HV_OPTS -initrd $INITRAMFS"
178 HV_OPTS="$HV_OPTS $HV_DISK_OPTS"
179 HV_OPTS="$HV_OPTS $HV_NET_OPTS"
180}
181
182# ============================================================================
183# VM Lifecycle
184# ============================================================================
185
186hv_start_vm_background() {
187 local kernel_append="$1"
188 local log_file="$2"
189 local timeout_val="$3"
190
191 if [ -n "$timeout_val" ]; then
192 timeout $timeout_val $HV_CMD $HV_OPTS -append "$kernel_append" > "$log_file" 2>&1 &
193 else
194 $HV_CMD $HV_OPTS -append "$kernel_append" > "$log_file" 2>&1 &
195 fi
196 HV_VM_PID=$!
197}
198
199hv_start_vm_foreground() {
200 local kernel_append="$1"
201 $HV_CMD $HV_OPTS -append "$kernel_append"
202}
203
204hv_is_vm_running() {
205 [ -n "$HV_VM_PID" ] && [ -d "/proc/$HV_VM_PID" ]
206}
207
208hv_wait_vm_exit() {
209 local timeout="${1:-30}"
210 for i in $(seq 1 "$timeout"); do
211 hv_is_vm_running || return 0
212 sleep 1
213 done
214 return 1
215}
216
217hv_stop_vm() {
218 if [ -n "$HV_VM_PID" ] && kill -0 "$HV_VM_PID" 2>/dev/null; then
219 log "WARN" "QEMU still running, forcing termination..."
220 kill $HV_VM_PID 2>/dev/null || true
221 wait $HV_VM_PID 2>/dev/null || true
222 fi
223}
224
225hv_destroy_vm() {
226 if [ -n "$HV_VM_PID" ]; then
227 kill -9 $HV_VM_PID 2>/dev/null || true
228 wait $HV_VM_PID 2>/dev/null || true
229 fi
230}
231
232hv_get_vm_id() {
233 echo "$HV_VM_PID"
234}
235
236# ============================================================================
237# Port Forwarding (handled by QEMU hostfwd, no separate setup needed)
238# ============================================================================
239
240hv_setup_port_forwards() {
241 # QEMU port forwards are built into the -netdev hostfwd= options
242 # Nothing extra to do at runtime
243 :
244}
245
246hv_cleanup_port_forwards() {
247 # QEMU port forwards die with the process
248 :
249}
250
251# ============================================================================
252# Idle Timeout / QMP Control
253# ============================================================================
254
255hv_idle_shutdown() {
256 if [ -S "$QMP_SOCKET" ]; then
257 echo '{"execute":"qmp_capabilities"}{"execute":"quit"}' | \
258 socat - "UNIX-CONNECT:$QMP_SOCKET" >/dev/null 2>&1 || true
259 fi
260}