blob: 69cd44864e5d8537cfe50f57fd59113dd5a3b93c [file] [log] [blame]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001#!/usr/bin/env python3
2
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003# Handle running OE images standalone with QEMU
4#
5# Copyright (C) 2006-2011 Linux Foundation
Patrick Williamsc0f7c042017-02-23 20:41:17 -06006# Copyright (c) 2016 Wind River Systems, Inc.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05007#
Brad Bishopc342db32019-05-15 21:57:59 -04008# SPDX-License-Identifier: GPL-2.0-only
Patrick Williamsc124f4f2015-09-15 14:41:29 -05009#
Patrick Williamsc124f4f2015-09-15 14:41:29 -050010
Patrick Williamsc0f7c042017-02-23 20:41:17 -060011import os
12import sys
13import logging
14import subprocess
15import re
16import fcntl
17import shutil
18import glob
19import configparser
Brad Bishop004d4992018-10-02 23:54:45 +020020import signal
Andrew Geissler09036742021-06-25 14:25:14 -050021import time
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050022
Brad Bishopd7bf8c12018-02-25 22:55:05 -050023class RunQemuError(Exception):
24 """Custom exception to raise on known errors."""
25 pass
26
27class OEPathError(RunQemuError):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060028 """Custom Exception to give better guidance on missing binaries"""
29 def __init__(self, message):
Brad Bishopd7bf8c12018-02-25 22:55:05 -050030 super().__init__("In order for this script to dynamically infer paths\n \
Patrick Williamsc0f7c042017-02-23 20:41:17 -060031kernels or filesystem images, you either need bitbake in your PATH\n \
32or to source oe-init-build-env before running this script.\n\n \
33Dynamic path inference can be avoided by passing a *.qemuboot.conf to\n \
Brad Bishopd7bf8c12018-02-25 22:55:05 -050034runqemu, i.e. `runqemu /path/to/my-image-name.qemuboot.conf`\n\n %s" % message)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060035
36
37def create_logger():
38 logger = logging.getLogger('runqemu')
39 logger.setLevel(logging.INFO)
40
41 # create console handler and set level to debug
42 ch = logging.StreamHandler()
Brad Bishopd7bf8c12018-02-25 22:55:05 -050043 ch.setLevel(logging.DEBUG)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060044
45 # create formatter
46 formatter = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
47
48 # add formatter to ch
49 ch.setFormatter(formatter)
50
51 # add ch to logger
52 logger.addHandler(ch)
53
54 return logger
55
56logger = create_logger()
57
58def print_usage():
59 print("""
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050060Usage: you can run this script with any valid combination
61of the following environment variables (in any order):
62 KERNEL - the kernel image file to use
Brad Bishopc68388fc2019-08-26 01:33:31 -040063 BIOS - the bios image file to use
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050064 ROOTFS - the rootfs image file or nfsroot directory to use
Brad Bishop316dfdd2018-06-25 12:45:53 -040065 DEVICE_TREE - the device tree blob to use
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050066 MACHINE - the machine name (optional, autodetected from KERNEL filename if unspecified)
67 Simplified QEMU command-line options can be passed with:
Patrick Williamsc0f7c042017-02-23 20:41:17 -060068 nographic - disable video console
Patrick Williams8e7b46e2023-05-01 14:19:06 -050069 nonetwork - disable network connectivity
Andrew Geissler90fd73c2021-03-05 15:25:55 -060070 novga - Disable VGA emulation completely
Brad Bishopa34c0302019-09-23 22:34:48 -040071 sdl - choose the SDL UI frontend
72 gtk - choose the Gtk UI frontend
Brad Bishop6dbb3162019-11-25 09:41:34 -050073 gl - enable virgl-based GL acceleration (also needs gtk or sdl options)
74 gl-es - enable virgl-based GL acceleration, using OpenGL ES (also needs gtk or sdl options)
75 egl-headless - enable headless EGL output; use vnc (via publicvnc option) or spice to see it
Andrew Geisslerd159c7f2021-09-02 21:05:58 -050076 (hint: if /dev/dri/renderD* is absent due to lack of suitable GPU, 'modprobe vgem' will create
Andrew Geissler9aee5002022-03-30 16:27:02 +000077 one suitable for mesa llvmpipe software renderer)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060078 serial - enable a serial console on /dev/ttyS0
Brad Bishop19323692019-04-05 15:28:33 -040079 serialstdio - enable a serial console on the console (regardless of graphics mode)
Andrew Geissler9aee5002022-03-30 16:27:02 +000080 slirp - enable user networking, no root privilege is required
81 snapshot - don't write changes back to images
Patrick Williamsc0f7c042017-02-23 20:41:17 -060082 kvm - enable KVM when running x86/x86_64 (VT-capable CPU required)
83 kvm-vhost - enable KVM with vhost when running x86/x86_64 (VT-capable CPU required)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050084 publicvnc - enable a VNC server open to all hosts
Patrick Williamsc0f7c042017-02-23 20:41:17 -060085 audio - enable audio
Andrew Geisslerfc113ea2023-03-31 09:59:46 -050086 guestagent - enable guest agent communication
Patrick Williams169d7bc2024-01-05 11:33:25 -060087 qmp=<path> - create a QMP socket (defaults to unix:qmp.sock if unspecified)
Brad Bishop6e60e8b2018-02-01 10:27:11 -050088 [*/]ovmf* - OVMF firmware file or base name for booting with UEFI
Patrick Williamsc0f7c042017-02-23 20:41:17 -060089 tcpserial=<port> - specify tcp serial port number
Patrick Williamsc0f7c042017-02-23 20:41:17 -060090 qemuparams=<xyz> - specify custom parameters to QEMU
91 bootparams=<xyz> - specify custom kernel parameters during boot
Brad Bishop6e60e8b2018-02-01 10:27:11 -050092 help, -h, --help: print this text
Brad Bishopd7bf8c12018-02-25 22:55:05 -050093 -d, --debug: Enable debug output
Brad Bishop79641f22019-09-10 07:20:22 -040094 -q, --quiet: Hide most output except error messages
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050095
96Examples:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050097 runqemu
Patrick Williamsc0f7c042017-02-23 20:41:17 -060098 runqemu qemuarm
99 runqemu tmp/deploy/images/qemuarm
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500100 runqemu tmp/deploy/images/qemux86/<qemuboot.conf>
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600101 runqemu qemux86-64 core-image-sato ext4
102 runqemu qemux86-64 wic-image-minimal wic
103 runqemu path/to/bzImage-qemux86.bin path/to/nfsrootdir/ serial
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600104 runqemu qemux86 iso/hddimg/wic.vmdk/wic.vhd/wic.vhdx/wic.qcow2/wic.vdi/ramfs/cpio.gz...
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600105 runqemu qemux86 qemuparams="-m 256"
106 runqemu qemux86 bootparams="psplash=false"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600107 runqemu path/to/<image>-<machine>.wic
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500108 runqemu path/to/<image>-<machine>.wic.vmdk
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600109 runqemu path/to/<image>-<machine>.wic.vhdx
110 runqemu path/to/<image>-<machine>.wic.vhd
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600111""")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500112
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600113def check_tun():
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500114 """Check /dev/net/tun"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600115 dev_tun = '/dev/net/tun'
116 if not os.path.exists(dev_tun):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500117 raise RunQemuError("TUN control device %s is unavailable; you may need to enable TUN (e.g. sudo modprobe tun)" % dev_tun)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500118
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600119 if not os.access(dev_tun, os.W_OK):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500120 raise RunQemuError("TUN control device %s is not writable, please fix (e.g. sudo chmod 666 %s)" % (dev_tun, dev_tun))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500121
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500122def get_first_file(globs):
123 """Return first file found in wildcard globs"""
124 for g in globs:
125 all_files = glob.glob(g)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600126 if all_files:
127 for f in all_files:
128 if not os.path.isdir(f):
129 return f
130 return ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500131
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600132class BaseConfig(object):
133 def __init__(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500134 # The self.d saved vars from self.set(), part of them are from qemuboot.conf
135 self.d = {'QB_KERNEL_ROOT': '/dev/vda'}
136
137 # Supported env vars, add it here if a var can be got from env,
138 # and don't use os.getenv in the code.
139 self.env_vars = ('MACHINE',
140 'ROOTFS',
141 'KERNEL',
Brad Bishopc68388fc2019-08-26 01:33:31 -0400142 'BIOS',
Brad Bishop316dfdd2018-06-25 12:45:53 -0400143 'DEVICE_TREE',
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500144 'DEPLOY_DIR_IMAGE',
145 'OE_TMPDIR',
146 'OECORE_NATIVE_SYSROOT',
Andrew Geissler82c905d2020-04-13 13:39:40 -0500147 'MULTICONFIG',
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500148 'SERIAL_CONSOLES',
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500149 )
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500150
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600151 self.qemu_opt = ''
152 self.qemu_opt_script = ''
Andrew Geissler99467da2019-02-25 18:54:23 -0600153 self.qemuparams = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600154 self.nfs_server = ''
155 self.rootfs = ''
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500156 # File name(s) of a OVMF firmware file or variable store,
157 # to be added with -drive if=pflash.
158 # Found in the same places as the rootfs, with or without one of
159 # these suffices: qcow2, bin.
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500160 self.ovmf_bios = []
Brad Bishop08902b02019-08-20 09:16:51 -0400161 # When enrolling default Secure Boot keys, the hypervisor
162 # must provide the Platform Key and the first Key Exchange Key
163 # certificate in the Type 11 SMBIOS table.
164 self.ovmf_secboot_pkkek1 = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600165 self.qemuboot = ''
166 self.qbconfload = False
167 self.kernel = ''
Brad Bishopc68388fc2019-08-26 01:33:31 -0400168 self.bios = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600169 self.kernel_cmdline = ''
170 self.kernel_cmdline_script = ''
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500171 self.bootparams = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600172 self.dtb = ''
173 self.fstype = ''
174 self.kvm_enabled = False
175 self.vhost_enabled = False
176 self.slirp_enabled = False
Andrew Geissler82c905d2020-04-13 13:39:40 -0500177 self.net_bridge = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600178 self.nfs_instance = 0
179 self.nfs_running = False
Brad Bishop19323692019-04-05 15:28:33 -0400180 self.serialconsole = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600181 self.serialstdio = False
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500182 self.nographic = False
Patrick Williams8e7b46e2023-05-01 14:19:06 -0500183 self.nonetwork = False
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500184 self.sdl = False
185 self.gtk = False
186 self.gl = False
187 self.gl_es = False
188 self.egl_headless = False
Patrick Williams03907ee2022-05-01 06:28:52 -0500189 self.publicvnc = False
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500190 self.novga = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600191 self.cleantap = False
192 self.saved_stty = ''
193 self.audio_enabled = False
194 self.tcpserial_portnum = ''
Brad Bishop08902b02019-08-20 09:16:51 -0400195 self.taplock = ''
196 self.taplock_descriptor = None
197 self.portlocks = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600198 self.bitbake_e = ''
199 self.snapshot = False
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600200 self.wictypes = ('wic', 'wic.vmdk', 'wic.qcow2', 'wic.vdi', "wic.vhd", "wic.vhdx")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500201 self.fstypes = ('ext2', 'ext3', 'ext4', 'jffs2', 'nfs', 'btrfs',
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600202 'cpio.gz', 'cpio', 'ramfs', 'tar.bz2', 'tar.gz',
203 'squashfs', 'squashfs-xz', 'squashfs-lzo',
204 'squashfs-lz4', 'squashfs-zst')
Brad Bishop08902b02019-08-20 09:16:51 -0400205 self.vmtypes = ('hddimg', 'iso')
Brad Bishop15ae2502019-06-18 21:44:24 -0400206 self.fsinfo = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500207 self.network_device = "-device e1000,netdev=net0,mac=@MAC@"
Andrew Geissler82c905d2020-04-13 13:39:40 -0500208 self.cmdline_ip_slirp = "ip=dhcp"
Patrick Williams2a254922023-08-11 09:48:11 -0500209 self.cmdline_ip_tap = "ip=192.168.7.@CLIENT@::192.168.7.@GATEWAY@:255.255.255.0::eth0:off:8.8.8.8 net.ifnames=0"
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500210 # Use different mac section for tap and slirp to avoid
211 # conflicts, e.g., when one is running with tap, the other is
212 # running with slirp.
213 # The last section is dynamic, which is for avoiding conflicts,
214 # when multiple qemus are running, e.g., when multiple tap or
215 # slirp qemus are running.
216 self.mac_tap = "52:54:00:12:34:"
217 self.mac_slirp = "52:54:00:12:35:"
Brad Bishop004d4992018-10-02 23:54:45 +0200218 # pid of the actual qemu process
Patrick Williams2390b1b2022-11-03 13:47:49 -0500219 self.qemu_environ = os.environ.copy()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600220 self.qemuprocess = None
Brad Bishop004d4992018-10-02 23:54:45 +0200221 # avoid cleanup twice
222 self.cleaned = False
Andrew Geisslerc926e172021-05-07 16:11:35 -0500223 # Files to cleanup after run
224 self.cleanup_files = []
Patrick Williams169d7bc2024-01-05 11:33:25 -0600225 self.qmp = None
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500226 self.guest_agent = False
227 self.guest_agent_sockpath = '/tmp/qga.sock'
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500228
Brad Bishop08902b02019-08-20 09:16:51 -0400229 def acquire_taplock(self, error=True):
230 logger.debug("Acquiring lockfile %s..." % self.taplock)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600231 try:
Brad Bishop08902b02019-08-20 09:16:51 -0400232 self.taplock_descriptor = open(self.taplock, 'w')
233 fcntl.flock(self.taplock_descriptor, fcntl.LOCK_EX|fcntl.LOCK_NB)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600234 except Exception as e:
Brad Bishop08902b02019-08-20 09:16:51 -0400235 msg = "Acquiring lockfile %s failed: %s" % (self.taplock, e)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500236 if error:
237 logger.error(msg)
238 else:
239 logger.info(msg)
Brad Bishop08902b02019-08-20 09:16:51 -0400240 if self.taplock_descriptor:
241 self.taplock_descriptor.close()
242 self.taplock_descriptor = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600243 return False
244 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500245
Brad Bishop08902b02019-08-20 09:16:51 -0400246 def release_taplock(self):
247 if self.taplock_descriptor:
Brad Bishopf86d0552018-12-04 14:18:15 -0800248 logger.debug("Releasing lockfile for tap device '%s'" % self.tap)
Andrew Geissler5f350902021-07-23 13:09:54 -0400249 # We pass the fd to the qemu process and if we unlock here, it would unlock for
250 # that too. Therefore don't unlock, just close
251 # fcntl.flock(self.taplock_descriptor, fcntl.LOCK_UN)
Brad Bishop08902b02019-08-20 09:16:51 -0400252 self.taplock_descriptor.close()
Andrew Geissler5f350902021-07-23 13:09:54 -0400253 # Removing the file is a potential race, don't do that either
254 # os.remove(self.taplock)
Brad Bishop08902b02019-08-20 09:16:51 -0400255 self.taplock_descriptor = None
256
257 def check_free_port(self, host, port, lockdir):
258 """ Check whether the port is free or not """
259 import socket
260 from contextlib import closing
261
262 lockfile = os.path.join(lockdir, str(port) + '.lock')
263 if self.acquire_portlock(lockfile):
264 with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
265 if sock.connect_ex((host, port)) == 0:
266 # Port is open, so not free
267 self.release_portlock(lockfile)
268 return False
269 else:
270 # Port is not open, so free
271 return True
272 else:
273 return False
274
275 def acquire_portlock(self, lockfile):
276 logger.debug("Acquiring lockfile %s..." % lockfile)
277 try:
278 portlock_descriptor = open(lockfile, 'w')
279 self.portlocks.update({lockfile: portlock_descriptor})
280 fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_EX|fcntl.LOCK_NB)
281 except Exception as e:
282 msg = "Acquiring lockfile %s failed: %s" % (lockfile, e)
283 logger.info(msg)
284 if lockfile in self.portlocks.keys() and self.portlocks[lockfile]:
285 self.portlocks[lockfile].close()
286 del self.portlocks[lockfile]
287 return False
288 return True
289
290 def release_portlock(self, lockfile=None):
291 if lockfile != None:
Andrew Geissler5f350902021-07-23 13:09:54 -0400292 logger.debug("Releasing lockfile '%s'" % lockfile)
293 # We pass the fd to the qemu process and if we unlock here, it would unlock for
294 # that too. Therefore don't unlock, just close
295 # fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_UN)
296 self.portlocks[lockfile].close()
297 # Removing the file is a potential race, don't do that either
298 # os.remove(lockfile)
299 del self.portlocks[lockfile]
Brad Bishop08902b02019-08-20 09:16:51 -0400300 elif len(self.portlocks):
301 for lockfile, descriptor in self.portlocks.items():
302 logger.debug("Releasing lockfile '%s'" % lockfile)
Andrew Geissler5f350902021-07-23 13:09:54 -0400303 # We pass the fd to the qemu process and if we unlock here, it would unlock for
304 # that too. Therefore don't unlock, just close
305 # fcntl.flock(descriptor, fcntl.LOCK_UN)
Brad Bishop08902b02019-08-20 09:16:51 -0400306 descriptor.close()
Andrew Geissler5f350902021-07-23 13:09:54 -0400307 # Removing the file is a potential race, don't do that either
308 # os.remove(lockfile)
Brad Bishop08902b02019-08-20 09:16:51 -0400309 self.portlocks = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500310
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600311 def get(self, key):
312 if key in self.d:
313 return self.d.get(key)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500314 elif os.getenv(key):
315 return os.getenv(key)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600316 else:
317 return ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500318
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600319 def set(self, key, value):
320 self.d[key] = value
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500321
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600322 def is_deploy_dir_image(self, p):
323 if os.path.isdir(p):
324 if not re.search('.qemuboot.conf$', '\n'.join(os.listdir(p)), re.M):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500325 logger.debug("Can't find required *.qemuboot.conf in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600326 return False
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500327 if not any(map(lambda name: '-image-' in name, os.listdir(p))):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500328 logger.debug("Can't find *-image-* in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600329 return False
330 return True
331 else:
332 return False
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500333
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600334 def check_arg_fstype(self, fst):
335 """Check and set FSTYPE"""
Brad Bishop15ae2502019-06-18 21:44:24 -0400336 if fst not in self.fstypes + self.vmtypes + self.wictypes:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800337 logger.warning("Maybe unsupported FSTYPE: %s" % fst)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600338 if not self.fstype or self.fstype == fst:
339 if fst == 'ramfs':
340 fst = 'cpio.gz'
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500341 if fst in ('tar.bz2', 'tar.gz'):
342 fst = 'nfs'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600343 self.fstype = fst
344 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500345 raise RunQemuError("Conflicting: FSTYPE %s and %s" % (self.fstype, fst))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500346
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600347 def set_machine_deploy_dir(self, machine, deploy_dir_image):
348 """Set MACHINE and DEPLOY_DIR_IMAGE"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500349 logger.debug('MACHINE: %s' % machine)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600350 self.set("MACHINE", machine)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500351 logger.debug('DEPLOY_DIR_IMAGE: %s' % deploy_dir_image)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600352 self.set("DEPLOY_DIR_IMAGE", deploy_dir_image)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500353
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600354 def check_arg_nfs(self, p):
355 if os.path.isdir(p):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500356 self.rootfs = p
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600357 else:
358 m = re.match('(.*):(.*)', p)
359 self.nfs_server = m.group(1)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500360 self.rootfs = m.group(2)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600361 self.check_arg_fstype('nfs')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500362
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600363 def check_arg_path(self, p):
364 """
365 - Check whether it is <image>.qemuboot.conf or contains <image>.qemuboot.conf
Andrew Geissler9aee5002022-03-30 16:27:02 +0000366 - Check whether it is a kernel file
367 - Check whether it is an image file
368 - Check whether it is an NFS dir
369 - Check whether it is an OVMF flash file
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600370 """
371 if p.endswith('.qemuboot.conf'):
372 self.qemuboot = p
373 self.qbconfload = True
Patrick Williams169d7bc2024-01-05 11:33:25 -0600374 elif re.search('\\.bin$', p) or re.search('bzImage', p) or \
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600375 re.search('zImage', p) or re.search('vmlinux', p) or \
376 re.search('fitImage', p) or re.search('uImage', p):
377 self.kernel = p
Patrick Williams169d7bc2024-01-05 11:33:25 -0600378 elif os.path.isfile(p) and ('-image-' in os.path.basename(p) or '.rootfs.' in os.path.basename(p)):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600379 self.rootfs = p
Andrew Geissler9aee5002022-03-30 16:27:02 +0000380 # Check filename against self.fstypes can handle <file>.cpio.gz,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500381 # otherwise, its type would be "gz", which is incorrect.
382 fst = ""
383 for t in self.fstypes:
384 if p.endswith(t):
385 fst = t
386 break
387 if not fst:
Patrick Williams169d7bc2024-01-05 11:33:25 -0600388 m = re.search('.*\\.(.*)$', self.rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500389 if m:
390 fst = m.group(1)
391 if fst:
392 self.check_arg_fstype(fst)
Patrick Williams169d7bc2024-01-05 11:33:25 -0600393 qb = re.sub('\\.' + fst + "$", '.qemuboot.conf', self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600394 if os.path.exists(qb):
395 self.qemuboot = qb
396 self.qbconfload = True
397 else:
Patrick Williams8e7b46e2023-05-01 14:19:06 -0500398 logger.warning("%s doesn't exist, will try to remove '.rootfs' from filename" % qb)
399 # They to remove .rootfs (IMAGE_NAME_SUFFIX) as well
Patrick Williams169d7bc2024-01-05 11:33:25 -0600400 qb = re.sub('\\.rootfs.qemuboot.conf$', '.qemuboot.conf', qb)
Patrick Williams8e7b46e2023-05-01 14:19:06 -0500401 if os.path.exists(qb):
402 self.qemuboot = qb
403 self.qbconfload = True
404 else:
405 logger.warning("%s doesn't exist" % qb)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600406 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500407 raise RunQemuError("Can't find FSTYPE from: %s" % p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500408
409 elif os.path.isdir(p) or re.search(':', p) and re.search('/', p):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600410 if self.is_deploy_dir_image(p):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500411 logger.debug('DEPLOY_DIR_IMAGE: %s' % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600412 self.set("DEPLOY_DIR_IMAGE", p)
413 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500414 logger.debug("Assuming %s is an nfs rootfs" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600415 self.check_arg_nfs(p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500416 elif os.path.basename(p).startswith('ovmf'):
417 self.ovmf_bios.append(p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600418 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500419 raise RunQemuError("Unknown path arg %s" % p)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500420
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600421 def check_arg_machine(self, arg):
422 """Check whether it is a machine"""
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500423 if self.get('MACHINE') == arg:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600424 return
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500425 elif self.get('MACHINE') and self.get('MACHINE') != arg:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500426 raise RunQemuError("Maybe conflicted MACHINE: %s vs %s" % (self.get('MACHINE'), arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500427 elif re.search('/', arg):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500428 raise RunQemuError("Unknown arg: %s" % arg)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500429
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500430 logger.debug('Assuming MACHINE = %s' % arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500431
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600432 # if we're running under testimage, or similarly as a child
433 # of an existing bitbake invocation, we can't invoke bitbake
434 # to validate the MACHINE setting and must assume it's correct...
435 # FIXME: testimage.bbclass exports these two variables into env,
436 # are there other scenarios in which we need to support being
437 # invoked by bitbake?
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500438 deploy = self.get('DEPLOY_DIR_IMAGE')
Patrick Williams8e7b46e2023-05-01 14:19:06 -0500439 image_link_name = self.get('IMAGE_LINK_NAME')
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500440 bbchild = deploy and self.get('OE_TMPDIR')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600441 if bbchild:
442 self.set_machine_deploy_dir(arg, deploy)
443 return
444 # also check whether we're running under a sourced toolchain
445 # environment file
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500446 if self.get('OECORE_NATIVE_SYSROOT'):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600447 self.set("MACHINE", arg)
448 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500449
Andrew Geissler82c905d2020-04-13 13:39:40 -0500450 self.bitbake_e = self.run_bitbake_env(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600451 # bitbake -e doesn't report invalid MACHINE as an error, so
452 # let's check DEPLOY_DIR_IMAGE to make sure that it is a valid
453 # MACHINE.
454 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
455 if s:
456 deploy_dir_image = s.group(1)
457 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500458 raise RunQemuError("bitbake -e %s" % self.bitbake_e)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600459 if self.is_deploy_dir_image(deploy_dir_image):
460 self.set_machine_deploy_dir(arg, deploy_dir_image)
461 else:
462 logger.error("%s not a directory valid DEPLOY_DIR_IMAGE" % deploy_dir_image)
463 self.set("MACHINE", arg)
Patrick Williams8e7b46e2023-05-01 14:19:06 -0500464 if not image_link_name:
465 s = re.search('^IMAGE_LINK_NAME="(.*)"', self.bitbake_e, re.M)
466 if s:
467 image_link_name = s.group(1)
468 self.set("IMAGE_LINK_NAME", image_link_name)
469 logger.debug('Using IMAGE_LINK_NAME = "%s"' % image_link_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500470
Andrew Geisslerc182c622020-05-15 14:13:32 -0500471 def set_dri_path(self):
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500472 drivers_path = os.path.join(self.bindir_native, '../lib/dri')
473 if not os.path.exists(drivers_path) or not os.listdir(drivers_path):
474 raise RunQemuError("""
475qemu has been built without opengl support and accelerated graphics support is not available.
476To enable it, add:
477DISTRO_FEATURES_NATIVE:append = " opengl"
478DISTRO_FEATURES_NATIVESDK:append = " opengl"
479to your build configuration.
480""")
481 self.qemu_environ['LIBGL_DRIVERS_PATH'] = drivers_path
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000482
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600483 def check_args(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500484 for debug in ("-d", "--debug"):
485 if debug in sys.argv:
486 logger.setLevel(logging.DEBUG)
487 sys.argv.remove(debug)
488
489 for quiet in ("-q", "--quiet"):
490 if quiet in sys.argv:
491 logger.setLevel(logging.ERROR)
492 sys.argv.remove(quiet)
493
Andrew Geisslerc182c622020-05-15 14:13:32 -0500494 if 'gl' not in sys.argv[1:] and 'gl-es' not in sys.argv[1:]:
Patrick Williams2390b1b2022-11-03 13:47:49 -0500495 self.qemu_environ['SDL_RENDER_DRIVER'] = 'software'
496 self.qemu_environ['SDL_FRAMEBUFFER_ACCELERATION'] = 'false'
Andrew Geisslerc182c622020-05-15 14:13:32 -0500497
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600498 unknown_arg = ""
499 for arg in sys.argv[1:]:
Brad Bishop15ae2502019-06-18 21:44:24 -0400500 if arg in self.fstypes + self.vmtypes + self.wictypes:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600501 self.check_arg_fstype(arg)
502 elif arg == 'nographic':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500503 self.nographic = True
Patrick Williams8e7b46e2023-05-01 14:19:06 -0500504 elif arg == "nonetwork":
505 self.nonetwork = True
Brad Bishop19323692019-04-05 15:28:33 -0400506 elif arg == 'sdl':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500507 self.sdl = True
Brad Bishopa34c0302019-09-23 22:34:48 -0400508 elif arg == 'gtk':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500509 self.gtk = True
510 elif arg == 'gl':
511 self.gl = True
Patrick Williams2390b1b2022-11-03 13:47:49 -0500512 elif arg == 'gl-es':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500513 self.gl_es = True
Brad Bishop19323692019-04-05 15:28:33 -0400514 elif arg == 'egl-headless':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500515 self.egl_headless = True
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600516 elif arg == 'novga':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500517 self.novga = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600518 elif arg == 'serial':
Brad Bishop19323692019-04-05 15:28:33 -0400519 self.serialconsole = True
520 elif arg == "serialstdio":
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600521 self.serialstdio = True
522 elif arg == 'audio':
523 logger.info("Enabling audio in qemu")
524 logger.info("Please install sound drivers in linux host")
525 self.audio_enabled = True
526 elif arg == 'kvm':
527 self.kvm_enabled = True
528 elif arg == 'kvm-vhost':
529 self.vhost_enabled = True
530 elif arg == 'slirp':
531 self.slirp_enabled = True
Andrew Geissler82c905d2020-04-13 13:39:40 -0500532 elif arg.startswith('bridge='):
533 self.net_bridge = '%s' % arg[len('bridge='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600534 elif arg == 'snapshot':
535 self.snapshot = True
536 elif arg == 'publicvnc':
Patrick Williams03907ee2022-05-01 06:28:52 -0500537 self.publicvnc = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600538 self.qemu_opt_script += ' -vnc :0'
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500539 elif arg == 'guestagent':
540 self.guest_agent = True
Patrick Williams169d7bc2024-01-05 11:33:25 -0600541 elif arg == "qmp":
542 self.qmp = "unix:qmp.sock"
543 elif arg.startswith("qmp="):
544 self.qmp = arg[len('qmp='):]
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500545 elif arg.startswith('guestagent-sockpath='):
546 self.guest_agent_sockpath = '%s' % arg[len('guestagent-sockpath='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600547 elif arg.startswith('tcpserial='):
Brad Bishop15ae2502019-06-18 21:44:24 -0400548 self.tcpserial_portnum = '%s' % arg[len('tcpserial='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600549 elif arg.startswith('qemuparams='):
Andrew Geissler99467da2019-02-25 18:54:23 -0600550 self.qemuparams = ' %s' % arg[len('qemuparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600551 elif arg.startswith('bootparams='):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500552 self.bootparams = arg[len('bootparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600553 elif os.path.exists(arg) or (re.search(':', arg) and re.search('/', arg)):
554 self.check_arg_path(os.path.abspath(arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500555 elif re.search(r'-image-|-image$', arg):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600556 # Lazy rootfs
557 self.rootfs = arg
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500558 elif arg.startswith('ovmf'):
559 self.ovmf_bios.append(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600560 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500561 # At last, assume it is the MACHINE
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600562 if (not unknown_arg) or unknown_arg == arg:
563 unknown_arg = arg
564 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500565 raise RunQemuError("Can't handle two unknown args: %s %s\n"
566 "Try 'runqemu help' on how to use it" % \
567 (unknown_arg, arg))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600568 # Check to make sure it is a valid machine
Brad Bishopa5c52ff2018-11-23 10:55:50 +1300569 if unknown_arg and self.get('MACHINE') != unknown_arg:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500570 if self.get('DEPLOY_DIR_IMAGE'):
571 machine = os.path.basename(self.get('DEPLOY_DIR_IMAGE'))
572 if unknown_arg == machine:
573 self.set("MACHINE", machine)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500574
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600575 self.check_arg_machine(unknown_arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500576
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500577 if not (self.get('DEPLOY_DIR_IMAGE') or self.qbconfload):
Patrick Williams8e7b46e2023-05-01 14:19:06 -0500578 self.load_bitbake_env(target=self.rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500579 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
580 if s:
581 self.set("DEPLOY_DIR_IMAGE", s.group(1))
582
Patrick Williams8e7b46e2023-05-01 14:19:06 -0500583 if not self.get('IMAGE_LINK_NAME') and self.rootfs:
584 s = re.search('^IMAGE_LINK_NAME="(.*)"', self.bitbake_e, re.M)
585 if s:
586 image_link_name = s.group(1)
587 self.set("IMAGE_LINK_NAME", image_link_name)
588 logger.debug('Using IMAGE_LINK_NAME = "%s"' % image_link_name)
589
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600590 def check_kvm(self):
591 """Check kvm and kvm-host"""
592 if not (self.kvm_enabled or self.vhost_enabled):
William A. Kennington IIIac69b482021-06-02 12:28:27 -0700593 self.qemu_opt_script += ' %s %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU'), self.get('QB_SMP'))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600594 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500595
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600596 if not self.get('QB_CPU_KVM'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500597 raise RunQemuError("QB_CPU_KVM is NULL, this board doesn't support kvm")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500598
William A. Kennington IIIac69b482021-06-02 12:28:27 -0700599 self.qemu_opt_script += ' %s %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU_KVM'), self.get('QB_SMP'))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600600 yocto_kvm_wiki = "https://wiki.yoctoproject.org/wiki/How_to_enable_KVM_for_Poky_qemu"
601 yocto_paravirt_kvm_wiki = "https://wiki.yoctoproject.org/wiki/Running_an_x86_Yocto_Linux_image_under_QEMU_KVM"
602 dev_kvm = '/dev/kvm'
603 dev_vhost = '/dev/vhost-net'
Brad Bishop15ae2502019-06-18 21:44:24 -0400604 if self.qemu_system.endswith(('i386', 'x86_64')):
605 with open('/proc/cpuinfo', 'r') as f:
606 kvm_cap = re.search('vmx|svm', "".join(f.readlines()))
607 if not kvm_cap:
608 logger.error("You are trying to enable KVM on a cpu without VT support.")
609 logger.error("Remove kvm from the command-line, or refer:")
610 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500611
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600612 if not os.path.exists(dev_kvm):
613 logger.error("Missing KVM device. Have you inserted kvm modules?")
614 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500615 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500616
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600617 if os.access(dev_kvm, os.W_OK|os.R_OK):
618 self.qemu_opt_script += ' -enable-kvm'
619 else:
620 logger.error("You have no read or write permission on /dev/kvm.")
621 logger.error("Please change the ownership of this file as described at:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500622 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500623
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600624 if self.vhost_enabled:
625 if not os.path.exists(dev_vhost):
626 logger.error("Missing virtio net device. Have you inserted vhost-net module?")
627 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500628 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500629
Andrew Geissler635e0e42020-08-21 15:58:33 -0500630 if not os.access(dev_vhost, os.W_OK|os.R_OK):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600631 logger.error("You have no read or write permission on /dev/vhost-net.")
632 logger.error("Please change the ownership of this file as described at:")
Andrew Geissler635e0e42020-08-21 15:58:33 -0500633 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500634
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600635 def check_fstype(self):
636 """Check and setup FSTYPE"""
637 if not self.fstype:
638 fstype = self.get('QB_DEFAULT_FSTYPE')
639 if fstype:
640 self.fstype = fstype
641 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500642 raise RunQemuError("FSTYPE is NULL!")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500643
Brad Bishop15ae2502019-06-18 21:44:24 -0400644 # parse QB_FSINFO into dict, e.g. { 'wic': ['no-kernel-in-fs', 'a-flag'], 'ext4': ['another-flag']}
645 wic_fs = False
646 qb_fsinfo = self.get('QB_FSINFO')
647 if qb_fsinfo:
648 qb_fsinfo = qb_fsinfo.split()
649 for fsinfo in qb_fsinfo:
650 try:
651 fstype, fsflag = fsinfo.split(':')
652
653 if fstype == 'wic':
654 if fsflag == 'no-kernel-in-fs':
655 wic_fs = True
656 elif fsflag == 'kernel-in-fs':
657 wic_fs = False
658 else:
Andrew Geissler8f840682023-07-21 09:09:43 -0500659 logger.warning('Unknown flag "%s:%s" in QB_FSINFO', fstype, fsflag)
Brad Bishop15ae2502019-06-18 21:44:24 -0400660 continue
661 else:
Andrew Geissler8f840682023-07-21 09:09:43 -0500662 logger.warning('QB_FSINFO is not supported for image type "%s"', fstype)
Brad Bishop15ae2502019-06-18 21:44:24 -0400663 continue
664
665 if fstype in self.fsinfo:
666 self.fsinfo[fstype].append(fsflag)
667 else:
668 self.fsinfo[fstype] = [fsflag]
669 except Exception:
670 logger.error('Invalid parameter "%s" in QB_FSINFO', fsinfo)
671
672 # treat wic images as vmimages (with kernel) or as fsimages (rootfs only)
673 if wic_fs:
674 self.fstypes = self.fstypes + self.wictypes
675 else:
676 self.vmtypes = self.vmtypes + self.wictypes
677
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600678 def check_rootfs(self):
679 """Check and set rootfs"""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500680
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500681 if self.fstype == "none":
682 return
683
684 if self.get('ROOTFS'):
685 if not self.rootfs:
686 self.rootfs = self.get('ROOTFS')
687 elif self.get('ROOTFS') != self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500688 raise RunQemuError("Maybe conflicted ROOTFS: %s vs %s" % (self.get('ROOTFS'), self.rootfs))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500689
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600690 if self.fstype == 'nfs':
691 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500692
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600693 if self.rootfs and not os.path.exists(self.rootfs):
694 # Lazy rootfs
Patrick Williams8e7b46e2023-05-01 14:19:06 -0500695 self.rootfs = "%s/%s.%s" % (self.get('DEPLOY_DIR_IMAGE'),
696 self.get('IMAGE_LINK_NAME'),
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600697 self.fstype)
698 elif not self.rootfs:
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500699 glob_name = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_NAME'), self.fstype)
700 glob_link = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'), self.fstype)
701 globs = (glob_name, glob_link)
702 self.rootfs = get_first_file(globs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600703 if not self.rootfs:
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500704 raise RunQemuError("Failed to find rootfs: %s or %s" % globs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500705
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600706 if not os.path.exists(self.rootfs):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500707 raise RunQemuError("Can't find rootfs: %s" % self.rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500708
Brad Bishop08902b02019-08-20 09:16:51 -0400709 def setup_pkkek1(self):
710 """
711 Extract from PEM certificate the Platform Key and first Key
712 Exchange Key certificate string. The hypervisor needs to provide
713 it in the Type 11 SMBIOS table
714 """
715 pemcert = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), 'OvmfPkKek1.pem')
716 try:
717 with open(pemcert, 'r') as pemfile:
718 key = pemfile.read().replace('\n', ''). \
719 replace('-----BEGIN CERTIFICATE-----', ''). \
720 replace('-----END CERTIFICATE-----', '')
721 self.ovmf_secboot_pkkek1 = key
722
723 except FileNotFoundError:
724 raise RunQemuError("Can't open PEM certificate %s " % pemcert)
725
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500726 def check_ovmf(self):
727 """Check and set full path for OVMF firmware and variable file(s)."""
728
729 for index, ovmf in enumerate(self.ovmf_bios):
730 if os.path.exists(ovmf):
731 continue
732 for suffix in ('qcow2', 'bin'):
733 path = '%s/%s.%s' % (self.get('DEPLOY_DIR_IMAGE'), ovmf, suffix)
734 if os.path.exists(path):
735 self.ovmf_bios[index] = path
Brad Bishop08902b02019-08-20 09:16:51 -0400736 if ovmf.endswith('secboot'):
737 self.setup_pkkek1()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500738 break
739 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500740 raise RunQemuError("Can't find OVMF firmware: %s" % ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500741
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600742 def check_kernel(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400743 """Check and set kernel"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600744 # The vm image doesn't need a kernel
745 if self.fstype in self.vmtypes:
746 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500747
Brad Bishop316dfdd2018-06-25 12:45:53 -0400748 # See if the user supplied a KERNEL option
749 if self.get('KERNEL'):
750 self.kernel = self.get('KERNEL')
751
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500752 # QB_DEFAULT_KERNEL is always a full file path
753 kernel_name = os.path.basename(self.get('QB_DEFAULT_KERNEL'))
754
755 # The user didn't want a kernel to be loaded
Brad Bishop316dfdd2018-06-25 12:45:53 -0400756 if kernel_name == "none" and not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500757 return
758
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600759 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
760 if not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500761 kernel_match_name = "%s/%s" % (deploy_dir_image, kernel_name)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600762 kernel_match_link = "%s/%s" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
763 kernel_startswith = "%s/%s*" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500764 globs = (kernel_match_name, kernel_match_link, kernel_startswith)
765 self.kernel = get_first_file(globs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600766 if not self.kernel:
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500767 raise RunQemuError('KERNEL not found: %s, %s or %s' % globs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500768
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600769 if not os.path.exists(self.kernel):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500770 raise RunQemuError("KERNEL %s not found" % self.kernel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500771
Brad Bishop316dfdd2018-06-25 12:45:53 -0400772 def check_dtb(self):
773 """Check and set dtb"""
774 # Did the user specify a device tree?
775 if self.get('DEVICE_TREE'):
776 self.dtb = self.get('DEVICE_TREE')
777 if not os.path.exists(self.dtb):
778 raise RunQemuError('Specified DTB not found: %s' % self.dtb)
779 return
780
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600781 dtb = self.get('QB_DTB')
782 if dtb:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400783 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500784 glob_match = "%s/%s" % (deploy_dir_image, dtb)
785 glob_startswith = "%s/%s*" % (deploy_dir_image, dtb)
786 glob_wild = "%s/*.dtb" % deploy_dir_image
787 globs = (glob_match, glob_startswith, glob_wild)
788 self.dtb = get_first_file(globs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600789 if not os.path.exists(self.dtb):
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500790 raise RunQemuError('DTB not found: %s, %s or %s' % globs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500791
Brad Bishopc68388fc2019-08-26 01:33:31 -0400792 def check_bios(self):
793 """Check and set bios"""
794
795 # See if the user supplied a BIOS option
796 if self.get('BIOS'):
797 self.bios = self.get('BIOS')
798
799 # QB_DEFAULT_BIOS is always a full file path
800 bios_name = os.path.basename(self.get('QB_DEFAULT_BIOS'))
801
802 # The user didn't want a bios to be loaded
803 if (bios_name == "" or bios_name == "none") and not self.bios:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600804 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500805
Brad Bishopc68388fc2019-08-26 01:33:31 -0400806 if not self.bios:
807 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
808 self.bios = "%s/%s" % (deploy_dir_image, bios_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500809
Brad Bishopc68388fc2019-08-26 01:33:31 -0400810 if not self.bios:
811 raise RunQemuError('BIOS not found: %s' % bios_match_name)
812
813 if not os.path.exists(self.bios):
Patrick Williams213cb262021-08-07 19:21:33 -0500814 raise RunQemuError("BIOS %s not found" % self.bios)
Brad Bishopc68388fc2019-08-26 01:33:31 -0400815
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500816
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600817 def check_mem(self):
Andrew Geissler99467da2019-02-25 18:54:23 -0600818 """
819 Both qemu and kernel needs memory settings, so check QB_MEM and set it
820 for both.
821 """
822 s = re.search('-m +([0-9]+)', self.qemuparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600823 if s:
824 self.set('QB_MEM', '-m %s' % s.group(1))
825 elif not self.get('QB_MEM'):
Brad Bishop79641f22019-09-10 07:20:22 -0400826 logger.info('QB_MEM is not set, use 256M by default')
827 self.set('QB_MEM', '-m 256')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500828
Andrew Geissler99467da2019-02-25 18:54:23 -0600829 # Check and remove M or m suffix
830 qb_mem = self.get('QB_MEM')
831 if qb_mem.endswith('M') or qb_mem.endswith('m'):
832 qb_mem = qb_mem[:-1]
833
834 # Add -m prefix it not present
835 if not qb_mem.startswith('-m'):
836 qb_mem = '-m %s' % qb_mem
837
838 self.set('QB_MEM', qb_mem)
839
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800840 mach = self.get('MACHINE')
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500841 if not mach.startswith(('qemumips', 'qemux86', 'qemuloongarch64')):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800842 self.kernel_cmdline_script += ' mem=%s' % self.get('QB_MEM').replace('-m','').strip() + 'M'
843
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600844 self.qemu_opt_script += ' %s' % self.get('QB_MEM')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500845
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600846 def check_tcpserial(self):
847 if self.tcpserial_portnum:
Brad Bishop15ae2502019-06-18 21:44:24 -0400848 ports = self.tcpserial_portnum.split(':')
849 port = ports[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600850 if self.get('QB_TCPSERIAL_OPT'):
Brad Bishop15ae2502019-06-18 21:44:24 -0400851 self.qemu_opt_script += ' ' + self.get('QB_TCPSERIAL_OPT').replace('@PORT@', port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600852 else:
Andrew Geissler20137392023-10-12 04:59:14 -0600853 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s,nodelay=on' % port
Brad Bishop15ae2502019-06-18 21:44:24 -0400854
855 if len(ports) > 1:
856 for port in ports[1:]:
Andrew Geissler20137392023-10-12 04:59:14 -0600857 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s,nodelay=on' % port
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500858
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600859 def check_and_set(self):
860 """Check configs sanity and set when needed"""
861 self.validate_paths()
Andrew Geissler82c905d2020-04-13 13:39:40 -0500862 if not self.slirp_enabled and not self.net_bridge:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500863 check_tun()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600864 # Check audio
865 if self.audio_enabled:
866 if not self.get('QB_AUDIO_DRV'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500867 raise RunQemuError("QB_AUDIO_DRV is NULL, this board doesn't support audio")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600868 if not self.get('QB_AUDIO_OPT'):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800869 logger.warning('QB_AUDIO_OPT is NULL, you may need define it to make audio work')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600870 else:
871 self.qemu_opt_script += ' %s' % self.get('QB_AUDIO_OPT')
872 os.putenv('QEMU_AUDIO_DRV', self.get('QB_AUDIO_DRV'))
873 else:
874 os.putenv('QEMU_AUDIO_DRV', 'none')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500875
Brad Bishop15ae2502019-06-18 21:44:24 -0400876 self.check_qemu_system()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600877 self.check_kvm()
878 self.check_fstype()
879 self.check_rootfs()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500880 self.check_ovmf()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600881 self.check_kernel()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400882 self.check_dtb()
Brad Bishopc68388fc2019-08-26 01:33:31 -0400883 self.check_bios()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600884 self.check_mem()
885 self.check_tcpserial()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500886
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600887 def read_qemuboot(self):
888 if not self.qemuboot:
889 if self.get('DEPLOY_DIR_IMAGE'):
890 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600891 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800892 logger.warning("Can't find qemuboot conf file, DEPLOY_DIR_IMAGE is NULL!")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600893 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500894
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600895 if self.rootfs and not os.path.exists(self.rootfs):
896 # Lazy rootfs
897 machine = self.get('MACHINE')
898 if not machine:
899 machine = os.path.basename(deploy_dir_image)
Patrick Williams8e7b46e2023-05-01 14:19:06 -0500900 if not self.get('IMAGE_LINK_NAME'):
901 raise RunQemuError("IMAGE_LINK_NAME wasn't set to find corresponding .qemuboot.conf file")
902 self.qemuboot = "%s/%s.qemuboot.conf" % (deploy_dir_image,
903 self.get('IMAGE_LINK_NAME'))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600904 else:
905 cmd = 'ls -t %s/*.qemuboot.conf' % deploy_dir_image
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500906 logger.debug('Running %s...' % cmd)
907 try:
908 qbs = subprocess.check_output(cmd, shell=True).decode('utf-8')
909 except subprocess.CalledProcessError as err:
910 raise RunQemuError(err)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600911 if qbs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500912 for qb in qbs.split():
913 # Don't use initramfs when other choices unless fstype is ramfs
914 if '-initramfs-' in os.path.basename(qb) and self.fstype != 'cpio.gz':
915 continue
916 self.qemuboot = qb
917 break
918 if not self.qemuboot:
919 # Use the first one when no choice
920 self.qemuboot = qbs.split()[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600921 self.qbconfload = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500922
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600923 if not self.qemuboot:
924 # If we haven't found a .qemuboot.conf at this point it probably
925 # doesn't exist, continue without
926 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500927
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600928 if not os.path.exists(self.qemuboot):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500929 raise RunQemuError("Failed to find %s (wrong image name or BSP does not support running under qemu?)." % self.qemuboot)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500930
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500931 logger.debug('CONFFILE: %s' % self.qemuboot)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500932
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600933 cf = configparser.ConfigParser()
934 cf.read(self.qemuboot)
935 for k, v in cf.items('config_bsp'):
936 k_upper = k.upper()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500937 if v.startswith("../"):
938 v = os.path.abspath(os.path.dirname(self.qemuboot) + "/" + v)
939 elif v == ".":
940 v = os.path.dirname(self.qemuboot)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600941 self.set(k_upper, v)
942
943 def validate_paths(self):
944 """Ensure all relevant path variables are set"""
945 # When we're started with a *.qemuboot.conf arg assume that image
946 # artefacts are relative to that file, rather than in whatever
947 # directory DEPLOY_DIR_IMAGE in the conf file points to.
948 if self.qbconfload:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500949 imgdir = os.path.realpath(os.path.dirname(self.qemuboot))
950 if imgdir != os.path.realpath(self.get('DEPLOY_DIR_IMAGE')):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600951 logger.info('Setting DEPLOY_DIR_IMAGE to folder containing %s (%s)' % (self.qemuboot, imgdir))
952 self.set('DEPLOY_DIR_IMAGE', imgdir)
953
954 # If the STAGING_*_NATIVE directories from the config file don't exist
955 # and we're in a sourced OE build directory try to extract the paths
956 # from `bitbake -e`
957 havenative = os.path.exists(self.get('STAGING_DIR_NATIVE')) and \
958 os.path.exists(self.get('STAGING_BINDIR_NATIVE'))
959
960 if not havenative:
961 if not self.bitbake_e:
962 self.load_bitbake_env()
963
964 if self.bitbake_e:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500965 native_vars = ['STAGING_DIR_NATIVE']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600966 for nv in native_vars:
967 s = re.search('^%s="(.*)"' % nv, self.bitbake_e, re.M)
968 if s and s.group(1) != self.get(nv):
969 logger.info('Overriding conf file setting of %s to %s from Bitbake environment' % (nv, s.group(1)))
970 self.set(nv, s.group(1))
971 else:
972 # when we're invoked from a running bitbake instance we won't
973 # be able to call `bitbake -e`, then try:
974 # - get OE_TMPDIR from environment and guess paths based on it
975 # - get OECORE_NATIVE_SYSROOT from environment (for sdk)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500976 tmpdir = self.get('OE_TMPDIR')
977 oecore_native_sysroot = self.get('OECORE_NATIVE_SYSROOT')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600978 if tmpdir:
979 logger.info('Setting STAGING_DIR_NATIVE and STAGING_BINDIR_NATIVE relative to OE_TMPDIR (%s)' % tmpdir)
980 hostos, _, _, _, machine = os.uname()
981 buildsys = '%s-%s' % (machine, hostos.lower())
982 staging_dir_native = '%s/sysroots/%s' % (tmpdir, buildsys)
983 self.set('STAGING_DIR_NATIVE', staging_dir_native)
984 elif oecore_native_sysroot:
985 logger.info('Setting STAGING_DIR_NATIVE to OECORE_NATIVE_SYSROOT (%s)' % oecore_native_sysroot)
986 self.set('STAGING_DIR_NATIVE', oecore_native_sysroot)
987 if self.get('STAGING_DIR_NATIVE'):
988 # we have to assume that STAGING_BINDIR_NATIVE is at usr/bin
989 staging_bindir_native = '%s/usr/bin' % self.get('STAGING_DIR_NATIVE')
990 logger.info('Setting STAGING_BINDIR_NATIVE to %s' % staging_bindir_native)
991 self.set('STAGING_BINDIR_NATIVE', '%s/usr/bin' % self.get('STAGING_DIR_NATIVE'))
992
993 def print_config(self):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500994 logoutput = ['Continuing with the following parameters:']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600995 if not self.fstype in self.vmtypes:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500996 logoutput.append('KERNEL: [%s]' % self.kernel)
Brad Bishopc68388fc2019-08-26 01:33:31 -0400997 if self.bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500998 logoutput.append('BIOS: [%s]' % self.bios)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600999 if self.dtb:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001000 logoutput.append('DTB: [%s]' % self.dtb)
1001 logoutput.append('MACHINE: [%s]' % self.get('MACHINE'))
Brad Bishop15ae2502019-06-18 21:44:24 -04001002 try:
1003 fstype_flags = ' (' + ', '.join(self.fsinfo[self.fstype]) + ')'
1004 except KeyError:
1005 fstype_flags = ''
Andrew Geissler82c905d2020-04-13 13:39:40 -05001006 logoutput.append('FSTYPE: [%s%s]' % (self.fstype, fstype_flags))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001007 if self.fstype == 'nfs':
Andrew Geissler82c905d2020-04-13 13:39:40 -05001008 logoutput.append('NFS_DIR: [%s]' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001009 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001010 logoutput.append('ROOTFS: [%s]' % self.rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001011 if self.ovmf_bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001012 logoutput.append('OVMF: %s' % self.ovmf_bios)
Brad Bishop08902b02019-08-20 09:16:51 -04001013 if (self.ovmf_secboot_pkkek1):
Andrew Geissler82c905d2020-04-13 13:39:40 -05001014 logoutput.append('SECBOOT PKKEK1: [%s...]' % self.ovmf_secboot_pkkek1[0:100])
1015 logoutput.append('CONFFILE: [%s]' % self.qemuboot)
1016 logoutput.append('')
1017 logger.info('\n'.join(logoutput))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001018
1019 def setup_nfs(self):
1020 if not self.nfs_server:
1021 if self.slirp_enabled:
1022 self.nfs_server = '10.0.2.2'
1023 else:
Andrew Geissler517393d2023-01-13 08:55:19 -06001024 self.nfs_server = '192.168.7.@GATEWAY@'
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001025
Patrick Williams520786c2023-06-25 16:20:36 -05001026 nfsd_port = 3048 + self.nfs_instance
1027 lockdir = "/tmp/qemu-port-locks"
1028 self.make_lock_dir(lockdir)
1029 while not self.check_free_port('localhost', nfsd_port, lockdir):
1030 self.nfs_instance += 1
1031 nfsd_port += 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001032
Patrick Williams520786c2023-06-25 16:20:36 -05001033 mountd_port = nfsd_port
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001034 # Export vars for runqemu-export-rootfs
1035 export_dict = {
1036 'NFS_INSTANCE': self.nfs_instance,
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001037 'NFSD_PORT': nfsd_port,
1038 'MOUNTD_PORT': mountd_port,
1039 }
1040 for k, v in export_dict.items():
1041 # Use '%s' since they are integers
1042 os.putenv(k, '%s' % v)
1043
Andrew Geisslerc5535c92023-01-27 16:10:19 -06001044 qb_nfsrootfs_extra_opt = self.get("QB_NFSROOTFS_EXTRA_OPT")
1045 if qb_nfsrootfs_extra_opt and not qb_nfsrootfs_extra_opt.startswith(","):
1046 qb_nfsrootfs_extra_opt = "," + qb_nfsrootfs_extra_opt
1047
1048 self.unfs_opts="nfsvers=3,port=%s,tcp,mountport=%s%s" % (nfsd_port, mountd_port, qb_nfsrootfs_extra_opt)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001049
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001050 # Extract .tar.bz2 or .tar.bz if no nfs dir
1051 if not (self.rootfs and os.path.isdir(self.rootfs)):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001052 src_prefix = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'))
1053 dest = "%s-nfsroot" % src_prefix
1054 if os.path.exists('%s.pseudo_state' % dest):
1055 logger.info('Use %s as NFS_DIR' % dest)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001056 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001057 else:
1058 src = ""
1059 src1 = '%s.tar.bz2' % src_prefix
1060 src2 = '%s.tar.gz' % src_prefix
1061 if os.path.exists(src1):
1062 src = src1
1063 elif os.path.exists(src2):
1064 src = src2
1065 if not src:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001066 raise RunQemuError("No NFS_DIR is set, and can't find %s or %s to extract" % (src1, src2))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001067 logger.info('NFS_DIR not found, extracting %s to %s' % (src, dest))
Brad Bishop977dc1a2019-02-06 16:01:43 -05001068 cmd = ('runqemu-extract-sdk', src, dest)
1069 logger.info('Running %s...' % str(cmd))
1070 if subprocess.call(cmd) != 0:
Andrew Geisslerfc113ea2023-03-31 09:59:46 -05001071 raise RunQemuError('Failed to run %s' % str(cmd))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001072 self.rootfs = dest
Andrew Geisslerc926e172021-05-07 16:11:35 -05001073 self.cleanup_files.append(self.rootfs)
1074 self.cleanup_files.append('%s.pseudo_state' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001075
1076 # Start the userspace NFS server
Brad Bishop977dc1a2019-02-06 16:01:43 -05001077 cmd = ('runqemu-export-rootfs', 'start', self.rootfs)
1078 logger.info('Running %s...' % str(cmd))
1079 if subprocess.call(cmd) != 0:
Andrew Geisslerfc113ea2023-03-31 09:59:46 -05001080 raise RunQemuError('Failed to run %s' % str(cmd))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001081
1082 self.nfs_running = True
1083
Andrew Geissler517393d2023-01-13 08:55:19 -06001084 def setup_cmd(self):
1085 cmd = self.get('QB_SETUP_CMD')
1086 if cmd != '':
1087 logger.info('Running setup command %s' % str(cmd))
1088 if subprocess.call(cmd, shell=True) != 0:
Andrew Geisslerfc113ea2023-03-31 09:59:46 -05001089 raise RunQemuError('Failed to run %s' % str(cmd))
Andrew Geissler517393d2023-01-13 08:55:19 -06001090
Andrew Geissler82c905d2020-04-13 13:39:40 -05001091 def setup_net_bridge(self):
1092 self.set('NETWORK_CMD', '-netdev bridge,br=%s,id=net0,helper=%s -device virtio-net-pci,netdev=net0 ' % (
1093 self.net_bridge, os.path.join(self.bindir_native, 'qemu-oe-bridge-helper')))
1094
Patrick Williams520786c2023-06-25 16:20:36 -05001095 def make_lock_dir(self, lockdir):
1096 if not os.path.exists(lockdir):
1097 # There might be a race issue when multi runqemu processess are
1098 # running at the same time.
1099 try:
1100 os.mkdir(lockdir)
1101 os.chmod(lockdir, 0o777)
1102 except FileExistsError:
1103 pass
1104 return
1105
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001106 def setup_slirp(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001107 """Setup user networking"""
1108
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001109 if self.fstype == 'nfs':
1110 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001111 netconf = " " + self.cmdline_ip_slirp
1112 logger.info("Network configuration:%s", netconf)
1113 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001114 # Port mapping
Andrew Geissler517393d2023-01-13 08:55:19 -06001115 hostfwd = ",hostfwd=tcp:127.0.0.1:2222-:22,hostfwd=tcp:127.0.0.1:2323-:23"
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001116 qb_slirp_opt_default = "-netdev user,id=net0%s,tftp=%s" % (hostfwd, self.get('DEPLOY_DIR_IMAGE'))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001117 qb_slirp_opt = self.get('QB_SLIRP_OPT') or qb_slirp_opt_default
1118 # Figure out the port
1119 ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt)
1120 ports = [int(i) for i in ports]
1121 mac = 2
Brad Bishop08902b02019-08-20 09:16:51 -04001122
1123 lockdir = "/tmp/qemu-port-locks"
Patrick Williams520786c2023-06-25 16:20:36 -05001124 self.make_lock_dir(lockdir)
Brad Bishop08902b02019-08-20 09:16:51 -04001125
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001126 # Find a free port to avoid conflicts
1127 for p in ports[:]:
1128 p_new = p
Brad Bishop08902b02019-08-20 09:16:51 -04001129 while not self.check_free_port('localhost', p_new, lockdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001130 p_new += 1
1131 mac += 1
1132 while p_new in ports:
1133 p_new += 1
1134 mac += 1
1135 if p != p_new:
1136 ports.append(p_new)
1137 qb_slirp_opt = re.sub(':%s-' % p, ':%s-' % p_new, qb_slirp_opt)
1138 logger.info("Port forward changed: %s -> %s" % (p, p_new))
1139 mac = "%s%02x" % (self.mac_slirp, mac)
1140 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qb_slirp_opt))
1141 # Print out port foward
1142 hostfwd = re.findall('(hostfwd=[^,]*)', qb_slirp_opt)
1143 if hostfwd:
1144 logger.info('Port forward: %s' % ' '.join(hostfwd))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001145
1146 def setup_tap(self):
1147 """Setup tap"""
1148
1149 # This file is created when runqemu-gen-tapdevs creates a bank of tap
1150 # devices, indicating that the user should not bring up new ones using
1151 # sudo.
1152 nosudo_flag = '/etc/runqemu-nosudo'
1153 self.qemuifup = shutil.which('runqemu-ifup')
1154 self.qemuifdown = shutil.which('runqemu-ifdown')
1155 ip = shutil.which('ip')
1156 lockdir = "/tmp/qemu-tap-locks"
1157
1158 if not (self.qemuifup and self.qemuifdown and ip):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001159 logger.error("runqemu-ifup: %s" % self.qemuifup)
1160 logger.error("runqemu-ifdown: %s" % self.qemuifdown)
1161 logger.error("ip: %s" % ip)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001162 raise OEPathError("runqemu-ifup, runqemu-ifdown or ip not found")
1163
Patrick Williams520786c2023-06-25 16:20:36 -05001164 self.make_lock_dir(lockdir)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001165
Brad Bishop977dc1a2019-02-06 16:01:43 -05001166 cmd = (ip, 'link')
1167 logger.debug('Running %s...' % str(cmd))
1168 ip_link = subprocess.check_output(cmd).decode('utf-8')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001169 # Matches line like: 6: tap0: <foo>
Patrick Williams520786c2023-06-25 16:20:36 -05001170 oe_tap_name = 'tap'
1171 if 'OE_TAP_NAME' in os.environ:
1172 oe_tap_name = os.environ['OE_TAP_NAME']
1173 tap_re = '^[0-9]+: +(' + oe_tap_name + '[0-9]+): <.*'
1174 possibles = re.findall(tap_re, ip_link, re.M)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001175 tap = ""
1176 for p in possibles:
1177 lockfile = os.path.join(lockdir, p)
1178 if os.path.exists('%s.skip' % lockfile):
1179 logger.info('Found %s.skip, skipping %s' % (lockfile, p))
1180 continue
Brad Bishop08902b02019-08-20 09:16:51 -04001181 self.taplock = lockfile + '.lock'
1182 if self.acquire_taplock(error=False):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001183 tap = p
1184 logger.info("Using preconfigured tap device %s" % tap)
1185 logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap))
1186 break
1187
1188 if not tap:
1189 if os.path.exists(nosudo_flag):
1190 logger.error("Error: There are no available tap devices to use for networking,")
1191 logger.error("and I see %s exists, so I am not going to try creating" % nosudo_flag)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001192 raise RunQemuError("a new one with sudo.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001193
1194 gid = os.getgid()
1195 uid = os.getuid()
1196 logger.info("Setting up tap interface under sudo")
Andrew Geissler8f840682023-07-21 09:09:43 -05001197 cmd = ('sudo', self.qemuifup, str(gid))
Andrew Geissler82c905d2020-04-13 13:39:40 -05001198 try:
1199 tap = subprocess.check_output(cmd).decode('utf-8').strip()
1200 except subprocess.CalledProcessError as e:
1201 logger.error('Setting up tap device failed:\n%s\nRun runqemu-gen-tapdevs to manually create one.' % str(e))
1202 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001203 lockfile = os.path.join(lockdir, tap)
Brad Bishop08902b02019-08-20 09:16:51 -04001204 self.taplock = lockfile + '.lock'
1205 self.acquire_taplock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001206 self.cleantap = True
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001207 logger.debug('Created tap: %s' % tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001208
1209 if not tap:
1210 logger.error("Failed to setup tap device. Run runqemu-gen-tapdevs to manually create.")
Andrew Geissler82c905d2020-04-13 13:39:40 -05001211 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001212 self.tap = tap
Patrick Williams520786c2023-06-25 16:20:36 -05001213 tapnum = int(tap[len(oe_tap_name):])
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001214 gateway = tapnum * 2 + 1
1215 client = gateway + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001216 if self.fstype == 'nfs':
1217 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001218 netconf = " " + self.cmdline_ip_tap
1219 netconf = netconf.replace('@CLIENT@', str(client))
1220 netconf = netconf.replace('@GATEWAY@', str(gateway))
Andrew Geissler517393d2023-01-13 08:55:19 -06001221 self.nfs_server = self.nfs_server.replace('@GATEWAY@', str(gateway))
Andrew Geissler82c905d2020-04-13 13:39:40 -05001222 logger.info("Network configuration:%s", netconf)
1223 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001224 mac = "%s%02x" % (self.mac_tap, client)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001225 qb_tap_opt = self.get('QB_TAP_OPT')
1226 if qb_tap_opt:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001227 qemu_tap_opt = qb_tap_opt.replace('@TAP@', tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001228 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001229 qemu_tap_opt = "-netdev tap,id=net0,ifname=%s,script=no,downscript=no" % (self.tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001230
1231 if self.vhost_enabled:
1232 qemu_tap_opt += ',vhost=on'
1233
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001234 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qemu_tap_opt))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001235
1236 def setup_network(self):
Patrick Williams8e7b46e2023-05-01 14:19:06 -05001237 if self.nonetwork or self.get('QB_NET') == 'none':
1238 self.set('NETWORK_CMD', '-nic none')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001239 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001240 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001241 self.saved_stty = subprocess.check_output(("stty", "-g")).decode('utf-8').strip()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001242 self.network_device = self.get('QB_NETWORK_DEVICE') or self.network_device
Andrew Geissler82c905d2020-04-13 13:39:40 -05001243 if self.net_bridge:
1244 self.setup_net_bridge()
1245 elif self.slirp_enabled:
1246 self.cmdline_ip_slirp = self.get('QB_CMDLINE_IP_SLIRP') or self.cmdline_ip_slirp
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001247 self.setup_slirp()
1248 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001249 self.cmdline_ip_tap = self.get('QB_CMDLINE_IP_TAP') or self.cmdline_ip_tap
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001250 self.setup_tap()
1251
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001252 def setup_rootfs(self):
1253 if self.get('QB_ROOTFS') == 'none':
1254 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001255 if 'wic.' in self.fstype:
1256 self.fstype = self.fstype[4:]
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001257 rootfs_format = self.fstype if self.fstype in ('vmdk', 'vhd', 'vhdx', 'qcow2', 'vdi') else 'raw'
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001258
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001259 tmpfsdir = os.environ.get("RUNQEMU_TMPFS_DIR", None)
1260 if self.snapshot and tmpfsdir:
1261 newrootfs = os.path.join(tmpfsdir, os.path.basename(self.rootfs)) + "." + str(os.getpid())
Andrew Geissler09036742021-06-25 14:25:14 -05001262 logger.info("Copying rootfs to %s" % newrootfs)
1263 copy_start = time.time()
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001264 shutil.copyfile(self.rootfs, newrootfs)
Andrew Geissler09036742021-06-25 14:25:14 -05001265 logger.info("Copy done in %s seconds" % (time.time() - copy_start))
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001266 self.rootfs = newrootfs
1267 # Don't need a second copy now!
1268 self.snapshot = False
Andrew Geisslerc926e172021-05-07 16:11:35 -05001269 self.cleanup_files.append(newrootfs)
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001270
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001271 qb_rootfs_opt = self.get('QB_ROOTFS_OPT')
1272 if qb_rootfs_opt:
1273 self.rootfs_options = qb_rootfs_opt.replace('@ROOTFS@', self.rootfs)
1274 else:
1275 self.rootfs_options = '-drive file=%s,if=virtio,format=%s' % (self.rootfs, rootfs_format)
1276
Andrew Geissler82c905d2020-04-13 13:39:40 -05001277 qb_rootfs_extra_opt = self.get("QB_ROOTFS_EXTRA_OPT")
1278 if qb_rootfs_extra_opt and not qb_rootfs_extra_opt.startswith(","):
1279 qb_rootfs_extra_opt = "," + qb_rootfs_extra_opt
1280
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001281 if self.fstype in ('cpio.gz', 'cpio'):
1282 self.kernel_cmdline = 'root=/dev/ram0 rw debugshell'
1283 self.rootfs_options = '-initrd %s' % self.rootfs
1284 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001285 vm_drive = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001286 if self.fstype in self.vmtypes:
1287 if self.fstype == 'iso':
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001288 vm_drive = '-drive file=%s,if=virtio,media=cdrom' % self.rootfs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001289 elif self.get('QB_DRIVE_TYPE'):
1290 drive_type = self.get('QB_DRIVE_TYPE')
1291 if drive_type.startswith("/dev/sd"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001292 logger.info('Using scsi drive')
Andrew Geissler82c905d2020-04-13 13:39:40 -05001293 vm_drive = '-drive if=none,id=hd,file=%s,format=%s -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd%s' \
1294 % (self.rootfs, rootfs_format, qb_rootfs_extra_opt)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001295 elif drive_type.startswith("/dev/hd"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001296 logger.info('Using ide drive')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001297 vm_drive = "-drive file=%s,format=%s" % (self.rootfs, rootfs_format)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001298 elif drive_type.startswith("/dev/vdb"):
1299 logger.info('Using block virtio drive');
1300 vm_drive = '-drive id=disk0,file=%s,if=none,format=%s -device virtio-blk-device,drive=disk0%s' \
1301 % (self.rootfs, rootfs_format,qb_rootfs_extra_opt)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001302 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001303 # virtio might have been selected explicitly (just use it), or
1304 # is used as fallback (then warn about that).
1305 if not drive_type.startswith("/dev/vd"):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001306 logger.warning("Unknown QB_DRIVE_TYPE: %s" % drive_type)
1307 logger.warning("Failed to figure out drive type, consider define or fix QB_DRIVE_TYPE")
1308 logger.warning('Trying to use virtio block drive')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001309 vm_drive = '-drive if=virtio,file=%s,format=%s' % (self.rootfs, rootfs_format)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001310
1311 # All branches above set vm_drive.
Andrew Geissler475cb722020-07-10 16:00:51 -05001312 self.rootfs_options = vm_drive
1313 if not self.fstype in self.vmtypes:
1314 self.rootfs_options += ' -no-reboot'
Andrew Geissler595f6302022-01-24 19:11:47 +00001315
1316 # By default, ' rw' is appended to QB_KERNEL_ROOT unless either ro or rw is explicitly passed.
1317 qb_kernel_root = self.get('QB_KERNEL_ROOT')
1318 qb_kernel_root_l = qb_kernel_root.split()
1319 if not ('ro' in qb_kernel_root_l or 'rw' in qb_kernel_root_l):
1320 qb_kernel_root += ' rw'
1321 self.kernel_cmdline = 'root=%s' % qb_kernel_root
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001322
1323 if self.fstype == 'nfs':
1324 self.rootfs_options = ''
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001325 k_root = '/dev/nfs nfsroot=%s:%s,%s' % (self.nfs_server, os.path.abspath(self.rootfs), self.unfs_opts)
Brad Bishopf3f93bb2019-10-16 14:33:32 -04001326 self.kernel_cmdline = 'root=%s rw' % k_root
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001327
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001328 if self.fstype == 'none':
1329 self.rootfs_options = ''
1330
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001331 self.set('ROOTFS_OPTIONS', self.rootfs_options)
1332
1333 def guess_qb_system(self):
1334 """attempt to determine the appropriate qemu-system binary"""
1335 mach = self.get('MACHINE')
1336 if not mach:
Patrick Williams864cc432023-02-09 14:54:44 -06001337 search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemuloongarch64|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*'
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001338 if self.rootfs:
1339 match = re.match(search, self.rootfs)
1340 if match:
1341 mach = match.group(1)
1342 elif self.kernel:
1343 match = re.match(search, self.kernel)
1344 if match:
1345 mach = match.group(1)
1346
1347 if not mach:
1348 return None
1349
1350 if mach == 'qemuarm':
1351 qbsys = 'arm'
1352 elif mach == 'qemuarm64':
1353 qbsys = 'aarch64'
1354 elif mach == 'qemux86':
1355 qbsys = 'i386'
1356 elif mach == 'qemux86-64':
1357 qbsys = 'x86_64'
1358 elif mach == 'qemuppc':
1359 qbsys = 'ppc'
Patrick Williams864cc432023-02-09 14:54:44 -06001360 elif mach == 'qemuloongarch64':
1361 qbsys = 'loongarch64'
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001362 elif mach == 'qemumips':
1363 qbsys = 'mips'
1364 elif mach == 'qemumips64':
1365 qbsys = 'mips64'
1366 elif mach == 'qemumipsel':
1367 qbsys = 'mipsel'
1368 elif mach == 'qemumips64el':
1369 qbsys = 'mips64el'
Brad Bishop316dfdd2018-06-25 12:45:53 -04001370 elif mach == 'qemuriscv64':
1371 qbsys = 'riscv64'
1372 elif mach == 'qemuriscv32':
1373 qbsys = 'riscv32'
Brad Bishop004d4992018-10-02 23:54:45 +02001374 else:
1375 logger.error("Unable to determine QEMU PC System emulator for %s machine." % mach)
1376 logger.error("As %s is not among valid QEMU machines such as," % mach)
1377 logger.error("qemux86-64, qemux86, qemuarm64, qemuarm, qemumips64, qemumips64el, qemumipsel, qemumips, qemuppc")
1378 raise RunQemuError("Set qb_system_name with suitable QEMU PC System emulator in .*qemuboot.conf.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001379
1380 return 'qemu-system-%s' % qbsys
1381
Brad Bishop15ae2502019-06-18 21:44:24 -04001382 def check_qemu_system(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001383 qemu_system = self.get('QB_SYSTEM_NAME')
1384 if not qemu_system:
1385 qemu_system = self.guess_qb_system()
1386 if not qemu_system:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001387 raise RunQemuError("Failed to boot, QB_SYSTEM_NAME is NULL!")
Brad Bishop15ae2502019-06-18 21:44:24 -04001388 self.qemu_system = qemu_system
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001389
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001390 def check_render_nodes(self):
1391 render_hint = """If /dev/dri/renderD* is absent due to lack of suitable GPU, 'modprobe vgem' will create one suitable for mesa llvmpipe software renderer."""
1392 try:
1393 content = os.listdir("/dev/dri")
Andrew Geissler5082cc72023-09-11 08:41:39 -04001394 nodes = [i for i in content if i.startswith('renderD')]
1395 if len(nodes) == 0:
1396 raise RunQemuError("No render nodes found in /dev/dri/: %s. %s" %(content, render_hint))
1397 for n in nodes:
1398 try:
1399 with open(os.path.join("/dev/dri", n), "w") as f:
1400 f.close()
1401 break
1402 except IOError:
1403 pass
1404 else:
1405 raise RunQemuError("None of the render nodes in /dev/dri/ are accessible: %s; you may need to add yourself to 'render' group or otherwise ensure you have read-write permissions on one of them." %(nodes))
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001406 except FileNotFoundError:
1407 raise RunQemuError("/dev/dri directory does not exist; no render nodes available on this machine. %s" %(render_hint))
1408
Andrew Geisslerfc113ea2023-03-31 09:59:46 -05001409 def setup_guest_agent(self):
1410 if self.guest_agent == True:
1411 self.qemu_opt += ' -chardev socket,path=' + self.guest_agent_sockpath + ',server,nowait,id=qga0 '
1412 self.qemu_opt += ' -device virtio-serial '
1413 self.qemu_opt += ' -device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0 '
1414
Patrick Williams169d7bc2024-01-05 11:33:25 -06001415 def setup_qmp(self):
1416 if self.qmp:
1417 self.qemu_opt += " -qmp %s,server,nowait" % self.qmp
1418
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001419 def setup_vga(self):
1420 if self.nographic == True:
1421 if self.sdl == True:
1422 raise RunQemuError('Option nographic makes no sense alongside the sdl option.')
1423 if self.gtk == True:
1424 raise RunQemuError('Option nographic makes no sense alongside the gtk option.')
1425 self.qemu_opt += ' -nographic'
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001426
1427 if self.novga == True:
1428 self.qemu_opt += ' -vga none'
1429 return
1430
1431 if (self.gl_es == True or self.gl == True) and (self.sdl == False and self.gtk == False):
1432 raise RunQemuError('Option gl/gl-es needs gtk or sdl option.')
1433
Patrick Williams03907ee2022-05-01 06:28:52 -05001434 # If we have no display option, we autodetect based upon what qemu supports. We
1435 # need our font setup and show-cusor below so we need to see what qemu --help says
1436 # is supported so we can pass our correct config in.
1437 if not self.nographic and not self.sdl and not self.gtk and not self.publicvnc and not self.egl_headless == True:
Patrick Williams2390b1b2022-11-03 13:47:49 -05001438 output = subprocess.check_output([self.qemu_bin, "--help"], universal_newlines=True, env=self.qemu_environ)
Patrick Williams03907ee2022-05-01 06:28:52 -05001439 if "-display gtk" in output:
1440 self.gtk = True
1441 elif "-display sdl" in output:
1442 self.sdl = True
Andrew Geissler595f6302022-01-24 19:11:47 +00001443 else:
Patrick Williamsdb4c27e2022-08-05 08:10:29 -05001444 self.qemu_opt += ' -display none'
Andrew Geissler595f6302022-01-24 19:11:47 +00001445
Patrick Williams03907ee2022-05-01 06:28:52 -05001446 if self.sdl == True or self.gtk == True or self.egl_headless == True:
1447
1448 if self.qemu_system.endswith(('i386', 'x86_64')):
1449 if self.gl or self.gl_es or self.egl_headless:
1450 self.qemu_opt += ' -device virtio-vga-gl '
1451 else:
1452 self.qemu_opt += ' -device virtio-vga '
1453
1454 self.qemu_opt += ' -display '
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001455 if self.egl_headless == True:
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001456 self.check_render_nodes()
Andrew Geissler595f6302022-01-24 19:11:47 +00001457 self.set_dri_path()
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001458 self.qemu_opt += 'egl-headless,'
1459 else:
1460 if self.sdl == True:
1461 self.qemu_opt += 'sdl,'
1462 elif self.gtk == True:
Patrick Williams2390b1b2022-11-03 13:47:49 -05001463 self.qemu_environ['FONTCONFIG_PATH'] = '/etc/fonts'
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001464 self.qemu_opt += 'gtk,'
1465
1466 if self.gl == True:
Andrew Geissler595f6302022-01-24 19:11:47 +00001467 self.set_dri_path()
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001468 self.qemu_opt += 'gl=on,'
1469 elif self.gl_es == True:
Andrew Geissler595f6302022-01-24 19:11:47 +00001470 self.set_dri_path()
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001471 self.qemu_opt += 'gl=es,'
1472 self.qemu_opt += 'show-cursor=on'
1473
1474 self.qemu_opt += ' %s' %self.get('QB_GRAPHICS')
1475
1476 def setup_serial(self):
1477 # Setup correct kernel command line for serial
Andrew Geissler595f6302022-01-24 19:11:47 +00001478 if self.get('SERIAL_CONSOLES') and (self.serialstdio == True or self.serialconsole == True or self.nographic == True or self.tcpserial_portnum):
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001479 for entry in self.get('SERIAL_CONSOLES').split(' '):
1480 self.kernel_cmdline_script += ' console=%s' %entry.split(';')[1]
1481
Andrew Geissler220dafd2023-10-04 10:18:08 -05001482 # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES).
1483 # If no serial or serialtcp options were specified, only ttyS0 is created
1484 # and sysvinit shows an error trying to enable ttyS1:
1485 # INIT: Id "S1" respawning too fast: disabled for 5 minutes
1486 serial_num = len(re.findall("-serial", self.qemu_opt))
1487
1488 # Assume if the user passed serial options, they know what they want
1489 # and pad to two devices
1490 if serial_num == 1:
1491 self.qemu_opt += " -serial null"
1492 elif serial_num >= 2:
1493 return
1494
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001495 if self.serialstdio == True or self.nographic == True:
1496 self.qemu_opt += " -serial mon:stdio"
1497 else:
1498 self.qemu_opt += " -serial mon:vc"
1499 if self.serialconsole:
1500 if sys.stdin.isatty():
1501 subprocess.check_call(("stty", "intr", "^]"))
1502 logger.info("Interrupt character is '^]'")
1503
1504 self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT")
1505
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001506 serial_num = len(re.findall("-serial", self.qemu_opt))
1507 if serial_num < 2:
1508 self.qemu_opt += " -serial null"
1509
Patrick Williams03907ee2022-05-01 06:28:52 -05001510 def find_qemu(self):
Brad Bishop15ae2502019-06-18 21:44:24 -04001511 qemu_bin = os.path.join(self.bindir_native, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001512
1513 # It is possible to have qemu-native in ASSUME_PROVIDED, and it won't
1514 # find QEMU in sysroot, it needs to use host's qemu.
1515 if not os.path.exists(qemu_bin):
1516 logger.info("QEMU binary not found in %s, trying host's QEMU" % qemu_bin)
1517 for path in (os.environ['PATH'] or '').split(':'):
Brad Bishop15ae2502019-06-18 21:44:24 -04001518 qemu_bin_tmp = os.path.join(path, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001519 logger.info("Trying: %s" % qemu_bin_tmp)
1520 if os.path.exists(qemu_bin_tmp):
1521 qemu_bin = qemu_bin_tmp
1522 if not os.path.isabs(qemu_bin):
1523 qemu_bin = os.path.abspath(qemu_bin)
1524 logger.info("Using host's QEMU: %s" % qemu_bin)
1525 break
1526
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001527 if not os.access(qemu_bin, os.X_OK):
1528 raise OEPathError("No QEMU binary '%s' could be found" % qemu_bin)
Patrick Williams03907ee2022-05-01 06:28:52 -05001529 self.qemu_bin = qemu_bin
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001530
Patrick Williams03907ee2022-05-01 06:28:52 -05001531 def setup_final(self):
1532
1533 self.find_qemu()
1534
1535 self.qemu_opt = "%s %s %s %s %s" % (self.qemu_bin, self.get('NETWORK_CMD'), self.get('QB_RNG'), self.get('ROOTFS_OPTIONS'), self.get('QB_OPT_APPEND').replace('@DEPLOY_DIR_IMAGE@', self.get('DEPLOY_DIR_IMAGE')))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001536
1537 for ovmf in self.ovmf_bios:
1538 format = ovmf.rsplit('.', 1)[-1]
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001539 if format == "bin":
1540 format = "raw"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001541 self.qemu_opt += ' -drive if=pflash,format=%s,file=%s' % (format, ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001542
1543 self.qemu_opt += ' ' + self.qemu_opt_script
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001544
Brad Bishop08902b02019-08-20 09:16:51 -04001545 if self.ovmf_secboot_pkkek1:
1546 # Provide the Platform Key and first Key Exchange Key certificate as an
1547 # OEM string in the SMBIOS Type 11 table. Prepend the certificate string
1548 # with "application prefix" of the EnrollDefaultKeys.efi application
1549 self.qemu_opt += ' -smbios type=11,value=4e32566d-8e9e-4f52-81d3-5bb9715f9727:' \
1550 + self.ovmf_secboot_pkkek1
1551
Andrew Geissler99467da2019-02-25 18:54:23 -06001552 # Append qemuparams to override previous settings
1553 if self.qemuparams:
1554 self.qemu_opt += ' ' + self.qemuparams
1555
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001556 if self.snapshot:
1557 self.qemu_opt += " -snapshot"
1558
Andrew Geisslerfc113ea2023-03-31 09:59:46 -05001559 self.setup_guest_agent()
Patrick Williams169d7bc2024-01-05 11:33:25 -06001560 self.setup_qmp()
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001561 self.setup_serial()
1562 self.setup_vga()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001563
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001564 def start_qemu(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001565 import shlex
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001566 if self.kernel:
Andrew Geissler615f2f12022-07-15 14:00:58 -05001567 kernel_opts = "-kernel %s" % (self.kernel)
1568 if self.get('QB_KERNEL_CMDLINE') == "none":
1569 if self.bootparams:
1570 kernel_opts += " -append '%s'" % (self.bootparams)
1571 else:
1572 kernel_opts += " -append '%s %s %s %s'" % (self.kernel_cmdline,
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001573 self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'),
1574 self.bootparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001575 if self.dtb:
1576 kernel_opts += " -dtb %s" % self.dtb
1577 else:
1578 kernel_opts = ""
Patrick Williams213cb262021-08-07 19:21:33 -05001579
1580 if self.bios:
1581 self.qemu_opt += " -bios %s" % self.bios
1582
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001583 cmd = "%s %s" % (self.qemu_opt, kernel_opts)
Brad Bishop004d4992018-10-02 23:54:45 +02001584 cmds = shlex.split(cmd)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001585 logger.info('Running %s\n' % cmd)
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001586 with open('/proc/uptime', 'r') as f:
1587 uptime_seconds = f.readline().split()[0]
1588 logger.info('Host uptime: %s\n' % uptime_seconds)
Brad Bishopf86d0552018-12-04 14:18:15 -08001589 pass_fds = []
Brad Bishop08902b02019-08-20 09:16:51 -04001590 if self.taplock_descriptor:
1591 pass_fds = [self.taplock_descriptor.fileno()]
1592 if len(self.portlocks):
1593 for descriptor in self.portlocks.values():
1594 pass_fds.append(descriptor.fileno())
Patrick Williams2390b1b2022-11-03 13:47:49 -05001595 process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds, env=self.qemu_environ)
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001596 self.qemuprocess = process
Brad Bishop004d4992018-10-02 23:54:45 +02001597 retcode = process.wait()
1598 if retcode:
1599 if retcode == -signal.SIGTERM:
1600 logger.info("Qemu terminated by SIGTERM")
1601 else:
1602 logger.error("Failed to run qemu: %s", process.stderr.read().decode())
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001603
Andrew Geissler517393d2023-01-13 08:55:19 -06001604 def cleanup_cmd(self):
1605 cmd = self.get('QB_CLEANUP_CMD')
1606 if cmd != '':
1607 logger.info('Running cleanup command %s' % str(cmd))
1608 if subprocess.call(cmd, shell=True) != 0:
Andrew Geisslerfc113ea2023-03-31 09:59:46 -05001609 raise RunQemuError('Failed to run %s' % str(cmd))
Andrew Geissler517393d2023-01-13 08:55:19 -06001610
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001611 def cleanup(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001612 if self.cleaned:
1613 return
1614
1615 # avoid dealing with SIGTERM when cleanup function is running
1616 signal.signal(signal.SIGTERM, signal.SIG_IGN)
1617
1618 logger.info("Cleaning up")
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001619
1620 if self.qemuprocess:
1621 try:
1622 # give it some time to shut down, ignore return values and output
1623 self.qemuprocess.send_signal(signal.SIGTERM)
1624 self.qemuprocess.communicate(timeout=5)
1625 except subprocess.TimeoutExpired:
1626 self.qemuprocess.kill()
1627
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001628 with open('/proc/uptime', 'r') as f:
1629 uptime_seconds = f.readline().split()[0]
1630 logger.info('Host uptime: %s\n' % uptime_seconds)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001631 if self.cleantap:
Andrew Geissler8f840682023-07-21 09:09:43 -05001632 cmd = ('sudo', self.qemuifdown, self.tap)
Brad Bishop977dc1a2019-02-06 16:01:43 -05001633 logger.debug('Running %s' % str(cmd))
1634 subprocess.check_call(cmd)
Brad Bishop08902b02019-08-20 09:16:51 -04001635 self.release_taplock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001636
1637 if self.nfs_running:
1638 logger.info("Shutting down the userspace NFS server...")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001639 cmd = ("runqemu-export-rootfs", "stop", self.rootfs)
1640 logger.debug('Running %s' % str(cmd))
1641 subprocess.check_call(cmd)
Patrick Williams520786c2023-06-25 16:20:36 -05001642 self.release_portlock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001643
1644 if self.saved_stty:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001645 subprocess.check_call(("stty", self.saved_stty))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001646
Andrew Geisslerc926e172021-05-07 16:11:35 -05001647 if self.cleanup_files:
1648 for ent in self.cleanup_files:
1649 logger.info('Removing %s' % ent)
1650 if os.path.isfile(ent):
1651 os.remove(ent)
1652 else:
1653 shutil.rmtree(ent)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001654
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001655 # Deliberately ignore the return code of 'tput smam'.
1656 subprocess.call(["tput", "smam"])
1657
Brad Bishop004d4992018-10-02 23:54:45 +02001658 self.cleaned = True
1659
Patrick Williams8e7b46e2023-05-01 14:19:06 -05001660 def run_bitbake_env(self, mach=None, target=''):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001661 bitbake = shutil.which('bitbake')
1662 if not bitbake:
1663 return
1664
1665 if not mach:
1666 mach = self.get('MACHINE')
1667
Andrew Geissler82c905d2020-04-13 13:39:40 -05001668 multiconfig = self.get('MULTICONFIG')
1669 if multiconfig:
1670 multiconfig = "mc:%s" % multiconfig
1671
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001672 if mach:
Patrick Williams8e7b46e2023-05-01 14:19:06 -05001673 cmd = 'MACHINE=%s bitbake -e %s %s' % (mach, multiconfig, target)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001674 else:
Patrick Williams8e7b46e2023-05-01 14:19:06 -05001675 cmd = 'bitbake -e %s %s' % (multiconfig, target)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001676
1677 logger.info('Running %s...' % cmd)
Patrick Williams8e7b46e2023-05-01 14:19:06 -05001678 try:
1679 return subprocess.check_output(cmd, shell=True).decode('utf-8')
1680 except subprocess.CalledProcessError as err:
1681 logger.warning("Couldn't run '%s' to gather environment information, maybe the target wasn't an image name, will retry with virtual/kernel as a target:\n%s" % (cmd, err.output.decode('utf-8')))
1682 # need something with IMAGE_NAME_SUFFIX/IMAGE_LINK_NAME defined (kernel also inherits image-artifact-names.bbclass)
1683 target = 'virtual/kernel'
1684 if mach:
1685 cmd = 'MACHINE=%s bitbake -e %s %s' % (mach, multiconfig, target)
1686 else:
1687 cmd = 'bitbake -e %s %s' % (multiconfig, target)
1688 try:
1689 return subprocess.check_output(cmd, shell=True).decode('utf-8')
1690 except subprocess.CalledProcessError as err:
1691 logger.warning("Couldn't run '%s' to gather environment information, giving up with 'bitbake -e':\n%s" % (cmd, err.output.decode('utf-8')))
1692 return ''
Andrew Geissler82c905d2020-04-13 13:39:40 -05001693
Patrick Williams8e7b46e2023-05-01 14:19:06 -05001694
1695 def load_bitbake_env(self, mach=None, target=None):
Andrew Geissler82c905d2020-04-13 13:39:40 -05001696 if self.bitbake_e:
1697 return
1698
Patrick Williams8e7b46e2023-05-01 14:19:06 -05001699 self.bitbake_e = self.run_bitbake_env(mach=mach, target=target)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001700
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001701 def validate_combos(self):
1702 if (self.fstype in self.vmtypes) and self.kernel:
1703 raise RunQemuError("%s doesn't need kernel %s!" % (self.fstype, self.kernel))
1704
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001705 @property
1706 def bindir_native(self):
1707 result = self.get('STAGING_BINDIR_NATIVE')
1708 if result and os.path.exists(result):
1709 return result
1710
Andrew Geissler82c905d2020-04-13 13:39:40 -05001711 cmd = ['bitbake', '-e']
1712 multiconfig = self.get('MULTICONFIG')
1713 if multiconfig:
1714 cmd.append('mc:%s:qemu-helper-native' % multiconfig)
1715 else:
1716 cmd.append('qemu-helper-native')
1717
Brad Bishop977dc1a2019-02-06 16:01:43 -05001718 logger.info('Running %s...' % str(cmd))
1719 out = subprocess.check_output(cmd).decode('utf-8')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001720
1721 match = re.search('^STAGING_BINDIR_NATIVE="(.*)"', out, re.M)
1722 if match:
1723 result = match.group(1)
1724 if os.path.exists(result):
1725 self.set('STAGING_BINDIR_NATIVE', result)
1726 return result
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001727 raise RunQemuError("Native sysroot directory %s doesn't exist" % result)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001728 else:
Andrew Geisslerfc113ea2023-03-31 09:59:46 -05001729 raise RunQemuError("Can't find STAGING_BINDIR_NATIVE in '%s' output" % str(cmd))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001730
1731
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001732def main():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001733 if "help" in sys.argv or '-h' in sys.argv or '--help' in sys.argv:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001734 print_usage()
1735 return 0
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001736 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001737 config = BaseConfig()
Brad Bishop004d4992018-10-02 23:54:45 +02001738
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001739 renice = os.path.expanduser("~/bin/runqemu-renice")
1740 if os.path.exists(renice):
1741 logger.info('Using %s to renice' % renice)
1742 subprocess.check_call([renice, str(os.getpid())])
1743
Brad Bishop004d4992018-10-02 23:54:45 +02001744 def sigterm_handler(signum, frame):
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001745 logger.info("Received signal: %s" % (signum))
Brad Bishop004d4992018-10-02 23:54:45 +02001746 config.cleanup()
Brad Bishop004d4992018-10-02 23:54:45 +02001747 signal.signal(signal.SIGTERM, sigterm_handler)
1748
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001749 config.check_args()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001750 config.read_qemuboot()
1751 config.check_and_set()
1752 # Check whether the combos is valid or not
1753 config.validate_combos()
1754 config.print_config()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001755 config.setup_network()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001756 config.setup_rootfs()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001757 config.setup_final()
Andrew Geissler517393d2023-01-13 08:55:19 -06001758 config.setup_cmd()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001759 config.start_qemu()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001760 except RunQemuError as err:
1761 logger.error(err)
1762 return 1
1763 except Exception as err:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001764 import traceback
1765 traceback.print_exc()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001766 return 1
1767 finally:
Andrew Geissler517393d2023-01-13 08:55:19 -06001768 config.cleanup_cmd()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001769 config.cleanup()
1770
1771if __name__ == "__main__":
1772 sys.exit(main())