blob: e5e66f34535296ce7a2295d242e2aca6a98996f9 [file] [log] [blame]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001#!/usr/bin/env python3
2
Patrick Williamsc124f4f2015-09-15 14:41:29 -05003# Handle running OE images standalone with QEMU
4#
5# Copyright (C) 2006-2011 Linux Foundation
Patrick Williamsc0f7c042017-02-23 20:41:17 -06006# Copyright (c) 2016 Wind River Systems, Inc.
Patrick Williamsc124f4f2015-09-15 14:41:29 -05007#
Brad Bishopc342db32019-05-15 21:57:59 -04008# SPDX-License-Identifier: GPL-2.0-only
Patrick Williamsc124f4f2015-09-15 14:41:29 -05009#
Patrick Williamsc124f4f2015-09-15 14:41:29 -050010
Patrick Williamsc0f7c042017-02-23 20:41:17 -060011import os
12import sys
13import logging
14import subprocess
15import re
16import fcntl
17import shutil
18import glob
19import configparser
Brad Bishop004d4992018-10-02 23:54:45 +020020import signal
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050021
Brad Bishopd7bf8c12018-02-25 22:55:05 -050022class RunQemuError(Exception):
23 """Custom exception to raise on known errors."""
24 pass
25
26class OEPathError(RunQemuError):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060027 """Custom Exception to give better guidance on missing binaries"""
28 def __init__(self, message):
Brad Bishopd7bf8c12018-02-25 22:55:05 -050029 super().__init__("In order for this script to dynamically infer paths\n \
Patrick Williamsc0f7c042017-02-23 20:41:17 -060030kernels or filesystem images, you either need bitbake in your PATH\n \
31or to source oe-init-build-env before running this script.\n\n \
32Dynamic path inference can be avoided by passing a *.qemuboot.conf to\n \
Brad Bishopd7bf8c12018-02-25 22:55:05 -050033runqemu, i.e. `runqemu /path/to/my-image-name.qemuboot.conf`\n\n %s" % message)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060034
35
36def create_logger():
37 logger = logging.getLogger('runqemu')
38 logger.setLevel(logging.INFO)
39
40 # create console handler and set level to debug
41 ch = logging.StreamHandler()
Brad Bishopd7bf8c12018-02-25 22:55:05 -050042 ch.setLevel(logging.DEBUG)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060043
44 # create formatter
45 formatter = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
46
47 # add formatter to ch
48 ch.setFormatter(formatter)
49
50 # add ch to logger
51 logger.addHandler(ch)
52
53 return logger
54
55logger = create_logger()
56
57def print_usage():
58 print("""
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050059Usage: you can run this script with any valid combination
60of the following environment variables (in any order):
61 KERNEL - the kernel image file to use
Brad Bishopc68388fc2019-08-26 01:33:31 -040062 BIOS - the bios image file to use
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050063 ROOTFS - the rootfs image file or nfsroot directory to use
Brad Bishop316dfdd2018-06-25 12:45:53 -040064 DEVICE_TREE - the device tree blob to use
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050065 MACHINE - the machine name (optional, autodetected from KERNEL filename if unspecified)
66 Simplified QEMU command-line options can be passed with:
Patrick Williamsc0f7c042017-02-23 20:41:17 -060067 nographic - disable video console
Brad Bishopa34c0302019-09-23 22:34:48 -040068 sdl - choose the SDL UI frontend
69 gtk - choose the Gtk UI frontend
Brad Bishop6dbb3162019-11-25 09:41:34 -050070 gl - enable virgl-based GL acceleration (also needs gtk or sdl options)
71 gl-es - enable virgl-based GL acceleration, using OpenGL ES (also needs gtk or sdl options)
72 egl-headless - enable headless EGL output; use vnc (via publicvnc option) or spice to see it
Patrick Williamsc0f7c042017-02-23 20:41:17 -060073 serial - enable a serial console on /dev/ttyS0
Brad Bishop19323692019-04-05 15:28:33 -040074 serialstdio - enable a serial console on the console (regardless of graphics mode)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060075 slirp - enable user networking, no root privileges is required
Brad Bishopa34c0302019-09-23 22:34:48 -040076 snapshot - don't write changes to back to images
Patrick Williamsc0f7c042017-02-23 20:41:17 -060077 kvm - enable KVM when running x86/x86_64 (VT-capable CPU required)
78 kvm-vhost - enable KVM with vhost when running x86/x86_64 (VT-capable CPU required)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050079 publicvnc - enable a VNC server open to all hosts
Patrick Williamsc0f7c042017-02-23 20:41:17 -060080 audio - enable audio
Brad Bishop6e60e8b2018-02-01 10:27:11 -050081 [*/]ovmf* - OVMF firmware file or base name for booting with UEFI
Patrick Williamsc0f7c042017-02-23 20:41:17 -060082 tcpserial=<port> - specify tcp serial port number
Patrick Williamsc0f7c042017-02-23 20:41:17 -060083 qemuparams=<xyz> - specify custom parameters to QEMU
84 bootparams=<xyz> - specify custom kernel parameters during boot
Brad Bishop6e60e8b2018-02-01 10:27:11 -050085 help, -h, --help: print this text
Brad Bishopd7bf8c12018-02-25 22:55:05 -050086 -d, --debug: Enable debug output
Brad Bishop79641f22019-09-10 07:20:22 -040087 -q, --quiet: Hide most output except error messages
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050088
89Examples:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050090 runqemu
Patrick Williamsc0f7c042017-02-23 20:41:17 -060091 runqemu qemuarm
92 runqemu tmp/deploy/images/qemuarm
Brad Bishop6e60e8b2018-02-01 10:27:11 -050093 runqemu tmp/deploy/images/qemux86/<qemuboot.conf>
Patrick Williamsc0f7c042017-02-23 20:41:17 -060094 runqemu qemux86-64 core-image-sato ext4
95 runqemu qemux86-64 wic-image-minimal wic
96 runqemu path/to/bzImage-qemux86.bin path/to/nfsrootdir/ serial
Brad Bishopd7bf8c12018-02-25 22:55:05 -050097 runqemu qemux86 iso/hddimg/wic.vmdk/wic.qcow2/wic.vdi/ramfs/cpio.gz...
Patrick Williamsc0f7c042017-02-23 20:41:17 -060098 runqemu qemux86 qemuparams="-m 256"
99 runqemu qemux86 bootparams="psplash=false"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600100 runqemu path/to/<image>-<machine>.wic
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500101 runqemu path/to/<image>-<machine>.wic.vmdk
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600102""")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500103
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600104def check_tun():
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500105 """Check /dev/net/tun"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600106 dev_tun = '/dev/net/tun'
107 if not os.path.exists(dev_tun):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500108 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 -0500109
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600110 if not os.access(dev_tun, os.W_OK):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500111 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 -0500112
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600113def get_first_file(cmds):
114 """Return first file found in wildcard cmds"""
115 for cmd in cmds:
116 all_files = glob.glob(cmd)
117 if all_files:
118 for f in all_files:
119 if not os.path.isdir(f):
120 return f
121 return ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500122
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600123class BaseConfig(object):
124 def __init__(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500125 # The self.d saved vars from self.set(), part of them are from qemuboot.conf
126 self.d = {'QB_KERNEL_ROOT': '/dev/vda'}
127
128 # Supported env vars, add it here if a var can be got from env,
129 # and don't use os.getenv in the code.
130 self.env_vars = ('MACHINE',
131 'ROOTFS',
132 'KERNEL',
Brad Bishopc68388fc2019-08-26 01:33:31 -0400133 'BIOS',
Brad Bishop316dfdd2018-06-25 12:45:53 -0400134 'DEVICE_TREE',
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500135 'DEPLOY_DIR_IMAGE',
136 'OE_TMPDIR',
137 'OECORE_NATIVE_SYSROOT',
Andrew Geissler82c905d2020-04-13 13:39:40 -0500138 'MULTICONFIG',
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500139 )
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500140
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600141 self.qemu_opt = ''
142 self.qemu_opt_script = ''
Andrew Geissler99467da2019-02-25 18:54:23 -0600143 self.qemuparams = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600144 self.clean_nfs_dir = False
145 self.nfs_server = ''
146 self.rootfs = ''
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500147 # File name(s) of a OVMF firmware file or variable store,
148 # to be added with -drive if=pflash.
149 # Found in the same places as the rootfs, with or without one of
150 # these suffices: qcow2, bin.
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500151 self.ovmf_bios = []
Brad Bishop08902b02019-08-20 09:16:51 -0400152 # When enrolling default Secure Boot keys, the hypervisor
153 # must provide the Platform Key and the first Key Exchange Key
154 # certificate in the Type 11 SMBIOS table.
155 self.ovmf_secboot_pkkek1 = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600156 self.qemuboot = ''
157 self.qbconfload = False
158 self.kernel = ''
Brad Bishopc68388fc2019-08-26 01:33:31 -0400159 self.bios = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600160 self.kernel_cmdline = ''
161 self.kernel_cmdline_script = ''
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500162 self.bootparams = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600163 self.dtb = ''
164 self.fstype = ''
165 self.kvm_enabled = False
166 self.vhost_enabled = False
167 self.slirp_enabled = False
Andrew Geissler82c905d2020-04-13 13:39:40 -0500168 self.net_bridge = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600169 self.nfs_instance = 0
170 self.nfs_running = False
Brad Bishop19323692019-04-05 15:28:33 -0400171 self.serialconsole = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600172 self.serialstdio = False
173 self.cleantap = False
174 self.saved_stty = ''
175 self.audio_enabled = False
176 self.tcpserial_portnum = ''
Brad Bishop08902b02019-08-20 09:16:51 -0400177 self.taplock = ''
178 self.taplock_descriptor = None
179 self.portlocks = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600180 self.bitbake_e = ''
181 self.snapshot = False
Brad Bishop15ae2502019-06-18 21:44:24 -0400182 self.wictypes = ('wic', 'wic.vmdk', 'wic.qcow2', 'wic.vdi')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500183 self.fstypes = ('ext2', 'ext3', 'ext4', 'jffs2', 'nfs', 'btrfs',
184 'cpio.gz', 'cpio', 'ramfs', 'tar.bz2', 'tar.gz')
Brad Bishop08902b02019-08-20 09:16:51 -0400185 self.vmtypes = ('hddimg', 'iso')
Brad Bishop15ae2502019-06-18 21:44:24 -0400186 self.fsinfo = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500187 self.network_device = "-device e1000,netdev=net0,mac=@MAC@"
Andrew Geissler82c905d2020-04-13 13:39:40 -0500188 self.cmdline_ip_slirp = "ip=dhcp"
189 self.cmdline_ip_tap = "ip=192.168.7.@CLIENT@::192.168.7.@GATEWAY@:255.255.255.0"
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500190 # Use different mac section for tap and slirp to avoid
191 # conflicts, e.g., when one is running with tap, the other is
192 # running with slirp.
193 # The last section is dynamic, which is for avoiding conflicts,
194 # when multiple qemus are running, e.g., when multiple tap or
195 # slirp qemus are running.
196 self.mac_tap = "52:54:00:12:34:"
197 self.mac_slirp = "52:54:00:12:35:"
Brad Bishop004d4992018-10-02 23:54:45 +0200198 # pid of the actual qemu process
199 self.qemupid = None
200 # avoid cleanup twice
201 self.cleaned = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500202
Brad Bishop08902b02019-08-20 09:16:51 -0400203 def acquire_taplock(self, error=True):
204 logger.debug("Acquiring lockfile %s..." % self.taplock)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600205 try:
Brad Bishop08902b02019-08-20 09:16:51 -0400206 self.taplock_descriptor = open(self.taplock, 'w')
207 fcntl.flock(self.taplock_descriptor, fcntl.LOCK_EX|fcntl.LOCK_NB)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600208 except Exception as e:
Brad Bishop08902b02019-08-20 09:16:51 -0400209 msg = "Acquiring lockfile %s failed: %s" % (self.taplock, e)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500210 if error:
211 logger.error(msg)
212 else:
213 logger.info(msg)
Brad Bishop08902b02019-08-20 09:16:51 -0400214 if self.taplock_descriptor:
215 self.taplock_descriptor.close()
216 self.taplock_descriptor = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600217 return False
218 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500219
Brad Bishop08902b02019-08-20 09:16:51 -0400220 def release_taplock(self):
221 if self.taplock_descriptor:
Brad Bishopf86d0552018-12-04 14:18:15 -0800222 logger.debug("Releasing lockfile for tap device '%s'" % self.tap)
Brad Bishop08902b02019-08-20 09:16:51 -0400223 fcntl.flock(self.taplock_descriptor, fcntl.LOCK_UN)
224 self.taplock_descriptor.close()
225 os.remove(self.taplock)
226 self.taplock_descriptor = None
227
228 def check_free_port(self, host, port, lockdir):
229 """ Check whether the port is free or not """
230 import socket
231 from contextlib import closing
232
233 lockfile = os.path.join(lockdir, str(port) + '.lock')
234 if self.acquire_portlock(lockfile):
235 with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
236 if sock.connect_ex((host, port)) == 0:
237 # Port is open, so not free
238 self.release_portlock(lockfile)
239 return False
240 else:
241 # Port is not open, so free
242 return True
243 else:
244 return False
245
246 def acquire_portlock(self, lockfile):
247 logger.debug("Acquiring lockfile %s..." % lockfile)
248 try:
249 portlock_descriptor = open(lockfile, 'w')
250 self.portlocks.update({lockfile: portlock_descriptor})
251 fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_EX|fcntl.LOCK_NB)
252 except Exception as e:
253 msg = "Acquiring lockfile %s failed: %s" % (lockfile, e)
254 logger.info(msg)
255 if lockfile in self.portlocks.keys() and self.portlocks[lockfile]:
256 self.portlocks[lockfile].close()
257 del self.portlocks[lockfile]
258 return False
259 return True
260
261 def release_portlock(self, lockfile=None):
262 if lockfile != None:
263 logger.debug("Releasing lockfile '%s'" % lockfile)
264 fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_UN)
265 self.portlocks[lockfile].close()
266 os.remove(lockfile)
267 del self.portlocks[lockfile]
268 elif len(self.portlocks):
269 for lockfile, descriptor in self.portlocks.items():
270 logger.debug("Releasing lockfile '%s'" % lockfile)
271 fcntl.flock(descriptor, fcntl.LOCK_UN)
272 descriptor.close()
273 os.remove(lockfile)
274 self.portlocks = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500275
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600276 def get(self, key):
277 if key in self.d:
278 return self.d.get(key)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500279 elif os.getenv(key):
280 return os.getenv(key)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600281 else:
282 return ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500283
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600284 def set(self, key, value):
285 self.d[key] = value
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500286
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600287 def is_deploy_dir_image(self, p):
288 if os.path.isdir(p):
289 if not re.search('.qemuboot.conf$', '\n'.join(os.listdir(p)), re.M):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500290 logger.debug("Can't find required *.qemuboot.conf in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600291 return False
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500292 if not any(map(lambda name: '-image-' in name, os.listdir(p))):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500293 logger.debug("Can't find *-image-* in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600294 return False
295 return True
296 else:
297 return False
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500298
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600299 def check_arg_fstype(self, fst):
300 """Check and set FSTYPE"""
Brad Bishop15ae2502019-06-18 21:44:24 -0400301 if fst not in self.fstypes + self.vmtypes + self.wictypes:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800302 logger.warning("Maybe unsupported FSTYPE: %s" % fst)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600303 if not self.fstype or self.fstype == fst:
304 if fst == 'ramfs':
305 fst = 'cpio.gz'
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500306 if fst in ('tar.bz2', 'tar.gz'):
307 fst = 'nfs'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600308 self.fstype = fst
309 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500310 raise RunQemuError("Conflicting: FSTYPE %s and %s" % (self.fstype, fst))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500311
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600312 def set_machine_deploy_dir(self, machine, deploy_dir_image):
313 """Set MACHINE and DEPLOY_DIR_IMAGE"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500314 logger.debug('MACHINE: %s' % machine)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600315 self.set("MACHINE", machine)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500316 logger.debug('DEPLOY_DIR_IMAGE: %s' % deploy_dir_image)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600317 self.set("DEPLOY_DIR_IMAGE", deploy_dir_image)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500318
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600319 def check_arg_nfs(self, p):
320 if os.path.isdir(p):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500321 self.rootfs = p
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600322 else:
323 m = re.match('(.*):(.*)', p)
324 self.nfs_server = m.group(1)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500325 self.rootfs = m.group(2)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600326 self.check_arg_fstype('nfs')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500327
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600328 def check_arg_path(self, p):
329 """
330 - Check whether it is <image>.qemuboot.conf or contains <image>.qemuboot.conf
331 - Check whether is a kernel file
332 - Check whether is a image file
333 - Check whether it is a nfs dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500334 - Check whether it is a OVMF flash file
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600335 """
336 if p.endswith('.qemuboot.conf'):
337 self.qemuboot = p
338 self.qbconfload = True
339 elif re.search('\.bin$', p) or re.search('bzImage', p) or \
340 re.search('zImage', p) or re.search('vmlinux', p) or \
341 re.search('fitImage', p) or re.search('uImage', p):
342 self.kernel = p
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500343 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 -0600344 self.rootfs = p
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500345 # Check filename against self.fstypes can hanlde <file>.cpio.gz,
346 # otherwise, its type would be "gz", which is incorrect.
347 fst = ""
348 for t in self.fstypes:
349 if p.endswith(t):
350 fst = t
351 break
352 if not fst:
353 m = re.search('.*\.(.*)$', self.rootfs)
354 if m:
355 fst = m.group(1)
356 if fst:
357 self.check_arg_fstype(fst)
358 qb = re.sub('\.' + fst + "$", '', self.rootfs)
359 qb = '%s%s' % (re.sub('\.rootfs$', '', qb), '.qemuboot.conf')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600360 if os.path.exists(qb):
361 self.qemuboot = qb
362 self.qbconfload = True
363 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800364 logger.warning("%s doesn't exist" % qb)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600365 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500366 raise RunQemuError("Can't find FSTYPE from: %s" % p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500367
368 elif os.path.isdir(p) or re.search(':', p) and re.search('/', p):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600369 if self.is_deploy_dir_image(p):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500370 logger.debug('DEPLOY_DIR_IMAGE: %s' % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600371 self.set("DEPLOY_DIR_IMAGE", p)
372 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500373 logger.debug("Assuming %s is an nfs rootfs" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600374 self.check_arg_nfs(p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500375 elif os.path.basename(p).startswith('ovmf'):
376 self.ovmf_bios.append(p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600377 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500378 raise RunQemuError("Unknown path arg %s" % p)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500379
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600380 def check_arg_machine(self, arg):
381 """Check whether it is a machine"""
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500382 if self.get('MACHINE') == arg:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600383 return
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500384 elif self.get('MACHINE') and self.get('MACHINE') != arg:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500385 raise RunQemuError("Maybe conflicted MACHINE: %s vs %s" % (self.get('MACHINE'), arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500386 elif re.search('/', arg):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500387 raise RunQemuError("Unknown arg: %s" % arg)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500388
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500389 logger.debug('Assuming MACHINE = %s' % arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500390
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600391 # if we're running under testimage, or similarly as a child
392 # of an existing bitbake invocation, we can't invoke bitbake
393 # to validate the MACHINE setting and must assume it's correct...
394 # FIXME: testimage.bbclass exports these two variables into env,
395 # are there other scenarios in which we need to support being
396 # invoked by bitbake?
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500397 deploy = self.get('DEPLOY_DIR_IMAGE')
398 bbchild = deploy and self.get('OE_TMPDIR')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600399 if bbchild:
400 self.set_machine_deploy_dir(arg, deploy)
401 return
402 # also check whether we're running under a sourced toolchain
403 # environment file
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500404 if self.get('OECORE_NATIVE_SYSROOT'):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600405 self.set("MACHINE", arg)
406 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500407
Andrew Geissler82c905d2020-04-13 13:39:40 -0500408 self.bitbake_e = self.run_bitbake_env(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600409 # bitbake -e doesn't report invalid MACHINE as an error, so
410 # let's check DEPLOY_DIR_IMAGE to make sure that it is a valid
411 # MACHINE.
412 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
413 if s:
414 deploy_dir_image = s.group(1)
415 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500416 raise RunQemuError("bitbake -e %s" % self.bitbake_e)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600417 if self.is_deploy_dir_image(deploy_dir_image):
418 self.set_machine_deploy_dir(arg, deploy_dir_image)
419 else:
420 logger.error("%s not a directory valid DEPLOY_DIR_IMAGE" % deploy_dir_image)
421 self.set("MACHINE", arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500422
Andrew Geisslerc182c622020-05-15 14:13:32 -0500423 def set_dri_path(self):
424 # As runqemu can be run within bitbake (when using testimage, for example),
425 # we need to ensure that we run host pkg-config, and that it does not
426 # get mis-directed to native build paths set by bitbake.
427 try:
428 del os.environ['PKG_CONFIG_PATH']
429 del os.environ['PKG_CONFIG_DIR']
430 del os.environ['PKG_CONFIG_LIBDIR']
431 del os.environ['PKG_CONFIG_SYSROOT_DIR']
432 except KeyError:
433 pass
434 try:
435 dripath = subprocess.check_output("PATH=/bin:/usr/bin:$PATH pkg-config --variable=dridriverdir dri", shell=True)
436 except subprocess.CalledProcessError as e:
437 raise RunQemuError("Could not determine the path to dri drivers on the host via pkg-config.\nPlease install Mesa development files (particularly, dri.pc) on the host machine.")
438 os.environ['LIBGL_DRIVERS_PATH'] = dripath.decode('utf-8').strip()
439
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600440 def check_args(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500441 for debug in ("-d", "--debug"):
442 if debug in sys.argv:
443 logger.setLevel(logging.DEBUG)
444 sys.argv.remove(debug)
445
446 for quiet in ("-q", "--quiet"):
447 if quiet in sys.argv:
448 logger.setLevel(logging.ERROR)
449 sys.argv.remove(quiet)
450
Andrew Geisslerc182c622020-05-15 14:13:32 -0500451 if 'gl' not in sys.argv[1:] and 'gl-es' not in sys.argv[1:]:
452 os.environ['SDL_RENDER_DRIVER'] = 'software'
453
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600454 unknown_arg = ""
455 for arg in sys.argv[1:]:
Brad Bishop15ae2502019-06-18 21:44:24 -0400456 if arg in self.fstypes + self.vmtypes + self.wictypes:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600457 self.check_arg_fstype(arg)
458 elif arg == 'nographic':
Andrew Geisslerc9f78652020-09-18 14:11:35 -0500459 if ('sdl' in sys.argv):
460 raise RunQemuError('Option nographic makes no sense alongside the sdl option.' % (arg))
461 if ('gtk' in sys.argv):
462 raise RunQemuError('Option nographic makes no sense alongside the gtk option.' % (arg))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600463 self.qemu_opt_script += ' -nographic'
464 self.kernel_cmdline_script += ' console=ttyS0'
Brad Bishop19323692019-04-05 15:28:33 -0400465 elif arg == 'sdl':
Brad Bishop6dbb3162019-11-25 09:41:34 -0500466 if 'gl' in sys.argv[1:]:
Andrew Geisslerc182c622020-05-15 14:13:32 -0500467 self.set_dri_path()
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500468 self.qemu_opt_script += ' -vga virtio -display sdl,gl=on,show-cursor=on'
Brad Bishop6dbb3162019-11-25 09:41:34 -0500469 elif 'gl-es' in sys.argv[1:]:
Andrew Geisslerc182c622020-05-15 14:13:32 -0500470 self.set_dri_path()
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500471 self.qemu_opt_script += ' -vga virtio -display sdl,gl=es,show-cursor=on'
Brad Bishop6dbb3162019-11-25 09:41:34 -0500472 else:
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500473 self.qemu_opt_script += ' -display sdl,show-cursor=on'
Brad Bishopa34c0302019-09-23 22:34:48 -0400474 elif arg == 'gtk':
475 if 'gl' in sys.argv[1:]:
Andrew Geisslerc182c622020-05-15 14:13:32 -0500476 self.set_dri_path()
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500477 self.qemu_opt_script += ' -vga virtio -display gtk,gl=on,show-cursor=on'
Brad Bishopa34c0302019-09-23 22:34:48 -0400478 elif 'gl-es' in sys.argv[1:]:
Andrew Geisslerc182c622020-05-15 14:13:32 -0500479 self.set_dri_path()
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500480 self.qemu_opt_script += ' -vga virtio -display gtk,gl=es,show-cursor=on'
Brad Bishopa34c0302019-09-23 22:34:48 -0400481 else:
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500482 self.qemu_opt_script += ' -display gtk,show-cursor=on'
Brad Bishopa34c0302019-09-23 22:34:48 -0400483 elif arg == 'gl' or arg == 'gl-es':
484 # These args are handled inside sdl or gtk blocks above
Andrew Geissler635e0e42020-08-21 15:58:33 -0500485 if ('gtk' not in sys.argv) and ('sdl' not in sys.argv):
486 raise RunQemuError('Option %s also needs gtk or sdl option.' % (arg))
Brad Bishop19323692019-04-05 15:28:33 -0400487 elif arg == 'egl-headless':
Andrew Geisslerc182c622020-05-15 14:13:32 -0500488 self.set_dri_path()
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500489 self.qemu_opt_script += ' -vga virtio -display egl-headless,show-cursor=on'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600490 elif arg == 'serial':
491 self.kernel_cmdline_script += ' console=ttyS0'
Brad Bishop19323692019-04-05 15:28:33 -0400492 self.serialconsole = True
493 elif arg == "serialstdio":
494 self.kernel_cmdline_script += ' console=ttyS0'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600495 self.serialstdio = True
496 elif arg == 'audio':
497 logger.info("Enabling audio in qemu")
498 logger.info("Please install sound drivers in linux host")
499 self.audio_enabled = True
500 elif arg == 'kvm':
501 self.kvm_enabled = True
502 elif arg == 'kvm-vhost':
503 self.vhost_enabled = True
504 elif arg == 'slirp':
505 self.slirp_enabled = True
Andrew Geissler82c905d2020-04-13 13:39:40 -0500506 elif arg.startswith('bridge='):
507 self.net_bridge = '%s' % arg[len('bridge='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600508 elif arg == 'snapshot':
509 self.snapshot = True
510 elif arg == 'publicvnc':
511 self.qemu_opt_script += ' -vnc :0'
512 elif arg.startswith('tcpserial='):
Brad Bishop15ae2502019-06-18 21:44:24 -0400513 self.tcpserial_portnum = '%s' % arg[len('tcpserial='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600514 elif arg.startswith('qemuparams='):
Andrew Geissler99467da2019-02-25 18:54:23 -0600515 self.qemuparams = ' %s' % arg[len('qemuparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600516 elif arg.startswith('bootparams='):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500517 self.bootparams = arg[len('bootparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600518 elif os.path.exists(arg) or (re.search(':', arg) and re.search('/', arg)):
519 self.check_arg_path(os.path.abspath(arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500520 elif re.search(r'-image-|-image$', arg):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600521 # Lazy rootfs
522 self.rootfs = arg
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500523 elif arg.startswith('ovmf'):
524 self.ovmf_bios.append(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600525 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500526 # At last, assume it is the MACHINE
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600527 if (not unknown_arg) or unknown_arg == arg:
528 unknown_arg = arg
529 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500530 raise RunQemuError("Can't handle two unknown args: %s %s\n"
531 "Try 'runqemu help' on how to use it" % \
532 (unknown_arg, arg))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600533 # Check to make sure it is a valid machine
Brad Bishopa5c52ff2018-11-23 10:55:50 +1300534 if unknown_arg and self.get('MACHINE') != unknown_arg:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500535 if self.get('DEPLOY_DIR_IMAGE'):
536 machine = os.path.basename(self.get('DEPLOY_DIR_IMAGE'))
537 if unknown_arg == machine:
538 self.set("MACHINE", machine)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500539
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600540 self.check_arg_machine(unknown_arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500541
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500542 if not (self.get('DEPLOY_DIR_IMAGE') or self.qbconfload):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500543 self.load_bitbake_env()
544 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
545 if s:
546 self.set("DEPLOY_DIR_IMAGE", s.group(1))
547
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600548 def check_kvm(self):
549 """Check kvm and kvm-host"""
550 if not (self.kvm_enabled or self.vhost_enabled):
551 self.qemu_opt_script += ' %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU'))
552 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500553
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600554 if not self.get('QB_CPU_KVM'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500555 raise RunQemuError("QB_CPU_KVM is NULL, this board doesn't support kvm")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500556
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600557 self.qemu_opt_script += ' %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU_KVM'))
558 yocto_kvm_wiki = "https://wiki.yoctoproject.org/wiki/How_to_enable_KVM_for_Poky_qemu"
559 yocto_paravirt_kvm_wiki = "https://wiki.yoctoproject.org/wiki/Running_an_x86_Yocto_Linux_image_under_QEMU_KVM"
560 dev_kvm = '/dev/kvm'
561 dev_vhost = '/dev/vhost-net'
Brad Bishop15ae2502019-06-18 21:44:24 -0400562 if self.qemu_system.endswith(('i386', 'x86_64')):
563 with open('/proc/cpuinfo', 'r') as f:
564 kvm_cap = re.search('vmx|svm', "".join(f.readlines()))
565 if not kvm_cap:
566 logger.error("You are trying to enable KVM on a cpu without VT support.")
567 logger.error("Remove kvm from the command-line, or refer:")
568 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500569
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600570 if not os.path.exists(dev_kvm):
571 logger.error("Missing KVM device. Have you inserted kvm modules?")
572 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500573 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500574
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600575 if os.access(dev_kvm, os.W_OK|os.R_OK):
576 self.qemu_opt_script += ' -enable-kvm'
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500577 if self.get('MACHINE') == "qemux86":
578 # Workaround for broken APIC window on pre 4.15 host kernels which causes boot hangs
579 # See YOCTO #12301
580 # On 64 bit we use x2apic
581 self.kernel_cmdline_script += " clocksource=kvm-clock hpet=disable noapic nolapic"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600582 else:
583 logger.error("You have no read or write permission on /dev/kvm.")
584 logger.error("Please change the ownership of this file as described at:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500585 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500586
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600587 if self.vhost_enabled:
588 if not os.path.exists(dev_vhost):
589 logger.error("Missing virtio net device. Have you inserted vhost-net module?")
590 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500591 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500592
Andrew Geissler635e0e42020-08-21 15:58:33 -0500593 if not os.access(dev_vhost, os.W_OK|os.R_OK):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600594 logger.error("You have no read or write permission on /dev/vhost-net.")
595 logger.error("Please change the ownership of this file as described at:")
Andrew Geissler635e0e42020-08-21 15:58:33 -0500596 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500597
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600598 def check_fstype(self):
599 """Check and setup FSTYPE"""
600 if not self.fstype:
601 fstype = self.get('QB_DEFAULT_FSTYPE')
602 if fstype:
603 self.fstype = fstype
604 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500605 raise RunQemuError("FSTYPE is NULL!")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500606
Brad Bishop15ae2502019-06-18 21:44:24 -0400607 # parse QB_FSINFO into dict, e.g. { 'wic': ['no-kernel-in-fs', 'a-flag'], 'ext4': ['another-flag']}
608 wic_fs = False
609 qb_fsinfo = self.get('QB_FSINFO')
610 if qb_fsinfo:
611 qb_fsinfo = qb_fsinfo.split()
612 for fsinfo in qb_fsinfo:
613 try:
614 fstype, fsflag = fsinfo.split(':')
615
616 if fstype == 'wic':
617 if fsflag == 'no-kernel-in-fs':
618 wic_fs = True
619 elif fsflag == 'kernel-in-fs':
620 wic_fs = False
621 else:
622 logger.warn('Unknown flag "%s:%s" in QB_FSINFO', fstype, fsflag)
623 continue
624 else:
625 logger.warn('QB_FSINFO is not supported for image type "%s"', fstype)
626 continue
627
628 if fstype in self.fsinfo:
629 self.fsinfo[fstype].append(fsflag)
630 else:
631 self.fsinfo[fstype] = [fsflag]
632 except Exception:
633 logger.error('Invalid parameter "%s" in QB_FSINFO', fsinfo)
634
635 # treat wic images as vmimages (with kernel) or as fsimages (rootfs only)
636 if wic_fs:
637 self.fstypes = self.fstypes + self.wictypes
638 else:
639 self.vmtypes = self.vmtypes + self.wictypes
640
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600641 def check_rootfs(self):
642 """Check and set rootfs"""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500643
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500644 if self.fstype == "none":
645 return
646
647 if self.get('ROOTFS'):
648 if not self.rootfs:
649 self.rootfs = self.get('ROOTFS')
650 elif self.get('ROOTFS') != self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500651 raise RunQemuError("Maybe conflicted ROOTFS: %s vs %s" % (self.get('ROOTFS'), self.rootfs))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500652
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600653 if self.fstype == 'nfs':
654 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500655
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600656 if self.rootfs and not os.path.exists(self.rootfs):
657 # Lazy rootfs
658 self.rootfs = "%s/%s-%s.%s" % (self.get('DEPLOY_DIR_IMAGE'),
659 self.rootfs, self.get('MACHINE'),
660 self.fstype)
661 elif not self.rootfs:
662 cmd_name = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_NAME'), self.fstype)
663 cmd_link = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'), self.fstype)
664 cmds = (cmd_name, cmd_link)
665 self.rootfs = get_first_file(cmds)
666 if not self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500667 raise RunQemuError("Failed to find rootfs: %s or %s" % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500668
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600669 if not os.path.exists(self.rootfs):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500670 raise RunQemuError("Can't find rootfs: %s" % self.rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500671
Brad Bishop08902b02019-08-20 09:16:51 -0400672 def setup_pkkek1(self):
673 """
674 Extract from PEM certificate the Platform Key and first Key
675 Exchange Key certificate string. The hypervisor needs to provide
676 it in the Type 11 SMBIOS table
677 """
678 pemcert = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), 'OvmfPkKek1.pem')
679 try:
680 with open(pemcert, 'r') as pemfile:
681 key = pemfile.read().replace('\n', ''). \
682 replace('-----BEGIN CERTIFICATE-----', ''). \
683 replace('-----END CERTIFICATE-----', '')
684 self.ovmf_secboot_pkkek1 = key
685
686 except FileNotFoundError:
687 raise RunQemuError("Can't open PEM certificate %s " % pemcert)
688
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500689 def check_ovmf(self):
690 """Check and set full path for OVMF firmware and variable file(s)."""
691
692 for index, ovmf in enumerate(self.ovmf_bios):
693 if os.path.exists(ovmf):
694 continue
695 for suffix in ('qcow2', 'bin'):
696 path = '%s/%s.%s' % (self.get('DEPLOY_DIR_IMAGE'), ovmf, suffix)
697 if os.path.exists(path):
698 self.ovmf_bios[index] = path
Brad Bishop08902b02019-08-20 09:16:51 -0400699 if ovmf.endswith('secboot'):
700 self.setup_pkkek1()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500701 break
702 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500703 raise RunQemuError("Can't find OVMF firmware: %s" % ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500704
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600705 def check_kernel(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400706 """Check and set kernel"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600707 # The vm image doesn't need a kernel
708 if self.fstype in self.vmtypes:
709 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500710
Brad Bishop316dfdd2018-06-25 12:45:53 -0400711 # See if the user supplied a KERNEL option
712 if self.get('KERNEL'):
713 self.kernel = self.get('KERNEL')
714
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500715 # QB_DEFAULT_KERNEL is always a full file path
716 kernel_name = os.path.basename(self.get('QB_DEFAULT_KERNEL'))
717
718 # The user didn't want a kernel to be loaded
Brad Bishop316dfdd2018-06-25 12:45:53 -0400719 if kernel_name == "none" and not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500720 return
721
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600722 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
723 if not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500724 kernel_match_name = "%s/%s" % (deploy_dir_image, kernel_name)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600725 kernel_match_link = "%s/%s" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
726 kernel_startswith = "%s/%s*" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
727 cmds = (kernel_match_name, kernel_match_link, kernel_startswith)
728 self.kernel = get_first_file(cmds)
729 if not self.kernel:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500730 raise RunQemuError('KERNEL not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500731
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600732 if not os.path.exists(self.kernel):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500733 raise RunQemuError("KERNEL %s not found" % self.kernel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500734
Brad Bishop316dfdd2018-06-25 12:45:53 -0400735 def check_dtb(self):
736 """Check and set dtb"""
737 # Did the user specify a device tree?
738 if self.get('DEVICE_TREE'):
739 self.dtb = self.get('DEVICE_TREE')
740 if not os.path.exists(self.dtb):
741 raise RunQemuError('Specified DTB not found: %s' % self.dtb)
742 return
743
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600744 dtb = self.get('QB_DTB')
745 if dtb:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400746 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600747 cmd_match = "%s/%s" % (deploy_dir_image, dtb)
748 cmd_startswith = "%s/%s*" % (deploy_dir_image, dtb)
749 cmd_wild = "%s/*.dtb" % deploy_dir_image
750 cmds = (cmd_match, cmd_startswith, cmd_wild)
751 self.dtb = get_first_file(cmds)
752 if not os.path.exists(self.dtb):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500753 raise RunQemuError('DTB not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500754
Brad Bishopc68388fc2019-08-26 01:33:31 -0400755 def check_bios(self):
756 """Check and set bios"""
757
758 # See if the user supplied a BIOS option
759 if self.get('BIOS'):
760 self.bios = self.get('BIOS')
761
762 # QB_DEFAULT_BIOS is always a full file path
763 bios_name = os.path.basename(self.get('QB_DEFAULT_BIOS'))
764
765 # The user didn't want a bios to be loaded
766 if (bios_name == "" or bios_name == "none") and not self.bios:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600767 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500768
Brad Bishopc68388fc2019-08-26 01:33:31 -0400769 if not self.bios:
770 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
771 self.bios = "%s/%s" % (deploy_dir_image, bios_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500772
Brad Bishopc68388fc2019-08-26 01:33:31 -0400773 if not self.bios:
774 raise RunQemuError('BIOS not found: %s' % bios_match_name)
775
776 if not os.path.exists(self.bios):
777 raise RunQemuError("KERNEL %s not found" % self.bios)
778
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500779
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600780 def check_mem(self):
Andrew Geissler99467da2019-02-25 18:54:23 -0600781 """
782 Both qemu and kernel needs memory settings, so check QB_MEM and set it
783 for both.
784 """
785 s = re.search('-m +([0-9]+)', self.qemuparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600786 if s:
787 self.set('QB_MEM', '-m %s' % s.group(1))
788 elif not self.get('QB_MEM'):
Brad Bishop79641f22019-09-10 07:20:22 -0400789 logger.info('QB_MEM is not set, use 256M by default')
790 self.set('QB_MEM', '-m 256')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500791
Andrew Geissler99467da2019-02-25 18:54:23 -0600792 # Check and remove M or m suffix
793 qb_mem = self.get('QB_MEM')
794 if qb_mem.endswith('M') or qb_mem.endswith('m'):
795 qb_mem = qb_mem[:-1]
796
797 # Add -m prefix it not present
798 if not qb_mem.startswith('-m'):
799 qb_mem = '-m %s' % qb_mem
800
801 self.set('QB_MEM', qb_mem)
802
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800803 mach = self.get('MACHINE')
804 if not mach.startswith('qemumips'):
805 self.kernel_cmdline_script += ' mem=%s' % self.get('QB_MEM').replace('-m','').strip() + 'M'
806
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600807 self.qemu_opt_script += ' %s' % self.get('QB_MEM')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500808
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600809 def check_tcpserial(self):
810 if self.tcpserial_portnum:
Brad Bishop15ae2502019-06-18 21:44:24 -0400811 ports = self.tcpserial_portnum.split(':')
812 port = ports[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600813 if self.get('QB_TCPSERIAL_OPT'):
Brad Bishop15ae2502019-06-18 21:44:24 -0400814 self.qemu_opt_script += ' ' + self.get('QB_TCPSERIAL_OPT').replace('@PORT@', port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600815 else:
Brad Bishop15ae2502019-06-18 21:44:24 -0400816 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
817
818 if len(ports) > 1:
819 for port in ports[1:]:
820 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500821
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600822 def check_and_set(self):
823 """Check configs sanity and set when needed"""
824 self.validate_paths()
Andrew Geissler82c905d2020-04-13 13:39:40 -0500825 if not self.slirp_enabled and not self.net_bridge:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500826 check_tun()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600827 # Check audio
828 if self.audio_enabled:
829 if not self.get('QB_AUDIO_DRV'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500830 raise RunQemuError("QB_AUDIO_DRV is NULL, this board doesn't support audio")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600831 if not self.get('QB_AUDIO_OPT'):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800832 logger.warning('QB_AUDIO_OPT is NULL, you may need define it to make audio work')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600833 else:
834 self.qemu_opt_script += ' %s' % self.get('QB_AUDIO_OPT')
835 os.putenv('QEMU_AUDIO_DRV', self.get('QB_AUDIO_DRV'))
836 else:
837 os.putenv('QEMU_AUDIO_DRV', 'none')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500838
Brad Bishop15ae2502019-06-18 21:44:24 -0400839 self.check_qemu_system()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600840 self.check_kvm()
841 self.check_fstype()
842 self.check_rootfs()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500843 self.check_ovmf()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600844 self.check_kernel()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400845 self.check_dtb()
Brad Bishopc68388fc2019-08-26 01:33:31 -0400846 self.check_bios()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600847 self.check_mem()
848 self.check_tcpserial()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500849
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600850 def read_qemuboot(self):
851 if not self.qemuboot:
852 if self.get('DEPLOY_DIR_IMAGE'):
853 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600854 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800855 logger.warning("Can't find qemuboot conf file, DEPLOY_DIR_IMAGE is NULL!")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600856 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500857
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600858 if self.rootfs and not os.path.exists(self.rootfs):
859 # Lazy rootfs
860 machine = self.get('MACHINE')
861 if not machine:
862 machine = os.path.basename(deploy_dir_image)
863 self.qemuboot = "%s/%s-%s.qemuboot.conf" % (deploy_dir_image,
864 self.rootfs, machine)
865 else:
866 cmd = 'ls -t %s/*.qemuboot.conf' % deploy_dir_image
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500867 logger.debug('Running %s...' % cmd)
868 try:
869 qbs = subprocess.check_output(cmd, shell=True).decode('utf-8')
870 except subprocess.CalledProcessError as err:
871 raise RunQemuError(err)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600872 if qbs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500873 for qb in qbs.split():
874 # Don't use initramfs when other choices unless fstype is ramfs
875 if '-initramfs-' in os.path.basename(qb) and self.fstype != 'cpio.gz':
876 continue
877 self.qemuboot = qb
878 break
879 if not self.qemuboot:
880 # Use the first one when no choice
881 self.qemuboot = qbs.split()[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600882 self.qbconfload = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500883
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600884 if not self.qemuboot:
885 # If we haven't found a .qemuboot.conf at this point it probably
886 # doesn't exist, continue without
887 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500888
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600889 if not os.path.exists(self.qemuboot):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500890 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 -0500891
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500892 logger.debug('CONFFILE: %s' % self.qemuboot)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500893
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600894 cf = configparser.ConfigParser()
895 cf.read(self.qemuboot)
896 for k, v in cf.items('config_bsp'):
897 k_upper = k.upper()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500898 if v.startswith("../"):
899 v = os.path.abspath(os.path.dirname(self.qemuboot) + "/" + v)
900 elif v == ".":
901 v = os.path.dirname(self.qemuboot)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600902 self.set(k_upper, v)
903
904 def validate_paths(self):
905 """Ensure all relevant path variables are set"""
906 # When we're started with a *.qemuboot.conf arg assume that image
907 # artefacts are relative to that file, rather than in whatever
908 # directory DEPLOY_DIR_IMAGE in the conf file points to.
909 if self.qbconfload:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500910 imgdir = os.path.realpath(os.path.dirname(self.qemuboot))
911 if imgdir != os.path.realpath(self.get('DEPLOY_DIR_IMAGE')):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600912 logger.info('Setting DEPLOY_DIR_IMAGE to folder containing %s (%s)' % (self.qemuboot, imgdir))
913 self.set('DEPLOY_DIR_IMAGE', imgdir)
914
915 # If the STAGING_*_NATIVE directories from the config file don't exist
916 # and we're in a sourced OE build directory try to extract the paths
917 # from `bitbake -e`
918 havenative = os.path.exists(self.get('STAGING_DIR_NATIVE')) and \
919 os.path.exists(self.get('STAGING_BINDIR_NATIVE'))
920
921 if not havenative:
922 if not self.bitbake_e:
923 self.load_bitbake_env()
924
925 if self.bitbake_e:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500926 native_vars = ['STAGING_DIR_NATIVE']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600927 for nv in native_vars:
928 s = re.search('^%s="(.*)"' % nv, self.bitbake_e, re.M)
929 if s and s.group(1) != self.get(nv):
930 logger.info('Overriding conf file setting of %s to %s from Bitbake environment' % (nv, s.group(1)))
931 self.set(nv, s.group(1))
932 else:
933 # when we're invoked from a running bitbake instance we won't
934 # be able to call `bitbake -e`, then try:
935 # - get OE_TMPDIR from environment and guess paths based on it
936 # - get OECORE_NATIVE_SYSROOT from environment (for sdk)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500937 tmpdir = self.get('OE_TMPDIR')
938 oecore_native_sysroot = self.get('OECORE_NATIVE_SYSROOT')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600939 if tmpdir:
940 logger.info('Setting STAGING_DIR_NATIVE and STAGING_BINDIR_NATIVE relative to OE_TMPDIR (%s)' % tmpdir)
941 hostos, _, _, _, machine = os.uname()
942 buildsys = '%s-%s' % (machine, hostos.lower())
943 staging_dir_native = '%s/sysroots/%s' % (tmpdir, buildsys)
944 self.set('STAGING_DIR_NATIVE', staging_dir_native)
945 elif oecore_native_sysroot:
946 logger.info('Setting STAGING_DIR_NATIVE to OECORE_NATIVE_SYSROOT (%s)' % oecore_native_sysroot)
947 self.set('STAGING_DIR_NATIVE', oecore_native_sysroot)
948 if self.get('STAGING_DIR_NATIVE'):
949 # we have to assume that STAGING_BINDIR_NATIVE is at usr/bin
950 staging_bindir_native = '%s/usr/bin' % self.get('STAGING_DIR_NATIVE')
951 logger.info('Setting STAGING_BINDIR_NATIVE to %s' % staging_bindir_native)
952 self.set('STAGING_BINDIR_NATIVE', '%s/usr/bin' % self.get('STAGING_DIR_NATIVE'))
953
954 def print_config(self):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500955 logoutput = ['Continuing with the following parameters:']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600956 if not self.fstype in self.vmtypes:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500957 logoutput.append('KERNEL: [%s]' % self.kernel)
Brad Bishopc68388fc2019-08-26 01:33:31 -0400958 if self.bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500959 logoutput.append('BIOS: [%s]' % self.bios)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600960 if self.dtb:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500961 logoutput.append('DTB: [%s]' % self.dtb)
962 logoutput.append('MACHINE: [%s]' % self.get('MACHINE'))
Brad Bishop15ae2502019-06-18 21:44:24 -0400963 try:
964 fstype_flags = ' (' + ', '.join(self.fsinfo[self.fstype]) + ')'
965 except KeyError:
966 fstype_flags = ''
Andrew Geissler82c905d2020-04-13 13:39:40 -0500967 logoutput.append('FSTYPE: [%s%s]' % (self.fstype, fstype_flags))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600968 if self.fstype == 'nfs':
Andrew Geissler82c905d2020-04-13 13:39:40 -0500969 logoutput.append('NFS_DIR: [%s]' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600970 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500971 logoutput.append('ROOTFS: [%s]' % self.rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500972 if self.ovmf_bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500973 logoutput.append('OVMF: %s' % self.ovmf_bios)
Brad Bishop08902b02019-08-20 09:16:51 -0400974 if (self.ovmf_secboot_pkkek1):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500975 logoutput.append('SECBOOT PKKEK1: [%s...]' % self.ovmf_secboot_pkkek1[0:100])
976 logoutput.append('CONFFILE: [%s]' % self.qemuboot)
977 logoutput.append('')
978 logger.info('\n'.join(logoutput))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600979
980 def setup_nfs(self):
981 if not self.nfs_server:
982 if self.slirp_enabled:
983 self.nfs_server = '10.0.2.2'
984 else:
985 self.nfs_server = '192.168.7.1'
986
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500987 # Figure out a new nfs_instance to allow multiple qemus running.
Brad Bishop977dc1a2019-02-06 16:01:43 -0500988 ps = subprocess.check_output(("ps", "auxww")).decode('utf-8')
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500989 pattern = '/bin/unfsd .* -i .*\.pid -e .*/exports([0-9]+) '
990 all_instances = re.findall(pattern, ps, re.M)
991 if all_instances:
992 all_instances.sort(key=int)
993 self.nfs_instance = int(all_instances.pop()) + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600994
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500995 nfsd_port = 3049 + 2 * self.nfs_instance
996 mountd_port = 3048 + 2 * self.nfs_instance
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600997
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500998 # Export vars for runqemu-export-rootfs
999 export_dict = {
1000 'NFS_INSTANCE': self.nfs_instance,
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001001 'NFSD_PORT': nfsd_port,
1002 'MOUNTD_PORT': mountd_port,
1003 }
1004 for k, v in export_dict.items():
1005 # Use '%s' since they are integers
1006 os.putenv(k, '%s' % v)
1007
Andrew Geissler82c905d2020-04-13 13:39:40 -05001008 self.unfs_opts="nfsvers=3,port=%s,tcp,mountport=%s" % (nfsd_port, mountd_port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001009
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001010 # Extract .tar.bz2 or .tar.bz if no nfs dir
1011 if not (self.rootfs and os.path.isdir(self.rootfs)):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001012 src_prefix = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'))
1013 dest = "%s-nfsroot" % src_prefix
1014 if os.path.exists('%s.pseudo_state' % dest):
1015 logger.info('Use %s as NFS_DIR' % dest)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001016 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001017 else:
1018 src = ""
1019 src1 = '%s.tar.bz2' % src_prefix
1020 src2 = '%s.tar.gz' % src_prefix
1021 if os.path.exists(src1):
1022 src = src1
1023 elif os.path.exists(src2):
1024 src = src2
1025 if not src:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001026 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 -06001027 logger.info('NFS_DIR not found, extracting %s to %s' % (src, dest))
Brad Bishop977dc1a2019-02-06 16:01:43 -05001028 cmd = ('runqemu-extract-sdk', src, dest)
1029 logger.info('Running %s...' % str(cmd))
1030 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001031 raise RunQemuError('Failed to run %s' % cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001032 self.clean_nfs_dir = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001033 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001034
1035 # Start the userspace NFS server
Brad Bishop977dc1a2019-02-06 16:01:43 -05001036 cmd = ('runqemu-export-rootfs', 'start', self.rootfs)
1037 logger.info('Running %s...' % str(cmd))
1038 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001039 raise RunQemuError('Failed to run %s' % cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001040
1041 self.nfs_running = True
1042
Andrew Geissler82c905d2020-04-13 13:39:40 -05001043 def setup_net_bridge(self):
1044 self.set('NETWORK_CMD', '-netdev bridge,br=%s,id=net0,helper=%s -device virtio-net-pci,netdev=net0 ' % (
1045 self.net_bridge, os.path.join(self.bindir_native, 'qemu-oe-bridge-helper')))
1046
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001047 def setup_slirp(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001048 """Setup user networking"""
1049
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001050 if self.fstype == 'nfs':
1051 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001052 netconf = " " + self.cmdline_ip_slirp
1053 logger.info("Network configuration:%s", netconf)
1054 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001055 # Port mapping
1056 hostfwd = ",hostfwd=tcp::2222-:22,hostfwd=tcp::2323-:23"
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001057 qb_slirp_opt_default = "-netdev user,id=net0%s,tftp=%s" % (hostfwd, self.get('DEPLOY_DIR_IMAGE'))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001058 qb_slirp_opt = self.get('QB_SLIRP_OPT') or qb_slirp_opt_default
1059 # Figure out the port
1060 ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt)
1061 ports = [int(i) for i in ports]
1062 mac = 2
Brad Bishop08902b02019-08-20 09:16:51 -04001063
1064 lockdir = "/tmp/qemu-port-locks"
1065 if not os.path.exists(lockdir):
1066 # There might be a race issue when multi runqemu processess are
1067 # running at the same time.
1068 try:
1069 os.mkdir(lockdir)
1070 os.chmod(lockdir, 0o777)
1071 except FileExistsError:
1072 pass
1073
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001074 # Find a free port to avoid conflicts
1075 for p in ports[:]:
1076 p_new = p
Brad Bishop08902b02019-08-20 09:16:51 -04001077 while not self.check_free_port('localhost', p_new, lockdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001078 p_new += 1
1079 mac += 1
1080 while p_new in ports:
1081 p_new += 1
1082 mac += 1
1083 if p != p_new:
1084 ports.append(p_new)
1085 qb_slirp_opt = re.sub(':%s-' % p, ':%s-' % p_new, qb_slirp_opt)
1086 logger.info("Port forward changed: %s -> %s" % (p, p_new))
1087 mac = "%s%02x" % (self.mac_slirp, mac)
1088 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qb_slirp_opt))
1089 # Print out port foward
1090 hostfwd = re.findall('(hostfwd=[^,]*)', qb_slirp_opt)
1091 if hostfwd:
1092 logger.info('Port forward: %s' % ' '.join(hostfwd))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001093
1094 def setup_tap(self):
1095 """Setup tap"""
1096
1097 # This file is created when runqemu-gen-tapdevs creates a bank of tap
1098 # devices, indicating that the user should not bring up new ones using
1099 # sudo.
1100 nosudo_flag = '/etc/runqemu-nosudo'
1101 self.qemuifup = shutil.which('runqemu-ifup')
1102 self.qemuifdown = shutil.which('runqemu-ifdown')
1103 ip = shutil.which('ip')
1104 lockdir = "/tmp/qemu-tap-locks"
1105
1106 if not (self.qemuifup and self.qemuifdown and ip):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001107 logger.error("runqemu-ifup: %s" % self.qemuifup)
1108 logger.error("runqemu-ifdown: %s" % self.qemuifdown)
1109 logger.error("ip: %s" % ip)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001110 raise OEPathError("runqemu-ifup, runqemu-ifdown or ip not found")
1111
1112 if not os.path.exists(lockdir):
1113 # There might be a race issue when multi runqemu processess are
1114 # running at the same time.
1115 try:
1116 os.mkdir(lockdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001117 os.chmod(lockdir, 0o777)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001118 except FileExistsError:
1119 pass
1120
Brad Bishop977dc1a2019-02-06 16:01:43 -05001121 cmd = (ip, 'link')
1122 logger.debug('Running %s...' % str(cmd))
1123 ip_link = subprocess.check_output(cmd).decode('utf-8')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001124 # Matches line like: 6: tap0: <foo>
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001125 possibles = re.findall('^[0-9]+: +(tap[0-9]+): <.*', ip_link, re.M)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001126 tap = ""
1127 for p in possibles:
1128 lockfile = os.path.join(lockdir, p)
1129 if os.path.exists('%s.skip' % lockfile):
1130 logger.info('Found %s.skip, skipping %s' % (lockfile, p))
1131 continue
Brad Bishop08902b02019-08-20 09:16:51 -04001132 self.taplock = lockfile + '.lock'
1133 if self.acquire_taplock(error=False):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001134 tap = p
1135 logger.info("Using preconfigured tap device %s" % tap)
1136 logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap))
1137 break
1138
1139 if not tap:
1140 if os.path.exists(nosudo_flag):
1141 logger.error("Error: There are no available tap devices to use for networking,")
1142 logger.error("and I see %s exists, so I am not going to try creating" % nosudo_flag)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001143 raise RunQemuError("a new one with sudo.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001144
1145 gid = os.getgid()
1146 uid = os.getuid()
1147 logger.info("Setting up tap interface under sudo")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001148 cmd = ('sudo', self.qemuifup, str(uid), str(gid), self.bindir_native)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001149 try:
1150 tap = subprocess.check_output(cmd).decode('utf-8').strip()
1151 except subprocess.CalledProcessError as e:
1152 logger.error('Setting up tap device failed:\n%s\nRun runqemu-gen-tapdevs to manually create one.' % str(e))
1153 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001154 lockfile = os.path.join(lockdir, tap)
Brad Bishop08902b02019-08-20 09:16:51 -04001155 self.taplock = lockfile + '.lock'
1156 self.acquire_taplock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001157 self.cleantap = True
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001158 logger.debug('Created tap: %s' % tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001159
1160 if not tap:
1161 logger.error("Failed to setup tap device. Run runqemu-gen-tapdevs to manually create.")
Andrew Geissler82c905d2020-04-13 13:39:40 -05001162 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001163 self.tap = tap
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001164 tapnum = int(tap[3:])
1165 gateway = tapnum * 2 + 1
1166 client = gateway + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001167 if self.fstype == 'nfs':
1168 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001169 netconf = " " + self.cmdline_ip_tap
1170 netconf = netconf.replace('@CLIENT@', str(client))
1171 netconf = netconf.replace('@GATEWAY@', str(gateway))
1172 logger.info("Network configuration:%s", netconf)
1173 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001174 mac = "%s%02x" % (self.mac_tap, client)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001175 qb_tap_opt = self.get('QB_TAP_OPT')
1176 if qb_tap_opt:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001177 qemu_tap_opt = qb_tap_opt.replace('@TAP@', tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001178 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001179 qemu_tap_opt = "-netdev tap,id=net0,ifname=%s,script=no,downscript=no" % (self.tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001180
1181 if self.vhost_enabled:
1182 qemu_tap_opt += ',vhost=on'
1183
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001184 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qemu_tap_opt))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001185
1186 def setup_network(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001187 if self.get('QB_NET') == 'none':
1188 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001189 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001190 self.saved_stty = subprocess.check_output(("stty", "-g")).decode('utf-8').strip()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001191 self.network_device = self.get('QB_NETWORK_DEVICE') or self.network_device
Andrew Geissler82c905d2020-04-13 13:39:40 -05001192 if self.net_bridge:
1193 self.setup_net_bridge()
1194 elif self.slirp_enabled:
1195 self.cmdline_ip_slirp = self.get('QB_CMDLINE_IP_SLIRP') or self.cmdline_ip_slirp
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001196 self.setup_slirp()
1197 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001198 self.cmdline_ip_tap = self.get('QB_CMDLINE_IP_TAP') or self.cmdline_ip_tap
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001199 self.setup_tap()
1200
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001201 def setup_rootfs(self):
1202 if self.get('QB_ROOTFS') == 'none':
1203 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001204 if 'wic.' in self.fstype:
1205 self.fstype = self.fstype[4:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001206 rootfs_format = self.fstype if self.fstype in ('vmdk', 'qcow2', 'vdi') else 'raw'
1207
1208 qb_rootfs_opt = self.get('QB_ROOTFS_OPT')
1209 if qb_rootfs_opt:
1210 self.rootfs_options = qb_rootfs_opt.replace('@ROOTFS@', self.rootfs)
1211 else:
1212 self.rootfs_options = '-drive file=%s,if=virtio,format=%s' % (self.rootfs, rootfs_format)
1213
Andrew Geissler82c905d2020-04-13 13:39:40 -05001214 qb_rootfs_extra_opt = self.get("QB_ROOTFS_EXTRA_OPT")
1215 if qb_rootfs_extra_opt and not qb_rootfs_extra_opt.startswith(","):
1216 qb_rootfs_extra_opt = "," + qb_rootfs_extra_opt
1217
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001218 if self.fstype in ('cpio.gz', 'cpio'):
1219 self.kernel_cmdline = 'root=/dev/ram0 rw debugshell'
1220 self.rootfs_options = '-initrd %s' % self.rootfs
1221 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001222 vm_drive = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001223 if self.fstype in self.vmtypes:
1224 if self.fstype == 'iso':
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001225 vm_drive = '-drive file=%s,if=virtio,media=cdrom' % self.rootfs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001226 elif self.get('QB_DRIVE_TYPE'):
1227 drive_type = self.get('QB_DRIVE_TYPE')
1228 if drive_type.startswith("/dev/sd"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001229 logger.info('Using scsi drive')
Andrew Geissler82c905d2020-04-13 13:39:40 -05001230 vm_drive = '-drive if=none,id=hd,file=%s,format=%s -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd%s' \
1231 % (self.rootfs, rootfs_format, qb_rootfs_extra_opt)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001232 elif drive_type.startswith("/dev/hd"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001233 logger.info('Using ide drive')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001234 vm_drive = "-drive file=%s,format=%s" % (self.rootfs, rootfs_format)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001235 elif drive_type.startswith("/dev/vdb"):
1236 logger.info('Using block virtio drive');
1237 vm_drive = '-drive id=disk0,file=%s,if=none,format=%s -device virtio-blk-device,drive=disk0%s' \
1238 % (self.rootfs, rootfs_format,qb_rootfs_extra_opt)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001239 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001240 # virtio might have been selected explicitly (just use it), or
1241 # is used as fallback (then warn about that).
1242 if not drive_type.startswith("/dev/vd"):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001243 logger.warning("Unknown QB_DRIVE_TYPE: %s" % drive_type)
1244 logger.warning("Failed to figure out drive type, consider define or fix QB_DRIVE_TYPE")
1245 logger.warning('Trying to use virtio block drive')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001246 vm_drive = '-drive if=virtio,file=%s,format=%s' % (self.rootfs, rootfs_format)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001247
1248 # All branches above set vm_drive.
Andrew Geissler475cb722020-07-10 16:00:51 -05001249 self.rootfs_options = vm_drive
1250 if not self.fstype in self.vmtypes:
1251 self.rootfs_options += ' -no-reboot'
Brad Bishopf3f93bb2019-10-16 14:33:32 -04001252 self.kernel_cmdline = 'root=%s rw' % (self.get('QB_KERNEL_ROOT'))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001253
1254 if self.fstype == 'nfs':
1255 self.rootfs_options = ''
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001256 k_root = '/dev/nfs nfsroot=%s:%s,%s' % (self.nfs_server, os.path.abspath(self.rootfs), self.unfs_opts)
Brad Bishopf3f93bb2019-10-16 14:33:32 -04001257 self.kernel_cmdline = 'root=%s rw' % k_root
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001258
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001259 if self.fstype == 'none':
1260 self.rootfs_options = ''
1261
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001262 self.set('ROOTFS_OPTIONS', self.rootfs_options)
1263
1264 def guess_qb_system(self):
1265 """attempt to determine the appropriate qemu-system binary"""
1266 mach = self.get('MACHINE')
1267 if not mach:
1268 search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*'
1269 if self.rootfs:
1270 match = re.match(search, self.rootfs)
1271 if match:
1272 mach = match.group(1)
1273 elif self.kernel:
1274 match = re.match(search, self.kernel)
1275 if match:
1276 mach = match.group(1)
1277
1278 if not mach:
1279 return None
1280
1281 if mach == 'qemuarm':
1282 qbsys = 'arm'
1283 elif mach == 'qemuarm64':
1284 qbsys = 'aarch64'
1285 elif mach == 'qemux86':
1286 qbsys = 'i386'
1287 elif mach == 'qemux86-64':
1288 qbsys = 'x86_64'
1289 elif mach == 'qemuppc':
1290 qbsys = 'ppc'
1291 elif mach == 'qemumips':
1292 qbsys = 'mips'
1293 elif mach == 'qemumips64':
1294 qbsys = 'mips64'
1295 elif mach == 'qemumipsel':
1296 qbsys = 'mipsel'
1297 elif mach == 'qemumips64el':
1298 qbsys = 'mips64el'
Brad Bishop316dfdd2018-06-25 12:45:53 -04001299 elif mach == 'qemuriscv64':
1300 qbsys = 'riscv64'
1301 elif mach == 'qemuriscv32':
1302 qbsys = 'riscv32'
Brad Bishop004d4992018-10-02 23:54:45 +02001303 else:
1304 logger.error("Unable to determine QEMU PC System emulator for %s machine." % mach)
1305 logger.error("As %s is not among valid QEMU machines such as," % mach)
1306 logger.error("qemux86-64, qemux86, qemuarm64, qemuarm, qemumips64, qemumips64el, qemumipsel, qemumips, qemuppc")
1307 raise RunQemuError("Set qb_system_name with suitable QEMU PC System emulator in .*qemuboot.conf.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001308
1309 return 'qemu-system-%s' % qbsys
1310
Brad Bishop15ae2502019-06-18 21:44:24 -04001311 def check_qemu_system(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001312 qemu_system = self.get('QB_SYSTEM_NAME')
1313 if not qemu_system:
1314 qemu_system = self.guess_qb_system()
1315 if not qemu_system:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001316 raise RunQemuError("Failed to boot, QB_SYSTEM_NAME is NULL!")
Brad Bishop15ae2502019-06-18 21:44:24 -04001317 self.qemu_system = qemu_system
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001318
Brad Bishop15ae2502019-06-18 21:44:24 -04001319 def setup_final(self):
1320 qemu_bin = os.path.join(self.bindir_native, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001321
1322 # It is possible to have qemu-native in ASSUME_PROVIDED, and it won't
1323 # find QEMU in sysroot, it needs to use host's qemu.
1324 if not os.path.exists(qemu_bin):
1325 logger.info("QEMU binary not found in %s, trying host's QEMU" % qemu_bin)
1326 for path in (os.environ['PATH'] or '').split(':'):
Brad Bishop15ae2502019-06-18 21:44:24 -04001327 qemu_bin_tmp = os.path.join(path, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001328 logger.info("Trying: %s" % qemu_bin_tmp)
1329 if os.path.exists(qemu_bin_tmp):
1330 qemu_bin = qemu_bin_tmp
1331 if not os.path.isabs(qemu_bin):
1332 qemu_bin = os.path.abspath(qemu_bin)
1333 logger.info("Using host's QEMU: %s" % qemu_bin)
1334 break
1335
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001336 if not os.access(qemu_bin, os.X_OK):
1337 raise OEPathError("No QEMU binary '%s' could be found" % qemu_bin)
1338
Andrew Geisslerc3d88e42020-10-02 09:45:00 -05001339 self.qemu_opt = "%s %s %s %s %s" % (qemu_bin, self.get('NETWORK_CMD'), self.get('QB_RNG'), self.get('ROOTFS_OPTIONS'), self.get('QB_OPT_APPEND'))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001340
1341 for ovmf in self.ovmf_bios:
1342 format = ovmf.rsplit('.', 1)[-1]
1343 self.qemu_opt += ' -drive if=pflash,format=%s,file=%s' % (format, ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001344
1345 self.qemu_opt += ' ' + self.qemu_opt_script
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001346
Brad Bishop08902b02019-08-20 09:16:51 -04001347 if self.ovmf_secboot_pkkek1:
1348 # Provide the Platform Key and first Key Exchange Key certificate as an
1349 # OEM string in the SMBIOS Type 11 table. Prepend the certificate string
1350 # with "application prefix" of the EnrollDefaultKeys.efi application
1351 self.qemu_opt += ' -smbios type=11,value=4e32566d-8e9e-4f52-81d3-5bb9715f9727:' \
1352 + self.ovmf_secboot_pkkek1
1353
Andrew Geissler99467da2019-02-25 18:54:23 -06001354 # Append qemuparams to override previous settings
1355 if self.qemuparams:
1356 self.qemu_opt += ' ' + self.qemuparams
1357
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001358 if self.snapshot:
1359 self.qemu_opt += " -snapshot"
1360
Brad Bishop19323692019-04-05 15:28:33 -04001361 if self.serialconsole:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001362 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001363 subprocess.check_call(("stty", "intr", "^]"))
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001364 logger.info("Interrupt character is '^]'")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001365
1366 first_serial = ""
1367 if not re.search("-nographic", self.qemu_opt):
1368 first_serial = "-serial mon:vc"
1369 # We always want a ttyS1. Since qemu by default adds a serial
1370 # port when nodefaults is not specified, it seems that all that
1371 # would be needed is to make sure a "-serial" is there. However,
1372 # it appears that when "-serial" is specified, it ignores the
1373 # default serial port that is normally added. So here we make
1374 # sure to add two -serial if there are none. And only one if
1375 # there is one -serial already.
1376 serial_num = len(re.findall("-serial", self.qemu_opt))
1377 if serial_num == 0:
1378 self.qemu_opt += " %s %s" % (first_serial, self.get("QB_SERIAL_OPT"))
1379 elif serial_num == 1:
1380 self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT")
1381
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001382 # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES),
1383 # if not serial or serialtcp options was specified only ttyS0 is created
1384 # and sysvinit shows an error trying to enable ttyS1:
1385 # INIT: Id "S1" respawning too fast: disabled for 5 minutes
1386 serial_num = len(re.findall("-serial", self.qemu_opt))
1387 if serial_num == 0:
Brad Bishop19323692019-04-05 15:28:33 -04001388 if re.search("-nographic", self.qemu_opt) or self.serialstdio:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001389 self.qemu_opt += " -serial mon:stdio -serial null"
1390 else:
1391 self.qemu_opt += " -serial mon:vc -serial null"
1392
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001393 def start_qemu(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001394 import shlex
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001395 if self.kernel:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001396 kernel_opts = "-kernel %s -append '%s %s %s %s'" % (self.kernel, self.kernel_cmdline,
1397 self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'),
1398 self.bootparams)
Brad Bishopc68388fc2019-08-26 01:33:31 -04001399 if self.bios:
1400 kernel_opts += " -bios %s" % self.bios
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001401 if self.dtb:
1402 kernel_opts += " -dtb %s" % self.dtb
1403 else:
1404 kernel_opts = ""
1405 cmd = "%s %s" % (self.qemu_opt, kernel_opts)
Brad Bishop004d4992018-10-02 23:54:45 +02001406 cmds = shlex.split(cmd)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001407 logger.info('Running %s\n' % cmd)
Brad Bishopf86d0552018-12-04 14:18:15 -08001408 pass_fds = []
Brad Bishop08902b02019-08-20 09:16:51 -04001409 if self.taplock_descriptor:
1410 pass_fds = [self.taplock_descriptor.fileno()]
1411 if len(self.portlocks):
1412 for descriptor in self.portlocks.values():
1413 pass_fds.append(descriptor.fileno())
Brad Bishopf86d0552018-12-04 14:18:15 -08001414 process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds)
Brad Bishop004d4992018-10-02 23:54:45 +02001415 self.qemupid = process.pid
1416 retcode = process.wait()
1417 if retcode:
1418 if retcode == -signal.SIGTERM:
1419 logger.info("Qemu terminated by SIGTERM")
1420 else:
1421 logger.error("Failed to run qemu: %s", process.stderr.read().decode())
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001422
1423 def cleanup(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001424 if self.cleaned:
1425 return
1426
1427 # avoid dealing with SIGTERM when cleanup function is running
1428 signal.signal(signal.SIGTERM, signal.SIG_IGN)
1429
1430 logger.info("Cleaning up")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001431 if self.cleantap:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001432 cmd = ('sudo', self.qemuifdown, self.tap, self.bindir_native)
1433 logger.debug('Running %s' % str(cmd))
1434 subprocess.check_call(cmd)
Brad Bishop08902b02019-08-20 09:16:51 -04001435 self.release_taplock()
1436 self.release_portlock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001437
1438 if self.nfs_running:
1439 logger.info("Shutting down the userspace NFS server...")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001440 cmd = ("runqemu-export-rootfs", "stop", self.rootfs)
1441 logger.debug('Running %s' % str(cmd))
1442 subprocess.check_call(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001443
1444 if self.saved_stty:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001445 subprocess.check_call(("stty", self.saved_stty))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001446
1447 if self.clean_nfs_dir:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001448 logger.info('Removing %s' % self.rootfs)
1449 shutil.rmtree(self.rootfs)
1450 shutil.rmtree('%s.pseudo_state' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001451
Brad Bishop004d4992018-10-02 23:54:45 +02001452 self.cleaned = True
1453
Andrew Geissler82c905d2020-04-13 13:39:40 -05001454 def run_bitbake_env(self, mach=None):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001455 bitbake = shutil.which('bitbake')
1456 if not bitbake:
1457 return
1458
1459 if not mach:
1460 mach = self.get('MACHINE')
1461
Andrew Geissler82c905d2020-04-13 13:39:40 -05001462 multiconfig = self.get('MULTICONFIG')
1463 if multiconfig:
1464 multiconfig = "mc:%s" % multiconfig
1465
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001466 if mach:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001467 cmd = 'MACHINE=%s bitbake -e %s' % (mach, multiconfig)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001468 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001469 cmd = 'bitbake -e %s' % multiconfig
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001470
1471 logger.info('Running %s...' % cmd)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001472 return subprocess.check_output(cmd, shell=True).decode('utf-8')
1473
1474 def load_bitbake_env(self, mach=None):
1475 if self.bitbake_e:
1476 return
1477
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001478 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001479 self.bitbake_e = self.run_bitbake_env(mach=mach)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001480 except subprocess.CalledProcessError as err:
1481 self.bitbake_e = ''
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001482 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 -06001483
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001484 def validate_combos(self):
1485 if (self.fstype in self.vmtypes) and self.kernel:
1486 raise RunQemuError("%s doesn't need kernel %s!" % (self.fstype, self.kernel))
1487
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001488 @property
1489 def bindir_native(self):
1490 result = self.get('STAGING_BINDIR_NATIVE')
1491 if result and os.path.exists(result):
1492 return result
1493
Andrew Geissler82c905d2020-04-13 13:39:40 -05001494 cmd = ['bitbake', '-e']
1495 multiconfig = self.get('MULTICONFIG')
1496 if multiconfig:
1497 cmd.append('mc:%s:qemu-helper-native' % multiconfig)
1498 else:
1499 cmd.append('qemu-helper-native')
1500
Brad Bishop977dc1a2019-02-06 16:01:43 -05001501 logger.info('Running %s...' % str(cmd))
1502 out = subprocess.check_output(cmd).decode('utf-8')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001503
1504 match = re.search('^STAGING_BINDIR_NATIVE="(.*)"', out, re.M)
1505 if match:
1506 result = match.group(1)
1507 if os.path.exists(result):
1508 self.set('STAGING_BINDIR_NATIVE', result)
1509 return result
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001510 raise RunQemuError("Native sysroot directory %s doesn't exist" % result)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001511 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001512 raise RunQemuError("Can't find STAGING_BINDIR_NATIVE in '%s' output" % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001513
1514
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001515def main():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001516 if "help" in sys.argv or '-h' in sys.argv or '--help' in sys.argv:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001517 print_usage()
1518 return 0
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001519 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001520 config = BaseConfig()
Brad Bishop004d4992018-10-02 23:54:45 +02001521
Andrew Geisslerc9f78652020-09-18 14:11:35 -05001522 renice = os.path.expanduser("~/bin/runqemu-renice")
1523 if os.path.exists(renice):
1524 logger.info('Using %s to renice' % renice)
1525 subprocess.check_call([renice, str(os.getpid())])
1526
Brad Bishop004d4992018-10-02 23:54:45 +02001527 def sigterm_handler(signum, frame):
1528 logger.info("SIGTERM received")
1529 os.kill(config.qemupid, signal.SIGTERM)
1530 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001531 # Deliberately ignore the return code of 'tput smam'.
1532 subprocess.call(["tput", "smam"])
Brad Bishop004d4992018-10-02 23:54:45 +02001533 signal.signal(signal.SIGTERM, sigterm_handler)
1534
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001535 config.check_args()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001536 config.read_qemuboot()
1537 config.check_and_set()
1538 # Check whether the combos is valid or not
1539 config.validate_combos()
1540 config.print_config()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001541 config.setup_network()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001542 config.setup_rootfs()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001543 config.setup_final()
1544 config.start_qemu()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001545 except RunQemuError as err:
1546 logger.error(err)
1547 return 1
1548 except Exception as err:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001549 import traceback
1550 traceback.print_exc()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001551 return 1
1552 finally:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001553 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001554 # Deliberately ignore the return code of 'tput smam'.
1555 subprocess.call(["tput", "smam"])
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001556
1557if __name__ == "__main__":
1558 sys.exit(main())