blob: 4e05c1bb15ba1a3ec6ddfb4aec8c6b011cf979f1 [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"
Andrew Geissler595f6302022-01-24 19:11:47 +0000202 self.cmdline_ip_tap = "ip=192.168.7.@CLIENT@::192.168.7.@GATEWAY@:255.255.255.0::eth0:off:8.8.8.8"
Brad Bishop6e60e8b2018-02-01 10:27:11 -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'
Andrew Geissler595f6302022-01-24 19:11:47 +0000477 os.environ['SDL_FRAMEBUFFER_ACCELERATION'] = 'false'
Andrew Geisslerc182c622020-05-15 14:13:32 -0500478
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600479 unknown_arg = ""
480 for arg in sys.argv[1:]:
Brad Bishop15ae2502019-06-18 21:44:24 -0400481 if arg in self.fstypes + self.vmtypes + self.wictypes:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600482 self.check_arg_fstype(arg)
483 elif arg == 'nographic':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500484 self.nographic = True
Brad Bishop19323692019-04-05 15:28:33 -0400485 elif arg == 'sdl':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500486 self.sdl = True
Brad Bishopa34c0302019-09-23 22:34:48 -0400487 elif arg == 'gtk':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500488 self.gtk = True
489 elif arg == 'gl':
490 self.gl = True
491 elif 'gl-es' in sys.argv[1:]:
492 self.gl_es = True
Brad Bishop19323692019-04-05 15:28:33 -0400493 elif arg == 'egl-headless':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500494 self.egl_headless = True
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600495 elif arg == 'novga':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500496 self.novga = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600497 elif arg == 'serial':
Brad Bishop19323692019-04-05 15:28:33 -0400498 self.serialconsole = True
499 elif arg == "serialstdio":
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600500 self.serialstdio = True
501 elif arg == 'audio':
502 logger.info("Enabling audio in qemu")
503 logger.info("Please install sound drivers in linux host")
504 self.audio_enabled = True
505 elif arg == 'kvm':
506 self.kvm_enabled = True
507 elif arg == 'kvm-vhost':
508 self.vhost_enabled = True
509 elif arg == 'slirp':
510 self.slirp_enabled = True
Andrew Geissler82c905d2020-04-13 13:39:40 -0500511 elif arg.startswith('bridge='):
512 self.net_bridge = '%s' % arg[len('bridge='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600513 elif arg == 'snapshot':
514 self.snapshot = True
515 elif arg == 'publicvnc':
516 self.qemu_opt_script += ' -vnc :0'
517 elif arg.startswith('tcpserial='):
Brad Bishop15ae2502019-06-18 21:44:24 -0400518 self.tcpserial_portnum = '%s' % arg[len('tcpserial='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600519 elif arg.startswith('qemuparams='):
Andrew Geissler99467da2019-02-25 18:54:23 -0600520 self.qemuparams = ' %s' % arg[len('qemuparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600521 elif arg.startswith('bootparams='):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500522 self.bootparams = arg[len('bootparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600523 elif os.path.exists(arg) or (re.search(':', arg) and re.search('/', arg)):
524 self.check_arg_path(os.path.abspath(arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500525 elif re.search(r'-image-|-image$', arg):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600526 # Lazy rootfs
527 self.rootfs = arg
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500528 elif arg.startswith('ovmf'):
529 self.ovmf_bios.append(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600530 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500531 # At last, assume it is the MACHINE
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600532 if (not unknown_arg) or unknown_arg == arg:
533 unknown_arg = arg
534 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500535 raise RunQemuError("Can't handle two unknown args: %s %s\n"
536 "Try 'runqemu help' on how to use it" % \
537 (unknown_arg, arg))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600538 # Check to make sure it is a valid machine
Brad Bishopa5c52ff2018-11-23 10:55:50 +1300539 if unknown_arg and self.get('MACHINE') != unknown_arg:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500540 if self.get('DEPLOY_DIR_IMAGE'):
541 machine = os.path.basename(self.get('DEPLOY_DIR_IMAGE'))
542 if unknown_arg == machine:
543 self.set("MACHINE", machine)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500544
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600545 self.check_arg_machine(unknown_arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500546
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500547 if not (self.get('DEPLOY_DIR_IMAGE') or self.qbconfload):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500548 self.load_bitbake_env()
549 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
550 if s:
551 self.set("DEPLOY_DIR_IMAGE", s.group(1))
552
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600553 def check_kvm(self):
554 """Check kvm and kvm-host"""
555 if not (self.kvm_enabled or self.vhost_enabled):
William A. Kennington IIIac69b482021-06-02 12:28:27 -0700556 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 -0600557 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500558
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600559 if not self.get('QB_CPU_KVM'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500560 raise RunQemuError("QB_CPU_KVM is NULL, this board doesn't support kvm")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500561
William A. Kennington IIIac69b482021-06-02 12:28:27 -0700562 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 -0600563 yocto_kvm_wiki = "https://wiki.yoctoproject.org/wiki/How_to_enable_KVM_for_Poky_qemu"
564 yocto_paravirt_kvm_wiki = "https://wiki.yoctoproject.org/wiki/Running_an_x86_Yocto_Linux_image_under_QEMU_KVM"
565 dev_kvm = '/dev/kvm'
566 dev_vhost = '/dev/vhost-net'
Brad Bishop15ae2502019-06-18 21:44:24 -0400567 if self.qemu_system.endswith(('i386', 'x86_64')):
568 with open('/proc/cpuinfo', 'r') as f:
569 kvm_cap = re.search('vmx|svm', "".join(f.readlines()))
570 if not kvm_cap:
571 logger.error("You are trying to enable KVM on a cpu without VT support.")
572 logger.error("Remove kvm from the command-line, or refer:")
573 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500574
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600575 if not os.path.exists(dev_kvm):
576 logger.error("Missing KVM device. Have you inserted kvm modules?")
577 logger.error("For further help see:")
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 os.access(dev_kvm, os.W_OK|os.R_OK):
581 self.qemu_opt_script += ' -enable-kvm'
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500582 if self.get('MACHINE') == "qemux86":
583 # Workaround for broken APIC window on pre 4.15 host kernels which causes boot hangs
584 # See YOCTO #12301
585 # On 64 bit we use x2apic
586 self.kernel_cmdline_script += " clocksource=kvm-clock hpet=disable noapic nolapic"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600587 else:
588 logger.error("You have no read or write permission on /dev/kvm.")
589 logger.error("Please change the ownership of this file as described at:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500590 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500591
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600592 if self.vhost_enabled:
593 if not os.path.exists(dev_vhost):
594 logger.error("Missing virtio net device. Have you inserted vhost-net module?")
595 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500596 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500597
Andrew Geissler635e0e42020-08-21 15:58:33 -0500598 if not os.access(dev_vhost, os.W_OK|os.R_OK):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600599 logger.error("You have no read or write permission on /dev/vhost-net.")
600 logger.error("Please change the ownership of this file as described at:")
Andrew Geissler635e0e42020-08-21 15:58:33 -0500601 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500602
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600603 def check_fstype(self):
604 """Check and setup FSTYPE"""
605 if not self.fstype:
606 fstype = self.get('QB_DEFAULT_FSTYPE')
607 if fstype:
608 self.fstype = fstype
609 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500610 raise RunQemuError("FSTYPE is NULL!")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500611
Brad Bishop15ae2502019-06-18 21:44:24 -0400612 # parse QB_FSINFO into dict, e.g. { 'wic': ['no-kernel-in-fs', 'a-flag'], 'ext4': ['another-flag']}
613 wic_fs = False
614 qb_fsinfo = self.get('QB_FSINFO')
615 if qb_fsinfo:
616 qb_fsinfo = qb_fsinfo.split()
617 for fsinfo in qb_fsinfo:
618 try:
619 fstype, fsflag = fsinfo.split(':')
620
621 if fstype == 'wic':
622 if fsflag == 'no-kernel-in-fs':
623 wic_fs = True
624 elif fsflag == 'kernel-in-fs':
625 wic_fs = False
626 else:
627 logger.warn('Unknown flag "%s:%s" in QB_FSINFO', fstype, fsflag)
628 continue
629 else:
630 logger.warn('QB_FSINFO is not supported for image type "%s"', fstype)
631 continue
632
633 if fstype in self.fsinfo:
634 self.fsinfo[fstype].append(fsflag)
635 else:
636 self.fsinfo[fstype] = [fsflag]
637 except Exception:
638 logger.error('Invalid parameter "%s" in QB_FSINFO', fsinfo)
639
640 # treat wic images as vmimages (with kernel) or as fsimages (rootfs only)
641 if wic_fs:
642 self.fstypes = self.fstypes + self.wictypes
643 else:
644 self.vmtypes = self.vmtypes + self.wictypes
645
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600646 def check_rootfs(self):
647 """Check and set rootfs"""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500648
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500649 if self.fstype == "none":
650 return
651
652 if self.get('ROOTFS'):
653 if not self.rootfs:
654 self.rootfs = self.get('ROOTFS')
655 elif self.get('ROOTFS') != self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500656 raise RunQemuError("Maybe conflicted ROOTFS: %s vs %s" % (self.get('ROOTFS'), self.rootfs))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500657
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600658 if self.fstype == 'nfs':
659 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500660
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600661 if self.rootfs and not os.path.exists(self.rootfs):
662 # Lazy rootfs
663 self.rootfs = "%s/%s-%s.%s" % (self.get('DEPLOY_DIR_IMAGE'),
664 self.rootfs, self.get('MACHINE'),
665 self.fstype)
666 elif not self.rootfs:
667 cmd_name = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_NAME'), self.fstype)
668 cmd_link = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'), self.fstype)
669 cmds = (cmd_name, cmd_link)
670 self.rootfs = get_first_file(cmds)
671 if not self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500672 raise RunQemuError("Failed to find rootfs: %s or %s" % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500673
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600674 if not os.path.exists(self.rootfs):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500675 raise RunQemuError("Can't find rootfs: %s" % self.rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500676
Brad Bishop08902b02019-08-20 09:16:51 -0400677 def setup_pkkek1(self):
678 """
679 Extract from PEM certificate the Platform Key and first Key
680 Exchange Key certificate string. The hypervisor needs to provide
681 it in the Type 11 SMBIOS table
682 """
683 pemcert = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), 'OvmfPkKek1.pem')
684 try:
685 with open(pemcert, 'r') as pemfile:
686 key = pemfile.read().replace('\n', ''). \
687 replace('-----BEGIN CERTIFICATE-----', ''). \
688 replace('-----END CERTIFICATE-----', '')
689 self.ovmf_secboot_pkkek1 = key
690
691 except FileNotFoundError:
692 raise RunQemuError("Can't open PEM certificate %s " % pemcert)
693
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500694 def check_ovmf(self):
695 """Check and set full path for OVMF firmware and variable file(s)."""
696
697 for index, ovmf in enumerate(self.ovmf_bios):
698 if os.path.exists(ovmf):
699 continue
700 for suffix in ('qcow2', 'bin'):
701 path = '%s/%s.%s' % (self.get('DEPLOY_DIR_IMAGE'), ovmf, suffix)
702 if os.path.exists(path):
703 self.ovmf_bios[index] = path
Brad Bishop08902b02019-08-20 09:16:51 -0400704 if ovmf.endswith('secboot'):
705 self.setup_pkkek1()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500706 break
707 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500708 raise RunQemuError("Can't find OVMF firmware: %s" % ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500709
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600710 def check_kernel(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400711 """Check and set kernel"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600712 # The vm image doesn't need a kernel
713 if self.fstype in self.vmtypes:
714 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500715
Brad Bishop316dfdd2018-06-25 12:45:53 -0400716 # See if the user supplied a KERNEL option
717 if self.get('KERNEL'):
718 self.kernel = self.get('KERNEL')
719
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500720 # QB_DEFAULT_KERNEL is always a full file path
721 kernel_name = os.path.basename(self.get('QB_DEFAULT_KERNEL'))
722
723 # The user didn't want a kernel to be loaded
Brad Bishop316dfdd2018-06-25 12:45:53 -0400724 if kernel_name == "none" and not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500725 return
726
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600727 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
728 if not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500729 kernel_match_name = "%s/%s" % (deploy_dir_image, kernel_name)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600730 kernel_match_link = "%s/%s" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
731 kernel_startswith = "%s/%s*" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
732 cmds = (kernel_match_name, kernel_match_link, kernel_startswith)
733 self.kernel = get_first_file(cmds)
734 if not self.kernel:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500735 raise RunQemuError('KERNEL not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500736
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600737 if not os.path.exists(self.kernel):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500738 raise RunQemuError("KERNEL %s not found" % self.kernel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500739
Brad Bishop316dfdd2018-06-25 12:45:53 -0400740 def check_dtb(self):
741 """Check and set dtb"""
742 # Did the user specify a device tree?
743 if self.get('DEVICE_TREE'):
744 self.dtb = self.get('DEVICE_TREE')
745 if not os.path.exists(self.dtb):
746 raise RunQemuError('Specified DTB not found: %s' % self.dtb)
747 return
748
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600749 dtb = self.get('QB_DTB')
750 if dtb:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400751 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600752 cmd_match = "%s/%s" % (deploy_dir_image, dtb)
753 cmd_startswith = "%s/%s*" % (deploy_dir_image, dtb)
754 cmd_wild = "%s/*.dtb" % deploy_dir_image
755 cmds = (cmd_match, cmd_startswith, cmd_wild)
756 self.dtb = get_first_file(cmds)
757 if not os.path.exists(self.dtb):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500758 raise RunQemuError('DTB not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500759
Brad Bishopc68388fc2019-08-26 01:33:31 -0400760 def check_bios(self):
761 """Check and set bios"""
762
763 # See if the user supplied a BIOS option
764 if self.get('BIOS'):
765 self.bios = self.get('BIOS')
766
767 # QB_DEFAULT_BIOS is always a full file path
768 bios_name = os.path.basename(self.get('QB_DEFAULT_BIOS'))
769
770 # The user didn't want a bios to be loaded
771 if (bios_name == "" or bios_name == "none") and not self.bios:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600772 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500773
Brad Bishopc68388fc2019-08-26 01:33:31 -0400774 if not self.bios:
775 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
776 self.bios = "%s/%s" % (deploy_dir_image, bios_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500777
Brad Bishopc68388fc2019-08-26 01:33:31 -0400778 if not self.bios:
779 raise RunQemuError('BIOS not found: %s' % bios_match_name)
780
781 if not os.path.exists(self.bios):
Patrick Williams213cb262021-08-07 19:21:33 -0500782 raise RunQemuError("BIOS %s not found" % self.bios)
Brad Bishopc68388fc2019-08-26 01:33:31 -0400783
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500784
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600785 def check_mem(self):
Andrew Geissler99467da2019-02-25 18:54:23 -0600786 """
787 Both qemu and kernel needs memory settings, so check QB_MEM and set it
788 for both.
789 """
790 s = re.search('-m +([0-9]+)', self.qemuparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600791 if s:
792 self.set('QB_MEM', '-m %s' % s.group(1))
793 elif not self.get('QB_MEM'):
Brad Bishop79641f22019-09-10 07:20:22 -0400794 logger.info('QB_MEM is not set, use 256M by default')
795 self.set('QB_MEM', '-m 256')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500796
Andrew Geissler99467da2019-02-25 18:54:23 -0600797 # Check and remove M or m suffix
798 qb_mem = self.get('QB_MEM')
799 if qb_mem.endswith('M') or qb_mem.endswith('m'):
800 qb_mem = qb_mem[:-1]
801
802 # Add -m prefix it not present
803 if not qb_mem.startswith('-m'):
804 qb_mem = '-m %s' % qb_mem
805
806 self.set('QB_MEM', qb_mem)
807
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800808 mach = self.get('MACHINE')
809 if not mach.startswith('qemumips'):
810 self.kernel_cmdline_script += ' mem=%s' % self.get('QB_MEM').replace('-m','').strip() + 'M'
811
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600812 self.qemu_opt_script += ' %s' % self.get('QB_MEM')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500813
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600814 def check_tcpserial(self):
815 if self.tcpserial_portnum:
Brad Bishop15ae2502019-06-18 21:44:24 -0400816 ports = self.tcpserial_portnum.split(':')
817 port = ports[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600818 if self.get('QB_TCPSERIAL_OPT'):
Brad Bishop15ae2502019-06-18 21:44:24 -0400819 self.qemu_opt_script += ' ' + self.get('QB_TCPSERIAL_OPT').replace('@PORT@', port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600820 else:
Brad Bishop15ae2502019-06-18 21:44:24 -0400821 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
822
823 if len(ports) > 1:
824 for port in ports[1:]:
825 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500826
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600827 def check_and_set(self):
828 """Check configs sanity and set when needed"""
829 self.validate_paths()
Andrew Geissler82c905d2020-04-13 13:39:40 -0500830 if not self.slirp_enabled and not self.net_bridge:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500831 check_tun()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600832 # Check audio
833 if self.audio_enabled:
834 if not self.get('QB_AUDIO_DRV'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500835 raise RunQemuError("QB_AUDIO_DRV is NULL, this board doesn't support audio")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600836 if not self.get('QB_AUDIO_OPT'):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800837 logger.warning('QB_AUDIO_OPT is NULL, you may need define it to make audio work')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600838 else:
839 self.qemu_opt_script += ' %s' % self.get('QB_AUDIO_OPT')
840 os.putenv('QEMU_AUDIO_DRV', self.get('QB_AUDIO_DRV'))
841 else:
842 os.putenv('QEMU_AUDIO_DRV', 'none')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500843
Brad Bishop15ae2502019-06-18 21:44:24 -0400844 self.check_qemu_system()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600845 self.check_kvm()
846 self.check_fstype()
847 self.check_rootfs()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500848 self.check_ovmf()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600849 self.check_kernel()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400850 self.check_dtb()
Brad Bishopc68388fc2019-08-26 01:33:31 -0400851 self.check_bios()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600852 self.check_mem()
853 self.check_tcpserial()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500854
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600855 def read_qemuboot(self):
856 if not self.qemuboot:
857 if self.get('DEPLOY_DIR_IMAGE'):
858 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600859 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800860 logger.warning("Can't find qemuboot conf file, DEPLOY_DIR_IMAGE is NULL!")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600861 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500862
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600863 if self.rootfs and not os.path.exists(self.rootfs):
864 # Lazy rootfs
865 machine = self.get('MACHINE')
866 if not machine:
867 machine = os.path.basename(deploy_dir_image)
868 self.qemuboot = "%s/%s-%s.qemuboot.conf" % (deploy_dir_image,
869 self.rootfs, machine)
870 else:
871 cmd = 'ls -t %s/*.qemuboot.conf' % deploy_dir_image
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500872 logger.debug('Running %s...' % cmd)
873 try:
874 qbs = subprocess.check_output(cmd, shell=True).decode('utf-8')
875 except subprocess.CalledProcessError as err:
876 raise RunQemuError(err)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600877 if qbs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500878 for qb in qbs.split():
879 # Don't use initramfs when other choices unless fstype is ramfs
880 if '-initramfs-' in os.path.basename(qb) and self.fstype != 'cpio.gz':
881 continue
882 self.qemuboot = qb
883 break
884 if not self.qemuboot:
885 # Use the first one when no choice
886 self.qemuboot = qbs.split()[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600887 self.qbconfload = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500888
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600889 if not self.qemuboot:
890 # If we haven't found a .qemuboot.conf at this point it probably
891 # doesn't exist, continue without
892 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500893
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600894 if not os.path.exists(self.qemuboot):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500895 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 -0500896
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500897 logger.debug('CONFFILE: %s' % self.qemuboot)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500898
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600899 cf = configparser.ConfigParser()
900 cf.read(self.qemuboot)
901 for k, v in cf.items('config_bsp'):
902 k_upper = k.upper()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500903 if v.startswith("../"):
904 v = os.path.abspath(os.path.dirname(self.qemuboot) + "/" + v)
905 elif v == ".":
906 v = os.path.dirname(self.qemuboot)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600907 self.set(k_upper, v)
908
909 def validate_paths(self):
910 """Ensure all relevant path variables are set"""
911 # When we're started with a *.qemuboot.conf arg assume that image
912 # artefacts are relative to that file, rather than in whatever
913 # directory DEPLOY_DIR_IMAGE in the conf file points to.
914 if self.qbconfload:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500915 imgdir = os.path.realpath(os.path.dirname(self.qemuboot))
916 if imgdir != os.path.realpath(self.get('DEPLOY_DIR_IMAGE')):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600917 logger.info('Setting DEPLOY_DIR_IMAGE to folder containing %s (%s)' % (self.qemuboot, imgdir))
918 self.set('DEPLOY_DIR_IMAGE', imgdir)
919
920 # If the STAGING_*_NATIVE directories from the config file don't exist
921 # and we're in a sourced OE build directory try to extract the paths
922 # from `bitbake -e`
923 havenative = os.path.exists(self.get('STAGING_DIR_NATIVE')) and \
924 os.path.exists(self.get('STAGING_BINDIR_NATIVE'))
925
926 if not havenative:
927 if not self.bitbake_e:
928 self.load_bitbake_env()
929
930 if self.bitbake_e:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500931 native_vars = ['STAGING_DIR_NATIVE']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600932 for nv in native_vars:
933 s = re.search('^%s="(.*)"' % nv, self.bitbake_e, re.M)
934 if s and s.group(1) != self.get(nv):
935 logger.info('Overriding conf file setting of %s to %s from Bitbake environment' % (nv, s.group(1)))
936 self.set(nv, s.group(1))
937 else:
938 # when we're invoked from a running bitbake instance we won't
939 # be able to call `bitbake -e`, then try:
940 # - get OE_TMPDIR from environment and guess paths based on it
941 # - get OECORE_NATIVE_SYSROOT from environment (for sdk)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500942 tmpdir = self.get('OE_TMPDIR')
943 oecore_native_sysroot = self.get('OECORE_NATIVE_SYSROOT')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600944 if tmpdir:
945 logger.info('Setting STAGING_DIR_NATIVE and STAGING_BINDIR_NATIVE relative to OE_TMPDIR (%s)' % tmpdir)
946 hostos, _, _, _, machine = os.uname()
947 buildsys = '%s-%s' % (machine, hostos.lower())
948 staging_dir_native = '%s/sysroots/%s' % (tmpdir, buildsys)
949 self.set('STAGING_DIR_NATIVE', staging_dir_native)
950 elif oecore_native_sysroot:
951 logger.info('Setting STAGING_DIR_NATIVE to OECORE_NATIVE_SYSROOT (%s)' % oecore_native_sysroot)
952 self.set('STAGING_DIR_NATIVE', oecore_native_sysroot)
953 if self.get('STAGING_DIR_NATIVE'):
954 # we have to assume that STAGING_BINDIR_NATIVE is at usr/bin
955 staging_bindir_native = '%s/usr/bin' % self.get('STAGING_DIR_NATIVE')
956 logger.info('Setting STAGING_BINDIR_NATIVE to %s' % staging_bindir_native)
957 self.set('STAGING_BINDIR_NATIVE', '%s/usr/bin' % self.get('STAGING_DIR_NATIVE'))
958
959 def print_config(self):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500960 logoutput = ['Continuing with the following parameters:']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600961 if not self.fstype in self.vmtypes:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500962 logoutput.append('KERNEL: [%s]' % self.kernel)
Brad Bishopc68388fc2019-08-26 01:33:31 -0400963 if self.bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500964 logoutput.append('BIOS: [%s]' % self.bios)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600965 if self.dtb:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500966 logoutput.append('DTB: [%s]' % self.dtb)
967 logoutput.append('MACHINE: [%s]' % self.get('MACHINE'))
Brad Bishop15ae2502019-06-18 21:44:24 -0400968 try:
969 fstype_flags = ' (' + ', '.join(self.fsinfo[self.fstype]) + ')'
970 except KeyError:
971 fstype_flags = ''
Andrew Geissler82c905d2020-04-13 13:39:40 -0500972 logoutput.append('FSTYPE: [%s%s]' % (self.fstype, fstype_flags))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600973 if self.fstype == 'nfs':
Andrew Geissler82c905d2020-04-13 13:39:40 -0500974 logoutput.append('NFS_DIR: [%s]' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600975 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500976 logoutput.append('ROOTFS: [%s]' % self.rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500977 if self.ovmf_bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500978 logoutput.append('OVMF: %s' % self.ovmf_bios)
Brad Bishop08902b02019-08-20 09:16:51 -0400979 if (self.ovmf_secboot_pkkek1):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500980 logoutput.append('SECBOOT PKKEK1: [%s...]' % self.ovmf_secboot_pkkek1[0:100])
981 logoutput.append('CONFFILE: [%s]' % self.qemuboot)
982 logoutput.append('')
983 logger.info('\n'.join(logoutput))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600984
985 def setup_nfs(self):
986 if not self.nfs_server:
987 if self.slirp_enabled:
988 self.nfs_server = '10.0.2.2'
989 else:
990 self.nfs_server = '192.168.7.1'
991
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500992 # Figure out a new nfs_instance to allow multiple qemus running.
Brad Bishop977dc1a2019-02-06 16:01:43 -0500993 ps = subprocess.check_output(("ps", "auxww")).decode('utf-8')
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500994 pattern = '/bin/unfsd .* -i .*\.pid -e .*/exports([0-9]+) '
995 all_instances = re.findall(pattern, ps, re.M)
996 if all_instances:
997 all_instances.sort(key=int)
998 self.nfs_instance = int(all_instances.pop()) + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600999
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001000 nfsd_port = 3049 + 2 * self.nfs_instance
1001 mountd_port = 3048 + 2 * self.nfs_instance
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001002
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001003 # Export vars for runqemu-export-rootfs
1004 export_dict = {
1005 'NFS_INSTANCE': self.nfs_instance,
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001006 'NFSD_PORT': nfsd_port,
1007 'MOUNTD_PORT': mountd_port,
1008 }
1009 for k, v in export_dict.items():
1010 # Use '%s' since they are integers
1011 os.putenv(k, '%s' % v)
1012
Andrew Geissler82c905d2020-04-13 13:39:40 -05001013 self.unfs_opts="nfsvers=3,port=%s,tcp,mountport=%s" % (nfsd_port, mountd_port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001014
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001015 # Extract .tar.bz2 or .tar.bz if no nfs dir
1016 if not (self.rootfs and os.path.isdir(self.rootfs)):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001017 src_prefix = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'))
1018 dest = "%s-nfsroot" % src_prefix
1019 if os.path.exists('%s.pseudo_state' % dest):
1020 logger.info('Use %s as NFS_DIR' % dest)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001021 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001022 else:
1023 src = ""
1024 src1 = '%s.tar.bz2' % src_prefix
1025 src2 = '%s.tar.gz' % src_prefix
1026 if os.path.exists(src1):
1027 src = src1
1028 elif os.path.exists(src2):
1029 src = src2
1030 if not src:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001031 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 -06001032 logger.info('NFS_DIR not found, extracting %s to %s' % (src, dest))
Brad Bishop977dc1a2019-02-06 16:01:43 -05001033 cmd = ('runqemu-extract-sdk', src, dest)
1034 logger.info('Running %s...' % str(cmd))
1035 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001036 raise RunQemuError('Failed to run %s' % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001037 self.rootfs = dest
Andrew Geisslerc926e172021-05-07 16:11:35 -05001038 self.cleanup_files.append(self.rootfs)
1039 self.cleanup_files.append('%s.pseudo_state' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001040
1041 # Start the userspace NFS server
Brad Bishop977dc1a2019-02-06 16:01:43 -05001042 cmd = ('runqemu-export-rootfs', 'start', self.rootfs)
1043 logger.info('Running %s...' % str(cmd))
1044 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001045 raise RunQemuError('Failed to run %s' % cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001046
1047 self.nfs_running = True
1048
Andrew Geissler82c905d2020-04-13 13:39:40 -05001049 def setup_net_bridge(self):
1050 self.set('NETWORK_CMD', '-netdev bridge,br=%s,id=net0,helper=%s -device virtio-net-pci,netdev=net0 ' % (
1051 self.net_bridge, os.path.join(self.bindir_native, 'qemu-oe-bridge-helper')))
1052
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001053 def setup_slirp(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001054 """Setup user networking"""
1055
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001056 if self.fstype == 'nfs':
1057 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001058 netconf = " " + self.cmdline_ip_slirp
1059 logger.info("Network configuration:%s", netconf)
1060 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001061 # Port mapping
1062 hostfwd = ",hostfwd=tcp::2222-:22,hostfwd=tcp::2323-:23"
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001063 qb_slirp_opt_default = "-netdev user,id=net0%s,tftp=%s" % (hostfwd, self.get('DEPLOY_DIR_IMAGE'))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001064 qb_slirp_opt = self.get('QB_SLIRP_OPT') or qb_slirp_opt_default
1065 # Figure out the port
1066 ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt)
1067 ports = [int(i) for i in ports]
1068 mac = 2
Brad Bishop08902b02019-08-20 09:16:51 -04001069
1070 lockdir = "/tmp/qemu-port-locks"
1071 if not os.path.exists(lockdir):
1072 # There might be a race issue when multi runqemu processess are
1073 # running at the same time.
1074 try:
1075 os.mkdir(lockdir)
1076 os.chmod(lockdir, 0o777)
1077 except FileExistsError:
1078 pass
1079
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001080 # Find a free port to avoid conflicts
1081 for p in ports[:]:
1082 p_new = p
Brad Bishop08902b02019-08-20 09:16:51 -04001083 while not self.check_free_port('localhost', p_new, lockdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001084 p_new += 1
1085 mac += 1
1086 while p_new in ports:
1087 p_new += 1
1088 mac += 1
1089 if p != p_new:
1090 ports.append(p_new)
1091 qb_slirp_opt = re.sub(':%s-' % p, ':%s-' % p_new, qb_slirp_opt)
1092 logger.info("Port forward changed: %s -> %s" % (p, p_new))
1093 mac = "%s%02x" % (self.mac_slirp, mac)
1094 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qb_slirp_opt))
1095 # Print out port foward
1096 hostfwd = re.findall('(hostfwd=[^,]*)', qb_slirp_opt)
1097 if hostfwd:
1098 logger.info('Port forward: %s' % ' '.join(hostfwd))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001099
1100 def setup_tap(self):
1101 """Setup tap"""
1102
1103 # This file is created when runqemu-gen-tapdevs creates a bank of tap
1104 # devices, indicating that the user should not bring up new ones using
1105 # sudo.
1106 nosudo_flag = '/etc/runqemu-nosudo'
1107 self.qemuifup = shutil.which('runqemu-ifup')
1108 self.qemuifdown = shutil.which('runqemu-ifdown')
1109 ip = shutil.which('ip')
1110 lockdir = "/tmp/qemu-tap-locks"
1111
1112 if not (self.qemuifup and self.qemuifdown and ip):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001113 logger.error("runqemu-ifup: %s" % self.qemuifup)
1114 logger.error("runqemu-ifdown: %s" % self.qemuifdown)
1115 logger.error("ip: %s" % ip)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001116 raise OEPathError("runqemu-ifup, runqemu-ifdown or ip not found")
1117
1118 if not os.path.exists(lockdir):
1119 # There might be a race issue when multi runqemu processess are
1120 # running at the same time.
1121 try:
1122 os.mkdir(lockdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001123 os.chmod(lockdir, 0o777)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001124 except FileExistsError:
1125 pass
1126
Brad Bishop977dc1a2019-02-06 16:01:43 -05001127 cmd = (ip, 'link')
1128 logger.debug('Running %s...' % str(cmd))
1129 ip_link = subprocess.check_output(cmd).decode('utf-8')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001130 # Matches line like: 6: tap0: <foo>
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001131 possibles = re.findall('^[0-9]+: +(tap[0-9]+): <.*', ip_link, re.M)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001132 tap = ""
1133 for p in possibles:
1134 lockfile = os.path.join(lockdir, p)
1135 if os.path.exists('%s.skip' % lockfile):
1136 logger.info('Found %s.skip, skipping %s' % (lockfile, p))
1137 continue
Brad Bishop08902b02019-08-20 09:16:51 -04001138 self.taplock = lockfile + '.lock'
1139 if self.acquire_taplock(error=False):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001140 tap = p
1141 logger.info("Using preconfigured tap device %s" % tap)
1142 logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap))
1143 break
1144
1145 if not tap:
1146 if os.path.exists(nosudo_flag):
1147 logger.error("Error: There are no available tap devices to use for networking,")
1148 logger.error("and I see %s exists, so I am not going to try creating" % nosudo_flag)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001149 raise RunQemuError("a new one with sudo.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001150
1151 gid = os.getgid()
1152 uid = os.getuid()
1153 logger.info("Setting up tap interface under sudo")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001154 cmd = ('sudo', self.qemuifup, str(uid), str(gid), self.bindir_native)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001155 try:
1156 tap = subprocess.check_output(cmd).decode('utf-8').strip()
1157 except subprocess.CalledProcessError as e:
1158 logger.error('Setting up tap device failed:\n%s\nRun runqemu-gen-tapdevs to manually create one.' % str(e))
1159 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001160 lockfile = os.path.join(lockdir, tap)
Brad Bishop08902b02019-08-20 09:16:51 -04001161 self.taplock = lockfile + '.lock'
1162 self.acquire_taplock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001163 self.cleantap = True
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001164 logger.debug('Created tap: %s' % tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001165
1166 if not tap:
1167 logger.error("Failed to setup tap device. Run runqemu-gen-tapdevs to manually create.")
Andrew Geissler82c905d2020-04-13 13:39:40 -05001168 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001169 self.tap = tap
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001170 tapnum = int(tap[3:])
1171 gateway = tapnum * 2 + 1
1172 client = gateway + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001173 if self.fstype == 'nfs':
1174 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001175 netconf = " " + self.cmdline_ip_tap
1176 netconf = netconf.replace('@CLIENT@', str(client))
1177 netconf = netconf.replace('@GATEWAY@', str(gateway))
1178 logger.info("Network configuration:%s", netconf)
1179 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001180 mac = "%s%02x" % (self.mac_tap, client)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001181 qb_tap_opt = self.get('QB_TAP_OPT')
1182 if qb_tap_opt:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001183 qemu_tap_opt = qb_tap_opt.replace('@TAP@', tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001184 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001185 qemu_tap_opt = "-netdev tap,id=net0,ifname=%s,script=no,downscript=no" % (self.tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001186
1187 if self.vhost_enabled:
1188 qemu_tap_opt += ',vhost=on'
1189
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001190 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qemu_tap_opt))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001191
1192 def setup_network(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001193 if self.get('QB_NET') == 'none':
1194 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001195 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001196 self.saved_stty = subprocess.check_output(("stty", "-g")).decode('utf-8').strip()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001197 self.network_device = self.get('QB_NETWORK_DEVICE') or self.network_device
Andrew Geissler82c905d2020-04-13 13:39:40 -05001198 if self.net_bridge:
1199 self.setup_net_bridge()
1200 elif self.slirp_enabled:
1201 self.cmdline_ip_slirp = self.get('QB_CMDLINE_IP_SLIRP') or self.cmdline_ip_slirp
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001202 self.setup_slirp()
1203 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001204 self.cmdline_ip_tap = self.get('QB_CMDLINE_IP_TAP') or self.cmdline_ip_tap
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001205 self.setup_tap()
1206
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001207 def setup_rootfs(self):
1208 if self.get('QB_ROOTFS') == 'none':
1209 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001210 if 'wic.' in self.fstype:
1211 self.fstype = self.fstype[4:]
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001212 rootfs_format = self.fstype if self.fstype in ('vmdk', 'vhd', 'vhdx', 'qcow2', 'vdi') else 'raw'
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001213
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001214 tmpfsdir = os.environ.get("RUNQEMU_TMPFS_DIR", None)
1215 if self.snapshot and tmpfsdir:
1216 newrootfs = os.path.join(tmpfsdir, os.path.basename(self.rootfs)) + "." + str(os.getpid())
Andrew Geissler09036742021-06-25 14:25:14 -05001217 logger.info("Copying rootfs to %s" % newrootfs)
1218 copy_start = time.time()
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001219 shutil.copyfile(self.rootfs, newrootfs)
Andrew Geissler09036742021-06-25 14:25:14 -05001220 logger.info("Copy done in %s seconds" % (time.time() - copy_start))
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001221 self.rootfs = newrootfs
1222 # Don't need a second copy now!
1223 self.snapshot = False
Andrew Geisslerc926e172021-05-07 16:11:35 -05001224 self.cleanup_files.append(newrootfs)
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001225
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001226 qb_rootfs_opt = self.get('QB_ROOTFS_OPT')
1227 if qb_rootfs_opt:
1228 self.rootfs_options = qb_rootfs_opt.replace('@ROOTFS@', self.rootfs)
1229 else:
1230 self.rootfs_options = '-drive file=%s,if=virtio,format=%s' % (self.rootfs, rootfs_format)
1231
Andrew Geissler82c905d2020-04-13 13:39:40 -05001232 qb_rootfs_extra_opt = self.get("QB_ROOTFS_EXTRA_OPT")
1233 if qb_rootfs_extra_opt and not qb_rootfs_extra_opt.startswith(","):
1234 qb_rootfs_extra_opt = "," + qb_rootfs_extra_opt
1235
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001236 if self.fstype in ('cpio.gz', 'cpio'):
1237 self.kernel_cmdline = 'root=/dev/ram0 rw debugshell'
1238 self.rootfs_options = '-initrd %s' % self.rootfs
1239 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001240 vm_drive = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001241 if self.fstype in self.vmtypes:
1242 if self.fstype == 'iso':
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001243 vm_drive = '-drive file=%s,if=virtio,media=cdrom' % self.rootfs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001244 elif self.get('QB_DRIVE_TYPE'):
1245 drive_type = self.get('QB_DRIVE_TYPE')
1246 if drive_type.startswith("/dev/sd"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001247 logger.info('Using scsi drive')
Andrew Geissler82c905d2020-04-13 13:39:40 -05001248 vm_drive = '-drive if=none,id=hd,file=%s,format=%s -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd%s' \
1249 % (self.rootfs, rootfs_format, qb_rootfs_extra_opt)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001250 elif drive_type.startswith("/dev/hd"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001251 logger.info('Using ide drive')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001252 vm_drive = "-drive file=%s,format=%s" % (self.rootfs, rootfs_format)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001253 elif drive_type.startswith("/dev/vdb"):
1254 logger.info('Using block virtio drive');
1255 vm_drive = '-drive id=disk0,file=%s,if=none,format=%s -device virtio-blk-device,drive=disk0%s' \
1256 % (self.rootfs, rootfs_format,qb_rootfs_extra_opt)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001257 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001258 # virtio might have been selected explicitly (just use it), or
1259 # is used as fallback (then warn about that).
1260 if not drive_type.startswith("/dev/vd"):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001261 logger.warning("Unknown QB_DRIVE_TYPE: %s" % drive_type)
1262 logger.warning("Failed to figure out drive type, consider define or fix QB_DRIVE_TYPE")
1263 logger.warning('Trying to use virtio block drive')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001264 vm_drive = '-drive if=virtio,file=%s,format=%s' % (self.rootfs, rootfs_format)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001265
1266 # All branches above set vm_drive.
Andrew Geissler475cb722020-07-10 16:00:51 -05001267 self.rootfs_options = vm_drive
1268 if not self.fstype in self.vmtypes:
1269 self.rootfs_options += ' -no-reboot'
Andrew Geissler595f6302022-01-24 19:11:47 +00001270
1271 # By default, ' rw' is appended to QB_KERNEL_ROOT unless either ro or rw is explicitly passed.
1272 qb_kernel_root = self.get('QB_KERNEL_ROOT')
1273 qb_kernel_root_l = qb_kernel_root.split()
1274 if not ('ro' in qb_kernel_root_l or 'rw' in qb_kernel_root_l):
1275 qb_kernel_root += ' rw'
1276 self.kernel_cmdline = 'root=%s' % qb_kernel_root
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001277
1278 if self.fstype == 'nfs':
1279 self.rootfs_options = ''
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001280 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 -04001281 self.kernel_cmdline = 'root=%s rw' % k_root
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001282
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001283 if self.fstype == 'none':
1284 self.rootfs_options = ''
1285
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001286 self.set('ROOTFS_OPTIONS', self.rootfs_options)
1287
1288 def guess_qb_system(self):
1289 """attempt to determine the appropriate qemu-system binary"""
1290 mach = self.get('MACHINE')
1291 if not mach:
1292 search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*'
1293 if self.rootfs:
1294 match = re.match(search, self.rootfs)
1295 if match:
1296 mach = match.group(1)
1297 elif self.kernel:
1298 match = re.match(search, self.kernel)
1299 if match:
1300 mach = match.group(1)
1301
1302 if not mach:
1303 return None
1304
1305 if mach == 'qemuarm':
1306 qbsys = 'arm'
1307 elif mach == 'qemuarm64':
1308 qbsys = 'aarch64'
1309 elif mach == 'qemux86':
1310 qbsys = 'i386'
1311 elif mach == 'qemux86-64':
1312 qbsys = 'x86_64'
1313 elif mach == 'qemuppc':
1314 qbsys = 'ppc'
1315 elif mach == 'qemumips':
1316 qbsys = 'mips'
1317 elif mach == 'qemumips64':
1318 qbsys = 'mips64'
1319 elif mach == 'qemumipsel':
1320 qbsys = 'mipsel'
1321 elif mach == 'qemumips64el':
1322 qbsys = 'mips64el'
Brad Bishop316dfdd2018-06-25 12:45:53 -04001323 elif mach == 'qemuriscv64':
1324 qbsys = 'riscv64'
1325 elif mach == 'qemuriscv32':
1326 qbsys = 'riscv32'
Brad Bishop004d4992018-10-02 23:54:45 +02001327 else:
1328 logger.error("Unable to determine QEMU PC System emulator for %s machine." % mach)
1329 logger.error("As %s is not among valid QEMU machines such as," % mach)
1330 logger.error("qemux86-64, qemux86, qemuarm64, qemuarm, qemumips64, qemumips64el, qemumipsel, qemumips, qemuppc")
1331 raise RunQemuError("Set qb_system_name with suitable QEMU PC System emulator in .*qemuboot.conf.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001332
1333 return 'qemu-system-%s' % qbsys
1334
Brad Bishop15ae2502019-06-18 21:44:24 -04001335 def check_qemu_system(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001336 qemu_system = self.get('QB_SYSTEM_NAME')
1337 if not qemu_system:
1338 qemu_system = self.guess_qb_system()
1339 if not qemu_system:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001340 raise RunQemuError("Failed to boot, QB_SYSTEM_NAME is NULL!")
Brad Bishop15ae2502019-06-18 21:44:24 -04001341 self.qemu_system = qemu_system
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001342
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001343 def setup_vga(self):
1344 if self.nographic == True:
1345 if self.sdl == True:
1346 raise RunQemuError('Option nographic makes no sense alongside the sdl option.')
1347 if self.gtk == True:
1348 raise RunQemuError('Option nographic makes no sense alongside the gtk option.')
1349 self.qemu_opt += ' -nographic'
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001350
1351 if self.novga == True:
1352 self.qemu_opt += ' -vga none'
1353 return
1354
1355 if (self.gl_es == True or self.gl == True) and (self.sdl == False and self.gtk == False):
1356 raise RunQemuError('Option gl/gl-es needs gtk or sdl option.')
1357
1358 if self.sdl == True or self.gtk == True or self.egl_headless == True:
Andrew Geissler595f6302022-01-24 19:11:47 +00001359 if self.gl or self.gl_es or self.egl_headless:
1360 self.qemu_opt += ' -device virtio-vga-gl '
1361 else:
1362 self.qemu_opt += ' -device virtio-vga '
1363
1364 self.qemu_opt += '-display '
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001365 if self.egl_headless == True:
Andrew Geissler595f6302022-01-24 19:11:47 +00001366 self.set_dri_path()
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001367 self.qemu_opt += 'egl-headless,'
1368 else:
1369 if self.sdl == True:
1370 self.qemu_opt += 'sdl,'
1371 elif self.gtk == True:
1372 self.qemu_opt += 'gtk,'
1373
1374 if self.gl == True:
Andrew Geissler595f6302022-01-24 19:11:47 +00001375 self.set_dri_path()
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001376 self.qemu_opt += 'gl=on,'
1377 elif self.gl_es == True:
Andrew Geissler595f6302022-01-24 19:11:47 +00001378 self.set_dri_path()
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001379 self.qemu_opt += 'gl=es,'
1380 self.qemu_opt += 'show-cursor=on'
1381
1382 self.qemu_opt += ' %s' %self.get('QB_GRAPHICS')
1383
1384 def setup_serial(self):
1385 # Setup correct kernel command line for serial
Andrew Geissler595f6302022-01-24 19:11:47 +00001386 if self.get('SERIAL_CONSOLES') and (self.serialstdio == True or self.serialconsole == True or self.nographic == True or self.tcpserial_portnum):
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001387 for entry in self.get('SERIAL_CONSOLES').split(' '):
1388 self.kernel_cmdline_script += ' console=%s' %entry.split(';')[1]
1389
1390 if self.serialstdio == True or self.nographic == True:
1391 self.qemu_opt += " -serial mon:stdio"
1392 else:
1393 self.qemu_opt += " -serial mon:vc"
1394 if self.serialconsole:
1395 if sys.stdin.isatty():
1396 subprocess.check_call(("stty", "intr", "^]"))
1397 logger.info("Interrupt character is '^]'")
1398
1399 self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT")
1400
1401 # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES).
1402 # If no serial or serialtcp options were specified, only ttyS0 is created
1403 # and sysvinit shows an error trying to enable ttyS1:
1404 # INIT: Id "S1" respawning too fast: disabled for 5 minutes
1405 serial_num = len(re.findall("-serial", self.qemu_opt))
1406 if serial_num < 2:
1407 self.qemu_opt += " -serial null"
1408
Brad Bishop15ae2502019-06-18 21:44:24 -04001409 def setup_final(self):
1410 qemu_bin = os.path.join(self.bindir_native, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001411
1412 # It is possible to have qemu-native in ASSUME_PROVIDED, and it won't
1413 # find QEMU in sysroot, it needs to use host's qemu.
1414 if not os.path.exists(qemu_bin):
1415 logger.info("QEMU binary not found in %s, trying host's QEMU" % qemu_bin)
1416 for path in (os.environ['PATH'] or '').split(':'):
Brad Bishop15ae2502019-06-18 21:44:24 -04001417 qemu_bin_tmp = os.path.join(path, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001418 logger.info("Trying: %s" % qemu_bin_tmp)
1419 if os.path.exists(qemu_bin_tmp):
1420 qemu_bin = qemu_bin_tmp
1421 if not os.path.isabs(qemu_bin):
1422 qemu_bin = os.path.abspath(qemu_bin)
1423 logger.info("Using host's QEMU: %s" % qemu_bin)
1424 break
1425
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001426 if not os.access(qemu_bin, os.X_OK):
1427 raise OEPathError("No QEMU binary '%s' could be found" % qemu_bin)
1428
Andrew Geisslereff27472021-10-29 15:35:00 -05001429 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 -05001430
1431 for ovmf in self.ovmf_bios:
1432 format = ovmf.rsplit('.', 1)[-1]
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001433 if format == "bin":
1434 format = "raw"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001435 self.qemu_opt += ' -drive if=pflash,format=%s,file=%s' % (format, ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001436
1437 self.qemu_opt += ' ' + self.qemu_opt_script
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001438
Brad Bishop08902b02019-08-20 09:16:51 -04001439 if self.ovmf_secboot_pkkek1:
1440 # Provide the Platform Key and first Key Exchange Key certificate as an
1441 # OEM string in the SMBIOS Type 11 table. Prepend the certificate string
1442 # with "application prefix" of the EnrollDefaultKeys.efi application
1443 self.qemu_opt += ' -smbios type=11,value=4e32566d-8e9e-4f52-81d3-5bb9715f9727:' \
1444 + self.ovmf_secboot_pkkek1
1445
Andrew Geissler99467da2019-02-25 18:54:23 -06001446 # Append qemuparams to override previous settings
1447 if self.qemuparams:
1448 self.qemu_opt += ' ' + self.qemuparams
1449
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001450 if self.snapshot:
1451 self.qemu_opt += " -snapshot"
1452
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001453 self.setup_serial()
1454 self.setup_vga()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001455
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001456 def start_qemu(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001457 import shlex
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001458 if self.kernel:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001459 kernel_opts = "-kernel %s -append '%s %s %s %s'" % (self.kernel, self.kernel_cmdline,
1460 self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'),
1461 self.bootparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001462 if self.dtb:
1463 kernel_opts += " -dtb %s" % self.dtb
1464 else:
1465 kernel_opts = ""
Patrick Williams213cb262021-08-07 19:21:33 -05001466
1467 if self.bios:
1468 self.qemu_opt += " -bios %s" % self.bios
1469
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001470 cmd = "%s %s" % (self.qemu_opt, kernel_opts)
Brad Bishop004d4992018-10-02 23:54:45 +02001471 cmds = shlex.split(cmd)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001472 logger.info('Running %s\n' % cmd)
Brad Bishopf86d0552018-12-04 14:18:15 -08001473 pass_fds = []
Brad Bishop08902b02019-08-20 09:16:51 -04001474 if self.taplock_descriptor:
1475 pass_fds = [self.taplock_descriptor.fileno()]
1476 if len(self.portlocks):
1477 for descriptor in self.portlocks.values():
1478 pass_fds.append(descriptor.fileno())
Brad Bishopf86d0552018-12-04 14:18:15 -08001479 process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds)
Brad Bishop004d4992018-10-02 23:54:45 +02001480 self.qemupid = process.pid
1481 retcode = process.wait()
1482 if retcode:
1483 if retcode == -signal.SIGTERM:
1484 logger.info("Qemu terminated by SIGTERM")
1485 else:
1486 logger.error("Failed to run qemu: %s", process.stderr.read().decode())
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001487
1488 def cleanup(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001489 if self.cleaned:
1490 return
1491
1492 # avoid dealing with SIGTERM when cleanup function is running
1493 signal.signal(signal.SIGTERM, signal.SIG_IGN)
1494
1495 logger.info("Cleaning up")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001496 if self.cleantap:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001497 cmd = ('sudo', self.qemuifdown, self.tap, self.bindir_native)
1498 logger.debug('Running %s' % str(cmd))
1499 subprocess.check_call(cmd)
Brad Bishop08902b02019-08-20 09:16:51 -04001500 self.release_taplock()
1501 self.release_portlock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001502
1503 if self.nfs_running:
1504 logger.info("Shutting down the userspace NFS server...")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001505 cmd = ("runqemu-export-rootfs", "stop", self.rootfs)
1506 logger.debug('Running %s' % str(cmd))
1507 subprocess.check_call(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001508
1509 if self.saved_stty:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001510 subprocess.check_call(("stty", self.saved_stty))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001511
Andrew Geisslerc926e172021-05-07 16:11:35 -05001512 if self.cleanup_files:
1513 for ent in self.cleanup_files:
1514 logger.info('Removing %s' % ent)
1515 if os.path.isfile(ent):
1516 os.remove(ent)
1517 else:
1518 shutil.rmtree(ent)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001519
Brad Bishop004d4992018-10-02 23:54:45 +02001520 self.cleaned = True
1521
Andrew Geissler82c905d2020-04-13 13:39:40 -05001522 def run_bitbake_env(self, mach=None):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001523 bitbake = shutil.which('bitbake')
1524 if not bitbake:
1525 return
1526
1527 if not mach:
1528 mach = self.get('MACHINE')
1529
Andrew Geissler82c905d2020-04-13 13:39:40 -05001530 multiconfig = self.get('MULTICONFIG')
1531 if multiconfig:
1532 multiconfig = "mc:%s" % multiconfig
1533
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001534 if mach:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001535 cmd = 'MACHINE=%s bitbake -e %s' % (mach, multiconfig)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001536 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001537 cmd = 'bitbake -e %s' % multiconfig
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001538
1539 logger.info('Running %s...' % cmd)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001540 return subprocess.check_output(cmd, shell=True).decode('utf-8')
1541
1542 def load_bitbake_env(self, mach=None):
1543 if self.bitbake_e:
1544 return
1545
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001546 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001547 self.bitbake_e = self.run_bitbake_env(mach=mach)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001548 except subprocess.CalledProcessError as err:
1549 self.bitbake_e = ''
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001550 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 -06001551
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001552 def validate_combos(self):
1553 if (self.fstype in self.vmtypes) and self.kernel:
1554 raise RunQemuError("%s doesn't need kernel %s!" % (self.fstype, self.kernel))
1555
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001556 @property
1557 def bindir_native(self):
1558 result = self.get('STAGING_BINDIR_NATIVE')
1559 if result and os.path.exists(result):
1560 return result
1561
Andrew Geissler82c905d2020-04-13 13:39:40 -05001562 cmd = ['bitbake', '-e']
1563 multiconfig = self.get('MULTICONFIG')
1564 if multiconfig:
1565 cmd.append('mc:%s:qemu-helper-native' % multiconfig)
1566 else:
1567 cmd.append('qemu-helper-native')
1568
Brad Bishop977dc1a2019-02-06 16:01:43 -05001569 logger.info('Running %s...' % str(cmd))
1570 out = subprocess.check_output(cmd).decode('utf-8')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001571
1572 match = re.search('^STAGING_BINDIR_NATIVE="(.*)"', out, re.M)
1573 if match:
1574 result = match.group(1)
1575 if os.path.exists(result):
1576 self.set('STAGING_BINDIR_NATIVE', result)
1577 return result
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001578 raise RunQemuError("Native sysroot directory %s doesn't exist" % result)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001579 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001580 raise RunQemuError("Can't find STAGING_BINDIR_NATIVE in '%s' output" % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001581
1582
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001583def main():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001584 if "help" in sys.argv or '-h' in sys.argv or '--help' in sys.argv:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001585 print_usage()
1586 return 0
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001587 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001588 config = BaseConfig()
Brad Bishop004d4992018-10-02 23:54:45 +02001589
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001590 renice = os.path.expanduser("~/bin/runqemu-renice")
1591 if os.path.exists(renice):
1592 logger.info('Using %s to renice' % renice)
1593 subprocess.check_call([renice, str(os.getpid())])
1594
Brad Bishop004d4992018-10-02 23:54:45 +02001595 def sigterm_handler(signum, frame):
1596 logger.info("SIGTERM received")
Andrew Geissler595f6302022-01-24 19:11:47 +00001597 if config.qemupid:
1598 os.kill(config.qemupid, signal.SIGTERM)
Brad Bishop004d4992018-10-02 23:54:45 +02001599 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001600 # Deliberately ignore the return code of 'tput smam'.
1601 subprocess.call(["tput", "smam"])
Brad Bishop004d4992018-10-02 23:54:45 +02001602 signal.signal(signal.SIGTERM, sigterm_handler)
1603
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001604 config.check_args()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001605 config.read_qemuboot()
1606 config.check_and_set()
1607 # Check whether the combos is valid or not
1608 config.validate_combos()
1609 config.print_config()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001610 config.setup_network()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001611 config.setup_rootfs()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001612 config.setup_final()
1613 config.start_qemu()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001614 except RunQemuError as err:
1615 logger.error(err)
1616 return 1
1617 except Exception as err:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001618 import traceback
1619 traceback.print_exc()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001620 return 1
1621 finally:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001622 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001623 # Deliberately ignore the return code of 'tput smam'.
1624 subprocess.call(["tput", "smam"])
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001625
1626if __name__ == "__main__":
1627 sys.exit(main())