blob: a05facd0db6e5d631b36d4189eb940f36d952360 [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
70 gl - enable virgl-based GL acceleration (also needs gtk option)
71 gl-es - enable virgl-based GL acceleration, using OpenGL ES (also needs gtk option)
Brad Bishop19323692019-04-05 15:28:33 -040072 egl-headless - enable headless EGL output; use vnc 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':
440 self.qemu_opt_script += ' -display sdl'
Brad Bishopa34c0302019-09-23 22:34:48 -0400441 elif arg == 'gtk':
442 if 'gl' in sys.argv[1:]:
443 self.qemu_opt_script += ' -vga virtio -display gtk,gl=on'
444 elif 'gl-es' in sys.argv[1:]:
445 self.qemu_opt_script += ' -vga virtio -display gtk,gl=es'
446 else:
447 self.qemu_opt_script += ' -display gtk'
448 elif arg == 'gl' or arg == 'gl-es':
449 # These args are handled inside sdl or gtk blocks above
450 pass
Brad Bishop19323692019-04-05 15:28:33 -0400451 elif arg == 'egl-headless':
452 self.qemu_opt_script += ' -vga virtio -display egl-headless'
453 # As runqemu can be run within bitbake (when using testimage, for example),
454 # we need to ensure that we run host pkg-config, and that it does not
455 # get mis-directed to native build paths set by bitbake.
456 try:
457 del os.environ['PKG_CONFIG_PATH']
458 del os.environ['PKG_CONFIG_DIR']
459 del os.environ['PKG_CONFIG_LIBDIR']
Brad Bishopf3f93bb2019-10-16 14:33:32 -0400460 del os.environ['PKG_CONFIG_SYSROOT_DIR']
Brad Bishop19323692019-04-05 15:28:33 -0400461 except KeyError:
462 pass
463 try:
464 dripath = subprocess.check_output("PATH=/bin:/usr/bin:$PATH pkg-config --variable=dridriverdir dri", shell=True)
465 except subprocess.CalledProcessError as e:
466 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.")
467 os.environ['LIBGL_DRIVERS_PATH'] = dripath.decode('utf-8').strip()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600468 elif arg == 'serial':
469 self.kernel_cmdline_script += ' console=ttyS0'
Brad Bishop19323692019-04-05 15:28:33 -0400470 self.serialconsole = True
471 elif arg == "serialstdio":
472 self.kernel_cmdline_script += ' console=ttyS0'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600473 self.serialstdio = True
474 elif arg == 'audio':
475 logger.info("Enabling audio in qemu")
476 logger.info("Please install sound drivers in linux host")
477 self.audio_enabled = True
478 elif arg == 'kvm':
479 self.kvm_enabled = True
480 elif arg == 'kvm-vhost':
481 self.vhost_enabled = True
482 elif arg == 'slirp':
483 self.slirp_enabled = True
484 elif arg == 'snapshot':
485 self.snapshot = True
486 elif arg == 'publicvnc':
487 self.qemu_opt_script += ' -vnc :0'
488 elif arg.startswith('tcpserial='):
Brad Bishop15ae2502019-06-18 21:44:24 -0400489 self.tcpserial_portnum = '%s' % arg[len('tcpserial='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600490 elif arg.startswith('qemuparams='):
Andrew Geissler99467da2019-02-25 18:54:23 -0600491 self.qemuparams = ' %s' % arg[len('qemuparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600492 elif arg.startswith('bootparams='):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500493 self.bootparams = arg[len('bootparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600494 elif os.path.exists(arg) or (re.search(':', arg) and re.search('/', arg)):
495 self.check_arg_path(os.path.abspath(arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500496 elif re.search(r'-image-|-image$', arg):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600497 # Lazy rootfs
498 self.rootfs = arg
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500499 elif arg.startswith('ovmf'):
500 self.ovmf_bios.append(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600501 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500502 # At last, assume it is the MACHINE
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600503 if (not unknown_arg) or unknown_arg == arg:
504 unknown_arg = arg
505 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500506 raise RunQemuError("Can't handle two unknown args: %s %s\n"
507 "Try 'runqemu help' on how to use it" % \
508 (unknown_arg, arg))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600509 # Check to make sure it is a valid machine
Brad Bishopa5c52ff2018-11-23 10:55:50 +1300510 if unknown_arg and self.get('MACHINE') != unknown_arg:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500511 if self.get('DEPLOY_DIR_IMAGE'):
512 machine = os.path.basename(self.get('DEPLOY_DIR_IMAGE'))
513 if unknown_arg == machine:
514 self.set("MACHINE", machine)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500515
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600516 self.check_arg_machine(unknown_arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500517
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500518 if not (self.get('DEPLOY_DIR_IMAGE') or self.qbconfload):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500519 self.load_bitbake_env()
520 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
521 if s:
522 self.set("DEPLOY_DIR_IMAGE", s.group(1))
523
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600524 def check_kvm(self):
525 """Check kvm and kvm-host"""
526 if not (self.kvm_enabled or self.vhost_enabled):
527 self.qemu_opt_script += ' %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU'))
528 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500529
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600530 if not self.get('QB_CPU_KVM'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500531 raise RunQemuError("QB_CPU_KVM is NULL, this board doesn't support kvm")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500532
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600533 self.qemu_opt_script += ' %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU_KVM'))
534 yocto_kvm_wiki = "https://wiki.yoctoproject.org/wiki/How_to_enable_KVM_for_Poky_qemu"
535 yocto_paravirt_kvm_wiki = "https://wiki.yoctoproject.org/wiki/Running_an_x86_Yocto_Linux_image_under_QEMU_KVM"
536 dev_kvm = '/dev/kvm'
537 dev_vhost = '/dev/vhost-net'
Brad Bishop15ae2502019-06-18 21:44:24 -0400538 if self.qemu_system.endswith(('i386', 'x86_64')):
539 with open('/proc/cpuinfo', 'r') as f:
540 kvm_cap = re.search('vmx|svm', "".join(f.readlines()))
541 if not kvm_cap:
542 logger.error("You are trying to enable KVM on a cpu without VT support.")
543 logger.error("Remove kvm from the command-line, or refer:")
544 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500545
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600546 if not os.path.exists(dev_kvm):
547 logger.error("Missing KVM device. Have you inserted kvm modules?")
548 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500549 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500550
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600551 if os.access(dev_kvm, os.W_OK|os.R_OK):
552 self.qemu_opt_script += ' -enable-kvm'
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500553 if self.get('MACHINE') == "qemux86":
554 # Workaround for broken APIC window on pre 4.15 host kernels which causes boot hangs
555 # See YOCTO #12301
556 # On 64 bit we use x2apic
557 self.kernel_cmdline_script += " clocksource=kvm-clock hpet=disable noapic nolapic"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600558 else:
559 logger.error("You have no read or write permission on /dev/kvm.")
560 logger.error("Please change the ownership of this file as described at:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500561 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500562
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600563 if self.vhost_enabled:
564 if not os.path.exists(dev_vhost):
565 logger.error("Missing virtio net device. Have you inserted vhost-net module?")
566 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500567 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500568
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600569 if not os.access(dev_kvm, os.W_OK|os.R_OK):
570 logger.error("You have no read or write permission on /dev/vhost-net.")
571 logger.error("Please change the ownership of this file as described at:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500572 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500573
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600574 def check_fstype(self):
575 """Check and setup FSTYPE"""
576 if not self.fstype:
577 fstype = self.get('QB_DEFAULT_FSTYPE')
578 if fstype:
579 self.fstype = fstype
580 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500581 raise RunQemuError("FSTYPE is NULL!")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500582
Brad Bishop15ae2502019-06-18 21:44:24 -0400583 # parse QB_FSINFO into dict, e.g. { 'wic': ['no-kernel-in-fs', 'a-flag'], 'ext4': ['another-flag']}
584 wic_fs = False
585 qb_fsinfo = self.get('QB_FSINFO')
586 if qb_fsinfo:
587 qb_fsinfo = qb_fsinfo.split()
588 for fsinfo in qb_fsinfo:
589 try:
590 fstype, fsflag = fsinfo.split(':')
591
592 if fstype == 'wic':
593 if fsflag == 'no-kernel-in-fs':
594 wic_fs = True
595 elif fsflag == 'kernel-in-fs':
596 wic_fs = False
597 else:
598 logger.warn('Unknown flag "%s:%s" in QB_FSINFO', fstype, fsflag)
599 continue
600 else:
601 logger.warn('QB_FSINFO is not supported for image type "%s"', fstype)
602 continue
603
604 if fstype in self.fsinfo:
605 self.fsinfo[fstype].append(fsflag)
606 else:
607 self.fsinfo[fstype] = [fsflag]
608 except Exception:
609 logger.error('Invalid parameter "%s" in QB_FSINFO', fsinfo)
610
611 # treat wic images as vmimages (with kernel) or as fsimages (rootfs only)
612 if wic_fs:
613 self.fstypes = self.fstypes + self.wictypes
614 else:
615 self.vmtypes = self.vmtypes + self.wictypes
616
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600617 def check_rootfs(self):
618 """Check and set rootfs"""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500619
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500620 if self.fstype == "none":
621 return
622
623 if self.get('ROOTFS'):
624 if not self.rootfs:
625 self.rootfs = self.get('ROOTFS')
626 elif self.get('ROOTFS') != self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500627 raise RunQemuError("Maybe conflicted ROOTFS: %s vs %s" % (self.get('ROOTFS'), self.rootfs))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500628
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600629 if self.fstype == 'nfs':
630 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500631
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600632 if self.rootfs and not os.path.exists(self.rootfs):
633 # Lazy rootfs
634 self.rootfs = "%s/%s-%s.%s" % (self.get('DEPLOY_DIR_IMAGE'),
635 self.rootfs, self.get('MACHINE'),
636 self.fstype)
637 elif not self.rootfs:
638 cmd_name = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_NAME'), self.fstype)
639 cmd_link = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'), self.fstype)
640 cmds = (cmd_name, cmd_link)
641 self.rootfs = get_first_file(cmds)
642 if not self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500643 raise RunQemuError("Failed to find rootfs: %s or %s" % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500644
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600645 if not os.path.exists(self.rootfs):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500646 raise RunQemuError("Can't find rootfs: %s" % self.rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500647
Brad Bishop08902b02019-08-20 09:16:51 -0400648 def setup_pkkek1(self):
649 """
650 Extract from PEM certificate the Platform Key and first Key
651 Exchange Key certificate string. The hypervisor needs to provide
652 it in the Type 11 SMBIOS table
653 """
654 pemcert = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), 'OvmfPkKek1.pem')
655 try:
656 with open(pemcert, 'r') as pemfile:
657 key = pemfile.read().replace('\n', ''). \
658 replace('-----BEGIN CERTIFICATE-----', ''). \
659 replace('-----END CERTIFICATE-----', '')
660 self.ovmf_secboot_pkkek1 = key
661
662 except FileNotFoundError:
663 raise RunQemuError("Can't open PEM certificate %s " % pemcert)
664
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500665 def check_ovmf(self):
666 """Check and set full path for OVMF firmware and variable file(s)."""
667
668 for index, ovmf in enumerate(self.ovmf_bios):
669 if os.path.exists(ovmf):
670 continue
671 for suffix in ('qcow2', 'bin'):
672 path = '%s/%s.%s' % (self.get('DEPLOY_DIR_IMAGE'), ovmf, suffix)
673 if os.path.exists(path):
674 self.ovmf_bios[index] = path
Brad Bishop08902b02019-08-20 09:16:51 -0400675 if ovmf.endswith('secboot'):
676 self.setup_pkkek1()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500677 break
678 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500679 raise RunQemuError("Can't find OVMF firmware: %s" % ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500680
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600681 def check_kernel(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400682 """Check and set kernel"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600683 # The vm image doesn't need a kernel
684 if self.fstype in self.vmtypes:
685 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500686
Brad Bishop316dfdd2018-06-25 12:45:53 -0400687 # See if the user supplied a KERNEL option
688 if self.get('KERNEL'):
689 self.kernel = self.get('KERNEL')
690
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500691 # QB_DEFAULT_KERNEL is always a full file path
692 kernel_name = os.path.basename(self.get('QB_DEFAULT_KERNEL'))
693
694 # The user didn't want a kernel to be loaded
Brad Bishop316dfdd2018-06-25 12:45:53 -0400695 if kernel_name == "none" and not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500696 return
697
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600698 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
699 if not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500700 kernel_match_name = "%s/%s" % (deploy_dir_image, kernel_name)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600701 kernel_match_link = "%s/%s" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
702 kernel_startswith = "%s/%s*" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
703 cmds = (kernel_match_name, kernel_match_link, kernel_startswith)
704 self.kernel = get_first_file(cmds)
705 if not self.kernel:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500706 raise RunQemuError('KERNEL not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500707
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600708 if not os.path.exists(self.kernel):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500709 raise RunQemuError("KERNEL %s not found" % self.kernel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500710
Brad Bishop316dfdd2018-06-25 12:45:53 -0400711 def check_dtb(self):
712 """Check and set dtb"""
713 # Did the user specify a device tree?
714 if self.get('DEVICE_TREE'):
715 self.dtb = self.get('DEVICE_TREE')
716 if not os.path.exists(self.dtb):
717 raise RunQemuError('Specified DTB not found: %s' % self.dtb)
718 return
719
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600720 dtb = self.get('QB_DTB')
721 if dtb:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400722 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600723 cmd_match = "%s/%s" % (deploy_dir_image, dtb)
724 cmd_startswith = "%s/%s*" % (deploy_dir_image, dtb)
725 cmd_wild = "%s/*.dtb" % deploy_dir_image
726 cmds = (cmd_match, cmd_startswith, cmd_wild)
727 self.dtb = get_first_file(cmds)
728 if not os.path.exists(self.dtb):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500729 raise RunQemuError('DTB not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500730
Brad Bishopc68388fc2019-08-26 01:33:31 -0400731 def check_bios(self):
732 """Check and set bios"""
733
734 # See if the user supplied a BIOS option
735 if self.get('BIOS'):
736 self.bios = self.get('BIOS')
737
738 # QB_DEFAULT_BIOS is always a full file path
739 bios_name = os.path.basename(self.get('QB_DEFAULT_BIOS'))
740
741 # The user didn't want a bios to be loaded
742 if (bios_name == "" or bios_name == "none") and not self.bios:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600743 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500744
Brad Bishopc68388fc2019-08-26 01:33:31 -0400745 if not self.bios:
746 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
747 self.bios = "%s/%s" % (deploy_dir_image, bios_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500748
Brad Bishopc68388fc2019-08-26 01:33:31 -0400749 if not self.bios:
750 raise RunQemuError('BIOS not found: %s' % bios_match_name)
751
752 if not os.path.exists(self.bios):
753 raise RunQemuError("KERNEL %s not found" % self.bios)
754
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500755
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600756 def check_mem(self):
Andrew Geissler99467da2019-02-25 18:54:23 -0600757 """
758 Both qemu and kernel needs memory settings, so check QB_MEM and set it
759 for both.
760 """
761 s = re.search('-m +([0-9]+)', self.qemuparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600762 if s:
763 self.set('QB_MEM', '-m %s' % s.group(1))
764 elif not self.get('QB_MEM'):
Brad Bishop79641f22019-09-10 07:20:22 -0400765 logger.info('QB_MEM is not set, use 256M by default')
766 self.set('QB_MEM', '-m 256')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500767
Andrew Geissler99467da2019-02-25 18:54:23 -0600768 # Check and remove M or m suffix
769 qb_mem = self.get('QB_MEM')
770 if qb_mem.endswith('M') or qb_mem.endswith('m'):
771 qb_mem = qb_mem[:-1]
772
773 # Add -m prefix it not present
774 if not qb_mem.startswith('-m'):
775 qb_mem = '-m %s' % qb_mem
776
777 self.set('QB_MEM', qb_mem)
778
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800779 mach = self.get('MACHINE')
780 if not mach.startswith('qemumips'):
781 self.kernel_cmdline_script += ' mem=%s' % self.get('QB_MEM').replace('-m','').strip() + 'M'
782
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600783 self.qemu_opt_script += ' %s' % self.get('QB_MEM')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500784
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600785 def check_tcpserial(self):
786 if self.tcpserial_portnum:
Brad Bishop15ae2502019-06-18 21:44:24 -0400787 ports = self.tcpserial_portnum.split(':')
788 port = ports[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600789 if self.get('QB_TCPSERIAL_OPT'):
Brad Bishop15ae2502019-06-18 21:44:24 -0400790 self.qemu_opt_script += ' ' + self.get('QB_TCPSERIAL_OPT').replace('@PORT@', port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600791 else:
Brad Bishop15ae2502019-06-18 21:44:24 -0400792 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
793
794 if len(ports) > 1:
795 for port in ports[1:]:
796 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500797
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600798 def check_and_set(self):
799 """Check configs sanity and set when needed"""
800 self.validate_paths()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500801 if not self.slirp_enabled:
802 check_tun()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600803 # Check audio
804 if self.audio_enabled:
805 if not self.get('QB_AUDIO_DRV'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500806 raise RunQemuError("QB_AUDIO_DRV is NULL, this board doesn't support audio")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600807 if not self.get('QB_AUDIO_OPT'):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800808 logger.warning('QB_AUDIO_OPT is NULL, you may need define it to make audio work')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600809 else:
810 self.qemu_opt_script += ' %s' % self.get('QB_AUDIO_OPT')
811 os.putenv('QEMU_AUDIO_DRV', self.get('QB_AUDIO_DRV'))
812 else:
813 os.putenv('QEMU_AUDIO_DRV', 'none')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500814
Brad Bishop15ae2502019-06-18 21:44:24 -0400815 self.check_qemu_system()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600816 self.check_kvm()
817 self.check_fstype()
818 self.check_rootfs()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500819 self.check_ovmf()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600820 self.check_kernel()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400821 self.check_dtb()
Brad Bishopc68388fc2019-08-26 01:33:31 -0400822 self.check_bios()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600823 self.check_mem()
824 self.check_tcpserial()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500825
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600826 def read_qemuboot(self):
827 if not self.qemuboot:
828 if self.get('DEPLOY_DIR_IMAGE'):
829 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600830 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800831 logger.warning("Can't find qemuboot conf file, DEPLOY_DIR_IMAGE is NULL!")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600832 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500833
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600834 if self.rootfs and not os.path.exists(self.rootfs):
835 # Lazy rootfs
836 machine = self.get('MACHINE')
837 if not machine:
838 machine = os.path.basename(deploy_dir_image)
839 self.qemuboot = "%s/%s-%s.qemuboot.conf" % (deploy_dir_image,
840 self.rootfs, machine)
841 else:
842 cmd = 'ls -t %s/*.qemuboot.conf' % deploy_dir_image
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500843 logger.debug('Running %s...' % cmd)
844 try:
845 qbs = subprocess.check_output(cmd, shell=True).decode('utf-8')
846 except subprocess.CalledProcessError as err:
847 raise RunQemuError(err)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600848 if qbs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500849 for qb in qbs.split():
850 # Don't use initramfs when other choices unless fstype is ramfs
851 if '-initramfs-' in os.path.basename(qb) and self.fstype != 'cpio.gz':
852 continue
853 self.qemuboot = qb
854 break
855 if not self.qemuboot:
856 # Use the first one when no choice
857 self.qemuboot = qbs.split()[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600858 self.qbconfload = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500859
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600860 if not self.qemuboot:
861 # If we haven't found a .qemuboot.conf at this point it probably
862 # doesn't exist, continue without
863 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500864
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600865 if not os.path.exists(self.qemuboot):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500866 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 -0500867
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500868 logger.debug('CONFFILE: %s' % self.qemuboot)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500869
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600870 cf = configparser.ConfigParser()
871 cf.read(self.qemuboot)
872 for k, v in cf.items('config_bsp'):
873 k_upper = k.upper()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500874 if v.startswith("../"):
875 v = os.path.abspath(os.path.dirname(self.qemuboot) + "/" + v)
876 elif v == ".":
877 v = os.path.dirname(self.qemuboot)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600878 self.set(k_upper, v)
879
880 def validate_paths(self):
881 """Ensure all relevant path variables are set"""
882 # When we're started with a *.qemuboot.conf arg assume that image
883 # artefacts are relative to that file, rather than in whatever
884 # directory DEPLOY_DIR_IMAGE in the conf file points to.
885 if self.qbconfload:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500886 imgdir = os.path.realpath(os.path.dirname(self.qemuboot))
887 if imgdir != os.path.realpath(self.get('DEPLOY_DIR_IMAGE')):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600888 logger.info('Setting DEPLOY_DIR_IMAGE to folder containing %s (%s)' % (self.qemuboot, imgdir))
889 self.set('DEPLOY_DIR_IMAGE', imgdir)
890
891 # If the STAGING_*_NATIVE directories from the config file don't exist
892 # and we're in a sourced OE build directory try to extract the paths
893 # from `bitbake -e`
894 havenative = os.path.exists(self.get('STAGING_DIR_NATIVE')) and \
895 os.path.exists(self.get('STAGING_BINDIR_NATIVE'))
896
897 if not havenative:
898 if not self.bitbake_e:
899 self.load_bitbake_env()
900
901 if self.bitbake_e:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500902 native_vars = ['STAGING_DIR_NATIVE']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600903 for nv in native_vars:
904 s = re.search('^%s="(.*)"' % nv, self.bitbake_e, re.M)
905 if s and s.group(1) != self.get(nv):
906 logger.info('Overriding conf file setting of %s to %s from Bitbake environment' % (nv, s.group(1)))
907 self.set(nv, s.group(1))
908 else:
909 # when we're invoked from a running bitbake instance we won't
910 # be able to call `bitbake -e`, then try:
911 # - get OE_TMPDIR from environment and guess paths based on it
912 # - get OECORE_NATIVE_SYSROOT from environment (for sdk)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500913 tmpdir = self.get('OE_TMPDIR')
914 oecore_native_sysroot = self.get('OECORE_NATIVE_SYSROOT')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600915 if tmpdir:
916 logger.info('Setting STAGING_DIR_NATIVE and STAGING_BINDIR_NATIVE relative to OE_TMPDIR (%s)' % tmpdir)
917 hostos, _, _, _, machine = os.uname()
918 buildsys = '%s-%s' % (machine, hostos.lower())
919 staging_dir_native = '%s/sysroots/%s' % (tmpdir, buildsys)
920 self.set('STAGING_DIR_NATIVE', staging_dir_native)
921 elif oecore_native_sysroot:
922 logger.info('Setting STAGING_DIR_NATIVE to OECORE_NATIVE_SYSROOT (%s)' % oecore_native_sysroot)
923 self.set('STAGING_DIR_NATIVE', oecore_native_sysroot)
924 if self.get('STAGING_DIR_NATIVE'):
925 # we have to assume that STAGING_BINDIR_NATIVE is at usr/bin
926 staging_bindir_native = '%s/usr/bin' % self.get('STAGING_DIR_NATIVE')
927 logger.info('Setting STAGING_BINDIR_NATIVE to %s' % staging_bindir_native)
928 self.set('STAGING_BINDIR_NATIVE', '%s/usr/bin' % self.get('STAGING_DIR_NATIVE'))
929
930 def print_config(self):
931 logger.info('Continuing with the following parameters:\n')
932 if not self.fstype in self.vmtypes:
933 print('KERNEL: [%s]' % self.kernel)
Brad Bishopc68388fc2019-08-26 01:33:31 -0400934 if self.bios:
935 print('BIOS: [%s]' % self.bios)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600936 if self.dtb:
937 print('DTB: [%s]' % self.dtb)
938 print('MACHINE: [%s]' % self.get('MACHINE'))
Brad Bishop15ae2502019-06-18 21:44:24 -0400939 try:
940 fstype_flags = ' (' + ', '.join(self.fsinfo[self.fstype]) + ')'
941 except KeyError:
942 fstype_flags = ''
943 print('FSTYPE: [%s%s]' % (self.fstype, fstype_flags))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600944 if self.fstype == 'nfs':
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500945 print('NFS_DIR: [%s]' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600946 else:
947 print('ROOTFS: [%s]' % self.rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500948 if self.ovmf_bios:
949 print('OVMF: %s' % self.ovmf_bios)
Brad Bishop08902b02019-08-20 09:16:51 -0400950 if (self.ovmf_secboot_pkkek1):
951 print('SECBOOT PKKEK1: [%s...]' % self.ovmf_secboot_pkkek1[0:100])
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600952 print('CONFFILE: [%s]' % self.qemuboot)
953 print('')
954
955 def setup_nfs(self):
956 if not self.nfs_server:
957 if self.slirp_enabled:
958 self.nfs_server = '10.0.2.2'
959 else:
960 self.nfs_server = '192.168.7.1'
961
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500962 # Figure out a new nfs_instance to allow multiple qemus running.
Brad Bishop977dc1a2019-02-06 16:01:43 -0500963 ps = subprocess.check_output(("ps", "auxww")).decode('utf-8')
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500964 pattern = '/bin/unfsd .* -i .*\.pid -e .*/exports([0-9]+) '
965 all_instances = re.findall(pattern, ps, re.M)
966 if all_instances:
967 all_instances.sort(key=int)
968 self.nfs_instance = int(all_instances.pop()) + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600969
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500970 nfsd_port = 3049 + 2 * self.nfs_instance
971 mountd_port = 3048 + 2 * self.nfs_instance
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600972
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500973 # Export vars for runqemu-export-rootfs
974 export_dict = {
975 'NFS_INSTANCE': self.nfs_instance,
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500976 'NFSD_PORT': nfsd_port,
977 'MOUNTD_PORT': mountd_port,
978 }
979 for k, v in export_dict.items():
980 # Use '%s' since they are integers
981 os.putenv(k, '%s' % v)
982
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500983 self.unfs_opts="nfsvers=3,port=%s,udp,mountport=%s" % (nfsd_port, mountd_port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600984
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500985 # Extract .tar.bz2 or .tar.bz if no nfs dir
986 if not (self.rootfs and os.path.isdir(self.rootfs)):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600987 src_prefix = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'))
988 dest = "%s-nfsroot" % src_prefix
989 if os.path.exists('%s.pseudo_state' % dest):
990 logger.info('Use %s as NFS_DIR' % dest)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500991 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600992 else:
993 src = ""
994 src1 = '%s.tar.bz2' % src_prefix
995 src2 = '%s.tar.gz' % src_prefix
996 if os.path.exists(src1):
997 src = src1
998 elif os.path.exists(src2):
999 src = src2
1000 if not src:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001001 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 -06001002 logger.info('NFS_DIR not found, extracting %s to %s' % (src, dest))
Brad Bishop977dc1a2019-02-06 16:01:43 -05001003 cmd = ('runqemu-extract-sdk', src, dest)
1004 logger.info('Running %s...' % str(cmd))
1005 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001006 raise RunQemuError('Failed to run %s' % cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001007 self.clean_nfs_dir = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001008 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001009
1010 # Start the userspace NFS server
Brad Bishop977dc1a2019-02-06 16:01:43 -05001011 cmd = ('runqemu-export-rootfs', 'start', self.rootfs)
1012 logger.info('Running %s...' % str(cmd))
1013 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001014 raise RunQemuError('Failed to run %s' % cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001015
1016 self.nfs_running = True
1017
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001018 def setup_slirp(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001019 """Setup user networking"""
1020
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001021 if self.fstype == 'nfs':
1022 self.setup_nfs()
1023 self.kernel_cmdline_script += ' ip=dhcp'
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001024 # Port mapping
1025 hostfwd = ",hostfwd=tcp::2222-:22,hostfwd=tcp::2323-:23"
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001026 qb_slirp_opt_default = "-netdev user,id=net0%s,tftp=%s" % (hostfwd, self.get('DEPLOY_DIR_IMAGE'))
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001027 qb_slirp_opt = self.get('QB_SLIRP_OPT') or qb_slirp_opt_default
1028 # Figure out the port
1029 ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt)
1030 ports = [int(i) for i in ports]
1031 mac = 2
Brad Bishop08902b02019-08-20 09:16:51 -04001032
1033 lockdir = "/tmp/qemu-port-locks"
1034 if not os.path.exists(lockdir):
1035 # There might be a race issue when multi runqemu processess are
1036 # running at the same time.
1037 try:
1038 os.mkdir(lockdir)
1039 os.chmod(lockdir, 0o777)
1040 except FileExistsError:
1041 pass
1042
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001043 # Find a free port to avoid conflicts
1044 for p in ports[:]:
1045 p_new = p
Brad Bishop08902b02019-08-20 09:16:51 -04001046 while not self.check_free_port('localhost', p_new, lockdir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001047 p_new += 1
1048 mac += 1
1049 while p_new in ports:
1050 p_new += 1
1051 mac += 1
1052 if p != p_new:
1053 ports.append(p_new)
1054 qb_slirp_opt = re.sub(':%s-' % p, ':%s-' % p_new, qb_slirp_opt)
1055 logger.info("Port forward changed: %s -> %s" % (p, p_new))
1056 mac = "%s%02x" % (self.mac_slirp, mac)
1057 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qb_slirp_opt))
1058 # Print out port foward
1059 hostfwd = re.findall('(hostfwd=[^,]*)', qb_slirp_opt)
1060 if hostfwd:
1061 logger.info('Port forward: %s' % ' '.join(hostfwd))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001062
1063 def setup_tap(self):
1064 """Setup tap"""
1065
1066 # This file is created when runqemu-gen-tapdevs creates a bank of tap
1067 # devices, indicating that the user should not bring up new ones using
1068 # sudo.
1069 nosudo_flag = '/etc/runqemu-nosudo'
1070 self.qemuifup = shutil.which('runqemu-ifup')
1071 self.qemuifdown = shutil.which('runqemu-ifdown')
1072 ip = shutil.which('ip')
1073 lockdir = "/tmp/qemu-tap-locks"
1074
1075 if not (self.qemuifup and self.qemuifdown and ip):
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001076 logger.error("runqemu-ifup: %s" % self.qemuifup)
1077 logger.error("runqemu-ifdown: %s" % self.qemuifdown)
1078 logger.error("ip: %s" % ip)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001079 raise OEPathError("runqemu-ifup, runqemu-ifdown or ip not found")
1080
1081 if not os.path.exists(lockdir):
1082 # There might be a race issue when multi runqemu processess are
1083 # running at the same time.
1084 try:
1085 os.mkdir(lockdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001086 os.chmod(lockdir, 0o777)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001087 except FileExistsError:
1088 pass
1089
Brad Bishop977dc1a2019-02-06 16:01:43 -05001090 cmd = (ip, 'link')
1091 logger.debug('Running %s...' % str(cmd))
1092 ip_link = subprocess.check_output(cmd).decode('utf-8')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001093 # Matches line like: 6: tap0: <foo>
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001094 possibles = re.findall('^[0-9]+: +(tap[0-9]+): <.*', ip_link, re.M)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001095 tap = ""
1096 for p in possibles:
1097 lockfile = os.path.join(lockdir, p)
1098 if os.path.exists('%s.skip' % lockfile):
1099 logger.info('Found %s.skip, skipping %s' % (lockfile, p))
1100 continue
Brad Bishop08902b02019-08-20 09:16:51 -04001101 self.taplock = lockfile + '.lock'
1102 if self.acquire_taplock(error=False):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001103 tap = p
1104 logger.info("Using preconfigured tap device %s" % tap)
1105 logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap))
1106 break
1107
1108 if not tap:
1109 if os.path.exists(nosudo_flag):
1110 logger.error("Error: There are no available tap devices to use for networking,")
1111 logger.error("and I see %s exists, so I am not going to try creating" % nosudo_flag)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001112 raise RunQemuError("a new one with sudo.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001113
1114 gid = os.getgid()
1115 uid = os.getuid()
1116 logger.info("Setting up tap interface under sudo")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001117 cmd = ('sudo', self.qemuifup, str(uid), str(gid), self.bindir_native)
1118 tap = subprocess.check_output(cmd).decode('utf-8').strip()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001119 lockfile = os.path.join(lockdir, tap)
Brad Bishop08902b02019-08-20 09:16:51 -04001120 self.taplock = lockfile + '.lock'
1121 self.acquire_taplock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001122 self.cleantap = True
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001123 logger.debug('Created tap: %s' % tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001124
1125 if not tap:
1126 logger.error("Failed to setup tap device. Run runqemu-gen-tapdevs to manually create.")
1127 return 1
1128 self.tap = tap
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001129 tapnum = int(tap[3:])
1130 gateway = tapnum * 2 + 1
1131 client = gateway + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001132 if self.fstype == 'nfs':
1133 self.setup_nfs()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001134 netconf = "192.168.7.%s::192.168.7.%s:255.255.255.0" % (client, gateway)
1135 logger.info("Network configuration: %s", netconf)
1136 self.kernel_cmdline_script += " ip=%s" % netconf
1137 mac = "%s%02x" % (self.mac_tap, client)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001138 qb_tap_opt = self.get('QB_TAP_OPT')
1139 if qb_tap_opt:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001140 qemu_tap_opt = qb_tap_opt.replace('@TAP@', tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001141 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001142 qemu_tap_opt = "-netdev tap,id=net0,ifname=%s,script=no,downscript=no" % (self.tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001143
1144 if self.vhost_enabled:
1145 qemu_tap_opt += ',vhost=on'
1146
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001147 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qemu_tap_opt))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001148
1149 def setup_network(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001150 if self.get('QB_NET') == 'none':
1151 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001152 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001153 self.saved_stty = subprocess.check_output(("stty", "-g")).decode('utf-8').strip()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001154 self.network_device = self.get('QB_NETWORK_DEVICE') or self.network_device
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001155 if self.slirp_enabled:
1156 self.setup_slirp()
1157 else:
1158 self.setup_tap()
1159
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001160 def setup_rootfs(self):
1161 if self.get('QB_ROOTFS') == 'none':
1162 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001163 if 'wic.' in self.fstype:
1164 self.fstype = self.fstype[4:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001165 rootfs_format = self.fstype if self.fstype in ('vmdk', 'qcow2', 'vdi') else 'raw'
1166
1167 qb_rootfs_opt = self.get('QB_ROOTFS_OPT')
1168 if qb_rootfs_opt:
1169 self.rootfs_options = qb_rootfs_opt.replace('@ROOTFS@', self.rootfs)
1170 else:
1171 self.rootfs_options = '-drive file=%s,if=virtio,format=%s' % (self.rootfs, rootfs_format)
1172
1173 if self.fstype in ('cpio.gz', 'cpio'):
1174 self.kernel_cmdline = 'root=/dev/ram0 rw debugshell'
1175 self.rootfs_options = '-initrd %s' % self.rootfs
1176 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001177 vm_drive = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001178 if self.fstype in self.vmtypes:
1179 if self.fstype == 'iso':
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001180 vm_drive = '-drive file=%s,if=virtio,media=cdrom' % self.rootfs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001181 elif self.get('QB_DRIVE_TYPE'):
1182 drive_type = self.get('QB_DRIVE_TYPE')
1183 if drive_type.startswith("/dev/sd"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001184 logger.info('Using scsi drive')
1185 vm_drive = '-drive if=none,id=hd,file=%s,format=%s -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd' \
1186 % (self.rootfs, rootfs_format)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001187 elif drive_type.startswith("/dev/hd"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001188 logger.info('Using ide drive')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001189 vm_drive = "-drive file=%s,format=%s" % (self.rootfs, rootfs_format)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001190 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001191 # virtio might have been selected explicitly (just use it), or
1192 # is used as fallback (then warn about that).
1193 if not drive_type.startswith("/dev/vd"):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001194 logger.warning("Unknown QB_DRIVE_TYPE: %s" % drive_type)
1195 logger.warning("Failed to figure out drive type, consider define or fix QB_DRIVE_TYPE")
1196 logger.warning('Trying to use virtio block drive')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001197 vm_drive = '-drive if=virtio,file=%s,format=%s' % (self.rootfs, rootfs_format)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001198
1199 # All branches above set vm_drive.
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001200 self.rootfs_options = '%s -no-reboot' % vm_drive
Brad Bishopf3f93bb2019-10-16 14:33:32 -04001201 self.kernel_cmdline = 'root=%s rw' % (self.get('QB_KERNEL_ROOT'))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001202
1203 if self.fstype == 'nfs':
1204 self.rootfs_options = ''
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001205 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 -04001206 self.kernel_cmdline = 'root=%s rw' % k_root
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001207
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001208 if self.fstype == 'none':
1209 self.rootfs_options = ''
1210
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001211 self.set('ROOTFS_OPTIONS', self.rootfs_options)
1212
1213 def guess_qb_system(self):
1214 """attempt to determine the appropriate qemu-system binary"""
1215 mach = self.get('MACHINE')
1216 if not mach:
1217 search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*'
1218 if self.rootfs:
1219 match = re.match(search, self.rootfs)
1220 if match:
1221 mach = match.group(1)
1222 elif self.kernel:
1223 match = re.match(search, self.kernel)
1224 if match:
1225 mach = match.group(1)
1226
1227 if not mach:
1228 return None
1229
1230 if mach == 'qemuarm':
1231 qbsys = 'arm'
1232 elif mach == 'qemuarm64':
1233 qbsys = 'aarch64'
1234 elif mach == 'qemux86':
1235 qbsys = 'i386'
1236 elif mach == 'qemux86-64':
1237 qbsys = 'x86_64'
1238 elif mach == 'qemuppc':
1239 qbsys = 'ppc'
1240 elif mach == 'qemumips':
1241 qbsys = 'mips'
1242 elif mach == 'qemumips64':
1243 qbsys = 'mips64'
1244 elif mach == 'qemumipsel':
1245 qbsys = 'mipsel'
1246 elif mach == 'qemumips64el':
1247 qbsys = 'mips64el'
Brad Bishop316dfdd2018-06-25 12:45:53 -04001248 elif mach == 'qemuriscv64':
1249 qbsys = 'riscv64'
1250 elif mach == 'qemuriscv32':
1251 qbsys = 'riscv32'
Brad Bishop004d4992018-10-02 23:54:45 +02001252 else:
1253 logger.error("Unable to determine QEMU PC System emulator for %s machine." % mach)
1254 logger.error("As %s is not among valid QEMU machines such as," % mach)
1255 logger.error("qemux86-64, qemux86, qemuarm64, qemuarm, qemumips64, qemumips64el, qemumipsel, qemumips, qemuppc")
1256 raise RunQemuError("Set qb_system_name with suitable QEMU PC System emulator in .*qemuboot.conf.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001257
1258 return 'qemu-system-%s' % qbsys
1259
Brad Bishop15ae2502019-06-18 21:44:24 -04001260 def check_qemu_system(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001261 qemu_system = self.get('QB_SYSTEM_NAME')
1262 if not qemu_system:
1263 qemu_system = self.guess_qb_system()
1264 if not qemu_system:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001265 raise RunQemuError("Failed to boot, QB_SYSTEM_NAME is NULL!")
Brad Bishop15ae2502019-06-18 21:44:24 -04001266 self.qemu_system = qemu_system
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001267
Brad Bishop15ae2502019-06-18 21:44:24 -04001268 def setup_final(self):
1269 qemu_bin = os.path.join(self.bindir_native, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001270
1271 # It is possible to have qemu-native in ASSUME_PROVIDED, and it won't
1272 # find QEMU in sysroot, it needs to use host's qemu.
1273 if not os.path.exists(qemu_bin):
1274 logger.info("QEMU binary not found in %s, trying host's QEMU" % qemu_bin)
1275 for path in (os.environ['PATH'] or '').split(':'):
Brad Bishop15ae2502019-06-18 21:44:24 -04001276 qemu_bin_tmp = os.path.join(path, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001277 logger.info("Trying: %s" % qemu_bin_tmp)
1278 if os.path.exists(qemu_bin_tmp):
1279 qemu_bin = qemu_bin_tmp
1280 if not os.path.isabs(qemu_bin):
1281 qemu_bin = os.path.abspath(qemu_bin)
1282 logger.info("Using host's QEMU: %s" % qemu_bin)
1283 break
1284
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001285 if not os.access(qemu_bin, os.X_OK):
1286 raise OEPathError("No QEMU binary '%s' could be found" % qemu_bin)
1287
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001288 self.qemu_opt = "%s %s %s %s" % (qemu_bin, self.get('NETWORK_CMD'), self.get('ROOTFS_OPTIONS'), self.get('QB_OPT_APPEND'))
1289
1290 for ovmf in self.ovmf_bios:
1291 format = ovmf.rsplit('.', 1)[-1]
1292 self.qemu_opt += ' -drive if=pflash,format=%s,file=%s' % (format, ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001293
1294 self.qemu_opt += ' ' + self.qemu_opt_script
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001295
Brad Bishop08902b02019-08-20 09:16:51 -04001296 if self.ovmf_secboot_pkkek1:
1297 # Provide the Platform Key and first Key Exchange Key certificate as an
1298 # OEM string in the SMBIOS Type 11 table. Prepend the certificate string
1299 # with "application prefix" of the EnrollDefaultKeys.efi application
1300 self.qemu_opt += ' -smbios type=11,value=4e32566d-8e9e-4f52-81d3-5bb9715f9727:' \
1301 + self.ovmf_secboot_pkkek1
1302
Andrew Geissler99467da2019-02-25 18:54:23 -06001303 # Append qemuparams to override previous settings
1304 if self.qemuparams:
1305 self.qemu_opt += ' ' + self.qemuparams
1306
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001307 if self.snapshot:
1308 self.qemu_opt += " -snapshot"
1309
Brad Bishop19323692019-04-05 15:28:33 -04001310 if self.serialconsole:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001311 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001312 subprocess.check_call(("stty", "intr", "^]"))
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001313 logger.info("Interrupt character is '^]'")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001314
1315 first_serial = ""
1316 if not re.search("-nographic", self.qemu_opt):
1317 first_serial = "-serial mon:vc"
1318 # We always want a ttyS1. Since qemu by default adds a serial
1319 # port when nodefaults is not specified, it seems that all that
1320 # would be needed is to make sure a "-serial" is there. However,
1321 # it appears that when "-serial" is specified, it ignores the
1322 # default serial port that is normally added. So here we make
1323 # sure to add two -serial if there are none. And only one if
1324 # there is one -serial already.
1325 serial_num = len(re.findall("-serial", self.qemu_opt))
1326 if serial_num == 0:
1327 self.qemu_opt += " %s %s" % (first_serial, self.get("QB_SERIAL_OPT"))
1328 elif serial_num == 1:
1329 self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT")
1330
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001331 # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES),
1332 # if not serial or serialtcp options was specified only ttyS0 is created
1333 # and sysvinit shows an error trying to enable ttyS1:
1334 # INIT: Id "S1" respawning too fast: disabled for 5 minutes
1335 serial_num = len(re.findall("-serial", self.qemu_opt))
1336 if serial_num == 0:
Brad Bishop19323692019-04-05 15:28:33 -04001337 if re.search("-nographic", self.qemu_opt) or self.serialstdio:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001338 self.qemu_opt += " -serial mon:stdio -serial null"
1339 else:
1340 self.qemu_opt += " -serial mon:vc -serial null"
1341
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001342 def start_qemu(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001343 import shlex
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001344 if self.kernel:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001345 kernel_opts = "-kernel %s -append '%s %s %s %s'" % (self.kernel, self.kernel_cmdline,
1346 self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'),
1347 self.bootparams)
Brad Bishopc68388fc2019-08-26 01:33:31 -04001348 if self.bios:
1349 kernel_opts += " -bios %s" % self.bios
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001350 if self.dtb:
1351 kernel_opts += " -dtb %s" % self.dtb
1352 else:
1353 kernel_opts = ""
1354 cmd = "%s %s" % (self.qemu_opt, kernel_opts)
Brad Bishop004d4992018-10-02 23:54:45 +02001355 cmds = shlex.split(cmd)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001356 logger.info('Running %s\n' % cmd)
Brad Bishopf86d0552018-12-04 14:18:15 -08001357 pass_fds = []
Brad Bishop08902b02019-08-20 09:16:51 -04001358 if self.taplock_descriptor:
1359 pass_fds = [self.taplock_descriptor.fileno()]
1360 if len(self.portlocks):
1361 for descriptor in self.portlocks.values():
1362 pass_fds.append(descriptor.fileno())
Brad Bishopf86d0552018-12-04 14:18:15 -08001363 process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds)
Brad Bishop004d4992018-10-02 23:54:45 +02001364 self.qemupid = process.pid
1365 retcode = process.wait()
1366 if retcode:
1367 if retcode == -signal.SIGTERM:
1368 logger.info("Qemu terminated by SIGTERM")
1369 else:
1370 logger.error("Failed to run qemu: %s", process.stderr.read().decode())
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001371
1372 def cleanup(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001373 if self.cleaned:
1374 return
1375
1376 # avoid dealing with SIGTERM when cleanup function is running
1377 signal.signal(signal.SIGTERM, signal.SIG_IGN)
1378
1379 logger.info("Cleaning up")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001380 if self.cleantap:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001381 cmd = ('sudo', self.qemuifdown, self.tap, self.bindir_native)
1382 logger.debug('Running %s' % str(cmd))
1383 subprocess.check_call(cmd)
Brad Bishop08902b02019-08-20 09:16:51 -04001384 self.release_taplock()
1385 self.release_portlock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001386
1387 if self.nfs_running:
1388 logger.info("Shutting down the userspace NFS server...")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001389 cmd = ("runqemu-export-rootfs", "stop", self.rootfs)
1390 logger.debug('Running %s' % str(cmd))
1391 subprocess.check_call(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001392
1393 if self.saved_stty:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001394 subprocess.check_call(("stty", self.saved_stty))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001395
1396 if self.clean_nfs_dir:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001397 logger.info('Removing %s' % self.rootfs)
1398 shutil.rmtree(self.rootfs)
1399 shutil.rmtree('%s.pseudo_state' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001400
Brad Bishop004d4992018-10-02 23:54:45 +02001401 self.cleaned = True
1402
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001403 def load_bitbake_env(self, mach=None):
1404 if self.bitbake_e:
1405 return
1406
1407 bitbake = shutil.which('bitbake')
1408 if not bitbake:
1409 return
1410
1411 if not mach:
1412 mach = self.get('MACHINE')
1413
1414 if mach:
1415 cmd = 'MACHINE=%s bitbake -e' % mach
1416 else:
1417 cmd = 'bitbake -e'
1418
1419 logger.info('Running %s...' % cmd)
1420 try:
1421 self.bitbake_e = subprocess.check_output(cmd, shell=True).decode('utf-8')
1422 except subprocess.CalledProcessError as err:
1423 self.bitbake_e = ''
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001424 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 -06001425
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001426 def validate_combos(self):
1427 if (self.fstype in self.vmtypes) and self.kernel:
1428 raise RunQemuError("%s doesn't need kernel %s!" % (self.fstype, self.kernel))
1429
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001430 @property
1431 def bindir_native(self):
1432 result = self.get('STAGING_BINDIR_NATIVE')
1433 if result and os.path.exists(result):
1434 return result
1435
Brad Bishop977dc1a2019-02-06 16:01:43 -05001436 cmd = ('bitbake', 'qemu-helper-native', '-e')
1437 logger.info('Running %s...' % str(cmd))
1438 out = subprocess.check_output(cmd).decode('utf-8')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001439
1440 match = re.search('^STAGING_BINDIR_NATIVE="(.*)"', out, re.M)
1441 if match:
1442 result = match.group(1)
1443 if os.path.exists(result):
1444 self.set('STAGING_BINDIR_NATIVE', result)
1445 return result
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001446 raise RunQemuError("Native sysroot directory %s doesn't exist" % result)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001447 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001448 raise RunQemuError("Can't find STAGING_BINDIR_NATIVE in '%s' output" % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001449
1450
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001451def main():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001452 if "help" in sys.argv or '-h' in sys.argv or '--help' in sys.argv:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001453 print_usage()
1454 return 0
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001455 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001456 config = BaseConfig()
Brad Bishop004d4992018-10-02 23:54:45 +02001457
1458 def sigterm_handler(signum, frame):
1459 logger.info("SIGTERM received")
1460 os.kill(config.qemupid, signal.SIGTERM)
1461 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001462 # Deliberately ignore the return code of 'tput smam'.
1463 subprocess.call(["tput", "smam"])
Brad Bishop004d4992018-10-02 23:54:45 +02001464 signal.signal(signal.SIGTERM, sigterm_handler)
1465
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001466 config.check_args()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001467 config.read_qemuboot()
1468 config.check_and_set()
1469 # Check whether the combos is valid or not
1470 config.validate_combos()
1471 config.print_config()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001472 config.setup_network()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001473 config.setup_rootfs()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001474 config.setup_final()
1475 config.start_qemu()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001476 except RunQemuError as err:
1477 logger.error(err)
1478 return 1
1479 except Exception as err:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001480 import traceback
1481 traceback.print_exc()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001482 return 1
1483 finally:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001484 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001485 # Deliberately ignore the return code of 'tput smam'.
1486 subprocess.call(["tput", "smam"])
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001487
1488if __name__ == "__main__":
1489 sys.exit(main())