blob: 58b0c191e15bc7462dbe5dfcd01341007af702e9 [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
Andrew Geissler90fd73c2021-03-05 15:25:55 -060069 novga - Disable VGA emulation completely
Brad Bishopa34c0302019-09-23 22:34:48 -040070 sdl - choose the SDL UI frontend
71 gtk - choose the Gtk UI frontend
Brad Bishop6dbb3162019-11-25 09:41:34 -050072 gl - enable virgl-based GL acceleration (also needs gtk or sdl options)
73 gl-es - enable virgl-based GL acceleration, using OpenGL ES (also needs gtk or sdl options)
74 egl-headless - enable headless EGL output; use vnc (via publicvnc option) or spice to see it
Andrew Geisslerd159c7f2021-09-02 21:05:58 -050075 (hint: if /dev/dri/renderD* is absent due to lack of suitable GPU, 'modprobe vgem' will create
Andrew Geissler9aee5002022-03-30 16:27:02 +000076 one suitable for mesa llvmpipe software renderer)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060077 serial - enable a serial console on /dev/ttyS0
Brad Bishop19323692019-04-05 15:28:33 -040078 serialstdio - enable a serial console on the console (regardless of graphics mode)
Andrew Geissler9aee5002022-03-30 16:27:02 +000079 slirp - enable user networking, no root privilege is required
80 snapshot - don't write changes back to images
Patrick Williamsc0f7c042017-02-23 20:41:17 -060081 kvm - enable KVM when running x86/x86_64 (VT-capable CPU required)
82 kvm-vhost - enable KVM with vhost when running x86/x86_64 (VT-capable CPU required)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050083 publicvnc - enable a VNC server open to all hosts
Patrick Williamsc0f7c042017-02-23 20:41:17 -060084 audio - enable audio
Brad Bishop6e60e8b2018-02-01 10:27:11 -050085 [*/]ovmf* - OVMF firmware file or base name for booting with UEFI
Patrick Williamsc0f7c042017-02-23 20:41:17 -060086 tcpserial=<port> - specify tcp serial port number
Patrick Williamsc0f7c042017-02-23 20:41:17 -060087 qemuparams=<xyz> - specify custom parameters to QEMU
88 bootparams=<xyz> - specify custom kernel parameters during boot
Brad Bishop6e60e8b2018-02-01 10:27:11 -050089 help, -h, --help: print this text
Brad Bishopd7bf8c12018-02-25 22:55:05 -050090 -d, --debug: Enable debug output
Brad Bishop79641f22019-09-10 07:20:22 -040091 -q, --quiet: Hide most output except error messages
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050092
93Examples:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050094 runqemu
Patrick Williamsc0f7c042017-02-23 20:41:17 -060095 runqemu qemuarm
96 runqemu tmp/deploy/images/qemuarm
Brad Bishop6e60e8b2018-02-01 10:27:11 -050097 runqemu tmp/deploy/images/qemux86/<qemuboot.conf>
Patrick Williamsc0f7c042017-02-23 20:41:17 -060098 runqemu qemux86-64 core-image-sato ext4
99 runqemu qemux86-64 wic-image-minimal wic
100 runqemu path/to/bzImage-qemux86.bin path/to/nfsrootdir/ serial
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600101 runqemu qemux86 iso/hddimg/wic.vmdk/wic.vhd/wic.vhdx/wic.qcow2/wic.vdi/ramfs/cpio.gz...
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600102 runqemu qemux86 qemuparams="-m 256"
103 runqemu qemux86 bootparams="psplash=false"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600104 runqemu path/to/<image>-<machine>.wic
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500105 runqemu path/to/<image>-<machine>.wic.vmdk
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600106 runqemu path/to/<image>-<machine>.wic.vhdx
107 runqemu path/to/<image>-<machine>.wic.vhd
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600108""")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500109
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600110def check_tun():
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500111 """Check /dev/net/tun"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600112 dev_tun = '/dev/net/tun'
113 if not os.path.exists(dev_tun):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500114 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 -0500115
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600116 if not os.access(dev_tun, os.W_OK):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500117 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 -0500118
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600119def get_first_file(cmds):
120 """Return first file found in wildcard cmds"""
121 for cmd in cmds:
122 all_files = glob.glob(cmd)
123 if all_files:
124 for f in all_files:
125 if not os.path.isdir(f):
126 return f
127 return ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500128
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600129class BaseConfig(object):
130 def __init__(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500131 # The self.d saved vars from self.set(), part of them are from qemuboot.conf
132 self.d = {'QB_KERNEL_ROOT': '/dev/vda'}
133
134 # Supported env vars, add it here if a var can be got from env,
135 # and don't use os.getenv in the code.
136 self.env_vars = ('MACHINE',
137 'ROOTFS',
138 'KERNEL',
Brad Bishopc68388fc2019-08-26 01:33:31 -0400139 'BIOS',
Brad Bishop316dfdd2018-06-25 12:45:53 -0400140 'DEVICE_TREE',
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500141 'DEPLOY_DIR_IMAGE',
142 'OE_TMPDIR',
143 'OECORE_NATIVE_SYSROOT',
Andrew Geissler82c905d2020-04-13 13:39:40 -0500144 'MULTICONFIG',
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500145 'SERIAL_CONSOLES',
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500146 )
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500147
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600148 self.qemu_opt = ''
149 self.qemu_opt_script = ''
Andrew Geissler99467da2019-02-25 18:54:23 -0600150 self.qemuparams = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600151 self.nfs_server = ''
152 self.rootfs = ''
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500153 # File name(s) of a OVMF firmware file or variable store,
154 # to be added with -drive if=pflash.
155 # Found in the same places as the rootfs, with or without one of
156 # these suffices: qcow2, bin.
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500157 self.ovmf_bios = []
Brad Bishop08902b02019-08-20 09:16:51 -0400158 # When enrolling default Secure Boot keys, the hypervisor
159 # must provide the Platform Key and the first Key Exchange Key
160 # certificate in the Type 11 SMBIOS table.
161 self.ovmf_secboot_pkkek1 = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600162 self.qemuboot = ''
163 self.qbconfload = False
164 self.kernel = ''
Brad Bishopc68388fc2019-08-26 01:33:31 -0400165 self.bios = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600166 self.kernel_cmdline = ''
167 self.kernel_cmdline_script = ''
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500168 self.bootparams = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600169 self.dtb = ''
170 self.fstype = ''
171 self.kvm_enabled = False
172 self.vhost_enabled = False
173 self.slirp_enabled = False
Andrew Geissler82c905d2020-04-13 13:39:40 -0500174 self.net_bridge = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600175 self.nfs_instance = 0
176 self.nfs_running = False
Brad Bishop19323692019-04-05 15:28:33 -0400177 self.serialconsole = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600178 self.serialstdio = False
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500179 self.nographic = False
180 self.sdl = False
181 self.gtk = False
182 self.gl = False
183 self.gl_es = False
184 self.egl_headless = False
Patrick Williams03907ee2022-05-01 06:28:52 -0500185 self.publicvnc = False
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500186 self.novga = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600187 self.cleantap = False
188 self.saved_stty = ''
189 self.audio_enabled = False
190 self.tcpserial_portnum = ''
Brad Bishop08902b02019-08-20 09:16:51 -0400191 self.taplock = ''
192 self.taplock_descriptor = None
193 self.portlocks = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600194 self.bitbake_e = ''
195 self.snapshot = False
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600196 self.wictypes = ('wic', 'wic.vmdk', 'wic.qcow2', 'wic.vdi', "wic.vhd", "wic.vhdx")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500197 self.fstypes = ('ext2', 'ext3', 'ext4', 'jffs2', 'nfs', 'btrfs',
198 'cpio.gz', 'cpio', 'ramfs', 'tar.bz2', 'tar.gz')
Brad Bishop08902b02019-08-20 09:16:51 -0400199 self.vmtypes = ('hddimg', 'iso')
Brad Bishop15ae2502019-06-18 21:44:24 -0400200 self.fsinfo = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500201 self.network_device = "-device e1000,netdev=net0,mac=@MAC@"
Andrew Geissler82c905d2020-04-13 13:39:40 -0500202 self.cmdline_ip_slirp = "ip=dhcp"
Andrew Geissler595f6302022-01-24 19:11:47 +0000203 self.cmdline_ip_tap = "ip=192.168.7.@CLIENT@::192.168.7.@GATEWAY@:255.255.255.0::eth0:off:8.8.8.8"
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500204 # Use different mac section for tap and slirp to avoid
205 # conflicts, e.g., when one is running with tap, the other is
206 # running with slirp.
207 # The last section is dynamic, which is for avoiding conflicts,
208 # when multiple qemus are running, e.g., when multiple tap or
209 # slirp qemus are running.
210 self.mac_tap = "52:54:00:12:34:"
211 self.mac_slirp = "52:54:00:12:35:"
Brad Bishop004d4992018-10-02 23:54:45 +0200212 # pid of the actual qemu process
Patrick Williams2390b1b2022-11-03 13:47:49 -0500213 self.qemu_environ = os.environ.copy()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600214 self.qemuprocess = None
Brad Bishop004d4992018-10-02 23:54:45 +0200215 # avoid cleanup twice
216 self.cleaned = False
Andrew Geisslerc926e172021-05-07 16:11:35 -0500217 # Files to cleanup after run
218 self.cleanup_files = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500219
Brad Bishop08902b02019-08-20 09:16:51 -0400220 def acquire_taplock(self, error=True):
221 logger.debug("Acquiring lockfile %s..." % self.taplock)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600222 try:
Brad Bishop08902b02019-08-20 09:16:51 -0400223 self.taplock_descriptor = open(self.taplock, 'w')
224 fcntl.flock(self.taplock_descriptor, fcntl.LOCK_EX|fcntl.LOCK_NB)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600225 except Exception as e:
Brad Bishop08902b02019-08-20 09:16:51 -0400226 msg = "Acquiring lockfile %s failed: %s" % (self.taplock, e)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500227 if error:
228 logger.error(msg)
229 else:
230 logger.info(msg)
Brad Bishop08902b02019-08-20 09:16:51 -0400231 if self.taplock_descriptor:
232 self.taplock_descriptor.close()
233 self.taplock_descriptor = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600234 return False
235 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500236
Brad Bishop08902b02019-08-20 09:16:51 -0400237 def release_taplock(self):
238 if self.taplock_descriptor:
Brad Bishopf86d0552018-12-04 14:18:15 -0800239 logger.debug("Releasing lockfile for tap device '%s'" % self.tap)
Andrew Geissler5f350902021-07-23 13:09:54 -0400240 # We pass the fd to the qemu process and if we unlock here, it would unlock for
241 # that too. Therefore don't unlock, just close
242 # fcntl.flock(self.taplock_descriptor, fcntl.LOCK_UN)
Brad Bishop08902b02019-08-20 09:16:51 -0400243 self.taplock_descriptor.close()
Andrew Geissler5f350902021-07-23 13:09:54 -0400244 # Removing the file is a potential race, don't do that either
245 # os.remove(self.taplock)
Brad Bishop08902b02019-08-20 09:16:51 -0400246 self.taplock_descriptor = None
247
248 def check_free_port(self, host, port, lockdir):
249 """ Check whether the port is free or not """
250 import socket
251 from contextlib import closing
252
253 lockfile = os.path.join(lockdir, str(port) + '.lock')
254 if self.acquire_portlock(lockfile):
255 with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
256 if sock.connect_ex((host, port)) == 0:
257 # Port is open, so not free
258 self.release_portlock(lockfile)
259 return False
260 else:
261 # Port is not open, so free
262 return True
263 else:
264 return False
265
266 def acquire_portlock(self, lockfile):
267 logger.debug("Acquiring lockfile %s..." % lockfile)
268 try:
269 portlock_descriptor = open(lockfile, 'w')
270 self.portlocks.update({lockfile: portlock_descriptor})
271 fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_EX|fcntl.LOCK_NB)
272 except Exception as e:
273 msg = "Acquiring lockfile %s failed: %s" % (lockfile, e)
274 logger.info(msg)
275 if lockfile in self.portlocks.keys() and self.portlocks[lockfile]:
276 self.portlocks[lockfile].close()
277 del self.portlocks[lockfile]
278 return False
279 return True
280
281 def release_portlock(self, lockfile=None):
282 if lockfile != None:
Andrew Geissler5f350902021-07-23 13:09:54 -0400283 logger.debug("Releasing lockfile '%s'" % lockfile)
284 # We pass the fd to the qemu process and if we unlock here, it would unlock for
285 # that too. Therefore don't unlock, just close
286 # fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_UN)
287 self.portlocks[lockfile].close()
288 # Removing the file is a potential race, don't do that either
289 # os.remove(lockfile)
290 del self.portlocks[lockfile]
Brad Bishop08902b02019-08-20 09:16:51 -0400291 elif len(self.portlocks):
292 for lockfile, descriptor in self.portlocks.items():
293 logger.debug("Releasing lockfile '%s'" % lockfile)
Andrew Geissler5f350902021-07-23 13:09:54 -0400294 # We pass the fd to the qemu process and if we unlock here, it would unlock for
295 # that too. Therefore don't unlock, just close
296 # fcntl.flock(descriptor, fcntl.LOCK_UN)
Brad Bishop08902b02019-08-20 09:16:51 -0400297 descriptor.close()
Andrew Geissler5f350902021-07-23 13:09:54 -0400298 # Removing the file is a potential race, don't do that either
299 # os.remove(lockfile)
Brad Bishop08902b02019-08-20 09:16:51 -0400300 self.portlocks = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500301
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600302 def get(self, key):
303 if key in self.d:
304 return self.d.get(key)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500305 elif os.getenv(key):
306 return os.getenv(key)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600307 else:
308 return ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500309
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600310 def set(self, key, value):
311 self.d[key] = value
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500312
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600313 def is_deploy_dir_image(self, p):
314 if os.path.isdir(p):
315 if not re.search('.qemuboot.conf$', '\n'.join(os.listdir(p)), re.M):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500316 logger.debug("Can't find required *.qemuboot.conf in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600317 return False
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500318 if not any(map(lambda name: '-image-' in name, os.listdir(p))):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500319 logger.debug("Can't find *-image-* in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600320 return False
321 return True
322 else:
323 return False
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500324
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600325 def check_arg_fstype(self, fst):
326 """Check and set FSTYPE"""
Brad Bishop15ae2502019-06-18 21:44:24 -0400327 if fst not in self.fstypes + self.vmtypes + self.wictypes:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800328 logger.warning("Maybe unsupported FSTYPE: %s" % fst)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600329 if not self.fstype or self.fstype == fst:
330 if fst == 'ramfs':
331 fst = 'cpio.gz'
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500332 if fst in ('tar.bz2', 'tar.gz'):
333 fst = 'nfs'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600334 self.fstype = fst
335 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500336 raise RunQemuError("Conflicting: FSTYPE %s and %s" % (self.fstype, fst))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500337
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600338 def set_machine_deploy_dir(self, machine, deploy_dir_image):
339 """Set MACHINE and DEPLOY_DIR_IMAGE"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500340 logger.debug('MACHINE: %s' % machine)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600341 self.set("MACHINE", machine)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500342 logger.debug('DEPLOY_DIR_IMAGE: %s' % deploy_dir_image)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600343 self.set("DEPLOY_DIR_IMAGE", deploy_dir_image)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500344
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600345 def check_arg_nfs(self, p):
346 if os.path.isdir(p):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500347 self.rootfs = p
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600348 else:
349 m = re.match('(.*):(.*)', p)
350 self.nfs_server = m.group(1)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500351 self.rootfs = m.group(2)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600352 self.check_arg_fstype('nfs')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500353
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600354 def check_arg_path(self, p):
355 """
356 - Check whether it is <image>.qemuboot.conf or contains <image>.qemuboot.conf
Andrew Geissler9aee5002022-03-30 16:27:02 +0000357 - Check whether it is a kernel file
358 - Check whether it is an image file
359 - Check whether it is an NFS dir
360 - Check whether it is an OVMF flash file
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600361 """
362 if p.endswith('.qemuboot.conf'):
363 self.qemuboot = p
364 self.qbconfload = True
365 elif re.search('\.bin$', p) or re.search('bzImage', p) or \
366 re.search('zImage', p) or re.search('vmlinux', p) or \
367 re.search('fitImage', p) or re.search('uImage', p):
368 self.kernel = p
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500369 elif os.path.exists(p) and (not os.path.isdir(p)) and '-image-' in os.path.basename(p):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600370 self.rootfs = p
Andrew Geissler9aee5002022-03-30 16:27:02 +0000371 # Check filename against self.fstypes can handle <file>.cpio.gz,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500372 # otherwise, its type would be "gz", which is incorrect.
373 fst = ""
374 for t in self.fstypes:
375 if p.endswith(t):
376 fst = t
377 break
378 if not fst:
379 m = re.search('.*\.(.*)$', self.rootfs)
380 if m:
381 fst = m.group(1)
382 if fst:
383 self.check_arg_fstype(fst)
384 qb = re.sub('\.' + fst + "$", '', self.rootfs)
385 qb = '%s%s' % (re.sub('\.rootfs$', '', qb), '.qemuboot.conf')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600386 if os.path.exists(qb):
387 self.qemuboot = qb
388 self.qbconfload = True
389 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800390 logger.warning("%s doesn't exist" % qb)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600391 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500392 raise RunQemuError("Can't find FSTYPE from: %s" % p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500393
394 elif os.path.isdir(p) or re.search(':', p) and re.search('/', p):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600395 if self.is_deploy_dir_image(p):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500396 logger.debug('DEPLOY_DIR_IMAGE: %s' % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600397 self.set("DEPLOY_DIR_IMAGE", p)
398 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500399 logger.debug("Assuming %s is an nfs rootfs" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600400 self.check_arg_nfs(p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500401 elif os.path.basename(p).startswith('ovmf'):
402 self.ovmf_bios.append(p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600403 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500404 raise RunQemuError("Unknown path arg %s" % p)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500405
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600406 def check_arg_machine(self, arg):
407 """Check whether it is a machine"""
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500408 if self.get('MACHINE') == arg:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600409 return
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500410 elif self.get('MACHINE') and self.get('MACHINE') != arg:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500411 raise RunQemuError("Maybe conflicted MACHINE: %s vs %s" % (self.get('MACHINE'), arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500412 elif re.search('/', arg):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500413 raise RunQemuError("Unknown arg: %s" % arg)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500414
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500415 logger.debug('Assuming MACHINE = %s' % arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500416
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600417 # if we're running under testimage, or similarly as a child
418 # of an existing bitbake invocation, we can't invoke bitbake
419 # to validate the MACHINE setting and must assume it's correct...
420 # FIXME: testimage.bbclass exports these two variables into env,
421 # are there other scenarios in which we need to support being
422 # invoked by bitbake?
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500423 deploy = self.get('DEPLOY_DIR_IMAGE')
424 bbchild = deploy and self.get('OE_TMPDIR')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600425 if bbchild:
426 self.set_machine_deploy_dir(arg, deploy)
427 return
428 # also check whether we're running under a sourced toolchain
429 # environment file
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500430 if self.get('OECORE_NATIVE_SYSROOT'):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600431 self.set("MACHINE", arg)
432 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500433
Andrew Geissler82c905d2020-04-13 13:39:40 -0500434 self.bitbake_e = self.run_bitbake_env(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600435 # bitbake -e doesn't report invalid MACHINE as an error, so
436 # let's check DEPLOY_DIR_IMAGE to make sure that it is a valid
437 # MACHINE.
438 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
439 if s:
440 deploy_dir_image = s.group(1)
441 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500442 raise RunQemuError("bitbake -e %s" % self.bitbake_e)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600443 if self.is_deploy_dir_image(deploy_dir_image):
444 self.set_machine_deploy_dir(arg, deploy_dir_image)
445 else:
446 logger.error("%s not a directory valid DEPLOY_DIR_IMAGE" % deploy_dir_image)
447 self.set("MACHINE", arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500448
Andrew Geisslerc182c622020-05-15 14:13:32 -0500449 def set_dri_path(self):
450 # As runqemu can be run within bitbake (when using testimage, for example),
451 # we need to ensure that we run host pkg-config, and that it does not
452 # get mis-directed to native build paths set by bitbake.
Patrick Williams2390b1b2022-11-03 13:47:49 -0500453 env = os.environ.copy()
Andrew Geisslerc182c622020-05-15 14:13:32 -0500454 try:
Patrick Williams2390b1b2022-11-03 13:47:49 -0500455 del env['PKG_CONFIG_PATH']
456 del env['PKG_CONFIG_DIR']
457 del env['PKG_CONFIG_LIBDIR']
458 del env['PKG_CONFIG_SYSROOT_DIR']
Andrew Geisslerc182c622020-05-15 14:13:32 -0500459 except KeyError:
460 pass
461 try:
Patrick Williams2390b1b2022-11-03 13:47:49 -0500462 dripath = subprocess.check_output("PATH=/bin:/usr/bin:$PATH pkg-config --variable=dridriverdir dri", shell=True, env=env)
Andrew Geisslerc182c622020-05-15 14:13:32 -0500463 except subprocess.CalledProcessError as e:
464 raise RunQemuError("Could not determine the path to dri drivers on the host via pkg-config.\nPlease install Mesa development files (particularly, dri.pc) on the host machine.")
Patrick Williams2390b1b2022-11-03 13:47:49 -0500465 self.qemu_environ['LIBGL_DRIVERS_PATH'] = dripath.decode('utf-8').strip()
Andrew Geisslerc182c622020-05-15 14:13:32 -0500466
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000467 # This preloads uninative libc pieces and therefore ensures that RPATH/RUNPATH
468 # in host mesa drivers doesn't trick uninative into loading host libc.
469 preload_items = ['libdl.so.2', 'librt.so.1', 'libpthread.so.0']
470 uninative_path = os.path.dirname(self.get("UNINATIVE_LOADER"))
471 if os.path.exists(uninative_path):
472 preload_paths = [os.path.join(uninative_path, i) for i in preload_items]
Patrick Williams2390b1b2022-11-03 13:47:49 -0500473 self.qemu_environ['LD_PRELOAD'] = " ".join(preload_paths)
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000474
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600475 def check_args(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500476 for debug in ("-d", "--debug"):
477 if debug in sys.argv:
478 logger.setLevel(logging.DEBUG)
479 sys.argv.remove(debug)
480
481 for quiet in ("-q", "--quiet"):
482 if quiet in sys.argv:
483 logger.setLevel(logging.ERROR)
484 sys.argv.remove(quiet)
485
Andrew Geisslerc182c622020-05-15 14:13:32 -0500486 if 'gl' not in sys.argv[1:] and 'gl-es' not in sys.argv[1:]:
Patrick Williams2390b1b2022-11-03 13:47:49 -0500487 self.qemu_environ['SDL_RENDER_DRIVER'] = 'software'
488 self.qemu_environ['SDL_FRAMEBUFFER_ACCELERATION'] = 'false'
Andrew Geisslerc182c622020-05-15 14:13:32 -0500489
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600490 unknown_arg = ""
491 for arg in sys.argv[1:]:
Brad Bishop15ae2502019-06-18 21:44:24 -0400492 if arg in self.fstypes + self.vmtypes + self.wictypes:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600493 self.check_arg_fstype(arg)
494 elif arg == 'nographic':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500495 self.nographic = True
Brad Bishop19323692019-04-05 15:28:33 -0400496 elif arg == 'sdl':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500497 self.sdl = True
Brad Bishopa34c0302019-09-23 22:34:48 -0400498 elif arg == 'gtk':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500499 self.gtk = True
500 elif arg == 'gl':
501 self.gl = True
Patrick Williams2390b1b2022-11-03 13:47:49 -0500502 elif arg == 'gl-es':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500503 self.gl_es = True
Brad Bishop19323692019-04-05 15:28:33 -0400504 elif arg == 'egl-headless':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500505 self.egl_headless = True
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600506 elif arg == 'novga':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500507 self.novga = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600508 elif arg == 'serial':
Brad Bishop19323692019-04-05 15:28:33 -0400509 self.serialconsole = True
510 elif arg == "serialstdio":
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600511 self.serialstdio = True
512 elif arg == 'audio':
513 logger.info("Enabling audio in qemu")
514 logger.info("Please install sound drivers in linux host")
515 self.audio_enabled = True
516 elif arg == 'kvm':
517 self.kvm_enabled = True
518 elif arg == 'kvm-vhost':
519 self.vhost_enabled = True
520 elif arg == 'slirp':
521 self.slirp_enabled = True
Andrew Geissler82c905d2020-04-13 13:39:40 -0500522 elif arg.startswith('bridge='):
523 self.net_bridge = '%s' % arg[len('bridge='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600524 elif arg == 'snapshot':
525 self.snapshot = True
526 elif arg == 'publicvnc':
Patrick Williams03907ee2022-05-01 06:28:52 -0500527 self.publicvnc = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600528 self.qemu_opt_script += ' -vnc :0'
529 elif arg.startswith('tcpserial='):
Brad Bishop15ae2502019-06-18 21:44:24 -0400530 self.tcpserial_portnum = '%s' % arg[len('tcpserial='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600531 elif arg.startswith('qemuparams='):
Andrew Geissler99467da2019-02-25 18:54:23 -0600532 self.qemuparams = ' %s' % arg[len('qemuparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600533 elif arg.startswith('bootparams='):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500534 self.bootparams = arg[len('bootparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600535 elif os.path.exists(arg) or (re.search(':', arg) and re.search('/', arg)):
536 self.check_arg_path(os.path.abspath(arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500537 elif re.search(r'-image-|-image$', arg):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600538 # Lazy rootfs
539 self.rootfs = arg
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500540 elif arg.startswith('ovmf'):
541 self.ovmf_bios.append(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600542 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500543 # At last, assume it is the MACHINE
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600544 if (not unknown_arg) or unknown_arg == arg:
545 unknown_arg = arg
546 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500547 raise RunQemuError("Can't handle two unknown args: %s %s\n"
548 "Try 'runqemu help' on how to use it" % \
549 (unknown_arg, arg))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600550 # Check to make sure it is a valid machine
Brad Bishopa5c52ff2018-11-23 10:55:50 +1300551 if unknown_arg and self.get('MACHINE') != unknown_arg:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500552 if self.get('DEPLOY_DIR_IMAGE'):
553 machine = os.path.basename(self.get('DEPLOY_DIR_IMAGE'))
554 if unknown_arg == machine:
555 self.set("MACHINE", machine)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500556
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600557 self.check_arg_machine(unknown_arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500558
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500559 if not (self.get('DEPLOY_DIR_IMAGE') or self.qbconfload):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500560 self.load_bitbake_env()
561 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
562 if s:
563 self.set("DEPLOY_DIR_IMAGE", s.group(1))
564
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600565 def check_kvm(self):
566 """Check kvm and kvm-host"""
567 if not (self.kvm_enabled or self.vhost_enabled):
William A. Kennington IIIac69b482021-06-02 12:28:27 -0700568 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 -0600569 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500570
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600571 if not self.get('QB_CPU_KVM'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500572 raise RunQemuError("QB_CPU_KVM is NULL, this board doesn't support kvm")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500573
William A. Kennington IIIac69b482021-06-02 12:28:27 -0700574 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 -0600575 yocto_kvm_wiki = "https://wiki.yoctoproject.org/wiki/How_to_enable_KVM_for_Poky_qemu"
576 yocto_paravirt_kvm_wiki = "https://wiki.yoctoproject.org/wiki/Running_an_x86_Yocto_Linux_image_under_QEMU_KVM"
577 dev_kvm = '/dev/kvm'
578 dev_vhost = '/dev/vhost-net'
Brad Bishop15ae2502019-06-18 21:44:24 -0400579 if self.qemu_system.endswith(('i386', 'x86_64')):
580 with open('/proc/cpuinfo', 'r') as f:
581 kvm_cap = re.search('vmx|svm', "".join(f.readlines()))
582 if not kvm_cap:
583 logger.error("You are trying to enable KVM on a cpu without VT support.")
584 logger.error("Remove kvm from the command-line, or refer:")
585 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500586
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600587 if not os.path.exists(dev_kvm):
588 logger.error("Missing KVM device. Have you inserted kvm modules?")
589 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500590 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500591
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600592 if os.access(dev_kvm, os.W_OK|os.R_OK):
593 self.qemu_opt_script += ' -enable-kvm'
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500594 if self.get('MACHINE') == "qemux86":
595 # Workaround for broken APIC window on pre 4.15 host kernels which causes boot hangs
596 # See YOCTO #12301
597 # On 64 bit we use x2apic
598 self.kernel_cmdline_script += " clocksource=kvm-clock hpet=disable noapic nolapic"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600599 else:
600 logger.error("You have no read or write permission on /dev/kvm.")
601 logger.error("Please change the ownership of this file as described at:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500602 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500603
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600604 if self.vhost_enabled:
605 if not os.path.exists(dev_vhost):
606 logger.error("Missing virtio net device. Have you inserted vhost-net module?")
607 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500608 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500609
Andrew Geissler635e0e42020-08-21 15:58:33 -0500610 if not os.access(dev_vhost, os.W_OK|os.R_OK):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600611 logger.error("You have no read or write permission on /dev/vhost-net.")
612 logger.error("Please change the ownership of this file as described at:")
Andrew Geissler635e0e42020-08-21 15:58:33 -0500613 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500614
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600615 def check_fstype(self):
616 """Check and setup FSTYPE"""
617 if not self.fstype:
618 fstype = self.get('QB_DEFAULT_FSTYPE')
619 if fstype:
620 self.fstype = fstype
621 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500622 raise RunQemuError("FSTYPE is NULL!")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500623
Brad Bishop15ae2502019-06-18 21:44:24 -0400624 # parse QB_FSINFO into dict, e.g. { 'wic': ['no-kernel-in-fs', 'a-flag'], 'ext4': ['another-flag']}
625 wic_fs = False
626 qb_fsinfo = self.get('QB_FSINFO')
627 if qb_fsinfo:
628 qb_fsinfo = qb_fsinfo.split()
629 for fsinfo in qb_fsinfo:
630 try:
631 fstype, fsflag = fsinfo.split(':')
632
633 if fstype == 'wic':
634 if fsflag == 'no-kernel-in-fs':
635 wic_fs = True
636 elif fsflag == 'kernel-in-fs':
637 wic_fs = False
638 else:
639 logger.warn('Unknown flag "%s:%s" in QB_FSINFO', fstype, fsflag)
640 continue
641 else:
642 logger.warn('QB_FSINFO is not supported for image type "%s"', fstype)
643 continue
644
645 if fstype in self.fsinfo:
646 self.fsinfo[fstype].append(fsflag)
647 else:
648 self.fsinfo[fstype] = [fsflag]
649 except Exception:
650 logger.error('Invalid parameter "%s" in QB_FSINFO', fsinfo)
651
652 # treat wic images as vmimages (with kernel) or as fsimages (rootfs only)
653 if wic_fs:
654 self.fstypes = self.fstypes + self.wictypes
655 else:
656 self.vmtypes = self.vmtypes + self.wictypes
657
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600658 def check_rootfs(self):
659 """Check and set rootfs"""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500660
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500661 if self.fstype == "none":
662 return
663
664 if self.get('ROOTFS'):
665 if not self.rootfs:
666 self.rootfs = self.get('ROOTFS')
667 elif self.get('ROOTFS') != self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500668 raise RunQemuError("Maybe conflicted ROOTFS: %s vs %s" % (self.get('ROOTFS'), self.rootfs))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500669
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600670 if self.fstype == 'nfs':
671 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500672
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600673 if self.rootfs and not os.path.exists(self.rootfs):
674 # Lazy rootfs
675 self.rootfs = "%s/%s-%s.%s" % (self.get('DEPLOY_DIR_IMAGE'),
676 self.rootfs, self.get('MACHINE'),
677 self.fstype)
678 elif not self.rootfs:
679 cmd_name = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_NAME'), self.fstype)
680 cmd_link = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'), self.fstype)
681 cmds = (cmd_name, cmd_link)
682 self.rootfs = get_first_file(cmds)
683 if not self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500684 raise RunQemuError("Failed to find rootfs: %s or %s" % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500685
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600686 if not os.path.exists(self.rootfs):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500687 raise RunQemuError("Can't find rootfs: %s" % self.rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500688
Brad Bishop08902b02019-08-20 09:16:51 -0400689 def setup_pkkek1(self):
690 """
691 Extract from PEM certificate the Platform Key and first Key
692 Exchange Key certificate string. The hypervisor needs to provide
693 it in the Type 11 SMBIOS table
694 """
695 pemcert = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), 'OvmfPkKek1.pem')
696 try:
697 with open(pemcert, 'r') as pemfile:
698 key = pemfile.read().replace('\n', ''). \
699 replace('-----BEGIN CERTIFICATE-----', ''). \
700 replace('-----END CERTIFICATE-----', '')
701 self.ovmf_secboot_pkkek1 = key
702
703 except FileNotFoundError:
704 raise RunQemuError("Can't open PEM certificate %s " % pemcert)
705
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500706 def check_ovmf(self):
707 """Check and set full path for OVMF firmware and variable file(s)."""
708
709 for index, ovmf in enumerate(self.ovmf_bios):
710 if os.path.exists(ovmf):
711 continue
712 for suffix in ('qcow2', 'bin'):
713 path = '%s/%s.%s' % (self.get('DEPLOY_DIR_IMAGE'), ovmf, suffix)
714 if os.path.exists(path):
715 self.ovmf_bios[index] = path
Brad Bishop08902b02019-08-20 09:16:51 -0400716 if ovmf.endswith('secboot'):
717 self.setup_pkkek1()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500718 break
719 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500720 raise RunQemuError("Can't find OVMF firmware: %s" % ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500721
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600722 def check_kernel(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400723 """Check and set kernel"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600724 # The vm image doesn't need a kernel
725 if self.fstype in self.vmtypes:
726 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500727
Brad Bishop316dfdd2018-06-25 12:45:53 -0400728 # See if the user supplied a KERNEL option
729 if self.get('KERNEL'):
730 self.kernel = self.get('KERNEL')
731
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500732 # QB_DEFAULT_KERNEL is always a full file path
733 kernel_name = os.path.basename(self.get('QB_DEFAULT_KERNEL'))
734
735 # The user didn't want a kernel to be loaded
Brad Bishop316dfdd2018-06-25 12:45:53 -0400736 if kernel_name == "none" and not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500737 return
738
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600739 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
740 if not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500741 kernel_match_name = "%s/%s" % (deploy_dir_image, kernel_name)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600742 kernel_match_link = "%s/%s" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
743 kernel_startswith = "%s/%s*" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
744 cmds = (kernel_match_name, kernel_match_link, kernel_startswith)
745 self.kernel = get_first_file(cmds)
746 if not self.kernel:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500747 raise RunQemuError('KERNEL not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500748
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600749 if not os.path.exists(self.kernel):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500750 raise RunQemuError("KERNEL %s not found" % self.kernel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500751
Brad Bishop316dfdd2018-06-25 12:45:53 -0400752 def check_dtb(self):
753 """Check and set dtb"""
754 # Did the user specify a device tree?
755 if self.get('DEVICE_TREE'):
756 self.dtb = self.get('DEVICE_TREE')
757 if not os.path.exists(self.dtb):
758 raise RunQemuError('Specified DTB not found: %s' % self.dtb)
759 return
760
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600761 dtb = self.get('QB_DTB')
762 if dtb:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400763 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600764 cmd_match = "%s/%s" % (deploy_dir_image, dtb)
765 cmd_startswith = "%s/%s*" % (deploy_dir_image, dtb)
766 cmd_wild = "%s/*.dtb" % deploy_dir_image
767 cmds = (cmd_match, cmd_startswith, cmd_wild)
768 self.dtb = get_first_file(cmds)
769 if not os.path.exists(self.dtb):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500770 raise RunQemuError('DTB not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500771
Brad Bishopc68388fc2019-08-26 01:33:31 -0400772 def check_bios(self):
773 """Check and set bios"""
774
775 # See if the user supplied a BIOS option
776 if self.get('BIOS'):
777 self.bios = self.get('BIOS')
778
779 # QB_DEFAULT_BIOS is always a full file path
780 bios_name = os.path.basename(self.get('QB_DEFAULT_BIOS'))
781
782 # The user didn't want a bios to be loaded
783 if (bios_name == "" or bios_name == "none") and not self.bios:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600784 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500785
Brad Bishopc68388fc2019-08-26 01:33:31 -0400786 if not self.bios:
787 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
788 self.bios = "%s/%s" % (deploy_dir_image, bios_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500789
Brad Bishopc68388fc2019-08-26 01:33:31 -0400790 if not self.bios:
791 raise RunQemuError('BIOS not found: %s' % bios_match_name)
792
793 if not os.path.exists(self.bios):
Patrick Williams213cb262021-08-07 19:21:33 -0500794 raise RunQemuError("BIOS %s not found" % self.bios)
Brad Bishopc68388fc2019-08-26 01:33:31 -0400795
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500796
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600797 def check_mem(self):
Andrew Geissler99467da2019-02-25 18:54:23 -0600798 """
799 Both qemu and kernel needs memory settings, so check QB_MEM and set it
800 for both.
801 """
802 s = re.search('-m +([0-9]+)', self.qemuparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600803 if s:
804 self.set('QB_MEM', '-m %s' % s.group(1))
805 elif not self.get('QB_MEM'):
Brad Bishop79641f22019-09-10 07:20:22 -0400806 logger.info('QB_MEM is not set, use 256M by default')
807 self.set('QB_MEM', '-m 256')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500808
Andrew Geissler99467da2019-02-25 18:54:23 -0600809 # Check and remove M or m suffix
810 qb_mem = self.get('QB_MEM')
811 if qb_mem.endswith('M') or qb_mem.endswith('m'):
812 qb_mem = qb_mem[:-1]
813
814 # Add -m prefix it not present
815 if not qb_mem.startswith('-m'):
816 qb_mem = '-m %s' % qb_mem
817
818 self.set('QB_MEM', qb_mem)
819
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800820 mach = self.get('MACHINE')
Andrew Geissler9aee5002022-03-30 16:27:02 +0000821 if not mach.startswith(('qemumips', 'qemux86')):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800822 self.kernel_cmdline_script += ' mem=%s' % self.get('QB_MEM').replace('-m','').strip() + 'M'
823
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600824 self.qemu_opt_script += ' %s' % self.get('QB_MEM')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500825
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600826 def check_tcpserial(self):
827 if self.tcpserial_portnum:
Brad Bishop15ae2502019-06-18 21:44:24 -0400828 ports = self.tcpserial_portnum.split(':')
829 port = ports[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600830 if self.get('QB_TCPSERIAL_OPT'):
Brad Bishop15ae2502019-06-18 21:44:24 -0400831 self.qemu_opt_script += ' ' + self.get('QB_TCPSERIAL_OPT').replace('@PORT@', port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600832 else:
Brad Bishop15ae2502019-06-18 21:44:24 -0400833 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
834
835 if len(ports) > 1:
836 for port in ports[1:]:
837 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500838
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600839 def check_and_set(self):
840 """Check configs sanity and set when needed"""
841 self.validate_paths()
Andrew Geissler82c905d2020-04-13 13:39:40 -0500842 if not self.slirp_enabled and not self.net_bridge:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500843 check_tun()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600844 # Check audio
845 if self.audio_enabled:
846 if not self.get('QB_AUDIO_DRV'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500847 raise RunQemuError("QB_AUDIO_DRV is NULL, this board doesn't support audio")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600848 if not self.get('QB_AUDIO_OPT'):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800849 logger.warning('QB_AUDIO_OPT is NULL, you may need define it to make audio work')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600850 else:
851 self.qemu_opt_script += ' %s' % self.get('QB_AUDIO_OPT')
852 os.putenv('QEMU_AUDIO_DRV', self.get('QB_AUDIO_DRV'))
853 else:
854 os.putenv('QEMU_AUDIO_DRV', 'none')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500855
Brad Bishop15ae2502019-06-18 21:44:24 -0400856 self.check_qemu_system()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600857 self.check_kvm()
858 self.check_fstype()
859 self.check_rootfs()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500860 self.check_ovmf()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600861 self.check_kernel()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400862 self.check_dtb()
Brad Bishopc68388fc2019-08-26 01:33:31 -0400863 self.check_bios()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600864 self.check_mem()
865 self.check_tcpserial()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500866
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600867 def read_qemuboot(self):
868 if not self.qemuboot:
869 if self.get('DEPLOY_DIR_IMAGE'):
870 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600871 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800872 logger.warning("Can't find qemuboot conf file, DEPLOY_DIR_IMAGE is NULL!")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600873 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500874
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600875 if self.rootfs and not os.path.exists(self.rootfs):
876 # Lazy rootfs
877 machine = self.get('MACHINE')
878 if not machine:
879 machine = os.path.basename(deploy_dir_image)
880 self.qemuboot = "%s/%s-%s.qemuboot.conf" % (deploy_dir_image,
881 self.rootfs, machine)
882 else:
883 cmd = 'ls -t %s/*.qemuboot.conf' % deploy_dir_image
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500884 logger.debug('Running %s...' % cmd)
885 try:
886 qbs = subprocess.check_output(cmd, shell=True).decode('utf-8')
887 except subprocess.CalledProcessError as err:
888 raise RunQemuError(err)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600889 if qbs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500890 for qb in qbs.split():
891 # Don't use initramfs when other choices unless fstype is ramfs
892 if '-initramfs-' in os.path.basename(qb) and self.fstype != 'cpio.gz':
893 continue
894 self.qemuboot = qb
895 break
896 if not self.qemuboot:
897 # Use the first one when no choice
898 self.qemuboot = qbs.split()[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600899 self.qbconfload = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500900
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600901 if not self.qemuboot:
902 # If we haven't found a .qemuboot.conf at this point it probably
903 # doesn't exist, continue without
904 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500905
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600906 if not os.path.exists(self.qemuboot):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500907 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 -0500908
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500909 logger.debug('CONFFILE: %s' % self.qemuboot)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500910
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600911 cf = configparser.ConfigParser()
912 cf.read(self.qemuboot)
913 for k, v in cf.items('config_bsp'):
914 k_upper = k.upper()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500915 if v.startswith("../"):
916 v = os.path.abspath(os.path.dirname(self.qemuboot) + "/" + v)
917 elif v == ".":
918 v = os.path.dirname(self.qemuboot)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600919 self.set(k_upper, v)
920
921 def validate_paths(self):
922 """Ensure all relevant path variables are set"""
923 # When we're started with a *.qemuboot.conf arg assume that image
924 # artefacts are relative to that file, rather than in whatever
925 # directory DEPLOY_DIR_IMAGE in the conf file points to.
926 if self.qbconfload:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500927 imgdir = os.path.realpath(os.path.dirname(self.qemuboot))
928 if imgdir != os.path.realpath(self.get('DEPLOY_DIR_IMAGE')):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600929 logger.info('Setting DEPLOY_DIR_IMAGE to folder containing %s (%s)' % (self.qemuboot, imgdir))
930 self.set('DEPLOY_DIR_IMAGE', imgdir)
931
932 # If the STAGING_*_NATIVE directories from the config file don't exist
933 # and we're in a sourced OE build directory try to extract the paths
934 # from `bitbake -e`
935 havenative = os.path.exists(self.get('STAGING_DIR_NATIVE')) and \
936 os.path.exists(self.get('STAGING_BINDIR_NATIVE'))
937
938 if not havenative:
939 if not self.bitbake_e:
940 self.load_bitbake_env()
941
942 if self.bitbake_e:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500943 native_vars = ['STAGING_DIR_NATIVE']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600944 for nv in native_vars:
945 s = re.search('^%s="(.*)"' % nv, self.bitbake_e, re.M)
946 if s and s.group(1) != self.get(nv):
947 logger.info('Overriding conf file setting of %s to %s from Bitbake environment' % (nv, s.group(1)))
948 self.set(nv, s.group(1))
949 else:
950 # when we're invoked from a running bitbake instance we won't
951 # be able to call `bitbake -e`, then try:
952 # - get OE_TMPDIR from environment and guess paths based on it
953 # - get OECORE_NATIVE_SYSROOT from environment (for sdk)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500954 tmpdir = self.get('OE_TMPDIR')
955 oecore_native_sysroot = self.get('OECORE_NATIVE_SYSROOT')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600956 if tmpdir:
957 logger.info('Setting STAGING_DIR_NATIVE and STAGING_BINDIR_NATIVE relative to OE_TMPDIR (%s)' % tmpdir)
958 hostos, _, _, _, machine = os.uname()
959 buildsys = '%s-%s' % (machine, hostos.lower())
960 staging_dir_native = '%s/sysroots/%s' % (tmpdir, buildsys)
961 self.set('STAGING_DIR_NATIVE', staging_dir_native)
962 elif oecore_native_sysroot:
963 logger.info('Setting STAGING_DIR_NATIVE to OECORE_NATIVE_SYSROOT (%s)' % oecore_native_sysroot)
964 self.set('STAGING_DIR_NATIVE', oecore_native_sysroot)
965 if self.get('STAGING_DIR_NATIVE'):
966 # we have to assume that STAGING_BINDIR_NATIVE is at usr/bin
967 staging_bindir_native = '%s/usr/bin' % self.get('STAGING_DIR_NATIVE')
968 logger.info('Setting STAGING_BINDIR_NATIVE to %s' % staging_bindir_native)
969 self.set('STAGING_BINDIR_NATIVE', '%s/usr/bin' % self.get('STAGING_DIR_NATIVE'))
970
971 def print_config(self):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500972 logoutput = ['Continuing with the following parameters:']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600973 if not self.fstype in self.vmtypes:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500974 logoutput.append('KERNEL: [%s]' % self.kernel)
Brad Bishopc68388fc2019-08-26 01:33:31 -0400975 if self.bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500976 logoutput.append('BIOS: [%s]' % self.bios)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600977 if self.dtb:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500978 logoutput.append('DTB: [%s]' % self.dtb)
979 logoutput.append('MACHINE: [%s]' % self.get('MACHINE'))
Brad Bishop15ae2502019-06-18 21:44:24 -0400980 try:
981 fstype_flags = ' (' + ', '.join(self.fsinfo[self.fstype]) + ')'
982 except KeyError:
983 fstype_flags = ''
Andrew Geissler82c905d2020-04-13 13:39:40 -0500984 logoutput.append('FSTYPE: [%s%s]' % (self.fstype, fstype_flags))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600985 if self.fstype == 'nfs':
Andrew Geissler82c905d2020-04-13 13:39:40 -0500986 logoutput.append('NFS_DIR: [%s]' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600987 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500988 logoutput.append('ROOTFS: [%s]' % self.rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500989 if self.ovmf_bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500990 logoutput.append('OVMF: %s' % self.ovmf_bios)
Brad Bishop08902b02019-08-20 09:16:51 -0400991 if (self.ovmf_secboot_pkkek1):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500992 logoutput.append('SECBOOT PKKEK1: [%s...]' % self.ovmf_secboot_pkkek1[0:100])
993 logoutput.append('CONFFILE: [%s]' % self.qemuboot)
994 logoutput.append('')
995 logger.info('\n'.join(logoutput))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600996
997 def setup_nfs(self):
998 if not self.nfs_server:
999 if self.slirp_enabled:
1000 self.nfs_server = '10.0.2.2'
1001 else:
Andrew Geissler517393d2023-01-13 08:55:19 -06001002 self.nfs_server = '192.168.7.@GATEWAY@'
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001003
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001004 # Figure out a new nfs_instance to allow multiple qemus running.
Brad Bishop977dc1a2019-02-06 16:01:43 -05001005 ps = subprocess.check_output(("ps", "auxww")).decode('utf-8')
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001006 pattern = '/bin/unfsd .* -i .*\.pid -e .*/exports([0-9]+) '
1007 all_instances = re.findall(pattern, ps, re.M)
1008 if all_instances:
1009 all_instances.sort(key=int)
1010 self.nfs_instance = int(all_instances.pop()) + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001011
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001012 nfsd_port = 3049 + 2 * self.nfs_instance
1013 mountd_port = 3048 + 2 * self.nfs_instance
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001014
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001015 # Export vars for runqemu-export-rootfs
1016 export_dict = {
1017 'NFS_INSTANCE': self.nfs_instance,
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001018 'NFSD_PORT': nfsd_port,
1019 'MOUNTD_PORT': mountd_port,
1020 }
1021 for k, v in export_dict.items():
1022 # Use '%s' since they are integers
1023 os.putenv(k, '%s' % v)
1024
Andrew Geisslerc5535c92023-01-27 16:10:19 -06001025 qb_nfsrootfs_extra_opt = self.get("QB_NFSROOTFS_EXTRA_OPT")
1026 if qb_nfsrootfs_extra_opt and not qb_nfsrootfs_extra_opt.startswith(","):
1027 qb_nfsrootfs_extra_opt = "," + qb_nfsrootfs_extra_opt
1028
1029 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 -06001030
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001031 # Extract .tar.bz2 or .tar.bz if no nfs dir
1032 if not (self.rootfs and os.path.isdir(self.rootfs)):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001033 src_prefix = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'))
1034 dest = "%s-nfsroot" % src_prefix
1035 if os.path.exists('%s.pseudo_state' % dest):
1036 logger.info('Use %s as NFS_DIR' % dest)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001037 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001038 else:
1039 src = ""
1040 src1 = '%s.tar.bz2' % src_prefix
1041 src2 = '%s.tar.gz' % src_prefix
1042 if os.path.exists(src1):
1043 src = src1
1044 elif os.path.exists(src2):
1045 src = src2
1046 if not src:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001047 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 -06001048 logger.info('NFS_DIR not found, extracting %s to %s' % (src, dest))
Brad Bishop977dc1a2019-02-06 16:01:43 -05001049 cmd = ('runqemu-extract-sdk', src, dest)
1050 logger.info('Running %s...' % str(cmd))
1051 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001052 raise RunQemuError('Failed to run %s' % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001053 self.rootfs = dest
Andrew Geisslerc926e172021-05-07 16:11:35 -05001054 self.cleanup_files.append(self.rootfs)
1055 self.cleanup_files.append('%s.pseudo_state' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001056
1057 # Start the userspace NFS server
Brad Bishop977dc1a2019-02-06 16:01:43 -05001058 cmd = ('runqemu-export-rootfs', 'start', self.rootfs)
1059 logger.info('Running %s...' % str(cmd))
1060 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001061 raise RunQemuError('Failed to run %s' % cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001062
1063 self.nfs_running = True
1064
Andrew Geissler517393d2023-01-13 08:55:19 -06001065 def setup_cmd(self):
1066 cmd = self.get('QB_SETUP_CMD')
1067 if cmd != '':
1068 logger.info('Running setup command %s' % str(cmd))
1069 if subprocess.call(cmd, shell=True) != 0:
1070 raise RunQemuError('Failed to run %s' % cmd)
1071
Andrew Geissler82c905d2020-04-13 13:39:40 -05001072 def setup_net_bridge(self):
1073 self.set('NETWORK_CMD', '-netdev bridge,br=%s,id=net0,helper=%s -device virtio-net-pci,netdev=net0 ' % (
1074 self.net_bridge, os.path.join(self.bindir_native, 'qemu-oe-bridge-helper')))
1075
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001076 def setup_slirp(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001077 """Setup user networking"""
1078
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001079 if self.fstype == 'nfs':
1080 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001081 netconf = " " + self.cmdline_ip_slirp
1082 logger.info("Network configuration:%s", netconf)
1083 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001084 # Port mapping
Andrew Geissler517393d2023-01-13 08:55:19 -06001085 hostfwd = ",hostfwd=tcp:127.0.0.1:2222-:22,hostfwd=tcp:127.0.0.1:2323-:23"
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001086 qb_slirp_opt_default = "-netdev user,id=net0%s,tftp=%s" % (hostfwd, self.get('DEPLOY_DIR_IMAGE'))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001087 qb_slirp_opt = self.get('QB_SLIRP_OPT') or qb_slirp_opt_default
1088 # Figure out the port
1089 ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt)
1090 ports = [int(i) for i in ports]
1091 mac = 2
Brad Bishop08902b02019-08-20 09:16:51 -04001092
1093 lockdir = "/tmp/qemu-port-locks"
1094 if not os.path.exists(lockdir):
1095 # There might be a race issue when multi runqemu processess are
1096 # running at the same time.
1097 try:
1098 os.mkdir(lockdir)
1099 os.chmod(lockdir, 0o777)
1100 except FileExistsError:
1101 pass
1102
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001103 # Find a free port to avoid conflicts
1104 for p in ports[:]:
1105 p_new = p
Brad Bishop08902b02019-08-20 09:16:51 -04001106 while not self.check_free_port('localhost', p_new, lockdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001107 p_new += 1
1108 mac += 1
1109 while p_new in ports:
1110 p_new += 1
1111 mac += 1
1112 if p != p_new:
1113 ports.append(p_new)
1114 qb_slirp_opt = re.sub(':%s-' % p, ':%s-' % p_new, qb_slirp_opt)
1115 logger.info("Port forward changed: %s -> %s" % (p, p_new))
1116 mac = "%s%02x" % (self.mac_slirp, mac)
1117 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qb_slirp_opt))
1118 # Print out port foward
1119 hostfwd = re.findall('(hostfwd=[^,]*)', qb_slirp_opt)
1120 if hostfwd:
1121 logger.info('Port forward: %s' % ' '.join(hostfwd))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001122
1123 def setup_tap(self):
1124 """Setup tap"""
1125
1126 # This file is created when runqemu-gen-tapdevs creates a bank of tap
1127 # devices, indicating that the user should not bring up new ones using
1128 # sudo.
1129 nosudo_flag = '/etc/runqemu-nosudo'
1130 self.qemuifup = shutil.which('runqemu-ifup')
1131 self.qemuifdown = shutil.which('runqemu-ifdown')
1132 ip = shutil.which('ip')
1133 lockdir = "/tmp/qemu-tap-locks"
1134
1135 if not (self.qemuifup and self.qemuifdown and ip):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001136 logger.error("runqemu-ifup: %s" % self.qemuifup)
1137 logger.error("runqemu-ifdown: %s" % self.qemuifdown)
1138 logger.error("ip: %s" % ip)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001139 raise OEPathError("runqemu-ifup, runqemu-ifdown or ip not found")
1140
1141 if not os.path.exists(lockdir):
1142 # There might be a race issue when multi runqemu processess are
1143 # running at the same time.
1144 try:
1145 os.mkdir(lockdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001146 os.chmod(lockdir, 0o777)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001147 except FileExistsError:
1148 pass
1149
Brad Bishop977dc1a2019-02-06 16:01:43 -05001150 cmd = (ip, 'link')
1151 logger.debug('Running %s...' % str(cmd))
1152 ip_link = subprocess.check_output(cmd).decode('utf-8')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001153 # Matches line like: 6: tap0: <foo>
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001154 possibles = re.findall('^[0-9]+: +(tap[0-9]+): <.*', ip_link, re.M)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001155 tap = ""
1156 for p in possibles:
1157 lockfile = os.path.join(lockdir, p)
1158 if os.path.exists('%s.skip' % lockfile):
1159 logger.info('Found %s.skip, skipping %s' % (lockfile, p))
1160 continue
Brad Bishop08902b02019-08-20 09:16:51 -04001161 self.taplock = lockfile + '.lock'
1162 if self.acquire_taplock(error=False):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001163 tap = p
1164 logger.info("Using preconfigured tap device %s" % tap)
1165 logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap))
1166 break
1167
1168 if not tap:
1169 if os.path.exists(nosudo_flag):
1170 logger.error("Error: There are no available tap devices to use for networking,")
1171 logger.error("and I see %s exists, so I am not going to try creating" % nosudo_flag)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001172 raise RunQemuError("a new one with sudo.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001173
1174 gid = os.getgid()
1175 uid = os.getuid()
1176 logger.info("Setting up tap interface under sudo")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001177 cmd = ('sudo', self.qemuifup, str(uid), str(gid), self.bindir_native)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001178 try:
1179 tap = subprocess.check_output(cmd).decode('utf-8').strip()
1180 except subprocess.CalledProcessError as e:
1181 logger.error('Setting up tap device failed:\n%s\nRun runqemu-gen-tapdevs to manually create one.' % str(e))
1182 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001183 lockfile = os.path.join(lockdir, tap)
Brad Bishop08902b02019-08-20 09:16:51 -04001184 self.taplock = lockfile + '.lock'
1185 self.acquire_taplock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001186 self.cleantap = True
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001187 logger.debug('Created tap: %s' % tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001188
1189 if not tap:
1190 logger.error("Failed to setup tap device. Run runqemu-gen-tapdevs to manually create.")
Andrew Geissler82c905d2020-04-13 13:39:40 -05001191 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001192 self.tap = tap
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001193 tapnum = int(tap[3:])
1194 gateway = tapnum * 2 + 1
1195 client = gateway + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001196 if self.fstype == 'nfs':
1197 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001198 netconf = " " + self.cmdline_ip_tap
1199 netconf = netconf.replace('@CLIENT@', str(client))
1200 netconf = netconf.replace('@GATEWAY@', str(gateway))
Andrew Geissler517393d2023-01-13 08:55:19 -06001201 self.nfs_server = self.nfs_server.replace('@GATEWAY@', str(gateway))
Andrew Geissler82c905d2020-04-13 13:39:40 -05001202 logger.info("Network configuration:%s", netconf)
1203 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001204 mac = "%s%02x" % (self.mac_tap, client)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001205 qb_tap_opt = self.get('QB_TAP_OPT')
1206 if qb_tap_opt:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001207 qemu_tap_opt = qb_tap_opt.replace('@TAP@', tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001208 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001209 qemu_tap_opt = "-netdev tap,id=net0,ifname=%s,script=no,downscript=no" % (self.tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001210
1211 if self.vhost_enabled:
1212 qemu_tap_opt += ',vhost=on'
1213
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001214 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qemu_tap_opt))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001215
1216 def setup_network(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001217 if self.get('QB_NET') == 'none':
1218 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001219 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001220 self.saved_stty = subprocess.check_output(("stty", "-g")).decode('utf-8').strip()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001221 self.network_device = self.get('QB_NETWORK_DEVICE') or self.network_device
Andrew Geissler82c905d2020-04-13 13:39:40 -05001222 if self.net_bridge:
1223 self.setup_net_bridge()
1224 elif self.slirp_enabled:
1225 self.cmdline_ip_slirp = self.get('QB_CMDLINE_IP_SLIRP') or self.cmdline_ip_slirp
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001226 self.setup_slirp()
1227 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001228 self.cmdline_ip_tap = self.get('QB_CMDLINE_IP_TAP') or self.cmdline_ip_tap
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001229 self.setup_tap()
1230
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001231 def setup_rootfs(self):
1232 if self.get('QB_ROOTFS') == 'none':
1233 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001234 if 'wic.' in self.fstype:
1235 self.fstype = self.fstype[4:]
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001236 rootfs_format = self.fstype if self.fstype in ('vmdk', 'vhd', 'vhdx', 'qcow2', 'vdi') else 'raw'
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001237
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001238 tmpfsdir = os.environ.get("RUNQEMU_TMPFS_DIR", None)
1239 if self.snapshot and tmpfsdir:
1240 newrootfs = os.path.join(tmpfsdir, os.path.basename(self.rootfs)) + "." + str(os.getpid())
Andrew Geissler09036742021-06-25 14:25:14 -05001241 logger.info("Copying rootfs to %s" % newrootfs)
1242 copy_start = time.time()
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001243 shutil.copyfile(self.rootfs, newrootfs)
Andrew Geissler09036742021-06-25 14:25:14 -05001244 logger.info("Copy done in %s seconds" % (time.time() - copy_start))
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001245 self.rootfs = newrootfs
1246 # Don't need a second copy now!
1247 self.snapshot = False
Andrew Geisslerc926e172021-05-07 16:11:35 -05001248 self.cleanup_files.append(newrootfs)
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001249
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001250 qb_rootfs_opt = self.get('QB_ROOTFS_OPT')
1251 if qb_rootfs_opt:
1252 self.rootfs_options = qb_rootfs_opt.replace('@ROOTFS@', self.rootfs)
1253 else:
1254 self.rootfs_options = '-drive file=%s,if=virtio,format=%s' % (self.rootfs, rootfs_format)
1255
Andrew Geissler82c905d2020-04-13 13:39:40 -05001256 qb_rootfs_extra_opt = self.get("QB_ROOTFS_EXTRA_OPT")
1257 if qb_rootfs_extra_opt and not qb_rootfs_extra_opt.startswith(","):
1258 qb_rootfs_extra_opt = "," + qb_rootfs_extra_opt
1259
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001260 if self.fstype in ('cpio.gz', 'cpio'):
1261 self.kernel_cmdline = 'root=/dev/ram0 rw debugshell'
1262 self.rootfs_options = '-initrd %s' % self.rootfs
1263 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001264 vm_drive = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001265 if self.fstype in self.vmtypes:
1266 if self.fstype == 'iso':
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001267 vm_drive = '-drive file=%s,if=virtio,media=cdrom' % self.rootfs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001268 elif self.get('QB_DRIVE_TYPE'):
1269 drive_type = self.get('QB_DRIVE_TYPE')
1270 if drive_type.startswith("/dev/sd"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001271 logger.info('Using scsi drive')
Andrew Geissler82c905d2020-04-13 13:39:40 -05001272 vm_drive = '-drive if=none,id=hd,file=%s,format=%s -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd%s' \
1273 % (self.rootfs, rootfs_format, qb_rootfs_extra_opt)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001274 elif drive_type.startswith("/dev/hd"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001275 logger.info('Using ide drive')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001276 vm_drive = "-drive file=%s,format=%s" % (self.rootfs, rootfs_format)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001277 elif drive_type.startswith("/dev/vdb"):
1278 logger.info('Using block virtio drive');
1279 vm_drive = '-drive id=disk0,file=%s,if=none,format=%s -device virtio-blk-device,drive=disk0%s' \
1280 % (self.rootfs, rootfs_format,qb_rootfs_extra_opt)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001281 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001282 # virtio might have been selected explicitly (just use it), or
1283 # is used as fallback (then warn about that).
1284 if not drive_type.startswith("/dev/vd"):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001285 logger.warning("Unknown QB_DRIVE_TYPE: %s" % drive_type)
1286 logger.warning("Failed to figure out drive type, consider define or fix QB_DRIVE_TYPE")
1287 logger.warning('Trying to use virtio block drive')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001288 vm_drive = '-drive if=virtio,file=%s,format=%s' % (self.rootfs, rootfs_format)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001289
1290 # All branches above set vm_drive.
Andrew Geissler475cb722020-07-10 16:00:51 -05001291 self.rootfs_options = vm_drive
1292 if not self.fstype in self.vmtypes:
1293 self.rootfs_options += ' -no-reboot'
Andrew Geissler595f6302022-01-24 19:11:47 +00001294
1295 # By default, ' rw' is appended to QB_KERNEL_ROOT unless either ro or rw is explicitly passed.
1296 qb_kernel_root = self.get('QB_KERNEL_ROOT')
1297 qb_kernel_root_l = qb_kernel_root.split()
1298 if not ('ro' in qb_kernel_root_l or 'rw' in qb_kernel_root_l):
1299 qb_kernel_root += ' rw'
1300 self.kernel_cmdline = 'root=%s' % qb_kernel_root
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001301
1302 if self.fstype == 'nfs':
1303 self.rootfs_options = ''
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001304 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 -04001305 self.kernel_cmdline = 'root=%s rw' % k_root
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001306
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001307 if self.fstype == 'none':
1308 self.rootfs_options = ''
1309
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001310 self.set('ROOTFS_OPTIONS', self.rootfs_options)
1311
1312 def guess_qb_system(self):
1313 """attempt to determine the appropriate qemu-system binary"""
1314 mach = self.get('MACHINE')
1315 if not mach:
Patrick Williams864cc432023-02-09 14:54:44 -06001316 search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemuloongarch64|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*'
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001317 if self.rootfs:
1318 match = re.match(search, self.rootfs)
1319 if match:
1320 mach = match.group(1)
1321 elif self.kernel:
1322 match = re.match(search, self.kernel)
1323 if match:
1324 mach = match.group(1)
1325
1326 if not mach:
1327 return None
1328
1329 if mach == 'qemuarm':
1330 qbsys = 'arm'
1331 elif mach == 'qemuarm64':
1332 qbsys = 'aarch64'
1333 elif mach == 'qemux86':
1334 qbsys = 'i386'
1335 elif mach == 'qemux86-64':
1336 qbsys = 'x86_64'
1337 elif mach == 'qemuppc':
1338 qbsys = 'ppc'
Patrick Williams864cc432023-02-09 14:54:44 -06001339 elif mach == 'qemuloongarch64':
1340 qbsys = 'loongarch64'
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001341 elif mach == 'qemumips':
1342 qbsys = 'mips'
1343 elif mach == 'qemumips64':
1344 qbsys = 'mips64'
1345 elif mach == 'qemumipsel':
1346 qbsys = 'mipsel'
1347 elif mach == 'qemumips64el':
1348 qbsys = 'mips64el'
Brad Bishop316dfdd2018-06-25 12:45:53 -04001349 elif mach == 'qemuriscv64':
1350 qbsys = 'riscv64'
1351 elif mach == 'qemuriscv32':
1352 qbsys = 'riscv32'
Brad Bishop004d4992018-10-02 23:54:45 +02001353 else:
1354 logger.error("Unable to determine QEMU PC System emulator for %s machine." % mach)
1355 logger.error("As %s is not among valid QEMU machines such as," % mach)
1356 logger.error("qemux86-64, qemux86, qemuarm64, qemuarm, qemumips64, qemumips64el, qemumipsel, qemumips, qemuppc")
1357 raise RunQemuError("Set qb_system_name with suitable QEMU PC System emulator in .*qemuboot.conf.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001358
1359 return 'qemu-system-%s' % qbsys
1360
Brad Bishop15ae2502019-06-18 21:44:24 -04001361 def check_qemu_system(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001362 qemu_system = self.get('QB_SYSTEM_NAME')
1363 if not qemu_system:
1364 qemu_system = self.guess_qb_system()
1365 if not qemu_system:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001366 raise RunQemuError("Failed to boot, QB_SYSTEM_NAME is NULL!")
Brad Bishop15ae2502019-06-18 21:44:24 -04001367 self.qemu_system = qemu_system
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001368
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001369 def check_render_nodes(self):
1370 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."""
1371 try:
1372 content = os.listdir("/dev/dri")
1373 if len([i for i in content if i.startswith('render')]) == 0:
1374 raise RunQemuError("No render nodes found in /dev/dri: %s. %s" %(content, render_hint))
1375 except FileNotFoundError:
1376 raise RunQemuError("/dev/dri directory does not exist; no render nodes available on this machine. %s" %(render_hint))
1377
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001378 def setup_vga(self):
1379 if self.nographic == True:
1380 if self.sdl == True:
1381 raise RunQemuError('Option nographic makes no sense alongside the sdl option.')
1382 if self.gtk == True:
1383 raise RunQemuError('Option nographic makes no sense alongside the gtk option.')
1384 self.qemu_opt += ' -nographic'
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001385
1386 if self.novga == True:
1387 self.qemu_opt += ' -vga none'
1388 return
1389
1390 if (self.gl_es == True or self.gl == True) and (self.sdl == False and self.gtk == False):
1391 raise RunQemuError('Option gl/gl-es needs gtk or sdl option.')
1392
Patrick Williams03907ee2022-05-01 06:28:52 -05001393 # If we have no display option, we autodetect based upon what qemu supports. We
1394 # need our font setup and show-cusor below so we need to see what qemu --help says
1395 # is supported so we can pass our correct config in.
1396 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 -05001397 output = subprocess.check_output([self.qemu_bin, "--help"], universal_newlines=True, env=self.qemu_environ)
Patrick Williams03907ee2022-05-01 06:28:52 -05001398 if "-display gtk" in output:
1399 self.gtk = True
1400 elif "-display sdl" in output:
1401 self.sdl = True
Andrew Geissler595f6302022-01-24 19:11:47 +00001402 else:
Patrick Williamsdb4c27e2022-08-05 08:10:29 -05001403 self.qemu_opt += ' -display none'
Andrew Geissler595f6302022-01-24 19:11:47 +00001404
Patrick Williams03907ee2022-05-01 06:28:52 -05001405 if self.sdl == True or self.gtk == True or self.egl_headless == True:
1406
1407 if self.qemu_system.endswith(('i386', 'x86_64')):
1408 if self.gl or self.gl_es or self.egl_headless:
1409 self.qemu_opt += ' -device virtio-vga-gl '
1410 else:
1411 self.qemu_opt += ' -device virtio-vga '
1412
1413 self.qemu_opt += ' -display '
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001414 if self.egl_headless == True:
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001415 self.check_render_nodes()
Andrew Geissler595f6302022-01-24 19:11:47 +00001416 self.set_dri_path()
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001417 self.qemu_opt += 'egl-headless,'
1418 else:
1419 if self.sdl == True:
1420 self.qemu_opt += 'sdl,'
1421 elif self.gtk == True:
Patrick Williams2390b1b2022-11-03 13:47:49 -05001422 self.qemu_environ['FONTCONFIG_PATH'] = '/etc/fonts'
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001423 self.qemu_opt += 'gtk,'
1424
1425 if self.gl == True:
Andrew Geissler595f6302022-01-24 19:11:47 +00001426 self.set_dri_path()
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001427 self.qemu_opt += 'gl=on,'
1428 elif self.gl_es == True:
Andrew Geissler595f6302022-01-24 19:11:47 +00001429 self.set_dri_path()
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001430 self.qemu_opt += 'gl=es,'
1431 self.qemu_opt += 'show-cursor=on'
1432
1433 self.qemu_opt += ' %s' %self.get('QB_GRAPHICS')
1434
1435 def setup_serial(self):
1436 # Setup correct kernel command line for serial
Andrew Geissler595f6302022-01-24 19:11:47 +00001437 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 -05001438 for entry in self.get('SERIAL_CONSOLES').split(' '):
1439 self.kernel_cmdline_script += ' console=%s' %entry.split(';')[1]
1440
1441 if self.serialstdio == True or self.nographic == True:
1442 self.qemu_opt += " -serial mon:stdio"
1443 else:
1444 self.qemu_opt += " -serial mon:vc"
1445 if self.serialconsole:
1446 if sys.stdin.isatty():
1447 subprocess.check_call(("stty", "intr", "^]"))
1448 logger.info("Interrupt character is '^]'")
1449
1450 self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT")
1451
1452 # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES).
1453 # If no serial or serialtcp options were specified, only ttyS0 is created
1454 # and sysvinit shows an error trying to enable ttyS1:
1455 # INIT: Id "S1" respawning too fast: disabled for 5 minutes
1456 serial_num = len(re.findall("-serial", self.qemu_opt))
1457 if serial_num < 2:
1458 self.qemu_opt += " -serial null"
1459
Patrick Williams03907ee2022-05-01 06:28:52 -05001460 def find_qemu(self):
Brad Bishop15ae2502019-06-18 21:44:24 -04001461 qemu_bin = os.path.join(self.bindir_native, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001462
1463 # It is possible to have qemu-native in ASSUME_PROVIDED, and it won't
1464 # find QEMU in sysroot, it needs to use host's qemu.
1465 if not os.path.exists(qemu_bin):
1466 logger.info("QEMU binary not found in %s, trying host's QEMU" % qemu_bin)
1467 for path in (os.environ['PATH'] or '').split(':'):
Brad Bishop15ae2502019-06-18 21:44:24 -04001468 qemu_bin_tmp = os.path.join(path, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001469 logger.info("Trying: %s" % qemu_bin_tmp)
1470 if os.path.exists(qemu_bin_tmp):
1471 qemu_bin = qemu_bin_tmp
1472 if not os.path.isabs(qemu_bin):
1473 qemu_bin = os.path.abspath(qemu_bin)
1474 logger.info("Using host's QEMU: %s" % qemu_bin)
1475 break
1476
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001477 if not os.access(qemu_bin, os.X_OK):
1478 raise OEPathError("No QEMU binary '%s' could be found" % qemu_bin)
Patrick Williams03907ee2022-05-01 06:28:52 -05001479 self.qemu_bin = qemu_bin
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001480
Patrick Williams03907ee2022-05-01 06:28:52 -05001481 def setup_final(self):
1482
1483 self.find_qemu()
1484
1485 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 -05001486
1487 for ovmf in self.ovmf_bios:
1488 format = ovmf.rsplit('.', 1)[-1]
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001489 if format == "bin":
1490 format = "raw"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001491 self.qemu_opt += ' -drive if=pflash,format=%s,file=%s' % (format, ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001492
1493 self.qemu_opt += ' ' + self.qemu_opt_script
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001494
Brad Bishop08902b02019-08-20 09:16:51 -04001495 if self.ovmf_secboot_pkkek1:
1496 # Provide the Platform Key and first Key Exchange Key certificate as an
1497 # OEM string in the SMBIOS Type 11 table. Prepend the certificate string
1498 # with "application prefix" of the EnrollDefaultKeys.efi application
1499 self.qemu_opt += ' -smbios type=11,value=4e32566d-8e9e-4f52-81d3-5bb9715f9727:' \
1500 + self.ovmf_secboot_pkkek1
1501
Andrew Geissler99467da2019-02-25 18:54:23 -06001502 # Append qemuparams to override previous settings
1503 if self.qemuparams:
1504 self.qemu_opt += ' ' + self.qemuparams
1505
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001506 if self.snapshot:
1507 self.qemu_opt += " -snapshot"
1508
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001509 self.setup_serial()
1510 self.setup_vga()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001511
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001512 def start_qemu(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001513 import shlex
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001514 if self.kernel:
Andrew Geissler615f2f12022-07-15 14:00:58 -05001515 kernel_opts = "-kernel %s" % (self.kernel)
1516 if self.get('QB_KERNEL_CMDLINE') == "none":
1517 if self.bootparams:
1518 kernel_opts += " -append '%s'" % (self.bootparams)
1519 else:
1520 kernel_opts += " -append '%s %s %s %s'" % (self.kernel_cmdline,
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001521 self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'),
1522 self.bootparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001523 if self.dtb:
1524 kernel_opts += " -dtb %s" % self.dtb
1525 else:
1526 kernel_opts = ""
Patrick Williams213cb262021-08-07 19:21:33 -05001527
1528 if self.bios:
1529 self.qemu_opt += " -bios %s" % self.bios
1530
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001531 cmd = "%s %s" % (self.qemu_opt, kernel_opts)
Brad Bishop004d4992018-10-02 23:54:45 +02001532 cmds = shlex.split(cmd)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001533 logger.info('Running %s\n' % cmd)
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001534 with open('/proc/uptime', 'r') as f:
1535 uptime_seconds = f.readline().split()[0]
1536 logger.info('Host uptime: %s\n' % uptime_seconds)
Brad Bishopf86d0552018-12-04 14:18:15 -08001537 pass_fds = []
Brad Bishop08902b02019-08-20 09:16:51 -04001538 if self.taplock_descriptor:
1539 pass_fds = [self.taplock_descriptor.fileno()]
1540 if len(self.portlocks):
1541 for descriptor in self.portlocks.values():
1542 pass_fds.append(descriptor.fileno())
Patrick Williams2390b1b2022-11-03 13:47:49 -05001543 process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds, env=self.qemu_environ)
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001544 self.qemuprocess = process
Brad Bishop004d4992018-10-02 23:54:45 +02001545 retcode = process.wait()
1546 if retcode:
1547 if retcode == -signal.SIGTERM:
1548 logger.info("Qemu terminated by SIGTERM")
1549 else:
1550 logger.error("Failed to run qemu: %s", process.stderr.read().decode())
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001551
Andrew Geissler517393d2023-01-13 08:55:19 -06001552 def cleanup_cmd(self):
1553 cmd = self.get('QB_CLEANUP_CMD')
1554 if cmd != '':
1555 logger.info('Running cleanup command %s' % str(cmd))
1556 if subprocess.call(cmd, shell=True) != 0:
1557 raise RunQemuError('Failed to run %s' % cmd)
1558
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001559 def cleanup(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001560 if self.cleaned:
1561 return
1562
1563 # avoid dealing with SIGTERM when cleanup function is running
1564 signal.signal(signal.SIGTERM, signal.SIG_IGN)
1565
1566 logger.info("Cleaning up")
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001567
1568 if self.qemuprocess:
1569 try:
1570 # give it some time to shut down, ignore return values and output
1571 self.qemuprocess.send_signal(signal.SIGTERM)
1572 self.qemuprocess.communicate(timeout=5)
1573 except subprocess.TimeoutExpired:
1574 self.qemuprocess.kill()
1575
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001576 with open('/proc/uptime', 'r') as f:
1577 uptime_seconds = f.readline().split()[0]
1578 logger.info('Host uptime: %s\n' % uptime_seconds)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001579 if self.cleantap:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001580 cmd = ('sudo', self.qemuifdown, self.tap, self.bindir_native)
1581 logger.debug('Running %s' % str(cmd))
1582 subprocess.check_call(cmd)
Brad Bishop08902b02019-08-20 09:16:51 -04001583 self.release_taplock()
1584 self.release_portlock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001585
1586 if self.nfs_running:
1587 logger.info("Shutting down the userspace NFS server...")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001588 cmd = ("runqemu-export-rootfs", "stop", self.rootfs)
1589 logger.debug('Running %s' % str(cmd))
1590 subprocess.check_call(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001591
1592 if self.saved_stty:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001593 subprocess.check_call(("stty", self.saved_stty))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001594
Andrew Geisslerc926e172021-05-07 16:11:35 -05001595 if self.cleanup_files:
1596 for ent in self.cleanup_files:
1597 logger.info('Removing %s' % ent)
1598 if os.path.isfile(ent):
1599 os.remove(ent)
1600 else:
1601 shutil.rmtree(ent)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001602
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001603 # Deliberately ignore the return code of 'tput smam'.
1604 subprocess.call(["tput", "smam"])
1605
Brad Bishop004d4992018-10-02 23:54:45 +02001606 self.cleaned = True
1607
Andrew Geissler82c905d2020-04-13 13:39:40 -05001608 def run_bitbake_env(self, mach=None):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001609 bitbake = shutil.which('bitbake')
1610 if not bitbake:
1611 return
1612
1613 if not mach:
1614 mach = self.get('MACHINE')
1615
Andrew Geissler82c905d2020-04-13 13:39:40 -05001616 multiconfig = self.get('MULTICONFIG')
1617 if multiconfig:
1618 multiconfig = "mc:%s" % multiconfig
1619
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001620 if mach:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001621 cmd = 'MACHINE=%s bitbake -e %s' % (mach, multiconfig)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001622 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001623 cmd = 'bitbake -e %s' % multiconfig
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001624
1625 logger.info('Running %s...' % cmd)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001626 return subprocess.check_output(cmd, shell=True).decode('utf-8')
1627
1628 def load_bitbake_env(self, mach=None):
1629 if self.bitbake_e:
1630 return
1631
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001632 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001633 self.bitbake_e = self.run_bitbake_env(mach=mach)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001634 except subprocess.CalledProcessError as err:
1635 self.bitbake_e = ''
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001636 logger.warning("Couldn't run 'bitbake -e' to gather environment information:\n%s" % err.output.decode('utf-8'))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001637
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001638 def validate_combos(self):
1639 if (self.fstype in self.vmtypes) and self.kernel:
1640 raise RunQemuError("%s doesn't need kernel %s!" % (self.fstype, self.kernel))
1641
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001642 @property
1643 def bindir_native(self):
1644 result = self.get('STAGING_BINDIR_NATIVE')
1645 if result and os.path.exists(result):
1646 return result
1647
Andrew Geissler82c905d2020-04-13 13:39:40 -05001648 cmd = ['bitbake', '-e']
1649 multiconfig = self.get('MULTICONFIG')
1650 if multiconfig:
1651 cmd.append('mc:%s:qemu-helper-native' % multiconfig)
1652 else:
1653 cmd.append('qemu-helper-native')
1654
Brad Bishop977dc1a2019-02-06 16:01:43 -05001655 logger.info('Running %s...' % str(cmd))
1656 out = subprocess.check_output(cmd).decode('utf-8')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001657
1658 match = re.search('^STAGING_BINDIR_NATIVE="(.*)"', out, re.M)
1659 if match:
1660 result = match.group(1)
1661 if os.path.exists(result):
1662 self.set('STAGING_BINDIR_NATIVE', result)
1663 return result
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001664 raise RunQemuError("Native sysroot directory %s doesn't exist" % result)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001665 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001666 raise RunQemuError("Can't find STAGING_BINDIR_NATIVE in '%s' output" % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001667
1668
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001669def main():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001670 if "help" in sys.argv or '-h' in sys.argv or '--help' in sys.argv:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001671 print_usage()
1672 return 0
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001673 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001674 config = BaseConfig()
Brad Bishop004d4992018-10-02 23:54:45 +02001675
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001676 renice = os.path.expanduser("~/bin/runqemu-renice")
1677 if os.path.exists(renice):
1678 logger.info('Using %s to renice' % renice)
1679 subprocess.check_call([renice, str(os.getpid())])
1680
Brad Bishop004d4992018-10-02 23:54:45 +02001681 def sigterm_handler(signum, frame):
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001682 logger.info("Received signal: %s" % (signum))
Brad Bishop004d4992018-10-02 23:54:45 +02001683 config.cleanup()
Brad Bishop004d4992018-10-02 23:54:45 +02001684 signal.signal(signal.SIGTERM, sigterm_handler)
1685
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001686 config.check_args()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001687 config.read_qemuboot()
1688 config.check_and_set()
1689 # Check whether the combos is valid or not
1690 config.validate_combos()
1691 config.print_config()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001692 config.setup_network()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001693 config.setup_rootfs()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001694 config.setup_final()
Andrew Geissler517393d2023-01-13 08:55:19 -06001695 config.setup_cmd()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001696 config.start_qemu()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001697 except RunQemuError as err:
1698 logger.error(err)
1699 return 1
1700 except Exception as err:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001701 import traceback
1702 traceback.print_exc()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001703 return 1
1704 finally:
Andrew Geissler517393d2023-01-13 08:55:19 -06001705 config.cleanup_cmd()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001706 config.cleanup()
1707
1708if __name__ == "__main__":
1709 sys.exit(main())