1eb8dc403SDave Cobbley#!/usr/bin/env python3 2eb8dc403SDave Cobbley 3eb8dc403SDave Cobbley# Handle running OE images standalone with QEMU 4eb8dc403SDave Cobbley# 5eb8dc403SDave Cobbley# Copyright (C) 2006-2011 Linux Foundation 6eb8dc403SDave Cobbley# Copyright (c) 2016 Wind River Systems, Inc. 7eb8dc403SDave Cobbley# 8c342db35SBrad Bishop# SPDX-License-Identifier: GPL-2.0-only 9eb8dc403SDave Cobbley# 10eb8dc403SDave Cobbley 11eb8dc403SDave Cobbleyimport os 12eb8dc403SDave Cobbleyimport sys 13eb8dc403SDave Cobbleyimport logging 14eb8dc403SDave Cobbleyimport subprocess 15eb8dc403SDave Cobbleyimport re 16eb8dc403SDave Cobbleyimport fcntl 17eb8dc403SDave Cobbleyimport shutil 18eb8dc403SDave Cobbleyimport glob 19eb8dc403SDave Cobbleyimport configparser 20004d4995SBrad Bishopimport signal 210903674eSAndrew Geisslerimport time 22eb8dc403SDave Cobbley 23eb8dc403SDave Cobbleyclass RunQemuError(Exception): 24eb8dc403SDave Cobbley """Custom exception to raise on known errors.""" 25eb8dc403SDave Cobbley pass 26eb8dc403SDave Cobbley 27eb8dc403SDave Cobbleyclass OEPathError(RunQemuError): 28eb8dc403SDave Cobbley """Custom Exception to give better guidance on missing binaries""" 29eb8dc403SDave Cobbley def __init__(self, message): 30eb8dc403SDave Cobbley super().__init__("In order for this script to dynamically infer paths\n \ 31eb8dc403SDave Cobbleykernels or filesystem images, you either need bitbake in your PATH\n \ 32eb8dc403SDave Cobbleyor to source oe-init-build-env before running this script.\n\n \ 33eb8dc403SDave CobbleyDynamic path inference can be avoided by passing a *.qemuboot.conf to\n \ 34eb8dc403SDave Cobbleyrunqemu, i.e. `runqemu /path/to/my-image-name.qemuboot.conf`\n\n %s" % message) 35eb8dc403SDave Cobbley 36eb8dc403SDave Cobbley 37eb8dc403SDave Cobbleydef create_logger(): 38eb8dc403SDave Cobbley logger = logging.getLogger('runqemu') 39eb8dc403SDave Cobbley logger.setLevel(logging.INFO) 40eb8dc403SDave Cobbley 41eb8dc403SDave Cobbley # create console handler and set level to debug 42eb8dc403SDave Cobbley ch = logging.StreamHandler() 43eb8dc403SDave Cobbley ch.setLevel(logging.DEBUG) 44eb8dc403SDave Cobbley 45eb8dc403SDave Cobbley # create formatter 46eb8dc403SDave Cobbley formatter = logging.Formatter('%(name)s - %(levelname)s - %(message)s') 47eb8dc403SDave Cobbley 48eb8dc403SDave Cobbley # add formatter to ch 49eb8dc403SDave Cobbley ch.setFormatter(formatter) 50eb8dc403SDave Cobbley 51eb8dc403SDave Cobbley # add ch to logger 52eb8dc403SDave Cobbley logger.addHandler(ch) 53eb8dc403SDave Cobbley 54eb8dc403SDave Cobbley return logger 55eb8dc403SDave Cobbley 56eb8dc403SDave Cobbleylogger = create_logger() 57eb8dc403SDave Cobbley 58eb8dc403SDave Cobbleydef print_usage(): 59eb8dc403SDave Cobbley print(""" 60eb8dc403SDave CobbleyUsage: you can run this script with any valid combination 61eb8dc403SDave Cobbleyof the following environment variables (in any order): 62eb8dc403SDave Cobbley KERNEL - the kernel image file to use 63c68388fcSBrad Bishop BIOS - the bios image file to use 64eb8dc403SDave Cobbley ROOTFS - the rootfs image file or nfsroot directory to use 65eb8dc403SDave Cobbley DEVICE_TREE - the device tree blob to use 66eb8dc403SDave Cobbley MACHINE - the machine name (optional, autodetected from KERNEL filename if unspecified) 67eb8dc403SDave Cobbley Simplified QEMU command-line options can be passed with: 68eb8dc403SDave Cobbley nographic - disable video console 698e7b46e2SPatrick Williams nonetwork - disable network connectivity 7090fd73cbSAndrew Geissler novga - Disable VGA emulation completely 71a34c030eSBrad Bishop sdl - choose the SDL UI frontend 72a34c030eSBrad Bishop gtk - choose the Gtk UI frontend 736dbb316aSBrad Bishop gl - enable virgl-based GL acceleration (also needs gtk or sdl options) 746dbb316aSBrad Bishop gl-es - enable virgl-based GL acceleration, using OpenGL ES (also needs gtk or sdl options) 756dbb316aSBrad Bishop egl-headless - enable headless EGL output; use vnc (via publicvnc option) or spice to see it 76d159c7fbSAndrew Geissler (hint: if /dev/dri/renderD* is absent due to lack of suitable GPU, 'modprobe vgem' will create 779aee5003SAndrew Geissler one suitable for mesa llvmpipe software renderer) 78eb8dc403SDave Cobbley serial - enable a serial console on /dev/ttyS0 7919323693SBrad Bishop serialstdio - enable a serial console on the console (regardless of graphics mode) 809aee5003SAndrew Geissler slirp - enable user networking, no root privilege is required 819aee5003SAndrew Geissler snapshot - don't write changes back to images 82eb8dc403SDave Cobbley kvm - enable KVM when running x86/x86_64 (VT-capable CPU required) 83eb8dc403SDave Cobbley kvm-vhost - enable KVM with vhost when running x86/x86_64 (VT-capable CPU required) 84eb8dc403SDave Cobbley publicvnc - enable a VNC server open to all hosts 85eb8dc403SDave Cobbley audio - enable audio 86fc113eadSAndrew Geissler guestagent - enable guest agent communication 87*169d7bccSPatrick Williams qmp=<path> - create a QMP socket (defaults to unix:qmp.sock if unspecified) 88eb8dc403SDave Cobbley [*/]ovmf* - OVMF firmware file or base name for booting with UEFI 89eb8dc403SDave Cobbley tcpserial=<port> - specify tcp serial port number 90eb8dc403SDave Cobbley qemuparams=<xyz> - specify custom parameters to QEMU 91eb8dc403SDave Cobbley bootparams=<xyz> - specify custom kernel parameters during boot 92eb8dc403SDave Cobbley help, -h, --help: print this text 93eb8dc403SDave Cobbley -d, --debug: Enable debug output 9479641f25SBrad Bishop -q, --quiet: Hide most output except error messages 95eb8dc403SDave Cobbley 96eb8dc403SDave CobbleyExamples: 97eb8dc403SDave Cobbley runqemu 98eb8dc403SDave Cobbley runqemu qemuarm 99eb8dc403SDave Cobbley runqemu tmp/deploy/images/qemuarm 100eb8dc403SDave Cobbley runqemu tmp/deploy/images/qemux86/<qemuboot.conf> 101eb8dc403SDave Cobbley runqemu qemux86-64 core-image-sato ext4 102eb8dc403SDave Cobbley runqemu qemux86-64 wic-image-minimal wic 103eb8dc403SDave Cobbley runqemu path/to/bzImage-qemux86.bin path/to/nfsrootdir/ serial 104d1e89497SAndrew Geissler runqemu qemux86 iso/hddimg/wic.vmdk/wic.vhd/wic.vhdx/wic.qcow2/wic.vdi/ramfs/cpio.gz... 105eb8dc403SDave Cobbley runqemu qemux86 qemuparams="-m 256" 106eb8dc403SDave Cobbley runqemu qemux86 bootparams="psplash=false" 107eb8dc403SDave Cobbley runqemu path/to/<image>-<machine>.wic 108eb8dc403SDave Cobbley runqemu path/to/<image>-<machine>.wic.vmdk 109d1e89497SAndrew Geissler runqemu path/to/<image>-<machine>.wic.vhdx 110d1e89497SAndrew Geissler runqemu path/to/<image>-<machine>.wic.vhd 111eb8dc403SDave Cobbley""") 112eb8dc403SDave Cobbley 113eb8dc403SDave Cobbleydef check_tun(): 114eb8dc403SDave Cobbley """Check /dev/net/tun""" 115eb8dc403SDave Cobbley dev_tun = '/dev/net/tun' 116eb8dc403SDave Cobbley if not os.path.exists(dev_tun): 117eb8dc403SDave Cobbley raise RunQemuError("TUN control device %s is unavailable; you may need to enable TUN (e.g. sudo modprobe tun)" % dev_tun) 118eb8dc403SDave Cobbley 119eb8dc403SDave Cobbley if not os.access(dev_tun, os.W_OK): 120eb8dc403SDave Cobbley raise RunQemuError("TUN control device %s is not writable, please fix (e.g. sudo chmod 666 %s)" % (dev_tun, dev_tun)) 121eb8dc403SDave Cobbley 122fc113eadSAndrew Geisslerdef get_first_file(globs): 123fc113eadSAndrew Geissler """Return first file found in wildcard globs""" 124fc113eadSAndrew Geissler for g in globs: 125fc113eadSAndrew Geissler all_files = glob.glob(g) 126eb8dc403SDave Cobbley if all_files: 127eb8dc403SDave Cobbley for f in all_files: 128eb8dc403SDave Cobbley if not os.path.isdir(f): 129eb8dc403SDave Cobbley return f 130eb8dc403SDave Cobbley return '' 131eb8dc403SDave Cobbley 132eb8dc403SDave Cobbleyclass BaseConfig(object): 133eb8dc403SDave Cobbley def __init__(self): 134eb8dc403SDave Cobbley # The self.d saved vars from self.set(), part of them are from qemuboot.conf 135eb8dc403SDave Cobbley self.d = {'QB_KERNEL_ROOT': '/dev/vda'} 136eb8dc403SDave Cobbley 137eb8dc403SDave Cobbley # Supported env vars, add it here if a var can be got from env, 138eb8dc403SDave Cobbley # and don't use os.getenv in the code. 139eb8dc403SDave Cobbley self.env_vars = ('MACHINE', 140eb8dc403SDave Cobbley 'ROOTFS', 141eb8dc403SDave Cobbley 'KERNEL', 142c68388fcSBrad Bishop 'BIOS', 143eb8dc403SDave Cobbley 'DEVICE_TREE', 144eb8dc403SDave Cobbley 'DEPLOY_DIR_IMAGE', 145eb8dc403SDave Cobbley 'OE_TMPDIR', 146eb8dc403SDave Cobbley 'OECORE_NATIVE_SYSROOT', 14782c905dcSAndrew Geissler 'MULTICONFIG', 14895ac1b8dSAndrew Geissler 'SERIAL_CONSOLES', 149eb8dc403SDave Cobbley ) 150eb8dc403SDave Cobbley 151eb8dc403SDave Cobbley self.qemu_opt = '' 152eb8dc403SDave Cobbley self.qemu_opt_script = '' 15399467dabSAndrew Geissler self.qemuparams = '' 154eb8dc403SDave Cobbley self.nfs_server = '' 155eb8dc403SDave Cobbley self.rootfs = '' 156eb8dc403SDave Cobbley # File name(s) of a OVMF firmware file or variable store, 157eb8dc403SDave Cobbley # to be added with -drive if=pflash. 158eb8dc403SDave Cobbley # Found in the same places as the rootfs, with or without one of 159eb8dc403SDave Cobbley # these suffices: qcow2, bin. 160eb8dc403SDave Cobbley self.ovmf_bios = [] 16108902b01SBrad Bishop # When enrolling default Secure Boot keys, the hypervisor 16208902b01SBrad Bishop # must provide the Platform Key and the first Key Exchange Key 16308902b01SBrad Bishop # certificate in the Type 11 SMBIOS table. 16408902b01SBrad Bishop self.ovmf_secboot_pkkek1 = '' 165eb8dc403SDave Cobbley self.qemuboot = '' 166eb8dc403SDave Cobbley self.qbconfload = False 167eb8dc403SDave Cobbley self.kernel = '' 168c68388fcSBrad Bishop self.bios = '' 169eb8dc403SDave Cobbley self.kernel_cmdline = '' 170eb8dc403SDave Cobbley self.kernel_cmdline_script = '' 171eb8dc403SDave Cobbley self.bootparams = '' 172eb8dc403SDave Cobbley self.dtb = '' 173eb8dc403SDave Cobbley self.fstype = '' 174eb8dc403SDave Cobbley self.kvm_enabled = False 175eb8dc403SDave Cobbley self.vhost_enabled = False 176eb8dc403SDave Cobbley self.slirp_enabled = False 17782c905dcSAndrew Geissler self.net_bridge = None 178eb8dc403SDave Cobbley self.nfs_instance = 0 179eb8dc403SDave Cobbley self.nfs_running = False 18019323693SBrad Bishop self.serialconsole = False 181eb8dc403SDave Cobbley self.serialstdio = False 18295ac1b8dSAndrew Geissler self.nographic = False 1838e7b46e2SPatrick Williams self.nonetwork = False 18495ac1b8dSAndrew Geissler self.sdl = False 18595ac1b8dSAndrew Geissler self.gtk = False 18695ac1b8dSAndrew Geissler self.gl = False 18795ac1b8dSAndrew Geissler self.gl_es = False 18895ac1b8dSAndrew Geissler self.egl_headless = False 18903907ee1SPatrick Williams self.publicvnc = False 19095ac1b8dSAndrew Geissler self.novga = False 191eb8dc403SDave Cobbley self.cleantap = False 192eb8dc403SDave Cobbley self.saved_stty = '' 193eb8dc403SDave Cobbley self.audio_enabled = False 194eb8dc403SDave Cobbley self.tcpserial_portnum = '' 19508902b01SBrad Bishop self.taplock = '' 19608902b01SBrad Bishop self.taplock_descriptor = None 19708902b01SBrad Bishop self.portlocks = {} 198eb8dc403SDave Cobbley self.bitbake_e = '' 199eb8dc403SDave Cobbley self.snapshot = False 200d1e89497SAndrew Geissler self.wictypes = ('wic', 'wic.vmdk', 'wic.qcow2', 'wic.vdi', "wic.vhd", "wic.vhdx") 201eb8dc403SDave Cobbley self.fstypes = ('ext2', 'ext3', 'ext4', 'jffs2', 'nfs', 'btrfs', 202ac13d5f3SPatrick Williams 'cpio.gz', 'cpio', 'ramfs', 'tar.bz2', 'tar.gz', 203ac13d5f3SPatrick Williams 'squashfs', 'squashfs-xz', 'squashfs-lzo', 204ac13d5f3SPatrick Williams 'squashfs-lz4', 'squashfs-zst') 20508902b01SBrad Bishop self.vmtypes = ('hddimg', 'iso') 20615ae2509SBrad Bishop self.fsinfo = {} 207eb8dc403SDave Cobbley self.network_device = "-device e1000,netdev=net0,mac=@MAC@" 20882c905dcSAndrew Geissler self.cmdline_ip_slirp = "ip=dhcp" 2092a25492cSPatrick Williams self.cmdline_ip_tap = "ip=192.168.7.@CLIENT@::192.168.7.@GATEWAY@:255.255.255.0::eth0:off:8.8.8.8 net.ifnames=0" 210eb8dc403SDave Cobbley # Use different mac section for tap and slirp to avoid 211eb8dc403SDave Cobbley # conflicts, e.g., when one is running with tap, the other is 212eb8dc403SDave Cobbley # running with slirp. 213eb8dc403SDave Cobbley # The last section is dynamic, which is for avoiding conflicts, 214eb8dc403SDave Cobbley # when multiple qemus are running, e.g., when multiple tap or 215eb8dc403SDave Cobbley # slirp qemus are running. 216eb8dc403SDave Cobbley self.mac_tap = "52:54:00:12:34:" 217eb8dc403SDave Cobbley self.mac_slirp = "52:54:00:12:35:" 218004d4995SBrad Bishop # pid of the actual qemu process 2192390b1b6SPatrick Williams self.qemu_environ = os.environ.copy() 2206aa7eec5SAndrew Geissler self.qemuprocess = None 221004d4995SBrad Bishop # avoid cleanup twice 222004d4995SBrad Bishop self.cleaned = False 223c926e17cSAndrew Geissler # Files to cleanup after run 224c926e17cSAndrew Geissler self.cleanup_files = [] 225*169d7bccSPatrick Williams self.qmp = None 226fc113eadSAndrew Geissler self.guest_agent = False 227fc113eadSAndrew Geissler self.guest_agent_sockpath = '/tmp/qga.sock' 228eb8dc403SDave Cobbley 22908902b01SBrad Bishop def acquire_taplock(self, error=True): 23008902b01SBrad Bishop logger.debug("Acquiring lockfile %s..." % self.taplock) 231eb8dc403SDave Cobbley try: 23208902b01SBrad Bishop self.taplock_descriptor = open(self.taplock, 'w') 23308902b01SBrad Bishop fcntl.flock(self.taplock_descriptor, fcntl.LOCK_EX|fcntl.LOCK_NB) 234eb8dc403SDave Cobbley except Exception as e: 23508902b01SBrad Bishop msg = "Acquiring lockfile %s failed: %s" % (self.taplock, e) 236eb8dc403SDave Cobbley if error: 237eb8dc403SDave Cobbley logger.error(msg) 238eb8dc403SDave Cobbley else: 239eb8dc403SDave Cobbley logger.info(msg) 24008902b01SBrad Bishop if self.taplock_descriptor: 24108902b01SBrad Bishop self.taplock_descriptor.close() 24208902b01SBrad Bishop self.taplock_descriptor = None 243eb8dc403SDave Cobbley return False 244eb8dc403SDave Cobbley return True 245eb8dc403SDave Cobbley 24608902b01SBrad Bishop def release_taplock(self): 24708902b01SBrad Bishop if self.taplock_descriptor: 248f86d0556SBrad Bishop logger.debug("Releasing lockfile for tap device '%s'" % self.tap) 2495f35090dSAndrew Geissler # We pass the fd to the qemu process and if we unlock here, it would unlock for 2505f35090dSAndrew Geissler # that too. Therefore don't unlock, just close 2515f35090dSAndrew Geissler # fcntl.flock(self.taplock_descriptor, fcntl.LOCK_UN) 25208902b01SBrad Bishop self.taplock_descriptor.close() 2535f35090dSAndrew Geissler # Removing the file is a potential race, don't do that either 2545f35090dSAndrew Geissler # os.remove(self.taplock) 25508902b01SBrad Bishop self.taplock_descriptor = None 25608902b01SBrad Bishop 25708902b01SBrad Bishop def check_free_port(self, host, port, lockdir): 25808902b01SBrad Bishop """ Check whether the port is free or not """ 25908902b01SBrad Bishop import socket 26008902b01SBrad Bishop from contextlib import closing 26108902b01SBrad Bishop 26208902b01SBrad Bishop lockfile = os.path.join(lockdir, str(port) + '.lock') 26308902b01SBrad Bishop if self.acquire_portlock(lockfile): 26408902b01SBrad Bishop with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock: 26508902b01SBrad Bishop if sock.connect_ex((host, port)) == 0: 26608902b01SBrad Bishop # Port is open, so not free 26708902b01SBrad Bishop self.release_portlock(lockfile) 26808902b01SBrad Bishop return False 26908902b01SBrad Bishop else: 27008902b01SBrad Bishop # Port is not open, so free 27108902b01SBrad Bishop return True 27208902b01SBrad Bishop else: 27308902b01SBrad Bishop return False 27408902b01SBrad Bishop 27508902b01SBrad Bishop def acquire_portlock(self, lockfile): 27608902b01SBrad Bishop logger.debug("Acquiring lockfile %s..." % lockfile) 27708902b01SBrad Bishop try: 27808902b01SBrad Bishop portlock_descriptor = open(lockfile, 'w') 27908902b01SBrad Bishop self.portlocks.update({lockfile: portlock_descriptor}) 28008902b01SBrad Bishop fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_EX|fcntl.LOCK_NB) 28108902b01SBrad Bishop except Exception as e: 28208902b01SBrad Bishop msg = "Acquiring lockfile %s failed: %s" % (lockfile, e) 28308902b01SBrad Bishop logger.info(msg) 28408902b01SBrad Bishop if lockfile in self.portlocks.keys() and self.portlocks[lockfile]: 28508902b01SBrad Bishop self.portlocks[lockfile].close() 28608902b01SBrad Bishop del self.portlocks[lockfile] 28708902b01SBrad Bishop return False 28808902b01SBrad Bishop return True 28908902b01SBrad Bishop 29008902b01SBrad Bishop def release_portlock(self, lockfile=None): 29108902b01SBrad Bishop if lockfile != None: 29208902b01SBrad Bishop logger.debug("Releasing lockfile '%s'" % lockfile) 2935f35090dSAndrew Geissler # We pass the fd to the qemu process and if we unlock here, it would unlock for 2945f35090dSAndrew Geissler # that too. Therefore don't unlock, just close 2955f35090dSAndrew Geissler # fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_UN) 29608902b01SBrad Bishop self.portlocks[lockfile].close() 2975f35090dSAndrew Geissler # Removing the file is a potential race, don't do that either 2985f35090dSAndrew Geissler # os.remove(lockfile) 29908902b01SBrad Bishop del self.portlocks[lockfile] 30008902b01SBrad Bishop elif len(self.portlocks): 30108902b01SBrad Bishop for lockfile, descriptor in self.portlocks.items(): 30208902b01SBrad Bishop logger.debug("Releasing lockfile '%s'" % lockfile) 3035f35090dSAndrew Geissler # We pass the fd to the qemu process and if we unlock here, it would unlock for 3045f35090dSAndrew Geissler # that too. Therefore don't unlock, just close 3055f35090dSAndrew Geissler # fcntl.flock(descriptor, fcntl.LOCK_UN) 30608902b01SBrad Bishop descriptor.close() 3075f35090dSAndrew Geissler # Removing the file is a potential race, don't do that either 3085f35090dSAndrew Geissler # os.remove(lockfile) 30908902b01SBrad Bishop self.portlocks = {} 310eb8dc403SDave Cobbley 311eb8dc403SDave Cobbley def get(self, key): 312eb8dc403SDave Cobbley if key in self.d: 313eb8dc403SDave Cobbley return self.d.get(key) 314eb8dc403SDave Cobbley elif os.getenv(key): 315eb8dc403SDave Cobbley return os.getenv(key) 316eb8dc403SDave Cobbley else: 317eb8dc403SDave Cobbley return '' 318eb8dc403SDave Cobbley 319eb8dc403SDave Cobbley def set(self, key, value): 320eb8dc403SDave Cobbley self.d[key] = value 321eb8dc403SDave Cobbley 322eb8dc403SDave Cobbley def is_deploy_dir_image(self, p): 323eb8dc403SDave Cobbley if os.path.isdir(p): 324eb8dc403SDave Cobbley if not re.search('.qemuboot.conf$', '\n'.join(os.listdir(p)), re.M): 325eb8dc403SDave Cobbley logger.debug("Can't find required *.qemuboot.conf in %s" % p) 326eb8dc403SDave Cobbley return False 327eb8dc403SDave Cobbley if not any(map(lambda name: '-image-' in name, os.listdir(p))): 328eb8dc403SDave Cobbley logger.debug("Can't find *-image-* in %s" % p) 329eb8dc403SDave Cobbley return False 330eb8dc403SDave Cobbley return True 331eb8dc403SDave Cobbley else: 332eb8dc403SDave Cobbley return False 333eb8dc403SDave Cobbley 334eb8dc403SDave Cobbley def check_arg_fstype(self, fst): 335eb8dc403SDave Cobbley """Check and set FSTYPE""" 33615ae2509SBrad Bishop if fst not in self.fstypes + self.vmtypes + self.wictypes: 3371a4b7ee2SBrad Bishop logger.warning("Maybe unsupported FSTYPE: %s" % fst) 338eb8dc403SDave Cobbley if not self.fstype or self.fstype == fst: 339eb8dc403SDave Cobbley if fst == 'ramfs': 340eb8dc403SDave Cobbley fst = 'cpio.gz' 341eb8dc403SDave Cobbley if fst in ('tar.bz2', 'tar.gz'): 342eb8dc403SDave Cobbley fst = 'nfs' 343eb8dc403SDave Cobbley self.fstype = fst 344eb8dc403SDave Cobbley else: 345eb8dc403SDave Cobbley raise RunQemuError("Conflicting: FSTYPE %s and %s" % (self.fstype, fst)) 346eb8dc403SDave Cobbley 347eb8dc403SDave Cobbley def set_machine_deploy_dir(self, machine, deploy_dir_image): 348eb8dc403SDave Cobbley """Set MACHINE and DEPLOY_DIR_IMAGE""" 349eb8dc403SDave Cobbley logger.debug('MACHINE: %s' % machine) 350eb8dc403SDave Cobbley self.set("MACHINE", machine) 351eb8dc403SDave Cobbley logger.debug('DEPLOY_DIR_IMAGE: %s' % deploy_dir_image) 352eb8dc403SDave Cobbley self.set("DEPLOY_DIR_IMAGE", deploy_dir_image) 353eb8dc403SDave Cobbley 354eb8dc403SDave Cobbley def check_arg_nfs(self, p): 355eb8dc403SDave Cobbley if os.path.isdir(p): 356eb8dc403SDave Cobbley self.rootfs = p 357eb8dc403SDave Cobbley else: 358eb8dc403SDave Cobbley m = re.match('(.*):(.*)', p) 359eb8dc403SDave Cobbley self.nfs_server = m.group(1) 360eb8dc403SDave Cobbley self.rootfs = m.group(2) 361eb8dc403SDave Cobbley self.check_arg_fstype('nfs') 362eb8dc403SDave Cobbley 363eb8dc403SDave Cobbley def check_arg_path(self, p): 364eb8dc403SDave Cobbley """ 365eb8dc403SDave Cobbley - Check whether it is <image>.qemuboot.conf or contains <image>.qemuboot.conf 3669aee5003SAndrew Geissler - Check whether it is a kernel file 3679aee5003SAndrew Geissler - Check whether it is an image file 3689aee5003SAndrew Geissler - Check whether it is an NFS dir 3699aee5003SAndrew Geissler - Check whether it is an OVMF flash file 370eb8dc403SDave Cobbley """ 371eb8dc403SDave Cobbley if p.endswith('.qemuboot.conf'): 372eb8dc403SDave Cobbley self.qemuboot = p 373eb8dc403SDave Cobbley self.qbconfload = True 374*169d7bccSPatrick Williams elif re.search('\\.bin$', p) or re.search('bzImage', p) or \ 375eb8dc403SDave Cobbley re.search('zImage', p) or re.search('vmlinux', p) or \ 376eb8dc403SDave Cobbley re.search('fitImage', p) or re.search('uImage', p): 377eb8dc403SDave Cobbley self.kernel = p 378*169d7bccSPatrick Williams elif os.path.isfile(p) and ('-image-' in os.path.basename(p) or '.rootfs.' in os.path.basename(p)): 379eb8dc403SDave Cobbley self.rootfs = p 3809aee5003SAndrew Geissler # Check filename against self.fstypes can handle <file>.cpio.gz, 381eb8dc403SDave Cobbley # otherwise, its type would be "gz", which is incorrect. 382eb8dc403SDave Cobbley fst = "" 383eb8dc403SDave Cobbley for t in self.fstypes: 384eb8dc403SDave Cobbley if p.endswith(t): 385eb8dc403SDave Cobbley fst = t 386eb8dc403SDave Cobbley break 387eb8dc403SDave Cobbley if not fst: 388*169d7bccSPatrick Williams m = re.search('.*\\.(.*)$', self.rootfs) 389eb8dc403SDave Cobbley if m: 390eb8dc403SDave Cobbley fst = m.group(1) 391eb8dc403SDave Cobbley if fst: 392eb8dc403SDave Cobbley self.check_arg_fstype(fst) 393*169d7bccSPatrick Williams qb = re.sub('\\.' + fst + "$", '.qemuboot.conf', self.rootfs) 3948e7b46e2SPatrick Williams if os.path.exists(qb): 3958e7b46e2SPatrick Williams self.qemuboot = qb 3968e7b46e2SPatrick Williams self.qbconfload = True 3978e7b46e2SPatrick Williams else: 3988e7b46e2SPatrick Williams logger.warning("%s doesn't exist, will try to remove '.rootfs' from filename" % qb) 3998e7b46e2SPatrick Williams # They to remove .rootfs (IMAGE_NAME_SUFFIX) as well 400*169d7bccSPatrick Williams qb = re.sub('\\.rootfs.qemuboot.conf$', '.qemuboot.conf', qb) 401eb8dc403SDave Cobbley if os.path.exists(qb): 402eb8dc403SDave Cobbley self.qemuboot = qb 403eb8dc403SDave Cobbley self.qbconfload = True 404eb8dc403SDave Cobbley else: 4051a4b7ee2SBrad Bishop logger.warning("%s doesn't exist" % qb) 406eb8dc403SDave Cobbley else: 407eb8dc403SDave Cobbley raise RunQemuError("Can't find FSTYPE from: %s" % p) 408eb8dc403SDave Cobbley 409eb8dc403SDave Cobbley elif os.path.isdir(p) or re.search(':', p) and re.search('/', p): 410eb8dc403SDave Cobbley if self.is_deploy_dir_image(p): 411eb8dc403SDave Cobbley logger.debug('DEPLOY_DIR_IMAGE: %s' % p) 412eb8dc403SDave Cobbley self.set("DEPLOY_DIR_IMAGE", p) 413eb8dc403SDave Cobbley else: 414eb8dc403SDave Cobbley logger.debug("Assuming %s is an nfs rootfs" % p) 415eb8dc403SDave Cobbley self.check_arg_nfs(p) 416eb8dc403SDave Cobbley elif os.path.basename(p).startswith('ovmf'): 417eb8dc403SDave Cobbley self.ovmf_bios.append(p) 418eb8dc403SDave Cobbley else: 419eb8dc403SDave Cobbley raise RunQemuError("Unknown path arg %s" % p) 420eb8dc403SDave Cobbley 421eb8dc403SDave Cobbley def check_arg_machine(self, arg): 422eb8dc403SDave Cobbley """Check whether it is a machine""" 423eb8dc403SDave Cobbley if self.get('MACHINE') == arg: 424eb8dc403SDave Cobbley return 425eb8dc403SDave Cobbley elif self.get('MACHINE') and self.get('MACHINE') != arg: 426eb8dc403SDave Cobbley raise RunQemuError("Maybe conflicted MACHINE: %s vs %s" % (self.get('MACHINE'), arg)) 427eb8dc403SDave Cobbley elif re.search('/', arg): 428eb8dc403SDave Cobbley raise RunQemuError("Unknown arg: %s" % arg) 429eb8dc403SDave Cobbley 430eb8dc403SDave Cobbley logger.debug('Assuming MACHINE = %s' % arg) 431eb8dc403SDave Cobbley 432eb8dc403SDave Cobbley # if we're running under testimage, or similarly as a child 433eb8dc403SDave Cobbley # of an existing bitbake invocation, we can't invoke bitbake 434eb8dc403SDave Cobbley # to validate the MACHINE setting and must assume it's correct... 435eb8dc403SDave Cobbley # FIXME: testimage.bbclass exports these two variables into env, 436eb8dc403SDave Cobbley # are there other scenarios in which we need to support being 437eb8dc403SDave Cobbley # invoked by bitbake? 438eb8dc403SDave Cobbley deploy = self.get('DEPLOY_DIR_IMAGE') 4398e7b46e2SPatrick Williams image_link_name = self.get('IMAGE_LINK_NAME') 440eb8dc403SDave Cobbley bbchild = deploy and self.get('OE_TMPDIR') 441eb8dc403SDave Cobbley if bbchild: 442eb8dc403SDave Cobbley self.set_machine_deploy_dir(arg, deploy) 443eb8dc403SDave Cobbley return 444eb8dc403SDave Cobbley # also check whether we're running under a sourced toolchain 445eb8dc403SDave Cobbley # environment file 446eb8dc403SDave Cobbley if self.get('OECORE_NATIVE_SYSROOT'): 447eb8dc403SDave Cobbley self.set("MACHINE", arg) 448eb8dc403SDave Cobbley return 449eb8dc403SDave Cobbley 45082c905dcSAndrew Geissler self.bitbake_e = self.run_bitbake_env(arg) 451eb8dc403SDave Cobbley # bitbake -e doesn't report invalid MACHINE as an error, so 452eb8dc403SDave Cobbley # let's check DEPLOY_DIR_IMAGE to make sure that it is a valid 453eb8dc403SDave Cobbley # MACHINE. 454eb8dc403SDave Cobbley s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M) 455eb8dc403SDave Cobbley if s: 456eb8dc403SDave Cobbley deploy_dir_image = s.group(1) 457eb8dc403SDave Cobbley else: 458eb8dc403SDave Cobbley raise RunQemuError("bitbake -e %s" % self.bitbake_e) 459eb8dc403SDave Cobbley if self.is_deploy_dir_image(deploy_dir_image): 460eb8dc403SDave Cobbley self.set_machine_deploy_dir(arg, deploy_dir_image) 461eb8dc403SDave Cobbley else: 462eb8dc403SDave Cobbley logger.error("%s not a directory valid DEPLOY_DIR_IMAGE" % deploy_dir_image) 463eb8dc403SDave Cobbley self.set("MACHINE", arg) 4648e7b46e2SPatrick Williams if not image_link_name: 4658e7b46e2SPatrick Williams s = re.search('^IMAGE_LINK_NAME="(.*)"', self.bitbake_e, re.M) 4668e7b46e2SPatrick Williams if s: 4678e7b46e2SPatrick Williams image_link_name = s.group(1) 4688e7b46e2SPatrick Williams self.set("IMAGE_LINK_NAME", image_link_name) 4698e7b46e2SPatrick Williams logger.debug('Using IMAGE_LINK_NAME = "%s"' % image_link_name) 470eb8dc403SDave Cobbley 471c182c62dSAndrew Geissler def set_dri_path(self): 472fc113eadSAndrew Geissler drivers_path = os.path.join(self.bindir_native, '../lib/dri') 473fc113eadSAndrew Geissler if not os.path.exists(drivers_path) or not os.listdir(drivers_path): 474fc113eadSAndrew Geissler raise RunQemuError(""" 475fc113eadSAndrew Geisslerqemu has been built without opengl support and accelerated graphics support is not available. 476fc113eadSAndrew GeisslerTo enable it, add: 477fc113eadSAndrew GeisslerDISTRO_FEATURES_NATIVE:append = " opengl" 478fc113eadSAndrew GeisslerDISTRO_FEATURES_NATIVESDK:append = " opengl" 479fc113eadSAndrew Geisslerto your build configuration. 480fc113eadSAndrew Geissler""") 481fc113eadSAndrew Geissler self.qemu_environ['LIBGL_DRIVERS_PATH'] = drivers_path 4827e0e3c0cSAndrew Geissler 483c182c62dSAndrew Geissler def check_args(self): 484c182c62dSAndrew Geissler for debug in ("-d", "--debug"): 485c182c62dSAndrew Geissler if debug in sys.argv: 486c182c62dSAndrew Geissler logger.setLevel(logging.DEBUG) 487c182c62dSAndrew Geissler sys.argv.remove(debug) 488c182c62dSAndrew Geissler 489c182c62dSAndrew Geissler for quiet in ("-q", "--quiet"): 490c182c62dSAndrew Geissler if quiet in sys.argv: 491c182c62dSAndrew Geissler logger.setLevel(logging.ERROR) 492c182c62dSAndrew Geissler sys.argv.remove(quiet) 493c182c62dSAndrew Geissler 494c182c62dSAndrew Geissler if 'gl' not in sys.argv[1:] and 'gl-es' not in sys.argv[1:]: 4952390b1b6SPatrick Williams self.qemu_environ['SDL_RENDER_DRIVER'] = 'software' 4962390b1b6SPatrick Williams self.qemu_environ['SDL_FRAMEBUFFER_ACCELERATION'] = 'false' 497c182c62dSAndrew Geissler 498c182c62dSAndrew Geissler unknown_arg = "" 499c182c62dSAndrew Geissler for arg in sys.argv[1:]: 500c182c62dSAndrew Geissler if arg in self.fstypes + self.vmtypes + self.wictypes: 501c182c62dSAndrew Geissler self.check_arg_fstype(arg) 502c182c62dSAndrew Geissler elif arg == 'nographic': 50395ac1b8dSAndrew Geissler self.nographic = True 5048e7b46e2SPatrick Williams elif arg == "nonetwork": 5058e7b46e2SPatrick Williams self.nonetwork = True 506c182c62dSAndrew Geissler elif arg == 'sdl': 50795ac1b8dSAndrew Geissler self.sdl = True 508c182c62dSAndrew Geissler elif arg == 'gtk': 50995ac1b8dSAndrew Geissler self.gtk = True 51095ac1b8dSAndrew Geissler elif arg == 'gl': 51195ac1b8dSAndrew Geissler self.gl = True 5122390b1b6SPatrick Williams elif arg == 'gl-es': 51395ac1b8dSAndrew Geissler self.gl_es = True 514c182c62dSAndrew Geissler elif arg == 'egl-headless': 51595ac1b8dSAndrew Geissler self.egl_headless = True 51690fd73cbSAndrew Geissler elif arg == 'novga': 51795ac1b8dSAndrew Geissler self.novga = True 518eb8dc403SDave Cobbley elif arg == 'serial': 51919323693SBrad Bishop self.serialconsole = True 52019323693SBrad Bishop elif arg == "serialstdio": 521eb8dc403SDave Cobbley self.serialstdio = True 522eb8dc403SDave Cobbley elif arg == 'audio': 523eb8dc403SDave Cobbley logger.info("Enabling audio in qemu") 524eb8dc403SDave Cobbley logger.info("Please install sound drivers in linux host") 525eb8dc403SDave Cobbley self.audio_enabled = True 526eb8dc403SDave Cobbley elif arg == 'kvm': 527eb8dc403SDave Cobbley self.kvm_enabled = True 528eb8dc403SDave Cobbley elif arg == 'kvm-vhost': 529eb8dc403SDave Cobbley self.vhost_enabled = True 530eb8dc403SDave Cobbley elif arg == 'slirp': 531eb8dc403SDave Cobbley self.slirp_enabled = True 53282c905dcSAndrew Geissler elif arg.startswith('bridge='): 53382c905dcSAndrew Geissler self.net_bridge = '%s' % arg[len('bridge='):] 534eb8dc403SDave Cobbley elif arg == 'snapshot': 535eb8dc403SDave Cobbley self.snapshot = True 536eb8dc403SDave Cobbley elif arg == 'publicvnc': 53703907ee1SPatrick Williams self.publicvnc = True 538eb8dc403SDave Cobbley self.qemu_opt_script += ' -vnc :0' 539fc113eadSAndrew Geissler elif arg == 'guestagent': 540fc113eadSAndrew Geissler self.guest_agent = True 541*169d7bccSPatrick Williams elif arg == "qmp": 542*169d7bccSPatrick Williams self.qmp = "unix:qmp.sock" 543*169d7bccSPatrick Williams elif arg.startswith("qmp="): 544*169d7bccSPatrick Williams self.qmp = arg[len('qmp='):] 545fc113eadSAndrew Geissler elif arg.startswith('guestagent-sockpath='): 546fc113eadSAndrew Geissler self.guest_agent_sockpath = '%s' % arg[len('guestagent-sockpath='):] 547eb8dc403SDave Cobbley elif arg.startswith('tcpserial='): 54815ae2509SBrad Bishop self.tcpserial_portnum = '%s' % arg[len('tcpserial='):] 549eb8dc403SDave Cobbley elif arg.startswith('qemuparams='): 55099467dabSAndrew Geissler self.qemuparams = ' %s' % arg[len('qemuparams='):] 551eb8dc403SDave Cobbley elif arg.startswith('bootparams='): 552eb8dc403SDave Cobbley self.bootparams = arg[len('bootparams='):] 553eb8dc403SDave Cobbley elif os.path.exists(arg) or (re.search(':', arg) and re.search('/', arg)): 554eb8dc403SDave Cobbley self.check_arg_path(os.path.abspath(arg)) 555eb8dc403SDave Cobbley elif re.search(r'-image-|-image$', arg): 556eb8dc403SDave Cobbley # Lazy rootfs 557eb8dc403SDave Cobbley self.rootfs = arg 558eb8dc403SDave Cobbley elif arg.startswith('ovmf'): 559eb8dc403SDave Cobbley self.ovmf_bios.append(arg) 560eb8dc403SDave Cobbley else: 561eb8dc403SDave Cobbley # At last, assume it is the MACHINE 562eb8dc403SDave Cobbley if (not unknown_arg) or unknown_arg == arg: 563eb8dc403SDave Cobbley unknown_arg = arg 564eb8dc403SDave Cobbley else: 565eb8dc403SDave Cobbley raise RunQemuError("Can't handle two unknown args: %s %s\n" 566eb8dc403SDave Cobbley "Try 'runqemu help' on how to use it" % \ 567eb8dc403SDave Cobbley (unknown_arg, arg)) 568eb8dc403SDave Cobbley # Check to make sure it is a valid machine 569a5c52ff0SBrad Bishop if unknown_arg and self.get('MACHINE') != unknown_arg: 570eb8dc403SDave Cobbley if self.get('DEPLOY_DIR_IMAGE'): 571eb8dc403SDave Cobbley machine = os.path.basename(self.get('DEPLOY_DIR_IMAGE')) 572eb8dc403SDave Cobbley if unknown_arg == machine: 573eb8dc403SDave Cobbley self.set("MACHINE", machine) 574eb8dc403SDave Cobbley 575eb8dc403SDave Cobbley self.check_arg_machine(unknown_arg) 576eb8dc403SDave Cobbley 577eb8dc403SDave Cobbley if not (self.get('DEPLOY_DIR_IMAGE') or self.qbconfload): 5788e7b46e2SPatrick Williams self.load_bitbake_env(target=self.rootfs) 579eb8dc403SDave Cobbley s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M) 580eb8dc403SDave Cobbley if s: 581eb8dc403SDave Cobbley self.set("DEPLOY_DIR_IMAGE", s.group(1)) 582eb8dc403SDave Cobbley 5838e7b46e2SPatrick Williams if not self.get('IMAGE_LINK_NAME') and self.rootfs: 5848e7b46e2SPatrick Williams s = re.search('^IMAGE_LINK_NAME="(.*)"', self.bitbake_e, re.M) 5858e7b46e2SPatrick Williams if s: 5868e7b46e2SPatrick Williams image_link_name = s.group(1) 5878e7b46e2SPatrick Williams self.set("IMAGE_LINK_NAME", image_link_name) 5888e7b46e2SPatrick Williams logger.debug('Using IMAGE_LINK_NAME = "%s"' % image_link_name) 5898e7b46e2SPatrick Williams 590eb8dc403SDave Cobbley def check_kvm(self): 591eb8dc403SDave Cobbley """Check kvm and kvm-host""" 592eb8dc403SDave Cobbley if not (self.kvm_enabled or self.vhost_enabled): 593ac69b488SWilliam A. Kennington III self.qemu_opt_script += ' %s %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU'), self.get('QB_SMP')) 594eb8dc403SDave Cobbley return 595eb8dc403SDave Cobbley 596eb8dc403SDave Cobbley if not self.get('QB_CPU_KVM'): 597eb8dc403SDave Cobbley raise RunQemuError("QB_CPU_KVM is NULL, this board doesn't support kvm") 598eb8dc403SDave Cobbley 599ac69b488SWilliam A. Kennington III self.qemu_opt_script += ' %s %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU_KVM'), self.get('QB_SMP')) 600eb8dc403SDave Cobbley yocto_kvm_wiki = "https://wiki.yoctoproject.org/wiki/How_to_enable_KVM_for_Poky_qemu" 601eb8dc403SDave Cobbley yocto_paravirt_kvm_wiki = "https://wiki.yoctoproject.org/wiki/Running_an_x86_Yocto_Linux_image_under_QEMU_KVM" 602eb8dc403SDave Cobbley dev_kvm = '/dev/kvm' 603eb8dc403SDave Cobbley dev_vhost = '/dev/vhost-net' 60415ae2509SBrad Bishop if self.qemu_system.endswith(('i386', 'x86_64')): 605eb8dc403SDave Cobbley with open('/proc/cpuinfo', 'r') as f: 606eb8dc403SDave Cobbley kvm_cap = re.search('vmx|svm', "".join(f.readlines())) 607eb8dc403SDave Cobbley if not kvm_cap: 608eb8dc403SDave Cobbley logger.error("You are trying to enable KVM on a cpu without VT support.") 609eb8dc403SDave Cobbley logger.error("Remove kvm from the command-line, or refer:") 610eb8dc403SDave Cobbley raise RunQemuError(yocto_kvm_wiki) 611eb8dc403SDave Cobbley 612eb8dc403SDave Cobbley if not os.path.exists(dev_kvm): 613eb8dc403SDave Cobbley logger.error("Missing KVM device. Have you inserted kvm modules?") 614eb8dc403SDave Cobbley logger.error("For further help see:") 615eb8dc403SDave Cobbley raise RunQemuError(yocto_kvm_wiki) 616eb8dc403SDave Cobbley 617eb8dc403SDave Cobbley if os.access(dev_kvm, os.W_OK|os.R_OK): 618eb8dc403SDave Cobbley self.qemu_opt_script += ' -enable-kvm' 619eb8dc403SDave Cobbley else: 620eb8dc403SDave Cobbley logger.error("You have no read or write permission on /dev/kvm.") 621eb8dc403SDave Cobbley logger.error("Please change the ownership of this file as described at:") 622eb8dc403SDave Cobbley raise RunQemuError(yocto_kvm_wiki) 623eb8dc403SDave Cobbley 624eb8dc403SDave Cobbley if self.vhost_enabled: 625eb8dc403SDave Cobbley if not os.path.exists(dev_vhost): 626eb8dc403SDave Cobbley logger.error("Missing virtio net device. Have you inserted vhost-net module?") 627eb8dc403SDave Cobbley logger.error("For further help see:") 628eb8dc403SDave Cobbley raise RunQemuError(yocto_paravirt_kvm_wiki) 629eb8dc403SDave Cobbley 630635e0e46SAndrew Geissler if not os.access(dev_vhost, os.W_OK|os.R_OK): 631eb8dc403SDave Cobbley logger.error("You have no read or write permission on /dev/vhost-net.") 632eb8dc403SDave Cobbley logger.error("Please change the ownership of this file as described at:") 633635e0e46SAndrew Geissler raise RunQemuError(yocto_paravirt_kvm_wiki) 634eb8dc403SDave Cobbley 635eb8dc403SDave Cobbley def check_fstype(self): 636eb8dc403SDave Cobbley """Check and setup FSTYPE""" 637eb8dc403SDave Cobbley if not self.fstype: 638eb8dc403SDave Cobbley fstype = self.get('QB_DEFAULT_FSTYPE') 639eb8dc403SDave Cobbley if fstype: 640eb8dc403SDave Cobbley self.fstype = fstype 641eb8dc403SDave Cobbley else: 642eb8dc403SDave Cobbley raise RunQemuError("FSTYPE is NULL!") 643eb8dc403SDave Cobbley 64415ae2509SBrad Bishop # parse QB_FSINFO into dict, e.g. { 'wic': ['no-kernel-in-fs', 'a-flag'], 'ext4': ['another-flag']} 64515ae2509SBrad Bishop wic_fs = False 64615ae2509SBrad Bishop qb_fsinfo = self.get('QB_FSINFO') 64715ae2509SBrad Bishop if qb_fsinfo: 64815ae2509SBrad Bishop qb_fsinfo = qb_fsinfo.split() 64915ae2509SBrad Bishop for fsinfo in qb_fsinfo: 65015ae2509SBrad Bishop try: 65115ae2509SBrad Bishop fstype, fsflag = fsinfo.split(':') 65215ae2509SBrad Bishop 65315ae2509SBrad Bishop if fstype == 'wic': 65415ae2509SBrad Bishop if fsflag == 'no-kernel-in-fs': 65515ae2509SBrad Bishop wic_fs = True 65615ae2509SBrad Bishop elif fsflag == 'kernel-in-fs': 65715ae2509SBrad Bishop wic_fs = False 65815ae2509SBrad Bishop else: 6598f840685SAndrew Geissler logger.warning('Unknown flag "%s:%s" in QB_FSINFO', fstype, fsflag) 66015ae2509SBrad Bishop continue 66115ae2509SBrad Bishop else: 6628f840685SAndrew Geissler logger.warning('QB_FSINFO is not supported for image type "%s"', fstype) 66315ae2509SBrad Bishop continue 66415ae2509SBrad Bishop 66515ae2509SBrad Bishop if fstype in self.fsinfo: 66615ae2509SBrad Bishop self.fsinfo[fstype].append(fsflag) 66715ae2509SBrad Bishop else: 66815ae2509SBrad Bishop self.fsinfo[fstype] = [fsflag] 66915ae2509SBrad Bishop except Exception: 67015ae2509SBrad Bishop logger.error('Invalid parameter "%s" in QB_FSINFO', fsinfo) 67115ae2509SBrad Bishop 67215ae2509SBrad Bishop # treat wic images as vmimages (with kernel) or as fsimages (rootfs only) 67315ae2509SBrad Bishop if wic_fs: 67415ae2509SBrad Bishop self.fstypes = self.fstypes + self.wictypes 67515ae2509SBrad Bishop else: 67615ae2509SBrad Bishop self.vmtypes = self.vmtypes + self.wictypes 67715ae2509SBrad Bishop 678eb8dc403SDave Cobbley def check_rootfs(self): 679eb8dc403SDave Cobbley """Check and set rootfs""" 680eb8dc403SDave Cobbley 681eb8dc403SDave Cobbley if self.fstype == "none": 682eb8dc403SDave Cobbley return 683eb8dc403SDave Cobbley 684eb8dc403SDave Cobbley if self.get('ROOTFS'): 685eb8dc403SDave Cobbley if not self.rootfs: 686eb8dc403SDave Cobbley self.rootfs = self.get('ROOTFS') 687eb8dc403SDave Cobbley elif self.get('ROOTFS') != self.rootfs: 688eb8dc403SDave Cobbley raise RunQemuError("Maybe conflicted ROOTFS: %s vs %s" % (self.get('ROOTFS'), self.rootfs)) 689eb8dc403SDave Cobbley 690eb8dc403SDave Cobbley if self.fstype == 'nfs': 691eb8dc403SDave Cobbley return 692eb8dc403SDave Cobbley 693eb8dc403SDave Cobbley if self.rootfs and not os.path.exists(self.rootfs): 694eb8dc403SDave Cobbley # Lazy rootfs 6958e7b46e2SPatrick Williams self.rootfs = "%s/%s.%s" % (self.get('DEPLOY_DIR_IMAGE'), 6968e7b46e2SPatrick Williams self.get('IMAGE_LINK_NAME'), 697eb8dc403SDave Cobbley self.fstype) 698eb8dc403SDave Cobbley elif not self.rootfs: 699fc113eadSAndrew Geissler glob_name = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_NAME'), self.fstype) 700fc113eadSAndrew Geissler glob_link = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'), self.fstype) 701fc113eadSAndrew Geissler globs = (glob_name, glob_link) 702fc113eadSAndrew Geissler self.rootfs = get_first_file(globs) 703eb8dc403SDave Cobbley if not self.rootfs: 704fc113eadSAndrew Geissler raise RunQemuError("Failed to find rootfs: %s or %s" % globs) 705eb8dc403SDave Cobbley 706eb8dc403SDave Cobbley if not os.path.exists(self.rootfs): 707eb8dc403SDave Cobbley raise RunQemuError("Can't find rootfs: %s" % self.rootfs) 708eb8dc403SDave Cobbley 70908902b01SBrad Bishop def setup_pkkek1(self): 71008902b01SBrad Bishop """ 71108902b01SBrad Bishop Extract from PEM certificate the Platform Key and first Key 71208902b01SBrad Bishop Exchange Key certificate string. The hypervisor needs to provide 71308902b01SBrad Bishop it in the Type 11 SMBIOS table 71408902b01SBrad Bishop """ 71508902b01SBrad Bishop pemcert = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), 'OvmfPkKek1.pem') 71608902b01SBrad Bishop try: 71708902b01SBrad Bishop with open(pemcert, 'r') as pemfile: 71808902b01SBrad Bishop key = pemfile.read().replace('\n', ''). \ 71908902b01SBrad Bishop replace('-----BEGIN CERTIFICATE-----', ''). \ 72008902b01SBrad Bishop replace('-----END CERTIFICATE-----', '') 72108902b01SBrad Bishop self.ovmf_secboot_pkkek1 = key 72208902b01SBrad Bishop 72308902b01SBrad Bishop except FileNotFoundError: 72408902b01SBrad Bishop raise RunQemuError("Can't open PEM certificate %s " % pemcert) 72508902b01SBrad Bishop 726eb8dc403SDave Cobbley def check_ovmf(self): 727eb8dc403SDave Cobbley """Check and set full path for OVMF firmware and variable file(s).""" 728eb8dc403SDave Cobbley 729eb8dc403SDave Cobbley for index, ovmf in enumerate(self.ovmf_bios): 730eb8dc403SDave Cobbley if os.path.exists(ovmf): 731eb8dc403SDave Cobbley continue 732eb8dc403SDave Cobbley for suffix in ('qcow2', 'bin'): 733eb8dc403SDave Cobbley path = '%s/%s.%s' % (self.get('DEPLOY_DIR_IMAGE'), ovmf, suffix) 734eb8dc403SDave Cobbley if os.path.exists(path): 735eb8dc403SDave Cobbley self.ovmf_bios[index] = path 73608902b01SBrad Bishop if ovmf.endswith('secboot'): 73708902b01SBrad Bishop self.setup_pkkek1() 738eb8dc403SDave Cobbley break 739eb8dc403SDave Cobbley else: 740eb8dc403SDave Cobbley raise RunQemuError("Can't find OVMF firmware: %s" % ovmf) 741eb8dc403SDave Cobbley 742eb8dc403SDave Cobbley def check_kernel(self): 743eb8dc403SDave Cobbley """Check and set kernel""" 744eb8dc403SDave Cobbley # The vm image doesn't need a kernel 745eb8dc403SDave Cobbley if self.fstype in self.vmtypes: 746eb8dc403SDave Cobbley return 747eb8dc403SDave Cobbley 748eb8dc403SDave Cobbley # See if the user supplied a KERNEL option 749eb8dc403SDave Cobbley if self.get('KERNEL'): 750eb8dc403SDave Cobbley self.kernel = self.get('KERNEL') 751eb8dc403SDave Cobbley 752eb8dc403SDave Cobbley # QB_DEFAULT_KERNEL is always a full file path 753eb8dc403SDave Cobbley kernel_name = os.path.basename(self.get('QB_DEFAULT_KERNEL')) 754eb8dc403SDave Cobbley 755eb8dc403SDave Cobbley # The user didn't want a kernel to be loaded 756eb8dc403SDave Cobbley if kernel_name == "none" and not self.kernel: 757eb8dc403SDave Cobbley return 758eb8dc403SDave Cobbley 759eb8dc403SDave Cobbley deploy_dir_image = self.get('DEPLOY_DIR_IMAGE') 760eb8dc403SDave Cobbley if not self.kernel: 761eb8dc403SDave Cobbley kernel_match_name = "%s/%s" % (deploy_dir_image, kernel_name) 762eb8dc403SDave Cobbley kernel_match_link = "%s/%s" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE')) 763eb8dc403SDave Cobbley kernel_startswith = "%s/%s*" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE')) 764fc113eadSAndrew Geissler globs = (kernel_match_name, kernel_match_link, kernel_startswith) 765fc113eadSAndrew Geissler self.kernel = get_first_file(globs) 766eb8dc403SDave Cobbley if not self.kernel: 767fc113eadSAndrew Geissler raise RunQemuError('KERNEL not found: %s, %s or %s' % globs) 768eb8dc403SDave Cobbley 769eb8dc403SDave Cobbley if not os.path.exists(self.kernel): 770eb8dc403SDave Cobbley raise RunQemuError("KERNEL %s not found" % self.kernel) 771eb8dc403SDave Cobbley 772eb8dc403SDave Cobbley def check_dtb(self): 773eb8dc403SDave Cobbley """Check and set dtb""" 774eb8dc403SDave Cobbley # Did the user specify a device tree? 775eb8dc403SDave Cobbley if self.get('DEVICE_TREE'): 776eb8dc403SDave Cobbley self.dtb = self.get('DEVICE_TREE') 777eb8dc403SDave Cobbley if not os.path.exists(self.dtb): 778eb8dc403SDave Cobbley raise RunQemuError('Specified DTB not found: %s' % self.dtb) 779eb8dc403SDave Cobbley return 780eb8dc403SDave Cobbley 781eb8dc403SDave Cobbley dtb = self.get('QB_DTB') 782eb8dc403SDave Cobbley if dtb: 783eb8dc403SDave Cobbley deploy_dir_image = self.get('DEPLOY_DIR_IMAGE') 784fc113eadSAndrew Geissler glob_match = "%s/%s" % (deploy_dir_image, dtb) 785fc113eadSAndrew Geissler glob_startswith = "%s/%s*" % (deploy_dir_image, dtb) 786fc113eadSAndrew Geissler glob_wild = "%s/*.dtb" % deploy_dir_image 787fc113eadSAndrew Geissler globs = (glob_match, glob_startswith, glob_wild) 788fc113eadSAndrew Geissler self.dtb = get_first_file(globs) 789eb8dc403SDave Cobbley if not os.path.exists(self.dtb): 790fc113eadSAndrew Geissler raise RunQemuError('DTB not found: %s, %s or %s' % globs) 791eb8dc403SDave Cobbley 792c68388fcSBrad Bishop def check_bios(self): 793c68388fcSBrad Bishop """Check and set bios""" 794c68388fcSBrad Bishop 795c68388fcSBrad Bishop # See if the user supplied a BIOS option 796c68388fcSBrad Bishop if self.get('BIOS'): 797c68388fcSBrad Bishop self.bios = self.get('BIOS') 798c68388fcSBrad Bishop 799c68388fcSBrad Bishop # QB_DEFAULT_BIOS is always a full file path 800c68388fcSBrad Bishop bios_name = os.path.basename(self.get('QB_DEFAULT_BIOS')) 801c68388fcSBrad Bishop 802c68388fcSBrad Bishop # The user didn't want a bios to be loaded 803c68388fcSBrad Bishop if (bios_name == "" or bios_name == "none") and not self.bios: 804eb8dc403SDave Cobbley return 805eb8dc403SDave Cobbley 806c68388fcSBrad Bishop if not self.bios: 807c68388fcSBrad Bishop deploy_dir_image = self.get('DEPLOY_DIR_IMAGE') 808c68388fcSBrad Bishop self.bios = "%s/%s" % (deploy_dir_image, bios_name) 809eb8dc403SDave Cobbley 810c68388fcSBrad Bishop if not self.bios: 811c68388fcSBrad Bishop raise RunQemuError('BIOS not found: %s' % bios_match_name) 812c68388fcSBrad Bishop 813c68388fcSBrad Bishop if not os.path.exists(self.bios): 814213cb269SPatrick Williams raise RunQemuError("BIOS %s not found" % self.bios) 815c68388fcSBrad Bishop 816eb8dc403SDave Cobbley 817eb8dc403SDave Cobbley def check_mem(self): 81899467dabSAndrew Geissler """ 81999467dabSAndrew Geissler Both qemu and kernel needs memory settings, so check QB_MEM and set it 82099467dabSAndrew Geissler for both. 82199467dabSAndrew Geissler """ 82299467dabSAndrew Geissler s = re.search('-m +([0-9]+)', self.qemuparams) 823eb8dc403SDave Cobbley if s: 824eb8dc403SDave Cobbley self.set('QB_MEM', '-m %s' % s.group(1)) 825eb8dc403SDave Cobbley elif not self.get('QB_MEM'): 82679641f25SBrad Bishop logger.info('QB_MEM is not set, use 256M by default') 82779641f25SBrad Bishop self.set('QB_MEM', '-m 256') 828eb8dc403SDave Cobbley 82999467dabSAndrew Geissler # Check and remove M or m suffix 83099467dabSAndrew Geissler qb_mem = self.get('QB_MEM') 83199467dabSAndrew Geissler if qb_mem.endswith('M') or qb_mem.endswith('m'): 83299467dabSAndrew Geissler qb_mem = qb_mem[:-1] 83399467dabSAndrew Geissler 83499467dabSAndrew Geissler # Add -m prefix it not present 83599467dabSAndrew Geissler if not qb_mem.startswith('-m'): 83699467dabSAndrew Geissler qb_mem = '-m %s' % qb_mem 83799467dabSAndrew Geissler 83899467dabSAndrew Geissler self.set('QB_MEM', qb_mem) 83999467dabSAndrew Geissler 8401a4b7ee2SBrad Bishop mach = self.get('MACHINE') 841fc113eadSAndrew Geissler if not mach.startswith(('qemumips', 'qemux86', 'qemuloongarch64')): 842eb8dc403SDave Cobbley self.kernel_cmdline_script += ' mem=%s' % self.get('QB_MEM').replace('-m','').strip() + 'M' 8431a4b7ee2SBrad Bishop 844eb8dc403SDave Cobbley self.qemu_opt_script += ' %s' % self.get('QB_MEM') 845eb8dc403SDave Cobbley 846eb8dc403SDave Cobbley def check_tcpserial(self): 847eb8dc403SDave Cobbley if self.tcpserial_portnum: 84815ae2509SBrad Bishop ports = self.tcpserial_portnum.split(':') 84915ae2509SBrad Bishop port = ports[0] 850eb8dc403SDave Cobbley if self.get('QB_TCPSERIAL_OPT'): 85115ae2509SBrad Bishop self.qemu_opt_script += ' ' + self.get('QB_TCPSERIAL_OPT').replace('@PORT@', port) 852eb8dc403SDave Cobbley else: 85320137395SAndrew Geissler self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s,nodelay=on' % port 85415ae2509SBrad Bishop 85515ae2509SBrad Bishop if len(ports) > 1: 85615ae2509SBrad Bishop for port in ports[1:]: 85720137395SAndrew Geissler self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s,nodelay=on' % port 858eb8dc403SDave Cobbley 859eb8dc403SDave Cobbley def check_and_set(self): 860eb8dc403SDave Cobbley """Check configs sanity and set when needed""" 861eb8dc403SDave Cobbley self.validate_paths() 86282c905dcSAndrew Geissler if not self.slirp_enabled and not self.net_bridge: 863eb8dc403SDave Cobbley check_tun() 864eb8dc403SDave Cobbley # Check audio 865eb8dc403SDave Cobbley if self.audio_enabled: 866eb8dc403SDave Cobbley if not self.get('QB_AUDIO_DRV'): 867eb8dc403SDave Cobbley raise RunQemuError("QB_AUDIO_DRV is NULL, this board doesn't support audio") 868eb8dc403SDave Cobbley if not self.get('QB_AUDIO_OPT'): 8691a4b7ee2SBrad Bishop logger.warning('QB_AUDIO_OPT is NULL, you may need define it to make audio work') 870eb8dc403SDave Cobbley else: 871eb8dc403SDave Cobbley self.qemu_opt_script += ' %s' % self.get('QB_AUDIO_OPT') 872eb8dc403SDave Cobbley os.putenv('QEMU_AUDIO_DRV', self.get('QB_AUDIO_DRV')) 873eb8dc403SDave Cobbley else: 874eb8dc403SDave Cobbley os.putenv('QEMU_AUDIO_DRV', 'none') 875eb8dc403SDave Cobbley 87615ae2509SBrad Bishop self.check_qemu_system() 877eb8dc403SDave Cobbley self.check_kvm() 878eb8dc403SDave Cobbley self.check_fstype() 879eb8dc403SDave Cobbley self.check_rootfs() 880eb8dc403SDave Cobbley self.check_ovmf() 881eb8dc403SDave Cobbley self.check_kernel() 882eb8dc403SDave Cobbley self.check_dtb() 883c68388fcSBrad Bishop self.check_bios() 884eb8dc403SDave Cobbley self.check_mem() 885eb8dc403SDave Cobbley self.check_tcpserial() 886eb8dc403SDave Cobbley 887eb8dc403SDave Cobbley def read_qemuboot(self): 888eb8dc403SDave Cobbley if not self.qemuboot: 889eb8dc403SDave Cobbley if self.get('DEPLOY_DIR_IMAGE'): 890eb8dc403SDave Cobbley deploy_dir_image = self.get('DEPLOY_DIR_IMAGE') 891eb8dc403SDave Cobbley else: 8921a4b7ee2SBrad Bishop logger.warning("Can't find qemuboot conf file, DEPLOY_DIR_IMAGE is NULL!") 893eb8dc403SDave Cobbley return 894eb8dc403SDave Cobbley 895eb8dc403SDave Cobbley if self.rootfs and not os.path.exists(self.rootfs): 896eb8dc403SDave Cobbley # Lazy rootfs 897eb8dc403SDave Cobbley machine = self.get('MACHINE') 898eb8dc403SDave Cobbley if not machine: 899eb8dc403SDave Cobbley machine = os.path.basename(deploy_dir_image) 9008e7b46e2SPatrick Williams if not self.get('IMAGE_LINK_NAME'): 9018e7b46e2SPatrick Williams raise RunQemuError("IMAGE_LINK_NAME wasn't set to find corresponding .qemuboot.conf file") 9028e7b46e2SPatrick Williams self.qemuboot = "%s/%s.qemuboot.conf" % (deploy_dir_image, 9038e7b46e2SPatrick Williams self.get('IMAGE_LINK_NAME')) 904eb8dc403SDave Cobbley else: 905eb8dc403SDave Cobbley cmd = 'ls -t %s/*.qemuboot.conf' % deploy_dir_image 906eb8dc403SDave Cobbley logger.debug('Running %s...' % cmd) 907eb8dc403SDave Cobbley try: 908eb8dc403SDave Cobbley qbs = subprocess.check_output(cmd, shell=True).decode('utf-8') 909eb8dc403SDave Cobbley except subprocess.CalledProcessError as err: 910eb8dc403SDave Cobbley raise RunQemuError(err) 911eb8dc403SDave Cobbley if qbs: 912eb8dc403SDave Cobbley for qb in qbs.split(): 913eb8dc403SDave Cobbley # Don't use initramfs when other choices unless fstype is ramfs 914eb8dc403SDave Cobbley if '-initramfs-' in os.path.basename(qb) and self.fstype != 'cpio.gz': 915eb8dc403SDave Cobbley continue 916eb8dc403SDave Cobbley self.qemuboot = qb 917eb8dc403SDave Cobbley break 918eb8dc403SDave Cobbley if not self.qemuboot: 919eb8dc403SDave Cobbley # Use the first one when no choice 920eb8dc403SDave Cobbley self.qemuboot = qbs.split()[0] 921eb8dc403SDave Cobbley self.qbconfload = True 922eb8dc403SDave Cobbley 923eb8dc403SDave Cobbley if not self.qemuboot: 924eb8dc403SDave Cobbley # If we haven't found a .qemuboot.conf at this point it probably 925eb8dc403SDave Cobbley # doesn't exist, continue without 926eb8dc403SDave Cobbley return 927eb8dc403SDave Cobbley 928eb8dc403SDave Cobbley if not os.path.exists(self.qemuboot): 929eb8dc403SDave Cobbley raise RunQemuError("Failed to find %s (wrong image name or BSP does not support running under qemu?)." % self.qemuboot) 930eb8dc403SDave Cobbley 931eb8dc403SDave Cobbley logger.debug('CONFFILE: %s' % self.qemuboot) 932eb8dc403SDave Cobbley 933eb8dc403SDave Cobbley cf = configparser.ConfigParser() 934eb8dc403SDave Cobbley cf.read(self.qemuboot) 935eb8dc403SDave Cobbley for k, v in cf.items('config_bsp'): 936eb8dc403SDave Cobbley k_upper = k.upper() 937eb8dc403SDave Cobbley if v.startswith("../"): 938eb8dc403SDave Cobbley v = os.path.abspath(os.path.dirname(self.qemuboot) + "/" + v) 939eb8dc403SDave Cobbley elif v == ".": 940eb8dc403SDave Cobbley v = os.path.dirname(self.qemuboot) 941eb8dc403SDave Cobbley self.set(k_upper, v) 942eb8dc403SDave Cobbley 943eb8dc403SDave Cobbley def validate_paths(self): 944eb8dc403SDave Cobbley """Ensure all relevant path variables are set""" 945eb8dc403SDave Cobbley # When we're started with a *.qemuboot.conf arg assume that image 946eb8dc403SDave Cobbley # artefacts are relative to that file, rather than in whatever 947eb8dc403SDave Cobbley # directory DEPLOY_DIR_IMAGE in the conf file points to. 948eb8dc403SDave Cobbley if self.qbconfload: 949eb8dc403SDave Cobbley imgdir = os.path.realpath(os.path.dirname(self.qemuboot)) 950eb8dc403SDave Cobbley if imgdir != os.path.realpath(self.get('DEPLOY_DIR_IMAGE')): 951eb8dc403SDave Cobbley logger.info('Setting DEPLOY_DIR_IMAGE to folder containing %s (%s)' % (self.qemuboot, imgdir)) 952eb8dc403SDave Cobbley self.set('DEPLOY_DIR_IMAGE', imgdir) 953eb8dc403SDave Cobbley 954eb8dc403SDave Cobbley # If the STAGING_*_NATIVE directories from the config file don't exist 955eb8dc403SDave Cobbley # and we're in a sourced OE build directory try to extract the paths 956eb8dc403SDave Cobbley # from `bitbake -e` 957eb8dc403SDave Cobbley havenative = os.path.exists(self.get('STAGING_DIR_NATIVE')) and \ 958eb8dc403SDave Cobbley os.path.exists(self.get('STAGING_BINDIR_NATIVE')) 959eb8dc403SDave Cobbley 960eb8dc403SDave Cobbley if not havenative: 961eb8dc403SDave Cobbley if not self.bitbake_e: 962eb8dc403SDave Cobbley self.load_bitbake_env() 963eb8dc403SDave Cobbley 964eb8dc403SDave Cobbley if self.bitbake_e: 965eb8dc403SDave Cobbley native_vars = ['STAGING_DIR_NATIVE'] 966eb8dc403SDave Cobbley for nv in native_vars: 967eb8dc403SDave Cobbley s = re.search('^%s="(.*)"' % nv, self.bitbake_e, re.M) 968eb8dc403SDave Cobbley if s and s.group(1) != self.get(nv): 969eb8dc403SDave Cobbley logger.info('Overriding conf file setting of %s to %s from Bitbake environment' % (nv, s.group(1))) 970eb8dc403SDave Cobbley self.set(nv, s.group(1)) 971eb8dc403SDave Cobbley else: 972eb8dc403SDave Cobbley # when we're invoked from a running bitbake instance we won't 973eb8dc403SDave Cobbley # be able to call `bitbake -e`, then try: 974eb8dc403SDave Cobbley # - get OE_TMPDIR from environment and guess paths based on it 975eb8dc403SDave Cobbley # - get OECORE_NATIVE_SYSROOT from environment (for sdk) 976eb8dc403SDave Cobbley tmpdir = self.get('OE_TMPDIR') 977eb8dc403SDave Cobbley oecore_native_sysroot = self.get('OECORE_NATIVE_SYSROOT') 978eb8dc403SDave Cobbley if tmpdir: 979eb8dc403SDave Cobbley logger.info('Setting STAGING_DIR_NATIVE and STAGING_BINDIR_NATIVE relative to OE_TMPDIR (%s)' % tmpdir) 980eb8dc403SDave Cobbley hostos, _, _, _, machine = os.uname() 981eb8dc403SDave Cobbley buildsys = '%s-%s' % (machine, hostos.lower()) 982eb8dc403SDave Cobbley staging_dir_native = '%s/sysroots/%s' % (tmpdir, buildsys) 983eb8dc403SDave Cobbley self.set('STAGING_DIR_NATIVE', staging_dir_native) 984eb8dc403SDave Cobbley elif oecore_native_sysroot: 985eb8dc403SDave Cobbley logger.info('Setting STAGING_DIR_NATIVE to OECORE_NATIVE_SYSROOT (%s)' % oecore_native_sysroot) 986eb8dc403SDave Cobbley self.set('STAGING_DIR_NATIVE', oecore_native_sysroot) 987eb8dc403SDave Cobbley if self.get('STAGING_DIR_NATIVE'): 988eb8dc403SDave Cobbley # we have to assume that STAGING_BINDIR_NATIVE is at usr/bin 989eb8dc403SDave Cobbley staging_bindir_native = '%s/usr/bin' % self.get('STAGING_DIR_NATIVE') 990eb8dc403SDave Cobbley logger.info('Setting STAGING_BINDIR_NATIVE to %s' % staging_bindir_native) 991eb8dc403SDave Cobbley self.set('STAGING_BINDIR_NATIVE', '%s/usr/bin' % self.get('STAGING_DIR_NATIVE')) 992eb8dc403SDave Cobbley 993eb8dc403SDave Cobbley def print_config(self): 99482c905dcSAndrew Geissler logoutput = ['Continuing with the following parameters:'] 995eb8dc403SDave Cobbley if not self.fstype in self.vmtypes: 99682c905dcSAndrew Geissler logoutput.append('KERNEL: [%s]' % self.kernel) 997c68388fcSBrad Bishop if self.bios: 99882c905dcSAndrew Geissler logoutput.append('BIOS: [%s]' % self.bios) 999eb8dc403SDave Cobbley if self.dtb: 100082c905dcSAndrew Geissler logoutput.append('DTB: [%s]' % self.dtb) 100182c905dcSAndrew Geissler logoutput.append('MACHINE: [%s]' % self.get('MACHINE')) 100215ae2509SBrad Bishop try: 100315ae2509SBrad Bishop fstype_flags = ' (' + ', '.join(self.fsinfo[self.fstype]) + ')' 100415ae2509SBrad Bishop except KeyError: 100515ae2509SBrad Bishop fstype_flags = '' 100682c905dcSAndrew Geissler logoutput.append('FSTYPE: [%s%s]' % (self.fstype, fstype_flags)) 1007eb8dc403SDave Cobbley if self.fstype == 'nfs': 100882c905dcSAndrew Geissler logoutput.append('NFS_DIR: [%s]' % self.rootfs) 1009eb8dc403SDave Cobbley else: 101082c905dcSAndrew Geissler logoutput.append('ROOTFS: [%s]' % self.rootfs) 1011eb8dc403SDave Cobbley if self.ovmf_bios: 101282c905dcSAndrew Geissler logoutput.append('OVMF: %s' % self.ovmf_bios) 101308902b01SBrad Bishop if (self.ovmf_secboot_pkkek1): 101482c905dcSAndrew Geissler logoutput.append('SECBOOT PKKEK1: [%s...]' % self.ovmf_secboot_pkkek1[0:100]) 101582c905dcSAndrew Geissler logoutput.append('CONFFILE: [%s]' % self.qemuboot) 101682c905dcSAndrew Geissler logoutput.append('') 101782c905dcSAndrew Geissler logger.info('\n'.join(logoutput)) 1018eb8dc403SDave Cobbley 1019eb8dc403SDave Cobbley def setup_nfs(self): 1020eb8dc403SDave Cobbley if not self.nfs_server: 1021eb8dc403SDave Cobbley if self.slirp_enabled: 1022eb8dc403SDave Cobbley self.nfs_server = '10.0.2.2' 1023eb8dc403SDave Cobbley else: 1024517393d9SAndrew Geissler self.nfs_server = '192.168.7.@GATEWAY@' 1025eb8dc403SDave Cobbley 1026520786ccSPatrick Williams nfsd_port = 3048 + self.nfs_instance 1027520786ccSPatrick Williams lockdir = "/tmp/qemu-port-locks" 1028520786ccSPatrick Williams self.make_lock_dir(lockdir) 1029520786ccSPatrick Williams while not self.check_free_port('localhost', nfsd_port, lockdir): 1030520786ccSPatrick Williams self.nfs_instance += 1 1031520786ccSPatrick Williams nfsd_port += 1 1032eb8dc403SDave Cobbley 1033520786ccSPatrick Williams mountd_port = nfsd_port 1034eb8dc403SDave Cobbley # Export vars for runqemu-export-rootfs 1035eb8dc403SDave Cobbley export_dict = { 1036eb8dc403SDave Cobbley 'NFS_INSTANCE': self.nfs_instance, 1037eb8dc403SDave Cobbley 'NFSD_PORT': nfsd_port, 1038eb8dc403SDave Cobbley 'MOUNTD_PORT': mountd_port, 1039eb8dc403SDave Cobbley } 1040eb8dc403SDave Cobbley for k, v in export_dict.items(): 1041eb8dc403SDave Cobbley # Use '%s' since they are integers 1042eb8dc403SDave Cobbley os.putenv(k, '%s' % v) 1043eb8dc403SDave Cobbley 1044c5535c91SAndrew Geissler qb_nfsrootfs_extra_opt = self.get("QB_NFSROOTFS_EXTRA_OPT") 1045c5535c91SAndrew Geissler if qb_nfsrootfs_extra_opt and not qb_nfsrootfs_extra_opt.startswith(","): 1046c5535c91SAndrew Geissler qb_nfsrootfs_extra_opt = "," + qb_nfsrootfs_extra_opt 1047c5535c91SAndrew Geissler 1048c5535c91SAndrew Geissler self.unfs_opts="nfsvers=3,port=%s,tcp,mountport=%s%s" % (nfsd_port, mountd_port, qb_nfsrootfs_extra_opt) 1049eb8dc403SDave Cobbley 1050eb8dc403SDave Cobbley # Extract .tar.bz2 or .tar.bz if no nfs dir 1051eb8dc403SDave Cobbley if not (self.rootfs and os.path.isdir(self.rootfs)): 1052eb8dc403SDave Cobbley src_prefix = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME')) 1053eb8dc403SDave Cobbley dest = "%s-nfsroot" % src_prefix 1054eb8dc403SDave Cobbley if os.path.exists('%s.pseudo_state' % dest): 1055eb8dc403SDave Cobbley logger.info('Use %s as NFS_DIR' % dest) 1056eb8dc403SDave Cobbley self.rootfs = dest 1057eb8dc403SDave Cobbley else: 1058eb8dc403SDave Cobbley src = "" 1059eb8dc403SDave Cobbley src1 = '%s.tar.bz2' % src_prefix 1060eb8dc403SDave Cobbley src2 = '%s.tar.gz' % src_prefix 1061eb8dc403SDave Cobbley if os.path.exists(src1): 1062eb8dc403SDave Cobbley src = src1 1063eb8dc403SDave Cobbley elif os.path.exists(src2): 1064eb8dc403SDave Cobbley src = src2 1065eb8dc403SDave Cobbley if not src: 1066eb8dc403SDave Cobbley raise RunQemuError("No NFS_DIR is set, and can't find %s or %s to extract" % (src1, src2)) 1067eb8dc403SDave Cobbley logger.info('NFS_DIR not found, extracting %s to %s' % (src, dest)) 1068977dc1acSBrad Bishop cmd = ('runqemu-extract-sdk', src, dest) 1069977dc1acSBrad Bishop logger.info('Running %s...' % str(cmd)) 1070977dc1acSBrad Bishop if subprocess.call(cmd) != 0: 1071fc113eadSAndrew Geissler raise RunQemuError('Failed to run %s' % str(cmd)) 1072eb8dc403SDave Cobbley self.rootfs = dest 1073c926e17cSAndrew Geissler self.cleanup_files.append(self.rootfs) 1074c926e17cSAndrew Geissler self.cleanup_files.append('%s.pseudo_state' % self.rootfs) 1075eb8dc403SDave Cobbley 1076eb8dc403SDave Cobbley # Start the userspace NFS server 1077977dc1acSBrad Bishop cmd = ('runqemu-export-rootfs', 'start', self.rootfs) 1078977dc1acSBrad Bishop logger.info('Running %s...' % str(cmd)) 1079977dc1acSBrad Bishop if subprocess.call(cmd) != 0: 1080fc113eadSAndrew Geissler raise RunQemuError('Failed to run %s' % str(cmd)) 1081eb8dc403SDave Cobbley 1082eb8dc403SDave Cobbley self.nfs_running = True 1083eb8dc403SDave Cobbley 1084517393d9SAndrew Geissler def setup_cmd(self): 1085517393d9SAndrew Geissler cmd = self.get('QB_SETUP_CMD') 1086517393d9SAndrew Geissler if cmd != '': 1087517393d9SAndrew Geissler logger.info('Running setup command %s' % str(cmd)) 1088517393d9SAndrew Geissler if subprocess.call(cmd, shell=True) != 0: 1089fc113eadSAndrew Geissler raise RunQemuError('Failed to run %s' % str(cmd)) 1090517393d9SAndrew Geissler 109182c905dcSAndrew Geissler def setup_net_bridge(self): 109282c905dcSAndrew Geissler self.set('NETWORK_CMD', '-netdev bridge,br=%s,id=net0,helper=%s -device virtio-net-pci,netdev=net0 ' % ( 109382c905dcSAndrew Geissler self.net_bridge, os.path.join(self.bindir_native, 'qemu-oe-bridge-helper'))) 109482c905dcSAndrew Geissler 1095520786ccSPatrick Williams def make_lock_dir(self, lockdir): 1096520786ccSPatrick Williams if not os.path.exists(lockdir): 1097520786ccSPatrick Williams # There might be a race issue when multi runqemu processess are 1098520786ccSPatrick Williams # running at the same time. 1099520786ccSPatrick Williams try: 1100520786ccSPatrick Williams os.mkdir(lockdir) 1101520786ccSPatrick Williams os.chmod(lockdir, 0o777) 1102520786ccSPatrick Williams except FileExistsError: 1103520786ccSPatrick Williams pass 1104520786ccSPatrick Williams return 1105520786ccSPatrick Williams 1106eb8dc403SDave Cobbley def setup_slirp(self): 1107eb8dc403SDave Cobbley """Setup user networking""" 1108eb8dc403SDave Cobbley 1109eb8dc403SDave Cobbley if self.fstype == 'nfs': 1110eb8dc403SDave Cobbley self.setup_nfs() 111182c905dcSAndrew Geissler netconf = " " + self.cmdline_ip_slirp 111282c905dcSAndrew Geissler logger.info("Network configuration:%s", netconf) 111382c905dcSAndrew Geissler self.kernel_cmdline_script += netconf 1114eb8dc403SDave Cobbley # Port mapping 1115517393d9SAndrew Geissler hostfwd = ",hostfwd=tcp:127.0.0.1:2222-:22,hostfwd=tcp:127.0.0.1:2323-:23" 1116eb8dc403SDave Cobbley qb_slirp_opt_default = "-netdev user,id=net0%s,tftp=%s" % (hostfwd, self.get('DEPLOY_DIR_IMAGE')) 1117eb8dc403SDave Cobbley qb_slirp_opt = self.get('QB_SLIRP_OPT') or qb_slirp_opt_default 1118eb8dc403SDave Cobbley # Figure out the port 1119eb8dc403SDave Cobbley ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt) 1120eb8dc403SDave Cobbley ports = [int(i) for i in ports] 1121eb8dc403SDave Cobbley mac = 2 112208902b01SBrad Bishop 112308902b01SBrad Bishop lockdir = "/tmp/qemu-port-locks" 1124520786ccSPatrick Williams self.make_lock_dir(lockdir) 112508902b01SBrad Bishop 1126eb8dc403SDave Cobbley # Find a free port to avoid conflicts 1127eb8dc403SDave Cobbley for p in ports[:]: 1128eb8dc403SDave Cobbley p_new = p 112908902b01SBrad Bishop while not self.check_free_port('localhost', p_new, lockdir): 1130eb8dc403SDave Cobbley p_new += 1 1131eb8dc403SDave Cobbley mac += 1 1132eb8dc403SDave Cobbley while p_new in ports: 1133eb8dc403SDave Cobbley p_new += 1 1134eb8dc403SDave Cobbley mac += 1 1135eb8dc403SDave Cobbley if p != p_new: 1136eb8dc403SDave Cobbley ports.append(p_new) 1137eb8dc403SDave Cobbley qb_slirp_opt = re.sub(':%s-' % p, ':%s-' % p_new, qb_slirp_opt) 1138eb8dc403SDave Cobbley logger.info("Port forward changed: %s -> %s" % (p, p_new)) 1139eb8dc403SDave Cobbley mac = "%s%02x" % (self.mac_slirp, mac) 1140eb8dc403SDave Cobbley self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qb_slirp_opt)) 1141eb8dc403SDave Cobbley # Print out port foward 1142eb8dc403SDave Cobbley hostfwd = re.findall('(hostfwd=[^,]*)', qb_slirp_opt) 1143eb8dc403SDave Cobbley if hostfwd: 1144eb8dc403SDave Cobbley logger.info('Port forward: %s' % ' '.join(hostfwd)) 1145eb8dc403SDave Cobbley 1146eb8dc403SDave Cobbley def setup_tap(self): 1147eb8dc403SDave Cobbley """Setup tap""" 1148eb8dc403SDave Cobbley 1149eb8dc403SDave Cobbley # This file is created when runqemu-gen-tapdevs creates a bank of tap 1150eb8dc403SDave Cobbley # devices, indicating that the user should not bring up new ones using 1151eb8dc403SDave Cobbley # sudo. 1152eb8dc403SDave Cobbley nosudo_flag = '/etc/runqemu-nosudo' 1153eb8dc403SDave Cobbley self.qemuifup = shutil.which('runqemu-ifup') 1154eb8dc403SDave Cobbley self.qemuifdown = shutil.which('runqemu-ifdown') 1155eb8dc403SDave Cobbley ip = shutil.which('ip') 1156eb8dc403SDave Cobbley lockdir = "/tmp/qemu-tap-locks" 1157eb8dc403SDave Cobbley 1158eb8dc403SDave Cobbley if not (self.qemuifup and self.qemuifdown and ip): 1159eb8dc403SDave Cobbley logger.error("runqemu-ifup: %s" % self.qemuifup) 1160eb8dc403SDave Cobbley logger.error("runqemu-ifdown: %s" % self.qemuifdown) 1161eb8dc403SDave Cobbley logger.error("ip: %s" % ip) 1162eb8dc403SDave Cobbley raise OEPathError("runqemu-ifup, runqemu-ifdown or ip not found") 1163eb8dc403SDave Cobbley 1164520786ccSPatrick Williams self.make_lock_dir(lockdir) 1165eb8dc403SDave Cobbley 1166977dc1acSBrad Bishop cmd = (ip, 'link') 1167977dc1acSBrad Bishop logger.debug('Running %s...' % str(cmd)) 1168977dc1acSBrad Bishop ip_link = subprocess.check_output(cmd).decode('utf-8') 1169eb8dc403SDave Cobbley # Matches line like: 6: tap0: <foo> 1170520786ccSPatrick Williams oe_tap_name = 'tap' 1171520786ccSPatrick Williams if 'OE_TAP_NAME' in os.environ: 1172520786ccSPatrick Williams oe_tap_name = os.environ['OE_TAP_NAME'] 1173520786ccSPatrick Williams tap_re = '^[0-9]+: +(' + oe_tap_name + '[0-9]+): <.*' 1174520786ccSPatrick Williams possibles = re.findall(tap_re, ip_link, re.M) 1175eb8dc403SDave Cobbley tap = "" 1176eb8dc403SDave Cobbley for p in possibles: 1177eb8dc403SDave Cobbley lockfile = os.path.join(lockdir, p) 1178eb8dc403SDave Cobbley if os.path.exists('%s.skip' % lockfile): 1179eb8dc403SDave Cobbley logger.info('Found %s.skip, skipping %s' % (lockfile, p)) 1180eb8dc403SDave Cobbley continue 118108902b01SBrad Bishop self.taplock = lockfile + '.lock' 118208902b01SBrad Bishop if self.acquire_taplock(error=False): 1183eb8dc403SDave Cobbley tap = p 1184eb8dc403SDave Cobbley logger.info("Using preconfigured tap device %s" % tap) 1185eb8dc403SDave Cobbley logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap)) 1186eb8dc403SDave Cobbley break 1187eb8dc403SDave Cobbley 1188eb8dc403SDave Cobbley if not tap: 1189eb8dc403SDave Cobbley if os.path.exists(nosudo_flag): 1190eb8dc403SDave Cobbley logger.error("Error: There are no available tap devices to use for networking,") 1191eb8dc403SDave Cobbley logger.error("and I see %s exists, so I am not going to try creating" % nosudo_flag) 1192eb8dc403SDave Cobbley raise RunQemuError("a new one with sudo.") 1193eb8dc403SDave Cobbley 1194eb8dc403SDave Cobbley gid = os.getgid() 1195eb8dc403SDave Cobbley uid = os.getuid() 1196eb8dc403SDave Cobbley logger.info("Setting up tap interface under sudo") 11978f840685SAndrew Geissler cmd = ('sudo', self.qemuifup, str(gid)) 119882c905dcSAndrew Geissler try: 1199977dc1acSBrad Bishop tap = subprocess.check_output(cmd).decode('utf-8').strip() 120082c905dcSAndrew Geissler except subprocess.CalledProcessError as e: 120182c905dcSAndrew Geissler logger.error('Setting up tap device failed:\n%s\nRun runqemu-gen-tapdevs to manually create one.' % str(e)) 120282c905dcSAndrew Geissler sys.exit(1) 1203eb8dc403SDave Cobbley lockfile = os.path.join(lockdir, tap) 120408902b01SBrad Bishop self.taplock = lockfile + '.lock' 120508902b01SBrad Bishop self.acquire_taplock() 1206eb8dc403SDave Cobbley self.cleantap = True 1207eb8dc403SDave Cobbley logger.debug('Created tap: %s' % tap) 1208eb8dc403SDave Cobbley 1209eb8dc403SDave Cobbley if not tap: 1210eb8dc403SDave Cobbley logger.error("Failed to setup tap device. Run runqemu-gen-tapdevs to manually create.") 121182c905dcSAndrew Geissler sys.exit(1) 1212eb8dc403SDave Cobbley self.tap = tap 1213520786ccSPatrick Williams tapnum = int(tap[len(oe_tap_name):]) 1214eb8dc403SDave Cobbley gateway = tapnum * 2 + 1 1215eb8dc403SDave Cobbley client = gateway + 1 1216eb8dc403SDave Cobbley if self.fstype == 'nfs': 1217eb8dc403SDave Cobbley self.setup_nfs() 121882c905dcSAndrew Geissler netconf = " " + self.cmdline_ip_tap 121982c905dcSAndrew Geissler netconf = netconf.replace('@CLIENT@', str(client)) 122082c905dcSAndrew Geissler netconf = netconf.replace('@GATEWAY@', str(gateway)) 1221517393d9SAndrew Geissler self.nfs_server = self.nfs_server.replace('@GATEWAY@', str(gateway)) 1222eb8dc403SDave Cobbley logger.info("Network configuration:%s", netconf) 122382c905dcSAndrew Geissler self.kernel_cmdline_script += netconf 1224eb8dc403SDave Cobbley mac = "%s%02x" % (self.mac_tap, client) 1225eb8dc403SDave Cobbley qb_tap_opt = self.get('QB_TAP_OPT') 1226eb8dc403SDave Cobbley if qb_tap_opt: 1227eb8dc403SDave Cobbley qemu_tap_opt = qb_tap_opt.replace('@TAP@', tap) 1228eb8dc403SDave Cobbley else: 1229eb8dc403SDave Cobbley qemu_tap_opt = "-netdev tap,id=net0,ifname=%s,script=no,downscript=no" % (self.tap) 1230eb8dc403SDave Cobbley 1231eb8dc403SDave Cobbley if self.vhost_enabled: 1232eb8dc403SDave Cobbley qemu_tap_opt += ',vhost=on' 1233eb8dc403SDave Cobbley 1234eb8dc403SDave Cobbley self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qemu_tap_opt)) 1235eb8dc403SDave Cobbley 1236eb8dc403SDave Cobbley def setup_network(self): 12378e7b46e2SPatrick Williams if self.nonetwork or self.get('QB_NET') == 'none': 12388e7b46e2SPatrick Williams self.set('NETWORK_CMD', '-nic none') 1239eb8dc403SDave Cobbley return 1240eb8dc403SDave Cobbley if sys.stdin.isatty(): 1241977dc1acSBrad Bishop self.saved_stty = subprocess.check_output(("stty", "-g")).decode('utf-8').strip() 1242eb8dc403SDave Cobbley self.network_device = self.get('QB_NETWORK_DEVICE') or self.network_device 124382c905dcSAndrew Geissler if self.net_bridge: 124482c905dcSAndrew Geissler self.setup_net_bridge() 124582c905dcSAndrew Geissler elif self.slirp_enabled: 124682c905dcSAndrew Geissler self.cmdline_ip_slirp = self.get('QB_CMDLINE_IP_SLIRP') or self.cmdline_ip_slirp 1247eb8dc403SDave Cobbley self.setup_slirp() 1248eb8dc403SDave Cobbley else: 124982c905dcSAndrew Geissler self.cmdline_ip_tap = self.get('QB_CMDLINE_IP_TAP') or self.cmdline_ip_tap 1250eb8dc403SDave Cobbley self.setup_tap() 1251eb8dc403SDave Cobbley 1252eb8dc403SDave Cobbley def setup_rootfs(self): 1253eb8dc403SDave Cobbley if self.get('QB_ROOTFS') == 'none': 1254eb8dc403SDave Cobbley return 1255eb8dc403SDave Cobbley if 'wic.' in self.fstype: 1256eb8dc403SDave Cobbley self.fstype = self.fstype[4:] 1257d1e89497SAndrew Geissler rootfs_format = self.fstype if self.fstype in ('vmdk', 'vhd', 'vhdx', 'qcow2', 'vdi') else 'raw' 1258eb8dc403SDave Cobbley 12593b8a17c1SAndrew Geissler tmpfsdir = os.environ.get("RUNQEMU_TMPFS_DIR", None) 12603b8a17c1SAndrew Geissler if self.snapshot and tmpfsdir: 12613b8a17c1SAndrew Geissler newrootfs = os.path.join(tmpfsdir, os.path.basename(self.rootfs)) + "." + str(os.getpid()) 12620903674eSAndrew Geissler logger.info("Copying rootfs to %s" % newrootfs) 12630903674eSAndrew Geissler copy_start = time.time() 12643b8a17c1SAndrew Geissler shutil.copyfile(self.rootfs, newrootfs) 12650903674eSAndrew Geissler logger.info("Copy done in %s seconds" % (time.time() - copy_start)) 12663b8a17c1SAndrew Geissler self.rootfs = newrootfs 12673b8a17c1SAndrew Geissler # Don't need a second copy now! 12683b8a17c1SAndrew Geissler self.snapshot = False 1269c926e17cSAndrew Geissler self.cleanup_files.append(newrootfs) 12703b8a17c1SAndrew Geissler 1271eb8dc403SDave Cobbley qb_rootfs_opt = self.get('QB_ROOTFS_OPT') 1272eb8dc403SDave Cobbley if qb_rootfs_opt: 1273eb8dc403SDave Cobbley self.rootfs_options = qb_rootfs_opt.replace('@ROOTFS@', self.rootfs) 1274eb8dc403SDave Cobbley else: 1275eb8dc403SDave Cobbley self.rootfs_options = '-drive file=%s,if=virtio,format=%s' % (self.rootfs, rootfs_format) 1276eb8dc403SDave Cobbley 127782c905dcSAndrew Geissler qb_rootfs_extra_opt = self.get("QB_ROOTFS_EXTRA_OPT") 127882c905dcSAndrew Geissler if qb_rootfs_extra_opt and not qb_rootfs_extra_opt.startswith(","): 127982c905dcSAndrew Geissler qb_rootfs_extra_opt = "," + qb_rootfs_extra_opt 128082c905dcSAndrew Geissler 1281eb8dc403SDave Cobbley if self.fstype in ('cpio.gz', 'cpio'): 1282eb8dc403SDave Cobbley self.kernel_cmdline = 'root=/dev/ram0 rw debugshell' 1283eb8dc403SDave Cobbley self.rootfs_options = '-initrd %s' % self.rootfs 1284eb8dc403SDave Cobbley else: 1285eb8dc403SDave Cobbley vm_drive = '' 1286eb8dc403SDave Cobbley if self.fstype in self.vmtypes: 1287eb8dc403SDave Cobbley if self.fstype == 'iso': 1288eb8dc403SDave Cobbley vm_drive = '-drive file=%s,if=virtio,media=cdrom' % self.rootfs 1289eb8dc403SDave Cobbley elif self.get('QB_DRIVE_TYPE'): 1290eb8dc403SDave Cobbley drive_type = self.get('QB_DRIVE_TYPE') 1291eb8dc403SDave Cobbley if drive_type.startswith("/dev/sd"): 1292eb8dc403SDave Cobbley logger.info('Using scsi drive') 129382c905dcSAndrew Geissler vm_drive = '-drive if=none,id=hd,file=%s,format=%s -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd%s' \ 129482c905dcSAndrew Geissler % (self.rootfs, rootfs_format, qb_rootfs_extra_opt) 1295eb8dc403SDave Cobbley elif drive_type.startswith("/dev/hd"): 1296eb8dc403SDave Cobbley logger.info('Using ide drive') 1297eb8dc403SDave Cobbley vm_drive = "-drive file=%s,format=%s" % (self.rootfs, rootfs_format) 129882c905dcSAndrew Geissler elif drive_type.startswith("/dev/vdb"): 129982c905dcSAndrew Geissler logger.info('Using block virtio drive'); 130082c905dcSAndrew Geissler vm_drive = '-drive id=disk0,file=%s,if=none,format=%s -device virtio-blk-device,drive=disk0%s' \ 130182c905dcSAndrew Geissler % (self.rootfs, rootfs_format,qb_rootfs_extra_opt) 1302eb8dc403SDave Cobbley else: 1303eb8dc403SDave Cobbley # virtio might have been selected explicitly (just use it), or 1304eb8dc403SDave Cobbley # is used as fallback (then warn about that). 1305eb8dc403SDave Cobbley if not drive_type.startswith("/dev/vd"): 13061a4b7ee2SBrad Bishop logger.warning("Unknown QB_DRIVE_TYPE: %s" % drive_type) 13071a4b7ee2SBrad Bishop logger.warning("Failed to figure out drive type, consider define or fix QB_DRIVE_TYPE") 13081a4b7ee2SBrad Bishop logger.warning('Trying to use virtio block drive') 1309eb8dc403SDave Cobbley vm_drive = '-drive if=virtio,file=%s,format=%s' % (self.rootfs, rootfs_format) 1310eb8dc403SDave Cobbley 1311eb8dc403SDave Cobbley # All branches above set vm_drive. 1312475cb72dSAndrew Geissler self.rootfs_options = vm_drive 1313475cb72dSAndrew Geissler if not self.fstype in self.vmtypes: 1314475cb72dSAndrew Geissler self.rootfs_options += ' -no-reboot' 1315595f6308SAndrew Geissler 1316595f6308SAndrew Geissler # By default, ' rw' is appended to QB_KERNEL_ROOT unless either ro or rw is explicitly passed. 1317595f6308SAndrew Geissler qb_kernel_root = self.get('QB_KERNEL_ROOT') 1318595f6308SAndrew Geissler qb_kernel_root_l = qb_kernel_root.split() 1319595f6308SAndrew Geissler if not ('ro' in qb_kernel_root_l or 'rw' in qb_kernel_root_l): 1320595f6308SAndrew Geissler qb_kernel_root += ' rw' 1321595f6308SAndrew Geissler self.kernel_cmdline = 'root=%s' % qb_kernel_root 1322eb8dc403SDave Cobbley 1323eb8dc403SDave Cobbley if self.fstype == 'nfs': 1324eb8dc403SDave Cobbley self.rootfs_options = '' 1325eb8dc403SDave Cobbley k_root = '/dev/nfs nfsroot=%s:%s,%s' % (self.nfs_server, os.path.abspath(self.rootfs), self.unfs_opts) 1326f3f93bb8SBrad Bishop self.kernel_cmdline = 'root=%s rw' % k_root 1327eb8dc403SDave Cobbley 1328eb8dc403SDave Cobbley if self.fstype == 'none': 1329eb8dc403SDave Cobbley self.rootfs_options = '' 1330eb8dc403SDave Cobbley 1331eb8dc403SDave Cobbley self.set('ROOTFS_OPTIONS', self.rootfs_options) 1332eb8dc403SDave Cobbley 1333eb8dc403SDave Cobbley def guess_qb_system(self): 1334eb8dc403SDave Cobbley """attempt to determine the appropriate qemu-system binary""" 1335eb8dc403SDave Cobbley mach = self.get('MACHINE') 1336eb8dc403SDave Cobbley if not mach: 1337864cc43bSPatrick Williams search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemuloongarch64|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*' 1338eb8dc403SDave Cobbley if self.rootfs: 1339eb8dc403SDave Cobbley match = re.match(search, self.rootfs) 1340eb8dc403SDave Cobbley if match: 1341eb8dc403SDave Cobbley mach = match.group(1) 1342eb8dc403SDave Cobbley elif self.kernel: 1343eb8dc403SDave Cobbley match = re.match(search, self.kernel) 1344eb8dc403SDave Cobbley if match: 1345eb8dc403SDave Cobbley mach = match.group(1) 1346eb8dc403SDave Cobbley 1347eb8dc403SDave Cobbley if not mach: 1348eb8dc403SDave Cobbley return None 1349eb8dc403SDave Cobbley 1350eb8dc403SDave Cobbley if mach == 'qemuarm': 1351eb8dc403SDave Cobbley qbsys = 'arm' 1352eb8dc403SDave Cobbley elif mach == 'qemuarm64': 1353eb8dc403SDave Cobbley qbsys = 'aarch64' 1354eb8dc403SDave Cobbley elif mach == 'qemux86': 1355eb8dc403SDave Cobbley qbsys = 'i386' 1356eb8dc403SDave Cobbley elif mach == 'qemux86-64': 1357eb8dc403SDave Cobbley qbsys = 'x86_64' 1358eb8dc403SDave Cobbley elif mach == 'qemuppc': 1359eb8dc403SDave Cobbley qbsys = 'ppc' 1360864cc43bSPatrick Williams elif mach == 'qemuloongarch64': 1361864cc43bSPatrick Williams qbsys = 'loongarch64' 1362eb8dc403SDave Cobbley elif mach == 'qemumips': 1363eb8dc403SDave Cobbley qbsys = 'mips' 1364eb8dc403SDave Cobbley elif mach == 'qemumips64': 1365eb8dc403SDave Cobbley qbsys = 'mips64' 1366eb8dc403SDave Cobbley elif mach == 'qemumipsel': 1367eb8dc403SDave Cobbley qbsys = 'mipsel' 1368eb8dc403SDave Cobbley elif mach == 'qemumips64el': 1369eb8dc403SDave Cobbley qbsys = 'mips64el' 1370eb8dc403SDave Cobbley elif mach == 'qemuriscv64': 1371eb8dc403SDave Cobbley qbsys = 'riscv64' 1372eb8dc403SDave Cobbley elif mach == 'qemuriscv32': 1373eb8dc403SDave Cobbley qbsys = 'riscv32' 1374004d4995SBrad Bishop else: 1375004d4995SBrad Bishop logger.error("Unable to determine QEMU PC System emulator for %s machine." % mach) 1376004d4995SBrad Bishop logger.error("As %s is not among valid QEMU machines such as," % mach) 1377004d4995SBrad Bishop logger.error("qemux86-64, qemux86, qemuarm64, qemuarm, qemumips64, qemumips64el, qemumipsel, qemumips, qemuppc") 1378004d4995SBrad Bishop raise RunQemuError("Set qb_system_name with suitable QEMU PC System emulator in .*qemuboot.conf.") 1379eb8dc403SDave Cobbley 1380eb8dc403SDave Cobbley return 'qemu-system-%s' % qbsys 1381eb8dc403SDave Cobbley 138215ae2509SBrad Bishop def check_qemu_system(self): 1383eb8dc403SDave Cobbley qemu_system = self.get('QB_SYSTEM_NAME') 1384eb8dc403SDave Cobbley if not qemu_system: 1385eb8dc403SDave Cobbley qemu_system = self.guess_qb_system() 1386eb8dc403SDave Cobbley if not qemu_system: 1387eb8dc403SDave Cobbley raise RunQemuError("Failed to boot, QB_SYSTEM_NAME is NULL!") 138815ae2509SBrad Bishop self.qemu_system = qemu_system 1389eb8dc403SDave Cobbley 13906aa7eec5SAndrew Geissler def check_render_nodes(self): 13916aa7eec5SAndrew Geissler render_hint = """If /dev/dri/renderD* is absent due to lack of suitable GPU, 'modprobe vgem' will create one suitable for mesa llvmpipe software renderer.""" 13926aa7eec5SAndrew Geissler try: 13936aa7eec5SAndrew Geissler content = os.listdir("/dev/dri") 13945082cc7fSAndrew Geissler nodes = [i for i in content if i.startswith('renderD')] 13955082cc7fSAndrew Geissler if len(nodes) == 0: 13965082cc7fSAndrew Geissler raise RunQemuError("No render nodes found in /dev/dri/: %s. %s" %(content, render_hint)) 13975082cc7fSAndrew Geissler for n in nodes: 13985082cc7fSAndrew Geissler try: 13995082cc7fSAndrew Geissler with open(os.path.join("/dev/dri", n), "w") as f: 14005082cc7fSAndrew Geissler f.close() 14015082cc7fSAndrew Geissler break 14025082cc7fSAndrew Geissler except IOError: 14035082cc7fSAndrew Geissler pass 14045082cc7fSAndrew Geissler else: 14055082cc7fSAndrew Geissler raise RunQemuError("None of the render nodes in /dev/dri/ are accessible: %s; you may need to add yourself to 'render' group or otherwise ensure you have read-write permissions on one of them." %(nodes)) 14066aa7eec5SAndrew Geissler except FileNotFoundError: 14076aa7eec5SAndrew Geissler raise RunQemuError("/dev/dri directory does not exist; no render nodes available on this machine. %s" %(render_hint)) 14086aa7eec5SAndrew Geissler 1409fc113eadSAndrew Geissler def setup_guest_agent(self): 1410fc113eadSAndrew Geissler if self.guest_agent == True: 1411fc113eadSAndrew Geissler self.qemu_opt += ' -chardev socket,path=' + self.guest_agent_sockpath + ',server,nowait,id=qga0 ' 1412fc113eadSAndrew Geissler self.qemu_opt += ' -device virtio-serial ' 1413fc113eadSAndrew Geissler self.qemu_opt += ' -device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0 ' 1414fc113eadSAndrew Geissler 1415*169d7bccSPatrick Williams def setup_qmp(self): 1416*169d7bccSPatrick Williams if self.qmp: 1417*169d7bccSPatrick Williams self.qemu_opt += " -qmp %s,server,nowait" % self.qmp 1418*169d7bccSPatrick Williams 141995ac1b8dSAndrew Geissler def setup_vga(self): 142095ac1b8dSAndrew Geissler if self.nographic == True: 142195ac1b8dSAndrew Geissler if self.sdl == True: 142295ac1b8dSAndrew Geissler raise RunQemuError('Option nographic makes no sense alongside the sdl option.') 142395ac1b8dSAndrew Geissler if self.gtk == True: 142495ac1b8dSAndrew Geissler raise RunQemuError('Option nographic makes no sense alongside the gtk option.') 142595ac1b8dSAndrew Geissler self.qemu_opt += ' -nographic' 142695ac1b8dSAndrew Geissler 142795ac1b8dSAndrew Geissler if self.novga == True: 142895ac1b8dSAndrew Geissler self.qemu_opt += ' -vga none' 142995ac1b8dSAndrew Geissler return 143095ac1b8dSAndrew Geissler 143195ac1b8dSAndrew Geissler if (self.gl_es == True or self.gl == True) and (self.sdl == False and self.gtk == False): 143295ac1b8dSAndrew Geissler raise RunQemuError('Option gl/gl-es needs gtk or sdl option.') 143395ac1b8dSAndrew Geissler 143403907ee1SPatrick Williams # If we have no display option, we autodetect based upon what qemu supports. We 143503907ee1SPatrick Williams # need our font setup and show-cusor below so we need to see what qemu --help says 143603907ee1SPatrick Williams # is supported so we can pass our correct config in. 143703907ee1SPatrick Williams if not self.nographic and not self.sdl and not self.gtk and not self.publicvnc and not self.egl_headless == True: 14382390b1b6SPatrick Williams output = subprocess.check_output([self.qemu_bin, "--help"], universal_newlines=True, env=self.qemu_environ) 143903907ee1SPatrick Williams if "-display gtk" in output: 144003907ee1SPatrick Williams self.gtk = True 144103907ee1SPatrick Williams elif "-display sdl" in output: 144203907ee1SPatrick Williams self.sdl = True 144303907ee1SPatrick Williams else: 144403907ee1SPatrick Williams self.qemu_opt += ' -display none' 144503907ee1SPatrick Williams 144695ac1b8dSAndrew Geissler if self.sdl == True or self.gtk == True or self.egl_headless == True: 144703907ee1SPatrick Williams 144803907ee1SPatrick Williams if self.qemu_system.endswith(('i386', 'x86_64')): 1449595f6308SAndrew Geissler if self.gl or self.gl_es or self.egl_headless: 1450595f6308SAndrew Geissler self.qemu_opt += ' -device virtio-vga-gl ' 1451595f6308SAndrew Geissler else: 1452595f6308SAndrew Geissler self.qemu_opt += ' -device virtio-vga ' 1453595f6308SAndrew Geissler 1454595f6308SAndrew Geissler self.qemu_opt += ' -display ' 145595ac1b8dSAndrew Geissler if self.egl_headless == True: 14566aa7eec5SAndrew Geissler self.check_render_nodes() 1457595f6308SAndrew Geissler self.set_dri_path() 145895ac1b8dSAndrew Geissler self.qemu_opt += 'egl-headless,' 145995ac1b8dSAndrew Geissler else: 146095ac1b8dSAndrew Geissler if self.sdl == True: 146195ac1b8dSAndrew Geissler self.qemu_opt += 'sdl,' 146295ac1b8dSAndrew Geissler elif self.gtk == True: 14632390b1b6SPatrick Williams self.qemu_environ['FONTCONFIG_PATH'] = '/etc/fonts' 146495ac1b8dSAndrew Geissler self.qemu_opt += 'gtk,' 146595ac1b8dSAndrew Geissler 146695ac1b8dSAndrew Geissler if self.gl == True: 1467595f6308SAndrew Geissler self.set_dri_path() 146895ac1b8dSAndrew Geissler self.qemu_opt += 'gl=on,' 146995ac1b8dSAndrew Geissler elif self.gl_es == True: 1470595f6308SAndrew Geissler self.set_dri_path() 147195ac1b8dSAndrew Geissler self.qemu_opt += 'gl=es,' 147295ac1b8dSAndrew Geissler self.qemu_opt += 'show-cursor=on' 147395ac1b8dSAndrew Geissler 147495ac1b8dSAndrew Geissler self.qemu_opt += ' %s' %self.get('QB_GRAPHICS') 147595ac1b8dSAndrew Geissler 147695ac1b8dSAndrew Geissler def setup_serial(self): 147795ac1b8dSAndrew Geissler # Setup correct kernel command line for serial 1478595f6308SAndrew Geissler if self.get('SERIAL_CONSOLES') and (self.serialstdio == True or self.serialconsole == True or self.nographic == True or self.tcpserial_portnum): 147995ac1b8dSAndrew Geissler for entry in self.get('SERIAL_CONSOLES').split(' '): 148095ac1b8dSAndrew Geissler self.kernel_cmdline_script += ' console=%s' %entry.split(';')[1] 148195ac1b8dSAndrew Geissler 1482220dafdbSAndrew Geissler # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES). 1483220dafdbSAndrew Geissler # If no serial or serialtcp options were specified, only ttyS0 is created 1484220dafdbSAndrew Geissler # and sysvinit shows an error trying to enable ttyS1: 1485220dafdbSAndrew Geissler # INIT: Id "S1" respawning too fast: disabled for 5 minutes 1486220dafdbSAndrew Geissler serial_num = len(re.findall("-serial", self.qemu_opt)) 1487220dafdbSAndrew Geissler 1488220dafdbSAndrew Geissler # Assume if the user passed serial options, they know what they want 1489220dafdbSAndrew Geissler # and pad to two devices 1490220dafdbSAndrew Geissler if serial_num == 1: 1491220dafdbSAndrew Geissler self.qemu_opt += " -serial null" 1492220dafdbSAndrew Geissler elif serial_num >= 2: 1493220dafdbSAndrew Geissler return 1494220dafdbSAndrew Geissler 149595ac1b8dSAndrew Geissler if self.serialstdio == True or self.nographic == True: 149695ac1b8dSAndrew Geissler self.qemu_opt += " -serial mon:stdio" 149795ac1b8dSAndrew Geissler else: 149895ac1b8dSAndrew Geissler self.qemu_opt += " -serial mon:vc" 149995ac1b8dSAndrew Geissler if self.serialconsole: 150095ac1b8dSAndrew Geissler if sys.stdin.isatty(): 150195ac1b8dSAndrew Geissler subprocess.check_call(("stty", "intr", "^]")) 150295ac1b8dSAndrew Geissler logger.info("Interrupt character is '^]'") 150395ac1b8dSAndrew Geissler 150495ac1b8dSAndrew Geissler self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT") 150595ac1b8dSAndrew Geissler 150695ac1b8dSAndrew Geissler serial_num = len(re.findall("-serial", self.qemu_opt)) 150795ac1b8dSAndrew Geissler if serial_num < 2: 150895ac1b8dSAndrew Geissler self.qemu_opt += " -serial null" 150995ac1b8dSAndrew Geissler 151003907ee1SPatrick Williams def find_qemu(self): 151115ae2509SBrad Bishop qemu_bin = os.path.join(self.bindir_native, self.qemu_system) 1512eb8dc403SDave Cobbley 1513eb8dc403SDave Cobbley # It is possible to have qemu-native in ASSUME_PROVIDED, and it won't 1514eb8dc403SDave Cobbley # find QEMU in sysroot, it needs to use host's qemu. 1515eb8dc403SDave Cobbley if not os.path.exists(qemu_bin): 1516eb8dc403SDave Cobbley logger.info("QEMU binary not found in %s, trying host's QEMU" % qemu_bin) 1517eb8dc403SDave Cobbley for path in (os.environ['PATH'] or '').split(':'): 151815ae2509SBrad Bishop qemu_bin_tmp = os.path.join(path, self.qemu_system) 1519eb8dc403SDave Cobbley logger.info("Trying: %s" % qemu_bin_tmp) 1520eb8dc403SDave Cobbley if os.path.exists(qemu_bin_tmp): 1521eb8dc403SDave Cobbley qemu_bin = qemu_bin_tmp 1522eb8dc403SDave Cobbley if not os.path.isabs(qemu_bin): 1523eb8dc403SDave Cobbley qemu_bin = os.path.abspath(qemu_bin) 1524eb8dc403SDave Cobbley logger.info("Using host's QEMU: %s" % qemu_bin) 1525eb8dc403SDave Cobbley break 1526eb8dc403SDave Cobbley 1527eb8dc403SDave Cobbley if not os.access(qemu_bin, os.X_OK): 1528eb8dc403SDave Cobbley raise OEPathError("No QEMU binary '%s' could be found" % qemu_bin) 152903907ee1SPatrick Williams self.qemu_bin = qemu_bin 1530eb8dc403SDave Cobbley 153103907ee1SPatrick Williams def setup_final(self): 153203907ee1SPatrick Williams 153303907ee1SPatrick Williams self.find_qemu() 153403907ee1SPatrick Williams 153503907ee1SPatrick Williams self.qemu_opt = "%s %s %s %s %s" % (self.qemu_bin, self.get('NETWORK_CMD'), self.get('QB_RNG'), self.get('ROOTFS_OPTIONS'), self.get('QB_OPT_APPEND').replace('@DEPLOY_DIR_IMAGE@', self.get('DEPLOY_DIR_IMAGE'))) 1536eb8dc403SDave Cobbley 1537eb8dc403SDave Cobbley for ovmf in self.ovmf_bios: 1538eb8dc403SDave Cobbley format = ovmf.rsplit('.', 1)[-1] 153995ac1b8dSAndrew Geissler if format == "bin": 154095ac1b8dSAndrew Geissler format = "raw" 1541eb8dc403SDave Cobbley self.qemu_opt += ' -drive if=pflash,format=%s,file=%s' % (format, ovmf) 1542eb8dc403SDave Cobbley 1543eb8dc403SDave Cobbley self.qemu_opt += ' ' + self.qemu_opt_script 1544eb8dc403SDave Cobbley 154508902b01SBrad Bishop if self.ovmf_secboot_pkkek1: 154608902b01SBrad Bishop # Provide the Platform Key and first Key Exchange Key certificate as an 154708902b01SBrad Bishop # OEM string in the SMBIOS Type 11 table. Prepend the certificate string 154808902b01SBrad Bishop # with "application prefix" of the EnrollDefaultKeys.efi application 154908902b01SBrad Bishop self.qemu_opt += ' -smbios type=11,value=4e32566d-8e9e-4f52-81d3-5bb9715f9727:' \ 155008902b01SBrad Bishop + self.ovmf_secboot_pkkek1 155108902b01SBrad Bishop 155299467dabSAndrew Geissler # Append qemuparams to override previous settings 155399467dabSAndrew Geissler if self.qemuparams: 155499467dabSAndrew Geissler self.qemu_opt += ' ' + self.qemuparams 155599467dabSAndrew Geissler 1556eb8dc403SDave Cobbley if self.snapshot: 1557eb8dc403SDave Cobbley self.qemu_opt += " -snapshot" 1558eb8dc403SDave Cobbley 1559fc113eadSAndrew Geissler self.setup_guest_agent() 1560*169d7bccSPatrick Williams self.setup_qmp() 156195ac1b8dSAndrew Geissler self.setup_serial() 156295ac1b8dSAndrew Geissler self.setup_vga() 1563eb8dc403SDave Cobbley 1564eb8dc403SDave Cobbley def start_qemu(self): 1565004d4995SBrad Bishop import shlex 1566eb8dc403SDave Cobbley if self.kernel: 1567615f2f11SAndrew Geissler kernel_opts = "-kernel %s" % (self.kernel) 1568615f2f11SAndrew Geissler if self.get('QB_KERNEL_CMDLINE') == "none": 1569615f2f11SAndrew Geissler if self.bootparams: 1570615f2f11SAndrew Geissler kernel_opts += " -append '%s'" % (self.bootparams) 1571615f2f11SAndrew Geissler else: 1572615f2f11SAndrew Geissler kernel_opts += " -append '%s %s %s %s'" % (self.kernel_cmdline, 1573eb8dc403SDave Cobbley self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'), 1574eb8dc403SDave Cobbley self.bootparams) 1575eb8dc403SDave Cobbley if self.dtb: 1576eb8dc403SDave Cobbley kernel_opts += " -dtb %s" % self.dtb 1577eb8dc403SDave Cobbley else: 1578eb8dc403SDave Cobbley kernel_opts = "" 1579213cb269SPatrick Williams 1580213cb269SPatrick Williams if self.bios: 1581213cb269SPatrick Williams self.qemu_opt += " -bios %s" % self.bios 1582213cb269SPatrick Williams 1583eb8dc403SDave Cobbley cmd = "%s %s" % (self.qemu_opt, kernel_opts) 1584004d4995SBrad Bishop cmds = shlex.split(cmd) 1585eb8dc403SDave Cobbley logger.info('Running %s\n' % cmd) 158687f5cff0SAndrew Geissler with open('/proc/uptime', 'r') as f: 158787f5cff0SAndrew Geissler uptime_seconds = f.readline().split()[0] 158887f5cff0SAndrew Geissler logger.info('Host uptime: %s\n' % uptime_seconds) 1589f86d0556SBrad Bishop pass_fds = [] 159008902b01SBrad Bishop if self.taplock_descriptor: 159108902b01SBrad Bishop pass_fds = [self.taplock_descriptor.fileno()] 159208902b01SBrad Bishop if len(self.portlocks): 159308902b01SBrad Bishop for descriptor in self.portlocks.values(): 159408902b01SBrad Bishop pass_fds.append(descriptor.fileno()) 15952390b1b6SPatrick Williams process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds, env=self.qemu_environ) 15966aa7eec5SAndrew Geissler self.qemuprocess = process 1597004d4995SBrad Bishop retcode = process.wait() 1598004d4995SBrad Bishop if retcode: 1599004d4995SBrad Bishop if retcode == -signal.SIGTERM: 1600004d4995SBrad Bishop logger.info("Qemu terminated by SIGTERM") 1601004d4995SBrad Bishop else: 1602eb8dc403SDave Cobbley logger.error("Failed to run qemu: %s", process.stderr.read().decode()) 1603eb8dc403SDave Cobbley 1604517393d9SAndrew Geissler def cleanup_cmd(self): 1605517393d9SAndrew Geissler cmd = self.get('QB_CLEANUP_CMD') 1606517393d9SAndrew Geissler if cmd != '': 1607517393d9SAndrew Geissler logger.info('Running cleanup command %s' % str(cmd)) 1608517393d9SAndrew Geissler if subprocess.call(cmd, shell=True) != 0: 1609fc113eadSAndrew Geissler raise RunQemuError('Failed to run %s' % str(cmd)) 1610517393d9SAndrew Geissler 1611eb8dc403SDave Cobbley def cleanup(self): 1612004d4995SBrad Bishop if self.cleaned: 1613004d4995SBrad Bishop return 1614004d4995SBrad Bishop 1615004d4995SBrad Bishop # avoid dealing with SIGTERM when cleanup function is running 1616004d4995SBrad Bishop signal.signal(signal.SIGTERM, signal.SIG_IGN) 1617004d4995SBrad Bishop 1618004d4995SBrad Bishop logger.info("Cleaning up") 16196aa7eec5SAndrew Geissler 16206aa7eec5SAndrew Geissler if self.qemuprocess: 16216aa7eec5SAndrew Geissler try: 16226aa7eec5SAndrew Geissler # give it some time to shut down, ignore return values and output 16236aa7eec5SAndrew Geissler self.qemuprocess.send_signal(signal.SIGTERM) 16246aa7eec5SAndrew Geissler self.qemuprocess.communicate(timeout=5) 16256aa7eec5SAndrew Geissler except subprocess.TimeoutExpired: 16266aa7eec5SAndrew Geissler self.qemuprocess.kill() 16276aa7eec5SAndrew Geissler 162887f5cff0SAndrew Geissler with open('/proc/uptime', 'r') as f: 162987f5cff0SAndrew Geissler uptime_seconds = f.readline().split()[0] 163087f5cff0SAndrew Geissler logger.info('Host uptime: %s\n' % uptime_seconds) 1631eb8dc403SDave Cobbley if self.cleantap: 16328f840685SAndrew Geissler cmd = ('sudo', self.qemuifdown, self.tap) 1633977dc1acSBrad Bishop logger.debug('Running %s' % str(cmd)) 1634977dc1acSBrad Bishop subprocess.check_call(cmd) 163508902b01SBrad Bishop self.release_taplock() 1636eb8dc403SDave Cobbley 1637eb8dc403SDave Cobbley if self.nfs_running: 1638eb8dc403SDave Cobbley logger.info("Shutting down the userspace NFS server...") 1639977dc1acSBrad Bishop cmd = ("runqemu-export-rootfs", "stop", self.rootfs) 1640977dc1acSBrad Bishop logger.debug('Running %s' % str(cmd)) 1641977dc1acSBrad Bishop subprocess.check_call(cmd) 1642520786ccSPatrick Williams self.release_portlock() 1643eb8dc403SDave Cobbley 1644eb8dc403SDave Cobbley if self.saved_stty: 1645977dc1acSBrad Bishop subprocess.check_call(("stty", self.saved_stty)) 1646eb8dc403SDave Cobbley 1647c926e17cSAndrew Geissler if self.cleanup_files: 1648c926e17cSAndrew Geissler for ent in self.cleanup_files: 1649c926e17cSAndrew Geissler logger.info('Removing %s' % ent) 1650c926e17cSAndrew Geissler if os.path.isfile(ent): 1651c926e17cSAndrew Geissler os.remove(ent) 1652c926e17cSAndrew Geissler else: 1653c926e17cSAndrew Geissler shutil.rmtree(ent) 1654eb8dc403SDave Cobbley 16556aa7eec5SAndrew Geissler # Deliberately ignore the return code of 'tput smam'. 16566aa7eec5SAndrew Geissler subprocess.call(["tput", "smam"]) 16576aa7eec5SAndrew Geissler 1658004d4995SBrad Bishop self.cleaned = True 1659004d4995SBrad Bishop 16608e7b46e2SPatrick Williams def run_bitbake_env(self, mach=None, target=''): 1661eb8dc403SDave Cobbley bitbake = shutil.which('bitbake') 1662eb8dc403SDave Cobbley if not bitbake: 1663eb8dc403SDave Cobbley return 1664eb8dc403SDave Cobbley 1665eb8dc403SDave Cobbley if not mach: 1666eb8dc403SDave Cobbley mach = self.get('MACHINE') 1667eb8dc403SDave Cobbley 166882c905dcSAndrew Geissler multiconfig = self.get('MULTICONFIG') 166982c905dcSAndrew Geissler if multiconfig: 167082c905dcSAndrew Geissler multiconfig = "mc:%s" % multiconfig 167182c905dcSAndrew Geissler 1672eb8dc403SDave Cobbley if mach: 16738e7b46e2SPatrick Williams cmd = 'MACHINE=%s bitbake -e %s %s' % (mach, multiconfig, target) 1674eb8dc403SDave Cobbley else: 16758e7b46e2SPatrick Williams cmd = 'bitbake -e %s %s' % (multiconfig, target) 1676eb8dc403SDave Cobbley 1677eb8dc403SDave Cobbley logger.info('Running %s...' % cmd) 16788e7b46e2SPatrick Williams try: 167982c905dcSAndrew Geissler return subprocess.check_output(cmd, shell=True).decode('utf-8') 16808e7b46e2SPatrick Williams except subprocess.CalledProcessError as err: 16818e7b46e2SPatrick Williams logger.warning("Couldn't run '%s' to gather environment information, maybe the target wasn't an image name, will retry with virtual/kernel as a target:\n%s" % (cmd, err.output.decode('utf-8'))) 16828e7b46e2SPatrick Williams # need something with IMAGE_NAME_SUFFIX/IMAGE_LINK_NAME defined (kernel also inherits image-artifact-names.bbclass) 16838e7b46e2SPatrick Williams target = 'virtual/kernel' 16848e7b46e2SPatrick Williams if mach: 16858e7b46e2SPatrick Williams cmd = 'MACHINE=%s bitbake -e %s %s' % (mach, multiconfig, target) 16868e7b46e2SPatrick Williams else: 16878e7b46e2SPatrick Williams cmd = 'bitbake -e %s %s' % (multiconfig, target) 16888e7b46e2SPatrick Williams try: 16898e7b46e2SPatrick Williams return subprocess.check_output(cmd, shell=True).decode('utf-8') 16908e7b46e2SPatrick Williams except subprocess.CalledProcessError as err: 16918e7b46e2SPatrick Williams logger.warning("Couldn't run '%s' to gather environment information, giving up with 'bitbake -e':\n%s" % (cmd, err.output.decode('utf-8'))) 16928e7b46e2SPatrick Williams return '' 169382c905dcSAndrew Geissler 16948e7b46e2SPatrick Williams 16958e7b46e2SPatrick Williams def load_bitbake_env(self, mach=None, target=None): 169682c905dcSAndrew Geissler if self.bitbake_e: 169782c905dcSAndrew Geissler return 169882c905dcSAndrew Geissler 16998e7b46e2SPatrick Williams self.bitbake_e = self.run_bitbake_env(mach=mach, target=target) 1700eb8dc403SDave Cobbley 1701eb8dc403SDave Cobbley def validate_combos(self): 1702eb8dc403SDave Cobbley if (self.fstype in self.vmtypes) and self.kernel: 1703eb8dc403SDave Cobbley raise RunQemuError("%s doesn't need kernel %s!" % (self.fstype, self.kernel)) 1704eb8dc403SDave Cobbley 1705eb8dc403SDave Cobbley @property 1706eb8dc403SDave Cobbley def bindir_native(self): 1707eb8dc403SDave Cobbley result = self.get('STAGING_BINDIR_NATIVE') 1708eb8dc403SDave Cobbley if result and os.path.exists(result): 1709eb8dc403SDave Cobbley return result 1710eb8dc403SDave Cobbley 171182c905dcSAndrew Geissler cmd = ['bitbake', '-e'] 171282c905dcSAndrew Geissler multiconfig = self.get('MULTICONFIG') 171382c905dcSAndrew Geissler if multiconfig: 171482c905dcSAndrew Geissler cmd.append('mc:%s:qemu-helper-native' % multiconfig) 171582c905dcSAndrew Geissler else: 171682c905dcSAndrew Geissler cmd.append('qemu-helper-native') 171782c905dcSAndrew Geissler 1718977dc1acSBrad Bishop logger.info('Running %s...' % str(cmd)) 1719977dc1acSBrad Bishop out = subprocess.check_output(cmd).decode('utf-8') 1720eb8dc403SDave Cobbley 1721eb8dc403SDave Cobbley match = re.search('^STAGING_BINDIR_NATIVE="(.*)"', out, re.M) 1722eb8dc403SDave Cobbley if match: 1723eb8dc403SDave Cobbley result = match.group(1) 1724eb8dc403SDave Cobbley if os.path.exists(result): 1725eb8dc403SDave Cobbley self.set('STAGING_BINDIR_NATIVE', result) 1726eb8dc403SDave Cobbley return result 1727eb8dc403SDave Cobbley raise RunQemuError("Native sysroot directory %s doesn't exist" % result) 1728eb8dc403SDave Cobbley else: 1729fc113eadSAndrew Geissler raise RunQemuError("Can't find STAGING_BINDIR_NATIVE in '%s' output" % str(cmd)) 1730eb8dc403SDave Cobbley 1731eb8dc403SDave Cobbley 1732eb8dc403SDave Cobbleydef main(): 1733eb8dc403SDave Cobbley if "help" in sys.argv or '-h' in sys.argv or '--help' in sys.argv: 1734eb8dc403SDave Cobbley print_usage() 1735eb8dc403SDave Cobbley return 0 1736eb8dc403SDave Cobbley try: 1737eb8dc403SDave Cobbley config = BaseConfig() 1738004d4995SBrad Bishop 1739c9f7865aSAndrew Geissler renice = os.path.expanduser("~/bin/runqemu-renice") 1740c9f7865aSAndrew Geissler if os.path.exists(renice): 1741c9f7865aSAndrew Geissler logger.info('Using %s to renice' % renice) 1742c9f7865aSAndrew Geissler subprocess.check_call([renice, str(os.getpid())]) 1743c9f7865aSAndrew Geissler 1744004d4995SBrad Bishop def sigterm_handler(signum, frame): 17456aa7eec5SAndrew Geissler logger.info("Received signal: %s" % (signum)) 1746004d4995SBrad Bishop config.cleanup() 1747004d4995SBrad Bishop signal.signal(signal.SIGTERM, sigterm_handler) 1748004d4995SBrad Bishop 1749eb8dc403SDave Cobbley config.check_args() 1750eb8dc403SDave Cobbley config.read_qemuboot() 1751eb8dc403SDave Cobbley config.check_and_set() 1752eb8dc403SDave Cobbley # Check whether the combos is valid or not 1753eb8dc403SDave Cobbley config.validate_combos() 1754eb8dc403SDave Cobbley config.print_config() 1755eb8dc403SDave Cobbley config.setup_network() 1756eb8dc403SDave Cobbley config.setup_rootfs() 1757eb8dc403SDave Cobbley config.setup_final() 1758517393d9SAndrew Geissler config.setup_cmd() 1759eb8dc403SDave Cobbley config.start_qemu() 1760eb8dc403SDave Cobbley except RunQemuError as err: 1761eb8dc403SDave Cobbley logger.error(err) 1762eb8dc403SDave Cobbley return 1 1763eb8dc403SDave Cobbley except Exception as err: 1764eb8dc403SDave Cobbley import traceback 1765eb8dc403SDave Cobbley traceback.print_exc() 1766eb8dc403SDave Cobbley return 1 1767eb8dc403SDave Cobbley finally: 1768517393d9SAndrew Geissler config.cleanup_cmd() 1769eb8dc403SDave Cobbley config.cleanup() 1770eb8dc403SDave Cobbley 1771eb8dc403SDave Cobbleyif __name__ == "__main__": 1772eb8dc403SDave Cobbley sys.exit(main()) 1773