blob: 310d79fdc5a8c2cb18f1bf804f29204b397b6ab0 [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
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600423 def check_args(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500424 for debug in ("-d", "--debug"):
425 if debug in sys.argv:
426 logger.setLevel(logging.DEBUG)
427 sys.argv.remove(debug)
428
429 for quiet in ("-q", "--quiet"):
430 if quiet in sys.argv:
431 logger.setLevel(logging.ERROR)
432 sys.argv.remove(quiet)
433
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600434 unknown_arg = ""
435 for arg in sys.argv[1:]:
Brad Bishop15ae2502019-06-18 21:44:24 -0400436 if arg in self.fstypes + self.vmtypes + self.wictypes:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600437 self.check_arg_fstype(arg)
438 elif arg == 'nographic':
439 self.qemu_opt_script += ' -nographic'
440 self.kernel_cmdline_script += ' console=ttyS0'
Brad Bishop19323692019-04-05 15:28:33 -0400441 elif arg == 'sdl':
Brad Bishop6dbb3162019-11-25 09:41:34 -0500442 if 'gl' in sys.argv[1:]:
443 self.qemu_opt_script += ' -vga virtio -display sdl,gl=on'
444 elif 'gl-es' in sys.argv[1:]:
445 self.qemu_opt_script += ' -vga virtio -display sdl,gl=es'
446 else:
447 self.qemu_opt_script += ' -display sdl'
Brad Bishopa34c0302019-09-23 22:34:48 -0400448 elif arg == 'gtk':
449 if 'gl' in sys.argv[1:]:
450 self.qemu_opt_script += ' -vga virtio -display gtk,gl=on'
451 elif 'gl-es' in sys.argv[1:]:
452 self.qemu_opt_script += ' -vga virtio -display gtk,gl=es'
453 else:
454 self.qemu_opt_script += ' -display gtk'
455 elif arg == 'gl' or arg == 'gl-es':
456 # These args are handled inside sdl or gtk blocks above
457 pass
Brad Bishop19323692019-04-05 15:28:33 -0400458 elif arg == 'egl-headless':
459 self.qemu_opt_script += ' -vga virtio -display egl-headless'
460 # As runqemu can be run within bitbake (when using testimage, for example),
461 # we need to ensure that we run host pkg-config, and that it does not
462 # get mis-directed to native build paths set by bitbake.
463 try:
464 del os.environ['PKG_CONFIG_PATH']
465 del os.environ['PKG_CONFIG_DIR']
466 del os.environ['PKG_CONFIG_LIBDIR']
Brad Bishopf3f93bb2019-10-16 14:33:32 -0400467 del os.environ['PKG_CONFIG_SYSROOT_DIR']
Brad Bishop19323692019-04-05 15:28:33 -0400468 except KeyError:
469 pass
470 try:
471 dripath = subprocess.check_output("PATH=/bin:/usr/bin:$PATH pkg-config --variable=dridriverdir dri", shell=True)
472 except subprocess.CalledProcessError as e:
473 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.")
474 os.environ['LIBGL_DRIVERS_PATH'] = dripath.decode('utf-8').strip()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600475 elif arg == 'serial':
476 self.kernel_cmdline_script += ' console=ttyS0'
Brad Bishop19323692019-04-05 15:28:33 -0400477 self.serialconsole = True
478 elif arg == "serialstdio":
479 self.kernel_cmdline_script += ' console=ttyS0'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600480 self.serialstdio = True
481 elif arg == 'audio':
482 logger.info("Enabling audio in qemu")
483 logger.info("Please install sound drivers in linux host")
484 self.audio_enabled = True
485 elif arg == 'kvm':
486 self.kvm_enabled = True
487 elif arg == 'kvm-vhost':
488 self.vhost_enabled = True
489 elif arg == 'slirp':
490 self.slirp_enabled = True
Andrew Geissler82c905d2020-04-13 13:39:40 -0500491 elif arg.startswith('bridge='):
492 self.net_bridge = '%s' % arg[len('bridge='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600493 elif arg == 'snapshot':
494 self.snapshot = True
495 elif arg == 'publicvnc':
496 self.qemu_opt_script += ' -vnc :0'
497 elif arg.startswith('tcpserial='):
Brad Bishop15ae2502019-06-18 21:44:24 -0400498 self.tcpserial_portnum = '%s' % arg[len('tcpserial='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600499 elif arg.startswith('qemuparams='):
Andrew Geissler99467da2019-02-25 18:54:23 -0600500 self.qemuparams = ' %s' % arg[len('qemuparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600501 elif arg.startswith('bootparams='):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500502 self.bootparams = arg[len('bootparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600503 elif os.path.exists(arg) or (re.search(':', arg) and re.search('/', arg)):
504 self.check_arg_path(os.path.abspath(arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500505 elif re.search(r'-image-|-image$', arg):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600506 # Lazy rootfs
507 self.rootfs = arg
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500508 elif arg.startswith('ovmf'):
509 self.ovmf_bios.append(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600510 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500511 # At last, assume it is the MACHINE
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600512 if (not unknown_arg) or unknown_arg == arg:
513 unknown_arg = arg
514 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500515 raise RunQemuError("Can't handle two unknown args: %s %s\n"
516 "Try 'runqemu help' on how to use it" % \
517 (unknown_arg, arg))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600518 # Check to make sure it is a valid machine
Brad Bishopa5c52ff2018-11-23 10:55:50 +1300519 if unknown_arg and self.get('MACHINE') != unknown_arg:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500520 if self.get('DEPLOY_DIR_IMAGE'):
521 machine = os.path.basename(self.get('DEPLOY_DIR_IMAGE'))
522 if unknown_arg == machine:
523 self.set("MACHINE", machine)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500524
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600525 self.check_arg_machine(unknown_arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500526
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500527 if not (self.get('DEPLOY_DIR_IMAGE') or self.qbconfload):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500528 self.load_bitbake_env()
529 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
530 if s:
531 self.set("DEPLOY_DIR_IMAGE", s.group(1))
532
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600533 def check_kvm(self):
534 """Check kvm and kvm-host"""
535 if not (self.kvm_enabled or self.vhost_enabled):
536 self.qemu_opt_script += ' %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU'))
537 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500538
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600539 if not self.get('QB_CPU_KVM'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500540 raise RunQemuError("QB_CPU_KVM is NULL, this board doesn't support kvm")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500541
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600542 self.qemu_opt_script += ' %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU_KVM'))
543 yocto_kvm_wiki = "https://wiki.yoctoproject.org/wiki/How_to_enable_KVM_for_Poky_qemu"
544 yocto_paravirt_kvm_wiki = "https://wiki.yoctoproject.org/wiki/Running_an_x86_Yocto_Linux_image_under_QEMU_KVM"
545 dev_kvm = '/dev/kvm'
546 dev_vhost = '/dev/vhost-net'
Brad Bishop15ae2502019-06-18 21:44:24 -0400547 if self.qemu_system.endswith(('i386', 'x86_64')):
548 with open('/proc/cpuinfo', 'r') as f:
549 kvm_cap = re.search('vmx|svm', "".join(f.readlines()))
550 if not kvm_cap:
551 logger.error("You are trying to enable KVM on a cpu without VT support.")
552 logger.error("Remove kvm from the command-line, or refer:")
553 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500554
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600555 if not os.path.exists(dev_kvm):
556 logger.error("Missing KVM device. Have you inserted kvm modules?")
557 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500558 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500559
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600560 if os.access(dev_kvm, os.W_OK|os.R_OK):
561 self.qemu_opt_script += ' -enable-kvm'
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500562 if self.get('MACHINE') == "qemux86":
563 # Workaround for broken APIC window on pre 4.15 host kernels which causes boot hangs
564 # See YOCTO #12301
565 # On 64 bit we use x2apic
566 self.kernel_cmdline_script += " clocksource=kvm-clock hpet=disable noapic nolapic"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600567 else:
568 logger.error("You have no read or write permission on /dev/kvm.")
569 logger.error("Please change the ownership of this file as described at:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500570 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500571
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600572 if self.vhost_enabled:
573 if not os.path.exists(dev_vhost):
574 logger.error("Missing virtio net device. Have you inserted vhost-net module?")
575 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500576 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500577
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600578 if not os.access(dev_kvm, os.W_OK|os.R_OK):
579 logger.error("You have no read or write permission on /dev/vhost-net.")
580 logger.error("Please change the ownership of this file as described at:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500581 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500582
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600583 def check_fstype(self):
584 """Check and setup FSTYPE"""
585 if not self.fstype:
586 fstype = self.get('QB_DEFAULT_FSTYPE')
587 if fstype:
588 self.fstype = fstype
589 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500590 raise RunQemuError("FSTYPE is NULL!")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500591
Brad Bishop15ae2502019-06-18 21:44:24 -0400592 # parse QB_FSINFO into dict, e.g. { 'wic': ['no-kernel-in-fs', 'a-flag'], 'ext4': ['another-flag']}
593 wic_fs = False
594 qb_fsinfo = self.get('QB_FSINFO')
595 if qb_fsinfo:
596 qb_fsinfo = qb_fsinfo.split()
597 for fsinfo in qb_fsinfo:
598 try:
599 fstype, fsflag = fsinfo.split(':')
600
601 if fstype == 'wic':
602 if fsflag == 'no-kernel-in-fs':
603 wic_fs = True
604 elif fsflag == 'kernel-in-fs':
605 wic_fs = False
606 else:
607 logger.warn('Unknown flag "%s:%s" in QB_FSINFO', fstype, fsflag)
608 continue
609 else:
610 logger.warn('QB_FSINFO is not supported for image type "%s"', fstype)
611 continue
612
613 if fstype in self.fsinfo:
614 self.fsinfo[fstype].append(fsflag)
615 else:
616 self.fsinfo[fstype] = [fsflag]
617 except Exception:
618 logger.error('Invalid parameter "%s" in QB_FSINFO', fsinfo)
619
620 # treat wic images as vmimages (with kernel) or as fsimages (rootfs only)
621 if wic_fs:
622 self.fstypes = self.fstypes + self.wictypes
623 else:
624 self.vmtypes = self.vmtypes + self.wictypes
625
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600626 def check_rootfs(self):
627 """Check and set rootfs"""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500628
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500629 if self.fstype == "none":
630 return
631
632 if self.get('ROOTFS'):
633 if not self.rootfs:
634 self.rootfs = self.get('ROOTFS')
635 elif self.get('ROOTFS') != self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500636 raise RunQemuError("Maybe conflicted ROOTFS: %s vs %s" % (self.get('ROOTFS'), self.rootfs))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500637
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600638 if self.fstype == 'nfs':
639 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500640
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600641 if self.rootfs and not os.path.exists(self.rootfs):
642 # Lazy rootfs
643 self.rootfs = "%s/%s-%s.%s" % (self.get('DEPLOY_DIR_IMAGE'),
644 self.rootfs, self.get('MACHINE'),
645 self.fstype)
646 elif not self.rootfs:
647 cmd_name = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_NAME'), self.fstype)
648 cmd_link = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'), self.fstype)
649 cmds = (cmd_name, cmd_link)
650 self.rootfs = get_first_file(cmds)
651 if not self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500652 raise RunQemuError("Failed to find rootfs: %s or %s" % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500653
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600654 if not os.path.exists(self.rootfs):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500655 raise RunQemuError("Can't find rootfs: %s" % self.rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500656
Brad Bishop08902b02019-08-20 09:16:51 -0400657 def setup_pkkek1(self):
658 """
659 Extract from PEM certificate the Platform Key and first Key
660 Exchange Key certificate string. The hypervisor needs to provide
661 it in the Type 11 SMBIOS table
662 """
663 pemcert = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), 'OvmfPkKek1.pem')
664 try:
665 with open(pemcert, 'r') as pemfile:
666 key = pemfile.read().replace('\n', ''). \
667 replace('-----BEGIN CERTIFICATE-----', ''). \
668 replace('-----END CERTIFICATE-----', '')
669 self.ovmf_secboot_pkkek1 = key
670
671 except FileNotFoundError:
672 raise RunQemuError("Can't open PEM certificate %s " % pemcert)
673
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500674 def check_ovmf(self):
675 """Check and set full path for OVMF firmware and variable file(s)."""
676
677 for index, ovmf in enumerate(self.ovmf_bios):
678 if os.path.exists(ovmf):
679 continue
680 for suffix in ('qcow2', 'bin'):
681 path = '%s/%s.%s' % (self.get('DEPLOY_DIR_IMAGE'), ovmf, suffix)
682 if os.path.exists(path):
683 self.ovmf_bios[index] = path
Brad Bishop08902b02019-08-20 09:16:51 -0400684 if ovmf.endswith('secboot'):
685 self.setup_pkkek1()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500686 break
687 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500688 raise RunQemuError("Can't find OVMF firmware: %s" % ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500689
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600690 def check_kernel(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400691 """Check and set kernel"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600692 # The vm image doesn't need a kernel
693 if self.fstype in self.vmtypes:
694 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500695
Brad Bishop316dfdd2018-06-25 12:45:53 -0400696 # See if the user supplied a KERNEL option
697 if self.get('KERNEL'):
698 self.kernel = self.get('KERNEL')
699
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500700 # QB_DEFAULT_KERNEL is always a full file path
701 kernel_name = os.path.basename(self.get('QB_DEFAULT_KERNEL'))
702
703 # The user didn't want a kernel to be loaded
Brad Bishop316dfdd2018-06-25 12:45:53 -0400704 if kernel_name == "none" and not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500705 return
706
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600707 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
708 if not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500709 kernel_match_name = "%s/%s" % (deploy_dir_image, kernel_name)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600710 kernel_match_link = "%s/%s" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
711 kernel_startswith = "%s/%s*" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
712 cmds = (kernel_match_name, kernel_match_link, kernel_startswith)
713 self.kernel = get_first_file(cmds)
714 if not self.kernel:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500715 raise RunQemuError('KERNEL not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500716
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600717 if not os.path.exists(self.kernel):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500718 raise RunQemuError("KERNEL %s not found" % self.kernel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500719
Brad Bishop316dfdd2018-06-25 12:45:53 -0400720 def check_dtb(self):
721 """Check and set dtb"""
722 # Did the user specify a device tree?
723 if self.get('DEVICE_TREE'):
724 self.dtb = self.get('DEVICE_TREE')
725 if not os.path.exists(self.dtb):
726 raise RunQemuError('Specified DTB not found: %s' % self.dtb)
727 return
728
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600729 dtb = self.get('QB_DTB')
730 if dtb:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400731 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600732 cmd_match = "%s/%s" % (deploy_dir_image, dtb)
733 cmd_startswith = "%s/%s*" % (deploy_dir_image, dtb)
734 cmd_wild = "%s/*.dtb" % deploy_dir_image
735 cmds = (cmd_match, cmd_startswith, cmd_wild)
736 self.dtb = get_first_file(cmds)
737 if not os.path.exists(self.dtb):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500738 raise RunQemuError('DTB not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500739
Brad Bishopc68388fc2019-08-26 01:33:31 -0400740 def check_bios(self):
741 """Check and set bios"""
742
743 # See if the user supplied a BIOS option
744 if self.get('BIOS'):
745 self.bios = self.get('BIOS')
746
747 # QB_DEFAULT_BIOS is always a full file path
748 bios_name = os.path.basename(self.get('QB_DEFAULT_BIOS'))
749
750 # The user didn't want a bios to be loaded
751 if (bios_name == "" or bios_name == "none") and not self.bios:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600752 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500753
Brad Bishopc68388fc2019-08-26 01:33:31 -0400754 if not self.bios:
755 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
756 self.bios = "%s/%s" % (deploy_dir_image, bios_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500757
Brad Bishopc68388fc2019-08-26 01:33:31 -0400758 if not self.bios:
759 raise RunQemuError('BIOS not found: %s' % bios_match_name)
760
761 if not os.path.exists(self.bios):
762 raise RunQemuError("KERNEL %s not found" % self.bios)
763
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500764
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600765 def check_mem(self):
Andrew Geissler99467da2019-02-25 18:54:23 -0600766 """
767 Both qemu and kernel needs memory settings, so check QB_MEM and set it
768 for both.
769 """
770 s = re.search('-m +([0-9]+)', self.qemuparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600771 if s:
772 self.set('QB_MEM', '-m %s' % s.group(1))
773 elif not self.get('QB_MEM'):
Brad Bishop79641f22019-09-10 07:20:22 -0400774 logger.info('QB_MEM is not set, use 256M by default')
775 self.set('QB_MEM', '-m 256')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500776
Andrew Geissler99467da2019-02-25 18:54:23 -0600777 # Check and remove M or m suffix
778 qb_mem = self.get('QB_MEM')
779 if qb_mem.endswith('M') or qb_mem.endswith('m'):
780 qb_mem = qb_mem[:-1]
781
782 # Add -m prefix it not present
783 if not qb_mem.startswith('-m'):
784 qb_mem = '-m %s' % qb_mem
785
786 self.set('QB_MEM', qb_mem)
787
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800788 mach = self.get('MACHINE')
789 if not mach.startswith('qemumips'):
790 self.kernel_cmdline_script += ' mem=%s' % self.get('QB_MEM').replace('-m','').strip() + 'M'
791
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600792 self.qemu_opt_script += ' %s' % self.get('QB_MEM')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500793
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600794 def check_tcpserial(self):
795 if self.tcpserial_portnum:
Brad Bishop15ae2502019-06-18 21:44:24 -0400796 ports = self.tcpserial_portnum.split(':')
797 port = ports[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600798 if self.get('QB_TCPSERIAL_OPT'):
Brad Bishop15ae2502019-06-18 21:44:24 -0400799 self.qemu_opt_script += ' ' + self.get('QB_TCPSERIAL_OPT').replace('@PORT@', port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600800 else:
Brad Bishop15ae2502019-06-18 21:44:24 -0400801 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
802
803 if len(ports) > 1:
804 for port in ports[1:]:
805 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500806
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600807 def check_and_set(self):
808 """Check configs sanity and set when needed"""
809 self.validate_paths()
Andrew Geissler82c905d2020-04-13 13:39:40 -0500810 if not self.slirp_enabled and not self.net_bridge:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500811 check_tun()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600812 # Check audio
813 if self.audio_enabled:
814 if not self.get('QB_AUDIO_DRV'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500815 raise RunQemuError("QB_AUDIO_DRV is NULL, this board doesn't support audio")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600816 if not self.get('QB_AUDIO_OPT'):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800817 logger.warning('QB_AUDIO_OPT is NULL, you may need define it to make audio work')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600818 else:
819 self.qemu_opt_script += ' %s' % self.get('QB_AUDIO_OPT')
820 os.putenv('QEMU_AUDIO_DRV', self.get('QB_AUDIO_DRV'))
821 else:
822 os.putenv('QEMU_AUDIO_DRV', 'none')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500823
Brad Bishop15ae2502019-06-18 21:44:24 -0400824 self.check_qemu_system()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600825 self.check_kvm()
826 self.check_fstype()
827 self.check_rootfs()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500828 self.check_ovmf()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600829 self.check_kernel()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400830 self.check_dtb()
Brad Bishopc68388fc2019-08-26 01:33:31 -0400831 self.check_bios()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600832 self.check_mem()
833 self.check_tcpserial()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500834
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600835 def read_qemuboot(self):
836 if not self.qemuboot:
837 if self.get('DEPLOY_DIR_IMAGE'):
838 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600839 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800840 logger.warning("Can't find qemuboot conf file, DEPLOY_DIR_IMAGE is NULL!")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600841 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500842
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600843 if self.rootfs and not os.path.exists(self.rootfs):
844 # Lazy rootfs
845 machine = self.get('MACHINE')
846 if not machine:
847 machine = os.path.basename(deploy_dir_image)
848 self.qemuboot = "%s/%s-%s.qemuboot.conf" % (deploy_dir_image,
849 self.rootfs, machine)
850 else:
851 cmd = 'ls -t %s/*.qemuboot.conf' % deploy_dir_image
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500852 logger.debug('Running %s...' % cmd)
853 try:
854 qbs = subprocess.check_output(cmd, shell=True).decode('utf-8')
855 except subprocess.CalledProcessError as err:
856 raise RunQemuError(err)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600857 if qbs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500858 for qb in qbs.split():
859 # Don't use initramfs when other choices unless fstype is ramfs
860 if '-initramfs-' in os.path.basename(qb) and self.fstype != 'cpio.gz':
861 continue
862 self.qemuboot = qb
863 break
864 if not self.qemuboot:
865 # Use the first one when no choice
866 self.qemuboot = qbs.split()[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600867 self.qbconfload = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500868
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600869 if not self.qemuboot:
870 # If we haven't found a .qemuboot.conf at this point it probably
871 # doesn't exist, continue without
872 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500873
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600874 if not os.path.exists(self.qemuboot):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500875 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 -0500876
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500877 logger.debug('CONFFILE: %s' % self.qemuboot)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500878
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600879 cf = configparser.ConfigParser()
880 cf.read(self.qemuboot)
881 for k, v in cf.items('config_bsp'):
882 k_upper = k.upper()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500883 if v.startswith("../"):
884 v = os.path.abspath(os.path.dirname(self.qemuboot) + "/" + v)
885 elif v == ".":
886 v = os.path.dirname(self.qemuboot)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600887 self.set(k_upper, v)
888
889 def validate_paths(self):
890 """Ensure all relevant path variables are set"""
891 # When we're started with a *.qemuboot.conf arg assume that image
892 # artefacts are relative to that file, rather than in whatever
893 # directory DEPLOY_DIR_IMAGE in the conf file points to.
894 if self.qbconfload:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500895 imgdir = os.path.realpath(os.path.dirname(self.qemuboot))
896 if imgdir != os.path.realpath(self.get('DEPLOY_DIR_IMAGE')):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600897 logger.info('Setting DEPLOY_DIR_IMAGE to folder containing %s (%s)' % (self.qemuboot, imgdir))
898 self.set('DEPLOY_DIR_IMAGE', imgdir)
899
900 # If the STAGING_*_NATIVE directories from the config file don't exist
901 # and we're in a sourced OE build directory try to extract the paths
902 # from `bitbake -e`
903 havenative = os.path.exists(self.get('STAGING_DIR_NATIVE')) and \
904 os.path.exists(self.get('STAGING_BINDIR_NATIVE'))
905
906 if not havenative:
907 if not self.bitbake_e:
908 self.load_bitbake_env()
909
910 if self.bitbake_e:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500911 native_vars = ['STAGING_DIR_NATIVE']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600912 for nv in native_vars:
913 s = re.search('^%s="(.*)"' % nv, self.bitbake_e, re.M)
914 if s and s.group(1) != self.get(nv):
915 logger.info('Overriding conf file setting of %s to %s from Bitbake environment' % (nv, s.group(1)))
916 self.set(nv, s.group(1))
917 else:
918 # when we're invoked from a running bitbake instance we won't
919 # be able to call `bitbake -e`, then try:
920 # - get OE_TMPDIR from environment and guess paths based on it
921 # - get OECORE_NATIVE_SYSROOT from environment (for sdk)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500922 tmpdir = self.get('OE_TMPDIR')
923 oecore_native_sysroot = self.get('OECORE_NATIVE_SYSROOT')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600924 if tmpdir:
925 logger.info('Setting STAGING_DIR_NATIVE and STAGING_BINDIR_NATIVE relative to OE_TMPDIR (%s)' % tmpdir)
926 hostos, _, _, _, machine = os.uname()
927 buildsys = '%s-%s' % (machine, hostos.lower())
928 staging_dir_native = '%s/sysroots/%s' % (tmpdir, buildsys)
929 self.set('STAGING_DIR_NATIVE', staging_dir_native)
930 elif oecore_native_sysroot:
931 logger.info('Setting STAGING_DIR_NATIVE to OECORE_NATIVE_SYSROOT (%s)' % oecore_native_sysroot)
932 self.set('STAGING_DIR_NATIVE', oecore_native_sysroot)
933 if self.get('STAGING_DIR_NATIVE'):
934 # we have to assume that STAGING_BINDIR_NATIVE is at usr/bin
935 staging_bindir_native = '%s/usr/bin' % self.get('STAGING_DIR_NATIVE')
936 logger.info('Setting STAGING_BINDIR_NATIVE to %s' % staging_bindir_native)
937 self.set('STAGING_BINDIR_NATIVE', '%s/usr/bin' % self.get('STAGING_DIR_NATIVE'))
938
939 def print_config(self):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500940 logoutput = ['Continuing with the following parameters:']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600941 if not self.fstype in self.vmtypes:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500942 logoutput.append('KERNEL: [%s]' % self.kernel)
Brad Bishopc68388fc2019-08-26 01:33:31 -0400943 if self.bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500944 logoutput.append('BIOS: [%s]' % self.bios)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600945 if self.dtb:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500946 logoutput.append('DTB: [%s]' % self.dtb)
947 logoutput.append('MACHINE: [%s]' % self.get('MACHINE'))
Brad Bishop15ae2502019-06-18 21:44:24 -0400948 try:
949 fstype_flags = ' (' + ', '.join(self.fsinfo[self.fstype]) + ')'
950 except KeyError:
951 fstype_flags = ''
Andrew Geissler82c905d2020-04-13 13:39:40 -0500952 logoutput.append('FSTYPE: [%s%s]' % (self.fstype, fstype_flags))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600953 if self.fstype == 'nfs':
Andrew Geissler82c905d2020-04-13 13:39:40 -0500954 logoutput.append('NFS_DIR: [%s]' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600955 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500956 logoutput.append('ROOTFS: [%s]' % self.rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500957 if self.ovmf_bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500958 logoutput.append('OVMF: %s' % self.ovmf_bios)
Brad Bishop08902b02019-08-20 09:16:51 -0400959 if (self.ovmf_secboot_pkkek1):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500960 logoutput.append('SECBOOT PKKEK1: [%s...]' % self.ovmf_secboot_pkkek1[0:100])
961 logoutput.append('CONFFILE: [%s]' % self.qemuboot)
962 logoutput.append('')
963 logger.info('\n'.join(logoutput))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600964
965 def setup_nfs(self):
966 if not self.nfs_server:
967 if self.slirp_enabled:
968 self.nfs_server = '10.0.2.2'
969 else:
970 self.nfs_server = '192.168.7.1'
971
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500972 # Figure out a new nfs_instance to allow multiple qemus running.
Brad Bishop977dc1a2019-02-06 16:01:43 -0500973 ps = subprocess.check_output(("ps", "auxww")).decode('utf-8')
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500974 pattern = '/bin/unfsd .* -i .*\.pid -e .*/exports([0-9]+) '
975 all_instances = re.findall(pattern, ps, re.M)
976 if all_instances:
977 all_instances.sort(key=int)
978 self.nfs_instance = int(all_instances.pop()) + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600979
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500980 nfsd_port = 3049 + 2 * self.nfs_instance
981 mountd_port = 3048 + 2 * self.nfs_instance
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600982
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500983 # Export vars for runqemu-export-rootfs
984 export_dict = {
985 'NFS_INSTANCE': self.nfs_instance,
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500986 'NFSD_PORT': nfsd_port,
987 'MOUNTD_PORT': mountd_port,
988 }
989 for k, v in export_dict.items():
990 # Use '%s' since they are integers
991 os.putenv(k, '%s' % v)
992
Andrew Geissler82c905d2020-04-13 13:39:40 -0500993 self.unfs_opts="nfsvers=3,port=%s,tcp,mountport=%s" % (nfsd_port, mountd_port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600994
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500995 # Extract .tar.bz2 or .tar.bz if no nfs dir
996 if not (self.rootfs and os.path.isdir(self.rootfs)):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600997 src_prefix = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'))
998 dest = "%s-nfsroot" % src_prefix
999 if os.path.exists('%s.pseudo_state' % dest):
1000 logger.info('Use %s as NFS_DIR' % dest)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001001 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001002 else:
1003 src = ""
1004 src1 = '%s.tar.bz2' % src_prefix
1005 src2 = '%s.tar.gz' % src_prefix
1006 if os.path.exists(src1):
1007 src = src1
1008 elif os.path.exists(src2):
1009 src = src2
1010 if not src:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001011 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 -06001012 logger.info('NFS_DIR not found, extracting %s to %s' % (src, dest))
Brad Bishop977dc1a2019-02-06 16:01:43 -05001013 cmd = ('runqemu-extract-sdk', src, dest)
1014 logger.info('Running %s...' % str(cmd))
1015 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001016 raise RunQemuError('Failed to run %s' % cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001017 self.clean_nfs_dir = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001018 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001019
1020 # Start the userspace NFS server
Brad Bishop977dc1a2019-02-06 16:01:43 -05001021 cmd = ('runqemu-export-rootfs', 'start', self.rootfs)
1022 logger.info('Running %s...' % str(cmd))
1023 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001024 raise RunQemuError('Failed to run %s' % cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001025
1026 self.nfs_running = True
1027
Andrew Geissler82c905d2020-04-13 13:39:40 -05001028 def setup_net_bridge(self):
1029 self.set('NETWORK_CMD', '-netdev bridge,br=%s,id=net0,helper=%s -device virtio-net-pci,netdev=net0 ' % (
1030 self.net_bridge, os.path.join(self.bindir_native, 'qemu-oe-bridge-helper')))
1031
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001032 def setup_slirp(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001033 """Setup user networking"""
1034
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001035 if self.fstype == 'nfs':
1036 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001037 netconf = " " + self.cmdline_ip_slirp
1038 logger.info("Network configuration:%s", netconf)
1039 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001040 # Port mapping
1041 hostfwd = ",hostfwd=tcp::2222-:22,hostfwd=tcp::2323-:23"
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001042 qb_slirp_opt_default = "-netdev user,id=net0%s,tftp=%s" % (hostfwd, self.get('DEPLOY_DIR_IMAGE'))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001043 qb_slirp_opt = self.get('QB_SLIRP_OPT') or qb_slirp_opt_default
1044 # Figure out the port
1045 ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt)
1046 ports = [int(i) for i in ports]
1047 mac = 2
Brad Bishop08902b02019-08-20 09:16:51 -04001048
1049 lockdir = "/tmp/qemu-port-locks"
1050 if not os.path.exists(lockdir):
1051 # There might be a race issue when multi runqemu processess are
1052 # running at the same time.
1053 try:
1054 os.mkdir(lockdir)
1055 os.chmod(lockdir, 0o777)
1056 except FileExistsError:
1057 pass
1058
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001059 # Find a free port to avoid conflicts
1060 for p in ports[:]:
1061 p_new = p
Brad Bishop08902b02019-08-20 09:16:51 -04001062 while not self.check_free_port('localhost', p_new, lockdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001063 p_new += 1
1064 mac += 1
1065 while p_new in ports:
1066 p_new += 1
1067 mac += 1
1068 if p != p_new:
1069 ports.append(p_new)
1070 qb_slirp_opt = re.sub(':%s-' % p, ':%s-' % p_new, qb_slirp_opt)
1071 logger.info("Port forward changed: %s -> %s" % (p, p_new))
1072 mac = "%s%02x" % (self.mac_slirp, mac)
1073 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qb_slirp_opt))
1074 # Print out port foward
1075 hostfwd = re.findall('(hostfwd=[^,]*)', qb_slirp_opt)
1076 if hostfwd:
1077 logger.info('Port forward: %s' % ' '.join(hostfwd))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001078
1079 def setup_tap(self):
1080 """Setup tap"""
1081
1082 # This file is created when runqemu-gen-tapdevs creates a bank of tap
1083 # devices, indicating that the user should not bring up new ones using
1084 # sudo.
1085 nosudo_flag = '/etc/runqemu-nosudo'
1086 self.qemuifup = shutil.which('runqemu-ifup')
1087 self.qemuifdown = shutil.which('runqemu-ifdown')
1088 ip = shutil.which('ip')
1089 lockdir = "/tmp/qemu-tap-locks"
1090
1091 if not (self.qemuifup and self.qemuifdown and ip):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001092 logger.error("runqemu-ifup: %s" % self.qemuifup)
1093 logger.error("runqemu-ifdown: %s" % self.qemuifdown)
1094 logger.error("ip: %s" % ip)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001095 raise OEPathError("runqemu-ifup, runqemu-ifdown or ip not found")
1096
1097 if not os.path.exists(lockdir):
1098 # There might be a race issue when multi runqemu processess are
1099 # running at the same time.
1100 try:
1101 os.mkdir(lockdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001102 os.chmod(lockdir, 0o777)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001103 except FileExistsError:
1104 pass
1105
Brad Bishop977dc1a2019-02-06 16:01:43 -05001106 cmd = (ip, 'link')
1107 logger.debug('Running %s...' % str(cmd))
1108 ip_link = subprocess.check_output(cmd).decode('utf-8')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001109 # Matches line like: 6: tap0: <foo>
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001110 possibles = re.findall('^[0-9]+: +(tap[0-9]+): <.*', ip_link, re.M)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001111 tap = ""
1112 for p in possibles:
1113 lockfile = os.path.join(lockdir, p)
1114 if os.path.exists('%s.skip' % lockfile):
1115 logger.info('Found %s.skip, skipping %s' % (lockfile, p))
1116 continue
Brad Bishop08902b02019-08-20 09:16:51 -04001117 self.taplock = lockfile + '.lock'
1118 if self.acquire_taplock(error=False):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001119 tap = p
1120 logger.info("Using preconfigured tap device %s" % tap)
1121 logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap))
1122 break
1123
1124 if not tap:
1125 if os.path.exists(nosudo_flag):
1126 logger.error("Error: There are no available tap devices to use for networking,")
1127 logger.error("and I see %s exists, so I am not going to try creating" % nosudo_flag)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001128 raise RunQemuError("a new one with sudo.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001129
1130 gid = os.getgid()
1131 uid = os.getuid()
1132 logger.info("Setting up tap interface under sudo")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001133 cmd = ('sudo', self.qemuifup, str(uid), str(gid), self.bindir_native)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001134 try:
1135 tap = subprocess.check_output(cmd).decode('utf-8').strip()
1136 except subprocess.CalledProcessError as e:
1137 logger.error('Setting up tap device failed:\n%s\nRun runqemu-gen-tapdevs to manually create one.' % str(e))
1138 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001139 lockfile = os.path.join(lockdir, tap)
Brad Bishop08902b02019-08-20 09:16:51 -04001140 self.taplock = lockfile + '.lock'
1141 self.acquire_taplock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001142 self.cleantap = True
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001143 logger.debug('Created tap: %s' % tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001144
1145 if not tap:
1146 logger.error("Failed to setup tap device. Run runqemu-gen-tapdevs to manually create.")
Andrew Geissler82c905d2020-04-13 13:39:40 -05001147 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001148 self.tap = tap
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001149 tapnum = int(tap[3:])
1150 gateway = tapnum * 2 + 1
1151 client = gateway + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001152 if self.fstype == 'nfs':
1153 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001154 netconf = " " + self.cmdline_ip_tap
1155 netconf = netconf.replace('@CLIENT@', str(client))
1156 netconf = netconf.replace('@GATEWAY@', str(gateway))
1157 logger.info("Network configuration:%s", netconf)
1158 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001159 mac = "%s%02x" % (self.mac_tap, client)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001160 qb_tap_opt = self.get('QB_TAP_OPT')
1161 if qb_tap_opt:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001162 qemu_tap_opt = qb_tap_opt.replace('@TAP@', tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001163 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001164 qemu_tap_opt = "-netdev tap,id=net0,ifname=%s,script=no,downscript=no" % (self.tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001165
1166 if self.vhost_enabled:
1167 qemu_tap_opt += ',vhost=on'
1168
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001169 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qemu_tap_opt))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001170
1171 def setup_network(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001172 if self.get('QB_NET') == 'none':
1173 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001174 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001175 self.saved_stty = subprocess.check_output(("stty", "-g")).decode('utf-8').strip()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001176 self.network_device = self.get('QB_NETWORK_DEVICE') or self.network_device
Andrew Geissler82c905d2020-04-13 13:39:40 -05001177 if self.net_bridge:
1178 self.setup_net_bridge()
1179 elif self.slirp_enabled:
1180 self.cmdline_ip_slirp = self.get('QB_CMDLINE_IP_SLIRP') or self.cmdline_ip_slirp
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001181 self.setup_slirp()
1182 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001183 self.cmdline_ip_tap = self.get('QB_CMDLINE_IP_TAP') or self.cmdline_ip_tap
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001184 self.setup_tap()
1185
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001186 def setup_rootfs(self):
1187 if self.get('QB_ROOTFS') == 'none':
1188 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001189 if 'wic.' in self.fstype:
1190 self.fstype = self.fstype[4:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001191 rootfs_format = self.fstype if self.fstype in ('vmdk', 'qcow2', 'vdi') else 'raw'
1192
1193 qb_rootfs_opt = self.get('QB_ROOTFS_OPT')
1194 if qb_rootfs_opt:
1195 self.rootfs_options = qb_rootfs_opt.replace('@ROOTFS@', self.rootfs)
1196 else:
1197 self.rootfs_options = '-drive file=%s,if=virtio,format=%s' % (self.rootfs, rootfs_format)
1198
Andrew Geissler82c905d2020-04-13 13:39:40 -05001199 qb_rootfs_extra_opt = self.get("QB_ROOTFS_EXTRA_OPT")
1200 if qb_rootfs_extra_opt and not qb_rootfs_extra_opt.startswith(","):
1201 qb_rootfs_extra_opt = "," + qb_rootfs_extra_opt
1202
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001203 if self.fstype in ('cpio.gz', 'cpio'):
1204 self.kernel_cmdline = 'root=/dev/ram0 rw debugshell'
1205 self.rootfs_options = '-initrd %s' % self.rootfs
1206 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001207 vm_drive = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001208 if self.fstype in self.vmtypes:
1209 if self.fstype == 'iso':
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001210 vm_drive = '-drive file=%s,if=virtio,media=cdrom' % self.rootfs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001211 elif self.get('QB_DRIVE_TYPE'):
1212 drive_type = self.get('QB_DRIVE_TYPE')
1213 if drive_type.startswith("/dev/sd"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001214 logger.info('Using scsi drive')
Andrew Geissler82c905d2020-04-13 13:39:40 -05001215 vm_drive = '-drive if=none,id=hd,file=%s,format=%s -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd%s' \
1216 % (self.rootfs, rootfs_format, qb_rootfs_extra_opt)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001217 elif drive_type.startswith("/dev/hd"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001218 logger.info('Using ide drive')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001219 vm_drive = "-drive file=%s,format=%s" % (self.rootfs, rootfs_format)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001220 elif drive_type.startswith("/dev/vdb"):
1221 logger.info('Using block virtio drive');
1222 vm_drive = '-drive id=disk0,file=%s,if=none,format=%s -device virtio-blk-device,drive=disk0%s' \
1223 % (self.rootfs, rootfs_format,qb_rootfs_extra_opt)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001224 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001225 # virtio might have been selected explicitly (just use it), or
1226 # is used as fallback (then warn about that).
1227 if not drive_type.startswith("/dev/vd"):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001228 logger.warning("Unknown QB_DRIVE_TYPE: %s" % drive_type)
1229 logger.warning("Failed to figure out drive type, consider define or fix QB_DRIVE_TYPE")
1230 logger.warning('Trying to use virtio block drive')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001231 vm_drive = '-drive if=virtio,file=%s,format=%s' % (self.rootfs, rootfs_format)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001232
1233 # All branches above set vm_drive.
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001234 self.rootfs_options = '%s -no-reboot' % vm_drive
Brad Bishopf3f93bb2019-10-16 14:33:32 -04001235 self.kernel_cmdline = 'root=%s rw' % (self.get('QB_KERNEL_ROOT'))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001236
1237 if self.fstype == 'nfs':
1238 self.rootfs_options = ''
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001239 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 -04001240 self.kernel_cmdline = 'root=%s rw' % k_root
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001241
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001242 if self.fstype == 'none':
1243 self.rootfs_options = ''
1244
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001245 self.set('ROOTFS_OPTIONS', self.rootfs_options)
1246
1247 def guess_qb_system(self):
1248 """attempt to determine the appropriate qemu-system binary"""
1249 mach = self.get('MACHINE')
1250 if not mach:
1251 search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*'
1252 if self.rootfs:
1253 match = re.match(search, self.rootfs)
1254 if match:
1255 mach = match.group(1)
1256 elif self.kernel:
1257 match = re.match(search, self.kernel)
1258 if match:
1259 mach = match.group(1)
1260
1261 if not mach:
1262 return None
1263
1264 if mach == 'qemuarm':
1265 qbsys = 'arm'
1266 elif mach == 'qemuarm64':
1267 qbsys = 'aarch64'
1268 elif mach == 'qemux86':
1269 qbsys = 'i386'
1270 elif mach == 'qemux86-64':
1271 qbsys = 'x86_64'
1272 elif mach == 'qemuppc':
1273 qbsys = 'ppc'
1274 elif mach == 'qemumips':
1275 qbsys = 'mips'
1276 elif mach == 'qemumips64':
1277 qbsys = 'mips64'
1278 elif mach == 'qemumipsel':
1279 qbsys = 'mipsel'
1280 elif mach == 'qemumips64el':
1281 qbsys = 'mips64el'
Brad Bishop316dfdd2018-06-25 12:45:53 -04001282 elif mach == 'qemuriscv64':
1283 qbsys = 'riscv64'
1284 elif mach == 'qemuriscv32':
1285 qbsys = 'riscv32'
Brad Bishop004d4992018-10-02 23:54:45 +02001286 else:
1287 logger.error("Unable to determine QEMU PC System emulator for %s machine." % mach)
1288 logger.error("As %s is not among valid QEMU machines such as," % mach)
1289 logger.error("qemux86-64, qemux86, qemuarm64, qemuarm, qemumips64, qemumips64el, qemumipsel, qemumips, qemuppc")
1290 raise RunQemuError("Set qb_system_name with suitable QEMU PC System emulator in .*qemuboot.conf.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001291
1292 return 'qemu-system-%s' % qbsys
1293
Brad Bishop15ae2502019-06-18 21:44:24 -04001294 def check_qemu_system(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001295 qemu_system = self.get('QB_SYSTEM_NAME')
1296 if not qemu_system:
1297 qemu_system = self.guess_qb_system()
1298 if not qemu_system:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001299 raise RunQemuError("Failed to boot, QB_SYSTEM_NAME is NULL!")
Brad Bishop15ae2502019-06-18 21:44:24 -04001300 self.qemu_system = qemu_system
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001301
Brad Bishop15ae2502019-06-18 21:44:24 -04001302 def setup_final(self):
1303 qemu_bin = os.path.join(self.bindir_native, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001304
1305 # It is possible to have qemu-native in ASSUME_PROVIDED, and it won't
1306 # find QEMU in sysroot, it needs to use host's qemu.
1307 if not os.path.exists(qemu_bin):
1308 logger.info("QEMU binary not found in %s, trying host's QEMU" % qemu_bin)
1309 for path in (os.environ['PATH'] or '').split(':'):
Brad Bishop15ae2502019-06-18 21:44:24 -04001310 qemu_bin_tmp = os.path.join(path, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001311 logger.info("Trying: %s" % qemu_bin_tmp)
1312 if os.path.exists(qemu_bin_tmp):
1313 qemu_bin = qemu_bin_tmp
1314 if not os.path.isabs(qemu_bin):
1315 qemu_bin = os.path.abspath(qemu_bin)
1316 logger.info("Using host's QEMU: %s" % qemu_bin)
1317 break
1318
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001319 if not os.access(qemu_bin, os.X_OK):
1320 raise OEPathError("No QEMU binary '%s' could be found" % qemu_bin)
1321
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001322 self.qemu_opt = "%s %s %s %s" % (qemu_bin, self.get('NETWORK_CMD'), self.get('ROOTFS_OPTIONS'), self.get('QB_OPT_APPEND'))
1323
1324 for ovmf in self.ovmf_bios:
1325 format = ovmf.rsplit('.', 1)[-1]
1326 self.qemu_opt += ' -drive if=pflash,format=%s,file=%s' % (format, ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001327
1328 self.qemu_opt += ' ' + self.qemu_opt_script
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001329
Brad Bishop08902b02019-08-20 09:16:51 -04001330 if self.ovmf_secboot_pkkek1:
1331 # Provide the Platform Key and first Key Exchange Key certificate as an
1332 # OEM string in the SMBIOS Type 11 table. Prepend the certificate string
1333 # with "application prefix" of the EnrollDefaultKeys.efi application
1334 self.qemu_opt += ' -smbios type=11,value=4e32566d-8e9e-4f52-81d3-5bb9715f9727:' \
1335 + self.ovmf_secboot_pkkek1
1336
Andrew Geissler99467da2019-02-25 18:54:23 -06001337 # Append qemuparams to override previous settings
1338 if self.qemuparams:
1339 self.qemu_opt += ' ' + self.qemuparams
1340
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001341 if self.snapshot:
1342 self.qemu_opt += " -snapshot"
1343
Brad Bishop19323692019-04-05 15:28:33 -04001344 if self.serialconsole:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001345 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001346 subprocess.check_call(("stty", "intr", "^]"))
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001347 logger.info("Interrupt character is '^]'")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001348
1349 first_serial = ""
1350 if not re.search("-nographic", self.qemu_opt):
1351 first_serial = "-serial mon:vc"
1352 # We always want a ttyS1. Since qemu by default adds a serial
1353 # port when nodefaults is not specified, it seems that all that
1354 # would be needed is to make sure a "-serial" is there. However,
1355 # it appears that when "-serial" is specified, it ignores the
1356 # default serial port that is normally added. So here we make
1357 # sure to add two -serial if there are none. And only one if
1358 # there is one -serial already.
1359 serial_num = len(re.findall("-serial", self.qemu_opt))
1360 if serial_num == 0:
1361 self.qemu_opt += " %s %s" % (first_serial, self.get("QB_SERIAL_OPT"))
1362 elif serial_num == 1:
1363 self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT")
1364
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001365 # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES),
1366 # if not serial or serialtcp options was specified only ttyS0 is created
1367 # and sysvinit shows an error trying to enable ttyS1:
1368 # INIT: Id "S1" respawning too fast: disabled for 5 minutes
1369 serial_num = len(re.findall("-serial", self.qemu_opt))
1370 if serial_num == 0:
Brad Bishop19323692019-04-05 15:28:33 -04001371 if re.search("-nographic", self.qemu_opt) or self.serialstdio:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001372 self.qemu_opt += " -serial mon:stdio -serial null"
1373 else:
1374 self.qemu_opt += " -serial mon:vc -serial null"
1375
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001376 def start_qemu(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001377 import shlex
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001378 if self.kernel:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001379 kernel_opts = "-kernel %s -append '%s %s %s %s'" % (self.kernel, self.kernel_cmdline,
1380 self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'),
1381 self.bootparams)
Brad Bishopc68388fc2019-08-26 01:33:31 -04001382 if self.bios:
1383 kernel_opts += " -bios %s" % self.bios
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001384 if self.dtb:
1385 kernel_opts += " -dtb %s" % self.dtb
1386 else:
1387 kernel_opts = ""
1388 cmd = "%s %s" % (self.qemu_opt, kernel_opts)
Brad Bishop004d4992018-10-02 23:54:45 +02001389 cmds = shlex.split(cmd)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001390 logger.info('Running %s\n' % cmd)
Brad Bishopf86d0552018-12-04 14:18:15 -08001391 pass_fds = []
Brad Bishop08902b02019-08-20 09:16:51 -04001392 if self.taplock_descriptor:
1393 pass_fds = [self.taplock_descriptor.fileno()]
1394 if len(self.portlocks):
1395 for descriptor in self.portlocks.values():
1396 pass_fds.append(descriptor.fileno())
Brad Bishopf86d0552018-12-04 14:18:15 -08001397 process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds)
Brad Bishop004d4992018-10-02 23:54:45 +02001398 self.qemupid = process.pid
1399 retcode = process.wait()
1400 if retcode:
1401 if retcode == -signal.SIGTERM:
1402 logger.info("Qemu terminated by SIGTERM")
1403 else:
1404 logger.error("Failed to run qemu: %s", process.stderr.read().decode())
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001405
1406 def cleanup(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001407 if self.cleaned:
1408 return
1409
1410 # avoid dealing with SIGTERM when cleanup function is running
1411 signal.signal(signal.SIGTERM, signal.SIG_IGN)
1412
1413 logger.info("Cleaning up")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001414 if self.cleantap:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001415 cmd = ('sudo', self.qemuifdown, self.tap, self.bindir_native)
1416 logger.debug('Running %s' % str(cmd))
1417 subprocess.check_call(cmd)
Brad Bishop08902b02019-08-20 09:16:51 -04001418 self.release_taplock()
1419 self.release_portlock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001420
1421 if self.nfs_running:
1422 logger.info("Shutting down the userspace NFS server...")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001423 cmd = ("runqemu-export-rootfs", "stop", self.rootfs)
1424 logger.debug('Running %s' % str(cmd))
1425 subprocess.check_call(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001426
1427 if self.saved_stty:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001428 subprocess.check_call(("stty", self.saved_stty))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001429
1430 if self.clean_nfs_dir:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001431 logger.info('Removing %s' % self.rootfs)
1432 shutil.rmtree(self.rootfs)
1433 shutil.rmtree('%s.pseudo_state' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001434
Brad Bishop004d4992018-10-02 23:54:45 +02001435 self.cleaned = True
1436
Andrew Geissler82c905d2020-04-13 13:39:40 -05001437 def run_bitbake_env(self, mach=None):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001438 bitbake = shutil.which('bitbake')
1439 if not bitbake:
1440 return
1441
1442 if not mach:
1443 mach = self.get('MACHINE')
1444
Andrew Geissler82c905d2020-04-13 13:39:40 -05001445 multiconfig = self.get('MULTICONFIG')
1446 if multiconfig:
1447 multiconfig = "mc:%s" % multiconfig
1448
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001449 if mach:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001450 cmd = 'MACHINE=%s bitbake -e %s' % (mach, multiconfig)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001451 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001452 cmd = 'bitbake -e %s' % multiconfig
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001453
1454 logger.info('Running %s...' % cmd)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001455 return subprocess.check_output(cmd, shell=True).decode('utf-8')
1456
1457 def load_bitbake_env(self, mach=None):
1458 if self.bitbake_e:
1459 return
1460
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001461 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001462 self.bitbake_e = self.run_bitbake_env(mach=mach)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001463 except subprocess.CalledProcessError as err:
1464 self.bitbake_e = ''
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001465 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 -06001466
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001467 def validate_combos(self):
1468 if (self.fstype in self.vmtypes) and self.kernel:
1469 raise RunQemuError("%s doesn't need kernel %s!" % (self.fstype, self.kernel))
1470
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001471 @property
1472 def bindir_native(self):
1473 result = self.get('STAGING_BINDIR_NATIVE')
1474 if result and os.path.exists(result):
1475 return result
1476
Andrew Geissler82c905d2020-04-13 13:39:40 -05001477 cmd = ['bitbake', '-e']
1478 multiconfig = self.get('MULTICONFIG')
1479 if multiconfig:
1480 cmd.append('mc:%s:qemu-helper-native' % multiconfig)
1481 else:
1482 cmd.append('qemu-helper-native')
1483
Brad Bishop977dc1a2019-02-06 16:01:43 -05001484 logger.info('Running %s...' % str(cmd))
1485 out = subprocess.check_output(cmd).decode('utf-8')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001486
1487 match = re.search('^STAGING_BINDIR_NATIVE="(.*)"', out, re.M)
1488 if match:
1489 result = match.group(1)
1490 if os.path.exists(result):
1491 self.set('STAGING_BINDIR_NATIVE', result)
1492 return result
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001493 raise RunQemuError("Native sysroot directory %s doesn't exist" % result)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001494 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001495 raise RunQemuError("Can't find STAGING_BINDIR_NATIVE in '%s' output" % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001496
1497
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001498def main():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001499 if "help" in sys.argv or '-h' in sys.argv or '--help' in sys.argv:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001500 print_usage()
1501 return 0
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001502 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001503 config = BaseConfig()
Brad Bishop004d4992018-10-02 23:54:45 +02001504
1505 def sigterm_handler(signum, frame):
1506 logger.info("SIGTERM received")
1507 os.kill(config.qemupid, signal.SIGTERM)
1508 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001509 # Deliberately ignore the return code of 'tput smam'.
1510 subprocess.call(["tput", "smam"])
Brad Bishop004d4992018-10-02 23:54:45 +02001511 signal.signal(signal.SIGTERM, sigterm_handler)
1512
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001513 config.check_args()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001514 config.read_qemuboot()
1515 config.check_and_set()
1516 # Check whether the combos is valid or not
1517 config.validate_combos()
1518 config.print_config()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001519 config.setup_network()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001520 config.setup_rootfs()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001521 config.setup_final()
1522 config.start_qemu()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001523 except RunQemuError as err:
1524 logger.error(err)
1525 return 1
1526 except Exception as err:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001527 import traceback
1528 traceback.print_exc()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001529 return 1
1530 finally:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001531 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001532 # Deliberately ignore the return code of 'tput smam'.
1533 subprocess.call(["tput", "smam"])
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001534
1535if __name__ == "__main__":
1536 sys.exit(main())