blob: 5c56c3fe6c1dd13bcb88d2a7752d4b93d4f92255 [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',
138 )
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500139
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600140 self.qemu_opt = ''
141 self.qemu_opt_script = ''
Andrew Geissler99467da2019-02-25 18:54:23 -0600142 self.qemuparams = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600143 self.clean_nfs_dir = False
144 self.nfs_server = ''
145 self.rootfs = ''
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500146 # File name(s) of a OVMF firmware file or variable store,
147 # to be added with -drive if=pflash.
148 # Found in the same places as the rootfs, with or without one of
149 # these suffices: qcow2, bin.
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500150 self.ovmf_bios = []
Brad Bishop08902b02019-08-20 09:16:51 -0400151 # When enrolling default Secure Boot keys, the hypervisor
152 # must provide the Platform Key and the first Key Exchange Key
153 # certificate in the Type 11 SMBIOS table.
154 self.ovmf_secboot_pkkek1 = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600155 self.qemuboot = ''
156 self.qbconfload = False
157 self.kernel = ''
Brad Bishopc68388fc2019-08-26 01:33:31 -0400158 self.bios = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600159 self.kernel_cmdline = ''
160 self.kernel_cmdline_script = ''
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500161 self.bootparams = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600162 self.dtb = ''
163 self.fstype = ''
164 self.kvm_enabled = False
165 self.vhost_enabled = False
166 self.slirp_enabled = False
167 self.nfs_instance = 0
168 self.nfs_running = False
Brad Bishop19323692019-04-05 15:28:33 -0400169 self.serialconsole = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600170 self.serialstdio = False
171 self.cleantap = False
172 self.saved_stty = ''
173 self.audio_enabled = False
174 self.tcpserial_portnum = ''
Brad Bishop08902b02019-08-20 09:16:51 -0400175 self.taplock = ''
176 self.taplock_descriptor = None
177 self.portlocks = {}
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600178 self.bitbake_e = ''
179 self.snapshot = False
Brad Bishop15ae2502019-06-18 21:44:24 -0400180 self.wictypes = ('wic', 'wic.vmdk', 'wic.qcow2', 'wic.vdi')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500181 self.fstypes = ('ext2', 'ext3', 'ext4', 'jffs2', 'nfs', 'btrfs',
182 'cpio.gz', 'cpio', 'ramfs', 'tar.bz2', 'tar.gz')
Brad Bishop08902b02019-08-20 09:16:51 -0400183 self.vmtypes = ('hddimg', 'iso')
Brad Bishop15ae2502019-06-18 21:44:24 -0400184 self.fsinfo = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500185 self.network_device = "-device e1000,netdev=net0,mac=@MAC@"
186 # Use different mac section for tap and slirp to avoid
187 # conflicts, e.g., when one is running with tap, the other is
188 # running with slirp.
189 # The last section is dynamic, which is for avoiding conflicts,
190 # when multiple qemus are running, e.g., when multiple tap or
191 # slirp qemus are running.
192 self.mac_tap = "52:54:00:12:34:"
193 self.mac_slirp = "52:54:00:12:35:"
Brad Bishop004d4992018-10-02 23:54:45 +0200194 # pid of the actual qemu process
195 self.qemupid = None
196 # avoid cleanup twice
197 self.cleaned = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500198
Brad Bishop08902b02019-08-20 09:16:51 -0400199 def acquire_taplock(self, error=True):
200 logger.debug("Acquiring lockfile %s..." % self.taplock)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600201 try:
Brad Bishop08902b02019-08-20 09:16:51 -0400202 self.taplock_descriptor = open(self.taplock, 'w')
203 fcntl.flock(self.taplock_descriptor, fcntl.LOCK_EX|fcntl.LOCK_NB)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600204 except Exception as e:
Brad Bishop08902b02019-08-20 09:16:51 -0400205 msg = "Acquiring lockfile %s failed: %s" % (self.taplock, e)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500206 if error:
207 logger.error(msg)
208 else:
209 logger.info(msg)
Brad Bishop08902b02019-08-20 09:16:51 -0400210 if self.taplock_descriptor:
211 self.taplock_descriptor.close()
212 self.taplock_descriptor = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600213 return False
214 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500215
Brad Bishop08902b02019-08-20 09:16:51 -0400216 def release_taplock(self):
217 if self.taplock_descriptor:
Brad Bishopf86d0552018-12-04 14:18:15 -0800218 logger.debug("Releasing lockfile for tap device '%s'" % self.tap)
Brad Bishop08902b02019-08-20 09:16:51 -0400219 fcntl.flock(self.taplock_descriptor, fcntl.LOCK_UN)
220 self.taplock_descriptor.close()
221 os.remove(self.taplock)
222 self.taplock_descriptor = None
223
224 def check_free_port(self, host, port, lockdir):
225 """ Check whether the port is free or not """
226 import socket
227 from contextlib import closing
228
229 lockfile = os.path.join(lockdir, str(port) + '.lock')
230 if self.acquire_portlock(lockfile):
231 with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
232 if sock.connect_ex((host, port)) == 0:
233 # Port is open, so not free
234 self.release_portlock(lockfile)
235 return False
236 else:
237 # Port is not open, so free
238 return True
239 else:
240 return False
241
242 def acquire_portlock(self, lockfile):
243 logger.debug("Acquiring lockfile %s..." % lockfile)
244 try:
245 portlock_descriptor = open(lockfile, 'w')
246 self.portlocks.update({lockfile: portlock_descriptor})
247 fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_EX|fcntl.LOCK_NB)
248 except Exception as e:
249 msg = "Acquiring lockfile %s failed: %s" % (lockfile, e)
250 logger.info(msg)
251 if lockfile in self.portlocks.keys() and self.portlocks[lockfile]:
252 self.portlocks[lockfile].close()
253 del self.portlocks[lockfile]
254 return False
255 return True
256
257 def release_portlock(self, lockfile=None):
258 if lockfile != None:
259 logger.debug("Releasing lockfile '%s'" % lockfile)
260 fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_UN)
261 self.portlocks[lockfile].close()
262 os.remove(lockfile)
263 del self.portlocks[lockfile]
264 elif len(self.portlocks):
265 for lockfile, descriptor in self.portlocks.items():
266 logger.debug("Releasing lockfile '%s'" % lockfile)
267 fcntl.flock(descriptor, fcntl.LOCK_UN)
268 descriptor.close()
269 os.remove(lockfile)
270 self.portlocks = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500271
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600272 def get(self, key):
273 if key in self.d:
274 return self.d.get(key)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500275 elif os.getenv(key):
276 return os.getenv(key)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600277 else:
278 return ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500279
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600280 def set(self, key, value):
281 self.d[key] = value
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500282
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600283 def is_deploy_dir_image(self, p):
284 if os.path.isdir(p):
285 if not re.search('.qemuboot.conf$', '\n'.join(os.listdir(p)), re.M):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500286 logger.debug("Can't find required *.qemuboot.conf in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600287 return False
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500288 if not any(map(lambda name: '-image-' in name, os.listdir(p))):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500289 logger.debug("Can't find *-image-* in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600290 return False
291 return True
292 else:
293 return False
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500294
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600295 def check_arg_fstype(self, fst):
296 """Check and set FSTYPE"""
Brad Bishop15ae2502019-06-18 21:44:24 -0400297 if fst not in self.fstypes + self.vmtypes + self.wictypes:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800298 logger.warning("Maybe unsupported FSTYPE: %s" % fst)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600299 if not self.fstype or self.fstype == fst:
300 if fst == 'ramfs':
301 fst = 'cpio.gz'
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500302 if fst in ('tar.bz2', 'tar.gz'):
303 fst = 'nfs'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600304 self.fstype = fst
305 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500306 raise RunQemuError("Conflicting: FSTYPE %s and %s" % (self.fstype, fst))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500307
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600308 def set_machine_deploy_dir(self, machine, deploy_dir_image):
309 """Set MACHINE and DEPLOY_DIR_IMAGE"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500310 logger.debug('MACHINE: %s' % machine)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600311 self.set("MACHINE", machine)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500312 logger.debug('DEPLOY_DIR_IMAGE: %s' % deploy_dir_image)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600313 self.set("DEPLOY_DIR_IMAGE", deploy_dir_image)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500314
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600315 def check_arg_nfs(self, p):
316 if os.path.isdir(p):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500317 self.rootfs = p
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600318 else:
319 m = re.match('(.*):(.*)', p)
320 self.nfs_server = m.group(1)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500321 self.rootfs = m.group(2)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600322 self.check_arg_fstype('nfs')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500323
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600324 def check_arg_path(self, p):
325 """
326 - Check whether it is <image>.qemuboot.conf or contains <image>.qemuboot.conf
327 - Check whether is a kernel file
328 - Check whether is a image file
329 - Check whether it is a nfs dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500330 - Check whether it is a OVMF flash file
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600331 """
332 if p.endswith('.qemuboot.conf'):
333 self.qemuboot = p
334 self.qbconfload = True
335 elif re.search('\.bin$', p) or re.search('bzImage', p) or \
336 re.search('zImage', p) or re.search('vmlinux', p) or \
337 re.search('fitImage', p) or re.search('uImage', p):
338 self.kernel = p
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500339 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 -0600340 self.rootfs = p
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500341 # Check filename against self.fstypes can hanlde <file>.cpio.gz,
342 # otherwise, its type would be "gz", which is incorrect.
343 fst = ""
344 for t in self.fstypes:
345 if p.endswith(t):
346 fst = t
347 break
348 if not fst:
349 m = re.search('.*\.(.*)$', self.rootfs)
350 if m:
351 fst = m.group(1)
352 if fst:
353 self.check_arg_fstype(fst)
354 qb = re.sub('\.' + fst + "$", '', self.rootfs)
355 qb = '%s%s' % (re.sub('\.rootfs$', '', qb), '.qemuboot.conf')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600356 if os.path.exists(qb):
357 self.qemuboot = qb
358 self.qbconfload = True
359 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800360 logger.warning("%s doesn't exist" % qb)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600361 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500362 raise RunQemuError("Can't find FSTYPE from: %s" % p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500363
364 elif os.path.isdir(p) or re.search(':', p) and re.search('/', p):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600365 if self.is_deploy_dir_image(p):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500366 logger.debug('DEPLOY_DIR_IMAGE: %s' % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600367 self.set("DEPLOY_DIR_IMAGE", p)
368 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500369 logger.debug("Assuming %s is an nfs rootfs" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600370 self.check_arg_nfs(p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500371 elif os.path.basename(p).startswith('ovmf'):
372 self.ovmf_bios.append(p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600373 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500374 raise RunQemuError("Unknown path arg %s" % p)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500375
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600376 def check_arg_machine(self, arg):
377 """Check whether it is a machine"""
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500378 if self.get('MACHINE') == arg:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600379 return
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500380 elif self.get('MACHINE') and self.get('MACHINE') != arg:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500381 raise RunQemuError("Maybe conflicted MACHINE: %s vs %s" % (self.get('MACHINE'), arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500382 elif re.search('/', arg):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500383 raise RunQemuError("Unknown arg: %s" % arg)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500384
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500385 logger.debug('Assuming MACHINE = %s' % arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500386
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600387 # if we're running under testimage, or similarly as a child
388 # of an existing bitbake invocation, we can't invoke bitbake
389 # to validate the MACHINE setting and must assume it's correct...
390 # FIXME: testimage.bbclass exports these two variables into env,
391 # are there other scenarios in which we need to support being
392 # invoked by bitbake?
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500393 deploy = self.get('DEPLOY_DIR_IMAGE')
394 bbchild = deploy and self.get('OE_TMPDIR')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600395 if bbchild:
396 self.set_machine_deploy_dir(arg, deploy)
397 return
398 # also check whether we're running under a sourced toolchain
399 # environment file
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500400 if self.get('OECORE_NATIVE_SYSROOT'):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600401 self.set("MACHINE", arg)
402 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500403
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600404 cmd = 'MACHINE=%s bitbake -e' % arg
405 logger.info('Running %s...' % cmd)
Brad Bishop977dc1a2019-02-06 16:01:43 -0500406 self.bitbake_e = subprocess.check_output(cmd, shell=True).decode('utf-8')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600407 # bitbake -e doesn't report invalid MACHINE as an error, so
408 # let's check DEPLOY_DIR_IMAGE to make sure that it is a valid
409 # MACHINE.
410 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
411 if s:
412 deploy_dir_image = s.group(1)
413 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500414 raise RunQemuError("bitbake -e %s" % self.bitbake_e)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600415 if self.is_deploy_dir_image(deploy_dir_image):
416 self.set_machine_deploy_dir(arg, deploy_dir_image)
417 else:
418 logger.error("%s not a directory valid DEPLOY_DIR_IMAGE" % deploy_dir_image)
419 self.set("MACHINE", arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500420
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600421 def check_args(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500422 for debug in ("-d", "--debug"):
423 if debug in sys.argv:
424 logger.setLevel(logging.DEBUG)
425 sys.argv.remove(debug)
426
427 for quiet in ("-q", "--quiet"):
428 if quiet in sys.argv:
429 logger.setLevel(logging.ERROR)
430 sys.argv.remove(quiet)
431
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600432 unknown_arg = ""
433 for arg in sys.argv[1:]:
Brad Bishop15ae2502019-06-18 21:44:24 -0400434 if arg in self.fstypes + self.vmtypes + self.wictypes:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600435 self.check_arg_fstype(arg)
436 elif arg == 'nographic':
437 self.qemu_opt_script += ' -nographic'
438 self.kernel_cmdline_script += ' console=ttyS0'
Brad Bishop19323692019-04-05 15:28:33 -0400439 elif arg == 'sdl':
Brad Bishop6dbb3162019-11-25 09:41:34 -0500440 if 'gl' in sys.argv[1:]:
441 self.qemu_opt_script += ' -vga virtio -display sdl,gl=on'
442 elif 'gl-es' in sys.argv[1:]:
443 self.qemu_opt_script += ' -vga virtio -display sdl,gl=es'
444 else:
445 self.qemu_opt_script += ' -display sdl'
Brad Bishopa34c0302019-09-23 22:34:48 -0400446 elif arg == 'gtk':
447 if 'gl' in sys.argv[1:]:
448 self.qemu_opt_script += ' -vga virtio -display gtk,gl=on'
449 elif 'gl-es' in sys.argv[1:]:
450 self.qemu_opt_script += ' -vga virtio -display gtk,gl=es'
451 else:
452 self.qemu_opt_script += ' -display gtk'
453 elif arg == 'gl' or arg == 'gl-es':
454 # These args are handled inside sdl or gtk blocks above
455 pass
Brad Bishop19323692019-04-05 15:28:33 -0400456 elif arg == 'egl-headless':
457 self.qemu_opt_script += ' -vga virtio -display egl-headless'
458 # As runqemu can be run within bitbake (when using testimage, for example),
459 # we need to ensure that we run host pkg-config, and that it does not
460 # get mis-directed to native build paths set by bitbake.
461 try:
462 del os.environ['PKG_CONFIG_PATH']
463 del os.environ['PKG_CONFIG_DIR']
464 del os.environ['PKG_CONFIG_LIBDIR']
Brad Bishopf3f93bb2019-10-16 14:33:32 -0400465 del os.environ['PKG_CONFIG_SYSROOT_DIR']
Brad Bishop19323692019-04-05 15:28:33 -0400466 except KeyError:
467 pass
468 try:
469 dripath = subprocess.check_output("PATH=/bin:/usr/bin:$PATH pkg-config --variable=dridriverdir dri", shell=True)
470 except subprocess.CalledProcessError as e:
471 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.")
472 os.environ['LIBGL_DRIVERS_PATH'] = dripath.decode('utf-8').strip()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600473 elif arg == 'serial':
474 self.kernel_cmdline_script += ' console=ttyS0'
Brad Bishop19323692019-04-05 15:28:33 -0400475 self.serialconsole = True
476 elif arg == "serialstdio":
477 self.kernel_cmdline_script += ' console=ttyS0'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600478 self.serialstdio = True
479 elif arg == 'audio':
480 logger.info("Enabling audio in qemu")
481 logger.info("Please install sound drivers in linux host")
482 self.audio_enabled = True
483 elif arg == 'kvm':
484 self.kvm_enabled = True
485 elif arg == 'kvm-vhost':
486 self.vhost_enabled = True
487 elif arg == 'slirp':
488 self.slirp_enabled = True
489 elif arg == 'snapshot':
490 self.snapshot = True
491 elif arg == 'publicvnc':
492 self.qemu_opt_script += ' -vnc :0'
493 elif arg.startswith('tcpserial='):
Brad Bishop15ae2502019-06-18 21:44:24 -0400494 self.tcpserial_portnum = '%s' % arg[len('tcpserial='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600495 elif arg.startswith('qemuparams='):
Andrew Geissler99467da2019-02-25 18:54:23 -0600496 self.qemuparams = ' %s' % arg[len('qemuparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600497 elif arg.startswith('bootparams='):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500498 self.bootparams = arg[len('bootparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600499 elif os.path.exists(arg) or (re.search(':', arg) and re.search('/', arg)):
500 self.check_arg_path(os.path.abspath(arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500501 elif re.search(r'-image-|-image$', arg):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600502 # Lazy rootfs
503 self.rootfs = arg
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500504 elif arg.startswith('ovmf'):
505 self.ovmf_bios.append(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600506 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500507 # At last, assume it is the MACHINE
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600508 if (not unknown_arg) or unknown_arg == arg:
509 unknown_arg = arg
510 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500511 raise RunQemuError("Can't handle two unknown args: %s %s\n"
512 "Try 'runqemu help' on how to use it" % \
513 (unknown_arg, arg))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600514 # Check to make sure it is a valid machine
Brad Bishopa5c52ff2018-11-23 10:55:50 +1300515 if unknown_arg and self.get('MACHINE') != unknown_arg:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500516 if self.get('DEPLOY_DIR_IMAGE'):
517 machine = os.path.basename(self.get('DEPLOY_DIR_IMAGE'))
518 if unknown_arg == machine:
519 self.set("MACHINE", machine)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500520
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600521 self.check_arg_machine(unknown_arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500522
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500523 if not (self.get('DEPLOY_DIR_IMAGE') or self.qbconfload):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500524 self.load_bitbake_env()
525 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
526 if s:
527 self.set("DEPLOY_DIR_IMAGE", s.group(1))
528
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600529 def check_kvm(self):
530 """Check kvm and kvm-host"""
531 if not (self.kvm_enabled or self.vhost_enabled):
532 self.qemu_opt_script += ' %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU'))
533 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500534
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600535 if not self.get('QB_CPU_KVM'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500536 raise RunQemuError("QB_CPU_KVM is NULL, this board doesn't support kvm")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500537
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600538 self.qemu_opt_script += ' %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU_KVM'))
539 yocto_kvm_wiki = "https://wiki.yoctoproject.org/wiki/How_to_enable_KVM_for_Poky_qemu"
540 yocto_paravirt_kvm_wiki = "https://wiki.yoctoproject.org/wiki/Running_an_x86_Yocto_Linux_image_under_QEMU_KVM"
541 dev_kvm = '/dev/kvm'
542 dev_vhost = '/dev/vhost-net'
Brad Bishop15ae2502019-06-18 21:44:24 -0400543 if self.qemu_system.endswith(('i386', 'x86_64')):
544 with open('/proc/cpuinfo', 'r') as f:
545 kvm_cap = re.search('vmx|svm', "".join(f.readlines()))
546 if not kvm_cap:
547 logger.error("You are trying to enable KVM on a cpu without VT support.")
548 logger.error("Remove kvm from the command-line, or refer:")
549 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500550
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600551 if not os.path.exists(dev_kvm):
552 logger.error("Missing KVM device. Have you inserted kvm modules?")
553 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500554 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500555
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600556 if os.access(dev_kvm, os.W_OK|os.R_OK):
557 self.qemu_opt_script += ' -enable-kvm'
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500558 if self.get('MACHINE') == "qemux86":
559 # Workaround for broken APIC window on pre 4.15 host kernels which causes boot hangs
560 # See YOCTO #12301
561 # On 64 bit we use x2apic
562 self.kernel_cmdline_script += " clocksource=kvm-clock hpet=disable noapic nolapic"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600563 else:
564 logger.error("You have no read or write permission on /dev/kvm.")
565 logger.error("Please change the ownership of this file as described at:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500566 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500567
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600568 if self.vhost_enabled:
569 if not os.path.exists(dev_vhost):
570 logger.error("Missing virtio net device. Have you inserted vhost-net module?")
571 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500572 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500573
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600574 if not os.access(dev_kvm, os.W_OK|os.R_OK):
575 logger.error("You have no read or write permission on /dev/vhost-net.")
576 logger.error("Please change the ownership of this file as described at:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500577 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500578
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600579 def check_fstype(self):
580 """Check and setup FSTYPE"""
581 if not self.fstype:
582 fstype = self.get('QB_DEFAULT_FSTYPE')
583 if fstype:
584 self.fstype = fstype
585 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500586 raise RunQemuError("FSTYPE is NULL!")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500587
Brad Bishop15ae2502019-06-18 21:44:24 -0400588 # parse QB_FSINFO into dict, e.g. { 'wic': ['no-kernel-in-fs', 'a-flag'], 'ext4': ['another-flag']}
589 wic_fs = False
590 qb_fsinfo = self.get('QB_FSINFO')
591 if qb_fsinfo:
592 qb_fsinfo = qb_fsinfo.split()
593 for fsinfo in qb_fsinfo:
594 try:
595 fstype, fsflag = fsinfo.split(':')
596
597 if fstype == 'wic':
598 if fsflag == 'no-kernel-in-fs':
599 wic_fs = True
600 elif fsflag == 'kernel-in-fs':
601 wic_fs = False
602 else:
603 logger.warn('Unknown flag "%s:%s" in QB_FSINFO', fstype, fsflag)
604 continue
605 else:
606 logger.warn('QB_FSINFO is not supported for image type "%s"', fstype)
607 continue
608
609 if fstype in self.fsinfo:
610 self.fsinfo[fstype].append(fsflag)
611 else:
612 self.fsinfo[fstype] = [fsflag]
613 except Exception:
614 logger.error('Invalid parameter "%s" in QB_FSINFO', fsinfo)
615
616 # treat wic images as vmimages (with kernel) or as fsimages (rootfs only)
617 if wic_fs:
618 self.fstypes = self.fstypes + self.wictypes
619 else:
620 self.vmtypes = self.vmtypes + self.wictypes
621
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600622 def check_rootfs(self):
623 """Check and set rootfs"""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500624
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500625 if self.fstype == "none":
626 return
627
628 if self.get('ROOTFS'):
629 if not self.rootfs:
630 self.rootfs = self.get('ROOTFS')
631 elif self.get('ROOTFS') != self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500632 raise RunQemuError("Maybe conflicted ROOTFS: %s vs %s" % (self.get('ROOTFS'), self.rootfs))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500633
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600634 if self.fstype == 'nfs':
635 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500636
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600637 if self.rootfs and not os.path.exists(self.rootfs):
638 # Lazy rootfs
639 self.rootfs = "%s/%s-%s.%s" % (self.get('DEPLOY_DIR_IMAGE'),
640 self.rootfs, self.get('MACHINE'),
641 self.fstype)
642 elif not self.rootfs:
643 cmd_name = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_NAME'), self.fstype)
644 cmd_link = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'), self.fstype)
645 cmds = (cmd_name, cmd_link)
646 self.rootfs = get_first_file(cmds)
647 if not self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500648 raise RunQemuError("Failed to find rootfs: %s or %s" % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500649
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600650 if not os.path.exists(self.rootfs):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500651 raise RunQemuError("Can't find rootfs: %s" % self.rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500652
Brad Bishop08902b02019-08-20 09:16:51 -0400653 def setup_pkkek1(self):
654 """
655 Extract from PEM certificate the Platform Key and first Key
656 Exchange Key certificate string. The hypervisor needs to provide
657 it in the Type 11 SMBIOS table
658 """
659 pemcert = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), 'OvmfPkKek1.pem')
660 try:
661 with open(pemcert, 'r') as pemfile:
662 key = pemfile.read().replace('\n', ''). \
663 replace('-----BEGIN CERTIFICATE-----', ''). \
664 replace('-----END CERTIFICATE-----', '')
665 self.ovmf_secboot_pkkek1 = key
666
667 except FileNotFoundError:
668 raise RunQemuError("Can't open PEM certificate %s " % pemcert)
669
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500670 def check_ovmf(self):
671 """Check and set full path for OVMF firmware and variable file(s)."""
672
673 for index, ovmf in enumerate(self.ovmf_bios):
674 if os.path.exists(ovmf):
675 continue
676 for suffix in ('qcow2', 'bin'):
677 path = '%s/%s.%s' % (self.get('DEPLOY_DIR_IMAGE'), ovmf, suffix)
678 if os.path.exists(path):
679 self.ovmf_bios[index] = path
Brad Bishop08902b02019-08-20 09:16:51 -0400680 if ovmf.endswith('secboot'):
681 self.setup_pkkek1()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500682 break
683 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500684 raise RunQemuError("Can't find OVMF firmware: %s" % ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500685
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600686 def check_kernel(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400687 """Check and set kernel"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600688 # The vm image doesn't need a kernel
689 if self.fstype in self.vmtypes:
690 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500691
Brad Bishop316dfdd2018-06-25 12:45:53 -0400692 # See if the user supplied a KERNEL option
693 if self.get('KERNEL'):
694 self.kernel = self.get('KERNEL')
695
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500696 # QB_DEFAULT_KERNEL is always a full file path
697 kernel_name = os.path.basename(self.get('QB_DEFAULT_KERNEL'))
698
699 # The user didn't want a kernel to be loaded
Brad Bishop316dfdd2018-06-25 12:45:53 -0400700 if kernel_name == "none" and not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500701 return
702
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600703 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
704 if not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500705 kernel_match_name = "%s/%s" % (deploy_dir_image, kernel_name)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600706 kernel_match_link = "%s/%s" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
707 kernel_startswith = "%s/%s*" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
708 cmds = (kernel_match_name, kernel_match_link, kernel_startswith)
709 self.kernel = get_first_file(cmds)
710 if not self.kernel:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500711 raise RunQemuError('KERNEL not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500712
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600713 if not os.path.exists(self.kernel):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500714 raise RunQemuError("KERNEL %s not found" % self.kernel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500715
Brad Bishop316dfdd2018-06-25 12:45:53 -0400716 def check_dtb(self):
717 """Check and set dtb"""
718 # Did the user specify a device tree?
719 if self.get('DEVICE_TREE'):
720 self.dtb = self.get('DEVICE_TREE')
721 if not os.path.exists(self.dtb):
722 raise RunQemuError('Specified DTB not found: %s' % self.dtb)
723 return
724
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600725 dtb = self.get('QB_DTB')
726 if dtb:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400727 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600728 cmd_match = "%s/%s" % (deploy_dir_image, dtb)
729 cmd_startswith = "%s/%s*" % (deploy_dir_image, dtb)
730 cmd_wild = "%s/*.dtb" % deploy_dir_image
731 cmds = (cmd_match, cmd_startswith, cmd_wild)
732 self.dtb = get_first_file(cmds)
733 if not os.path.exists(self.dtb):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500734 raise RunQemuError('DTB not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500735
Brad Bishopc68388fc2019-08-26 01:33:31 -0400736 def check_bios(self):
737 """Check and set bios"""
738
739 # See if the user supplied a BIOS option
740 if self.get('BIOS'):
741 self.bios = self.get('BIOS')
742
743 # QB_DEFAULT_BIOS is always a full file path
744 bios_name = os.path.basename(self.get('QB_DEFAULT_BIOS'))
745
746 # The user didn't want a bios to be loaded
747 if (bios_name == "" or bios_name == "none") and not self.bios:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600748 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500749
Brad Bishopc68388fc2019-08-26 01:33:31 -0400750 if not self.bios:
751 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
752 self.bios = "%s/%s" % (deploy_dir_image, bios_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500753
Brad Bishopc68388fc2019-08-26 01:33:31 -0400754 if not self.bios:
755 raise RunQemuError('BIOS not found: %s' % bios_match_name)
756
757 if not os.path.exists(self.bios):
758 raise RunQemuError("KERNEL %s not found" % self.bios)
759
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500760
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600761 def check_mem(self):
Andrew Geissler99467da2019-02-25 18:54:23 -0600762 """
763 Both qemu and kernel needs memory settings, so check QB_MEM and set it
764 for both.
765 """
766 s = re.search('-m +([0-9]+)', self.qemuparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600767 if s:
768 self.set('QB_MEM', '-m %s' % s.group(1))
769 elif not self.get('QB_MEM'):
Brad Bishop79641f22019-09-10 07:20:22 -0400770 logger.info('QB_MEM is not set, use 256M by default')
771 self.set('QB_MEM', '-m 256')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500772
Andrew Geissler99467da2019-02-25 18:54:23 -0600773 # Check and remove M or m suffix
774 qb_mem = self.get('QB_MEM')
775 if qb_mem.endswith('M') or qb_mem.endswith('m'):
776 qb_mem = qb_mem[:-1]
777
778 # Add -m prefix it not present
779 if not qb_mem.startswith('-m'):
780 qb_mem = '-m %s' % qb_mem
781
782 self.set('QB_MEM', qb_mem)
783
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800784 mach = self.get('MACHINE')
785 if not mach.startswith('qemumips'):
786 self.kernel_cmdline_script += ' mem=%s' % self.get('QB_MEM').replace('-m','').strip() + 'M'
787
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600788 self.qemu_opt_script += ' %s' % self.get('QB_MEM')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500789
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600790 def check_tcpserial(self):
791 if self.tcpserial_portnum:
Brad Bishop15ae2502019-06-18 21:44:24 -0400792 ports = self.tcpserial_portnum.split(':')
793 port = ports[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600794 if self.get('QB_TCPSERIAL_OPT'):
Brad Bishop15ae2502019-06-18 21:44:24 -0400795 self.qemu_opt_script += ' ' + self.get('QB_TCPSERIAL_OPT').replace('@PORT@', port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600796 else:
Brad Bishop15ae2502019-06-18 21:44:24 -0400797 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
798
799 if len(ports) > 1:
800 for port in ports[1:]:
801 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500802
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600803 def check_and_set(self):
804 """Check configs sanity and set when needed"""
805 self.validate_paths()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500806 if not self.slirp_enabled:
807 check_tun()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600808 # Check audio
809 if self.audio_enabled:
810 if not self.get('QB_AUDIO_DRV'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500811 raise RunQemuError("QB_AUDIO_DRV is NULL, this board doesn't support audio")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600812 if not self.get('QB_AUDIO_OPT'):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800813 logger.warning('QB_AUDIO_OPT is NULL, you may need define it to make audio work')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600814 else:
815 self.qemu_opt_script += ' %s' % self.get('QB_AUDIO_OPT')
816 os.putenv('QEMU_AUDIO_DRV', self.get('QB_AUDIO_DRV'))
817 else:
818 os.putenv('QEMU_AUDIO_DRV', 'none')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500819
Brad Bishop15ae2502019-06-18 21:44:24 -0400820 self.check_qemu_system()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600821 self.check_kvm()
822 self.check_fstype()
823 self.check_rootfs()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500824 self.check_ovmf()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600825 self.check_kernel()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400826 self.check_dtb()
Brad Bishopc68388fc2019-08-26 01:33:31 -0400827 self.check_bios()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600828 self.check_mem()
829 self.check_tcpserial()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500830
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600831 def read_qemuboot(self):
832 if not self.qemuboot:
833 if self.get('DEPLOY_DIR_IMAGE'):
834 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600835 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800836 logger.warning("Can't find qemuboot conf file, DEPLOY_DIR_IMAGE is NULL!")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600837 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500838
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600839 if self.rootfs and not os.path.exists(self.rootfs):
840 # Lazy rootfs
841 machine = self.get('MACHINE')
842 if not machine:
843 machine = os.path.basename(deploy_dir_image)
844 self.qemuboot = "%s/%s-%s.qemuboot.conf" % (deploy_dir_image,
845 self.rootfs, machine)
846 else:
847 cmd = 'ls -t %s/*.qemuboot.conf' % deploy_dir_image
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500848 logger.debug('Running %s...' % cmd)
849 try:
850 qbs = subprocess.check_output(cmd, shell=True).decode('utf-8')
851 except subprocess.CalledProcessError as err:
852 raise RunQemuError(err)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600853 if qbs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500854 for qb in qbs.split():
855 # Don't use initramfs when other choices unless fstype is ramfs
856 if '-initramfs-' in os.path.basename(qb) and self.fstype != 'cpio.gz':
857 continue
858 self.qemuboot = qb
859 break
860 if not self.qemuboot:
861 # Use the first one when no choice
862 self.qemuboot = qbs.split()[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600863 self.qbconfload = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500864
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600865 if not self.qemuboot:
866 # If we haven't found a .qemuboot.conf at this point it probably
867 # doesn't exist, continue without
868 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500869
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600870 if not os.path.exists(self.qemuboot):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500871 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 -0500872
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500873 logger.debug('CONFFILE: %s' % self.qemuboot)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500874
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600875 cf = configparser.ConfigParser()
876 cf.read(self.qemuboot)
877 for k, v in cf.items('config_bsp'):
878 k_upper = k.upper()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500879 if v.startswith("../"):
880 v = os.path.abspath(os.path.dirname(self.qemuboot) + "/" + v)
881 elif v == ".":
882 v = os.path.dirname(self.qemuboot)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600883 self.set(k_upper, v)
884
885 def validate_paths(self):
886 """Ensure all relevant path variables are set"""
887 # When we're started with a *.qemuboot.conf arg assume that image
888 # artefacts are relative to that file, rather than in whatever
889 # directory DEPLOY_DIR_IMAGE in the conf file points to.
890 if self.qbconfload:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500891 imgdir = os.path.realpath(os.path.dirname(self.qemuboot))
892 if imgdir != os.path.realpath(self.get('DEPLOY_DIR_IMAGE')):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600893 logger.info('Setting DEPLOY_DIR_IMAGE to folder containing %s (%s)' % (self.qemuboot, imgdir))
894 self.set('DEPLOY_DIR_IMAGE', imgdir)
895
896 # If the STAGING_*_NATIVE directories from the config file don't exist
897 # and we're in a sourced OE build directory try to extract the paths
898 # from `bitbake -e`
899 havenative = os.path.exists(self.get('STAGING_DIR_NATIVE')) and \
900 os.path.exists(self.get('STAGING_BINDIR_NATIVE'))
901
902 if not havenative:
903 if not self.bitbake_e:
904 self.load_bitbake_env()
905
906 if self.bitbake_e:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500907 native_vars = ['STAGING_DIR_NATIVE']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600908 for nv in native_vars:
909 s = re.search('^%s="(.*)"' % nv, self.bitbake_e, re.M)
910 if s and s.group(1) != self.get(nv):
911 logger.info('Overriding conf file setting of %s to %s from Bitbake environment' % (nv, s.group(1)))
912 self.set(nv, s.group(1))
913 else:
914 # when we're invoked from a running bitbake instance we won't
915 # be able to call `bitbake -e`, then try:
916 # - get OE_TMPDIR from environment and guess paths based on it
917 # - get OECORE_NATIVE_SYSROOT from environment (for sdk)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500918 tmpdir = self.get('OE_TMPDIR')
919 oecore_native_sysroot = self.get('OECORE_NATIVE_SYSROOT')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600920 if tmpdir:
921 logger.info('Setting STAGING_DIR_NATIVE and STAGING_BINDIR_NATIVE relative to OE_TMPDIR (%s)' % tmpdir)
922 hostos, _, _, _, machine = os.uname()
923 buildsys = '%s-%s' % (machine, hostos.lower())
924 staging_dir_native = '%s/sysroots/%s' % (tmpdir, buildsys)
925 self.set('STAGING_DIR_NATIVE', staging_dir_native)
926 elif oecore_native_sysroot:
927 logger.info('Setting STAGING_DIR_NATIVE to OECORE_NATIVE_SYSROOT (%s)' % oecore_native_sysroot)
928 self.set('STAGING_DIR_NATIVE', oecore_native_sysroot)
929 if self.get('STAGING_DIR_NATIVE'):
930 # we have to assume that STAGING_BINDIR_NATIVE is at usr/bin
931 staging_bindir_native = '%s/usr/bin' % self.get('STAGING_DIR_NATIVE')
932 logger.info('Setting STAGING_BINDIR_NATIVE to %s' % staging_bindir_native)
933 self.set('STAGING_BINDIR_NATIVE', '%s/usr/bin' % self.get('STAGING_DIR_NATIVE'))
934
935 def print_config(self):
936 logger.info('Continuing with the following parameters:\n')
937 if not self.fstype in self.vmtypes:
938 print('KERNEL: [%s]' % self.kernel)
Brad Bishopc68388fc2019-08-26 01:33:31 -0400939 if self.bios:
940 print('BIOS: [%s]' % self.bios)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600941 if self.dtb:
942 print('DTB: [%s]' % self.dtb)
943 print('MACHINE: [%s]' % self.get('MACHINE'))
Brad Bishop15ae2502019-06-18 21:44:24 -0400944 try:
945 fstype_flags = ' (' + ', '.join(self.fsinfo[self.fstype]) + ')'
946 except KeyError:
947 fstype_flags = ''
948 print('FSTYPE: [%s%s]' % (self.fstype, fstype_flags))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600949 if self.fstype == 'nfs':
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500950 print('NFS_DIR: [%s]' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600951 else:
952 print('ROOTFS: [%s]' % self.rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500953 if self.ovmf_bios:
954 print('OVMF: %s' % self.ovmf_bios)
Brad Bishop08902b02019-08-20 09:16:51 -0400955 if (self.ovmf_secboot_pkkek1):
956 print('SECBOOT PKKEK1: [%s...]' % self.ovmf_secboot_pkkek1[0:100])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600957 print('CONFFILE: [%s]' % self.qemuboot)
958 print('')
959
960 def setup_nfs(self):
961 if not self.nfs_server:
962 if self.slirp_enabled:
963 self.nfs_server = '10.0.2.2'
964 else:
965 self.nfs_server = '192.168.7.1'
966
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500967 # Figure out a new nfs_instance to allow multiple qemus running.
Brad Bishop977dc1a2019-02-06 16:01:43 -0500968 ps = subprocess.check_output(("ps", "auxww")).decode('utf-8')
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500969 pattern = '/bin/unfsd .* -i .*\.pid -e .*/exports([0-9]+) '
970 all_instances = re.findall(pattern, ps, re.M)
971 if all_instances:
972 all_instances.sort(key=int)
973 self.nfs_instance = int(all_instances.pop()) + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600974
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500975 nfsd_port = 3049 + 2 * self.nfs_instance
976 mountd_port = 3048 + 2 * self.nfs_instance
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600977
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500978 # Export vars for runqemu-export-rootfs
979 export_dict = {
980 'NFS_INSTANCE': self.nfs_instance,
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500981 'NFSD_PORT': nfsd_port,
982 'MOUNTD_PORT': mountd_port,
983 }
984 for k, v in export_dict.items():
985 # Use '%s' since they are integers
986 os.putenv(k, '%s' % v)
987
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500988 self.unfs_opts="nfsvers=3,port=%s,udp,mountport=%s" % (nfsd_port, mountd_port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600989
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500990 # Extract .tar.bz2 or .tar.bz if no nfs dir
991 if not (self.rootfs and os.path.isdir(self.rootfs)):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600992 src_prefix = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'))
993 dest = "%s-nfsroot" % src_prefix
994 if os.path.exists('%s.pseudo_state' % dest):
995 logger.info('Use %s as NFS_DIR' % dest)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500996 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600997 else:
998 src = ""
999 src1 = '%s.tar.bz2' % src_prefix
1000 src2 = '%s.tar.gz' % src_prefix
1001 if os.path.exists(src1):
1002 src = src1
1003 elif os.path.exists(src2):
1004 src = src2
1005 if not src:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001006 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 -06001007 logger.info('NFS_DIR not found, extracting %s to %s' % (src, dest))
Brad Bishop977dc1a2019-02-06 16:01:43 -05001008 cmd = ('runqemu-extract-sdk', src, dest)
1009 logger.info('Running %s...' % str(cmd))
1010 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001011 raise RunQemuError('Failed to run %s' % cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001012 self.clean_nfs_dir = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001013 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001014
1015 # Start the userspace NFS server
Brad Bishop977dc1a2019-02-06 16:01:43 -05001016 cmd = ('runqemu-export-rootfs', 'start', self.rootfs)
1017 logger.info('Running %s...' % str(cmd))
1018 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001019 raise RunQemuError('Failed to run %s' % cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001020
1021 self.nfs_running = True
1022
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001023 def setup_slirp(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001024 """Setup user networking"""
1025
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001026 if self.fstype == 'nfs':
1027 self.setup_nfs()
1028 self.kernel_cmdline_script += ' ip=dhcp'
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001029 # Port mapping
1030 hostfwd = ",hostfwd=tcp::2222-:22,hostfwd=tcp::2323-:23"
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001031 qb_slirp_opt_default = "-netdev user,id=net0%s,tftp=%s" % (hostfwd, self.get('DEPLOY_DIR_IMAGE'))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001032 qb_slirp_opt = self.get('QB_SLIRP_OPT') or qb_slirp_opt_default
1033 # Figure out the port
1034 ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt)
1035 ports = [int(i) for i in ports]
1036 mac = 2
Brad Bishop08902b02019-08-20 09:16:51 -04001037
1038 lockdir = "/tmp/qemu-port-locks"
1039 if not os.path.exists(lockdir):
1040 # There might be a race issue when multi runqemu processess are
1041 # running at the same time.
1042 try:
1043 os.mkdir(lockdir)
1044 os.chmod(lockdir, 0o777)
1045 except FileExistsError:
1046 pass
1047
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001048 # Find a free port to avoid conflicts
1049 for p in ports[:]:
1050 p_new = p
Brad Bishop08902b02019-08-20 09:16:51 -04001051 while not self.check_free_port('localhost', p_new, lockdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001052 p_new += 1
1053 mac += 1
1054 while p_new in ports:
1055 p_new += 1
1056 mac += 1
1057 if p != p_new:
1058 ports.append(p_new)
1059 qb_slirp_opt = re.sub(':%s-' % p, ':%s-' % p_new, qb_slirp_opt)
1060 logger.info("Port forward changed: %s -> %s" % (p, p_new))
1061 mac = "%s%02x" % (self.mac_slirp, mac)
1062 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qb_slirp_opt))
1063 # Print out port foward
1064 hostfwd = re.findall('(hostfwd=[^,]*)', qb_slirp_opt)
1065 if hostfwd:
1066 logger.info('Port forward: %s' % ' '.join(hostfwd))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001067
1068 def setup_tap(self):
1069 """Setup tap"""
1070
1071 # This file is created when runqemu-gen-tapdevs creates a bank of tap
1072 # devices, indicating that the user should not bring up new ones using
1073 # sudo.
1074 nosudo_flag = '/etc/runqemu-nosudo'
1075 self.qemuifup = shutil.which('runqemu-ifup')
1076 self.qemuifdown = shutil.which('runqemu-ifdown')
1077 ip = shutil.which('ip')
1078 lockdir = "/tmp/qemu-tap-locks"
1079
1080 if not (self.qemuifup and self.qemuifdown and ip):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001081 logger.error("runqemu-ifup: %s" % self.qemuifup)
1082 logger.error("runqemu-ifdown: %s" % self.qemuifdown)
1083 logger.error("ip: %s" % ip)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001084 raise OEPathError("runqemu-ifup, runqemu-ifdown or ip not found")
1085
1086 if not os.path.exists(lockdir):
1087 # There might be a race issue when multi runqemu processess are
1088 # running at the same time.
1089 try:
1090 os.mkdir(lockdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001091 os.chmod(lockdir, 0o777)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001092 except FileExistsError:
1093 pass
1094
Brad Bishop977dc1a2019-02-06 16:01:43 -05001095 cmd = (ip, 'link')
1096 logger.debug('Running %s...' % str(cmd))
1097 ip_link = subprocess.check_output(cmd).decode('utf-8')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001098 # Matches line like: 6: tap0: <foo>
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001099 possibles = re.findall('^[0-9]+: +(tap[0-9]+): <.*', ip_link, re.M)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001100 tap = ""
1101 for p in possibles:
1102 lockfile = os.path.join(lockdir, p)
1103 if os.path.exists('%s.skip' % lockfile):
1104 logger.info('Found %s.skip, skipping %s' % (lockfile, p))
1105 continue
Brad Bishop08902b02019-08-20 09:16:51 -04001106 self.taplock = lockfile + '.lock'
1107 if self.acquire_taplock(error=False):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001108 tap = p
1109 logger.info("Using preconfigured tap device %s" % tap)
1110 logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap))
1111 break
1112
1113 if not tap:
1114 if os.path.exists(nosudo_flag):
1115 logger.error("Error: There are no available tap devices to use for networking,")
1116 logger.error("and I see %s exists, so I am not going to try creating" % nosudo_flag)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001117 raise RunQemuError("a new one with sudo.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001118
1119 gid = os.getgid()
1120 uid = os.getuid()
1121 logger.info("Setting up tap interface under sudo")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001122 cmd = ('sudo', self.qemuifup, str(uid), str(gid), self.bindir_native)
1123 tap = subprocess.check_output(cmd).decode('utf-8').strip()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001124 lockfile = os.path.join(lockdir, tap)
Brad Bishop08902b02019-08-20 09:16:51 -04001125 self.taplock = lockfile + '.lock'
1126 self.acquire_taplock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001127 self.cleantap = True
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001128 logger.debug('Created tap: %s' % tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001129
1130 if not tap:
1131 logger.error("Failed to setup tap device. Run runqemu-gen-tapdevs to manually create.")
1132 return 1
1133 self.tap = tap
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001134 tapnum = int(tap[3:])
1135 gateway = tapnum * 2 + 1
1136 client = gateway + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001137 if self.fstype == 'nfs':
1138 self.setup_nfs()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001139 netconf = "192.168.7.%s::192.168.7.%s:255.255.255.0" % (client, gateway)
1140 logger.info("Network configuration: %s", netconf)
1141 self.kernel_cmdline_script += " ip=%s" % netconf
1142 mac = "%s%02x" % (self.mac_tap, client)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001143 qb_tap_opt = self.get('QB_TAP_OPT')
1144 if qb_tap_opt:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001145 qemu_tap_opt = qb_tap_opt.replace('@TAP@', tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001146 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001147 qemu_tap_opt = "-netdev tap,id=net0,ifname=%s,script=no,downscript=no" % (self.tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001148
1149 if self.vhost_enabled:
1150 qemu_tap_opt += ',vhost=on'
1151
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001152 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qemu_tap_opt))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001153
1154 def setup_network(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001155 if self.get('QB_NET') == 'none':
1156 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001157 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001158 self.saved_stty = subprocess.check_output(("stty", "-g")).decode('utf-8').strip()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001159 self.network_device = self.get('QB_NETWORK_DEVICE') or self.network_device
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001160 if self.slirp_enabled:
1161 self.setup_slirp()
1162 else:
1163 self.setup_tap()
1164
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001165 def setup_rootfs(self):
1166 if self.get('QB_ROOTFS') == 'none':
1167 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001168 if 'wic.' in self.fstype:
1169 self.fstype = self.fstype[4:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001170 rootfs_format = self.fstype if self.fstype in ('vmdk', 'qcow2', 'vdi') else 'raw'
1171
1172 qb_rootfs_opt = self.get('QB_ROOTFS_OPT')
1173 if qb_rootfs_opt:
1174 self.rootfs_options = qb_rootfs_opt.replace('@ROOTFS@', self.rootfs)
1175 else:
1176 self.rootfs_options = '-drive file=%s,if=virtio,format=%s' % (self.rootfs, rootfs_format)
1177
1178 if self.fstype in ('cpio.gz', 'cpio'):
1179 self.kernel_cmdline = 'root=/dev/ram0 rw debugshell'
1180 self.rootfs_options = '-initrd %s' % self.rootfs
1181 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001182 vm_drive = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001183 if self.fstype in self.vmtypes:
1184 if self.fstype == 'iso':
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001185 vm_drive = '-drive file=%s,if=virtio,media=cdrom' % self.rootfs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001186 elif self.get('QB_DRIVE_TYPE'):
1187 drive_type = self.get('QB_DRIVE_TYPE')
1188 if drive_type.startswith("/dev/sd"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001189 logger.info('Using scsi drive')
1190 vm_drive = '-drive if=none,id=hd,file=%s,format=%s -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd' \
1191 % (self.rootfs, rootfs_format)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001192 elif drive_type.startswith("/dev/hd"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001193 logger.info('Using ide drive')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001194 vm_drive = "-drive file=%s,format=%s" % (self.rootfs, rootfs_format)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001195 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001196 # virtio might have been selected explicitly (just use it), or
1197 # is used as fallback (then warn about that).
1198 if not drive_type.startswith("/dev/vd"):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001199 logger.warning("Unknown QB_DRIVE_TYPE: %s" % drive_type)
1200 logger.warning("Failed to figure out drive type, consider define or fix QB_DRIVE_TYPE")
1201 logger.warning('Trying to use virtio block drive')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001202 vm_drive = '-drive if=virtio,file=%s,format=%s' % (self.rootfs, rootfs_format)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001203
1204 # All branches above set vm_drive.
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001205 self.rootfs_options = '%s -no-reboot' % vm_drive
Brad Bishopf3f93bb2019-10-16 14:33:32 -04001206 self.kernel_cmdline = 'root=%s rw' % (self.get('QB_KERNEL_ROOT'))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001207
1208 if self.fstype == 'nfs':
1209 self.rootfs_options = ''
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001210 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 -04001211 self.kernel_cmdline = 'root=%s rw' % k_root
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001212
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001213 if self.fstype == 'none':
1214 self.rootfs_options = ''
1215
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001216 self.set('ROOTFS_OPTIONS', self.rootfs_options)
1217
1218 def guess_qb_system(self):
1219 """attempt to determine the appropriate qemu-system binary"""
1220 mach = self.get('MACHINE')
1221 if not mach:
1222 search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*'
1223 if self.rootfs:
1224 match = re.match(search, self.rootfs)
1225 if match:
1226 mach = match.group(1)
1227 elif self.kernel:
1228 match = re.match(search, self.kernel)
1229 if match:
1230 mach = match.group(1)
1231
1232 if not mach:
1233 return None
1234
1235 if mach == 'qemuarm':
1236 qbsys = 'arm'
1237 elif mach == 'qemuarm64':
1238 qbsys = 'aarch64'
1239 elif mach == 'qemux86':
1240 qbsys = 'i386'
1241 elif mach == 'qemux86-64':
1242 qbsys = 'x86_64'
1243 elif mach == 'qemuppc':
1244 qbsys = 'ppc'
1245 elif mach == 'qemumips':
1246 qbsys = 'mips'
1247 elif mach == 'qemumips64':
1248 qbsys = 'mips64'
1249 elif mach == 'qemumipsel':
1250 qbsys = 'mipsel'
1251 elif mach == 'qemumips64el':
1252 qbsys = 'mips64el'
Brad Bishop316dfdd2018-06-25 12:45:53 -04001253 elif mach == 'qemuriscv64':
1254 qbsys = 'riscv64'
1255 elif mach == 'qemuriscv32':
1256 qbsys = 'riscv32'
Brad Bishop004d4992018-10-02 23:54:45 +02001257 else:
1258 logger.error("Unable to determine QEMU PC System emulator for %s machine." % mach)
1259 logger.error("As %s is not among valid QEMU machines such as," % mach)
1260 logger.error("qemux86-64, qemux86, qemuarm64, qemuarm, qemumips64, qemumips64el, qemumipsel, qemumips, qemuppc")
1261 raise RunQemuError("Set qb_system_name with suitable QEMU PC System emulator in .*qemuboot.conf.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001262
1263 return 'qemu-system-%s' % qbsys
1264
Brad Bishop15ae2502019-06-18 21:44:24 -04001265 def check_qemu_system(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001266 qemu_system = self.get('QB_SYSTEM_NAME')
1267 if not qemu_system:
1268 qemu_system = self.guess_qb_system()
1269 if not qemu_system:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001270 raise RunQemuError("Failed to boot, QB_SYSTEM_NAME is NULL!")
Brad Bishop15ae2502019-06-18 21:44:24 -04001271 self.qemu_system = qemu_system
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001272
Brad Bishop15ae2502019-06-18 21:44:24 -04001273 def setup_final(self):
1274 qemu_bin = os.path.join(self.bindir_native, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001275
1276 # It is possible to have qemu-native in ASSUME_PROVIDED, and it won't
1277 # find QEMU in sysroot, it needs to use host's qemu.
1278 if not os.path.exists(qemu_bin):
1279 logger.info("QEMU binary not found in %s, trying host's QEMU" % qemu_bin)
1280 for path in (os.environ['PATH'] or '').split(':'):
Brad Bishop15ae2502019-06-18 21:44:24 -04001281 qemu_bin_tmp = os.path.join(path, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001282 logger.info("Trying: %s" % qemu_bin_tmp)
1283 if os.path.exists(qemu_bin_tmp):
1284 qemu_bin = qemu_bin_tmp
1285 if not os.path.isabs(qemu_bin):
1286 qemu_bin = os.path.abspath(qemu_bin)
1287 logger.info("Using host's QEMU: %s" % qemu_bin)
1288 break
1289
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001290 if not os.access(qemu_bin, os.X_OK):
1291 raise OEPathError("No QEMU binary '%s' could be found" % qemu_bin)
1292
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001293 self.qemu_opt = "%s %s %s %s" % (qemu_bin, self.get('NETWORK_CMD'), self.get('ROOTFS_OPTIONS'), self.get('QB_OPT_APPEND'))
1294
1295 for ovmf in self.ovmf_bios:
1296 format = ovmf.rsplit('.', 1)[-1]
1297 self.qemu_opt += ' -drive if=pflash,format=%s,file=%s' % (format, ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001298
1299 self.qemu_opt += ' ' + self.qemu_opt_script
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001300
Brad Bishop08902b02019-08-20 09:16:51 -04001301 if self.ovmf_secboot_pkkek1:
1302 # Provide the Platform Key and first Key Exchange Key certificate as an
1303 # OEM string in the SMBIOS Type 11 table. Prepend the certificate string
1304 # with "application prefix" of the EnrollDefaultKeys.efi application
1305 self.qemu_opt += ' -smbios type=11,value=4e32566d-8e9e-4f52-81d3-5bb9715f9727:' \
1306 + self.ovmf_secboot_pkkek1
1307
Andrew Geissler99467da2019-02-25 18:54:23 -06001308 # Append qemuparams to override previous settings
1309 if self.qemuparams:
1310 self.qemu_opt += ' ' + self.qemuparams
1311
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001312 if self.snapshot:
1313 self.qemu_opt += " -snapshot"
1314
Brad Bishop19323692019-04-05 15:28:33 -04001315 if self.serialconsole:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001316 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001317 subprocess.check_call(("stty", "intr", "^]"))
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001318 logger.info("Interrupt character is '^]'")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001319
1320 first_serial = ""
1321 if not re.search("-nographic", self.qemu_opt):
1322 first_serial = "-serial mon:vc"
1323 # We always want a ttyS1. Since qemu by default adds a serial
1324 # port when nodefaults is not specified, it seems that all that
1325 # would be needed is to make sure a "-serial" is there. However,
1326 # it appears that when "-serial" is specified, it ignores the
1327 # default serial port that is normally added. So here we make
1328 # sure to add two -serial if there are none. And only one if
1329 # there is one -serial already.
1330 serial_num = len(re.findall("-serial", self.qemu_opt))
1331 if serial_num == 0:
1332 self.qemu_opt += " %s %s" % (first_serial, self.get("QB_SERIAL_OPT"))
1333 elif serial_num == 1:
1334 self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT")
1335
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001336 # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES),
1337 # if not serial or serialtcp options was specified only ttyS0 is created
1338 # and sysvinit shows an error trying to enable ttyS1:
1339 # INIT: Id "S1" respawning too fast: disabled for 5 minutes
1340 serial_num = len(re.findall("-serial", self.qemu_opt))
1341 if serial_num == 0:
Brad Bishop19323692019-04-05 15:28:33 -04001342 if re.search("-nographic", self.qemu_opt) or self.serialstdio:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001343 self.qemu_opt += " -serial mon:stdio -serial null"
1344 else:
1345 self.qemu_opt += " -serial mon:vc -serial null"
1346
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001347 def start_qemu(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001348 import shlex
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001349 if self.kernel:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001350 kernel_opts = "-kernel %s -append '%s %s %s %s'" % (self.kernel, self.kernel_cmdline,
1351 self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'),
1352 self.bootparams)
Brad Bishopc68388fc2019-08-26 01:33:31 -04001353 if self.bios:
1354 kernel_opts += " -bios %s" % self.bios
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001355 if self.dtb:
1356 kernel_opts += " -dtb %s" % self.dtb
1357 else:
1358 kernel_opts = ""
1359 cmd = "%s %s" % (self.qemu_opt, kernel_opts)
Brad Bishop004d4992018-10-02 23:54:45 +02001360 cmds = shlex.split(cmd)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001361 logger.info('Running %s\n' % cmd)
Brad Bishopf86d0552018-12-04 14:18:15 -08001362 pass_fds = []
Brad Bishop08902b02019-08-20 09:16:51 -04001363 if self.taplock_descriptor:
1364 pass_fds = [self.taplock_descriptor.fileno()]
1365 if len(self.portlocks):
1366 for descriptor in self.portlocks.values():
1367 pass_fds.append(descriptor.fileno())
Brad Bishopf86d0552018-12-04 14:18:15 -08001368 process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds)
Brad Bishop004d4992018-10-02 23:54:45 +02001369 self.qemupid = process.pid
1370 retcode = process.wait()
1371 if retcode:
1372 if retcode == -signal.SIGTERM:
1373 logger.info("Qemu terminated by SIGTERM")
1374 else:
1375 logger.error("Failed to run qemu: %s", process.stderr.read().decode())
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001376
1377 def cleanup(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001378 if self.cleaned:
1379 return
1380
1381 # avoid dealing with SIGTERM when cleanup function is running
1382 signal.signal(signal.SIGTERM, signal.SIG_IGN)
1383
1384 logger.info("Cleaning up")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001385 if self.cleantap:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001386 cmd = ('sudo', self.qemuifdown, self.tap, self.bindir_native)
1387 logger.debug('Running %s' % str(cmd))
1388 subprocess.check_call(cmd)
Brad Bishop08902b02019-08-20 09:16:51 -04001389 self.release_taplock()
1390 self.release_portlock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001391
1392 if self.nfs_running:
1393 logger.info("Shutting down the userspace NFS server...")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001394 cmd = ("runqemu-export-rootfs", "stop", self.rootfs)
1395 logger.debug('Running %s' % str(cmd))
1396 subprocess.check_call(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001397
1398 if self.saved_stty:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001399 subprocess.check_call(("stty", self.saved_stty))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001400
1401 if self.clean_nfs_dir:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001402 logger.info('Removing %s' % self.rootfs)
1403 shutil.rmtree(self.rootfs)
1404 shutil.rmtree('%s.pseudo_state' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001405
Brad Bishop004d4992018-10-02 23:54:45 +02001406 self.cleaned = True
1407
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001408 def load_bitbake_env(self, mach=None):
1409 if self.bitbake_e:
1410 return
1411
1412 bitbake = shutil.which('bitbake')
1413 if not bitbake:
1414 return
1415
1416 if not mach:
1417 mach = self.get('MACHINE')
1418
1419 if mach:
1420 cmd = 'MACHINE=%s bitbake -e' % mach
1421 else:
1422 cmd = 'bitbake -e'
1423
1424 logger.info('Running %s...' % cmd)
1425 try:
1426 self.bitbake_e = subprocess.check_output(cmd, shell=True).decode('utf-8')
1427 except subprocess.CalledProcessError as err:
1428 self.bitbake_e = ''
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001429 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 -06001430
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001431 def validate_combos(self):
1432 if (self.fstype in self.vmtypes) and self.kernel:
1433 raise RunQemuError("%s doesn't need kernel %s!" % (self.fstype, self.kernel))
1434
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001435 @property
1436 def bindir_native(self):
1437 result = self.get('STAGING_BINDIR_NATIVE')
1438 if result and os.path.exists(result):
1439 return result
1440
Brad Bishop977dc1a2019-02-06 16:01:43 -05001441 cmd = ('bitbake', 'qemu-helper-native', '-e')
1442 logger.info('Running %s...' % str(cmd))
1443 out = subprocess.check_output(cmd).decode('utf-8')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001444
1445 match = re.search('^STAGING_BINDIR_NATIVE="(.*)"', out, re.M)
1446 if match:
1447 result = match.group(1)
1448 if os.path.exists(result):
1449 self.set('STAGING_BINDIR_NATIVE', result)
1450 return result
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001451 raise RunQemuError("Native sysroot directory %s doesn't exist" % result)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001452 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001453 raise RunQemuError("Can't find STAGING_BINDIR_NATIVE in '%s' output" % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001454
1455
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001456def main():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001457 if "help" in sys.argv or '-h' in sys.argv or '--help' in sys.argv:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001458 print_usage()
1459 return 0
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001460 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001461 config = BaseConfig()
Brad Bishop004d4992018-10-02 23:54:45 +02001462
1463 def sigterm_handler(signum, frame):
1464 logger.info("SIGTERM received")
1465 os.kill(config.qemupid, signal.SIGTERM)
1466 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001467 # Deliberately ignore the return code of 'tput smam'.
1468 subprocess.call(["tput", "smam"])
Brad Bishop004d4992018-10-02 23:54:45 +02001469 signal.signal(signal.SIGTERM, sigterm_handler)
1470
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001471 config.check_args()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001472 config.read_qemuboot()
1473 config.check_and_set()
1474 # Check whether the combos is valid or not
1475 config.validate_combos()
1476 config.print_config()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001477 config.setup_network()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001478 config.setup_rootfs()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001479 config.setup_final()
1480 config.start_qemu()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001481 except RunQemuError as err:
1482 logger.error(err)
1483 return 1
1484 except Exception as err:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001485 import traceback
1486 traceback.print_exc()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001487 return 1
1488 finally:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001489 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001490 # Deliberately ignore the return code of 'tput smam'.
1491 subprocess.call(["tput", "smam"])
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001492
1493if __name__ == "__main__":
1494 sys.exit(main())