blob: 0976273eb07b4d0edd534d6440d893505771fe76 [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
Patrick Williamsc0f7c042017-02-23 20:41:17 -060075 serial - enable a serial console on /dev/ttyS0
Brad Bishop19323692019-04-05 15:28:33 -040076 serialstdio - enable a serial console on the console (regardless of graphics mode)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060077 slirp - enable user networking, no root privileges is required
Brad Bishopa34c0302019-09-23 22:34:48 -040078 snapshot - don't write changes to back to images
Patrick Williamsc0f7c042017-02-23 20:41:17 -060079 kvm - enable KVM when running x86/x86_64 (VT-capable CPU required)
80 kvm-vhost - enable KVM with vhost when running x86/x86_64 (VT-capable CPU required)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050081 publicvnc - enable a VNC server open to all hosts
Patrick Williamsc0f7c042017-02-23 20:41:17 -060082 audio - enable audio
Brad Bishop6e60e8b2018-02-01 10:27:11 -050083 [*/]ovmf* - OVMF firmware file or base name for booting with UEFI
Patrick Williamsc0f7c042017-02-23 20:41:17 -060084 tcpserial=<port> - specify tcp serial port number
Patrick Williamsc0f7c042017-02-23 20:41:17 -060085 qemuparams=<xyz> - specify custom parameters to QEMU
86 bootparams=<xyz> - specify custom kernel parameters during boot
Brad Bishop6e60e8b2018-02-01 10:27:11 -050087 help, -h, --help: print this text
Brad Bishopd7bf8c12018-02-25 22:55:05 -050088 -d, --debug: Enable debug output
Brad Bishop79641f22019-09-10 07:20:22 -040089 -q, --quiet: Hide most output except error messages
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050090
91Examples:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050092 runqemu
Patrick Williamsc0f7c042017-02-23 20:41:17 -060093 runqemu qemuarm
94 runqemu tmp/deploy/images/qemuarm
Brad Bishop6e60e8b2018-02-01 10:27:11 -050095 runqemu tmp/deploy/images/qemux86/<qemuboot.conf>
Patrick Williamsc0f7c042017-02-23 20:41:17 -060096 runqemu qemux86-64 core-image-sato ext4
97 runqemu qemux86-64 wic-image-minimal wic
98 runqemu path/to/bzImage-qemux86.bin path/to/nfsrootdir/ serial
Andrew Geisslerd1e89492021-02-12 15:35:20 -060099 runqemu qemux86 iso/hddimg/wic.vmdk/wic.vhd/wic.vhdx/wic.qcow2/wic.vdi/ramfs/cpio.gz...
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600100 runqemu qemux86 qemuparams="-m 256"
101 runqemu qemux86 bootparams="psplash=false"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600102 runqemu path/to/<image>-<machine>.wic
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500103 runqemu path/to/<image>-<machine>.wic.vmdk
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600104 runqemu path/to/<image>-<machine>.wic.vhdx
105 runqemu path/to/<image>-<machine>.wic.vhd
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600106""")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500107
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600108def check_tun():
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500109 """Check /dev/net/tun"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600110 dev_tun = '/dev/net/tun'
111 if not os.path.exists(dev_tun):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500112 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 -0500113
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600114 if not os.access(dev_tun, os.W_OK):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500115 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 -0500116
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600117def get_first_file(cmds):
118 """Return first file found in wildcard cmds"""
119 for cmd in cmds:
120 all_files = glob.glob(cmd)
121 if all_files:
122 for f in all_files:
123 if not os.path.isdir(f):
124 return f
125 return ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500126
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600127class BaseConfig(object):
128 def __init__(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500129 # The self.d saved vars from self.set(), part of them are from qemuboot.conf
130 self.d = {'QB_KERNEL_ROOT': '/dev/vda'}
131
132 # Supported env vars, add it here if a var can be got from env,
133 # and don't use os.getenv in the code.
134 self.env_vars = ('MACHINE',
135 'ROOTFS',
136 'KERNEL',
Brad Bishopc68388fc2019-08-26 01:33:31 -0400137 'BIOS',
Brad Bishop316dfdd2018-06-25 12:45:53 -0400138 'DEVICE_TREE',
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500139 'DEPLOY_DIR_IMAGE',
140 'OE_TMPDIR',
141 'OECORE_NATIVE_SYSROOT',
Andrew Geissler82c905d2020-04-13 13:39:40 -0500142 'MULTICONFIG',
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500143 'SERIAL_CONSOLES',
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500144 )
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500145
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600146 self.qemu_opt = ''
147 self.qemu_opt_script = ''
Andrew Geissler99467da2019-02-25 18:54:23 -0600148 self.qemuparams = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600149 self.nfs_server = ''
150 self.rootfs = ''
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500151 # File name(s) of a OVMF firmware file or variable store,
152 # to be added with -drive if=pflash.
153 # Found in the same places as the rootfs, with or without one of
154 # these suffices: qcow2, bin.
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500155 self.ovmf_bios = []
Brad Bishop08902b02019-08-20 09:16:51 -0400156 # When enrolling default Secure Boot keys, the hypervisor
157 # must provide the Platform Key and the first Key Exchange Key
158 # certificate in the Type 11 SMBIOS table.
159 self.ovmf_secboot_pkkek1 = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600160 self.qemuboot = ''
161 self.qbconfload = False
162 self.kernel = ''
Brad Bishopc68388fc2019-08-26 01:33:31 -0400163 self.bios = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600164 self.kernel_cmdline = ''
165 self.kernel_cmdline_script = ''
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500166 self.bootparams = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600167 self.dtb = ''
168 self.fstype = ''
169 self.kvm_enabled = False
170 self.vhost_enabled = False
171 self.slirp_enabled = False
Andrew Geissler82c905d2020-04-13 13:39:40 -0500172 self.net_bridge = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600173 self.nfs_instance = 0
174 self.nfs_running = False
Brad Bishop19323692019-04-05 15:28:33 -0400175 self.serialconsole = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600176 self.serialstdio = False
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500177 self.nographic = False
178 self.sdl = False
179 self.gtk = False
180 self.gl = False
181 self.gl_es = False
182 self.egl_headless = False
183 self.novga = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600184 self.cleantap = False
185 self.saved_stty = ''
186 self.audio_enabled = False
187 self.tcpserial_portnum = ''
Brad Bishop08902b02019-08-20 09:16:51 -0400188 self.taplock = ''
189 self.taplock_descriptor = None
190 self.portlocks = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600191 self.bitbake_e = ''
192 self.snapshot = False
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600193 self.wictypes = ('wic', 'wic.vmdk', 'wic.qcow2', 'wic.vdi', "wic.vhd", "wic.vhdx")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500194 self.fstypes = ('ext2', 'ext3', 'ext4', 'jffs2', 'nfs', 'btrfs',
195 'cpio.gz', 'cpio', 'ramfs', 'tar.bz2', 'tar.gz')
Brad Bishop08902b02019-08-20 09:16:51 -0400196 self.vmtypes = ('hddimg', 'iso')
Brad Bishop15ae2502019-06-18 21:44:24 -0400197 self.fsinfo = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500198 self.network_device = "-device e1000,netdev=net0,mac=@MAC@"
Andrew Geissler82c905d2020-04-13 13:39:40 -0500199 self.cmdline_ip_slirp = "ip=dhcp"
200 self.cmdline_ip_tap = "ip=192.168.7.@CLIENT@::192.168.7.@GATEWAY@:255.255.255.0"
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500201 # Use different mac section for tap and slirp to avoid
202 # conflicts, e.g., when one is running with tap, the other is
203 # running with slirp.
204 # The last section is dynamic, which is for avoiding conflicts,
205 # when multiple qemus are running, e.g., when multiple tap or
206 # slirp qemus are running.
207 self.mac_tap = "52:54:00:12:34:"
208 self.mac_slirp = "52:54:00:12:35:"
Brad Bishop004d4992018-10-02 23:54:45 +0200209 # pid of the actual qemu process
210 self.qemupid = None
211 # avoid cleanup twice
212 self.cleaned = False
Andrew Geisslerc926e172021-05-07 16:11:35 -0500213 # Files to cleanup after run
214 self.cleanup_files = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500215
Brad Bishop08902b02019-08-20 09:16:51 -0400216 def acquire_taplock(self, error=True):
217 logger.debug("Acquiring lockfile %s..." % self.taplock)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600218 try:
Brad Bishop08902b02019-08-20 09:16:51 -0400219 self.taplock_descriptor = open(self.taplock, 'w')
220 fcntl.flock(self.taplock_descriptor, fcntl.LOCK_EX|fcntl.LOCK_NB)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600221 except Exception as e:
Brad Bishop08902b02019-08-20 09:16:51 -0400222 msg = "Acquiring lockfile %s failed: %s" % (self.taplock, e)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500223 if error:
224 logger.error(msg)
225 else:
226 logger.info(msg)
Brad Bishop08902b02019-08-20 09:16:51 -0400227 if self.taplock_descriptor:
228 self.taplock_descriptor.close()
229 self.taplock_descriptor = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600230 return False
231 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500232
Brad Bishop08902b02019-08-20 09:16:51 -0400233 def release_taplock(self):
234 if self.taplock_descriptor:
Brad Bishopf86d0552018-12-04 14:18:15 -0800235 logger.debug("Releasing lockfile for tap device '%s'" % self.tap)
Andrew Geissler5f350902021-07-23 13:09:54 -0400236 # We pass the fd to the qemu process and if we unlock here, it would unlock for
237 # that too. Therefore don't unlock, just close
238 # fcntl.flock(self.taplock_descriptor, fcntl.LOCK_UN)
Brad Bishop08902b02019-08-20 09:16:51 -0400239 self.taplock_descriptor.close()
Andrew Geissler5f350902021-07-23 13:09:54 -0400240 # Removing the file is a potential race, don't do that either
241 # os.remove(self.taplock)
Brad Bishop08902b02019-08-20 09:16:51 -0400242 self.taplock_descriptor = None
243
244 def check_free_port(self, host, port, lockdir):
245 """ Check whether the port is free or not """
246 import socket
247 from contextlib import closing
248
249 lockfile = os.path.join(lockdir, str(port) + '.lock')
250 if self.acquire_portlock(lockfile):
251 with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
252 if sock.connect_ex((host, port)) == 0:
253 # Port is open, so not free
254 self.release_portlock(lockfile)
255 return False
256 else:
257 # Port is not open, so free
258 return True
259 else:
260 return False
261
262 def acquire_portlock(self, lockfile):
263 logger.debug("Acquiring lockfile %s..." % lockfile)
264 try:
265 portlock_descriptor = open(lockfile, 'w')
266 self.portlocks.update({lockfile: portlock_descriptor})
267 fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_EX|fcntl.LOCK_NB)
268 except Exception as e:
269 msg = "Acquiring lockfile %s failed: %s" % (lockfile, e)
270 logger.info(msg)
271 if lockfile in self.portlocks.keys() and self.portlocks[lockfile]:
272 self.portlocks[lockfile].close()
273 del self.portlocks[lockfile]
274 return False
275 return True
276
277 def release_portlock(self, lockfile=None):
278 if lockfile != None:
Andrew Geissler5f350902021-07-23 13:09:54 -0400279 logger.debug("Releasing lockfile '%s'" % lockfile)
280 # We pass the fd to the qemu process and if we unlock here, it would unlock for
281 # that too. Therefore don't unlock, just close
282 # fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_UN)
283 self.portlocks[lockfile].close()
284 # Removing the file is a potential race, don't do that either
285 # os.remove(lockfile)
286 del self.portlocks[lockfile]
Brad Bishop08902b02019-08-20 09:16:51 -0400287 elif len(self.portlocks):
288 for lockfile, descriptor in self.portlocks.items():
289 logger.debug("Releasing lockfile '%s'" % lockfile)
Andrew Geissler5f350902021-07-23 13:09:54 -0400290 # We pass the fd to the qemu process and if we unlock here, it would unlock for
291 # that too. Therefore don't unlock, just close
292 # fcntl.flock(descriptor, fcntl.LOCK_UN)
Brad Bishop08902b02019-08-20 09:16:51 -0400293 descriptor.close()
Andrew Geissler5f350902021-07-23 13:09:54 -0400294 # Removing the file is a potential race, don't do that either
295 # os.remove(lockfile)
Brad Bishop08902b02019-08-20 09:16:51 -0400296 self.portlocks = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500297
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600298 def get(self, key):
299 if key in self.d:
300 return self.d.get(key)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500301 elif os.getenv(key):
302 return os.getenv(key)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600303 else:
304 return ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500305
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600306 def set(self, key, value):
307 self.d[key] = value
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500308
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600309 def is_deploy_dir_image(self, p):
310 if os.path.isdir(p):
311 if not re.search('.qemuboot.conf$', '\n'.join(os.listdir(p)), re.M):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500312 logger.debug("Can't find required *.qemuboot.conf in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600313 return False
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500314 if not any(map(lambda name: '-image-' in name, os.listdir(p))):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500315 logger.debug("Can't find *-image-* in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600316 return False
317 return True
318 else:
319 return False
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500320
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600321 def check_arg_fstype(self, fst):
322 """Check and set FSTYPE"""
Brad Bishop15ae2502019-06-18 21:44:24 -0400323 if fst not in self.fstypes + self.vmtypes + self.wictypes:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800324 logger.warning("Maybe unsupported FSTYPE: %s" % fst)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600325 if not self.fstype or self.fstype == fst:
326 if fst == 'ramfs':
327 fst = 'cpio.gz'
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500328 if fst in ('tar.bz2', 'tar.gz'):
329 fst = 'nfs'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600330 self.fstype = fst
331 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500332 raise RunQemuError("Conflicting: FSTYPE %s and %s" % (self.fstype, fst))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500333
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600334 def set_machine_deploy_dir(self, machine, deploy_dir_image):
335 """Set MACHINE and DEPLOY_DIR_IMAGE"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500336 logger.debug('MACHINE: %s' % machine)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600337 self.set("MACHINE", machine)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500338 logger.debug('DEPLOY_DIR_IMAGE: %s' % deploy_dir_image)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600339 self.set("DEPLOY_DIR_IMAGE", deploy_dir_image)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500340
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600341 def check_arg_nfs(self, p):
342 if os.path.isdir(p):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500343 self.rootfs = p
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600344 else:
345 m = re.match('(.*):(.*)', p)
346 self.nfs_server = m.group(1)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500347 self.rootfs = m.group(2)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600348 self.check_arg_fstype('nfs')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500349
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600350 def check_arg_path(self, p):
351 """
352 - Check whether it is <image>.qemuboot.conf or contains <image>.qemuboot.conf
353 - Check whether is a kernel file
354 - Check whether is a image file
355 - Check whether it is a nfs dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500356 - Check whether it is a OVMF flash file
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600357 """
358 if p.endswith('.qemuboot.conf'):
359 self.qemuboot = p
360 self.qbconfload = True
361 elif re.search('\.bin$', p) or re.search('bzImage', p) or \
362 re.search('zImage', p) or re.search('vmlinux', p) or \
363 re.search('fitImage', p) or re.search('uImage', p):
364 self.kernel = p
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500365 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 -0600366 self.rootfs = p
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500367 # Check filename against self.fstypes can hanlde <file>.cpio.gz,
368 # otherwise, its type would be "gz", which is incorrect.
369 fst = ""
370 for t in self.fstypes:
371 if p.endswith(t):
372 fst = t
373 break
374 if not fst:
375 m = re.search('.*\.(.*)$', self.rootfs)
376 if m:
377 fst = m.group(1)
378 if fst:
379 self.check_arg_fstype(fst)
380 qb = re.sub('\.' + fst + "$", '', self.rootfs)
381 qb = '%s%s' % (re.sub('\.rootfs$', '', qb), '.qemuboot.conf')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600382 if os.path.exists(qb):
383 self.qemuboot = qb
384 self.qbconfload = True
385 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800386 logger.warning("%s doesn't exist" % qb)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600387 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500388 raise RunQemuError("Can't find FSTYPE from: %s" % p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500389
390 elif os.path.isdir(p) or re.search(':', p) and re.search('/', p):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600391 if self.is_deploy_dir_image(p):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500392 logger.debug('DEPLOY_DIR_IMAGE: %s' % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600393 self.set("DEPLOY_DIR_IMAGE", p)
394 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500395 logger.debug("Assuming %s is an nfs rootfs" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600396 self.check_arg_nfs(p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500397 elif os.path.basename(p).startswith('ovmf'):
398 self.ovmf_bios.append(p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600399 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500400 raise RunQemuError("Unknown path arg %s" % p)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500401
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600402 def check_arg_machine(self, arg):
403 """Check whether it is a machine"""
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500404 if self.get('MACHINE') == arg:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600405 return
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500406 elif self.get('MACHINE') and self.get('MACHINE') != arg:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500407 raise RunQemuError("Maybe conflicted MACHINE: %s vs %s" % (self.get('MACHINE'), arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500408 elif re.search('/', arg):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500409 raise RunQemuError("Unknown arg: %s" % arg)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500410
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500411 logger.debug('Assuming MACHINE = %s' % arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500412
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600413 # if we're running under testimage, or similarly as a child
414 # of an existing bitbake invocation, we can't invoke bitbake
415 # to validate the MACHINE setting and must assume it's correct...
416 # FIXME: testimage.bbclass exports these two variables into env,
417 # are there other scenarios in which we need to support being
418 # invoked by bitbake?
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500419 deploy = self.get('DEPLOY_DIR_IMAGE')
420 bbchild = deploy and self.get('OE_TMPDIR')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600421 if bbchild:
422 self.set_machine_deploy_dir(arg, deploy)
423 return
424 # also check whether we're running under a sourced toolchain
425 # environment file
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500426 if self.get('OECORE_NATIVE_SYSROOT'):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600427 self.set("MACHINE", arg)
428 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500429
Andrew Geissler82c905d2020-04-13 13:39:40 -0500430 self.bitbake_e = self.run_bitbake_env(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600431 # bitbake -e doesn't report invalid MACHINE as an error, so
432 # let's check DEPLOY_DIR_IMAGE to make sure that it is a valid
433 # MACHINE.
434 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
435 if s:
436 deploy_dir_image = s.group(1)
437 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500438 raise RunQemuError("bitbake -e %s" % self.bitbake_e)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600439 if self.is_deploy_dir_image(deploy_dir_image):
440 self.set_machine_deploy_dir(arg, deploy_dir_image)
441 else:
442 logger.error("%s not a directory valid DEPLOY_DIR_IMAGE" % deploy_dir_image)
443 self.set("MACHINE", arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500444
Andrew Geisslerc182c622020-05-15 14:13:32 -0500445 def set_dri_path(self):
446 # As runqemu can be run within bitbake (when using testimage, for example),
447 # we need to ensure that we run host pkg-config, and that it does not
448 # get mis-directed to native build paths set by bitbake.
449 try:
450 del os.environ['PKG_CONFIG_PATH']
451 del os.environ['PKG_CONFIG_DIR']
452 del os.environ['PKG_CONFIG_LIBDIR']
453 del os.environ['PKG_CONFIG_SYSROOT_DIR']
454 except KeyError:
455 pass
456 try:
457 dripath = subprocess.check_output("PATH=/bin:/usr/bin:$PATH pkg-config --variable=dridriverdir dri", shell=True)
458 except subprocess.CalledProcessError as e:
459 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.")
460 os.environ['LIBGL_DRIVERS_PATH'] = dripath.decode('utf-8').strip()
461
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600462 def check_args(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500463 for debug in ("-d", "--debug"):
464 if debug in sys.argv:
465 logger.setLevel(logging.DEBUG)
466 sys.argv.remove(debug)
467
468 for quiet in ("-q", "--quiet"):
469 if quiet in sys.argv:
470 logger.setLevel(logging.ERROR)
471 sys.argv.remove(quiet)
472
Andrew Geisslerc182c622020-05-15 14:13:32 -0500473 if 'gl' not in sys.argv[1:] and 'gl-es' not in sys.argv[1:]:
474 os.environ['SDL_RENDER_DRIVER'] = 'software'
475
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600476 unknown_arg = ""
477 for arg in sys.argv[1:]:
Brad Bishop15ae2502019-06-18 21:44:24 -0400478 if arg in self.fstypes + self.vmtypes + self.wictypes:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600479 self.check_arg_fstype(arg)
480 elif arg == 'nographic':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500481 self.nographic = True
Brad Bishop19323692019-04-05 15:28:33 -0400482 elif arg == 'sdl':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500483 self.sdl = True
Brad Bishopa34c0302019-09-23 22:34:48 -0400484 elif arg == 'gtk':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500485 self.gtk = True
486 elif arg == 'gl':
487 self.gl = True
488 elif 'gl-es' in sys.argv[1:]:
489 self.gl_es = True
Brad Bishop19323692019-04-05 15:28:33 -0400490 elif arg == 'egl-headless':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500491 self.egl_headless = True
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600492 elif arg == 'novga':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500493 self.novga = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600494 elif arg == 'serial':
Brad Bishop19323692019-04-05 15:28:33 -0400495 self.serialconsole = True
496 elif arg == "serialstdio":
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600497 self.serialstdio = True
498 elif arg == 'audio':
499 logger.info("Enabling audio in qemu")
500 logger.info("Please install sound drivers in linux host")
501 self.audio_enabled = True
502 elif arg == 'kvm':
503 self.kvm_enabled = True
504 elif arg == 'kvm-vhost':
505 self.vhost_enabled = True
506 elif arg == 'slirp':
507 self.slirp_enabled = True
Andrew Geissler82c905d2020-04-13 13:39:40 -0500508 elif arg.startswith('bridge='):
509 self.net_bridge = '%s' % arg[len('bridge='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600510 elif arg == 'snapshot':
511 self.snapshot = True
512 elif arg == 'publicvnc':
513 self.qemu_opt_script += ' -vnc :0'
514 elif arg.startswith('tcpserial='):
Brad Bishop15ae2502019-06-18 21:44:24 -0400515 self.tcpserial_portnum = '%s' % arg[len('tcpserial='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600516 elif arg.startswith('qemuparams='):
Andrew Geissler99467da2019-02-25 18:54:23 -0600517 self.qemuparams = ' %s' % arg[len('qemuparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600518 elif arg.startswith('bootparams='):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500519 self.bootparams = arg[len('bootparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600520 elif os.path.exists(arg) or (re.search(':', arg) and re.search('/', arg)):
521 self.check_arg_path(os.path.abspath(arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500522 elif re.search(r'-image-|-image$', arg):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600523 # Lazy rootfs
524 self.rootfs = arg
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500525 elif arg.startswith('ovmf'):
526 self.ovmf_bios.append(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600527 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500528 # At last, assume it is the MACHINE
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600529 if (not unknown_arg) or unknown_arg == arg:
530 unknown_arg = arg
531 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500532 raise RunQemuError("Can't handle two unknown args: %s %s\n"
533 "Try 'runqemu help' on how to use it" % \
534 (unknown_arg, arg))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600535 # Check to make sure it is a valid machine
Brad Bishopa5c52ff2018-11-23 10:55:50 +1300536 if unknown_arg and self.get('MACHINE') != unknown_arg:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500537 if self.get('DEPLOY_DIR_IMAGE'):
538 machine = os.path.basename(self.get('DEPLOY_DIR_IMAGE'))
539 if unknown_arg == machine:
540 self.set("MACHINE", machine)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500541
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600542 self.check_arg_machine(unknown_arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500543
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500544 if not (self.get('DEPLOY_DIR_IMAGE') or self.qbconfload):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500545 self.load_bitbake_env()
546 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
547 if s:
548 self.set("DEPLOY_DIR_IMAGE", s.group(1))
549
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600550 def check_kvm(self):
551 """Check kvm and kvm-host"""
552 if not (self.kvm_enabled or self.vhost_enabled):
William A. Kennington IIIac69b482021-06-02 12:28:27 -0700553 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 -0600554 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500555
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600556 if not self.get('QB_CPU_KVM'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500557 raise RunQemuError("QB_CPU_KVM is NULL, this board doesn't support kvm")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500558
William A. Kennington IIIac69b482021-06-02 12:28:27 -0700559 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 -0600560 yocto_kvm_wiki = "https://wiki.yoctoproject.org/wiki/How_to_enable_KVM_for_Poky_qemu"
561 yocto_paravirt_kvm_wiki = "https://wiki.yoctoproject.org/wiki/Running_an_x86_Yocto_Linux_image_under_QEMU_KVM"
562 dev_kvm = '/dev/kvm'
563 dev_vhost = '/dev/vhost-net'
Brad Bishop15ae2502019-06-18 21:44:24 -0400564 if self.qemu_system.endswith(('i386', 'x86_64')):
565 with open('/proc/cpuinfo', 'r') as f:
566 kvm_cap = re.search('vmx|svm', "".join(f.readlines()))
567 if not kvm_cap:
568 logger.error("You are trying to enable KVM on a cpu without VT support.")
569 logger.error("Remove kvm from the command-line, or refer:")
570 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500571
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600572 if not os.path.exists(dev_kvm):
573 logger.error("Missing KVM device. Have you inserted kvm modules?")
574 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500575 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500576
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600577 if os.access(dev_kvm, os.W_OK|os.R_OK):
578 self.qemu_opt_script += ' -enable-kvm'
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500579 if self.get('MACHINE') == "qemux86":
580 # Workaround for broken APIC window on pre 4.15 host kernels which causes boot hangs
581 # See YOCTO #12301
582 # On 64 bit we use x2apic
583 self.kernel_cmdline_script += " clocksource=kvm-clock hpet=disable noapic nolapic"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600584 else:
585 logger.error("You have no read or write permission on /dev/kvm.")
586 logger.error("Please change the ownership of this file as described at:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500587 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500588
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600589 if self.vhost_enabled:
590 if not os.path.exists(dev_vhost):
591 logger.error("Missing virtio net device. Have you inserted vhost-net module?")
592 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500593 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500594
Andrew Geissler635e0e42020-08-21 15:58:33 -0500595 if not os.access(dev_vhost, os.W_OK|os.R_OK):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600596 logger.error("You have no read or write permission on /dev/vhost-net.")
597 logger.error("Please change the ownership of this file as described at:")
Andrew Geissler635e0e42020-08-21 15:58:33 -0500598 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500599
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600600 def check_fstype(self):
601 """Check and setup FSTYPE"""
602 if not self.fstype:
603 fstype = self.get('QB_DEFAULT_FSTYPE')
604 if fstype:
605 self.fstype = fstype
606 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500607 raise RunQemuError("FSTYPE is NULL!")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500608
Brad Bishop15ae2502019-06-18 21:44:24 -0400609 # parse QB_FSINFO into dict, e.g. { 'wic': ['no-kernel-in-fs', 'a-flag'], 'ext4': ['another-flag']}
610 wic_fs = False
611 qb_fsinfo = self.get('QB_FSINFO')
612 if qb_fsinfo:
613 qb_fsinfo = qb_fsinfo.split()
614 for fsinfo in qb_fsinfo:
615 try:
616 fstype, fsflag = fsinfo.split(':')
617
618 if fstype == 'wic':
619 if fsflag == 'no-kernel-in-fs':
620 wic_fs = True
621 elif fsflag == 'kernel-in-fs':
622 wic_fs = False
623 else:
624 logger.warn('Unknown flag "%s:%s" in QB_FSINFO', fstype, fsflag)
625 continue
626 else:
627 logger.warn('QB_FSINFO is not supported for image type "%s"', fstype)
628 continue
629
630 if fstype in self.fsinfo:
631 self.fsinfo[fstype].append(fsflag)
632 else:
633 self.fsinfo[fstype] = [fsflag]
634 except Exception:
635 logger.error('Invalid parameter "%s" in QB_FSINFO', fsinfo)
636
637 # treat wic images as vmimages (with kernel) or as fsimages (rootfs only)
638 if wic_fs:
639 self.fstypes = self.fstypes + self.wictypes
640 else:
641 self.vmtypes = self.vmtypes + self.wictypes
642
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600643 def check_rootfs(self):
644 """Check and set rootfs"""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500645
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500646 if self.fstype == "none":
647 return
648
649 if self.get('ROOTFS'):
650 if not self.rootfs:
651 self.rootfs = self.get('ROOTFS')
652 elif self.get('ROOTFS') != self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500653 raise RunQemuError("Maybe conflicted ROOTFS: %s vs %s" % (self.get('ROOTFS'), self.rootfs))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500654
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600655 if self.fstype == 'nfs':
656 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500657
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600658 if self.rootfs and not os.path.exists(self.rootfs):
659 # Lazy rootfs
660 self.rootfs = "%s/%s-%s.%s" % (self.get('DEPLOY_DIR_IMAGE'),
661 self.rootfs, self.get('MACHINE'),
662 self.fstype)
663 elif not self.rootfs:
664 cmd_name = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_NAME'), self.fstype)
665 cmd_link = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'), self.fstype)
666 cmds = (cmd_name, cmd_link)
667 self.rootfs = get_first_file(cmds)
668 if not self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500669 raise RunQemuError("Failed to find rootfs: %s or %s" % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500670
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600671 if not os.path.exists(self.rootfs):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500672 raise RunQemuError("Can't find rootfs: %s" % self.rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500673
Brad Bishop08902b02019-08-20 09:16:51 -0400674 def setup_pkkek1(self):
675 """
676 Extract from PEM certificate the Platform Key and first Key
677 Exchange Key certificate string. The hypervisor needs to provide
678 it in the Type 11 SMBIOS table
679 """
680 pemcert = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), 'OvmfPkKek1.pem')
681 try:
682 with open(pemcert, 'r') as pemfile:
683 key = pemfile.read().replace('\n', ''). \
684 replace('-----BEGIN CERTIFICATE-----', ''). \
685 replace('-----END CERTIFICATE-----', '')
686 self.ovmf_secboot_pkkek1 = key
687
688 except FileNotFoundError:
689 raise RunQemuError("Can't open PEM certificate %s " % pemcert)
690
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500691 def check_ovmf(self):
692 """Check and set full path for OVMF firmware and variable file(s)."""
693
694 for index, ovmf in enumerate(self.ovmf_bios):
695 if os.path.exists(ovmf):
696 continue
697 for suffix in ('qcow2', 'bin'):
698 path = '%s/%s.%s' % (self.get('DEPLOY_DIR_IMAGE'), ovmf, suffix)
699 if os.path.exists(path):
700 self.ovmf_bios[index] = path
Brad Bishop08902b02019-08-20 09:16:51 -0400701 if ovmf.endswith('secboot'):
702 self.setup_pkkek1()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500703 break
704 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500705 raise RunQemuError("Can't find OVMF firmware: %s" % ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500706
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600707 def check_kernel(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400708 """Check and set kernel"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600709 # The vm image doesn't need a kernel
710 if self.fstype in self.vmtypes:
711 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500712
Brad Bishop316dfdd2018-06-25 12:45:53 -0400713 # See if the user supplied a KERNEL option
714 if self.get('KERNEL'):
715 self.kernel = self.get('KERNEL')
716
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500717 # QB_DEFAULT_KERNEL is always a full file path
718 kernel_name = os.path.basename(self.get('QB_DEFAULT_KERNEL'))
719
720 # The user didn't want a kernel to be loaded
Brad Bishop316dfdd2018-06-25 12:45:53 -0400721 if kernel_name == "none" and not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500722 return
723
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600724 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
725 if not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500726 kernel_match_name = "%s/%s" % (deploy_dir_image, kernel_name)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600727 kernel_match_link = "%s/%s" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
728 kernel_startswith = "%s/%s*" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
729 cmds = (kernel_match_name, kernel_match_link, kernel_startswith)
730 self.kernel = get_first_file(cmds)
731 if not self.kernel:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500732 raise RunQemuError('KERNEL not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500733
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600734 if not os.path.exists(self.kernel):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500735 raise RunQemuError("KERNEL %s not found" % self.kernel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500736
Brad Bishop316dfdd2018-06-25 12:45:53 -0400737 def check_dtb(self):
738 """Check and set dtb"""
739 # Did the user specify a device tree?
740 if self.get('DEVICE_TREE'):
741 self.dtb = self.get('DEVICE_TREE')
742 if not os.path.exists(self.dtb):
743 raise RunQemuError('Specified DTB not found: %s' % self.dtb)
744 return
745
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600746 dtb = self.get('QB_DTB')
747 if dtb:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400748 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600749 cmd_match = "%s/%s" % (deploy_dir_image, dtb)
750 cmd_startswith = "%s/%s*" % (deploy_dir_image, dtb)
751 cmd_wild = "%s/*.dtb" % deploy_dir_image
752 cmds = (cmd_match, cmd_startswith, cmd_wild)
753 self.dtb = get_first_file(cmds)
754 if not os.path.exists(self.dtb):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500755 raise RunQemuError('DTB not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500756
Brad Bishopc68388fc2019-08-26 01:33:31 -0400757 def check_bios(self):
758 """Check and set bios"""
759
760 # See if the user supplied a BIOS option
761 if self.get('BIOS'):
762 self.bios = self.get('BIOS')
763
764 # QB_DEFAULT_BIOS is always a full file path
765 bios_name = os.path.basename(self.get('QB_DEFAULT_BIOS'))
766
767 # The user didn't want a bios to be loaded
768 if (bios_name == "" or bios_name == "none") and not self.bios:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600769 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500770
Brad Bishopc68388fc2019-08-26 01:33:31 -0400771 if not self.bios:
772 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
773 self.bios = "%s/%s" % (deploy_dir_image, bios_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500774
Brad Bishopc68388fc2019-08-26 01:33:31 -0400775 if not self.bios:
776 raise RunQemuError('BIOS not found: %s' % bios_match_name)
777
778 if not os.path.exists(self.bios):
Patrick Williams213cb262021-08-07 19:21:33 -0500779 raise RunQemuError("BIOS %s not found" % self.bios)
Brad Bishopc68388fc2019-08-26 01:33:31 -0400780
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500781
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600782 def check_mem(self):
Andrew Geissler99467da2019-02-25 18:54:23 -0600783 """
784 Both qemu and kernel needs memory settings, so check QB_MEM and set it
785 for both.
786 """
787 s = re.search('-m +([0-9]+)', self.qemuparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600788 if s:
789 self.set('QB_MEM', '-m %s' % s.group(1))
790 elif not self.get('QB_MEM'):
Brad Bishop79641f22019-09-10 07:20:22 -0400791 logger.info('QB_MEM is not set, use 256M by default')
792 self.set('QB_MEM', '-m 256')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500793
Andrew Geissler99467da2019-02-25 18:54:23 -0600794 # Check and remove M or m suffix
795 qb_mem = self.get('QB_MEM')
796 if qb_mem.endswith('M') or qb_mem.endswith('m'):
797 qb_mem = qb_mem[:-1]
798
799 # Add -m prefix it not present
800 if not qb_mem.startswith('-m'):
801 qb_mem = '-m %s' % qb_mem
802
803 self.set('QB_MEM', qb_mem)
804
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800805 mach = self.get('MACHINE')
806 if not mach.startswith('qemumips'):
807 self.kernel_cmdline_script += ' mem=%s' % self.get('QB_MEM').replace('-m','').strip() + 'M'
808
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600809 self.qemu_opt_script += ' %s' % self.get('QB_MEM')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500810
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600811 def check_tcpserial(self):
812 if self.tcpserial_portnum:
Brad Bishop15ae2502019-06-18 21:44:24 -0400813 ports = self.tcpserial_portnum.split(':')
814 port = ports[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600815 if self.get('QB_TCPSERIAL_OPT'):
Brad Bishop15ae2502019-06-18 21:44:24 -0400816 self.qemu_opt_script += ' ' + self.get('QB_TCPSERIAL_OPT').replace('@PORT@', port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600817 else:
Brad Bishop15ae2502019-06-18 21:44:24 -0400818 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
819
820 if len(ports) > 1:
821 for port in ports[1:]:
822 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500823
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600824 def check_and_set(self):
825 """Check configs sanity and set when needed"""
826 self.validate_paths()
Andrew Geissler82c905d2020-04-13 13:39:40 -0500827 if not self.slirp_enabled and not self.net_bridge:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500828 check_tun()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600829 # Check audio
830 if self.audio_enabled:
831 if not self.get('QB_AUDIO_DRV'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500832 raise RunQemuError("QB_AUDIO_DRV is NULL, this board doesn't support audio")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600833 if not self.get('QB_AUDIO_OPT'):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800834 logger.warning('QB_AUDIO_OPT is NULL, you may need define it to make audio work')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600835 else:
836 self.qemu_opt_script += ' %s' % self.get('QB_AUDIO_OPT')
837 os.putenv('QEMU_AUDIO_DRV', self.get('QB_AUDIO_DRV'))
838 else:
839 os.putenv('QEMU_AUDIO_DRV', 'none')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500840
Brad Bishop15ae2502019-06-18 21:44:24 -0400841 self.check_qemu_system()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600842 self.check_kvm()
843 self.check_fstype()
844 self.check_rootfs()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500845 self.check_ovmf()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600846 self.check_kernel()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400847 self.check_dtb()
Brad Bishopc68388fc2019-08-26 01:33:31 -0400848 self.check_bios()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600849 self.check_mem()
850 self.check_tcpserial()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500851
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600852 def read_qemuboot(self):
853 if not self.qemuboot:
854 if self.get('DEPLOY_DIR_IMAGE'):
855 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600856 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800857 logger.warning("Can't find qemuboot conf file, DEPLOY_DIR_IMAGE is NULL!")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600858 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500859
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600860 if self.rootfs and not os.path.exists(self.rootfs):
861 # Lazy rootfs
862 machine = self.get('MACHINE')
863 if not machine:
864 machine = os.path.basename(deploy_dir_image)
865 self.qemuboot = "%s/%s-%s.qemuboot.conf" % (deploy_dir_image,
866 self.rootfs, machine)
867 else:
868 cmd = 'ls -t %s/*.qemuboot.conf' % deploy_dir_image
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500869 logger.debug('Running %s...' % cmd)
870 try:
871 qbs = subprocess.check_output(cmd, shell=True).decode('utf-8')
872 except subprocess.CalledProcessError as err:
873 raise RunQemuError(err)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600874 if qbs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500875 for qb in qbs.split():
876 # Don't use initramfs when other choices unless fstype is ramfs
877 if '-initramfs-' in os.path.basename(qb) and self.fstype != 'cpio.gz':
878 continue
879 self.qemuboot = qb
880 break
881 if not self.qemuboot:
882 # Use the first one when no choice
883 self.qemuboot = qbs.split()[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600884 self.qbconfload = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500885
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600886 if not self.qemuboot:
887 # If we haven't found a .qemuboot.conf at this point it probably
888 # doesn't exist, continue without
889 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500890
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600891 if not os.path.exists(self.qemuboot):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500892 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 -0500893
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500894 logger.debug('CONFFILE: %s' % self.qemuboot)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500895
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600896 cf = configparser.ConfigParser()
897 cf.read(self.qemuboot)
898 for k, v in cf.items('config_bsp'):
899 k_upper = k.upper()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500900 if v.startswith("../"):
901 v = os.path.abspath(os.path.dirname(self.qemuboot) + "/" + v)
902 elif v == ".":
903 v = os.path.dirname(self.qemuboot)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600904 self.set(k_upper, v)
905
906 def validate_paths(self):
907 """Ensure all relevant path variables are set"""
908 # When we're started with a *.qemuboot.conf arg assume that image
909 # artefacts are relative to that file, rather than in whatever
910 # directory DEPLOY_DIR_IMAGE in the conf file points to.
911 if self.qbconfload:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500912 imgdir = os.path.realpath(os.path.dirname(self.qemuboot))
913 if imgdir != os.path.realpath(self.get('DEPLOY_DIR_IMAGE')):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600914 logger.info('Setting DEPLOY_DIR_IMAGE to folder containing %s (%s)' % (self.qemuboot, imgdir))
915 self.set('DEPLOY_DIR_IMAGE', imgdir)
916
917 # If the STAGING_*_NATIVE directories from the config file don't exist
918 # and we're in a sourced OE build directory try to extract the paths
919 # from `bitbake -e`
920 havenative = os.path.exists(self.get('STAGING_DIR_NATIVE')) and \
921 os.path.exists(self.get('STAGING_BINDIR_NATIVE'))
922
923 if not havenative:
924 if not self.bitbake_e:
925 self.load_bitbake_env()
926
927 if self.bitbake_e:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500928 native_vars = ['STAGING_DIR_NATIVE']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600929 for nv in native_vars:
930 s = re.search('^%s="(.*)"' % nv, self.bitbake_e, re.M)
931 if s and s.group(1) != self.get(nv):
932 logger.info('Overriding conf file setting of %s to %s from Bitbake environment' % (nv, s.group(1)))
933 self.set(nv, s.group(1))
934 else:
935 # when we're invoked from a running bitbake instance we won't
936 # be able to call `bitbake -e`, then try:
937 # - get OE_TMPDIR from environment and guess paths based on it
938 # - get OECORE_NATIVE_SYSROOT from environment (for sdk)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500939 tmpdir = self.get('OE_TMPDIR')
940 oecore_native_sysroot = self.get('OECORE_NATIVE_SYSROOT')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600941 if tmpdir:
942 logger.info('Setting STAGING_DIR_NATIVE and STAGING_BINDIR_NATIVE relative to OE_TMPDIR (%s)' % tmpdir)
943 hostos, _, _, _, machine = os.uname()
944 buildsys = '%s-%s' % (machine, hostos.lower())
945 staging_dir_native = '%s/sysroots/%s' % (tmpdir, buildsys)
946 self.set('STAGING_DIR_NATIVE', staging_dir_native)
947 elif oecore_native_sysroot:
948 logger.info('Setting STAGING_DIR_NATIVE to OECORE_NATIVE_SYSROOT (%s)' % oecore_native_sysroot)
949 self.set('STAGING_DIR_NATIVE', oecore_native_sysroot)
950 if self.get('STAGING_DIR_NATIVE'):
951 # we have to assume that STAGING_BINDIR_NATIVE is at usr/bin
952 staging_bindir_native = '%s/usr/bin' % self.get('STAGING_DIR_NATIVE')
953 logger.info('Setting STAGING_BINDIR_NATIVE to %s' % staging_bindir_native)
954 self.set('STAGING_BINDIR_NATIVE', '%s/usr/bin' % self.get('STAGING_DIR_NATIVE'))
955
956 def print_config(self):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500957 logoutput = ['Continuing with the following parameters:']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600958 if not self.fstype in self.vmtypes:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500959 logoutput.append('KERNEL: [%s]' % self.kernel)
Brad Bishopc68388fc2019-08-26 01:33:31 -0400960 if self.bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500961 logoutput.append('BIOS: [%s]' % self.bios)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600962 if self.dtb:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500963 logoutput.append('DTB: [%s]' % self.dtb)
964 logoutput.append('MACHINE: [%s]' % self.get('MACHINE'))
Brad Bishop15ae2502019-06-18 21:44:24 -0400965 try:
966 fstype_flags = ' (' + ', '.join(self.fsinfo[self.fstype]) + ')'
967 except KeyError:
968 fstype_flags = ''
Andrew Geissler82c905d2020-04-13 13:39:40 -0500969 logoutput.append('FSTYPE: [%s%s]' % (self.fstype, fstype_flags))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600970 if self.fstype == 'nfs':
Andrew Geissler82c905d2020-04-13 13:39:40 -0500971 logoutput.append('NFS_DIR: [%s]' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600972 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500973 logoutput.append('ROOTFS: [%s]' % self.rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500974 if self.ovmf_bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500975 logoutput.append('OVMF: %s' % self.ovmf_bios)
Brad Bishop08902b02019-08-20 09:16:51 -0400976 if (self.ovmf_secboot_pkkek1):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500977 logoutput.append('SECBOOT PKKEK1: [%s...]' % self.ovmf_secboot_pkkek1[0:100])
978 logoutput.append('CONFFILE: [%s]' % self.qemuboot)
979 logoutput.append('')
980 logger.info('\n'.join(logoutput))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600981
982 def setup_nfs(self):
983 if not self.nfs_server:
984 if self.slirp_enabled:
985 self.nfs_server = '10.0.2.2'
986 else:
987 self.nfs_server = '192.168.7.1'
988
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500989 # Figure out a new nfs_instance to allow multiple qemus running.
Brad Bishop977dc1a2019-02-06 16:01:43 -0500990 ps = subprocess.check_output(("ps", "auxww")).decode('utf-8')
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500991 pattern = '/bin/unfsd .* -i .*\.pid -e .*/exports([0-9]+) '
992 all_instances = re.findall(pattern, ps, re.M)
993 if all_instances:
994 all_instances.sort(key=int)
995 self.nfs_instance = int(all_instances.pop()) + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600996
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500997 nfsd_port = 3049 + 2 * self.nfs_instance
998 mountd_port = 3048 + 2 * self.nfs_instance
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600999
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001000 # Export vars for runqemu-export-rootfs
1001 export_dict = {
1002 'NFS_INSTANCE': self.nfs_instance,
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001003 'NFSD_PORT': nfsd_port,
1004 'MOUNTD_PORT': mountd_port,
1005 }
1006 for k, v in export_dict.items():
1007 # Use '%s' since they are integers
1008 os.putenv(k, '%s' % v)
1009
Andrew Geissler82c905d2020-04-13 13:39:40 -05001010 self.unfs_opts="nfsvers=3,port=%s,tcp,mountport=%s" % (nfsd_port, mountd_port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001011
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001012 # Extract .tar.bz2 or .tar.bz if no nfs dir
1013 if not (self.rootfs and os.path.isdir(self.rootfs)):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001014 src_prefix = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'))
1015 dest = "%s-nfsroot" % src_prefix
1016 if os.path.exists('%s.pseudo_state' % dest):
1017 logger.info('Use %s as NFS_DIR' % dest)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001018 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001019 else:
1020 src = ""
1021 src1 = '%s.tar.bz2' % src_prefix
1022 src2 = '%s.tar.gz' % src_prefix
1023 if os.path.exists(src1):
1024 src = src1
1025 elif os.path.exists(src2):
1026 src = src2
1027 if not src:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001028 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 -06001029 logger.info('NFS_DIR not found, extracting %s to %s' % (src, dest))
Brad Bishop977dc1a2019-02-06 16:01:43 -05001030 cmd = ('runqemu-extract-sdk', src, dest)
1031 logger.info('Running %s...' % str(cmd))
1032 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001033 raise RunQemuError('Failed to run %s' % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001034 self.rootfs = dest
Andrew Geisslerc926e172021-05-07 16:11:35 -05001035 self.cleanup_files.append(self.rootfs)
1036 self.cleanup_files.append('%s.pseudo_state' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001037
1038 # Start the userspace NFS server
Brad Bishop977dc1a2019-02-06 16:01:43 -05001039 cmd = ('runqemu-export-rootfs', 'start', self.rootfs)
1040 logger.info('Running %s...' % str(cmd))
1041 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001042 raise RunQemuError('Failed to run %s' % cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001043
1044 self.nfs_running = True
1045
Andrew Geissler82c905d2020-04-13 13:39:40 -05001046 def setup_net_bridge(self):
1047 self.set('NETWORK_CMD', '-netdev bridge,br=%s,id=net0,helper=%s -device virtio-net-pci,netdev=net0 ' % (
1048 self.net_bridge, os.path.join(self.bindir_native, 'qemu-oe-bridge-helper')))
1049
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001050 def setup_slirp(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001051 """Setup user networking"""
1052
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001053 if self.fstype == 'nfs':
1054 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001055 netconf = " " + self.cmdline_ip_slirp
1056 logger.info("Network configuration:%s", netconf)
1057 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001058 # Port mapping
1059 hostfwd = ",hostfwd=tcp::2222-:22,hostfwd=tcp::2323-:23"
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001060 qb_slirp_opt_default = "-netdev user,id=net0%s,tftp=%s" % (hostfwd, self.get('DEPLOY_DIR_IMAGE'))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001061 qb_slirp_opt = self.get('QB_SLIRP_OPT') or qb_slirp_opt_default
1062 # Figure out the port
1063 ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt)
1064 ports = [int(i) for i in ports]
1065 mac = 2
Brad Bishop08902b02019-08-20 09:16:51 -04001066
1067 lockdir = "/tmp/qemu-port-locks"
1068 if not os.path.exists(lockdir):
1069 # There might be a race issue when multi runqemu processess are
1070 # running at the same time.
1071 try:
1072 os.mkdir(lockdir)
1073 os.chmod(lockdir, 0o777)
1074 except FileExistsError:
1075 pass
1076
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001077 # Find a free port to avoid conflicts
1078 for p in ports[:]:
1079 p_new = p
Brad Bishop08902b02019-08-20 09:16:51 -04001080 while not self.check_free_port('localhost', p_new, lockdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001081 p_new += 1
1082 mac += 1
1083 while p_new in ports:
1084 p_new += 1
1085 mac += 1
1086 if p != p_new:
1087 ports.append(p_new)
1088 qb_slirp_opt = re.sub(':%s-' % p, ':%s-' % p_new, qb_slirp_opt)
1089 logger.info("Port forward changed: %s -> %s" % (p, p_new))
1090 mac = "%s%02x" % (self.mac_slirp, mac)
1091 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qb_slirp_opt))
1092 # Print out port foward
1093 hostfwd = re.findall('(hostfwd=[^,]*)', qb_slirp_opt)
1094 if hostfwd:
1095 logger.info('Port forward: %s' % ' '.join(hostfwd))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001096
1097 def setup_tap(self):
1098 """Setup tap"""
1099
1100 # This file is created when runqemu-gen-tapdevs creates a bank of tap
1101 # devices, indicating that the user should not bring up new ones using
1102 # sudo.
1103 nosudo_flag = '/etc/runqemu-nosudo'
1104 self.qemuifup = shutil.which('runqemu-ifup')
1105 self.qemuifdown = shutil.which('runqemu-ifdown')
1106 ip = shutil.which('ip')
1107 lockdir = "/tmp/qemu-tap-locks"
1108
1109 if not (self.qemuifup and self.qemuifdown and ip):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001110 logger.error("runqemu-ifup: %s" % self.qemuifup)
1111 logger.error("runqemu-ifdown: %s" % self.qemuifdown)
1112 logger.error("ip: %s" % ip)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001113 raise OEPathError("runqemu-ifup, runqemu-ifdown or ip not found")
1114
1115 if not os.path.exists(lockdir):
1116 # There might be a race issue when multi runqemu processess are
1117 # running at the same time.
1118 try:
1119 os.mkdir(lockdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001120 os.chmod(lockdir, 0o777)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001121 except FileExistsError:
1122 pass
1123
Brad Bishop977dc1a2019-02-06 16:01:43 -05001124 cmd = (ip, 'link')
1125 logger.debug('Running %s...' % str(cmd))
1126 ip_link = subprocess.check_output(cmd).decode('utf-8')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001127 # Matches line like: 6: tap0: <foo>
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001128 possibles = re.findall('^[0-9]+: +(tap[0-9]+): <.*', ip_link, re.M)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001129 tap = ""
1130 for p in possibles:
1131 lockfile = os.path.join(lockdir, p)
1132 if os.path.exists('%s.skip' % lockfile):
1133 logger.info('Found %s.skip, skipping %s' % (lockfile, p))
1134 continue
Brad Bishop08902b02019-08-20 09:16:51 -04001135 self.taplock = lockfile + '.lock'
1136 if self.acquire_taplock(error=False):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001137 tap = p
1138 logger.info("Using preconfigured tap device %s" % tap)
1139 logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap))
1140 break
1141
1142 if not tap:
1143 if os.path.exists(nosudo_flag):
1144 logger.error("Error: There are no available tap devices to use for networking,")
1145 logger.error("and I see %s exists, so I am not going to try creating" % nosudo_flag)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001146 raise RunQemuError("a new one with sudo.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001147
1148 gid = os.getgid()
1149 uid = os.getuid()
1150 logger.info("Setting up tap interface under sudo")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001151 cmd = ('sudo', self.qemuifup, str(uid), str(gid), self.bindir_native)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001152 try:
1153 tap = subprocess.check_output(cmd).decode('utf-8').strip()
1154 except subprocess.CalledProcessError as e:
1155 logger.error('Setting up tap device failed:\n%s\nRun runqemu-gen-tapdevs to manually create one.' % str(e))
1156 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001157 lockfile = os.path.join(lockdir, tap)
Brad Bishop08902b02019-08-20 09:16:51 -04001158 self.taplock = lockfile + '.lock'
1159 self.acquire_taplock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001160 self.cleantap = True
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001161 logger.debug('Created tap: %s' % tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001162
1163 if not tap:
1164 logger.error("Failed to setup tap device. Run runqemu-gen-tapdevs to manually create.")
Andrew Geissler82c905d2020-04-13 13:39:40 -05001165 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001166 self.tap = tap
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001167 tapnum = int(tap[3:])
1168 gateway = tapnum * 2 + 1
1169 client = gateway + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001170 if self.fstype == 'nfs':
1171 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001172 netconf = " " + self.cmdline_ip_tap
1173 netconf = netconf.replace('@CLIENT@', str(client))
1174 netconf = netconf.replace('@GATEWAY@', str(gateway))
1175 logger.info("Network configuration:%s", netconf)
1176 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001177 mac = "%s%02x" % (self.mac_tap, client)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001178 qb_tap_opt = self.get('QB_TAP_OPT')
1179 if qb_tap_opt:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001180 qemu_tap_opt = qb_tap_opt.replace('@TAP@', tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001181 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001182 qemu_tap_opt = "-netdev tap,id=net0,ifname=%s,script=no,downscript=no" % (self.tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001183
1184 if self.vhost_enabled:
1185 qemu_tap_opt += ',vhost=on'
1186
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001187 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qemu_tap_opt))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001188
1189 def setup_network(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001190 if self.get('QB_NET') == 'none':
1191 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001192 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001193 self.saved_stty = subprocess.check_output(("stty", "-g")).decode('utf-8').strip()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001194 self.network_device = self.get('QB_NETWORK_DEVICE') or self.network_device
Andrew Geissler82c905d2020-04-13 13:39:40 -05001195 if self.net_bridge:
1196 self.setup_net_bridge()
1197 elif self.slirp_enabled:
1198 self.cmdline_ip_slirp = self.get('QB_CMDLINE_IP_SLIRP') or self.cmdline_ip_slirp
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001199 self.setup_slirp()
1200 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001201 self.cmdline_ip_tap = self.get('QB_CMDLINE_IP_TAP') or self.cmdline_ip_tap
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001202 self.setup_tap()
1203
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001204 def setup_rootfs(self):
1205 if self.get('QB_ROOTFS') == 'none':
1206 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001207 if 'wic.' in self.fstype:
1208 self.fstype = self.fstype[4:]
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001209 rootfs_format = self.fstype if self.fstype in ('vmdk', 'vhd', 'vhdx', 'qcow2', 'vdi') else 'raw'
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001210
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001211 tmpfsdir = os.environ.get("RUNQEMU_TMPFS_DIR", None)
1212 if self.snapshot and tmpfsdir:
1213 newrootfs = os.path.join(tmpfsdir, os.path.basename(self.rootfs)) + "." + str(os.getpid())
Andrew Geissler09036742021-06-25 14:25:14 -05001214 logger.info("Copying rootfs to %s" % newrootfs)
1215 copy_start = time.time()
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001216 shutil.copyfile(self.rootfs, newrootfs)
Andrew Geissler09036742021-06-25 14:25:14 -05001217 logger.info("Copy done in %s seconds" % (time.time() - copy_start))
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001218 self.rootfs = newrootfs
1219 # Don't need a second copy now!
1220 self.snapshot = False
Andrew Geisslerc926e172021-05-07 16:11:35 -05001221 self.cleanup_files.append(newrootfs)
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001222
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001223 qb_rootfs_opt = self.get('QB_ROOTFS_OPT')
1224 if qb_rootfs_opt:
1225 self.rootfs_options = qb_rootfs_opt.replace('@ROOTFS@', self.rootfs)
1226 else:
1227 self.rootfs_options = '-drive file=%s,if=virtio,format=%s' % (self.rootfs, rootfs_format)
1228
Andrew Geissler82c905d2020-04-13 13:39:40 -05001229 qb_rootfs_extra_opt = self.get("QB_ROOTFS_EXTRA_OPT")
1230 if qb_rootfs_extra_opt and not qb_rootfs_extra_opt.startswith(","):
1231 qb_rootfs_extra_opt = "," + qb_rootfs_extra_opt
1232
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001233 if self.fstype in ('cpio.gz', 'cpio'):
1234 self.kernel_cmdline = 'root=/dev/ram0 rw debugshell'
1235 self.rootfs_options = '-initrd %s' % self.rootfs
1236 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001237 vm_drive = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001238 if self.fstype in self.vmtypes:
1239 if self.fstype == 'iso':
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001240 vm_drive = '-drive file=%s,if=virtio,media=cdrom' % self.rootfs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001241 elif self.get('QB_DRIVE_TYPE'):
1242 drive_type = self.get('QB_DRIVE_TYPE')
1243 if drive_type.startswith("/dev/sd"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001244 logger.info('Using scsi drive')
Andrew Geissler82c905d2020-04-13 13:39:40 -05001245 vm_drive = '-drive if=none,id=hd,file=%s,format=%s -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd%s' \
1246 % (self.rootfs, rootfs_format, qb_rootfs_extra_opt)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001247 elif drive_type.startswith("/dev/hd"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001248 logger.info('Using ide drive')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001249 vm_drive = "-drive file=%s,format=%s" % (self.rootfs, rootfs_format)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001250 elif drive_type.startswith("/dev/vdb"):
1251 logger.info('Using block virtio drive');
1252 vm_drive = '-drive id=disk0,file=%s,if=none,format=%s -device virtio-blk-device,drive=disk0%s' \
1253 % (self.rootfs, rootfs_format,qb_rootfs_extra_opt)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001254 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001255 # virtio might have been selected explicitly (just use it), or
1256 # is used as fallback (then warn about that).
1257 if not drive_type.startswith("/dev/vd"):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001258 logger.warning("Unknown QB_DRIVE_TYPE: %s" % drive_type)
1259 logger.warning("Failed to figure out drive type, consider define or fix QB_DRIVE_TYPE")
1260 logger.warning('Trying to use virtio block drive')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001261 vm_drive = '-drive if=virtio,file=%s,format=%s' % (self.rootfs, rootfs_format)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001262
1263 # All branches above set vm_drive.
Andrew Geissler475cb722020-07-10 16:00:51 -05001264 self.rootfs_options = vm_drive
1265 if not self.fstype in self.vmtypes:
1266 self.rootfs_options += ' -no-reboot'
Brad Bishopf3f93bb2019-10-16 14:33:32 -04001267 self.kernel_cmdline = 'root=%s rw' % (self.get('QB_KERNEL_ROOT'))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001268
1269 if self.fstype == 'nfs':
1270 self.rootfs_options = ''
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001271 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 -04001272 self.kernel_cmdline = 'root=%s rw' % k_root
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001273
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001274 if self.fstype == 'none':
1275 self.rootfs_options = ''
1276
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001277 self.set('ROOTFS_OPTIONS', self.rootfs_options)
1278
1279 def guess_qb_system(self):
1280 """attempt to determine the appropriate qemu-system binary"""
1281 mach = self.get('MACHINE')
1282 if not mach:
1283 search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*'
1284 if self.rootfs:
1285 match = re.match(search, self.rootfs)
1286 if match:
1287 mach = match.group(1)
1288 elif self.kernel:
1289 match = re.match(search, self.kernel)
1290 if match:
1291 mach = match.group(1)
1292
1293 if not mach:
1294 return None
1295
1296 if mach == 'qemuarm':
1297 qbsys = 'arm'
1298 elif mach == 'qemuarm64':
1299 qbsys = 'aarch64'
1300 elif mach == 'qemux86':
1301 qbsys = 'i386'
1302 elif mach == 'qemux86-64':
1303 qbsys = 'x86_64'
1304 elif mach == 'qemuppc':
1305 qbsys = 'ppc'
1306 elif mach == 'qemumips':
1307 qbsys = 'mips'
1308 elif mach == 'qemumips64':
1309 qbsys = 'mips64'
1310 elif mach == 'qemumipsel':
1311 qbsys = 'mipsel'
1312 elif mach == 'qemumips64el':
1313 qbsys = 'mips64el'
Brad Bishop316dfdd2018-06-25 12:45:53 -04001314 elif mach == 'qemuriscv64':
1315 qbsys = 'riscv64'
1316 elif mach == 'qemuriscv32':
1317 qbsys = 'riscv32'
Brad Bishop004d4992018-10-02 23:54:45 +02001318 else:
1319 logger.error("Unable to determine QEMU PC System emulator for %s machine." % mach)
1320 logger.error("As %s is not among valid QEMU machines such as," % mach)
1321 logger.error("qemux86-64, qemux86, qemuarm64, qemuarm, qemumips64, qemumips64el, qemumipsel, qemumips, qemuppc")
1322 raise RunQemuError("Set qb_system_name with suitable QEMU PC System emulator in .*qemuboot.conf.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001323
1324 return 'qemu-system-%s' % qbsys
1325
Brad Bishop15ae2502019-06-18 21:44:24 -04001326 def check_qemu_system(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001327 qemu_system = self.get('QB_SYSTEM_NAME')
1328 if not qemu_system:
1329 qemu_system = self.guess_qb_system()
1330 if not qemu_system:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001331 raise RunQemuError("Failed to boot, QB_SYSTEM_NAME is NULL!")
Brad Bishop15ae2502019-06-18 21:44:24 -04001332 self.qemu_system = qemu_system
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001333
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001334 def setup_vga(self):
1335 if self.nographic == True:
1336 if self.sdl == True:
1337 raise RunQemuError('Option nographic makes no sense alongside the sdl option.')
1338 if self.gtk == True:
1339 raise RunQemuError('Option nographic makes no sense alongside the gtk option.')
1340 self.qemu_opt += ' -nographic'
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001341
1342 if self.novga == True:
1343 self.qemu_opt += ' -vga none'
1344 return
1345
1346 if (self.gl_es == True or self.gl == True) and (self.sdl == False and self.gtk == False):
1347 raise RunQemuError('Option gl/gl-es needs gtk or sdl option.')
1348
1349 if self.sdl == True or self.gtk == True or self.egl_headless == True:
1350 self.set_dri_path()
1351 self.qemu_opt += ' -vga virtio -display '
1352 if self.egl_headless == True:
1353 self.qemu_opt += 'egl-headless,'
1354 else:
1355 if self.sdl == True:
1356 self.qemu_opt += 'sdl,'
1357 elif self.gtk == True:
1358 self.qemu_opt += 'gtk,'
1359
1360 if self.gl == True:
1361 self.qemu_opt += 'gl=on,'
1362 elif self.gl_es == True:
1363 self.qemu_opt += 'gl=es,'
1364 self.qemu_opt += 'show-cursor=on'
1365
1366 self.qemu_opt += ' %s' %self.get('QB_GRAPHICS')
1367
1368 def setup_serial(self):
1369 # Setup correct kernel command line for serial
1370 if self.serialstdio == True or self.serialconsole == True or self.nographic == True or self.tcpserial_portnum:
1371 for entry in self.get('SERIAL_CONSOLES').split(' '):
1372 self.kernel_cmdline_script += ' console=%s' %entry.split(';')[1]
1373
1374 if self.serialstdio == True or self.nographic == True:
1375 self.qemu_opt += " -serial mon:stdio"
1376 else:
1377 self.qemu_opt += " -serial mon:vc"
1378 if self.serialconsole:
1379 if sys.stdin.isatty():
1380 subprocess.check_call(("stty", "intr", "^]"))
1381 logger.info("Interrupt character is '^]'")
1382
1383 self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT")
1384
1385 # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES).
1386 # If no serial or serialtcp options were specified, only ttyS0 is created
1387 # and sysvinit shows an error trying to enable ttyS1:
1388 # INIT: Id "S1" respawning too fast: disabled for 5 minutes
1389 serial_num = len(re.findall("-serial", self.qemu_opt))
1390 if serial_num < 2:
1391 self.qemu_opt += " -serial null"
1392
Brad Bishop15ae2502019-06-18 21:44:24 -04001393 def setup_final(self):
1394 qemu_bin = os.path.join(self.bindir_native, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001395
1396 # It is possible to have qemu-native in ASSUME_PROVIDED, and it won't
1397 # find QEMU in sysroot, it needs to use host's qemu.
1398 if not os.path.exists(qemu_bin):
1399 logger.info("QEMU binary not found in %s, trying host's QEMU" % qemu_bin)
1400 for path in (os.environ['PATH'] or '').split(':'):
Brad Bishop15ae2502019-06-18 21:44:24 -04001401 qemu_bin_tmp = os.path.join(path, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001402 logger.info("Trying: %s" % qemu_bin_tmp)
1403 if os.path.exists(qemu_bin_tmp):
1404 qemu_bin = qemu_bin_tmp
1405 if not os.path.isabs(qemu_bin):
1406 qemu_bin = os.path.abspath(qemu_bin)
1407 logger.info("Using host's QEMU: %s" % qemu_bin)
1408 break
1409
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001410 if not os.access(qemu_bin, os.X_OK):
1411 raise OEPathError("No QEMU binary '%s' could be found" % qemu_bin)
1412
Andrew Geisslerc3d88e42020-10-02 09:45:00 -05001413 self.qemu_opt = "%s %s %s %s %s" % (qemu_bin, self.get('NETWORK_CMD'), self.get('QB_RNG'), self.get('ROOTFS_OPTIONS'), self.get('QB_OPT_APPEND'))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001414
1415 for ovmf in self.ovmf_bios:
1416 format = ovmf.rsplit('.', 1)[-1]
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001417 if format == "bin":
1418 format = "raw"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001419 self.qemu_opt += ' -drive if=pflash,format=%s,file=%s' % (format, ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001420
1421 self.qemu_opt += ' ' + self.qemu_opt_script
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001422
Brad Bishop08902b02019-08-20 09:16:51 -04001423 if self.ovmf_secboot_pkkek1:
1424 # Provide the Platform Key and first Key Exchange Key certificate as an
1425 # OEM string in the SMBIOS Type 11 table. Prepend the certificate string
1426 # with "application prefix" of the EnrollDefaultKeys.efi application
1427 self.qemu_opt += ' -smbios type=11,value=4e32566d-8e9e-4f52-81d3-5bb9715f9727:' \
1428 + self.ovmf_secboot_pkkek1
1429
Andrew Geissler99467da2019-02-25 18:54:23 -06001430 # Append qemuparams to override previous settings
1431 if self.qemuparams:
1432 self.qemu_opt += ' ' + self.qemuparams
1433
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001434 if self.snapshot:
1435 self.qemu_opt += " -snapshot"
1436
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001437 self.setup_serial()
1438 self.setup_vga()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001439
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001440 def start_qemu(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001441 import shlex
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001442 if self.kernel:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001443 kernel_opts = "-kernel %s -append '%s %s %s %s'" % (self.kernel, self.kernel_cmdline,
1444 self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'),
1445 self.bootparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001446 if self.dtb:
1447 kernel_opts += " -dtb %s" % self.dtb
1448 else:
1449 kernel_opts = ""
Patrick Williams213cb262021-08-07 19:21:33 -05001450
1451 if self.bios:
1452 self.qemu_opt += " -bios %s" % self.bios
1453
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001454 cmd = "%s %s" % (self.qemu_opt, kernel_opts)
Brad Bishop004d4992018-10-02 23:54:45 +02001455 cmds = shlex.split(cmd)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001456 logger.info('Running %s\n' % cmd)
Brad Bishopf86d0552018-12-04 14:18:15 -08001457 pass_fds = []
Brad Bishop08902b02019-08-20 09:16:51 -04001458 if self.taplock_descriptor:
1459 pass_fds = [self.taplock_descriptor.fileno()]
1460 if len(self.portlocks):
1461 for descriptor in self.portlocks.values():
1462 pass_fds.append(descriptor.fileno())
Brad Bishopf86d0552018-12-04 14:18:15 -08001463 process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds)
Brad Bishop004d4992018-10-02 23:54:45 +02001464 self.qemupid = process.pid
1465 retcode = process.wait()
1466 if retcode:
1467 if retcode == -signal.SIGTERM:
1468 logger.info("Qemu terminated by SIGTERM")
1469 else:
1470 logger.error("Failed to run qemu: %s", process.stderr.read().decode())
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001471
1472 def cleanup(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001473 if self.cleaned:
1474 return
1475
1476 # avoid dealing with SIGTERM when cleanup function is running
1477 signal.signal(signal.SIGTERM, signal.SIG_IGN)
1478
1479 logger.info("Cleaning up")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001480 if self.cleantap:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001481 cmd = ('sudo', self.qemuifdown, self.tap, self.bindir_native)
1482 logger.debug('Running %s' % str(cmd))
1483 subprocess.check_call(cmd)
Brad Bishop08902b02019-08-20 09:16:51 -04001484 self.release_taplock()
1485 self.release_portlock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001486
1487 if self.nfs_running:
1488 logger.info("Shutting down the userspace NFS server...")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001489 cmd = ("runqemu-export-rootfs", "stop", self.rootfs)
1490 logger.debug('Running %s' % str(cmd))
1491 subprocess.check_call(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001492
1493 if self.saved_stty:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001494 subprocess.check_call(("stty", self.saved_stty))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001495
Andrew Geisslerc926e172021-05-07 16:11:35 -05001496 if self.cleanup_files:
1497 for ent in self.cleanup_files:
1498 logger.info('Removing %s' % ent)
1499 if os.path.isfile(ent):
1500 os.remove(ent)
1501 else:
1502 shutil.rmtree(ent)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001503
Brad Bishop004d4992018-10-02 23:54:45 +02001504 self.cleaned = True
1505
Andrew Geissler82c905d2020-04-13 13:39:40 -05001506 def run_bitbake_env(self, mach=None):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001507 bitbake = shutil.which('bitbake')
1508 if not bitbake:
1509 return
1510
1511 if not mach:
1512 mach = self.get('MACHINE')
1513
Andrew Geissler82c905d2020-04-13 13:39:40 -05001514 multiconfig = self.get('MULTICONFIG')
1515 if multiconfig:
1516 multiconfig = "mc:%s" % multiconfig
1517
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001518 if mach:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001519 cmd = 'MACHINE=%s bitbake -e %s' % (mach, multiconfig)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001520 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001521 cmd = 'bitbake -e %s' % multiconfig
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001522
1523 logger.info('Running %s...' % cmd)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001524 return subprocess.check_output(cmd, shell=True).decode('utf-8')
1525
1526 def load_bitbake_env(self, mach=None):
1527 if self.bitbake_e:
1528 return
1529
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001530 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001531 self.bitbake_e = self.run_bitbake_env(mach=mach)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001532 except subprocess.CalledProcessError as err:
1533 self.bitbake_e = ''
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001534 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 -06001535
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001536 def validate_combos(self):
1537 if (self.fstype in self.vmtypes) and self.kernel:
1538 raise RunQemuError("%s doesn't need kernel %s!" % (self.fstype, self.kernel))
1539
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001540 @property
1541 def bindir_native(self):
1542 result = self.get('STAGING_BINDIR_NATIVE')
1543 if result and os.path.exists(result):
1544 return result
1545
Andrew Geissler82c905d2020-04-13 13:39:40 -05001546 cmd = ['bitbake', '-e']
1547 multiconfig = self.get('MULTICONFIG')
1548 if multiconfig:
1549 cmd.append('mc:%s:qemu-helper-native' % multiconfig)
1550 else:
1551 cmd.append('qemu-helper-native')
1552
Brad Bishop977dc1a2019-02-06 16:01:43 -05001553 logger.info('Running %s...' % str(cmd))
1554 out = subprocess.check_output(cmd).decode('utf-8')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001555
1556 match = re.search('^STAGING_BINDIR_NATIVE="(.*)"', out, re.M)
1557 if match:
1558 result = match.group(1)
1559 if os.path.exists(result):
1560 self.set('STAGING_BINDIR_NATIVE', result)
1561 return result
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001562 raise RunQemuError("Native sysroot directory %s doesn't exist" % result)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001563 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001564 raise RunQemuError("Can't find STAGING_BINDIR_NATIVE in '%s' output" % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001565
1566
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001567def main():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001568 if "help" in sys.argv or '-h' in sys.argv or '--help' in sys.argv:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001569 print_usage()
1570 return 0
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001571 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001572 config = BaseConfig()
Brad Bishop004d4992018-10-02 23:54:45 +02001573
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001574 renice = os.path.expanduser("~/bin/runqemu-renice")
1575 if os.path.exists(renice):
1576 logger.info('Using %s to renice' % renice)
1577 subprocess.check_call([renice, str(os.getpid())])
1578
Brad Bishop004d4992018-10-02 23:54:45 +02001579 def sigterm_handler(signum, frame):
1580 logger.info("SIGTERM received")
1581 os.kill(config.qemupid, signal.SIGTERM)
1582 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001583 # Deliberately ignore the return code of 'tput smam'.
1584 subprocess.call(["tput", "smam"])
Brad Bishop004d4992018-10-02 23:54:45 +02001585 signal.signal(signal.SIGTERM, sigterm_handler)
1586
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001587 config.check_args()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001588 config.read_qemuboot()
1589 config.check_and_set()
1590 # Check whether the combos is valid or not
1591 config.validate_combos()
1592 config.print_config()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001593 config.setup_network()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001594 config.setup_rootfs()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001595 config.setup_final()
1596 config.start_qemu()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001597 except RunQemuError as err:
1598 logger.error(err)
1599 return 1
1600 except Exception as err:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001601 import traceback
1602 traceback.print_exc()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001603 return 1
1604 finally:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001605 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001606 # Deliberately ignore the return code of 'tput smam'.
1607 subprocess.call(["tput", "smam"])
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001608
1609if __name__ == "__main__":
1610 sys.exit(main())