blob: 7fb5f7db57b34c455e9657338d45c127f68b775e [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()
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500464 self.qemu_opt_script += ' -vga virtio -display sdl,gl=on,show-cursor=on'
Brad Bishop6dbb3162019-11-25 09:41:34 -0500465 elif 'gl-es' in sys.argv[1:]:
Andrew Geisslerc182c622020-05-15 14:13:32 -0500466 self.set_dri_path()
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500467 self.qemu_opt_script += ' -vga virtio -display sdl,gl=es,show-cursor=on'
Brad Bishop6dbb3162019-11-25 09:41:34 -0500468 else:
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500469 self.qemu_opt_script += ' -display sdl,show-cursor=on'
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()
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500473 self.qemu_opt_script += ' -vga virtio -display gtk,gl=on,show-cursor=on'
Brad Bishopa34c0302019-09-23 22:34:48 -0400474 elif 'gl-es' in sys.argv[1:]:
Andrew Geisslerc182c622020-05-15 14:13:32 -0500475 self.set_dri_path()
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500476 self.qemu_opt_script += ' -vga virtio -display gtk,gl=es,show-cursor=on'
Brad Bishopa34c0302019-09-23 22:34:48 -0400477 else:
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500478 self.qemu_opt_script += ' -display gtk,show-cursor=on'
Brad Bishopa34c0302019-09-23 22:34:48 -0400479 elif arg == 'gl' or arg == 'gl-es':
480 # These args are handled inside sdl or gtk blocks above
Andrew Geissler635e0e42020-08-21 15:58:33 -0500481 if ('gtk' not in sys.argv) and ('sdl' not in sys.argv):
482 raise RunQemuError('Option %s also needs gtk or sdl option.' % (arg))
Brad Bishop19323692019-04-05 15:28:33 -0400483 elif arg == 'egl-headless':
Andrew Geisslerc182c622020-05-15 14:13:32 -0500484 self.set_dri_path()
Andrew Geisslerd25ed322020-06-27 00:28:28 -0500485 self.qemu_opt_script += ' -vga virtio -display egl-headless,show-cursor=on'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600486 elif arg == 'serial':
487 self.kernel_cmdline_script += ' console=ttyS0'
Brad Bishop19323692019-04-05 15:28:33 -0400488 self.serialconsole = True
489 elif arg == "serialstdio":
490 self.kernel_cmdline_script += ' console=ttyS0'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600491 self.serialstdio = True
492 elif arg == 'audio':
493 logger.info("Enabling audio in qemu")
494 logger.info("Please install sound drivers in linux host")
495 self.audio_enabled = True
496 elif arg == 'kvm':
497 self.kvm_enabled = True
498 elif arg == 'kvm-vhost':
499 self.vhost_enabled = True
500 elif arg == 'slirp':
501 self.slirp_enabled = True
Andrew Geissler82c905d2020-04-13 13:39:40 -0500502 elif arg.startswith('bridge='):
503 self.net_bridge = '%s' % arg[len('bridge='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600504 elif arg == 'snapshot':
505 self.snapshot = True
506 elif arg == 'publicvnc':
507 self.qemu_opt_script += ' -vnc :0'
508 elif arg.startswith('tcpserial='):
Brad Bishop15ae2502019-06-18 21:44:24 -0400509 self.tcpserial_portnum = '%s' % arg[len('tcpserial='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600510 elif arg.startswith('qemuparams='):
Andrew Geissler99467da2019-02-25 18:54:23 -0600511 self.qemuparams = ' %s' % arg[len('qemuparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600512 elif arg.startswith('bootparams='):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500513 self.bootparams = arg[len('bootparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600514 elif os.path.exists(arg) or (re.search(':', arg) and re.search('/', arg)):
515 self.check_arg_path(os.path.abspath(arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500516 elif re.search(r'-image-|-image$', arg):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600517 # Lazy rootfs
518 self.rootfs = arg
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500519 elif arg.startswith('ovmf'):
520 self.ovmf_bios.append(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600521 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500522 # At last, assume it is the MACHINE
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600523 if (not unknown_arg) or unknown_arg == arg:
524 unknown_arg = arg
525 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500526 raise RunQemuError("Can't handle two unknown args: %s %s\n"
527 "Try 'runqemu help' on how to use it" % \
528 (unknown_arg, arg))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600529 # Check to make sure it is a valid machine
Brad Bishopa5c52ff2018-11-23 10:55:50 +1300530 if unknown_arg and self.get('MACHINE') != unknown_arg:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500531 if self.get('DEPLOY_DIR_IMAGE'):
532 machine = os.path.basename(self.get('DEPLOY_DIR_IMAGE'))
533 if unknown_arg == machine:
534 self.set("MACHINE", machine)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500535
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600536 self.check_arg_machine(unknown_arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500537
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500538 if not (self.get('DEPLOY_DIR_IMAGE') or self.qbconfload):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500539 self.load_bitbake_env()
540 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
541 if s:
542 self.set("DEPLOY_DIR_IMAGE", s.group(1))
543
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600544 def check_kvm(self):
545 """Check kvm and kvm-host"""
546 if not (self.kvm_enabled or self.vhost_enabled):
547 self.qemu_opt_script += ' %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU'))
548 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500549
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600550 if not self.get('QB_CPU_KVM'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500551 raise RunQemuError("QB_CPU_KVM is NULL, this board doesn't support kvm")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500552
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600553 self.qemu_opt_script += ' %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU_KVM'))
554 yocto_kvm_wiki = "https://wiki.yoctoproject.org/wiki/How_to_enable_KVM_for_Poky_qemu"
555 yocto_paravirt_kvm_wiki = "https://wiki.yoctoproject.org/wiki/Running_an_x86_Yocto_Linux_image_under_QEMU_KVM"
556 dev_kvm = '/dev/kvm'
557 dev_vhost = '/dev/vhost-net'
Brad Bishop15ae2502019-06-18 21:44:24 -0400558 if self.qemu_system.endswith(('i386', 'x86_64')):
559 with open('/proc/cpuinfo', 'r') as f:
560 kvm_cap = re.search('vmx|svm', "".join(f.readlines()))
561 if not kvm_cap:
562 logger.error("You are trying to enable KVM on a cpu without VT support.")
563 logger.error("Remove kvm from the command-line, or refer:")
564 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500565
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600566 if not os.path.exists(dev_kvm):
567 logger.error("Missing KVM device. Have you inserted kvm modules?")
568 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500569 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500570
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600571 if os.access(dev_kvm, os.W_OK|os.R_OK):
572 self.qemu_opt_script += ' -enable-kvm'
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500573 if self.get('MACHINE') == "qemux86":
574 # Workaround for broken APIC window on pre 4.15 host kernels which causes boot hangs
575 # See YOCTO #12301
576 # On 64 bit we use x2apic
577 self.kernel_cmdline_script += " clocksource=kvm-clock hpet=disable noapic nolapic"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600578 else:
579 logger.error("You have no read or write permission on /dev/kvm.")
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 if self.vhost_enabled:
584 if not os.path.exists(dev_vhost):
585 logger.error("Missing virtio net device. Have you inserted vhost-net module?")
586 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500587 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500588
Andrew Geissler635e0e42020-08-21 15:58:33 -0500589 if not os.access(dev_vhost, os.W_OK|os.R_OK):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600590 logger.error("You have no read or write permission on /dev/vhost-net.")
591 logger.error("Please change the ownership of this file as described at:")
Andrew Geissler635e0e42020-08-21 15:58:33 -0500592 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500593
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600594 def check_fstype(self):
595 """Check and setup FSTYPE"""
596 if not self.fstype:
597 fstype = self.get('QB_DEFAULT_FSTYPE')
598 if fstype:
599 self.fstype = fstype
600 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500601 raise RunQemuError("FSTYPE is NULL!")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500602
Brad Bishop15ae2502019-06-18 21:44:24 -0400603 # parse QB_FSINFO into dict, e.g. { 'wic': ['no-kernel-in-fs', 'a-flag'], 'ext4': ['another-flag']}
604 wic_fs = False
605 qb_fsinfo = self.get('QB_FSINFO')
606 if qb_fsinfo:
607 qb_fsinfo = qb_fsinfo.split()
608 for fsinfo in qb_fsinfo:
609 try:
610 fstype, fsflag = fsinfo.split(':')
611
612 if fstype == 'wic':
613 if fsflag == 'no-kernel-in-fs':
614 wic_fs = True
615 elif fsflag == 'kernel-in-fs':
616 wic_fs = False
617 else:
618 logger.warn('Unknown flag "%s:%s" in QB_FSINFO', fstype, fsflag)
619 continue
620 else:
621 logger.warn('QB_FSINFO is not supported for image type "%s"', fstype)
622 continue
623
624 if fstype in self.fsinfo:
625 self.fsinfo[fstype].append(fsflag)
626 else:
627 self.fsinfo[fstype] = [fsflag]
628 except Exception:
629 logger.error('Invalid parameter "%s" in QB_FSINFO', fsinfo)
630
631 # treat wic images as vmimages (with kernel) or as fsimages (rootfs only)
632 if wic_fs:
633 self.fstypes = self.fstypes + self.wictypes
634 else:
635 self.vmtypes = self.vmtypes + self.wictypes
636
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600637 def check_rootfs(self):
638 """Check and set rootfs"""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500639
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500640 if self.fstype == "none":
641 return
642
643 if self.get('ROOTFS'):
644 if not self.rootfs:
645 self.rootfs = self.get('ROOTFS')
646 elif self.get('ROOTFS') != self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500647 raise RunQemuError("Maybe conflicted ROOTFS: %s vs %s" % (self.get('ROOTFS'), self.rootfs))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500648
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600649 if self.fstype == 'nfs':
650 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500651
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600652 if self.rootfs and not os.path.exists(self.rootfs):
653 # Lazy rootfs
654 self.rootfs = "%s/%s-%s.%s" % (self.get('DEPLOY_DIR_IMAGE'),
655 self.rootfs, self.get('MACHINE'),
656 self.fstype)
657 elif not self.rootfs:
658 cmd_name = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_NAME'), self.fstype)
659 cmd_link = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'), self.fstype)
660 cmds = (cmd_name, cmd_link)
661 self.rootfs = get_first_file(cmds)
662 if not self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500663 raise RunQemuError("Failed to find rootfs: %s or %s" % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500664
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600665 if not os.path.exists(self.rootfs):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500666 raise RunQemuError("Can't find rootfs: %s" % self.rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500667
Brad Bishop08902b02019-08-20 09:16:51 -0400668 def setup_pkkek1(self):
669 """
670 Extract from PEM certificate the Platform Key and first Key
671 Exchange Key certificate string. The hypervisor needs to provide
672 it in the Type 11 SMBIOS table
673 """
674 pemcert = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), 'OvmfPkKek1.pem')
675 try:
676 with open(pemcert, 'r') as pemfile:
677 key = pemfile.read().replace('\n', ''). \
678 replace('-----BEGIN CERTIFICATE-----', ''). \
679 replace('-----END CERTIFICATE-----', '')
680 self.ovmf_secboot_pkkek1 = key
681
682 except FileNotFoundError:
683 raise RunQemuError("Can't open PEM certificate %s " % pemcert)
684
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500685 def check_ovmf(self):
686 """Check and set full path for OVMF firmware and variable file(s)."""
687
688 for index, ovmf in enumerate(self.ovmf_bios):
689 if os.path.exists(ovmf):
690 continue
691 for suffix in ('qcow2', 'bin'):
692 path = '%s/%s.%s' % (self.get('DEPLOY_DIR_IMAGE'), ovmf, suffix)
693 if os.path.exists(path):
694 self.ovmf_bios[index] = path
Brad Bishop08902b02019-08-20 09:16:51 -0400695 if ovmf.endswith('secboot'):
696 self.setup_pkkek1()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500697 break
698 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500699 raise RunQemuError("Can't find OVMF firmware: %s" % ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500700
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600701 def check_kernel(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400702 """Check and set kernel"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600703 # The vm image doesn't need a kernel
704 if self.fstype in self.vmtypes:
705 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500706
Brad Bishop316dfdd2018-06-25 12:45:53 -0400707 # See if the user supplied a KERNEL option
708 if self.get('KERNEL'):
709 self.kernel = self.get('KERNEL')
710
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500711 # QB_DEFAULT_KERNEL is always a full file path
712 kernel_name = os.path.basename(self.get('QB_DEFAULT_KERNEL'))
713
714 # The user didn't want a kernel to be loaded
Brad Bishop316dfdd2018-06-25 12:45:53 -0400715 if kernel_name == "none" and not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500716 return
717
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600718 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
719 if not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500720 kernel_match_name = "%s/%s" % (deploy_dir_image, kernel_name)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600721 kernel_match_link = "%s/%s" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
722 kernel_startswith = "%s/%s*" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
723 cmds = (kernel_match_name, kernel_match_link, kernel_startswith)
724 self.kernel = get_first_file(cmds)
725 if not self.kernel:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500726 raise RunQemuError('KERNEL not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500727
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600728 if not os.path.exists(self.kernel):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500729 raise RunQemuError("KERNEL %s not found" % self.kernel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500730
Brad Bishop316dfdd2018-06-25 12:45:53 -0400731 def check_dtb(self):
732 """Check and set dtb"""
733 # Did the user specify a device tree?
734 if self.get('DEVICE_TREE'):
735 self.dtb = self.get('DEVICE_TREE')
736 if not os.path.exists(self.dtb):
737 raise RunQemuError('Specified DTB not found: %s' % self.dtb)
738 return
739
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600740 dtb = self.get('QB_DTB')
741 if dtb:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400742 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600743 cmd_match = "%s/%s" % (deploy_dir_image, dtb)
744 cmd_startswith = "%s/%s*" % (deploy_dir_image, dtb)
745 cmd_wild = "%s/*.dtb" % deploy_dir_image
746 cmds = (cmd_match, cmd_startswith, cmd_wild)
747 self.dtb = get_first_file(cmds)
748 if not os.path.exists(self.dtb):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500749 raise RunQemuError('DTB not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500750
Brad Bishopc68388fc2019-08-26 01:33:31 -0400751 def check_bios(self):
752 """Check and set bios"""
753
754 # See if the user supplied a BIOS option
755 if self.get('BIOS'):
756 self.bios = self.get('BIOS')
757
758 # QB_DEFAULT_BIOS is always a full file path
759 bios_name = os.path.basename(self.get('QB_DEFAULT_BIOS'))
760
761 # The user didn't want a bios to be loaded
762 if (bios_name == "" or bios_name == "none") and not self.bios:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600763 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500764
Brad Bishopc68388fc2019-08-26 01:33:31 -0400765 if not self.bios:
766 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
767 self.bios = "%s/%s" % (deploy_dir_image, bios_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500768
Brad Bishopc68388fc2019-08-26 01:33:31 -0400769 if not self.bios:
770 raise RunQemuError('BIOS not found: %s' % bios_match_name)
771
772 if not os.path.exists(self.bios):
773 raise RunQemuError("KERNEL %s not found" % self.bios)
774
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500775
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600776 def check_mem(self):
Andrew Geissler99467da2019-02-25 18:54:23 -0600777 """
778 Both qemu and kernel needs memory settings, so check QB_MEM and set it
779 for both.
780 """
781 s = re.search('-m +([0-9]+)', self.qemuparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600782 if s:
783 self.set('QB_MEM', '-m %s' % s.group(1))
784 elif not self.get('QB_MEM'):
Brad Bishop79641f22019-09-10 07:20:22 -0400785 logger.info('QB_MEM is not set, use 256M by default')
786 self.set('QB_MEM', '-m 256')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500787
Andrew Geissler99467da2019-02-25 18:54:23 -0600788 # Check and remove M or m suffix
789 qb_mem = self.get('QB_MEM')
790 if qb_mem.endswith('M') or qb_mem.endswith('m'):
791 qb_mem = qb_mem[:-1]
792
793 # Add -m prefix it not present
794 if not qb_mem.startswith('-m'):
795 qb_mem = '-m %s' % qb_mem
796
797 self.set('QB_MEM', qb_mem)
798
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800799 mach = self.get('MACHINE')
800 if not mach.startswith('qemumips'):
801 self.kernel_cmdline_script += ' mem=%s' % self.get('QB_MEM').replace('-m','').strip() + 'M'
802
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600803 self.qemu_opt_script += ' %s' % self.get('QB_MEM')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500804
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600805 def check_tcpserial(self):
806 if self.tcpserial_portnum:
Brad Bishop15ae2502019-06-18 21:44:24 -0400807 ports = self.tcpserial_portnum.split(':')
808 port = ports[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600809 if self.get('QB_TCPSERIAL_OPT'):
Brad Bishop15ae2502019-06-18 21:44:24 -0400810 self.qemu_opt_script += ' ' + self.get('QB_TCPSERIAL_OPT').replace('@PORT@', port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600811 else:
Brad Bishop15ae2502019-06-18 21:44:24 -0400812 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
813
814 if len(ports) > 1:
815 for port in ports[1:]:
816 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500817
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600818 def check_and_set(self):
819 """Check configs sanity and set when needed"""
820 self.validate_paths()
Andrew Geissler82c905d2020-04-13 13:39:40 -0500821 if not self.slirp_enabled and not self.net_bridge:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500822 check_tun()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600823 # Check audio
824 if self.audio_enabled:
825 if not self.get('QB_AUDIO_DRV'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500826 raise RunQemuError("QB_AUDIO_DRV is NULL, this board doesn't support audio")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600827 if not self.get('QB_AUDIO_OPT'):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800828 logger.warning('QB_AUDIO_OPT is NULL, you may need define it to make audio work')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600829 else:
830 self.qemu_opt_script += ' %s' % self.get('QB_AUDIO_OPT')
831 os.putenv('QEMU_AUDIO_DRV', self.get('QB_AUDIO_DRV'))
832 else:
833 os.putenv('QEMU_AUDIO_DRV', 'none')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500834
Brad Bishop15ae2502019-06-18 21:44:24 -0400835 self.check_qemu_system()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600836 self.check_kvm()
837 self.check_fstype()
838 self.check_rootfs()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500839 self.check_ovmf()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600840 self.check_kernel()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400841 self.check_dtb()
Brad Bishopc68388fc2019-08-26 01:33:31 -0400842 self.check_bios()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600843 self.check_mem()
844 self.check_tcpserial()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500845
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600846 def read_qemuboot(self):
847 if not self.qemuboot:
848 if self.get('DEPLOY_DIR_IMAGE'):
849 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600850 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800851 logger.warning("Can't find qemuboot conf file, DEPLOY_DIR_IMAGE is NULL!")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600852 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500853
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600854 if self.rootfs and not os.path.exists(self.rootfs):
855 # Lazy rootfs
856 machine = self.get('MACHINE')
857 if not machine:
858 machine = os.path.basename(deploy_dir_image)
859 self.qemuboot = "%s/%s-%s.qemuboot.conf" % (deploy_dir_image,
860 self.rootfs, machine)
861 else:
862 cmd = 'ls -t %s/*.qemuboot.conf' % deploy_dir_image
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500863 logger.debug('Running %s...' % cmd)
864 try:
865 qbs = subprocess.check_output(cmd, shell=True).decode('utf-8')
866 except subprocess.CalledProcessError as err:
867 raise RunQemuError(err)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600868 if qbs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500869 for qb in qbs.split():
870 # Don't use initramfs when other choices unless fstype is ramfs
871 if '-initramfs-' in os.path.basename(qb) and self.fstype != 'cpio.gz':
872 continue
873 self.qemuboot = qb
874 break
875 if not self.qemuboot:
876 # Use the first one when no choice
877 self.qemuboot = qbs.split()[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600878 self.qbconfload = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500879
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600880 if not self.qemuboot:
881 # If we haven't found a .qemuboot.conf at this point it probably
882 # doesn't exist, continue without
883 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500884
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600885 if not os.path.exists(self.qemuboot):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500886 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 -0500887
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500888 logger.debug('CONFFILE: %s' % self.qemuboot)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500889
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600890 cf = configparser.ConfigParser()
891 cf.read(self.qemuboot)
892 for k, v in cf.items('config_bsp'):
893 k_upper = k.upper()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500894 if v.startswith("../"):
895 v = os.path.abspath(os.path.dirname(self.qemuboot) + "/" + v)
896 elif v == ".":
897 v = os.path.dirname(self.qemuboot)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600898 self.set(k_upper, v)
899
900 def validate_paths(self):
901 """Ensure all relevant path variables are set"""
902 # When we're started with a *.qemuboot.conf arg assume that image
903 # artefacts are relative to that file, rather than in whatever
904 # directory DEPLOY_DIR_IMAGE in the conf file points to.
905 if self.qbconfload:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500906 imgdir = os.path.realpath(os.path.dirname(self.qemuboot))
907 if imgdir != os.path.realpath(self.get('DEPLOY_DIR_IMAGE')):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600908 logger.info('Setting DEPLOY_DIR_IMAGE to folder containing %s (%s)' % (self.qemuboot, imgdir))
909 self.set('DEPLOY_DIR_IMAGE', imgdir)
910
911 # If the STAGING_*_NATIVE directories from the config file don't exist
912 # and we're in a sourced OE build directory try to extract the paths
913 # from `bitbake -e`
914 havenative = os.path.exists(self.get('STAGING_DIR_NATIVE')) and \
915 os.path.exists(self.get('STAGING_BINDIR_NATIVE'))
916
917 if not havenative:
918 if not self.bitbake_e:
919 self.load_bitbake_env()
920
921 if self.bitbake_e:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500922 native_vars = ['STAGING_DIR_NATIVE']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600923 for nv in native_vars:
924 s = re.search('^%s="(.*)"' % nv, self.bitbake_e, re.M)
925 if s and s.group(1) != self.get(nv):
926 logger.info('Overriding conf file setting of %s to %s from Bitbake environment' % (nv, s.group(1)))
927 self.set(nv, s.group(1))
928 else:
929 # when we're invoked from a running bitbake instance we won't
930 # be able to call `bitbake -e`, then try:
931 # - get OE_TMPDIR from environment and guess paths based on it
932 # - get OECORE_NATIVE_SYSROOT from environment (for sdk)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500933 tmpdir = self.get('OE_TMPDIR')
934 oecore_native_sysroot = self.get('OECORE_NATIVE_SYSROOT')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600935 if tmpdir:
936 logger.info('Setting STAGING_DIR_NATIVE and STAGING_BINDIR_NATIVE relative to OE_TMPDIR (%s)' % tmpdir)
937 hostos, _, _, _, machine = os.uname()
938 buildsys = '%s-%s' % (machine, hostos.lower())
939 staging_dir_native = '%s/sysroots/%s' % (tmpdir, buildsys)
940 self.set('STAGING_DIR_NATIVE', staging_dir_native)
941 elif oecore_native_sysroot:
942 logger.info('Setting STAGING_DIR_NATIVE to OECORE_NATIVE_SYSROOT (%s)' % oecore_native_sysroot)
943 self.set('STAGING_DIR_NATIVE', oecore_native_sysroot)
944 if self.get('STAGING_DIR_NATIVE'):
945 # we have to assume that STAGING_BINDIR_NATIVE is at usr/bin
946 staging_bindir_native = '%s/usr/bin' % self.get('STAGING_DIR_NATIVE')
947 logger.info('Setting STAGING_BINDIR_NATIVE to %s' % staging_bindir_native)
948 self.set('STAGING_BINDIR_NATIVE', '%s/usr/bin' % self.get('STAGING_DIR_NATIVE'))
949
950 def print_config(self):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500951 logoutput = ['Continuing with the following parameters:']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600952 if not self.fstype in self.vmtypes:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500953 logoutput.append('KERNEL: [%s]' % self.kernel)
Brad Bishopc68388fc2019-08-26 01:33:31 -0400954 if self.bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500955 logoutput.append('BIOS: [%s]' % self.bios)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600956 if self.dtb:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500957 logoutput.append('DTB: [%s]' % self.dtb)
958 logoutput.append('MACHINE: [%s]' % self.get('MACHINE'))
Brad Bishop15ae2502019-06-18 21:44:24 -0400959 try:
960 fstype_flags = ' (' + ', '.join(self.fsinfo[self.fstype]) + ')'
961 except KeyError:
962 fstype_flags = ''
Andrew Geissler82c905d2020-04-13 13:39:40 -0500963 logoutput.append('FSTYPE: [%s%s]' % (self.fstype, fstype_flags))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600964 if self.fstype == 'nfs':
Andrew Geissler82c905d2020-04-13 13:39:40 -0500965 logoutput.append('NFS_DIR: [%s]' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600966 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500967 logoutput.append('ROOTFS: [%s]' % self.rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500968 if self.ovmf_bios:
Andrew Geissler82c905d2020-04-13 13:39:40 -0500969 logoutput.append('OVMF: %s' % self.ovmf_bios)
Brad Bishop08902b02019-08-20 09:16:51 -0400970 if (self.ovmf_secboot_pkkek1):
Andrew Geissler82c905d2020-04-13 13:39:40 -0500971 logoutput.append('SECBOOT PKKEK1: [%s...]' % self.ovmf_secboot_pkkek1[0:100])
972 logoutput.append('CONFFILE: [%s]' % self.qemuboot)
973 logoutput.append('')
974 logger.info('\n'.join(logoutput))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600975
976 def setup_nfs(self):
977 if not self.nfs_server:
978 if self.slirp_enabled:
979 self.nfs_server = '10.0.2.2'
980 else:
981 self.nfs_server = '192.168.7.1'
982
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500983 # Figure out a new nfs_instance to allow multiple qemus running.
Brad Bishop977dc1a2019-02-06 16:01:43 -0500984 ps = subprocess.check_output(("ps", "auxww")).decode('utf-8')
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500985 pattern = '/bin/unfsd .* -i .*\.pid -e .*/exports([0-9]+) '
986 all_instances = re.findall(pattern, ps, re.M)
987 if all_instances:
988 all_instances.sort(key=int)
989 self.nfs_instance = int(all_instances.pop()) + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600990
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500991 nfsd_port = 3049 + 2 * self.nfs_instance
992 mountd_port = 3048 + 2 * self.nfs_instance
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600993
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500994 # Export vars for runqemu-export-rootfs
995 export_dict = {
996 'NFS_INSTANCE': self.nfs_instance,
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500997 'NFSD_PORT': nfsd_port,
998 'MOUNTD_PORT': mountd_port,
999 }
1000 for k, v in export_dict.items():
1001 # Use '%s' since they are integers
1002 os.putenv(k, '%s' % v)
1003
Andrew Geissler82c905d2020-04-13 13:39:40 -05001004 self.unfs_opts="nfsvers=3,port=%s,tcp,mountport=%s" % (nfsd_port, mountd_port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001005
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001006 # Extract .tar.bz2 or .tar.bz if no nfs dir
1007 if not (self.rootfs and os.path.isdir(self.rootfs)):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001008 src_prefix = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'))
1009 dest = "%s-nfsroot" % src_prefix
1010 if os.path.exists('%s.pseudo_state' % dest):
1011 logger.info('Use %s as NFS_DIR' % dest)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001012 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001013 else:
1014 src = ""
1015 src1 = '%s.tar.bz2' % src_prefix
1016 src2 = '%s.tar.gz' % src_prefix
1017 if os.path.exists(src1):
1018 src = src1
1019 elif os.path.exists(src2):
1020 src = src2
1021 if not src:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001022 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 -06001023 logger.info('NFS_DIR not found, extracting %s to %s' % (src, dest))
Brad Bishop977dc1a2019-02-06 16:01:43 -05001024 cmd = ('runqemu-extract-sdk', src, dest)
1025 logger.info('Running %s...' % str(cmd))
1026 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001027 raise RunQemuError('Failed to run %s' % cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001028 self.clean_nfs_dir = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001029 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001030
1031 # Start the userspace NFS server
Brad Bishop977dc1a2019-02-06 16:01:43 -05001032 cmd = ('runqemu-export-rootfs', 'start', self.rootfs)
1033 logger.info('Running %s...' % str(cmd))
1034 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001035 raise RunQemuError('Failed to run %s' % cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001036
1037 self.nfs_running = True
1038
Andrew Geissler82c905d2020-04-13 13:39:40 -05001039 def setup_net_bridge(self):
1040 self.set('NETWORK_CMD', '-netdev bridge,br=%s,id=net0,helper=%s -device virtio-net-pci,netdev=net0 ' % (
1041 self.net_bridge, os.path.join(self.bindir_native, 'qemu-oe-bridge-helper')))
1042
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001043 def setup_slirp(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001044 """Setup user networking"""
1045
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001046 if self.fstype == 'nfs':
1047 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001048 netconf = " " + self.cmdline_ip_slirp
1049 logger.info("Network configuration:%s", netconf)
1050 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001051 # Port mapping
1052 hostfwd = ",hostfwd=tcp::2222-:22,hostfwd=tcp::2323-:23"
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001053 qb_slirp_opt_default = "-netdev user,id=net0%s,tftp=%s" % (hostfwd, self.get('DEPLOY_DIR_IMAGE'))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001054 qb_slirp_opt = self.get('QB_SLIRP_OPT') or qb_slirp_opt_default
1055 # Figure out the port
1056 ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt)
1057 ports = [int(i) for i in ports]
1058 mac = 2
Brad Bishop08902b02019-08-20 09:16:51 -04001059
1060 lockdir = "/tmp/qemu-port-locks"
1061 if not os.path.exists(lockdir):
1062 # There might be a race issue when multi runqemu processess are
1063 # running at the same time.
1064 try:
1065 os.mkdir(lockdir)
1066 os.chmod(lockdir, 0o777)
1067 except FileExistsError:
1068 pass
1069
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001070 # Find a free port to avoid conflicts
1071 for p in ports[:]:
1072 p_new = p
Brad Bishop08902b02019-08-20 09:16:51 -04001073 while not self.check_free_port('localhost', p_new, lockdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001074 p_new += 1
1075 mac += 1
1076 while p_new in ports:
1077 p_new += 1
1078 mac += 1
1079 if p != p_new:
1080 ports.append(p_new)
1081 qb_slirp_opt = re.sub(':%s-' % p, ':%s-' % p_new, qb_slirp_opt)
1082 logger.info("Port forward changed: %s -> %s" % (p, p_new))
1083 mac = "%s%02x" % (self.mac_slirp, mac)
1084 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qb_slirp_opt))
1085 # Print out port foward
1086 hostfwd = re.findall('(hostfwd=[^,]*)', qb_slirp_opt)
1087 if hostfwd:
1088 logger.info('Port forward: %s' % ' '.join(hostfwd))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001089
1090 def setup_tap(self):
1091 """Setup tap"""
1092
1093 # This file is created when runqemu-gen-tapdevs creates a bank of tap
1094 # devices, indicating that the user should not bring up new ones using
1095 # sudo.
1096 nosudo_flag = '/etc/runqemu-nosudo'
1097 self.qemuifup = shutil.which('runqemu-ifup')
1098 self.qemuifdown = shutil.which('runqemu-ifdown')
1099 ip = shutil.which('ip')
1100 lockdir = "/tmp/qemu-tap-locks"
1101
1102 if not (self.qemuifup and self.qemuifdown and ip):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001103 logger.error("runqemu-ifup: %s" % self.qemuifup)
1104 logger.error("runqemu-ifdown: %s" % self.qemuifdown)
1105 logger.error("ip: %s" % ip)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001106 raise OEPathError("runqemu-ifup, runqemu-ifdown or ip not found")
1107
1108 if not os.path.exists(lockdir):
1109 # There might be a race issue when multi runqemu processess are
1110 # running at the same time.
1111 try:
1112 os.mkdir(lockdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001113 os.chmod(lockdir, 0o777)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001114 except FileExistsError:
1115 pass
1116
Brad Bishop977dc1a2019-02-06 16:01:43 -05001117 cmd = (ip, 'link')
1118 logger.debug('Running %s...' % str(cmd))
1119 ip_link = subprocess.check_output(cmd).decode('utf-8')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001120 # Matches line like: 6: tap0: <foo>
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001121 possibles = re.findall('^[0-9]+: +(tap[0-9]+): <.*', ip_link, re.M)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001122 tap = ""
1123 for p in possibles:
1124 lockfile = os.path.join(lockdir, p)
1125 if os.path.exists('%s.skip' % lockfile):
1126 logger.info('Found %s.skip, skipping %s' % (lockfile, p))
1127 continue
Brad Bishop08902b02019-08-20 09:16:51 -04001128 self.taplock = lockfile + '.lock'
1129 if self.acquire_taplock(error=False):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001130 tap = p
1131 logger.info("Using preconfigured tap device %s" % tap)
1132 logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap))
1133 break
1134
1135 if not tap:
1136 if os.path.exists(nosudo_flag):
1137 logger.error("Error: There are no available tap devices to use for networking,")
1138 logger.error("and I see %s exists, so I am not going to try creating" % nosudo_flag)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001139 raise RunQemuError("a new one with sudo.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001140
1141 gid = os.getgid()
1142 uid = os.getuid()
1143 logger.info("Setting up tap interface under sudo")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001144 cmd = ('sudo', self.qemuifup, str(uid), str(gid), self.bindir_native)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001145 try:
1146 tap = subprocess.check_output(cmd).decode('utf-8').strip()
1147 except subprocess.CalledProcessError as e:
1148 logger.error('Setting up tap device failed:\n%s\nRun runqemu-gen-tapdevs to manually create one.' % str(e))
1149 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001150 lockfile = os.path.join(lockdir, tap)
Brad Bishop08902b02019-08-20 09:16:51 -04001151 self.taplock = lockfile + '.lock'
1152 self.acquire_taplock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001153 self.cleantap = True
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001154 logger.debug('Created tap: %s' % tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001155
1156 if not tap:
1157 logger.error("Failed to setup tap device. Run runqemu-gen-tapdevs to manually create.")
Andrew Geissler82c905d2020-04-13 13:39:40 -05001158 sys.exit(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001159 self.tap = tap
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001160 tapnum = int(tap[3:])
1161 gateway = tapnum * 2 + 1
1162 client = gateway + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001163 if self.fstype == 'nfs':
1164 self.setup_nfs()
Andrew Geissler82c905d2020-04-13 13:39:40 -05001165 netconf = " " + self.cmdline_ip_tap
1166 netconf = netconf.replace('@CLIENT@', str(client))
1167 netconf = netconf.replace('@GATEWAY@', str(gateway))
1168 logger.info("Network configuration:%s", netconf)
1169 self.kernel_cmdline_script += netconf
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001170 mac = "%s%02x" % (self.mac_tap, client)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001171 qb_tap_opt = self.get('QB_TAP_OPT')
1172 if qb_tap_opt:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001173 qemu_tap_opt = qb_tap_opt.replace('@TAP@', tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001174 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001175 qemu_tap_opt = "-netdev tap,id=net0,ifname=%s,script=no,downscript=no" % (self.tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001176
1177 if self.vhost_enabled:
1178 qemu_tap_opt += ',vhost=on'
1179
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001180 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qemu_tap_opt))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001181
1182 def setup_network(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001183 if self.get('QB_NET') == 'none':
1184 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001185 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001186 self.saved_stty = subprocess.check_output(("stty", "-g")).decode('utf-8').strip()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001187 self.network_device = self.get('QB_NETWORK_DEVICE') or self.network_device
Andrew Geissler82c905d2020-04-13 13:39:40 -05001188 if self.net_bridge:
1189 self.setup_net_bridge()
1190 elif self.slirp_enabled:
1191 self.cmdline_ip_slirp = self.get('QB_CMDLINE_IP_SLIRP') or self.cmdline_ip_slirp
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001192 self.setup_slirp()
1193 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001194 self.cmdline_ip_tap = self.get('QB_CMDLINE_IP_TAP') or self.cmdline_ip_tap
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001195 self.setup_tap()
1196
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001197 def setup_rootfs(self):
1198 if self.get('QB_ROOTFS') == 'none':
1199 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001200 if 'wic.' in self.fstype:
1201 self.fstype = self.fstype[4:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001202 rootfs_format = self.fstype if self.fstype in ('vmdk', 'qcow2', 'vdi') else 'raw'
1203
1204 qb_rootfs_opt = self.get('QB_ROOTFS_OPT')
1205 if qb_rootfs_opt:
1206 self.rootfs_options = qb_rootfs_opt.replace('@ROOTFS@', self.rootfs)
1207 else:
1208 self.rootfs_options = '-drive file=%s,if=virtio,format=%s' % (self.rootfs, rootfs_format)
1209
Andrew Geissler82c905d2020-04-13 13:39:40 -05001210 qb_rootfs_extra_opt = self.get("QB_ROOTFS_EXTRA_OPT")
1211 if qb_rootfs_extra_opt and not qb_rootfs_extra_opt.startswith(","):
1212 qb_rootfs_extra_opt = "," + qb_rootfs_extra_opt
1213
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001214 if self.fstype in ('cpio.gz', 'cpio'):
1215 self.kernel_cmdline = 'root=/dev/ram0 rw debugshell'
1216 self.rootfs_options = '-initrd %s' % self.rootfs
1217 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001218 vm_drive = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001219 if self.fstype in self.vmtypes:
1220 if self.fstype == 'iso':
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001221 vm_drive = '-drive file=%s,if=virtio,media=cdrom' % self.rootfs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001222 elif self.get('QB_DRIVE_TYPE'):
1223 drive_type = self.get('QB_DRIVE_TYPE')
1224 if drive_type.startswith("/dev/sd"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001225 logger.info('Using scsi drive')
Andrew Geissler82c905d2020-04-13 13:39:40 -05001226 vm_drive = '-drive if=none,id=hd,file=%s,format=%s -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd%s' \
1227 % (self.rootfs, rootfs_format, qb_rootfs_extra_opt)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001228 elif drive_type.startswith("/dev/hd"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001229 logger.info('Using ide drive')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001230 vm_drive = "-drive file=%s,format=%s" % (self.rootfs, rootfs_format)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001231 elif drive_type.startswith("/dev/vdb"):
1232 logger.info('Using block virtio drive');
1233 vm_drive = '-drive id=disk0,file=%s,if=none,format=%s -device virtio-blk-device,drive=disk0%s' \
1234 % (self.rootfs, rootfs_format,qb_rootfs_extra_opt)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001235 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001236 # virtio might have been selected explicitly (just use it), or
1237 # is used as fallback (then warn about that).
1238 if not drive_type.startswith("/dev/vd"):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001239 logger.warning("Unknown QB_DRIVE_TYPE: %s" % drive_type)
1240 logger.warning("Failed to figure out drive type, consider define or fix QB_DRIVE_TYPE")
1241 logger.warning('Trying to use virtio block drive')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001242 vm_drive = '-drive if=virtio,file=%s,format=%s' % (self.rootfs, rootfs_format)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001243
1244 # All branches above set vm_drive.
Andrew Geissler475cb722020-07-10 16:00:51 -05001245 self.rootfs_options = vm_drive
1246 if not self.fstype in self.vmtypes:
1247 self.rootfs_options += ' -no-reboot'
Brad Bishopf3f93bb2019-10-16 14:33:32 -04001248 self.kernel_cmdline = 'root=%s rw' % (self.get('QB_KERNEL_ROOT'))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001249
1250 if self.fstype == 'nfs':
1251 self.rootfs_options = ''
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001252 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 -04001253 self.kernel_cmdline = 'root=%s rw' % k_root
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001254
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001255 if self.fstype == 'none':
1256 self.rootfs_options = ''
1257
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001258 self.set('ROOTFS_OPTIONS', self.rootfs_options)
1259
1260 def guess_qb_system(self):
1261 """attempt to determine the appropriate qemu-system binary"""
1262 mach = self.get('MACHINE')
1263 if not mach:
1264 search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*'
1265 if self.rootfs:
1266 match = re.match(search, self.rootfs)
1267 if match:
1268 mach = match.group(1)
1269 elif self.kernel:
1270 match = re.match(search, self.kernel)
1271 if match:
1272 mach = match.group(1)
1273
1274 if not mach:
1275 return None
1276
1277 if mach == 'qemuarm':
1278 qbsys = 'arm'
1279 elif mach == 'qemuarm64':
1280 qbsys = 'aarch64'
1281 elif mach == 'qemux86':
1282 qbsys = 'i386'
1283 elif mach == 'qemux86-64':
1284 qbsys = 'x86_64'
1285 elif mach == 'qemuppc':
1286 qbsys = 'ppc'
1287 elif mach == 'qemumips':
1288 qbsys = 'mips'
1289 elif mach == 'qemumips64':
1290 qbsys = 'mips64'
1291 elif mach == 'qemumipsel':
1292 qbsys = 'mipsel'
1293 elif mach == 'qemumips64el':
1294 qbsys = 'mips64el'
Brad Bishop316dfdd2018-06-25 12:45:53 -04001295 elif mach == 'qemuriscv64':
1296 qbsys = 'riscv64'
1297 elif mach == 'qemuriscv32':
1298 qbsys = 'riscv32'
Brad Bishop004d4992018-10-02 23:54:45 +02001299 else:
1300 logger.error("Unable to determine QEMU PC System emulator for %s machine." % mach)
1301 logger.error("As %s is not among valid QEMU machines such as," % mach)
1302 logger.error("qemux86-64, qemux86, qemuarm64, qemuarm, qemumips64, qemumips64el, qemumipsel, qemumips, qemuppc")
1303 raise RunQemuError("Set qb_system_name with suitable QEMU PC System emulator in .*qemuboot.conf.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001304
1305 return 'qemu-system-%s' % qbsys
1306
Brad Bishop15ae2502019-06-18 21:44:24 -04001307 def check_qemu_system(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001308 qemu_system = self.get('QB_SYSTEM_NAME')
1309 if not qemu_system:
1310 qemu_system = self.guess_qb_system()
1311 if not qemu_system:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001312 raise RunQemuError("Failed to boot, QB_SYSTEM_NAME is NULL!")
Brad Bishop15ae2502019-06-18 21:44:24 -04001313 self.qemu_system = qemu_system
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001314
Brad Bishop15ae2502019-06-18 21:44:24 -04001315 def setup_final(self):
1316 qemu_bin = os.path.join(self.bindir_native, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001317
1318 # It is possible to have qemu-native in ASSUME_PROVIDED, and it won't
1319 # find QEMU in sysroot, it needs to use host's qemu.
1320 if not os.path.exists(qemu_bin):
1321 logger.info("QEMU binary not found in %s, trying host's QEMU" % qemu_bin)
1322 for path in (os.environ['PATH'] or '').split(':'):
Brad Bishop15ae2502019-06-18 21:44:24 -04001323 qemu_bin_tmp = os.path.join(path, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001324 logger.info("Trying: %s" % qemu_bin_tmp)
1325 if os.path.exists(qemu_bin_tmp):
1326 qemu_bin = qemu_bin_tmp
1327 if not os.path.isabs(qemu_bin):
1328 qemu_bin = os.path.abspath(qemu_bin)
1329 logger.info("Using host's QEMU: %s" % qemu_bin)
1330 break
1331
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001332 if not os.access(qemu_bin, os.X_OK):
1333 raise OEPathError("No QEMU binary '%s' could be found" % qemu_bin)
1334
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001335 self.qemu_opt = "%s %s %s %s" % (qemu_bin, self.get('NETWORK_CMD'), self.get('ROOTFS_OPTIONS'), self.get('QB_OPT_APPEND'))
1336
1337 for ovmf in self.ovmf_bios:
1338 format = ovmf.rsplit('.', 1)[-1]
1339 self.qemu_opt += ' -drive if=pflash,format=%s,file=%s' % (format, ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001340
1341 self.qemu_opt += ' ' + self.qemu_opt_script
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001342
Brad Bishop08902b02019-08-20 09:16:51 -04001343 if self.ovmf_secboot_pkkek1:
1344 # Provide the Platform Key and first Key Exchange Key certificate as an
1345 # OEM string in the SMBIOS Type 11 table. Prepend the certificate string
1346 # with "application prefix" of the EnrollDefaultKeys.efi application
1347 self.qemu_opt += ' -smbios type=11,value=4e32566d-8e9e-4f52-81d3-5bb9715f9727:' \
1348 + self.ovmf_secboot_pkkek1
1349
Andrew Geissler99467da2019-02-25 18:54:23 -06001350 # Append qemuparams to override previous settings
1351 if self.qemuparams:
1352 self.qemu_opt += ' ' + self.qemuparams
1353
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001354 if self.snapshot:
1355 self.qemu_opt += " -snapshot"
1356
Brad Bishop19323692019-04-05 15:28:33 -04001357 if self.serialconsole:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001358 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001359 subprocess.check_call(("stty", "intr", "^]"))
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001360 logger.info("Interrupt character is '^]'")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001361
1362 first_serial = ""
1363 if not re.search("-nographic", self.qemu_opt):
1364 first_serial = "-serial mon:vc"
1365 # We always want a ttyS1. Since qemu by default adds a serial
1366 # port when nodefaults is not specified, it seems that all that
1367 # would be needed is to make sure a "-serial" is there. However,
1368 # it appears that when "-serial" is specified, it ignores the
1369 # default serial port that is normally added. So here we make
1370 # sure to add two -serial if there are none. And only one if
1371 # there is one -serial already.
1372 serial_num = len(re.findall("-serial", self.qemu_opt))
1373 if serial_num == 0:
1374 self.qemu_opt += " %s %s" % (first_serial, self.get("QB_SERIAL_OPT"))
1375 elif serial_num == 1:
1376 self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT")
1377
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001378 # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES),
1379 # if not serial or serialtcp options was specified only ttyS0 is created
1380 # and sysvinit shows an error trying to enable ttyS1:
1381 # INIT: Id "S1" respawning too fast: disabled for 5 minutes
1382 serial_num = len(re.findall("-serial", self.qemu_opt))
1383 if serial_num == 0:
Brad Bishop19323692019-04-05 15:28:33 -04001384 if re.search("-nographic", self.qemu_opt) or self.serialstdio:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001385 self.qemu_opt += " -serial mon:stdio -serial null"
1386 else:
1387 self.qemu_opt += " -serial mon:vc -serial null"
1388
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001389 def start_qemu(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001390 import shlex
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001391 if self.kernel:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001392 kernel_opts = "-kernel %s -append '%s %s %s %s'" % (self.kernel, self.kernel_cmdline,
1393 self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'),
1394 self.bootparams)
Brad Bishopc68388fc2019-08-26 01:33:31 -04001395 if self.bios:
1396 kernel_opts += " -bios %s" % self.bios
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001397 if self.dtb:
1398 kernel_opts += " -dtb %s" % self.dtb
1399 else:
1400 kernel_opts = ""
1401 cmd = "%s %s" % (self.qemu_opt, kernel_opts)
Brad Bishop004d4992018-10-02 23:54:45 +02001402 cmds = shlex.split(cmd)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001403 logger.info('Running %s\n' % cmd)
Brad Bishopf86d0552018-12-04 14:18:15 -08001404 pass_fds = []
Brad Bishop08902b02019-08-20 09:16:51 -04001405 if self.taplock_descriptor:
1406 pass_fds = [self.taplock_descriptor.fileno()]
1407 if len(self.portlocks):
1408 for descriptor in self.portlocks.values():
1409 pass_fds.append(descriptor.fileno())
Brad Bishopf86d0552018-12-04 14:18:15 -08001410 process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds)
Brad Bishop004d4992018-10-02 23:54:45 +02001411 self.qemupid = process.pid
1412 retcode = process.wait()
1413 if retcode:
1414 if retcode == -signal.SIGTERM:
1415 logger.info("Qemu terminated by SIGTERM")
1416 else:
1417 logger.error("Failed to run qemu: %s", process.stderr.read().decode())
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001418
1419 def cleanup(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001420 if self.cleaned:
1421 return
1422
1423 # avoid dealing with SIGTERM when cleanup function is running
1424 signal.signal(signal.SIGTERM, signal.SIG_IGN)
1425
1426 logger.info("Cleaning up")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001427 if self.cleantap:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001428 cmd = ('sudo', self.qemuifdown, self.tap, self.bindir_native)
1429 logger.debug('Running %s' % str(cmd))
1430 subprocess.check_call(cmd)
Brad Bishop08902b02019-08-20 09:16:51 -04001431 self.release_taplock()
1432 self.release_portlock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001433
1434 if self.nfs_running:
1435 logger.info("Shutting down the userspace NFS server...")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001436 cmd = ("runqemu-export-rootfs", "stop", self.rootfs)
1437 logger.debug('Running %s' % str(cmd))
1438 subprocess.check_call(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001439
1440 if self.saved_stty:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001441 subprocess.check_call(("stty", self.saved_stty))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001442
1443 if self.clean_nfs_dir:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001444 logger.info('Removing %s' % self.rootfs)
1445 shutil.rmtree(self.rootfs)
1446 shutil.rmtree('%s.pseudo_state' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001447
Brad Bishop004d4992018-10-02 23:54:45 +02001448 self.cleaned = True
1449
Andrew Geissler82c905d2020-04-13 13:39:40 -05001450 def run_bitbake_env(self, mach=None):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001451 bitbake = shutil.which('bitbake')
1452 if not bitbake:
1453 return
1454
1455 if not mach:
1456 mach = self.get('MACHINE')
1457
Andrew Geissler82c905d2020-04-13 13:39:40 -05001458 multiconfig = self.get('MULTICONFIG')
1459 if multiconfig:
1460 multiconfig = "mc:%s" % multiconfig
1461
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001462 if mach:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001463 cmd = 'MACHINE=%s bitbake -e %s' % (mach, multiconfig)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001464 else:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001465 cmd = 'bitbake -e %s' % multiconfig
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001466
1467 logger.info('Running %s...' % cmd)
Andrew Geissler82c905d2020-04-13 13:39:40 -05001468 return subprocess.check_output(cmd, shell=True).decode('utf-8')
1469
1470 def load_bitbake_env(self, mach=None):
1471 if self.bitbake_e:
1472 return
1473
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001474 try:
Andrew Geissler82c905d2020-04-13 13:39:40 -05001475 self.bitbake_e = self.run_bitbake_env(mach=mach)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001476 except subprocess.CalledProcessError as err:
1477 self.bitbake_e = ''
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001478 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 -06001479
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001480 def validate_combos(self):
1481 if (self.fstype in self.vmtypes) and self.kernel:
1482 raise RunQemuError("%s doesn't need kernel %s!" % (self.fstype, self.kernel))
1483
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001484 @property
1485 def bindir_native(self):
1486 result = self.get('STAGING_BINDIR_NATIVE')
1487 if result and os.path.exists(result):
1488 return result
1489
Andrew Geissler82c905d2020-04-13 13:39:40 -05001490 cmd = ['bitbake', '-e']
1491 multiconfig = self.get('MULTICONFIG')
1492 if multiconfig:
1493 cmd.append('mc:%s:qemu-helper-native' % multiconfig)
1494 else:
1495 cmd.append('qemu-helper-native')
1496
Brad Bishop977dc1a2019-02-06 16:01:43 -05001497 logger.info('Running %s...' % str(cmd))
1498 out = subprocess.check_output(cmd).decode('utf-8')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001499
1500 match = re.search('^STAGING_BINDIR_NATIVE="(.*)"', out, re.M)
1501 if match:
1502 result = match.group(1)
1503 if os.path.exists(result):
1504 self.set('STAGING_BINDIR_NATIVE', result)
1505 return result
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001506 raise RunQemuError("Native sysroot directory %s doesn't exist" % result)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001507 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001508 raise RunQemuError("Can't find STAGING_BINDIR_NATIVE in '%s' output" % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001509
1510
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001511def main():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001512 if "help" in sys.argv or '-h' in sys.argv or '--help' in sys.argv:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001513 print_usage()
1514 return 0
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001515 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001516 config = BaseConfig()
Brad Bishop004d4992018-10-02 23:54:45 +02001517
1518 def sigterm_handler(signum, frame):
1519 logger.info("SIGTERM received")
1520 os.kill(config.qemupid, signal.SIGTERM)
1521 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001522 # Deliberately ignore the return code of 'tput smam'.
1523 subprocess.call(["tput", "smam"])
Brad Bishop004d4992018-10-02 23:54:45 +02001524 signal.signal(signal.SIGTERM, sigterm_handler)
1525
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001526 config.check_args()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001527 config.read_qemuboot()
1528 config.check_and_set()
1529 # Check whether the combos is valid or not
1530 config.validate_combos()
1531 config.print_config()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001532 config.setup_network()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001533 config.setup_rootfs()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001534 config.setup_final()
1535 config.start_qemu()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001536 except RunQemuError as err:
1537 logger.error(err)
1538 return 1
1539 except Exception as err:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001540 import traceback
1541 traceback.print_exc()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001542 return 1
1543 finally:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001544 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001545 # Deliberately ignore the return code of 'tput smam'.
1546 subprocess.call(["tput", "smam"])
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001547
1548if __name__ == "__main__":
1549 sys.exit(main())