blob: edd17d09c4ac276886831ea3107a7e21330dea42 [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.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
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500176 self.nographic = False
177 self.sdl = False
178 self.gtk = False
179 self.gl = False
180 self.gl_es = False
181 self.egl_headless = False
182 self.novga = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600183 self.cleantap = False
184 self.saved_stty = ''
185 self.audio_enabled = False
186 self.tcpserial_portnum = ''
Brad Bishop08902b02019-08-20 09:16:51 -0400187 self.taplock = ''
188 self.taplock_descriptor = None
189 self.portlocks = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600190 self.bitbake_e = ''
191 self.snapshot = False
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600192 self.wictypes = ('wic', 'wic.vmdk', 'wic.qcow2', 'wic.vdi', "wic.vhd", "wic.vhdx")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500193 self.fstypes = ('ext2', 'ext3', 'ext4', 'jffs2', 'nfs', 'btrfs',
194 'cpio.gz', 'cpio', 'ramfs', 'tar.bz2', 'tar.gz')
Brad Bishop08902b02019-08-20 09:16:51 -0400195 self.vmtypes = ('hddimg', 'iso')
Brad Bishop15ae2502019-06-18 21:44:24 -0400196 self.fsinfo = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500197 self.network_device = "-device e1000,netdev=net0,mac=@MAC@"
Andrew Geissler82c905d2020-04-13 13:39:40 -0500198 self.cmdline_ip_slirp = "ip=dhcp"
199 self.cmdline_ip_tap = "ip=192.168.7.@CLIENT@::192.168.7.@GATEWAY@:255.255.255.0"
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500200 # Use different mac section for tap and slirp to avoid
201 # conflicts, e.g., when one is running with tap, the other is
202 # running with slirp.
203 # The last section is dynamic, which is for avoiding conflicts,
204 # when multiple qemus are running, e.g., when multiple tap or
205 # slirp qemus are running.
206 self.mac_tap = "52:54:00:12:34:"
207 self.mac_slirp = "52:54:00:12:35:"
Brad Bishop004d4992018-10-02 23:54:45 +0200208 # pid of the actual qemu process
209 self.qemupid = None
210 # avoid cleanup twice
211 self.cleaned = False
Andrew Geisslerc926e172021-05-07 16:11:35 -0500212 # Files to cleanup after run
213 self.cleanup_files = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500214
Brad Bishop08902b02019-08-20 09:16:51 -0400215 def acquire_taplock(self, error=True):
216 logger.debug("Acquiring lockfile %s..." % self.taplock)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600217 try:
Brad Bishop08902b02019-08-20 09:16:51 -0400218 self.taplock_descriptor = open(self.taplock, 'w')
219 fcntl.flock(self.taplock_descriptor, fcntl.LOCK_EX|fcntl.LOCK_NB)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600220 except Exception as e:
Brad Bishop08902b02019-08-20 09:16:51 -0400221 msg = "Acquiring lockfile %s failed: %s" % (self.taplock, e)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500222 if error:
223 logger.error(msg)
224 else:
225 logger.info(msg)
Brad Bishop08902b02019-08-20 09:16:51 -0400226 if self.taplock_descriptor:
227 self.taplock_descriptor.close()
228 self.taplock_descriptor = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600229 return False
230 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500231
Brad Bishop08902b02019-08-20 09:16:51 -0400232 def release_taplock(self):
233 if self.taplock_descriptor:
Brad Bishopf86d0552018-12-04 14:18:15 -0800234 logger.debug("Releasing lockfile for tap device '%s'" % self.tap)
Brad Bishop08902b02019-08-20 09:16:51 -0400235 fcntl.flock(self.taplock_descriptor, fcntl.LOCK_UN)
236 self.taplock_descriptor.close()
237 os.remove(self.taplock)
238 self.taplock_descriptor = None
239
240 def check_free_port(self, host, port, lockdir):
241 """ Check whether the port is free or not """
242 import socket
243 from contextlib import closing
244
245 lockfile = os.path.join(lockdir, str(port) + '.lock')
246 if self.acquire_portlock(lockfile):
247 with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
248 if sock.connect_ex((host, port)) == 0:
249 # Port is open, so not free
250 self.release_portlock(lockfile)
251 return False
252 else:
253 # Port is not open, so free
254 return True
255 else:
256 return False
257
258 def acquire_portlock(self, lockfile):
259 logger.debug("Acquiring lockfile %s..." % lockfile)
260 try:
261 portlock_descriptor = open(lockfile, 'w')
262 self.portlocks.update({lockfile: portlock_descriptor})
263 fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_EX|fcntl.LOCK_NB)
264 except Exception as e:
265 msg = "Acquiring lockfile %s failed: %s" % (lockfile, e)
266 logger.info(msg)
267 if lockfile in self.portlocks.keys() and self.portlocks[lockfile]:
268 self.portlocks[lockfile].close()
269 del self.portlocks[lockfile]
270 return False
271 return True
272
273 def release_portlock(self, lockfile=None):
274 if lockfile != None:
275 logger.debug("Releasing lockfile '%s'" % lockfile)
276 fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_UN)
277 self.portlocks[lockfile].close()
278 os.remove(lockfile)
279 del self.portlocks[lockfile]
280 elif len(self.portlocks):
281 for lockfile, descriptor in self.portlocks.items():
282 logger.debug("Releasing lockfile '%s'" % lockfile)
283 fcntl.flock(descriptor, fcntl.LOCK_UN)
284 descriptor.close()
285 os.remove(lockfile)
286 self.portlocks = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500287
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600288 def get(self, key):
289 if key in self.d:
290 return self.d.get(key)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500291 elif os.getenv(key):
292 return os.getenv(key)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600293 else:
294 return ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500295
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600296 def set(self, key, value):
297 self.d[key] = value
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500298
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600299 def is_deploy_dir_image(self, p):
300 if os.path.isdir(p):
301 if not re.search('.qemuboot.conf$', '\n'.join(os.listdir(p)), re.M):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500302 logger.debug("Can't find required *.qemuboot.conf in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600303 return False
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500304 if not any(map(lambda name: '-image-' in name, os.listdir(p))):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500305 logger.debug("Can't find *-image-* in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600306 return False
307 return True
308 else:
309 return False
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500310
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600311 def check_arg_fstype(self, fst):
312 """Check and set FSTYPE"""
Brad Bishop15ae2502019-06-18 21:44:24 -0400313 if fst not in self.fstypes + self.vmtypes + self.wictypes:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800314 logger.warning("Maybe unsupported FSTYPE: %s" % fst)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600315 if not self.fstype or self.fstype == fst:
316 if fst == 'ramfs':
317 fst = 'cpio.gz'
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500318 if fst in ('tar.bz2', 'tar.gz'):
319 fst = 'nfs'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600320 self.fstype = fst
321 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500322 raise RunQemuError("Conflicting: FSTYPE %s and %s" % (self.fstype, fst))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500323
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600324 def set_machine_deploy_dir(self, machine, deploy_dir_image):
325 """Set MACHINE and DEPLOY_DIR_IMAGE"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500326 logger.debug('MACHINE: %s' % machine)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600327 self.set("MACHINE", machine)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500328 logger.debug('DEPLOY_DIR_IMAGE: %s' % deploy_dir_image)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600329 self.set("DEPLOY_DIR_IMAGE", deploy_dir_image)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500330
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600331 def check_arg_nfs(self, p):
332 if os.path.isdir(p):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500333 self.rootfs = p
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600334 else:
335 m = re.match('(.*):(.*)', p)
336 self.nfs_server = m.group(1)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500337 self.rootfs = m.group(2)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600338 self.check_arg_fstype('nfs')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500339
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600340 def check_arg_path(self, p):
341 """
342 - Check whether it is <image>.qemuboot.conf or contains <image>.qemuboot.conf
343 - Check whether is a kernel file
344 - Check whether is a image file
345 - Check whether it is a nfs dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500346 - Check whether it is a OVMF flash file
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600347 """
348 if p.endswith('.qemuboot.conf'):
349 self.qemuboot = p
350 self.qbconfload = True
351 elif re.search('\.bin$', p) or re.search('bzImage', p) or \
352 re.search('zImage', p) or re.search('vmlinux', p) or \
353 re.search('fitImage', p) or re.search('uImage', p):
354 self.kernel = p
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500355 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 -0600356 self.rootfs = p
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500357 # Check filename against self.fstypes can hanlde <file>.cpio.gz,
358 # otherwise, its type would be "gz", which is incorrect.
359 fst = ""
360 for t in self.fstypes:
361 if p.endswith(t):
362 fst = t
363 break
364 if not fst:
365 m = re.search('.*\.(.*)$', self.rootfs)
366 if m:
367 fst = m.group(1)
368 if fst:
369 self.check_arg_fstype(fst)
370 qb = re.sub('\.' + fst + "$", '', self.rootfs)
371 qb = '%s%s' % (re.sub('\.rootfs$', '', qb), '.qemuboot.conf')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600372 if os.path.exists(qb):
373 self.qemuboot = qb
374 self.qbconfload = True
375 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800376 logger.warning("%s doesn't exist" % qb)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600377 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500378 raise RunQemuError("Can't find FSTYPE from: %s" % p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500379
380 elif os.path.isdir(p) or re.search(':', p) and re.search('/', p):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600381 if self.is_deploy_dir_image(p):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500382 logger.debug('DEPLOY_DIR_IMAGE: %s' % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600383 self.set("DEPLOY_DIR_IMAGE", p)
384 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500385 logger.debug("Assuming %s is an nfs rootfs" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600386 self.check_arg_nfs(p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500387 elif os.path.basename(p).startswith('ovmf'):
388 self.ovmf_bios.append(p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600389 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500390 raise RunQemuError("Unknown path arg %s" % p)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500391
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600392 def check_arg_machine(self, arg):
393 """Check whether it is a machine"""
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500394 if self.get('MACHINE') == arg:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600395 return
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500396 elif self.get('MACHINE') and self.get('MACHINE') != arg:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500397 raise RunQemuError("Maybe conflicted MACHINE: %s vs %s" % (self.get('MACHINE'), arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500398 elif re.search('/', arg):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500399 raise RunQemuError("Unknown arg: %s" % arg)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500400
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500401 logger.debug('Assuming MACHINE = %s' % arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500402
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600403 # if we're running under testimage, or similarly as a child
404 # of an existing bitbake invocation, we can't invoke bitbake
405 # to validate the MACHINE setting and must assume it's correct...
406 # FIXME: testimage.bbclass exports these two variables into env,
407 # are there other scenarios in which we need to support being
408 # invoked by bitbake?
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500409 deploy = self.get('DEPLOY_DIR_IMAGE')
410 bbchild = deploy and self.get('OE_TMPDIR')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600411 if bbchild:
412 self.set_machine_deploy_dir(arg, deploy)
413 return
414 # also check whether we're running under a sourced toolchain
415 # environment file
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500416 if self.get('OECORE_NATIVE_SYSROOT'):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600417 self.set("MACHINE", arg)
418 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500419
Andrew Geissler82c905d2020-04-13 13:39:40 -0500420 self.bitbake_e = self.run_bitbake_env(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600421 # bitbake -e doesn't report invalid MACHINE as an error, so
422 # let's check DEPLOY_DIR_IMAGE to make sure that it is a valid
423 # MACHINE.
424 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
425 if s:
426 deploy_dir_image = s.group(1)
427 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500428 raise RunQemuError("bitbake -e %s" % self.bitbake_e)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600429 if self.is_deploy_dir_image(deploy_dir_image):
430 self.set_machine_deploy_dir(arg, deploy_dir_image)
431 else:
432 logger.error("%s not a directory valid DEPLOY_DIR_IMAGE" % deploy_dir_image)
433 self.set("MACHINE", arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500434
Andrew Geisslerc182c622020-05-15 14:13:32 -0500435 def set_dri_path(self):
436 # As runqemu can be run within bitbake (when using testimage, for example),
437 # we need to ensure that we run host pkg-config, and that it does not
438 # get mis-directed to native build paths set by bitbake.
439 try:
440 del os.environ['PKG_CONFIG_PATH']
441 del os.environ['PKG_CONFIG_DIR']
442 del os.environ['PKG_CONFIG_LIBDIR']
443 del os.environ['PKG_CONFIG_SYSROOT_DIR']
444 except KeyError:
445 pass
446 try:
447 dripath = subprocess.check_output("PATH=/bin:/usr/bin:$PATH pkg-config --variable=dridriverdir dri", shell=True)
448 except subprocess.CalledProcessError as e:
449 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.")
450 os.environ['LIBGL_DRIVERS_PATH'] = dripath.decode('utf-8').strip()
451
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600452 def check_args(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500453 for debug in ("-d", "--debug"):
454 if debug in sys.argv:
455 logger.setLevel(logging.DEBUG)
456 sys.argv.remove(debug)
457
458 for quiet in ("-q", "--quiet"):
459 if quiet in sys.argv:
460 logger.setLevel(logging.ERROR)
461 sys.argv.remove(quiet)
462
Andrew Geisslerc182c622020-05-15 14:13:32 -0500463 if 'gl' not in sys.argv[1:] and 'gl-es' not in sys.argv[1:]:
464 os.environ['SDL_RENDER_DRIVER'] = 'software'
465
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600466 unknown_arg = ""
467 for arg in sys.argv[1:]:
Brad Bishop15ae2502019-06-18 21:44:24 -0400468 if arg in self.fstypes + self.vmtypes + self.wictypes:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600469 self.check_arg_fstype(arg)
470 elif arg == 'nographic':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500471 self.nographic = True
Brad Bishop19323692019-04-05 15:28:33 -0400472 elif arg == 'sdl':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500473 self.sdl = True
Brad Bishopa34c0302019-09-23 22:34:48 -0400474 elif arg == 'gtk':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500475 self.gtk = True
476 elif arg == 'gl':
477 self.gl = True
478 elif 'gl-es' in sys.argv[1:]:
479 self.gl_es = True
Brad Bishop19323692019-04-05 15:28:33 -0400480 elif arg == 'egl-headless':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500481 self.egl_headless = True
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600482 elif arg == 'novga':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500483 self.novga = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600484 elif arg == 'serial':
Brad Bishop19323692019-04-05 15:28:33 -0400485 self.serialconsole = True
486 elif arg == "serialstdio":
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600487 self.serialstdio = True
488 elif arg == 'audio':
489 logger.info("Enabling audio in qemu")
490 logger.info("Please install sound drivers in linux host")
491 self.audio_enabled = True
492 elif arg == 'kvm':
493 self.kvm_enabled = True
494 elif arg == 'kvm-vhost':
495 self.vhost_enabled = True
496 elif arg == 'slirp':
497 self.slirp_enabled = True
Andrew Geissler82c905d2020-04-13 13:39:40 -0500498 elif arg.startswith('bridge='):
499 self.net_bridge = '%s' % arg[len('bridge='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600500 elif arg == 'snapshot':
501 self.snapshot = True
502 elif arg == 'publicvnc':
503 self.qemu_opt_script += ' -vnc :0'
504 elif arg.startswith('tcpserial='):
Brad Bishop15ae2502019-06-18 21:44:24 -0400505 self.tcpserial_portnum = '%s' % arg[len('tcpserial='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600506 elif arg.startswith('qemuparams='):
Andrew Geissler99467da2019-02-25 18:54:23 -0600507 self.qemuparams = ' %s' % arg[len('qemuparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600508 elif arg.startswith('bootparams='):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500509 self.bootparams = arg[len('bootparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600510 elif os.path.exists(arg) or (re.search(':', arg) and re.search('/', arg)):
511 self.check_arg_path(os.path.abspath(arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500512 elif re.search(r'-image-|-image$', arg):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600513 # Lazy rootfs
514 self.rootfs = arg
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500515 elif arg.startswith('ovmf'):
516 self.ovmf_bios.append(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600517 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500518 # At last, assume it is the MACHINE
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600519 if (not unknown_arg) or unknown_arg == arg:
520 unknown_arg = arg
521 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500522 raise RunQemuError("Can't handle two unknown args: %s %s\n"
523 "Try 'runqemu help' on how to use it" % \
524 (unknown_arg, arg))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600525 # Check to make sure it is a valid machine
Brad Bishopa5c52ff2018-11-23 10:55:50 +1300526 if unknown_arg and self.get('MACHINE') != unknown_arg:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500527 if self.get('DEPLOY_DIR_IMAGE'):
528 machine = os.path.basename(self.get('DEPLOY_DIR_IMAGE'))
529 if unknown_arg == machine:
530 self.set("MACHINE", machine)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500531
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600532 self.check_arg_machine(unknown_arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500533
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500534 if not (self.get('DEPLOY_DIR_IMAGE') or self.qbconfload):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500535 self.load_bitbake_env()
536 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
537 if s:
538 self.set("DEPLOY_DIR_IMAGE", s.group(1))
539
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600540 def check_kvm(self):
541 """Check kvm and kvm-host"""
542 if not (self.kvm_enabled or self.vhost_enabled):
543 self.qemu_opt_script += ' %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU'))
544 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500545
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600546 if not self.get('QB_CPU_KVM'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500547 raise RunQemuError("QB_CPU_KVM is NULL, this board doesn't support kvm")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500548
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600549 self.qemu_opt_script += ' %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU_KVM'))
550 yocto_kvm_wiki = "https://wiki.yoctoproject.org/wiki/How_to_enable_KVM_for_Poky_qemu"
551 yocto_paravirt_kvm_wiki = "https://wiki.yoctoproject.org/wiki/Running_an_x86_Yocto_Linux_image_under_QEMU_KVM"
552 dev_kvm = '/dev/kvm'
553 dev_vhost = '/dev/vhost-net'
Brad Bishop15ae2502019-06-18 21:44:24 -0400554 if self.qemu_system.endswith(('i386', 'x86_64')):
555 with open('/proc/cpuinfo', 'r') as f:
556 kvm_cap = re.search('vmx|svm', "".join(f.readlines()))
557 if not kvm_cap:
558 logger.error("You are trying to enable KVM on a cpu without VT support.")
559 logger.error("Remove kvm from the command-line, or refer:")
560 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500561
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600562 if not os.path.exists(dev_kvm):
563 logger.error("Missing KVM device. Have you inserted kvm modules?")
564 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500565 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500566
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600567 if os.access(dev_kvm, os.W_OK|os.R_OK):
568 self.qemu_opt_script += ' -enable-kvm'
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500569 if self.get('MACHINE') == "qemux86":
570 # Workaround for broken APIC window on pre 4.15 host kernels which causes boot hangs
571 # See YOCTO #12301
572 # On 64 bit we use x2apic
573 self.kernel_cmdline_script += " clocksource=kvm-clock hpet=disable noapic nolapic"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600574 else:
575 logger.error("You have no read or write permission on /dev/kvm.")
576 logger.error("Please change the ownership of this file as described at:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500577 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500578
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600579 if self.vhost_enabled:
580 if not os.path.exists(dev_vhost):
581 logger.error("Missing virtio net device. Have you inserted vhost-net module?")
582 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500583 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500584
Andrew Geissler635e0e42020-08-21 15:58:33 -0500585 if not os.access(dev_vhost, os.W_OK|os.R_OK):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600586 logger.error("You have no read or write permission on /dev/vhost-net.")
587 logger.error("Please change the ownership of this file as described at:")
Andrew Geissler635e0e42020-08-21 15:58:33 -0500588 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500589
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600590 def check_fstype(self):
591 """Check and setup FSTYPE"""
592 if not self.fstype:
593 fstype = self.get('QB_DEFAULT_FSTYPE')
594 if fstype:
595 self.fstype = fstype
596 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500597 raise RunQemuError("FSTYPE is NULL!")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500598
Brad Bishop15ae2502019-06-18 21:44:24 -0400599 # parse QB_FSINFO into dict, e.g. { 'wic': ['no-kernel-in-fs', 'a-flag'], 'ext4': ['another-flag']}
600 wic_fs = False
601 qb_fsinfo = self.get('QB_FSINFO')
602 if qb_fsinfo:
603 qb_fsinfo = qb_fsinfo.split()
604 for fsinfo in qb_fsinfo:
605 try:
606 fstype, fsflag = fsinfo.split(':')
607
608 if fstype == 'wic':
609 if fsflag == 'no-kernel-in-fs':
610 wic_fs = True
611 elif fsflag == 'kernel-in-fs':
612 wic_fs = False
613 else:
614 logger.warn('Unknown flag "%s:%s" in QB_FSINFO', fstype, fsflag)
615 continue
616 else:
617 logger.warn('QB_FSINFO is not supported for image type "%s"', fstype)
618 continue
619
620 if fstype in self.fsinfo:
621 self.fsinfo[fstype].append(fsflag)
622 else:
623 self.fsinfo[fstype] = [fsflag]
624 except Exception:
625 logger.error('Invalid parameter "%s" in QB_FSINFO', fsinfo)
626
627 # treat wic images as vmimages (with kernel) or as fsimages (rootfs only)
628 if wic_fs:
629 self.fstypes = self.fstypes + self.wictypes
630 else:
631 self.vmtypes = self.vmtypes + self.wictypes
632
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600633 def check_rootfs(self):
634 """Check and set rootfs"""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500635
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500636 if self.fstype == "none":
637 return
638
639 if self.get('ROOTFS'):
640 if not self.rootfs:
641 self.rootfs = self.get('ROOTFS')
642 elif self.get('ROOTFS') != self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500643 raise RunQemuError("Maybe conflicted ROOTFS: %s vs %s" % (self.get('ROOTFS'), self.rootfs))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500644
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600645 if self.fstype == 'nfs':
646 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500647
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600648 if self.rootfs and not os.path.exists(self.rootfs):
649 # Lazy rootfs
650 self.rootfs = "%s/%s-%s.%s" % (self.get('DEPLOY_DIR_IMAGE'),
651 self.rootfs, self.get('MACHINE'),
652 self.fstype)
653 elif not self.rootfs:
654 cmd_name = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_NAME'), self.fstype)
655 cmd_link = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'), self.fstype)
656 cmds = (cmd_name, cmd_link)
657 self.rootfs = get_first_file(cmds)
658 if not self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500659 raise RunQemuError("Failed to find rootfs: %s or %s" % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500660
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600661 if not os.path.exists(self.rootfs):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500662 raise RunQemuError("Can't find rootfs: %s" % self.rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500663
Brad Bishop08902b02019-08-20 09:16:51 -0400664 def setup_pkkek1(self):
665 """
666 Extract from PEM certificate the Platform Key and first Key
667 Exchange Key certificate string. The hypervisor needs to provide
668 it in the Type 11 SMBIOS table
669 """
670 pemcert = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), 'OvmfPkKek1.pem')
671 try:
672 with open(pemcert, 'r') as pemfile:
673 key = pemfile.read().replace('\n', ''). \
674 replace('-----BEGIN CERTIFICATE-----', ''). \
675 replace('-----END CERTIFICATE-----', '')
676 self.ovmf_secboot_pkkek1 = key
677
678 except FileNotFoundError:
679 raise RunQemuError("Can't open PEM certificate %s " % pemcert)
680
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500681 def check_ovmf(self):
682 """Check and set full path for OVMF firmware and variable file(s)."""
683
684 for index, ovmf in enumerate(self.ovmf_bios):
685 if os.path.exists(ovmf):
686 continue
687 for suffix in ('qcow2', 'bin'):
688 path = '%s/%s.%s' % (self.get('DEPLOY_DIR_IMAGE'), ovmf, suffix)
689 if os.path.exists(path):
690 self.ovmf_bios[index] = path
Brad Bishop08902b02019-08-20 09:16:51 -0400691 if ovmf.endswith('secboot'):
692 self.setup_pkkek1()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500693 break
694 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500695 raise RunQemuError("Can't find OVMF firmware: %s" % ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500696
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600697 def check_kernel(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400698 """Check and set kernel"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600699 # The vm image doesn't need a kernel
700 if self.fstype in self.vmtypes:
701 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500702
Brad Bishop316dfdd2018-06-25 12:45:53 -0400703 # See if the user supplied a KERNEL option
704 if self.get('KERNEL'):
705 self.kernel = self.get('KERNEL')
706
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500707 # QB_DEFAULT_KERNEL is always a full file path
708 kernel_name = os.path.basename(self.get('QB_DEFAULT_KERNEL'))
709
710 # The user didn't want a kernel to be loaded
Brad Bishop316dfdd2018-06-25 12:45:53 -0400711 if kernel_name == "none" and not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500712 return
713
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600714 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
715 if not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500716 kernel_match_name = "%s/%s" % (deploy_dir_image, kernel_name)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600717 kernel_match_link = "%s/%s" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
718 kernel_startswith = "%s/%s*" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
719 cmds = (kernel_match_name, kernel_match_link, kernel_startswith)
720 self.kernel = get_first_file(cmds)
721 if not self.kernel:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500722 raise RunQemuError('KERNEL not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500723
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600724 if not os.path.exists(self.kernel):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500725 raise RunQemuError("KERNEL %s not found" % self.kernel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500726
Brad Bishop316dfdd2018-06-25 12:45:53 -0400727 def check_dtb(self):
728 """Check and set dtb"""
729 # Did the user specify a device tree?
730 if self.get('DEVICE_TREE'):
731 self.dtb = self.get('DEVICE_TREE')
732 if not os.path.exists(self.dtb):
733 raise RunQemuError('Specified DTB not found: %s' % self.dtb)
734 return
735
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600736 dtb = self.get('QB_DTB')
737 if dtb:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400738 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600739 cmd_match = "%s/%s" % (deploy_dir_image, dtb)
740 cmd_startswith = "%s/%s*" % (deploy_dir_image, dtb)
741 cmd_wild = "%s/*.dtb" % deploy_dir_image
742 cmds = (cmd_match, cmd_startswith, cmd_wild)
743 self.dtb = get_first_file(cmds)
744 if not os.path.exists(self.dtb):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500745 raise RunQemuError('DTB not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500746
Brad Bishopc68388fc2019-08-26 01:33:31 -0400747 def check_bios(self):
748 """Check and set bios"""
749
750 # See if the user supplied a BIOS option
751 if self.get('BIOS'):
752 self.bios = self.get('BIOS')
753
754 # QB_DEFAULT_BIOS is always a full file path
755 bios_name = os.path.basename(self.get('QB_DEFAULT_BIOS'))
756
757 # The user didn't want a bios to be loaded
758 if (bios_name == "" or bios_name == "none") and not self.bios:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600759 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500760
Brad Bishopc68388fc2019-08-26 01:33:31 -0400761 if not self.bios:
762 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
763 self.bios = "%s/%s" % (deploy_dir_image, bios_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500764
Brad Bishopc68388fc2019-08-26 01:33:31 -0400765 if not self.bios:
766 raise RunQemuError('BIOS not found: %s' % bios_match_name)
767
768 if not os.path.exists(self.bios):
769 raise RunQemuError("KERNEL %s not found" % self.bios)
770
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500771
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600772 def check_mem(self):
Andrew Geissler99467da2019-02-25 18:54:23 -0600773 """
774 Both qemu and kernel needs memory settings, so check QB_MEM and set it
775 for both.
776 """
777 s = re.search('-m +([0-9]+)', self.qemuparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600778 if s:
779 self.set('QB_MEM', '-m %s' % s.group(1))
780 elif not self.get('QB_MEM'):
Brad Bishop79641f22019-09-10 07:20:22 -0400781 logger.info('QB_MEM is not set, use 256M by default')
782 self.set('QB_MEM', '-m 256')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500783
Andrew Geissler99467da2019-02-25 18:54:23 -0600784 # Check and remove M or m suffix
785 qb_mem = self.get('QB_MEM')
786 if qb_mem.endswith('M') or qb_mem.endswith('m'):
787 qb_mem = qb_mem[:-1]
788
789 # Add -m prefix it not present
790 if not qb_mem.startswith('-m'):
791 qb_mem = '-m %s' % qb_mem
792
793 self.set('QB_MEM', qb_mem)
794
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800795 mach = self.get('MACHINE')
796 if not mach.startswith('qemumips'):
797 self.kernel_cmdline_script += ' mem=%s' % self.get('QB_MEM').replace('-m','').strip() + 'M'
798
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600799 self.qemu_opt_script += ' %s' % self.get('QB_MEM')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500800
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600801 def check_tcpserial(self):
802 if self.tcpserial_portnum:
Brad Bishop15ae2502019-06-18 21:44:24 -0400803 ports = self.tcpserial_portnum.split(':')
804 port = ports[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600805 if self.get('QB_TCPSERIAL_OPT'):
Brad Bishop15ae2502019-06-18 21:44:24 -0400806 self.qemu_opt_script += ' ' + self.get('QB_TCPSERIAL_OPT').replace('@PORT@', port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600807 else:
Brad Bishop15ae2502019-06-18 21:44:24 -0400808 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
809
810 if len(ports) > 1:
811 for port in ports[1:]:
812 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500813
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600814 def check_and_set(self):
815 """Check configs sanity and set when needed"""
816 self.validate_paths()
Andrew Geissler82c905d2020-04-13 13:39:40 -0500817 if not self.slirp_enabled and not self.net_bridge:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500818 check_tun()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600819 # Check audio
820 if self.audio_enabled:
821 if not self.get('QB_AUDIO_DRV'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500822 raise RunQemuError("QB_AUDIO_DRV is NULL, this board doesn't support audio")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600823 if not self.get('QB_AUDIO_OPT'):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800824 logger.warning('QB_AUDIO_OPT is NULL, you may need define it to make audio work')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600825 else:
826 self.qemu_opt_script += ' %s' % self.get('QB_AUDIO_OPT')
827 os.putenv('QEMU_AUDIO_DRV', self.get('QB_AUDIO_DRV'))
828 else:
829 os.putenv('QEMU_AUDIO_DRV', 'none')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500830
Brad Bishop15ae2502019-06-18 21:44:24 -0400831 self.check_qemu_system()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600832 self.check_kvm()
833 self.check_fstype()
834 self.check_rootfs()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500835 self.check_ovmf()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600836 self.check_kernel()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400837 self.check_dtb()
Brad Bishopc68388fc2019-08-26 01:33:31 -0400838 self.check_bios()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600839 self.check_mem()
840 self.check_tcpserial()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500841
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600842 def read_qemuboot(self):
843 if not self.qemuboot:
844 if self.get('DEPLOY_DIR_IMAGE'):
845 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600846 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800847 logger.warning("Can't find qemuboot conf file, DEPLOY_DIR_IMAGE is NULL!")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600848 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500849
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600850 if self.rootfs and not os.path.exists(self.rootfs):
851 # Lazy rootfs
852 machine = self.get('MACHINE')
853 if not machine:
854 machine = os.path.basename(deploy_dir_image)
855 self.qemuboot = "%s/%s-%s.qemuboot.conf" % (deploy_dir_image,
856 self.rootfs, machine)
857 else:
858 cmd = 'ls -t %s/*.qemuboot.conf' % deploy_dir_image
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500859 logger.debug('Running %s...' % cmd)
860 try:
861 qbs = subprocess.check_output(cmd, shell=True).decode('utf-8')
862 except subprocess.CalledProcessError as err:
863 raise RunQemuError(err)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600864 if qbs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500865 for qb in qbs.split():
866 # Don't use initramfs when other choices unless fstype is ramfs
867 if '-initramfs-' in os.path.basename(qb) and self.fstype != 'cpio.gz':
868 continue
869 self.qemuboot = qb
870 break
871 if not self.qemuboot:
872 # Use the first one when no choice
873 self.qemuboot = qbs.split()[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600874 self.qbconfload = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500875
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600876 if not self.qemuboot:
877 # If we haven't found a .qemuboot.conf at this point it probably
878 # doesn't exist, continue without
879 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500880
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600881 if not os.path.exists(self.qemuboot):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500882 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 -0500883
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500884 logger.debug('CONFFILE: %s' % self.qemuboot)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500885
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600886 cf = configparser.ConfigParser()
887 cf.read(self.qemuboot)
888 for k, v in cf.items('config_bsp'):
889 k_upper = k.upper()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500890 if v.startswith("../"):
891 v = os.path.abspath(os.path.dirname(self.qemuboot) + "/" + v)
892 elif v == ".":
893 v = os.path.dirname(self.qemuboot)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600894 self.set(k_upper, v)
895
896 def validate_paths(self):
897 """Ensure all relevant path variables are set"""
898 # When we're started with a *.qemuboot.conf arg assume that image
899 # artefacts are relative to that file, rather than in whatever
900 # directory DEPLOY_DIR_IMAGE in the conf file points to.
901 if self.qbconfload:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500902 imgdir = os.path.realpath(os.path.dirname(self.qemuboot))
903 if imgdir != os.path.realpath(self.get('DEPLOY_DIR_IMAGE')):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600904 logger.info('Setting DEPLOY_DIR_IMAGE to folder containing %s (%s)' % (self.qemuboot, imgdir))
905 self.set('DEPLOY_DIR_IMAGE', imgdir)
906
907 # If the STAGING_*_NATIVE directories from the config file don't exist
908 # and we're in a sourced OE build directory try to extract the paths
909 # from `bitbake -e`
910 havenative = os.path.exists(self.get('STAGING_DIR_NATIVE')) and \
911 os.path.exists(self.get('STAGING_BINDIR_NATIVE'))
912
913 if not havenative:
914 if not self.bitbake_e:
915 self.load_bitbake_env()
916
917 if self.bitbake_e:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500918 native_vars = ['STAGING_DIR_NATIVE']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600919 for nv in native_vars:
920 s = re.search('^%s="(.*)"' % nv, self.bitbake_e, re.M)
921 if s and s.group(1) != self.get(nv):
922 logger.info('Overriding conf file setting of %s to %s from Bitbake environment' % (nv, s.group(1)))
923 self.set(nv, s.group(1))
924 else:
925 # when we're invoked from a running bitbake instance we won't
926 # be able to call `bitbake -e`, then try:
927 # - get OE_TMPDIR from environment and guess paths based on it
928 # - get OECORE_NATIVE_SYSROOT from environment (for sdk)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500929 tmpdir = self.get('OE_TMPDIR')
930 oecore_native_sysroot = self.get('OECORE_NATIVE_SYSROOT')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600931 if tmpdir:
932 logger.info('Setting STAGING_DIR_NATIVE and STAGING_BINDIR_NATIVE relative to OE_TMPDIR (%s)' % tmpdir)
933 hostos, _, _, _, machine = os.uname()
934 buildsys = '%s-%s' % (machine, hostos.lower())
935 staging_dir_native = '%s/sysroots/%s' % (tmpdir, buildsys)
936 self.set('STAGING_DIR_NATIVE', staging_dir_native)
937 elif oecore_native_sysroot:
938 logger.info('Setting STAGING_DIR_NATIVE to OECORE_NATIVE_SYSROOT (%s)' % oecore_native_sysroot)
939 self.set('STAGING_DIR_NATIVE', oecore_native_sysroot)
940 if self.get('STAGING_DIR_NATIVE'):
941 # we have to assume that STAGING_BINDIR_NATIVE is at usr/bin
942 staging_bindir_native = '%s/usr/bin' % self.get('STAGING_DIR_NATIVE')
943 logger.info('Setting STAGING_BINDIR_NATIVE to %s' % staging_bindir_native)
944 self.set('STAGING_BINDIR_NATIVE', '%s/usr/bin' % self.get('STAGING_DIR_NATIVE'))
945
946 def print_config(self):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500947 logoutput = ['Continuing with the following parameters:']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600948 if not self.fstype in self.vmtypes:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500949 logoutput.append('KERNEL: [%s]' % self.kernel)
Brad Bishopc68388fc2019-08-26 01:33:31 -0400950 if self.bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500951 logoutput.append('BIOS: [%s]' % self.bios)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600952 if self.dtb:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500953 logoutput.append('DTB: [%s]' % self.dtb)
954 logoutput.append('MACHINE: [%s]' % self.get('MACHINE'))
Brad Bishop15ae2502019-06-18 21:44:24 -0400955 try:
956 fstype_flags = ' (' + ', '.join(self.fsinfo[self.fstype]) + ')'
957 except KeyError:
958 fstype_flags = ''
Andrew Geissler82c905d2020-04-13 13:39:40 -0500959 logoutput.append('FSTYPE: [%s%s]' % (self.fstype, fstype_flags))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600960 if self.fstype == 'nfs':
Andrew Geissler82c905d2020-04-13 13:39:40 -0500961 logoutput.append('NFS_DIR: [%s]' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600962 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500963 logoutput.append('ROOTFS: [%s]' % self.rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500964 if self.ovmf_bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500965 logoutput.append('OVMF: %s' % self.ovmf_bios)
Brad Bishop08902b02019-08-20 09:16:51 -0400966 if (self.ovmf_secboot_pkkek1):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500967 logoutput.append('SECBOOT PKKEK1: [%s...]' % self.ovmf_secboot_pkkek1[0:100])
968 logoutput.append('CONFFILE: [%s]' % self.qemuboot)
969 logoutput.append('')
970 logger.info('\n'.join(logoutput))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600971
972 def setup_nfs(self):
973 if not self.nfs_server:
974 if self.slirp_enabled:
975 self.nfs_server = '10.0.2.2'
976 else:
977 self.nfs_server = '192.168.7.1'
978
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500979 # Figure out a new nfs_instance to allow multiple qemus running.
Brad Bishop977dc1a2019-02-06 16:01:43 -0500980 ps = subprocess.check_output(("ps", "auxww")).decode('utf-8')
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500981 pattern = '/bin/unfsd .* -i .*\.pid -e .*/exports([0-9]+) '
982 all_instances = re.findall(pattern, ps, re.M)
983 if all_instances:
984 all_instances.sort(key=int)
985 self.nfs_instance = int(all_instances.pop()) + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600986
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500987 nfsd_port = 3049 + 2 * self.nfs_instance
988 mountd_port = 3048 + 2 * self.nfs_instance
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600989
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500990 # Export vars for runqemu-export-rootfs
991 export_dict = {
992 'NFS_INSTANCE': self.nfs_instance,
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500993 'NFSD_PORT': nfsd_port,
994 'MOUNTD_PORT': mountd_port,
995 }
996 for k, v in export_dict.items():
997 # Use '%s' since they are integers
998 os.putenv(k, '%s' % v)
999
Andrew Geissler82c905d2020-04-13 13:39:40 -05001000 self.unfs_opts="nfsvers=3,port=%s,tcp,mountport=%s" % (nfsd_port, mountd_port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001001
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001002 # Extract .tar.bz2 or .tar.bz if no nfs dir
1003 if not (self.rootfs and os.path.isdir(self.rootfs)):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001004 src_prefix = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'))
1005 dest = "%s-nfsroot" % src_prefix
1006 if os.path.exists('%s.pseudo_state' % dest):
1007 logger.info('Use %s as NFS_DIR' % dest)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001008 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001009 else:
1010 src = ""
1011 src1 = '%s.tar.bz2' % src_prefix
1012 src2 = '%s.tar.gz' % src_prefix
1013 if os.path.exists(src1):
1014 src = src1
1015 elif os.path.exists(src2):
1016 src = src2
1017 if not src:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001018 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 -06001019 logger.info('NFS_DIR not found, extracting %s to %s' % (src, dest))
Brad Bishop977dc1a2019-02-06 16:01:43 -05001020 cmd = ('runqemu-extract-sdk', src, dest)
1021 logger.info('Running %s...' % str(cmd))
1022 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001023 raise RunQemuError('Failed to run %s' % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001024 self.rootfs = dest
Andrew Geisslerc926e172021-05-07 16:11:35 -05001025 self.cleanup_files.append(self.rootfs)
1026 self.cleanup_files.append('%s.pseudo_state' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001027
1028 # Start the userspace NFS server
Brad Bishop977dc1a2019-02-06 16:01:43 -05001029 cmd = ('runqemu-export-rootfs', 'start', self.rootfs)
1030 logger.info('Running %s...' % str(cmd))
1031 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001032 raise RunQemuError('Failed to run %s' % cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001033
1034 self.nfs_running = True
1035
Andrew Geissler82c905d2020-04-13 13:39:40 -05001036 def setup_net_bridge(self):
1037 self.set('NETWORK_CMD', '-netdev bridge,br=%s,id=net0,helper=%s -device virtio-net-pci,netdev=net0 ' % (
1038 self.net_bridge, os.path.join(self.bindir_native, 'qemu-oe-bridge-helper')))
1039
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001040 def setup_slirp(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001041 """Setup user networking"""
1042
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001043 if self.fstype == 'nfs':
1044 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001045 netconf = " " + self.cmdline_ip_slirp
1046 logger.info("Network configuration:%s", netconf)
1047 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001048 # Port mapping
1049 hostfwd = ",hostfwd=tcp::2222-:22,hostfwd=tcp::2323-:23"
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001050 qb_slirp_opt_default = "-netdev user,id=net0%s,tftp=%s" % (hostfwd, self.get('DEPLOY_DIR_IMAGE'))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001051 qb_slirp_opt = self.get('QB_SLIRP_OPT') or qb_slirp_opt_default
1052 # Figure out the port
1053 ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt)
1054 ports = [int(i) for i in ports]
1055 mac = 2
Brad Bishop08902b02019-08-20 09:16:51 -04001056
1057 lockdir = "/tmp/qemu-port-locks"
1058 if not os.path.exists(lockdir):
1059 # There might be a race issue when multi runqemu processess are
1060 # running at the same time.
1061 try:
1062 os.mkdir(lockdir)
1063 os.chmod(lockdir, 0o777)
1064 except FileExistsError:
1065 pass
1066
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001067 # Find a free port to avoid conflicts
1068 for p in ports[:]:
1069 p_new = p
Brad Bishop08902b02019-08-20 09:16:51 -04001070 while not self.check_free_port('localhost', p_new, lockdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001071 p_new += 1
1072 mac += 1
1073 while p_new in ports:
1074 p_new += 1
1075 mac += 1
1076 if p != p_new:
1077 ports.append(p_new)
1078 qb_slirp_opt = re.sub(':%s-' % p, ':%s-' % p_new, qb_slirp_opt)
1079 logger.info("Port forward changed: %s -> %s" % (p, p_new))
1080 mac = "%s%02x" % (self.mac_slirp, mac)
1081 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qb_slirp_opt))
1082 # Print out port foward
1083 hostfwd = re.findall('(hostfwd=[^,]*)', qb_slirp_opt)
1084 if hostfwd:
1085 logger.info('Port forward: %s' % ' '.join(hostfwd))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001086
1087 def setup_tap(self):
1088 """Setup tap"""
1089
1090 # This file is created when runqemu-gen-tapdevs creates a bank of tap
1091 # devices, indicating that the user should not bring up new ones using
1092 # sudo.
1093 nosudo_flag = '/etc/runqemu-nosudo'
1094 self.qemuifup = shutil.which('runqemu-ifup')
1095 self.qemuifdown = shutil.which('runqemu-ifdown')
1096 ip = shutil.which('ip')
1097 lockdir = "/tmp/qemu-tap-locks"
1098
1099 if not (self.qemuifup and self.qemuifdown and ip):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001100 logger.error("runqemu-ifup: %s" % self.qemuifup)
1101 logger.error("runqemu-ifdown: %s" % self.qemuifdown)
1102 logger.error("ip: %s" % ip)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001103 raise OEPathError("runqemu-ifup, runqemu-ifdown or ip not found")
1104
1105 if not os.path.exists(lockdir):
1106 # There might be a race issue when multi runqemu processess are
1107 # running at the same time.
1108 try:
1109 os.mkdir(lockdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001110 os.chmod(lockdir, 0o777)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001111 except FileExistsError:
1112 pass
1113
Brad Bishop977dc1a2019-02-06 16:01:43 -05001114 cmd = (ip, 'link')
1115 logger.debug('Running %s...' % str(cmd))
1116 ip_link = subprocess.check_output(cmd).decode('utf-8')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001117 # Matches line like: 6: tap0: <foo>
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001118 possibles = re.findall('^[0-9]+: +(tap[0-9]+): <.*', ip_link, re.M)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001119 tap = ""
1120 for p in possibles:
1121 lockfile = os.path.join(lockdir, p)
1122 if os.path.exists('%s.skip' % lockfile):
1123 logger.info('Found %s.skip, skipping %s' % (lockfile, p))
1124 continue
Brad Bishop08902b02019-08-20 09:16:51 -04001125 self.taplock = lockfile + '.lock'
1126 if self.acquire_taplock(error=False):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001127 tap = p
1128 logger.info("Using preconfigured tap device %s" % tap)
1129 logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap))
1130 break
1131
1132 if not tap:
1133 if os.path.exists(nosudo_flag):
1134 logger.error("Error: There are no available tap devices to use for networking,")
1135 logger.error("and I see %s exists, so I am not going to try creating" % nosudo_flag)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001136 raise RunQemuError("a new one with sudo.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001137
1138 gid = os.getgid()
1139 uid = os.getuid()
1140 logger.info("Setting up tap interface under sudo")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001141 cmd = ('sudo', self.qemuifup, str(uid), str(gid), self.bindir_native)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001142 try:
1143 tap = subprocess.check_output(cmd).decode('utf-8').strip()
1144 except subprocess.CalledProcessError as e:
1145 logger.error('Setting up tap device failed:\n%s\nRun runqemu-gen-tapdevs to manually create one.' % str(e))
1146 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001147 lockfile = os.path.join(lockdir, tap)
Brad Bishop08902b02019-08-20 09:16:51 -04001148 self.taplock = lockfile + '.lock'
1149 self.acquire_taplock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001150 self.cleantap = True
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001151 logger.debug('Created tap: %s' % tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001152
1153 if not tap:
1154 logger.error("Failed to setup tap device. Run runqemu-gen-tapdevs to manually create.")
Andrew Geissler82c905d2020-04-13 13:39:40 -05001155 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001156 self.tap = tap
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001157 tapnum = int(tap[3:])
1158 gateway = tapnum * 2 + 1
1159 client = gateway + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001160 if self.fstype == 'nfs':
1161 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001162 netconf = " " + self.cmdline_ip_tap
1163 netconf = netconf.replace('@CLIENT@', str(client))
1164 netconf = netconf.replace('@GATEWAY@', str(gateway))
1165 logger.info("Network configuration:%s", netconf)
1166 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001167 mac = "%s%02x" % (self.mac_tap, client)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001168 qb_tap_opt = self.get('QB_TAP_OPT')
1169 if qb_tap_opt:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001170 qemu_tap_opt = qb_tap_opt.replace('@TAP@', tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001171 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001172 qemu_tap_opt = "-netdev tap,id=net0,ifname=%s,script=no,downscript=no" % (self.tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001173
1174 if self.vhost_enabled:
1175 qemu_tap_opt += ',vhost=on'
1176
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001177 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qemu_tap_opt))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001178
1179 def setup_network(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001180 if self.get('QB_NET') == 'none':
1181 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001182 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001183 self.saved_stty = subprocess.check_output(("stty", "-g")).decode('utf-8').strip()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001184 self.network_device = self.get('QB_NETWORK_DEVICE') or self.network_device
Andrew Geissler82c905d2020-04-13 13:39:40 -05001185 if self.net_bridge:
1186 self.setup_net_bridge()
1187 elif self.slirp_enabled:
1188 self.cmdline_ip_slirp = self.get('QB_CMDLINE_IP_SLIRP') or self.cmdline_ip_slirp
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001189 self.setup_slirp()
1190 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001191 self.cmdline_ip_tap = self.get('QB_CMDLINE_IP_TAP') or self.cmdline_ip_tap
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001192 self.setup_tap()
1193
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001194 def setup_rootfs(self):
1195 if self.get('QB_ROOTFS') == 'none':
1196 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001197 if 'wic.' in self.fstype:
1198 self.fstype = self.fstype[4:]
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001199 rootfs_format = self.fstype if self.fstype in ('vmdk', 'vhd', 'vhdx', 'qcow2', 'vdi') else 'raw'
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001200
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001201 tmpfsdir = os.environ.get("RUNQEMU_TMPFS_DIR", None)
1202 if self.snapshot and tmpfsdir:
1203 newrootfs = os.path.join(tmpfsdir, os.path.basename(self.rootfs)) + "." + str(os.getpid())
1204 shutil.copyfile(self.rootfs, newrootfs)
1205 #print("Copying rootfs to tmpfs: %s" % newrootfs)
1206 self.rootfs = newrootfs
1207 # Don't need a second copy now!
1208 self.snapshot = False
Andrew Geisslerc926e172021-05-07 16:11:35 -05001209 self.cleanup_files.append(newrootfs)
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001210
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001211 qb_rootfs_opt = self.get('QB_ROOTFS_OPT')
1212 if qb_rootfs_opt:
1213 self.rootfs_options = qb_rootfs_opt.replace('@ROOTFS@', self.rootfs)
1214 else:
1215 self.rootfs_options = '-drive file=%s,if=virtio,format=%s' % (self.rootfs, rootfs_format)
1216
Andrew Geissler82c905d2020-04-13 13:39:40 -05001217 qb_rootfs_extra_opt = self.get("QB_ROOTFS_EXTRA_OPT")
1218 if qb_rootfs_extra_opt and not qb_rootfs_extra_opt.startswith(","):
1219 qb_rootfs_extra_opt = "," + qb_rootfs_extra_opt
1220
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001221 if self.fstype in ('cpio.gz', 'cpio'):
1222 self.kernel_cmdline = 'root=/dev/ram0 rw debugshell'
1223 self.rootfs_options = '-initrd %s' % self.rootfs
1224 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001225 vm_drive = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001226 if self.fstype in self.vmtypes:
1227 if self.fstype == 'iso':
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001228 vm_drive = '-drive file=%s,if=virtio,media=cdrom' % self.rootfs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001229 elif self.get('QB_DRIVE_TYPE'):
1230 drive_type = self.get('QB_DRIVE_TYPE')
1231 if drive_type.startswith("/dev/sd"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001232 logger.info('Using scsi drive')
Andrew Geissler82c905d2020-04-13 13:39:40 -05001233 vm_drive = '-drive if=none,id=hd,file=%s,format=%s -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd%s' \
1234 % (self.rootfs, rootfs_format, qb_rootfs_extra_opt)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001235 elif drive_type.startswith("/dev/hd"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001236 logger.info('Using ide drive')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001237 vm_drive = "-drive file=%s,format=%s" % (self.rootfs, rootfs_format)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001238 elif drive_type.startswith("/dev/vdb"):
1239 logger.info('Using block virtio drive');
1240 vm_drive = '-drive id=disk0,file=%s,if=none,format=%s -device virtio-blk-device,drive=disk0%s' \
1241 % (self.rootfs, rootfs_format,qb_rootfs_extra_opt)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001242 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001243 # virtio might have been selected explicitly (just use it), or
1244 # is used as fallback (then warn about that).
1245 if not drive_type.startswith("/dev/vd"):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001246 logger.warning("Unknown QB_DRIVE_TYPE: %s" % drive_type)
1247 logger.warning("Failed to figure out drive type, consider define or fix QB_DRIVE_TYPE")
1248 logger.warning('Trying to use virtio block drive')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001249 vm_drive = '-drive if=virtio,file=%s,format=%s' % (self.rootfs, rootfs_format)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001250
1251 # All branches above set vm_drive.
Andrew Geissler475cb722020-07-10 16:00:51 -05001252 self.rootfs_options = vm_drive
1253 if not self.fstype in self.vmtypes:
1254 self.rootfs_options += ' -no-reboot'
Brad Bishopf3f93bb2019-10-16 14:33:32 -04001255 self.kernel_cmdline = 'root=%s rw' % (self.get('QB_KERNEL_ROOT'))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001256
1257 if self.fstype == 'nfs':
1258 self.rootfs_options = ''
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001259 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 -04001260 self.kernel_cmdline = 'root=%s rw' % k_root
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001261
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001262 if self.fstype == 'none':
1263 self.rootfs_options = ''
1264
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001265 self.set('ROOTFS_OPTIONS', self.rootfs_options)
1266
1267 def guess_qb_system(self):
1268 """attempt to determine the appropriate qemu-system binary"""
1269 mach = self.get('MACHINE')
1270 if not mach:
1271 search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*'
1272 if self.rootfs:
1273 match = re.match(search, self.rootfs)
1274 if match:
1275 mach = match.group(1)
1276 elif self.kernel:
1277 match = re.match(search, self.kernel)
1278 if match:
1279 mach = match.group(1)
1280
1281 if not mach:
1282 return None
1283
1284 if mach == 'qemuarm':
1285 qbsys = 'arm'
1286 elif mach == 'qemuarm64':
1287 qbsys = 'aarch64'
1288 elif mach == 'qemux86':
1289 qbsys = 'i386'
1290 elif mach == 'qemux86-64':
1291 qbsys = 'x86_64'
1292 elif mach == 'qemuppc':
1293 qbsys = 'ppc'
1294 elif mach == 'qemumips':
1295 qbsys = 'mips'
1296 elif mach == 'qemumips64':
1297 qbsys = 'mips64'
1298 elif mach == 'qemumipsel':
1299 qbsys = 'mipsel'
1300 elif mach == 'qemumips64el':
1301 qbsys = 'mips64el'
Brad Bishop316dfdd2018-06-25 12:45:53 -04001302 elif mach == 'qemuriscv64':
1303 qbsys = 'riscv64'
1304 elif mach == 'qemuriscv32':
1305 qbsys = 'riscv32'
Brad Bishop004d4992018-10-02 23:54:45 +02001306 else:
1307 logger.error("Unable to determine QEMU PC System emulator for %s machine." % mach)
1308 logger.error("As %s is not among valid QEMU machines such as," % mach)
1309 logger.error("qemux86-64, qemux86, qemuarm64, qemuarm, qemumips64, qemumips64el, qemumipsel, qemumips, qemuppc")
1310 raise RunQemuError("Set qb_system_name with suitable QEMU PC System emulator in .*qemuboot.conf.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001311
1312 return 'qemu-system-%s' % qbsys
1313
Brad Bishop15ae2502019-06-18 21:44:24 -04001314 def check_qemu_system(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001315 qemu_system = self.get('QB_SYSTEM_NAME')
1316 if not qemu_system:
1317 qemu_system = self.guess_qb_system()
1318 if not qemu_system:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001319 raise RunQemuError("Failed to boot, QB_SYSTEM_NAME is NULL!")
Brad Bishop15ae2502019-06-18 21:44:24 -04001320 self.qemu_system = qemu_system
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001321
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001322 def setup_vga(self):
1323 if self.nographic == True:
1324 if self.sdl == True:
1325 raise RunQemuError('Option nographic makes no sense alongside the sdl option.')
1326 if self.gtk == True:
1327 raise RunQemuError('Option nographic makes no sense alongside the gtk option.')
1328 self.qemu_opt += ' -nographic'
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001329
1330 if self.novga == True:
1331 self.qemu_opt += ' -vga none'
1332 return
1333
1334 if (self.gl_es == True or self.gl == True) and (self.sdl == False and self.gtk == False):
1335 raise RunQemuError('Option gl/gl-es needs gtk or sdl option.')
1336
1337 if self.sdl == True or self.gtk == True or self.egl_headless == True:
1338 self.set_dri_path()
1339 self.qemu_opt += ' -vga virtio -display '
1340 if self.egl_headless == True:
1341 self.qemu_opt += 'egl-headless,'
1342 else:
1343 if self.sdl == True:
1344 self.qemu_opt += 'sdl,'
1345 elif self.gtk == True:
1346 self.qemu_opt += 'gtk,'
1347
1348 if self.gl == True:
1349 self.qemu_opt += 'gl=on,'
1350 elif self.gl_es == True:
1351 self.qemu_opt += 'gl=es,'
1352 self.qemu_opt += 'show-cursor=on'
1353
1354 self.qemu_opt += ' %s' %self.get('QB_GRAPHICS')
1355
1356 def setup_serial(self):
1357 # Setup correct kernel command line for serial
1358 if self.serialstdio == True or self.serialconsole == True or self.nographic == True or self.tcpserial_portnum:
1359 for entry in self.get('SERIAL_CONSOLES').split(' '):
1360 self.kernel_cmdline_script += ' console=%s' %entry.split(';')[1]
1361
1362 if self.serialstdio == True or self.nographic == True:
1363 self.qemu_opt += " -serial mon:stdio"
1364 else:
1365 self.qemu_opt += " -serial mon:vc"
1366 if self.serialconsole:
1367 if sys.stdin.isatty():
1368 subprocess.check_call(("stty", "intr", "^]"))
1369 logger.info("Interrupt character is '^]'")
1370
1371 self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT")
1372
1373 # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES).
1374 # If no serial or serialtcp options were specified, only ttyS0 is created
1375 # and sysvinit shows an error trying to enable ttyS1:
1376 # INIT: Id "S1" respawning too fast: disabled for 5 minutes
1377 serial_num = len(re.findall("-serial", self.qemu_opt))
1378 if serial_num < 2:
1379 self.qemu_opt += " -serial null"
1380
Brad Bishop15ae2502019-06-18 21:44:24 -04001381 def setup_final(self):
1382 qemu_bin = os.path.join(self.bindir_native, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001383
1384 # It is possible to have qemu-native in ASSUME_PROVIDED, and it won't
1385 # find QEMU in sysroot, it needs to use host's qemu.
1386 if not os.path.exists(qemu_bin):
1387 logger.info("QEMU binary not found in %s, trying host's QEMU" % qemu_bin)
1388 for path in (os.environ['PATH'] or '').split(':'):
Brad Bishop15ae2502019-06-18 21:44:24 -04001389 qemu_bin_tmp = os.path.join(path, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001390 logger.info("Trying: %s" % qemu_bin_tmp)
1391 if os.path.exists(qemu_bin_tmp):
1392 qemu_bin = qemu_bin_tmp
1393 if not os.path.isabs(qemu_bin):
1394 qemu_bin = os.path.abspath(qemu_bin)
1395 logger.info("Using host's QEMU: %s" % qemu_bin)
1396 break
1397
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001398 if not os.access(qemu_bin, os.X_OK):
1399 raise OEPathError("No QEMU binary '%s' could be found" % qemu_bin)
1400
Andrew Geisslerc3d88e42020-10-02 09:45:00 -05001401 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 -05001402
1403 for ovmf in self.ovmf_bios:
1404 format = ovmf.rsplit('.', 1)[-1]
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001405 if format == "bin":
1406 format = "raw"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001407 self.qemu_opt += ' -drive if=pflash,format=%s,file=%s' % (format, ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001408
1409 self.qemu_opt += ' ' + self.qemu_opt_script
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001410
Brad Bishop08902b02019-08-20 09:16:51 -04001411 if self.ovmf_secboot_pkkek1:
1412 # Provide the Platform Key and first Key Exchange Key certificate as an
1413 # OEM string in the SMBIOS Type 11 table. Prepend the certificate string
1414 # with "application prefix" of the EnrollDefaultKeys.efi application
1415 self.qemu_opt += ' -smbios type=11,value=4e32566d-8e9e-4f52-81d3-5bb9715f9727:' \
1416 + self.ovmf_secboot_pkkek1
1417
Andrew Geissler99467da2019-02-25 18:54:23 -06001418 # Append qemuparams to override previous settings
1419 if self.qemuparams:
1420 self.qemu_opt += ' ' + self.qemuparams
1421
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001422 if self.snapshot:
1423 self.qemu_opt += " -snapshot"
1424
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001425 self.setup_serial()
1426 self.setup_vga()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001427
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001428 def start_qemu(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001429 import shlex
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001430 if self.kernel:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001431 kernel_opts = "-kernel %s -append '%s %s %s %s'" % (self.kernel, self.kernel_cmdline,
1432 self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'),
1433 self.bootparams)
Brad Bishopc68388fc2019-08-26 01:33:31 -04001434 if self.bios:
1435 kernel_opts += " -bios %s" % self.bios
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001436 if self.dtb:
1437 kernel_opts += " -dtb %s" % self.dtb
1438 else:
1439 kernel_opts = ""
1440 cmd = "%s %s" % (self.qemu_opt, kernel_opts)
Brad Bishop004d4992018-10-02 23:54:45 +02001441 cmds = shlex.split(cmd)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001442 logger.info('Running %s\n' % cmd)
Brad Bishopf86d0552018-12-04 14:18:15 -08001443 pass_fds = []
Brad Bishop08902b02019-08-20 09:16:51 -04001444 if self.taplock_descriptor:
1445 pass_fds = [self.taplock_descriptor.fileno()]
1446 if len(self.portlocks):
1447 for descriptor in self.portlocks.values():
1448 pass_fds.append(descriptor.fileno())
Brad Bishopf86d0552018-12-04 14:18:15 -08001449 process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds)
Brad Bishop004d4992018-10-02 23:54:45 +02001450 self.qemupid = process.pid
1451 retcode = process.wait()
1452 if retcode:
1453 if retcode == -signal.SIGTERM:
1454 logger.info("Qemu terminated by SIGTERM")
1455 else:
1456 logger.error("Failed to run qemu: %s", process.stderr.read().decode())
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001457
1458 def cleanup(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001459 if self.cleaned:
1460 return
1461
1462 # avoid dealing with SIGTERM when cleanup function is running
1463 signal.signal(signal.SIGTERM, signal.SIG_IGN)
1464
1465 logger.info("Cleaning up")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001466 if self.cleantap:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001467 cmd = ('sudo', self.qemuifdown, self.tap, self.bindir_native)
1468 logger.debug('Running %s' % str(cmd))
1469 subprocess.check_call(cmd)
Brad Bishop08902b02019-08-20 09:16:51 -04001470 self.release_taplock()
1471 self.release_portlock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001472
1473 if self.nfs_running:
1474 logger.info("Shutting down the userspace NFS server...")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001475 cmd = ("runqemu-export-rootfs", "stop", self.rootfs)
1476 logger.debug('Running %s' % str(cmd))
1477 subprocess.check_call(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001478
1479 if self.saved_stty:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001480 subprocess.check_call(("stty", self.saved_stty))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001481
Andrew Geisslerc926e172021-05-07 16:11:35 -05001482 if self.cleanup_files:
1483 for ent in self.cleanup_files:
1484 logger.info('Removing %s' % ent)
1485 if os.path.isfile(ent):
1486 os.remove(ent)
1487 else:
1488 shutil.rmtree(ent)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001489
Brad Bishop004d4992018-10-02 23:54:45 +02001490 self.cleaned = True
1491
Andrew Geissler82c905d2020-04-13 13:39:40 -05001492 def run_bitbake_env(self, mach=None):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001493 bitbake = shutil.which('bitbake')
1494 if not bitbake:
1495 return
1496
1497 if not mach:
1498 mach = self.get('MACHINE')
1499
Andrew Geissler82c905d2020-04-13 13:39:40 -05001500 multiconfig = self.get('MULTICONFIG')
1501 if multiconfig:
1502 multiconfig = "mc:%s" % multiconfig
1503
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001504 if mach:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001505 cmd = 'MACHINE=%s bitbake -e %s' % (mach, multiconfig)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001506 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001507 cmd = 'bitbake -e %s' % multiconfig
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001508
1509 logger.info('Running %s...' % cmd)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001510 return subprocess.check_output(cmd, shell=True).decode('utf-8')
1511
1512 def load_bitbake_env(self, mach=None):
1513 if self.bitbake_e:
1514 return
1515
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001516 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001517 self.bitbake_e = self.run_bitbake_env(mach=mach)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001518 except subprocess.CalledProcessError as err:
1519 self.bitbake_e = ''
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001520 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 -06001521
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001522 def validate_combos(self):
1523 if (self.fstype in self.vmtypes) and self.kernel:
1524 raise RunQemuError("%s doesn't need kernel %s!" % (self.fstype, self.kernel))
1525
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001526 @property
1527 def bindir_native(self):
1528 result = self.get('STAGING_BINDIR_NATIVE')
1529 if result and os.path.exists(result):
1530 return result
1531
Andrew Geissler82c905d2020-04-13 13:39:40 -05001532 cmd = ['bitbake', '-e']
1533 multiconfig = self.get('MULTICONFIG')
1534 if multiconfig:
1535 cmd.append('mc:%s:qemu-helper-native' % multiconfig)
1536 else:
1537 cmd.append('qemu-helper-native')
1538
Brad Bishop977dc1a2019-02-06 16:01:43 -05001539 logger.info('Running %s...' % str(cmd))
1540 out = subprocess.check_output(cmd).decode('utf-8')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001541
1542 match = re.search('^STAGING_BINDIR_NATIVE="(.*)"', out, re.M)
1543 if match:
1544 result = match.group(1)
1545 if os.path.exists(result):
1546 self.set('STAGING_BINDIR_NATIVE', result)
1547 return result
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001548 raise RunQemuError("Native sysroot directory %s doesn't exist" % result)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001549 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001550 raise RunQemuError("Can't find STAGING_BINDIR_NATIVE in '%s' output" % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001551
1552
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001553def main():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001554 if "help" in sys.argv or '-h' in sys.argv or '--help' in sys.argv:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001555 print_usage()
1556 return 0
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001557 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001558 config = BaseConfig()
Brad Bishop004d4992018-10-02 23:54:45 +02001559
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001560 renice = os.path.expanduser("~/bin/runqemu-renice")
1561 if os.path.exists(renice):
1562 logger.info('Using %s to renice' % renice)
1563 subprocess.check_call([renice, str(os.getpid())])
1564
Brad Bishop004d4992018-10-02 23:54:45 +02001565 def sigterm_handler(signum, frame):
1566 logger.info("SIGTERM received")
1567 os.kill(config.qemupid, signal.SIGTERM)
1568 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001569 # Deliberately ignore the return code of 'tput smam'.
1570 subprocess.call(["tput", "smam"])
Brad Bishop004d4992018-10-02 23:54:45 +02001571 signal.signal(signal.SIGTERM, sigterm_handler)
1572
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001573 config.check_args()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001574 config.read_qemuboot()
1575 config.check_and_set()
1576 # Check whether the combos is valid or not
1577 config.validate_combos()
1578 config.print_config()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001579 config.setup_network()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001580 config.setup_rootfs()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001581 config.setup_final()
1582 config.start_qemu()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001583 except RunQemuError as err:
1584 logger.error(err)
1585 return 1
1586 except Exception as err:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001587 import traceback
1588 traceback.print_exc()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001589 return 1
1590 finally:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001591 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001592 # Deliberately ignore the return code of 'tput smam'.
1593 subprocess.call(["tput", "smam"])
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001594
1595if __name__ == "__main__":
1596 sys.exit(main())