blob: df4ee21d53167990880eadecdad4c5aa1f2f684c [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
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050021
Brad Bishopd7bf8c12018-02-25 22:55:05 -050022class RunQemuError(Exception):
23 """Custom exception to raise on known errors."""
24 pass
25
26class OEPathError(RunQemuError):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060027 """Custom Exception to give better guidance on missing binaries"""
28 def __init__(self, message):
Brad Bishopd7bf8c12018-02-25 22:55:05 -050029 super().__init__("In order for this script to dynamically infer paths\n \
Patrick Williamsc0f7c042017-02-23 20:41:17 -060030kernels or filesystem images, you either need bitbake in your PATH\n \
31or to source oe-init-build-env before running this script.\n\n \
32Dynamic path inference can be avoided by passing a *.qemuboot.conf to\n \
Brad Bishopd7bf8c12018-02-25 22:55:05 -050033runqemu, i.e. `runqemu /path/to/my-image-name.qemuboot.conf`\n\n %s" % message)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060034
35
36def create_logger():
37 logger = logging.getLogger('runqemu')
38 logger.setLevel(logging.INFO)
39
40 # create console handler and set level to debug
41 ch = logging.StreamHandler()
Brad Bishopd7bf8c12018-02-25 22:55:05 -050042 ch.setLevel(logging.DEBUG)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060043
44 # create formatter
45 formatter = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
46
47 # add formatter to ch
48 ch.setFormatter(formatter)
49
50 # add ch to logger
51 logger.addHandler(ch)
52
53 return logger
54
55logger = create_logger()
56
57def print_usage():
58 print("""
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050059Usage: you can run this script with any valid combination
60of the following environment variables (in any order):
61 KERNEL - the kernel image file to use
Brad Bishopc68388fc2019-08-26 01:33:31 -040062 BIOS - the bios image file to use
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050063 ROOTFS - the rootfs image file or nfsroot directory to use
Brad Bishop316dfdd2018-06-25 12:45:53 -040064 DEVICE_TREE - the device tree blob to use
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050065 MACHINE - the machine name (optional, autodetected from KERNEL filename if unspecified)
66 Simplified QEMU command-line options can be passed with:
Patrick Williamsc0f7c042017-02-23 20:41:17 -060067 nographic - disable video console
Andrew Geissler90fd73c2021-03-05 15:25:55 -060068 novga - Disable VGA emulation completely
Brad Bishopa34c0302019-09-23 22:34:48 -040069 sdl - choose the SDL UI frontend
70 gtk - choose the Gtk UI frontend
Brad Bishop6dbb3162019-11-25 09:41:34 -050071 gl - enable virgl-based GL acceleration (also needs gtk or sdl options)
72 gl-es - enable virgl-based GL acceleration, using OpenGL ES (also needs gtk or sdl options)
73 egl-headless - enable headless EGL output; use vnc (via publicvnc option) or spice to see it
Patrick Williamsc0f7c042017-02-23 20:41:17 -060074 serial - enable a serial console on /dev/ttyS0
Brad Bishop19323692019-04-05 15:28:33 -040075 serialstdio - enable a serial console on the console (regardless of graphics mode)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060076 slirp - enable user networking, no root privileges is required
Brad Bishopa34c0302019-09-23 22:34:48 -040077 snapshot - don't write changes to back to images
Patrick Williamsc0f7c042017-02-23 20:41:17 -060078 kvm - enable KVM when running x86/x86_64 (VT-capable CPU required)
79 kvm-vhost - enable KVM with vhost when running x86/x86_64 (VT-capable CPU required)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050080 publicvnc - enable a VNC server open to all hosts
Patrick Williamsc0f7c042017-02-23 20:41:17 -060081 audio - enable audio
Brad Bishop6e60e8b2018-02-01 10:27:11 -050082 [*/]ovmf* - OVMF firmware file or base name for booting with UEFI
Patrick Williamsc0f7c042017-02-23 20:41:17 -060083 tcpserial=<port> - specify tcp serial port number
Patrick Williamsc0f7c042017-02-23 20:41:17 -060084 qemuparams=<xyz> - specify custom parameters to QEMU
85 bootparams=<xyz> - specify custom kernel parameters during boot
Brad Bishop6e60e8b2018-02-01 10:27:11 -050086 help, -h, --help: print this text
Brad Bishopd7bf8c12018-02-25 22:55:05 -050087 -d, --debug: Enable debug output
Brad Bishop79641f22019-09-10 07:20:22 -040088 -q, --quiet: Hide most output except error messages
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050089
90Examples:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050091 runqemu
Patrick Williamsc0f7c042017-02-23 20:41:17 -060092 runqemu qemuarm
93 runqemu tmp/deploy/images/qemuarm
Brad Bishop6e60e8b2018-02-01 10:27:11 -050094 runqemu tmp/deploy/images/qemux86/<qemuboot.conf>
Patrick Williamsc0f7c042017-02-23 20:41:17 -060095 runqemu qemux86-64 core-image-sato ext4
96 runqemu qemux86-64 wic-image-minimal wic
97 runqemu path/to/bzImage-qemux86.bin path/to/nfsrootdir/ serial
Andrew Geisslerd1e89492021-02-12 15:35:20 -060098 runqemu qemux86 iso/hddimg/wic.vmdk/wic.vhd/wic.vhdx/wic.qcow2/wic.vdi/ramfs/cpio.gz...
Patrick Williamsc0f7c042017-02-23 20:41:17 -060099 runqemu qemux86 qemuparams="-m 256"
100 runqemu qemux86 bootparams="psplash=false"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600101 runqemu path/to/<image>-<machine>.wic
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500102 runqemu path/to/<image>-<machine>.wic.vmdk
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600103 runqemu path/to/<image>-<machine>.wic.vhdx
104 runqemu path/to/<image>-<machine>.wic.vhd
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600105""")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500106
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600107def check_tun():
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500108 """Check /dev/net/tun"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600109 dev_tun = '/dev/net/tun'
110 if not os.path.exists(dev_tun):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500111 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 -0500112
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600113 if not os.access(dev_tun, os.W_OK):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500114 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 -0500115
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600116def get_first_file(cmds):
117 """Return first file found in wildcard cmds"""
118 for cmd in cmds:
119 all_files = glob.glob(cmd)
120 if all_files:
121 for f in all_files:
122 if not os.path.isdir(f):
123 return f
124 return ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500125
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600126class BaseConfig(object):
127 def __init__(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500128 # The self.d saved vars from self.set(), part of them are from qemuboot.conf
129 self.d = {'QB_KERNEL_ROOT': '/dev/vda'}
130
131 # Supported env vars, add it here if a var can be got from env,
132 # and don't use os.getenv in the code.
133 self.env_vars = ('MACHINE',
134 'ROOTFS',
135 'KERNEL',
Brad Bishopc68388fc2019-08-26 01:33:31 -0400136 'BIOS',
Brad Bishop316dfdd2018-06-25 12:45:53 -0400137 'DEVICE_TREE',
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500138 'DEPLOY_DIR_IMAGE',
139 'OE_TMPDIR',
140 'OECORE_NATIVE_SYSROOT',
Andrew Geissler82c905d2020-04-13 13:39:40 -0500141 'MULTICONFIG',
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500142 'SERIAL_CONSOLES',
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500143 )
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500144
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600145 self.qemu_opt = ''
146 self.qemu_opt_script = ''
Andrew Geissler99467da2019-02-25 18:54:23 -0600147 self.qemuparams = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600148 self.clean_nfs_dir = False
149 self.nfs_server = ''
150 self.rootfs = ''
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500151 # File name(s) of a OVMF firmware file or variable store,
152 # to be added with -drive if=pflash.
153 # Found in the same places as the rootfs, with or without one of
154 # these suffices: qcow2, bin.
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500155 self.ovmf_bios = []
Brad Bishop08902b02019-08-20 09:16:51 -0400156 # When enrolling default Secure Boot keys, the hypervisor
157 # must provide the Platform Key and the first Key Exchange Key
158 # certificate in the Type 11 SMBIOS table.
159 self.ovmf_secboot_pkkek1 = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600160 self.qemuboot = ''
161 self.qbconfload = False
162 self.kernel = ''
Brad Bishopc68388fc2019-08-26 01:33:31 -0400163 self.bios = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600164 self.kernel_cmdline = ''
165 self.kernel_cmdline_script = ''
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500166 self.bootparams = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600167 self.dtb = ''
168 self.fstype = ''
169 self.kvm_enabled = False
170 self.vhost_enabled = False
171 self.slirp_enabled = False
Andrew Geissler82c905d2020-04-13 13:39:40 -0500172 self.net_bridge = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600173 self.nfs_instance = 0
174 self.nfs_running = False
Brad Bishop19323692019-04-05 15:28:33 -0400175 self.serialconsole = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600176 self.serialstdio = False
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500177 self.nographic = False
178 self.sdl = False
179 self.gtk = False
180 self.gl = False
181 self.gl_es = False
182 self.egl_headless = False
183 self.novga = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600184 self.cleantap = False
185 self.saved_stty = ''
186 self.audio_enabled = False
187 self.tcpserial_portnum = ''
Brad Bishop08902b02019-08-20 09:16:51 -0400188 self.taplock = ''
189 self.taplock_descriptor = None
190 self.portlocks = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600191 self.bitbake_e = ''
192 self.snapshot = False
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600193 self.wictypes = ('wic', 'wic.vmdk', 'wic.qcow2', 'wic.vdi', "wic.vhd", "wic.vhdx")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500194 self.fstypes = ('ext2', 'ext3', 'ext4', 'jffs2', 'nfs', 'btrfs',
195 'cpio.gz', 'cpio', 'ramfs', 'tar.bz2', 'tar.gz')
Brad Bishop08902b02019-08-20 09:16:51 -0400196 self.vmtypes = ('hddimg', 'iso')
Brad Bishop15ae2502019-06-18 21:44:24 -0400197 self.fsinfo = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500198 self.network_device = "-device e1000,netdev=net0,mac=@MAC@"
Andrew Geissler82c905d2020-04-13 13:39:40 -0500199 self.cmdline_ip_slirp = "ip=dhcp"
200 self.cmdline_ip_tap = "ip=192.168.7.@CLIENT@::192.168.7.@GATEWAY@:255.255.255.0"
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500201 # Use different mac section for tap and slirp to avoid
202 # conflicts, e.g., when one is running with tap, the other is
203 # running with slirp.
204 # The last section is dynamic, which is for avoiding conflicts,
205 # when multiple qemus are running, e.g., when multiple tap or
206 # slirp qemus are running.
207 self.mac_tap = "52:54:00:12:34:"
208 self.mac_slirp = "52:54:00:12:35:"
Brad Bishop004d4992018-10-02 23:54:45 +0200209 # pid of the actual qemu process
210 self.qemupid = None
211 # avoid cleanup twice
212 self.cleaned = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500213
Brad Bishop08902b02019-08-20 09:16:51 -0400214 def acquire_taplock(self, error=True):
215 logger.debug("Acquiring lockfile %s..." % self.taplock)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600216 try:
Brad Bishop08902b02019-08-20 09:16:51 -0400217 self.taplock_descriptor = open(self.taplock, 'w')
218 fcntl.flock(self.taplock_descriptor, fcntl.LOCK_EX|fcntl.LOCK_NB)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600219 except Exception as e:
Brad Bishop08902b02019-08-20 09:16:51 -0400220 msg = "Acquiring lockfile %s failed: %s" % (self.taplock, e)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500221 if error:
222 logger.error(msg)
223 else:
224 logger.info(msg)
Brad Bishop08902b02019-08-20 09:16:51 -0400225 if self.taplock_descriptor:
226 self.taplock_descriptor.close()
227 self.taplock_descriptor = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600228 return False
229 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500230
Brad Bishop08902b02019-08-20 09:16:51 -0400231 def release_taplock(self):
232 if self.taplock_descriptor:
Brad Bishopf86d0552018-12-04 14:18:15 -0800233 logger.debug("Releasing lockfile for tap device '%s'" % self.tap)
Brad Bishop08902b02019-08-20 09:16:51 -0400234 fcntl.flock(self.taplock_descriptor, fcntl.LOCK_UN)
235 self.taplock_descriptor.close()
236 os.remove(self.taplock)
237 self.taplock_descriptor = None
238
239 def check_free_port(self, host, port, lockdir):
240 """ Check whether the port is free or not """
241 import socket
242 from contextlib import closing
243
244 lockfile = os.path.join(lockdir, str(port) + '.lock')
245 if self.acquire_portlock(lockfile):
246 with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
247 if sock.connect_ex((host, port)) == 0:
248 # Port is open, so not free
249 self.release_portlock(lockfile)
250 return False
251 else:
252 # Port is not open, so free
253 return True
254 else:
255 return False
256
257 def acquire_portlock(self, lockfile):
258 logger.debug("Acquiring lockfile %s..." % lockfile)
259 try:
260 portlock_descriptor = open(lockfile, 'w')
261 self.portlocks.update({lockfile: portlock_descriptor})
262 fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_EX|fcntl.LOCK_NB)
263 except Exception as e:
264 msg = "Acquiring lockfile %s failed: %s" % (lockfile, e)
265 logger.info(msg)
266 if lockfile in self.portlocks.keys() and self.portlocks[lockfile]:
267 self.portlocks[lockfile].close()
268 del self.portlocks[lockfile]
269 return False
270 return True
271
272 def release_portlock(self, lockfile=None):
273 if lockfile != None:
274 logger.debug("Releasing lockfile '%s'" % lockfile)
275 fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_UN)
276 self.portlocks[lockfile].close()
277 os.remove(lockfile)
278 del self.portlocks[lockfile]
279 elif len(self.portlocks):
280 for lockfile, descriptor in self.portlocks.items():
281 logger.debug("Releasing lockfile '%s'" % lockfile)
282 fcntl.flock(descriptor, fcntl.LOCK_UN)
283 descriptor.close()
284 os.remove(lockfile)
285 self.portlocks = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500286
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600287 def get(self, key):
288 if key in self.d:
289 return self.d.get(key)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500290 elif os.getenv(key):
291 return os.getenv(key)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600292 else:
293 return ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500294
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600295 def set(self, key, value):
296 self.d[key] = value
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500297
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600298 def is_deploy_dir_image(self, p):
299 if os.path.isdir(p):
300 if not re.search('.qemuboot.conf$', '\n'.join(os.listdir(p)), re.M):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500301 logger.debug("Can't find required *.qemuboot.conf in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600302 return False
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500303 if not any(map(lambda name: '-image-' in name, os.listdir(p))):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500304 logger.debug("Can't find *-image-* in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600305 return False
306 return True
307 else:
308 return False
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500309
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600310 def check_arg_fstype(self, fst):
311 """Check and set FSTYPE"""
Brad Bishop15ae2502019-06-18 21:44:24 -0400312 if fst not in self.fstypes + self.vmtypes + self.wictypes:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800313 logger.warning("Maybe unsupported FSTYPE: %s" % fst)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600314 if not self.fstype or self.fstype == fst:
315 if fst == 'ramfs':
316 fst = 'cpio.gz'
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500317 if fst in ('tar.bz2', 'tar.gz'):
318 fst = 'nfs'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600319 self.fstype = fst
320 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500321 raise RunQemuError("Conflicting: FSTYPE %s and %s" % (self.fstype, fst))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500322
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600323 def set_machine_deploy_dir(self, machine, deploy_dir_image):
324 """Set MACHINE and DEPLOY_DIR_IMAGE"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500325 logger.debug('MACHINE: %s' % machine)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600326 self.set("MACHINE", machine)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500327 logger.debug('DEPLOY_DIR_IMAGE: %s' % deploy_dir_image)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600328 self.set("DEPLOY_DIR_IMAGE", deploy_dir_image)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500329
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600330 def check_arg_nfs(self, p):
331 if os.path.isdir(p):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500332 self.rootfs = p
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600333 else:
334 m = re.match('(.*):(.*)', p)
335 self.nfs_server = m.group(1)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500336 self.rootfs = m.group(2)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600337 self.check_arg_fstype('nfs')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500338
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600339 def check_arg_path(self, p):
340 """
341 - Check whether it is <image>.qemuboot.conf or contains <image>.qemuboot.conf
342 - Check whether is a kernel file
343 - Check whether is a image file
344 - Check whether it is a nfs dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500345 - Check whether it is a OVMF flash file
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600346 """
347 if p.endswith('.qemuboot.conf'):
348 self.qemuboot = p
349 self.qbconfload = True
350 elif re.search('\.bin$', p) or re.search('bzImage', p) or \
351 re.search('zImage', p) or re.search('vmlinux', p) or \
352 re.search('fitImage', p) or re.search('uImage', p):
353 self.kernel = p
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500354 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 -0600355 self.rootfs = p
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500356 # Check filename against self.fstypes can hanlde <file>.cpio.gz,
357 # otherwise, its type would be "gz", which is incorrect.
358 fst = ""
359 for t in self.fstypes:
360 if p.endswith(t):
361 fst = t
362 break
363 if not fst:
364 m = re.search('.*\.(.*)$', self.rootfs)
365 if m:
366 fst = m.group(1)
367 if fst:
368 self.check_arg_fstype(fst)
369 qb = re.sub('\.' + fst + "$", '', self.rootfs)
370 qb = '%s%s' % (re.sub('\.rootfs$', '', qb), '.qemuboot.conf')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600371 if os.path.exists(qb):
372 self.qemuboot = qb
373 self.qbconfload = True
374 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800375 logger.warning("%s doesn't exist" % qb)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600376 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500377 raise RunQemuError("Can't find FSTYPE from: %s" % p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500378
379 elif os.path.isdir(p) or re.search(':', p) and re.search('/', p):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600380 if self.is_deploy_dir_image(p):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500381 logger.debug('DEPLOY_DIR_IMAGE: %s' % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600382 self.set("DEPLOY_DIR_IMAGE", p)
383 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500384 logger.debug("Assuming %s is an nfs rootfs" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600385 self.check_arg_nfs(p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500386 elif os.path.basename(p).startswith('ovmf'):
387 self.ovmf_bios.append(p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600388 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500389 raise RunQemuError("Unknown path arg %s" % p)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500390
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600391 def check_arg_machine(self, arg):
392 """Check whether it is a machine"""
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500393 if self.get('MACHINE') == arg:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600394 return
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500395 elif self.get('MACHINE') and self.get('MACHINE') != arg:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500396 raise RunQemuError("Maybe conflicted MACHINE: %s vs %s" % (self.get('MACHINE'), arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500397 elif re.search('/', arg):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500398 raise RunQemuError("Unknown arg: %s" % arg)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500399
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500400 logger.debug('Assuming MACHINE = %s' % arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500401
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600402 # if we're running under testimage, or similarly as a child
403 # of an existing bitbake invocation, we can't invoke bitbake
404 # to validate the MACHINE setting and must assume it's correct...
405 # FIXME: testimage.bbclass exports these two variables into env,
406 # are there other scenarios in which we need to support being
407 # invoked by bitbake?
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500408 deploy = self.get('DEPLOY_DIR_IMAGE')
409 bbchild = deploy and self.get('OE_TMPDIR')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600410 if bbchild:
411 self.set_machine_deploy_dir(arg, deploy)
412 return
413 # also check whether we're running under a sourced toolchain
414 # environment file
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500415 if self.get('OECORE_NATIVE_SYSROOT'):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600416 self.set("MACHINE", arg)
417 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500418
Andrew Geissler82c905d2020-04-13 13:39:40 -0500419 self.bitbake_e = self.run_bitbake_env(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600420 # bitbake -e doesn't report invalid MACHINE as an error, so
421 # let's check DEPLOY_DIR_IMAGE to make sure that it is a valid
422 # MACHINE.
423 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
424 if s:
425 deploy_dir_image = s.group(1)
426 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500427 raise RunQemuError("bitbake -e %s" % self.bitbake_e)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600428 if self.is_deploy_dir_image(deploy_dir_image):
429 self.set_machine_deploy_dir(arg, deploy_dir_image)
430 else:
431 logger.error("%s not a directory valid DEPLOY_DIR_IMAGE" % deploy_dir_image)
432 self.set("MACHINE", arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500433
Andrew Geisslerc182c622020-05-15 14:13:32 -0500434 def set_dri_path(self):
435 # As runqemu can be run within bitbake (when using testimage, for example),
436 # we need to ensure that we run host pkg-config, and that it does not
437 # get mis-directed to native build paths set by bitbake.
438 try:
439 del os.environ['PKG_CONFIG_PATH']
440 del os.environ['PKG_CONFIG_DIR']
441 del os.environ['PKG_CONFIG_LIBDIR']
442 del os.environ['PKG_CONFIG_SYSROOT_DIR']
443 except KeyError:
444 pass
445 try:
446 dripath = subprocess.check_output("PATH=/bin:/usr/bin:$PATH pkg-config --variable=dridriverdir dri", shell=True)
447 except subprocess.CalledProcessError as e:
448 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.")
449 os.environ['LIBGL_DRIVERS_PATH'] = dripath.decode('utf-8').strip()
450
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600451 def check_args(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500452 for debug in ("-d", "--debug"):
453 if debug in sys.argv:
454 logger.setLevel(logging.DEBUG)
455 sys.argv.remove(debug)
456
457 for quiet in ("-q", "--quiet"):
458 if quiet in sys.argv:
459 logger.setLevel(logging.ERROR)
460 sys.argv.remove(quiet)
461
Andrew Geisslerc182c622020-05-15 14:13:32 -0500462 if 'gl' not in sys.argv[1:] and 'gl-es' not in sys.argv[1:]:
463 os.environ['SDL_RENDER_DRIVER'] = 'software'
464
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600465 unknown_arg = ""
466 for arg in sys.argv[1:]:
Brad Bishop15ae2502019-06-18 21:44:24 -0400467 if arg in self.fstypes + self.vmtypes + self.wictypes:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600468 self.check_arg_fstype(arg)
469 elif arg == 'nographic':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500470 self.nographic = True
Brad Bishop19323692019-04-05 15:28:33 -0400471 elif arg == 'sdl':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500472 self.sdl = True
Brad Bishopa34c0302019-09-23 22:34:48 -0400473 elif arg == 'gtk':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500474 self.gtk = True
475 elif arg == 'gl':
476 self.gl = True
477 elif 'gl-es' in sys.argv[1:]:
478 self.gl_es = True
Brad Bishop19323692019-04-05 15:28:33 -0400479 elif arg == 'egl-headless':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500480 self.egl_headless = True
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600481 elif arg == 'novga':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500482 self.novga = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600483 elif arg == 'serial':
Brad Bishop19323692019-04-05 15:28:33 -0400484 self.serialconsole = True
485 elif arg == "serialstdio":
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600486 self.serialstdio = True
487 elif arg == 'audio':
488 logger.info("Enabling audio in qemu")
489 logger.info("Please install sound drivers in linux host")
490 self.audio_enabled = True
491 elif arg == 'kvm':
492 self.kvm_enabled = True
493 elif arg == 'kvm-vhost':
494 self.vhost_enabled = True
495 elif arg == 'slirp':
496 self.slirp_enabled = True
Andrew Geissler82c905d2020-04-13 13:39:40 -0500497 elif arg.startswith('bridge='):
498 self.net_bridge = '%s' % arg[len('bridge='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600499 elif arg == 'snapshot':
500 self.snapshot = True
501 elif arg == 'publicvnc':
502 self.qemu_opt_script += ' -vnc :0'
503 elif arg.startswith('tcpserial='):
Brad Bishop15ae2502019-06-18 21:44:24 -0400504 self.tcpserial_portnum = '%s' % arg[len('tcpserial='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600505 elif arg.startswith('qemuparams='):
Andrew Geissler99467da2019-02-25 18:54:23 -0600506 self.qemuparams = ' %s' % arg[len('qemuparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600507 elif arg.startswith('bootparams='):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500508 self.bootparams = arg[len('bootparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600509 elif os.path.exists(arg) or (re.search(':', arg) and re.search('/', arg)):
510 self.check_arg_path(os.path.abspath(arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500511 elif re.search(r'-image-|-image$', arg):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600512 # Lazy rootfs
513 self.rootfs = arg
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500514 elif arg.startswith('ovmf'):
515 self.ovmf_bios.append(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600516 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500517 # At last, assume it is the MACHINE
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600518 if (not unknown_arg) or unknown_arg == arg:
519 unknown_arg = arg
520 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500521 raise RunQemuError("Can't handle two unknown args: %s %s\n"
522 "Try 'runqemu help' on how to use it" % \
523 (unknown_arg, arg))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600524 # Check to make sure it is a valid machine
Brad Bishopa5c52ff2018-11-23 10:55:50 +1300525 if unknown_arg and self.get('MACHINE') != unknown_arg:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500526 if self.get('DEPLOY_DIR_IMAGE'):
527 machine = os.path.basename(self.get('DEPLOY_DIR_IMAGE'))
528 if unknown_arg == machine:
529 self.set("MACHINE", machine)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500530
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600531 self.check_arg_machine(unknown_arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500532
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500533 if not (self.get('DEPLOY_DIR_IMAGE') or self.qbconfload):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500534 self.load_bitbake_env()
535 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
536 if s:
537 self.set("DEPLOY_DIR_IMAGE", s.group(1))
538
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600539 def check_kvm(self):
540 """Check kvm and kvm-host"""
541 if not (self.kvm_enabled or self.vhost_enabled):
542 self.qemu_opt_script += ' %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU'))
543 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500544
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600545 if not self.get('QB_CPU_KVM'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500546 raise RunQemuError("QB_CPU_KVM is NULL, this board doesn't support kvm")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500547
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600548 self.qemu_opt_script += ' %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU_KVM'))
549 yocto_kvm_wiki = "https://wiki.yoctoproject.org/wiki/How_to_enable_KVM_for_Poky_qemu"
550 yocto_paravirt_kvm_wiki = "https://wiki.yoctoproject.org/wiki/Running_an_x86_Yocto_Linux_image_under_QEMU_KVM"
551 dev_kvm = '/dev/kvm'
552 dev_vhost = '/dev/vhost-net'
Brad Bishop15ae2502019-06-18 21:44:24 -0400553 if self.qemu_system.endswith(('i386', 'x86_64')):
554 with open('/proc/cpuinfo', 'r') as f:
555 kvm_cap = re.search('vmx|svm', "".join(f.readlines()))
556 if not kvm_cap:
557 logger.error("You are trying to enable KVM on a cpu without VT support.")
558 logger.error("Remove kvm from the command-line, or refer:")
559 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500560
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600561 if not os.path.exists(dev_kvm):
562 logger.error("Missing KVM device. Have you inserted kvm modules?")
563 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500564 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500565
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600566 if os.access(dev_kvm, os.W_OK|os.R_OK):
567 self.qemu_opt_script += ' -enable-kvm'
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500568 if self.get('MACHINE') == "qemux86":
569 # Workaround for broken APIC window on pre 4.15 host kernels which causes boot hangs
570 # See YOCTO #12301
571 # On 64 bit we use x2apic
572 self.kernel_cmdline_script += " clocksource=kvm-clock hpet=disable noapic nolapic"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600573 else:
574 logger.error("You have no read or write permission on /dev/kvm.")
575 logger.error("Please change the ownership of this file as described at:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500576 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500577
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600578 if self.vhost_enabled:
579 if not os.path.exists(dev_vhost):
580 logger.error("Missing virtio net device. Have you inserted vhost-net module?")
581 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500582 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500583
Andrew Geissler635e0e42020-08-21 15:58:33 -0500584 if not os.access(dev_vhost, os.W_OK|os.R_OK):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600585 logger.error("You have no read or write permission on /dev/vhost-net.")
586 logger.error("Please change the ownership of this file as described at:")
Andrew Geissler635e0e42020-08-21 15:58:33 -0500587 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500588
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600589 def check_fstype(self):
590 """Check and setup FSTYPE"""
591 if not self.fstype:
592 fstype = self.get('QB_DEFAULT_FSTYPE')
593 if fstype:
594 self.fstype = fstype
595 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500596 raise RunQemuError("FSTYPE is NULL!")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500597
Brad Bishop15ae2502019-06-18 21:44:24 -0400598 # parse QB_FSINFO into dict, e.g. { 'wic': ['no-kernel-in-fs', 'a-flag'], 'ext4': ['another-flag']}
599 wic_fs = False
600 qb_fsinfo = self.get('QB_FSINFO')
601 if qb_fsinfo:
602 qb_fsinfo = qb_fsinfo.split()
603 for fsinfo in qb_fsinfo:
604 try:
605 fstype, fsflag = fsinfo.split(':')
606
607 if fstype == 'wic':
608 if fsflag == 'no-kernel-in-fs':
609 wic_fs = True
610 elif fsflag == 'kernel-in-fs':
611 wic_fs = False
612 else:
613 logger.warn('Unknown flag "%s:%s" in QB_FSINFO', fstype, fsflag)
614 continue
615 else:
616 logger.warn('QB_FSINFO is not supported for image type "%s"', fstype)
617 continue
618
619 if fstype in self.fsinfo:
620 self.fsinfo[fstype].append(fsflag)
621 else:
622 self.fsinfo[fstype] = [fsflag]
623 except Exception:
624 logger.error('Invalid parameter "%s" in QB_FSINFO', fsinfo)
625
626 # treat wic images as vmimages (with kernel) or as fsimages (rootfs only)
627 if wic_fs:
628 self.fstypes = self.fstypes + self.wictypes
629 else:
630 self.vmtypes = self.vmtypes + self.wictypes
631
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600632 def check_rootfs(self):
633 """Check and set rootfs"""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500634
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500635 if self.fstype == "none":
636 return
637
638 if self.get('ROOTFS'):
639 if not self.rootfs:
640 self.rootfs = self.get('ROOTFS')
641 elif self.get('ROOTFS') != self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500642 raise RunQemuError("Maybe conflicted ROOTFS: %s vs %s" % (self.get('ROOTFS'), self.rootfs))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500643
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600644 if self.fstype == 'nfs':
645 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500646
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600647 if self.rootfs and not os.path.exists(self.rootfs):
648 # Lazy rootfs
649 self.rootfs = "%s/%s-%s.%s" % (self.get('DEPLOY_DIR_IMAGE'),
650 self.rootfs, self.get('MACHINE'),
651 self.fstype)
652 elif not self.rootfs:
653 cmd_name = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_NAME'), self.fstype)
654 cmd_link = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'), self.fstype)
655 cmds = (cmd_name, cmd_link)
656 self.rootfs = get_first_file(cmds)
657 if not self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500658 raise RunQemuError("Failed to find rootfs: %s or %s" % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500659
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600660 if not os.path.exists(self.rootfs):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500661 raise RunQemuError("Can't find rootfs: %s" % self.rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500662
Brad Bishop08902b02019-08-20 09:16:51 -0400663 def setup_pkkek1(self):
664 """
665 Extract from PEM certificate the Platform Key and first Key
666 Exchange Key certificate string. The hypervisor needs to provide
667 it in the Type 11 SMBIOS table
668 """
669 pemcert = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), 'OvmfPkKek1.pem')
670 try:
671 with open(pemcert, 'r') as pemfile:
672 key = pemfile.read().replace('\n', ''). \
673 replace('-----BEGIN CERTIFICATE-----', ''). \
674 replace('-----END CERTIFICATE-----', '')
675 self.ovmf_secboot_pkkek1 = key
676
677 except FileNotFoundError:
678 raise RunQemuError("Can't open PEM certificate %s " % pemcert)
679
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500680 def check_ovmf(self):
681 """Check and set full path for OVMF firmware and variable file(s)."""
682
683 for index, ovmf in enumerate(self.ovmf_bios):
684 if os.path.exists(ovmf):
685 continue
686 for suffix in ('qcow2', 'bin'):
687 path = '%s/%s.%s' % (self.get('DEPLOY_DIR_IMAGE'), ovmf, suffix)
688 if os.path.exists(path):
689 self.ovmf_bios[index] = path
Brad Bishop08902b02019-08-20 09:16:51 -0400690 if ovmf.endswith('secboot'):
691 self.setup_pkkek1()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500692 break
693 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500694 raise RunQemuError("Can't find OVMF firmware: %s" % ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500695
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600696 def check_kernel(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400697 """Check and set kernel"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600698 # The vm image doesn't need a kernel
699 if self.fstype in self.vmtypes:
700 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500701
Brad Bishop316dfdd2018-06-25 12:45:53 -0400702 # See if the user supplied a KERNEL option
703 if self.get('KERNEL'):
704 self.kernel = self.get('KERNEL')
705
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500706 # QB_DEFAULT_KERNEL is always a full file path
707 kernel_name = os.path.basename(self.get('QB_DEFAULT_KERNEL'))
708
709 # The user didn't want a kernel to be loaded
Brad Bishop316dfdd2018-06-25 12:45:53 -0400710 if kernel_name == "none" and not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500711 return
712
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600713 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
714 if not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500715 kernel_match_name = "%s/%s" % (deploy_dir_image, kernel_name)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600716 kernel_match_link = "%s/%s" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
717 kernel_startswith = "%s/%s*" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
718 cmds = (kernel_match_name, kernel_match_link, kernel_startswith)
719 self.kernel = get_first_file(cmds)
720 if not self.kernel:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500721 raise RunQemuError('KERNEL not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500722
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600723 if not os.path.exists(self.kernel):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500724 raise RunQemuError("KERNEL %s not found" % self.kernel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500725
Brad Bishop316dfdd2018-06-25 12:45:53 -0400726 def check_dtb(self):
727 """Check and set dtb"""
728 # Did the user specify a device tree?
729 if self.get('DEVICE_TREE'):
730 self.dtb = self.get('DEVICE_TREE')
731 if not os.path.exists(self.dtb):
732 raise RunQemuError('Specified DTB not found: %s' % self.dtb)
733 return
734
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600735 dtb = self.get('QB_DTB')
736 if dtb:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400737 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600738 cmd_match = "%s/%s" % (deploy_dir_image, dtb)
739 cmd_startswith = "%s/%s*" % (deploy_dir_image, dtb)
740 cmd_wild = "%s/*.dtb" % deploy_dir_image
741 cmds = (cmd_match, cmd_startswith, cmd_wild)
742 self.dtb = get_first_file(cmds)
743 if not os.path.exists(self.dtb):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500744 raise RunQemuError('DTB not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500745
Brad Bishopc68388fc2019-08-26 01:33:31 -0400746 def check_bios(self):
747 """Check and set bios"""
748
749 # See if the user supplied a BIOS option
750 if self.get('BIOS'):
751 self.bios = self.get('BIOS')
752
753 # QB_DEFAULT_BIOS is always a full file path
754 bios_name = os.path.basename(self.get('QB_DEFAULT_BIOS'))
755
756 # The user didn't want a bios to be loaded
757 if (bios_name == "" or bios_name == "none") and not self.bios:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600758 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500759
Brad Bishopc68388fc2019-08-26 01:33:31 -0400760 if not self.bios:
761 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
762 self.bios = "%s/%s" % (deploy_dir_image, bios_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500763
Brad Bishopc68388fc2019-08-26 01:33:31 -0400764 if not self.bios:
765 raise RunQemuError('BIOS not found: %s' % bios_match_name)
766
767 if not os.path.exists(self.bios):
768 raise RunQemuError("KERNEL %s not found" % self.bios)
769
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500770
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600771 def check_mem(self):
Andrew Geissler99467da2019-02-25 18:54:23 -0600772 """
773 Both qemu and kernel needs memory settings, so check QB_MEM and set it
774 for both.
775 """
776 s = re.search('-m +([0-9]+)', self.qemuparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600777 if s:
778 self.set('QB_MEM', '-m %s' % s.group(1))
779 elif not self.get('QB_MEM'):
Brad Bishop79641f22019-09-10 07:20:22 -0400780 logger.info('QB_MEM is not set, use 256M by default')
781 self.set('QB_MEM', '-m 256')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500782
Andrew Geissler99467da2019-02-25 18:54:23 -0600783 # Check and remove M or m suffix
784 qb_mem = self.get('QB_MEM')
785 if qb_mem.endswith('M') or qb_mem.endswith('m'):
786 qb_mem = qb_mem[:-1]
787
788 # Add -m prefix it not present
789 if not qb_mem.startswith('-m'):
790 qb_mem = '-m %s' % qb_mem
791
792 self.set('QB_MEM', qb_mem)
793
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800794 mach = self.get('MACHINE')
795 if not mach.startswith('qemumips'):
796 self.kernel_cmdline_script += ' mem=%s' % self.get('QB_MEM').replace('-m','').strip() + 'M'
797
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600798 self.qemu_opt_script += ' %s' % self.get('QB_MEM')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500799
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600800 def check_tcpserial(self):
801 if self.tcpserial_portnum:
Brad Bishop15ae2502019-06-18 21:44:24 -0400802 ports = self.tcpserial_portnum.split(':')
803 port = ports[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600804 if self.get('QB_TCPSERIAL_OPT'):
Brad Bishop15ae2502019-06-18 21:44:24 -0400805 self.qemu_opt_script += ' ' + self.get('QB_TCPSERIAL_OPT').replace('@PORT@', port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600806 else:
Brad Bishop15ae2502019-06-18 21:44:24 -0400807 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
808
809 if len(ports) > 1:
810 for port in ports[1:]:
811 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500812
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600813 def check_and_set(self):
814 """Check configs sanity and set when needed"""
815 self.validate_paths()
Andrew Geissler82c905d2020-04-13 13:39:40 -0500816 if not self.slirp_enabled and not self.net_bridge:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500817 check_tun()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600818 # Check audio
819 if self.audio_enabled:
820 if not self.get('QB_AUDIO_DRV'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500821 raise RunQemuError("QB_AUDIO_DRV is NULL, this board doesn't support audio")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600822 if not self.get('QB_AUDIO_OPT'):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800823 logger.warning('QB_AUDIO_OPT is NULL, you may need define it to make audio work')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600824 else:
825 self.qemu_opt_script += ' %s' % self.get('QB_AUDIO_OPT')
826 os.putenv('QEMU_AUDIO_DRV', self.get('QB_AUDIO_DRV'))
827 else:
828 os.putenv('QEMU_AUDIO_DRV', 'none')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500829
Brad Bishop15ae2502019-06-18 21:44:24 -0400830 self.check_qemu_system()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600831 self.check_kvm()
832 self.check_fstype()
833 self.check_rootfs()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500834 self.check_ovmf()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600835 self.check_kernel()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400836 self.check_dtb()
Brad Bishopc68388fc2019-08-26 01:33:31 -0400837 self.check_bios()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600838 self.check_mem()
839 self.check_tcpserial()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500840
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600841 def read_qemuboot(self):
842 if not self.qemuboot:
843 if self.get('DEPLOY_DIR_IMAGE'):
844 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600845 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800846 logger.warning("Can't find qemuboot conf file, DEPLOY_DIR_IMAGE is NULL!")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600847 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500848
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600849 if self.rootfs and not os.path.exists(self.rootfs):
850 # Lazy rootfs
851 machine = self.get('MACHINE')
852 if not machine:
853 machine = os.path.basename(deploy_dir_image)
854 self.qemuboot = "%s/%s-%s.qemuboot.conf" % (deploy_dir_image,
855 self.rootfs, machine)
856 else:
857 cmd = 'ls -t %s/*.qemuboot.conf' % deploy_dir_image
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500858 logger.debug('Running %s...' % cmd)
859 try:
860 qbs = subprocess.check_output(cmd, shell=True).decode('utf-8')
861 except subprocess.CalledProcessError as err:
862 raise RunQemuError(err)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600863 if qbs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500864 for qb in qbs.split():
865 # Don't use initramfs when other choices unless fstype is ramfs
866 if '-initramfs-' in os.path.basename(qb) and self.fstype != 'cpio.gz':
867 continue
868 self.qemuboot = qb
869 break
870 if not self.qemuboot:
871 # Use the first one when no choice
872 self.qemuboot = qbs.split()[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600873 self.qbconfload = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500874
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600875 if not self.qemuboot:
876 # If we haven't found a .qemuboot.conf at this point it probably
877 # doesn't exist, continue without
878 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500879
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600880 if not os.path.exists(self.qemuboot):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500881 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 -0500882
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500883 logger.debug('CONFFILE: %s' % self.qemuboot)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500884
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600885 cf = configparser.ConfigParser()
886 cf.read(self.qemuboot)
887 for k, v in cf.items('config_bsp'):
888 k_upper = k.upper()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500889 if v.startswith("../"):
890 v = os.path.abspath(os.path.dirname(self.qemuboot) + "/" + v)
891 elif v == ".":
892 v = os.path.dirname(self.qemuboot)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600893 self.set(k_upper, v)
894
895 def validate_paths(self):
896 """Ensure all relevant path variables are set"""
897 # When we're started with a *.qemuboot.conf arg assume that image
898 # artefacts are relative to that file, rather than in whatever
899 # directory DEPLOY_DIR_IMAGE in the conf file points to.
900 if self.qbconfload:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500901 imgdir = os.path.realpath(os.path.dirname(self.qemuboot))
902 if imgdir != os.path.realpath(self.get('DEPLOY_DIR_IMAGE')):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600903 logger.info('Setting DEPLOY_DIR_IMAGE to folder containing %s (%s)' % (self.qemuboot, imgdir))
904 self.set('DEPLOY_DIR_IMAGE', imgdir)
905
906 # If the STAGING_*_NATIVE directories from the config file don't exist
907 # and we're in a sourced OE build directory try to extract the paths
908 # from `bitbake -e`
909 havenative = os.path.exists(self.get('STAGING_DIR_NATIVE')) and \
910 os.path.exists(self.get('STAGING_BINDIR_NATIVE'))
911
912 if not havenative:
913 if not self.bitbake_e:
914 self.load_bitbake_env()
915
916 if self.bitbake_e:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500917 native_vars = ['STAGING_DIR_NATIVE']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600918 for nv in native_vars:
919 s = re.search('^%s="(.*)"' % nv, self.bitbake_e, re.M)
920 if s and s.group(1) != self.get(nv):
921 logger.info('Overriding conf file setting of %s to %s from Bitbake environment' % (nv, s.group(1)))
922 self.set(nv, s.group(1))
923 else:
924 # when we're invoked from a running bitbake instance we won't
925 # be able to call `bitbake -e`, then try:
926 # - get OE_TMPDIR from environment and guess paths based on it
927 # - get OECORE_NATIVE_SYSROOT from environment (for sdk)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500928 tmpdir = self.get('OE_TMPDIR')
929 oecore_native_sysroot = self.get('OECORE_NATIVE_SYSROOT')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600930 if tmpdir:
931 logger.info('Setting STAGING_DIR_NATIVE and STAGING_BINDIR_NATIVE relative to OE_TMPDIR (%s)' % tmpdir)
932 hostos, _, _, _, machine = os.uname()
933 buildsys = '%s-%s' % (machine, hostos.lower())
934 staging_dir_native = '%s/sysroots/%s' % (tmpdir, buildsys)
935 self.set('STAGING_DIR_NATIVE', staging_dir_native)
936 elif oecore_native_sysroot:
937 logger.info('Setting STAGING_DIR_NATIVE to OECORE_NATIVE_SYSROOT (%s)' % oecore_native_sysroot)
938 self.set('STAGING_DIR_NATIVE', oecore_native_sysroot)
939 if self.get('STAGING_DIR_NATIVE'):
940 # we have to assume that STAGING_BINDIR_NATIVE is at usr/bin
941 staging_bindir_native = '%s/usr/bin' % self.get('STAGING_DIR_NATIVE')
942 logger.info('Setting STAGING_BINDIR_NATIVE to %s' % staging_bindir_native)
943 self.set('STAGING_BINDIR_NATIVE', '%s/usr/bin' % self.get('STAGING_DIR_NATIVE'))
944
945 def print_config(self):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500946 logoutput = ['Continuing with the following parameters:']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600947 if not self.fstype in self.vmtypes:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500948 logoutput.append('KERNEL: [%s]' % self.kernel)
Brad Bishopc68388fc2019-08-26 01:33:31 -0400949 if self.bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500950 logoutput.append('BIOS: [%s]' % self.bios)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600951 if self.dtb:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500952 logoutput.append('DTB: [%s]' % self.dtb)
953 logoutput.append('MACHINE: [%s]' % self.get('MACHINE'))
Brad Bishop15ae2502019-06-18 21:44:24 -0400954 try:
955 fstype_flags = ' (' + ', '.join(self.fsinfo[self.fstype]) + ')'
956 except KeyError:
957 fstype_flags = ''
Andrew Geissler82c905d2020-04-13 13:39:40 -0500958 logoutput.append('FSTYPE: [%s%s]' % (self.fstype, fstype_flags))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600959 if self.fstype == 'nfs':
Andrew Geissler82c905d2020-04-13 13:39:40 -0500960 logoutput.append('NFS_DIR: [%s]' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600961 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500962 logoutput.append('ROOTFS: [%s]' % self.rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500963 if self.ovmf_bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500964 logoutput.append('OVMF: %s' % self.ovmf_bios)
Brad Bishop08902b02019-08-20 09:16:51 -0400965 if (self.ovmf_secboot_pkkek1):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500966 logoutput.append('SECBOOT PKKEK1: [%s...]' % self.ovmf_secboot_pkkek1[0:100])
967 logoutput.append('CONFFILE: [%s]' % self.qemuboot)
968 logoutput.append('')
969 logger.info('\n'.join(logoutput))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600970
971 def setup_nfs(self):
972 if not self.nfs_server:
973 if self.slirp_enabled:
974 self.nfs_server = '10.0.2.2'
975 else:
976 self.nfs_server = '192.168.7.1'
977
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500978 # Figure out a new nfs_instance to allow multiple qemus running.
Brad Bishop977dc1a2019-02-06 16:01:43 -0500979 ps = subprocess.check_output(("ps", "auxww")).decode('utf-8')
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500980 pattern = '/bin/unfsd .* -i .*\.pid -e .*/exports([0-9]+) '
981 all_instances = re.findall(pattern, ps, re.M)
982 if all_instances:
983 all_instances.sort(key=int)
984 self.nfs_instance = int(all_instances.pop()) + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600985
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500986 nfsd_port = 3049 + 2 * self.nfs_instance
987 mountd_port = 3048 + 2 * self.nfs_instance
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600988
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500989 # Export vars for runqemu-export-rootfs
990 export_dict = {
991 'NFS_INSTANCE': self.nfs_instance,
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500992 'NFSD_PORT': nfsd_port,
993 'MOUNTD_PORT': mountd_port,
994 }
995 for k, v in export_dict.items():
996 # Use '%s' since they are integers
997 os.putenv(k, '%s' % v)
998
Andrew Geissler82c905d2020-04-13 13:39:40 -0500999 self.unfs_opts="nfsvers=3,port=%s,tcp,mountport=%s" % (nfsd_port, mountd_port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001000
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001001 # Extract .tar.bz2 or .tar.bz if no nfs dir
1002 if not (self.rootfs and os.path.isdir(self.rootfs)):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001003 src_prefix = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'))
1004 dest = "%s-nfsroot" % src_prefix
1005 if os.path.exists('%s.pseudo_state' % dest):
1006 logger.info('Use %s as NFS_DIR' % dest)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001007 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001008 else:
1009 src = ""
1010 src1 = '%s.tar.bz2' % src_prefix
1011 src2 = '%s.tar.gz' % src_prefix
1012 if os.path.exists(src1):
1013 src = src1
1014 elif os.path.exists(src2):
1015 src = src2
1016 if not src:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001017 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 -06001018 logger.info('NFS_DIR not found, extracting %s to %s' % (src, dest))
Brad Bishop977dc1a2019-02-06 16:01:43 -05001019 cmd = ('runqemu-extract-sdk', src, dest)
1020 logger.info('Running %s...' % str(cmd))
1021 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001022 raise RunQemuError('Failed to run %s' % cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001023 self.clean_nfs_dir = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001024 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001025
1026 # Start the userspace NFS server
Brad Bishop977dc1a2019-02-06 16:01:43 -05001027 cmd = ('runqemu-export-rootfs', 'start', self.rootfs)
1028 logger.info('Running %s...' % str(cmd))
1029 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001030 raise RunQemuError('Failed to run %s' % cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001031
1032 self.nfs_running = True
1033
Andrew Geissler82c905d2020-04-13 13:39:40 -05001034 def setup_net_bridge(self):
1035 self.set('NETWORK_CMD', '-netdev bridge,br=%s,id=net0,helper=%s -device virtio-net-pci,netdev=net0 ' % (
1036 self.net_bridge, os.path.join(self.bindir_native, 'qemu-oe-bridge-helper')))
1037
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001038 def setup_slirp(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001039 """Setup user networking"""
1040
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001041 if self.fstype == 'nfs':
1042 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001043 netconf = " " + self.cmdline_ip_slirp
1044 logger.info("Network configuration:%s", netconf)
1045 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001046 # Port mapping
1047 hostfwd = ",hostfwd=tcp::2222-:22,hostfwd=tcp::2323-:23"
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001048 qb_slirp_opt_default = "-netdev user,id=net0%s,tftp=%s" % (hostfwd, self.get('DEPLOY_DIR_IMAGE'))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001049 qb_slirp_opt = self.get('QB_SLIRP_OPT') or qb_slirp_opt_default
1050 # Figure out the port
1051 ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt)
1052 ports = [int(i) for i in ports]
1053 mac = 2
Brad Bishop08902b02019-08-20 09:16:51 -04001054
1055 lockdir = "/tmp/qemu-port-locks"
1056 if not os.path.exists(lockdir):
1057 # There might be a race issue when multi runqemu processess are
1058 # running at the same time.
1059 try:
1060 os.mkdir(lockdir)
1061 os.chmod(lockdir, 0o777)
1062 except FileExistsError:
1063 pass
1064
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001065 # Find a free port to avoid conflicts
1066 for p in ports[:]:
1067 p_new = p
Brad Bishop08902b02019-08-20 09:16:51 -04001068 while not self.check_free_port('localhost', p_new, lockdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001069 p_new += 1
1070 mac += 1
1071 while p_new in ports:
1072 p_new += 1
1073 mac += 1
1074 if p != p_new:
1075 ports.append(p_new)
1076 qb_slirp_opt = re.sub(':%s-' % p, ':%s-' % p_new, qb_slirp_opt)
1077 logger.info("Port forward changed: %s -> %s" % (p, p_new))
1078 mac = "%s%02x" % (self.mac_slirp, mac)
1079 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qb_slirp_opt))
1080 # Print out port foward
1081 hostfwd = re.findall('(hostfwd=[^,]*)', qb_slirp_opt)
1082 if hostfwd:
1083 logger.info('Port forward: %s' % ' '.join(hostfwd))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001084
1085 def setup_tap(self):
1086 """Setup tap"""
1087
1088 # This file is created when runqemu-gen-tapdevs creates a bank of tap
1089 # devices, indicating that the user should not bring up new ones using
1090 # sudo.
1091 nosudo_flag = '/etc/runqemu-nosudo'
1092 self.qemuifup = shutil.which('runqemu-ifup')
1093 self.qemuifdown = shutil.which('runqemu-ifdown')
1094 ip = shutil.which('ip')
1095 lockdir = "/tmp/qemu-tap-locks"
1096
1097 if not (self.qemuifup and self.qemuifdown and ip):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001098 logger.error("runqemu-ifup: %s" % self.qemuifup)
1099 logger.error("runqemu-ifdown: %s" % self.qemuifdown)
1100 logger.error("ip: %s" % ip)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001101 raise OEPathError("runqemu-ifup, runqemu-ifdown or ip not found")
1102
1103 if not os.path.exists(lockdir):
1104 # There might be a race issue when multi runqemu processess are
1105 # running at the same time.
1106 try:
1107 os.mkdir(lockdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001108 os.chmod(lockdir, 0o777)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001109 except FileExistsError:
1110 pass
1111
Brad Bishop977dc1a2019-02-06 16:01:43 -05001112 cmd = (ip, 'link')
1113 logger.debug('Running %s...' % str(cmd))
1114 ip_link = subprocess.check_output(cmd).decode('utf-8')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001115 # Matches line like: 6: tap0: <foo>
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001116 possibles = re.findall('^[0-9]+: +(tap[0-9]+): <.*', ip_link, re.M)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001117 tap = ""
1118 for p in possibles:
1119 lockfile = os.path.join(lockdir, p)
1120 if os.path.exists('%s.skip' % lockfile):
1121 logger.info('Found %s.skip, skipping %s' % (lockfile, p))
1122 continue
Brad Bishop08902b02019-08-20 09:16:51 -04001123 self.taplock = lockfile + '.lock'
1124 if self.acquire_taplock(error=False):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001125 tap = p
1126 logger.info("Using preconfigured tap device %s" % tap)
1127 logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap))
1128 break
1129
1130 if not tap:
1131 if os.path.exists(nosudo_flag):
1132 logger.error("Error: There are no available tap devices to use for networking,")
1133 logger.error("and I see %s exists, so I am not going to try creating" % nosudo_flag)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001134 raise RunQemuError("a new one with sudo.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001135
1136 gid = os.getgid()
1137 uid = os.getuid()
1138 logger.info("Setting up tap interface under sudo")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001139 cmd = ('sudo', self.qemuifup, str(uid), str(gid), self.bindir_native)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001140 try:
1141 tap = subprocess.check_output(cmd).decode('utf-8').strip()
1142 except subprocess.CalledProcessError as e:
1143 logger.error('Setting up tap device failed:\n%s\nRun runqemu-gen-tapdevs to manually create one.' % str(e))
1144 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001145 lockfile = os.path.join(lockdir, tap)
Brad Bishop08902b02019-08-20 09:16:51 -04001146 self.taplock = lockfile + '.lock'
1147 self.acquire_taplock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001148 self.cleantap = True
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001149 logger.debug('Created tap: %s' % tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001150
1151 if not tap:
1152 logger.error("Failed to setup tap device. Run runqemu-gen-tapdevs to manually create.")
Andrew Geissler82c905d2020-04-13 13:39:40 -05001153 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001154 self.tap = tap
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001155 tapnum = int(tap[3:])
1156 gateway = tapnum * 2 + 1
1157 client = gateway + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001158 if self.fstype == 'nfs':
1159 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001160 netconf = " " + self.cmdline_ip_tap
1161 netconf = netconf.replace('@CLIENT@', str(client))
1162 netconf = netconf.replace('@GATEWAY@', str(gateway))
1163 logger.info("Network configuration:%s", netconf)
1164 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001165 mac = "%s%02x" % (self.mac_tap, client)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001166 qb_tap_opt = self.get('QB_TAP_OPT')
1167 if qb_tap_opt:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001168 qemu_tap_opt = qb_tap_opt.replace('@TAP@', tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001169 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001170 qemu_tap_opt = "-netdev tap,id=net0,ifname=%s,script=no,downscript=no" % (self.tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001171
1172 if self.vhost_enabled:
1173 qemu_tap_opt += ',vhost=on'
1174
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001175 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qemu_tap_opt))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001176
1177 def setup_network(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001178 if self.get('QB_NET') == 'none':
1179 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001180 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001181 self.saved_stty = subprocess.check_output(("stty", "-g")).decode('utf-8').strip()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001182 self.network_device = self.get('QB_NETWORK_DEVICE') or self.network_device
Andrew Geissler82c905d2020-04-13 13:39:40 -05001183 if self.net_bridge:
1184 self.setup_net_bridge()
1185 elif self.slirp_enabled:
1186 self.cmdline_ip_slirp = self.get('QB_CMDLINE_IP_SLIRP') or self.cmdline_ip_slirp
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001187 self.setup_slirp()
1188 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001189 self.cmdline_ip_tap = self.get('QB_CMDLINE_IP_TAP') or self.cmdline_ip_tap
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001190 self.setup_tap()
1191
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001192 def setup_rootfs(self):
1193 if self.get('QB_ROOTFS') == 'none':
1194 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001195 if 'wic.' in self.fstype:
1196 self.fstype = self.fstype[4:]
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001197 rootfs_format = self.fstype if self.fstype in ('vmdk', 'vhd', 'vhdx', 'qcow2', 'vdi') else 'raw'
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001198
1199 qb_rootfs_opt = self.get('QB_ROOTFS_OPT')
1200 if qb_rootfs_opt:
1201 self.rootfs_options = qb_rootfs_opt.replace('@ROOTFS@', self.rootfs)
1202 else:
1203 self.rootfs_options = '-drive file=%s,if=virtio,format=%s' % (self.rootfs, rootfs_format)
1204
Andrew Geissler82c905d2020-04-13 13:39:40 -05001205 qb_rootfs_extra_opt = self.get("QB_ROOTFS_EXTRA_OPT")
1206 if qb_rootfs_extra_opt and not qb_rootfs_extra_opt.startswith(","):
1207 qb_rootfs_extra_opt = "," + qb_rootfs_extra_opt
1208
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001209 if self.fstype in ('cpio.gz', 'cpio'):
1210 self.kernel_cmdline = 'root=/dev/ram0 rw debugshell'
1211 self.rootfs_options = '-initrd %s' % self.rootfs
1212 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001213 vm_drive = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001214 if self.fstype in self.vmtypes:
1215 if self.fstype == 'iso':
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001216 vm_drive = '-drive file=%s,if=virtio,media=cdrom' % self.rootfs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001217 elif self.get('QB_DRIVE_TYPE'):
1218 drive_type = self.get('QB_DRIVE_TYPE')
1219 if drive_type.startswith("/dev/sd"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001220 logger.info('Using scsi drive')
Andrew Geissler82c905d2020-04-13 13:39:40 -05001221 vm_drive = '-drive if=none,id=hd,file=%s,format=%s -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd%s' \
1222 % (self.rootfs, rootfs_format, qb_rootfs_extra_opt)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001223 elif drive_type.startswith("/dev/hd"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001224 logger.info('Using ide drive')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001225 vm_drive = "-drive file=%s,format=%s" % (self.rootfs, rootfs_format)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001226 elif drive_type.startswith("/dev/vdb"):
1227 logger.info('Using block virtio drive');
1228 vm_drive = '-drive id=disk0,file=%s,if=none,format=%s -device virtio-blk-device,drive=disk0%s' \
1229 % (self.rootfs, rootfs_format,qb_rootfs_extra_opt)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001230 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001231 # virtio might have been selected explicitly (just use it), or
1232 # is used as fallback (then warn about that).
1233 if not drive_type.startswith("/dev/vd"):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001234 logger.warning("Unknown QB_DRIVE_TYPE: %s" % drive_type)
1235 logger.warning("Failed to figure out drive type, consider define or fix QB_DRIVE_TYPE")
1236 logger.warning('Trying to use virtio block drive')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001237 vm_drive = '-drive if=virtio,file=%s,format=%s' % (self.rootfs, rootfs_format)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001238
1239 # All branches above set vm_drive.
Andrew Geissler475cb722020-07-10 16:00:51 -05001240 self.rootfs_options = vm_drive
1241 if not self.fstype in self.vmtypes:
1242 self.rootfs_options += ' -no-reboot'
Brad Bishopf3f93bb2019-10-16 14:33:32 -04001243 self.kernel_cmdline = 'root=%s rw' % (self.get('QB_KERNEL_ROOT'))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001244
1245 if self.fstype == 'nfs':
1246 self.rootfs_options = ''
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001247 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 -04001248 self.kernel_cmdline = 'root=%s rw' % k_root
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001249
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001250 if self.fstype == 'none':
1251 self.rootfs_options = ''
1252
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001253 self.set('ROOTFS_OPTIONS', self.rootfs_options)
1254
1255 def guess_qb_system(self):
1256 """attempt to determine the appropriate qemu-system binary"""
1257 mach = self.get('MACHINE')
1258 if not mach:
1259 search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*'
1260 if self.rootfs:
1261 match = re.match(search, self.rootfs)
1262 if match:
1263 mach = match.group(1)
1264 elif self.kernel:
1265 match = re.match(search, self.kernel)
1266 if match:
1267 mach = match.group(1)
1268
1269 if not mach:
1270 return None
1271
1272 if mach == 'qemuarm':
1273 qbsys = 'arm'
1274 elif mach == 'qemuarm64':
1275 qbsys = 'aarch64'
1276 elif mach == 'qemux86':
1277 qbsys = 'i386'
1278 elif mach == 'qemux86-64':
1279 qbsys = 'x86_64'
1280 elif mach == 'qemuppc':
1281 qbsys = 'ppc'
1282 elif mach == 'qemumips':
1283 qbsys = 'mips'
1284 elif mach == 'qemumips64':
1285 qbsys = 'mips64'
1286 elif mach == 'qemumipsel':
1287 qbsys = 'mipsel'
1288 elif mach == 'qemumips64el':
1289 qbsys = 'mips64el'
Brad Bishop316dfdd2018-06-25 12:45:53 -04001290 elif mach == 'qemuriscv64':
1291 qbsys = 'riscv64'
1292 elif mach == 'qemuriscv32':
1293 qbsys = 'riscv32'
Brad Bishop004d4992018-10-02 23:54:45 +02001294 else:
1295 logger.error("Unable to determine QEMU PC System emulator for %s machine." % mach)
1296 logger.error("As %s is not among valid QEMU machines such as," % mach)
1297 logger.error("qemux86-64, qemux86, qemuarm64, qemuarm, qemumips64, qemumips64el, qemumipsel, qemumips, qemuppc")
1298 raise RunQemuError("Set qb_system_name with suitable QEMU PC System emulator in .*qemuboot.conf.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001299
1300 return 'qemu-system-%s' % qbsys
1301
Brad Bishop15ae2502019-06-18 21:44:24 -04001302 def check_qemu_system(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001303 qemu_system = self.get('QB_SYSTEM_NAME')
1304 if not qemu_system:
1305 qemu_system = self.guess_qb_system()
1306 if not qemu_system:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001307 raise RunQemuError("Failed to boot, QB_SYSTEM_NAME is NULL!")
Brad Bishop15ae2502019-06-18 21:44:24 -04001308 self.qemu_system = qemu_system
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001309
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001310 def setup_vga(self):
1311 if self.nographic == True:
1312 if self.sdl == True:
1313 raise RunQemuError('Option nographic makes no sense alongside the sdl option.')
1314 if self.gtk == True:
1315 raise RunQemuError('Option nographic makes no sense alongside the gtk option.')
1316 self.qemu_opt += ' -nographic'
1317 return
1318
1319 if self.novga == True:
1320 self.qemu_opt += ' -vga none'
1321 return
1322
1323 if (self.gl_es == True or self.gl == True) and (self.sdl == False and self.gtk == False):
1324 raise RunQemuError('Option gl/gl-es needs gtk or sdl option.')
1325
1326 if self.sdl == True or self.gtk == True or self.egl_headless == True:
1327 self.set_dri_path()
1328 self.qemu_opt += ' -vga virtio -display '
1329 if self.egl_headless == True:
1330 self.qemu_opt += 'egl-headless,'
1331 else:
1332 if self.sdl == True:
1333 self.qemu_opt += 'sdl,'
1334 elif self.gtk == True:
1335 self.qemu_opt += 'gtk,'
1336
1337 if self.gl == True:
1338 self.qemu_opt += 'gl=on,'
1339 elif self.gl_es == True:
1340 self.qemu_opt += 'gl=es,'
1341 self.qemu_opt += 'show-cursor=on'
1342
1343 self.qemu_opt += ' %s' %self.get('QB_GRAPHICS')
1344
1345 def setup_serial(self):
1346 # Setup correct kernel command line for serial
1347 if self.serialstdio == True or self.serialconsole == True or self.nographic == True or self.tcpserial_portnum:
1348 for entry in self.get('SERIAL_CONSOLES').split(' '):
1349 self.kernel_cmdline_script += ' console=%s' %entry.split(';')[1]
1350
1351 if self.serialstdio == True or self.nographic == True:
1352 self.qemu_opt += " -serial mon:stdio"
1353 else:
1354 self.qemu_opt += " -serial mon:vc"
1355 if self.serialconsole:
1356 if sys.stdin.isatty():
1357 subprocess.check_call(("stty", "intr", "^]"))
1358 logger.info("Interrupt character is '^]'")
1359
1360 self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT")
1361
1362 # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES).
1363 # If no serial or serialtcp options were specified, only ttyS0 is created
1364 # and sysvinit shows an error trying to enable ttyS1:
1365 # INIT: Id "S1" respawning too fast: disabled for 5 minutes
1366 serial_num = len(re.findall("-serial", self.qemu_opt))
1367 if serial_num < 2:
1368 self.qemu_opt += " -serial null"
1369
Brad Bishop15ae2502019-06-18 21:44:24 -04001370 def setup_final(self):
1371 qemu_bin = os.path.join(self.bindir_native, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001372
1373 # It is possible to have qemu-native in ASSUME_PROVIDED, and it won't
1374 # find QEMU in sysroot, it needs to use host's qemu.
1375 if not os.path.exists(qemu_bin):
1376 logger.info("QEMU binary not found in %s, trying host's QEMU" % qemu_bin)
1377 for path in (os.environ['PATH'] or '').split(':'):
Brad Bishop15ae2502019-06-18 21:44:24 -04001378 qemu_bin_tmp = os.path.join(path, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001379 logger.info("Trying: %s" % qemu_bin_tmp)
1380 if os.path.exists(qemu_bin_tmp):
1381 qemu_bin = qemu_bin_tmp
1382 if not os.path.isabs(qemu_bin):
1383 qemu_bin = os.path.abspath(qemu_bin)
1384 logger.info("Using host's QEMU: %s" % qemu_bin)
1385 break
1386
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001387 if not os.access(qemu_bin, os.X_OK):
1388 raise OEPathError("No QEMU binary '%s' could be found" % qemu_bin)
1389
Andrew Geisslerc3d88e42020-10-02 09:45:00 -05001390 self.qemu_opt = "%s %s %s %s %s" % (qemu_bin, self.get('NETWORK_CMD'), self.get('QB_RNG'), self.get('ROOTFS_OPTIONS'), self.get('QB_OPT_APPEND'))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001391
1392 for ovmf in self.ovmf_bios:
1393 format = ovmf.rsplit('.', 1)[-1]
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001394 if format == "bin":
1395 format = "raw"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001396 self.qemu_opt += ' -drive if=pflash,format=%s,file=%s' % (format, ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001397
1398 self.qemu_opt += ' ' + self.qemu_opt_script
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001399
Brad Bishop08902b02019-08-20 09:16:51 -04001400 if self.ovmf_secboot_pkkek1:
1401 # Provide the Platform Key and first Key Exchange Key certificate as an
1402 # OEM string in the SMBIOS Type 11 table. Prepend the certificate string
1403 # with "application prefix" of the EnrollDefaultKeys.efi application
1404 self.qemu_opt += ' -smbios type=11,value=4e32566d-8e9e-4f52-81d3-5bb9715f9727:' \
1405 + self.ovmf_secboot_pkkek1
1406
Andrew Geissler99467da2019-02-25 18:54:23 -06001407 # Append qemuparams to override previous settings
1408 if self.qemuparams:
1409 self.qemu_opt += ' ' + self.qemuparams
1410
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001411 if self.snapshot:
1412 self.qemu_opt += " -snapshot"
1413
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001414 self.setup_serial()
1415 self.setup_vga()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001416
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001417 def start_qemu(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001418 import shlex
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001419 if self.kernel:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001420 kernel_opts = "-kernel %s -append '%s %s %s %s'" % (self.kernel, self.kernel_cmdline,
1421 self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'),
1422 self.bootparams)
Brad Bishopc68388fc2019-08-26 01:33:31 -04001423 if self.bios:
1424 kernel_opts += " -bios %s" % self.bios
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001425 if self.dtb:
1426 kernel_opts += " -dtb %s" % self.dtb
1427 else:
1428 kernel_opts = ""
1429 cmd = "%s %s" % (self.qemu_opt, kernel_opts)
Brad Bishop004d4992018-10-02 23:54:45 +02001430 cmds = shlex.split(cmd)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001431 logger.info('Running %s\n' % cmd)
Brad Bishopf86d0552018-12-04 14:18:15 -08001432 pass_fds = []
Brad Bishop08902b02019-08-20 09:16:51 -04001433 if self.taplock_descriptor:
1434 pass_fds = [self.taplock_descriptor.fileno()]
1435 if len(self.portlocks):
1436 for descriptor in self.portlocks.values():
1437 pass_fds.append(descriptor.fileno())
Brad Bishopf86d0552018-12-04 14:18:15 -08001438 process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds)
Brad Bishop004d4992018-10-02 23:54:45 +02001439 self.qemupid = process.pid
1440 retcode = process.wait()
1441 if retcode:
1442 if retcode == -signal.SIGTERM:
1443 logger.info("Qemu terminated by SIGTERM")
1444 else:
1445 logger.error("Failed to run qemu: %s", process.stderr.read().decode())
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001446
1447 def cleanup(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001448 if self.cleaned:
1449 return
1450
1451 # avoid dealing with SIGTERM when cleanup function is running
1452 signal.signal(signal.SIGTERM, signal.SIG_IGN)
1453
1454 logger.info("Cleaning up")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001455 if self.cleantap:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001456 cmd = ('sudo', self.qemuifdown, self.tap, self.bindir_native)
1457 logger.debug('Running %s' % str(cmd))
1458 subprocess.check_call(cmd)
Brad Bishop08902b02019-08-20 09:16:51 -04001459 self.release_taplock()
1460 self.release_portlock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001461
1462 if self.nfs_running:
1463 logger.info("Shutting down the userspace NFS server...")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001464 cmd = ("runqemu-export-rootfs", "stop", self.rootfs)
1465 logger.debug('Running %s' % str(cmd))
1466 subprocess.check_call(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001467
1468 if self.saved_stty:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001469 subprocess.check_call(("stty", self.saved_stty))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001470
1471 if self.clean_nfs_dir:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001472 logger.info('Removing %s' % self.rootfs)
1473 shutil.rmtree(self.rootfs)
1474 shutil.rmtree('%s.pseudo_state' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001475
Brad Bishop004d4992018-10-02 23:54:45 +02001476 self.cleaned = True
1477
Andrew Geissler82c905d2020-04-13 13:39:40 -05001478 def run_bitbake_env(self, mach=None):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001479 bitbake = shutil.which('bitbake')
1480 if not bitbake:
1481 return
1482
1483 if not mach:
1484 mach = self.get('MACHINE')
1485
Andrew Geissler82c905d2020-04-13 13:39:40 -05001486 multiconfig = self.get('MULTICONFIG')
1487 if multiconfig:
1488 multiconfig = "mc:%s" % multiconfig
1489
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001490 if mach:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001491 cmd = 'MACHINE=%s bitbake -e %s' % (mach, multiconfig)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001492 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001493 cmd = 'bitbake -e %s' % multiconfig
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001494
1495 logger.info('Running %s...' % cmd)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001496 return subprocess.check_output(cmd, shell=True).decode('utf-8')
1497
1498 def load_bitbake_env(self, mach=None):
1499 if self.bitbake_e:
1500 return
1501
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001502 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001503 self.bitbake_e = self.run_bitbake_env(mach=mach)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001504 except subprocess.CalledProcessError as err:
1505 self.bitbake_e = ''
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001506 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 -06001507
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001508 def validate_combos(self):
1509 if (self.fstype in self.vmtypes) and self.kernel:
1510 raise RunQemuError("%s doesn't need kernel %s!" % (self.fstype, self.kernel))
1511
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001512 @property
1513 def bindir_native(self):
1514 result = self.get('STAGING_BINDIR_NATIVE')
1515 if result and os.path.exists(result):
1516 return result
1517
Andrew Geissler82c905d2020-04-13 13:39:40 -05001518 cmd = ['bitbake', '-e']
1519 multiconfig = self.get('MULTICONFIG')
1520 if multiconfig:
1521 cmd.append('mc:%s:qemu-helper-native' % multiconfig)
1522 else:
1523 cmd.append('qemu-helper-native')
1524
Brad Bishop977dc1a2019-02-06 16:01:43 -05001525 logger.info('Running %s...' % str(cmd))
1526 out = subprocess.check_output(cmd).decode('utf-8')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001527
1528 match = re.search('^STAGING_BINDIR_NATIVE="(.*)"', out, re.M)
1529 if match:
1530 result = match.group(1)
1531 if os.path.exists(result):
1532 self.set('STAGING_BINDIR_NATIVE', result)
1533 return result
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001534 raise RunQemuError("Native sysroot directory %s doesn't exist" % result)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001535 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001536 raise RunQemuError("Can't find STAGING_BINDIR_NATIVE in '%s' output" % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001537
1538
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001539def main():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001540 if "help" in sys.argv or '-h' in sys.argv or '--help' in sys.argv:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001541 print_usage()
1542 return 0
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001543 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001544 config = BaseConfig()
Brad Bishop004d4992018-10-02 23:54:45 +02001545
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001546 renice = os.path.expanduser("~/bin/runqemu-renice")
1547 if os.path.exists(renice):
1548 logger.info('Using %s to renice' % renice)
1549 subprocess.check_call([renice, str(os.getpid())])
1550
Brad Bishop004d4992018-10-02 23:54:45 +02001551 def sigterm_handler(signum, frame):
1552 logger.info("SIGTERM received")
1553 os.kill(config.qemupid, signal.SIGTERM)
1554 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001555 # Deliberately ignore the return code of 'tput smam'.
1556 subprocess.call(["tput", "smam"])
Brad Bishop004d4992018-10-02 23:54:45 +02001557 signal.signal(signal.SIGTERM, sigterm_handler)
1558
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001559 config.check_args()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001560 config.read_qemuboot()
1561 config.check_and_set()
1562 # Check whether the combos is valid or not
1563 config.validate_combos()
1564 config.print_config()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001565 config.setup_network()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001566 config.setup_rootfs()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001567 config.setup_final()
1568 config.start_qemu()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001569 except RunQemuError as err:
1570 logger.error(err)
1571 return 1
1572 except Exception as err:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001573 import traceback
1574 traceback.print_exc()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001575 return 1
1576 finally:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001577 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001578 # Deliberately ignore the return code of 'tput smam'.
1579 subprocess.call(["tput", "smam"])
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001580
1581if __name__ == "__main__":
1582 sys.exit(main())