blob: 09b0ad5ed5bd4bac9e1dd4bd15f60614ae535da9 [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
Andrew Geissler9aee5002022-03-30 16:27:02 +000076 one suitable for mesa llvmpipe software 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)
Andrew Geissler9aee5002022-03-30 16:27:02 +000079 slirp - enable user networking, no root privilege is required
80 snapshot - don't write changes 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
Andrew Geisslerfc113ea2023-03-31 09:59:46 -050085 guestagent - enable guest agent communication
Brad Bishop6e60e8b2018-02-01 10:27:11 -050086 [*/]ovmf* - OVMF firmware file or base name for booting with UEFI
Patrick Williamsc0f7c042017-02-23 20:41:17 -060087 tcpserial=<port> - specify tcp serial port number
Patrick Williamsc0f7c042017-02-23 20:41:17 -060088 qemuparams=<xyz> - specify custom parameters to QEMU
89 bootparams=<xyz> - specify custom kernel parameters during boot
Brad Bishop6e60e8b2018-02-01 10:27:11 -050090 help, -h, --help: print this text
Brad Bishopd7bf8c12018-02-25 22:55:05 -050091 -d, --debug: Enable debug output
Brad Bishop79641f22019-09-10 07:20:22 -040092 -q, --quiet: Hide most output except error messages
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050093
94Examples:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050095 runqemu
Patrick Williamsc0f7c042017-02-23 20:41:17 -060096 runqemu qemuarm
97 runqemu tmp/deploy/images/qemuarm
Brad Bishop6e60e8b2018-02-01 10:27:11 -050098 runqemu tmp/deploy/images/qemux86/<qemuboot.conf>
Patrick Williamsc0f7c042017-02-23 20:41:17 -060099 runqemu qemux86-64 core-image-sato ext4
100 runqemu qemux86-64 wic-image-minimal wic
101 runqemu path/to/bzImage-qemux86.bin path/to/nfsrootdir/ serial
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600102 runqemu qemux86 iso/hddimg/wic.vmdk/wic.vhd/wic.vhdx/wic.qcow2/wic.vdi/ramfs/cpio.gz...
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600103 runqemu qemux86 qemuparams="-m 256"
104 runqemu qemux86 bootparams="psplash=false"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600105 runqemu path/to/<image>-<machine>.wic
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500106 runqemu path/to/<image>-<machine>.wic.vmdk
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600107 runqemu path/to/<image>-<machine>.wic.vhdx
108 runqemu path/to/<image>-<machine>.wic.vhd
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600109""")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500110
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600111def check_tun():
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500112 """Check /dev/net/tun"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600113 dev_tun = '/dev/net/tun'
114 if not os.path.exists(dev_tun):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500115 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 -0500116
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600117 if not os.access(dev_tun, os.W_OK):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500118 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 -0500119
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500120def get_first_file(globs):
121 """Return first file found in wildcard globs"""
122 for g in globs:
123 all_files = glob.glob(g)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600124 if all_files:
125 for f in all_files:
126 if not os.path.isdir(f):
127 return f
128 return ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500129
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600130class BaseConfig(object):
131 def __init__(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500132 # The self.d saved vars from self.set(), part of them are from qemuboot.conf
133 self.d = {'QB_KERNEL_ROOT': '/dev/vda'}
134
135 # Supported env vars, add it here if a var can be got from env,
136 # and don't use os.getenv in the code.
137 self.env_vars = ('MACHINE',
138 'ROOTFS',
139 'KERNEL',
Brad Bishopc68388fc2019-08-26 01:33:31 -0400140 'BIOS',
Brad Bishop316dfdd2018-06-25 12:45:53 -0400141 'DEVICE_TREE',
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500142 'DEPLOY_DIR_IMAGE',
143 'OE_TMPDIR',
144 'OECORE_NATIVE_SYSROOT',
Andrew Geissler82c905d2020-04-13 13:39:40 -0500145 'MULTICONFIG',
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500146 'SERIAL_CONSOLES',
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500147 )
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500148
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600149 self.qemu_opt = ''
150 self.qemu_opt_script = ''
Andrew Geissler99467da2019-02-25 18:54:23 -0600151 self.qemuparams = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600152 self.nfs_server = ''
153 self.rootfs = ''
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500154 # File name(s) of a OVMF firmware file or variable store,
155 # to be added with -drive if=pflash.
156 # Found in the same places as the rootfs, with or without one of
157 # these suffices: qcow2, bin.
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500158 self.ovmf_bios = []
Brad Bishop08902b02019-08-20 09:16:51 -0400159 # When enrolling default Secure Boot keys, the hypervisor
160 # must provide the Platform Key and the first Key Exchange Key
161 # certificate in the Type 11 SMBIOS table.
162 self.ovmf_secboot_pkkek1 = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600163 self.qemuboot = ''
164 self.qbconfload = False
165 self.kernel = ''
Brad Bishopc68388fc2019-08-26 01:33:31 -0400166 self.bios = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600167 self.kernel_cmdline = ''
168 self.kernel_cmdline_script = ''
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500169 self.bootparams = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600170 self.dtb = ''
171 self.fstype = ''
172 self.kvm_enabled = False
173 self.vhost_enabled = False
174 self.slirp_enabled = False
Andrew Geissler82c905d2020-04-13 13:39:40 -0500175 self.net_bridge = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600176 self.nfs_instance = 0
177 self.nfs_running = False
Brad Bishop19323692019-04-05 15:28:33 -0400178 self.serialconsole = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600179 self.serialstdio = False
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500180 self.nographic = False
181 self.sdl = False
182 self.gtk = False
183 self.gl = False
184 self.gl_es = False
185 self.egl_headless = False
Patrick Williams03907ee2022-05-01 06:28:52 -0500186 self.publicvnc = False
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500187 self.novga = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600188 self.cleantap = False
189 self.saved_stty = ''
190 self.audio_enabled = False
191 self.tcpserial_portnum = ''
Brad Bishop08902b02019-08-20 09:16:51 -0400192 self.taplock = ''
193 self.taplock_descriptor = None
194 self.portlocks = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600195 self.bitbake_e = ''
196 self.snapshot = False
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600197 self.wictypes = ('wic', 'wic.vmdk', 'wic.qcow2', 'wic.vdi', "wic.vhd", "wic.vhdx")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500198 self.fstypes = ('ext2', 'ext3', 'ext4', 'jffs2', 'nfs', 'btrfs',
199 'cpio.gz', 'cpio', 'ramfs', 'tar.bz2', 'tar.gz')
Brad Bishop08902b02019-08-20 09:16:51 -0400200 self.vmtypes = ('hddimg', 'iso')
Brad Bishop15ae2502019-06-18 21:44:24 -0400201 self.fsinfo = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500202 self.network_device = "-device e1000,netdev=net0,mac=@MAC@"
Andrew Geissler82c905d2020-04-13 13:39:40 -0500203 self.cmdline_ip_slirp = "ip=dhcp"
Andrew Geissler595f6302022-01-24 19:11:47 +0000204 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 -0500205 # Use different mac section for tap and slirp to avoid
206 # conflicts, e.g., when one is running with tap, the other is
207 # running with slirp.
208 # The last section is dynamic, which is for avoiding conflicts,
209 # when multiple qemus are running, e.g., when multiple tap or
210 # slirp qemus are running.
211 self.mac_tap = "52:54:00:12:34:"
212 self.mac_slirp = "52:54:00:12:35:"
Brad Bishop004d4992018-10-02 23:54:45 +0200213 # pid of the actual qemu process
Patrick Williams2390b1b2022-11-03 13:47:49 -0500214 self.qemu_environ = os.environ.copy()
Andrew Geissler6aa7eec2023-03-03 12:41:14 -0600215 self.qemuprocess = None
Brad Bishop004d4992018-10-02 23:54:45 +0200216 # avoid cleanup twice
217 self.cleaned = False
Andrew Geisslerc926e172021-05-07 16:11:35 -0500218 # Files to cleanup after run
219 self.cleanup_files = []
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500220 self.guest_agent = False
221 self.guest_agent_sockpath = '/tmp/qga.sock'
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500222
Brad Bishop08902b02019-08-20 09:16:51 -0400223 def acquire_taplock(self, error=True):
224 logger.debug("Acquiring lockfile %s..." % self.taplock)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600225 try:
Brad Bishop08902b02019-08-20 09:16:51 -0400226 self.taplock_descriptor = open(self.taplock, 'w')
227 fcntl.flock(self.taplock_descriptor, fcntl.LOCK_EX|fcntl.LOCK_NB)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600228 except Exception as e:
Brad Bishop08902b02019-08-20 09:16:51 -0400229 msg = "Acquiring lockfile %s failed: %s" % (self.taplock, e)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500230 if error:
231 logger.error(msg)
232 else:
233 logger.info(msg)
Brad Bishop08902b02019-08-20 09:16:51 -0400234 if self.taplock_descriptor:
235 self.taplock_descriptor.close()
236 self.taplock_descriptor = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600237 return False
238 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500239
Brad Bishop08902b02019-08-20 09:16:51 -0400240 def release_taplock(self):
241 if self.taplock_descriptor:
Brad Bishopf86d0552018-12-04 14:18:15 -0800242 logger.debug("Releasing lockfile for tap device '%s'" % self.tap)
Andrew Geissler5f350902021-07-23 13:09:54 -0400243 # We pass the fd to the qemu process and if we unlock here, it would unlock for
244 # that too. Therefore don't unlock, just close
245 # fcntl.flock(self.taplock_descriptor, fcntl.LOCK_UN)
Brad Bishop08902b02019-08-20 09:16:51 -0400246 self.taplock_descriptor.close()
Andrew Geissler5f350902021-07-23 13:09:54 -0400247 # Removing the file is a potential race, don't do that either
248 # os.remove(self.taplock)
Brad Bishop08902b02019-08-20 09:16:51 -0400249 self.taplock_descriptor = None
250
251 def check_free_port(self, host, port, lockdir):
252 """ Check whether the port is free or not """
253 import socket
254 from contextlib import closing
255
256 lockfile = os.path.join(lockdir, str(port) + '.lock')
257 if self.acquire_portlock(lockfile):
258 with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
259 if sock.connect_ex((host, port)) == 0:
260 # Port is open, so not free
261 self.release_portlock(lockfile)
262 return False
263 else:
264 # Port is not open, so free
265 return True
266 else:
267 return False
268
269 def acquire_portlock(self, lockfile):
270 logger.debug("Acquiring lockfile %s..." % lockfile)
271 try:
272 portlock_descriptor = open(lockfile, 'w')
273 self.portlocks.update({lockfile: portlock_descriptor})
274 fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_EX|fcntl.LOCK_NB)
275 except Exception as e:
276 msg = "Acquiring lockfile %s failed: %s" % (lockfile, e)
277 logger.info(msg)
278 if lockfile in self.portlocks.keys() and self.portlocks[lockfile]:
279 self.portlocks[lockfile].close()
280 del self.portlocks[lockfile]
281 return False
282 return True
283
284 def release_portlock(self, lockfile=None):
285 if lockfile != None:
Andrew Geissler5f350902021-07-23 13:09:54 -0400286 logger.debug("Releasing lockfile '%s'" % lockfile)
287 # We pass the fd to the qemu process and if we unlock here, it would unlock for
288 # that too. Therefore don't unlock, just close
289 # fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_UN)
290 self.portlocks[lockfile].close()
291 # Removing the file is a potential race, don't do that either
292 # os.remove(lockfile)
293 del self.portlocks[lockfile]
Brad Bishop08902b02019-08-20 09:16:51 -0400294 elif len(self.portlocks):
295 for lockfile, descriptor in self.portlocks.items():
296 logger.debug("Releasing lockfile '%s'" % lockfile)
Andrew Geissler5f350902021-07-23 13:09:54 -0400297 # We pass the fd to the qemu process and if we unlock here, it would unlock for
298 # that too. Therefore don't unlock, just close
299 # fcntl.flock(descriptor, fcntl.LOCK_UN)
Brad Bishop08902b02019-08-20 09:16:51 -0400300 descriptor.close()
Andrew Geissler5f350902021-07-23 13:09:54 -0400301 # Removing the file is a potential race, don't do that either
302 # os.remove(lockfile)
Brad Bishop08902b02019-08-20 09:16:51 -0400303 self.portlocks = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500304
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600305 def get(self, key):
306 if key in self.d:
307 return self.d.get(key)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500308 elif os.getenv(key):
309 return os.getenv(key)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600310 else:
311 return ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500312
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600313 def set(self, key, value):
314 self.d[key] = value
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500315
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600316 def is_deploy_dir_image(self, p):
317 if os.path.isdir(p):
318 if not re.search('.qemuboot.conf$', '\n'.join(os.listdir(p)), re.M):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500319 logger.debug("Can't find required *.qemuboot.conf in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600320 return False
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500321 if not any(map(lambda name: '-image-' in name, os.listdir(p))):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500322 logger.debug("Can't find *-image-* in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600323 return False
324 return True
325 else:
326 return False
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500327
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600328 def check_arg_fstype(self, fst):
329 """Check and set FSTYPE"""
Brad Bishop15ae2502019-06-18 21:44:24 -0400330 if fst not in self.fstypes + self.vmtypes + self.wictypes:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800331 logger.warning("Maybe unsupported FSTYPE: %s" % fst)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600332 if not self.fstype or self.fstype == fst:
333 if fst == 'ramfs':
334 fst = 'cpio.gz'
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500335 if fst in ('tar.bz2', 'tar.gz'):
336 fst = 'nfs'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600337 self.fstype = fst
338 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500339 raise RunQemuError("Conflicting: FSTYPE %s and %s" % (self.fstype, fst))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500340
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600341 def set_machine_deploy_dir(self, machine, deploy_dir_image):
342 """Set MACHINE and DEPLOY_DIR_IMAGE"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500343 logger.debug('MACHINE: %s' % machine)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600344 self.set("MACHINE", machine)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500345 logger.debug('DEPLOY_DIR_IMAGE: %s' % deploy_dir_image)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600346 self.set("DEPLOY_DIR_IMAGE", deploy_dir_image)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500347
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600348 def check_arg_nfs(self, p):
349 if os.path.isdir(p):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500350 self.rootfs = p
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600351 else:
352 m = re.match('(.*):(.*)', p)
353 self.nfs_server = m.group(1)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500354 self.rootfs = m.group(2)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600355 self.check_arg_fstype('nfs')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500356
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600357 def check_arg_path(self, p):
358 """
359 - Check whether it is <image>.qemuboot.conf or contains <image>.qemuboot.conf
Andrew Geissler9aee5002022-03-30 16:27:02 +0000360 - Check whether it is a kernel file
361 - Check whether it is an image file
362 - Check whether it is an NFS dir
363 - Check whether it is an OVMF flash file
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600364 """
365 if p.endswith('.qemuboot.conf'):
366 self.qemuboot = p
367 self.qbconfload = True
368 elif re.search('\.bin$', p) or re.search('bzImage', p) or \
369 re.search('zImage', p) or re.search('vmlinux', p) or \
370 re.search('fitImage', p) or re.search('uImage', p):
371 self.kernel = p
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500372 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 -0600373 self.rootfs = p
Andrew Geissler9aee5002022-03-30 16:27:02 +0000374 # Check filename against self.fstypes can handle <file>.cpio.gz,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500375 # otherwise, its type would be "gz", which is incorrect.
376 fst = ""
377 for t in self.fstypes:
378 if p.endswith(t):
379 fst = t
380 break
381 if not fst:
382 m = re.search('.*\.(.*)$', self.rootfs)
383 if m:
384 fst = m.group(1)
385 if fst:
386 self.check_arg_fstype(fst)
387 qb = re.sub('\.' + fst + "$", '', self.rootfs)
388 qb = '%s%s' % (re.sub('\.rootfs$', '', qb), '.qemuboot.conf')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600389 if os.path.exists(qb):
390 self.qemuboot = qb
391 self.qbconfload = True
392 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800393 logger.warning("%s doesn't exist" % qb)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600394 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500395 raise RunQemuError("Can't find FSTYPE from: %s" % p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500396
397 elif os.path.isdir(p) or re.search(':', p) and re.search('/', p):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600398 if self.is_deploy_dir_image(p):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500399 logger.debug('DEPLOY_DIR_IMAGE: %s' % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600400 self.set("DEPLOY_DIR_IMAGE", p)
401 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500402 logger.debug("Assuming %s is an nfs rootfs" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600403 self.check_arg_nfs(p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500404 elif os.path.basename(p).startswith('ovmf'):
405 self.ovmf_bios.append(p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600406 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500407 raise RunQemuError("Unknown path arg %s" % p)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500408
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600409 def check_arg_machine(self, arg):
410 """Check whether it is a machine"""
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500411 if self.get('MACHINE') == arg:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600412 return
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500413 elif self.get('MACHINE') and self.get('MACHINE') != arg:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500414 raise RunQemuError("Maybe conflicted MACHINE: %s vs %s" % (self.get('MACHINE'), arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500415 elif re.search('/', arg):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500416 raise RunQemuError("Unknown arg: %s" % arg)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500417
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500418 logger.debug('Assuming MACHINE = %s' % arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500419
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600420 # if we're running under testimage, or similarly as a child
421 # of an existing bitbake invocation, we can't invoke bitbake
422 # to validate the MACHINE setting and must assume it's correct...
423 # FIXME: testimage.bbclass exports these two variables into env,
424 # are there other scenarios in which we need to support being
425 # invoked by bitbake?
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500426 deploy = self.get('DEPLOY_DIR_IMAGE')
427 bbchild = deploy and self.get('OE_TMPDIR')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600428 if bbchild:
429 self.set_machine_deploy_dir(arg, deploy)
430 return
431 # also check whether we're running under a sourced toolchain
432 # environment file
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500433 if self.get('OECORE_NATIVE_SYSROOT'):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600434 self.set("MACHINE", arg)
435 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500436
Andrew Geissler82c905d2020-04-13 13:39:40 -0500437 self.bitbake_e = self.run_bitbake_env(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600438 # bitbake -e doesn't report invalid MACHINE as an error, so
439 # let's check DEPLOY_DIR_IMAGE to make sure that it is a valid
440 # MACHINE.
441 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
442 if s:
443 deploy_dir_image = s.group(1)
444 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500445 raise RunQemuError("bitbake -e %s" % self.bitbake_e)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600446 if self.is_deploy_dir_image(deploy_dir_image):
447 self.set_machine_deploy_dir(arg, deploy_dir_image)
448 else:
449 logger.error("%s not a directory valid DEPLOY_DIR_IMAGE" % deploy_dir_image)
450 self.set("MACHINE", arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500451
Andrew Geisslerc182c622020-05-15 14:13:32 -0500452 def set_dri_path(self):
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500453 drivers_path = os.path.join(self.bindir_native, '../lib/dri')
454 if not os.path.exists(drivers_path) or not os.listdir(drivers_path):
455 raise RunQemuError("""
456qemu has been built without opengl support and accelerated graphics support is not available.
457To enable it, add:
458DISTRO_FEATURES_NATIVE:append = " opengl"
459DISTRO_FEATURES_NATIVESDK:append = " opengl"
460to your build configuration.
461""")
462 self.qemu_environ['LIBGL_DRIVERS_PATH'] = drivers_path
Andrew Geissler7e0e3c02022-02-25 20:34:39 +0000463
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600464 def check_args(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500465 for debug in ("-d", "--debug"):
466 if debug in sys.argv:
467 logger.setLevel(logging.DEBUG)
468 sys.argv.remove(debug)
469
470 for quiet in ("-q", "--quiet"):
471 if quiet in sys.argv:
472 logger.setLevel(logging.ERROR)
473 sys.argv.remove(quiet)
474
Andrew Geisslerc182c622020-05-15 14:13:32 -0500475 if 'gl' not in sys.argv[1:] and 'gl-es' not in sys.argv[1:]:
Patrick Williams2390b1b2022-11-03 13:47:49 -0500476 self.qemu_environ['SDL_RENDER_DRIVER'] = 'software'
477 self.qemu_environ['SDL_FRAMEBUFFER_ACCELERATION'] = 'false'
Andrew Geisslerc182c622020-05-15 14:13:32 -0500478
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600479 unknown_arg = ""
480 for arg in sys.argv[1:]:
Brad Bishop15ae2502019-06-18 21:44:24 -0400481 if arg in self.fstypes + self.vmtypes + self.wictypes:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600482 self.check_arg_fstype(arg)
483 elif arg == 'nographic':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500484 self.nographic = True
Brad Bishop19323692019-04-05 15:28:33 -0400485 elif arg == 'sdl':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500486 self.sdl = True
Brad Bishopa34c0302019-09-23 22:34:48 -0400487 elif arg == 'gtk':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500488 self.gtk = True
489 elif arg == 'gl':
490 self.gl = True
Patrick Williams2390b1b2022-11-03 13:47:49 -0500491 elif arg == 'gl-es':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500492 self.gl_es = True
Brad Bishop19323692019-04-05 15:28:33 -0400493 elif arg == 'egl-headless':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500494 self.egl_headless = True
Andrew Geissler90fd73c2021-03-05 15:25:55 -0600495 elif arg == 'novga':
Andrew Geissler95ac1b82021-03-31 14:34:31 -0500496 self.novga = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600497 elif arg == 'serial':
Brad Bishop19323692019-04-05 15:28:33 -0400498 self.serialconsole = True
499 elif arg == "serialstdio":
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600500 self.serialstdio = True
501 elif arg == 'audio':
502 logger.info("Enabling audio in qemu")
503 logger.info("Please install sound drivers in linux host")
504 self.audio_enabled = True
505 elif arg == 'kvm':
506 self.kvm_enabled = True
507 elif arg == 'kvm-vhost':
508 self.vhost_enabled = True
509 elif arg == 'slirp':
510 self.slirp_enabled = True
Andrew Geissler82c905d2020-04-13 13:39:40 -0500511 elif arg.startswith('bridge='):
512 self.net_bridge = '%s' % arg[len('bridge='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600513 elif arg == 'snapshot':
514 self.snapshot = True
515 elif arg == 'publicvnc':
Patrick Williams03907ee2022-05-01 06:28:52 -0500516 self.publicvnc = True
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600517 self.qemu_opt_script += ' -vnc :0'
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500518 elif arg == 'guestagent':
519 self.guest_agent = True
520 elif arg.startswith('guestagent-sockpath='):
521 self.guest_agent_sockpath = '%s' % arg[len('guestagent-sockpath='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600522 elif arg.startswith('tcpserial='):
Brad Bishop15ae2502019-06-18 21:44:24 -0400523 self.tcpserial_portnum = '%s' % arg[len('tcpserial='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600524 elif arg.startswith('qemuparams='):
Andrew Geissler99467da2019-02-25 18:54:23 -0600525 self.qemuparams = ' %s' % arg[len('qemuparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600526 elif arg.startswith('bootparams='):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500527 self.bootparams = arg[len('bootparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600528 elif os.path.exists(arg) or (re.search(':', arg) and re.search('/', arg)):
529 self.check_arg_path(os.path.abspath(arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500530 elif re.search(r'-image-|-image$', arg):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600531 # Lazy rootfs
532 self.rootfs = arg
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500533 elif arg.startswith('ovmf'):
534 self.ovmf_bios.append(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600535 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500536 # At last, assume it is the MACHINE
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600537 if (not unknown_arg) or unknown_arg == arg:
538 unknown_arg = arg
539 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500540 raise RunQemuError("Can't handle two unknown args: %s %s\n"
541 "Try 'runqemu help' on how to use it" % \
542 (unknown_arg, arg))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600543 # Check to make sure it is a valid machine
Brad Bishopa5c52ff2018-11-23 10:55:50 +1300544 if unknown_arg and self.get('MACHINE') != unknown_arg:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500545 if self.get('DEPLOY_DIR_IMAGE'):
546 machine = os.path.basename(self.get('DEPLOY_DIR_IMAGE'))
547 if unknown_arg == machine:
548 self.set("MACHINE", machine)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500549
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600550 self.check_arg_machine(unknown_arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500551
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500552 if not (self.get('DEPLOY_DIR_IMAGE') or self.qbconfload):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500553 self.load_bitbake_env()
554 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
555 if s:
556 self.set("DEPLOY_DIR_IMAGE", s.group(1))
557
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600558 def check_kvm(self):
559 """Check kvm and kvm-host"""
560 if not (self.kvm_enabled or self.vhost_enabled):
William A. Kennington IIIac69b482021-06-02 12:28:27 -0700561 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 -0600562 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500563
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600564 if not self.get('QB_CPU_KVM'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500565 raise RunQemuError("QB_CPU_KVM is NULL, this board doesn't support kvm")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500566
William A. Kennington IIIac69b482021-06-02 12:28:27 -0700567 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 -0600568 yocto_kvm_wiki = "https://wiki.yoctoproject.org/wiki/How_to_enable_KVM_for_Poky_qemu"
569 yocto_paravirt_kvm_wiki = "https://wiki.yoctoproject.org/wiki/Running_an_x86_Yocto_Linux_image_under_QEMU_KVM"
570 dev_kvm = '/dev/kvm'
571 dev_vhost = '/dev/vhost-net'
Brad Bishop15ae2502019-06-18 21:44:24 -0400572 if self.qemu_system.endswith(('i386', 'x86_64')):
573 with open('/proc/cpuinfo', 'r') as f:
574 kvm_cap = re.search('vmx|svm', "".join(f.readlines()))
575 if not kvm_cap:
576 logger.error("You are trying to enable KVM on a cpu without VT support.")
577 logger.error("Remove kvm from the command-line, or refer:")
578 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500579
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600580 if not os.path.exists(dev_kvm):
581 logger.error("Missing KVM device. Have you inserted kvm modules?")
582 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500583 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500584
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600585 if os.access(dev_kvm, os.W_OK|os.R_OK):
586 self.qemu_opt_script += ' -enable-kvm'
587 else:
588 logger.error("You have no read or write permission on /dev/kvm.")
589 logger.error("Please change the ownership of this file as described at:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500590 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500591
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600592 if self.vhost_enabled:
593 if not os.path.exists(dev_vhost):
594 logger.error("Missing virtio net device. Have you inserted vhost-net module?")
595 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500596 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500597
Andrew Geissler635e0e42020-08-21 15:58:33 -0500598 if not os.access(dev_vhost, os.W_OK|os.R_OK):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600599 logger.error("You have no read or write permission on /dev/vhost-net.")
600 logger.error("Please change the ownership of this file as described at:")
Andrew Geissler635e0e42020-08-21 15:58:33 -0500601 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500602
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600603 def check_fstype(self):
604 """Check and setup FSTYPE"""
605 if not self.fstype:
606 fstype = self.get('QB_DEFAULT_FSTYPE')
607 if fstype:
608 self.fstype = fstype
609 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500610 raise RunQemuError("FSTYPE is NULL!")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500611
Brad Bishop15ae2502019-06-18 21:44:24 -0400612 # parse QB_FSINFO into dict, e.g. { 'wic': ['no-kernel-in-fs', 'a-flag'], 'ext4': ['another-flag']}
613 wic_fs = False
614 qb_fsinfo = self.get('QB_FSINFO')
615 if qb_fsinfo:
616 qb_fsinfo = qb_fsinfo.split()
617 for fsinfo in qb_fsinfo:
618 try:
619 fstype, fsflag = fsinfo.split(':')
620
621 if fstype == 'wic':
622 if fsflag == 'no-kernel-in-fs':
623 wic_fs = True
624 elif fsflag == 'kernel-in-fs':
625 wic_fs = False
626 else:
627 logger.warn('Unknown flag "%s:%s" in QB_FSINFO', fstype, fsflag)
628 continue
629 else:
630 logger.warn('QB_FSINFO is not supported for image type "%s"', fstype)
631 continue
632
633 if fstype in self.fsinfo:
634 self.fsinfo[fstype].append(fsflag)
635 else:
636 self.fsinfo[fstype] = [fsflag]
637 except Exception:
638 logger.error('Invalid parameter "%s" in QB_FSINFO', fsinfo)
639
640 # treat wic images as vmimages (with kernel) or as fsimages (rootfs only)
641 if wic_fs:
642 self.fstypes = self.fstypes + self.wictypes
643 else:
644 self.vmtypes = self.vmtypes + self.wictypes
645
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600646 def check_rootfs(self):
647 """Check and set rootfs"""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500648
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500649 if self.fstype == "none":
650 return
651
652 if self.get('ROOTFS'):
653 if not self.rootfs:
654 self.rootfs = self.get('ROOTFS')
655 elif self.get('ROOTFS') != self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500656 raise RunQemuError("Maybe conflicted ROOTFS: %s vs %s" % (self.get('ROOTFS'), self.rootfs))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500657
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600658 if self.fstype == 'nfs':
659 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500660
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600661 if self.rootfs and not os.path.exists(self.rootfs):
662 # Lazy rootfs
663 self.rootfs = "%s/%s-%s.%s" % (self.get('DEPLOY_DIR_IMAGE'),
664 self.rootfs, self.get('MACHINE'),
665 self.fstype)
666 elif not self.rootfs:
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500667 glob_name = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_NAME'), self.fstype)
668 glob_link = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'), self.fstype)
669 globs = (glob_name, glob_link)
670 self.rootfs = get_first_file(globs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600671 if not self.rootfs:
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500672 raise RunQemuError("Failed to find rootfs: %s or %s" % globs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500673
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600674 if not os.path.exists(self.rootfs):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500675 raise RunQemuError("Can't find rootfs: %s" % self.rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500676
Brad Bishop08902b02019-08-20 09:16:51 -0400677 def setup_pkkek1(self):
678 """
679 Extract from PEM certificate the Platform Key and first Key
680 Exchange Key certificate string. The hypervisor needs to provide
681 it in the Type 11 SMBIOS table
682 """
683 pemcert = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), 'OvmfPkKek1.pem')
684 try:
685 with open(pemcert, 'r') as pemfile:
686 key = pemfile.read().replace('\n', ''). \
687 replace('-----BEGIN CERTIFICATE-----', ''). \
688 replace('-----END CERTIFICATE-----', '')
689 self.ovmf_secboot_pkkek1 = key
690
691 except FileNotFoundError:
692 raise RunQemuError("Can't open PEM certificate %s " % pemcert)
693
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500694 def check_ovmf(self):
695 """Check and set full path for OVMF firmware and variable file(s)."""
696
697 for index, ovmf in enumerate(self.ovmf_bios):
698 if os.path.exists(ovmf):
699 continue
700 for suffix in ('qcow2', 'bin'):
701 path = '%s/%s.%s' % (self.get('DEPLOY_DIR_IMAGE'), ovmf, suffix)
702 if os.path.exists(path):
703 self.ovmf_bios[index] = path
Brad Bishop08902b02019-08-20 09:16:51 -0400704 if ovmf.endswith('secboot'):
705 self.setup_pkkek1()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500706 break
707 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500708 raise RunQemuError("Can't find OVMF firmware: %s" % ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500709
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600710 def check_kernel(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400711 """Check and set kernel"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600712 # The vm image doesn't need a kernel
713 if self.fstype in self.vmtypes:
714 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500715
Brad Bishop316dfdd2018-06-25 12:45:53 -0400716 # See if the user supplied a KERNEL option
717 if self.get('KERNEL'):
718 self.kernel = self.get('KERNEL')
719
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500720 # QB_DEFAULT_KERNEL is always a full file path
721 kernel_name = os.path.basename(self.get('QB_DEFAULT_KERNEL'))
722
723 # The user didn't want a kernel to be loaded
Brad Bishop316dfdd2018-06-25 12:45:53 -0400724 if kernel_name == "none" and not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500725 return
726
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600727 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
728 if not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500729 kernel_match_name = "%s/%s" % (deploy_dir_image, kernel_name)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600730 kernel_match_link = "%s/%s" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
731 kernel_startswith = "%s/%s*" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500732 globs = (kernel_match_name, kernel_match_link, kernel_startswith)
733 self.kernel = get_first_file(globs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600734 if not self.kernel:
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500735 raise RunQemuError('KERNEL not found: %s, %s or %s' % globs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500736
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600737 if not os.path.exists(self.kernel):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500738 raise RunQemuError("KERNEL %s not found" % self.kernel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500739
Brad Bishop316dfdd2018-06-25 12:45:53 -0400740 def check_dtb(self):
741 """Check and set dtb"""
742 # Did the user specify a device tree?
743 if self.get('DEVICE_TREE'):
744 self.dtb = self.get('DEVICE_TREE')
745 if not os.path.exists(self.dtb):
746 raise RunQemuError('Specified DTB not found: %s' % self.dtb)
747 return
748
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600749 dtb = self.get('QB_DTB')
750 if dtb:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400751 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500752 glob_match = "%s/%s" % (deploy_dir_image, dtb)
753 glob_startswith = "%s/%s*" % (deploy_dir_image, dtb)
754 glob_wild = "%s/*.dtb" % deploy_dir_image
755 globs = (glob_match, glob_startswith, glob_wild)
756 self.dtb = get_first_file(globs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600757 if not os.path.exists(self.dtb):
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500758 raise RunQemuError('DTB not found: %s, %s or %s' % globs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500759
Brad Bishopc68388fc2019-08-26 01:33:31 -0400760 def check_bios(self):
761 """Check and set bios"""
762
763 # See if the user supplied a BIOS option
764 if self.get('BIOS'):
765 self.bios = self.get('BIOS')
766
767 # QB_DEFAULT_BIOS is always a full file path
768 bios_name = os.path.basename(self.get('QB_DEFAULT_BIOS'))
769
770 # The user didn't want a bios to be loaded
771 if (bios_name == "" or bios_name == "none") and not self.bios:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600772 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500773
Brad Bishopc68388fc2019-08-26 01:33:31 -0400774 if not self.bios:
775 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
776 self.bios = "%s/%s" % (deploy_dir_image, bios_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500777
Brad Bishopc68388fc2019-08-26 01:33:31 -0400778 if not self.bios:
779 raise RunQemuError('BIOS not found: %s' % bios_match_name)
780
781 if not os.path.exists(self.bios):
Patrick Williams213cb262021-08-07 19:21:33 -0500782 raise RunQemuError("BIOS %s not found" % self.bios)
Brad Bishopc68388fc2019-08-26 01:33:31 -0400783
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500784
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600785 def check_mem(self):
Andrew Geissler99467da2019-02-25 18:54:23 -0600786 """
787 Both qemu and kernel needs memory settings, so check QB_MEM and set it
788 for both.
789 """
790 s = re.search('-m +([0-9]+)', self.qemuparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600791 if s:
792 self.set('QB_MEM', '-m %s' % s.group(1))
793 elif not self.get('QB_MEM'):
Brad Bishop79641f22019-09-10 07:20:22 -0400794 logger.info('QB_MEM is not set, use 256M by default')
795 self.set('QB_MEM', '-m 256')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500796
Andrew Geissler99467da2019-02-25 18:54:23 -0600797 # Check and remove M or m suffix
798 qb_mem = self.get('QB_MEM')
799 if qb_mem.endswith('M') or qb_mem.endswith('m'):
800 qb_mem = qb_mem[:-1]
801
802 # Add -m prefix it not present
803 if not qb_mem.startswith('-m'):
804 qb_mem = '-m %s' % qb_mem
805
806 self.set('QB_MEM', qb_mem)
807
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800808 mach = self.get('MACHINE')
Andrew Geisslerfc113ea2023-03-31 09:59:46 -0500809 if not mach.startswith(('qemumips', 'qemux86', 'qemuloongarch64')):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800810 self.kernel_cmdline_script += ' mem=%s' % self.get('QB_MEM').replace('-m','').strip() + 'M'
811
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600812 self.qemu_opt_script += ' %s' % self.get('QB_MEM')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500813
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600814 def check_tcpserial(self):
815 if self.tcpserial_portnum:
Brad Bishop15ae2502019-06-18 21:44:24 -0400816 ports = self.tcpserial_portnum.split(':')
817 port = ports[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600818 if self.get('QB_TCPSERIAL_OPT'):
Brad Bishop15ae2502019-06-18 21:44:24 -0400819 self.qemu_opt_script += ' ' + self.get('QB_TCPSERIAL_OPT').replace('@PORT@', port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600820 else:
Brad Bishop15ae2502019-06-18 21:44:24 -0400821 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
822
823 if len(ports) > 1:
824 for port in ports[1:]:
825 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500826
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600827 def check_and_set(self):
828 """Check configs sanity and set when needed"""
829 self.validate_paths()
Andrew Geissler82c905d2020-04-13 13:39:40 -0500830 if not self.slirp_enabled and not self.net_bridge:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500831 check_tun()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600832 # Check audio
833 if self.audio_enabled:
834 if not self.get('QB_AUDIO_DRV'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500835 raise RunQemuError("QB_AUDIO_DRV is NULL, this board doesn't support audio")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600836 if not self.get('QB_AUDIO_OPT'):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800837 logger.warning('QB_AUDIO_OPT is NULL, you may need define it to make audio work')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600838 else:
839 self.qemu_opt_script += ' %s' % self.get('QB_AUDIO_OPT')
840 os.putenv('QEMU_AUDIO_DRV', self.get('QB_AUDIO_DRV'))
841 else:
842 os.putenv('QEMU_AUDIO_DRV', 'none')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500843
Brad Bishop15ae2502019-06-18 21:44:24 -0400844 self.check_qemu_system()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600845 self.check_kvm()
846 self.check_fstype()
847 self.check_rootfs()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500848 self.check_ovmf()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600849 self.check_kernel()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400850 self.check_dtb()
Brad Bishopc68388fc2019-08-26 01:33:31 -0400851 self.check_bios()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600852 self.check_mem()
853 self.check_tcpserial()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500854
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600855 def read_qemuboot(self):
856 if not self.qemuboot:
857 if self.get('DEPLOY_DIR_IMAGE'):
858 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600859 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800860 logger.warning("Can't find qemuboot conf file, DEPLOY_DIR_IMAGE is NULL!")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600861 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500862
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600863 if self.rootfs and not os.path.exists(self.rootfs):
864 # Lazy rootfs
865 machine = self.get('MACHINE')
866 if not machine:
867 machine = os.path.basename(deploy_dir_image)
868 self.qemuboot = "%s/%s-%s.qemuboot.conf" % (deploy_dir_image,
869 self.rootfs, machine)
870 else:
871 cmd = 'ls -t %s/*.qemuboot.conf' % deploy_dir_image
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500872 logger.debug('Running %s...' % cmd)
873 try:
874 qbs = subprocess.check_output(cmd, shell=True).decode('utf-8')
875 except subprocess.CalledProcessError as err:
876 raise RunQemuError(err)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600877 if qbs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500878 for qb in qbs.split():
879 # Don't use initramfs when other choices unless fstype is ramfs
880 if '-initramfs-' in os.path.basename(qb) and self.fstype != 'cpio.gz':
881 continue
882 self.qemuboot = qb
883 break
884 if not self.qemuboot:
885 # Use the first one when no choice
886 self.qemuboot = qbs.split()[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600887 self.qbconfload = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500888
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600889 if not self.qemuboot:
890 # If we haven't found a .qemuboot.conf at this point it probably
891 # doesn't exist, continue without
892 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500893
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600894 if not os.path.exists(self.qemuboot):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500895 raise RunQemuError("Failed to find %s (wrong image name or BSP does not support running under qemu?)." % self.qemuboot)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500896
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500897 logger.debug('CONFFILE: %s' % self.qemuboot)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500898
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600899 cf = configparser.ConfigParser()
900 cf.read(self.qemuboot)
901 for k, v in cf.items('config_bsp'):
902 k_upper = k.upper()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500903 if v.startswith("../"):
904 v = os.path.abspath(os.path.dirname(self.qemuboot) + "/" + v)
905 elif v == ".":
906 v = os.path.dirname(self.qemuboot)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600907 self.set(k_upper, v)
908
909 def validate_paths(self):
910 """Ensure all relevant path variables are set"""
911 # When we're started with a *.qemuboot.conf arg assume that image
912 # artefacts are relative to that file, rather than in whatever
913 # directory DEPLOY_DIR_IMAGE in the conf file points to.
914 if self.qbconfload:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500915 imgdir = os.path.realpath(os.path.dirname(self.qemuboot))
916 if imgdir != os.path.realpath(self.get('DEPLOY_DIR_IMAGE')):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600917 logger.info('Setting DEPLOY_DIR_IMAGE to folder containing %s (%s)' % (self.qemuboot, imgdir))
918 self.set('DEPLOY_DIR_IMAGE', imgdir)
919
920 # If the STAGING_*_NATIVE directories from the config file don't exist
921 # and we're in a sourced OE build directory try to extract the paths
922 # from `bitbake -e`
923 havenative = os.path.exists(self.get('STAGING_DIR_NATIVE')) and \
924 os.path.exists(self.get('STAGING_BINDIR_NATIVE'))
925
926 if not havenative:
927 if not self.bitbake_e:
928 self.load_bitbake_env()
929
930 if self.bitbake_e:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500931 native_vars = ['STAGING_DIR_NATIVE']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600932 for nv in native_vars:
933 s = re.search('^%s="(.*)"' % nv, self.bitbake_e, re.M)
934 if s and s.group(1) != self.get(nv):
935 logger.info('Overriding conf file setting of %s to %s from Bitbake environment' % (nv, s.group(1)))
936 self.set(nv, s.group(1))
937 else:
938 # when we're invoked from a running bitbake instance we won't
939 # be able to call `bitbake -e`, then try:
940 # - get OE_TMPDIR from environment and guess paths based on it
941 # - get OECORE_NATIVE_SYSROOT from environment (for sdk)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500942 tmpdir = self.get('OE_TMPDIR')
943 oecore_native_sysroot = self.get('OECORE_NATIVE_SYSROOT')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600944 if tmpdir:
945 logger.info('Setting STAGING_DIR_NATIVE and STAGING_BINDIR_NATIVE relative to OE_TMPDIR (%s)' % tmpdir)
946 hostos, _, _, _, machine = os.uname()
947 buildsys = '%s-%s' % (machine, hostos.lower())
948 staging_dir_native = '%s/sysroots/%s' % (tmpdir, buildsys)
949 self.set('STAGING_DIR_NATIVE', staging_dir_native)
950 elif oecore_native_sysroot:
951 logger.info('Setting STAGING_DIR_NATIVE to OECORE_NATIVE_SYSROOT (%s)' % oecore_native_sysroot)
952 self.set('STAGING_DIR_NATIVE', oecore_native_sysroot)
953 if self.get('STAGING_DIR_NATIVE'):
954 # we have to assume that STAGING_BINDIR_NATIVE is at usr/bin
955 staging_bindir_native = '%s/usr/bin' % self.get('STAGING_DIR_NATIVE')
956 logger.info('Setting STAGING_BINDIR_NATIVE to %s' % staging_bindir_native)
957 self.set('STAGING_BINDIR_NATIVE', '%s/usr/bin' % self.get('STAGING_DIR_NATIVE'))
958
959 def print_config(self):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500960 logoutput = ['Continuing with the following parameters:']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600961 if not self.fstype in self.vmtypes:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500962 logoutput.append('KERNEL: [%s]' % self.kernel)
Brad Bishopc68388fc2019-08-26 01:33:31 -0400963 if self.bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500964 logoutput.append('BIOS: [%s]' % self.bios)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600965 if self.dtb:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500966 logoutput.append('DTB: [%s]' % self.dtb)
967 logoutput.append('MACHINE: [%s]' % self.get('MACHINE'))
Brad Bishop15ae2502019-06-18 21:44:24 -0400968 try:
969 fstype_flags = ' (' + ', '.join(self.fsinfo[self.fstype]) + ')'
970 except KeyError:
971 fstype_flags = ''
Andrew Geissler82c905d2020-04-13 13:39:40 -0500972 logoutput.append('FSTYPE: [%s%s]' % (self.fstype, fstype_flags))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600973 if self.fstype == 'nfs':
Andrew Geissler82c905d2020-04-13 13:39:40 -0500974 logoutput.append('NFS_DIR: [%s]' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600975 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500976 logoutput.append('ROOTFS: [%s]' % self.rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500977 if self.ovmf_bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500978 logoutput.append('OVMF: %s' % self.ovmf_bios)
Brad Bishop08902b02019-08-20 09:16:51 -0400979 if (self.ovmf_secboot_pkkek1):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500980 logoutput.append('SECBOOT PKKEK1: [%s...]' % self.ovmf_secboot_pkkek1[0:100])
981 logoutput.append('CONFFILE: [%s]' % self.qemuboot)
982 logoutput.append('')
983 logger.info('\n'.join(logoutput))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600984
985 def setup_nfs(self):
986 if not self.nfs_server:
987 if self.slirp_enabled:
988 self.nfs_server = '10.0.2.2'
989 else:
Andrew Geissler517393d2023-01-13 08:55:19 -0600990 self.nfs_server = '192.168.7.@GATEWAY@'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600991
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500992 # Figure out a new nfs_instance to allow multiple qemus running.
Brad Bishop977dc1a2019-02-06 16:01:43 -0500993 ps = subprocess.check_output(("ps", "auxww")).decode('utf-8')
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500994 pattern = '/bin/unfsd .* -i .*\.pid -e .*/exports([0-9]+) '
995 all_instances = re.findall(pattern, ps, re.M)
996 if all_instances:
997 all_instances.sort(key=int)
998 self.nfs_instance = int(all_instances.pop()) + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600999
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001000 nfsd_port = 3049 + 2 * self.nfs_instance
1001 mountd_port = 3048 + 2 * self.nfs_instance
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001002
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001003 # Export vars for runqemu-export-rootfs
1004 export_dict = {
1005 'NFS_INSTANCE': self.nfs_instance,
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001006 'NFSD_PORT': nfsd_port,
1007 'MOUNTD_PORT': mountd_port,
1008 }
1009 for k, v in export_dict.items():
1010 # Use '%s' since they are integers
1011 os.putenv(k, '%s' % v)
1012
Andrew Geisslerc5535c92023-01-27 16:10:19 -06001013 qb_nfsrootfs_extra_opt = self.get("QB_NFSROOTFS_EXTRA_OPT")
1014 if qb_nfsrootfs_extra_opt and not qb_nfsrootfs_extra_opt.startswith(","):
1015 qb_nfsrootfs_extra_opt = "," + qb_nfsrootfs_extra_opt
1016
1017 self.unfs_opts="nfsvers=3,port=%s,tcp,mountport=%s%s" % (nfsd_port, mountd_port, qb_nfsrootfs_extra_opt)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001018
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001019 # Extract .tar.bz2 or .tar.bz if no nfs dir
1020 if not (self.rootfs and os.path.isdir(self.rootfs)):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001021 src_prefix = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'))
1022 dest = "%s-nfsroot" % src_prefix
1023 if os.path.exists('%s.pseudo_state' % dest):
1024 logger.info('Use %s as NFS_DIR' % dest)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001025 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001026 else:
1027 src = ""
1028 src1 = '%s.tar.bz2' % src_prefix
1029 src2 = '%s.tar.gz' % src_prefix
1030 if os.path.exists(src1):
1031 src = src1
1032 elif os.path.exists(src2):
1033 src = src2
1034 if not src:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001035 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 -06001036 logger.info('NFS_DIR not found, extracting %s to %s' % (src, dest))
Brad Bishop977dc1a2019-02-06 16:01:43 -05001037 cmd = ('runqemu-extract-sdk', src, dest)
1038 logger.info('Running %s...' % str(cmd))
1039 if subprocess.call(cmd) != 0:
Andrew Geisslerfc113ea2023-03-31 09:59:46 -05001040 raise RunQemuError('Failed to run %s' % str(cmd))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001041 self.rootfs = dest
Andrew Geisslerc926e172021-05-07 16:11:35 -05001042 self.cleanup_files.append(self.rootfs)
1043 self.cleanup_files.append('%s.pseudo_state' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001044
1045 # Start the userspace NFS server
Brad Bishop977dc1a2019-02-06 16:01:43 -05001046 cmd = ('runqemu-export-rootfs', 'start', self.rootfs)
1047 logger.info('Running %s...' % str(cmd))
1048 if subprocess.call(cmd) != 0:
Andrew Geisslerfc113ea2023-03-31 09:59:46 -05001049 raise RunQemuError('Failed to run %s' % str(cmd))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001050
1051 self.nfs_running = True
1052
Andrew Geissler517393d2023-01-13 08:55:19 -06001053 def setup_cmd(self):
1054 cmd = self.get('QB_SETUP_CMD')
1055 if cmd != '':
1056 logger.info('Running setup command %s' % str(cmd))
1057 if subprocess.call(cmd, shell=True) != 0:
Andrew Geisslerfc113ea2023-03-31 09:59:46 -05001058 raise RunQemuError('Failed to run %s' % str(cmd))
Andrew Geissler517393d2023-01-13 08:55:19 -06001059
Andrew Geissler82c905d2020-04-13 13:39:40 -05001060 def setup_net_bridge(self):
1061 self.set('NETWORK_CMD', '-netdev bridge,br=%s,id=net0,helper=%s -device virtio-net-pci,netdev=net0 ' % (
1062 self.net_bridge, os.path.join(self.bindir_native, 'qemu-oe-bridge-helper')))
1063
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001064 def setup_slirp(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001065 """Setup user networking"""
1066
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001067 if self.fstype == 'nfs':
1068 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001069 netconf = " " + self.cmdline_ip_slirp
1070 logger.info("Network configuration:%s", netconf)
1071 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001072 # Port mapping
Andrew Geissler517393d2023-01-13 08:55:19 -06001073 hostfwd = ",hostfwd=tcp:127.0.0.1:2222-:22,hostfwd=tcp:127.0.0.1:2323-:23"
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001074 qb_slirp_opt_default = "-netdev user,id=net0%s,tftp=%s" % (hostfwd, self.get('DEPLOY_DIR_IMAGE'))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001075 qb_slirp_opt = self.get('QB_SLIRP_OPT') or qb_slirp_opt_default
1076 # Figure out the port
1077 ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt)
1078 ports = [int(i) for i in ports]
1079 mac = 2
Brad Bishop08902b02019-08-20 09:16:51 -04001080
1081 lockdir = "/tmp/qemu-port-locks"
1082 if not os.path.exists(lockdir):
1083 # There might be a race issue when multi runqemu processess are
1084 # running at the same time.
1085 try:
1086 os.mkdir(lockdir)
1087 os.chmod(lockdir, 0o777)
1088 except FileExistsError:
1089 pass
1090
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001091 # Find a free port to avoid conflicts
1092 for p in ports[:]:
1093 p_new = p
Brad Bishop08902b02019-08-20 09:16:51 -04001094 while not self.check_free_port('localhost', p_new, lockdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001095 p_new += 1
1096 mac += 1
1097 while p_new in ports:
1098 p_new += 1
1099 mac += 1
1100 if p != p_new:
1101 ports.append(p_new)
1102 qb_slirp_opt = re.sub(':%s-' % p, ':%s-' % p_new, qb_slirp_opt)
1103 logger.info("Port forward changed: %s -> %s" % (p, p_new))
1104 mac = "%s%02x" % (self.mac_slirp, mac)
1105 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qb_slirp_opt))
1106 # Print out port foward
1107 hostfwd = re.findall('(hostfwd=[^,]*)', qb_slirp_opt)
1108 if hostfwd:
1109 logger.info('Port forward: %s' % ' '.join(hostfwd))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001110
1111 def setup_tap(self):
1112 """Setup tap"""
1113
1114 # This file is created when runqemu-gen-tapdevs creates a bank of tap
1115 # devices, indicating that the user should not bring up new ones using
1116 # sudo.
1117 nosudo_flag = '/etc/runqemu-nosudo'
1118 self.qemuifup = shutil.which('runqemu-ifup')
1119 self.qemuifdown = shutil.which('runqemu-ifdown')
1120 ip = shutil.which('ip')
1121 lockdir = "/tmp/qemu-tap-locks"
1122
1123 if not (self.qemuifup and self.qemuifdown and ip):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001124 logger.error("runqemu-ifup: %s" % self.qemuifup)
1125 logger.error("runqemu-ifdown: %s" % self.qemuifdown)
1126 logger.error("ip: %s" % ip)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001127 raise OEPathError("runqemu-ifup, runqemu-ifdown or ip not found")
1128
1129 if not os.path.exists(lockdir):
1130 # There might be a race issue when multi runqemu processess are
1131 # running at the same time.
1132 try:
1133 os.mkdir(lockdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001134 os.chmod(lockdir, 0o777)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001135 except FileExistsError:
1136 pass
1137
Brad Bishop977dc1a2019-02-06 16:01:43 -05001138 cmd = (ip, 'link')
1139 logger.debug('Running %s...' % str(cmd))
1140 ip_link = subprocess.check_output(cmd).decode('utf-8')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001141 # Matches line like: 6: tap0: <foo>
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001142 possibles = re.findall('^[0-9]+: +(tap[0-9]+): <.*', ip_link, re.M)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001143 tap = ""
1144 for p in possibles:
1145 lockfile = os.path.join(lockdir, p)
1146 if os.path.exists('%s.skip' % lockfile):
1147 logger.info('Found %s.skip, skipping %s' % (lockfile, p))
1148 continue
Brad Bishop08902b02019-08-20 09:16:51 -04001149 self.taplock = lockfile + '.lock'
1150 if self.acquire_taplock(error=False):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001151 tap = p
1152 logger.info("Using preconfigured tap device %s" % tap)
1153 logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap))
1154 break
1155
1156 if not tap:
1157 if os.path.exists(nosudo_flag):
1158 logger.error("Error: There are no available tap devices to use for networking,")
1159 logger.error("and I see %s exists, so I am not going to try creating" % nosudo_flag)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001160 raise RunQemuError("a new one with sudo.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001161
1162 gid = os.getgid()
1163 uid = os.getuid()
1164 logger.info("Setting up tap interface under sudo")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001165 cmd = ('sudo', self.qemuifup, str(uid), str(gid), self.bindir_native)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001166 try:
1167 tap = subprocess.check_output(cmd).decode('utf-8').strip()
1168 except subprocess.CalledProcessError as e:
1169 logger.error('Setting up tap device failed:\n%s\nRun runqemu-gen-tapdevs to manually create one.' % str(e))
1170 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001171 lockfile = os.path.join(lockdir, tap)
Brad Bishop08902b02019-08-20 09:16:51 -04001172 self.taplock = lockfile + '.lock'
1173 self.acquire_taplock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001174 self.cleantap = True
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001175 logger.debug('Created tap: %s' % tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001176
1177 if not tap:
1178 logger.error("Failed to setup tap device. Run runqemu-gen-tapdevs to manually create.")
Andrew Geissler82c905d2020-04-13 13:39:40 -05001179 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001180 self.tap = tap
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001181 tapnum = int(tap[3:])
1182 gateway = tapnum * 2 + 1
1183 client = gateway + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001184 if self.fstype == 'nfs':
1185 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001186 netconf = " " + self.cmdline_ip_tap
1187 netconf = netconf.replace('@CLIENT@', str(client))
1188 netconf = netconf.replace('@GATEWAY@', str(gateway))
Andrew Geissler517393d2023-01-13 08:55:19 -06001189 self.nfs_server = self.nfs_server.replace('@GATEWAY@', str(gateway))
Andrew Geissler82c905d2020-04-13 13:39:40 -05001190 logger.info("Network configuration:%s", netconf)
1191 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001192 mac = "%s%02x" % (self.mac_tap, client)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001193 qb_tap_opt = self.get('QB_TAP_OPT')
1194 if qb_tap_opt:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001195 qemu_tap_opt = qb_tap_opt.replace('@TAP@', tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001196 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001197 qemu_tap_opt = "-netdev tap,id=net0,ifname=%s,script=no,downscript=no" % (self.tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001198
1199 if self.vhost_enabled:
1200 qemu_tap_opt += ',vhost=on'
1201
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001202 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qemu_tap_opt))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001203
1204 def setup_network(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001205 if self.get('QB_NET') == 'none':
1206 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001207 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001208 self.saved_stty = subprocess.check_output(("stty", "-g")).decode('utf-8').strip()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001209 self.network_device = self.get('QB_NETWORK_DEVICE') or self.network_device
Andrew Geissler82c905d2020-04-13 13:39:40 -05001210 if self.net_bridge:
1211 self.setup_net_bridge()
1212 elif self.slirp_enabled:
1213 self.cmdline_ip_slirp = self.get('QB_CMDLINE_IP_SLIRP') or self.cmdline_ip_slirp
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001214 self.setup_slirp()
1215 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001216 self.cmdline_ip_tap = self.get('QB_CMDLINE_IP_TAP') or self.cmdline_ip_tap
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001217 self.setup_tap()
1218
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001219 def setup_rootfs(self):
1220 if self.get('QB_ROOTFS') == 'none':
1221 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001222 if 'wic.' in self.fstype:
1223 self.fstype = self.fstype[4:]
Andrew Geisslerd1e89492021-02-12 15:35:20 -06001224 rootfs_format = self.fstype if self.fstype in ('vmdk', 'vhd', 'vhdx', 'qcow2', 'vdi') else 'raw'
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001225
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001226 tmpfsdir = os.environ.get("RUNQEMU_TMPFS_DIR", None)
1227 if self.snapshot and tmpfsdir:
1228 newrootfs = os.path.join(tmpfsdir, os.path.basename(self.rootfs)) + "." + str(os.getpid())
Andrew Geissler09036742021-06-25 14:25:14 -05001229 logger.info("Copying rootfs to %s" % newrootfs)
1230 copy_start = time.time()
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001231 shutil.copyfile(self.rootfs, newrootfs)
Andrew Geissler09036742021-06-25 14:25:14 -05001232 logger.info("Copy done in %s seconds" % (time.time() - copy_start))
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001233 self.rootfs = newrootfs
1234 # Don't need a second copy now!
1235 self.snapshot = False
Andrew Geisslerc926e172021-05-07 16:11:35 -05001236 self.cleanup_files.append(newrootfs)
Andrew Geissler3b8a17c2021-04-15 15:55:55 -05001237
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001238 qb_rootfs_opt = self.get('QB_ROOTFS_OPT')
1239 if qb_rootfs_opt:
1240 self.rootfs_options = qb_rootfs_opt.replace('@ROOTFS@', self.rootfs)
1241 else:
1242 self.rootfs_options = '-drive file=%s,if=virtio,format=%s' % (self.rootfs, rootfs_format)
1243
Andrew Geissler82c905d2020-04-13 13:39:40 -05001244 qb_rootfs_extra_opt = self.get("QB_ROOTFS_EXTRA_OPT")
1245 if qb_rootfs_extra_opt and not qb_rootfs_extra_opt.startswith(","):
1246 qb_rootfs_extra_opt = "," + qb_rootfs_extra_opt
1247
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001248 if self.fstype in ('cpio.gz', 'cpio'):
1249 self.kernel_cmdline = 'root=/dev/ram0 rw debugshell'
1250 self.rootfs_options = '-initrd %s' % self.rootfs
1251 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001252 vm_drive = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001253 if self.fstype in self.vmtypes:
1254 if self.fstype == 'iso':
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001255 vm_drive = '-drive file=%s,if=virtio,media=cdrom' % self.rootfs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001256 elif self.get('QB_DRIVE_TYPE'):
1257 drive_type = self.get('QB_DRIVE_TYPE')
1258 if drive_type.startswith("/dev/sd"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001259 logger.info('Using scsi drive')
Andrew Geissler82c905d2020-04-13 13:39:40 -05001260 vm_drive = '-drive if=none,id=hd,file=%s,format=%s -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd%s' \
1261 % (self.rootfs, rootfs_format, qb_rootfs_extra_opt)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001262 elif drive_type.startswith("/dev/hd"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001263 logger.info('Using ide drive')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001264 vm_drive = "-drive file=%s,format=%s" % (self.rootfs, rootfs_format)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001265 elif drive_type.startswith("/dev/vdb"):
1266 logger.info('Using block virtio drive');
1267 vm_drive = '-drive id=disk0,file=%s,if=none,format=%s -device virtio-blk-device,drive=disk0%s' \
1268 % (self.rootfs, rootfs_format,qb_rootfs_extra_opt)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001269 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001270 # virtio might have been selected explicitly (just use it), or
1271 # is used as fallback (then warn about that).
1272 if not drive_type.startswith("/dev/vd"):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001273 logger.warning("Unknown QB_DRIVE_TYPE: %s" % drive_type)
1274 logger.warning("Failed to figure out drive type, consider define or fix QB_DRIVE_TYPE")
1275 logger.warning('Trying to use virtio block drive')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001276 vm_drive = '-drive if=virtio,file=%s,format=%s' % (self.rootfs, rootfs_format)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001277
1278 # All branches above set vm_drive.
Andrew Geissler475cb722020-07-10 16:00:51 -05001279 self.rootfs_options = vm_drive
1280 if not self.fstype in self.vmtypes:
1281 self.rootfs_options += ' -no-reboot'
Andrew Geissler595f6302022-01-24 19:11:47 +00001282
1283 # By default, ' rw' is appended to QB_KERNEL_ROOT unless either ro or rw is explicitly passed.
1284 qb_kernel_root = self.get('QB_KERNEL_ROOT')
1285 qb_kernel_root_l = qb_kernel_root.split()
1286 if not ('ro' in qb_kernel_root_l or 'rw' in qb_kernel_root_l):
1287 qb_kernel_root += ' rw'
1288 self.kernel_cmdline = 'root=%s' % qb_kernel_root
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001289
1290 if self.fstype == 'nfs':
1291 self.rootfs_options = ''
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001292 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 -04001293 self.kernel_cmdline = 'root=%s rw' % k_root
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001294
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001295 if self.fstype == 'none':
1296 self.rootfs_options = ''
1297
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001298 self.set('ROOTFS_OPTIONS', self.rootfs_options)
1299
1300 def guess_qb_system(self):
1301 """attempt to determine the appropriate qemu-system binary"""
1302 mach = self.get('MACHINE')
1303 if not mach:
Patrick Williams864cc432023-02-09 14:54:44 -06001304 search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemuloongarch64|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*'
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001305 if self.rootfs:
1306 match = re.match(search, self.rootfs)
1307 if match:
1308 mach = match.group(1)
1309 elif self.kernel:
1310 match = re.match(search, self.kernel)
1311 if match:
1312 mach = match.group(1)
1313
1314 if not mach:
1315 return None
1316
1317 if mach == 'qemuarm':
1318 qbsys = 'arm'
1319 elif mach == 'qemuarm64':
1320 qbsys = 'aarch64'
1321 elif mach == 'qemux86':
1322 qbsys = 'i386'
1323 elif mach == 'qemux86-64':
1324 qbsys = 'x86_64'
1325 elif mach == 'qemuppc':
1326 qbsys = 'ppc'
Patrick Williams864cc432023-02-09 14:54:44 -06001327 elif mach == 'qemuloongarch64':
1328 qbsys = 'loongarch64'
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001329 elif mach == 'qemumips':
1330 qbsys = 'mips'
1331 elif mach == 'qemumips64':
1332 qbsys = 'mips64'
1333 elif mach == 'qemumipsel':
1334 qbsys = 'mipsel'
1335 elif mach == 'qemumips64el':
1336 qbsys = 'mips64el'
Brad Bishop316dfdd2018-06-25 12:45:53 -04001337 elif mach == 'qemuriscv64':
1338 qbsys = 'riscv64'
1339 elif mach == 'qemuriscv32':
1340 qbsys = 'riscv32'
Brad Bishop004d4992018-10-02 23:54:45 +02001341 else:
1342 logger.error("Unable to determine QEMU PC System emulator for %s machine." % mach)
1343 logger.error("As %s is not among valid QEMU machines such as," % mach)
1344 logger.error("qemux86-64, qemux86, qemuarm64, qemuarm, qemumips64, qemumips64el, qemumipsel, qemumips, qemuppc")
1345 raise RunQemuError("Set qb_system_name with suitable QEMU PC System emulator in .*qemuboot.conf.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001346
1347 return 'qemu-system-%s' % qbsys
1348
Brad Bishop15ae2502019-06-18 21:44:24 -04001349 def check_qemu_system(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001350 qemu_system = self.get('QB_SYSTEM_NAME')
1351 if not qemu_system:
1352 qemu_system = self.guess_qb_system()
1353 if not qemu_system:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001354 raise RunQemuError("Failed to boot, QB_SYSTEM_NAME is NULL!")
Brad Bishop15ae2502019-06-18 21:44:24 -04001355 self.qemu_system = qemu_system
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001356
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001357 def check_render_nodes(self):
1358 render_hint = """If /dev/dri/renderD* is absent due to lack of suitable GPU, 'modprobe vgem' will create one suitable for mesa llvmpipe software renderer."""
1359 try:
1360 content = os.listdir("/dev/dri")
1361 if len([i for i in content if i.startswith('render')]) == 0:
1362 raise RunQemuError("No render nodes found in /dev/dri: %s. %s" %(content, render_hint))
1363 except FileNotFoundError:
1364 raise RunQemuError("/dev/dri directory does not exist; no render nodes available on this machine. %s" %(render_hint))
1365
Andrew Geisslerfc113ea2023-03-31 09:59:46 -05001366 def setup_guest_agent(self):
1367 if self.guest_agent == True:
1368 self.qemu_opt += ' -chardev socket,path=' + self.guest_agent_sockpath + ',server,nowait,id=qga0 '
1369 self.qemu_opt += ' -device virtio-serial '
1370 self.qemu_opt += ' -device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0 '
1371
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001372 def setup_vga(self):
1373 if self.nographic == True:
1374 if self.sdl == True:
1375 raise RunQemuError('Option nographic makes no sense alongside the sdl option.')
1376 if self.gtk == True:
1377 raise RunQemuError('Option nographic makes no sense alongside the gtk option.')
1378 self.qemu_opt += ' -nographic'
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001379
1380 if self.novga == True:
1381 self.qemu_opt += ' -vga none'
1382 return
1383
1384 if (self.gl_es == True or self.gl == True) and (self.sdl == False and self.gtk == False):
1385 raise RunQemuError('Option gl/gl-es needs gtk or sdl option.')
1386
Patrick Williams03907ee2022-05-01 06:28:52 -05001387 # If we have no display option, we autodetect based upon what qemu supports. We
1388 # need our font setup and show-cusor below so we need to see what qemu --help says
1389 # is supported so we can pass our correct config in.
1390 if not self.nographic and not self.sdl and not self.gtk and not self.publicvnc and not self.egl_headless == True:
Patrick Williams2390b1b2022-11-03 13:47:49 -05001391 output = subprocess.check_output([self.qemu_bin, "--help"], universal_newlines=True, env=self.qemu_environ)
Patrick Williams03907ee2022-05-01 06:28:52 -05001392 if "-display gtk" in output:
1393 self.gtk = True
1394 elif "-display sdl" in output:
1395 self.sdl = True
Andrew Geissler595f6302022-01-24 19:11:47 +00001396 else:
Patrick Williamsdb4c27e2022-08-05 08:10:29 -05001397 self.qemu_opt += ' -display none'
Andrew Geissler595f6302022-01-24 19:11:47 +00001398
Patrick Williams03907ee2022-05-01 06:28:52 -05001399 if self.sdl == True or self.gtk == True or self.egl_headless == True:
1400
1401 if self.qemu_system.endswith(('i386', 'x86_64')):
1402 if self.gl or self.gl_es or self.egl_headless:
1403 self.qemu_opt += ' -device virtio-vga-gl '
1404 else:
1405 self.qemu_opt += ' -device virtio-vga '
1406
1407 self.qemu_opt += ' -display '
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001408 if self.egl_headless == True:
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001409 self.check_render_nodes()
Andrew Geissler595f6302022-01-24 19:11:47 +00001410 self.set_dri_path()
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001411 self.qemu_opt += 'egl-headless,'
1412 else:
1413 if self.sdl == True:
1414 self.qemu_opt += 'sdl,'
1415 elif self.gtk == True:
Patrick Williams2390b1b2022-11-03 13:47:49 -05001416 self.qemu_environ['FONTCONFIG_PATH'] = '/etc/fonts'
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001417 self.qemu_opt += 'gtk,'
1418
1419 if self.gl == True:
Andrew Geissler595f6302022-01-24 19:11:47 +00001420 self.set_dri_path()
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001421 self.qemu_opt += 'gl=on,'
1422 elif self.gl_es == True:
Andrew Geissler595f6302022-01-24 19:11:47 +00001423 self.set_dri_path()
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001424 self.qemu_opt += 'gl=es,'
1425 self.qemu_opt += 'show-cursor=on'
1426
1427 self.qemu_opt += ' %s' %self.get('QB_GRAPHICS')
1428
1429 def setup_serial(self):
1430 # Setup correct kernel command line for serial
Andrew Geissler595f6302022-01-24 19:11:47 +00001431 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 -05001432 for entry in self.get('SERIAL_CONSOLES').split(' '):
1433 self.kernel_cmdline_script += ' console=%s' %entry.split(';')[1]
1434
1435 if self.serialstdio == True or self.nographic == True:
1436 self.qemu_opt += " -serial mon:stdio"
1437 else:
1438 self.qemu_opt += " -serial mon:vc"
1439 if self.serialconsole:
1440 if sys.stdin.isatty():
1441 subprocess.check_call(("stty", "intr", "^]"))
1442 logger.info("Interrupt character is '^]'")
1443
1444 self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT")
1445
1446 # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES).
1447 # If no serial or serialtcp options were specified, only ttyS0 is created
1448 # and sysvinit shows an error trying to enable ttyS1:
1449 # INIT: Id "S1" respawning too fast: disabled for 5 minutes
1450 serial_num = len(re.findall("-serial", self.qemu_opt))
1451 if serial_num < 2:
1452 self.qemu_opt += " -serial null"
1453
Patrick Williams03907ee2022-05-01 06:28:52 -05001454 def find_qemu(self):
Brad Bishop15ae2502019-06-18 21:44:24 -04001455 qemu_bin = os.path.join(self.bindir_native, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001456
1457 # It is possible to have qemu-native in ASSUME_PROVIDED, and it won't
1458 # find QEMU in sysroot, it needs to use host's qemu.
1459 if not os.path.exists(qemu_bin):
1460 logger.info("QEMU binary not found in %s, trying host's QEMU" % qemu_bin)
1461 for path in (os.environ['PATH'] or '').split(':'):
Brad Bishop15ae2502019-06-18 21:44:24 -04001462 qemu_bin_tmp = os.path.join(path, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001463 logger.info("Trying: %s" % qemu_bin_tmp)
1464 if os.path.exists(qemu_bin_tmp):
1465 qemu_bin = qemu_bin_tmp
1466 if not os.path.isabs(qemu_bin):
1467 qemu_bin = os.path.abspath(qemu_bin)
1468 logger.info("Using host's QEMU: %s" % qemu_bin)
1469 break
1470
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001471 if not os.access(qemu_bin, os.X_OK):
1472 raise OEPathError("No QEMU binary '%s' could be found" % qemu_bin)
Patrick Williams03907ee2022-05-01 06:28:52 -05001473 self.qemu_bin = qemu_bin
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001474
Patrick Williams03907ee2022-05-01 06:28:52 -05001475 def setup_final(self):
1476
1477 self.find_qemu()
1478
1479 self.qemu_opt = "%s %s %s %s %s" % (self.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 -05001480
1481 for ovmf in self.ovmf_bios:
1482 format = ovmf.rsplit('.', 1)[-1]
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001483 if format == "bin":
1484 format = "raw"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001485 self.qemu_opt += ' -drive if=pflash,format=%s,file=%s' % (format, ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001486
1487 self.qemu_opt += ' ' + self.qemu_opt_script
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001488
Brad Bishop08902b02019-08-20 09:16:51 -04001489 if self.ovmf_secboot_pkkek1:
1490 # Provide the Platform Key and first Key Exchange Key certificate as an
1491 # OEM string in the SMBIOS Type 11 table. Prepend the certificate string
1492 # with "application prefix" of the EnrollDefaultKeys.efi application
1493 self.qemu_opt += ' -smbios type=11,value=4e32566d-8e9e-4f52-81d3-5bb9715f9727:' \
1494 + self.ovmf_secboot_pkkek1
1495
Andrew Geissler99467da2019-02-25 18:54:23 -06001496 # Append qemuparams to override previous settings
1497 if self.qemuparams:
1498 self.qemu_opt += ' ' + self.qemuparams
1499
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001500 if self.snapshot:
1501 self.qemu_opt += " -snapshot"
1502
Andrew Geisslerfc113ea2023-03-31 09:59:46 -05001503 self.setup_guest_agent()
Andrew Geissler95ac1b82021-03-31 14:34:31 -05001504 self.setup_serial()
1505 self.setup_vga()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001506
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001507 def start_qemu(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001508 import shlex
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001509 if self.kernel:
Andrew Geissler615f2f12022-07-15 14:00:58 -05001510 kernel_opts = "-kernel %s" % (self.kernel)
1511 if self.get('QB_KERNEL_CMDLINE') == "none":
1512 if self.bootparams:
1513 kernel_opts += " -append '%s'" % (self.bootparams)
1514 else:
1515 kernel_opts += " -append '%s %s %s %s'" % (self.kernel_cmdline,
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001516 self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'),
1517 self.bootparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001518 if self.dtb:
1519 kernel_opts += " -dtb %s" % self.dtb
1520 else:
1521 kernel_opts = ""
Patrick Williams213cb262021-08-07 19:21:33 -05001522
1523 if self.bios:
1524 self.qemu_opt += " -bios %s" % self.bios
1525
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001526 cmd = "%s %s" % (self.qemu_opt, kernel_opts)
Brad Bishop004d4992018-10-02 23:54:45 +02001527 cmds = shlex.split(cmd)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001528 logger.info('Running %s\n' % cmd)
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001529 with open('/proc/uptime', 'r') as f:
1530 uptime_seconds = f.readline().split()[0]
1531 logger.info('Host uptime: %s\n' % uptime_seconds)
Brad Bishopf86d0552018-12-04 14:18:15 -08001532 pass_fds = []
Brad Bishop08902b02019-08-20 09:16:51 -04001533 if self.taplock_descriptor:
1534 pass_fds = [self.taplock_descriptor.fileno()]
1535 if len(self.portlocks):
1536 for descriptor in self.portlocks.values():
1537 pass_fds.append(descriptor.fileno())
Patrick Williams2390b1b2022-11-03 13:47:49 -05001538 process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds, env=self.qemu_environ)
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001539 self.qemuprocess = process
Brad Bishop004d4992018-10-02 23:54:45 +02001540 retcode = process.wait()
1541 if retcode:
1542 if retcode == -signal.SIGTERM:
1543 logger.info("Qemu terminated by SIGTERM")
1544 else:
1545 logger.error("Failed to run qemu: %s", process.stderr.read().decode())
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001546
Andrew Geissler517393d2023-01-13 08:55:19 -06001547 def cleanup_cmd(self):
1548 cmd = self.get('QB_CLEANUP_CMD')
1549 if cmd != '':
1550 logger.info('Running cleanup command %s' % str(cmd))
1551 if subprocess.call(cmd, shell=True) != 0:
Andrew Geisslerfc113ea2023-03-31 09:59:46 -05001552 raise RunQemuError('Failed to run %s' % str(cmd))
Andrew Geissler517393d2023-01-13 08:55:19 -06001553
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001554 def cleanup(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001555 if self.cleaned:
1556 return
1557
1558 # avoid dealing with SIGTERM when cleanup function is running
1559 signal.signal(signal.SIGTERM, signal.SIG_IGN)
1560
1561 logger.info("Cleaning up")
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001562
1563 if self.qemuprocess:
1564 try:
1565 # give it some time to shut down, ignore return values and output
1566 self.qemuprocess.send_signal(signal.SIGTERM)
1567 self.qemuprocess.communicate(timeout=5)
1568 except subprocess.TimeoutExpired:
1569 self.qemuprocess.kill()
1570
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001571 with open('/proc/uptime', 'r') as f:
1572 uptime_seconds = f.readline().split()[0]
1573 logger.info('Host uptime: %s\n' % uptime_seconds)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001574 if self.cleantap:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001575 cmd = ('sudo', self.qemuifdown, self.tap, self.bindir_native)
1576 logger.debug('Running %s' % str(cmd))
1577 subprocess.check_call(cmd)
Brad Bishop08902b02019-08-20 09:16:51 -04001578 self.release_taplock()
1579 self.release_portlock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001580
1581 if self.nfs_running:
1582 logger.info("Shutting down the userspace NFS server...")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001583 cmd = ("runqemu-export-rootfs", "stop", self.rootfs)
1584 logger.debug('Running %s' % str(cmd))
1585 subprocess.check_call(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001586
1587 if self.saved_stty:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001588 subprocess.check_call(("stty", self.saved_stty))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001589
Andrew Geisslerc926e172021-05-07 16:11:35 -05001590 if self.cleanup_files:
1591 for ent in self.cleanup_files:
1592 logger.info('Removing %s' % ent)
1593 if os.path.isfile(ent):
1594 os.remove(ent)
1595 else:
1596 shutil.rmtree(ent)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001597
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001598 # Deliberately ignore the return code of 'tput smam'.
1599 subprocess.call(["tput", "smam"])
1600
Brad Bishop004d4992018-10-02 23:54:45 +02001601 self.cleaned = True
1602
Andrew Geissler82c905d2020-04-13 13:39:40 -05001603 def run_bitbake_env(self, mach=None):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001604 bitbake = shutil.which('bitbake')
1605 if not bitbake:
1606 return
1607
1608 if not mach:
1609 mach = self.get('MACHINE')
1610
Andrew Geissler82c905d2020-04-13 13:39:40 -05001611 multiconfig = self.get('MULTICONFIG')
1612 if multiconfig:
1613 multiconfig = "mc:%s" % multiconfig
1614
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001615 if mach:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001616 cmd = 'MACHINE=%s bitbake -e %s' % (mach, multiconfig)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001617 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001618 cmd = 'bitbake -e %s' % multiconfig
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001619
1620 logger.info('Running %s...' % cmd)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001621 return subprocess.check_output(cmd, shell=True).decode('utf-8')
1622
1623 def load_bitbake_env(self, mach=None):
1624 if self.bitbake_e:
1625 return
1626
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001627 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001628 self.bitbake_e = self.run_bitbake_env(mach=mach)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001629 except subprocess.CalledProcessError as err:
1630 self.bitbake_e = ''
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001631 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 -06001632
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001633 def validate_combos(self):
1634 if (self.fstype in self.vmtypes) and self.kernel:
1635 raise RunQemuError("%s doesn't need kernel %s!" % (self.fstype, self.kernel))
1636
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001637 @property
1638 def bindir_native(self):
1639 result = self.get('STAGING_BINDIR_NATIVE')
1640 if result and os.path.exists(result):
1641 return result
1642
Andrew Geissler82c905d2020-04-13 13:39:40 -05001643 cmd = ['bitbake', '-e']
1644 multiconfig = self.get('MULTICONFIG')
1645 if multiconfig:
1646 cmd.append('mc:%s:qemu-helper-native' % multiconfig)
1647 else:
1648 cmd.append('qemu-helper-native')
1649
Brad Bishop977dc1a2019-02-06 16:01:43 -05001650 logger.info('Running %s...' % str(cmd))
1651 out = subprocess.check_output(cmd).decode('utf-8')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001652
1653 match = re.search('^STAGING_BINDIR_NATIVE="(.*)"', out, re.M)
1654 if match:
1655 result = match.group(1)
1656 if os.path.exists(result):
1657 self.set('STAGING_BINDIR_NATIVE', result)
1658 return result
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001659 raise RunQemuError("Native sysroot directory %s doesn't exist" % result)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001660 else:
Andrew Geisslerfc113ea2023-03-31 09:59:46 -05001661 raise RunQemuError("Can't find STAGING_BINDIR_NATIVE in '%s' output" % str(cmd))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001662
1663
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001664def main():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001665 if "help" in sys.argv or '-h' in sys.argv or '--help' in sys.argv:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001666 print_usage()
1667 return 0
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001668 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001669 config = BaseConfig()
Brad Bishop004d4992018-10-02 23:54:45 +02001670
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001671 renice = os.path.expanduser("~/bin/runqemu-renice")
1672 if os.path.exists(renice):
1673 logger.info('Using %s to renice' % renice)
1674 subprocess.check_call([renice, str(os.getpid())])
1675
Brad Bishop004d4992018-10-02 23:54:45 +02001676 def sigterm_handler(signum, frame):
Andrew Geissler6aa7eec2023-03-03 12:41:14 -06001677 logger.info("Received signal: %s" % (signum))
Brad Bishop004d4992018-10-02 23:54:45 +02001678 config.cleanup()
Brad Bishop004d4992018-10-02 23:54:45 +02001679 signal.signal(signal.SIGTERM, sigterm_handler)
1680
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001681 config.check_args()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001682 config.read_qemuboot()
1683 config.check_and_set()
1684 # Check whether the combos is valid or not
1685 config.validate_combos()
1686 config.print_config()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001687 config.setup_network()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001688 config.setup_rootfs()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001689 config.setup_final()
Andrew Geissler517393d2023-01-13 08:55:19 -06001690 config.setup_cmd()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001691 config.start_qemu()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001692 except RunQemuError as err:
1693 logger.error(err)
1694 return 1
1695 except Exception as err:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001696 import traceback
1697 traceback.print_exc()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001698 return 1
1699 finally:
Andrew Geissler517393d2023-01-13 08:55:19 -06001700 config.cleanup_cmd()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001701 config.cleanup()
1702
1703if __name__ == "__main__":
1704 sys.exit(main())