blob: 1f332ef525294fea7294f7fb699179eb9fb02006 [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)
Brad Bishop08902b02019-08-20 09:16:51 -0400236 fcntl.flock(self.taplock_descriptor, fcntl.LOCK_UN)
237 self.taplock_descriptor.close()
238 os.remove(self.taplock)
239 self.taplock_descriptor = None
240
241 def check_free_port(self, host, port, lockdir):
242 """ Check whether the port is free or not """
243 import socket
244 from contextlib import closing
245
246 lockfile = os.path.join(lockdir, str(port) + '.lock')
247 if self.acquire_portlock(lockfile):
248 with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
249 if sock.connect_ex((host, port)) == 0:
250 # Port is open, so not free
251 self.release_portlock(lockfile)
252 return False
253 else:
254 # Port is not open, so free
255 return True
256 else:
257 return False
258
259 def acquire_portlock(self, lockfile):
260 logger.debug("Acquiring lockfile %s..." % lockfile)
261 try:
262 portlock_descriptor = open(lockfile, 'w')
263 self.portlocks.update({lockfile: portlock_descriptor})
264 fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_EX|fcntl.LOCK_NB)
265 except Exception as e:
266 msg = "Acquiring lockfile %s failed: %s" % (lockfile, e)
267 logger.info(msg)
268 if lockfile in self.portlocks.keys() and self.portlocks[lockfile]:
269 self.portlocks[lockfile].close()
270 del self.portlocks[lockfile]
271 return False
272 return True
273
274 def release_portlock(self, lockfile=None):
275 if lockfile != None:
276 logger.debug("Releasing lockfile '%s'" % lockfile)
277 fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_UN)
278 self.portlocks[lockfile].close()
279 os.remove(lockfile)
280 del self.portlocks[lockfile]
281 elif len(self.portlocks):
282 for lockfile, descriptor in self.portlocks.items():
283 logger.debug("Releasing lockfile '%s'" % lockfile)
284 fcntl.flock(descriptor, fcntl.LOCK_UN)
285 descriptor.close()
286 os.remove(lockfile)
287 self.portlocks = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500288
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600289 def get(self, key):
290 if key in self.d:
291 return self.d.get(key)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500292 elif os.getenv(key):
293 return os.getenv(key)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600294 else:
295 return ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500296
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600297 def set(self, key, value):
298 self.d[key] = value
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500299
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600300 def is_deploy_dir_image(self, p):
301 if os.path.isdir(p):
302 if not re.search('.qemuboot.conf$', '\n'.join(os.listdir(p)), re.M):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500303 logger.debug("Can't find required *.qemuboot.conf in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600304 return False
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500305 if not any(map(lambda name: '-image-' in name, os.listdir(p))):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500306 logger.debug("Can't find *-image-* in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600307 return False
308 return True
309 else:
310 return False
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500311
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600312 def check_arg_fstype(self, fst):
313 """Check and set FSTYPE"""
Brad Bishop15ae2502019-06-18 21:44:24 -0400314 if fst not in self.fstypes + self.vmtypes + self.wictypes:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800315 logger.warning("Maybe unsupported FSTYPE: %s" % fst)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600316 if not self.fstype or self.fstype == fst:
317 if fst == 'ramfs':
318 fst = 'cpio.gz'
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500319 if fst in ('tar.bz2', 'tar.gz'):
320 fst = 'nfs'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600321 self.fstype = fst
322 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500323 raise RunQemuError("Conflicting: FSTYPE %s and %s" % (self.fstype, fst))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500324
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600325 def set_machine_deploy_dir(self, machine, deploy_dir_image):
326 """Set MACHINE and DEPLOY_DIR_IMAGE"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500327 logger.debug('MACHINE: %s' % machine)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600328 self.set("MACHINE", machine)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500329 logger.debug('DEPLOY_DIR_IMAGE: %s' % deploy_dir_image)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600330 self.set("DEPLOY_DIR_IMAGE", deploy_dir_image)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500331
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600332 def check_arg_nfs(self, p):
333 if os.path.isdir(p):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500334 self.rootfs = p
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600335 else:
336 m = re.match('(.*):(.*)', p)
337 self.nfs_server = m.group(1)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500338 self.rootfs = m.group(2)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600339 self.check_arg_fstype('nfs')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500340
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600341 def check_arg_path(self, p):
342 """
343 - Check whether it is <image>.qemuboot.conf or contains <image>.qemuboot.conf
344 - Check whether is a kernel file
345 - Check whether is a image file
346 - Check whether it is a nfs dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500347 - Check whether it is a OVMF flash file
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600348 """
349 if p.endswith('.qemuboot.conf'):
350 self.qemuboot = p
351 self.qbconfload = True
352 elif re.search('\.bin$', p) or re.search('bzImage', p) or \
353 re.search('zImage', p) or re.search('vmlinux', p) or \
354 re.search('fitImage', p) or re.search('uImage', p):
355 self.kernel = p
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500356 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 -0600357 self.rootfs = p
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500358 # Check filename against self.fstypes can hanlde <file>.cpio.gz,
359 # otherwise, its type would be "gz", which is incorrect.
360 fst = ""
361 for t in self.fstypes:
362 if p.endswith(t):
363 fst = t
364 break
365 if not fst:
366 m = re.search('.*\.(.*)$', self.rootfs)
367 if m:
368 fst = m.group(1)
369 if fst:
370 self.check_arg_fstype(fst)
371 qb = re.sub('\.' + fst + "$", '', self.rootfs)
372 qb = '%s%s' % (re.sub('\.rootfs$', '', qb), '.qemuboot.conf')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600373 if os.path.exists(qb):
374 self.qemuboot = qb
375 self.qbconfload = True
376 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800377 logger.warning("%s doesn't exist" % qb)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600378 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500379 raise RunQemuError("Can't find FSTYPE from: %s" % p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500380
381 elif os.path.isdir(p) or re.search(':', p) and re.search('/', p):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600382 if self.is_deploy_dir_image(p):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500383 logger.debug('DEPLOY_DIR_IMAGE: %s' % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600384 self.set("DEPLOY_DIR_IMAGE", p)
385 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500386 logger.debug("Assuming %s is an nfs rootfs" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600387 self.check_arg_nfs(p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500388 elif os.path.basename(p).startswith('ovmf'):
389 self.ovmf_bios.append(p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600390 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500391 raise RunQemuError("Unknown path arg %s" % p)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500392
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600393 def check_arg_machine(self, arg):
394 """Check whether it is a machine"""
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500395 if self.get('MACHINE') == arg:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600396 return
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500397 elif self.get('MACHINE') and self.get('MACHINE') != arg:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500398 raise RunQemuError("Maybe conflicted MACHINE: %s vs %s" % (self.get('MACHINE'), arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500399 elif re.search('/', arg):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500400 raise RunQemuError("Unknown arg: %s" % arg)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500401
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500402 logger.debug('Assuming MACHINE = %s' % arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500403
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600404 # if we're running under testimage, or similarly as a child
405 # of an existing bitbake invocation, we can't invoke bitbake
406 # to validate the MACHINE setting and must assume it's correct...
407 # FIXME: testimage.bbclass exports these two variables into env,
408 # are there other scenarios in which we need to support being
409 # invoked by bitbake?
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500410 deploy = self.get('DEPLOY_DIR_IMAGE')
411 bbchild = deploy and self.get('OE_TMPDIR')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600412 if bbchild:
413 self.set_machine_deploy_dir(arg, deploy)
414 return
415 # also check whether we're running under a sourced toolchain
416 # environment file
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500417 if self.get('OECORE_NATIVE_SYSROOT'):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600418 self.set("MACHINE", arg)
419 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500420
Andrew Geissler82c905d2020-04-13 13:39:40 -0500421 self.bitbake_e = self.run_bitbake_env(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600422 # bitbake -e doesn't report invalid MACHINE as an error, so
423 # let's check DEPLOY_DIR_IMAGE to make sure that it is a valid
424 # MACHINE.
425 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
426 if s:
427 deploy_dir_image = s.group(1)
428 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500429 raise RunQemuError("bitbake -e %s" % self.bitbake_e)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600430 if self.is_deploy_dir_image(deploy_dir_image):
431 self.set_machine_deploy_dir(arg, deploy_dir_image)
432 else:
433 logger.error("%s not a directory valid DEPLOY_DIR_IMAGE" % deploy_dir_image)
434 self.set("MACHINE", arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500435
Andrew Geisslerc182c622020-05-15 14:13:32 -0500436 def set_dri_path(self):
437 # As runqemu can be run within bitbake (when using testimage, for example),
438 # we need to ensure that we run host pkg-config, and that it does not
439 # get mis-directed to native build paths set by bitbake.
440 try:
441 del os.environ['PKG_CONFIG_PATH']
442 del os.environ['PKG_CONFIG_DIR']
443 del os.environ['PKG_CONFIG_LIBDIR']
444 del os.environ['PKG_CONFIG_SYSROOT_DIR']
445 except KeyError:
446 pass
447 try:
448 dripath = subprocess.check_output("PATH=/bin:/usr/bin:$PATH pkg-config --variable=dridriverdir dri", shell=True)
449 except subprocess.CalledProcessError as e:
450 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.")
451 os.environ['LIBGL_DRIVERS_PATH'] = dripath.decode('utf-8').strip()
452
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600453 def check_args(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500454 for debug in ("-d", "--debug"):
455 if debug in sys.argv:
456 logger.setLevel(logging.DEBUG)
457 sys.argv.remove(debug)
458
459 for quiet in ("-q", "--quiet"):
460 if quiet in sys.argv:
461 logger.setLevel(logging.ERROR)
462 sys.argv.remove(quiet)
463
Andrew Geisslerc182c622020-05-15 14:13:32 -0500464 if 'gl' not in sys.argv[1:] and 'gl-es' not in sys.argv[1:]:
465 os.environ['SDL_RENDER_DRIVER'] = 'software'
466
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600467 unknown_arg = ""
468 for arg in sys.argv[1:]:
Brad Bishop15ae2502019-06-18 21:44:24 -0400469 if arg in self.fstypes + self.vmtypes + self.wictypes:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600470 self.check_arg_fstype(arg)
471 elif arg == 'nographic':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500472 self.nographic = True
Brad Bishop19323692019-04-05 15:28:33 -0400473 elif arg == 'sdl':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500474 self.sdl = True
Brad Bishopa34c0302019-09-23 22:34:48 -0400475 elif arg == 'gtk':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500476 self.gtk = True
477 elif arg == 'gl':
478 self.gl = True
479 elif 'gl-es' in sys.argv[1:]:
480 self.gl_es = True
Brad Bishop19323692019-04-05 15:28:33 -0400481 elif arg == 'egl-headless':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500482 self.egl_headless = True
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600483 elif arg == 'novga':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500484 self.novga = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600485 elif arg == 'serial':
Brad Bishop19323692019-04-05 15:28:33 -0400486 self.serialconsole = True
487 elif arg == "serialstdio":
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600488 self.serialstdio = True
489 elif arg == 'audio':
490 logger.info("Enabling audio in qemu")
491 logger.info("Please install sound drivers in linux host")
492 self.audio_enabled = True
493 elif arg == 'kvm':
494 self.kvm_enabled = True
495 elif arg == 'kvm-vhost':
496 self.vhost_enabled = True
497 elif arg == 'slirp':
498 self.slirp_enabled = True
Andrew Geissler82c905d2020-04-13 13:39:40 -0500499 elif arg.startswith('bridge='):
500 self.net_bridge = '%s' % arg[len('bridge='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600501 elif arg == 'snapshot':
502 self.snapshot = True
503 elif arg == 'publicvnc':
504 self.qemu_opt_script += ' -vnc :0'
505 elif arg.startswith('tcpserial='):
Brad Bishop15ae2502019-06-18 21:44:24 -0400506 self.tcpserial_portnum = '%s' % arg[len('tcpserial='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600507 elif arg.startswith('qemuparams='):
Andrew Geissler99467da2019-02-25 18:54:23 -0600508 self.qemuparams = ' %s' % arg[len('qemuparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600509 elif arg.startswith('bootparams='):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500510 self.bootparams = arg[len('bootparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600511 elif os.path.exists(arg) or (re.search(':', arg) and re.search('/', arg)):
512 self.check_arg_path(os.path.abspath(arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500513 elif re.search(r'-image-|-image$', arg):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600514 # Lazy rootfs
515 self.rootfs = arg
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500516 elif arg.startswith('ovmf'):
517 self.ovmf_bios.append(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600518 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500519 # At last, assume it is the MACHINE
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600520 if (not unknown_arg) or unknown_arg == arg:
521 unknown_arg = arg
522 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500523 raise RunQemuError("Can't handle two unknown args: %s %s\n"
524 "Try 'runqemu help' on how to use it" % \
525 (unknown_arg, arg))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600526 # Check to make sure it is a valid machine
Brad Bishopa5c52ff2018-11-23 10:55:50 +1300527 if unknown_arg and self.get('MACHINE') != unknown_arg:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500528 if self.get('DEPLOY_DIR_IMAGE'):
529 machine = os.path.basename(self.get('DEPLOY_DIR_IMAGE'))
530 if unknown_arg == machine:
531 self.set("MACHINE", machine)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500532
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600533 self.check_arg_machine(unknown_arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500534
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500535 if not (self.get('DEPLOY_DIR_IMAGE') or self.qbconfload):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500536 self.load_bitbake_env()
537 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
538 if s:
539 self.set("DEPLOY_DIR_IMAGE", s.group(1))
540
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600541 def check_kvm(self):
542 """Check kvm and kvm-host"""
543 if not (self.kvm_enabled or self.vhost_enabled):
William A. Kennington IIIac69b482021-06-02 12:28:27 -0700544 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 -0600545 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500546
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600547 if not self.get('QB_CPU_KVM'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500548 raise RunQemuError("QB_CPU_KVM is NULL, this board doesn't support kvm")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500549
William A. Kennington IIIac69b482021-06-02 12:28:27 -0700550 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 -0600551 yocto_kvm_wiki = "https://wiki.yoctoproject.org/wiki/How_to_enable_KVM_for_Poky_qemu"
552 yocto_paravirt_kvm_wiki = "https://wiki.yoctoproject.org/wiki/Running_an_x86_Yocto_Linux_image_under_QEMU_KVM"
553 dev_kvm = '/dev/kvm'
554 dev_vhost = '/dev/vhost-net'
Brad Bishop15ae2502019-06-18 21:44:24 -0400555 if self.qemu_system.endswith(('i386', 'x86_64')):
556 with open('/proc/cpuinfo', 'r') as f:
557 kvm_cap = re.search('vmx|svm', "".join(f.readlines()))
558 if not kvm_cap:
559 logger.error("You are trying to enable KVM on a cpu without VT support.")
560 logger.error("Remove kvm from the command-line, or refer:")
561 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500562
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600563 if not os.path.exists(dev_kvm):
564 logger.error("Missing KVM device. Have you inserted kvm modules?")
565 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500566 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500567
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600568 if os.access(dev_kvm, os.W_OK|os.R_OK):
569 self.qemu_opt_script += ' -enable-kvm'
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500570 if self.get('MACHINE') == "qemux86":
571 # Workaround for broken APIC window on pre 4.15 host kernels which causes boot hangs
572 # See YOCTO #12301
573 # On 64 bit we use x2apic
574 self.kernel_cmdline_script += " clocksource=kvm-clock hpet=disable noapic nolapic"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600575 else:
576 logger.error("You have no read or write permission on /dev/kvm.")
577 logger.error("Please change the ownership of this file as described at:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500578 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500579
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600580 if self.vhost_enabled:
581 if not os.path.exists(dev_vhost):
582 logger.error("Missing virtio net device. Have you inserted vhost-net module?")
583 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500584 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500585
Andrew Geissler635e0e42020-08-21 15:58:33 -0500586 if not os.access(dev_vhost, os.W_OK|os.R_OK):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600587 logger.error("You have no read or write permission on /dev/vhost-net.")
588 logger.error("Please change the ownership of this file as described at:")
Andrew Geissler635e0e42020-08-21 15:58:33 -0500589 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500590
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600591 def check_fstype(self):
592 """Check and setup FSTYPE"""
593 if not self.fstype:
594 fstype = self.get('QB_DEFAULT_FSTYPE')
595 if fstype:
596 self.fstype = fstype
597 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500598 raise RunQemuError("FSTYPE is NULL!")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500599
Brad Bishop15ae2502019-06-18 21:44:24 -0400600 # parse QB_FSINFO into dict, e.g. { 'wic': ['no-kernel-in-fs', 'a-flag'], 'ext4': ['another-flag']}
601 wic_fs = False
602 qb_fsinfo = self.get('QB_FSINFO')
603 if qb_fsinfo:
604 qb_fsinfo = qb_fsinfo.split()
605 for fsinfo in qb_fsinfo:
606 try:
607 fstype, fsflag = fsinfo.split(':')
608
609 if fstype == 'wic':
610 if fsflag == 'no-kernel-in-fs':
611 wic_fs = True
612 elif fsflag == 'kernel-in-fs':
613 wic_fs = False
614 else:
615 logger.warn('Unknown flag "%s:%s" in QB_FSINFO', fstype, fsflag)
616 continue
617 else:
618 logger.warn('QB_FSINFO is not supported for image type "%s"', fstype)
619 continue
620
621 if fstype in self.fsinfo:
622 self.fsinfo[fstype].append(fsflag)
623 else:
624 self.fsinfo[fstype] = [fsflag]
625 except Exception:
626 logger.error('Invalid parameter "%s" in QB_FSINFO', fsinfo)
627
628 # treat wic images as vmimages (with kernel) or as fsimages (rootfs only)
629 if wic_fs:
630 self.fstypes = self.fstypes + self.wictypes
631 else:
632 self.vmtypes = self.vmtypes + self.wictypes
633
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600634 def check_rootfs(self):
635 """Check and set rootfs"""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500636
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500637 if self.fstype == "none":
638 return
639
640 if self.get('ROOTFS'):
641 if not self.rootfs:
642 self.rootfs = self.get('ROOTFS')
643 elif self.get('ROOTFS') != self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500644 raise RunQemuError("Maybe conflicted ROOTFS: %s vs %s" % (self.get('ROOTFS'), self.rootfs))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500645
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600646 if self.fstype == 'nfs':
647 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500648
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600649 if self.rootfs and not os.path.exists(self.rootfs):
650 # Lazy rootfs
651 self.rootfs = "%s/%s-%s.%s" % (self.get('DEPLOY_DIR_IMAGE'),
652 self.rootfs, self.get('MACHINE'),
653 self.fstype)
654 elif not self.rootfs:
655 cmd_name = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_NAME'), self.fstype)
656 cmd_link = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'), self.fstype)
657 cmds = (cmd_name, cmd_link)
658 self.rootfs = get_first_file(cmds)
659 if not self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500660 raise RunQemuError("Failed to find rootfs: %s or %s" % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500661
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600662 if not os.path.exists(self.rootfs):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500663 raise RunQemuError("Can't find rootfs: %s" % self.rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500664
Brad Bishop08902b02019-08-20 09:16:51 -0400665 def setup_pkkek1(self):
666 """
667 Extract from PEM certificate the Platform Key and first Key
668 Exchange Key certificate string. The hypervisor needs to provide
669 it in the Type 11 SMBIOS table
670 """
671 pemcert = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), 'OvmfPkKek1.pem')
672 try:
673 with open(pemcert, 'r') as pemfile:
674 key = pemfile.read().replace('\n', ''). \
675 replace('-----BEGIN CERTIFICATE-----', ''). \
676 replace('-----END CERTIFICATE-----', '')
677 self.ovmf_secboot_pkkek1 = key
678
679 except FileNotFoundError:
680 raise RunQemuError("Can't open PEM certificate %s " % pemcert)
681
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500682 def check_ovmf(self):
683 """Check and set full path for OVMF firmware and variable file(s)."""
684
685 for index, ovmf in enumerate(self.ovmf_bios):
686 if os.path.exists(ovmf):
687 continue
688 for suffix in ('qcow2', 'bin'):
689 path = '%s/%s.%s' % (self.get('DEPLOY_DIR_IMAGE'), ovmf, suffix)
690 if os.path.exists(path):
691 self.ovmf_bios[index] = path
Brad Bishop08902b02019-08-20 09:16:51 -0400692 if ovmf.endswith('secboot'):
693 self.setup_pkkek1()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500694 break
695 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500696 raise RunQemuError("Can't find OVMF firmware: %s" % ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500697
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600698 def check_kernel(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400699 """Check and set kernel"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600700 # The vm image doesn't need a kernel
701 if self.fstype in self.vmtypes:
702 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500703
Brad Bishop316dfdd2018-06-25 12:45:53 -0400704 # See if the user supplied a KERNEL option
705 if self.get('KERNEL'):
706 self.kernel = self.get('KERNEL')
707
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500708 # QB_DEFAULT_KERNEL is always a full file path
709 kernel_name = os.path.basename(self.get('QB_DEFAULT_KERNEL'))
710
711 # The user didn't want a kernel to be loaded
Brad Bishop316dfdd2018-06-25 12:45:53 -0400712 if kernel_name == "none" and not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500713 return
714
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600715 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
716 if not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500717 kernel_match_name = "%s/%s" % (deploy_dir_image, kernel_name)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600718 kernel_match_link = "%s/%s" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
719 kernel_startswith = "%s/%s*" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
720 cmds = (kernel_match_name, kernel_match_link, kernel_startswith)
721 self.kernel = get_first_file(cmds)
722 if not self.kernel:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500723 raise RunQemuError('KERNEL not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500724
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600725 if not os.path.exists(self.kernel):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500726 raise RunQemuError("KERNEL %s not found" % self.kernel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500727
Brad Bishop316dfdd2018-06-25 12:45:53 -0400728 def check_dtb(self):
729 """Check and set dtb"""
730 # Did the user specify a device tree?
731 if self.get('DEVICE_TREE'):
732 self.dtb = self.get('DEVICE_TREE')
733 if not os.path.exists(self.dtb):
734 raise RunQemuError('Specified DTB not found: %s' % self.dtb)
735 return
736
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600737 dtb = self.get('QB_DTB')
738 if dtb:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400739 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600740 cmd_match = "%s/%s" % (deploy_dir_image, dtb)
741 cmd_startswith = "%s/%s*" % (deploy_dir_image, dtb)
742 cmd_wild = "%s/*.dtb" % deploy_dir_image
743 cmds = (cmd_match, cmd_startswith, cmd_wild)
744 self.dtb = get_first_file(cmds)
745 if not os.path.exists(self.dtb):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500746 raise RunQemuError('DTB not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500747
Brad Bishopc68388fc2019-08-26 01:33:31 -0400748 def check_bios(self):
749 """Check and set bios"""
750
751 # See if the user supplied a BIOS option
752 if self.get('BIOS'):
753 self.bios = self.get('BIOS')
754
755 # QB_DEFAULT_BIOS is always a full file path
756 bios_name = os.path.basename(self.get('QB_DEFAULT_BIOS'))
757
758 # The user didn't want a bios to be loaded
759 if (bios_name == "" or bios_name == "none") and not self.bios:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600760 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500761
Brad Bishopc68388fc2019-08-26 01:33:31 -0400762 if not self.bios:
763 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
764 self.bios = "%s/%s" % (deploy_dir_image, bios_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500765
Brad Bishopc68388fc2019-08-26 01:33:31 -0400766 if not self.bios:
767 raise RunQemuError('BIOS not found: %s' % bios_match_name)
768
769 if not os.path.exists(self.bios):
770 raise RunQemuError("KERNEL %s not found" % self.bios)
771
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500772
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600773 def check_mem(self):
Andrew Geissler99467da2019-02-25 18:54:23 -0600774 """
775 Both qemu and kernel needs memory settings, so check QB_MEM and set it
776 for both.
777 """
778 s = re.search('-m +([0-9]+)', self.qemuparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600779 if s:
780 self.set('QB_MEM', '-m %s' % s.group(1))
781 elif not self.get('QB_MEM'):
Brad Bishop79641f22019-09-10 07:20:22 -0400782 logger.info('QB_MEM is not set, use 256M by default')
783 self.set('QB_MEM', '-m 256')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500784
Andrew Geissler99467da2019-02-25 18:54:23 -0600785 # Check and remove M or m suffix
786 qb_mem = self.get('QB_MEM')
787 if qb_mem.endswith('M') or qb_mem.endswith('m'):
788 qb_mem = qb_mem[:-1]
789
790 # Add -m prefix it not present
791 if not qb_mem.startswith('-m'):
792 qb_mem = '-m %s' % qb_mem
793
794 self.set('QB_MEM', qb_mem)
795
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800796 mach = self.get('MACHINE')
797 if not mach.startswith('qemumips'):
798 self.kernel_cmdline_script += ' mem=%s' % self.get('QB_MEM').replace('-m','').strip() + 'M'
799
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600800 self.qemu_opt_script += ' %s' % self.get('QB_MEM')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500801
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600802 def check_tcpserial(self):
803 if self.tcpserial_portnum:
Brad Bishop15ae2502019-06-18 21:44:24 -0400804 ports = self.tcpserial_portnum.split(':')
805 port = ports[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600806 if self.get('QB_TCPSERIAL_OPT'):
Brad Bishop15ae2502019-06-18 21:44:24 -0400807 self.qemu_opt_script += ' ' + self.get('QB_TCPSERIAL_OPT').replace('@PORT@', port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600808 else:
Brad Bishop15ae2502019-06-18 21:44:24 -0400809 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
810
811 if len(ports) > 1:
812 for port in ports[1:]:
813 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500814
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600815 def check_and_set(self):
816 """Check configs sanity and set when needed"""
817 self.validate_paths()
Andrew Geissler82c905d2020-04-13 13:39:40 -0500818 if not self.slirp_enabled and not self.net_bridge:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500819 check_tun()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600820 # Check audio
821 if self.audio_enabled:
822 if not self.get('QB_AUDIO_DRV'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500823 raise RunQemuError("QB_AUDIO_DRV is NULL, this board doesn't support audio")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600824 if not self.get('QB_AUDIO_OPT'):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800825 logger.warning('QB_AUDIO_OPT is NULL, you may need define it to make audio work')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600826 else:
827 self.qemu_opt_script += ' %s' % self.get('QB_AUDIO_OPT')
828 os.putenv('QEMU_AUDIO_DRV', self.get('QB_AUDIO_DRV'))
829 else:
830 os.putenv('QEMU_AUDIO_DRV', 'none')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500831
Brad Bishop15ae2502019-06-18 21:44:24 -0400832 self.check_qemu_system()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600833 self.check_kvm()
834 self.check_fstype()
835 self.check_rootfs()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500836 self.check_ovmf()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600837 self.check_kernel()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400838 self.check_dtb()
Brad Bishopc68388fc2019-08-26 01:33:31 -0400839 self.check_bios()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600840 self.check_mem()
841 self.check_tcpserial()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500842
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600843 def read_qemuboot(self):
844 if not self.qemuboot:
845 if self.get('DEPLOY_DIR_IMAGE'):
846 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600847 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800848 logger.warning("Can't find qemuboot conf file, DEPLOY_DIR_IMAGE is NULL!")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600849 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500850
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600851 if self.rootfs and not os.path.exists(self.rootfs):
852 # Lazy rootfs
853 machine = self.get('MACHINE')
854 if not machine:
855 machine = os.path.basename(deploy_dir_image)
856 self.qemuboot = "%s/%s-%s.qemuboot.conf" % (deploy_dir_image,
857 self.rootfs, machine)
858 else:
859 cmd = 'ls -t %s/*.qemuboot.conf' % deploy_dir_image
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500860 logger.debug('Running %s...' % cmd)
861 try:
862 qbs = subprocess.check_output(cmd, shell=True).decode('utf-8')
863 except subprocess.CalledProcessError as err:
864 raise RunQemuError(err)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600865 if qbs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500866 for qb in qbs.split():
867 # Don't use initramfs when other choices unless fstype is ramfs
868 if '-initramfs-' in os.path.basename(qb) and self.fstype != 'cpio.gz':
869 continue
870 self.qemuboot = qb
871 break
872 if not self.qemuboot:
873 # Use the first one when no choice
874 self.qemuboot = qbs.split()[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600875 self.qbconfload = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500876
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600877 if not self.qemuboot:
878 # If we haven't found a .qemuboot.conf at this point it probably
879 # doesn't exist, continue without
880 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500881
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600882 if not os.path.exists(self.qemuboot):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500883 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 -0500884
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500885 logger.debug('CONFFILE: %s' % self.qemuboot)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500886
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600887 cf = configparser.ConfigParser()
888 cf.read(self.qemuboot)
889 for k, v in cf.items('config_bsp'):
890 k_upper = k.upper()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500891 if v.startswith("../"):
892 v = os.path.abspath(os.path.dirname(self.qemuboot) + "/" + v)
893 elif v == ".":
894 v = os.path.dirname(self.qemuboot)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600895 self.set(k_upper, v)
896
897 def validate_paths(self):
898 """Ensure all relevant path variables are set"""
899 # When we're started with a *.qemuboot.conf arg assume that image
900 # artefacts are relative to that file, rather than in whatever
901 # directory DEPLOY_DIR_IMAGE in the conf file points to.
902 if self.qbconfload:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500903 imgdir = os.path.realpath(os.path.dirname(self.qemuboot))
904 if imgdir != os.path.realpath(self.get('DEPLOY_DIR_IMAGE')):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600905 logger.info('Setting DEPLOY_DIR_IMAGE to folder containing %s (%s)' % (self.qemuboot, imgdir))
906 self.set('DEPLOY_DIR_IMAGE', imgdir)
907
908 # If the STAGING_*_NATIVE directories from the config file don't exist
909 # and we're in a sourced OE build directory try to extract the paths
910 # from `bitbake -e`
911 havenative = os.path.exists(self.get('STAGING_DIR_NATIVE')) and \
912 os.path.exists(self.get('STAGING_BINDIR_NATIVE'))
913
914 if not havenative:
915 if not self.bitbake_e:
916 self.load_bitbake_env()
917
918 if self.bitbake_e:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500919 native_vars = ['STAGING_DIR_NATIVE']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600920 for nv in native_vars:
921 s = re.search('^%s="(.*)"' % nv, self.bitbake_e, re.M)
922 if s and s.group(1) != self.get(nv):
923 logger.info('Overriding conf file setting of %s to %s from Bitbake environment' % (nv, s.group(1)))
924 self.set(nv, s.group(1))
925 else:
926 # when we're invoked from a running bitbake instance we won't
927 # be able to call `bitbake -e`, then try:
928 # - get OE_TMPDIR from environment and guess paths based on it
929 # - get OECORE_NATIVE_SYSROOT from environment (for sdk)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500930 tmpdir = self.get('OE_TMPDIR')
931 oecore_native_sysroot = self.get('OECORE_NATIVE_SYSROOT')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600932 if tmpdir:
933 logger.info('Setting STAGING_DIR_NATIVE and STAGING_BINDIR_NATIVE relative to OE_TMPDIR (%s)' % tmpdir)
934 hostos, _, _, _, machine = os.uname()
935 buildsys = '%s-%s' % (machine, hostos.lower())
936 staging_dir_native = '%s/sysroots/%s' % (tmpdir, buildsys)
937 self.set('STAGING_DIR_NATIVE', staging_dir_native)
938 elif oecore_native_sysroot:
939 logger.info('Setting STAGING_DIR_NATIVE to OECORE_NATIVE_SYSROOT (%s)' % oecore_native_sysroot)
940 self.set('STAGING_DIR_NATIVE', oecore_native_sysroot)
941 if self.get('STAGING_DIR_NATIVE'):
942 # we have to assume that STAGING_BINDIR_NATIVE is at usr/bin
943 staging_bindir_native = '%s/usr/bin' % self.get('STAGING_DIR_NATIVE')
944 logger.info('Setting STAGING_BINDIR_NATIVE to %s' % staging_bindir_native)
945 self.set('STAGING_BINDIR_NATIVE', '%s/usr/bin' % self.get('STAGING_DIR_NATIVE'))
946
947 def print_config(self):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500948 logoutput = ['Continuing with the following parameters:']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600949 if not self.fstype in self.vmtypes:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500950 logoutput.append('KERNEL: [%s]' % self.kernel)
Brad Bishopc68388fc2019-08-26 01:33:31 -0400951 if self.bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500952 logoutput.append('BIOS: [%s]' % self.bios)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600953 if self.dtb:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500954 logoutput.append('DTB: [%s]' % self.dtb)
955 logoutput.append('MACHINE: [%s]' % self.get('MACHINE'))
Brad Bishop15ae2502019-06-18 21:44:24 -0400956 try:
957 fstype_flags = ' (' + ', '.join(self.fsinfo[self.fstype]) + ')'
958 except KeyError:
959 fstype_flags = ''
Andrew Geissler82c905d2020-04-13 13:39:40 -0500960 logoutput.append('FSTYPE: [%s%s]' % (self.fstype, fstype_flags))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600961 if self.fstype == 'nfs':
Andrew Geissler82c905d2020-04-13 13:39:40 -0500962 logoutput.append('NFS_DIR: [%s]' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600963 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500964 logoutput.append('ROOTFS: [%s]' % self.rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500965 if self.ovmf_bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500966 logoutput.append('OVMF: %s' % self.ovmf_bios)
Brad Bishop08902b02019-08-20 09:16:51 -0400967 if (self.ovmf_secboot_pkkek1):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500968 logoutput.append('SECBOOT PKKEK1: [%s...]' % self.ovmf_secboot_pkkek1[0:100])
969 logoutput.append('CONFFILE: [%s]' % self.qemuboot)
970 logoutput.append('')
971 logger.info('\n'.join(logoutput))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600972
973 def setup_nfs(self):
974 if not self.nfs_server:
975 if self.slirp_enabled:
976 self.nfs_server = '10.0.2.2'
977 else:
978 self.nfs_server = '192.168.7.1'
979
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500980 # Figure out a new nfs_instance to allow multiple qemus running.
Brad Bishop977dc1a2019-02-06 16:01:43 -0500981 ps = subprocess.check_output(("ps", "auxww")).decode('utf-8')
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500982 pattern = '/bin/unfsd .* -i .*\.pid -e .*/exports([0-9]+) '
983 all_instances = re.findall(pattern, ps, re.M)
984 if all_instances:
985 all_instances.sort(key=int)
986 self.nfs_instance = int(all_instances.pop()) + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600987
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500988 nfsd_port = 3049 + 2 * self.nfs_instance
989 mountd_port = 3048 + 2 * self.nfs_instance
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600990
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500991 # Export vars for runqemu-export-rootfs
992 export_dict = {
993 'NFS_INSTANCE': self.nfs_instance,
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500994 'NFSD_PORT': nfsd_port,
995 'MOUNTD_PORT': mountd_port,
996 }
997 for k, v in export_dict.items():
998 # Use '%s' since they are integers
999 os.putenv(k, '%s' % v)
1000
Andrew Geissler82c905d2020-04-13 13:39:40 -05001001 self.unfs_opts="nfsvers=3,port=%s,tcp,mountport=%s" % (nfsd_port, mountd_port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001002
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001003 # Extract .tar.bz2 or .tar.bz if no nfs dir
1004 if not (self.rootfs and os.path.isdir(self.rootfs)):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001005 src_prefix = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'))
1006 dest = "%s-nfsroot" % src_prefix
1007 if os.path.exists('%s.pseudo_state' % dest):
1008 logger.info('Use %s as NFS_DIR' % dest)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001009 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001010 else:
1011 src = ""
1012 src1 = '%s.tar.bz2' % src_prefix
1013 src2 = '%s.tar.gz' % src_prefix
1014 if os.path.exists(src1):
1015 src = src1
1016 elif os.path.exists(src2):
1017 src = src2
1018 if not src:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001019 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 -06001020 logger.info('NFS_DIR not found, extracting %s to %s' % (src, dest))
Brad Bishop977dc1a2019-02-06 16:01:43 -05001021 cmd = ('runqemu-extract-sdk', src, dest)
1022 logger.info('Running %s...' % str(cmd))
1023 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001024 raise RunQemuError('Failed to run %s' % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001025 self.rootfs = dest
Andrew Geisslerc926e172021-05-07 16:11:35 -05001026 self.cleanup_files.append(self.rootfs)
1027 self.cleanup_files.append('%s.pseudo_state' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001028
1029 # Start the userspace NFS server
Brad Bishop977dc1a2019-02-06 16:01:43 -05001030 cmd = ('runqemu-export-rootfs', 'start', self.rootfs)
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)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001034
1035 self.nfs_running = True
1036
Andrew Geissler82c905d2020-04-13 13:39:40 -05001037 def setup_net_bridge(self):
1038 self.set('NETWORK_CMD', '-netdev bridge,br=%s,id=net0,helper=%s -device virtio-net-pci,netdev=net0 ' % (
1039 self.net_bridge, os.path.join(self.bindir_native, 'qemu-oe-bridge-helper')))
1040
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001041 def setup_slirp(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001042 """Setup user networking"""
1043
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001044 if self.fstype == 'nfs':
1045 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001046 netconf = " " + self.cmdline_ip_slirp
1047 logger.info("Network configuration:%s", netconf)
1048 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001049 # Port mapping
1050 hostfwd = ",hostfwd=tcp::2222-:22,hostfwd=tcp::2323-:23"
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001051 qb_slirp_opt_default = "-netdev user,id=net0%s,tftp=%s" % (hostfwd, self.get('DEPLOY_DIR_IMAGE'))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001052 qb_slirp_opt = self.get('QB_SLIRP_OPT') or qb_slirp_opt_default
1053 # Figure out the port
1054 ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt)
1055 ports = [int(i) for i in ports]
1056 mac = 2
Brad Bishop08902b02019-08-20 09:16:51 -04001057
1058 lockdir = "/tmp/qemu-port-locks"
1059 if not os.path.exists(lockdir):
1060 # There might be a race issue when multi runqemu processess are
1061 # running at the same time.
1062 try:
1063 os.mkdir(lockdir)
1064 os.chmod(lockdir, 0o777)
1065 except FileExistsError:
1066 pass
1067
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001068 # Find a free port to avoid conflicts
1069 for p in ports[:]:
1070 p_new = p
Brad Bishop08902b02019-08-20 09:16:51 -04001071 while not self.check_free_port('localhost', p_new, lockdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001072 p_new += 1
1073 mac += 1
1074 while p_new in ports:
1075 p_new += 1
1076 mac += 1
1077 if p != p_new:
1078 ports.append(p_new)
1079 qb_slirp_opt = re.sub(':%s-' % p, ':%s-' % p_new, qb_slirp_opt)
1080 logger.info("Port forward changed: %s -> %s" % (p, p_new))
1081 mac = "%s%02x" % (self.mac_slirp, mac)
1082 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qb_slirp_opt))
1083 # Print out port foward
1084 hostfwd = re.findall('(hostfwd=[^,]*)', qb_slirp_opt)
1085 if hostfwd:
1086 logger.info('Port forward: %s' % ' '.join(hostfwd))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001087
1088 def setup_tap(self):
1089 """Setup tap"""
1090
1091 # This file is created when runqemu-gen-tapdevs creates a bank of tap
1092 # devices, indicating that the user should not bring up new ones using
1093 # sudo.
1094 nosudo_flag = '/etc/runqemu-nosudo'
1095 self.qemuifup = shutil.which('runqemu-ifup')
1096 self.qemuifdown = shutil.which('runqemu-ifdown')
1097 ip = shutil.which('ip')
1098 lockdir = "/tmp/qemu-tap-locks"
1099
1100 if not (self.qemuifup and self.qemuifdown and ip):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001101 logger.error("runqemu-ifup: %s" % self.qemuifup)
1102 logger.error("runqemu-ifdown: %s" % self.qemuifdown)
1103 logger.error("ip: %s" % ip)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001104 raise OEPathError("runqemu-ifup, runqemu-ifdown or ip not found")
1105
1106 if not os.path.exists(lockdir):
1107 # There might be a race issue when multi runqemu processess are
1108 # running at the same time.
1109 try:
1110 os.mkdir(lockdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001111 os.chmod(lockdir, 0o777)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001112 except FileExistsError:
1113 pass
1114
Brad Bishop977dc1a2019-02-06 16:01:43 -05001115 cmd = (ip, 'link')
1116 logger.debug('Running %s...' % str(cmd))
1117 ip_link = subprocess.check_output(cmd).decode('utf-8')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001118 # Matches line like: 6: tap0: <foo>
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001119 possibles = re.findall('^[0-9]+: +(tap[0-9]+): <.*', ip_link, re.M)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001120 tap = ""
1121 for p in possibles:
1122 lockfile = os.path.join(lockdir, p)
1123 if os.path.exists('%s.skip' % lockfile):
1124 logger.info('Found %s.skip, skipping %s' % (lockfile, p))
1125 continue
Brad Bishop08902b02019-08-20 09:16:51 -04001126 self.taplock = lockfile + '.lock'
1127 if self.acquire_taplock(error=False):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001128 tap = p
1129 logger.info("Using preconfigured tap device %s" % tap)
1130 logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap))
1131 break
1132
1133 if not tap:
1134 if os.path.exists(nosudo_flag):
1135 logger.error("Error: There are no available tap devices to use for networking,")
1136 logger.error("and I see %s exists, so I am not going to try creating" % nosudo_flag)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001137 raise RunQemuError("a new one with sudo.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001138
1139 gid = os.getgid()
1140 uid = os.getuid()
1141 logger.info("Setting up tap interface under sudo")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001142 cmd = ('sudo', self.qemuifup, str(uid), str(gid), self.bindir_native)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001143 try:
1144 tap = subprocess.check_output(cmd).decode('utf-8').strip()
1145 except subprocess.CalledProcessError as e:
1146 logger.error('Setting up tap device failed:\n%s\nRun runqemu-gen-tapdevs to manually create one.' % str(e))
1147 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001148 lockfile = os.path.join(lockdir, tap)
Brad Bishop08902b02019-08-20 09:16:51 -04001149 self.taplock = lockfile + '.lock'
1150 self.acquire_taplock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001151 self.cleantap = True
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001152 logger.debug('Created tap: %s' % tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001153
1154 if not tap:
1155 logger.error("Failed to setup tap device. Run runqemu-gen-tapdevs to manually create.")
Andrew Geissler82c905d2020-04-13 13:39:40 -05001156 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001157 self.tap = tap
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001158 tapnum = int(tap[3:])
1159 gateway = tapnum * 2 + 1
1160 client = gateway + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001161 if self.fstype == 'nfs':
1162 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001163 netconf = " " + self.cmdline_ip_tap
1164 netconf = netconf.replace('@CLIENT@', str(client))
1165 netconf = netconf.replace('@GATEWAY@', str(gateway))
1166 logger.info("Network configuration:%s", netconf)
1167 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001168 mac = "%s%02x" % (self.mac_tap, client)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001169 qb_tap_opt = self.get('QB_TAP_OPT')
1170 if qb_tap_opt:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001171 qemu_tap_opt = qb_tap_opt.replace('@TAP@', tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001172 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001173 qemu_tap_opt = "-netdev tap,id=net0,ifname=%s,script=no,downscript=no" % (self.tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001174
1175 if self.vhost_enabled:
1176 qemu_tap_opt += ',vhost=on'
1177
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001178 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qemu_tap_opt))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001179
1180 def setup_network(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001181 if self.get('QB_NET') == 'none':
1182 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001183 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001184 self.saved_stty = subprocess.check_output(("stty", "-g")).decode('utf-8').strip()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001185 self.network_device = self.get('QB_NETWORK_DEVICE') or self.network_device
Andrew Geissler82c905d2020-04-13 13:39:40 -05001186 if self.net_bridge:
1187 self.setup_net_bridge()
1188 elif self.slirp_enabled:
1189 self.cmdline_ip_slirp = self.get('QB_CMDLINE_IP_SLIRP') or self.cmdline_ip_slirp
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001190 self.setup_slirp()
1191 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001192 self.cmdline_ip_tap = self.get('QB_CMDLINE_IP_TAP') or self.cmdline_ip_tap
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001193 self.setup_tap()
1194
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001195 def setup_rootfs(self):
1196 if self.get('QB_ROOTFS') == 'none':
1197 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001198 if 'wic.' in self.fstype:
1199 self.fstype = self.fstype[4:]
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001200 rootfs_format = self.fstype if self.fstype in ('vmdk', 'vhd', 'vhdx', 'qcow2', 'vdi') else 'raw'
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001201
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001202 tmpfsdir = os.environ.get("RUNQEMU_TMPFS_DIR", None)
1203 if self.snapshot and tmpfsdir:
1204 newrootfs = os.path.join(tmpfsdir, os.path.basename(self.rootfs)) + "." + str(os.getpid())
Andrew Geissler09036742021-06-25 14:25:14 -05001205 logger.info("Copying rootfs to %s" % newrootfs)
1206 copy_start = time.time()
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001207 shutil.copyfile(self.rootfs, newrootfs)
Andrew Geissler09036742021-06-25 14:25:14 -05001208 logger.info("Copy done in %s seconds" % (time.time() - copy_start))
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001209 self.rootfs = newrootfs
1210 # Don't need a second copy now!
1211 self.snapshot = False
Andrew Geisslerc926e172021-05-07 16:11:35 -05001212 self.cleanup_files.append(newrootfs)
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001213
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001214 qb_rootfs_opt = self.get('QB_ROOTFS_OPT')
1215 if qb_rootfs_opt:
1216 self.rootfs_options = qb_rootfs_opt.replace('@ROOTFS@', self.rootfs)
1217 else:
1218 self.rootfs_options = '-drive file=%s,if=virtio,format=%s' % (self.rootfs, rootfs_format)
1219
Andrew Geissler82c905d2020-04-13 13:39:40 -05001220 qb_rootfs_extra_opt = self.get("QB_ROOTFS_EXTRA_OPT")
1221 if qb_rootfs_extra_opt and not qb_rootfs_extra_opt.startswith(","):
1222 qb_rootfs_extra_opt = "," + qb_rootfs_extra_opt
1223
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001224 if self.fstype in ('cpio.gz', 'cpio'):
1225 self.kernel_cmdline = 'root=/dev/ram0 rw debugshell'
1226 self.rootfs_options = '-initrd %s' % self.rootfs
1227 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001228 vm_drive = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001229 if self.fstype in self.vmtypes:
1230 if self.fstype == 'iso':
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001231 vm_drive = '-drive file=%s,if=virtio,media=cdrom' % self.rootfs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001232 elif self.get('QB_DRIVE_TYPE'):
1233 drive_type = self.get('QB_DRIVE_TYPE')
1234 if drive_type.startswith("/dev/sd"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001235 logger.info('Using scsi drive')
Andrew Geissler82c905d2020-04-13 13:39:40 -05001236 vm_drive = '-drive if=none,id=hd,file=%s,format=%s -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd%s' \
1237 % (self.rootfs, rootfs_format, qb_rootfs_extra_opt)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001238 elif drive_type.startswith("/dev/hd"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001239 logger.info('Using ide drive')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001240 vm_drive = "-drive file=%s,format=%s" % (self.rootfs, rootfs_format)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001241 elif drive_type.startswith("/dev/vdb"):
1242 logger.info('Using block virtio drive');
1243 vm_drive = '-drive id=disk0,file=%s,if=none,format=%s -device virtio-blk-device,drive=disk0%s' \
1244 % (self.rootfs, rootfs_format,qb_rootfs_extra_opt)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001245 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001246 # virtio might have been selected explicitly (just use it), or
1247 # is used as fallback (then warn about that).
1248 if not drive_type.startswith("/dev/vd"):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001249 logger.warning("Unknown QB_DRIVE_TYPE: %s" % drive_type)
1250 logger.warning("Failed to figure out drive type, consider define or fix QB_DRIVE_TYPE")
1251 logger.warning('Trying to use virtio block drive')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001252 vm_drive = '-drive if=virtio,file=%s,format=%s' % (self.rootfs, rootfs_format)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001253
1254 # All branches above set vm_drive.
Andrew Geissler475cb722020-07-10 16:00:51 -05001255 self.rootfs_options = vm_drive
1256 if not self.fstype in self.vmtypes:
1257 self.rootfs_options += ' -no-reboot'
Brad Bishopf3f93bb2019-10-16 14:33:32 -04001258 self.kernel_cmdline = 'root=%s rw' % (self.get('QB_KERNEL_ROOT'))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001259
1260 if self.fstype == 'nfs':
1261 self.rootfs_options = ''
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001262 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 -04001263 self.kernel_cmdline = 'root=%s rw' % k_root
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001264
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001265 if self.fstype == 'none':
1266 self.rootfs_options = ''
1267
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001268 self.set('ROOTFS_OPTIONS', self.rootfs_options)
1269
1270 def guess_qb_system(self):
1271 """attempt to determine the appropriate qemu-system binary"""
1272 mach = self.get('MACHINE')
1273 if not mach:
1274 search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*'
1275 if self.rootfs:
1276 match = re.match(search, self.rootfs)
1277 if match:
1278 mach = match.group(1)
1279 elif self.kernel:
1280 match = re.match(search, self.kernel)
1281 if match:
1282 mach = match.group(1)
1283
1284 if not mach:
1285 return None
1286
1287 if mach == 'qemuarm':
1288 qbsys = 'arm'
1289 elif mach == 'qemuarm64':
1290 qbsys = 'aarch64'
1291 elif mach == 'qemux86':
1292 qbsys = 'i386'
1293 elif mach == 'qemux86-64':
1294 qbsys = 'x86_64'
1295 elif mach == 'qemuppc':
1296 qbsys = 'ppc'
1297 elif mach == 'qemumips':
1298 qbsys = 'mips'
1299 elif mach == 'qemumips64':
1300 qbsys = 'mips64'
1301 elif mach == 'qemumipsel':
1302 qbsys = 'mipsel'
1303 elif mach == 'qemumips64el':
1304 qbsys = 'mips64el'
Brad Bishop316dfdd2018-06-25 12:45:53 -04001305 elif mach == 'qemuriscv64':
1306 qbsys = 'riscv64'
1307 elif mach == 'qemuriscv32':
1308 qbsys = 'riscv32'
Brad Bishop004d4992018-10-02 23:54:45 +02001309 else:
1310 logger.error("Unable to determine QEMU PC System emulator for %s machine." % mach)
1311 logger.error("As %s is not among valid QEMU machines such as," % mach)
1312 logger.error("qemux86-64, qemux86, qemuarm64, qemuarm, qemumips64, qemumips64el, qemumipsel, qemumips, qemuppc")
1313 raise RunQemuError("Set qb_system_name with suitable QEMU PC System emulator in .*qemuboot.conf.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001314
1315 return 'qemu-system-%s' % qbsys
1316
Brad Bishop15ae2502019-06-18 21:44:24 -04001317 def check_qemu_system(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001318 qemu_system = self.get('QB_SYSTEM_NAME')
1319 if not qemu_system:
1320 qemu_system = self.guess_qb_system()
1321 if not qemu_system:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001322 raise RunQemuError("Failed to boot, QB_SYSTEM_NAME is NULL!")
Brad Bishop15ae2502019-06-18 21:44:24 -04001323 self.qemu_system = qemu_system
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001324
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001325 def setup_vga(self):
1326 if self.nographic == True:
1327 if self.sdl == True:
1328 raise RunQemuError('Option nographic makes no sense alongside the sdl option.')
1329 if self.gtk == True:
1330 raise RunQemuError('Option nographic makes no sense alongside the gtk option.')
1331 self.qemu_opt += ' -nographic'
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001332
1333 if self.novga == True:
1334 self.qemu_opt += ' -vga none'
1335 return
1336
1337 if (self.gl_es == True or self.gl == True) and (self.sdl == False and self.gtk == False):
1338 raise RunQemuError('Option gl/gl-es needs gtk or sdl option.')
1339
1340 if self.sdl == True or self.gtk == True or self.egl_headless == True:
1341 self.set_dri_path()
1342 self.qemu_opt += ' -vga virtio -display '
1343 if self.egl_headless == True:
1344 self.qemu_opt += 'egl-headless,'
1345 else:
1346 if self.sdl == True:
1347 self.qemu_opt += 'sdl,'
1348 elif self.gtk == True:
1349 self.qemu_opt += 'gtk,'
1350
1351 if self.gl == True:
1352 self.qemu_opt += 'gl=on,'
1353 elif self.gl_es == True:
1354 self.qemu_opt += 'gl=es,'
1355 self.qemu_opt += 'show-cursor=on'
1356
1357 self.qemu_opt += ' %s' %self.get('QB_GRAPHICS')
1358
1359 def setup_serial(self):
1360 # Setup correct kernel command line for serial
1361 if self.serialstdio == True or self.serialconsole == True or self.nographic == True or self.tcpserial_portnum:
1362 for entry in self.get('SERIAL_CONSOLES').split(' '):
1363 self.kernel_cmdline_script += ' console=%s' %entry.split(';')[1]
1364
1365 if self.serialstdio == True or self.nographic == True:
1366 self.qemu_opt += " -serial mon:stdio"
1367 else:
1368 self.qemu_opt += " -serial mon:vc"
1369 if self.serialconsole:
1370 if sys.stdin.isatty():
1371 subprocess.check_call(("stty", "intr", "^]"))
1372 logger.info("Interrupt character is '^]'")
1373
1374 self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT")
1375
1376 # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES).
1377 # If no serial or serialtcp options were specified, only ttyS0 is created
1378 # and sysvinit shows an error trying to enable ttyS1:
1379 # INIT: Id "S1" respawning too fast: disabled for 5 minutes
1380 serial_num = len(re.findall("-serial", self.qemu_opt))
1381 if serial_num < 2:
1382 self.qemu_opt += " -serial null"
1383
Brad Bishop15ae2502019-06-18 21:44:24 -04001384 def setup_final(self):
1385 qemu_bin = os.path.join(self.bindir_native, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001386
1387 # It is possible to have qemu-native in ASSUME_PROVIDED, and it won't
1388 # find QEMU in sysroot, it needs to use host's qemu.
1389 if not os.path.exists(qemu_bin):
1390 logger.info("QEMU binary not found in %s, trying host's QEMU" % qemu_bin)
1391 for path in (os.environ['PATH'] or '').split(':'):
Brad Bishop15ae2502019-06-18 21:44:24 -04001392 qemu_bin_tmp = os.path.join(path, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001393 logger.info("Trying: %s" % qemu_bin_tmp)
1394 if os.path.exists(qemu_bin_tmp):
1395 qemu_bin = qemu_bin_tmp
1396 if not os.path.isabs(qemu_bin):
1397 qemu_bin = os.path.abspath(qemu_bin)
1398 logger.info("Using host's QEMU: %s" % qemu_bin)
1399 break
1400
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001401 if not os.access(qemu_bin, os.X_OK):
1402 raise OEPathError("No QEMU binary '%s' could be found" % qemu_bin)
1403
Andrew Geisslerc3d88e42020-10-02 09:45:00 -05001404 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 -05001405
1406 for ovmf in self.ovmf_bios:
1407 format = ovmf.rsplit('.', 1)[-1]
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001408 if format == "bin":
1409 format = "raw"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001410 self.qemu_opt += ' -drive if=pflash,format=%s,file=%s' % (format, ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001411
1412 self.qemu_opt += ' ' + self.qemu_opt_script
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001413
Brad Bishop08902b02019-08-20 09:16:51 -04001414 if self.ovmf_secboot_pkkek1:
1415 # Provide the Platform Key and first Key Exchange Key certificate as an
1416 # OEM string in the SMBIOS Type 11 table. Prepend the certificate string
1417 # with "application prefix" of the EnrollDefaultKeys.efi application
1418 self.qemu_opt += ' -smbios type=11,value=4e32566d-8e9e-4f52-81d3-5bb9715f9727:' \
1419 + self.ovmf_secboot_pkkek1
1420
Andrew Geissler99467da2019-02-25 18:54:23 -06001421 # Append qemuparams to override previous settings
1422 if self.qemuparams:
1423 self.qemu_opt += ' ' + self.qemuparams
1424
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001425 if self.snapshot:
1426 self.qemu_opt += " -snapshot"
1427
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001428 self.setup_serial()
1429 self.setup_vga()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001430
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001431 def start_qemu(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001432 import shlex
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001433 if self.kernel:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001434 kernel_opts = "-kernel %s -append '%s %s %s %s'" % (self.kernel, self.kernel_cmdline,
1435 self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'),
1436 self.bootparams)
Brad Bishopc68388fc2019-08-26 01:33:31 -04001437 if self.bios:
1438 kernel_opts += " -bios %s" % self.bios
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001439 if self.dtb:
1440 kernel_opts += " -dtb %s" % self.dtb
1441 else:
1442 kernel_opts = ""
1443 cmd = "%s %s" % (self.qemu_opt, kernel_opts)
Brad Bishop004d4992018-10-02 23:54:45 +02001444 cmds = shlex.split(cmd)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001445 logger.info('Running %s\n' % cmd)
Brad Bishopf86d0552018-12-04 14:18:15 -08001446 pass_fds = []
Brad Bishop08902b02019-08-20 09:16:51 -04001447 if self.taplock_descriptor:
1448 pass_fds = [self.taplock_descriptor.fileno()]
1449 if len(self.portlocks):
1450 for descriptor in self.portlocks.values():
1451 pass_fds.append(descriptor.fileno())
Brad Bishopf86d0552018-12-04 14:18:15 -08001452 process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds)
Brad Bishop004d4992018-10-02 23:54:45 +02001453 self.qemupid = process.pid
1454 retcode = process.wait()
1455 if retcode:
1456 if retcode == -signal.SIGTERM:
1457 logger.info("Qemu terminated by SIGTERM")
1458 else:
1459 logger.error("Failed to run qemu: %s", process.stderr.read().decode())
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001460
1461 def cleanup(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001462 if self.cleaned:
1463 return
1464
1465 # avoid dealing with SIGTERM when cleanup function is running
1466 signal.signal(signal.SIGTERM, signal.SIG_IGN)
1467
1468 logger.info("Cleaning up")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001469 if self.cleantap:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001470 cmd = ('sudo', self.qemuifdown, self.tap, self.bindir_native)
1471 logger.debug('Running %s' % str(cmd))
1472 subprocess.check_call(cmd)
Brad Bishop08902b02019-08-20 09:16:51 -04001473 self.release_taplock()
1474 self.release_portlock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001475
1476 if self.nfs_running:
1477 logger.info("Shutting down the userspace NFS server...")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001478 cmd = ("runqemu-export-rootfs", "stop", self.rootfs)
1479 logger.debug('Running %s' % str(cmd))
1480 subprocess.check_call(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001481
1482 if self.saved_stty:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001483 subprocess.check_call(("stty", self.saved_stty))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001484
Andrew Geisslerc926e172021-05-07 16:11:35 -05001485 if self.cleanup_files:
1486 for ent in self.cleanup_files:
1487 logger.info('Removing %s' % ent)
1488 if os.path.isfile(ent):
1489 os.remove(ent)
1490 else:
1491 shutil.rmtree(ent)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001492
Brad Bishop004d4992018-10-02 23:54:45 +02001493 self.cleaned = True
1494
Andrew Geissler82c905d2020-04-13 13:39:40 -05001495 def run_bitbake_env(self, mach=None):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001496 bitbake = shutil.which('bitbake')
1497 if not bitbake:
1498 return
1499
1500 if not mach:
1501 mach = self.get('MACHINE')
1502
Andrew Geissler82c905d2020-04-13 13:39:40 -05001503 multiconfig = self.get('MULTICONFIG')
1504 if multiconfig:
1505 multiconfig = "mc:%s" % multiconfig
1506
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001507 if mach:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001508 cmd = 'MACHINE=%s bitbake -e %s' % (mach, multiconfig)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001509 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001510 cmd = 'bitbake -e %s' % multiconfig
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001511
1512 logger.info('Running %s...' % cmd)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001513 return subprocess.check_output(cmd, shell=True).decode('utf-8')
1514
1515 def load_bitbake_env(self, mach=None):
1516 if self.bitbake_e:
1517 return
1518
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001519 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001520 self.bitbake_e = self.run_bitbake_env(mach=mach)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001521 except subprocess.CalledProcessError as err:
1522 self.bitbake_e = ''
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001523 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 -06001524
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001525 def validate_combos(self):
1526 if (self.fstype in self.vmtypes) and self.kernel:
1527 raise RunQemuError("%s doesn't need kernel %s!" % (self.fstype, self.kernel))
1528
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001529 @property
1530 def bindir_native(self):
1531 result = self.get('STAGING_BINDIR_NATIVE')
1532 if result and os.path.exists(result):
1533 return result
1534
Andrew Geissler82c905d2020-04-13 13:39:40 -05001535 cmd = ['bitbake', '-e']
1536 multiconfig = self.get('MULTICONFIG')
1537 if multiconfig:
1538 cmd.append('mc:%s:qemu-helper-native' % multiconfig)
1539 else:
1540 cmd.append('qemu-helper-native')
1541
Brad Bishop977dc1a2019-02-06 16:01:43 -05001542 logger.info('Running %s...' % str(cmd))
1543 out = subprocess.check_output(cmd).decode('utf-8')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001544
1545 match = re.search('^STAGING_BINDIR_NATIVE="(.*)"', out, re.M)
1546 if match:
1547 result = match.group(1)
1548 if os.path.exists(result):
1549 self.set('STAGING_BINDIR_NATIVE', result)
1550 return result
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001551 raise RunQemuError("Native sysroot directory %s doesn't exist" % result)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001552 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001553 raise RunQemuError("Can't find STAGING_BINDIR_NATIVE in '%s' output" % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001554
1555
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001556def main():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001557 if "help" in sys.argv or '-h' in sys.argv or '--help' in sys.argv:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001558 print_usage()
1559 return 0
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001560 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001561 config = BaseConfig()
Brad Bishop004d4992018-10-02 23:54:45 +02001562
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001563 renice = os.path.expanduser("~/bin/runqemu-renice")
1564 if os.path.exists(renice):
1565 logger.info('Using %s to renice' % renice)
1566 subprocess.check_call([renice, str(os.getpid())])
1567
Brad Bishop004d4992018-10-02 23:54:45 +02001568 def sigterm_handler(signum, frame):
1569 logger.info("SIGTERM received")
1570 os.kill(config.qemupid, signal.SIGTERM)
1571 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001572 # Deliberately ignore the return code of 'tput smam'.
1573 subprocess.call(["tput", "smam"])
Brad Bishop004d4992018-10-02 23:54:45 +02001574 signal.signal(signal.SIGTERM, sigterm_handler)
1575
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001576 config.check_args()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001577 config.read_qemuboot()
1578 config.check_and_set()
1579 # Check whether the combos is valid or not
1580 config.validate_combos()
1581 config.print_config()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001582 config.setup_network()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001583 config.setup_rootfs()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001584 config.setup_final()
1585 config.start_qemu()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001586 except RunQemuError as err:
1587 logger.error(err)
1588 return 1
1589 except Exception as err:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001590 import traceback
1591 traceback.print_exc()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001592 return 1
1593 finally:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001594 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001595 # Deliberately ignore the return code of 'tput smam'.
1596 subprocess.call(["tput", "smam"])
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001597
1598if __name__ == "__main__":
1599 sys.exit(main())