blob: 4079f2b17d8d27a9ea749b97c84f0d87381283c9 [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
62 ROOTFS - the rootfs image file or nfsroot directory to use
Brad Bishop316dfdd2018-06-25 12:45:53 -040063 DEVICE_TREE - the device tree blob to use
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050064 MACHINE - the machine name (optional, autodetected from KERNEL filename if unspecified)
65 Simplified QEMU command-line options can be passed with:
Patrick Williamsc0f7c042017-02-23 20:41:17 -060066 nographic - disable video console
Brad Bishop19323692019-04-05 15:28:33 -040067 sdl - choose the SDL frontend instead of the Gtk+ default
68 gtk-gl - enable virgl-based GL acceleration using Gtk+ frontend
69 gtk-gl-es - enable virgl-based GL acceleration, using OpenGL ES and Gtk+ frontend
70 egl-headless - enable headless EGL output; use vnc or spice to see it
Patrick Williamsc0f7c042017-02-23 20:41:17 -060071 serial - enable a serial console on /dev/ttyS0
Brad Bishop19323692019-04-05 15:28:33 -040072 serialstdio - enable a serial console on the console (regardless of graphics mode)
Patrick Williamsc0f7c042017-02-23 20:41:17 -060073 slirp - enable user networking, no root privileges is required
74 kvm - enable KVM when running x86/x86_64 (VT-capable CPU required)
75 kvm-vhost - enable KVM with vhost when running x86/x86_64 (VT-capable CPU required)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050076 publicvnc - enable a VNC server open to all hosts
Patrick Williamsc0f7c042017-02-23 20:41:17 -060077 audio - enable audio
Brad Bishop6e60e8b2018-02-01 10:27:11 -050078 [*/]ovmf* - OVMF firmware file or base name for booting with UEFI
Patrick Williamsc0f7c042017-02-23 20:41:17 -060079 tcpserial=<port> - specify tcp serial port number
80 biosdir=<dir> - specify custom bios dir
81 biosfilename=<filename> - specify bios filename
82 qemuparams=<xyz> - specify custom parameters to QEMU
83 bootparams=<xyz> - specify custom kernel parameters during boot
Brad Bishop6e60e8b2018-02-01 10:27:11 -050084 help, -h, --help: print this text
Brad Bishopd7bf8c12018-02-25 22:55:05 -050085 -d, --debug: Enable debug output
86 -q, --quite: Hide most output except error messages
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050087
88Examples:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050089 runqemu
Patrick Williamsc0f7c042017-02-23 20:41:17 -060090 runqemu qemuarm
91 runqemu tmp/deploy/images/qemuarm
Brad Bishop6e60e8b2018-02-01 10:27:11 -050092 runqemu tmp/deploy/images/qemux86/<qemuboot.conf>
Patrick Williamsc0f7c042017-02-23 20:41:17 -060093 runqemu qemux86-64 core-image-sato ext4
94 runqemu qemux86-64 wic-image-minimal wic
95 runqemu path/to/bzImage-qemux86.bin path/to/nfsrootdir/ serial
Brad Bishopd7bf8c12018-02-25 22:55:05 -050096 runqemu qemux86 iso/hddimg/wic.vmdk/wic.qcow2/wic.vdi/ramfs/cpio.gz...
Patrick Williamsc0f7c042017-02-23 20:41:17 -060097 runqemu qemux86 qemuparams="-m 256"
98 runqemu qemux86 bootparams="psplash=false"
Patrick Williamsc0f7c042017-02-23 20:41:17 -060099 runqemu path/to/<image>-<machine>.wic
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500100 runqemu path/to/<image>-<machine>.wic.vmdk
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600101""")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500102
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600103def check_tun():
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500104 """Check /dev/net/tun"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600105 dev_tun = '/dev/net/tun'
106 if not os.path.exists(dev_tun):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500107 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 -0500108
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600109 if not os.access(dev_tun, os.W_OK):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500110 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 -0500111
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600112def get_first_file(cmds):
113 """Return first file found in wildcard cmds"""
114 for cmd in cmds:
115 all_files = glob.glob(cmd)
116 if all_files:
117 for f in all_files:
118 if not os.path.isdir(f):
119 return f
120 return ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500121
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500122def check_free_port(host, port):
123 """ Check whether the port is free or not """
124 import socket
125 from contextlib import closing
126
127 with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
128 if sock.connect_ex((host, port)) == 0:
129 # Port is open, so not free
130 return False
131 else:
132 # Port is not open, so free
133 return True
134
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600135class BaseConfig(object):
136 def __init__(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500137 # The self.d saved vars from self.set(), part of them are from qemuboot.conf
138 self.d = {'QB_KERNEL_ROOT': '/dev/vda'}
139
140 # Supported env vars, add it here if a var can be got from env,
141 # and don't use os.getenv in the code.
142 self.env_vars = ('MACHINE',
143 'ROOTFS',
144 'KERNEL',
Brad Bishop316dfdd2018-06-25 12:45:53 -0400145 'DEVICE_TREE',
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500146 'DEPLOY_DIR_IMAGE',
147 'OE_TMPDIR',
148 'OECORE_NATIVE_SYSROOT',
149 )
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500150
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600151 self.qemu_opt = ''
152 self.qemu_opt_script = ''
Andrew Geissler99467da2019-02-25 18:54:23 -0600153 self.qemuparams = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600154 self.clean_nfs_dir = False
155 self.nfs_server = ''
156 self.rootfs = ''
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500157 # File name(s) of a OVMF firmware file or variable store,
158 # to be added with -drive if=pflash.
159 # Found in the same places as the rootfs, with or without one of
160 # these suffices: qcow2, bin.
161 # Setting one also adds "-vga std" because that is all that
162 # OVMF supports.
163 self.ovmf_bios = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600164 self.qemuboot = ''
165 self.qbconfload = False
166 self.kernel = ''
167 self.kernel_cmdline = ''
168 self.kernel_cmdline_script = ''
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500169 self.bootparams = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600170 self.dtb = ''
171 self.fstype = ''
172 self.kvm_enabled = False
173 self.vhost_enabled = False
174 self.slirp_enabled = False
175 self.nfs_instance = 0
176 self.nfs_running = False
Brad Bishop19323692019-04-05 15:28:33 -0400177 self.serialconsole = False
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600178 self.serialstdio = False
179 self.cleantap = False
180 self.saved_stty = ''
181 self.audio_enabled = False
182 self.tcpserial_portnum = ''
183 self.custombiosdir = ''
184 self.lock = ''
Brad Bishopf86d0552018-12-04 14:18:15 -0800185 self.lock_descriptor = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600186 self.bitbake_e = ''
187 self.snapshot = False
Brad Bishop15ae2502019-06-18 21:44:24 -0400188 self.wictypes = ('wic', 'wic.vmdk', 'wic.qcow2', 'wic.vdi')
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500189 self.fstypes = ('ext2', 'ext3', 'ext4', 'jffs2', 'nfs', 'btrfs',
190 'cpio.gz', 'cpio', 'ramfs', 'tar.bz2', 'tar.gz')
Brad Bishop15ae2502019-06-18 21:44:24 -0400191 self.vmtypes = ('hddimg', 'hdddirect', 'iso')
192 self.fsinfo = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500193 self.network_device = "-device e1000,netdev=net0,mac=@MAC@"
194 # Use different mac section for tap and slirp to avoid
195 # conflicts, e.g., when one is running with tap, the other is
196 # running with slirp.
197 # The last section is dynamic, which is for avoiding conflicts,
198 # when multiple qemus are running, e.g., when multiple tap or
199 # slirp qemus are running.
200 self.mac_tap = "52:54:00:12:34:"
201 self.mac_slirp = "52:54:00:12:35:"
Brad Bishop004d4992018-10-02 23:54:45 +0200202 # pid of the actual qemu process
203 self.qemupid = None
204 # avoid cleanup twice
205 self.cleaned = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500206
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500207 def acquire_lock(self, error=True):
208 logger.debug("Acquiring lockfile %s..." % self.lock)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600209 try:
210 self.lock_descriptor = open(self.lock, 'w')
211 fcntl.flock(self.lock_descriptor, fcntl.LOCK_EX|fcntl.LOCK_NB)
212 except Exception as e:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500213 msg = "Acquiring lockfile %s failed: %s" % (self.lock, e)
214 if error:
215 logger.error(msg)
216 else:
217 logger.info(msg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600218 if self.lock_descriptor:
219 self.lock_descriptor.close()
Brad Bishopf86d0552018-12-04 14:18:15 -0800220 self.lock_descriptor = None
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600221 return False
222 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500223
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600224 def release_lock(self):
Brad Bishopf86d0552018-12-04 14:18:15 -0800225 if self.lock_descriptor:
226 logger.debug("Releasing lockfile for tap device '%s'" % self.tap)
227 fcntl.flock(self.lock_descriptor, fcntl.LOCK_UN)
228 self.lock_descriptor.close()
229 os.remove(self.lock)
230 self.lock_descriptor = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500231
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600232 def get(self, key):
233 if key in self.d:
234 return self.d.get(key)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500235 elif os.getenv(key):
236 return os.getenv(key)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600237 else:
238 return ''
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500239
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600240 def set(self, key, value):
241 self.d[key] = value
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500242
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600243 def is_deploy_dir_image(self, p):
244 if os.path.isdir(p):
245 if not re.search('.qemuboot.conf$', '\n'.join(os.listdir(p)), re.M):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500246 logger.debug("Can't find required *.qemuboot.conf in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600247 return False
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500248 if not any(map(lambda name: '-image-' in name, os.listdir(p))):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500249 logger.debug("Can't find *-image-* in %s" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600250 return False
251 return True
252 else:
253 return False
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500254
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600255 def check_arg_fstype(self, fst):
256 """Check and set FSTYPE"""
Brad Bishop15ae2502019-06-18 21:44:24 -0400257 if fst not in self.fstypes + self.vmtypes + self.wictypes:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800258 logger.warning("Maybe unsupported FSTYPE: %s" % fst)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600259 if not self.fstype or self.fstype == fst:
260 if fst == 'ramfs':
261 fst = 'cpio.gz'
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500262 if fst in ('tar.bz2', 'tar.gz'):
263 fst = 'nfs'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600264 self.fstype = fst
265 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500266 raise RunQemuError("Conflicting: FSTYPE %s and %s" % (self.fstype, fst))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500267
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600268 def set_machine_deploy_dir(self, machine, deploy_dir_image):
269 """Set MACHINE and DEPLOY_DIR_IMAGE"""
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500270 logger.debug('MACHINE: %s' % machine)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600271 self.set("MACHINE", machine)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500272 logger.debug('DEPLOY_DIR_IMAGE: %s' % deploy_dir_image)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600273 self.set("DEPLOY_DIR_IMAGE", deploy_dir_image)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500274
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600275 def check_arg_nfs(self, p):
276 if os.path.isdir(p):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500277 self.rootfs = p
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600278 else:
279 m = re.match('(.*):(.*)', p)
280 self.nfs_server = m.group(1)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500281 self.rootfs = m.group(2)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600282 self.check_arg_fstype('nfs')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500283
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600284 def check_arg_path(self, p):
285 """
286 - Check whether it is <image>.qemuboot.conf or contains <image>.qemuboot.conf
287 - Check whether is a kernel file
288 - Check whether is a image file
289 - Check whether it is a nfs dir
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500290 - Check whether it is a OVMF flash file
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600291 """
292 if p.endswith('.qemuboot.conf'):
293 self.qemuboot = p
294 self.qbconfload = True
295 elif re.search('\.bin$', p) or re.search('bzImage', p) or \
296 re.search('zImage', p) or re.search('vmlinux', p) or \
297 re.search('fitImage', p) or re.search('uImage', p):
298 self.kernel = p
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500299 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 -0600300 self.rootfs = p
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500301 # Check filename against self.fstypes can hanlde <file>.cpio.gz,
302 # otherwise, its type would be "gz", which is incorrect.
303 fst = ""
304 for t in self.fstypes:
305 if p.endswith(t):
306 fst = t
307 break
308 if not fst:
309 m = re.search('.*\.(.*)$', self.rootfs)
310 if m:
311 fst = m.group(1)
312 if fst:
313 self.check_arg_fstype(fst)
314 qb = re.sub('\.' + fst + "$", '', self.rootfs)
315 qb = '%s%s' % (re.sub('\.rootfs$', '', qb), '.qemuboot.conf')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600316 if os.path.exists(qb):
317 self.qemuboot = qb
318 self.qbconfload = True
319 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800320 logger.warning("%s doesn't exist" % qb)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600321 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500322 raise RunQemuError("Can't find FSTYPE from: %s" % p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500323
324 elif os.path.isdir(p) or re.search(':', p) and re.search('/', p):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600325 if self.is_deploy_dir_image(p):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500326 logger.debug('DEPLOY_DIR_IMAGE: %s' % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600327 self.set("DEPLOY_DIR_IMAGE", p)
328 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500329 logger.debug("Assuming %s is an nfs rootfs" % p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600330 self.check_arg_nfs(p)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500331 elif os.path.basename(p).startswith('ovmf'):
332 self.ovmf_bios.append(p)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600333 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500334 raise RunQemuError("Unknown path arg %s" % p)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500335
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600336 def check_arg_machine(self, arg):
337 """Check whether it is a machine"""
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500338 if self.get('MACHINE') == arg:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600339 return
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500340 elif self.get('MACHINE') and self.get('MACHINE') != arg:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500341 raise RunQemuError("Maybe conflicted MACHINE: %s vs %s" % (self.get('MACHINE'), arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500342 elif re.search('/', arg):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500343 raise RunQemuError("Unknown arg: %s" % arg)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500344
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500345 logger.debug('Assuming MACHINE = %s' % arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500346
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600347 # if we're running under testimage, or similarly as a child
348 # of an existing bitbake invocation, we can't invoke bitbake
349 # to validate the MACHINE setting and must assume it's correct...
350 # FIXME: testimage.bbclass exports these two variables into env,
351 # are there other scenarios in which we need to support being
352 # invoked by bitbake?
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500353 deploy = self.get('DEPLOY_DIR_IMAGE')
354 bbchild = deploy and self.get('OE_TMPDIR')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600355 if bbchild:
356 self.set_machine_deploy_dir(arg, deploy)
357 return
358 # also check whether we're running under a sourced toolchain
359 # environment file
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500360 if self.get('OECORE_NATIVE_SYSROOT'):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600361 self.set("MACHINE", arg)
362 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500363
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600364 cmd = 'MACHINE=%s bitbake -e' % arg
365 logger.info('Running %s...' % cmd)
Brad Bishop977dc1a2019-02-06 16:01:43 -0500366 self.bitbake_e = subprocess.check_output(cmd, shell=True).decode('utf-8')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600367 # bitbake -e doesn't report invalid MACHINE as an error, so
368 # let's check DEPLOY_DIR_IMAGE to make sure that it is a valid
369 # MACHINE.
370 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
371 if s:
372 deploy_dir_image = s.group(1)
373 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500374 raise RunQemuError("bitbake -e %s" % self.bitbake_e)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600375 if self.is_deploy_dir_image(deploy_dir_image):
376 self.set_machine_deploy_dir(arg, deploy_dir_image)
377 else:
378 logger.error("%s not a directory valid DEPLOY_DIR_IMAGE" % deploy_dir_image)
379 self.set("MACHINE", arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500380
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600381 def check_args(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500382 for debug in ("-d", "--debug"):
383 if debug in sys.argv:
384 logger.setLevel(logging.DEBUG)
385 sys.argv.remove(debug)
386
387 for quiet in ("-q", "--quiet"):
388 if quiet in sys.argv:
389 logger.setLevel(logging.ERROR)
390 sys.argv.remove(quiet)
391
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600392 unknown_arg = ""
393 for arg in sys.argv[1:]:
Brad Bishop15ae2502019-06-18 21:44:24 -0400394 if arg in self.fstypes + self.vmtypes + self.wictypes:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600395 self.check_arg_fstype(arg)
396 elif arg == 'nographic':
397 self.qemu_opt_script += ' -nographic'
398 self.kernel_cmdline_script += ' console=ttyS0'
Brad Bishop19323692019-04-05 15:28:33 -0400399 elif arg == 'sdl':
400 self.qemu_opt_script += ' -display sdl'
401 elif arg == 'gtk-gl':
402 self.qemu_opt_script += ' -vga virtio -display gtk,gl=on'
403 elif arg == 'gtk-gl-es':
404 self.qemu_opt_script += ' -vga virtio -display gtk,gl=es'
405 elif arg == 'egl-headless':
406 self.qemu_opt_script += ' -vga virtio -display egl-headless'
407 # As runqemu can be run within bitbake (when using testimage, for example),
408 # we need to ensure that we run host pkg-config, and that it does not
409 # get mis-directed to native build paths set by bitbake.
410 try:
411 del os.environ['PKG_CONFIG_PATH']
412 del os.environ['PKG_CONFIG_DIR']
413 del os.environ['PKG_CONFIG_LIBDIR']
414 except KeyError:
415 pass
416 try:
417 dripath = subprocess.check_output("PATH=/bin:/usr/bin:$PATH pkg-config --variable=dridriverdir dri", shell=True)
418 except subprocess.CalledProcessError as e:
419 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.")
420 os.environ['LIBGL_DRIVERS_PATH'] = dripath.decode('utf-8').strip()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600421 elif arg == 'serial':
422 self.kernel_cmdline_script += ' console=ttyS0'
Brad Bishop19323692019-04-05 15:28:33 -0400423 self.serialconsole = True
424 elif arg == "serialstdio":
425 self.kernel_cmdline_script += ' console=ttyS0'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600426 self.serialstdio = True
427 elif arg == 'audio':
428 logger.info("Enabling audio in qemu")
429 logger.info("Please install sound drivers in linux host")
430 self.audio_enabled = True
431 elif arg == 'kvm':
432 self.kvm_enabled = True
433 elif arg == 'kvm-vhost':
434 self.vhost_enabled = True
435 elif arg == 'slirp':
436 self.slirp_enabled = True
437 elif arg == 'snapshot':
438 self.snapshot = True
439 elif arg == 'publicvnc':
440 self.qemu_opt_script += ' -vnc :0'
441 elif arg.startswith('tcpserial='):
Brad Bishop15ae2502019-06-18 21:44:24 -0400442 self.tcpserial_portnum = '%s' % arg[len('tcpserial='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600443 elif arg.startswith('biosdir='):
444 self.custombiosdir = arg[len('biosdir='):]
445 elif arg.startswith('biosfilename='):
446 self.qemu_opt_script += ' -bios %s' % arg[len('biosfilename='):]
447 elif arg.startswith('qemuparams='):
Andrew Geissler99467da2019-02-25 18:54:23 -0600448 self.qemuparams = ' %s' % arg[len('qemuparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600449 elif arg.startswith('bootparams='):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500450 self.bootparams = arg[len('bootparams='):]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600451 elif os.path.exists(arg) or (re.search(':', arg) and re.search('/', arg)):
452 self.check_arg_path(os.path.abspath(arg))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500453 elif re.search(r'-image-|-image$', arg):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600454 # Lazy rootfs
455 self.rootfs = arg
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500456 elif arg.startswith('ovmf'):
457 self.ovmf_bios.append(arg)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600458 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500459 # At last, assume it is the MACHINE
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600460 if (not unknown_arg) or unknown_arg == arg:
461 unknown_arg = arg
462 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500463 raise RunQemuError("Can't handle two unknown args: %s %s\n"
464 "Try 'runqemu help' on how to use it" % \
465 (unknown_arg, arg))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600466 # Check to make sure it is a valid machine
Brad Bishopa5c52ff2018-11-23 10:55:50 +1300467 if unknown_arg and self.get('MACHINE') != unknown_arg:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500468 if self.get('DEPLOY_DIR_IMAGE'):
469 machine = os.path.basename(self.get('DEPLOY_DIR_IMAGE'))
470 if unknown_arg == machine:
471 self.set("MACHINE", machine)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500472
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600473 self.check_arg_machine(unknown_arg)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500474
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500475 if not (self.get('DEPLOY_DIR_IMAGE') or self.qbconfload):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500476 self.load_bitbake_env()
477 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
478 if s:
479 self.set("DEPLOY_DIR_IMAGE", s.group(1))
480
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600481 def check_kvm(self):
482 """Check kvm and kvm-host"""
483 if not (self.kvm_enabled or self.vhost_enabled):
484 self.qemu_opt_script += ' %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU'))
485 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500486
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600487 if not self.get('QB_CPU_KVM'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500488 raise RunQemuError("QB_CPU_KVM is NULL, this board doesn't support kvm")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500489
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600490 self.qemu_opt_script += ' %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU_KVM'))
491 yocto_kvm_wiki = "https://wiki.yoctoproject.org/wiki/How_to_enable_KVM_for_Poky_qemu"
492 yocto_paravirt_kvm_wiki = "https://wiki.yoctoproject.org/wiki/Running_an_x86_Yocto_Linux_image_under_QEMU_KVM"
493 dev_kvm = '/dev/kvm'
494 dev_vhost = '/dev/vhost-net'
Brad Bishop15ae2502019-06-18 21:44:24 -0400495 if self.qemu_system.endswith(('i386', 'x86_64')):
496 with open('/proc/cpuinfo', 'r') as f:
497 kvm_cap = re.search('vmx|svm', "".join(f.readlines()))
498 if not kvm_cap:
499 logger.error("You are trying to enable KVM on a cpu without VT support.")
500 logger.error("Remove kvm from the command-line, or refer:")
501 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500502
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600503 if not os.path.exists(dev_kvm):
504 logger.error("Missing KVM device. Have you inserted kvm modules?")
505 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500506 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500507
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600508 if os.access(dev_kvm, os.W_OK|os.R_OK):
509 self.qemu_opt_script += ' -enable-kvm'
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500510 if self.get('MACHINE') == "qemux86":
511 # Workaround for broken APIC window on pre 4.15 host kernels which causes boot hangs
512 # See YOCTO #12301
513 # On 64 bit we use x2apic
514 self.kernel_cmdline_script += " clocksource=kvm-clock hpet=disable noapic nolapic"
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600515 else:
516 logger.error("You have no read or write permission on /dev/kvm.")
517 logger.error("Please change the ownership of this file as described at:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500518 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500519
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600520 if self.vhost_enabled:
521 if not os.path.exists(dev_vhost):
522 logger.error("Missing virtio net device. Have you inserted vhost-net module?")
523 logger.error("For further help see:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500524 raise RunQemuError(yocto_paravirt_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500525
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600526 if not os.access(dev_kvm, os.W_OK|os.R_OK):
527 logger.error("You have no read or write permission on /dev/vhost-net.")
528 logger.error("Please change the ownership of this file as described at:")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500529 raise RunQemuError(yocto_kvm_wiki)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500530
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600531 def check_fstype(self):
532 """Check and setup FSTYPE"""
533 if not self.fstype:
534 fstype = self.get('QB_DEFAULT_FSTYPE')
535 if fstype:
536 self.fstype = fstype
537 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500538 raise RunQemuError("FSTYPE is NULL!")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500539
Brad Bishop15ae2502019-06-18 21:44:24 -0400540 # parse QB_FSINFO into dict, e.g. { 'wic': ['no-kernel-in-fs', 'a-flag'], 'ext4': ['another-flag']}
541 wic_fs = False
542 qb_fsinfo = self.get('QB_FSINFO')
543 if qb_fsinfo:
544 qb_fsinfo = qb_fsinfo.split()
545 for fsinfo in qb_fsinfo:
546 try:
547 fstype, fsflag = fsinfo.split(':')
548
549 if fstype == 'wic':
550 if fsflag == 'no-kernel-in-fs':
551 wic_fs = True
552 elif fsflag == 'kernel-in-fs':
553 wic_fs = False
554 else:
555 logger.warn('Unknown flag "%s:%s" in QB_FSINFO', fstype, fsflag)
556 continue
557 else:
558 logger.warn('QB_FSINFO is not supported for image type "%s"', fstype)
559 continue
560
561 if fstype in self.fsinfo:
562 self.fsinfo[fstype].append(fsflag)
563 else:
564 self.fsinfo[fstype] = [fsflag]
565 except Exception:
566 logger.error('Invalid parameter "%s" in QB_FSINFO', fsinfo)
567
568 # treat wic images as vmimages (with kernel) or as fsimages (rootfs only)
569 if wic_fs:
570 self.fstypes = self.fstypes + self.wictypes
571 else:
572 self.vmtypes = self.vmtypes + self.wictypes
573
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600574 def check_rootfs(self):
575 """Check and set rootfs"""
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500576
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500577 if self.fstype == "none":
578 return
579
580 if self.get('ROOTFS'):
581 if not self.rootfs:
582 self.rootfs = self.get('ROOTFS')
583 elif self.get('ROOTFS') != self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500584 raise RunQemuError("Maybe conflicted ROOTFS: %s vs %s" % (self.get('ROOTFS'), self.rootfs))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500585
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600586 if self.fstype == 'nfs':
587 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500588
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600589 if self.rootfs and not os.path.exists(self.rootfs):
590 # Lazy rootfs
591 self.rootfs = "%s/%s-%s.%s" % (self.get('DEPLOY_DIR_IMAGE'),
592 self.rootfs, self.get('MACHINE'),
593 self.fstype)
594 elif not self.rootfs:
595 cmd_name = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_NAME'), self.fstype)
596 cmd_link = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'), self.fstype)
597 cmds = (cmd_name, cmd_link)
598 self.rootfs = get_first_file(cmds)
599 if not self.rootfs:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500600 raise RunQemuError("Failed to find rootfs: %s or %s" % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500601
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600602 if not os.path.exists(self.rootfs):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500603 raise RunQemuError("Can't find rootfs: %s" % self.rootfs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500604
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500605 def check_ovmf(self):
606 """Check and set full path for OVMF firmware and variable file(s)."""
607
608 for index, ovmf in enumerate(self.ovmf_bios):
609 if os.path.exists(ovmf):
610 continue
611 for suffix in ('qcow2', 'bin'):
612 path = '%s/%s.%s' % (self.get('DEPLOY_DIR_IMAGE'), ovmf, suffix)
613 if os.path.exists(path):
614 self.ovmf_bios[index] = path
615 break
616 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500617 raise RunQemuError("Can't find OVMF firmware: %s" % ovmf)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500618
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600619 def check_kernel(self):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400620 """Check and set kernel"""
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600621 # The vm image doesn't need a kernel
622 if self.fstype in self.vmtypes:
623 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500624
Brad Bishop316dfdd2018-06-25 12:45:53 -0400625 # See if the user supplied a KERNEL option
626 if self.get('KERNEL'):
627 self.kernel = self.get('KERNEL')
628
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500629 # QB_DEFAULT_KERNEL is always a full file path
630 kernel_name = os.path.basename(self.get('QB_DEFAULT_KERNEL'))
631
632 # The user didn't want a kernel to be loaded
Brad Bishop316dfdd2018-06-25 12:45:53 -0400633 if kernel_name == "none" and not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500634 return
635
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600636 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
637 if not self.kernel:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500638 kernel_match_name = "%s/%s" % (deploy_dir_image, kernel_name)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600639 kernel_match_link = "%s/%s" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
640 kernel_startswith = "%s/%s*" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
641 cmds = (kernel_match_name, kernel_match_link, kernel_startswith)
642 self.kernel = get_first_file(cmds)
643 if not self.kernel:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500644 raise RunQemuError('KERNEL not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500645
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600646 if not os.path.exists(self.kernel):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500647 raise RunQemuError("KERNEL %s not found" % self.kernel)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500648
Brad Bishop316dfdd2018-06-25 12:45:53 -0400649 def check_dtb(self):
650 """Check and set dtb"""
651 # Did the user specify a device tree?
652 if self.get('DEVICE_TREE'):
653 self.dtb = self.get('DEVICE_TREE')
654 if not os.path.exists(self.dtb):
655 raise RunQemuError('Specified DTB not found: %s' % self.dtb)
656 return
657
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600658 dtb = self.get('QB_DTB')
659 if dtb:
Brad Bishop316dfdd2018-06-25 12:45:53 -0400660 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600661 cmd_match = "%s/%s" % (deploy_dir_image, dtb)
662 cmd_startswith = "%s/%s*" % (deploy_dir_image, dtb)
663 cmd_wild = "%s/*.dtb" % deploy_dir_image
664 cmds = (cmd_match, cmd_startswith, cmd_wild)
665 self.dtb = get_first_file(cmds)
666 if not os.path.exists(self.dtb):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500667 raise RunQemuError('DTB not found: %s, %s or %s' % cmds)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500668
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600669 def check_biosdir(self):
670 """Check custombiosdir"""
671 if not self.custombiosdir:
672 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500673
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600674 biosdir = ""
675 biosdir_native = "%s/%s" % (self.get('STAGING_DIR_NATIVE'), self.custombiosdir)
676 biosdir_host = "%s/%s" % (self.get('STAGING_DIR_HOST'), self.custombiosdir)
677 for i in (self.custombiosdir, biosdir_native, biosdir_host):
678 if os.path.isdir(i):
679 biosdir = i
680 break
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500681
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600682 if biosdir:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500683 logger.debug("Assuming biosdir is: %s" % biosdir)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600684 self.qemu_opt_script += ' -L %s' % biosdir
685 else:
686 logger.error("Custom BIOS directory not found. Tried: %s, %s, and %s" % (self.custombiosdir, biosdir_native, biosdir_host))
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500687 raise RunQemuError("Invalid custombiosdir: %s" % self.custombiosdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500688
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600689 def check_mem(self):
Andrew Geissler99467da2019-02-25 18:54:23 -0600690 """
691 Both qemu and kernel needs memory settings, so check QB_MEM and set it
692 for both.
693 """
694 s = re.search('-m +([0-9]+)', self.qemuparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600695 if s:
696 self.set('QB_MEM', '-m %s' % s.group(1))
697 elif not self.get('QB_MEM'):
698 logger.info('QB_MEM is not set, use 512M by default')
699 self.set('QB_MEM', '-m 512')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500700
Andrew Geissler99467da2019-02-25 18:54:23 -0600701 # Check and remove M or m suffix
702 qb_mem = self.get('QB_MEM')
703 if qb_mem.endswith('M') or qb_mem.endswith('m'):
704 qb_mem = qb_mem[:-1]
705
706 # Add -m prefix it not present
707 if not qb_mem.startswith('-m'):
708 qb_mem = '-m %s' % qb_mem
709
710 self.set('QB_MEM', qb_mem)
711
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800712 mach = self.get('MACHINE')
713 if not mach.startswith('qemumips'):
714 self.kernel_cmdline_script += ' mem=%s' % self.get('QB_MEM').replace('-m','').strip() + 'M'
715
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600716 self.qemu_opt_script += ' %s' % self.get('QB_MEM')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500717
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600718 def check_tcpserial(self):
719 if self.tcpserial_portnum:
Brad Bishop15ae2502019-06-18 21:44:24 -0400720 ports = self.tcpserial_portnum.split(':')
721 port = ports[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600722 if self.get('QB_TCPSERIAL_OPT'):
Brad Bishop15ae2502019-06-18 21:44:24 -0400723 self.qemu_opt_script += ' ' + self.get('QB_TCPSERIAL_OPT').replace('@PORT@', port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600724 else:
Brad Bishop15ae2502019-06-18 21:44:24 -0400725 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
726
727 if len(ports) > 1:
728 for port in ports[1:]:
729 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500730
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600731 def check_and_set(self):
732 """Check configs sanity and set when needed"""
733 self.validate_paths()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500734 if not self.slirp_enabled:
735 check_tun()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600736 # Check audio
737 if self.audio_enabled:
738 if not self.get('QB_AUDIO_DRV'):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500739 raise RunQemuError("QB_AUDIO_DRV is NULL, this board doesn't support audio")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600740 if not self.get('QB_AUDIO_OPT'):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800741 logger.warning('QB_AUDIO_OPT is NULL, you may need define it to make audio work')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600742 else:
743 self.qemu_opt_script += ' %s' % self.get('QB_AUDIO_OPT')
744 os.putenv('QEMU_AUDIO_DRV', self.get('QB_AUDIO_DRV'))
745 else:
746 os.putenv('QEMU_AUDIO_DRV', 'none')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500747
Brad Bishop15ae2502019-06-18 21:44:24 -0400748 self.check_qemu_system()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600749 self.check_kvm()
750 self.check_fstype()
751 self.check_rootfs()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500752 self.check_ovmf()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600753 self.check_kernel()
Brad Bishop316dfdd2018-06-25 12:45:53 -0400754 self.check_dtb()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600755 self.check_biosdir()
756 self.check_mem()
757 self.check_tcpserial()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500758
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600759 def read_qemuboot(self):
760 if not self.qemuboot:
761 if self.get('DEPLOY_DIR_IMAGE'):
762 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600763 else:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800764 logger.warning("Can't find qemuboot conf file, DEPLOY_DIR_IMAGE is NULL!")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600765 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500766
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600767 if self.rootfs and not os.path.exists(self.rootfs):
768 # Lazy rootfs
769 machine = self.get('MACHINE')
770 if not machine:
771 machine = os.path.basename(deploy_dir_image)
772 self.qemuboot = "%s/%s-%s.qemuboot.conf" % (deploy_dir_image,
773 self.rootfs, machine)
774 else:
775 cmd = 'ls -t %s/*.qemuboot.conf' % deploy_dir_image
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500776 logger.debug('Running %s...' % cmd)
777 try:
778 qbs = subprocess.check_output(cmd, shell=True).decode('utf-8')
779 except subprocess.CalledProcessError as err:
780 raise RunQemuError(err)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600781 if qbs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500782 for qb in qbs.split():
783 # Don't use initramfs when other choices unless fstype is ramfs
784 if '-initramfs-' in os.path.basename(qb) and self.fstype != 'cpio.gz':
785 continue
786 self.qemuboot = qb
787 break
788 if not self.qemuboot:
789 # Use the first one when no choice
790 self.qemuboot = qbs.split()[0]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600791 self.qbconfload = True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500792
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600793 if not self.qemuboot:
794 # If we haven't found a .qemuboot.conf at this point it probably
795 # doesn't exist, continue without
796 return
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500797
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600798 if not os.path.exists(self.qemuboot):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500799 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 -0500800
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500801 logger.debug('CONFFILE: %s' % self.qemuboot)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500802
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600803 cf = configparser.ConfigParser()
804 cf.read(self.qemuboot)
805 for k, v in cf.items('config_bsp'):
806 k_upper = k.upper()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500807 if v.startswith("../"):
808 v = os.path.abspath(os.path.dirname(self.qemuboot) + "/" + v)
809 elif v == ".":
810 v = os.path.dirname(self.qemuboot)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600811 self.set(k_upper, v)
812
813 def validate_paths(self):
814 """Ensure all relevant path variables are set"""
815 # When we're started with a *.qemuboot.conf arg assume that image
816 # artefacts are relative to that file, rather than in whatever
817 # directory DEPLOY_DIR_IMAGE in the conf file points to.
818 if self.qbconfload:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500819 imgdir = os.path.realpath(os.path.dirname(self.qemuboot))
820 if imgdir != os.path.realpath(self.get('DEPLOY_DIR_IMAGE')):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600821 logger.info('Setting DEPLOY_DIR_IMAGE to folder containing %s (%s)' % (self.qemuboot, imgdir))
822 self.set('DEPLOY_DIR_IMAGE', imgdir)
823
824 # If the STAGING_*_NATIVE directories from the config file don't exist
825 # and we're in a sourced OE build directory try to extract the paths
826 # from `bitbake -e`
827 havenative = os.path.exists(self.get('STAGING_DIR_NATIVE')) and \
828 os.path.exists(self.get('STAGING_BINDIR_NATIVE'))
829
830 if not havenative:
831 if not self.bitbake_e:
832 self.load_bitbake_env()
833
834 if self.bitbake_e:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500835 native_vars = ['STAGING_DIR_NATIVE']
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600836 for nv in native_vars:
837 s = re.search('^%s="(.*)"' % nv, self.bitbake_e, re.M)
838 if s and s.group(1) != self.get(nv):
839 logger.info('Overriding conf file setting of %s to %s from Bitbake environment' % (nv, s.group(1)))
840 self.set(nv, s.group(1))
841 else:
842 # when we're invoked from a running bitbake instance we won't
843 # be able to call `bitbake -e`, then try:
844 # - get OE_TMPDIR from environment and guess paths based on it
845 # - get OECORE_NATIVE_SYSROOT from environment (for sdk)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500846 tmpdir = self.get('OE_TMPDIR')
847 oecore_native_sysroot = self.get('OECORE_NATIVE_SYSROOT')
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600848 if tmpdir:
849 logger.info('Setting STAGING_DIR_NATIVE and STAGING_BINDIR_NATIVE relative to OE_TMPDIR (%s)' % tmpdir)
850 hostos, _, _, _, machine = os.uname()
851 buildsys = '%s-%s' % (machine, hostos.lower())
852 staging_dir_native = '%s/sysroots/%s' % (tmpdir, buildsys)
853 self.set('STAGING_DIR_NATIVE', staging_dir_native)
854 elif oecore_native_sysroot:
855 logger.info('Setting STAGING_DIR_NATIVE to OECORE_NATIVE_SYSROOT (%s)' % oecore_native_sysroot)
856 self.set('STAGING_DIR_NATIVE', oecore_native_sysroot)
857 if self.get('STAGING_DIR_NATIVE'):
858 # we have to assume that STAGING_BINDIR_NATIVE is at usr/bin
859 staging_bindir_native = '%s/usr/bin' % self.get('STAGING_DIR_NATIVE')
860 logger.info('Setting STAGING_BINDIR_NATIVE to %s' % staging_bindir_native)
861 self.set('STAGING_BINDIR_NATIVE', '%s/usr/bin' % self.get('STAGING_DIR_NATIVE'))
862
863 def print_config(self):
864 logger.info('Continuing with the following parameters:\n')
865 if not self.fstype in self.vmtypes:
866 print('KERNEL: [%s]' % self.kernel)
867 if self.dtb:
868 print('DTB: [%s]' % self.dtb)
869 print('MACHINE: [%s]' % self.get('MACHINE'))
Brad Bishop15ae2502019-06-18 21:44:24 -0400870 try:
871 fstype_flags = ' (' + ', '.join(self.fsinfo[self.fstype]) + ')'
872 except KeyError:
873 fstype_flags = ''
874 print('FSTYPE: [%s%s]' % (self.fstype, fstype_flags))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600875 if self.fstype == 'nfs':
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500876 print('NFS_DIR: [%s]' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600877 else:
878 print('ROOTFS: [%s]' % self.rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500879 if self.ovmf_bios:
880 print('OVMF: %s' % self.ovmf_bios)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600881 print('CONFFILE: [%s]' % self.qemuboot)
882 print('')
883
884 def setup_nfs(self):
885 if not self.nfs_server:
886 if self.slirp_enabled:
887 self.nfs_server = '10.0.2.2'
888 else:
889 self.nfs_server = '192.168.7.1'
890
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500891 # Figure out a new nfs_instance to allow multiple qemus running.
Brad Bishop977dc1a2019-02-06 16:01:43 -0500892 ps = subprocess.check_output(("ps", "auxww")).decode('utf-8')
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500893 pattern = '/bin/unfsd .* -i .*\.pid -e .*/exports([0-9]+) '
894 all_instances = re.findall(pattern, ps, re.M)
895 if all_instances:
896 all_instances.sort(key=int)
897 self.nfs_instance = int(all_instances.pop()) + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600898
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500899 nfsd_port = 3049 + 2 * self.nfs_instance
900 mountd_port = 3048 + 2 * self.nfs_instance
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600901
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500902 # Export vars for runqemu-export-rootfs
903 export_dict = {
904 'NFS_INSTANCE': self.nfs_instance,
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500905 'NFSD_PORT': nfsd_port,
906 'MOUNTD_PORT': mountd_port,
907 }
908 for k, v in export_dict.items():
909 # Use '%s' since they are integers
910 os.putenv(k, '%s' % v)
911
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500912 self.unfs_opts="nfsvers=3,port=%s,udp,mountport=%s" % (nfsd_port, mountd_port)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600913
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500914 # Extract .tar.bz2 or .tar.bz if no nfs dir
915 if not (self.rootfs and os.path.isdir(self.rootfs)):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600916 src_prefix = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'))
917 dest = "%s-nfsroot" % src_prefix
918 if os.path.exists('%s.pseudo_state' % dest):
919 logger.info('Use %s as NFS_DIR' % dest)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500920 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600921 else:
922 src = ""
923 src1 = '%s.tar.bz2' % src_prefix
924 src2 = '%s.tar.gz' % src_prefix
925 if os.path.exists(src1):
926 src = src1
927 elif os.path.exists(src2):
928 src = src2
929 if not src:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500930 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 -0600931 logger.info('NFS_DIR not found, extracting %s to %s' % (src, dest))
Brad Bishop977dc1a2019-02-06 16:01:43 -0500932 cmd = ('runqemu-extract-sdk', src, dest)
933 logger.info('Running %s...' % str(cmd))
934 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500935 raise RunQemuError('Failed to run %s' % cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600936 self.clean_nfs_dir = True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500937 self.rootfs = dest
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600938
939 # Start the userspace NFS server
Brad Bishop977dc1a2019-02-06 16:01:43 -0500940 cmd = ('runqemu-export-rootfs', 'start', self.rootfs)
941 logger.info('Running %s...' % str(cmd))
942 if subprocess.call(cmd) != 0:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500943 raise RunQemuError('Failed to run %s' % cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600944
945 self.nfs_running = True
946
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600947 def setup_slirp(self):
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500948 """Setup user networking"""
949
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600950 if self.fstype == 'nfs':
951 self.setup_nfs()
952 self.kernel_cmdline_script += ' ip=dhcp'
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500953 # Port mapping
954 hostfwd = ",hostfwd=tcp::2222-:22,hostfwd=tcp::2323-:23"
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500955 qb_slirp_opt_default = "-netdev user,id=net0%s,tftp=%s" % (hostfwd, self.get('DEPLOY_DIR_IMAGE'))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500956 qb_slirp_opt = self.get('QB_SLIRP_OPT') or qb_slirp_opt_default
957 # Figure out the port
958 ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt)
959 ports = [int(i) for i in ports]
960 mac = 2
961 # Find a free port to avoid conflicts
962 for p in ports[:]:
963 p_new = p
964 while not check_free_port('localhost', p_new):
965 p_new += 1
966 mac += 1
967 while p_new in ports:
968 p_new += 1
969 mac += 1
970 if p != p_new:
971 ports.append(p_new)
972 qb_slirp_opt = re.sub(':%s-' % p, ':%s-' % p_new, qb_slirp_opt)
973 logger.info("Port forward changed: %s -> %s" % (p, p_new))
974 mac = "%s%02x" % (self.mac_slirp, mac)
975 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qb_slirp_opt))
976 # Print out port foward
977 hostfwd = re.findall('(hostfwd=[^,]*)', qb_slirp_opt)
978 if hostfwd:
979 logger.info('Port forward: %s' % ' '.join(hostfwd))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600980
981 def setup_tap(self):
982 """Setup tap"""
983
984 # This file is created when runqemu-gen-tapdevs creates a bank of tap
985 # devices, indicating that the user should not bring up new ones using
986 # sudo.
987 nosudo_flag = '/etc/runqemu-nosudo'
988 self.qemuifup = shutil.which('runqemu-ifup')
989 self.qemuifdown = shutil.which('runqemu-ifdown')
990 ip = shutil.which('ip')
991 lockdir = "/tmp/qemu-tap-locks"
992
993 if not (self.qemuifup and self.qemuifdown and ip):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500994 logger.error("runqemu-ifup: %s" % self.qemuifup)
995 logger.error("runqemu-ifdown: %s" % self.qemuifdown)
996 logger.error("ip: %s" % ip)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600997 raise OEPathError("runqemu-ifup, runqemu-ifdown or ip not found")
998
999 if not os.path.exists(lockdir):
1000 # There might be a race issue when multi runqemu processess are
1001 # running at the same time.
1002 try:
1003 os.mkdir(lockdir)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001004 os.chmod(lockdir, 0o777)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001005 except FileExistsError:
1006 pass
1007
Brad Bishop977dc1a2019-02-06 16:01:43 -05001008 cmd = (ip, 'link')
1009 logger.debug('Running %s...' % str(cmd))
1010 ip_link = subprocess.check_output(cmd).decode('utf-8')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001011 # Matches line like: 6: tap0: <foo>
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001012 possibles = re.findall('^[0-9]+: +(tap[0-9]+): <.*', ip_link, re.M)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001013 tap = ""
1014 for p in possibles:
1015 lockfile = os.path.join(lockdir, p)
1016 if os.path.exists('%s.skip' % lockfile):
1017 logger.info('Found %s.skip, skipping %s' % (lockfile, p))
1018 continue
1019 self.lock = lockfile + '.lock'
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001020 if self.acquire_lock(error=False):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001021 tap = p
1022 logger.info("Using preconfigured tap device %s" % tap)
1023 logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap))
1024 break
1025
1026 if not tap:
1027 if os.path.exists(nosudo_flag):
1028 logger.error("Error: There are no available tap devices to use for networking,")
1029 logger.error("and I see %s exists, so I am not going to try creating" % nosudo_flag)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001030 raise RunQemuError("a new one with sudo.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001031
1032 gid = os.getgid()
1033 uid = os.getuid()
1034 logger.info("Setting up tap interface under sudo")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001035 cmd = ('sudo', self.qemuifup, str(uid), str(gid), self.bindir_native)
1036 tap = subprocess.check_output(cmd).decode('utf-8').strip()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001037 lockfile = os.path.join(lockdir, tap)
1038 self.lock = lockfile + '.lock'
1039 self.acquire_lock()
1040 self.cleantap = True
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001041 logger.debug('Created tap: %s' % tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001042
1043 if not tap:
1044 logger.error("Failed to setup tap device. Run runqemu-gen-tapdevs to manually create.")
1045 return 1
1046 self.tap = tap
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001047 tapnum = int(tap[3:])
1048 gateway = tapnum * 2 + 1
1049 client = gateway + 1
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001050 if self.fstype == 'nfs':
1051 self.setup_nfs()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001052 netconf = "192.168.7.%s::192.168.7.%s:255.255.255.0" % (client, gateway)
1053 logger.info("Network configuration: %s", netconf)
1054 self.kernel_cmdline_script += " ip=%s" % netconf
1055 mac = "%s%02x" % (self.mac_tap, client)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001056 qb_tap_opt = self.get('QB_TAP_OPT')
1057 if qb_tap_opt:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001058 qemu_tap_opt = qb_tap_opt.replace('@TAP@', tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001059 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001060 qemu_tap_opt = "-netdev tap,id=net0,ifname=%s,script=no,downscript=no" % (self.tap)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001061
1062 if self.vhost_enabled:
1063 qemu_tap_opt += ',vhost=on'
1064
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001065 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qemu_tap_opt))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001066
1067 def setup_network(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001068 if self.get('QB_NET') == 'none':
1069 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001070 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001071 self.saved_stty = subprocess.check_output(("stty", "-g")).decode('utf-8').strip()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001072 self.network_device = self.get('QB_NETWORK_DEVICE') or self.network_device
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001073 if self.slirp_enabled:
1074 self.setup_slirp()
1075 else:
1076 self.setup_tap()
1077
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001078 def setup_rootfs(self):
1079 if self.get('QB_ROOTFS') == 'none':
1080 return
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001081 if 'wic.' in self.fstype:
1082 self.fstype = self.fstype[4:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001083 rootfs_format = self.fstype if self.fstype in ('vmdk', 'qcow2', 'vdi') else 'raw'
1084
1085 qb_rootfs_opt = self.get('QB_ROOTFS_OPT')
1086 if qb_rootfs_opt:
1087 self.rootfs_options = qb_rootfs_opt.replace('@ROOTFS@', self.rootfs)
1088 else:
1089 self.rootfs_options = '-drive file=%s,if=virtio,format=%s' % (self.rootfs, rootfs_format)
1090
1091 if self.fstype in ('cpio.gz', 'cpio'):
1092 self.kernel_cmdline = 'root=/dev/ram0 rw debugshell'
1093 self.rootfs_options = '-initrd %s' % self.rootfs
1094 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001095 vm_drive = ''
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001096 if self.fstype in self.vmtypes:
1097 if self.fstype == 'iso':
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001098 vm_drive = '-drive file=%s,if=virtio,media=cdrom' % self.rootfs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001099 elif self.get('QB_DRIVE_TYPE'):
1100 drive_type = self.get('QB_DRIVE_TYPE')
1101 if drive_type.startswith("/dev/sd"):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001102 logger.info('Using scsi drive')
1103 vm_drive = '-drive if=none,id=hd,file=%s,format=%s -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd' \
1104 % (self.rootfs, rootfs_format)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001105 elif drive_type.startswith("/dev/hd"):
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001106 logger.info('Using ide drive')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001107 vm_drive = "-drive file=%s,format=%s" % (self.rootfs, rootfs_format)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001108 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001109 # virtio might have been selected explicitly (just use it), or
1110 # is used as fallback (then warn about that).
1111 if not drive_type.startswith("/dev/vd"):
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001112 logger.warning("Unknown QB_DRIVE_TYPE: %s" % drive_type)
1113 logger.warning("Failed to figure out drive type, consider define or fix QB_DRIVE_TYPE")
1114 logger.warning('Trying to use virtio block drive')
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001115 vm_drive = '-drive if=virtio,file=%s,format=%s' % (self.rootfs, rootfs_format)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001116
1117 # All branches above set vm_drive.
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001118 self.rootfs_options = '%s -no-reboot' % vm_drive
1119 self.kernel_cmdline = 'root=%s rw highres=off' % (self.get('QB_KERNEL_ROOT'))
1120
1121 if self.fstype == 'nfs':
1122 self.rootfs_options = ''
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001123 k_root = '/dev/nfs nfsroot=%s:%s,%s' % (self.nfs_server, os.path.abspath(self.rootfs), self.unfs_opts)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001124 self.kernel_cmdline = 'root=%s rw highres=off' % k_root
1125
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001126 if self.fstype == 'none':
1127 self.rootfs_options = ''
1128
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001129 self.set('ROOTFS_OPTIONS', self.rootfs_options)
1130
1131 def guess_qb_system(self):
1132 """attempt to determine the appropriate qemu-system binary"""
1133 mach = self.get('MACHINE')
1134 if not mach:
1135 search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*'
1136 if self.rootfs:
1137 match = re.match(search, self.rootfs)
1138 if match:
1139 mach = match.group(1)
1140 elif self.kernel:
1141 match = re.match(search, self.kernel)
1142 if match:
1143 mach = match.group(1)
1144
1145 if not mach:
1146 return None
1147
1148 if mach == 'qemuarm':
1149 qbsys = 'arm'
1150 elif mach == 'qemuarm64':
1151 qbsys = 'aarch64'
1152 elif mach == 'qemux86':
1153 qbsys = 'i386'
1154 elif mach == 'qemux86-64':
1155 qbsys = 'x86_64'
1156 elif mach == 'qemuppc':
1157 qbsys = 'ppc'
1158 elif mach == 'qemumips':
1159 qbsys = 'mips'
1160 elif mach == 'qemumips64':
1161 qbsys = 'mips64'
1162 elif mach == 'qemumipsel':
1163 qbsys = 'mipsel'
1164 elif mach == 'qemumips64el':
1165 qbsys = 'mips64el'
Brad Bishop316dfdd2018-06-25 12:45:53 -04001166 elif mach == 'qemuriscv64':
1167 qbsys = 'riscv64'
1168 elif mach == 'qemuriscv32':
1169 qbsys = 'riscv32'
Brad Bishop004d4992018-10-02 23:54:45 +02001170 else:
1171 logger.error("Unable to determine QEMU PC System emulator for %s machine." % mach)
1172 logger.error("As %s is not among valid QEMU machines such as," % mach)
1173 logger.error("qemux86-64, qemux86, qemuarm64, qemuarm, qemumips64, qemumips64el, qemumipsel, qemumips, qemuppc")
1174 raise RunQemuError("Set qb_system_name with suitable QEMU PC System emulator in .*qemuboot.conf.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001175
1176 return 'qemu-system-%s' % qbsys
1177
Brad Bishop15ae2502019-06-18 21:44:24 -04001178 def check_qemu_system(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001179 qemu_system = self.get('QB_SYSTEM_NAME')
1180 if not qemu_system:
1181 qemu_system = self.guess_qb_system()
1182 if not qemu_system:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001183 raise RunQemuError("Failed to boot, QB_SYSTEM_NAME is NULL!")
Brad Bishop15ae2502019-06-18 21:44:24 -04001184 self.qemu_system = qemu_system
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001185
Brad Bishop15ae2502019-06-18 21:44:24 -04001186 def setup_final(self):
1187 qemu_bin = os.path.join(self.bindir_native, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001188
1189 # It is possible to have qemu-native in ASSUME_PROVIDED, and it won't
1190 # find QEMU in sysroot, it needs to use host's qemu.
1191 if not os.path.exists(qemu_bin):
1192 logger.info("QEMU binary not found in %s, trying host's QEMU" % qemu_bin)
1193 for path in (os.environ['PATH'] or '').split(':'):
Brad Bishop15ae2502019-06-18 21:44:24 -04001194 qemu_bin_tmp = os.path.join(path, self.qemu_system)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001195 logger.info("Trying: %s" % qemu_bin_tmp)
1196 if os.path.exists(qemu_bin_tmp):
1197 qemu_bin = qemu_bin_tmp
1198 if not os.path.isabs(qemu_bin):
1199 qemu_bin = os.path.abspath(qemu_bin)
1200 logger.info("Using host's QEMU: %s" % qemu_bin)
1201 break
1202
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001203 if not os.access(qemu_bin, os.X_OK):
1204 raise OEPathError("No QEMU binary '%s' could be found" % qemu_bin)
1205
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001206 self.qemu_opt = "%s %s %s %s" % (qemu_bin, self.get('NETWORK_CMD'), self.get('ROOTFS_OPTIONS'), self.get('QB_OPT_APPEND'))
1207
1208 for ovmf in self.ovmf_bios:
1209 format = ovmf.rsplit('.', 1)[-1]
1210 self.qemu_opt += ' -drive if=pflash,format=%s,file=%s' % (format, ovmf)
1211 if self.ovmf_bios:
1212 # OVMF only supports normal VGA, i.e. we need to override a -vga vmware
1213 # that gets added for example for normal qemux86.
1214 self.qemu_opt += ' -vga std'
1215
1216 self.qemu_opt += ' ' + self.qemu_opt_script
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001217
Andrew Geissler99467da2019-02-25 18:54:23 -06001218 # Append qemuparams to override previous settings
1219 if self.qemuparams:
1220 self.qemu_opt += ' ' + self.qemuparams
1221
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001222 if self.snapshot:
1223 self.qemu_opt += " -snapshot"
1224
Brad Bishop19323692019-04-05 15:28:33 -04001225 if self.serialconsole:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001226 if sys.stdin.isatty():
Brad Bishop977dc1a2019-02-06 16:01:43 -05001227 subprocess.check_call(("stty", "intr", "^]"))
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001228 logger.info("Interrupt character is '^]'")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001229
1230 first_serial = ""
1231 if not re.search("-nographic", self.qemu_opt):
1232 first_serial = "-serial mon:vc"
1233 # We always want a ttyS1. Since qemu by default adds a serial
1234 # port when nodefaults is not specified, it seems that all that
1235 # would be needed is to make sure a "-serial" is there. However,
1236 # it appears that when "-serial" is specified, it ignores the
1237 # default serial port that is normally added. So here we make
1238 # sure to add two -serial if there are none. And only one if
1239 # there is one -serial already.
1240 serial_num = len(re.findall("-serial", self.qemu_opt))
1241 if serial_num == 0:
1242 self.qemu_opt += " %s %s" % (first_serial, self.get("QB_SERIAL_OPT"))
1243 elif serial_num == 1:
1244 self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT")
1245
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001246 # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES),
1247 # if not serial or serialtcp options was specified only ttyS0 is created
1248 # and sysvinit shows an error trying to enable ttyS1:
1249 # INIT: Id "S1" respawning too fast: disabled for 5 minutes
1250 serial_num = len(re.findall("-serial", self.qemu_opt))
1251 if serial_num == 0:
Brad Bishop19323692019-04-05 15:28:33 -04001252 if re.search("-nographic", self.qemu_opt) or self.serialstdio:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001253 self.qemu_opt += " -serial mon:stdio -serial null"
1254 else:
1255 self.qemu_opt += " -serial mon:vc -serial null"
1256
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001257 def start_qemu(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001258 import shlex
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001259 if self.kernel:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001260 kernel_opts = "-kernel %s -append '%s %s %s %s'" % (self.kernel, self.kernel_cmdline,
1261 self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'),
1262 self.bootparams)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001263 if self.dtb:
1264 kernel_opts += " -dtb %s" % self.dtb
1265 else:
1266 kernel_opts = ""
1267 cmd = "%s %s" % (self.qemu_opt, kernel_opts)
Brad Bishop004d4992018-10-02 23:54:45 +02001268 cmds = shlex.split(cmd)
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001269 logger.info('Running %s\n' % cmd)
Brad Bishopf86d0552018-12-04 14:18:15 -08001270 pass_fds = []
1271 if self.lock_descriptor:
1272 pass_fds = [self.lock_descriptor.fileno()]
1273 process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds)
Brad Bishop004d4992018-10-02 23:54:45 +02001274 self.qemupid = process.pid
1275 retcode = process.wait()
1276 if retcode:
1277 if retcode == -signal.SIGTERM:
1278 logger.info("Qemu terminated by SIGTERM")
1279 else:
1280 logger.error("Failed to run qemu: %s", process.stderr.read().decode())
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001281
1282 def cleanup(self):
Brad Bishop004d4992018-10-02 23:54:45 +02001283 if self.cleaned:
1284 return
1285
1286 # avoid dealing with SIGTERM when cleanup function is running
1287 signal.signal(signal.SIGTERM, signal.SIG_IGN)
1288
1289 logger.info("Cleaning up")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001290 if self.cleantap:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001291 cmd = ('sudo', self.qemuifdown, self.tap, self.bindir_native)
1292 logger.debug('Running %s' % str(cmd))
1293 subprocess.check_call(cmd)
Brad Bishopf86d0552018-12-04 14:18:15 -08001294 self.release_lock()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001295
1296 if self.nfs_running:
1297 logger.info("Shutting down the userspace NFS server...")
Brad Bishop977dc1a2019-02-06 16:01:43 -05001298 cmd = ("runqemu-export-rootfs", "stop", self.rootfs)
1299 logger.debug('Running %s' % str(cmd))
1300 subprocess.check_call(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001301
1302 if self.saved_stty:
Brad Bishop977dc1a2019-02-06 16:01:43 -05001303 subprocess.check_call(("stty", self.saved_stty))
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001304
1305 if self.clean_nfs_dir:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001306 logger.info('Removing %s' % self.rootfs)
1307 shutil.rmtree(self.rootfs)
1308 shutil.rmtree('%s.pseudo_state' % self.rootfs)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001309
Brad Bishop004d4992018-10-02 23:54:45 +02001310 self.cleaned = True
1311
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001312 def load_bitbake_env(self, mach=None):
1313 if self.bitbake_e:
1314 return
1315
1316 bitbake = shutil.which('bitbake')
1317 if not bitbake:
1318 return
1319
1320 if not mach:
1321 mach = self.get('MACHINE')
1322
1323 if mach:
1324 cmd = 'MACHINE=%s bitbake -e' % mach
1325 else:
1326 cmd = 'bitbake -e'
1327
1328 logger.info('Running %s...' % cmd)
1329 try:
1330 self.bitbake_e = subprocess.check_output(cmd, shell=True).decode('utf-8')
1331 except subprocess.CalledProcessError as err:
1332 self.bitbake_e = ''
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001333 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 -06001334
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001335 def validate_combos(self):
1336 if (self.fstype in self.vmtypes) and self.kernel:
1337 raise RunQemuError("%s doesn't need kernel %s!" % (self.fstype, self.kernel))
1338
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001339 @property
1340 def bindir_native(self):
1341 result = self.get('STAGING_BINDIR_NATIVE')
1342 if result and os.path.exists(result):
1343 return result
1344
Brad Bishop977dc1a2019-02-06 16:01:43 -05001345 cmd = ('bitbake', 'qemu-helper-native', '-e')
1346 logger.info('Running %s...' % str(cmd))
1347 out = subprocess.check_output(cmd).decode('utf-8')
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001348
1349 match = re.search('^STAGING_BINDIR_NATIVE="(.*)"', out, re.M)
1350 if match:
1351 result = match.group(1)
1352 if os.path.exists(result):
1353 self.set('STAGING_BINDIR_NATIVE', result)
1354 return result
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001355 raise RunQemuError("Native sysroot directory %s doesn't exist" % result)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001356 else:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001357 raise RunQemuError("Can't find STAGING_BINDIR_NATIVE in '%s' output" % cmd)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001358
1359
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001360def main():
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001361 if "help" in sys.argv or '-h' in sys.argv or '--help' in sys.argv:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001362 print_usage()
1363 return 0
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001364 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001365 config = BaseConfig()
Brad Bishop004d4992018-10-02 23:54:45 +02001366
1367 def sigterm_handler(signum, frame):
1368 logger.info("SIGTERM received")
1369 os.kill(config.qemupid, signal.SIGTERM)
1370 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001371 # Deliberately ignore the return code of 'tput smam'.
1372 subprocess.call(["tput", "smam"])
Brad Bishop004d4992018-10-02 23:54:45 +02001373 signal.signal(signal.SIGTERM, sigterm_handler)
1374
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001375 config.check_args()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001376 config.read_qemuboot()
1377 config.check_and_set()
1378 # Check whether the combos is valid or not
1379 config.validate_combos()
1380 config.print_config()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001381 config.setup_network()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001382 config.setup_rootfs()
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001383 config.setup_final()
1384 config.start_qemu()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001385 except RunQemuError as err:
1386 logger.error(err)
1387 return 1
1388 except Exception as err:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001389 import traceback
1390 traceback.print_exc()
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001391 return 1
1392 finally:
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001393 config.cleanup()
Brad Bishopc342db32019-05-15 21:57:59 -04001394 # Deliberately ignore the return code of 'tput smam'.
1395 subprocess.call(["tput", "smam"])
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001396
1397if __name__ == "__main__":
1398 sys.exit(main())