blob: 532f2e338d526d148a51aedc5cdedd97b85eaac6 [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',
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500142 )
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500143
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600144 self.qemu_opt = ''
145 self.qemu_opt_script = ''
Andrew Geissler99467da2019-02-25 18:54:23 -0600146 self.qemuparams = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600147 self.clean_nfs_dir = False
148 self.nfs_server = ''
149 self.rootfs = ''
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500150 # File name(s) of a OVMF firmware file or variable store,
151 # to be added with -drive if=pflash.
152 # Found in the same places as the rootfs, with or without one of
153 # these suffices: qcow2, bin.
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500154 self.ovmf_bios = []
Brad Bishop08902b02019-08-20 09:16:51 -0400155 # When enrolling default Secure Boot keys, the hypervisor
156 # must provide the Platform Key and the first Key Exchange Key
157 # certificate in the Type 11 SMBIOS table.
158 self.ovmf_secboot_pkkek1 = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600159 self.qemuboot = ''
160 self.qbconfload = False
161 self.kernel = ''
Brad Bishopc68388fc2019-08-26 01:33:31 -0400162 self.bios = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600163 self.kernel_cmdline = ''
164 self.kernel_cmdline_script = ''
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500165 self.bootparams = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600166 self.dtb = ''
167 self.fstype = ''
168 self.kvm_enabled = False
169 self.vhost_enabled = False
170 self.slirp_enabled = False
Andrew Geissler82c905d2020-04-13 13:39:40 -0500171 self.net_bridge = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600172 self.nfs_instance = 0
173 self.nfs_running = False
Brad Bishop19323692019-04-05 15:28:33 -0400174 self.serialconsole = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600175 self.serialstdio = False
176 self.cleantap = False
177 self.saved_stty = ''
178 self.audio_enabled = False
179 self.tcpserial_portnum = ''
Brad Bishop08902b02019-08-20 09:16:51 -0400180 self.taplock = ''
181 self.taplock_descriptor = None
182 self.portlocks = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600183 self.bitbake_e = ''
184 self.snapshot = False
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600185 self.wictypes = ('wic', 'wic.vmdk', 'wic.qcow2', 'wic.vdi', "wic.vhd", "wic.vhdx")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500186 self.fstypes = ('ext2', 'ext3', 'ext4', 'jffs2', 'nfs', 'btrfs',
187 'cpio.gz', 'cpio', 'ramfs', 'tar.bz2', 'tar.gz')
Brad Bishop08902b02019-08-20 09:16:51 -0400188 self.vmtypes = ('hddimg', 'iso')
Brad Bishop15ae2502019-06-18 21:44:24 -0400189 self.fsinfo = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500190 self.network_device = "-device e1000,netdev=net0,mac=@MAC@"
Andrew Geissler82c905d2020-04-13 13:39:40 -0500191 self.cmdline_ip_slirp = "ip=dhcp"
192 self.cmdline_ip_tap = "ip=192.168.7.@CLIENT@::192.168.7.@GATEWAY@:255.255.255.0"
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500193 # Use different mac section for tap and slirp to avoid
194 # conflicts, e.g., when one is running with tap, the other is
195 # running with slirp.
196 # The last section is dynamic, which is for avoiding conflicts,
197 # when multiple qemus are running, e.g., when multiple tap or
198 # slirp qemus are running.
199 self.mac_tap = "52:54:00:12:34:"
200 self.mac_slirp = "52:54:00:12:35:"
Brad Bishop004d4992018-10-02 23:54:45 +0200201 # pid of the actual qemu process
202 self.qemupid = None
203 # avoid cleanup twice
204 self.cleaned = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500205
Brad Bishop08902b02019-08-20 09:16:51 -0400206 def acquire_taplock(self, error=True):
207 logger.debug("Acquiring lockfile %s..." % self.taplock)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600208 try:
Brad Bishop08902b02019-08-20 09:16:51 -0400209 self.taplock_descriptor = open(self.taplock, 'w')
210 fcntl.flock(self.taplock_descriptor, fcntl.LOCK_EX|fcntl.LOCK_NB)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600211 except Exception as e:
Brad Bishop08902b02019-08-20 09:16:51 -0400212 msg = "Acquiring lockfile %s failed: %s" % (self.taplock, e)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500213 if error:
214 logger.error(msg)
215 else:
216 logger.info(msg)
Brad Bishop08902b02019-08-20 09:16:51 -0400217 if self.taplock_descriptor:
218 self.taplock_descriptor.close()
219 self.taplock_descriptor = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600220 return False
221 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500222
Brad Bishop08902b02019-08-20 09:16:51 -0400223 def release_taplock(self):
224 if self.taplock_descriptor:
Brad Bishopf86d0552018-12-04 14:18:15 -0800225 logger.debug("Releasing lockfile for tap device '%s'" % self.tap)
Brad Bishop08902b02019-08-20 09:16:51 -0400226 fcntl.flock(self.taplock_descriptor, fcntl.LOCK_UN)
227 self.taplock_descriptor.close()
228 os.remove(self.taplock)
229 self.taplock_descriptor = None
230
231 def check_free_port(self, host, port, lockdir):
232 """ Check whether the port is free or not """
233 import socket
234 from contextlib import closing
235
236 lockfile = os.path.join(lockdir, str(port) + '.lock')
237 if self.acquire_portlock(lockfile):
238 with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
239 if sock.connect_ex((host, port)) == 0:
240 # Port is open, so not free
241 self.release_portlock(lockfile)
242 return False
243 else:
244 # Port is not open, so free
245 return True
246 else:
247 return False
248
249 def acquire_portlock(self, lockfile):
250 logger.debug("Acquiring lockfile %s..." % lockfile)
251 try:
252 portlock_descriptor = open(lockfile, 'w')
253 self.portlocks.update({lockfile: portlock_descriptor})
254 fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_EX|fcntl.LOCK_NB)
255 except Exception as e:
256 msg = "Acquiring lockfile %s failed: %s" % (lockfile, e)
257 logger.info(msg)
258 if lockfile in self.portlocks.keys() and self.portlocks[lockfile]:
259 self.portlocks[lockfile].close()
260 del self.portlocks[lockfile]
261 return False
262 return True
263
264 def release_portlock(self, lockfile=None):
265 if lockfile != None:
266 logger.debug("Releasing lockfile '%s'" % lockfile)
267 fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_UN)
268 self.portlocks[lockfile].close()
269 os.remove(lockfile)
270 del self.portlocks[lockfile]
271 elif len(self.portlocks):
272 for lockfile, descriptor in self.portlocks.items():
273 logger.debug("Releasing lockfile '%s'" % lockfile)
274 fcntl.flock(descriptor, fcntl.LOCK_UN)
275 descriptor.close()
276 os.remove(lockfile)
277 self.portlocks = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500278
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600279 def get(self, key):
280 if key in self.d:
281 return self.d.get(key)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500282 elif os.getenv(key):
283 return os.getenv(key)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600284 else:
285 return ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500286
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600287 def set(self, key, value):
288 self.d[key] = value
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500289
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600290 def is_deploy_dir_image(self, p):
291 if os.path.isdir(p):
292 if not re.search('.qemuboot.conf$', '\n'.join(os.listdir(p)), re.M):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500293 logger.debug("Can't find required *.qemuboot.conf in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600294 return False
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500295 if not any(map(lambda name: '-image-' in name, os.listdir(p))):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500296 logger.debug("Can't find *-image-* in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600297 return False
298 return True
299 else:
300 return False
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500301
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600302 def check_arg_fstype(self, fst):
303 """Check and set FSTYPE"""
Brad Bishop15ae2502019-06-18 21:44:24 -0400304 if fst not in self.fstypes + self.vmtypes + self.wictypes:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800305 logger.warning("Maybe unsupported FSTYPE: %s" % fst)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600306 if not self.fstype or self.fstype == fst:
307 if fst == 'ramfs':
308 fst = 'cpio.gz'
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500309 if fst in ('tar.bz2', 'tar.gz'):
310 fst = 'nfs'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600311 self.fstype = fst
312 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500313 raise RunQemuError("Conflicting: FSTYPE %s and %s" % (self.fstype, fst))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500314
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600315 def set_machine_deploy_dir(self, machine, deploy_dir_image):
316 """Set MACHINE and DEPLOY_DIR_IMAGE"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500317 logger.debug('MACHINE: %s' % machine)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600318 self.set("MACHINE", machine)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500319 logger.debug('DEPLOY_DIR_IMAGE: %s' % deploy_dir_image)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600320 self.set("DEPLOY_DIR_IMAGE", deploy_dir_image)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500321
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600322 def check_arg_nfs(self, p):
323 if os.path.isdir(p):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500324 self.rootfs = p
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600325 else:
326 m = re.match('(.*):(.*)', p)
327 self.nfs_server = m.group(1)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500328 self.rootfs = m.group(2)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600329 self.check_arg_fstype('nfs')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500330
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600331 def check_arg_path(self, p):
332 """
333 - Check whether it is <image>.qemuboot.conf or contains <image>.qemuboot.conf
334 - Check whether is a kernel file
335 - Check whether is a image file
336 - Check whether it is a nfs dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500337 - Check whether it is a OVMF flash file
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600338 """
339 if p.endswith('.qemuboot.conf'):
340 self.qemuboot = p
341 self.qbconfload = True
342 elif re.search('\.bin$', p) or re.search('bzImage', p) or \
343 re.search('zImage', p) or re.search('vmlinux', p) or \
344 re.search('fitImage', p) or re.search('uImage', p):
345 self.kernel = p
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500346 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 -0600347 self.rootfs = p
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500348 # Check filename against self.fstypes can hanlde <file>.cpio.gz,
349 # otherwise, its type would be "gz", which is incorrect.
350 fst = ""
351 for t in self.fstypes:
352 if p.endswith(t):
353 fst = t
354 break
355 if not fst:
356 m = re.search('.*\.(.*)$', self.rootfs)
357 if m:
358 fst = m.group(1)
359 if fst:
360 self.check_arg_fstype(fst)
361 qb = re.sub('\.' + fst + "$", '', self.rootfs)
362 qb = '%s%s' % (re.sub('\.rootfs$', '', qb), '.qemuboot.conf')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600363 if os.path.exists(qb):
364 self.qemuboot = qb
365 self.qbconfload = True
366 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800367 logger.warning("%s doesn't exist" % qb)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600368 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500369 raise RunQemuError("Can't find FSTYPE from: %s" % p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500370
371 elif os.path.isdir(p) or re.search(':', p) and re.search('/', p):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600372 if self.is_deploy_dir_image(p):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500373 logger.debug('DEPLOY_DIR_IMAGE: %s' % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600374 self.set("DEPLOY_DIR_IMAGE", p)
375 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500376 logger.debug("Assuming %s is an nfs rootfs" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600377 self.check_arg_nfs(p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500378 elif os.path.basename(p).startswith('ovmf'):
379 self.ovmf_bios.append(p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600380 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500381 raise RunQemuError("Unknown path arg %s" % p)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500382
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600383 def check_arg_machine(self, arg):
384 """Check whether it is a machine"""
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500385 if self.get('MACHINE') == arg:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600386 return
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500387 elif self.get('MACHINE') and self.get('MACHINE') != arg:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500388 raise RunQemuError("Maybe conflicted MACHINE: %s vs %s" % (self.get('MACHINE'), arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500389 elif re.search('/', arg):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500390 raise RunQemuError("Unknown arg: %s" % arg)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500391
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500392 logger.debug('Assuming MACHINE = %s' % arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500393
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600394 # if we're running under testimage, or similarly as a child
395 # of an existing bitbake invocation, we can't invoke bitbake
396 # to validate the MACHINE setting and must assume it's correct...
397 # FIXME: testimage.bbclass exports these two variables into env,
398 # are there other scenarios in which we need to support being
399 # invoked by bitbake?
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500400 deploy = self.get('DEPLOY_DIR_IMAGE')
401 bbchild = deploy and self.get('OE_TMPDIR')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600402 if bbchild:
403 self.set_machine_deploy_dir(arg, deploy)
404 return
405 # also check whether we're running under a sourced toolchain
406 # environment file
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500407 if self.get('OECORE_NATIVE_SYSROOT'):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600408 self.set("MACHINE", arg)
409 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500410
Andrew Geissler82c905d2020-04-13 13:39:40 -0500411 self.bitbake_e = self.run_bitbake_env(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600412 # bitbake -e doesn't report invalid MACHINE as an error, so
413 # let's check DEPLOY_DIR_IMAGE to make sure that it is a valid
414 # MACHINE.
415 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
416 if s:
417 deploy_dir_image = s.group(1)
418 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500419 raise RunQemuError("bitbake -e %s" % self.bitbake_e)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600420 if self.is_deploy_dir_image(deploy_dir_image):
421 self.set_machine_deploy_dir(arg, deploy_dir_image)
422 else:
423 logger.error("%s not a directory valid DEPLOY_DIR_IMAGE" % deploy_dir_image)
424 self.set("MACHINE", arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500425
Andrew Geisslerc182c622020-05-15 14:13:32 -0500426 def set_dri_path(self):
427 # As runqemu can be run within bitbake (when using testimage, for example),
428 # we need to ensure that we run host pkg-config, and that it does not
429 # get mis-directed to native build paths set by bitbake.
430 try:
431 del os.environ['PKG_CONFIG_PATH']
432 del os.environ['PKG_CONFIG_DIR']
433 del os.environ['PKG_CONFIG_LIBDIR']
434 del os.environ['PKG_CONFIG_SYSROOT_DIR']
435 except KeyError:
436 pass
437 try:
438 dripath = subprocess.check_output("PATH=/bin:/usr/bin:$PATH pkg-config --variable=dridriverdir dri", shell=True)
439 except subprocess.CalledProcessError as e:
440 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.")
441 os.environ['LIBGL_DRIVERS_PATH'] = dripath.decode('utf-8').strip()
442
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600443 def check_args(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500444 for debug in ("-d", "--debug"):
445 if debug in sys.argv:
446 logger.setLevel(logging.DEBUG)
447 sys.argv.remove(debug)
448
449 for quiet in ("-q", "--quiet"):
450 if quiet in sys.argv:
451 logger.setLevel(logging.ERROR)
452 sys.argv.remove(quiet)
453
Andrew Geisslerc182c622020-05-15 14:13:32 -0500454 if 'gl' not in sys.argv[1:] and 'gl-es' not in sys.argv[1:]:
455 os.environ['SDL_RENDER_DRIVER'] = 'software'
456
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600457 unknown_arg = ""
458 for arg in sys.argv[1:]:
Brad Bishop15ae2502019-06-18 21:44:24 -0400459 if arg in self.fstypes + self.vmtypes + self.wictypes:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600460 self.check_arg_fstype(arg)
461 elif arg == 'nographic':
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500462 if ('sdl' in sys.argv):
463 raise RunQemuError('Option nographic makes no sense alongside the sdl option.' % (arg))
464 if ('gtk' in sys.argv):
465 raise RunQemuError('Option nographic makes no sense alongside the gtk option.' % (arg))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600466 self.qemu_opt_script += ' -nographic'
467 self.kernel_cmdline_script += ' console=ttyS0'
Brad Bishop19323692019-04-05 15:28:33 -0400468 elif arg == 'sdl':
Brad Bishop6dbb3162019-11-25 09:41:34 -0500469 if 'gl' in sys.argv[1:]:
Andrew Geisslerc182c622020-05-15 14:13:32 -0500470 self.set_dri_path()
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500471 self.qemu_opt_script += ' -vga virtio -display sdl,gl=on,show-cursor=on'
Brad Bishop6dbb3162019-11-25 09:41:34 -0500472 elif 'gl-es' in sys.argv[1:]:
Andrew Geisslerc182c622020-05-15 14:13:32 -0500473 self.set_dri_path()
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500474 self.qemu_opt_script += ' -vga virtio -display sdl,gl=es,show-cursor=on'
Brad Bishop6dbb3162019-11-25 09:41:34 -0500475 else:
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500476 self.qemu_opt_script += ' -display sdl,show-cursor=on'
Brad Bishopa34c0302019-09-23 22:34:48 -0400477 elif arg == 'gtk':
478 if 'gl' in sys.argv[1:]:
Andrew Geisslerc182c622020-05-15 14:13:32 -0500479 self.set_dri_path()
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500480 self.qemu_opt_script += ' -vga virtio -display gtk,gl=on,show-cursor=on'
Brad Bishopa34c0302019-09-23 22:34:48 -0400481 elif 'gl-es' in sys.argv[1:]:
Andrew Geisslerc182c622020-05-15 14:13:32 -0500482 self.set_dri_path()
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500483 self.qemu_opt_script += ' -vga virtio -display gtk,gl=es,show-cursor=on'
Brad Bishopa34c0302019-09-23 22:34:48 -0400484 else:
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500485 self.qemu_opt_script += ' -display gtk,show-cursor=on'
Brad Bishopa34c0302019-09-23 22:34:48 -0400486 elif arg == 'gl' or arg == 'gl-es':
487 # These args are handled inside sdl or gtk blocks above
Andrew Geissler635e0e42020-08-21 15:58:33 -0500488 if ('gtk' not in sys.argv) and ('sdl' not in sys.argv):
489 raise RunQemuError('Option %s also needs gtk or sdl option.' % (arg))
Brad Bishop19323692019-04-05 15:28:33 -0400490 elif arg == 'egl-headless':
Andrew Geisslerc182c622020-05-15 14:13:32 -0500491 self.set_dri_path()
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500492 self.qemu_opt_script += ' -vga virtio -display egl-headless,show-cursor=on'
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600493 elif arg == 'novga':
494 self.qemu_opt_script += ' -vga none'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600495 elif arg == 'serial':
496 self.kernel_cmdline_script += ' console=ttyS0'
Brad Bishop19323692019-04-05 15:28:33 -0400497 self.serialconsole = True
498 elif arg == "serialstdio":
499 self.kernel_cmdline_script += ' console=ttyS0'
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):
556 self.qemu_opt_script += ' %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU'))
557 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
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600562 self.qemu_opt_script += ' %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU_KVM'))
563 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):
782 raise RunQemuError("KERNEL %s not found" % self.bios)
783
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)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001037 self.clean_nfs_dir = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001038 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001039
1040 # Start the userspace NFS server
Brad Bishop977dc1a2019-02-06 16:01:43 -05001041 cmd = ('runqemu-export-rootfs', 'start', self.rootfs)
1042 logger.info('Running %s...' % str(cmd))
1043 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001044 raise RunQemuError('Failed to run %s' % cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001045
1046 self.nfs_running = True
1047
Andrew Geissler82c905d2020-04-13 13:39:40 -05001048 def setup_net_bridge(self):
1049 self.set('NETWORK_CMD', '-netdev bridge,br=%s,id=net0,helper=%s -device virtio-net-pci,netdev=net0 ' % (
1050 self.net_bridge, os.path.join(self.bindir_native, 'qemu-oe-bridge-helper')))
1051
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001052 def setup_slirp(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001053 """Setup user networking"""
1054
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001055 if self.fstype == 'nfs':
1056 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001057 netconf = " " + self.cmdline_ip_slirp
1058 logger.info("Network configuration:%s", netconf)
1059 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001060 # Port mapping
1061 hostfwd = ",hostfwd=tcp::2222-:22,hostfwd=tcp::2323-:23"
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001062 qb_slirp_opt_default = "-netdev user,id=net0%s,tftp=%s" % (hostfwd, self.get('DEPLOY_DIR_IMAGE'))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001063 qb_slirp_opt = self.get('QB_SLIRP_OPT') or qb_slirp_opt_default
1064 # Figure out the port
1065 ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt)
1066 ports = [int(i) for i in ports]
1067 mac = 2
Brad Bishop08902b02019-08-20 09:16:51 -04001068
1069 lockdir = "/tmp/qemu-port-locks"
1070 if not os.path.exists(lockdir):
1071 # There might be a race issue when multi runqemu processess are
1072 # running at the same time.
1073 try:
1074 os.mkdir(lockdir)
1075 os.chmod(lockdir, 0o777)
1076 except FileExistsError:
1077 pass
1078
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001079 # Find a free port to avoid conflicts
1080 for p in ports[:]:
1081 p_new = p
Brad Bishop08902b02019-08-20 09:16:51 -04001082 while not self.check_free_port('localhost', p_new, lockdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001083 p_new += 1
1084 mac += 1
1085 while p_new in ports:
1086 p_new += 1
1087 mac += 1
1088 if p != p_new:
1089 ports.append(p_new)
1090 qb_slirp_opt = re.sub(':%s-' % p, ':%s-' % p_new, qb_slirp_opt)
1091 logger.info("Port forward changed: %s -> %s" % (p, p_new))
1092 mac = "%s%02x" % (self.mac_slirp, mac)
1093 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qb_slirp_opt))
1094 # Print out port foward
1095 hostfwd = re.findall('(hostfwd=[^,]*)', qb_slirp_opt)
1096 if hostfwd:
1097 logger.info('Port forward: %s' % ' '.join(hostfwd))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001098
1099 def setup_tap(self):
1100 """Setup tap"""
1101
1102 # This file is created when runqemu-gen-tapdevs creates a bank of tap
1103 # devices, indicating that the user should not bring up new ones using
1104 # sudo.
1105 nosudo_flag = '/etc/runqemu-nosudo'
1106 self.qemuifup = shutil.which('runqemu-ifup')
1107 self.qemuifdown = shutil.which('runqemu-ifdown')
1108 ip = shutil.which('ip')
1109 lockdir = "/tmp/qemu-tap-locks"
1110
1111 if not (self.qemuifup and self.qemuifdown and ip):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001112 logger.error("runqemu-ifup: %s" % self.qemuifup)
1113 logger.error("runqemu-ifdown: %s" % self.qemuifdown)
1114 logger.error("ip: %s" % ip)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001115 raise OEPathError("runqemu-ifup, runqemu-ifdown or ip not found")
1116
1117 if not os.path.exists(lockdir):
1118 # There might be a race issue when multi runqemu processess are
1119 # running at the same time.
1120 try:
1121 os.mkdir(lockdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001122 os.chmod(lockdir, 0o777)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001123 except FileExistsError:
1124 pass
1125
Brad Bishop977dc1a2019-02-06 16:01:43 -05001126 cmd = (ip, 'link')
1127 logger.debug('Running %s...' % str(cmd))
1128 ip_link = subprocess.check_output(cmd).decode('utf-8')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001129 # Matches line like: 6: tap0: <foo>
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001130 possibles = re.findall('^[0-9]+: +(tap[0-9]+): <.*', ip_link, re.M)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001131 tap = ""
1132 for p in possibles:
1133 lockfile = os.path.join(lockdir, p)
1134 if os.path.exists('%s.skip' % lockfile):
1135 logger.info('Found %s.skip, skipping %s' % (lockfile, p))
1136 continue
Brad Bishop08902b02019-08-20 09:16:51 -04001137 self.taplock = lockfile + '.lock'
1138 if self.acquire_taplock(error=False):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001139 tap = p
1140 logger.info("Using preconfigured tap device %s" % tap)
1141 logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap))
1142 break
1143
1144 if not tap:
1145 if os.path.exists(nosudo_flag):
1146 logger.error("Error: There are no available tap devices to use for networking,")
1147 logger.error("and I see %s exists, so I am not going to try creating" % nosudo_flag)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001148 raise RunQemuError("a new one with sudo.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001149
1150 gid = os.getgid()
1151 uid = os.getuid()
1152 logger.info("Setting up tap interface under sudo")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001153 cmd = ('sudo', self.qemuifup, str(uid), str(gid), self.bindir_native)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001154 try:
1155 tap = subprocess.check_output(cmd).decode('utf-8').strip()
1156 except subprocess.CalledProcessError as e:
1157 logger.error('Setting up tap device failed:\n%s\nRun runqemu-gen-tapdevs to manually create one.' % str(e))
1158 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001159 lockfile = os.path.join(lockdir, tap)
Brad Bishop08902b02019-08-20 09:16:51 -04001160 self.taplock = lockfile + '.lock'
1161 self.acquire_taplock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001162 self.cleantap = True
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001163 logger.debug('Created tap: %s' % tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001164
1165 if not tap:
1166 logger.error("Failed to setup tap device. Run runqemu-gen-tapdevs to manually create.")
Andrew Geissler82c905d2020-04-13 13:39:40 -05001167 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001168 self.tap = tap
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001169 tapnum = int(tap[3:])
1170 gateway = tapnum * 2 + 1
1171 client = gateway + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001172 if self.fstype == 'nfs':
1173 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001174 netconf = " " + self.cmdline_ip_tap
1175 netconf = netconf.replace('@CLIENT@', str(client))
1176 netconf = netconf.replace('@GATEWAY@', str(gateway))
1177 logger.info("Network configuration:%s", netconf)
1178 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001179 mac = "%s%02x" % (self.mac_tap, client)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001180 qb_tap_opt = self.get('QB_TAP_OPT')
1181 if qb_tap_opt:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001182 qemu_tap_opt = qb_tap_opt.replace('@TAP@', tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001183 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001184 qemu_tap_opt = "-netdev tap,id=net0,ifname=%s,script=no,downscript=no" % (self.tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001185
1186 if self.vhost_enabled:
1187 qemu_tap_opt += ',vhost=on'
1188
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001189 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qemu_tap_opt))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001190
1191 def setup_network(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001192 if self.get('QB_NET') == 'none':
1193 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001194 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001195 self.saved_stty = subprocess.check_output(("stty", "-g")).decode('utf-8').strip()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001196 self.network_device = self.get('QB_NETWORK_DEVICE') or self.network_device
Andrew Geissler82c905d2020-04-13 13:39:40 -05001197 if self.net_bridge:
1198 self.setup_net_bridge()
1199 elif self.slirp_enabled:
1200 self.cmdline_ip_slirp = self.get('QB_CMDLINE_IP_SLIRP') or self.cmdline_ip_slirp
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001201 self.setup_slirp()
1202 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001203 self.cmdline_ip_tap = self.get('QB_CMDLINE_IP_TAP') or self.cmdline_ip_tap
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001204 self.setup_tap()
1205
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001206 def setup_rootfs(self):
1207 if self.get('QB_ROOTFS') == 'none':
1208 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001209 if 'wic.' in self.fstype:
1210 self.fstype = self.fstype[4:]
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001211 rootfs_format = self.fstype if self.fstype in ('vmdk', 'vhd', 'vhdx', 'qcow2', 'vdi') else 'raw'
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001212
1213 qb_rootfs_opt = self.get('QB_ROOTFS_OPT')
1214 if qb_rootfs_opt:
1215 self.rootfs_options = qb_rootfs_opt.replace('@ROOTFS@', self.rootfs)
1216 else:
1217 self.rootfs_options = '-drive file=%s,if=virtio,format=%s' % (self.rootfs, rootfs_format)
1218
Andrew Geissler82c905d2020-04-13 13:39:40 -05001219 qb_rootfs_extra_opt = self.get("QB_ROOTFS_EXTRA_OPT")
1220 if qb_rootfs_extra_opt and not qb_rootfs_extra_opt.startswith(","):
1221 qb_rootfs_extra_opt = "," + qb_rootfs_extra_opt
1222
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001223 if self.fstype in ('cpio.gz', 'cpio'):
1224 self.kernel_cmdline = 'root=/dev/ram0 rw debugshell'
1225 self.rootfs_options = '-initrd %s' % self.rootfs
1226 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001227 vm_drive = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001228 if self.fstype in self.vmtypes:
1229 if self.fstype == 'iso':
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001230 vm_drive = '-drive file=%s,if=virtio,media=cdrom' % self.rootfs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001231 elif self.get('QB_DRIVE_TYPE'):
1232 drive_type = self.get('QB_DRIVE_TYPE')
1233 if drive_type.startswith("/dev/sd"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001234 logger.info('Using scsi drive')
Andrew Geissler82c905d2020-04-13 13:39:40 -05001235 vm_drive = '-drive if=none,id=hd,file=%s,format=%s -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd%s' \
1236 % (self.rootfs, rootfs_format, qb_rootfs_extra_opt)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001237 elif drive_type.startswith("/dev/hd"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001238 logger.info('Using ide drive')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001239 vm_drive = "-drive file=%s,format=%s" % (self.rootfs, rootfs_format)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001240 elif drive_type.startswith("/dev/vdb"):
1241 logger.info('Using block virtio drive');
1242 vm_drive = '-drive id=disk0,file=%s,if=none,format=%s -device virtio-blk-device,drive=disk0%s' \
1243 % (self.rootfs, rootfs_format,qb_rootfs_extra_opt)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001244 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001245 # virtio might have been selected explicitly (just use it), or
1246 # is used as fallback (then warn about that).
1247 if not drive_type.startswith("/dev/vd"):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001248 logger.warning("Unknown QB_DRIVE_TYPE: %s" % drive_type)
1249 logger.warning("Failed to figure out drive type, consider define or fix QB_DRIVE_TYPE")
1250 logger.warning('Trying to use virtio block drive')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001251 vm_drive = '-drive if=virtio,file=%s,format=%s' % (self.rootfs, rootfs_format)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001252
1253 # All branches above set vm_drive.
Andrew Geissler475cb722020-07-10 16:00:51 -05001254 self.rootfs_options = vm_drive
1255 if not self.fstype in self.vmtypes:
1256 self.rootfs_options += ' -no-reboot'
Brad Bishopf3f93bb2019-10-16 14:33:32 -04001257 self.kernel_cmdline = 'root=%s rw' % (self.get('QB_KERNEL_ROOT'))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001258
1259 if self.fstype == 'nfs':
1260 self.rootfs_options = ''
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001261 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 -04001262 self.kernel_cmdline = 'root=%s rw' % k_root
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001263
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001264 if self.fstype == 'none':
1265 self.rootfs_options = ''
1266
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001267 self.set('ROOTFS_OPTIONS', self.rootfs_options)
1268
1269 def guess_qb_system(self):
1270 """attempt to determine the appropriate qemu-system binary"""
1271 mach = self.get('MACHINE')
1272 if not mach:
1273 search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*'
1274 if self.rootfs:
1275 match = re.match(search, self.rootfs)
1276 if match:
1277 mach = match.group(1)
1278 elif self.kernel:
1279 match = re.match(search, self.kernel)
1280 if match:
1281 mach = match.group(1)
1282
1283 if not mach:
1284 return None
1285
1286 if mach == 'qemuarm':
1287 qbsys = 'arm'
1288 elif mach == 'qemuarm64':
1289 qbsys = 'aarch64'
1290 elif mach == 'qemux86':
1291 qbsys = 'i386'
1292 elif mach == 'qemux86-64':
1293 qbsys = 'x86_64'
1294 elif mach == 'qemuppc':
1295 qbsys = 'ppc'
1296 elif mach == 'qemumips':
1297 qbsys = 'mips'
1298 elif mach == 'qemumips64':
1299 qbsys = 'mips64'
1300 elif mach == 'qemumipsel':
1301 qbsys = 'mipsel'
1302 elif mach == 'qemumips64el':
1303 qbsys = 'mips64el'
Brad Bishop316dfdd2018-06-25 12:45:53 -04001304 elif mach == 'qemuriscv64':
1305 qbsys = 'riscv64'
1306 elif mach == 'qemuriscv32':
1307 qbsys = 'riscv32'
Brad Bishop004d4992018-10-02 23:54:45 +02001308 else:
1309 logger.error("Unable to determine QEMU PC System emulator for %s machine." % mach)
1310 logger.error("As %s is not among valid QEMU machines such as," % mach)
1311 logger.error("qemux86-64, qemux86, qemuarm64, qemuarm, qemumips64, qemumips64el, qemumipsel, qemumips, qemuppc")
1312 raise RunQemuError("Set qb_system_name with suitable QEMU PC System emulator in .*qemuboot.conf.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001313
1314 return 'qemu-system-%s' % qbsys
1315
Brad Bishop15ae2502019-06-18 21:44:24 -04001316 def check_qemu_system(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001317 qemu_system = self.get('QB_SYSTEM_NAME')
1318 if not qemu_system:
1319 qemu_system = self.guess_qb_system()
1320 if not qemu_system:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001321 raise RunQemuError("Failed to boot, QB_SYSTEM_NAME is NULL!")
Brad Bishop15ae2502019-06-18 21:44:24 -04001322 self.qemu_system = qemu_system
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001323
Brad Bishop15ae2502019-06-18 21:44:24 -04001324 def setup_final(self):
1325 qemu_bin = os.path.join(self.bindir_native, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001326
1327 # It is possible to have qemu-native in ASSUME_PROVIDED, and it won't
1328 # find QEMU in sysroot, it needs to use host's qemu.
1329 if not os.path.exists(qemu_bin):
1330 logger.info("QEMU binary not found in %s, trying host's QEMU" % qemu_bin)
1331 for path in (os.environ['PATH'] or '').split(':'):
Brad Bishop15ae2502019-06-18 21:44:24 -04001332 qemu_bin_tmp = os.path.join(path, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001333 logger.info("Trying: %s" % qemu_bin_tmp)
1334 if os.path.exists(qemu_bin_tmp):
1335 qemu_bin = qemu_bin_tmp
1336 if not os.path.isabs(qemu_bin):
1337 qemu_bin = os.path.abspath(qemu_bin)
1338 logger.info("Using host's QEMU: %s" % qemu_bin)
1339 break
1340
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001341 if not os.access(qemu_bin, os.X_OK):
1342 raise OEPathError("No QEMU binary '%s' could be found" % qemu_bin)
1343
Andrew Geisslerc3d88e42020-10-02 09:45:00 -05001344 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 -05001345
1346 for ovmf in self.ovmf_bios:
1347 format = ovmf.rsplit('.', 1)[-1]
1348 self.qemu_opt += ' -drive if=pflash,format=%s,file=%s' % (format, ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001349
1350 self.qemu_opt += ' ' + self.qemu_opt_script
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001351
Brad Bishop08902b02019-08-20 09:16:51 -04001352 if self.ovmf_secboot_pkkek1:
1353 # Provide the Platform Key and first Key Exchange Key certificate as an
1354 # OEM string in the SMBIOS Type 11 table. Prepend the certificate string
1355 # with "application prefix" of the EnrollDefaultKeys.efi application
1356 self.qemu_opt += ' -smbios type=11,value=4e32566d-8e9e-4f52-81d3-5bb9715f9727:' \
1357 + self.ovmf_secboot_pkkek1
1358
Andrew Geissler99467da2019-02-25 18:54:23 -06001359 # Append qemuparams to override previous settings
1360 if self.qemuparams:
1361 self.qemu_opt += ' ' + self.qemuparams
1362
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001363 if self.snapshot:
1364 self.qemu_opt += " -snapshot"
1365
Brad Bishop19323692019-04-05 15:28:33 -04001366 if self.serialconsole:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001367 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001368 subprocess.check_call(("stty", "intr", "^]"))
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001369 logger.info("Interrupt character is '^]'")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001370
1371 first_serial = ""
1372 if not re.search("-nographic", self.qemu_opt):
1373 first_serial = "-serial mon:vc"
1374 # We always want a ttyS1. Since qemu by default adds a serial
1375 # port when nodefaults is not specified, it seems that all that
1376 # would be needed is to make sure a "-serial" is there. However,
1377 # it appears that when "-serial" is specified, it ignores the
1378 # default serial port that is normally added. So here we make
1379 # sure to add two -serial if there are none. And only one if
1380 # there is one -serial already.
1381 serial_num = len(re.findall("-serial", self.qemu_opt))
1382 if serial_num == 0:
1383 self.qemu_opt += " %s %s" % (first_serial, self.get("QB_SERIAL_OPT"))
1384 elif serial_num == 1:
1385 self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT")
1386
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001387 # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES),
1388 # if not serial or serialtcp options was specified only ttyS0 is created
1389 # and sysvinit shows an error trying to enable ttyS1:
1390 # INIT: Id "S1" respawning too fast: disabled for 5 minutes
1391 serial_num = len(re.findall("-serial", self.qemu_opt))
1392 if serial_num == 0:
Brad Bishop19323692019-04-05 15:28:33 -04001393 if re.search("-nographic", self.qemu_opt) or self.serialstdio:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001394 self.qemu_opt += " -serial mon:stdio -serial null"
1395 else:
1396 self.qemu_opt += " -serial mon:vc -serial null"
1397
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001398 def start_qemu(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001399 import shlex
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001400 if self.kernel:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001401 kernel_opts = "-kernel %s -append '%s %s %s %s'" % (self.kernel, self.kernel_cmdline,
1402 self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'),
1403 self.bootparams)
Brad Bishopc68388fc2019-08-26 01:33:31 -04001404 if self.bios:
1405 kernel_opts += " -bios %s" % self.bios
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001406 if self.dtb:
1407 kernel_opts += " -dtb %s" % self.dtb
1408 else:
1409 kernel_opts = ""
1410 cmd = "%s %s" % (self.qemu_opt, kernel_opts)
Brad Bishop004d4992018-10-02 23:54:45 +02001411 cmds = shlex.split(cmd)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001412 logger.info('Running %s\n' % cmd)
Brad Bishopf86d0552018-12-04 14:18:15 -08001413 pass_fds = []
Brad Bishop08902b02019-08-20 09:16:51 -04001414 if self.taplock_descriptor:
1415 pass_fds = [self.taplock_descriptor.fileno()]
1416 if len(self.portlocks):
1417 for descriptor in self.portlocks.values():
1418 pass_fds.append(descriptor.fileno())
Brad Bishopf86d0552018-12-04 14:18:15 -08001419 process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds)
Brad Bishop004d4992018-10-02 23:54:45 +02001420 self.qemupid = process.pid
1421 retcode = process.wait()
1422 if retcode:
1423 if retcode == -signal.SIGTERM:
1424 logger.info("Qemu terminated by SIGTERM")
1425 else:
1426 logger.error("Failed to run qemu: %s", process.stderr.read().decode())
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001427
1428 def cleanup(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001429 if self.cleaned:
1430 return
1431
1432 # avoid dealing with SIGTERM when cleanup function is running
1433 signal.signal(signal.SIGTERM, signal.SIG_IGN)
1434
1435 logger.info("Cleaning up")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001436 if self.cleantap:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001437 cmd = ('sudo', self.qemuifdown, self.tap, self.bindir_native)
1438 logger.debug('Running %s' % str(cmd))
1439 subprocess.check_call(cmd)
Brad Bishop08902b02019-08-20 09:16:51 -04001440 self.release_taplock()
1441 self.release_portlock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001442
1443 if self.nfs_running:
1444 logger.info("Shutting down the userspace NFS server...")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001445 cmd = ("runqemu-export-rootfs", "stop", self.rootfs)
1446 logger.debug('Running %s' % str(cmd))
1447 subprocess.check_call(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001448
1449 if self.saved_stty:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001450 subprocess.check_call(("stty", self.saved_stty))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001451
1452 if self.clean_nfs_dir:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001453 logger.info('Removing %s' % self.rootfs)
1454 shutil.rmtree(self.rootfs)
1455 shutil.rmtree('%s.pseudo_state' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001456
Brad Bishop004d4992018-10-02 23:54:45 +02001457 self.cleaned = True
1458
Andrew Geissler82c905d2020-04-13 13:39:40 -05001459 def run_bitbake_env(self, mach=None):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001460 bitbake = shutil.which('bitbake')
1461 if not bitbake:
1462 return
1463
1464 if not mach:
1465 mach = self.get('MACHINE')
1466
Andrew Geissler82c905d2020-04-13 13:39:40 -05001467 multiconfig = self.get('MULTICONFIG')
1468 if multiconfig:
1469 multiconfig = "mc:%s" % multiconfig
1470
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001471 if mach:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001472 cmd = 'MACHINE=%s bitbake -e %s' % (mach, multiconfig)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001473 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001474 cmd = 'bitbake -e %s' % multiconfig
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001475
1476 logger.info('Running %s...' % cmd)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001477 return subprocess.check_output(cmd, shell=True).decode('utf-8')
1478
1479 def load_bitbake_env(self, mach=None):
1480 if self.bitbake_e:
1481 return
1482
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001483 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001484 self.bitbake_e = self.run_bitbake_env(mach=mach)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001485 except subprocess.CalledProcessError as err:
1486 self.bitbake_e = ''
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001487 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 -06001488
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001489 def validate_combos(self):
1490 if (self.fstype in self.vmtypes) and self.kernel:
1491 raise RunQemuError("%s doesn't need kernel %s!" % (self.fstype, self.kernel))
1492
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001493 @property
1494 def bindir_native(self):
1495 result = self.get('STAGING_BINDIR_NATIVE')
1496 if result and os.path.exists(result):
1497 return result
1498
Andrew Geissler82c905d2020-04-13 13:39:40 -05001499 cmd = ['bitbake', '-e']
1500 multiconfig = self.get('MULTICONFIG')
1501 if multiconfig:
1502 cmd.append('mc:%s:qemu-helper-native' % multiconfig)
1503 else:
1504 cmd.append('qemu-helper-native')
1505
Brad Bishop977dc1a2019-02-06 16:01:43 -05001506 logger.info('Running %s...' % str(cmd))
1507 out = subprocess.check_output(cmd).decode('utf-8')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001508
1509 match = re.search('^STAGING_BINDIR_NATIVE="(.*)"', out, re.M)
1510 if match:
1511 result = match.group(1)
1512 if os.path.exists(result):
1513 self.set('STAGING_BINDIR_NATIVE', result)
1514 return result
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001515 raise RunQemuError("Native sysroot directory %s doesn't exist" % result)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001516 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001517 raise RunQemuError("Can't find STAGING_BINDIR_NATIVE in '%s' output" % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001518
1519
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001520def main():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001521 if "help" in sys.argv or '-h' in sys.argv or '--help' in sys.argv:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001522 print_usage()
1523 return 0
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001524 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001525 config = BaseConfig()
Brad Bishop004d4992018-10-02 23:54:45 +02001526
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001527 renice = os.path.expanduser("~/bin/runqemu-renice")
1528 if os.path.exists(renice):
1529 logger.info('Using %s to renice' % renice)
1530 subprocess.check_call([renice, str(os.getpid())])
1531
Brad Bishop004d4992018-10-02 23:54:45 +02001532 def sigterm_handler(signum, frame):
1533 logger.info("SIGTERM received")
1534 os.kill(config.qemupid, signal.SIGTERM)
1535 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001536 # Deliberately ignore the return code of 'tput smam'.
1537 subprocess.call(["tput", "smam"])
Brad Bishop004d4992018-10-02 23:54:45 +02001538 signal.signal(signal.SIGTERM, sigterm_handler)
1539
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001540 config.check_args()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001541 config.read_qemuboot()
1542 config.check_and_set()
1543 # Check whether the combos is valid or not
1544 config.validate_combos()
1545 config.print_config()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001546 config.setup_network()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001547 config.setup_rootfs()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001548 config.setup_final()
1549 config.start_qemu()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001550 except RunQemuError as err:
1551 logger.error(err)
1552 return 1
1553 except Exception as err:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001554 import traceback
1555 traceback.print_exc()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001556 return 1
1557 finally:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001558 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001559 # Deliberately ignore the return code of 'tput smam'.
1560 subprocess.call(["tput", "smam"])
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001561
1562if __name__ == "__main__":
1563 sys.exit(main())