blob: a6ea5785648bad3432adb0cffc260bacc049b340 [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()
Brad Bishop004d4992018-10-02 23:54:45 +0200214 self.qemupid = None
215 # 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:
1002 self.nfs_server = '192.168.7.1'
1003
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 Geissler82c905d2020-04-13 13:39:40 -05001025 self.unfs_opts="nfsvers=3,port=%s,tcp,mountport=%s" % (nfsd_port, mountd_port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001026
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001027 # Extract .tar.bz2 or .tar.bz if no nfs dir
1028 if not (self.rootfs and os.path.isdir(self.rootfs)):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001029 src_prefix = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'))
1030 dest = "%s-nfsroot" % src_prefix
1031 if os.path.exists('%s.pseudo_state' % dest):
1032 logger.info('Use %s as NFS_DIR' % dest)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001033 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001034 else:
1035 src = ""
1036 src1 = '%s.tar.bz2' % src_prefix
1037 src2 = '%s.tar.gz' % src_prefix
1038 if os.path.exists(src1):
1039 src = src1
1040 elif os.path.exists(src2):
1041 src = src2
1042 if not src:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001043 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 -06001044 logger.info('NFS_DIR not found, extracting %s to %s' % (src, dest))
Brad Bishop977dc1a2019-02-06 16:01:43 -05001045 cmd = ('runqemu-extract-sdk', src, dest)
1046 logger.info('Running %s...' % str(cmd))
1047 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001048 raise RunQemuError('Failed to run %s' % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001049 self.rootfs = dest
Andrew Geisslerc926e172021-05-07 16:11:35 -05001050 self.cleanup_files.append(self.rootfs)
1051 self.cleanup_files.append('%s.pseudo_state' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001052
1053 # Start the userspace NFS server
Brad Bishop977dc1a2019-02-06 16:01:43 -05001054 cmd = ('runqemu-export-rootfs', 'start', self.rootfs)
1055 logger.info('Running %s...' % str(cmd))
1056 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001057 raise RunQemuError('Failed to run %s' % cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001058
1059 self.nfs_running = True
1060
Andrew Geissler82c905d2020-04-13 13:39:40 -05001061 def setup_net_bridge(self):
1062 self.set('NETWORK_CMD', '-netdev bridge,br=%s,id=net0,helper=%s -device virtio-net-pci,netdev=net0 ' % (
1063 self.net_bridge, os.path.join(self.bindir_native, 'qemu-oe-bridge-helper')))
1064
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001065 def setup_slirp(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001066 """Setup user networking"""
1067
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001068 if self.fstype == 'nfs':
1069 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001070 netconf = " " + self.cmdline_ip_slirp
1071 logger.info("Network configuration:%s", netconf)
1072 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001073 # Port mapping
1074 hostfwd = ",hostfwd=tcp::2222-:22,hostfwd=tcp::2323-:23"
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001075 qb_slirp_opt_default = "-netdev user,id=net0%s,tftp=%s" % (hostfwd, self.get('DEPLOY_DIR_IMAGE'))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001076 qb_slirp_opt = self.get('QB_SLIRP_OPT') or qb_slirp_opt_default
1077 # Figure out the port
1078 ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt)
1079 ports = [int(i) for i in ports]
1080 mac = 2
Brad Bishop08902b02019-08-20 09:16:51 -04001081
1082 lockdir = "/tmp/qemu-port-locks"
1083 if not os.path.exists(lockdir):
1084 # There might be a race issue when multi runqemu processess are
1085 # running at the same time.
1086 try:
1087 os.mkdir(lockdir)
1088 os.chmod(lockdir, 0o777)
1089 except FileExistsError:
1090 pass
1091
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001092 # Find a free port to avoid conflicts
1093 for p in ports[:]:
1094 p_new = p
Brad Bishop08902b02019-08-20 09:16:51 -04001095 while not self.check_free_port('localhost', p_new, lockdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001096 p_new += 1
1097 mac += 1
1098 while p_new in ports:
1099 p_new += 1
1100 mac += 1
1101 if p != p_new:
1102 ports.append(p_new)
1103 qb_slirp_opt = re.sub(':%s-' % p, ':%s-' % p_new, qb_slirp_opt)
1104 logger.info("Port forward changed: %s -> %s" % (p, p_new))
1105 mac = "%s%02x" % (self.mac_slirp, mac)
1106 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qb_slirp_opt))
1107 # Print out port foward
1108 hostfwd = re.findall('(hostfwd=[^,]*)', qb_slirp_opt)
1109 if hostfwd:
1110 logger.info('Port forward: %s' % ' '.join(hostfwd))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001111
1112 def setup_tap(self):
1113 """Setup tap"""
1114
1115 # This file is created when runqemu-gen-tapdevs creates a bank of tap
1116 # devices, indicating that the user should not bring up new ones using
1117 # sudo.
1118 nosudo_flag = '/etc/runqemu-nosudo'
1119 self.qemuifup = shutil.which('runqemu-ifup')
1120 self.qemuifdown = shutil.which('runqemu-ifdown')
1121 ip = shutil.which('ip')
1122 lockdir = "/tmp/qemu-tap-locks"
1123
1124 if not (self.qemuifup and self.qemuifdown and ip):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001125 logger.error("runqemu-ifup: %s" % self.qemuifup)
1126 logger.error("runqemu-ifdown: %s" % self.qemuifdown)
1127 logger.error("ip: %s" % ip)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001128 raise OEPathError("runqemu-ifup, runqemu-ifdown or ip not found")
1129
1130 if not os.path.exists(lockdir):
1131 # There might be a race issue when multi runqemu processess are
1132 # running at the same time.
1133 try:
1134 os.mkdir(lockdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001135 os.chmod(lockdir, 0o777)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001136 except FileExistsError:
1137 pass
1138
Brad Bishop977dc1a2019-02-06 16:01:43 -05001139 cmd = (ip, 'link')
1140 logger.debug('Running %s...' % str(cmd))
1141 ip_link = subprocess.check_output(cmd).decode('utf-8')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001142 # Matches line like: 6: tap0: <foo>
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001143 possibles = re.findall('^[0-9]+: +(tap[0-9]+): <.*', ip_link, re.M)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001144 tap = ""
1145 for p in possibles:
1146 lockfile = os.path.join(lockdir, p)
1147 if os.path.exists('%s.skip' % lockfile):
1148 logger.info('Found %s.skip, skipping %s' % (lockfile, p))
1149 continue
Brad Bishop08902b02019-08-20 09:16:51 -04001150 self.taplock = lockfile + '.lock'
1151 if self.acquire_taplock(error=False):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001152 tap = p
1153 logger.info("Using preconfigured tap device %s" % tap)
1154 logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap))
1155 break
1156
1157 if not tap:
1158 if os.path.exists(nosudo_flag):
1159 logger.error("Error: There are no available tap devices to use for networking,")
1160 logger.error("and I see %s exists, so I am not going to try creating" % nosudo_flag)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001161 raise RunQemuError("a new one with sudo.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001162
1163 gid = os.getgid()
1164 uid = os.getuid()
1165 logger.info("Setting up tap interface under sudo")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001166 cmd = ('sudo', self.qemuifup, str(uid), str(gid), self.bindir_native)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001167 try:
1168 tap = subprocess.check_output(cmd).decode('utf-8').strip()
1169 except subprocess.CalledProcessError as e:
1170 logger.error('Setting up tap device failed:\n%s\nRun runqemu-gen-tapdevs to manually create one.' % str(e))
1171 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001172 lockfile = os.path.join(lockdir, tap)
Brad Bishop08902b02019-08-20 09:16:51 -04001173 self.taplock = lockfile + '.lock'
1174 self.acquire_taplock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001175 self.cleantap = True
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001176 logger.debug('Created tap: %s' % tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001177
1178 if not tap:
1179 logger.error("Failed to setup tap device. Run runqemu-gen-tapdevs to manually create.")
Andrew Geissler82c905d2020-04-13 13:39:40 -05001180 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001181 self.tap = tap
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001182 tapnum = int(tap[3:])
1183 gateway = tapnum * 2 + 1
1184 client = gateway + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001185 if self.fstype == 'nfs':
1186 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001187 netconf = " " + self.cmdline_ip_tap
1188 netconf = netconf.replace('@CLIENT@', str(client))
1189 netconf = netconf.replace('@GATEWAY@', str(gateway))
1190 logger.info("Network configuration:%s", netconf)
1191 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001192 mac = "%s%02x" % (self.mac_tap, client)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001193 qb_tap_opt = self.get('QB_TAP_OPT')
1194 if qb_tap_opt:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001195 qemu_tap_opt = qb_tap_opt.replace('@TAP@', tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001196 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001197 qemu_tap_opt = "-netdev tap,id=net0,ifname=%s,script=no,downscript=no" % (self.tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001198
1199 if self.vhost_enabled:
1200 qemu_tap_opt += ',vhost=on'
1201
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001202 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qemu_tap_opt))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001203
1204 def setup_network(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001205 if self.get('QB_NET') == 'none':
1206 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001207 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001208 self.saved_stty = subprocess.check_output(("stty", "-g")).decode('utf-8').strip()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001209 self.network_device = self.get('QB_NETWORK_DEVICE') or self.network_device
Andrew Geissler82c905d2020-04-13 13:39:40 -05001210 if self.net_bridge:
1211 self.setup_net_bridge()
1212 elif self.slirp_enabled:
1213 self.cmdline_ip_slirp = self.get('QB_CMDLINE_IP_SLIRP') or self.cmdline_ip_slirp
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001214 self.setup_slirp()
1215 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001216 self.cmdline_ip_tap = self.get('QB_CMDLINE_IP_TAP') or self.cmdline_ip_tap
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001217 self.setup_tap()
1218
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001219 def setup_rootfs(self):
1220 if self.get('QB_ROOTFS') == 'none':
1221 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001222 if 'wic.' in self.fstype:
1223 self.fstype = self.fstype[4:]
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001224 rootfs_format = self.fstype if self.fstype in ('vmdk', 'vhd', 'vhdx', 'qcow2', 'vdi') else 'raw'
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001225
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001226 tmpfsdir = os.environ.get("RUNQEMU_TMPFS_DIR", None)
1227 if self.snapshot and tmpfsdir:
1228 newrootfs = os.path.join(tmpfsdir, os.path.basename(self.rootfs)) + "." + str(os.getpid())
Andrew Geissler09036742021-06-25 14:25:14 -05001229 logger.info("Copying rootfs to %s" % newrootfs)
1230 copy_start = time.time()
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001231 shutil.copyfile(self.rootfs, newrootfs)
Andrew Geissler09036742021-06-25 14:25:14 -05001232 logger.info("Copy done in %s seconds" % (time.time() - copy_start))
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001233 self.rootfs = newrootfs
1234 # Don't need a second copy now!
1235 self.snapshot = False
Andrew Geisslerc926e172021-05-07 16:11:35 -05001236 self.cleanup_files.append(newrootfs)
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001237
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001238 qb_rootfs_opt = self.get('QB_ROOTFS_OPT')
1239 if qb_rootfs_opt:
1240 self.rootfs_options = qb_rootfs_opt.replace('@ROOTFS@', self.rootfs)
1241 else:
1242 self.rootfs_options = '-drive file=%s,if=virtio,format=%s' % (self.rootfs, rootfs_format)
1243
Andrew Geissler82c905d2020-04-13 13:39:40 -05001244 qb_rootfs_extra_opt = self.get("QB_ROOTFS_EXTRA_OPT")
1245 if qb_rootfs_extra_opt and not qb_rootfs_extra_opt.startswith(","):
1246 qb_rootfs_extra_opt = "," + qb_rootfs_extra_opt
1247
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001248 if self.fstype in ('cpio.gz', 'cpio'):
1249 self.kernel_cmdline = 'root=/dev/ram0 rw debugshell'
1250 self.rootfs_options = '-initrd %s' % self.rootfs
1251 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001252 vm_drive = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001253 if self.fstype in self.vmtypes:
1254 if self.fstype == 'iso':
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001255 vm_drive = '-drive file=%s,if=virtio,media=cdrom' % self.rootfs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001256 elif self.get('QB_DRIVE_TYPE'):
1257 drive_type = self.get('QB_DRIVE_TYPE')
1258 if drive_type.startswith("/dev/sd"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001259 logger.info('Using scsi drive')
Andrew Geissler82c905d2020-04-13 13:39:40 -05001260 vm_drive = '-drive if=none,id=hd,file=%s,format=%s -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd%s' \
1261 % (self.rootfs, rootfs_format, qb_rootfs_extra_opt)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001262 elif drive_type.startswith("/dev/hd"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001263 logger.info('Using ide drive')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001264 vm_drive = "-drive file=%s,format=%s" % (self.rootfs, rootfs_format)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001265 elif drive_type.startswith("/dev/vdb"):
1266 logger.info('Using block virtio drive');
1267 vm_drive = '-drive id=disk0,file=%s,if=none,format=%s -device virtio-blk-device,drive=disk0%s' \
1268 % (self.rootfs, rootfs_format,qb_rootfs_extra_opt)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001269 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001270 # virtio might have been selected explicitly (just use it), or
1271 # is used as fallback (then warn about that).
1272 if not drive_type.startswith("/dev/vd"):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001273 logger.warning("Unknown QB_DRIVE_TYPE: %s" % drive_type)
1274 logger.warning("Failed to figure out drive type, consider define or fix QB_DRIVE_TYPE")
1275 logger.warning('Trying to use virtio block drive')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001276 vm_drive = '-drive if=virtio,file=%s,format=%s' % (self.rootfs, rootfs_format)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001277
1278 # All branches above set vm_drive.
Andrew Geissler475cb722020-07-10 16:00:51 -05001279 self.rootfs_options = vm_drive
1280 if not self.fstype in self.vmtypes:
1281 self.rootfs_options += ' -no-reboot'
Andrew Geissler595f6302022-01-24 19:11:47 +00001282
1283 # By default, ' rw' is appended to QB_KERNEL_ROOT unless either ro or rw is explicitly passed.
1284 qb_kernel_root = self.get('QB_KERNEL_ROOT')
1285 qb_kernel_root_l = qb_kernel_root.split()
1286 if not ('ro' in qb_kernel_root_l or 'rw' in qb_kernel_root_l):
1287 qb_kernel_root += ' rw'
1288 self.kernel_cmdline = 'root=%s' % qb_kernel_root
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001289
1290 if self.fstype == 'nfs':
1291 self.rootfs_options = ''
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001292 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 -04001293 self.kernel_cmdline = 'root=%s rw' % k_root
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001294
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001295 if self.fstype == 'none':
1296 self.rootfs_options = ''
1297
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001298 self.set('ROOTFS_OPTIONS', self.rootfs_options)
1299
1300 def guess_qb_system(self):
1301 """attempt to determine the appropriate qemu-system binary"""
1302 mach = self.get('MACHINE')
1303 if not mach:
1304 search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*'
1305 if self.rootfs:
1306 match = re.match(search, self.rootfs)
1307 if match:
1308 mach = match.group(1)
1309 elif self.kernel:
1310 match = re.match(search, self.kernel)
1311 if match:
1312 mach = match.group(1)
1313
1314 if not mach:
1315 return None
1316
1317 if mach == 'qemuarm':
1318 qbsys = 'arm'
1319 elif mach == 'qemuarm64':
1320 qbsys = 'aarch64'
1321 elif mach == 'qemux86':
1322 qbsys = 'i386'
1323 elif mach == 'qemux86-64':
1324 qbsys = 'x86_64'
1325 elif mach == 'qemuppc':
1326 qbsys = 'ppc'
1327 elif mach == 'qemumips':
1328 qbsys = 'mips'
1329 elif mach == 'qemumips64':
1330 qbsys = 'mips64'
1331 elif mach == 'qemumipsel':
1332 qbsys = 'mipsel'
1333 elif mach == 'qemumips64el':
1334 qbsys = 'mips64el'
Brad Bishop316dfdd2018-06-25 12:45:53 -04001335 elif mach == 'qemuriscv64':
1336 qbsys = 'riscv64'
1337 elif mach == 'qemuriscv32':
1338 qbsys = 'riscv32'
Brad Bishop004d4992018-10-02 23:54:45 +02001339 else:
1340 logger.error("Unable to determine QEMU PC System emulator for %s machine." % mach)
1341 logger.error("As %s is not among valid QEMU machines such as," % mach)
1342 logger.error("qemux86-64, qemux86, qemuarm64, qemuarm, qemumips64, qemumips64el, qemumipsel, qemumips, qemuppc")
1343 raise RunQemuError("Set qb_system_name with suitable QEMU PC System emulator in .*qemuboot.conf.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001344
1345 return 'qemu-system-%s' % qbsys
1346
Brad Bishop15ae2502019-06-18 21:44:24 -04001347 def check_qemu_system(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001348 qemu_system = self.get('QB_SYSTEM_NAME')
1349 if not qemu_system:
1350 qemu_system = self.guess_qb_system()
1351 if not qemu_system:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001352 raise RunQemuError("Failed to boot, QB_SYSTEM_NAME is NULL!")
Brad Bishop15ae2502019-06-18 21:44:24 -04001353 self.qemu_system = qemu_system
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001354
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001355 def setup_vga(self):
1356 if self.nographic == True:
1357 if self.sdl == True:
1358 raise RunQemuError('Option nographic makes no sense alongside the sdl option.')
1359 if self.gtk == True:
1360 raise RunQemuError('Option nographic makes no sense alongside the gtk option.')
1361 self.qemu_opt += ' -nographic'
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001362
1363 if self.novga == True:
1364 self.qemu_opt += ' -vga none'
1365 return
1366
1367 if (self.gl_es == True or self.gl == True) and (self.sdl == False and self.gtk == False):
1368 raise RunQemuError('Option gl/gl-es needs gtk or sdl option.')
1369
Patrick Williams03907ee2022-05-01 06:28:52 -05001370 # If we have no display option, we autodetect based upon what qemu supports. We
1371 # need our font setup and show-cusor below so we need to see what qemu --help says
1372 # is supported so we can pass our correct config in.
1373 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 -05001374 output = subprocess.check_output([self.qemu_bin, "--help"], universal_newlines=True, env=self.qemu_environ)
Patrick Williams03907ee2022-05-01 06:28:52 -05001375 if "-display gtk" in output:
1376 self.gtk = True
1377 elif "-display sdl" in output:
1378 self.sdl = True
Andrew Geissler595f6302022-01-24 19:11:47 +00001379 else:
Patrick Williamsdb4c27e2022-08-05 08:10:29 -05001380 self.qemu_opt += ' -display none'
Andrew Geissler595f6302022-01-24 19:11:47 +00001381
Patrick Williams03907ee2022-05-01 06:28:52 -05001382 if self.sdl == True or self.gtk == True or self.egl_headless == True:
1383
1384 if self.qemu_system.endswith(('i386', 'x86_64')):
1385 if self.gl or self.gl_es or self.egl_headless:
1386 self.qemu_opt += ' -device virtio-vga-gl '
1387 else:
1388 self.qemu_opt += ' -device virtio-vga '
1389
1390 self.qemu_opt += ' -display '
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001391 if self.egl_headless == True:
Andrew Geissler595f6302022-01-24 19:11:47 +00001392 self.set_dri_path()
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001393 self.qemu_opt += 'egl-headless,'
1394 else:
1395 if self.sdl == True:
1396 self.qemu_opt += 'sdl,'
1397 elif self.gtk == True:
Patrick Williams2390b1b2022-11-03 13:47:49 -05001398 self.qemu_environ['FONTCONFIG_PATH'] = '/etc/fonts'
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001399 self.qemu_opt += 'gtk,'
1400
1401 if self.gl == True:
Andrew Geissler595f6302022-01-24 19:11:47 +00001402 self.set_dri_path()
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001403 self.qemu_opt += 'gl=on,'
1404 elif self.gl_es == True:
Andrew Geissler595f6302022-01-24 19:11:47 +00001405 self.set_dri_path()
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001406 self.qemu_opt += 'gl=es,'
1407 self.qemu_opt += 'show-cursor=on'
1408
1409 self.qemu_opt += ' %s' %self.get('QB_GRAPHICS')
1410
1411 def setup_serial(self):
1412 # Setup correct kernel command line for serial
Andrew Geissler595f6302022-01-24 19:11:47 +00001413 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 -05001414 for entry in self.get('SERIAL_CONSOLES').split(' '):
1415 self.kernel_cmdline_script += ' console=%s' %entry.split(';')[1]
1416
1417 if self.serialstdio == True or self.nographic == True:
1418 self.qemu_opt += " -serial mon:stdio"
1419 else:
1420 self.qemu_opt += " -serial mon:vc"
1421 if self.serialconsole:
1422 if sys.stdin.isatty():
1423 subprocess.check_call(("stty", "intr", "^]"))
1424 logger.info("Interrupt character is '^]'")
1425
1426 self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT")
1427
1428 # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES).
1429 # If no serial or serialtcp options were specified, only ttyS0 is created
1430 # and sysvinit shows an error trying to enable ttyS1:
1431 # INIT: Id "S1" respawning too fast: disabled for 5 minutes
1432 serial_num = len(re.findall("-serial", self.qemu_opt))
1433 if serial_num < 2:
1434 self.qemu_opt += " -serial null"
1435
Patrick Williams03907ee2022-05-01 06:28:52 -05001436 def find_qemu(self):
Brad Bishop15ae2502019-06-18 21:44:24 -04001437 qemu_bin = os.path.join(self.bindir_native, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001438
1439 # It is possible to have qemu-native in ASSUME_PROVIDED, and it won't
1440 # find QEMU in sysroot, it needs to use host's qemu.
1441 if not os.path.exists(qemu_bin):
1442 logger.info("QEMU binary not found in %s, trying host's QEMU" % qemu_bin)
1443 for path in (os.environ['PATH'] or '').split(':'):
Brad Bishop15ae2502019-06-18 21:44:24 -04001444 qemu_bin_tmp = os.path.join(path, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001445 logger.info("Trying: %s" % qemu_bin_tmp)
1446 if os.path.exists(qemu_bin_tmp):
1447 qemu_bin = qemu_bin_tmp
1448 if not os.path.isabs(qemu_bin):
1449 qemu_bin = os.path.abspath(qemu_bin)
1450 logger.info("Using host's QEMU: %s" % qemu_bin)
1451 break
1452
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001453 if not os.access(qemu_bin, os.X_OK):
1454 raise OEPathError("No QEMU binary '%s' could be found" % qemu_bin)
Patrick Williams03907ee2022-05-01 06:28:52 -05001455 self.qemu_bin = qemu_bin
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001456
Patrick Williams03907ee2022-05-01 06:28:52 -05001457 def setup_final(self):
1458
1459 self.find_qemu()
1460
1461 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 -05001462
1463 for ovmf in self.ovmf_bios:
1464 format = ovmf.rsplit('.', 1)[-1]
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001465 if format == "bin":
1466 format = "raw"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001467 self.qemu_opt += ' -drive if=pflash,format=%s,file=%s' % (format, ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001468
1469 self.qemu_opt += ' ' + self.qemu_opt_script
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001470
Brad Bishop08902b02019-08-20 09:16:51 -04001471 if self.ovmf_secboot_pkkek1:
1472 # Provide the Platform Key and first Key Exchange Key certificate as an
1473 # OEM string in the SMBIOS Type 11 table. Prepend the certificate string
1474 # with "application prefix" of the EnrollDefaultKeys.efi application
1475 self.qemu_opt += ' -smbios type=11,value=4e32566d-8e9e-4f52-81d3-5bb9715f9727:' \
1476 + self.ovmf_secboot_pkkek1
1477
Andrew Geissler99467da2019-02-25 18:54:23 -06001478 # Append qemuparams to override previous settings
1479 if self.qemuparams:
1480 self.qemu_opt += ' ' + self.qemuparams
1481
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001482 if self.snapshot:
1483 self.qemu_opt += " -snapshot"
1484
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001485 self.setup_serial()
1486 self.setup_vga()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001487
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001488 def start_qemu(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001489 import shlex
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001490 if self.kernel:
Andrew Geissler615f2f12022-07-15 14:00:58 -05001491 kernel_opts = "-kernel %s" % (self.kernel)
1492 if self.get('QB_KERNEL_CMDLINE') == "none":
1493 if self.bootparams:
1494 kernel_opts += " -append '%s'" % (self.bootparams)
1495 else:
1496 kernel_opts += " -append '%s %s %s %s'" % (self.kernel_cmdline,
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001497 self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'),
1498 self.bootparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001499 if self.dtb:
1500 kernel_opts += " -dtb %s" % self.dtb
1501 else:
1502 kernel_opts = ""
Patrick Williams213cb262021-08-07 19:21:33 -05001503
1504 if self.bios:
1505 self.qemu_opt += " -bios %s" % self.bios
1506
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001507 cmd = "%s %s" % (self.qemu_opt, kernel_opts)
Brad Bishop004d4992018-10-02 23:54:45 +02001508 cmds = shlex.split(cmd)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001509 logger.info('Running %s\n' % cmd)
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001510 with open('/proc/uptime', 'r') as f:
1511 uptime_seconds = f.readline().split()[0]
1512 logger.info('Host uptime: %s\n' % uptime_seconds)
Brad Bishopf86d0552018-12-04 14:18:15 -08001513 pass_fds = []
Brad Bishop08902b02019-08-20 09:16:51 -04001514 if self.taplock_descriptor:
1515 pass_fds = [self.taplock_descriptor.fileno()]
1516 if len(self.portlocks):
1517 for descriptor in self.portlocks.values():
1518 pass_fds.append(descriptor.fileno())
Patrick Williams2390b1b2022-11-03 13:47:49 -05001519 process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds, env=self.qemu_environ)
Brad Bishop004d4992018-10-02 23:54:45 +02001520 self.qemupid = process.pid
1521 retcode = process.wait()
1522 if retcode:
1523 if retcode == -signal.SIGTERM:
1524 logger.info("Qemu terminated by SIGTERM")
1525 else:
1526 logger.error("Failed to run qemu: %s", process.stderr.read().decode())
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001527
1528 def cleanup(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001529 if self.cleaned:
1530 return
1531
1532 # avoid dealing with SIGTERM when cleanup function is running
1533 signal.signal(signal.SIGTERM, signal.SIG_IGN)
1534
1535 logger.info("Cleaning up")
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001536 with open('/proc/uptime', 'r') as f:
1537 uptime_seconds = f.readline().split()[0]
1538 logger.info('Host uptime: %s\n' % uptime_seconds)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001539 if self.cleantap:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001540 cmd = ('sudo', self.qemuifdown, self.tap, self.bindir_native)
1541 logger.debug('Running %s' % str(cmd))
1542 subprocess.check_call(cmd)
Brad Bishop08902b02019-08-20 09:16:51 -04001543 self.release_taplock()
1544 self.release_portlock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001545
1546 if self.nfs_running:
1547 logger.info("Shutting down the userspace NFS server...")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001548 cmd = ("runqemu-export-rootfs", "stop", self.rootfs)
1549 logger.debug('Running %s' % str(cmd))
1550 subprocess.check_call(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001551
1552 if self.saved_stty:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001553 subprocess.check_call(("stty", self.saved_stty))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001554
Andrew Geisslerc926e172021-05-07 16:11:35 -05001555 if self.cleanup_files:
1556 for ent in self.cleanup_files:
1557 logger.info('Removing %s' % ent)
1558 if os.path.isfile(ent):
1559 os.remove(ent)
1560 else:
1561 shutil.rmtree(ent)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001562
Brad Bishop004d4992018-10-02 23:54:45 +02001563 self.cleaned = True
1564
Andrew Geissler82c905d2020-04-13 13:39:40 -05001565 def run_bitbake_env(self, mach=None):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001566 bitbake = shutil.which('bitbake')
1567 if not bitbake:
1568 return
1569
1570 if not mach:
1571 mach = self.get('MACHINE')
1572
Andrew Geissler82c905d2020-04-13 13:39:40 -05001573 multiconfig = self.get('MULTICONFIG')
1574 if multiconfig:
1575 multiconfig = "mc:%s" % multiconfig
1576
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001577 if mach:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001578 cmd = 'MACHINE=%s bitbake -e %s' % (mach, multiconfig)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001579 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001580 cmd = 'bitbake -e %s' % multiconfig
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001581
1582 logger.info('Running %s...' % cmd)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001583 return subprocess.check_output(cmd, shell=True).decode('utf-8')
1584
1585 def load_bitbake_env(self, mach=None):
1586 if self.bitbake_e:
1587 return
1588
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001589 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001590 self.bitbake_e = self.run_bitbake_env(mach=mach)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001591 except subprocess.CalledProcessError as err:
1592 self.bitbake_e = ''
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001593 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 -06001594
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001595 def validate_combos(self):
1596 if (self.fstype in self.vmtypes) and self.kernel:
1597 raise RunQemuError("%s doesn't need kernel %s!" % (self.fstype, self.kernel))
1598
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001599 @property
1600 def bindir_native(self):
1601 result = self.get('STAGING_BINDIR_NATIVE')
1602 if result and os.path.exists(result):
1603 return result
1604
Andrew Geissler82c905d2020-04-13 13:39:40 -05001605 cmd = ['bitbake', '-e']
1606 multiconfig = self.get('MULTICONFIG')
1607 if multiconfig:
1608 cmd.append('mc:%s:qemu-helper-native' % multiconfig)
1609 else:
1610 cmd.append('qemu-helper-native')
1611
Brad Bishop977dc1a2019-02-06 16:01:43 -05001612 logger.info('Running %s...' % str(cmd))
1613 out = subprocess.check_output(cmd).decode('utf-8')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001614
1615 match = re.search('^STAGING_BINDIR_NATIVE="(.*)"', out, re.M)
1616 if match:
1617 result = match.group(1)
1618 if os.path.exists(result):
1619 self.set('STAGING_BINDIR_NATIVE', result)
1620 return result
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001621 raise RunQemuError("Native sysroot directory %s doesn't exist" % result)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001622 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001623 raise RunQemuError("Can't find STAGING_BINDIR_NATIVE in '%s' output" % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001624
1625
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001626def main():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001627 if "help" in sys.argv or '-h' in sys.argv or '--help' in sys.argv:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001628 print_usage()
1629 return 0
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001630 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001631 config = BaseConfig()
Brad Bishop004d4992018-10-02 23:54:45 +02001632
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001633 renice = os.path.expanduser("~/bin/runqemu-renice")
1634 if os.path.exists(renice):
1635 logger.info('Using %s to renice' % renice)
1636 subprocess.check_call([renice, str(os.getpid())])
1637
Brad Bishop004d4992018-10-02 23:54:45 +02001638 def sigterm_handler(signum, frame):
1639 logger.info("SIGTERM received")
Andrew Geissler595f6302022-01-24 19:11:47 +00001640 if config.qemupid:
1641 os.kill(config.qemupid, signal.SIGTERM)
Brad Bishop004d4992018-10-02 23:54:45 +02001642 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001643 # Deliberately ignore the return code of 'tput smam'.
1644 subprocess.call(["tput", "smam"])
Brad Bishop004d4992018-10-02 23:54:45 +02001645 signal.signal(signal.SIGTERM, sigterm_handler)
1646
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001647 config.check_args()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001648 config.read_qemuboot()
1649 config.check_and_set()
1650 # Check whether the combos is valid or not
1651 config.validate_combos()
1652 config.print_config()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001653 config.setup_network()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001654 config.setup_rootfs()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001655 config.setup_final()
1656 config.start_qemu()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001657 except RunQemuError as err:
1658 logger.error(err)
1659 return 1
1660 except Exception as err:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001661 import traceback
1662 traceback.print_exc()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001663 return 1
1664 finally:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001665 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001666 # Deliberately ignore the return code of 'tput smam'.
1667 subprocess.call(["tput", "smam"])
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001668
1669if __name__ == "__main__":
1670 sys.exit(main())