blob: 54f2336ba97727ecb4855cb083fa9acd1b792986 [file] [log] [blame]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001#!/usr/bin/env python3
2
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003# Handle running OE images standalone with QEMU
4#
5# Copyright (C) 2006-2011 Linux Foundation
Patrick Williamsc0f7c042017-02-23 20:41:17 -06006# Copyright (c) 2016 Wind River Systems, Inc.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05007#
Brad Bishopc342db32019-05-15 21:57:59 -04008# SPDX-License-Identifier: GPL-2.0-only
Patrick Williamsc124f4f2015-09-15 14:41:29 -05009#
Patrick Williamsc124f4f2015-09-15 14:41:29 -050010
Patrick Williamsc0f7c042017-02-23 20:41:17 -060011import os
12import sys
13import logging
14import subprocess
15import re
16import fcntl
17import shutil
18import glob
19import configparser
Brad Bishop004d4992018-10-02 23:54:45 +020020import signal
Andrew Geissler09036742021-06-25 14:25:14 -050021import time
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050022
Brad Bishopd7bf8c12018-02-25 22:55:05 -050023class RunQemuError(Exception):
24 """Custom exception to raise on known errors."""
25 pass
26
27class OEPathError(RunQemuError):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060028 """Custom Exception to give better guidance on missing binaries"""
29 def __init__(self, message):
Brad Bishopd7bf8c12018-02-25 22:55:05 -050030 super().__init__("In order for this script to dynamically infer paths\n \
Patrick Williamsc0f7c042017-02-23 20:41:17 -060031kernels or filesystem images, you either need bitbake in your PATH\n \
32or to source oe-init-build-env before running this script.\n\n \
33Dynamic path inference can be avoided by passing a *.qemuboot.conf to\n \
Brad Bishopd7bf8c12018-02-25 22:55:05 -050034runqemu, i.e. `runqemu /path/to/my-image-name.qemuboot.conf`\n\n %s" % message)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060035
36
37def create_logger():
38 logger = logging.getLogger('runqemu')
39 logger.setLevel(logging.INFO)
40
41 # create console handler and set level to debug
42 ch = logging.StreamHandler()
Brad Bishopd7bf8c12018-02-25 22:55:05 -050043 ch.setLevel(logging.DEBUG)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060044
45 # create formatter
46 formatter = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
47
48 # add formatter to ch
49 ch.setFormatter(formatter)
50
51 # add ch to logger
52 logger.addHandler(ch)
53
54 return logger
55
56logger = create_logger()
57
58def print_usage():
59 print("""
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050060Usage: you can run this script with any valid combination
61of the following environment variables (in any order):
62 KERNEL - the kernel image file to use
Brad Bishopc68388fc2019-08-26 01:33:31 -040063 BIOS - the bios image file to use
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050064 ROOTFS - the rootfs image file or nfsroot directory to use
Brad Bishop316dfdd2018-06-25 12:45:53 -040065 DEVICE_TREE - the device tree blob to use
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050066 MACHINE - the machine name (optional, autodetected from KERNEL filename if unspecified)
67 Simplified QEMU command-line options can be passed with:
Patrick Williamsc0f7c042017-02-23 20:41:17 -060068 nographic - disable video console
Andrew Geissler90fd73c2021-03-05 15:25:55 -060069 novga - Disable VGA emulation completely
Brad Bishopa34c0302019-09-23 22:34:48 -040070 sdl - choose the SDL UI frontend
71 gtk - choose the Gtk UI frontend
Brad Bishop6dbb3162019-11-25 09:41:34 -050072 gl - enable virgl-based GL acceleration (also needs gtk or sdl options)
73 gl-es - enable virgl-based GL acceleration, using OpenGL ES (also needs gtk or sdl options)
74 egl-headless - enable headless EGL output; use vnc (via publicvnc option) or spice to see it
Andrew Geisslerd159c7f2021-09-02 21:05:58 -050075 (hint: if /dev/dri/renderD* is absent due to lack of suitable GPU, 'modprobe vgem' will create
76 one sutable for mesa llvmpipe sofware renderer)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060077 serial - enable a serial console on /dev/ttyS0
Brad Bishop19323692019-04-05 15:28:33 -040078 serialstdio - enable a serial console on the console (regardless of graphics mode)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060079 slirp - enable user networking, no root privileges is required
Brad Bishopa34c0302019-09-23 22:34:48 -040080 snapshot - don't write changes to back to images
Patrick Williamsc0f7c042017-02-23 20:41:17 -060081 kvm - enable KVM when running x86/x86_64 (VT-capable CPU required)
82 kvm-vhost - enable KVM with vhost when running x86/x86_64 (VT-capable CPU required)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050083 publicvnc - enable a VNC server open to all hosts
Patrick Williamsc0f7c042017-02-23 20:41:17 -060084 audio - enable audio
Brad Bishop6e60e8b2018-02-01 10:27:11 -050085 [*/]ovmf* - OVMF firmware file or base name for booting with UEFI
Patrick Williamsc0f7c042017-02-23 20:41:17 -060086 tcpserial=<port> - specify tcp serial port number
Patrick Williamsc0f7c042017-02-23 20:41:17 -060087 qemuparams=<xyz> - specify custom parameters to QEMU
88 bootparams=<xyz> - specify custom kernel parameters during boot
Brad Bishop6e60e8b2018-02-01 10:27:11 -050089 help, -h, --help: print this text
Brad Bishopd7bf8c12018-02-25 22:55:05 -050090 -d, --debug: Enable debug output
Brad Bishop79641f22019-09-10 07:20:22 -040091 -q, --quiet: Hide most output except error messages
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050092
93Examples:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050094 runqemu
Patrick Williamsc0f7c042017-02-23 20:41:17 -060095 runqemu qemuarm
96 runqemu tmp/deploy/images/qemuarm
Brad Bishop6e60e8b2018-02-01 10:27:11 -050097 runqemu tmp/deploy/images/qemux86/<qemuboot.conf>
Patrick Williamsc0f7c042017-02-23 20:41:17 -060098 runqemu qemux86-64 core-image-sato ext4
99 runqemu qemux86-64 wic-image-minimal wic
100 runqemu path/to/bzImage-qemux86.bin path/to/nfsrootdir/ serial
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600101 runqemu qemux86 iso/hddimg/wic.vmdk/wic.vhd/wic.vhdx/wic.qcow2/wic.vdi/ramfs/cpio.gz...
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600102 runqemu qemux86 qemuparams="-m 256"
103 runqemu qemux86 bootparams="psplash=false"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600104 runqemu path/to/<image>-<machine>.wic
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500105 runqemu path/to/<image>-<machine>.wic.vmdk
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600106 runqemu path/to/<image>-<machine>.wic.vhdx
107 runqemu path/to/<image>-<machine>.wic.vhd
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600108""")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500109
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600110def check_tun():
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500111 """Check /dev/net/tun"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600112 dev_tun = '/dev/net/tun'
113 if not os.path.exists(dev_tun):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500114 raise RunQemuError("TUN control device %s is unavailable; you may need to enable TUN (e.g. sudo modprobe tun)" % dev_tun)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500115
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600116 if not os.access(dev_tun, os.W_OK):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500117 raise RunQemuError("TUN control device %s is not writable, please fix (e.g. sudo chmod 666 %s)" % (dev_tun, dev_tun))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500118
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600119def get_first_file(cmds):
120 """Return first file found in wildcard cmds"""
121 for cmd in cmds:
122 all_files = glob.glob(cmd)
123 if all_files:
124 for f in all_files:
125 if not os.path.isdir(f):
126 return f
127 return ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500128
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600129class BaseConfig(object):
130 def __init__(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500131 # The self.d saved vars from self.set(), part of them are from qemuboot.conf
132 self.d = {'QB_KERNEL_ROOT': '/dev/vda'}
133
134 # Supported env vars, add it here if a var can be got from env,
135 # and don't use os.getenv in the code.
136 self.env_vars = ('MACHINE',
137 'ROOTFS',
138 'KERNEL',
Brad Bishopc68388fc2019-08-26 01:33:31 -0400139 'BIOS',
Brad Bishop316dfdd2018-06-25 12:45:53 -0400140 'DEVICE_TREE',
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500141 'DEPLOY_DIR_IMAGE',
142 'OE_TMPDIR',
143 'OECORE_NATIVE_SYSROOT',
Andrew Geissler82c905d2020-04-13 13:39:40 -0500144 'MULTICONFIG',
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500145 'SERIAL_CONSOLES',
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500146 )
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500147
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600148 self.qemu_opt = ''
149 self.qemu_opt_script = ''
Andrew Geissler99467da2019-02-25 18:54:23 -0600150 self.qemuparams = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600151 self.nfs_server = ''
152 self.rootfs = ''
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500153 # File name(s) of a OVMF firmware file or variable store,
154 # to be added with -drive if=pflash.
155 # Found in the same places as the rootfs, with or without one of
156 # these suffices: qcow2, bin.
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500157 self.ovmf_bios = []
Brad Bishop08902b02019-08-20 09:16:51 -0400158 # When enrolling default Secure Boot keys, the hypervisor
159 # must provide the Platform Key and the first Key Exchange Key
160 # certificate in the Type 11 SMBIOS table.
161 self.ovmf_secboot_pkkek1 = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600162 self.qemuboot = ''
163 self.qbconfload = False
164 self.kernel = ''
Brad Bishopc68388fc2019-08-26 01:33:31 -0400165 self.bios = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600166 self.kernel_cmdline = ''
167 self.kernel_cmdline_script = ''
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500168 self.bootparams = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600169 self.dtb = ''
170 self.fstype = ''
171 self.kvm_enabled = False
172 self.vhost_enabled = False
173 self.slirp_enabled = False
Andrew Geissler82c905d2020-04-13 13:39:40 -0500174 self.net_bridge = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600175 self.nfs_instance = 0
176 self.nfs_running = False
Brad Bishop19323692019-04-05 15:28:33 -0400177 self.serialconsole = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600178 self.serialstdio = False
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500179 self.nographic = False
180 self.sdl = False
181 self.gtk = False
182 self.gl = False
183 self.gl_es = False
184 self.egl_headless = False
185 self.novga = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600186 self.cleantap = False
187 self.saved_stty = ''
188 self.audio_enabled = False
189 self.tcpserial_portnum = ''
Brad Bishop08902b02019-08-20 09:16:51 -0400190 self.taplock = ''
191 self.taplock_descriptor = None
192 self.portlocks = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600193 self.bitbake_e = ''
194 self.snapshot = False
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600195 self.wictypes = ('wic', 'wic.vmdk', 'wic.qcow2', 'wic.vdi', "wic.vhd", "wic.vhdx")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500196 self.fstypes = ('ext2', 'ext3', 'ext4', 'jffs2', 'nfs', 'btrfs',
197 'cpio.gz', 'cpio', 'ramfs', 'tar.bz2', 'tar.gz')
Brad Bishop08902b02019-08-20 09:16:51 -0400198 self.vmtypes = ('hddimg', 'iso')
Brad Bishop15ae2502019-06-18 21:44:24 -0400199 self.fsinfo = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500200 self.network_device = "-device e1000,netdev=net0,mac=@MAC@"
Andrew Geissler82c905d2020-04-13 13:39:40 -0500201 self.cmdline_ip_slirp = "ip=dhcp"
202 self.cmdline_ip_tap = "ip=192.168.7.@CLIENT@::192.168.7.@GATEWAY@:255.255.255.0"
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500203 # Use different mac section for tap and slirp to avoid
204 # conflicts, e.g., when one is running with tap, the other is
205 # running with slirp.
206 # The last section is dynamic, which is for avoiding conflicts,
207 # when multiple qemus are running, e.g., when multiple tap or
208 # slirp qemus are running.
209 self.mac_tap = "52:54:00:12:34:"
210 self.mac_slirp = "52:54:00:12:35:"
Brad Bishop004d4992018-10-02 23:54:45 +0200211 # pid of the actual qemu process
212 self.qemupid = None
213 # avoid cleanup twice
214 self.cleaned = False
Andrew Geisslerc926e172021-05-07 16:11:35 -0500215 # Files to cleanup after run
216 self.cleanup_files = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500217
Brad Bishop08902b02019-08-20 09:16:51 -0400218 def acquire_taplock(self, error=True):
219 logger.debug("Acquiring lockfile %s..." % self.taplock)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600220 try:
Brad Bishop08902b02019-08-20 09:16:51 -0400221 self.taplock_descriptor = open(self.taplock, 'w')
222 fcntl.flock(self.taplock_descriptor, fcntl.LOCK_EX|fcntl.LOCK_NB)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600223 except Exception as e:
Brad Bishop08902b02019-08-20 09:16:51 -0400224 msg = "Acquiring lockfile %s failed: %s" % (self.taplock, e)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500225 if error:
226 logger.error(msg)
227 else:
228 logger.info(msg)
Brad Bishop08902b02019-08-20 09:16:51 -0400229 if self.taplock_descriptor:
230 self.taplock_descriptor.close()
231 self.taplock_descriptor = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600232 return False
233 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500234
Brad Bishop08902b02019-08-20 09:16:51 -0400235 def release_taplock(self):
236 if self.taplock_descriptor:
Brad Bishopf86d0552018-12-04 14:18:15 -0800237 logger.debug("Releasing lockfile for tap device '%s'" % self.tap)
Andrew Geissler5f350902021-07-23 13:09:54 -0400238 # We pass the fd to the qemu process and if we unlock here, it would unlock for
239 # that too. Therefore don't unlock, just close
240 # fcntl.flock(self.taplock_descriptor, fcntl.LOCK_UN)
Brad Bishop08902b02019-08-20 09:16:51 -0400241 self.taplock_descriptor.close()
Andrew Geissler5f350902021-07-23 13:09:54 -0400242 # Removing the file is a potential race, don't do that either
243 # os.remove(self.taplock)
Brad Bishop08902b02019-08-20 09:16:51 -0400244 self.taplock_descriptor = None
245
246 def check_free_port(self, host, port, lockdir):
247 """ Check whether the port is free or not """
248 import socket
249 from contextlib import closing
250
251 lockfile = os.path.join(lockdir, str(port) + '.lock')
252 if self.acquire_portlock(lockfile):
253 with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
254 if sock.connect_ex((host, port)) == 0:
255 # Port is open, so not free
256 self.release_portlock(lockfile)
257 return False
258 else:
259 # Port is not open, so free
260 return True
261 else:
262 return False
263
264 def acquire_portlock(self, lockfile):
265 logger.debug("Acquiring lockfile %s..." % lockfile)
266 try:
267 portlock_descriptor = open(lockfile, 'w')
268 self.portlocks.update({lockfile: portlock_descriptor})
269 fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_EX|fcntl.LOCK_NB)
270 except Exception as e:
271 msg = "Acquiring lockfile %s failed: %s" % (lockfile, e)
272 logger.info(msg)
273 if lockfile in self.portlocks.keys() and self.portlocks[lockfile]:
274 self.portlocks[lockfile].close()
275 del self.portlocks[lockfile]
276 return False
277 return True
278
279 def release_portlock(self, lockfile=None):
280 if lockfile != None:
Andrew Geissler5f350902021-07-23 13:09:54 -0400281 logger.debug("Releasing lockfile '%s'" % lockfile)
282 # We pass the fd to the qemu process and if we unlock here, it would unlock for
283 # that too. Therefore don't unlock, just close
284 # fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_UN)
285 self.portlocks[lockfile].close()
286 # Removing the file is a potential race, don't do that either
287 # os.remove(lockfile)
288 del self.portlocks[lockfile]
Brad Bishop08902b02019-08-20 09:16:51 -0400289 elif len(self.portlocks):
290 for lockfile, descriptor in self.portlocks.items():
291 logger.debug("Releasing lockfile '%s'" % lockfile)
Andrew Geissler5f350902021-07-23 13:09:54 -0400292 # We pass the fd to the qemu process and if we unlock here, it would unlock for
293 # that too. Therefore don't unlock, just close
294 # fcntl.flock(descriptor, fcntl.LOCK_UN)
Brad Bishop08902b02019-08-20 09:16:51 -0400295 descriptor.close()
Andrew Geissler5f350902021-07-23 13:09:54 -0400296 # Removing the file is a potential race, don't do that either
297 # os.remove(lockfile)
Brad Bishop08902b02019-08-20 09:16:51 -0400298 self.portlocks = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500299
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600300 def get(self, key):
301 if key in self.d:
302 return self.d.get(key)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500303 elif os.getenv(key):
304 return os.getenv(key)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600305 else:
306 return ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500307
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600308 def set(self, key, value):
309 self.d[key] = value
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500310
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600311 def is_deploy_dir_image(self, p):
312 if os.path.isdir(p):
313 if not re.search('.qemuboot.conf$', '\n'.join(os.listdir(p)), re.M):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500314 logger.debug("Can't find required *.qemuboot.conf in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600315 return False
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500316 if not any(map(lambda name: '-image-' in name, os.listdir(p))):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500317 logger.debug("Can't find *-image-* in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600318 return False
319 return True
320 else:
321 return False
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500322
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600323 def check_arg_fstype(self, fst):
324 """Check and set FSTYPE"""
Brad Bishop15ae2502019-06-18 21:44:24 -0400325 if fst not in self.fstypes + self.vmtypes + self.wictypes:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800326 logger.warning("Maybe unsupported FSTYPE: %s" % fst)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600327 if not self.fstype or self.fstype == fst:
328 if fst == 'ramfs':
329 fst = 'cpio.gz'
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500330 if fst in ('tar.bz2', 'tar.gz'):
331 fst = 'nfs'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600332 self.fstype = fst
333 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500334 raise RunQemuError("Conflicting: FSTYPE %s and %s" % (self.fstype, fst))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500335
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600336 def set_machine_deploy_dir(self, machine, deploy_dir_image):
337 """Set MACHINE and DEPLOY_DIR_IMAGE"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500338 logger.debug('MACHINE: %s' % machine)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600339 self.set("MACHINE", machine)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500340 logger.debug('DEPLOY_DIR_IMAGE: %s' % deploy_dir_image)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600341 self.set("DEPLOY_DIR_IMAGE", deploy_dir_image)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500342
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600343 def check_arg_nfs(self, p):
344 if os.path.isdir(p):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500345 self.rootfs = p
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600346 else:
347 m = re.match('(.*):(.*)', p)
348 self.nfs_server = m.group(1)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500349 self.rootfs = m.group(2)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600350 self.check_arg_fstype('nfs')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500351
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600352 def check_arg_path(self, p):
353 """
354 - Check whether it is <image>.qemuboot.conf or contains <image>.qemuboot.conf
355 - Check whether is a kernel file
356 - Check whether is a image file
357 - Check whether it is a nfs dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500358 - Check whether it is a OVMF flash file
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600359 """
360 if p.endswith('.qemuboot.conf'):
361 self.qemuboot = p
362 self.qbconfload = True
363 elif re.search('\.bin$', p) or re.search('bzImage', p) or \
364 re.search('zImage', p) or re.search('vmlinux', p) or \
365 re.search('fitImage', p) or re.search('uImage', p):
366 self.kernel = p
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500367 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 -0600368 self.rootfs = p
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500369 # Check filename against self.fstypes can hanlde <file>.cpio.gz,
370 # otherwise, its type would be "gz", which is incorrect.
371 fst = ""
372 for t in self.fstypes:
373 if p.endswith(t):
374 fst = t
375 break
376 if not fst:
377 m = re.search('.*\.(.*)$', self.rootfs)
378 if m:
379 fst = m.group(1)
380 if fst:
381 self.check_arg_fstype(fst)
382 qb = re.sub('\.' + fst + "$", '', self.rootfs)
383 qb = '%s%s' % (re.sub('\.rootfs$', '', qb), '.qemuboot.conf')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600384 if os.path.exists(qb):
385 self.qemuboot = qb
386 self.qbconfload = True
387 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800388 logger.warning("%s doesn't exist" % qb)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600389 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500390 raise RunQemuError("Can't find FSTYPE from: %s" % p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500391
392 elif os.path.isdir(p) or re.search(':', p) and re.search('/', p):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600393 if self.is_deploy_dir_image(p):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500394 logger.debug('DEPLOY_DIR_IMAGE: %s' % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600395 self.set("DEPLOY_DIR_IMAGE", p)
396 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500397 logger.debug("Assuming %s is an nfs rootfs" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600398 self.check_arg_nfs(p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500399 elif os.path.basename(p).startswith('ovmf'):
400 self.ovmf_bios.append(p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600401 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500402 raise RunQemuError("Unknown path arg %s" % p)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500403
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600404 def check_arg_machine(self, arg):
405 """Check whether it is a machine"""
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500406 if self.get('MACHINE') == arg:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600407 return
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500408 elif self.get('MACHINE') and self.get('MACHINE') != arg:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500409 raise RunQemuError("Maybe conflicted MACHINE: %s vs %s" % (self.get('MACHINE'), arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500410 elif re.search('/', arg):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500411 raise RunQemuError("Unknown arg: %s" % arg)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500412
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500413 logger.debug('Assuming MACHINE = %s' % arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500414
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600415 # if we're running under testimage, or similarly as a child
416 # of an existing bitbake invocation, we can't invoke bitbake
417 # to validate the MACHINE setting and must assume it's correct...
418 # FIXME: testimage.bbclass exports these two variables into env,
419 # are there other scenarios in which we need to support being
420 # invoked by bitbake?
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500421 deploy = self.get('DEPLOY_DIR_IMAGE')
422 bbchild = deploy and self.get('OE_TMPDIR')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600423 if bbchild:
424 self.set_machine_deploy_dir(arg, deploy)
425 return
426 # also check whether we're running under a sourced toolchain
427 # environment file
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500428 if self.get('OECORE_NATIVE_SYSROOT'):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600429 self.set("MACHINE", arg)
430 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500431
Andrew Geissler82c905d2020-04-13 13:39:40 -0500432 self.bitbake_e = self.run_bitbake_env(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600433 # bitbake -e doesn't report invalid MACHINE as an error, so
434 # let's check DEPLOY_DIR_IMAGE to make sure that it is a valid
435 # MACHINE.
436 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
437 if s:
438 deploy_dir_image = s.group(1)
439 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500440 raise RunQemuError("bitbake -e %s" % self.bitbake_e)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600441 if self.is_deploy_dir_image(deploy_dir_image):
442 self.set_machine_deploy_dir(arg, deploy_dir_image)
443 else:
444 logger.error("%s not a directory valid DEPLOY_DIR_IMAGE" % deploy_dir_image)
445 self.set("MACHINE", arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500446
Andrew Geisslerc182c622020-05-15 14:13:32 -0500447 def set_dri_path(self):
448 # As runqemu can be run within bitbake (when using testimage, for example),
449 # we need to ensure that we run host pkg-config, and that it does not
450 # get mis-directed to native build paths set by bitbake.
451 try:
452 del os.environ['PKG_CONFIG_PATH']
453 del os.environ['PKG_CONFIG_DIR']
454 del os.environ['PKG_CONFIG_LIBDIR']
455 del os.environ['PKG_CONFIG_SYSROOT_DIR']
456 except KeyError:
457 pass
458 try:
459 dripath = subprocess.check_output("PATH=/bin:/usr/bin:$PATH pkg-config --variable=dridriverdir dri", shell=True)
460 except subprocess.CalledProcessError as e:
461 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.")
462 os.environ['LIBGL_DRIVERS_PATH'] = dripath.decode('utf-8').strip()
463
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600464 def check_args(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500465 for debug in ("-d", "--debug"):
466 if debug in sys.argv:
467 logger.setLevel(logging.DEBUG)
468 sys.argv.remove(debug)
469
470 for quiet in ("-q", "--quiet"):
471 if quiet in sys.argv:
472 logger.setLevel(logging.ERROR)
473 sys.argv.remove(quiet)
474
Andrew Geisslerc182c622020-05-15 14:13:32 -0500475 if 'gl' not in sys.argv[1:] and 'gl-es' not in sys.argv[1:]:
476 os.environ['SDL_RENDER_DRIVER'] = 'software'
477
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600478 unknown_arg = ""
479 for arg in sys.argv[1:]:
Brad Bishop15ae2502019-06-18 21:44:24 -0400480 if arg in self.fstypes + self.vmtypes + self.wictypes:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600481 self.check_arg_fstype(arg)
482 elif arg == 'nographic':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500483 self.nographic = True
Brad Bishop19323692019-04-05 15:28:33 -0400484 elif arg == 'sdl':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500485 self.sdl = True
Brad Bishopa34c0302019-09-23 22:34:48 -0400486 elif arg == 'gtk':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500487 self.gtk = True
488 elif arg == 'gl':
489 self.gl = True
490 elif 'gl-es' in sys.argv[1:]:
491 self.gl_es = True
Brad Bishop19323692019-04-05 15:28:33 -0400492 elif arg == 'egl-headless':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500493 self.egl_headless = True
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600494 elif arg == 'novga':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500495 self.novga = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600496 elif arg == 'serial':
Brad Bishop19323692019-04-05 15:28:33 -0400497 self.serialconsole = True
498 elif arg == "serialstdio":
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600499 self.serialstdio = True
500 elif arg == 'audio':
501 logger.info("Enabling audio in qemu")
502 logger.info("Please install sound drivers in linux host")
503 self.audio_enabled = True
504 elif arg == 'kvm':
505 self.kvm_enabled = True
506 elif arg == 'kvm-vhost':
507 self.vhost_enabled = True
508 elif arg == 'slirp':
509 self.slirp_enabled = True
Andrew Geissler82c905d2020-04-13 13:39:40 -0500510 elif arg.startswith('bridge='):
511 self.net_bridge = '%s' % arg[len('bridge='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600512 elif arg == 'snapshot':
513 self.snapshot = True
514 elif arg == 'publicvnc':
515 self.qemu_opt_script += ' -vnc :0'
516 elif arg.startswith('tcpserial='):
Brad Bishop15ae2502019-06-18 21:44:24 -0400517 self.tcpserial_portnum = '%s' % arg[len('tcpserial='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600518 elif arg.startswith('qemuparams='):
Andrew Geissler99467da2019-02-25 18:54:23 -0600519 self.qemuparams = ' %s' % arg[len('qemuparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600520 elif arg.startswith('bootparams='):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500521 self.bootparams = arg[len('bootparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600522 elif os.path.exists(arg) or (re.search(':', arg) and re.search('/', arg)):
523 self.check_arg_path(os.path.abspath(arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500524 elif re.search(r'-image-|-image$', arg):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600525 # Lazy rootfs
526 self.rootfs = arg
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500527 elif arg.startswith('ovmf'):
528 self.ovmf_bios.append(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600529 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500530 # At last, assume it is the MACHINE
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600531 if (not unknown_arg) or unknown_arg == arg:
532 unknown_arg = arg
533 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500534 raise RunQemuError("Can't handle two unknown args: %s %s\n"
535 "Try 'runqemu help' on how to use it" % \
536 (unknown_arg, arg))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600537 # Check to make sure it is a valid machine
Brad Bishopa5c52ff2018-11-23 10:55:50 +1300538 if unknown_arg and self.get('MACHINE') != unknown_arg:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500539 if self.get('DEPLOY_DIR_IMAGE'):
540 machine = os.path.basename(self.get('DEPLOY_DIR_IMAGE'))
541 if unknown_arg == machine:
542 self.set("MACHINE", machine)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500543
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600544 self.check_arg_machine(unknown_arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500545
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500546 if not (self.get('DEPLOY_DIR_IMAGE') or self.qbconfload):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500547 self.load_bitbake_env()
548 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
549 if s:
550 self.set("DEPLOY_DIR_IMAGE", s.group(1))
551
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600552 def check_kvm(self):
553 """Check kvm and kvm-host"""
554 if not (self.kvm_enabled or self.vhost_enabled):
William A. Kennington IIIac69b482021-06-02 12:28:27 -0700555 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 -0600556 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500557
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600558 if not self.get('QB_CPU_KVM'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500559 raise RunQemuError("QB_CPU_KVM is NULL, this board doesn't support kvm")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500560
William A. Kennington IIIac69b482021-06-02 12:28:27 -0700561 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 -0600562 yocto_kvm_wiki = "https://wiki.yoctoproject.org/wiki/How_to_enable_KVM_for_Poky_qemu"
563 yocto_paravirt_kvm_wiki = "https://wiki.yoctoproject.org/wiki/Running_an_x86_Yocto_Linux_image_under_QEMU_KVM"
564 dev_kvm = '/dev/kvm'
565 dev_vhost = '/dev/vhost-net'
Brad Bishop15ae2502019-06-18 21:44:24 -0400566 if self.qemu_system.endswith(('i386', 'x86_64')):
567 with open('/proc/cpuinfo', 'r') as f:
568 kvm_cap = re.search('vmx|svm', "".join(f.readlines()))
569 if not kvm_cap:
570 logger.error("You are trying to enable KVM on a cpu without VT support.")
571 logger.error("Remove kvm from the command-line, or refer:")
572 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500573
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600574 if not os.path.exists(dev_kvm):
575 logger.error("Missing KVM device. Have you inserted kvm modules?")
576 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500577 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500578
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600579 if os.access(dev_kvm, os.W_OK|os.R_OK):
580 self.qemu_opt_script += ' -enable-kvm'
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500581 if self.get('MACHINE') == "qemux86":
582 # Workaround for broken APIC window on pre 4.15 host kernels which causes boot hangs
583 # See YOCTO #12301
584 # On 64 bit we use x2apic
585 self.kernel_cmdline_script += " clocksource=kvm-clock hpet=disable noapic nolapic"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600586 else:
587 logger.error("You have no read or write permission on /dev/kvm.")
588 logger.error("Please change the ownership of this file as described at:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500589 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500590
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600591 if self.vhost_enabled:
592 if not os.path.exists(dev_vhost):
593 logger.error("Missing virtio net device. Have you inserted vhost-net module?")
594 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500595 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500596
Andrew Geissler635e0e42020-08-21 15:58:33 -0500597 if not os.access(dev_vhost, os.W_OK|os.R_OK):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600598 logger.error("You have no read or write permission on /dev/vhost-net.")
599 logger.error("Please change the ownership of this file as described at:")
Andrew Geissler635e0e42020-08-21 15:58:33 -0500600 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500601
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600602 def check_fstype(self):
603 """Check and setup FSTYPE"""
604 if not self.fstype:
605 fstype = self.get('QB_DEFAULT_FSTYPE')
606 if fstype:
607 self.fstype = fstype
608 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500609 raise RunQemuError("FSTYPE is NULL!")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500610
Brad Bishop15ae2502019-06-18 21:44:24 -0400611 # parse QB_FSINFO into dict, e.g. { 'wic': ['no-kernel-in-fs', 'a-flag'], 'ext4': ['another-flag']}
612 wic_fs = False
613 qb_fsinfo = self.get('QB_FSINFO')
614 if qb_fsinfo:
615 qb_fsinfo = qb_fsinfo.split()
616 for fsinfo in qb_fsinfo:
617 try:
618 fstype, fsflag = fsinfo.split(':')
619
620 if fstype == 'wic':
621 if fsflag == 'no-kernel-in-fs':
622 wic_fs = True
623 elif fsflag == 'kernel-in-fs':
624 wic_fs = False
625 else:
626 logger.warn('Unknown flag "%s:%s" in QB_FSINFO', fstype, fsflag)
627 continue
628 else:
629 logger.warn('QB_FSINFO is not supported for image type "%s"', fstype)
630 continue
631
632 if fstype in self.fsinfo:
633 self.fsinfo[fstype].append(fsflag)
634 else:
635 self.fsinfo[fstype] = [fsflag]
636 except Exception:
637 logger.error('Invalid parameter "%s" in QB_FSINFO', fsinfo)
638
639 # treat wic images as vmimages (with kernel) or as fsimages (rootfs only)
640 if wic_fs:
641 self.fstypes = self.fstypes + self.wictypes
642 else:
643 self.vmtypes = self.vmtypes + self.wictypes
644
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600645 def check_rootfs(self):
646 """Check and set rootfs"""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500647
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500648 if self.fstype == "none":
649 return
650
651 if self.get('ROOTFS'):
652 if not self.rootfs:
653 self.rootfs = self.get('ROOTFS')
654 elif self.get('ROOTFS') != self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500655 raise RunQemuError("Maybe conflicted ROOTFS: %s vs %s" % (self.get('ROOTFS'), self.rootfs))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500656
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600657 if self.fstype == 'nfs':
658 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500659
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600660 if self.rootfs and not os.path.exists(self.rootfs):
661 # Lazy rootfs
662 self.rootfs = "%s/%s-%s.%s" % (self.get('DEPLOY_DIR_IMAGE'),
663 self.rootfs, self.get('MACHINE'),
664 self.fstype)
665 elif not self.rootfs:
666 cmd_name = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_NAME'), self.fstype)
667 cmd_link = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'), self.fstype)
668 cmds = (cmd_name, cmd_link)
669 self.rootfs = get_first_file(cmds)
670 if not self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500671 raise RunQemuError("Failed to find rootfs: %s or %s" % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500672
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600673 if not os.path.exists(self.rootfs):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500674 raise RunQemuError("Can't find rootfs: %s" % self.rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500675
Brad Bishop08902b02019-08-20 09:16:51 -0400676 def setup_pkkek1(self):
677 """
678 Extract from PEM certificate the Platform Key and first Key
679 Exchange Key certificate string. The hypervisor needs to provide
680 it in the Type 11 SMBIOS table
681 """
682 pemcert = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), 'OvmfPkKek1.pem')
683 try:
684 with open(pemcert, 'r') as pemfile:
685 key = pemfile.read().replace('\n', ''). \
686 replace('-----BEGIN CERTIFICATE-----', ''). \
687 replace('-----END CERTIFICATE-----', '')
688 self.ovmf_secboot_pkkek1 = key
689
690 except FileNotFoundError:
691 raise RunQemuError("Can't open PEM certificate %s " % pemcert)
692
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500693 def check_ovmf(self):
694 """Check and set full path for OVMF firmware and variable file(s)."""
695
696 for index, ovmf in enumerate(self.ovmf_bios):
697 if os.path.exists(ovmf):
698 continue
699 for suffix in ('qcow2', 'bin'):
700 path = '%s/%s.%s' % (self.get('DEPLOY_DIR_IMAGE'), ovmf, suffix)
701 if os.path.exists(path):
702 self.ovmf_bios[index] = path
Brad Bishop08902b02019-08-20 09:16:51 -0400703 if ovmf.endswith('secboot'):
704 self.setup_pkkek1()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500705 break
706 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500707 raise RunQemuError("Can't find OVMF firmware: %s" % ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500708
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600709 def check_kernel(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400710 """Check and set kernel"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600711 # The vm image doesn't need a kernel
712 if self.fstype in self.vmtypes:
713 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500714
Brad Bishop316dfdd2018-06-25 12:45:53 -0400715 # See if the user supplied a KERNEL option
716 if self.get('KERNEL'):
717 self.kernel = self.get('KERNEL')
718
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500719 # QB_DEFAULT_KERNEL is always a full file path
720 kernel_name = os.path.basename(self.get('QB_DEFAULT_KERNEL'))
721
722 # The user didn't want a kernel to be loaded
Brad Bishop316dfdd2018-06-25 12:45:53 -0400723 if kernel_name == "none" and not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500724 return
725
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600726 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
727 if not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500728 kernel_match_name = "%s/%s" % (deploy_dir_image, kernel_name)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600729 kernel_match_link = "%s/%s" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
730 kernel_startswith = "%s/%s*" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
731 cmds = (kernel_match_name, kernel_match_link, kernel_startswith)
732 self.kernel = get_first_file(cmds)
733 if not self.kernel:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500734 raise RunQemuError('KERNEL not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500735
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600736 if not os.path.exists(self.kernel):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500737 raise RunQemuError("KERNEL %s not found" % self.kernel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500738
Brad Bishop316dfdd2018-06-25 12:45:53 -0400739 def check_dtb(self):
740 """Check and set dtb"""
741 # Did the user specify a device tree?
742 if self.get('DEVICE_TREE'):
743 self.dtb = self.get('DEVICE_TREE')
744 if not os.path.exists(self.dtb):
745 raise RunQemuError('Specified DTB not found: %s' % self.dtb)
746 return
747
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600748 dtb = self.get('QB_DTB')
749 if dtb:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400750 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600751 cmd_match = "%s/%s" % (deploy_dir_image, dtb)
752 cmd_startswith = "%s/%s*" % (deploy_dir_image, dtb)
753 cmd_wild = "%s/*.dtb" % deploy_dir_image
754 cmds = (cmd_match, cmd_startswith, cmd_wild)
755 self.dtb = get_first_file(cmds)
756 if not os.path.exists(self.dtb):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500757 raise RunQemuError('DTB not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500758
Brad Bishopc68388fc2019-08-26 01:33:31 -0400759 def check_bios(self):
760 """Check and set bios"""
761
762 # See if the user supplied a BIOS option
763 if self.get('BIOS'):
764 self.bios = self.get('BIOS')
765
766 # QB_DEFAULT_BIOS is always a full file path
767 bios_name = os.path.basename(self.get('QB_DEFAULT_BIOS'))
768
769 # The user didn't want a bios to be loaded
770 if (bios_name == "" or bios_name == "none") and not self.bios:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600771 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500772
Brad Bishopc68388fc2019-08-26 01:33:31 -0400773 if not self.bios:
774 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
775 self.bios = "%s/%s" % (deploy_dir_image, bios_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500776
Brad Bishopc68388fc2019-08-26 01:33:31 -0400777 if not self.bios:
778 raise RunQemuError('BIOS not found: %s' % bios_match_name)
779
780 if not os.path.exists(self.bios):
Patrick Williams213cb262021-08-07 19:21:33 -0500781 raise RunQemuError("BIOS %s not found" % self.bios)
Brad Bishopc68388fc2019-08-26 01:33:31 -0400782
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500783
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600784 def check_mem(self):
Andrew Geissler99467da2019-02-25 18:54:23 -0600785 """
786 Both qemu and kernel needs memory settings, so check QB_MEM and set it
787 for both.
788 """
789 s = re.search('-m +([0-9]+)', self.qemuparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600790 if s:
791 self.set('QB_MEM', '-m %s' % s.group(1))
792 elif not self.get('QB_MEM'):
Brad Bishop79641f22019-09-10 07:20:22 -0400793 logger.info('QB_MEM is not set, use 256M by default')
794 self.set('QB_MEM', '-m 256')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500795
Andrew Geissler99467da2019-02-25 18:54:23 -0600796 # Check and remove M or m suffix
797 qb_mem = self.get('QB_MEM')
798 if qb_mem.endswith('M') or qb_mem.endswith('m'):
799 qb_mem = qb_mem[:-1]
800
801 # Add -m prefix it not present
802 if not qb_mem.startswith('-m'):
803 qb_mem = '-m %s' % qb_mem
804
805 self.set('QB_MEM', qb_mem)
806
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800807 mach = self.get('MACHINE')
808 if not mach.startswith('qemumips'):
809 self.kernel_cmdline_script += ' mem=%s' % self.get('QB_MEM').replace('-m','').strip() + 'M'
810
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600811 self.qemu_opt_script += ' %s' % self.get('QB_MEM')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500812
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600813 def check_tcpserial(self):
814 if self.tcpserial_portnum:
Brad Bishop15ae2502019-06-18 21:44:24 -0400815 ports = self.tcpserial_portnum.split(':')
816 port = ports[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600817 if self.get('QB_TCPSERIAL_OPT'):
Brad Bishop15ae2502019-06-18 21:44:24 -0400818 self.qemu_opt_script += ' ' + self.get('QB_TCPSERIAL_OPT').replace('@PORT@', port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600819 else:
Brad Bishop15ae2502019-06-18 21:44:24 -0400820 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
821
822 if len(ports) > 1:
823 for port in ports[1:]:
824 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500825
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600826 def check_and_set(self):
827 """Check configs sanity and set when needed"""
828 self.validate_paths()
Andrew Geissler82c905d2020-04-13 13:39:40 -0500829 if not self.slirp_enabled and not self.net_bridge:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500830 check_tun()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600831 # Check audio
832 if self.audio_enabled:
833 if not self.get('QB_AUDIO_DRV'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500834 raise RunQemuError("QB_AUDIO_DRV is NULL, this board doesn't support audio")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600835 if not self.get('QB_AUDIO_OPT'):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800836 logger.warning('QB_AUDIO_OPT is NULL, you may need define it to make audio work')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600837 else:
838 self.qemu_opt_script += ' %s' % self.get('QB_AUDIO_OPT')
839 os.putenv('QEMU_AUDIO_DRV', self.get('QB_AUDIO_DRV'))
840 else:
841 os.putenv('QEMU_AUDIO_DRV', 'none')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500842
Brad Bishop15ae2502019-06-18 21:44:24 -0400843 self.check_qemu_system()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600844 self.check_kvm()
845 self.check_fstype()
846 self.check_rootfs()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500847 self.check_ovmf()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600848 self.check_kernel()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400849 self.check_dtb()
Brad Bishopc68388fc2019-08-26 01:33:31 -0400850 self.check_bios()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600851 self.check_mem()
852 self.check_tcpserial()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500853
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600854 def read_qemuboot(self):
855 if not self.qemuboot:
856 if self.get('DEPLOY_DIR_IMAGE'):
857 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600858 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800859 logger.warning("Can't find qemuboot conf file, DEPLOY_DIR_IMAGE is NULL!")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600860 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500861
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600862 if self.rootfs and not os.path.exists(self.rootfs):
863 # Lazy rootfs
864 machine = self.get('MACHINE')
865 if not machine:
866 machine = os.path.basename(deploy_dir_image)
867 self.qemuboot = "%s/%s-%s.qemuboot.conf" % (deploy_dir_image,
868 self.rootfs, machine)
869 else:
870 cmd = 'ls -t %s/*.qemuboot.conf' % deploy_dir_image
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500871 logger.debug('Running %s...' % cmd)
872 try:
873 qbs = subprocess.check_output(cmd, shell=True).decode('utf-8')
874 except subprocess.CalledProcessError as err:
875 raise RunQemuError(err)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600876 if qbs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500877 for qb in qbs.split():
878 # Don't use initramfs when other choices unless fstype is ramfs
879 if '-initramfs-' in os.path.basename(qb) and self.fstype != 'cpio.gz':
880 continue
881 self.qemuboot = qb
882 break
883 if not self.qemuboot:
884 # Use the first one when no choice
885 self.qemuboot = qbs.split()[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600886 self.qbconfload = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500887
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600888 if not self.qemuboot:
889 # If we haven't found a .qemuboot.conf at this point it probably
890 # doesn't exist, continue without
891 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500892
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600893 if not os.path.exists(self.qemuboot):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500894 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 -0500895
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500896 logger.debug('CONFFILE: %s' % self.qemuboot)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500897
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600898 cf = configparser.ConfigParser()
899 cf.read(self.qemuboot)
900 for k, v in cf.items('config_bsp'):
901 k_upper = k.upper()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500902 if v.startswith("../"):
903 v = os.path.abspath(os.path.dirname(self.qemuboot) + "/" + v)
904 elif v == ".":
905 v = os.path.dirname(self.qemuboot)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600906 self.set(k_upper, v)
907
908 def validate_paths(self):
909 """Ensure all relevant path variables are set"""
910 # When we're started with a *.qemuboot.conf arg assume that image
911 # artefacts are relative to that file, rather than in whatever
912 # directory DEPLOY_DIR_IMAGE in the conf file points to.
913 if self.qbconfload:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500914 imgdir = os.path.realpath(os.path.dirname(self.qemuboot))
915 if imgdir != os.path.realpath(self.get('DEPLOY_DIR_IMAGE')):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600916 logger.info('Setting DEPLOY_DIR_IMAGE to folder containing %s (%s)' % (self.qemuboot, imgdir))
917 self.set('DEPLOY_DIR_IMAGE', imgdir)
918
919 # If the STAGING_*_NATIVE directories from the config file don't exist
920 # and we're in a sourced OE build directory try to extract the paths
921 # from `bitbake -e`
922 havenative = os.path.exists(self.get('STAGING_DIR_NATIVE')) and \
923 os.path.exists(self.get('STAGING_BINDIR_NATIVE'))
924
925 if not havenative:
926 if not self.bitbake_e:
927 self.load_bitbake_env()
928
929 if self.bitbake_e:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500930 native_vars = ['STAGING_DIR_NATIVE']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600931 for nv in native_vars:
932 s = re.search('^%s="(.*)"' % nv, self.bitbake_e, re.M)
933 if s and s.group(1) != self.get(nv):
934 logger.info('Overriding conf file setting of %s to %s from Bitbake environment' % (nv, s.group(1)))
935 self.set(nv, s.group(1))
936 else:
937 # when we're invoked from a running bitbake instance we won't
938 # be able to call `bitbake -e`, then try:
939 # - get OE_TMPDIR from environment and guess paths based on it
940 # - get OECORE_NATIVE_SYSROOT from environment (for sdk)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500941 tmpdir = self.get('OE_TMPDIR')
942 oecore_native_sysroot = self.get('OECORE_NATIVE_SYSROOT')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600943 if tmpdir:
944 logger.info('Setting STAGING_DIR_NATIVE and STAGING_BINDIR_NATIVE relative to OE_TMPDIR (%s)' % tmpdir)
945 hostos, _, _, _, machine = os.uname()
946 buildsys = '%s-%s' % (machine, hostos.lower())
947 staging_dir_native = '%s/sysroots/%s' % (tmpdir, buildsys)
948 self.set('STAGING_DIR_NATIVE', staging_dir_native)
949 elif oecore_native_sysroot:
950 logger.info('Setting STAGING_DIR_NATIVE to OECORE_NATIVE_SYSROOT (%s)' % oecore_native_sysroot)
951 self.set('STAGING_DIR_NATIVE', oecore_native_sysroot)
952 if self.get('STAGING_DIR_NATIVE'):
953 # we have to assume that STAGING_BINDIR_NATIVE is at usr/bin
954 staging_bindir_native = '%s/usr/bin' % self.get('STAGING_DIR_NATIVE')
955 logger.info('Setting STAGING_BINDIR_NATIVE to %s' % staging_bindir_native)
956 self.set('STAGING_BINDIR_NATIVE', '%s/usr/bin' % self.get('STAGING_DIR_NATIVE'))
957
958 def print_config(self):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500959 logoutput = ['Continuing with the following parameters:']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600960 if not self.fstype in self.vmtypes:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500961 logoutput.append('KERNEL: [%s]' % self.kernel)
Brad Bishopc68388fc2019-08-26 01:33:31 -0400962 if self.bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500963 logoutput.append('BIOS: [%s]' % self.bios)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600964 if self.dtb:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500965 logoutput.append('DTB: [%s]' % self.dtb)
966 logoutput.append('MACHINE: [%s]' % self.get('MACHINE'))
Brad Bishop15ae2502019-06-18 21:44:24 -0400967 try:
968 fstype_flags = ' (' + ', '.join(self.fsinfo[self.fstype]) + ')'
969 except KeyError:
970 fstype_flags = ''
Andrew Geissler82c905d2020-04-13 13:39:40 -0500971 logoutput.append('FSTYPE: [%s%s]' % (self.fstype, fstype_flags))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600972 if self.fstype == 'nfs':
Andrew Geissler82c905d2020-04-13 13:39:40 -0500973 logoutput.append('NFS_DIR: [%s]' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600974 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500975 logoutput.append('ROOTFS: [%s]' % self.rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500976 if self.ovmf_bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500977 logoutput.append('OVMF: %s' % self.ovmf_bios)
Brad Bishop08902b02019-08-20 09:16:51 -0400978 if (self.ovmf_secboot_pkkek1):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500979 logoutput.append('SECBOOT PKKEK1: [%s...]' % self.ovmf_secboot_pkkek1[0:100])
980 logoutput.append('CONFFILE: [%s]' % self.qemuboot)
981 logoutput.append('')
982 logger.info('\n'.join(logoutput))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600983
984 def setup_nfs(self):
985 if not self.nfs_server:
986 if self.slirp_enabled:
987 self.nfs_server = '10.0.2.2'
988 else:
989 self.nfs_server = '192.168.7.1'
990
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500991 # Figure out a new nfs_instance to allow multiple qemus running.
Brad Bishop977dc1a2019-02-06 16:01:43 -0500992 ps = subprocess.check_output(("ps", "auxww")).decode('utf-8')
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500993 pattern = '/bin/unfsd .* -i .*\.pid -e .*/exports([0-9]+) '
994 all_instances = re.findall(pattern, ps, re.M)
995 if all_instances:
996 all_instances.sort(key=int)
997 self.nfs_instance = int(all_instances.pop()) + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600998
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500999 nfsd_port = 3049 + 2 * self.nfs_instance
1000 mountd_port = 3048 + 2 * self.nfs_instance
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001001
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001002 # Export vars for runqemu-export-rootfs
1003 export_dict = {
1004 'NFS_INSTANCE': self.nfs_instance,
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001005 'NFSD_PORT': nfsd_port,
1006 'MOUNTD_PORT': mountd_port,
1007 }
1008 for k, v in export_dict.items():
1009 # Use '%s' since they are integers
1010 os.putenv(k, '%s' % v)
1011
Andrew Geissler82c905d2020-04-13 13:39:40 -05001012 self.unfs_opts="nfsvers=3,port=%s,tcp,mountport=%s" % (nfsd_port, mountd_port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001013
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001014 # Extract .tar.bz2 or .tar.bz if no nfs dir
1015 if not (self.rootfs and os.path.isdir(self.rootfs)):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001016 src_prefix = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'))
1017 dest = "%s-nfsroot" % src_prefix
1018 if os.path.exists('%s.pseudo_state' % dest):
1019 logger.info('Use %s as NFS_DIR' % dest)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001020 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001021 else:
1022 src = ""
1023 src1 = '%s.tar.bz2' % src_prefix
1024 src2 = '%s.tar.gz' % src_prefix
1025 if os.path.exists(src1):
1026 src = src1
1027 elif os.path.exists(src2):
1028 src = src2
1029 if not src:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001030 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 -06001031 logger.info('NFS_DIR not found, extracting %s to %s' % (src, dest))
Brad Bishop977dc1a2019-02-06 16:01:43 -05001032 cmd = ('runqemu-extract-sdk', src, dest)
1033 logger.info('Running %s...' % str(cmd))
1034 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001035 raise RunQemuError('Failed to run %s' % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001036 self.rootfs = dest
Andrew Geisslerc926e172021-05-07 16:11:35 -05001037 self.cleanup_files.append(self.rootfs)
1038 self.cleanup_files.append('%s.pseudo_state' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001039
1040 # Start the userspace NFS server
Brad Bishop977dc1a2019-02-06 16:01:43 -05001041 cmd = ('runqemu-export-rootfs', 'start', self.rootfs)
1042 logger.info('Running %s...' % str(cmd))
1043 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001044 raise RunQemuError('Failed to run %s' % cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001045
1046 self.nfs_running = True
1047
Andrew Geissler82c905d2020-04-13 13:39:40 -05001048 def setup_net_bridge(self):
1049 self.set('NETWORK_CMD', '-netdev bridge,br=%s,id=net0,helper=%s -device virtio-net-pci,netdev=net0 ' % (
1050 self.net_bridge, os.path.join(self.bindir_native, 'qemu-oe-bridge-helper')))
1051
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001052 def setup_slirp(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001053 """Setup user networking"""
1054
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001055 if self.fstype == 'nfs':
1056 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001057 netconf = " " + self.cmdline_ip_slirp
1058 logger.info("Network configuration:%s", netconf)
1059 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001060 # Port mapping
1061 hostfwd = ",hostfwd=tcp::2222-:22,hostfwd=tcp::2323-:23"
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001062 qb_slirp_opt_default = "-netdev user,id=net0%s,tftp=%s" % (hostfwd, self.get('DEPLOY_DIR_IMAGE'))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001063 qb_slirp_opt = self.get('QB_SLIRP_OPT') or qb_slirp_opt_default
1064 # Figure out the port
1065 ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt)
1066 ports = [int(i) for i in ports]
1067 mac = 2
Brad Bishop08902b02019-08-20 09:16:51 -04001068
1069 lockdir = "/tmp/qemu-port-locks"
1070 if not os.path.exists(lockdir):
1071 # There might be a race issue when multi runqemu processess are
1072 # running at the same time.
1073 try:
1074 os.mkdir(lockdir)
1075 os.chmod(lockdir, 0o777)
1076 except FileExistsError:
1077 pass
1078
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001079 # Find a free port to avoid conflicts
1080 for p in ports[:]:
1081 p_new = p
Brad Bishop08902b02019-08-20 09:16:51 -04001082 while not self.check_free_port('localhost', p_new, lockdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001083 p_new += 1
1084 mac += 1
1085 while p_new in ports:
1086 p_new += 1
1087 mac += 1
1088 if p != p_new:
1089 ports.append(p_new)
1090 qb_slirp_opt = re.sub(':%s-' % p, ':%s-' % p_new, qb_slirp_opt)
1091 logger.info("Port forward changed: %s -> %s" % (p, p_new))
1092 mac = "%s%02x" % (self.mac_slirp, mac)
1093 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qb_slirp_opt))
1094 # Print out port foward
1095 hostfwd = re.findall('(hostfwd=[^,]*)', qb_slirp_opt)
1096 if hostfwd:
1097 logger.info('Port forward: %s' % ' '.join(hostfwd))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001098
1099 def setup_tap(self):
1100 """Setup tap"""
1101
1102 # This file is created when runqemu-gen-tapdevs creates a bank of tap
1103 # devices, indicating that the user should not bring up new ones using
1104 # sudo.
1105 nosudo_flag = '/etc/runqemu-nosudo'
1106 self.qemuifup = shutil.which('runqemu-ifup')
1107 self.qemuifdown = shutil.which('runqemu-ifdown')
1108 ip = shutil.which('ip')
1109 lockdir = "/tmp/qemu-tap-locks"
1110
1111 if not (self.qemuifup and self.qemuifdown and ip):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001112 logger.error("runqemu-ifup: %s" % self.qemuifup)
1113 logger.error("runqemu-ifdown: %s" % self.qemuifdown)
1114 logger.error("ip: %s" % ip)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001115 raise OEPathError("runqemu-ifup, runqemu-ifdown or ip not found")
1116
1117 if not os.path.exists(lockdir):
1118 # There might be a race issue when multi runqemu processess are
1119 # running at the same time.
1120 try:
1121 os.mkdir(lockdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001122 os.chmod(lockdir, 0o777)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001123 except FileExistsError:
1124 pass
1125
Brad Bishop977dc1a2019-02-06 16:01:43 -05001126 cmd = (ip, 'link')
1127 logger.debug('Running %s...' % str(cmd))
1128 ip_link = subprocess.check_output(cmd).decode('utf-8')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001129 # Matches line like: 6: tap0: <foo>
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001130 possibles = re.findall('^[0-9]+: +(tap[0-9]+): <.*', ip_link, re.M)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001131 tap = ""
1132 for p in possibles:
1133 lockfile = os.path.join(lockdir, p)
1134 if os.path.exists('%s.skip' % lockfile):
1135 logger.info('Found %s.skip, skipping %s' % (lockfile, p))
1136 continue
Brad Bishop08902b02019-08-20 09:16:51 -04001137 self.taplock = lockfile + '.lock'
1138 if self.acquire_taplock(error=False):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001139 tap = p
1140 logger.info("Using preconfigured tap device %s" % tap)
1141 logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap))
1142 break
1143
1144 if not tap:
1145 if os.path.exists(nosudo_flag):
1146 logger.error("Error: There are no available tap devices to use for networking,")
1147 logger.error("and I see %s exists, so I am not going to try creating" % nosudo_flag)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001148 raise RunQemuError("a new one with sudo.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001149
1150 gid = os.getgid()
1151 uid = os.getuid()
1152 logger.info("Setting up tap interface under sudo")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001153 cmd = ('sudo', self.qemuifup, str(uid), str(gid), self.bindir_native)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001154 try:
1155 tap = subprocess.check_output(cmd).decode('utf-8').strip()
1156 except subprocess.CalledProcessError as e:
1157 logger.error('Setting up tap device failed:\n%s\nRun runqemu-gen-tapdevs to manually create one.' % str(e))
1158 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001159 lockfile = os.path.join(lockdir, tap)
Brad Bishop08902b02019-08-20 09:16:51 -04001160 self.taplock = lockfile + '.lock'
1161 self.acquire_taplock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001162 self.cleantap = True
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001163 logger.debug('Created tap: %s' % tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001164
1165 if not tap:
1166 logger.error("Failed to setup tap device. Run runqemu-gen-tapdevs to manually create.")
Andrew Geissler82c905d2020-04-13 13:39:40 -05001167 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001168 self.tap = tap
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001169 tapnum = int(tap[3:])
1170 gateway = tapnum * 2 + 1
1171 client = gateway + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001172 if self.fstype == 'nfs':
1173 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001174 netconf = " " + self.cmdline_ip_tap
1175 netconf = netconf.replace('@CLIENT@', str(client))
1176 netconf = netconf.replace('@GATEWAY@', str(gateway))
1177 logger.info("Network configuration:%s", netconf)
1178 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001179 mac = "%s%02x" % (self.mac_tap, client)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001180 qb_tap_opt = self.get('QB_TAP_OPT')
1181 if qb_tap_opt:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001182 qemu_tap_opt = qb_tap_opt.replace('@TAP@', tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001183 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001184 qemu_tap_opt = "-netdev tap,id=net0,ifname=%s,script=no,downscript=no" % (self.tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001185
1186 if self.vhost_enabled:
1187 qemu_tap_opt += ',vhost=on'
1188
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001189 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qemu_tap_opt))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001190
1191 def setup_network(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001192 if self.get('QB_NET') == 'none':
1193 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001194 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001195 self.saved_stty = subprocess.check_output(("stty", "-g")).decode('utf-8').strip()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001196 self.network_device = self.get('QB_NETWORK_DEVICE') or self.network_device
Andrew Geissler82c905d2020-04-13 13:39:40 -05001197 if self.net_bridge:
1198 self.setup_net_bridge()
1199 elif self.slirp_enabled:
1200 self.cmdline_ip_slirp = self.get('QB_CMDLINE_IP_SLIRP') or self.cmdline_ip_slirp
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001201 self.setup_slirp()
1202 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001203 self.cmdline_ip_tap = self.get('QB_CMDLINE_IP_TAP') or self.cmdline_ip_tap
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001204 self.setup_tap()
1205
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001206 def setup_rootfs(self):
1207 if self.get('QB_ROOTFS') == 'none':
1208 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001209 if 'wic.' in self.fstype:
1210 self.fstype = self.fstype[4:]
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001211 rootfs_format = self.fstype if self.fstype in ('vmdk', 'vhd', 'vhdx', 'qcow2', 'vdi') else 'raw'
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001212
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001213 tmpfsdir = os.environ.get("RUNQEMU_TMPFS_DIR", None)
1214 if self.snapshot and tmpfsdir:
1215 newrootfs = os.path.join(tmpfsdir, os.path.basename(self.rootfs)) + "." + str(os.getpid())
Andrew Geissler09036742021-06-25 14:25:14 -05001216 logger.info("Copying rootfs to %s" % newrootfs)
1217 copy_start = time.time()
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001218 shutil.copyfile(self.rootfs, newrootfs)
Andrew Geissler09036742021-06-25 14:25:14 -05001219 logger.info("Copy done in %s seconds" % (time.time() - copy_start))
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001220 self.rootfs = newrootfs
1221 # Don't need a second copy now!
1222 self.snapshot = False
Andrew Geisslerc926e172021-05-07 16:11:35 -05001223 self.cleanup_files.append(newrootfs)
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001224
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001225 qb_rootfs_opt = self.get('QB_ROOTFS_OPT')
1226 if qb_rootfs_opt:
1227 self.rootfs_options = qb_rootfs_opt.replace('@ROOTFS@', self.rootfs)
1228 else:
1229 self.rootfs_options = '-drive file=%s,if=virtio,format=%s' % (self.rootfs, rootfs_format)
1230
Andrew Geissler82c905d2020-04-13 13:39:40 -05001231 qb_rootfs_extra_opt = self.get("QB_ROOTFS_EXTRA_OPT")
1232 if qb_rootfs_extra_opt and not qb_rootfs_extra_opt.startswith(","):
1233 qb_rootfs_extra_opt = "," + qb_rootfs_extra_opt
1234
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001235 if self.fstype in ('cpio.gz', 'cpio'):
1236 self.kernel_cmdline = 'root=/dev/ram0 rw debugshell'
1237 self.rootfs_options = '-initrd %s' % self.rootfs
1238 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001239 vm_drive = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001240 if self.fstype in self.vmtypes:
1241 if self.fstype == 'iso':
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001242 vm_drive = '-drive file=%s,if=virtio,media=cdrom' % self.rootfs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001243 elif self.get('QB_DRIVE_TYPE'):
1244 drive_type = self.get('QB_DRIVE_TYPE')
1245 if drive_type.startswith("/dev/sd"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001246 logger.info('Using scsi drive')
Andrew Geissler82c905d2020-04-13 13:39:40 -05001247 vm_drive = '-drive if=none,id=hd,file=%s,format=%s -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd%s' \
1248 % (self.rootfs, rootfs_format, qb_rootfs_extra_opt)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001249 elif drive_type.startswith("/dev/hd"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001250 logger.info('Using ide drive')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001251 vm_drive = "-drive file=%s,format=%s" % (self.rootfs, rootfs_format)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001252 elif drive_type.startswith("/dev/vdb"):
1253 logger.info('Using block virtio drive');
1254 vm_drive = '-drive id=disk0,file=%s,if=none,format=%s -device virtio-blk-device,drive=disk0%s' \
1255 % (self.rootfs, rootfs_format,qb_rootfs_extra_opt)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001256 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001257 # virtio might have been selected explicitly (just use it), or
1258 # is used as fallback (then warn about that).
1259 if not drive_type.startswith("/dev/vd"):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001260 logger.warning("Unknown QB_DRIVE_TYPE: %s" % drive_type)
1261 logger.warning("Failed to figure out drive type, consider define or fix QB_DRIVE_TYPE")
1262 logger.warning('Trying to use virtio block drive')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001263 vm_drive = '-drive if=virtio,file=%s,format=%s' % (self.rootfs, rootfs_format)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001264
1265 # All branches above set vm_drive.
Andrew Geissler475cb722020-07-10 16:00:51 -05001266 self.rootfs_options = vm_drive
1267 if not self.fstype in self.vmtypes:
1268 self.rootfs_options += ' -no-reboot'
Brad Bishopf3f93bb2019-10-16 14:33:32 -04001269 self.kernel_cmdline = 'root=%s rw' % (self.get('QB_KERNEL_ROOT'))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001270
1271 if self.fstype == 'nfs':
1272 self.rootfs_options = ''
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001273 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 -04001274 self.kernel_cmdline = 'root=%s rw' % k_root
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001275
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001276 if self.fstype == 'none':
1277 self.rootfs_options = ''
1278
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001279 self.set('ROOTFS_OPTIONS', self.rootfs_options)
1280
1281 def guess_qb_system(self):
1282 """attempt to determine the appropriate qemu-system binary"""
1283 mach = self.get('MACHINE')
1284 if not mach:
1285 search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*'
1286 if self.rootfs:
1287 match = re.match(search, self.rootfs)
1288 if match:
1289 mach = match.group(1)
1290 elif self.kernel:
1291 match = re.match(search, self.kernel)
1292 if match:
1293 mach = match.group(1)
1294
1295 if not mach:
1296 return None
1297
1298 if mach == 'qemuarm':
1299 qbsys = 'arm'
1300 elif mach == 'qemuarm64':
1301 qbsys = 'aarch64'
1302 elif mach == 'qemux86':
1303 qbsys = 'i386'
1304 elif mach == 'qemux86-64':
1305 qbsys = 'x86_64'
1306 elif mach == 'qemuppc':
1307 qbsys = 'ppc'
1308 elif mach == 'qemumips':
1309 qbsys = 'mips'
1310 elif mach == 'qemumips64':
1311 qbsys = 'mips64'
1312 elif mach == 'qemumipsel':
1313 qbsys = 'mipsel'
1314 elif mach == 'qemumips64el':
1315 qbsys = 'mips64el'
Brad Bishop316dfdd2018-06-25 12:45:53 -04001316 elif mach == 'qemuriscv64':
1317 qbsys = 'riscv64'
1318 elif mach == 'qemuriscv32':
1319 qbsys = 'riscv32'
Brad Bishop004d4992018-10-02 23:54:45 +02001320 else:
1321 logger.error("Unable to determine QEMU PC System emulator for %s machine." % mach)
1322 logger.error("As %s is not among valid QEMU machines such as," % mach)
1323 logger.error("qemux86-64, qemux86, qemuarm64, qemuarm, qemumips64, qemumips64el, qemumipsel, qemumips, qemuppc")
1324 raise RunQemuError("Set qb_system_name with suitable QEMU PC System emulator in .*qemuboot.conf.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001325
1326 return 'qemu-system-%s' % qbsys
1327
Brad Bishop15ae2502019-06-18 21:44:24 -04001328 def check_qemu_system(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001329 qemu_system = self.get('QB_SYSTEM_NAME')
1330 if not qemu_system:
1331 qemu_system = self.guess_qb_system()
1332 if not qemu_system:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001333 raise RunQemuError("Failed to boot, QB_SYSTEM_NAME is NULL!")
Brad Bishop15ae2502019-06-18 21:44:24 -04001334 self.qemu_system = qemu_system
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001335
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001336 def setup_vga(self):
1337 if self.nographic == True:
1338 if self.sdl == True:
1339 raise RunQemuError('Option nographic makes no sense alongside the sdl option.')
1340 if self.gtk == True:
1341 raise RunQemuError('Option nographic makes no sense alongside the gtk option.')
1342 self.qemu_opt += ' -nographic'
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001343
1344 if self.novga == True:
1345 self.qemu_opt += ' -vga none'
1346 return
1347
1348 if (self.gl_es == True or self.gl == True) and (self.sdl == False and self.gtk == False):
1349 raise RunQemuError('Option gl/gl-es needs gtk or sdl option.')
1350
1351 if self.sdl == True or self.gtk == True or self.egl_headless == True:
1352 self.set_dri_path()
Andrew Geisslereff27472021-10-29 15:35:00 -05001353 self.qemu_opt += ' -device virtio-vga-gl -display '
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001354 if self.egl_headless == True:
1355 self.qemu_opt += 'egl-headless,'
1356 else:
1357 if self.sdl == True:
1358 self.qemu_opt += 'sdl,'
1359 elif self.gtk == True:
1360 self.qemu_opt += 'gtk,'
1361
1362 if self.gl == True:
1363 self.qemu_opt += 'gl=on,'
1364 elif self.gl_es == True:
1365 self.qemu_opt += 'gl=es,'
1366 self.qemu_opt += 'show-cursor=on'
1367
1368 self.qemu_opt += ' %s' %self.get('QB_GRAPHICS')
1369
1370 def setup_serial(self):
1371 # Setup correct kernel command line for serial
1372 if self.serialstdio == True or self.serialconsole == True or self.nographic == True or self.tcpserial_portnum:
1373 for entry in self.get('SERIAL_CONSOLES').split(' '):
1374 self.kernel_cmdline_script += ' console=%s' %entry.split(';')[1]
1375
1376 if self.serialstdio == True or self.nographic == True:
1377 self.qemu_opt += " -serial mon:stdio"
1378 else:
1379 self.qemu_opt += " -serial mon:vc"
1380 if self.serialconsole:
1381 if sys.stdin.isatty():
1382 subprocess.check_call(("stty", "intr", "^]"))
1383 logger.info("Interrupt character is '^]'")
1384
1385 self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT")
1386
1387 # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES).
1388 # If no serial or serialtcp options were specified, only ttyS0 is created
1389 # and sysvinit shows an error trying to enable ttyS1:
1390 # INIT: Id "S1" respawning too fast: disabled for 5 minutes
1391 serial_num = len(re.findall("-serial", self.qemu_opt))
1392 if serial_num < 2:
1393 self.qemu_opt += " -serial null"
1394
Brad Bishop15ae2502019-06-18 21:44:24 -04001395 def setup_final(self):
1396 qemu_bin = os.path.join(self.bindir_native, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001397
1398 # It is possible to have qemu-native in ASSUME_PROVIDED, and it won't
1399 # find QEMU in sysroot, it needs to use host's qemu.
1400 if not os.path.exists(qemu_bin):
1401 logger.info("QEMU binary not found in %s, trying host's QEMU" % qemu_bin)
1402 for path in (os.environ['PATH'] or '').split(':'):
Brad Bishop15ae2502019-06-18 21:44:24 -04001403 qemu_bin_tmp = os.path.join(path, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001404 logger.info("Trying: %s" % qemu_bin_tmp)
1405 if os.path.exists(qemu_bin_tmp):
1406 qemu_bin = qemu_bin_tmp
1407 if not os.path.isabs(qemu_bin):
1408 qemu_bin = os.path.abspath(qemu_bin)
1409 logger.info("Using host's QEMU: %s" % qemu_bin)
1410 break
1411
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001412 if not os.access(qemu_bin, os.X_OK):
1413 raise OEPathError("No QEMU binary '%s' could be found" % qemu_bin)
1414
Andrew Geisslereff27472021-10-29 15:35:00 -05001415 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').replace('@DEPLOY_DIR_IMAGE@', self.get('DEPLOY_DIR_IMAGE')))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001416
1417 for ovmf in self.ovmf_bios:
1418 format = ovmf.rsplit('.', 1)[-1]
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001419 if format == "bin":
1420 format = "raw"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001421 self.qemu_opt += ' -drive if=pflash,format=%s,file=%s' % (format, ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001422
1423 self.qemu_opt += ' ' + self.qemu_opt_script
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001424
Brad Bishop08902b02019-08-20 09:16:51 -04001425 if self.ovmf_secboot_pkkek1:
1426 # Provide the Platform Key and first Key Exchange Key certificate as an
1427 # OEM string in the SMBIOS Type 11 table. Prepend the certificate string
1428 # with "application prefix" of the EnrollDefaultKeys.efi application
1429 self.qemu_opt += ' -smbios type=11,value=4e32566d-8e9e-4f52-81d3-5bb9715f9727:' \
1430 + self.ovmf_secboot_pkkek1
1431
Andrew Geissler99467da2019-02-25 18:54:23 -06001432 # Append qemuparams to override previous settings
1433 if self.qemuparams:
1434 self.qemu_opt += ' ' + self.qemuparams
1435
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001436 if self.snapshot:
1437 self.qemu_opt += " -snapshot"
1438
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001439 self.setup_serial()
1440 self.setup_vga()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001441
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001442 def start_qemu(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001443 import shlex
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001444 if self.kernel:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001445 kernel_opts = "-kernel %s -append '%s %s %s %s'" % (self.kernel, self.kernel_cmdline,
1446 self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'),
1447 self.bootparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001448 if self.dtb:
1449 kernel_opts += " -dtb %s" % self.dtb
1450 else:
1451 kernel_opts = ""
Patrick Williams213cb262021-08-07 19:21:33 -05001452
1453 if self.bios:
1454 self.qemu_opt += " -bios %s" % self.bios
1455
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001456 cmd = "%s %s" % (self.qemu_opt, kernel_opts)
Brad Bishop004d4992018-10-02 23:54:45 +02001457 cmds = shlex.split(cmd)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001458 logger.info('Running %s\n' % cmd)
Brad Bishopf86d0552018-12-04 14:18:15 -08001459 pass_fds = []
Brad Bishop08902b02019-08-20 09:16:51 -04001460 if self.taplock_descriptor:
1461 pass_fds = [self.taplock_descriptor.fileno()]
1462 if len(self.portlocks):
1463 for descriptor in self.portlocks.values():
1464 pass_fds.append(descriptor.fileno())
Brad Bishopf86d0552018-12-04 14:18:15 -08001465 process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds)
Brad Bishop004d4992018-10-02 23:54:45 +02001466 self.qemupid = process.pid
1467 retcode = process.wait()
1468 if retcode:
1469 if retcode == -signal.SIGTERM:
1470 logger.info("Qemu terminated by SIGTERM")
1471 else:
1472 logger.error("Failed to run qemu: %s", process.stderr.read().decode())
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001473
1474 def cleanup(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001475 if self.cleaned:
1476 return
1477
1478 # avoid dealing with SIGTERM when cleanup function is running
1479 signal.signal(signal.SIGTERM, signal.SIG_IGN)
1480
1481 logger.info("Cleaning up")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001482 if self.cleantap:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001483 cmd = ('sudo', self.qemuifdown, self.tap, self.bindir_native)
1484 logger.debug('Running %s' % str(cmd))
1485 subprocess.check_call(cmd)
Brad Bishop08902b02019-08-20 09:16:51 -04001486 self.release_taplock()
1487 self.release_portlock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001488
1489 if self.nfs_running:
1490 logger.info("Shutting down the userspace NFS server...")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001491 cmd = ("runqemu-export-rootfs", "stop", self.rootfs)
1492 logger.debug('Running %s' % str(cmd))
1493 subprocess.check_call(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001494
1495 if self.saved_stty:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001496 subprocess.check_call(("stty", self.saved_stty))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001497
Andrew Geisslerc926e172021-05-07 16:11:35 -05001498 if self.cleanup_files:
1499 for ent in self.cleanup_files:
1500 logger.info('Removing %s' % ent)
1501 if os.path.isfile(ent):
1502 os.remove(ent)
1503 else:
1504 shutil.rmtree(ent)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001505
Brad Bishop004d4992018-10-02 23:54:45 +02001506 self.cleaned = True
1507
Andrew Geissler82c905d2020-04-13 13:39:40 -05001508 def run_bitbake_env(self, mach=None):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001509 bitbake = shutil.which('bitbake')
1510 if not bitbake:
1511 return
1512
1513 if not mach:
1514 mach = self.get('MACHINE')
1515
Andrew Geissler82c905d2020-04-13 13:39:40 -05001516 multiconfig = self.get('MULTICONFIG')
1517 if multiconfig:
1518 multiconfig = "mc:%s" % multiconfig
1519
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001520 if mach:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001521 cmd = 'MACHINE=%s bitbake -e %s' % (mach, multiconfig)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001522 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001523 cmd = 'bitbake -e %s' % multiconfig
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001524
1525 logger.info('Running %s...' % cmd)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001526 return subprocess.check_output(cmd, shell=True).decode('utf-8')
1527
1528 def load_bitbake_env(self, mach=None):
1529 if self.bitbake_e:
1530 return
1531
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001532 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001533 self.bitbake_e = self.run_bitbake_env(mach=mach)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001534 except subprocess.CalledProcessError as err:
1535 self.bitbake_e = ''
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001536 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 -06001537
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001538 def validate_combos(self):
1539 if (self.fstype in self.vmtypes) and self.kernel:
1540 raise RunQemuError("%s doesn't need kernel %s!" % (self.fstype, self.kernel))
1541
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001542 @property
1543 def bindir_native(self):
1544 result = self.get('STAGING_BINDIR_NATIVE')
1545 if result and os.path.exists(result):
1546 return result
1547
Andrew Geissler82c905d2020-04-13 13:39:40 -05001548 cmd = ['bitbake', '-e']
1549 multiconfig = self.get('MULTICONFIG')
1550 if multiconfig:
1551 cmd.append('mc:%s:qemu-helper-native' % multiconfig)
1552 else:
1553 cmd.append('qemu-helper-native')
1554
Brad Bishop977dc1a2019-02-06 16:01:43 -05001555 logger.info('Running %s...' % str(cmd))
1556 out = subprocess.check_output(cmd).decode('utf-8')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001557
1558 match = re.search('^STAGING_BINDIR_NATIVE="(.*)"', out, re.M)
1559 if match:
1560 result = match.group(1)
1561 if os.path.exists(result):
1562 self.set('STAGING_BINDIR_NATIVE', result)
1563 return result
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001564 raise RunQemuError("Native sysroot directory %s doesn't exist" % result)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001565 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001566 raise RunQemuError("Can't find STAGING_BINDIR_NATIVE in '%s' output" % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001567
1568
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001569def main():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001570 if "help" in sys.argv or '-h' in sys.argv or '--help' in sys.argv:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001571 print_usage()
1572 return 0
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001573 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001574 config = BaseConfig()
Brad Bishop004d4992018-10-02 23:54:45 +02001575
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001576 renice = os.path.expanduser("~/bin/runqemu-renice")
1577 if os.path.exists(renice):
1578 logger.info('Using %s to renice' % renice)
1579 subprocess.check_call([renice, str(os.getpid())])
1580
Brad Bishop004d4992018-10-02 23:54:45 +02001581 def sigterm_handler(signum, frame):
1582 logger.info("SIGTERM received")
1583 os.kill(config.qemupid, signal.SIGTERM)
1584 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001585 # Deliberately ignore the return code of 'tput smam'.
1586 subprocess.call(["tput", "smam"])
Brad Bishop004d4992018-10-02 23:54:45 +02001587 signal.signal(signal.SIGTERM, sigterm_handler)
1588
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001589 config.check_args()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001590 config.read_qemuboot()
1591 config.check_and_set()
1592 # Check whether the combos is valid or not
1593 config.validate_combos()
1594 config.print_config()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001595 config.setup_network()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001596 config.setup_rootfs()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001597 config.setup_final()
1598 config.start_qemu()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001599 except RunQemuError as err:
1600 logger.error(err)
1601 return 1
1602 except Exception as err:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001603 import traceback
1604 traceback.print_exc()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001605 return 1
1606 finally:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001607 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001608 # Deliberately ignore the return code of 'tput smam'.
1609 subprocess.call(["tput", "smam"])
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001610
1611if __name__ == "__main__":
1612 sys.exit(main())