blob: 5c108ec23aeb871040a1cc9d9d3ae23bc0f4c954 [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
Andrew Geissler09036742021-06-25 14:25:14 -050021import time
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050022
Brad Bishopd7bf8c12018-02-25 22:55:05 -050023class RunQemuError(Exception):
24 """Custom exception to raise on known errors."""
25 pass
26
27class OEPathError(RunQemuError):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060028 """Custom Exception to give better guidance on missing binaries"""
29 def __init__(self, message):
Brad Bishopd7bf8c12018-02-25 22:55:05 -050030 super().__init__("In order for this script to dynamically infer paths\n \
Patrick Williamsc0f7c042017-02-23 20:41:17 -060031kernels or filesystem images, you either need bitbake in your PATH\n \
32or to source oe-init-build-env before running this script.\n\n \
33Dynamic path inference can be avoided by passing a *.qemuboot.conf to\n \
Brad Bishopd7bf8c12018-02-25 22:55:05 -050034runqemu, i.e. `runqemu /path/to/my-image-name.qemuboot.conf`\n\n %s" % message)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060035
36
37def create_logger():
38 logger = logging.getLogger('runqemu')
39 logger.setLevel(logging.INFO)
40
41 # create console handler and set level to debug
42 ch = logging.StreamHandler()
Brad Bishopd7bf8c12018-02-25 22:55:05 -050043 ch.setLevel(logging.DEBUG)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060044
45 # create formatter
46 formatter = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
47
48 # add formatter to ch
49 ch.setFormatter(formatter)
50
51 # add ch to logger
52 logger.addHandler(ch)
53
54 return logger
55
56logger = create_logger()
57
58def print_usage():
59 print("""
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050060Usage: you can run this script with any valid combination
61of the following environment variables (in any order):
62 KERNEL - the kernel image file to use
Brad Bishopc68388fc2019-08-26 01:33:31 -040063 BIOS - the bios image file to use
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050064 ROOTFS - the rootfs image file or nfsroot directory to use
Brad Bishop316dfdd2018-06-25 12:45:53 -040065 DEVICE_TREE - the device tree blob to use
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050066 MACHINE - the machine name (optional, autodetected from KERNEL filename if unspecified)
67 Simplified QEMU command-line options can be passed with:
Patrick Williamsc0f7c042017-02-23 20:41:17 -060068 nographic - disable video console
Andrew Geissler90fd73c2021-03-05 15:25:55 -060069 novga - Disable VGA emulation completely
Brad Bishopa34c0302019-09-23 22:34:48 -040070 sdl - choose the SDL UI frontend
71 gtk - choose the Gtk UI frontend
Brad Bishop6dbb3162019-11-25 09:41:34 -050072 gl - enable virgl-based GL acceleration (also needs gtk or sdl options)
73 gl-es - enable virgl-based GL acceleration, using OpenGL ES (also needs gtk or sdl options)
74 egl-headless - enable headless EGL output; use vnc (via publicvnc option) or spice to see it
Andrew Geisslerd159c7f2021-09-02 21:05:58 -050075 (hint: if /dev/dri/renderD* is absent due to lack of suitable GPU, 'modprobe vgem' will create
76 one sutable for mesa llvmpipe sofware renderer)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060077 serial - enable a serial console on /dev/ttyS0
Brad Bishop19323692019-04-05 15:28:33 -040078 serialstdio - enable a serial console on the console (regardless of graphics mode)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060079 slirp - enable user networking, no root privileges is required
Brad Bishopa34c0302019-09-23 22:34:48 -040080 snapshot - don't write changes to back to images
Patrick Williamsc0f7c042017-02-23 20:41:17 -060081 kvm - enable KVM when running x86/x86_64 (VT-capable CPU required)
82 kvm-vhost - enable KVM with vhost when running x86/x86_64 (VT-capable CPU required)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050083 publicvnc - enable a VNC server open to all hosts
Patrick Williamsc0f7c042017-02-23 20:41:17 -060084 audio - enable audio
Brad Bishop6e60e8b2018-02-01 10:27:11 -050085 [*/]ovmf* - OVMF firmware file or base name for booting with UEFI
Patrick Williamsc0f7c042017-02-23 20:41:17 -060086 tcpserial=<port> - specify tcp serial port number
Patrick Williamsc0f7c042017-02-23 20:41:17 -060087 qemuparams=<xyz> - specify custom parameters to QEMU
88 bootparams=<xyz> - specify custom kernel parameters during boot
Brad Bishop6e60e8b2018-02-01 10:27:11 -050089 help, -h, --help: print this text
Brad Bishopd7bf8c12018-02-25 22:55:05 -050090 -d, --debug: Enable debug output
Brad Bishop79641f22019-09-10 07:20:22 -040091 -q, --quiet: Hide most output except error messages
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050092
93Examples:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050094 runqemu
Patrick Williamsc0f7c042017-02-23 20:41:17 -060095 runqemu qemuarm
96 runqemu tmp/deploy/images/qemuarm
Brad Bishop6e60e8b2018-02-01 10:27:11 -050097 runqemu tmp/deploy/images/qemux86/<qemuboot.conf>
Patrick Williamsc0f7c042017-02-23 20:41:17 -060098 runqemu qemux86-64 core-image-sato ext4
99 runqemu qemux86-64 wic-image-minimal wic
100 runqemu path/to/bzImage-qemux86.bin path/to/nfsrootdir/ serial
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600101 runqemu qemux86 iso/hddimg/wic.vmdk/wic.vhd/wic.vhdx/wic.qcow2/wic.vdi/ramfs/cpio.gz...
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600102 runqemu qemux86 qemuparams="-m 256"
103 runqemu qemux86 bootparams="psplash=false"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600104 runqemu path/to/<image>-<machine>.wic
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500105 runqemu path/to/<image>-<machine>.wic.vmdk
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600106 runqemu path/to/<image>-<machine>.wic.vhdx
107 runqemu path/to/<image>-<machine>.wic.vhd
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600108""")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500109
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600110def check_tun():
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500111 """Check /dev/net/tun"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600112 dev_tun = '/dev/net/tun'
113 if not os.path.exists(dev_tun):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500114 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 -0500115
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600116 if not os.access(dev_tun, os.W_OK):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500117 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 -0500118
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600119def get_first_file(cmds):
120 """Return first file found in wildcard cmds"""
121 for cmd in cmds:
122 all_files = glob.glob(cmd)
123 if all_files:
124 for f in all_files:
125 if not os.path.isdir(f):
126 return f
127 return ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500128
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600129class BaseConfig(object):
130 def __init__(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500131 # The self.d saved vars from self.set(), part of them are from qemuboot.conf
132 self.d = {'QB_KERNEL_ROOT': '/dev/vda'}
133
134 # Supported env vars, add it here if a var can be got from env,
135 # and don't use os.getenv in the code.
136 self.env_vars = ('MACHINE',
137 'ROOTFS',
138 'KERNEL',
Brad Bishopc68388fc2019-08-26 01:33:31 -0400139 'BIOS',
Brad Bishop316dfdd2018-06-25 12:45:53 -0400140 'DEVICE_TREE',
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500141 'DEPLOY_DIR_IMAGE',
142 'OE_TMPDIR',
143 'OECORE_NATIVE_SYSROOT',
Andrew Geissler82c905d2020-04-13 13:39:40 -0500144 'MULTICONFIG',
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500145 'SERIAL_CONSOLES',
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500146 )
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500147
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600148 self.qemu_opt = ''
149 self.qemu_opt_script = ''
Andrew Geissler99467da2019-02-25 18:54:23 -0600150 self.qemuparams = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600151 self.nfs_server = ''
152 self.rootfs = ''
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500153 # File name(s) of a OVMF firmware file or variable store,
154 # to be added with -drive if=pflash.
155 # Found in the same places as the rootfs, with or without one of
156 # these suffices: qcow2, bin.
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500157 self.ovmf_bios = []
Brad Bishop08902b02019-08-20 09:16:51 -0400158 # When enrolling default Secure Boot keys, the hypervisor
159 # must provide the Platform Key and the first Key Exchange Key
160 # certificate in the Type 11 SMBIOS table.
161 self.ovmf_secboot_pkkek1 = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600162 self.qemuboot = ''
163 self.qbconfload = False
164 self.kernel = ''
Brad Bishopc68388fc2019-08-26 01:33:31 -0400165 self.bios = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600166 self.kernel_cmdline = ''
167 self.kernel_cmdline_script = ''
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500168 self.bootparams = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600169 self.dtb = ''
170 self.fstype = ''
171 self.kvm_enabled = False
172 self.vhost_enabled = False
173 self.slirp_enabled = False
Andrew Geissler82c905d2020-04-13 13:39:40 -0500174 self.net_bridge = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600175 self.nfs_instance = 0
176 self.nfs_running = False
Brad Bishop19323692019-04-05 15:28:33 -0400177 self.serialconsole = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600178 self.serialstdio = False
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500179 self.nographic = False
180 self.sdl = False
181 self.gtk = False
182 self.gl = False
183 self.gl_es = False
184 self.egl_headless = False
185 self.novga = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600186 self.cleantap = False
187 self.saved_stty = ''
188 self.audio_enabled = False
189 self.tcpserial_portnum = ''
Brad Bishop08902b02019-08-20 09:16:51 -0400190 self.taplock = ''
191 self.taplock_descriptor = None
192 self.portlocks = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600193 self.bitbake_e = ''
194 self.snapshot = False
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600195 self.wictypes = ('wic', 'wic.vmdk', 'wic.qcow2', 'wic.vdi', "wic.vhd", "wic.vhdx")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500196 self.fstypes = ('ext2', 'ext3', 'ext4', 'jffs2', 'nfs', 'btrfs',
197 'cpio.gz', 'cpio', 'ramfs', 'tar.bz2', 'tar.gz')
Brad Bishop08902b02019-08-20 09:16:51 -0400198 self.vmtypes = ('hddimg', 'iso')
Brad Bishop15ae2502019-06-18 21:44:24 -0400199 self.fsinfo = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500200 self.network_device = "-device e1000,netdev=net0,mac=@MAC@"
Andrew Geissler82c905d2020-04-13 13:39:40 -0500201 self.cmdline_ip_slirp = "ip=dhcp"
Andrew Geissler595f6302022-01-24 19:11:47 +0000202 self.cmdline_ip_tap = "ip=192.168.7.@CLIENT@::192.168.7.@GATEWAY@:255.255.255.0::eth0:off:8.8.8.8"
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500203 # Use different mac section for tap and slirp to avoid
204 # conflicts, e.g., when one is running with tap, the other is
205 # running with slirp.
206 # The last section is dynamic, which is for avoiding conflicts,
207 # when multiple qemus are running, e.g., when multiple tap or
208 # slirp qemus are running.
209 self.mac_tap = "52:54:00:12:34:"
210 self.mac_slirp = "52:54:00:12:35:"
Brad Bishop004d4992018-10-02 23:54:45 +0200211 # pid of the actual qemu process
212 self.qemupid = None
213 # avoid cleanup twice
214 self.cleaned = False
Andrew Geisslerc926e172021-05-07 16:11:35 -0500215 # Files to cleanup after run
216 self.cleanup_files = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500217
Brad Bishop08902b02019-08-20 09:16:51 -0400218 def acquire_taplock(self, error=True):
219 logger.debug("Acquiring lockfile %s..." % self.taplock)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600220 try:
Brad Bishop08902b02019-08-20 09:16:51 -0400221 self.taplock_descriptor = open(self.taplock, 'w')
222 fcntl.flock(self.taplock_descriptor, fcntl.LOCK_EX|fcntl.LOCK_NB)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600223 except Exception as e:
Brad Bishop08902b02019-08-20 09:16:51 -0400224 msg = "Acquiring lockfile %s failed: %s" % (self.taplock, e)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500225 if error:
226 logger.error(msg)
227 else:
228 logger.info(msg)
Brad Bishop08902b02019-08-20 09:16:51 -0400229 if self.taplock_descriptor:
230 self.taplock_descriptor.close()
231 self.taplock_descriptor = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600232 return False
233 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500234
Brad Bishop08902b02019-08-20 09:16:51 -0400235 def release_taplock(self):
236 if self.taplock_descriptor:
Brad Bishopf86d0552018-12-04 14:18:15 -0800237 logger.debug("Releasing lockfile for tap device '%s'" % self.tap)
Andrew Geissler5f350902021-07-23 13:09:54 -0400238 # We pass the fd to the qemu process and if we unlock here, it would unlock for
239 # that too. Therefore don't unlock, just close
240 # fcntl.flock(self.taplock_descriptor, fcntl.LOCK_UN)
Brad Bishop08902b02019-08-20 09:16:51 -0400241 self.taplock_descriptor.close()
Andrew Geissler5f350902021-07-23 13:09:54 -0400242 # Removing the file is a potential race, don't do that either
243 # os.remove(self.taplock)
Brad Bishop08902b02019-08-20 09:16:51 -0400244 self.taplock_descriptor = None
245
246 def check_free_port(self, host, port, lockdir):
247 """ Check whether the port is free or not """
248 import socket
249 from contextlib import closing
250
251 lockfile = os.path.join(lockdir, str(port) + '.lock')
252 if self.acquire_portlock(lockfile):
253 with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
254 if sock.connect_ex((host, port)) == 0:
255 # Port is open, so not free
256 self.release_portlock(lockfile)
257 return False
258 else:
259 # Port is not open, so free
260 return True
261 else:
262 return False
263
264 def acquire_portlock(self, lockfile):
265 logger.debug("Acquiring lockfile %s..." % lockfile)
266 try:
267 portlock_descriptor = open(lockfile, 'w')
268 self.portlocks.update({lockfile: portlock_descriptor})
269 fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_EX|fcntl.LOCK_NB)
270 except Exception as e:
271 msg = "Acquiring lockfile %s failed: %s" % (lockfile, e)
272 logger.info(msg)
273 if lockfile in self.portlocks.keys() and self.portlocks[lockfile]:
274 self.portlocks[lockfile].close()
275 del self.portlocks[lockfile]
276 return False
277 return True
278
279 def release_portlock(self, lockfile=None):
280 if lockfile != None:
Andrew Geissler5f350902021-07-23 13:09:54 -0400281 logger.debug("Releasing lockfile '%s'" % lockfile)
282 # We pass the fd to the qemu process and if we unlock here, it would unlock for
283 # that too. Therefore don't unlock, just close
284 # fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_UN)
285 self.portlocks[lockfile].close()
286 # Removing the file is a potential race, don't do that either
287 # os.remove(lockfile)
288 del self.portlocks[lockfile]
Brad Bishop08902b02019-08-20 09:16:51 -0400289 elif len(self.portlocks):
290 for lockfile, descriptor in self.portlocks.items():
291 logger.debug("Releasing lockfile '%s'" % lockfile)
Andrew Geissler5f350902021-07-23 13:09:54 -0400292 # We pass the fd to the qemu process and if we unlock here, it would unlock for
293 # that too. Therefore don't unlock, just close
294 # fcntl.flock(descriptor, fcntl.LOCK_UN)
Brad Bishop08902b02019-08-20 09:16:51 -0400295 descriptor.close()
Andrew Geissler5f350902021-07-23 13:09:54 -0400296 # Removing the file is a potential race, don't do that either
297 # os.remove(lockfile)
Brad Bishop08902b02019-08-20 09:16:51 -0400298 self.portlocks = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500299
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600300 def get(self, key):
301 if key in self.d:
302 return self.d.get(key)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500303 elif os.getenv(key):
304 return os.getenv(key)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600305 else:
306 return ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500307
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600308 def set(self, key, value):
309 self.d[key] = value
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500310
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600311 def is_deploy_dir_image(self, p):
312 if os.path.isdir(p):
313 if not re.search('.qemuboot.conf$', '\n'.join(os.listdir(p)), re.M):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500314 logger.debug("Can't find required *.qemuboot.conf in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600315 return False
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500316 if not any(map(lambda name: '-image-' in name, os.listdir(p))):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500317 logger.debug("Can't find *-image-* in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600318 return False
319 return True
320 else:
321 return False
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500322
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600323 def check_arg_fstype(self, fst):
324 """Check and set FSTYPE"""
Brad Bishop15ae2502019-06-18 21:44:24 -0400325 if fst not in self.fstypes + self.vmtypes + self.wictypes:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800326 logger.warning("Maybe unsupported FSTYPE: %s" % fst)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600327 if not self.fstype or self.fstype == fst:
328 if fst == 'ramfs':
329 fst = 'cpio.gz'
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500330 if fst in ('tar.bz2', 'tar.gz'):
331 fst = 'nfs'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600332 self.fstype = fst
333 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500334 raise RunQemuError("Conflicting: FSTYPE %s and %s" % (self.fstype, fst))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500335
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600336 def set_machine_deploy_dir(self, machine, deploy_dir_image):
337 """Set MACHINE and DEPLOY_DIR_IMAGE"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500338 logger.debug('MACHINE: %s' % machine)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600339 self.set("MACHINE", machine)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500340 logger.debug('DEPLOY_DIR_IMAGE: %s' % deploy_dir_image)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600341 self.set("DEPLOY_DIR_IMAGE", deploy_dir_image)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500342
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600343 def check_arg_nfs(self, p):
344 if os.path.isdir(p):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500345 self.rootfs = p
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600346 else:
347 m = re.match('(.*):(.*)', p)
348 self.nfs_server = m.group(1)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500349 self.rootfs = m.group(2)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600350 self.check_arg_fstype('nfs')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500351
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600352 def check_arg_path(self, p):
353 """
354 - Check whether it is <image>.qemuboot.conf or contains <image>.qemuboot.conf
355 - Check whether is a kernel file
356 - Check whether is a image file
357 - Check whether it is a nfs dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500358 - Check whether it is a OVMF flash file
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600359 """
360 if p.endswith('.qemuboot.conf'):
361 self.qemuboot = p
362 self.qbconfload = True
363 elif re.search('\.bin$', p) or re.search('bzImage', p) or \
364 re.search('zImage', p) or re.search('vmlinux', p) or \
365 re.search('fitImage', p) or re.search('uImage', p):
366 self.kernel = p
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500367 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 -0600368 self.rootfs = p
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500369 # Check filename against self.fstypes can hanlde <file>.cpio.gz,
370 # otherwise, its type would be "gz", which is incorrect.
371 fst = ""
372 for t in self.fstypes:
373 if p.endswith(t):
374 fst = t
375 break
376 if not fst:
377 m = re.search('.*\.(.*)$', self.rootfs)
378 if m:
379 fst = m.group(1)
380 if fst:
381 self.check_arg_fstype(fst)
382 qb = re.sub('\.' + fst + "$", '', self.rootfs)
383 qb = '%s%s' % (re.sub('\.rootfs$', '', qb), '.qemuboot.conf')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600384 if os.path.exists(qb):
385 self.qemuboot = qb
386 self.qbconfload = True
387 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800388 logger.warning("%s doesn't exist" % qb)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600389 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500390 raise RunQemuError("Can't find FSTYPE from: %s" % p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500391
392 elif os.path.isdir(p) or re.search(':', p) and re.search('/', p):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600393 if self.is_deploy_dir_image(p):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500394 logger.debug('DEPLOY_DIR_IMAGE: %s' % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600395 self.set("DEPLOY_DIR_IMAGE", p)
396 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500397 logger.debug("Assuming %s is an nfs rootfs" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600398 self.check_arg_nfs(p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500399 elif os.path.basename(p).startswith('ovmf'):
400 self.ovmf_bios.append(p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600401 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500402 raise RunQemuError("Unknown path arg %s" % p)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500403
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600404 def check_arg_machine(self, arg):
405 """Check whether it is a machine"""
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500406 if self.get('MACHINE') == arg:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600407 return
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500408 elif self.get('MACHINE') and self.get('MACHINE') != arg:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500409 raise RunQemuError("Maybe conflicted MACHINE: %s vs %s" % (self.get('MACHINE'), arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500410 elif re.search('/', arg):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500411 raise RunQemuError("Unknown arg: %s" % arg)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500412
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500413 logger.debug('Assuming MACHINE = %s' % arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500414
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600415 # if we're running under testimage, or similarly as a child
416 # of an existing bitbake invocation, we can't invoke bitbake
417 # to validate the MACHINE setting and must assume it's correct...
418 # FIXME: testimage.bbclass exports these two variables into env,
419 # are there other scenarios in which we need to support being
420 # invoked by bitbake?
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500421 deploy = self.get('DEPLOY_DIR_IMAGE')
422 bbchild = deploy and self.get('OE_TMPDIR')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600423 if bbchild:
424 self.set_machine_deploy_dir(arg, deploy)
425 return
426 # also check whether we're running under a sourced toolchain
427 # environment file
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500428 if self.get('OECORE_NATIVE_SYSROOT'):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600429 self.set("MACHINE", arg)
430 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500431
Andrew Geissler82c905d2020-04-13 13:39:40 -0500432 self.bitbake_e = self.run_bitbake_env(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600433 # bitbake -e doesn't report invalid MACHINE as an error, so
434 # let's check DEPLOY_DIR_IMAGE to make sure that it is a valid
435 # MACHINE.
436 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
437 if s:
438 deploy_dir_image = s.group(1)
439 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500440 raise RunQemuError("bitbake -e %s" % self.bitbake_e)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600441 if self.is_deploy_dir_image(deploy_dir_image):
442 self.set_machine_deploy_dir(arg, deploy_dir_image)
443 else:
444 logger.error("%s not a directory valid DEPLOY_DIR_IMAGE" % deploy_dir_image)
445 self.set("MACHINE", arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500446
Andrew Geisslerc182c622020-05-15 14:13:32 -0500447 def set_dri_path(self):
448 # As runqemu can be run within bitbake (when using testimage, for example),
449 # we need to ensure that we run host pkg-config, and that it does not
450 # get mis-directed to native build paths set by bitbake.
451 try:
452 del os.environ['PKG_CONFIG_PATH']
453 del os.environ['PKG_CONFIG_DIR']
454 del os.environ['PKG_CONFIG_LIBDIR']
455 del os.environ['PKG_CONFIG_SYSROOT_DIR']
456 except KeyError:
457 pass
458 try:
459 dripath = subprocess.check_output("PATH=/bin:/usr/bin:$PATH pkg-config --variable=dridriverdir dri", shell=True)
460 except subprocess.CalledProcessError as e:
461 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.")
462 os.environ['LIBGL_DRIVERS_PATH'] = dripath.decode('utf-8').strip()
463
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000464 # This preloads uninative libc pieces and therefore ensures that RPATH/RUNPATH
465 # in host mesa drivers doesn't trick uninative into loading host libc.
466 preload_items = ['libdl.so.2', 'librt.so.1', 'libpthread.so.0']
467 uninative_path = os.path.dirname(self.get("UNINATIVE_LOADER"))
468 if os.path.exists(uninative_path):
469 preload_paths = [os.path.join(uninative_path, i) for i in preload_items]
470 os.environ['LD_PRELOAD'] = " ".join(preload_paths)
471
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600472 def check_args(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500473 for debug in ("-d", "--debug"):
474 if debug in sys.argv:
475 logger.setLevel(logging.DEBUG)
476 sys.argv.remove(debug)
477
478 for quiet in ("-q", "--quiet"):
479 if quiet in sys.argv:
480 logger.setLevel(logging.ERROR)
481 sys.argv.remove(quiet)
482
Andrew Geisslerc182c622020-05-15 14:13:32 -0500483 if 'gl' not in sys.argv[1:] and 'gl-es' not in sys.argv[1:]:
484 os.environ['SDL_RENDER_DRIVER'] = 'software'
Andrew Geissler595f6302022-01-24 19:11:47 +0000485 os.environ['SDL_FRAMEBUFFER_ACCELERATION'] = 'false'
Andrew Geisslerc182c622020-05-15 14:13:32 -0500486
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600487 unknown_arg = ""
488 for arg in sys.argv[1:]:
Brad Bishop15ae2502019-06-18 21:44:24 -0400489 if arg in self.fstypes + self.vmtypes + self.wictypes:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600490 self.check_arg_fstype(arg)
491 elif arg == 'nographic':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500492 self.nographic = True
Brad Bishop19323692019-04-05 15:28:33 -0400493 elif arg == 'sdl':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500494 self.sdl = True
Brad Bishopa34c0302019-09-23 22:34:48 -0400495 elif arg == 'gtk':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500496 self.gtk = True
497 elif arg == 'gl':
498 self.gl = True
499 elif 'gl-es' in sys.argv[1:]:
500 self.gl_es = True
Brad Bishop19323692019-04-05 15:28:33 -0400501 elif arg == 'egl-headless':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500502 self.egl_headless = True
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600503 elif arg == 'novga':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500504 self.novga = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600505 elif arg == 'serial':
Brad Bishop19323692019-04-05 15:28:33 -0400506 self.serialconsole = True
507 elif arg == "serialstdio":
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600508 self.serialstdio = True
509 elif arg == 'audio':
510 logger.info("Enabling audio in qemu")
511 logger.info("Please install sound drivers in linux host")
512 self.audio_enabled = True
513 elif arg == 'kvm':
514 self.kvm_enabled = True
515 elif arg == 'kvm-vhost':
516 self.vhost_enabled = True
517 elif arg == 'slirp':
518 self.slirp_enabled = True
Andrew Geissler82c905d2020-04-13 13:39:40 -0500519 elif arg.startswith('bridge='):
520 self.net_bridge = '%s' % arg[len('bridge='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600521 elif arg == 'snapshot':
522 self.snapshot = True
523 elif arg == 'publicvnc':
524 self.qemu_opt_script += ' -vnc :0'
525 elif arg.startswith('tcpserial='):
Brad Bishop15ae2502019-06-18 21:44:24 -0400526 self.tcpserial_portnum = '%s' % arg[len('tcpserial='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600527 elif arg.startswith('qemuparams='):
Andrew Geissler99467da2019-02-25 18:54:23 -0600528 self.qemuparams = ' %s' % arg[len('qemuparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600529 elif arg.startswith('bootparams='):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500530 self.bootparams = arg[len('bootparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600531 elif os.path.exists(arg) or (re.search(':', arg) and re.search('/', arg)):
532 self.check_arg_path(os.path.abspath(arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500533 elif re.search(r'-image-|-image$', arg):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600534 # Lazy rootfs
535 self.rootfs = arg
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500536 elif arg.startswith('ovmf'):
537 self.ovmf_bios.append(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600538 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500539 # At last, assume it is the MACHINE
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600540 if (not unknown_arg) or unknown_arg == arg:
541 unknown_arg = arg
542 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500543 raise RunQemuError("Can't handle two unknown args: %s %s\n"
544 "Try 'runqemu help' on how to use it" % \
545 (unknown_arg, arg))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600546 # Check to make sure it is a valid machine
Brad Bishopa5c52ff2018-11-23 10:55:50 +1300547 if unknown_arg and self.get('MACHINE') != unknown_arg:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500548 if self.get('DEPLOY_DIR_IMAGE'):
549 machine = os.path.basename(self.get('DEPLOY_DIR_IMAGE'))
550 if unknown_arg == machine:
551 self.set("MACHINE", machine)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500552
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600553 self.check_arg_machine(unknown_arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500554
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500555 if not (self.get('DEPLOY_DIR_IMAGE') or self.qbconfload):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500556 self.load_bitbake_env()
557 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
558 if s:
559 self.set("DEPLOY_DIR_IMAGE", s.group(1))
560
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600561 def check_kvm(self):
562 """Check kvm and kvm-host"""
563 if not (self.kvm_enabled or self.vhost_enabled):
William A. Kennington IIIac69b482021-06-02 12:28:27 -0700564 self.qemu_opt_script += ' %s %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU'), self.get('QB_SMP'))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600565 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500566
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600567 if not self.get('QB_CPU_KVM'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500568 raise RunQemuError("QB_CPU_KVM is NULL, this board doesn't support kvm")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500569
William A. Kennington IIIac69b482021-06-02 12:28:27 -0700570 self.qemu_opt_script += ' %s %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU_KVM'), self.get('QB_SMP'))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600571 yocto_kvm_wiki = "https://wiki.yoctoproject.org/wiki/How_to_enable_KVM_for_Poky_qemu"
572 yocto_paravirt_kvm_wiki = "https://wiki.yoctoproject.org/wiki/Running_an_x86_Yocto_Linux_image_under_QEMU_KVM"
573 dev_kvm = '/dev/kvm'
574 dev_vhost = '/dev/vhost-net'
Brad Bishop15ae2502019-06-18 21:44:24 -0400575 if self.qemu_system.endswith(('i386', 'x86_64')):
576 with open('/proc/cpuinfo', 'r') as f:
577 kvm_cap = re.search('vmx|svm', "".join(f.readlines()))
578 if not kvm_cap:
579 logger.error("You are trying to enable KVM on a cpu without VT support.")
580 logger.error("Remove kvm from the command-line, or refer:")
581 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500582
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600583 if not os.path.exists(dev_kvm):
584 logger.error("Missing KVM device. Have you inserted kvm modules?")
585 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500586 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500587
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600588 if os.access(dev_kvm, os.W_OK|os.R_OK):
589 self.qemu_opt_script += ' -enable-kvm'
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500590 if self.get('MACHINE') == "qemux86":
591 # Workaround for broken APIC window on pre 4.15 host kernels which causes boot hangs
592 # See YOCTO #12301
593 # On 64 bit we use x2apic
594 self.kernel_cmdline_script += " clocksource=kvm-clock hpet=disable noapic nolapic"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600595 else:
596 logger.error("You have no read or write permission on /dev/kvm.")
597 logger.error("Please change the ownership of this file as described at:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500598 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500599
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600600 if self.vhost_enabled:
601 if not os.path.exists(dev_vhost):
602 logger.error("Missing virtio net device. Have you inserted vhost-net module?")
603 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500604 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500605
Andrew Geissler635e0e42020-08-21 15:58:33 -0500606 if not os.access(dev_vhost, os.W_OK|os.R_OK):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600607 logger.error("You have no read or write permission on /dev/vhost-net.")
608 logger.error("Please change the ownership of this file as described at:")
Andrew Geissler635e0e42020-08-21 15:58:33 -0500609 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500610
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600611 def check_fstype(self):
612 """Check and setup FSTYPE"""
613 if not self.fstype:
614 fstype = self.get('QB_DEFAULT_FSTYPE')
615 if fstype:
616 self.fstype = fstype
617 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500618 raise RunQemuError("FSTYPE is NULL!")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500619
Brad Bishop15ae2502019-06-18 21:44:24 -0400620 # parse QB_FSINFO into dict, e.g. { 'wic': ['no-kernel-in-fs', 'a-flag'], 'ext4': ['another-flag']}
621 wic_fs = False
622 qb_fsinfo = self.get('QB_FSINFO')
623 if qb_fsinfo:
624 qb_fsinfo = qb_fsinfo.split()
625 for fsinfo in qb_fsinfo:
626 try:
627 fstype, fsflag = fsinfo.split(':')
628
629 if fstype == 'wic':
630 if fsflag == 'no-kernel-in-fs':
631 wic_fs = True
632 elif fsflag == 'kernel-in-fs':
633 wic_fs = False
634 else:
635 logger.warn('Unknown flag "%s:%s" in QB_FSINFO', fstype, fsflag)
636 continue
637 else:
638 logger.warn('QB_FSINFO is not supported for image type "%s"', fstype)
639 continue
640
641 if fstype in self.fsinfo:
642 self.fsinfo[fstype].append(fsflag)
643 else:
644 self.fsinfo[fstype] = [fsflag]
645 except Exception:
646 logger.error('Invalid parameter "%s" in QB_FSINFO', fsinfo)
647
648 # treat wic images as vmimages (with kernel) or as fsimages (rootfs only)
649 if wic_fs:
650 self.fstypes = self.fstypes + self.wictypes
651 else:
652 self.vmtypes = self.vmtypes + self.wictypes
653
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600654 def check_rootfs(self):
655 """Check and set rootfs"""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500656
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500657 if self.fstype == "none":
658 return
659
660 if self.get('ROOTFS'):
661 if not self.rootfs:
662 self.rootfs = self.get('ROOTFS')
663 elif self.get('ROOTFS') != self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500664 raise RunQemuError("Maybe conflicted ROOTFS: %s vs %s" % (self.get('ROOTFS'), self.rootfs))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500665
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600666 if self.fstype == 'nfs':
667 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500668
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600669 if self.rootfs and not os.path.exists(self.rootfs):
670 # Lazy rootfs
671 self.rootfs = "%s/%s-%s.%s" % (self.get('DEPLOY_DIR_IMAGE'),
672 self.rootfs, self.get('MACHINE'),
673 self.fstype)
674 elif not self.rootfs:
675 cmd_name = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_NAME'), self.fstype)
676 cmd_link = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'), self.fstype)
677 cmds = (cmd_name, cmd_link)
678 self.rootfs = get_first_file(cmds)
679 if not self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500680 raise RunQemuError("Failed to find rootfs: %s or %s" % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500681
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600682 if not os.path.exists(self.rootfs):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500683 raise RunQemuError("Can't find rootfs: %s" % self.rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500684
Brad Bishop08902b02019-08-20 09:16:51 -0400685 def setup_pkkek1(self):
686 """
687 Extract from PEM certificate the Platform Key and first Key
688 Exchange Key certificate string. The hypervisor needs to provide
689 it in the Type 11 SMBIOS table
690 """
691 pemcert = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), 'OvmfPkKek1.pem')
692 try:
693 with open(pemcert, 'r') as pemfile:
694 key = pemfile.read().replace('\n', ''). \
695 replace('-----BEGIN CERTIFICATE-----', ''). \
696 replace('-----END CERTIFICATE-----', '')
697 self.ovmf_secboot_pkkek1 = key
698
699 except FileNotFoundError:
700 raise RunQemuError("Can't open PEM certificate %s " % pemcert)
701
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500702 def check_ovmf(self):
703 """Check and set full path for OVMF firmware and variable file(s)."""
704
705 for index, ovmf in enumerate(self.ovmf_bios):
706 if os.path.exists(ovmf):
707 continue
708 for suffix in ('qcow2', 'bin'):
709 path = '%s/%s.%s' % (self.get('DEPLOY_DIR_IMAGE'), ovmf, suffix)
710 if os.path.exists(path):
711 self.ovmf_bios[index] = path
Brad Bishop08902b02019-08-20 09:16:51 -0400712 if ovmf.endswith('secboot'):
713 self.setup_pkkek1()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500714 break
715 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500716 raise RunQemuError("Can't find OVMF firmware: %s" % ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500717
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600718 def check_kernel(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400719 """Check and set kernel"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600720 # The vm image doesn't need a kernel
721 if self.fstype in self.vmtypes:
722 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500723
Brad Bishop316dfdd2018-06-25 12:45:53 -0400724 # See if the user supplied a KERNEL option
725 if self.get('KERNEL'):
726 self.kernel = self.get('KERNEL')
727
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500728 # QB_DEFAULT_KERNEL is always a full file path
729 kernel_name = os.path.basename(self.get('QB_DEFAULT_KERNEL'))
730
731 # The user didn't want a kernel to be loaded
Brad Bishop316dfdd2018-06-25 12:45:53 -0400732 if kernel_name == "none" and not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500733 return
734
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600735 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
736 if not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500737 kernel_match_name = "%s/%s" % (deploy_dir_image, kernel_name)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600738 kernel_match_link = "%s/%s" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
739 kernel_startswith = "%s/%s*" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
740 cmds = (kernel_match_name, kernel_match_link, kernel_startswith)
741 self.kernel = get_first_file(cmds)
742 if not self.kernel:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500743 raise RunQemuError('KERNEL not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500744
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600745 if not os.path.exists(self.kernel):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500746 raise RunQemuError("KERNEL %s not found" % self.kernel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500747
Brad Bishop316dfdd2018-06-25 12:45:53 -0400748 def check_dtb(self):
749 """Check and set dtb"""
750 # Did the user specify a device tree?
751 if self.get('DEVICE_TREE'):
752 self.dtb = self.get('DEVICE_TREE')
753 if not os.path.exists(self.dtb):
754 raise RunQemuError('Specified DTB not found: %s' % self.dtb)
755 return
756
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600757 dtb = self.get('QB_DTB')
758 if dtb:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400759 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600760 cmd_match = "%s/%s" % (deploy_dir_image, dtb)
761 cmd_startswith = "%s/%s*" % (deploy_dir_image, dtb)
762 cmd_wild = "%s/*.dtb" % deploy_dir_image
763 cmds = (cmd_match, cmd_startswith, cmd_wild)
764 self.dtb = get_first_file(cmds)
765 if not os.path.exists(self.dtb):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500766 raise RunQemuError('DTB not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500767
Brad Bishopc68388fc2019-08-26 01:33:31 -0400768 def check_bios(self):
769 """Check and set bios"""
770
771 # See if the user supplied a BIOS option
772 if self.get('BIOS'):
773 self.bios = self.get('BIOS')
774
775 # QB_DEFAULT_BIOS is always a full file path
776 bios_name = os.path.basename(self.get('QB_DEFAULT_BIOS'))
777
778 # The user didn't want a bios to be loaded
779 if (bios_name == "" or bios_name == "none") and not self.bios:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600780 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500781
Brad Bishopc68388fc2019-08-26 01:33:31 -0400782 if not self.bios:
783 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
784 self.bios = "%s/%s" % (deploy_dir_image, bios_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500785
Brad Bishopc68388fc2019-08-26 01:33:31 -0400786 if not self.bios:
787 raise RunQemuError('BIOS not found: %s' % bios_match_name)
788
789 if not os.path.exists(self.bios):
Patrick Williams213cb262021-08-07 19:21:33 -0500790 raise RunQemuError("BIOS %s not found" % self.bios)
Brad Bishopc68388fc2019-08-26 01:33:31 -0400791
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500792
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600793 def check_mem(self):
Andrew Geissler99467da2019-02-25 18:54:23 -0600794 """
795 Both qemu and kernel needs memory settings, so check QB_MEM and set it
796 for both.
797 """
798 s = re.search('-m +([0-9]+)', self.qemuparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600799 if s:
800 self.set('QB_MEM', '-m %s' % s.group(1))
801 elif not self.get('QB_MEM'):
Brad Bishop79641f22019-09-10 07:20:22 -0400802 logger.info('QB_MEM is not set, use 256M by default')
803 self.set('QB_MEM', '-m 256')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500804
Andrew Geissler99467da2019-02-25 18:54:23 -0600805 # Check and remove M or m suffix
806 qb_mem = self.get('QB_MEM')
807 if qb_mem.endswith('M') or qb_mem.endswith('m'):
808 qb_mem = qb_mem[:-1]
809
810 # Add -m prefix it not present
811 if not qb_mem.startswith('-m'):
812 qb_mem = '-m %s' % qb_mem
813
814 self.set('QB_MEM', qb_mem)
815
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800816 mach = self.get('MACHINE')
817 if not mach.startswith('qemumips'):
818 self.kernel_cmdline_script += ' mem=%s' % self.get('QB_MEM').replace('-m','').strip() + 'M'
819
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600820 self.qemu_opt_script += ' %s' % self.get('QB_MEM')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500821
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600822 def check_tcpserial(self):
823 if self.tcpserial_portnum:
Brad Bishop15ae2502019-06-18 21:44:24 -0400824 ports = self.tcpserial_portnum.split(':')
825 port = ports[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600826 if self.get('QB_TCPSERIAL_OPT'):
Brad Bishop15ae2502019-06-18 21:44:24 -0400827 self.qemu_opt_script += ' ' + self.get('QB_TCPSERIAL_OPT').replace('@PORT@', port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600828 else:
Brad Bishop15ae2502019-06-18 21:44:24 -0400829 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
830
831 if len(ports) > 1:
832 for port in ports[1:]:
833 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500834
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600835 def check_and_set(self):
836 """Check configs sanity and set when needed"""
837 self.validate_paths()
Andrew Geissler82c905d2020-04-13 13:39:40 -0500838 if not self.slirp_enabled and not self.net_bridge:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500839 check_tun()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600840 # Check audio
841 if self.audio_enabled:
842 if not self.get('QB_AUDIO_DRV'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500843 raise RunQemuError("QB_AUDIO_DRV is NULL, this board doesn't support audio")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600844 if not self.get('QB_AUDIO_OPT'):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800845 logger.warning('QB_AUDIO_OPT is NULL, you may need define it to make audio work')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600846 else:
847 self.qemu_opt_script += ' %s' % self.get('QB_AUDIO_OPT')
848 os.putenv('QEMU_AUDIO_DRV', self.get('QB_AUDIO_DRV'))
849 else:
850 os.putenv('QEMU_AUDIO_DRV', 'none')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500851
Brad Bishop15ae2502019-06-18 21:44:24 -0400852 self.check_qemu_system()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600853 self.check_kvm()
854 self.check_fstype()
855 self.check_rootfs()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500856 self.check_ovmf()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600857 self.check_kernel()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400858 self.check_dtb()
Brad Bishopc68388fc2019-08-26 01:33:31 -0400859 self.check_bios()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600860 self.check_mem()
861 self.check_tcpserial()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500862
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600863 def read_qemuboot(self):
864 if not self.qemuboot:
865 if self.get('DEPLOY_DIR_IMAGE'):
866 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600867 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800868 logger.warning("Can't find qemuboot conf file, DEPLOY_DIR_IMAGE is NULL!")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600869 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500870
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600871 if self.rootfs and not os.path.exists(self.rootfs):
872 # Lazy rootfs
873 machine = self.get('MACHINE')
874 if not machine:
875 machine = os.path.basename(deploy_dir_image)
876 self.qemuboot = "%s/%s-%s.qemuboot.conf" % (deploy_dir_image,
877 self.rootfs, machine)
878 else:
879 cmd = 'ls -t %s/*.qemuboot.conf' % deploy_dir_image
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500880 logger.debug('Running %s...' % cmd)
881 try:
882 qbs = subprocess.check_output(cmd, shell=True).decode('utf-8')
883 except subprocess.CalledProcessError as err:
884 raise RunQemuError(err)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600885 if qbs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500886 for qb in qbs.split():
887 # Don't use initramfs when other choices unless fstype is ramfs
888 if '-initramfs-' in os.path.basename(qb) and self.fstype != 'cpio.gz':
889 continue
890 self.qemuboot = qb
891 break
892 if not self.qemuboot:
893 # Use the first one when no choice
894 self.qemuboot = qbs.split()[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600895 self.qbconfload = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500896
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600897 if not self.qemuboot:
898 # If we haven't found a .qemuboot.conf at this point it probably
899 # doesn't exist, continue without
900 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500901
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600902 if not os.path.exists(self.qemuboot):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500903 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 -0500904
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500905 logger.debug('CONFFILE: %s' % self.qemuboot)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500906
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600907 cf = configparser.ConfigParser()
908 cf.read(self.qemuboot)
909 for k, v in cf.items('config_bsp'):
910 k_upper = k.upper()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500911 if v.startswith("../"):
912 v = os.path.abspath(os.path.dirname(self.qemuboot) + "/" + v)
913 elif v == ".":
914 v = os.path.dirname(self.qemuboot)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600915 self.set(k_upper, v)
916
917 def validate_paths(self):
918 """Ensure all relevant path variables are set"""
919 # When we're started with a *.qemuboot.conf arg assume that image
920 # artefacts are relative to that file, rather than in whatever
921 # directory DEPLOY_DIR_IMAGE in the conf file points to.
922 if self.qbconfload:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500923 imgdir = os.path.realpath(os.path.dirname(self.qemuboot))
924 if imgdir != os.path.realpath(self.get('DEPLOY_DIR_IMAGE')):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600925 logger.info('Setting DEPLOY_DIR_IMAGE to folder containing %s (%s)' % (self.qemuboot, imgdir))
926 self.set('DEPLOY_DIR_IMAGE', imgdir)
927
928 # If the STAGING_*_NATIVE directories from the config file don't exist
929 # and we're in a sourced OE build directory try to extract the paths
930 # from `bitbake -e`
931 havenative = os.path.exists(self.get('STAGING_DIR_NATIVE')) and \
932 os.path.exists(self.get('STAGING_BINDIR_NATIVE'))
933
934 if not havenative:
935 if not self.bitbake_e:
936 self.load_bitbake_env()
937
938 if self.bitbake_e:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500939 native_vars = ['STAGING_DIR_NATIVE']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600940 for nv in native_vars:
941 s = re.search('^%s="(.*)"' % nv, self.bitbake_e, re.M)
942 if s and s.group(1) != self.get(nv):
943 logger.info('Overriding conf file setting of %s to %s from Bitbake environment' % (nv, s.group(1)))
944 self.set(nv, s.group(1))
945 else:
946 # when we're invoked from a running bitbake instance we won't
947 # be able to call `bitbake -e`, then try:
948 # - get OE_TMPDIR from environment and guess paths based on it
949 # - get OECORE_NATIVE_SYSROOT from environment (for sdk)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500950 tmpdir = self.get('OE_TMPDIR')
951 oecore_native_sysroot = self.get('OECORE_NATIVE_SYSROOT')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600952 if tmpdir:
953 logger.info('Setting STAGING_DIR_NATIVE and STAGING_BINDIR_NATIVE relative to OE_TMPDIR (%s)' % tmpdir)
954 hostos, _, _, _, machine = os.uname()
955 buildsys = '%s-%s' % (machine, hostos.lower())
956 staging_dir_native = '%s/sysroots/%s' % (tmpdir, buildsys)
957 self.set('STAGING_DIR_NATIVE', staging_dir_native)
958 elif oecore_native_sysroot:
959 logger.info('Setting STAGING_DIR_NATIVE to OECORE_NATIVE_SYSROOT (%s)' % oecore_native_sysroot)
960 self.set('STAGING_DIR_NATIVE', oecore_native_sysroot)
961 if self.get('STAGING_DIR_NATIVE'):
962 # we have to assume that STAGING_BINDIR_NATIVE is at usr/bin
963 staging_bindir_native = '%s/usr/bin' % self.get('STAGING_DIR_NATIVE')
964 logger.info('Setting STAGING_BINDIR_NATIVE to %s' % staging_bindir_native)
965 self.set('STAGING_BINDIR_NATIVE', '%s/usr/bin' % self.get('STAGING_DIR_NATIVE'))
966
967 def print_config(self):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500968 logoutput = ['Continuing with the following parameters:']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600969 if not self.fstype in self.vmtypes:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500970 logoutput.append('KERNEL: [%s]' % self.kernel)
Brad Bishopc68388fc2019-08-26 01:33:31 -0400971 if self.bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500972 logoutput.append('BIOS: [%s]' % self.bios)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600973 if self.dtb:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500974 logoutput.append('DTB: [%s]' % self.dtb)
975 logoutput.append('MACHINE: [%s]' % self.get('MACHINE'))
Brad Bishop15ae2502019-06-18 21:44:24 -0400976 try:
977 fstype_flags = ' (' + ', '.join(self.fsinfo[self.fstype]) + ')'
978 except KeyError:
979 fstype_flags = ''
Andrew Geissler82c905d2020-04-13 13:39:40 -0500980 logoutput.append('FSTYPE: [%s%s]' % (self.fstype, fstype_flags))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600981 if self.fstype == 'nfs':
Andrew Geissler82c905d2020-04-13 13:39:40 -0500982 logoutput.append('NFS_DIR: [%s]' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600983 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500984 logoutput.append('ROOTFS: [%s]' % self.rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500985 if self.ovmf_bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500986 logoutput.append('OVMF: %s' % self.ovmf_bios)
Brad Bishop08902b02019-08-20 09:16:51 -0400987 if (self.ovmf_secboot_pkkek1):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500988 logoutput.append('SECBOOT PKKEK1: [%s...]' % self.ovmf_secboot_pkkek1[0:100])
989 logoutput.append('CONFFILE: [%s]' % self.qemuboot)
990 logoutput.append('')
991 logger.info('\n'.join(logoutput))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600992
993 def setup_nfs(self):
994 if not self.nfs_server:
995 if self.slirp_enabled:
996 self.nfs_server = '10.0.2.2'
997 else:
998 self.nfs_server = '192.168.7.1'
999
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001000 # Figure out a new nfs_instance to allow multiple qemus running.
Brad Bishop977dc1a2019-02-06 16:01:43 -05001001 ps = subprocess.check_output(("ps", "auxww")).decode('utf-8')
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001002 pattern = '/bin/unfsd .* -i .*\.pid -e .*/exports([0-9]+) '
1003 all_instances = re.findall(pattern, ps, re.M)
1004 if all_instances:
1005 all_instances.sort(key=int)
1006 self.nfs_instance = int(all_instances.pop()) + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001007
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001008 nfsd_port = 3049 + 2 * self.nfs_instance
1009 mountd_port = 3048 + 2 * self.nfs_instance
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001010
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001011 # Export vars for runqemu-export-rootfs
1012 export_dict = {
1013 'NFS_INSTANCE': self.nfs_instance,
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001014 'NFSD_PORT': nfsd_port,
1015 'MOUNTD_PORT': mountd_port,
1016 }
1017 for k, v in export_dict.items():
1018 # Use '%s' since they are integers
1019 os.putenv(k, '%s' % v)
1020
Andrew Geissler82c905d2020-04-13 13:39:40 -05001021 self.unfs_opts="nfsvers=3,port=%s,tcp,mountport=%s" % (nfsd_port, mountd_port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001022
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001023 # Extract .tar.bz2 or .tar.bz if no nfs dir
1024 if not (self.rootfs and os.path.isdir(self.rootfs)):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001025 src_prefix = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'))
1026 dest = "%s-nfsroot" % src_prefix
1027 if os.path.exists('%s.pseudo_state' % dest):
1028 logger.info('Use %s as NFS_DIR' % dest)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001029 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001030 else:
1031 src = ""
1032 src1 = '%s.tar.bz2' % src_prefix
1033 src2 = '%s.tar.gz' % src_prefix
1034 if os.path.exists(src1):
1035 src = src1
1036 elif os.path.exists(src2):
1037 src = src2
1038 if not src:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001039 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 -06001040 logger.info('NFS_DIR not found, extracting %s to %s' % (src, dest))
Brad Bishop977dc1a2019-02-06 16:01:43 -05001041 cmd = ('runqemu-extract-sdk', src, dest)
1042 logger.info('Running %s...' % str(cmd))
1043 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001044 raise RunQemuError('Failed to run %s' % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001045 self.rootfs = dest
Andrew Geisslerc926e172021-05-07 16:11:35 -05001046 self.cleanup_files.append(self.rootfs)
1047 self.cleanup_files.append('%s.pseudo_state' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001048
1049 # Start the userspace NFS server
Brad Bishop977dc1a2019-02-06 16:01:43 -05001050 cmd = ('runqemu-export-rootfs', 'start', self.rootfs)
1051 logger.info('Running %s...' % str(cmd))
1052 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001053 raise RunQemuError('Failed to run %s' % cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001054
1055 self.nfs_running = True
1056
Andrew Geissler82c905d2020-04-13 13:39:40 -05001057 def setup_net_bridge(self):
1058 self.set('NETWORK_CMD', '-netdev bridge,br=%s,id=net0,helper=%s -device virtio-net-pci,netdev=net0 ' % (
1059 self.net_bridge, os.path.join(self.bindir_native, 'qemu-oe-bridge-helper')))
1060
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001061 def setup_slirp(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001062 """Setup user networking"""
1063
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001064 if self.fstype == 'nfs':
1065 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001066 netconf = " " + self.cmdline_ip_slirp
1067 logger.info("Network configuration:%s", netconf)
1068 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001069 # Port mapping
1070 hostfwd = ",hostfwd=tcp::2222-:22,hostfwd=tcp::2323-:23"
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001071 qb_slirp_opt_default = "-netdev user,id=net0%s,tftp=%s" % (hostfwd, self.get('DEPLOY_DIR_IMAGE'))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001072 qb_slirp_opt = self.get('QB_SLIRP_OPT') or qb_slirp_opt_default
1073 # Figure out the port
1074 ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt)
1075 ports = [int(i) for i in ports]
1076 mac = 2
Brad Bishop08902b02019-08-20 09:16:51 -04001077
1078 lockdir = "/tmp/qemu-port-locks"
1079 if not os.path.exists(lockdir):
1080 # There might be a race issue when multi runqemu processess are
1081 # running at the same time.
1082 try:
1083 os.mkdir(lockdir)
1084 os.chmod(lockdir, 0o777)
1085 except FileExistsError:
1086 pass
1087
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001088 # Find a free port to avoid conflicts
1089 for p in ports[:]:
1090 p_new = p
Brad Bishop08902b02019-08-20 09:16:51 -04001091 while not self.check_free_port('localhost', p_new, lockdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001092 p_new += 1
1093 mac += 1
1094 while p_new in ports:
1095 p_new += 1
1096 mac += 1
1097 if p != p_new:
1098 ports.append(p_new)
1099 qb_slirp_opt = re.sub(':%s-' % p, ':%s-' % p_new, qb_slirp_opt)
1100 logger.info("Port forward changed: %s -> %s" % (p, p_new))
1101 mac = "%s%02x" % (self.mac_slirp, mac)
1102 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qb_slirp_opt))
1103 # Print out port foward
1104 hostfwd = re.findall('(hostfwd=[^,]*)', qb_slirp_opt)
1105 if hostfwd:
1106 logger.info('Port forward: %s' % ' '.join(hostfwd))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001107
1108 def setup_tap(self):
1109 """Setup tap"""
1110
1111 # This file is created when runqemu-gen-tapdevs creates a bank of tap
1112 # devices, indicating that the user should not bring up new ones using
1113 # sudo.
1114 nosudo_flag = '/etc/runqemu-nosudo'
1115 self.qemuifup = shutil.which('runqemu-ifup')
1116 self.qemuifdown = shutil.which('runqemu-ifdown')
1117 ip = shutil.which('ip')
1118 lockdir = "/tmp/qemu-tap-locks"
1119
1120 if not (self.qemuifup and self.qemuifdown and ip):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001121 logger.error("runqemu-ifup: %s" % self.qemuifup)
1122 logger.error("runqemu-ifdown: %s" % self.qemuifdown)
1123 logger.error("ip: %s" % ip)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001124 raise OEPathError("runqemu-ifup, runqemu-ifdown or ip not found")
1125
1126 if not os.path.exists(lockdir):
1127 # There might be a race issue when multi runqemu processess are
1128 # running at the same time.
1129 try:
1130 os.mkdir(lockdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001131 os.chmod(lockdir, 0o777)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001132 except FileExistsError:
1133 pass
1134
Brad Bishop977dc1a2019-02-06 16:01:43 -05001135 cmd = (ip, 'link')
1136 logger.debug('Running %s...' % str(cmd))
1137 ip_link = subprocess.check_output(cmd).decode('utf-8')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001138 # Matches line like: 6: tap0: <foo>
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001139 possibles = re.findall('^[0-9]+: +(tap[0-9]+): <.*', ip_link, re.M)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001140 tap = ""
1141 for p in possibles:
1142 lockfile = os.path.join(lockdir, p)
1143 if os.path.exists('%s.skip' % lockfile):
1144 logger.info('Found %s.skip, skipping %s' % (lockfile, p))
1145 continue
Brad Bishop08902b02019-08-20 09:16:51 -04001146 self.taplock = lockfile + '.lock'
1147 if self.acquire_taplock(error=False):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001148 tap = p
1149 logger.info("Using preconfigured tap device %s" % tap)
1150 logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap))
1151 break
1152
1153 if not tap:
1154 if os.path.exists(nosudo_flag):
1155 logger.error("Error: There are no available tap devices to use for networking,")
1156 logger.error("and I see %s exists, so I am not going to try creating" % nosudo_flag)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001157 raise RunQemuError("a new one with sudo.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001158
1159 gid = os.getgid()
1160 uid = os.getuid()
1161 logger.info("Setting up tap interface under sudo")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001162 cmd = ('sudo', self.qemuifup, str(uid), str(gid), self.bindir_native)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001163 try:
1164 tap = subprocess.check_output(cmd).decode('utf-8').strip()
1165 except subprocess.CalledProcessError as e:
1166 logger.error('Setting up tap device failed:\n%s\nRun runqemu-gen-tapdevs to manually create one.' % str(e))
1167 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001168 lockfile = os.path.join(lockdir, tap)
Brad Bishop08902b02019-08-20 09:16:51 -04001169 self.taplock = lockfile + '.lock'
1170 self.acquire_taplock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001171 self.cleantap = True
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001172 logger.debug('Created tap: %s' % tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001173
1174 if not tap:
1175 logger.error("Failed to setup tap device. Run runqemu-gen-tapdevs to manually create.")
Andrew Geissler82c905d2020-04-13 13:39:40 -05001176 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001177 self.tap = tap
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001178 tapnum = int(tap[3:])
1179 gateway = tapnum * 2 + 1
1180 client = gateway + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001181 if self.fstype == 'nfs':
1182 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001183 netconf = " " + self.cmdline_ip_tap
1184 netconf = netconf.replace('@CLIENT@', str(client))
1185 netconf = netconf.replace('@GATEWAY@', str(gateway))
1186 logger.info("Network configuration:%s", netconf)
1187 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001188 mac = "%s%02x" % (self.mac_tap, client)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001189 qb_tap_opt = self.get('QB_TAP_OPT')
1190 if qb_tap_opt:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001191 qemu_tap_opt = qb_tap_opt.replace('@TAP@', tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001192 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001193 qemu_tap_opt = "-netdev tap,id=net0,ifname=%s,script=no,downscript=no" % (self.tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001194
1195 if self.vhost_enabled:
1196 qemu_tap_opt += ',vhost=on'
1197
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001198 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qemu_tap_opt))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001199
1200 def setup_network(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001201 if self.get('QB_NET') == 'none':
1202 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001203 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001204 self.saved_stty = subprocess.check_output(("stty", "-g")).decode('utf-8').strip()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001205 self.network_device = self.get('QB_NETWORK_DEVICE') or self.network_device
Andrew Geissler82c905d2020-04-13 13:39:40 -05001206 if self.net_bridge:
1207 self.setup_net_bridge()
1208 elif self.slirp_enabled:
1209 self.cmdline_ip_slirp = self.get('QB_CMDLINE_IP_SLIRP') or self.cmdline_ip_slirp
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001210 self.setup_slirp()
1211 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001212 self.cmdline_ip_tap = self.get('QB_CMDLINE_IP_TAP') or self.cmdline_ip_tap
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001213 self.setup_tap()
1214
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001215 def setup_rootfs(self):
1216 if self.get('QB_ROOTFS') == 'none':
1217 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001218 if 'wic.' in self.fstype:
1219 self.fstype = self.fstype[4:]
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001220 rootfs_format = self.fstype if self.fstype in ('vmdk', 'vhd', 'vhdx', 'qcow2', 'vdi') else 'raw'
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001221
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001222 tmpfsdir = os.environ.get("RUNQEMU_TMPFS_DIR", None)
1223 if self.snapshot and tmpfsdir:
1224 newrootfs = os.path.join(tmpfsdir, os.path.basename(self.rootfs)) + "." + str(os.getpid())
Andrew Geissler09036742021-06-25 14:25:14 -05001225 logger.info("Copying rootfs to %s" % newrootfs)
1226 copy_start = time.time()
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001227 shutil.copyfile(self.rootfs, newrootfs)
Andrew Geissler09036742021-06-25 14:25:14 -05001228 logger.info("Copy done in %s seconds" % (time.time() - copy_start))
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001229 self.rootfs = newrootfs
1230 # Don't need a second copy now!
1231 self.snapshot = False
Andrew Geisslerc926e172021-05-07 16:11:35 -05001232 self.cleanup_files.append(newrootfs)
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001233
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001234 qb_rootfs_opt = self.get('QB_ROOTFS_OPT')
1235 if qb_rootfs_opt:
1236 self.rootfs_options = qb_rootfs_opt.replace('@ROOTFS@', self.rootfs)
1237 else:
1238 self.rootfs_options = '-drive file=%s,if=virtio,format=%s' % (self.rootfs, rootfs_format)
1239
Andrew Geissler82c905d2020-04-13 13:39:40 -05001240 qb_rootfs_extra_opt = self.get("QB_ROOTFS_EXTRA_OPT")
1241 if qb_rootfs_extra_opt and not qb_rootfs_extra_opt.startswith(","):
1242 qb_rootfs_extra_opt = "," + qb_rootfs_extra_opt
1243
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001244 if self.fstype in ('cpio.gz', 'cpio'):
1245 self.kernel_cmdline = 'root=/dev/ram0 rw debugshell'
1246 self.rootfs_options = '-initrd %s' % self.rootfs
1247 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001248 vm_drive = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001249 if self.fstype in self.vmtypes:
1250 if self.fstype == 'iso':
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001251 vm_drive = '-drive file=%s,if=virtio,media=cdrom' % self.rootfs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001252 elif self.get('QB_DRIVE_TYPE'):
1253 drive_type = self.get('QB_DRIVE_TYPE')
1254 if drive_type.startswith("/dev/sd"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001255 logger.info('Using scsi drive')
Andrew Geissler82c905d2020-04-13 13:39:40 -05001256 vm_drive = '-drive if=none,id=hd,file=%s,format=%s -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd%s' \
1257 % (self.rootfs, rootfs_format, qb_rootfs_extra_opt)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001258 elif drive_type.startswith("/dev/hd"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001259 logger.info('Using ide drive')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001260 vm_drive = "-drive file=%s,format=%s" % (self.rootfs, rootfs_format)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001261 elif drive_type.startswith("/dev/vdb"):
1262 logger.info('Using block virtio drive');
1263 vm_drive = '-drive id=disk0,file=%s,if=none,format=%s -device virtio-blk-device,drive=disk0%s' \
1264 % (self.rootfs, rootfs_format,qb_rootfs_extra_opt)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001265 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001266 # virtio might have been selected explicitly (just use it), or
1267 # is used as fallback (then warn about that).
1268 if not drive_type.startswith("/dev/vd"):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001269 logger.warning("Unknown QB_DRIVE_TYPE: %s" % drive_type)
1270 logger.warning("Failed to figure out drive type, consider define or fix QB_DRIVE_TYPE")
1271 logger.warning('Trying to use virtio block drive')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001272 vm_drive = '-drive if=virtio,file=%s,format=%s' % (self.rootfs, rootfs_format)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001273
1274 # All branches above set vm_drive.
Andrew Geissler475cb722020-07-10 16:00:51 -05001275 self.rootfs_options = vm_drive
1276 if not self.fstype in self.vmtypes:
1277 self.rootfs_options += ' -no-reboot'
Andrew Geissler595f6302022-01-24 19:11:47 +00001278
1279 # By default, ' rw' is appended to QB_KERNEL_ROOT unless either ro or rw is explicitly passed.
1280 qb_kernel_root = self.get('QB_KERNEL_ROOT')
1281 qb_kernel_root_l = qb_kernel_root.split()
1282 if not ('ro' in qb_kernel_root_l or 'rw' in qb_kernel_root_l):
1283 qb_kernel_root += ' rw'
1284 self.kernel_cmdline = 'root=%s' % qb_kernel_root
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001285
1286 if self.fstype == 'nfs':
1287 self.rootfs_options = ''
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001288 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 -04001289 self.kernel_cmdline = 'root=%s rw' % k_root
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001290
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001291 if self.fstype == 'none':
1292 self.rootfs_options = ''
1293
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001294 self.set('ROOTFS_OPTIONS', self.rootfs_options)
1295
1296 def guess_qb_system(self):
1297 """attempt to determine the appropriate qemu-system binary"""
1298 mach = self.get('MACHINE')
1299 if not mach:
1300 search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*'
1301 if self.rootfs:
1302 match = re.match(search, self.rootfs)
1303 if match:
1304 mach = match.group(1)
1305 elif self.kernel:
1306 match = re.match(search, self.kernel)
1307 if match:
1308 mach = match.group(1)
1309
1310 if not mach:
1311 return None
1312
1313 if mach == 'qemuarm':
1314 qbsys = 'arm'
1315 elif mach == 'qemuarm64':
1316 qbsys = 'aarch64'
1317 elif mach == 'qemux86':
1318 qbsys = 'i386'
1319 elif mach == 'qemux86-64':
1320 qbsys = 'x86_64'
1321 elif mach == 'qemuppc':
1322 qbsys = 'ppc'
1323 elif mach == 'qemumips':
1324 qbsys = 'mips'
1325 elif mach == 'qemumips64':
1326 qbsys = 'mips64'
1327 elif mach == 'qemumipsel':
1328 qbsys = 'mipsel'
1329 elif mach == 'qemumips64el':
1330 qbsys = 'mips64el'
Brad Bishop316dfdd2018-06-25 12:45:53 -04001331 elif mach == 'qemuriscv64':
1332 qbsys = 'riscv64'
1333 elif mach == 'qemuriscv32':
1334 qbsys = 'riscv32'
Brad Bishop004d4992018-10-02 23:54:45 +02001335 else:
1336 logger.error("Unable to determine QEMU PC System emulator for %s machine." % mach)
1337 logger.error("As %s is not among valid QEMU machines such as," % mach)
1338 logger.error("qemux86-64, qemux86, qemuarm64, qemuarm, qemumips64, qemumips64el, qemumipsel, qemumips, qemuppc")
1339 raise RunQemuError("Set qb_system_name with suitable QEMU PC System emulator in .*qemuboot.conf.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001340
1341 return 'qemu-system-%s' % qbsys
1342
Brad Bishop15ae2502019-06-18 21:44:24 -04001343 def check_qemu_system(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001344 qemu_system = self.get('QB_SYSTEM_NAME')
1345 if not qemu_system:
1346 qemu_system = self.guess_qb_system()
1347 if not qemu_system:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001348 raise RunQemuError("Failed to boot, QB_SYSTEM_NAME is NULL!")
Brad Bishop15ae2502019-06-18 21:44:24 -04001349 self.qemu_system = qemu_system
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001350
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001351 def setup_vga(self):
1352 if self.nographic == True:
1353 if self.sdl == True:
1354 raise RunQemuError('Option nographic makes no sense alongside the sdl option.')
1355 if self.gtk == True:
1356 raise RunQemuError('Option nographic makes no sense alongside the gtk option.')
1357 self.qemu_opt += ' -nographic'
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001358
1359 if self.novga == True:
1360 self.qemu_opt += ' -vga none'
1361 return
1362
1363 if (self.gl_es == True or self.gl == True) and (self.sdl == False and self.gtk == False):
1364 raise RunQemuError('Option gl/gl-es needs gtk or sdl option.')
1365
1366 if self.sdl == True or self.gtk == True or self.egl_headless == True:
Andrew Geissler595f6302022-01-24 19:11:47 +00001367 if self.gl or self.gl_es or self.egl_headless:
1368 self.qemu_opt += ' -device virtio-vga-gl '
1369 else:
1370 self.qemu_opt += ' -device virtio-vga '
1371
1372 self.qemu_opt += '-display '
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001373 if self.egl_headless == True:
Andrew Geissler595f6302022-01-24 19:11:47 +00001374 self.set_dri_path()
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001375 self.qemu_opt += 'egl-headless,'
1376 else:
1377 if self.sdl == True:
1378 self.qemu_opt += 'sdl,'
1379 elif self.gtk == True:
Andrew Geissler7e0e3c02022-02-25 20:34:39 +00001380 os.environ['FONTCONFIG_PATH'] = '/etc/fonts'
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001381 self.qemu_opt += 'gtk,'
1382
1383 if self.gl == True:
Andrew Geissler595f6302022-01-24 19:11:47 +00001384 self.set_dri_path()
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001385 self.qemu_opt += 'gl=on,'
1386 elif self.gl_es == True:
Andrew Geissler595f6302022-01-24 19:11:47 +00001387 self.set_dri_path()
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001388 self.qemu_opt += 'gl=es,'
1389 self.qemu_opt += 'show-cursor=on'
1390
1391 self.qemu_opt += ' %s' %self.get('QB_GRAPHICS')
1392
1393 def setup_serial(self):
1394 # Setup correct kernel command line for serial
Andrew Geissler595f6302022-01-24 19:11:47 +00001395 if self.get('SERIAL_CONSOLES') and (self.serialstdio == True or self.serialconsole == True or self.nographic == True or self.tcpserial_portnum):
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001396 for entry in self.get('SERIAL_CONSOLES').split(' '):
1397 self.kernel_cmdline_script += ' console=%s' %entry.split(';')[1]
1398
1399 if self.serialstdio == True or self.nographic == True:
1400 self.qemu_opt += " -serial mon:stdio"
1401 else:
1402 self.qemu_opt += " -serial mon:vc"
1403 if self.serialconsole:
1404 if sys.stdin.isatty():
1405 subprocess.check_call(("stty", "intr", "^]"))
1406 logger.info("Interrupt character is '^]'")
1407
1408 self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT")
1409
1410 # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES).
1411 # If no serial or serialtcp options were specified, only ttyS0 is created
1412 # and sysvinit shows an error trying to enable ttyS1:
1413 # INIT: Id "S1" respawning too fast: disabled for 5 minutes
1414 serial_num = len(re.findall("-serial", self.qemu_opt))
1415 if serial_num < 2:
1416 self.qemu_opt += " -serial null"
1417
Brad Bishop15ae2502019-06-18 21:44:24 -04001418 def setup_final(self):
1419 qemu_bin = os.path.join(self.bindir_native, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001420
1421 # It is possible to have qemu-native in ASSUME_PROVIDED, and it won't
1422 # find QEMU in sysroot, it needs to use host's qemu.
1423 if not os.path.exists(qemu_bin):
1424 logger.info("QEMU binary not found in %s, trying host's QEMU" % qemu_bin)
1425 for path in (os.environ['PATH'] or '').split(':'):
Brad Bishop15ae2502019-06-18 21:44:24 -04001426 qemu_bin_tmp = os.path.join(path, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001427 logger.info("Trying: %s" % qemu_bin_tmp)
1428 if os.path.exists(qemu_bin_tmp):
1429 qemu_bin = qemu_bin_tmp
1430 if not os.path.isabs(qemu_bin):
1431 qemu_bin = os.path.abspath(qemu_bin)
1432 logger.info("Using host's QEMU: %s" % qemu_bin)
1433 break
1434
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001435 if not os.access(qemu_bin, os.X_OK):
1436 raise OEPathError("No QEMU binary '%s' could be found" % qemu_bin)
1437
Andrew Geisslereff27472021-10-29 15:35:00 -05001438 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').replace('@DEPLOY_DIR_IMAGE@', self.get('DEPLOY_DIR_IMAGE')))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001439
1440 for ovmf in self.ovmf_bios:
1441 format = ovmf.rsplit('.', 1)[-1]
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001442 if format == "bin":
1443 format = "raw"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001444 self.qemu_opt += ' -drive if=pflash,format=%s,file=%s' % (format, ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001445
1446 self.qemu_opt += ' ' + self.qemu_opt_script
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001447
Brad Bishop08902b02019-08-20 09:16:51 -04001448 if self.ovmf_secboot_pkkek1:
1449 # Provide the Platform Key and first Key Exchange Key certificate as an
1450 # OEM string in the SMBIOS Type 11 table. Prepend the certificate string
1451 # with "application prefix" of the EnrollDefaultKeys.efi application
1452 self.qemu_opt += ' -smbios type=11,value=4e32566d-8e9e-4f52-81d3-5bb9715f9727:' \
1453 + self.ovmf_secboot_pkkek1
1454
Andrew Geissler99467da2019-02-25 18:54:23 -06001455 # Append qemuparams to override previous settings
1456 if self.qemuparams:
1457 self.qemu_opt += ' ' + self.qemuparams
1458
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001459 if self.snapshot:
1460 self.qemu_opt += " -snapshot"
1461
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001462 self.setup_serial()
1463 self.setup_vga()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001464
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001465 def start_qemu(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001466 import shlex
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001467 if self.kernel:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001468 kernel_opts = "-kernel %s -append '%s %s %s %s'" % (self.kernel, self.kernel_cmdline,
1469 self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'),
1470 self.bootparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001471 if self.dtb:
1472 kernel_opts += " -dtb %s" % self.dtb
1473 else:
1474 kernel_opts = ""
Patrick Williams213cb262021-08-07 19:21:33 -05001475
1476 if self.bios:
1477 self.qemu_opt += " -bios %s" % self.bios
1478
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001479 cmd = "%s %s" % (self.qemu_opt, kernel_opts)
Brad Bishop004d4992018-10-02 23:54:45 +02001480 cmds = shlex.split(cmd)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001481 logger.info('Running %s\n' % cmd)
Brad Bishopf86d0552018-12-04 14:18:15 -08001482 pass_fds = []
Brad Bishop08902b02019-08-20 09:16:51 -04001483 if self.taplock_descriptor:
1484 pass_fds = [self.taplock_descriptor.fileno()]
1485 if len(self.portlocks):
1486 for descriptor in self.portlocks.values():
1487 pass_fds.append(descriptor.fileno())
Brad Bishopf86d0552018-12-04 14:18:15 -08001488 process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds)
Brad Bishop004d4992018-10-02 23:54:45 +02001489 self.qemupid = process.pid
1490 retcode = process.wait()
1491 if retcode:
1492 if retcode == -signal.SIGTERM:
1493 logger.info("Qemu terminated by SIGTERM")
1494 else:
1495 logger.error("Failed to run qemu: %s", process.stderr.read().decode())
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001496
1497 def cleanup(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001498 if self.cleaned:
1499 return
1500
1501 # avoid dealing with SIGTERM when cleanup function is running
1502 signal.signal(signal.SIGTERM, signal.SIG_IGN)
1503
1504 logger.info("Cleaning up")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001505 if self.cleantap:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001506 cmd = ('sudo', self.qemuifdown, self.tap, self.bindir_native)
1507 logger.debug('Running %s' % str(cmd))
1508 subprocess.check_call(cmd)
Brad Bishop08902b02019-08-20 09:16:51 -04001509 self.release_taplock()
1510 self.release_portlock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001511
1512 if self.nfs_running:
1513 logger.info("Shutting down the userspace NFS server...")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001514 cmd = ("runqemu-export-rootfs", "stop", self.rootfs)
1515 logger.debug('Running %s' % str(cmd))
1516 subprocess.check_call(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001517
1518 if self.saved_stty:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001519 subprocess.check_call(("stty", self.saved_stty))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001520
Andrew Geisslerc926e172021-05-07 16:11:35 -05001521 if self.cleanup_files:
1522 for ent in self.cleanup_files:
1523 logger.info('Removing %s' % ent)
1524 if os.path.isfile(ent):
1525 os.remove(ent)
1526 else:
1527 shutil.rmtree(ent)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001528
Brad Bishop004d4992018-10-02 23:54:45 +02001529 self.cleaned = True
1530
Andrew Geissler82c905d2020-04-13 13:39:40 -05001531 def run_bitbake_env(self, mach=None):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001532 bitbake = shutil.which('bitbake')
1533 if not bitbake:
1534 return
1535
1536 if not mach:
1537 mach = self.get('MACHINE')
1538
Andrew Geissler82c905d2020-04-13 13:39:40 -05001539 multiconfig = self.get('MULTICONFIG')
1540 if multiconfig:
1541 multiconfig = "mc:%s" % multiconfig
1542
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001543 if mach:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001544 cmd = 'MACHINE=%s bitbake -e %s' % (mach, multiconfig)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001545 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001546 cmd = 'bitbake -e %s' % multiconfig
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001547
1548 logger.info('Running %s...' % cmd)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001549 return subprocess.check_output(cmd, shell=True).decode('utf-8')
1550
1551 def load_bitbake_env(self, mach=None):
1552 if self.bitbake_e:
1553 return
1554
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001555 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001556 self.bitbake_e = self.run_bitbake_env(mach=mach)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001557 except subprocess.CalledProcessError as err:
1558 self.bitbake_e = ''
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001559 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 -06001560
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001561 def validate_combos(self):
1562 if (self.fstype in self.vmtypes) and self.kernel:
1563 raise RunQemuError("%s doesn't need kernel %s!" % (self.fstype, self.kernel))
1564
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001565 @property
1566 def bindir_native(self):
1567 result = self.get('STAGING_BINDIR_NATIVE')
1568 if result and os.path.exists(result):
1569 return result
1570
Andrew Geissler82c905d2020-04-13 13:39:40 -05001571 cmd = ['bitbake', '-e']
1572 multiconfig = self.get('MULTICONFIG')
1573 if multiconfig:
1574 cmd.append('mc:%s:qemu-helper-native' % multiconfig)
1575 else:
1576 cmd.append('qemu-helper-native')
1577
Brad Bishop977dc1a2019-02-06 16:01:43 -05001578 logger.info('Running %s...' % str(cmd))
1579 out = subprocess.check_output(cmd).decode('utf-8')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001580
1581 match = re.search('^STAGING_BINDIR_NATIVE="(.*)"', out, re.M)
1582 if match:
1583 result = match.group(1)
1584 if os.path.exists(result):
1585 self.set('STAGING_BINDIR_NATIVE', result)
1586 return result
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001587 raise RunQemuError("Native sysroot directory %s doesn't exist" % result)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001588 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001589 raise RunQemuError("Can't find STAGING_BINDIR_NATIVE in '%s' output" % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001590
1591
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001592def main():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001593 if "help" in sys.argv or '-h' in sys.argv or '--help' in sys.argv:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001594 print_usage()
1595 return 0
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001596 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001597 config = BaseConfig()
Brad Bishop004d4992018-10-02 23:54:45 +02001598
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001599 renice = os.path.expanduser("~/bin/runqemu-renice")
1600 if os.path.exists(renice):
1601 logger.info('Using %s to renice' % renice)
1602 subprocess.check_call([renice, str(os.getpid())])
1603
Brad Bishop004d4992018-10-02 23:54:45 +02001604 def sigterm_handler(signum, frame):
1605 logger.info("SIGTERM received")
Andrew Geissler595f6302022-01-24 19:11:47 +00001606 if config.qemupid:
1607 os.kill(config.qemupid, signal.SIGTERM)
Brad Bishop004d4992018-10-02 23:54:45 +02001608 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001609 # Deliberately ignore the return code of 'tput smam'.
1610 subprocess.call(["tput", "smam"])
Brad Bishop004d4992018-10-02 23:54:45 +02001611 signal.signal(signal.SIGTERM, sigterm_handler)
1612
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001613 config.check_args()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001614 config.read_qemuboot()
1615 config.check_and_set()
1616 # Check whether the combos is valid or not
1617 config.validate_combos()
1618 config.print_config()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001619 config.setup_network()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001620 config.setup_rootfs()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001621 config.setup_final()
1622 config.start_qemu()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001623 except RunQemuError as err:
1624 logger.error(err)
1625 return 1
1626 except Exception as err:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001627 import traceback
1628 traceback.print_exc()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001629 return 1
1630 finally:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001631 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001632 # Deliberately ignore the return code of 'tput smam'.
1633 subprocess.call(["tput", "smam"])
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001634
1635if __name__ == "__main__":
1636 sys.exit(main())