blob: 21680b49d25acfcfee4212fa97e9293982d83020 [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':
459 self.qemu_opt_script += ' -nographic'
460 self.kernel_cmdline_script += ' console=ttyS0'
Brad Bishop19323692019-04-05 15:28:33 -0400461 elif arg == 'sdl':
Brad Bishop6dbb3162019-11-25 09:41:34 -0500462 if 'gl' in sys.argv[1:]:
Andrew Geisslerc182c622020-05-15 14:13:32 -0500463 self.set_dri_path()
Brad Bishop6dbb3162019-11-25 09:41:34 -0500464 self.qemu_opt_script += ' -vga virtio -display sdl,gl=on'
465 elif 'gl-es' in sys.argv[1:]:
Andrew Geisslerc182c622020-05-15 14:13:32 -0500466 self.set_dri_path()
Brad Bishop6dbb3162019-11-25 09:41:34 -0500467 self.qemu_opt_script += ' -vga virtio -display sdl,gl=es'
468 else:
469 self.qemu_opt_script += ' -display sdl'
Brad Bishopa34c0302019-09-23 22:34:48 -0400470 elif arg == 'gtk':
471 if 'gl' in sys.argv[1:]:
Andrew Geisslerc182c622020-05-15 14:13:32 -0500472 self.set_dri_path()
Brad Bishopa34c0302019-09-23 22:34:48 -0400473 self.qemu_opt_script += ' -vga virtio -display gtk,gl=on'
474 elif 'gl-es' in sys.argv[1:]:
Andrew Geisslerc182c622020-05-15 14:13:32 -0500475 self.set_dri_path()
Brad Bishopa34c0302019-09-23 22:34:48 -0400476 self.qemu_opt_script += ' -vga virtio -display gtk,gl=es'
477 else:
478 self.qemu_opt_script += ' -display gtk'
479 elif arg == 'gl' or arg == 'gl-es':
480 # These args are handled inside sdl or gtk blocks above
481 pass
Brad Bishop19323692019-04-05 15:28:33 -0400482 elif arg == 'egl-headless':
Andrew Geisslerc182c622020-05-15 14:13:32 -0500483 self.set_dri_path()
Brad Bishop19323692019-04-05 15:28:33 -0400484 self.qemu_opt_script += ' -vga virtio -display egl-headless'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600485 elif arg == 'serial':
486 self.kernel_cmdline_script += ' console=ttyS0'
Brad Bishop19323692019-04-05 15:28:33 -0400487 self.serialconsole = True
488 elif arg == "serialstdio":
489 self.kernel_cmdline_script += ' console=ttyS0'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600490 self.serialstdio = True
491 elif arg == 'audio':
492 logger.info("Enabling audio in qemu")
493 logger.info("Please install sound drivers in linux host")
494 self.audio_enabled = True
495 elif arg == 'kvm':
496 self.kvm_enabled = True
497 elif arg == 'kvm-vhost':
498 self.vhost_enabled = True
499 elif arg == 'slirp':
500 self.slirp_enabled = True
Andrew Geissler82c905d2020-04-13 13:39:40 -0500501 elif arg.startswith('bridge='):
502 self.net_bridge = '%s' % arg[len('bridge='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600503 elif arg == 'snapshot':
504 self.snapshot = True
505 elif arg == 'publicvnc':
506 self.qemu_opt_script += ' -vnc :0'
507 elif arg.startswith('tcpserial='):
Brad Bishop15ae2502019-06-18 21:44:24 -0400508 self.tcpserial_portnum = '%s' % arg[len('tcpserial='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600509 elif arg.startswith('qemuparams='):
Andrew Geissler99467da2019-02-25 18:54:23 -0600510 self.qemuparams = ' %s' % arg[len('qemuparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600511 elif arg.startswith('bootparams='):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500512 self.bootparams = arg[len('bootparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600513 elif os.path.exists(arg) or (re.search(':', arg) and re.search('/', arg)):
514 self.check_arg_path(os.path.abspath(arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500515 elif re.search(r'-image-|-image$', arg):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600516 # Lazy rootfs
517 self.rootfs = arg
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500518 elif arg.startswith('ovmf'):
519 self.ovmf_bios.append(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600520 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500521 # At last, assume it is the MACHINE
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600522 if (not unknown_arg) or unknown_arg == arg:
523 unknown_arg = arg
524 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500525 raise RunQemuError("Can't handle two unknown args: %s %s\n"
526 "Try 'runqemu help' on how to use it" % \
527 (unknown_arg, arg))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600528 # Check to make sure it is a valid machine
Brad Bishopa5c52ff2018-11-23 10:55:50 +1300529 if unknown_arg and self.get('MACHINE') != unknown_arg:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500530 if self.get('DEPLOY_DIR_IMAGE'):
531 machine = os.path.basename(self.get('DEPLOY_DIR_IMAGE'))
532 if unknown_arg == machine:
533 self.set("MACHINE", machine)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500534
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600535 self.check_arg_machine(unknown_arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500536
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500537 if not (self.get('DEPLOY_DIR_IMAGE') or self.qbconfload):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500538 self.load_bitbake_env()
539 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
540 if s:
541 self.set("DEPLOY_DIR_IMAGE", s.group(1))
542
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600543 def check_kvm(self):
544 """Check kvm and kvm-host"""
545 if not (self.kvm_enabled or self.vhost_enabled):
546 self.qemu_opt_script += ' %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU'))
547 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500548
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600549 if not self.get('QB_CPU_KVM'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500550 raise RunQemuError("QB_CPU_KVM is NULL, this board doesn't support kvm")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500551
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600552 self.qemu_opt_script += ' %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU_KVM'))
553 yocto_kvm_wiki = "https://wiki.yoctoproject.org/wiki/How_to_enable_KVM_for_Poky_qemu"
554 yocto_paravirt_kvm_wiki = "https://wiki.yoctoproject.org/wiki/Running_an_x86_Yocto_Linux_image_under_QEMU_KVM"
555 dev_kvm = '/dev/kvm'
556 dev_vhost = '/dev/vhost-net'
Brad Bishop15ae2502019-06-18 21:44:24 -0400557 if self.qemu_system.endswith(('i386', 'x86_64')):
558 with open('/proc/cpuinfo', 'r') as f:
559 kvm_cap = re.search('vmx|svm', "".join(f.readlines()))
560 if not kvm_cap:
561 logger.error("You are trying to enable KVM on a cpu without VT support.")
562 logger.error("Remove kvm from the command-line, or refer:")
563 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500564
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600565 if not os.path.exists(dev_kvm):
566 logger.error("Missing KVM device. Have you inserted kvm modules?")
567 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500568 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500569
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600570 if os.access(dev_kvm, os.W_OK|os.R_OK):
571 self.qemu_opt_script += ' -enable-kvm'
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500572 if self.get('MACHINE') == "qemux86":
573 # Workaround for broken APIC window on pre 4.15 host kernels which causes boot hangs
574 # See YOCTO #12301
575 # On 64 bit we use x2apic
576 self.kernel_cmdline_script += " clocksource=kvm-clock hpet=disable noapic nolapic"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600577 else:
578 logger.error("You have no read or write permission on /dev/kvm.")
579 logger.error("Please change the ownership of this file as described at:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500580 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500581
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600582 if self.vhost_enabled:
583 if not os.path.exists(dev_vhost):
584 logger.error("Missing virtio net device. Have you inserted vhost-net module?")
585 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500586 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500587
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600588 if not os.access(dev_kvm, os.W_OK|os.R_OK):
589 logger.error("You have no read or write permission on /dev/vhost-net.")
590 logger.error("Please change the ownership of this file as described at:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500591 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500592
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600593 def check_fstype(self):
594 """Check and setup FSTYPE"""
595 if not self.fstype:
596 fstype = self.get('QB_DEFAULT_FSTYPE')
597 if fstype:
598 self.fstype = fstype
599 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500600 raise RunQemuError("FSTYPE is NULL!")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500601
Brad Bishop15ae2502019-06-18 21:44:24 -0400602 # parse QB_FSINFO into dict, e.g. { 'wic': ['no-kernel-in-fs', 'a-flag'], 'ext4': ['another-flag']}
603 wic_fs = False
604 qb_fsinfo = self.get('QB_FSINFO')
605 if qb_fsinfo:
606 qb_fsinfo = qb_fsinfo.split()
607 for fsinfo in qb_fsinfo:
608 try:
609 fstype, fsflag = fsinfo.split(':')
610
611 if fstype == 'wic':
612 if fsflag == 'no-kernel-in-fs':
613 wic_fs = True
614 elif fsflag == 'kernel-in-fs':
615 wic_fs = False
616 else:
617 logger.warn('Unknown flag "%s:%s" in QB_FSINFO', fstype, fsflag)
618 continue
619 else:
620 logger.warn('QB_FSINFO is not supported for image type "%s"', fstype)
621 continue
622
623 if fstype in self.fsinfo:
624 self.fsinfo[fstype].append(fsflag)
625 else:
626 self.fsinfo[fstype] = [fsflag]
627 except Exception:
628 logger.error('Invalid parameter "%s" in QB_FSINFO', fsinfo)
629
630 # treat wic images as vmimages (with kernel) or as fsimages (rootfs only)
631 if wic_fs:
632 self.fstypes = self.fstypes + self.wictypes
633 else:
634 self.vmtypes = self.vmtypes + self.wictypes
635
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600636 def check_rootfs(self):
637 """Check and set rootfs"""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500638
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500639 if self.fstype == "none":
640 return
641
642 if self.get('ROOTFS'):
643 if not self.rootfs:
644 self.rootfs = self.get('ROOTFS')
645 elif self.get('ROOTFS') != self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500646 raise RunQemuError("Maybe conflicted ROOTFS: %s vs %s" % (self.get('ROOTFS'), self.rootfs))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500647
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600648 if self.fstype == 'nfs':
649 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500650
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600651 if self.rootfs and not os.path.exists(self.rootfs):
652 # Lazy rootfs
653 self.rootfs = "%s/%s-%s.%s" % (self.get('DEPLOY_DIR_IMAGE'),
654 self.rootfs, self.get('MACHINE'),
655 self.fstype)
656 elif not self.rootfs:
657 cmd_name = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_NAME'), self.fstype)
658 cmd_link = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'), self.fstype)
659 cmds = (cmd_name, cmd_link)
660 self.rootfs = get_first_file(cmds)
661 if not self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500662 raise RunQemuError("Failed to find rootfs: %s or %s" % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500663
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600664 if not os.path.exists(self.rootfs):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500665 raise RunQemuError("Can't find rootfs: %s" % self.rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500666
Brad Bishop08902b02019-08-20 09:16:51 -0400667 def setup_pkkek1(self):
668 """
669 Extract from PEM certificate the Platform Key and first Key
670 Exchange Key certificate string. The hypervisor needs to provide
671 it in the Type 11 SMBIOS table
672 """
673 pemcert = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), 'OvmfPkKek1.pem')
674 try:
675 with open(pemcert, 'r') as pemfile:
676 key = pemfile.read().replace('\n', ''). \
677 replace('-----BEGIN CERTIFICATE-----', ''). \
678 replace('-----END CERTIFICATE-----', '')
679 self.ovmf_secboot_pkkek1 = key
680
681 except FileNotFoundError:
682 raise RunQemuError("Can't open PEM certificate %s " % pemcert)
683
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500684 def check_ovmf(self):
685 """Check and set full path for OVMF firmware and variable file(s)."""
686
687 for index, ovmf in enumerate(self.ovmf_bios):
688 if os.path.exists(ovmf):
689 continue
690 for suffix in ('qcow2', 'bin'):
691 path = '%s/%s.%s' % (self.get('DEPLOY_DIR_IMAGE'), ovmf, suffix)
692 if os.path.exists(path):
693 self.ovmf_bios[index] = path
Brad Bishop08902b02019-08-20 09:16:51 -0400694 if ovmf.endswith('secboot'):
695 self.setup_pkkek1()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500696 break
697 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500698 raise RunQemuError("Can't find OVMF firmware: %s" % ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500699
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600700 def check_kernel(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400701 """Check and set kernel"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600702 # The vm image doesn't need a kernel
703 if self.fstype in self.vmtypes:
704 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500705
Brad Bishop316dfdd2018-06-25 12:45:53 -0400706 # See if the user supplied a KERNEL option
707 if self.get('KERNEL'):
708 self.kernel = self.get('KERNEL')
709
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500710 # QB_DEFAULT_KERNEL is always a full file path
711 kernel_name = os.path.basename(self.get('QB_DEFAULT_KERNEL'))
712
713 # The user didn't want a kernel to be loaded
Brad Bishop316dfdd2018-06-25 12:45:53 -0400714 if kernel_name == "none" and not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500715 return
716
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600717 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
718 if not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500719 kernel_match_name = "%s/%s" % (deploy_dir_image, kernel_name)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600720 kernel_match_link = "%s/%s" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
721 kernel_startswith = "%s/%s*" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
722 cmds = (kernel_match_name, kernel_match_link, kernel_startswith)
723 self.kernel = get_first_file(cmds)
724 if not self.kernel:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500725 raise RunQemuError('KERNEL not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500726
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600727 if not os.path.exists(self.kernel):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500728 raise RunQemuError("KERNEL %s not found" % self.kernel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500729
Brad Bishop316dfdd2018-06-25 12:45:53 -0400730 def check_dtb(self):
731 """Check and set dtb"""
732 # Did the user specify a device tree?
733 if self.get('DEVICE_TREE'):
734 self.dtb = self.get('DEVICE_TREE')
735 if not os.path.exists(self.dtb):
736 raise RunQemuError('Specified DTB not found: %s' % self.dtb)
737 return
738
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600739 dtb = self.get('QB_DTB')
740 if dtb:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400741 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600742 cmd_match = "%s/%s" % (deploy_dir_image, dtb)
743 cmd_startswith = "%s/%s*" % (deploy_dir_image, dtb)
744 cmd_wild = "%s/*.dtb" % deploy_dir_image
745 cmds = (cmd_match, cmd_startswith, cmd_wild)
746 self.dtb = get_first_file(cmds)
747 if not os.path.exists(self.dtb):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500748 raise RunQemuError('DTB not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500749
Brad Bishopc68388fc2019-08-26 01:33:31 -0400750 def check_bios(self):
751 """Check and set bios"""
752
753 # See if the user supplied a BIOS option
754 if self.get('BIOS'):
755 self.bios = self.get('BIOS')
756
757 # QB_DEFAULT_BIOS is always a full file path
758 bios_name = os.path.basename(self.get('QB_DEFAULT_BIOS'))
759
760 # The user didn't want a bios to be loaded
761 if (bios_name == "" or bios_name == "none") and not self.bios:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600762 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500763
Brad Bishopc68388fc2019-08-26 01:33:31 -0400764 if not self.bios:
765 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
766 self.bios = "%s/%s" % (deploy_dir_image, bios_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500767
Brad Bishopc68388fc2019-08-26 01:33:31 -0400768 if not self.bios:
769 raise RunQemuError('BIOS not found: %s' % bios_match_name)
770
771 if not os.path.exists(self.bios):
772 raise RunQemuError("KERNEL %s not found" % self.bios)
773
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500774
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600775 def check_mem(self):
Andrew Geissler99467da2019-02-25 18:54:23 -0600776 """
777 Both qemu and kernel needs memory settings, so check QB_MEM and set it
778 for both.
779 """
780 s = re.search('-m +([0-9]+)', self.qemuparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600781 if s:
782 self.set('QB_MEM', '-m %s' % s.group(1))
783 elif not self.get('QB_MEM'):
Brad Bishop79641f22019-09-10 07:20:22 -0400784 logger.info('QB_MEM is not set, use 256M by default')
785 self.set('QB_MEM', '-m 256')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500786
Andrew Geissler99467da2019-02-25 18:54:23 -0600787 # Check and remove M or m suffix
788 qb_mem = self.get('QB_MEM')
789 if qb_mem.endswith('M') or qb_mem.endswith('m'):
790 qb_mem = qb_mem[:-1]
791
792 # Add -m prefix it not present
793 if not qb_mem.startswith('-m'):
794 qb_mem = '-m %s' % qb_mem
795
796 self.set('QB_MEM', qb_mem)
797
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800798 mach = self.get('MACHINE')
799 if not mach.startswith('qemumips'):
800 self.kernel_cmdline_script += ' mem=%s' % self.get('QB_MEM').replace('-m','').strip() + 'M'
801
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600802 self.qemu_opt_script += ' %s' % self.get('QB_MEM')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500803
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600804 def check_tcpserial(self):
805 if self.tcpserial_portnum:
Brad Bishop15ae2502019-06-18 21:44:24 -0400806 ports = self.tcpserial_portnum.split(':')
807 port = ports[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600808 if self.get('QB_TCPSERIAL_OPT'):
Brad Bishop15ae2502019-06-18 21:44:24 -0400809 self.qemu_opt_script += ' ' + self.get('QB_TCPSERIAL_OPT').replace('@PORT@', port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600810 else:
Brad Bishop15ae2502019-06-18 21:44:24 -0400811 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
812
813 if len(ports) > 1:
814 for port in ports[1:]:
815 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500816
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600817 def check_and_set(self):
818 """Check configs sanity and set when needed"""
819 self.validate_paths()
Andrew Geissler82c905d2020-04-13 13:39:40 -0500820 if not self.slirp_enabled and not self.net_bridge:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500821 check_tun()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600822 # Check audio
823 if self.audio_enabled:
824 if not self.get('QB_AUDIO_DRV'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500825 raise RunQemuError("QB_AUDIO_DRV is NULL, this board doesn't support audio")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600826 if not self.get('QB_AUDIO_OPT'):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800827 logger.warning('QB_AUDIO_OPT is NULL, you may need define it to make audio work')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600828 else:
829 self.qemu_opt_script += ' %s' % self.get('QB_AUDIO_OPT')
830 os.putenv('QEMU_AUDIO_DRV', self.get('QB_AUDIO_DRV'))
831 else:
832 os.putenv('QEMU_AUDIO_DRV', 'none')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500833
Brad Bishop15ae2502019-06-18 21:44:24 -0400834 self.check_qemu_system()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600835 self.check_kvm()
836 self.check_fstype()
837 self.check_rootfs()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500838 self.check_ovmf()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600839 self.check_kernel()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400840 self.check_dtb()
Brad Bishopc68388fc2019-08-26 01:33:31 -0400841 self.check_bios()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600842 self.check_mem()
843 self.check_tcpserial()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500844
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600845 def read_qemuboot(self):
846 if not self.qemuboot:
847 if self.get('DEPLOY_DIR_IMAGE'):
848 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600849 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800850 logger.warning("Can't find qemuboot conf file, DEPLOY_DIR_IMAGE is NULL!")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600851 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500852
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600853 if self.rootfs and not os.path.exists(self.rootfs):
854 # Lazy rootfs
855 machine = self.get('MACHINE')
856 if not machine:
857 machine = os.path.basename(deploy_dir_image)
858 self.qemuboot = "%s/%s-%s.qemuboot.conf" % (deploy_dir_image,
859 self.rootfs, machine)
860 else:
861 cmd = 'ls -t %s/*.qemuboot.conf' % deploy_dir_image
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500862 logger.debug('Running %s...' % cmd)
863 try:
864 qbs = subprocess.check_output(cmd, shell=True).decode('utf-8')
865 except subprocess.CalledProcessError as err:
866 raise RunQemuError(err)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600867 if qbs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500868 for qb in qbs.split():
869 # Don't use initramfs when other choices unless fstype is ramfs
870 if '-initramfs-' in os.path.basename(qb) and self.fstype != 'cpio.gz':
871 continue
872 self.qemuboot = qb
873 break
874 if not self.qemuboot:
875 # Use the first one when no choice
876 self.qemuboot = qbs.split()[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600877 self.qbconfload = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500878
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600879 if not self.qemuboot:
880 # If we haven't found a .qemuboot.conf at this point it probably
881 # doesn't exist, continue without
882 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500883
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600884 if not os.path.exists(self.qemuboot):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500885 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 -0500886
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500887 logger.debug('CONFFILE: %s' % self.qemuboot)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500888
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600889 cf = configparser.ConfigParser()
890 cf.read(self.qemuboot)
891 for k, v in cf.items('config_bsp'):
892 k_upper = k.upper()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500893 if v.startswith("../"):
894 v = os.path.abspath(os.path.dirname(self.qemuboot) + "/" + v)
895 elif v == ".":
896 v = os.path.dirname(self.qemuboot)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600897 self.set(k_upper, v)
898
899 def validate_paths(self):
900 """Ensure all relevant path variables are set"""
901 # When we're started with a *.qemuboot.conf arg assume that image
902 # artefacts are relative to that file, rather than in whatever
903 # directory DEPLOY_DIR_IMAGE in the conf file points to.
904 if self.qbconfload:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500905 imgdir = os.path.realpath(os.path.dirname(self.qemuboot))
906 if imgdir != os.path.realpath(self.get('DEPLOY_DIR_IMAGE')):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600907 logger.info('Setting DEPLOY_DIR_IMAGE to folder containing %s (%s)' % (self.qemuboot, imgdir))
908 self.set('DEPLOY_DIR_IMAGE', imgdir)
909
910 # If the STAGING_*_NATIVE directories from the config file don't exist
911 # and we're in a sourced OE build directory try to extract the paths
912 # from `bitbake -e`
913 havenative = os.path.exists(self.get('STAGING_DIR_NATIVE')) and \
914 os.path.exists(self.get('STAGING_BINDIR_NATIVE'))
915
916 if not havenative:
917 if not self.bitbake_e:
918 self.load_bitbake_env()
919
920 if self.bitbake_e:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500921 native_vars = ['STAGING_DIR_NATIVE']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600922 for nv in native_vars:
923 s = re.search('^%s="(.*)"' % nv, self.bitbake_e, re.M)
924 if s and s.group(1) != self.get(nv):
925 logger.info('Overriding conf file setting of %s to %s from Bitbake environment' % (nv, s.group(1)))
926 self.set(nv, s.group(1))
927 else:
928 # when we're invoked from a running bitbake instance we won't
929 # be able to call `bitbake -e`, then try:
930 # - get OE_TMPDIR from environment and guess paths based on it
931 # - get OECORE_NATIVE_SYSROOT from environment (for sdk)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500932 tmpdir = self.get('OE_TMPDIR')
933 oecore_native_sysroot = self.get('OECORE_NATIVE_SYSROOT')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600934 if tmpdir:
935 logger.info('Setting STAGING_DIR_NATIVE and STAGING_BINDIR_NATIVE relative to OE_TMPDIR (%s)' % tmpdir)
936 hostos, _, _, _, machine = os.uname()
937 buildsys = '%s-%s' % (machine, hostos.lower())
938 staging_dir_native = '%s/sysroots/%s' % (tmpdir, buildsys)
939 self.set('STAGING_DIR_NATIVE', staging_dir_native)
940 elif oecore_native_sysroot:
941 logger.info('Setting STAGING_DIR_NATIVE to OECORE_NATIVE_SYSROOT (%s)' % oecore_native_sysroot)
942 self.set('STAGING_DIR_NATIVE', oecore_native_sysroot)
943 if self.get('STAGING_DIR_NATIVE'):
944 # we have to assume that STAGING_BINDIR_NATIVE is at usr/bin
945 staging_bindir_native = '%s/usr/bin' % self.get('STAGING_DIR_NATIVE')
946 logger.info('Setting STAGING_BINDIR_NATIVE to %s' % staging_bindir_native)
947 self.set('STAGING_BINDIR_NATIVE', '%s/usr/bin' % self.get('STAGING_DIR_NATIVE'))
948
949 def print_config(self):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500950 logoutput = ['Continuing with the following parameters:']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600951 if not self.fstype in self.vmtypes:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500952 logoutput.append('KERNEL: [%s]' % self.kernel)
Brad Bishopc68388fc2019-08-26 01:33:31 -0400953 if self.bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500954 logoutput.append('BIOS: [%s]' % self.bios)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600955 if self.dtb:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500956 logoutput.append('DTB: [%s]' % self.dtb)
957 logoutput.append('MACHINE: [%s]' % self.get('MACHINE'))
Brad Bishop15ae2502019-06-18 21:44:24 -0400958 try:
959 fstype_flags = ' (' + ', '.join(self.fsinfo[self.fstype]) + ')'
960 except KeyError:
961 fstype_flags = ''
Andrew Geissler82c905d2020-04-13 13:39:40 -0500962 logoutput.append('FSTYPE: [%s%s]' % (self.fstype, fstype_flags))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600963 if self.fstype == 'nfs':
Andrew Geissler82c905d2020-04-13 13:39:40 -0500964 logoutput.append('NFS_DIR: [%s]' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600965 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500966 logoutput.append('ROOTFS: [%s]' % self.rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500967 if self.ovmf_bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500968 logoutput.append('OVMF: %s' % self.ovmf_bios)
Brad Bishop08902b02019-08-20 09:16:51 -0400969 if (self.ovmf_secboot_pkkek1):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500970 logoutput.append('SECBOOT PKKEK1: [%s...]' % self.ovmf_secboot_pkkek1[0:100])
971 logoutput.append('CONFFILE: [%s]' % self.qemuboot)
972 logoutput.append('')
973 logger.info('\n'.join(logoutput))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600974
975 def setup_nfs(self):
976 if not self.nfs_server:
977 if self.slirp_enabled:
978 self.nfs_server = '10.0.2.2'
979 else:
980 self.nfs_server = '192.168.7.1'
981
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500982 # Figure out a new nfs_instance to allow multiple qemus running.
Brad Bishop977dc1a2019-02-06 16:01:43 -0500983 ps = subprocess.check_output(("ps", "auxww")).decode('utf-8')
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500984 pattern = '/bin/unfsd .* -i .*\.pid -e .*/exports([0-9]+) '
985 all_instances = re.findall(pattern, ps, re.M)
986 if all_instances:
987 all_instances.sort(key=int)
988 self.nfs_instance = int(all_instances.pop()) + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600989
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500990 nfsd_port = 3049 + 2 * self.nfs_instance
991 mountd_port = 3048 + 2 * self.nfs_instance
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600992
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500993 # Export vars for runqemu-export-rootfs
994 export_dict = {
995 'NFS_INSTANCE': self.nfs_instance,
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500996 'NFSD_PORT': nfsd_port,
997 'MOUNTD_PORT': mountd_port,
998 }
999 for k, v in export_dict.items():
1000 # Use '%s' since they are integers
1001 os.putenv(k, '%s' % v)
1002
Andrew Geissler82c905d2020-04-13 13:39:40 -05001003 self.unfs_opts="nfsvers=3,port=%s,tcp,mountport=%s" % (nfsd_port, mountd_port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001004
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001005 # Extract .tar.bz2 or .tar.bz if no nfs dir
1006 if not (self.rootfs and os.path.isdir(self.rootfs)):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001007 src_prefix = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'))
1008 dest = "%s-nfsroot" % src_prefix
1009 if os.path.exists('%s.pseudo_state' % dest):
1010 logger.info('Use %s as NFS_DIR' % dest)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001011 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001012 else:
1013 src = ""
1014 src1 = '%s.tar.bz2' % src_prefix
1015 src2 = '%s.tar.gz' % src_prefix
1016 if os.path.exists(src1):
1017 src = src1
1018 elif os.path.exists(src2):
1019 src = src2
1020 if not src:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001021 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 -06001022 logger.info('NFS_DIR not found, extracting %s to %s' % (src, dest))
Brad Bishop977dc1a2019-02-06 16:01:43 -05001023 cmd = ('runqemu-extract-sdk', src, dest)
1024 logger.info('Running %s...' % str(cmd))
1025 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001026 raise RunQemuError('Failed to run %s' % cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001027 self.clean_nfs_dir = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001028 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001029
1030 # Start the userspace NFS server
Brad Bishop977dc1a2019-02-06 16:01:43 -05001031 cmd = ('runqemu-export-rootfs', 'start', self.rootfs)
1032 logger.info('Running %s...' % str(cmd))
1033 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001034 raise RunQemuError('Failed to run %s' % cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001035
1036 self.nfs_running = True
1037
Andrew Geissler82c905d2020-04-13 13:39:40 -05001038 def setup_net_bridge(self):
1039 self.set('NETWORK_CMD', '-netdev bridge,br=%s,id=net0,helper=%s -device virtio-net-pci,netdev=net0 ' % (
1040 self.net_bridge, os.path.join(self.bindir_native, 'qemu-oe-bridge-helper')))
1041
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001042 def setup_slirp(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001043 """Setup user networking"""
1044
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001045 if self.fstype == 'nfs':
1046 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001047 netconf = " " + self.cmdline_ip_slirp
1048 logger.info("Network configuration:%s", netconf)
1049 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001050 # Port mapping
1051 hostfwd = ",hostfwd=tcp::2222-:22,hostfwd=tcp::2323-:23"
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001052 qb_slirp_opt_default = "-netdev user,id=net0%s,tftp=%s" % (hostfwd, self.get('DEPLOY_DIR_IMAGE'))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001053 qb_slirp_opt = self.get('QB_SLIRP_OPT') or qb_slirp_opt_default
1054 # Figure out the port
1055 ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt)
1056 ports = [int(i) for i in ports]
1057 mac = 2
Brad Bishop08902b02019-08-20 09:16:51 -04001058
1059 lockdir = "/tmp/qemu-port-locks"
1060 if not os.path.exists(lockdir):
1061 # There might be a race issue when multi runqemu processess are
1062 # running at the same time.
1063 try:
1064 os.mkdir(lockdir)
1065 os.chmod(lockdir, 0o777)
1066 except FileExistsError:
1067 pass
1068
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001069 # Find a free port to avoid conflicts
1070 for p in ports[:]:
1071 p_new = p
Brad Bishop08902b02019-08-20 09:16:51 -04001072 while not self.check_free_port('localhost', p_new, lockdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001073 p_new += 1
1074 mac += 1
1075 while p_new in ports:
1076 p_new += 1
1077 mac += 1
1078 if p != p_new:
1079 ports.append(p_new)
1080 qb_slirp_opt = re.sub(':%s-' % p, ':%s-' % p_new, qb_slirp_opt)
1081 logger.info("Port forward changed: %s -> %s" % (p, p_new))
1082 mac = "%s%02x" % (self.mac_slirp, mac)
1083 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qb_slirp_opt))
1084 # Print out port foward
1085 hostfwd = re.findall('(hostfwd=[^,]*)', qb_slirp_opt)
1086 if hostfwd:
1087 logger.info('Port forward: %s' % ' '.join(hostfwd))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001088
1089 def setup_tap(self):
1090 """Setup tap"""
1091
1092 # This file is created when runqemu-gen-tapdevs creates a bank of tap
1093 # devices, indicating that the user should not bring up new ones using
1094 # sudo.
1095 nosudo_flag = '/etc/runqemu-nosudo'
1096 self.qemuifup = shutil.which('runqemu-ifup')
1097 self.qemuifdown = shutil.which('runqemu-ifdown')
1098 ip = shutil.which('ip')
1099 lockdir = "/tmp/qemu-tap-locks"
1100
1101 if not (self.qemuifup and self.qemuifdown and ip):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001102 logger.error("runqemu-ifup: %s" % self.qemuifup)
1103 logger.error("runqemu-ifdown: %s" % self.qemuifdown)
1104 logger.error("ip: %s" % ip)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001105 raise OEPathError("runqemu-ifup, runqemu-ifdown or ip not found")
1106
1107 if not os.path.exists(lockdir):
1108 # There might be a race issue when multi runqemu processess are
1109 # running at the same time.
1110 try:
1111 os.mkdir(lockdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001112 os.chmod(lockdir, 0o777)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001113 except FileExistsError:
1114 pass
1115
Brad Bishop977dc1a2019-02-06 16:01:43 -05001116 cmd = (ip, 'link')
1117 logger.debug('Running %s...' % str(cmd))
1118 ip_link = subprocess.check_output(cmd).decode('utf-8')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001119 # Matches line like: 6: tap0: <foo>
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001120 possibles = re.findall('^[0-9]+: +(tap[0-9]+): <.*', ip_link, re.M)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001121 tap = ""
1122 for p in possibles:
1123 lockfile = os.path.join(lockdir, p)
1124 if os.path.exists('%s.skip' % lockfile):
1125 logger.info('Found %s.skip, skipping %s' % (lockfile, p))
1126 continue
Brad Bishop08902b02019-08-20 09:16:51 -04001127 self.taplock = lockfile + '.lock'
1128 if self.acquire_taplock(error=False):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001129 tap = p
1130 logger.info("Using preconfigured tap device %s" % tap)
1131 logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap))
1132 break
1133
1134 if not tap:
1135 if os.path.exists(nosudo_flag):
1136 logger.error("Error: There are no available tap devices to use for networking,")
1137 logger.error("and I see %s exists, so I am not going to try creating" % nosudo_flag)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001138 raise RunQemuError("a new one with sudo.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001139
1140 gid = os.getgid()
1141 uid = os.getuid()
1142 logger.info("Setting up tap interface under sudo")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001143 cmd = ('sudo', self.qemuifup, str(uid), str(gid), self.bindir_native)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001144 try:
1145 tap = subprocess.check_output(cmd).decode('utf-8').strip()
1146 except subprocess.CalledProcessError as e:
1147 logger.error('Setting up tap device failed:\n%s\nRun runqemu-gen-tapdevs to manually create one.' % str(e))
1148 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001149 lockfile = os.path.join(lockdir, tap)
Brad Bishop08902b02019-08-20 09:16:51 -04001150 self.taplock = lockfile + '.lock'
1151 self.acquire_taplock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001152 self.cleantap = True
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001153 logger.debug('Created tap: %s' % tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001154
1155 if not tap:
1156 logger.error("Failed to setup tap device. Run runqemu-gen-tapdevs to manually create.")
Andrew Geissler82c905d2020-04-13 13:39:40 -05001157 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001158 self.tap = tap
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001159 tapnum = int(tap[3:])
1160 gateway = tapnum * 2 + 1
1161 client = gateway + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001162 if self.fstype == 'nfs':
1163 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001164 netconf = " " + self.cmdline_ip_tap
1165 netconf = netconf.replace('@CLIENT@', str(client))
1166 netconf = netconf.replace('@GATEWAY@', str(gateway))
1167 logger.info("Network configuration:%s", netconf)
1168 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001169 mac = "%s%02x" % (self.mac_tap, client)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001170 qb_tap_opt = self.get('QB_TAP_OPT')
1171 if qb_tap_opt:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001172 qemu_tap_opt = qb_tap_opt.replace('@TAP@', tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001173 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001174 qemu_tap_opt = "-netdev tap,id=net0,ifname=%s,script=no,downscript=no" % (self.tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001175
1176 if self.vhost_enabled:
1177 qemu_tap_opt += ',vhost=on'
1178
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001179 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qemu_tap_opt))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001180
1181 def setup_network(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001182 if self.get('QB_NET') == 'none':
1183 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001184 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001185 self.saved_stty = subprocess.check_output(("stty", "-g")).decode('utf-8').strip()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001186 self.network_device = self.get('QB_NETWORK_DEVICE') or self.network_device
Andrew Geissler82c905d2020-04-13 13:39:40 -05001187 if self.net_bridge:
1188 self.setup_net_bridge()
1189 elif self.slirp_enabled:
1190 self.cmdline_ip_slirp = self.get('QB_CMDLINE_IP_SLIRP') or self.cmdline_ip_slirp
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001191 self.setup_slirp()
1192 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001193 self.cmdline_ip_tap = self.get('QB_CMDLINE_IP_TAP') or self.cmdline_ip_tap
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001194 self.setup_tap()
1195
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001196 def setup_rootfs(self):
1197 if self.get('QB_ROOTFS') == 'none':
1198 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001199 if 'wic.' in self.fstype:
1200 self.fstype = self.fstype[4:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001201 rootfs_format = self.fstype if self.fstype in ('vmdk', 'qcow2', 'vdi') else 'raw'
1202
1203 qb_rootfs_opt = self.get('QB_ROOTFS_OPT')
1204 if qb_rootfs_opt:
1205 self.rootfs_options = qb_rootfs_opt.replace('@ROOTFS@', self.rootfs)
1206 else:
1207 self.rootfs_options = '-drive file=%s,if=virtio,format=%s' % (self.rootfs, rootfs_format)
1208
Andrew Geissler82c905d2020-04-13 13:39:40 -05001209 qb_rootfs_extra_opt = self.get("QB_ROOTFS_EXTRA_OPT")
1210 if qb_rootfs_extra_opt and not qb_rootfs_extra_opt.startswith(","):
1211 qb_rootfs_extra_opt = "," + qb_rootfs_extra_opt
1212
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001213 if self.fstype in ('cpio.gz', 'cpio'):
1214 self.kernel_cmdline = 'root=/dev/ram0 rw debugshell'
1215 self.rootfs_options = '-initrd %s' % self.rootfs
1216 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001217 vm_drive = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001218 if self.fstype in self.vmtypes:
1219 if self.fstype == 'iso':
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001220 vm_drive = '-drive file=%s,if=virtio,media=cdrom' % self.rootfs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001221 elif self.get('QB_DRIVE_TYPE'):
1222 drive_type = self.get('QB_DRIVE_TYPE')
1223 if drive_type.startswith("/dev/sd"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001224 logger.info('Using scsi drive')
Andrew Geissler82c905d2020-04-13 13:39:40 -05001225 vm_drive = '-drive if=none,id=hd,file=%s,format=%s -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd%s' \
1226 % (self.rootfs, rootfs_format, qb_rootfs_extra_opt)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001227 elif drive_type.startswith("/dev/hd"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001228 logger.info('Using ide drive')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001229 vm_drive = "-drive file=%s,format=%s" % (self.rootfs, rootfs_format)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001230 elif drive_type.startswith("/dev/vdb"):
1231 logger.info('Using block virtio drive');
1232 vm_drive = '-drive id=disk0,file=%s,if=none,format=%s -device virtio-blk-device,drive=disk0%s' \
1233 % (self.rootfs, rootfs_format,qb_rootfs_extra_opt)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001234 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001235 # virtio might have been selected explicitly (just use it), or
1236 # is used as fallback (then warn about that).
1237 if not drive_type.startswith("/dev/vd"):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001238 logger.warning("Unknown QB_DRIVE_TYPE: %s" % drive_type)
1239 logger.warning("Failed to figure out drive type, consider define or fix QB_DRIVE_TYPE")
1240 logger.warning('Trying to use virtio block drive')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001241 vm_drive = '-drive if=virtio,file=%s,format=%s' % (self.rootfs, rootfs_format)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001242
1243 # All branches above set vm_drive.
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001244 self.rootfs_options = '%s -no-reboot' % vm_drive
Brad Bishopf3f93bb2019-10-16 14:33:32 -04001245 self.kernel_cmdline = 'root=%s rw' % (self.get('QB_KERNEL_ROOT'))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001246
1247 if self.fstype == 'nfs':
1248 self.rootfs_options = ''
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001249 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 -04001250 self.kernel_cmdline = 'root=%s rw' % k_root
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001251
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001252 if self.fstype == 'none':
1253 self.rootfs_options = ''
1254
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001255 self.set('ROOTFS_OPTIONS', self.rootfs_options)
1256
1257 def guess_qb_system(self):
1258 """attempt to determine the appropriate qemu-system binary"""
1259 mach = self.get('MACHINE')
1260 if not mach:
1261 search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*'
1262 if self.rootfs:
1263 match = re.match(search, self.rootfs)
1264 if match:
1265 mach = match.group(1)
1266 elif self.kernel:
1267 match = re.match(search, self.kernel)
1268 if match:
1269 mach = match.group(1)
1270
1271 if not mach:
1272 return None
1273
1274 if mach == 'qemuarm':
1275 qbsys = 'arm'
1276 elif mach == 'qemuarm64':
1277 qbsys = 'aarch64'
1278 elif mach == 'qemux86':
1279 qbsys = 'i386'
1280 elif mach == 'qemux86-64':
1281 qbsys = 'x86_64'
1282 elif mach == 'qemuppc':
1283 qbsys = 'ppc'
1284 elif mach == 'qemumips':
1285 qbsys = 'mips'
1286 elif mach == 'qemumips64':
1287 qbsys = 'mips64'
1288 elif mach == 'qemumipsel':
1289 qbsys = 'mipsel'
1290 elif mach == 'qemumips64el':
1291 qbsys = 'mips64el'
Brad Bishop316dfdd2018-06-25 12:45:53 -04001292 elif mach == 'qemuriscv64':
1293 qbsys = 'riscv64'
1294 elif mach == 'qemuriscv32':
1295 qbsys = 'riscv32'
Brad Bishop004d4992018-10-02 23:54:45 +02001296 else:
1297 logger.error("Unable to determine QEMU PC System emulator for %s machine." % mach)
1298 logger.error("As %s is not among valid QEMU machines such as," % mach)
1299 logger.error("qemux86-64, qemux86, qemuarm64, qemuarm, qemumips64, qemumips64el, qemumipsel, qemumips, qemuppc")
1300 raise RunQemuError("Set qb_system_name with suitable QEMU PC System emulator in .*qemuboot.conf.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001301
1302 return 'qemu-system-%s' % qbsys
1303
Brad Bishop15ae2502019-06-18 21:44:24 -04001304 def check_qemu_system(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001305 qemu_system = self.get('QB_SYSTEM_NAME')
1306 if not qemu_system:
1307 qemu_system = self.guess_qb_system()
1308 if not qemu_system:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001309 raise RunQemuError("Failed to boot, QB_SYSTEM_NAME is NULL!")
Brad Bishop15ae2502019-06-18 21:44:24 -04001310 self.qemu_system = qemu_system
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001311
Brad Bishop15ae2502019-06-18 21:44:24 -04001312 def setup_final(self):
1313 qemu_bin = os.path.join(self.bindir_native, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001314
1315 # It is possible to have qemu-native in ASSUME_PROVIDED, and it won't
1316 # find QEMU in sysroot, it needs to use host's qemu.
1317 if not os.path.exists(qemu_bin):
1318 logger.info("QEMU binary not found in %s, trying host's QEMU" % qemu_bin)
1319 for path in (os.environ['PATH'] or '').split(':'):
Brad Bishop15ae2502019-06-18 21:44:24 -04001320 qemu_bin_tmp = os.path.join(path, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001321 logger.info("Trying: %s" % qemu_bin_tmp)
1322 if os.path.exists(qemu_bin_tmp):
1323 qemu_bin = qemu_bin_tmp
1324 if not os.path.isabs(qemu_bin):
1325 qemu_bin = os.path.abspath(qemu_bin)
1326 logger.info("Using host's QEMU: %s" % qemu_bin)
1327 break
1328
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001329 if not os.access(qemu_bin, os.X_OK):
1330 raise OEPathError("No QEMU binary '%s' could be found" % qemu_bin)
1331
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001332 self.qemu_opt = "%s %s %s %s" % (qemu_bin, self.get('NETWORK_CMD'), self.get('ROOTFS_OPTIONS'), self.get('QB_OPT_APPEND'))
1333
1334 for ovmf in self.ovmf_bios:
1335 format = ovmf.rsplit('.', 1)[-1]
1336 self.qemu_opt += ' -drive if=pflash,format=%s,file=%s' % (format, ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001337
1338 self.qemu_opt += ' ' + self.qemu_opt_script
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001339
Brad Bishop08902b02019-08-20 09:16:51 -04001340 if self.ovmf_secboot_pkkek1:
1341 # Provide the Platform Key and first Key Exchange Key certificate as an
1342 # OEM string in the SMBIOS Type 11 table. Prepend the certificate string
1343 # with "application prefix" of the EnrollDefaultKeys.efi application
1344 self.qemu_opt += ' -smbios type=11,value=4e32566d-8e9e-4f52-81d3-5bb9715f9727:' \
1345 + self.ovmf_secboot_pkkek1
1346
Andrew Geissler99467da2019-02-25 18:54:23 -06001347 # Append qemuparams to override previous settings
1348 if self.qemuparams:
1349 self.qemu_opt += ' ' + self.qemuparams
1350
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001351 if self.snapshot:
1352 self.qemu_opt += " -snapshot"
1353
Brad Bishop19323692019-04-05 15:28:33 -04001354 if self.serialconsole:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001355 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001356 subprocess.check_call(("stty", "intr", "^]"))
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001357 logger.info("Interrupt character is '^]'")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001358
1359 first_serial = ""
1360 if not re.search("-nographic", self.qemu_opt):
1361 first_serial = "-serial mon:vc"
1362 # We always want a ttyS1. Since qemu by default adds a serial
1363 # port when nodefaults is not specified, it seems that all that
1364 # would be needed is to make sure a "-serial" is there. However,
1365 # it appears that when "-serial" is specified, it ignores the
1366 # default serial port that is normally added. So here we make
1367 # sure to add two -serial if there are none. And only one if
1368 # there is one -serial already.
1369 serial_num = len(re.findall("-serial", self.qemu_opt))
1370 if serial_num == 0:
1371 self.qemu_opt += " %s %s" % (first_serial, self.get("QB_SERIAL_OPT"))
1372 elif serial_num == 1:
1373 self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT")
1374
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001375 # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES),
1376 # if not serial or serialtcp options was specified only ttyS0 is created
1377 # and sysvinit shows an error trying to enable ttyS1:
1378 # INIT: Id "S1" respawning too fast: disabled for 5 minutes
1379 serial_num = len(re.findall("-serial", self.qemu_opt))
1380 if serial_num == 0:
Brad Bishop19323692019-04-05 15:28:33 -04001381 if re.search("-nographic", self.qemu_opt) or self.serialstdio:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001382 self.qemu_opt += " -serial mon:stdio -serial null"
1383 else:
1384 self.qemu_opt += " -serial mon:vc -serial null"
1385
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001386 def start_qemu(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001387 import shlex
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001388 if self.kernel:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001389 kernel_opts = "-kernel %s -append '%s %s %s %s'" % (self.kernel, self.kernel_cmdline,
1390 self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'),
1391 self.bootparams)
Brad Bishopc68388fc2019-08-26 01:33:31 -04001392 if self.bios:
1393 kernel_opts += " -bios %s" % self.bios
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001394 if self.dtb:
1395 kernel_opts += " -dtb %s" % self.dtb
1396 else:
1397 kernel_opts = ""
1398 cmd = "%s %s" % (self.qemu_opt, kernel_opts)
Brad Bishop004d4992018-10-02 23:54:45 +02001399 cmds = shlex.split(cmd)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001400 logger.info('Running %s\n' % cmd)
Brad Bishopf86d0552018-12-04 14:18:15 -08001401 pass_fds = []
Brad Bishop08902b02019-08-20 09:16:51 -04001402 if self.taplock_descriptor:
1403 pass_fds = [self.taplock_descriptor.fileno()]
1404 if len(self.portlocks):
1405 for descriptor in self.portlocks.values():
1406 pass_fds.append(descriptor.fileno())
Brad Bishopf86d0552018-12-04 14:18:15 -08001407 process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds)
Brad Bishop004d4992018-10-02 23:54:45 +02001408 self.qemupid = process.pid
1409 retcode = process.wait()
1410 if retcode:
1411 if retcode == -signal.SIGTERM:
1412 logger.info("Qemu terminated by SIGTERM")
1413 else:
1414 logger.error("Failed to run qemu: %s", process.stderr.read().decode())
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001415
1416 def cleanup(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001417 if self.cleaned:
1418 return
1419
1420 # avoid dealing with SIGTERM when cleanup function is running
1421 signal.signal(signal.SIGTERM, signal.SIG_IGN)
1422
1423 logger.info("Cleaning up")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001424 if self.cleantap:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001425 cmd = ('sudo', self.qemuifdown, self.tap, self.bindir_native)
1426 logger.debug('Running %s' % str(cmd))
1427 subprocess.check_call(cmd)
Brad Bishop08902b02019-08-20 09:16:51 -04001428 self.release_taplock()
1429 self.release_portlock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001430
1431 if self.nfs_running:
1432 logger.info("Shutting down the userspace NFS server...")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001433 cmd = ("runqemu-export-rootfs", "stop", self.rootfs)
1434 logger.debug('Running %s' % str(cmd))
1435 subprocess.check_call(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001436
1437 if self.saved_stty:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001438 subprocess.check_call(("stty", self.saved_stty))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001439
1440 if self.clean_nfs_dir:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001441 logger.info('Removing %s' % self.rootfs)
1442 shutil.rmtree(self.rootfs)
1443 shutil.rmtree('%s.pseudo_state' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001444
Brad Bishop004d4992018-10-02 23:54:45 +02001445 self.cleaned = True
1446
Andrew Geissler82c905d2020-04-13 13:39:40 -05001447 def run_bitbake_env(self, mach=None):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001448 bitbake = shutil.which('bitbake')
1449 if not bitbake:
1450 return
1451
1452 if not mach:
1453 mach = self.get('MACHINE')
1454
Andrew Geissler82c905d2020-04-13 13:39:40 -05001455 multiconfig = self.get('MULTICONFIG')
1456 if multiconfig:
1457 multiconfig = "mc:%s" % multiconfig
1458
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001459 if mach:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001460 cmd = 'MACHINE=%s bitbake -e %s' % (mach, multiconfig)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001461 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001462 cmd = 'bitbake -e %s' % multiconfig
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001463
1464 logger.info('Running %s...' % cmd)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001465 return subprocess.check_output(cmd, shell=True).decode('utf-8')
1466
1467 def load_bitbake_env(self, mach=None):
1468 if self.bitbake_e:
1469 return
1470
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001471 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001472 self.bitbake_e = self.run_bitbake_env(mach=mach)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001473 except subprocess.CalledProcessError as err:
1474 self.bitbake_e = ''
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001475 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 -06001476
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001477 def validate_combos(self):
1478 if (self.fstype in self.vmtypes) and self.kernel:
1479 raise RunQemuError("%s doesn't need kernel %s!" % (self.fstype, self.kernel))
1480
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001481 @property
1482 def bindir_native(self):
1483 result = self.get('STAGING_BINDIR_NATIVE')
1484 if result and os.path.exists(result):
1485 return result
1486
Andrew Geissler82c905d2020-04-13 13:39:40 -05001487 cmd = ['bitbake', '-e']
1488 multiconfig = self.get('MULTICONFIG')
1489 if multiconfig:
1490 cmd.append('mc:%s:qemu-helper-native' % multiconfig)
1491 else:
1492 cmd.append('qemu-helper-native')
1493
Brad Bishop977dc1a2019-02-06 16:01:43 -05001494 logger.info('Running %s...' % str(cmd))
1495 out = subprocess.check_output(cmd).decode('utf-8')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001496
1497 match = re.search('^STAGING_BINDIR_NATIVE="(.*)"', out, re.M)
1498 if match:
1499 result = match.group(1)
1500 if os.path.exists(result):
1501 self.set('STAGING_BINDIR_NATIVE', result)
1502 return result
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001503 raise RunQemuError("Native sysroot directory %s doesn't exist" % result)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001504 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001505 raise RunQemuError("Can't find STAGING_BINDIR_NATIVE in '%s' output" % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001506
1507
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001508def main():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001509 if "help" in sys.argv or '-h' in sys.argv or '--help' in sys.argv:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001510 print_usage()
1511 return 0
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001512 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001513 config = BaseConfig()
Brad Bishop004d4992018-10-02 23:54:45 +02001514
1515 def sigterm_handler(signum, frame):
1516 logger.info("SIGTERM received")
1517 os.kill(config.qemupid, signal.SIGTERM)
1518 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001519 # Deliberately ignore the return code of 'tput smam'.
1520 subprocess.call(["tput", "smam"])
Brad Bishop004d4992018-10-02 23:54:45 +02001521 signal.signal(signal.SIGTERM, sigterm_handler)
1522
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001523 config.check_args()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001524 config.read_qemuboot()
1525 config.check_and_set()
1526 # Check whether the combos is valid or not
1527 config.validate_combos()
1528 config.print_config()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001529 config.setup_network()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001530 config.setup_rootfs()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001531 config.setup_final()
1532 config.start_qemu()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001533 except RunQemuError as err:
1534 logger.error(err)
1535 return 1
1536 except Exception as err:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001537 import traceback
1538 traceback.print_exc()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001539 return 1
1540 finally:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001541 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001542 # Deliberately ignore the return code of 'tput smam'.
1543 subprocess.call(["tput", "smam"])
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001544
1545if __name__ == "__main__":
1546 sys.exit(main())