blob: dd92a645531b26dea5772945e742675ecf4e3c62 [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
Brad Bishopa34c0302019-09-23 22:34:48 -040068 sdl - choose the SDL UI frontend
69 gtk - choose the Gtk UI frontend
Brad Bishop6dbb3162019-11-25 09:41:34 -050070 gl - enable virgl-based GL acceleration (also needs gtk or sdl options)
71 gl-es - enable virgl-based GL acceleration, using OpenGL ES (also needs gtk or sdl options)
72 egl-headless - enable headless EGL output; use vnc (via publicvnc option) or spice to see it
Patrick Williamsc0f7c042017-02-23 20:41:17 -060073 serial - enable a serial console on /dev/ttyS0
Brad Bishop19323692019-04-05 15:28:33 -040074 serialstdio - enable a serial console on the console (regardless of graphics mode)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060075 slirp - enable user networking, no root privileges is required
Brad Bishopa34c0302019-09-23 22:34:48 -040076 snapshot - don't write changes to back to images
Patrick Williamsc0f7c042017-02-23 20:41:17 -060077 kvm - enable KVM when running x86/x86_64 (VT-capable CPU required)
78 kvm-vhost - enable KVM with vhost when running x86/x86_64 (VT-capable CPU required)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050079 publicvnc - enable a VNC server open to all hosts
Patrick Williamsc0f7c042017-02-23 20:41:17 -060080 audio - enable audio
Brad Bishop6e60e8b2018-02-01 10:27:11 -050081 [*/]ovmf* - OVMF firmware file or base name for booting with UEFI
Patrick Williamsc0f7c042017-02-23 20:41:17 -060082 tcpserial=<port> - specify tcp serial port number
Patrick Williamsc0f7c042017-02-23 20:41:17 -060083 qemuparams=<xyz> - specify custom parameters to QEMU
84 bootparams=<xyz> - specify custom kernel parameters during boot
Brad Bishop6e60e8b2018-02-01 10:27:11 -050085 help, -h, --help: print this text
Brad Bishopd7bf8c12018-02-25 22:55:05 -050086 -d, --debug: Enable debug output
Brad Bishop79641f22019-09-10 07:20:22 -040087 -q, --quiet: Hide most output except error messages
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050088
89Examples:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050090 runqemu
Patrick Williamsc0f7c042017-02-23 20:41:17 -060091 runqemu qemuarm
92 runqemu tmp/deploy/images/qemuarm
Brad Bishop6e60e8b2018-02-01 10:27:11 -050093 runqemu tmp/deploy/images/qemux86/<qemuboot.conf>
Patrick Williamsc0f7c042017-02-23 20:41:17 -060094 runqemu qemux86-64 core-image-sato ext4
95 runqemu qemux86-64 wic-image-minimal wic
96 runqemu path/to/bzImage-qemux86.bin path/to/nfsrootdir/ serial
Andrew Geisslerd1e89492021-02-12 15:35:20 -060097 runqemu qemux86 iso/hddimg/wic.vmdk/wic.vhd/wic.vhdx/wic.qcow2/wic.vdi/ramfs/cpio.gz...
Patrick Williamsc0f7c042017-02-23 20:41:17 -060098 runqemu qemux86 qemuparams="-m 256"
99 runqemu qemux86 bootparams="psplash=false"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600100 runqemu path/to/<image>-<machine>.wic
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500101 runqemu path/to/<image>-<machine>.wic.vmdk
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600102 runqemu path/to/<image>-<machine>.wic.vhdx
103 runqemu path/to/<image>-<machine>.wic.vhd
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600104""")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500105
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600106def check_tun():
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500107 """Check /dev/net/tun"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600108 dev_tun = '/dev/net/tun'
109 if not os.path.exists(dev_tun):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500110 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 -0500111
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600112 if not os.access(dev_tun, os.W_OK):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500113 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 -0500114
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600115def get_first_file(cmds):
116 """Return first file found in wildcard cmds"""
117 for cmd in cmds:
118 all_files = glob.glob(cmd)
119 if all_files:
120 for f in all_files:
121 if not os.path.isdir(f):
122 return f
123 return ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500124
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600125class BaseConfig(object):
126 def __init__(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500127 # The self.d saved vars from self.set(), part of them are from qemuboot.conf
128 self.d = {'QB_KERNEL_ROOT': '/dev/vda'}
129
130 # Supported env vars, add it here if a var can be got from env,
131 # and don't use os.getenv in the code.
132 self.env_vars = ('MACHINE',
133 'ROOTFS',
134 'KERNEL',
Brad Bishopc68388fc2019-08-26 01:33:31 -0400135 'BIOS',
Brad Bishop316dfdd2018-06-25 12:45:53 -0400136 'DEVICE_TREE',
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500137 'DEPLOY_DIR_IMAGE',
138 'OE_TMPDIR',
139 'OECORE_NATIVE_SYSROOT',
Andrew Geissler82c905d2020-04-13 13:39:40 -0500140 'MULTICONFIG',
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500141 )
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500142
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600143 self.qemu_opt = ''
144 self.qemu_opt_script = ''
Andrew Geissler99467da2019-02-25 18:54:23 -0600145 self.qemuparams = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600146 self.clean_nfs_dir = False
147 self.nfs_server = ''
148 self.rootfs = ''
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500149 # File name(s) of a OVMF firmware file or variable store,
150 # to be added with -drive if=pflash.
151 # Found in the same places as the rootfs, with or without one of
152 # these suffices: qcow2, bin.
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500153 self.ovmf_bios = []
Brad Bishop08902b02019-08-20 09:16:51 -0400154 # When enrolling default Secure Boot keys, the hypervisor
155 # must provide the Platform Key and the first Key Exchange Key
156 # certificate in the Type 11 SMBIOS table.
157 self.ovmf_secboot_pkkek1 = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600158 self.qemuboot = ''
159 self.qbconfload = False
160 self.kernel = ''
Brad Bishopc68388fc2019-08-26 01:33:31 -0400161 self.bios = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600162 self.kernel_cmdline = ''
163 self.kernel_cmdline_script = ''
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500164 self.bootparams = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600165 self.dtb = ''
166 self.fstype = ''
167 self.kvm_enabled = False
168 self.vhost_enabled = False
169 self.slirp_enabled = False
Andrew Geissler82c905d2020-04-13 13:39:40 -0500170 self.net_bridge = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600171 self.nfs_instance = 0
172 self.nfs_running = False
Brad Bishop19323692019-04-05 15:28:33 -0400173 self.serialconsole = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600174 self.serialstdio = False
175 self.cleantap = False
176 self.saved_stty = ''
177 self.audio_enabled = False
178 self.tcpserial_portnum = ''
Brad Bishop08902b02019-08-20 09:16:51 -0400179 self.taplock = ''
180 self.taplock_descriptor = None
181 self.portlocks = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600182 self.bitbake_e = ''
183 self.snapshot = False
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600184 self.wictypes = ('wic', 'wic.vmdk', 'wic.qcow2', 'wic.vdi', "wic.vhd", "wic.vhdx")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500185 self.fstypes = ('ext2', 'ext3', 'ext4', 'jffs2', 'nfs', 'btrfs',
186 'cpio.gz', 'cpio', 'ramfs', 'tar.bz2', 'tar.gz')
Brad Bishop08902b02019-08-20 09:16:51 -0400187 self.vmtypes = ('hddimg', 'iso')
Brad Bishop15ae2502019-06-18 21:44:24 -0400188 self.fsinfo = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500189 self.network_device = "-device e1000,netdev=net0,mac=@MAC@"
Andrew Geissler82c905d2020-04-13 13:39:40 -0500190 self.cmdline_ip_slirp = "ip=dhcp"
191 self.cmdline_ip_tap = "ip=192.168.7.@CLIENT@::192.168.7.@GATEWAY@:255.255.255.0"
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500192 # Use different mac section for tap and slirp to avoid
193 # conflicts, e.g., when one is running with tap, the other is
194 # running with slirp.
195 # The last section is dynamic, which is for avoiding conflicts,
196 # when multiple qemus are running, e.g., when multiple tap or
197 # slirp qemus are running.
198 self.mac_tap = "52:54:00:12:34:"
199 self.mac_slirp = "52:54:00:12:35:"
Brad Bishop004d4992018-10-02 23:54:45 +0200200 # pid of the actual qemu process
201 self.qemupid = None
202 # avoid cleanup twice
203 self.cleaned = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500204
Brad Bishop08902b02019-08-20 09:16:51 -0400205 def acquire_taplock(self, error=True):
206 logger.debug("Acquiring lockfile %s..." % self.taplock)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600207 try:
Brad Bishop08902b02019-08-20 09:16:51 -0400208 self.taplock_descriptor = open(self.taplock, 'w')
209 fcntl.flock(self.taplock_descriptor, fcntl.LOCK_EX|fcntl.LOCK_NB)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600210 except Exception as e:
Brad Bishop08902b02019-08-20 09:16:51 -0400211 msg = "Acquiring lockfile %s failed: %s" % (self.taplock, e)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500212 if error:
213 logger.error(msg)
214 else:
215 logger.info(msg)
Brad Bishop08902b02019-08-20 09:16:51 -0400216 if self.taplock_descriptor:
217 self.taplock_descriptor.close()
218 self.taplock_descriptor = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600219 return False
220 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500221
Brad Bishop08902b02019-08-20 09:16:51 -0400222 def release_taplock(self):
223 if self.taplock_descriptor:
Brad Bishopf86d0552018-12-04 14:18:15 -0800224 logger.debug("Releasing lockfile for tap device '%s'" % self.tap)
Brad Bishop08902b02019-08-20 09:16:51 -0400225 fcntl.flock(self.taplock_descriptor, fcntl.LOCK_UN)
226 self.taplock_descriptor.close()
227 os.remove(self.taplock)
228 self.taplock_descriptor = None
229
230 def check_free_port(self, host, port, lockdir):
231 """ Check whether the port is free or not """
232 import socket
233 from contextlib import closing
234
235 lockfile = os.path.join(lockdir, str(port) + '.lock')
236 if self.acquire_portlock(lockfile):
237 with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
238 if sock.connect_ex((host, port)) == 0:
239 # Port is open, so not free
240 self.release_portlock(lockfile)
241 return False
242 else:
243 # Port is not open, so free
244 return True
245 else:
246 return False
247
248 def acquire_portlock(self, lockfile):
249 logger.debug("Acquiring lockfile %s..." % lockfile)
250 try:
251 portlock_descriptor = open(lockfile, 'w')
252 self.portlocks.update({lockfile: portlock_descriptor})
253 fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_EX|fcntl.LOCK_NB)
254 except Exception as e:
255 msg = "Acquiring lockfile %s failed: %s" % (lockfile, e)
256 logger.info(msg)
257 if lockfile in self.portlocks.keys() and self.portlocks[lockfile]:
258 self.portlocks[lockfile].close()
259 del self.portlocks[lockfile]
260 return False
261 return True
262
263 def release_portlock(self, lockfile=None):
264 if lockfile != None:
265 logger.debug("Releasing lockfile '%s'" % lockfile)
266 fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_UN)
267 self.portlocks[lockfile].close()
268 os.remove(lockfile)
269 del self.portlocks[lockfile]
270 elif len(self.portlocks):
271 for lockfile, descriptor in self.portlocks.items():
272 logger.debug("Releasing lockfile '%s'" % lockfile)
273 fcntl.flock(descriptor, fcntl.LOCK_UN)
274 descriptor.close()
275 os.remove(lockfile)
276 self.portlocks = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500277
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600278 def get(self, key):
279 if key in self.d:
280 return self.d.get(key)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500281 elif os.getenv(key):
282 return os.getenv(key)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600283 else:
284 return ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500285
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600286 def set(self, key, value):
287 self.d[key] = value
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500288
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600289 def is_deploy_dir_image(self, p):
290 if os.path.isdir(p):
291 if not re.search('.qemuboot.conf$', '\n'.join(os.listdir(p)), re.M):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500292 logger.debug("Can't find required *.qemuboot.conf in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600293 return False
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500294 if not any(map(lambda name: '-image-' in name, os.listdir(p))):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500295 logger.debug("Can't find *-image-* in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600296 return False
297 return True
298 else:
299 return False
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500300
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600301 def check_arg_fstype(self, fst):
302 """Check and set FSTYPE"""
Brad Bishop15ae2502019-06-18 21:44:24 -0400303 if fst not in self.fstypes + self.vmtypes + self.wictypes:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800304 logger.warning("Maybe unsupported FSTYPE: %s" % fst)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600305 if not self.fstype or self.fstype == fst:
306 if fst == 'ramfs':
307 fst = 'cpio.gz'
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500308 if fst in ('tar.bz2', 'tar.gz'):
309 fst = 'nfs'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600310 self.fstype = fst
311 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500312 raise RunQemuError("Conflicting: FSTYPE %s and %s" % (self.fstype, fst))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500313
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600314 def set_machine_deploy_dir(self, machine, deploy_dir_image):
315 """Set MACHINE and DEPLOY_DIR_IMAGE"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500316 logger.debug('MACHINE: %s' % machine)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600317 self.set("MACHINE", machine)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500318 logger.debug('DEPLOY_DIR_IMAGE: %s' % deploy_dir_image)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600319 self.set("DEPLOY_DIR_IMAGE", deploy_dir_image)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500320
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600321 def check_arg_nfs(self, p):
322 if os.path.isdir(p):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500323 self.rootfs = p
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600324 else:
325 m = re.match('(.*):(.*)', p)
326 self.nfs_server = m.group(1)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500327 self.rootfs = m.group(2)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600328 self.check_arg_fstype('nfs')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500329
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600330 def check_arg_path(self, p):
331 """
332 - Check whether it is <image>.qemuboot.conf or contains <image>.qemuboot.conf
333 - Check whether is a kernel file
334 - Check whether is a image file
335 - Check whether it is a nfs dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500336 - Check whether it is a OVMF flash file
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600337 """
338 if p.endswith('.qemuboot.conf'):
339 self.qemuboot = p
340 self.qbconfload = True
341 elif re.search('\.bin$', p) or re.search('bzImage', p) or \
342 re.search('zImage', p) or re.search('vmlinux', p) or \
343 re.search('fitImage', p) or re.search('uImage', p):
344 self.kernel = p
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500345 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 -0600346 self.rootfs = p
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500347 # Check filename against self.fstypes can hanlde <file>.cpio.gz,
348 # otherwise, its type would be "gz", which is incorrect.
349 fst = ""
350 for t in self.fstypes:
351 if p.endswith(t):
352 fst = t
353 break
354 if not fst:
355 m = re.search('.*\.(.*)$', self.rootfs)
356 if m:
357 fst = m.group(1)
358 if fst:
359 self.check_arg_fstype(fst)
360 qb = re.sub('\.' + fst + "$", '', self.rootfs)
361 qb = '%s%s' % (re.sub('\.rootfs$', '', qb), '.qemuboot.conf')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600362 if os.path.exists(qb):
363 self.qemuboot = qb
364 self.qbconfload = True
365 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800366 logger.warning("%s doesn't exist" % qb)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600367 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500368 raise RunQemuError("Can't find FSTYPE from: %s" % p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500369
370 elif os.path.isdir(p) or re.search(':', p) and re.search('/', p):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600371 if self.is_deploy_dir_image(p):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500372 logger.debug('DEPLOY_DIR_IMAGE: %s' % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600373 self.set("DEPLOY_DIR_IMAGE", p)
374 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500375 logger.debug("Assuming %s is an nfs rootfs" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600376 self.check_arg_nfs(p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500377 elif os.path.basename(p).startswith('ovmf'):
378 self.ovmf_bios.append(p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600379 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500380 raise RunQemuError("Unknown path arg %s" % p)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500381
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600382 def check_arg_machine(self, arg):
383 """Check whether it is a machine"""
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500384 if self.get('MACHINE') == arg:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600385 return
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500386 elif self.get('MACHINE') and self.get('MACHINE') != arg:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500387 raise RunQemuError("Maybe conflicted MACHINE: %s vs %s" % (self.get('MACHINE'), arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500388 elif re.search('/', arg):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500389 raise RunQemuError("Unknown arg: %s" % arg)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500390
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500391 logger.debug('Assuming MACHINE = %s' % arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500392
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600393 # if we're running under testimage, or similarly as a child
394 # of an existing bitbake invocation, we can't invoke bitbake
395 # to validate the MACHINE setting and must assume it's correct...
396 # FIXME: testimage.bbclass exports these two variables into env,
397 # are there other scenarios in which we need to support being
398 # invoked by bitbake?
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500399 deploy = self.get('DEPLOY_DIR_IMAGE')
400 bbchild = deploy and self.get('OE_TMPDIR')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600401 if bbchild:
402 self.set_machine_deploy_dir(arg, deploy)
403 return
404 # also check whether we're running under a sourced toolchain
405 # environment file
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500406 if self.get('OECORE_NATIVE_SYSROOT'):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600407 self.set("MACHINE", arg)
408 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500409
Andrew Geissler82c905d2020-04-13 13:39:40 -0500410 self.bitbake_e = self.run_bitbake_env(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600411 # bitbake -e doesn't report invalid MACHINE as an error, so
412 # let's check DEPLOY_DIR_IMAGE to make sure that it is a valid
413 # MACHINE.
414 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
415 if s:
416 deploy_dir_image = s.group(1)
417 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500418 raise RunQemuError("bitbake -e %s" % self.bitbake_e)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600419 if self.is_deploy_dir_image(deploy_dir_image):
420 self.set_machine_deploy_dir(arg, deploy_dir_image)
421 else:
422 logger.error("%s not a directory valid DEPLOY_DIR_IMAGE" % deploy_dir_image)
423 self.set("MACHINE", arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500424
Andrew Geisslerc182c622020-05-15 14:13:32 -0500425 def set_dri_path(self):
426 # As runqemu can be run within bitbake (when using testimage, for example),
427 # we need to ensure that we run host pkg-config, and that it does not
428 # get mis-directed to native build paths set by bitbake.
429 try:
430 del os.environ['PKG_CONFIG_PATH']
431 del os.environ['PKG_CONFIG_DIR']
432 del os.environ['PKG_CONFIG_LIBDIR']
433 del os.environ['PKG_CONFIG_SYSROOT_DIR']
434 except KeyError:
435 pass
436 try:
437 dripath = subprocess.check_output("PATH=/bin:/usr/bin:$PATH pkg-config --variable=dridriverdir dri", shell=True)
438 except subprocess.CalledProcessError as e:
439 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.")
440 os.environ['LIBGL_DRIVERS_PATH'] = dripath.decode('utf-8').strip()
441
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600442 def check_args(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500443 for debug in ("-d", "--debug"):
444 if debug in sys.argv:
445 logger.setLevel(logging.DEBUG)
446 sys.argv.remove(debug)
447
448 for quiet in ("-q", "--quiet"):
449 if quiet in sys.argv:
450 logger.setLevel(logging.ERROR)
451 sys.argv.remove(quiet)
452
Andrew Geisslerc182c622020-05-15 14:13:32 -0500453 if 'gl' not in sys.argv[1:] and 'gl-es' not in sys.argv[1:]:
454 os.environ['SDL_RENDER_DRIVER'] = 'software'
455
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600456 unknown_arg = ""
457 for arg in sys.argv[1:]:
Brad Bishop15ae2502019-06-18 21:44:24 -0400458 if arg in self.fstypes + self.vmtypes + self.wictypes:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600459 self.check_arg_fstype(arg)
460 elif arg == 'nographic':
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500461 if ('sdl' in sys.argv):
462 raise RunQemuError('Option nographic makes no sense alongside the sdl option.' % (arg))
463 if ('gtk' in sys.argv):
464 raise RunQemuError('Option nographic makes no sense alongside the gtk option.' % (arg))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600465 self.qemu_opt_script += ' -nographic'
466 self.kernel_cmdline_script += ' console=ttyS0'
Brad Bishop19323692019-04-05 15:28:33 -0400467 elif arg == 'sdl':
Brad Bishop6dbb3162019-11-25 09:41:34 -0500468 if 'gl' in sys.argv[1:]:
Andrew Geisslerc182c622020-05-15 14:13:32 -0500469 self.set_dri_path()
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500470 self.qemu_opt_script += ' -vga virtio -display sdl,gl=on,show-cursor=on'
Brad Bishop6dbb3162019-11-25 09:41:34 -0500471 elif 'gl-es' in sys.argv[1:]:
Andrew Geisslerc182c622020-05-15 14:13:32 -0500472 self.set_dri_path()
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500473 self.qemu_opt_script += ' -vga virtio -display sdl,gl=es,show-cursor=on'
Brad Bishop6dbb3162019-11-25 09:41:34 -0500474 else:
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500475 self.qemu_opt_script += ' -display sdl,show-cursor=on'
Brad Bishopa34c0302019-09-23 22:34:48 -0400476 elif arg == 'gtk':
477 if 'gl' in sys.argv[1:]:
Andrew Geisslerc182c622020-05-15 14:13:32 -0500478 self.set_dri_path()
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500479 self.qemu_opt_script += ' -vga virtio -display gtk,gl=on,show-cursor=on'
Brad Bishopa34c0302019-09-23 22:34:48 -0400480 elif 'gl-es' in sys.argv[1:]:
Andrew Geisslerc182c622020-05-15 14:13:32 -0500481 self.set_dri_path()
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500482 self.qemu_opt_script += ' -vga virtio -display gtk,gl=es,show-cursor=on'
Brad Bishopa34c0302019-09-23 22:34:48 -0400483 else:
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500484 self.qemu_opt_script += ' -display gtk,show-cursor=on'
Brad Bishopa34c0302019-09-23 22:34:48 -0400485 elif arg == 'gl' or arg == 'gl-es':
486 # These args are handled inside sdl or gtk blocks above
Andrew Geissler635e0e42020-08-21 15:58:33 -0500487 if ('gtk' not in sys.argv) and ('sdl' not in sys.argv):
488 raise RunQemuError('Option %s also needs gtk or sdl option.' % (arg))
Brad Bishop19323692019-04-05 15:28:33 -0400489 elif arg == 'egl-headless':
Andrew Geisslerc182c622020-05-15 14:13:32 -0500490 self.set_dri_path()
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500491 self.qemu_opt_script += ' -vga virtio -display egl-headless,show-cursor=on'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600492 elif arg == 'serial':
493 self.kernel_cmdline_script += ' console=ttyS0'
Brad Bishop19323692019-04-05 15:28:33 -0400494 self.serialconsole = True
495 elif arg == "serialstdio":
496 self.kernel_cmdline_script += ' console=ttyS0'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600497 self.serialstdio = True
498 elif arg == 'audio':
499 logger.info("Enabling audio in qemu")
500 logger.info("Please install sound drivers in linux host")
501 self.audio_enabled = True
502 elif arg == 'kvm':
503 self.kvm_enabled = True
504 elif arg == 'kvm-vhost':
505 self.vhost_enabled = True
506 elif arg == 'slirp':
507 self.slirp_enabled = True
Andrew Geissler82c905d2020-04-13 13:39:40 -0500508 elif arg.startswith('bridge='):
509 self.net_bridge = '%s' % arg[len('bridge='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600510 elif arg == 'snapshot':
511 self.snapshot = True
512 elif arg == 'publicvnc':
513 self.qemu_opt_script += ' -vnc :0'
514 elif arg.startswith('tcpserial='):
Brad Bishop15ae2502019-06-18 21:44:24 -0400515 self.tcpserial_portnum = '%s' % arg[len('tcpserial='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600516 elif arg.startswith('qemuparams='):
Andrew Geissler99467da2019-02-25 18:54:23 -0600517 self.qemuparams = ' %s' % arg[len('qemuparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600518 elif arg.startswith('bootparams='):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500519 self.bootparams = arg[len('bootparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600520 elif os.path.exists(arg) or (re.search(':', arg) and re.search('/', arg)):
521 self.check_arg_path(os.path.abspath(arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500522 elif re.search(r'-image-|-image$', arg):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600523 # Lazy rootfs
524 self.rootfs = arg
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500525 elif arg.startswith('ovmf'):
526 self.ovmf_bios.append(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600527 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500528 # At last, assume it is the MACHINE
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600529 if (not unknown_arg) or unknown_arg == arg:
530 unknown_arg = arg
531 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500532 raise RunQemuError("Can't handle two unknown args: %s %s\n"
533 "Try 'runqemu help' on how to use it" % \
534 (unknown_arg, arg))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600535 # Check to make sure it is a valid machine
Brad Bishopa5c52ff2018-11-23 10:55:50 +1300536 if unknown_arg and self.get('MACHINE') != unknown_arg:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500537 if self.get('DEPLOY_DIR_IMAGE'):
538 machine = os.path.basename(self.get('DEPLOY_DIR_IMAGE'))
539 if unknown_arg == machine:
540 self.set("MACHINE", machine)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500541
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600542 self.check_arg_machine(unknown_arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500543
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500544 if not (self.get('DEPLOY_DIR_IMAGE') or self.qbconfload):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500545 self.load_bitbake_env()
546 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
547 if s:
548 self.set("DEPLOY_DIR_IMAGE", s.group(1))
549
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600550 def check_kvm(self):
551 """Check kvm and kvm-host"""
552 if not (self.kvm_enabled or self.vhost_enabled):
553 self.qemu_opt_script += ' %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU'))
554 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500555
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600556 if not self.get('QB_CPU_KVM'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500557 raise RunQemuError("QB_CPU_KVM is NULL, this board doesn't support kvm")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500558
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600559 self.qemu_opt_script += ' %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU_KVM'))
560 yocto_kvm_wiki = "https://wiki.yoctoproject.org/wiki/How_to_enable_KVM_for_Poky_qemu"
561 yocto_paravirt_kvm_wiki = "https://wiki.yoctoproject.org/wiki/Running_an_x86_Yocto_Linux_image_under_QEMU_KVM"
562 dev_kvm = '/dev/kvm'
563 dev_vhost = '/dev/vhost-net'
Brad Bishop15ae2502019-06-18 21:44:24 -0400564 if self.qemu_system.endswith(('i386', 'x86_64')):
565 with open('/proc/cpuinfo', 'r') as f:
566 kvm_cap = re.search('vmx|svm', "".join(f.readlines()))
567 if not kvm_cap:
568 logger.error("You are trying to enable KVM on a cpu without VT support.")
569 logger.error("Remove kvm from the command-line, or refer:")
570 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500571
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600572 if not os.path.exists(dev_kvm):
573 logger.error("Missing KVM device. Have you inserted kvm modules?")
574 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500575 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500576
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600577 if os.access(dev_kvm, os.W_OK|os.R_OK):
578 self.qemu_opt_script += ' -enable-kvm'
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500579 if self.get('MACHINE') == "qemux86":
580 # Workaround for broken APIC window on pre 4.15 host kernels which causes boot hangs
581 # See YOCTO #12301
582 # On 64 bit we use x2apic
583 self.kernel_cmdline_script += " clocksource=kvm-clock hpet=disable noapic nolapic"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600584 else:
585 logger.error("You have no read or write permission on /dev/kvm.")
586 logger.error("Please change the ownership of this file as described at:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500587 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500588
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600589 if self.vhost_enabled:
590 if not os.path.exists(dev_vhost):
591 logger.error("Missing virtio net device. Have you inserted vhost-net module?")
592 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500593 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500594
Andrew Geissler635e0e42020-08-21 15:58:33 -0500595 if not os.access(dev_vhost, os.W_OK|os.R_OK):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600596 logger.error("You have no read or write permission on /dev/vhost-net.")
597 logger.error("Please change the ownership of this file as described at:")
Andrew Geissler635e0e42020-08-21 15:58:33 -0500598 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500599
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600600 def check_fstype(self):
601 """Check and setup FSTYPE"""
602 if not self.fstype:
603 fstype = self.get('QB_DEFAULT_FSTYPE')
604 if fstype:
605 self.fstype = fstype
606 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500607 raise RunQemuError("FSTYPE is NULL!")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500608
Brad Bishop15ae2502019-06-18 21:44:24 -0400609 # parse QB_FSINFO into dict, e.g. { 'wic': ['no-kernel-in-fs', 'a-flag'], 'ext4': ['another-flag']}
610 wic_fs = False
611 qb_fsinfo = self.get('QB_FSINFO')
612 if qb_fsinfo:
613 qb_fsinfo = qb_fsinfo.split()
614 for fsinfo in qb_fsinfo:
615 try:
616 fstype, fsflag = fsinfo.split(':')
617
618 if fstype == 'wic':
619 if fsflag == 'no-kernel-in-fs':
620 wic_fs = True
621 elif fsflag == 'kernel-in-fs':
622 wic_fs = False
623 else:
624 logger.warn('Unknown flag "%s:%s" in QB_FSINFO', fstype, fsflag)
625 continue
626 else:
627 logger.warn('QB_FSINFO is not supported for image type "%s"', fstype)
628 continue
629
630 if fstype in self.fsinfo:
631 self.fsinfo[fstype].append(fsflag)
632 else:
633 self.fsinfo[fstype] = [fsflag]
634 except Exception:
635 logger.error('Invalid parameter "%s" in QB_FSINFO', fsinfo)
636
637 # treat wic images as vmimages (with kernel) or as fsimages (rootfs only)
638 if wic_fs:
639 self.fstypes = self.fstypes + self.wictypes
640 else:
641 self.vmtypes = self.vmtypes + self.wictypes
642
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600643 def check_rootfs(self):
644 """Check and set rootfs"""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500645
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500646 if self.fstype == "none":
647 return
648
649 if self.get('ROOTFS'):
650 if not self.rootfs:
651 self.rootfs = self.get('ROOTFS')
652 elif self.get('ROOTFS') != self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500653 raise RunQemuError("Maybe conflicted ROOTFS: %s vs %s" % (self.get('ROOTFS'), self.rootfs))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500654
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600655 if self.fstype == 'nfs':
656 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500657
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600658 if self.rootfs and not os.path.exists(self.rootfs):
659 # Lazy rootfs
660 self.rootfs = "%s/%s-%s.%s" % (self.get('DEPLOY_DIR_IMAGE'),
661 self.rootfs, self.get('MACHINE'),
662 self.fstype)
663 elif not self.rootfs:
664 cmd_name = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_NAME'), self.fstype)
665 cmd_link = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'), self.fstype)
666 cmds = (cmd_name, cmd_link)
667 self.rootfs = get_first_file(cmds)
668 if not self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500669 raise RunQemuError("Failed to find rootfs: %s or %s" % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500670
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600671 if not os.path.exists(self.rootfs):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500672 raise RunQemuError("Can't find rootfs: %s" % self.rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500673
Brad Bishop08902b02019-08-20 09:16:51 -0400674 def setup_pkkek1(self):
675 """
676 Extract from PEM certificate the Platform Key and first Key
677 Exchange Key certificate string. The hypervisor needs to provide
678 it in the Type 11 SMBIOS table
679 """
680 pemcert = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), 'OvmfPkKek1.pem')
681 try:
682 with open(pemcert, 'r') as pemfile:
683 key = pemfile.read().replace('\n', ''). \
684 replace('-----BEGIN CERTIFICATE-----', ''). \
685 replace('-----END CERTIFICATE-----', '')
686 self.ovmf_secboot_pkkek1 = key
687
688 except FileNotFoundError:
689 raise RunQemuError("Can't open PEM certificate %s " % pemcert)
690
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500691 def check_ovmf(self):
692 """Check and set full path for OVMF firmware and variable file(s)."""
693
694 for index, ovmf in enumerate(self.ovmf_bios):
695 if os.path.exists(ovmf):
696 continue
697 for suffix in ('qcow2', 'bin'):
698 path = '%s/%s.%s' % (self.get('DEPLOY_DIR_IMAGE'), ovmf, suffix)
699 if os.path.exists(path):
700 self.ovmf_bios[index] = path
Brad Bishop08902b02019-08-20 09:16:51 -0400701 if ovmf.endswith('secboot'):
702 self.setup_pkkek1()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500703 break
704 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500705 raise RunQemuError("Can't find OVMF firmware: %s" % ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500706
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600707 def check_kernel(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400708 """Check and set kernel"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600709 # The vm image doesn't need a kernel
710 if self.fstype in self.vmtypes:
711 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500712
Brad Bishop316dfdd2018-06-25 12:45:53 -0400713 # See if the user supplied a KERNEL option
714 if self.get('KERNEL'):
715 self.kernel = self.get('KERNEL')
716
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500717 # QB_DEFAULT_KERNEL is always a full file path
718 kernel_name = os.path.basename(self.get('QB_DEFAULT_KERNEL'))
719
720 # The user didn't want a kernel to be loaded
Brad Bishop316dfdd2018-06-25 12:45:53 -0400721 if kernel_name == "none" and not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500722 return
723
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600724 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
725 if not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500726 kernel_match_name = "%s/%s" % (deploy_dir_image, kernel_name)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600727 kernel_match_link = "%s/%s" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
728 kernel_startswith = "%s/%s*" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
729 cmds = (kernel_match_name, kernel_match_link, kernel_startswith)
730 self.kernel = get_first_file(cmds)
731 if not self.kernel:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500732 raise RunQemuError('KERNEL not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500733
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600734 if not os.path.exists(self.kernel):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500735 raise RunQemuError("KERNEL %s not found" % self.kernel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500736
Brad Bishop316dfdd2018-06-25 12:45:53 -0400737 def check_dtb(self):
738 """Check and set dtb"""
739 # Did the user specify a device tree?
740 if self.get('DEVICE_TREE'):
741 self.dtb = self.get('DEVICE_TREE')
742 if not os.path.exists(self.dtb):
743 raise RunQemuError('Specified DTB not found: %s' % self.dtb)
744 return
745
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600746 dtb = self.get('QB_DTB')
747 if dtb:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400748 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600749 cmd_match = "%s/%s" % (deploy_dir_image, dtb)
750 cmd_startswith = "%s/%s*" % (deploy_dir_image, dtb)
751 cmd_wild = "%s/*.dtb" % deploy_dir_image
752 cmds = (cmd_match, cmd_startswith, cmd_wild)
753 self.dtb = get_first_file(cmds)
754 if not os.path.exists(self.dtb):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500755 raise RunQemuError('DTB not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500756
Brad Bishopc68388fc2019-08-26 01:33:31 -0400757 def check_bios(self):
758 """Check and set bios"""
759
760 # See if the user supplied a BIOS option
761 if self.get('BIOS'):
762 self.bios = self.get('BIOS')
763
764 # QB_DEFAULT_BIOS is always a full file path
765 bios_name = os.path.basename(self.get('QB_DEFAULT_BIOS'))
766
767 # The user didn't want a bios to be loaded
768 if (bios_name == "" or bios_name == "none") and not self.bios:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600769 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500770
Brad Bishopc68388fc2019-08-26 01:33:31 -0400771 if not self.bios:
772 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
773 self.bios = "%s/%s" % (deploy_dir_image, bios_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500774
Brad Bishopc68388fc2019-08-26 01:33:31 -0400775 if not self.bios:
776 raise RunQemuError('BIOS not found: %s' % bios_match_name)
777
778 if not os.path.exists(self.bios):
779 raise RunQemuError("KERNEL %s not found" % self.bios)
780
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500781
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600782 def check_mem(self):
Andrew Geissler99467da2019-02-25 18:54:23 -0600783 """
784 Both qemu and kernel needs memory settings, so check QB_MEM and set it
785 for both.
786 """
787 s = re.search('-m +([0-9]+)', self.qemuparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600788 if s:
789 self.set('QB_MEM', '-m %s' % s.group(1))
790 elif not self.get('QB_MEM'):
Brad Bishop79641f22019-09-10 07:20:22 -0400791 logger.info('QB_MEM is not set, use 256M by default')
792 self.set('QB_MEM', '-m 256')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500793
Andrew Geissler99467da2019-02-25 18:54:23 -0600794 # Check and remove M or m suffix
795 qb_mem = self.get('QB_MEM')
796 if qb_mem.endswith('M') or qb_mem.endswith('m'):
797 qb_mem = qb_mem[:-1]
798
799 # Add -m prefix it not present
800 if not qb_mem.startswith('-m'):
801 qb_mem = '-m %s' % qb_mem
802
803 self.set('QB_MEM', qb_mem)
804
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800805 mach = self.get('MACHINE')
806 if not mach.startswith('qemumips'):
807 self.kernel_cmdline_script += ' mem=%s' % self.get('QB_MEM').replace('-m','').strip() + 'M'
808
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600809 self.qemu_opt_script += ' %s' % self.get('QB_MEM')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500810
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600811 def check_tcpserial(self):
812 if self.tcpserial_portnum:
Brad Bishop15ae2502019-06-18 21:44:24 -0400813 ports = self.tcpserial_portnum.split(':')
814 port = ports[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600815 if self.get('QB_TCPSERIAL_OPT'):
Brad Bishop15ae2502019-06-18 21:44:24 -0400816 self.qemu_opt_script += ' ' + self.get('QB_TCPSERIAL_OPT').replace('@PORT@', port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600817 else:
Brad Bishop15ae2502019-06-18 21:44:24 -0400818 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
819
820 if len(ports) > 1:
821 for port in ports[1:]:
822 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500823
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600824 def check_and_set(self):
825 """Check configs sanity and set when needed"""
826 self.validate_paths()
Andrew Geissler82c905d2020-04-13 13:39:40 -0500827 if not self.slirp_enabled and not self.net_bridge:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500828 check_tun()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600829 # Check audio
830 if self.audio_enabled:
831 if not self.get('QB_AUDIO_DRV'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500832 raise RunQemuError("QB_AUDIO_DRV is NULL, this board doesn't support audio")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600833 if not self.get('QB_AUDIO_OPT'):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800834 logger.warning('QB_AUDIO_OPT is NULL, you may need define it to make audio work')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600835 else:
836 self.qemu_opt_script += ' %s' % self.get('QB_AUDIO_OPT')
837 os.putenv('QEMU_AUDIO_DRV', self.get('QB_AUDIO_DRV'))
838 else:
839 os.putenv('QEMU_AUDIO_DRV', 'none')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500840
Brad Bishop15ae2502019-06-18 21:44:24 -0400841 self.check_qemu_system()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600842 self.check_kvm()
843 self.check_fstype()
844 self.check_rootfs()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500845 self.check_ovmf()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600846 self.check_kernel()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400847 self.check_dtb()
Brad Bishopc68388fc2019-08-26 01:33:31 -0400848 self.check_bios()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600849 self.check_mem()
850 self.check_tcpserial()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500851
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600852 def read_qemuboot(self):
853 if not self.qemuboot:
854 if self.get('DEPLOY_DIR_IMAGE'):
855 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600856 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800857 logger.warning("Can't find qemuboot conf file, DEPLOY_DIR_IMAGE is NULL!")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600858 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500859
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600860 if self.rootfs and not os.path.exists(self.rootfs):
861 # Lazy rootfs
862 machine = self.get('MACHINE')
863 if not machine:
864 machine = os.path.basename(deploy_dir_image)
865 self.qemuboot = "%s/%s-%s.qemuboot.conf" % (deploy_dir_image,
866 self.rootfs, machine)
867 else:
868 cmd = 'ls -t %s/*.qemuboot.conf' % deploy_dir_image
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500869 logger.debug('Running %s...' % cmd)
870 try:
871 qbs = subprocess.check_output(cmd, shell=True).decode('utf-8')
872 except subprocess.CalledProcessError as err:
873 raise RunQemuError(err)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600874 if qbs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500875 for qb in qbs.split():
876 # Don't use initramfs when other choices unless fstype is ramfs
877 if '-initramfs-' in os.path.basename(qb) and self.fstype != 'cpio.gz':
878 continue
879 self.qemuboot = qb
880 break
881 if not self.qemuboot:
882 # Use the first one when no choice
883 self.qemuboot = qbs.split()[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600884 self.qbconfload = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500885
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600886 if not self.qemuboot:
887 # If we haven't found a .qemuboot.conf at this point it probably
888 # doesn't exist, continue without
889 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500890
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600891 if not os.path.exists(self.qemuboot):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500892 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 -0500893
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500894 logger.debug('CONFFILE: %s' % self.qemuboot)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500895
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600896 cf = configparser.ConfigParser()
897 cf.read(self.qemuboot)
898 for k, v in cf.items('config_bsp'):
899 k_upper = k.upper()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500900 if v.startswith("../"):
901 v = os.path.abspath(os.path.dirname(self.qemuboot) + "/" + v)
902 elif v == ".":
903 v = os.path.dirname(self.qemuboot)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600904 self.set(k_upper, v)
905
906 def validate_paths(self):
907 """Ensure all relevant path variables are set"""
908 # When we're started with a *.qemuboot.conf arg assume that image
909 # artefacts are relative to that file, rather than in whatever
910 # directory DEPLOY_DIR_IMAGE in the conf file points to.
911 if self.qbconfload:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500912 imgdir = os.path.realpath(os.path.dirname(self.qemuboot))
913 if imgdir != os.path.realpath(self.get('DEPLOY_DIR_IMAGE')):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600914 logger.info('Setting DEPLOY_DIR_IMAGE to folder containing %s (%s)' % (self.qemuboot, imgdir))
915 self.set('DEPLOY_DIR_IMAGE', imgdir)
916
917 # If the STAGING_*_NATIVE directories from the config file don't exist
918 # and we're in a sourced OE build directory try to extract the paths
919 # from `bitbake -e`
920 havenative = os.path.exists(self.get('STAGING_DIR_NATIVE')) and \
921 os.path.exists(self.get('STAGING_BINDIR_NATIVE'))
922
923 if not havenative:
924 if not self.bitbake_e:
925 self.load_bitbake_env()
926
927 if self.bitbake_e:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500928 native_vars = ['STAGING_DIR_NATIVE']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600929 for nv in native_vars:
930 s = re.search('^%s="(.*)"' % nv, self.bitbake_e, re.M)
931 if s and s.group(1) != self.get(nv):
932 logger.info('Overriding conf file setting of %s to %s from Bitbake environment' % (nv, s.group(1)))
933 self.set(nv, s.group(1))
934 else:
935 # when we're invoked from a running bitbake instance we won't
936 # be able to call `bitbake -e`, then try:
937 # - get OE_TMPDIR from environment and guess paths based on it
938 # - get OECORE_NATIVE_SYSROOT from environment (for sdk)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500939 tmpdir = self.get('OE_TMPDIR')
940 oecore_native_sysroot = self.get('OECORE_NATIVE_SYSROOT')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600941 if tmpdir:
942 logger.info('Setting STAGING_DIR_NATIVE and STAGING_BINDIR_NATIVE relative to OE_TMPDIR (%s)' % tmpdir)
943 hostos, _, _, _, machine = os.uname()
944 buildsys = '%s-%s' % (machine, hostos.lower())
945 staging_dir_native = '%s/sysroots/%s' % (tmpdir, buildsys)
946 self.set('STAGING_DIR_NATIVE', staging_dir_native)
947 elif oecore_native_sysroot:
948 logger.info('Setting STAGING_DIR_NATIVE to OECORE_NATIVE_SYSROOT (%s)' % oecore_native_sysroot)
949 self.set('STAGING_DIR_NATIVE', oecore_native_sysroot)
950 if self.get('STAGING_DIR_NATIVE'):
951 # we have to assume that STAGING_BINDIR_NATIVE is at usr/bin
952 staging_bindir_native = '%s/usr/bin' % self.get('STAGING_DIR_NATIVE')
953 logger.info('Setting STAGING_BINDIR_NATIVE to %s' % staging_bindir_native)
954 self.set('STAGING_BINDIR_NATIVE', '%s/usr/bin' % self.get('STAGING_DIR_NATIVE'))
955
956 def print_config(self):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500957 logoutput = ['Continuing with the following parameters:']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600958 if not self.fstype in self.vmtypes:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500959 logoutput.append('KERNEL: [%s]' % self.kernel)
Brad Bishopc68388fc2019-08-26 01:33:31 -0400960 if self.bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500961 logoutput.append('BIOS: [%s]' % self.bios)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600962 if self.dtb:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500963 logoutput.append('DTB: [%s]' % self.dtb)
964 logoutput.append('MACHINE: [%s]' % self.get('MACHINE'))
Brad Bishop15ae2502019-06-18 21:44:24 -0400965 try:
966 fstype_flags = ' (' + ', '.join(self.fsinfo[self.fstype]) + ')'
967 except KeyError:
968 fstype_flags = ''
Andrew Geissler82c905d2020-04-13 13:39:40 -0500969 logoutput.append('FSTYPE: [%s%s]' % (self.fstype, fstype_flags))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600970 if self.fstype == 'nfs':
Andrew Geissler82c905d2020-04-13 13:39:40 -0500971 logoutput.append('NFS_DIR: [%s]' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600972 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500973 logoutput.append('ROOTFS: [%s]' % self.rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500974 if self.ovmf_bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500975 logoutput.append('OVMF: %s' % self.ovmf_bios)
Brad Bishop08902b02019-08-20 09:16:51 -0400976 if (self.ovmf_secboot_pkkek1):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500977 logoutput.append('SECBOOT PKKEK1: [%s...]' % self.ovmf_secboot_pkkek1[0:100])
978 logoutput.append('CONFFILE: [%s]' % self.qemuboot)
979 logoutput.append('')
980 logger.info('\n'.join(logoutput))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600981
982 def setup_nfs(self):
983 if not self.nfs_server:
984 if self.slirp_enabled:
985 self.nfs_server = '10.0.2.2'
986 else:
987 self.nfs_server = '192.168.7.1'
988
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500989 # Figure out a new nfs_instance to allow multiple qemus running.
Brad Bishop977dc1a2019-02-06 16:01:43 -0500990 ps = subprocess.check_output(("ps", "auxww")).decode('utf-8')
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500991 pattern = '/bin/unfsd .* -i .*\.pid -e .*/exports([0-9]+) '
992 all_instances = re.findall(pattern, ps, re.M)
993 if all_instances:
994 all_instances.sort(key=int)
995 self.nfs_instance = int(all_instances.pop()) + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600996
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500997 nfsd_port = 3049 + 2 * self.nfs_instance
998 mountd_port = 3048 + 2 * self.nfs_instance
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600999
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001000 # Export vars for runqemu-export-rootfs
1001 export_dict = {
1002 'NFS_INSTANCE': self.nfs_instance,
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001003 'NFSD_PORT': nfsd_port,
1004 'MOUNTD_PORT': mountd_port,
1005 }
1006 for k, v in export_dict.items():
1007 # Use '%s' since they are integers
1008 os.putenv(k, '%s' % v)
1009
Andrew Geissler82c905d2020-04-13 13:39:40 -05001010 self.unfs_opts="nfsvers=3,port=%s,tcp,mountport=%s" % (nfsd_port, mountd_port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001011
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001012 # Extract .tar.bz2 or .tar.bz if no nfs dir
1013 if not (self.rootfs and os.path.isdir(self.rootfs)):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001014 src_prefix = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'))
1015 dest = "%s-nfsroot" % src_prefix
1016 if os.path.exists('%s.pseudo_state' % dest):
1017 logger.info('Use %s as NFS_DIR' % dest)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001018 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001019 else:
1020 src = ""
1021 src1 = '%s.tar.bz2' % src_prefix
1022 src2 = '%s.tar.gz' % src_prefix
1023 if os.path.exists(src1):
1024 src = src1
1025 elif os.path.exists(src2):
1026 src = src2
1027 if not src:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001028 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 -06001029 logger.info('NFS_DIR not found, extracting %s to %s' % (src, dest))
Brad Bishop977dc1a2019-02-06 16:01:43 -05001030 cmd = ('runqemu-extract-sdk', src, dest)
1031 logger.info('Running %s...' % str(cmd))
1032 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001033 raise RunQemuError('Failed to run %s' % cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001034 self.clean_nfs_dir = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001035 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001036
1037 # Start the userspace NFS server
Brad Bishop977dc1a2019-02-06 16:01:43 -05001038 cmd = ('runqemu-export-rootfs', 'start', self.rootfs)
1039 logger.info('Running %s...' % str(cmd))
1040 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001041 raise RunQemuError('Failed to run %s' % cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001042
1043 self.nfs_running = True
1044
Andrew Geissler82c905d2020-04-13 13:39:40 -05001045 def setup_net_bridge(self):
1046 self.set('NETWORK_CMD', '-netdev bridge,br=%s,id=net0,helper=%s -device virtio-net-pci,netdev=net0 ' % (
1047 self.net_bridge, os.path.join(self.bindir_native, 'qemu-oe-bridge-helper')))
1048
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001049 def setup_slirp(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001050 """Setup user networking"""
1051
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001052 if self.fstype == 'nfs':
1053 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001054 netconf = " " + self.cmdline_ip_slirp
1055 logger.info("Network configuration:%s", netconf)
1056 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001057 # Port mapping
1058 hostfwd = ",hostfwd=tcp::2222-:22,hostfwd=tcp::2323-:23"
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001059 qb_slirp_opt_default = "-netdev user,id=net0%s,tftp=%s" % (hostfwd, self.get('DEPLOY_DIR_IMAGE'))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001060 qb_slirp_opt = self.get('QB_SLIRP_OPT') or qb_slirp_opt_default
1061 # Figure out the port
1062 ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt)
1063 ports = [int(i) for i in ports]
1064 mac = 2
Brad Bishop08902b02019-08-20 09:16:51 -04001065
1066 lockdir = "/tmp/qemu-port-locks"
1067 if not os.path.exists(lockdir):
1068 # There might be a race issue when multi runqemu processess are
1069 # running at the same time.
1070 try:
1071 os.mkdir(lockdir)
1072 os.chmod(lockdir, 0o777)
1073 except FileExistsError:
1074 pass
1075
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001076 # Find a free port to avoid conflicts
1077 for p in ports[:]:
1078 p_new = p
Brad Bishop08902b02019-08-20 09:16:51 -04001079 while not self.check_free_port('localhost', p_new, lockdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001080 p_new += 1
1081 mac += 1
1082 while p_new in ports:
1083 p_new += 1
1084 mac += 1
1085 if p != p_new:
1086 ports.append(p_new)
1087 qb_slirp_opt = re.sub(':%s-' % p, ':%s-' % p_new, qb_slirp_opt)
1088 logger.info("Port forward changed: %s -> %s" % (p, p_new))
1089 mac = "%s%02x" % (self.mac_slirp, mac)
1090 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qb_slirp_opt))
1091 # Print out port foward
1092 hostfwd = re.findall('(hostfwd=[^,]*)', qb_slirp_opt)
1093 if hostfwd:
1094 logger.info('Port forward: %s' % ' '.join(hostfwd))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001095
1096 def setup_tap(self):
1097 """Setup tap"""
1098
1099 # This file is created when runqemu-gen-tapdevs creates a bank of tap
1100 # devices, indicating that the user should not bring up new ones using
1101 # sudo.
1102 nosudo_flag = '/etc/runqemu-nosudo'
1103 self.qemuifup = shutil.which('runqemu-ifup')
1104 self.qemuifdown = shutil.which('runqemu-ifdown')
1105 ip = shutil.which('ip')
1106 lockdir = "/tmp/qemu-tap-locks"
1107
1108 if not (self.qemuifup and self.qemuifdown and ip):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001109 logger.error("runqemu-ifup: %s" % self.qemuifup)
1110 logger.error("runqemu-ifdown: %s" % self.qemuifdown)
1111 logger.error("ip: %s" % ip)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001112 raise OEPathError("runqemu-ifup, runqemu-ifdown or ip not found")
1113
1114 if not os.path.exists(lockdir):
1115 # There might be a race issue when multi runqemu processess are
1116 # running at the same time.
1117 try:
1118 os.mkdir(lockdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001119 os.chmod(lockdir, 0o777)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001120 except FileExistsError:
1121 pass
1122
Brad Bishop977dc1a2019-02-06 16:01:43 -05001123 cmd = (ip, 'link')
1124 logger.debug('Running %s...' % str(cmd))
1125 ip_link = subprocess.check_output(cmd).decode('utf-8')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001126 # Matches line like: 6: tap0: <foo>
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001127 possibles = re.findall('^[0-9]+: +(tap[0-9]+): <.*', ip_link, re.M)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001128 tap = ""
1129 for p in possibles:
1130 lockfile = os.path.join(lockdir, p)
1131 if os.path.exists('%s.skip' % lockfile):
1132 logger.info('Found %s.skip, skipping %s' % (lockfile, p))
1133 continue
Brad Bishop08902b02019-08-20 09:16:51 -04001134 self.taplock = lockfile + '.lock'
1135 if self.acquire_taplock(error=False):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001136 tap = p
1137 logger.info("Using preconfigured tap device %s" % tap)
1138 logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap))
1139 break
1140
1141 if not tap:
1142 if os.path.exists(nosudo_flag):
1143 logger.error("Error: There are no available tap devices to use for networking,")
1144 logger.error("and I see %s exists, so I am not going to try creating" % nosudo_flag)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001145 raise RunQemuError("a new one with sudo.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001146
1147 gid = os.getgid()
1148 uid = os.getuid()
1149 logger.info("Setting up tap interface under sudo")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001150 cmd = ('sudo', self.qemuifup, str(uid), str(gid), self.bindir_native)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001151 try:
1152 tap = subprocess.check_output(cmd).decode('utf-8').strip()
1153 except subprocess.CalledProcessError as e:
1154 logger.error('Setting up tap device failed:\n%s\nRun runqemu-gen-tapdevs to manually create one.' % str(e))
1155 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001156 lockfile = os.path.join(lockdir, tap)
Brad Bishop08902b02019-08-20 09:16:51 -04001157 self.taplock = lockfile + '.lock'
1158 self.acquire_taplock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001159 self.cleantap = True
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001160 logger.debug('Created tap: %s' % tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001161
1162 if not tap:
1163 logger.error("Failed to setup tap device. Run runqemu-gen-tapdevs to manually create.")
Andrew Geissler82c905d2020-04-13 13:39:40 -05001164 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001165 self.tap = tap
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001166 tapnum = int(tap[3:])
1167 gateway = tapnum * 2 + 1
1168 client = gateway + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001169 if self.fstype == 'nfs':
1170 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001171 netconf = " " + self.cmdline_ip_tap
1172 netconf = netconf.replace('@CLIENT@', str(client))
1173 netconf = netconf.replace('@GATEWAY@', str(gateway))
1174 logger.info("Network configuration:%s", netconf)
1175 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001176 mac = "%s%02x" % (self.mac_tap, client)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001177 qb_tap_opt = self.get('QB_TAP_OPT')
1178 if qb_tap_opt:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001179 qemu_tap_opt = qb_tap_opt.replace('@TAP@', tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001180 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001181 qemu_tap_opt = "-netdev tap,id=net0,ifname=%s,script=no,downscript=no" % (self.tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001182
1183 if self.vhost_enabled:
1184 qemu_tap_opt += ',vhost=on'
1185
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001186 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qemu_tap_opt))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001187
1188 def setup_network(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001189 if self.get('QB_NET') == 'none':
1190 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001191 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001192 self.saved_stty = subprocess.check_output(("stty", "-g")).decode('utf-8').strip()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001193 self.network_device = self.get('QB_NETWORK_DEVICE') or self.network_device
Andrew Geissler82c905d2020-04-13 13:39:40 -05001194 if self.net_bridge:
1195 self.setup_net_bridge()
1196 elif self.slirp_enabled:
1197 self.cmdline_ip_slirp = self.get('QB_CMDLINE_IP_SLIRP') or self.cmdline_ip_slirp
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001198 self.setup_slirp()
1199 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001200 self.cmdline_ip_tap = self.get('QB_CMDLINE_IP_TAP') or self.cmdline_ip_tap
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001201 self.setup_tap()
1202
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001203 def setup_rootfs(self):
1204 if self.get('QB_ROOTFS') == 'none':
1205 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001206 if 'wic.' in self.fstype:
1207 self.fstype = self.fstype[4:]
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001208 rootfs_format = self.fstype if self.fstype in ('vmdk', 'vhd', 'vhdx', 'qcow2', 'vdi') else 'raw'
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001209
1210 qb_rootfs_opt = self.get('QB_ROOTFS_OPT')
1211 if qb_rootfs_opt:
1212 self.rootfs_options = qb_rootfs_opt.replace('@ROOTFS@', self.rootfs)
1213 else:
1214 self.rootfs_options = '-drive file=%s,if=virtio,format=%s' % (self.rootfs, rootfs_format)
1215
Andrew Geissler82c905d2020-04-13 13:39:40 -05001216 qb_rootfs_extra_opt = self.get("QB_ROOTFS_EXTRA_OPT")
1217 if qb_rootfs_extra_opt and not qb_rootfs_extra_opt.startswith(","):
1218 qb_rootfs_extra_opt = "," + qb_rootfs_extra_opt
1219
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001220 if self.fstype in ('cpio.gz', 'cpio'):
1221 self.kernel_cmdline = 'root=/dev/ram0 rw debugshell'
1222 self.rootfs_options = '-initrd %s' % self.rootfs
1223 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001224 vm_drive = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001225 if self.fstype in self.vmtypes:
1226 if self.fstype == 'iso':
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001227 vm_drive = '-drive file=%s,if=virtio,media=cdrom' % self.rootfs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001228 elif self.get('QB_DRIVE_TYPE'):
1229 drive_type = self.get('QB_DRIVE_TYPE')
1230 if drive_type.startswith("/dev/sd"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001231 logger.info('Using scsi drive')
Andrew Geissler82c905d2020-04-13 13:39:40 -05001232 vm_drive = '-drive if=none,id=hd,file=%s,format=%s -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd%s' \
1233 % (self.rootfs, rootfs_format, qb_rootfs_extra_opt)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001234 elif drive_type.startswith("/dev/hd"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001235 logger.info('Using ide drive')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001236 vm_drive = "-drive file=%s,format=%s" % (self.rootfs, rootfs_format)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001237 elif drive_type.startswith("/dev/vdb"):
1238 logger.info('Using block virtio drive');
1239 vm_drive = '-drive id=disk0,file=%s,if=none,format=%s -device virtio-blk-device,drive=disk0%s' \
1240 % (self.rootfs, rootfs_format,qb_rootfs_extra_opt)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001241 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001242 # virtio might have been selected explicitly (just use it), or
1243 # is used as fallback (then warn about that).
1244 if not drive_type.startswith("/dev/vd"):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001245 logger.warning("Unknown QB_DRIVE_TYPE: %s" % drive_type)
1246 logger.warning("Failed to figure out drive type, consider define or fix QB_DRIVE_TYPE")
1247 logger.warning('Trying to use virtio block drive')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001248 vm_drive = '-drive if=virtio,file=%s,format=%s' % (self.rootfs, rootfs_format)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001249
1250 # All branches above set vm_drive.
Andrew Geissler475cb722020-07-10 16:00:51 -05001251 self.rootfs_options = vm_drive
1252 if not self.fstype in self.vmtypes:
1253 self.rootfs_options += ' -no-reboot'
Brad Bishopf3f93bb2019-10-16 14:33:32 -04001254 self.kernel_cmdline = 'root=%s rw' % (self.get('QB_KERNEL_ROOT'))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001255
1256 if self.fstype == 'nfs':
1257 self.rootfs_options = ''
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001258 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 -04001259 self.kernel_cmdline = 'root=%s rw' % k_root
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001260
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001261 if self.fstype == 'none':
1262 self.rootfs_options = ''
1263
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001264 self.set('ROOTFS_OPTIONS', self.rootfs_options)
1265
1266 def guess_qb_system(self):
1267 """attempt to determine the appropriate qemu-system binary"""
1268 mach = self.get('MACHINE')
1269 if not mach:
1270 search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*'
1271 if self.rootfs:
1272 match = re.match(search, self.rootfs)
1273 if match:
1274 mach = match.group(1)
1275 elif self.kernel:
1276 match = re.match(search, self.kernel)
1277 if match:
1278 mach = match.group(1)
1279
1280 if not mach:
1281 return None
1282
1283 if mach == 'qemuarm':
1284 qbsys = 'arm'
1285 elif mach == 'qemuarm64':
1286 qbsys = 'aarch64'
1287 elif mach == 'qemux86':
1288 qbsys = 'i386'
1289 elif mach == 'qemux86-64':
1290 qbsys = 'x86_64'
1291 elif mach == 'qemuppc':
1292 qbsys = 'ppc'
1293 elif mach == 'qemumips':
1294 qbsys = 'mips'
1295 elif mach == 'qemumips64':
1296 qbsys = 'mips64'
1297 elif mach == 'qemumipsel':
1298 qbsys = 'mipsel'
1299 elif mach == 'qemumips64el':
1300 qbsys = 'mips64el'
Brad Bishop316dfdd2018-06-25 12:45:53 -04001301 elif mach == 'qemuriscv64':
1302 qbsys = 'riscv64'
1303 elif mach == 'qemuriscv32':
1304 qbsys = 'riscv32'
Brad Bishop004d4992018-10-02 23:54:45 +02001305 else:
1306 logger.error("Unable to determine QEMU PC System emulator for %s machine." % mach)
1307 logger.error("As %s is not among valid QEMU machines such as," % mach)
1308 logger.error("qemux86-64, qemux86, qemuarm64, qemuarm, qemumips64, qemumips64el, qemumipsel, qemumips, qemuppc")
1309 raise RunQemuError("Set qb_system_name with suitable QEMU PC System emulator in .*qemuboot.conf.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001310
1311 return 'qemu-system-%s' % qbsys
1312
Brad Bishop15ae2502019-06-18 21:44:24 -04001313 def check_qemu_system(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001314 qemu_system = self.get('QB_SYSTEM_NAME')
1315 if not qemu_system:
1316 qemu_system = self.guess_qb_system()
1317 if not qemu_system:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001318 raise RunQemuError("Failed to boot, QB_SYSTEM_NAME is NULL!")
Brad Bishop15ae2502019-06-18 21:44:24 -04001319 self.qemu_system = qemu_system
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001320
Brad Bishop15ae2502019-06-18 21:44:24 -04001321 def setup_final(self):
1322 qemu_bin = os.path.join(self.bindir_native, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001323
1324 # It is possible to have qemu-native in ASSUME_PROVIDED, and it won't
1325 # find QEMU in sysroot, it needs to use host's qemu.
1326 if not os.path.exists(qemu_bin):
1327 logger.info("QEMU binary not found in %s, trying host's QEMU" % qemu_bin)
1328 for path in (os.environ['PATH'] or '').split(':'):
Brad Bishop15ae2502019-06-18 21:44:24 -04001329 qemu_bin_tmp = os.path.join(path, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001330 logger.info("Trying: %s" % qemu_bin_tmp)
1331 if os.path.exists(qemu_bin_tmp):
1332 qemu_bin = qemu_bin_tmp
1333 if not os.path.isabs(qemu_bin):
1334 qemu_bin = os.path.abspath(qemu_bin)
1335 logger.info("Using host's QEMU: %s" % qemu_bin)
1336 break
1337
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001338 if not os.access(qemu_bin, os.X_OK):
1339 raise OEPathError("No QEMU binary '%s' could be found" % qemu_bin)
1340
Andrew Geisslerc3d88e42020-10-02 09:45:00 -05001341 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 -05001342
1343 for ovmf in self.ovmf_bios:
1344 format = ovmf.rsplit('.', 1)[-1]
1345 self.qemu_opt += ' -drive if=pflash,format=%s,file=%s' % (format, ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001346
1347 self.qemu_opt += ' ' + self.qemu_opt_script
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001348
Brad Bishop08902b02019-08-20 09:16:51 -04001349 if self.ovmf_secboot_pkkek1:
1350 # Provide the Platform Key and first Key Exchange Key certificate as an
1351 # OEM string in the SMBIOS Type 11 table. Prepend the certificate string
1352 # with "application prefix" of the EnrollDefaultKeys.efi application
1353 self.qemu_opt += ' -smbios type=11,value=4e32566d-8e9e-4f52-81d3-5bb9715f9727:' \
1354 + self.ovmf_secboot_pkkek1
1355
Andrew Geissler99467da2019-02-25 18:54:23 -06001356 # Append qemuparams to override previous settings
1357 if self.qemuparams:
1358 self.qemu_opt += ' ' + self.qemuparams
1359
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001360 if self.snapshot:
1361 self.qemu_opt += " -snapshot"
1362
Brad Bishop19323692019-04-05 15:28:33 -04001363 if self.serialconsole:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001364 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001365 subprocess.check_call(("stty", "intr", "^]"))
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001366 logger.info("Interrupt character is '^]'")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001367
1368 first_serial = ""
1369 if not re.search("-nographic", self.qemu_opt):
1370 first_serial = "-serial mon:vc"
1371 # We always want a ttyS1. Since qemu by default adds a serial
1372 # port when nodefaults is not specified, it seems that all that
1373 # would be needed is to make sure a "-serial" is there. However,
1374 # it appears that when "-serial" is specified, it ignores the
1375 # default serial port that is normally added. So here we make
1376 # sure to add two -serial if there are none. And only one if
1377 # there is one -serial already.
1378 serial_num = len(re.findall("-serial", self.qemu_opt))
1379 if serial_num == 0:
1380 self.qemu_opt += " %s %s" % (first_serial, self.get("QB_SERIAL_OPT"))
1381 elif serial_num == 1:
1382 self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT")
1383
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001384 # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES),
1385 # if not serial or serialtcp options was specified only ttyS0 is created
1386 # and sysvinit shows an error trying to enable ttyS1:
1387 # INIT: Id "S1" respawning too fast: disabled for 5 minutes
1388 serial_num = len(re.findall("-serial", self.qemu_opt))
1389 if serial_num == 0:
Brad Bishop19323692019-04-05 15:28:33 -04001390 if re.search("-nographic", self.qemu_opt) or self.serialstdio:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001391 self.qemu_opt += " -serial mon:stdio -serial null"
1392 else:
1393 self.qemu_opt += " -serial mon:vc -serial null"
1394
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001395 def start_qemu(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001396 import shlex
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001397 if self.kernel:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001398 kernel_opts = "-kernel %s -append '%s %s %s %s'" % (self.kernel, self.kernel_cmdline,
1399 self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'),
1400 self.bootparams)
Brad Bishopc68388fc2019-08-26 01:33:31 -04001401 if self.bios:
1402 kernel_opts += " -bios %s" % self.bios
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001403 if self.dtb:
1404 kernel_opts += " -dtb %s" % self.dtb
1405 else:
1406 kernel_opts = ""
1407 cmd = "%s %s" % (self.qemu_opt, kernel_opts)
Brad Bishop004d4992018-10-02 23:54:45 +02001408 cmds = shlex.split(cmd)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001409 logger.info('Running %s\n' % cmd)
Brad Bishopf86d0552018-12-04 14:18:15 -08001410 pass_fds = []
Brad Bishop08902b02019-08-20 09:16:51 -04001411 if self.taplock_descriptor:
1412 pass_fds = [self.taplock_descriptor.fileno()]
1413 if len(self.portlocks):
1414 for descriptor in self.portlocks.values():
1415 pass_fds.append(descriptor.fileno())
Brad Bishopf86d0552018-12-04 14:18:15 -08001416 process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds)
Brad Bishop004d4992018-10-02 23:54:45 +02001417 self.qemupid = process.pid
1418 retcode = process.wait()
1419 if retcode:
1420 if retcode == -signal.SIGTERM:
1421 logger.info("Qemu terminated by SIGTERM")
1422 else:
1423 logger.error("Failed to run qemu: %s", process.stderr.read().decode())
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001424
1425 def cleanup(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001426 if self.cleaned:
1427 return
1428
1429 # avoid dealing with SIGTERM when cleanup function is running
1430 signal.signal(signal.SIGTERM, signal.SIG_IGN)
1431
1432 logger.info("Cleaning up")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001433 if self.cleantap:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001434 cmd = ('sudo', self.qemuifdown, self.tap, self.bindir_native)
1435 logger.debug('Running %s' % str(cmd))
1436 subprocess.check_call(cmd)
Brad Bishop08902b02019-08-20 09:16:51 -04001437 self.release_taplock()
1438 self.release_portlock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001439
1440 if self.nfs_running:
1441 logger.info("Shutting down the userspace NFS server...")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001442 cmd = ("runqemu-export-rootfs", "stop", self.rootfs)
1443 logger.debug('Running %s' % str(cmd))
1444 subprocess.check_call(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001445
1446 if self.saved_stty:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001447 subprocess.check_call(("stty", self.saved_stty))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001448
1449 if self.clean_nfs_dir:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001450 logger.info('Removing %s' % self.rootfs)
1451 shutil.rmtree(self.rootfs)
1452 shutil.rmtree('%s.pseudo_state' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001453
Brad Bishop004d4992018-10-02 23:54:45 +02001454 self.cleaned = True
1455
Andrew Geissler82c905d2020-04-13 13:39:40 -05001456 def run_bitbake_env(self, mach=None):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001457 bitbake = shutil.which('bitbake')
1458 if not bitbake:
1459 return
1460
1461 if not mach:
1462 mach = self.get('MACHINE')
1463
Andrew Geissler82c905d2020-04-13 13:39:40 -05001464 multiconfig = self.get('MULTICONFIG')
1465 if multiconfig:
1466 multiconfig = "mc:%s" % multiconfig
1467
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001468 if mach:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001469 cmd = 'MACHINE=%s bitbake -e %s' % (mach, multiconfig)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001470 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001471 cmd = 'bitbake -e %s' % multiconfig
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001472
1473 logger.info('Running %s...' % cmd)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001474 return subprocess.check_output(cmd, shell=True).decode('utf-8')
1475
1476 def load_bitbake_env(self, mach=None):
1477 if self.bitbake_e:
1478 return
1479
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001480 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001481 self.bitbake_e = self.run_bitbake_env(mach=mach)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001482 except subprocess.CalledProcessError as err:
1483 self.bitbake_e = ''
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001484 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 -06001485
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001486 def validate_combos(self):
1487 if (self.fstype in self.vmtypes) and self.kernel:
1488 raise RunQemuError("%s doesn't need kernel %s!" % (self.fstype, self.kernel))
1489
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001490 @property
1491 def bindir_native(self):
1492 result = self.get('STAGING_BINDIR_NATIVE')
1493 if result and os.path.exists(result):
1494 return result
1495
Andrew Geissler82c905d2020-04-13 13:39:40 -05001496 cmd = ['bitbake', '-e']
1497 multiconfig = self.get('MULTICONFIG')
1498 if multiconfig:
1499 cmd.append('mc:%s:qemu-helper-native' % multiconfig)
1500 else:
1501 cmd.append('qemu-helper-native')
1502
Brad Bishop977dc1a2019-02-06 16:01:43 -05001503 logger.info('Running %s...' % str(cmd))
1504 out = subprocess.check_output(cmd).decode('utf-8')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001505
1506 match = re.search('^STAGING_BINDIR_NATIVE="(.*)"', out, re.M)
1507 if match:
1508 result = match.group(1)
1509 if os.path.exists(result):
1510 self.set('STAGING_BINDIR_NATIVE', result)
1511 return result
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001512 raise RunQemuError("Native sysroot directory %s doesn't exist" % result)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001513 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001514 raise RunQemuError("Can't find STAGING_BINDIR_NATIVE in '%s' output" % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001515
1516
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001517def main():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001518 if "help" in sys.argv or '-h' in sys.argv or '--help' in sys.argv:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001519 print_usage()
1520 return 0
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001521 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001522 config = BaseConfig()
Brad Bishop004d4992018-10-02 23:54:45 +02001523
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001524 renice = os.path.expanduser("~/bin/runqemu-renice")
1525 if os.path.exists(renice):
1526 logger.info('Using %s to renice' % renice)
1527 subprocess.check_call([renice, str(os.getpid())])
1528
Brad Bishop004d4992018-10-02 23:54:45 +02001529 def sigterm_handler(signum, frame):
1530 logger.info("SIGTERM received")
1531 os.kill(config.qemupid, signal.SIGTERM)
1532 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001533 # Deliberately ignore the return code of 'tput smam'.
1534 subprocess.call(["tput", "smam"])
Brad Bishop004d4992018-10-02 23:54:45 +02001535 signal.signal(signal.SIGTERM, sigterm_handler)
1536
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001537 config.check_args()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001538 config.read_qemuboot()
1539 config.check_and_set()
1540 # Check whether the combos is valid or not
1541 config.validate_combos()
1542 config.print_config()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001543 config.setup_network()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001544 config.setup_rootfs()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001545 config.setup_final()
1546 config.start_qemu()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001547 except RunQemuError as err:
1548 logger.error(err)
1549 return 1
1550 except Exception as err:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001551 import traceback
1552 traceback.print_exc()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001553 return 1
1554 finally:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001555 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001556 # Deliberately ignore the return code of 'tput smam'.
1557 subprocess.call(["tput", "smam"])
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001558
1559if __name__ == "__main__":
1560 sys.exit(main())