blob: 6e1f073ed2e647cc2ffaf55598a8e9e8581cd818 [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
213 self.qemupid = None
214 # avoid cleanup twice
215 self.cleaned = False
Andrew Geisslerc926e172021-05-07 16:11:35 -0500216 # Files to cleanup after run
217 self.cleanup_files = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500218
Brad Bishop08902b02019-08-20 09:16:51 -0400219 def acquire_taplock(self, error=True):
220 logger.debug("Acquiring lockfile %s..." % self.taplock)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600221 try:
Brad Bishop08902b02019-08-20 09:16:51 -0400222 self.taplock_descriptor = open(self.taplock, 'w')
223 fcntl.flock(self.taplock_descriptor, fcntl.LOCK_EX|fcntl.LOCK_NB)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600224 except Exception as e:
Brad Bishop08902b02019-08-20 09:16:51 -0400225 msg = "Acquiring lockfile %s failed: %s" % (self.taplock, e)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500226 if error:
227 logger.error(msg)
228 else:
229 logger.info(msg)
Brad Bishop08902b02019-08-20 09:16:51 -0400230 if self.taplock_descriptor:
231 self.taplock_descriptor.close()
232 self.taplock_descriptor = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600233 return False
234 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500235
Brad Bishop08902b02019-08-20 09:16:51 -0400236 def release_taplock(self):
237 if self.taplock_descriptor:
Brad Bishopf86d0552018-12-04 14:18:15 -0800238 logger.debug("Releasing lockfile for tap device '%s'" % self.tap)
Andrew Geissler5f350902021-07-23 13:09:54 -0400239 # We pass the fd to the qemu process and if we unlock here, it would unlock for
240 # that too. Therefore don't unlock, just close
241 # fcntl.flock(self.taplock_descriptor, fcntl.LOCK_UN)
Brad Bishop08902b02019-08-20 09:16:51 -0400242 self.taplock_descriptor.close()
Andrew Geissler5f350902021-07-23 13:09:54 -0400243 # Removing the file is a potential race, don't do that either
244 # os.remove(self.taplock)
Brad Bishop08902b02019-08-20 09:16:51 -0400245 self.taplock_descriptor = None
246
247 def check_free_port(self, host, port, lockdir):
248 """ Check whether the port is free or not """
249 import socket
250 from contextlib import closing
251
252 lockfile = os.path.join(lockdir, str(port) + '.lock')
253 if self.acquire_portlock(lockfile):
254 with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
255 if sock.connect_ex((host, port)) == 0:
256 # Port is open, so not free
257 self.release_portlock(lockfile)
258 return False
259 else:
260 # Port is not open, so free
261 return True
262 else:
263 return False
264
265 def acquire_portlock(self, lockfile):
266 logger.debug("Acquiring lockfile %s..." % lockfile)
267 try:
268 portlock_descriptor = open(lockfile, 'w')
269 self.portlocks.update({lockfile: portlock_descriptor})
270 fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_EX|fcntl.LOCK_NB)
271 except Exception as e:
272 msg = "Acquiring lockfile %s failed: %s" % (lockfile, e)
273 logger.info(msg)
274 if lockfile in self.portlocks.keys() and self.portlocks[lockfile]:
275 self.portlocks[lockfile].close()
276 del self.portlocks[lockfile]
277 return False
278 return True
279
280 def release_portlock(self, lockfile=None):
281 if lockfile != None:
Andrew Geissler5f350902021-07-23 13:09:54 -0400282 logger.debug("Releasing lockfile '%s'" % lockfile)
283 # We pass the fd to the qemu process and if we unlock here, it would unlock for
284 # that too. Therefore don't unlock, just close
285 # fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_UN)
286 self.portlocks[lockfile].close()
287 # Removing the file is a potential race, don't do that either
288 # os.remove(lockfile)
289 del self.portlocks[lockfile]
Brad Bishop08902b02019-08-20 09:16:51 -0400290 elif len(self.portlocks):
291 for lockfile, descriptor in self.portlocks.items():
292 logger.debug("Releasing lockfile '%s'" % lockfile)
Andrew Geissler5f350902021-07-23 13:09:54 -0400293 # We pass the fd to the qemu process and if we unlock here, it would unlock for
294 # that too. Therefore don't unlock, just close
295 # fcntl.flock(descriptor, fcntl.LOCK_UN)
Brad Bishop08902b02019-08-20 09:16:51 -0400296 descriptor.close()
Andrew Geissler5f350902021-07-23 13:09:54 -0400297 # Removing the file is a potential race, don't do that either
298 # os.remove(lockfile)
Brad Bishop08902b02019-08-20 09:16:51 -0400299 self.portlocks = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500300
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600301 def get(self, key):
302 if key in self.d:
303 return self.d.get(key)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500304 elif os.getenv(key):
305 return os.getenv(key)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600306 else:
307 return ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500308
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600309 def set(self, key, value):
310 self.d[key] = value
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500311
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600312 def is_deploy_dir_image(self, p):
313 if os.path.isdir(p):
314 if not re.search('.qemuboot.conf$', '\n'.join(os.listdir(p)), re.M):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500315 logger.debug("Can't find required *.qemuboot.conf in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600316 return False
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500317 if not any(map(lambda name: '-image-' in name, os.listdir(p))):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500318 logger.debug("Can't find *-image-* in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600319 return False
320 return True
321 else:
322 return False
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500323
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600324 def check_arg_fstype(self, fst):
325 """Check and set FSTYPE"""
Brad Bishop15ae2502019-06-18 21:44:24 -0400326 if fst not in self.fstypes + self.vmtypes + self.wictypes:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800327 logger.warning("Maybe unsupported FSTYPE: %s" % fst)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600328 if not self.fstype or self.fstype == fst:
329 if fst == 'ramfs':
330 fst = 'cpio.gz'
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500331 if fst in ('tar.bz2', 'tar.gz'):
332 fst = 'nfs'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600333 self.fstype = fst
334 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500335 raise RunQemuError("Conflicting: FSTYPE %s and %s" % (self.fstype, fst))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500336
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600337 def set_machine_deploy_dir(self, machine, deploy_dir_image):
338 """Set MACHINE and DEPLOY_DIR_IMAGE"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500339 logger.debug('MACHINE: %s' % machine)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600340 self.set("MACHINE", machine)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500341 logger.debug('DEPLOY_DIR_IMAGE: %s' % deploy_dir_image)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600342 self.set("DEPLOY_DIR_IMAGE", deploy_dir_image)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500343
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600344 def check_arg_nfs(self, p):
345 if os.path.isdir(p):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500346 self.rootfs = p
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600347 else:
348 m = re.match('(.*):(.*)', p)
349 self.nfs_server = m.group(1)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500350 self.rootfs = m.group(2)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600351 self.check_arg_fstype('nfs')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500352
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600353 def check_arg_path(self, p):
354 """
355 - Check whether it is <image>.qemuboot.conf or contains <image>.qemuboot.conf
Andrew Geissler9aee5002022-03-30 16:27:02 +0000356 - Check whether it is a kernel file
357 - Check whether it is an image file
358 - Check whether it is an NFS dir
359 - Check whether it is an OVMF flash file
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600360 """
361 if p.endswith('.qemuboot.conf'):
362 self.qemuboot = p
363 self.qbconfload = True
364 elif re.search('\.bin$', p) or re.search('bzImage', p) or \
365 re.search('zImage', p) or re.search('vmlinux', p) or \
366 re.search('fitImage', p) or re.search('uImage', p):
367 self.kernel = p
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500368 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 -0600369 self.rootfs = p
Andrew Geissler9aee5002022-03-30 16:27:02 +0000370 # Check filename against self.fstypes can handle <file>.cpio.gz,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500371 # otherwise, its type would be "gz", which is incorrect.
372 fst = ""
373 for t in self.fstypes:
374 if p.endswith(t):
375 fst = t
376 break
377 if not fst:
378 m = re.search('.*\.(.*)$', self.rootfs)
379 if m:
380 fst = m.group(1)
381 if fst:
382 self.check_arg_fstype(fst)
383 qb = re.sub('\.' + fst + "$", '', self.rootfs)
384 qb = '%s%s' % (re.sub('\.rootfs$', '', qb), '.qemuboot.conf')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600385 if os.path.exists(qb):
386 self.qemuboot = qb
387 self.qbconfload = True
388 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800389 logger.warning("%s doesn't exist" % qb)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600390 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500391 raise RunQemuError("Can't find FSTYPE from: %s" % p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500392
393 elif os.path.isdir(p) or re.search(':', p) and re.search('/', p):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600394 if self.is_deploy_dir_image(p):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500395 logger.debug('DEPLOY_DIR_IMAGE: %s' % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600396 self.set("DEPLOY_DIR_IMAGE", p)
397 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500398 logger.debug("Assuming %s is an nfs rootfs" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600399 self.check_arg_nfs(p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500400 elif os.path.basename(p).startswith('ovmf'):
401 self.ovmf_bios.append(p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600402 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500403 raise RunQemuError("Unknown path arg %s" % p)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500404
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600405 def check_arg_machine(self, arg):
406 """Check whether it is a machine"""
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500407 if self.get('MACHINE') == arg:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600408 return
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500409 elif self.get('MACHINE') and self.get('MACHINE') != arg:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500410 raise RunQemuError("Maybe conflicted MACHINE: %s vs %s" % (self.get('MACHINE'), arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500411 elif re.search('/', arg):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500412 raise RunQemuError("Unknown arg: %s" % arg)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500413
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500414 logger.debug('Assuming MACHINE = %s' % arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500415
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600416 # if we're running under testimage, or similarly as a child
417 # of an existing bitbake invocation, we can't invoke bitbake
418 # to validate the MACHINE setting and must assume it's correct...
419 # FIXME: testimage.bbclass exports these two variables into env,
420 # are there other scenarios in which we need to support being
421 # invoked by bitbake?
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500422 deploy = self.get('DEPLOY_DIR_IMAGE')
423 bbchild = deploy and self.get('OE_TMPDIR')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600424 if bbchild:
425 self.set_machine_deploy_dir(arg, deploy)
426 return
427 # also check whether we're running under a sourced toolchain
428 # environment file
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500429 if self.get('OECORE_NATIVE_SYSROOT'):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600430 self.set("MACHINE", arg)
431 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500432
Andrew Geissler82c905d2020-04-13 13:39:40 -0500433 self.bitbake_e = self.run_bitbake_env(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600434 # bitbake -e doesn't report invalid MACHINE as an error, so
435 # let's check DEPLOY_DIR_IMAGE to make sure that it is a valid
436 # MACHINE.
437 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
438 if s:
439 deploy_dir_image = s.group(1)
440 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500441 raise RunQemuError("bitbake -e %s" % self.bitbake_e)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600442 if self.is_deploy_dir_image(deploy_dir_image):
443 self.set_machine_deploy_dir(arg, deploy_dir_image)
444 else:
445 logger.error("%s not a directory valid DEPLOY_DIR_IMAGE" % deploy_dir_image)
446 self.set("MACHINE", arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500447
Andrew Geisslerc182c622020-05-15 14:13:32 -0500448 def set_dri_path(self):
449 # As runqemu can be run within bitbake (when using testimage, for example),
450 # we need to ensure that we run host pkg-config, and that it does not
451 # get mis-directed to native build paths set by bitbake.
452 try:
453 del os.environ['PKG_CONFIG_PATH']
454 del os.environ['PKG_CONFIG_DIR']
455 del os.environ['PKG_CONFIG_LIBDIR']
456 del os.environ['PKG_CONFIG_SYSROOT_DIR']
457 except KeyError:
458 pass
459 try:
460 dripath = subprocess.check_output("PATH=/bin:/usr/bin:$PATH pkg-config --variable=dridriverdir dri", shell=True)
461 except subprocess.CalledProcessError as e:
462 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.")
463 os.environ['LIBGL_DRIVERS_PATH'] = dripath.decode('utf-8').strip()
464
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000465 # This preloads uninative libc pieces and therefore ensures that RPATH/RUNPATH
466 # in host mesa drivers doesn't trick uninative into loading host libc.
467 preload_items = ['libdl.so.2', 'librt.so.1', 'libpthread.so.0']
468 uninative_path = os.path.dirname(self.get("UNINATIVE_LOADER"))
469 if os.path.exists(uninative_path):
470 preload_paths = [os.path.join(uninative_path, i) for i in preload_items]
471 os.environ['LD_PRELOAD'] = " ".join(preload_paths)
472
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600473 def check_args(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500474 for debug in ("-d", "--debug"):
475 if debug in sys.argv:
476 logger.setLevel(logging.DEBUG)
477 sys.argv.remove(debug)
478
479 for quiet in ("-q", "--quiet"):
480 if quiet in sys.argv:
481 logger.setLevel(logging.ERROR)
482 sys.argv.remove(quiet)
483
Andrew Geisslerc182c622020-05-15 14:13:32 -0500484 if 'gl' not in sys.argv[1:] and 'gl-es' not in sys.argv[1:]:
485 os.environ['SDL_RENDER_DRIVER'] = 'software'
Andrew Geissler595f6302022-01-24 19:11:47 +0000486 os.environ['SDL_FRAMEBUFFER_ACCELERATION'] = 'false'
Andrew Geisslerc182c622020-05-15 14:13:32 -0500487
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600488 unknown_arg = ""
489 for arg in sys.argv[1:]:
Brad Bishop15ae2502019-06-18 21:44:24 -0400490 if arg in self.fstypes + self.vmtypes + self.wictypes:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600491 self.check_arg_fstype(arg)
492 elif arg == 'nographic':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500493 self.nographic = True
Brad Bishop19323692019-04-05 15:28:33 -0400494 elif arg == 'sdl':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500495 self.sdl = True
Brad Bishopa34c0302019-09-23 22:34:48 -0400496 elif arg == 'gtk':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500497 self.gtk = True
498 elif arg == 'gl':
499 self.gl = True
500 elif 'gl-es' in sys.argv[1:]:
501 self.gl_es = True
Brad Bishop19323692019-04-05 15:28:33 -0400502 elif arg == 'egl-headless':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500503 self.egl_headless = True
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600504 elif arg == 'novga':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500505 self.novga = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600506 elif arg == 'serial':
Brad Bishop19323692019-04-05 15:28:33 -0400507 self.serialconsole = True
508 elif arg == "serialstdio":
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600509 self.serialstdio = True
510 elif arg == 'audio':
511 logger.info("Enabling audio in qemu")
512 logger.info("Please install sound drivers in linux host")
513 self.audio_enabled = True
514 elif arg == 'kvm':
515 self.kvm_enabled = True
516 elif arg == 'kvm-vhost':
517 self.vhost_enabled = True
518 elif arg == 'slirp':
519 self.slirp_enabled = True
Andrew Geissler82c905d2020-04-13 13:39:40 -0500520 elif arg.startswith('bridge='):
521 self.net_bridge = '%s' % arg[len('bridge='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600522 elif arg == 'snapshot':
523 self.snapshot = True
524 elif arg == 'publicvnc':
Patrick Williams03907ee2022-05-01 06:28:52 -0500525 self.publicvnc = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600526 self.qemu_opt_script += ' -vnc :0'
527 elif arg.startswith('tcpserial='):
Brad Bishop15ae2502019-06-18 21:44:24 -0400528 self.tcpserial_portnum = '%s' % arg[len('tcpserial='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600529 elif arg.startswith('qemuparams='):
Andrew Geissler99467da2019-02-25 18:54:23 -0600530 self.qemuparams = ' %s' % arg[len('qemuparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600531 elif arg.startswith('bootparams='):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500532 self.bootparams = arg[len('bootparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600533 elif os.path.exists(arg) or (re.search(':', arg) and re.search('/', arg)):
534 self.check_arg_path(os.path.abspath(arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500535 elif re.search(r'-image-|-image$', arg):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600536 # Lazy rootfs
537 self.rootfs = arg
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500538 elif arg.startswith('ovmf'):
539 self.ovmf_bios.append(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600540 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500541 # At last, assume it is the MACHINE
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600542 if (not unknown_arg) or unknown_arg == arg:
543 unknown_arg = arg
544 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500545 raise RunQemuError("Can't handle two unknown args: %s %s\n"
546 "Try 'runqemu help' on how to use it" % \
547 (unknown_arg, arg))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600548 # Check to make sure it is a valid machine
Brad Bishopa5c52ff2018-11-23 10:55:50 +1300549 if unknown_arg and self.get('MACHINE') != unknown_arg:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500550 if self.get('DEPLOY_DIR_IMAGE'):
551 machine = os.path.basename(self.get('DEPLOY_DIR_IMAGE'))
552 if unknown_arg == machine:
553 self.set("MACHINE", machine)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500554
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600555 self.check_arg_machine(unknown_arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500556
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500557 if not (self.get('DEPLOY_DIR_IMAGE') or self.qbconfload):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500558 self.load_bitbake_env()
559 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
560 if s:
561 self.set("DEPLOY_DIR_IMAGE", s.group(1))
562
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600563 def check_kvm(self):
564 """Check kvm and kvm-host"""
565 if not (self.kvm_enabled or self.vhost_enabled):
William A. Kennington IIIac69b482021-06-02 12:28:27 -0700566 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 -0600567 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500568
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600569 if not self.get('QB_CPU_KVM'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500570 raise RunQemuError("QB_CPU_KVM is NULL, this board doesn't support kvm")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500571
William A. Kennington IIIac69b482021-06-02 12:28:27 -0700572 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 -0600573 yocto_kvm_wiki = "https://wiki.yoctoproject.org/wiki/How_to_enable_KVM_for_Poky_qemu"
574 yocto_paravirt_kvm_wiki = "https://wiki.yoctoproject.org/wiki/Running_an_x86_Yocto_Linux_image_under_QEMU_KVM"
575 dev_kvm = '/dev/kvm'
576 dev_vhost = '/dev/vhost-net'
Brad Bishop15ae2502019-06-18 21:44:24 -0400577 if self.qemu_system.endswith(('i386', 'x86_64')):
578 with open('/proc/cpuinfo', 'r') as f:
579 kvm_cap = re.search('vmx|svm', "".join(f.readlines()))
580 if not kvm_cap:
581 logger.error("You are trying to enable KVM on a cpu without VT support.")
582 logger.error("Remove kvm from the command-line, or refer:")
583 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500584
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600585 if not os.path.exists(dev_kvm):
586 logger.error("Missing KVM device. Have you inserted kvm modules?")
587 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500588 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500589
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600590 if os.access(dev_kvm, os.W_OK|os.R_OK):
591 self.qemu_opt_script += ' -enable-kvm'
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500592 if self.get('MACHINE') == "qemux86":
593 # Workaround for broken APIC window on pre 4.15 host kernels which causes boot hangs
594 # See YOCTO #12301
595 # On 64 bit we use x2apic
596 self.kernel_cmdline_script += " clocksource=kvm-clock hpet=disable noapic nolapic"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600597 else:
598 logger.error("You have no read or write permission on /dev/kvm.")
599 logger.error("Please change the ownership of this file as described at:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500600 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500601
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600602 if self.vhost_enabled:
603 if not os.path.exists(dev_vhost):
604 logger.error("Missing virtio net device. Have you inserted vhost-net module?")
605 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500606 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500607
Andrew Geissler635e0e42020-08-21 15:58:33 -0500608 if not os.access(dev_vhost, os.W_OK|os.R_OK):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600609 logger.error("You have no read or write permission on /dev/vhost-net.")
610 logger.error("Please change the ownership of this file as described at:")
Andrew Geissler635e0e42020-08-21 15:58:33 -0500611 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500612
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600613 def check_fstype(self):
614 """Check and setup FSTYPE"""
615 if not self.fstype:
616 fstype = self.get('QB_DEFAULT_FSTYPE')
617 if fstype:
618 self.fstype = fstype
619 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500620 raise RunQemuError("FSTYPE is NULL!")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500621
Brad Bishop15ae2502019-06-18 21:44:24 -0400622 # parse QB_FSINFO into dict, e.g. { 'wic': ['no-kernel-in-fs', 'a-flag'], 'ext4': ['another-flag']}
623 wic_fs = False
624 qb_fsinfo = self.get('QB_FSINFO')
625 if qb_fsinfo:
626 qb_fsinfo = qb_fsinfo.split()
627 for fsinfo in qb_fsinfo:
628 try:
629 fstype, fsflag = fsinfo.split(':')
630
631 if fstype == 'wic':
632 if fsflag == 'no-kernel-in-fs':
633 wic_fs = True
634 elif fsflag == 'kernel-in-fs':
635 wic_fs = False
636 else:
637 logger.warn('Unknown flag "%s:%s" in QB_FSINFO', fstype, fsflag)
638 continue
639 else:
640 logger.warn('QB_FSINFO is not supported for image type "%s"', fstype)
641 continue
642
643 if fstype in self.fsinfo:
644 self.fsinfo[fstype].append(fsflag)
645 else:
646 self.fsinfo[fstype] = [fsflag]
647 except Exception:
648 logger.error('Invalid parameter "%s" in QB_FSINFO', fsinfo)
649
650 # treat wic images as vmimages (with kernel) or as fsimages (rootfs only)
651 if wic_fs:
652 self.fstypes = self.fstypes + self.wictypes
653 else:
654 self.vmtypes = self.vmtypes + self.wictypes
655
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600656 def check_rootfs(self):
657 """Check and set rootfs"""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500658
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500659 if self.fstype == "none":
660 return
661
662 if self.get('ROOTFS'):
663 if not self.rootfs:
664 self.rootfs = self.get('ROOTFS')
665 elif self.get('ROOTFS') != self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500666 raise RunQemuError("Maybe conflicted ROOTFS: %s vs %s" % (self.get('ROOTFS'), self.rootfs))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500667
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600668 if self.fstype == 'nfs':
669 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500670
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600671 if self.rootfs and not os.path.exists(self.rootfs):
672 # Lazy rootfs
673 self.rootfs = "%s/%s-%s.%s" % (self.get('DEPLOY_DIR_IMAGE'),
674 self.rootfs, self.get('MACHINE'),
675 self.fstype)
676 elif not self.rootfs:
677 cmd_name = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_NAME'), self.fstype)
678 cmd_link = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'), self.fstype)
679 cmds = (cmd_name, cmd_link)
680 self.rootfs = get_first_file(cmds)
681 if not self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500682 raise RunQemuError("Failed to find rootfs: %s or %s" % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500683
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600684 if not os.path.exists(self.rootfs):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500685 raise RunQemuError("Can't find rootfs: %s" % self.rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500686
Brad Bishop08902b02019-08-20 09:16:51 -0400687 def setup_pkkek1(self):
688 """
689 Extract from PEM certificate the Platform Key and first Key
690 Exchange Key certificate string. The hypervisor needs to provide
691 it in the Type 11 SMBIOS table
692 """
693 pemcert = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), 'OvmfPkKek1.pem')
694 try:
695 with open(pemcert, 'r') as pemfile:
696 key = pemfile.read().replace('\n', ''). \
697 replace('-----BEGIN CERTIFICATE-----', ''). \
698 replace('-----END CERTIFICATE-----', '')
699 self.ovmf_secboot_pkkek1 = key
700
701 except FileNotFoundError:
702 raise RunQemuError("Can't open PEM certificate %s " % pemcert)
703
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500704 def check_ovmf(self):
705 """Check and set full path for OVMF firmware and variable file(s)."""
706
707 for index, ovmf in enumerate(self.ovmf_bios):
708 if os.path.exists(ovmf):
709 continue
710 for suffix in ('qcow2', 'bin'):
711 path = '%s/%s.%s' % (self.get('DEPLOY_DIR_IMAGE'), ovmf, suffix)
712 if os.path.exists(path):
713 self.ovmf_bios[index] = path
Brad Bishop08902b02019-08-20 09:16:51 -0400714 if ovmf.endswith('secboot'):
715 self.setup_pkkek1()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500716 break
717 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500718 raise RunQemuError("Can't find OVMF firmware: %s" % ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500719
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600720 def check_kernel(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400721 """Check and set kernel"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600722 # The vm image doesn't need a kernel
723 if self.fstype in self.vmtypes:
724 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500725
Brad Bishop316dfdd2018-06-25 12:45:53 -0400726 # See if the user supplied a KERNEL option
727 if self.get('KERNEL'):
728 self.kernel = self.get('KERNEL')
729
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500730 # QB_DEFAULT_KERNEL is always a full file path
731 kernel_name = os.path.basename(self.get('QB_DEFAULT_KERNEL'))
732
733 # The user didn't want a kernel to be loaded
Brad Bishop316dfdd2018-06-25 12:45:53 -0400734 if kernel_name == "none" and not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500735 return
736
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600737 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
738 if not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500739 kernel_match_name = "%s/%s" % (deploy_dir_image, kernel_name)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600740 kernel_match_link = "%s/%s" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
741 kernel_startswith = "%s/%s*" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
742 cmds = (kernel_match_name, kernel_match_link, kernel_startswith)
743 self.kernel = get_first_file(cmds)
744 if not self.kernel:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500745 raise RunQemuError('KERNEL not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500746
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600747 if not os.path.exists(self.kernel):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500748 raise RunQemuError("KERNEL %s not found" % self.kernel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500749
Brad Bishop316dfdd2018-06-25 12:45:53 -0400750 def check_dtb(self):
751 """Check and set dtb"""
752 # Did the user specify a device tree?
753 if self.get('DEVICE_TREE'):
754 self.dtb = self.get('DEVICE_TREE')
755 if not os.path.exists(self.dtb):
756 raise RunQemuError('Specified DTB not found: %s' % self.dtb)
757 return
758
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600759 dtb = self.get('QB_DTB')
760 if dtb:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400761 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600762 cmd_match = "%s/%s" % (deploy_dir_image, dtb)
763 cmd_startswith = "%s/%s*" % (deploy_dir_image, dtb)
764 cmd_wild = "%s/*.dtb" % deploy_dir_image
765 cmds = (cmd_match, cmd_startswith, cmd_wild)
766 self.dtb = get_first_file(cmds)
767 if not os.path.exists(self.dtb):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500768 raise RunQemuError('DTB not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500769
Brad Bishopc68388fc2019-08-26 01:33:31 -0400770 def check_bios(self):
771 """Check and set bios"""
772
773 # See if the user supplied a BIOS option
774 if self.get('BIOS'):
775 self.bios = self.get('BIOS')
776
777 # QB_DEFAULT_BIOS is always a full file path
778 bios_name = os.path.basename(self.get('QB_DEFAULT_BIOS'))
779
780 # The user didn't want a bios to be loaded
781 if (bios_name == "" or bios_name == "none") and not self.bios:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600782 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500783
Brad Bishopc68388fc2019-08-26 01:33:31 -0400784 if not self.bios:
785 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
786 self.bios = "%s/%s" % (deploy_dir_image, bios_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500787
Brad Bishopc68388fc2019-08-26 01:33:31 -0400788 if not self.bios:
789 raise RunQemuError('BIOS not found: %s' % bios_match_name)
790
791 if not os.path.exists(self.bios):
Patrick Williams213cb262021-08-07 19:21:33 -0500792 raise RunQemuError("BIOS %s not found" % self.bios)
Brad Bishopc68388fc2019-08-26 01:33:31 -0400793
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500794
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600795 def check_mem(self):
Andrew Geissler99467da2019-02-25 18:54:23 -0600796 """
797 Both qemu and kernel needs memory settings, so check QB_MEM and set it
798 for both.
799 """
800 s = re.search('-m +([0-9]+)', self.qemuparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600801 if s:
802 self.set('QB_MEM', '-m %s' % s.group(1))
803 elif not self.get('QB_MEM'):
Brad Bishop79641f22019-09-10 07:20:22 -0400804 logger.info('QB_MEM is not set, use 256M by default')
805 self.set('QB_MEM', '-m 256')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500806
Andrew Geissler99467da2019-02-25 18:54:23 -0600807 # Check and remove M or m suffix
808 qb_mem = self.get('QB_MEM')
809 if qb_mem.endswith('M') or qb_mem.endswith('m'):
810 qb_mem = qb_mem[:-1]
811
812 # Add -m prefix it not present
813 if not qb_mem.startswith('-m'):
814 qb_mem = '-m %s' % qb_mem
815
816 self.set('QB_MEM', qb_mem)
817
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800818 mach = self.get('MACHINE')
Andrew Geissler9aee5002022-03-30 16:27:02 +0000819 if not mach.startswith(('qemumips', 'qemux86')):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800820 self.kernel_cmdline_script += ' mem=%s' % self.get('QB_MEM').replace('-m','').strip() + 'M'
821
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600822 self.qemu_opt_script += ' %s' % self.get('QB_MEM')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500823
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600824 def check_tcpserial(self):
825 if self.tcpserial_portnum:
Brad Bishop15ae2502019-06-18 21:44:24 -0400826 ports = self.tcpserial_portnum.split(':')
827 port = ports[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600828 if self.get('QB_TCPSERIAL_OPT'):
Brad Bishop15ae2502019-06-18 21:44:24 -0400829 self.qemu_opt_script += ' ' + self.get('QB_TCPSERIAL_OPT').replace('@PORT@', port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600830 else:
Brad Bishop15ae2502019-06-18 21:44:24 -0400831 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
832
833 if len(ports) > 1:
834 for port in ports[1:]:
835 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500836
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600837 def check_and_set(self):
838 """Check configs sanity and set when needed"""
839 self.validate_paths()
Andrew Geissler82c905d2020-04-13 13:39:40 -0500840 if not self.slirp_enabled and not self.net_bridge:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500841 check_tun()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600842 # Check audio
843 if self.audio_enabled:
844 if not self.get('QB_AUDIO_DRV'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500845 raise RunQemuError("QB_AUDIO_DRV is NULL, this board doesn't support audio")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600846 if not self.get('QB_AUDIO_OPT'):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800847 logger.warning('QB_AUDIO_OPT is NULL, you may need define it to make audio work')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600848 else:
849 self.qemu_opt_script += ' %s' % self.get('QB_AUDIO_OPT')
850 os.putenv('QEMU_AUDIO_DRV', self.get('QB_AUDIO_DRV'))
851 else:
852 os.putenv('QEMU_AUDIO_DRV', 'none')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500853
Brad Bishop15ae2502019-06-18 21:44:24 -0400854 self.check_qemu_system()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600855 self.check_kvm()
856 self.check_fstype()
857 self.check_rootfs()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500858 self.check_ovmf()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600859 self.check_kernel()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400860 self.check_dtb()
Brad Bishopc68388fc2019-08-26 01:33:31 -0400861 self.check_bios()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600862 self.check_mem()
863 self.check_tcpserial()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500864
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600865 def read_qemuboot(self):
866 if not self.qemuboot:
867 if self.get('DEPLOY_DIR_IMAGE'):
868 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600869 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800870 logger.warning("Can't find qemuboot conf file, DEPLOY_DIR_IMAGE is NULL!")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600871 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500872
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600873 if self.rootfs and not os.path.exists(self.rootfs):
874 # Lazy rootfs
875 machine = self.get('MACHINE')
876 if not machine:
877 machine = os.path.basename(deploy_dir_image)
878 self.qemuboot = "%s/%s-%s.qemuboot.conf" % (deploy_dir_image,
879 self.rootfs, machine)
880 else:
881 cmd = 'ls -t %s/*.qemuboot.conf' % deploy_dir_image
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500882 logger.debug('Running %s...' % cmd)
883 try:
884 qbs = subprocess.check_output(cmd, shell=True).decode('utf-8')
885 except subprocess.CalledProcessError as err:
886 raise RunQemuError(err)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600887 if qbs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500888 for qb in qbs.split():
889 # Don't use initramfs when other choices unless fstype is ramfs
890 if '-initramfs-' in os.path.basename(qb) and self.fstype != 'cpio.gz':
891 continue
892 self.qemuboot = qb
893 break
894 if not self.qemuboot:
895 # Use the first one when no choice
896 self.qemuboot = qbs.split()[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600897 self.qbconfload = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500898
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600899 if not self.qemuboot:
900 # If we haven't found a .qemuboot.conf at this point it probably
901 # doesn't exist, continue without
902 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500903
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600904 if not os.path.exists(self.qemuboot):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500905 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 -0500906
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500907 logger.debug('CONFFILE: %s' % self.qemuboot)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500908
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600909 cf = configparser.ConfigParser()
910 cf.read(self.qemuboot)
911 for k, v in cf.items('config_bsp'):
912 k_upper = k.upper()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500913 if v.startswith("../"):
914 v = os.path.abspath(os.path.dirname(self.qemuboot) + "/" + v)
915 elif v == ".":
916 v = os.path.dirname(self.qemuboot)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600917 self.set(k_upper, v)
918
919 def validate_paths(self):
920 """Ensure all relevant path variables are set"""
921 # When we're started with a *.qemuboot.conf arg assume that image
922 # artefacts are relative to that file, rather than in whatever
923 # directory DEPLOY_DIR_IMAGE in the conf file points to.
924 if self.qbconfload:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500925 imgdir = os.path.realpath(os.path.dirname(self.qemuboot))
926 if imgdir != os.path.realpath(self.get('DEPLOY_DIR_IMAGE')):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600927 logger.info('Setting DEPLOY_DIR_IMAGE to folder containing %s (%s)' % (self.qemuboot, imgdir))
928 self.set('DEPLOY_DIR_IMAGE', imgdir)
929
930 # If the STAGING_*_NATIVE directories from the config file don't exist
931 # and we're in a sourced OE build directory try to extract the paths
932 # from `bitbake -e`
933 havenative = os.path.exists(self.get('STAGING_DIR_NATIVE')) and \
934 os.path.exists(self.get('STAGING_BINDIR_NATIVE'))
935
936 if not havenative:
937 if not self.bitbake_e:
938 self.load_bitbake_env()
939
940 if self.bitbake_e:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500941 native_vars = ['STAGING_DIR_NATIVE']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600942 for nv in native_vars:
943 s = re.search('^%s="(.*)"' % nv, self.bitbake_e, re.M)
944 if s and s.group(1) != self.get(nv):
945 logger.info('Overriding conf file setting of %s to %s from Bitbake environment' % (nv, s.group(1)))
946 self.set(nv, s.group(1))
947 else:
948 # when we're invoked from a running bitbake instance we won't
949 # be able to call `bitbake -e`, then try:
950 # - get OE_TMPDIR from environment and guess paths based on it
951 # - get OECORE_NATIVE_SYSROOT from environment (for sdk)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500952 tmpdir = self.get('OE_TMPDIR')
953 oecore_native_sysroot = self.get('OECORE_NATIVE_SYSROOT')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600954 if tmpdir:
955 logger.info('Setting STAGING_DIR_NATIVE and STAGING_BINDIR_NATIVE relative to OE_TMPDIR (%s)' % tmpdir)
956 hostos, _, _, _, machine = os.uname()
957 buildsys = '%s-%s' % (machine, hostos.lower())
958 staging_dir_native = '%s/sysroots/%s' % (tmpdir, buildsys)
959 self.set('STAGING_DIR_NATIVE', staging_dir_native)
960 elif oecore_native_sysroot:
961 logger.info('Setting STAGING_DIR_NATIVE to OECORE_NATIVE_SYSROOT (%s)' % oecore_native_sysroot)
962 self.set('STAGING_DIR_NATIVE', oecore_native_sysroot)
963 if self.get('STAGING_DIR_NATIVE'):
964 # we have to assume that STAGING_BINDIR_NATIVE is at usr/bin
965 staging_bindir_native = '%s/usr/bin' % self.get('STAGING_DIR_NATIVE')
966 logger.info('Setting STAGING_BINDIR_NATIVE to %s' % staging_bindir_native)
967 self.set('STAGING_BINDIR_NATIVE', '%s/usr/bin' % self.get('STAGING_DIR_NATIVE'))
968
969 def print_config(self):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500970 logoutput = ['Continuing with the following parameters:']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600971 if not self.fstype in self.vmtypes:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500972 logoutput.append('KERNEL: [%s]' % self.kernel)
Brad Bishopc68388fc2019-08-26 01:33:31 -0400973 if self.bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500974 logoutput.append('BIOS: [%s]' % self.bios)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600975 if self.dtb:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500976 logoutput.append('DTB: [%s]' % self.dtb)
977 logoutput.append('MACHINE: [%s]' % self.get('MACHINE'))
Brad Bishop15ae2502019-06-18 21:44:24 -0400978 try:
979 fstype_flags = ' (' + ', '.join(self.fsinfo[self.fstype]) + ')'
980 except KeyError:
981 fstype_flags = ''
Andrew Geissler82c905d2020-04-13 13:39:40 -0500982 logoutput.append('FSTYPE: [%s%s]' % (self.fstype, fstype_flags))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600983 if self.fstype == 'nfs':
Andrew Geissler82c905d2020-04-13 13:39:40 -0500984 logoutput.append('NFS_DIR: [%s]' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600985 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500986 logoutput.append('ROOTFS: [%s]' % self.rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500987 if self.ovmf_bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500988 logoutput.append('OVMF: %s' % self.ovmf_bios)
Brad Bishop08902b02019-08-20 09:16:51 -0400989 if (self.ovmf_secboot_pkkek1):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500990 logoutput.append('SECBOOT PKKEK1: [%s...]' % self.ovmf_secboot_pkkek1[0:100])
991 logoutput.append('CONFFILE: [%s]' % self.qemuboot)
992 logoutput.append('')
993 logger.info('\n'.join(logoutput))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600994
995 def setup_nfs(self):
996 if not self.nfs_server:
997 if self.slirp_enabled:
998 self.nfs_server = '10.0.2.2'
999 else:
1000 self.nfs_server = '192.168.7.1'
1001
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001002 # Figure out a new nfs_instance to allow multiple qemus running.
Brad Bishop977dc1a2019-02-06 16:01:43 -05001003 ps = subprocess.check_output(("ps", "auxww")).decode('utf-8')
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001004 pattern = '/bin/unfsd .* -i .*\.pid -e .*/exports([0-9]+) '
1005 all_instances = re.findall(pattern, ps, re.M)
1006 if all_instances:
1007 all_instances.sort(key=int)
1008 self.nfs_instance = int(all_instances.pop()) + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001009
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001010 nfsd_port = 3049 + 2 * self.nfs_instance
1011 mountd_port = 3048 + 2 * self.nfs_instance
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001012
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001013 # Export vars for runqemu-export-rootfs
1014 export_dict = {
1015 'NFS_INSTANCE': self.nfs_instance,
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001016 'NFSD_PORT': nfsd_port,
1017 'MOUNTD_PORT': mountd_port,
1018 }
1019 for k, v in export_dict.items():
1020 # Use '%s' since they are integers
1021 os.putenv(k, '%s' % v)
1022
Andrew Geissler82c905d2020-04-13 13:39:40 -05001023 self.unfs_opts="nfsvers=3,port=%s,tcp,mountport=%s" % (nfsd_port, mountd_port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001024
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001025 # Extract .tar.bz2 or .tar.bz if no nfs dir
1026 if not (self.rootfs and os.path.isdir(self.rootfs)):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001027 src_prefix = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'))
1028 dest = "%s-nfsroot" % src_prefix
1029 if os.path.exists('%s.pseudo_state' % dest):
1030 logger.info('Use %s as NFS_DIR' % dest)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001031 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001032 else:
1033 src = ""
1034 src1 = '%s.tar.bz2' % src_prefix
1035 src2 = '%s.tar.gz' % src_prefix
1036 if os.path.exists(src1):
1037 src = src1
1038 elif os.path.exists(src2):
1039 src = src2
1040 if not src:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001041 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 -06001042 logger.info('NFS_DIR not found, extracting %s to %s' % (src, dest))
Brad Bishop977dc1a2019-02-06 16:01:43 -05001043 cmd = ('runqemu-extract-sdk', src, dest)
1044 logger.info('Running %s...' % str(cmd))
1045 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001046 raise RunQemuError('Failed to run %s' % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001047 self.rootfs = dest
Andrew Geisslerc926e172021-05-07 16:11:35 -05001048 self.cleanup_files.append(self.rootfs)
1049 self.cleanup_files.append('%s.pseudo_state' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001050
1051 # Start the userspace NFS server
Brad Bishop977dc1a2019-02-06 16:01:43 -05001052 cmd = ('runqemu-export-rootfs', 'start', self.rootfs)
1053 logger.info('Running %s...' % str(cmd))
1054 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001055 raise RunQemuError('Failed to run %s' % cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001056
1057 self.nfs_running = True
1058
Andrew Geissler82c905d2020-04-13 13:39:40 -05001059 def setup_net_bridge(self):
1060 self.set('NETWORK_CMD', '-netdev bridge,br=%s,id=net0,helper=%s -device virtio-net-pci,netdev=net0 ' % (
1061 self.net_bridge, os.path.join(self.bindir_native, 'qemu-oe-bridge-helper')))
1062
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001063 def setup_slirp(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001064 """Setup user networking"""
1065
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001066 if self.fstype == 'nfs':
1067 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001068 netconf = " " + self.cmdline_ip_slirp
1069 logger.info("Network configuration:%s", netconf)
1070 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001071 # Port mapping
1072 hostfwd = ",hostfwd=tcp::2222-:22,hostfwd=tcp::2323-:23"
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001073 qb_slirp_opt_default = "-netdev user,id=net0%s,tftp=%s" % (hostfwd, self.get('DEPLOY_DIR_IMAGE'))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001074 qb_slirp_opt = self.get('QB_SLIRP_OPT') or qb_slirp_opt_default
1075 # Figure out the port
1076 ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt)
1077 ports = [int(i) for i in ports]
1078 mac = 2
Brad Bishop08902b02019-08-20 09:16:51 -04001079
1080 lockdir = "/tmp/qemu-port-locks"
1081 if not os.path.exists(lockdir):
1082 # There might be a race issue when multi runqemu processess are
1083 # running at the same time.
1084 try:
1085 os.mkdir(lockdir)
1086 os.chmod(lockdir, 0o777)
1087 except FileExistsError:
1088 pass
1089
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001090 # Find a free port to avoid conflicts
1091 for p in ports[:]:
1092 p_new = p
Brad Bishop08902b02019-08-20 09:16:51 -04001093 while not self.check_free_port('localhost', p_new, lockdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001094 p_new += 1
1095 mac += 1
1096 while p_new in ports:
1097 p_new += 1
1098 mac += 1
1099 if p != p_new:
1100 ports.append(p_new)
1101 qb_slirp_opt = re.sub(':%s-' % p, ':%s-' % p_new, qb_slirp_opt)
1102 logger.info("Port forward changed: %s -> %s" % (p, p_new))
1103 mac = "%s%02x" % (self.mac_slirp, mac)
1104 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qb_slirp_opt))
1105 # Print out port foward
1106 hostfwd = re.findall('(hostfwd=[^,]*)', qb_slirp_opt)
1107 if hostfwd:
1108 logger.info('Port forward: %s' % ' '.join(hostfwd))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001109
1110 def setup_tap(self):
1111 """Setup tap"""
1112
1113 # This file is created when runqemu-gen-tapdevs creates a bank of tap
1114 # devices, indicating that the user should not bring up new ones using
1115 # sudo.
1116 nosudo_flag = '/etc/runqemu-nosudo'
1117 self.qemuifup = shutil.which('runqemu-ifup')
1118 self.qemuifdown = shutil.which('runqemu-ifdown')
1119 ip = shutil.which('ip')
1120 lockdir = "/tmp/qemu-tap-locks"
1121
1122 if not (self.qemuifup and self.qemuifdown and ip):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001123 logger.error("runqemu-ifup: %s" % self.qemuifup)
1124 logger.error("runqemu-ifdown: %s" % self.qemuifdown)
1125 logger.error("ip: %s" % ip)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001126 raise OEPathError("runqemu-ifup, runqemu-ifdown or ip not found")
1127
1128 if not os.path.exists(lockdir):
1129 # There might be a race issue when multi runqemu processess are
1130 # running at the same time.
1131 try:
1132 os.mkdir(lockdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001133 os.chmod(lockdir, 0o777)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001134 except FileExistsError:
1135 pass
1136
Brad Bishop977dc1a2019-02-06 16:01:43 -05001137 cmd = (ip, 'link')
1138 logger.debug('Running %s...' % str(cmd))
1139 ip_link = subprocess.check_output(cmd).decode('utf-8')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001140 # Matches line like: 6: tap0: <foo>
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001141 possibles = re.findall('^[0-9]+: +(tap[0-9]+): <.*', ip_link, re.M)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001142 tap = ""
1143 for p in possibles:
1144 lockfile = os.path.join(lockdir, p)
1145 if os.path.exists('%s.skip' % lockfile):
1146 logger.info('Found %s.skip, skipping %s' % (lockfile, p))
1147 continue
Brad Bishop08902b02019-08-20 09:16:51 -04001148 self.taplock = lockfile + '.lock'
1149 if self.acquire_taplock(error=False):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001150 tap = p
1151 logger.info("Using preconfigured tap device %s" % tap)
1152 logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap))
1153 break
1154
1155 if not tap:
1156 if os.path.exists(nosudo_flag):
1157 logger.error("Error: There are no available tap devices to use for networking,")
1158 logger.error("and I see %s exists, so I am not going to try creating" % nosudo_flag)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001159 raise RunQemuError("a new one with sudo.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001160
1161 gid = os.getgid()
1162 uid = os.getuid()
1163 logger.info("Setting up tap interface under sudo")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001164 cmd = ('sudo', self.qemuifup, str(uid), str(gid), self.bindir_native)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001165 try:
1166 tap = subprocess.check_output(cmd).decode('utf-8').strip()
1167 except subprocess.CalledProcessError as e:
1168 logger.error('Setting up tap device failed:\n%s\nRun runqemu-gen-tapdevs to manually create one.' % str(e))
1169 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001170 lockfile = os.path.join(lockdir, tap)
Brad Bishop08902b02019-08-20 09:16:51 -04001171 self.taplock = lockfile + '.lock'
1172 self.acquire_taplock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001173 self.cleantap = True
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001174 logger.debug('Created tap: %s' % tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001175
1176 if not tap:
1177 logger.error("Failed to setup tap device. Run runqemu-gen-tapdevs to manually create.")
Andrew Geissler82c905d2020-04-13 13:39:40 -05001178 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001179 self.tap = tap
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001180 tapnum = int(tap[3:])
1181 gateway = tapnum * 2 + 1
1182 client = gateway + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001183 if self.fstype == 'nfs':
1184 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001185 netconf = " " + self.cmdline_ip_tap
1186 netconf = netconf.replace('@CLIENT@', str(client))
1187 netconf = netconf.replace('@GATEWAY@', str(gateway))
1188 logger.info("Network configuration:%s", netconf)
1189 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001190 mac = "%s%02x" % (self.mac_tap, client)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001191 qb_tap_opt = self.get('QB_TAP_OPT')
1192 if qb_tap_opt:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001193 qemu_tap_opt = qb_tap_opt.replace('@TAP@', tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001194 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001195 qemu_tap_opt = "-netdev tap,id=net0,ifname=%s,script=no,downscript=no" % (self.tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001196
1197 if self.vhost_enabled:
1198 qemu_tap_opt += ',vhost=on'
1199
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001200 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qemu_tap_opt))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001201
1202 def setup_network(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001203 if self.get('QB_NET') == 'none':
1204 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001205 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001206 self.saved_stty = subprocess.check_output(("stty", "-g")).decode('utf-8').strip()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001207 self.network_device = self.get('QB_NETWORK_DEVICE') or self.network_device
Andrew Geissler82c905d2020-04-13 13:39:40 -05001208 if self.net_bridge:
1209 self.setup_net_bridge()
1210 elif self.slirp_enabled:
1211 self.cmdline_ip_slirp = self.get('QB_CMDLINE_IP_SLIRP') or self.cmdline_ip_slirp
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001212 self.setup_slirp()
1213 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001214 self.cmdline_ip_tap = self.get('QB_CMDLINE_IP_TAP') or self.cmdline_ip_tap
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001215 self.setup_tap()
1216
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001217 def setup_rootfs(self):
1218 if self.get('QB_ROOTFS') == 'none':
1219 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001220 if 'wic.' in self.fstype:
1221 self.fstype = self.fstype[4:]
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001222 rootfs_format = self.fstype if self.fstype in ('vmdk', 'vhd', 'vhdx', 'qcow2', 'vdi') else 'raw'
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001223
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001224 tmpfsdir = os.environ.get("RUNQEMU_TMPFS_DIR", None)
1225 if self.snapshot and tmpfsdir:
1226 newrootfs = os.path.join(tmpfsdir, os.path.basename(self.rootfs)) + "." + str(os.getpid())
Andrew Geissler09036742021-06-25 14:25:14 -05001227 logger.info("Copying rootfs to %s" % newrootfs)
1228 copy_start = time.time()
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001229 shutil.copyfile(self.rootfs, newrootfs)
Andrew Geissler09036742021-06-25 14:25:14 -05001230 logger.info("Copy done in %s seconds" % (time.time() - copy_start))
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001231 self.rootfs = newrootfs
1232 # Don't need a second copy now!
1233 self.snapshot = False
Andrew Geisslerc926e172021-05-07 16:11:35 -05001234 self.cleanup_files.append(newrootfs)
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001235
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001236 qb_rootfs_opt = self.get('QB_ROOTFS_OPT')
1237 if qb_rootfs_opt:
1238 self.rootfs_options = qb_rootfs_opt.replace('@ROOTFS@', self.rootfs)
1239 else:
1240 self.rootfs_options = '-drive file=%s,if=virtio,format=%s' % (self.rootfs, rootfs_format)
1241
Andrew Geissler82c905d2020-04-13 13:39:40 -05001242 qb_rootfs_extra_opt = self.get("QB_ROOTFS_EXTRA_OPT")
1243 if qb_rootfs_extra_opt and not qb_rootfs_extra_opt.startswith(","):
1244 qb_rootfs_extra_opt = "," + qb_rootfs_extra_opt
1245
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001246 if self.fstype in ('cpio.gz', 'cpio'):
1247 self.kernel_cmdline = 'root=/dev/ram0 rw debugshell'
1248 self.rootfs_options = '-initrd %s' % self.rootfs
1249 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001250 vm_drive = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001251 if self.fstype in self.vmtypes:
1252 if self.fstype == 'iso':
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001253 vm_drive = '-drive file=%s,if=virtio,media=cdrom' % self.rootfs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001254 elif self.get('QB_DRIVE_TYPE'):
1255 drive_type = self.get('QB_DRIVE_TYPE')
1256 if drive_type.startswith("/dev/sd"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001257 logger.info('Using scsi drive')
Andrew Geissler82c905d2020-04-13 13:39:40 -05001258 vm_drive = '-drive if=none,id=hd,file=%s,format=%s -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd%s' \
1259 % (self.rootfs, rootfs_format, qb_rootfs_extra_opt)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001260 elif drive_type.startswith("/dev/hd"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001261 logger.info('Using ide drive')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001262 vm_drive = "-drive file=%s,format=%s" % (self.rootfs, rootfs_format)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001263 elif drive_type.startswith("/dev/vdb"):
1264 logger.info('Using block virtio drive');
1265 vm_drive = '-drive id=disk0,file=%s,if=none,format=%s -device virtio-blk-device,drive=disk0%s' \
1266 % (self.rootfs, rootfs_format,qb_rootfs_extra_opt)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001267 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001268 # virtio might have been selected explicitly (just use it), or
1269 # is used as fallback (then warn about that).
1270 if not drive_type.startswith("/dev/vd"):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001271 logger.warning("Unknown QB_DRIVE_TYPE: %s" % drive_type)
1272 logger.warning("Failed to figure out drive type, consider define or fix QB_DRIVE_TYPE")
1273 logger.warning('Trying to use virtio block drive')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001274 vm_drive = '-drive if=virtio,file=%s,format=%s' % (self.rootfs, rootfs_format)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001275
1276 # All branches above set vm_drive.
Andrew Geissler475cb722020-07-10 16:00:51 -05001277 self.rootfs_options = vm_drive
1278 if not self.fstype in self.vmtypes:
1279 self.rootfs_options += ' -no-reboot'
Andrew Geissler595f6302022-01-24 19:11:47 +00001280
1281 # By default, ' rw' is appended to QB_KERNEL_ROOT unless either ro or rw is explicitly passed.
1282 qb_kernel_root = self.get('QB_KERNEL_ROOT')
1283 qb_kernel_root_l = qb_kernel_root.split()
1284 if not ('ro' in qb_kernel_root_l or 'rw' in qb_kernel_root_l):
1285 qb_kernel_root += ' rw'
1286 self.kernel_cmdline = 'root=%s' % qb_kernel_root
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001287
1288 if self.fstype == 'nfs':
1289 self.rootfs_options = ''
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001290 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 -04001291 self.kernel_cmdline = 'root=%s rw' % k_root
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001292
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001293 if self.fstype == 'none':
1294 self.rootfs_options = ''
1295
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001296 self.set('ROOTFS_OPTIONS', self.rootfs_options)
1297
1298 def guess_qb_system(self):
1299 """attempt to determine the appropriate qemu-system binary"""
1300 mach = self.get('MACHINE')
1301 if not mach:
1302 search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*'
1303 if self.rootfs:
1304 match = re.match(search, self.rootfs)
1305 if match:
1306 mach = match.group(1)
1307 elif self.kernel:
1308 match = re.match(search, self.kernel)
1309 if match:
1310 mach = match.group(1)
1311
1312 if not mach:
1313 return None
1314
1315 if mach == 'qemuarm':
1316 qbsys = 'arm'
1317 elif mach == 'qemuarm64':
1318 qbsys = 'aarch64'
1319 elif mach == 'qemux86':
1320 qbsys = 'i386'
1321 elif mach == 'qemux86-64':
1322 qbsys = 'x86_64'
1323 elif mach == 'qemuppc':
1324 qbsys = 'ppc'
1325 elif mach == 'qemumips':
1326 qbsys = 'mips'
1327 elif mach == 'qemumips64':
1328 qbsys = 'mips64'
1329 elif mach == 'qemumipsel':
1330 qbsys = 'mipsel'
1331 elif mach == 'qemumips64el':
1332 qbsys = 'mips64el'
Brad Bishop316dfdd2018-06-25 12:45:53 -04001333 elif mach == 'qemuriscv64':
1334 qbsys = 'riscv64'
1335 elif mach == 'qemuriscv32':
1336 qbsys = 'riscv32'
Brad Bishop004d4992018-10-02 23:54:45 +02001337 else:
1338 logger.error("Unable to determine QEMU PC System emulator for %s machine." % mach)
1339 logger.error("As %s is not among valid QEMU machines such as," % mach)
1340 logger.error("qemux86-64, qemux86, qemuarm64, qemuarm, qemumips64, qemumips64el, qemumipsel, qemumips, qemuppc")
1341 raise RunQemuError("Set qb_system_name with suitable QEMU PC System emulator in .*qemuboot.conf.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001342
1343 return 'qemu-system-%s' % qbsys
1344
Brad Bishop15ae2502019-06-18 21:44:24 -04001345 def check_qemu_system(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001346 qemu_system = self.get('QB_SYSTEM_NAME')
1347 if not qemu_system:
1348 qemu_system = self.guess_qb_system()
1349 if not qemu_system:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001350 raise RunQemuError("Failed to boot, QB_SYSTEM_NAME is NULL!")
Brad Bishop15ae2502019-06-18 21:44:24 -04001351 self.qemu_system = qemu_system
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001352
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001353 def setup_vga(self):
1354 if self.nographic == True:
1355 if self.sdl == True:
1356 raise RunQemuError('Option nographic makes no sense alongside the sdl option.')
1357 if self.gtk == True:
1358 raise RunQemuError('Option nographic makes no sense alongside the gtk option.')
1359 self.qemu_opt += ' -nographic'
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001360
1361 if self.novga == True:
1362 self.qemu_opt += ' -vga none'
1363 return
1364
1365 if (self.gl_es == True or self.gl == True) and (self.sdl == False and self.gtk == False):
1366 raise RunQemuError('Option gl/gl-es needs gtk or sdl option.')
1367
Patrick Williams03907ee2022-05-01 06:28:52 -05001368 # If we have no display option, we autodetect based upon what qemu supports. We
1369 # need our font setup and show-cusor below so we need to see what qemu --help says
1370 # is supported so we can pass our correct config in.
1371 if not self.nographic and not self.sdl and not self.gtk and not self.publicvnc and not self.egl_headless == True:
1372 output = subprocess.check_output([self.qemu_bin, "--help"], universal_newlines=True)
1373 if "-display gtk" in output:
1374 self.gtk = True
1375 elif "-display sdl" in output:
1376 self.sdl = True
Andrew Geissler595f6302022-01-24 19:11:47 +00001377 else:
Patrick Williams03907ee2022-05-01 06:28:52 -05001378 self.qemu_opt += '-display none'
Andrew Geissler595f6302022-01-24 19:11:47 +00001379
Patrick Williams03907ee2022-05-01 06:28:52 -05001380 if self.sdl == True or self.gtk == True or self.egl_headless == True:
1381
1382 if self.qemu_system.endswith(('i386', 'x86_64')):
1383 if self.gl or self.gl_es or self.egl_headless:
1384 self.qemu_opt += ' -device virtio-vga-gl '
1385 else:
1386 self.qemu_opt += ' -device virtio-vga '
1387
1388 self.qemu_opt += ' -display '
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001389 if self.egl_headless == True:
Andrew Geissler595f6302022-01-24 19:11:47 +00001390 self.set_dri_path()
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001391 self.qemu_opt += 'egl-headless,'
1392 else:
1393 if self.sdl == True:
1394 self.qemu_opt += 'sdl,'
1395 elif self.gtk == True:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001396 os.environ['FONTCONFIG_PATH'] = '/etc/fonts'
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001397 self.qemu_opt += 'gtk,'
1398
1399 if self.gl == True:
Andrew Geissler595f6302022-01-24 19:11:47 +00001400 self.set_dri_path()
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001401 self.qemu_opt += 'gl=on,'
1402 elif self.gl_es == True:
Andrew Geissler595f6302022-01-24 19:11:47 +00001403 self.set_dri_path()
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001404 self.qemu_opt += 'gl=es,'
1405 self.qemu_opt += 'show-cursor=on'
1406
1407 self.qemu_opt += ' %s' %self.get('QB_GRAPHICS')
1408
1409 def setup_serial(self):
1410 # Setup correct kernel command line for serial
Andrew Geissler595f6302022-01-24 19:11:47 +00001411 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 -05001412 for entry in self.get('SERIAL_CONSOLES').split(' '):
1413 self.kernel_cmdline_script += ' console=%s' %entry.split(';')[1]
1414
1415 if self.serialstdio == True or self.nographic == True:
1416 self.qemu_opt += " -serial mon:stdio"
1417 else:
1418 self.qemu_opt += " -serial mon:vc"
1419 if self.serialconsole:
1420 if sys.stdin.isatty():
1421 subprocess.check_call(("stty", "intr", "^]"))
1422 logger.info("Interrupt character is '^]'")
1423
1424 self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT")
1425
1426 # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES).
1427 # If no serial or serialtcp options were specified, only ttyS0 is created
1428 # and sysvinit shows an error trying to enable ttyS1:
1429 # INIT: Id "S1" respawning too fast: disabled for 5 minutes
1430 serial_num = len(re.findall("-serial", self.qemu_opt))
1431 if serial_num < 2:
1432 self.qemu_opt += " -serial null"
1433
Patrick Williams03907ee2022-05-01 06:28:52 -05001434 def find_qemu(self):
Brad Bishop15ae2502019-06-18 21:44:24 -04001435 qemu_bin = os.path.join(self.bindir_native, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001436
1437 # It is possible to have qemu-native in ASSUME_PROVIDED, and it won't
1438 # find QEMU in sysroot, it needs to use host's qemu.
1439 if not os.path.exists(qemu_bin):
1440 logger.info("QEMU binary not found in %s, trying host's QEMU" % qemu_bin)
1441 for path in (os.environ['PATH'] or '').split(':'):
Brad Bishop15ae2502019-06-18 21:44:24 -04001442 qemu_bin_tmp = os.path.join(path, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001443 logger.info("Trying: %s" % qemu_bin_tmp)
1444 if os.path.exists(qemu_bin_tmp):
1445 qemu_bin = qemu_bin_tmp
1446 if not os.path.isabs(qemu_bin):
1447 qemu_bin = os.path.abspath(qemu_bin)
1448 logger.info("Using host's QEMU: %s" % qemu_bin)
1449 break
1450
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001451 if not os.access(qemu_bin, os.X_OK):
1452 raise OEPathError("No QEMU binary '%s' could be found" % qemu_bin)
Patrick Williams03907ee2022-05-01 06:28:52 -05001453 self.qemu_bin = qemu_bin
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001454
Patrick Williams03907ee2022-05-01 06:28:52 -05001455 def setup_final(self):
1456
1457 self.find_qemu()
1458
1459 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 -05001460
1461 for ovmf in self.ovmf_bios:
1462 format = ovmf.rsplit('.', 1)[-1]
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001463 if format == "bin":
1464 format = "raw"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001465 self.qemu_opt += ' -drive if=pflash,format=%s,file=%s' % (format, ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001466
1467 self.qemu_opt += ' ' + self.qemu_opt_script
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001468
Brad Bishop08902b02019-08-20 09:16:51 -04001469 if self.ovmf_secboot_pkkek1:
1470 # Provide the Platform Key and first Key Exchange Key certificate as an
1471 # OEM string in the SMBIOS Type 11 table. Prepend the certificate string
1472 # with "application prefix" of the EnrollDefaultKeys.efi application
1473 self.qemu_opt += ' -smbios type=11,value=4e32566d-8e9e-4f52-81d3-5bb9715f9727:' \
1474 + self.ovmf_secboot_pkkek1
1475
Andrew Geissler99467da2019-02-25 18:54:23 -06001476 # Append qemuparams to override previous settings
1477 if self.qemuparams:
1478 self.qemu_opt += ' ' + self.qemuparams
1479
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001480 if self.snapshot:
1481 self.qemu_opt += " -snapshot"
1482
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001483 self.setup_serial()
1484 self.setup_vga()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001485
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001486 def start_qemu(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001487 import shlex
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001488 if self.kernel:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001489 kernel_opts = "-kernel %s -append '%s %s %s %s'" % (self.kernel, self.kernel_cmdline,
1490 self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'),
1491 self.bootparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001492 if self.dtb:
1493 kernel_opts += " -dtb %s" % self.dtb
1494 else:
1495 kernel_opts = ""
Patrick Williams213cb262021-08-07 19:21:33 -05001496
1497 if self.bios:
1498 self.qemu_opt += " -bios %s" % self.bios
1499
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001500 cmd = "%s %s" % (self.qemu_opt, kernel_opts)
Brad Bishop004d4992018-10-02 23:54:45 +02001501 cmds = shlex.split(cmd)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001502 logger.info('Running %s\n' % cmd)
Brad Bishopf86d0552018-12-04 14:18:15 -08001503 pass_fds = []
Brad Bishop08902b02019-08-20 09:16:51 -04001504 if self.taplock_descriptor:
1505 pass_fds = [self.taplock_descriptor.fileno()]
1506 if len(self.portlocks):
1507 for descriptor in self.portlocks.values():
1508 pass_fds.append(descriptor.fileno())
Brad Bishopf86d0552018-12-04 14:18:15 -08001509 process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds)
Brad Bishop004d4992018-10-02 23:54:45 +02001510 self.qemupid = process.pid
1511 retcode = process.wait()
1512 if retcode:
1513 if retcode == -signal.SIGTERM:
1514 logger.info("Qemu terminated by SIGTERM")
1515 else:
1516 logger.error("Failed to run qemu: %s", process.stderr.read().decode())
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001517
1518 def cleanup(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001519 if self.cleaned:
1520 return
1521
1522 # avoid dealing with SIGTERM when cleanup function is running
1523 signal.signal(signal.SIGTERM, signal.SIG_IGN)
1524
1525 logger.info("Cleaning up")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001526 if self.cleantap:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001527 cmd = ('sudo', self.qemuifdown, self.tap, self.bindir_native)
1528 logger.debug('Running %s' % str(cmd))
1529 subprocess.check_call(cmd)
Brad Bishop08902b02019-08-20 09:16:51 -04001530 self.release_taplock()
1531 self.release_portlock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001532
1533 if self.nfs_running:
1534 logger.info("Shutting down the userspace NFS server...")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001535 cmd = ("runqemu-export-rootfs", "stop", self.rootfs)
1536 logger.debug('Running %s' % str(cmd))
1537 subprocess.check_call(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001538
1539 if self.saved_stty:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001540 subprocess.check_call(("stty", self.saved_stty))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001541
Andrew Geisslerc926e172021-05-07 16:11:35 -05001542 if self.cleanup_files:
1543 for ent in self.cleanup_files:
1544 logger.info('Removing %s' % ent)
1545 if os.path.isfile(ent):
1546 os.remove(ent)
1547 else:
1548 shutil.rmtree(ent)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001549
Brad Bishop004d4992018-10-02 23:54:45 +02001550 self.cleaned = True
1551
Andrew Geissler82c905d2020-04-13 13:39:40 -05001552 def run_bitbake_env(self, mach=None):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001553 bitbake = shutil.which('bitbake')
1554 if not bitbake:
1555 return
1556
1557 if not mach:
1558 mach = self.get('MACHINE')
1559
Andrew Geissler82c905d2020-04-13 13:39:40 -05001560 multiconfig = self.get('MULTICONFIG')
1561 if multiconfig:
1562 multiconfig = "mc:%s" % multiconfig
1563
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001564 if mach:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001565 cmd = 'MACHINE=%s bitbake -e %s' % (mach, multiconfig)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001566 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001567 cmd = 'bitbake -e %s' % multiconfig
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001568
1569 logger.info('Running %s...' % cmd)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001570 return subprocess.check_output(cmd, shell=True).decode('utf-8')
1571
1572 def load_bitbake_env(self, mach=None):
1573 if self.bitbake_e:
1574 return
1575
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001576 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001577 self.bitbake_e = self.run_bitbake_env(mach=mach)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001578 except subprocess.CalledProcessError as err:
1579 self.bitbake_e = ''
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001580 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 -06001581
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001582 def validate_combos(self):
1583 if (self.fstype in self.vmtypes) and self.kernel:
1584 raise RunQemuError("%s doesn't need kernel %s!" % (self.fstype, self.kernel))
1585
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001586 @property
1587 def bindir_native(self):
1588 result = self.get('STAGING_BINDIR_NATIVE')
1589 if result and os.path.exists(result):
1590 return result
1591
Andrew Geissler82c905d2020-04-13 13:39:40 -05001592 cmd = ['bitbake', '-e']
1593 multiconfig = self.get('MULTICONFIG')
1594 if multiconfig:
1595 cmd.append('mc:%s:qemu-helper-native' % multiconfig)
1596 else:
1597 cmd.append('qemu-helper-native')
1598
Brad Bishop977dc1a2019-02-06 16:01:43 -05001599 logger.info('Running %s...' % str(cmd))
1600 out = subprocess.check_output(cmd).decode('utf-8')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001601
1602 match = re.search('^STAGING_BINDIR_NATIVE="(.*)"', out, re.M)
1603 if match:
1604 result = match.group(1)
1605 if os.path.exists(result):
1606 self.set('STAGING_BINDIR_NATIVE', result)
1607 return result
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001608 raise RunQemuError("Native sysroot directory %s doesn't exist" % result)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001609 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001610 raise RunQemuError("Can't find STAGING_BINDIR_NATIVE in '%s' output" % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001611
1612
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001613def main():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001614 if "help" in sys.argv or '-h' in sys.argv or '--help' in sys.argv:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001615 print_usage()
1616 return 0
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001617 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001618 config = BaseConfig()
Brad Bishop004d4992018-10-02 23:54:45 +02001619
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001620 renice = os.path.expanduser("~/bin/runqemu-renice")
1621 if os.path.exists(renice):
1622 logger.info('Using %s to renice' % renice)
1623 subprocess.check_call([renice, str(os.getpid())])
1624
Brad Bishop004d4992018-10-02 23:54:45 +02001625 def sigterm_handler(signum, frame):
1626 logger.info("SIGTERM received")
Andrew Geissler595f6302022-01-24 19:11:47 +00001627 if config.qemupid:
1628 os.kill(config.qemupid, signal.SIGTERM)
Brad Bishop004d4992018-10-02 23:54:45 +02001629 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001630 # Deliberately ignore the return code of 'tput smam'.
1631 subprocess.call(["tput", "smam"])
Brad Bishop004d4992018-10-02 23:54:45 +02001632 signal.signal(signal.SIGTERM, sigterm_handler)
1633
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001634 config.check_args()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001635 config.read_qemuboot()
1636 config.check_and_set()
1637 # Check whether the combos is valid or not
1638 config.validate_combos()
1639 config.print_config()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001640 config.setup_network()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001641 config.setup_rootfs()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001642 config.setup_final()
1643 config.start_qemu()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001644 except RunQemuError as err:
1645 logger.error(err)
1646 return 1
1647 except Exception as err:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001648 import traceback
1649 traceback.print_exc()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001650 return 1
1651 finally:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001652 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001653 # Deliberately ignore the return code of 'tput smam'.
1654 subprocess.call(["tput", "smam"])
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001655
1656if __name__ == "__main__":
1657 sys.exit(main())