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