xref: /OK3568_Linux_fs/yocto/scripts/runqemu (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun#!/usr/bin/env python3
2*4882a593Smuzhiyun
3*4882a593Smuzhiyun# Handle running OE images standalone with QEMU
4*4882a593Smuzhiyun#
5*4882a593Smuzhiyun# Copyright (C) 2006-2011 Linux Foundation
6*4882a593Smuzhiyun# Copyright (c) 2016 Wind River Systems, Inc.
7*4882a593Smuzhiyun#
8*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only
9*4882a593Smuzhiyun#
10*4882a593Smuzhiyun
11*4882a593Smuzhiyunimport os
12*4882a593Smuzhiyunimport sys
13*4882a593Smuzhiyunimport logging
14*4882a593Smuzhiyunimport subprocess
15*4882a593Smuzhiyunimport re
16*4882a593Smuzhiyunimport fcntl
17*4882a593Smuzhiyunimport shutil
18*4882a593Smuzhiyunimport glob
19*4882a593Smuzhiyunimport configparser
20*4882a593Smuzhiyunimport signal
21*4882a593Smuzhiyunimport time
22*4882a593Smuzhiyun
23*4882a593Smuzhiyunclass RunQemuError(Exception):
24*4882a593Smuzhiyun    """Custom exception to raise on known errors."""
25*4882a593Smuzhiyun    pass
26*4882a593Smuzhiyun
27*4882a593Smuzhiyunclass OEPathError(RunQemuError):
28*4882a593Smuzhiyun    """Custom Exception to give better guidance on missing binaries"""
29*4882a593Smuzhiyun    def __init__(self, message):
30*4882a593Smuzhiyun        super().__init__("In order for this script to dynamically infer paths\n \
31*4882a593Smuzhiyunkernels or filesystem images, you either need bitbake in your PATH\n \
32*4882a593Smuzhiyunor to source oe-init-build-env before running this script.\n\n \
33*4882a593SmuzhiyunDynamic path inference can be avoided by passing a *.qemuboot.conf to\n \
34*4882a593Smuzhiyunrunqemu, i.e. `runqemu /path/to/my-image-name.qemuboot.conf`\n\n %s" % message)
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun
37*4882a593Smuzhiyundef create_logger():
38*4882a593Smuzhiyun    logger = logging.getLogger('runqemu')
39*4882a593Smuzhiyun    logger.setLevel(logging.INFO)
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun    # create console handler and set level to debug
42*4882a593Smuzhiyun    ch = logging.StreamHandler()
43*4882a593Smuzhiyun    ch.setLevel(logging.DEBUG)
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun    # create formatter
46*4882a593Smuzhiyun    formatter = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun    # add formatter to ch
49*4882a593Smuzhiyun    ch.setFormatter(formatter)
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun    # add ch to logger
52*4882a593Smuzhiyun    logger.addHandler(ch)
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun    return logger
55*4882a593Smuzhiyun
56*4882a593Smuzhiyunlogger = create_logger()
57*4882a593Smuzhiyun
58*4882a593Smuzhiyundef print_usage():
59*4882a593Smuzhiyun    print("""
60*4882a593SmuzhiyunUsage: you can run this script with any valid combination
61*4882a593Smuzhiyunof the following environment variables (in any order):
62*4882a593Smuzhiyun  KERNEL - the kernel image file to use
63*4882a593Smuzhiyun  BIOS - the bios image file to use
64*4882a593Smuzhiyun  ROOTFS - the rootfs image file or nfsroot directory to use
65*4882a593Smuzhiyun  DEVICE_TREE - the device tree blob to use
66*4882a593Smuzhiyun  MACHINE - the machine name (optional, autodetected from KERNEL filename if unspecified)
67*4882a593Smuzhiyun  Simplified QEMU command-line options can be passed with:
68*4882a593Smuzhiyun    nographic - disable video console
69*4882a593Smuzhiyun    novga - Disable VGA emulation completely
70*4882a593Smuzhiyun    sdl - choose the SDL UI frontend
71*4882a593Smuzhiyun    gtk - choose the Gtk UI frontend
72*4882a593Smuzhiyun    gl - enable virgl-based GL acceleration (also needs gtk or sdl options)
73*4882a593Smuzhiyun    gl-es - enable virgl-based GL acceleration, using OpenGL ES (also needs gtk or sdl options)
74*4882a593Smuzhiyun    egl-headless - enable headless EGL output; use vnc (via publicvnc option) or spice to see it
75*4882a593Smuzhiyun    (hint: if /dev/dri/renderD* is absent due to lack of suitable GPU, 'modprobe vgem' will create
76*4882a593Smuzhiyun    one suitable for mesa llvmpipe software renderer)
77*4882a593Smuzhiyun    serial - enable a serial console on /dev/ttyS0
78*4882a593Smuzhiyun    serialstdio - enable a serial console on the console (regardless of graphics mode)
79*4882a593Smuzhiyun    slirp - enable user networking, no root privilege is required
80*4882a593Smuzhiyun    snapshot - don't write changes back to images
81*4882a593Smuzhiyun    kvm - enable KVM when running x86/x86_64 (VT-capable CPU required)
82*4882a593Smuzhiyun    kvm-vhost - enable KVM with vhost when running x86/x86_64 (VT-capable CPU required)
83*4882a593Smuzhiyun    publicvnc - enable a VNC server open to all hosts
84*4882a593Smuzhiyun    audio - enable audio
85*4882a593Smuzhiyun    [*/]ovmf* - OVMF firmware file or base name for booting with UEFI
86*4882a593Smuzhiyun  tcpserial=<port> - specify tcp serial port number
87*4882a593Smuzhiyun  qemuparams=<xyz> - specify custom parameters to QEMU
88*4882a593Smuzhiyun  bootparams=<xyz> - specify custom kernel parameters during boot
89*4882a593Smuzhiyun  help, -h, --help: print this text
90*4882a593Smuzhiyun  -d, --debug: Enable debug output
91*4882a593Smuzhiyun  -q, --quiet: Hide most output except error messages
92*4882a593Smuzhiyun
93*4882a593SmuzhiyunExamples:
94*4882a593Smuzhiyun  runqemu
95*4882a593Smuzhiyun  runqemu qemuarm
96*4882a593Smuzhiyun  runqemu tmp/deploy/images/qemuarm
97*4882a593Smuzhiyun  runqemu tmp/deploy/images/qemux86/<qemuboot.conf>
98*4882a593Smuzhiyun  runqemu qemux86-64 core-image-sato ext4
99*4882a593Smuzhiyun  runqemu qemux86-64 wic-image-minimal wic
100*4882a593Smuzhiyun  runqemu path/to/bzImage-qemux86.bin path/to/nfsrootdir/ serial
101*4882a593Smuzhiyun  runqemu qemux86 iso/hddimg/wic.vmdk/wic.vhd/wic.vhdx/wic.qcow2/wic.vdi/ramfs/cpio.gz...
102*4882a593Smuzhiyun  runqemu qemux86 qemuparams="-m 256"
103*4882a593Smuzhiyun  runqemu qemux86 bootparams="psplash=false"
104*4882a593Smuzhiyun  runqemu path/to/<image>-<machine>.wic
105*4882a593Smuzhiyun  runqemu path/to/<image>-<machine>.wic.vmdk
106*4882a593Smuzhiyun  runqemu path/to/<image>-<machine>.wic.vhdx
107*4882a593Smuzhiyun  runqemu path/to/<image>-<machine>.wic.vhd
108*4882a593Smuzhiyun""")
109*4882a593Smuzhiyun
110*4882a593Smuzhiyundef check_tun():
111*4882a593Smuzhiyun    """Check /dev/net/tun"""
112*4882a593Smuzhiyun    dev_tun = '/dev/net/tun'
113*4882a593Smuzhiyun    if not os.path.exists(dev_tun):
114*4882a593Smuzhiyun        raise RunQemuError("TUN control device %s is unavailable; you may need to enable TUN (e.g. sudo modprobe tun)" % dev_tun)
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun    if not os.access(dev_tun, os.W_OK):
117*4882a593Smuzhiyun        raise RunQemuError("TUN control device %s is not writable, please fix (e.g. sudo chmod 666 %s)" % (dev_tun, dev_tun))
118*4882a593Smuzhiyun
119*4882a593Smuzhiyundef get_first_file(cmds):
120*4882a593Smuzhiyun    """Return first file found in wildcard cmds"""
121*4882a593Smuzhiyun    for cmd in cmds:
122*4882a593Smuzhiyun        all_files = glob.glob(cmd)
123*4882a593Smuzhiyun        if all_files:
124*4882a593Smuzhiyun            for f in all_files:
125*4882a593Smuzhiyun                if not os.path.isdir(f):
126*4882a593Smuzhiyun                    return f
127*4882a593Smuzhiyun    return ''
128*4882a593Smuzhiyun
129*4882a593Smuzhiyunclass BaseConfig(object):
130*4882a593Smuzhiyun    def __init__(self):
131*4882a593Smuzhiyun        # The self.d saved vars from self.set(), part of them are from qemuboot.conf
132*4882a593Smuzhiyun        self.d = {'QB_KERNEL_ROOT': '/dev/vda'}
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun        # Supported env vars, add it here if a var can be got from env,
135*4882a593Smuzhiyun        # and don't use os.getenv in the code.
136*4882a593Smuzhiyun        self.env_vars = ('MACHINE',
137*4882a593Smuzhiyun                        'ROOTFS',
138*4882a593Smuzhiyun                        'KERNEL',
139*4882a593Smuzhiyun                        'BIOS',
140*4882a593Smuzhiyun                        'DEVICE_TREE',
141*4882a593Smuzhiyun                        'DEPLOY_DIR_IMAGE',
142*4882a593Smuzhiyun                        'OE_TMPDIR',
143*4882a593Smuzhiyun                        'OECORE_NATIVE_SYSROOT',
144*4882a593Smuzhiyun                        'MULTICONFIG',
145*4882a593Smuzhiyun                        'SERIAL_CONSOLES',
146*4882a593Smuzhiyun                        )
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun        self.qemu_opt = ''
149*4882a593Smuzhiyun        self.qemu_opt_script = ''
150*4882a593Smuzhiyun        self.qemuparams = ''
151*4882a593Smuzhiyun        self.nfs_server = ''
152*4882a593Smuzhiyun        self.rootfs = ''
153*4882a593Smuzhiyun        # File name(s) of a OVMF firmware file or variable store,
154*4882a593Smuzhiyun        # to be added with -drive if=pflash.
155*4882a593Smuzhiyun        # Found in the same places as the rootfs, with or without one of
156*4882a593Smuzhiyun        # these suffices: qcow2, bin.
157*4882a593Smuzhiyun        self.ovmf_bios = []
158*4882a593Smuzhiyun        # When enrolling default Secure Boot keys, the hypervisor
159*4882a593Smuzhiyun        # must provide the Platform Key and the first Key Exchange Key
160*4882a593Smuzhiyun        # certificate in the Type 11 SMBIOS table.
161*4882a593Smuzhiyun        self.ovmf_secboot_pkkek1 = ''
162*4882a593Smuzhiyun        self.qemuboot = ''
163*4882a593Smuzhiyun        self.qbconfload = False
164*4882a593Smuzhiyun        self.kernel = ''
165*4882a593Smuzhiyun        self.bios = ''
166*4882a593Smuzhiyun        self.kernel_cmdline = ''
167*4882a593Smuzhiyun        self.kernel_cmdline_script = ''
168*4882a593Smuzhiyun        self.bootparams = ''
169*4882a593Smuzhiyun        self.dtb = ''
170*4882a593Smuzhiyun        self.fstype = ''
171*4882a593Smuzhiyun        self.kvm_enabled = False
172*4882a593Smuzhiyun        self.vhost_enabled = False
173*4882a593Smuzhiyun        self.slirp_enabled = False
174*4882a593Smuzhiyun        self.net_bridge = None
175*4882a593Smuzhiyun        self.nfs_instance = 0
176*4882a593Smuzhiyun        self.nfs_running = False
177*4882a593Smuzhiyun        self.serialconsole = False
178*4882a593Smuzhiyun        self.serialstdio = False
179*4882a593Smuzhiyun        self.nographic = False
180*4882a593Smuzhiyun        self.sdl = False
181*4882a593Smuzhiyun        self.gtk = False
182*4882a593Smuzhiyun        self.gl = False
183*4882a593Smuzhiyun        self.gl_es = False
184*4882a593Smuzhiyun        self.egl_headless = False
185*4882a593Smuzhiyun        self.publicvnc = False
186*4882a593Smuzhiyun        self.novga = False
187*4882a593Smuzhiyun        self.cleantap = False
188*4882a593Smuzhiyun        self.saved_stty = ''
189*4882a593Smuzhiyun        self.audio_enabled = False
190*4882a593Smuzhiyun        self.tcpserial_portnum = ''
191*4882a593Smuzhiyun        self.taplock = ''
192*4882a593Smuzhiyun        self.taplock_descriptor = None
193*4882a593Smuzhiyun        self.portlocks = {}
194*4882a593Smuzhiyun        self.bitbake_e = ''
195*4882a593Smuzhiyun        self.snapshot = False
196*4882a593Smuzhiyun        self.wictypes = ('wic', 'wic.vmdk', 'wic.qcow2', 'wic.vdi', "wic.vhd", "wic.vhdx")
197*4882a593Smuzhiyun        self.fstypes = ('ext2', 'ext3', 'ext4', 'jffs2', 'nfs', 'btrfs',
198*4882a593Smuzhiyun                        'cpio.gz', 'cpio', 'ramfs', 'tar.bz2', 'tar.gz')
199*4882a593Smuzhiyun        self.vmtypes = ('hddimg', 'iso')
200*4882a593Smuzhiyun        self.fsinfo = {}
201*4882a593Smuzhiyun        self.network_device = "-device e1000,netdev=net0,mac=@MAC@"
202*4882a593Smuzhiyun        self.cmdline_ip_slirp = "ip=dhcp"
203*4882a593Smuzhiyun        self.cmdline_ip_tap = "ip=192.168.7.@CLIENT@::192.168.7.@GATEWAY@:255.255.255.0::eth0:off:8.8.8.8"
204*4882a593Smuzhiyun        # Use different mac section for tap and slirp to avoid
205*4882a593Smuzhiyun        # conflicts, e.g., when one is running with tap, the other is
206*4882a593Smuzhiyun        # running with slirp.
207*4882a593Smuzhiyun        # The last section is dynamic, which is for avoiding conflicts,
208*4882a593Smuzhiyun        # when multiple qemus are running, e.g., when multiple tap or
209*4882a593Smuzhiyun        # slirp qemus are running.
210*4882a593Smuzhiyun        self.mac_tap = "52:54:00:12:34:"
211*4882a593Smuzhiyun        self.mac_slirp = "52:54:00:12:35:"
212*4882a593Smuzhiyun        # pid of the actual qemu process
213*4882a593Smuzhiyun        self.qemu_environ = os.environ.copy()
214*4882a593Smuzhiyun        self.qemuprocess = None
215*4882a593Smuzhiyun        # avoid cleanup twice
216*4882a593Smuzhiyun        self.cleaned = False
217*4882a593Smuzhiyun        # Files to cleanup after run
218*4882a593Smuzhiyun        self.cleanup_files = []
219*4882a593Smuzhiyun
220*4882a593Smuzhiyun    def acquire_taplock(self, error=True):
221*4882a593Smuzhiyun        logger.debug("Acquiring lockfile %s..." % self.taplock)
222*4882a593Smuzhiyun        try:
223*4882a593Smuzhiyun            self.taplock_descriptor = open(self.taplock, 'w')
224*4882a593Smuzhiyun            fcntl.flock(self.taplock_descriptor, fcntl.LOCK_EX|fcntl.LOCK_NB)
225*4882a593Smuzhiyun        except Exception as e:
226*4882a593Smuzhiyun            msg = "Acquiring lockfile %s failed: %s" % (self.taplock, e)
227*4882a593Smuzhiyun            if error:
228*4882a593Smuzhiyun                logger.error(msg)
229*4882a593Smuzhiyun            else:
230*4882a593Smuzhiyun                logger.info(msg)
231*4882a593Smuzhiyun            if self.taplock_descriptor:
232*4882a593Smuzhiyun                self.taplock_descriptor.close()
233*4882a593Smuzhiyun                self.taplock_descriptor = None
234*4882a593Smuzhiyun            return False
235*4882a593Smuzhiyun        return True
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun    def release_taplock(self):
238*4882a593Smuzhiyun        if self.taplock_descriptor:
239*4882a593Smuzhiyun            logger.debug("Releasing lockfile for tap device '%s'" % self.tap)
240*4882a593Smuzhiyun            # We pass the fd to the qemu process and if we unlock here, it would unlock for
241*4882a593Smuzhiyun            # that too. Therefore don't unlock, just close
242*4882a593Smuzhiyun            # fcntl.flock(self.taplock_descriptor, fcntl.LOCK_UN)
243*4882a593Smuzhiyun            self.taplock_descriptor.close()
244*4882a593Smuzhiyun            # Removing the file is a potential race, don't do that either
245*4882a593Smuzhiyun            # os.remove(self.taplock)
246*4882a593Smuzhiyun            self.taplock_descriptor = None
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun    def check_free_port(self, host, port, lockdir):
249*4882a593Smuzhiyun        """ Check whether the port is free or not """
250*4882a593Smuzhiyun        import socket
251*4882a593Smuzhiyun        from contextlib import closing
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun        lockfile = os.path.join(lockdir, str(port) + '.lock')
254*4882a593Smuzhiyun        if self.acquire_portlock(lockfile):
255*4882a593Smuzhiyun            with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
256*4882a593Smuzhiyun                if sock.connect_ex((host, port)) == 0:
257*4882a593Smuzhiyun                    # Port is open, so not free
258*4882a593Smuzhiyun                    self.release_portlock(lockfile)
259*4882a593Smuzhiyun                    return False
260*4882a593Smuzhiyun                else:
261*4882a593Smuzhiyun                    # Port is not open, so free
262*4882a593Smuzhiyun                    return True
263*4882a593Smuzhiyun        else:
264*4882a593Smuzhiyun            return False
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun    def acquire_portlock(self, lockfile):
267*4882a593Smuzhiyun        logger.debug("Acquiring lockfile %s..." % lockfile)
268*4882a593Smuzhiyun        try:
269*4882a593Smuzhiyun            portlock_descriptor = open(lockfile, 'w')
270*4882a593Smuzhiyun            self.portlocks.update({lockfile: portlock_descriptor})
271*4882a593Smuzhiyun            fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_EX|fcntl.LOCK_NB)
272*4882a593Smuzhiyun        except Exception as e:
273*4882a593Smuzhiyun            msg = "Acquiring lockfile %s failed: %s" % (lockfile, e)
274*4882a593Smuzhiyun            logger.info(msg)
275*4882a593Smuzhiyun            if lockfile in self.portlocks.keys() and self.portlocks[lockfile]:
276*4882a593Smuzhiyun                self.portlocks[lockfile].close()
277*4882a593Smuzhiyun                del self.portlocks[lockfile]
278*4882a593Smuzhiyun            return False
279*4882a593Smuzhiyun        return True
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun    def release_portlock(self, lockfile=None):
282*4882a593Smuzhiyun        if lockfile != None:
283*4882a593Smuzhiyun            logger.debug("Releasing lockfile '%s'" % lockfile)
284*4882a593Smuzhiyun            # We pass the fd to the qemu process and if we unlock here, it would unlock for
285*4882a593Smuzhiyun            # that too. Therefore don't unlock, just close
286*4882a593Smuzhiyun            # fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_UN)
287*4882a593Smuzhiyun            self.portlocks[lockfile].close()
288*4882a593Smuzhiyun            # Removing the file is a potential race, don't do that either
289*4882a593Smuzhiyun            # os.remove(lockfile)
290*4882a593Smuzhiyun            del self.portlocks[lockfile]
291*4882a593Smuzhiyun        elif len(self.portlocks):
292*4882a593Smuzhiyun            for lockfile, descriptor in self.portlocks.items():
293*4882a593Smuzhiyun                logger.debug("Releasing lockfile '%s'" % lockfile)
294*4882a593Smuzhiyun                # We pass the fd to the qemu process and if we unlock here, it would unlock for
295*4882a593Smuzhiyun                # that too. Therefore don't unlock, just close
296*4882a593Smuzhiyun                # fcntl.flock(descriptor, fcntl.LOCK_UN)
297*4882a593Smuzhiyun                descriptor.close()
298*4882a593Smuzhiyun                # Removing the file is a potential race, don't do that either
299*4882a593Smuzhiyun                # os.remove(lockfile)
300*4882a593Smuzhiyun            self.portlocks = {}
301*4882a593Smuzhiyun
302*4882a593Smuzhiyun    def get(self, key):
303*4882a593Smuzhiyun        if key in self.d:
304*4882a593Smuzhiyun            return self.d.get(key)
305*4882a593Smuzhiyun        elif os.getenv(key):
306*4882a593Smuzhiyun            return os.getenv(key)
307*4882a593Smuzhiyun        else:
308*4882a593Smuzhiyun            return ''
309*4882a593Smuzhiyun
310*4882a593Smuzhiyun    def set(self, key, value):
311*4882a593Smuzhiyun        self.d[key] = value
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun    def is_deploy_dir_image(self, p):
314*4882a593Smuzhiyun        if os.path.isdir(p):
315*4882a593Smuzhiyun            if not re.search('.qemuboot.conf$', '\n'.join(os.listdir(p)), re.M):
316*4882a593Smuzhiyun                logger.debug("Can't find required *.qemuboot.conf in %s" % p)
317*4882a593Smuzhiyun                return False
318*4882a593Smuzhiyun            if not any(map(lambda name: '-image-' in name, os.listdir(p))):
319*4882a593Smuzhiyun                logger.debug("Can't find *-image-* in %s" % p)
320*4882a593Smuzhiyun                return False
321*4882a593Smuzhiyun            return True
322*4882a593Smuzhiyun        else:
323*4882a593Smuzhiyun            return False
324*4882a593Smuzhiyun
325*4882a593Smuzhiyun    def check_arg_fstype(self, fst):
326*4882a593Smuzhiyun        """Check and set FSTYPE"""
327*4882a593Smuzhiyun        if fst not in self.fstypes + self.vmtypes + self.wictypes:
328*4882a593Smuzhiyun            logger.warning("Maybe unsupported FSTYPE: %s" % fst)
329*4882a593Smuzhiyun        if not self.fstype or self.fstype == fst:
330*4882a593Smuzhiyun            if fst == 'ramfs':
331*4882a593Smuzhiyun                fst = 'cpio.gz'
332*4882a593Smuzhiyun            if fst in ('tar.bz2', 'tar.gz'):
333*4882a593Smuzhiyun                fst = 'nfs'
334*4882a593Smuzhiyun            self.fstype = fst
335*4882a593Smuzhiyun        else:
336*4882a593Smuzhiyun            raise RunQemuError("Conflicting: FSTYPE %s and %s" % (self.fstype, fst))
337*4882a593Smuzhiyun
338*4882a593Smuzhiyun    def set_machine_deploy_dir(self, machine, deploy_dir_image):
339*4882a593Smuzhiyun        """Set MACHINE and DEPLOY_DIR_IMAGE"""
340*4882a593Smuzhiyun        logger.debug('MACHINE: %s' % machine)
341*4882a593Smuzhiyun        self.set("MACHINE", machine)
342*4882a593Smuzhiyun        logger.debug('DEPLOY_DIR_IMAGE: %s' % deploy_dir_image)
343*4882a593Smuzhiyun        self.set("DEPLOY_DIR_IMAGE", deploy_dir_image)
344*4882a593Smuzhiyun
345*4882a593Smuzhiyun    def check_arg_nfs(self, p):
346*4882a593Smuzhiyun        if os.path.isdir(p):
347*4882a593Smuzhiyun            self.rootfs = p
348*4882a593Smuzhiyun        else:
349*4882a593Smuzhiyun            m = re.match('(.*):(.*)', p)
350*4882a593Smuzhiyun            self.nfs_server = m.group(1)
351*4882a593Smuzhiyun            self.rootfs = m.group(2)
352*4882a593Smuzhiyun        self.check_arg_fstype('nfs')
353*4882a593Smuzhiyun
354*4882a593Smuzhiyun    def check_arg_path(self, p):
355*4882a593Smuzhiyun        """
356*4882a593Smuzhiyun        - Check whether it is <image>.qemuboot.conf or contains <image>.qemuboot.conf
357*4882a593Smuzhiyun        - Check whether it is a kernel file
358*4882a593Smuzhiyun        - Check whether it is an image file
359*4882a593Smuzhiyun        - Check whether it is an NFS dir
360*4882a593Smuzhiyun        - Check whether it is an OVMF flash file
361*4882a593Smuzhiyun        """
362*4882a593Smuzhiyun        if p.endswith('.qemuboot.conf'):
363*4882a593Smuzhiyun            self.qemuboot = p
364*4882a593Smuzhiyun            self.qbconfload = True
365*4882a593Smuzhiyun        elif re.search('\.bin$', p) or re.search('bzImage', p) or \
366*4882a593Smuzhiyun             re.search('zImage', p) or re.search('vmlinux', p) or \
367*4882a593Smuzhiyun             re.search('fitImage', p) or re.search('uImage', p):
368*4882a593Smuzhiyun            self.kernel =  p
369*4882a593Smuzhiyun        elif os.path.exists(p) and (not os.path.isdir(p)) and '-image-' in os.path.basename(p):
370*4882a593Smuzhiyun            self.rootfs = p
371*4882a593Smuzhiyun            # Check filename against self.fstypes can handle <file>.cpio.gz,
372*4882a593Smuzhiyun            # otherwise, its type would be "gz", which is incorrect.
373*4882a593Smuzhiyun            fst = ""
374*4882a593Smuzhiyun            for t in self.fstypes:
375*4882a593Smuzhiyun                if p.endswith(t):
376*4882a593Smuzhiyun                    fst = t
377*4882a593Smuzhiyun                    break
378*4882a593Smuzhiyun            if not fst:
379*4882a593Smuzhiyun                m = re.search('.*\.(.*)$', self.rootfs)
380*4882a593Smuzhiyun                if m:
381*4882a593Smuzhiyun                    fst =  m.group(1)
382*4882a593Smuzhiyun            if fst:
383*4882a593Smuzhiyun                self.check_arg_fstype(fst)
384*4882a593Smuzhiyun                qb = re.sub('\.' + fst + "$", '', self.rootfs)
385*4882a593Smuzhiyun                qb = '%s%s' % (re.sub('\.rootfs$', '', qb), '.qemuboot.conf')
386*4882a593Smuzhiyun                if os.path.exists(qb):
387*4882a593Smuzhiyun                    self.qemuboot = qb
388*4882a593Smuzhiyun                    self.qbconfload = True
389*4882a593Smuzhiyun                else:
390*4882a593Smuzhiyun                    logger.warning("%s doesn't exist" % qb)
391*4882a593Smuzhiyun            else:
392*4882a593Smuzhiyun                raise RunQemuError("Can't find FSTYPE from: %s" % p)
393*4882a593Smuzhiyun
394*4882a593Smuzhiyun        elif os.path.isdir(p) or re.search(':', p) and re.search('/', p):
395*4882a593Smuzhiyun            if self.is_deploy_dir_image(p):
396*4882a593Smuzhiyun                logger.debug('DEPLOY_DIR_IMAGE: %s' % p)
397*4882a593Smuzhiyun                self.set("DEPLOY_DIR_IMAGE", p)
398*4882a593Smuzhiyun            else:
399*4882a593Smuzhiyun                logger.debug("Assuming %s is an nfs rootfs" % p)
400*4882a593Smuzhiyun                self.check_arg_nfs(p)
401*4882a593Smuzhiyun        elif os.path.basename(p).startswith('ovmf'):
402*4882a593Smuzhiyun            self.ovmf_bios.append(p)
403*4882a593Smuzhiyun        else:
404*4882a593Smuzhiyun            raise RunQemuError("Unknown path arg %s" % p)
405*4882a593Smuzhiyun
406*4882a593Smuzhiyun    def check_arg_machine(self, arg):
407*4882a593Smuzhiyun        """Check whether it is a machine"""
408*4882a593Smuzhiyun        if self.get('MACHINE') == arg:
409*4882a593Smuzhiyun            return
410*4882a593Smuzhiyun        elif self.get('MACHINE') and self.get('MACHINE') != arg:
411*4882a593Smuzhiyun            raise RunQemuError("Maybe conflicted MACHINE: %s vs %s" % (self.get('MACHINE'), arg))
412*4882a593Smuzhiyun        elif re.search('/', arg):
413*4882a593Smuzhiyun            raise RunQemuError("Unknown arg: %s" % arg)
414*4882a593Smuzhiyun
415*4882a593Smuzhiyun        logger.debug('Assuming MACHINE = %s' % arg)
416*4882a593Smuzhiyun
417*4882a593Smuzhiyun        # if we're running under testimage, or similarly as a child
418*4882a593Smuzhiyun        # of an existing bitbake invocation, we can't invoke bitbake
419*4882a593Smuzhiyun        # to validate the MACHINE setting and must assume it's correct...
420*4882a593Smuzhiyun        # FIXME: testimage.bbclass exports these two variables into env,
421*4882a593Smuzhiyun        # are there other scenarios in which we need to support being
422*4882a593Smuzhiyun        # invoked by bitbake?
423*4882a593Smuzhiyun        deploy = self.get('DEPLOY_DIR_IMAGE')
424*4882a593Smuzhiyun        bbchild = deploy and self.get('OE_TMPDIR')
425*4882a593Smuzhiyun        if bbchild:
426*4882a593Smuzhiyun            self.set_machine_deploy_dir(arg, deploy)
427*4882a593Smuzhiyun            return
428*4882a593Smuzhiyun        # also check whether we're running under a sourced toolchain
429*4882a593Smuzhiyun        # environment file
430*4882a593Smuzhiyun        if self.get('OECORE_NATIVE_SYSROOT'):
431*4882a593Smuzhiyun            self.set("MACHINE", arg)
432*4882a593Smuzhiyun            return
433*4882a593Smuzhiyun
434*4882a593Smuzhiyun        self.bitbake_e = self.run_bitbake_env(arg)
435*4882a593Smuzhiyun        # bitbake -e doesn't report invalid MACHINE as an error, so
436*4882a593Smuzhiyun        # let's check DEPLOY_DIR_IMAGE to make sure that it is a valid
437*4882a593Smuzhiyun        # MACHINE.
438*4882a593Smuzhiyun        s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
439*4882a593Smuzhiyun        if s:
440*4882a593Smuzhiyun            deploy_dir_image = s.group(1)
441*4882a593Smuzhiyun        else:
442*4882a593Smuzhiyun            raise RunQemuError("bitbake -e %s" % self.bitbake_e)
443*4882a593Smuzhiyun        if self.is_deploy_dir_image(deploy_dir_image):
444*4882a593Smuzhiyun            self.set_machine_deploy_dir(arg, deploy_dir_image)
445*4882a593Smuzhiyun        else:
446*4882a593Smuzhiyun            logger.error("%s not a directory valid DEPLOY_DIR_IMAGE" % deploy_dir_image)
447*4882a593Smuzhiyun            self.set("MACHINE", arg)
448*4882a593Smuzhiyun
449*4882a593Smuzhiyun    def set_dri_path(self):
450*4882a593Smuzhiyun        # As runqemu can be run within bitbake (when using testimage, for example),
451*4882a593Smuzhiyun        # we need to ensure that we run host pkg-config, and that it does not
452*4882a593Smuzhiyun        # get mis-directed to native build paths set by bitbake.
453*4882a593Smuzhiyun        env = os.environ.copy()
454*4882a593Smuzhiyun        try:
455*4882a593Smuzhiyun            del env['PKG_CONFIG_PATH']
456*4882a593Smuzhiyun            del env['PKG_CONFIG_DIR']
457*4882a593Smuzhiyun            del env['PKG_CONFIG_LIBDIR']
458*4882a593Smuzhiyun            del env['PKG_CONFIG_SYSROOT_DIR']
459*4882a593Smuzhiyun        except KeyError:
460*4882a593Smuzhiyun            pass
461*4882a593Smuzhiyun        try:
462*4882a593Smuzhiyun            dripath = subprocess.check_output("PATH=/bin:/usr/bin:$PATH pkg-config --variable=dridriverdir dri", shell=True, env=env)
463*4882a593Smuzhiyun        except subprocess.CalledProcessError as e:
464*4882a593Smuzhiyun            raise RunQemuError("Could not determine the path to dri drivers on the host via pkg-config.\nPlease install Mesa development files (particularly, dri.pc) on the host machine.")
465*4882a593Smuzhiyun        self.qemu_environ['LIBGL_DRIVERS_PATH'] = dripath.decode('utf-8').strip()
466*4882a593Smuzhiyun
467*4882a593Smuzhiyun        # This preloads uninative libc pieces and therefore ensures that RPATH/RUNPATH
468*4882a593Smuzhiyun        # in host mesa drivers doesn't trick uninative into loading host libc.
469*4882a593Smuzhiyun        preload_items = ['libdl.so.2', 'librt.so.1', 'libpthread.so.0']
470*4882a593Smuzhiyun        uninative_path = os.path.dirname(self.get("UNINATIVE_LOADER"))
471*4882a593Smuzhiyun        if os.path.exists(uninative_path):
472*4882a593Smuzhiyun            preload_paths = [os.path.join(uninative_path, i) for i in preload_items]
473*4882a593Smuzhiyun            self.qemu_environ['LD_PRELOAD'] = " ".join(preload_paths)
474*4882a593Smuzhiyun
475*4882a593Smuzhiyun    def check_args(self):
476*4882a593Smuzhiyun        for debug in ("-d", "--debug"):
477*4882a593Smuzhiyun            if debug in sys.argv:
478*4882a593Smuzhiyun                logger.setLevel(logging.DEBUG)
479*4882a593Smuzhiyun                sys.argv.remove(debug)
480*4882a593Smuzhiyun
481*4882a593Smuzhiyun        for quiet in ("-q", "--quiet"):
482*4882a593Smuzhiyun            if quiet in sys.argv:
483*4882a593Smuzhiyun                logger.setLevel(logging.ERROR)
484*4882a593Smuzhiyun                sys.argv.remove(quiet)
485*4882a593Smuzhiyun
486*4882a593Smuzhiyun        if 'gl' not in sys.argv[1:] and 'gl-es' not in sys.argv[1:]:
487*4882a593Smuzhiyun            self.qemu_environ['SDL_RENDER_DRIVER'] = 'software'
488*4882a593Smuzhiyun            self.qemu_environ['SDL_FRAMEBUFFER_ACCELERATION'] = 'false'
489*4882a593Smuzhiyun
490*4882a593Smuzhiyun        unknown_arg = ""
491*4882a593Smuzhiyun        for arg in sys.argv[1:]:
492*4882a593Smuzhiyun            if arg in self.fstypes + self.vmtypes + self.wictypes:
493*4882a593Smuzhiyun                self.check_arg_fstype(arg)
494*4882a593Smuzhiyun            elif arg == 'nographic':
495*4882a593Smuzhiyun                self.nographic = True
496*4882a593Smuzhiyun            elif arg == 'sdl':
497*4882a593Smuzhiyun                self.sdl = True
498*4882a593Smuzhiyun            elif arg == 'gtk':
499*4882a593Smuzhiyun                self.gtk = True
500*4882a593Smuzhiyun            elif arg == 'gl':
501*4882a593Smuzhiyun                self.gl = True
502*4882a593Smuzhiyun            elif arg == 'gl-es':
503*4882a593Smuzhiyun                self.gl_es = True
504*4882a593Smuzhiyun            elif arg == 'egl-headless':
505*4882a593Smuzhiyun                self.egl_headless = True
506*4882a593Smuzhiyun            elif arg == 'novga':
507*4882a593Smuzhiyun                self.novga = True
508*4882a593Smuzhiyun            elif arg == 'serial':
509*4882a593Smuzhiyun                self.serialconsole = True
510*4882a593Smuzhiyun            elif arg == "serialstdio":
511*4882a593Smuzhiyun                self.serialstdio = True
512*4882a593Smuzhiyun            elif arg == 'audio':
513*4882a593Smuzhiyun                logger.info("Enabling audio in qemu")
514*4882a593Smuzhiyun                logger.info("Please install sound drivers in linux host")
515*4882a593Smuzhiyun                self.audio_enabled = True
516*4882a593Smuzhiyun            elif arg == 'kvm':
517*4882a593Smuzhiyun                self.kvm_enabled = True
518*4882a593Smuzhiyun            elif arg == 'kvm-vhost':
519*4882a593Smuzhiyun                self.vhost_enabled = True
520*4882a593Smuzhiyun            elif arg == 'slirp':
521*4882a593Smuzhiyun                self.slirp_enabled = True
522*4882a593Smuzhiyun            elif arg.startswith('bridge='):
523*4882a593Smuzhiyun                self.net_bridge = '%s' % arg[len('bridge='):]
524*4882a593Smuzhiyun            elif arg == 'snapshot':
525*4882a593Smuzhiyun                self.snapshot = True
526*4882a593Smuzhiyun            elif arg == 'publicvnc':
527*4882a593Smuzhiyun                self.publicvnc = True
528*4882a593Smuzhiyun                self.qemu_opt_script += ' -vnc :0'
529*4882a593Smuzhiyun            elif arg.startswith('tcpserial='):
530*4882a593Smuzhiyun                self.tcpserial_portnum = '%s' % arg[len('tcpserial='):]
531*4882a593Smuzhiyun            elif arg.startswith('qemuparams='):
532*4882a593Smuzhiyun                self.qemuparams = ' %s' % arg[len('qemuparams='):]
533*4882a593Smuzhiyun            elif arg.startswith('bootparams='):
534*4882a593Smuzhiyun                self.bootparams = arg[len('bootparams='):]
535*4882a593Smuzhiyun            elif os.path.exists(arg) or (re.search(':', arg) and re.search('/', arg)):
536*4882a593Smuzhiyun                self.check_arg_path(os.path.abspath(arg))
537*4882a593Smuzhiyun            elif re.search(r'-image-|-image$', arg):
538*4882a593Smuzhiyun                # Lazy rootfs
539*4882a593Smuzhiyun                self.rootfs = arg
540*4882a593Smuzhiyun            elif arg.startswith('ovmf'):
541*4882a593Smuzhiyun                self.ovmf_bios.append(arg)
542*4882a593Smuzhiyun            else:
543*4882a593Smuzhiyun                # At last, assume it is the MACHINE
544*4882a593Smuzhiyun                if (not unknown_arg) or unknown_arg == arg:
545*4882a593Smuzhiyun                    unknown_arg = arg
546*4882a593Smuzhiyun                else:
547*4882a593Smuzhiyun                    raise RunQemuError("Can't handle two unknown args: %s %s\n"
548*4882a593Smuzhiyun                                       "Try 'runqemu help' on how to use it" % \
549*4882a593Smuzhiyun                                        (unknown_arg, arg))
550*4882a593Smuzhiyun        # Check to make sure it is a valid machine
551*4882a593Smuzhiyun        if unknown_arg and self.get('MACHINE') != unknown_arg:
552*4882a593Smuzhiyun            if self.get('DEPLOY_DIR_IMAGE'):
553*4882a593Smuzhiyun                machine = os.path.basename(self.get('DEPLOY_DIR_IMAGE'))
554*4882a593Smuzhiyun                if unknown_arg == machine:
555*4882a593Smuzhiyun                    self.set("MACHINE", machine)
556*4882a593Smuzhiyun
557*4882a593Smuzhiyun            self.check_arg_machine(unknown_arg)
558*4882a593Smuzhiyun
559*4882a593Smuzhiyun        if not (self.get('DEPLOY_DIR_IMAGE') or self.qbconfload):
560*4882a593Smuzhiyun            self.load_bitbake_env()
561*4882a593Smuzhiyun            s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
562*4882a593Smuzhiyun            if s:
563*4882a593Smuzhiyun                self.set("DEPLOY_DIR_IMAGE", s.group(1))
564*4882a593Smuzhiyun
565*4882a593Smuzhiyun    def check_kvm(self):
566*4882a593Smuzhiyun        """Check kvm and kvm-host"""
567*4882a593Smuzhiyun        if not (self.kvm_enabled or self.vhost_enabled):
568*4882a593Smuzhiyun            self.qemu_opt_script += ' %s %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU'), self.get('QB_SMP'))
569*4882a593Smuzhiyun            return
570*4882a593Smuzhiyun
571*4882a593Smuzhiyun        if not self.get('QB_CPU_KVM'):
572*4882a593Smuzhiyun            raise RunQemuError("QB_CPU_KVM is NULL, this board doesn't support kvm")
573*4882a593Smuzhiyun
574*4882a593Smuzhiyun        self.qemu_opt_script += ' %s %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU_KVM'), self.get('QB_SMP'))
575*4882a593Smuzhiyun        yocto_kvm_wiki = "https://wiki.yoctoproject.org/wiki/How_to_enable_KVM_for_Poky_qemu"
576*4882a593Smuzhiyun        yocto_paravirt_kvm_wiki = "https://wiki.yoctoproject.org/wiki/Running_an_x86_Yocto_Linux_image_under_QEMU_KVM"
577*4882a593Smuzhiyun        dev_kvm = '/dev/kvm'
578*4882a593Smuzhiyun        dev_vhost = '/dev/vhost-net'
579*4882a593Smuzhiyun        if self.qemu_system.endswith(('i386', 'x86_64')):
580*4882a593Smuzhiyun            with open('/proc/cpuinfo', 'r') as f:
581*4882a593Smuzhiyun                kvm_cap = re.search('vmx|svm', "".join(f.readlines()))
582*4882a593Smuzhiyun            if not kvm_cap:
583*4882a593Smuzhiyun                logger.error("You are trying to enable KVM on a cpu without VT support.")
584*4882a593Smuzhiyun                logger.error("Remove kvm from the command-line, or refer:")
585*4882a593Smuzhiyun                raise RunQemuError(yocto_kvm_wiki)
586*4882a593Smuzhiyun
587*4882a593Smuzhiyun        if not os.path.exists(dev_kvm):
588*4882a593Smuzhiyun            logger.error("Missing KVM device. Have you inserted kvm modules?")
589*4882a593Smuzhiyun            logger.error("For further help see:")
590*4882a593Smuzhiyun            raise RunQemuError(yocto_kvm_wiki)
591*4882a593Smuzhiyun
592*4882a593Smuzhiyun        if os.access(dev_kvm, os.W_OK|os.R_OK):
593*4882a593Smuzhiyun            self.qemu_opt_script += ' -enable-kvm'
594*4882a593Smuzhiyun            if self.get('MACHINE') == "qemux86":
595*4882a593Smuzhiyun                # Workaround for broken APIC window on pre 4.15 host kernels which causes boot hangs
596*4882a593Smuzhiyun                # See YOCTO #12301
597*4882a593Smuzhiyun                # On 64 bit we use x2apic
598*4882a593Smuzhiyun                self.kernel_cmdline_script += " clocksource=kvm-clock hpet=disable noapic nolapic"
599*4882a593Smuzhiyun        else:
600*4882a593Smuzhiyun            logger.error("You have no read or write permission on /dev/kvm.")
601*4882a593Smuzhiyun            logger.error("Please change the ownership of this file as described at:")
602*4882a593Smuzhiyun            raise RunQemuError(yocto_kvm_wiki)
603*4882a593Smuzhiyun
604*4882a593Smuzhiyun        if self.vhost_enabled:
605*4882a593Smuzhiyun            if not os.path.exists(dev_vhost):
606*4882a593Smuzhiyun                logger.error("Missing virtio net device. Have you inserted vhost-net module?")
607*4882a593Smuzhiyun                logger.error("For further help see:")
608*4882a593Smuzhiyun                raise RunQemuError(yocto_paravirt_kvm_wiki)
609*4882a593Smuzhiyun
610*4882a593Smuzhiyun            if not os.access(dev_vhost, os.W_OK|os.R_OK):
611*4882a593Smuzhiyun                logger.error("You have no read or write permission on /dev/vhost-net.")
612*4882a593Smuzhiyun                logger.error("Please change the ownership of this file as described at:")
613*4882a593Smuzhiyun                raise RunQemuError(yocto_paravirt_kvm_wiki)
614*4882a593Smuzhiyun
615*4882a593Smuzhiyun    def check_fstype(self):
616*4882a593Smuzhiyun        """Check and setup FSTYPE"""
617*4882a593Smuzhiyun        if not self.fstype:
618*4882a593Smuzhiyun            fstype = self.get('QB_DEFAULT_FSTYPE')
619*4882a593Smuzhiyun            if fstype:
620*4882a593Smuzhiyun                self.fstype = fstype
621*4882a593Smuzhiyun            else:
622*4882a593Smuzhiyun                raise RunQemuError("FSTYPE is NULL!")
623*4882a593Smuzhiyun
624*4882a593Smuzhiyun        # parse QB_FSINFO into dict, e.g. { 'wic': ['no-kernel-in-fs', 'a-flag'], 'ext4': ['another-flag']}
625*4882a593Smuzhiyun        wic_fs = False
626*4882a593Smuzhiyun        qb_fsinfo = self.get('QB_FSINFO')
627*4882a593Smuzhiyun        if qb_fsinfo:
628*4882a593Smuzhiyun            qb_fsinfo = qb_fsinfo.split()
629*4882a593Smuzhiyun            for fsinfo in qb_fsinfo:
630*4882a593Smuzhiyun                try:
631*4882a593Smuzhiyun                    fstype, fsflag = fsinfo.split(':')
632*4882a593Smuzhiyun
633*4882a593Smuzhiyun                    if fstype == 'wic':
634*4882a593Smuzhiyun                        if fsflag == 'no-kernel-in-fs':
635*4882a593Smuzhiyun                            wic_fs = True
636*4882a593Smuzhiyun                        elif fsflag == 'kernel-in-fs':
637*4882a593Smuzhiyun                            wic_fs = False
638*4882a593Smuzhiyun                        else:
639*4882a593Smuzhiyun                            logger.warn('Unknown flag "%s:%s" in QB_FSINFO', fstype, fsflag)
640*4882a593Smuzhiyun                            continue
641*4882a593Smuzhiyun                    else:
642*4882a593Smuzhiyun                        logger.warn('QB_FSINFO is not supported for image type "%s"', fstype)
643*4882a593Smuzhiyun                        continue
644*4882a593Smuzhiyun
645*4882a593Smuzhiyun                    if fstype in self.fsinfo:
646*4882a593Smuzhiyun                        self.fsinfo[fstype].append(fsflag)
647*4882a593Smuzhiyun                    else:
648*4882a593Smuzhiyun                        self.fsinfo[fstype] = [fsflag]
649*4882a593Smuzhiyun                except Exception:
650*4882a593Smuzhiyun                    logger.error('Invalid parameter "%s" in QB_FSINFO', fsinfo)
651*4882a593Smuzhiyun
652*4882a593Smuzhiyun        # treat wic images as vmimages (with kernel) or as fsimages (rootfs only)
653*4882a593Smuzhiyun        if wic_fs:
654*4882a593Smuzhiyun            self.fstypes = self.fstypes + self.wictypes
655*4882a593Smuzhiyun        else:
656*4882a593Smuzhiyun            self.vmtypes = self.vmtypes + self.wictypes
657*4882a593Smuzhiyun
658*4882a593Smuzhiyun    def check_rootfs(self):
659*4882a593Smuzhiyun        """Check and set rootfs"""
660*4882a593Smuzhiyun
661*4882a593Smuzhiyun        if self.fstype == "none":
662*4882a593Smuzhiyun            return
663*4882a593Smuzhiyun
664*4882a593Smuzhiyun        if self.get('ROOTFS'):
665*4882a593Smuzhiyun            if not self.rootfs:
666*4882a593Smuzhiyun                self.rootfs = self.get('ROOTFS')
667*4882a593Smuzhiyun            elif self.get('ROOTFS') != self.rootfs:
668*4882a593Smuzhiyun                raise RunQemuError("Maybe conflicted ROOTFS: %s vs %s" % (self.get('ROOTFS'), self.rootfs))
669*4882a593Smuzhiyun
670*4882a593Smuzhiyun        if self.fstype == 'nfs':
671*4882a593Smuzhiyun            return
672*4882a593Smuzhiyun
673*4882a593Smuzhiyun        if self.rootfs and not os.path.exists(self.rootfs):
674*4882a593Smuzhiyun            # Lazy rootfs
675*4882a593Smuzhiyun            self.rootfs = "%s/%s-%s.%s" % (self.get('DEPLOY_DIR_IMAGE'),
676*4882a593Smuzhiyun                    self.rootfs, self.get('MACHINE'),
677*4882a593Smuzhiyun                    self.fstype)
678*4882a593Smuzhiyun        elif not self.rootfs:
679*4882a593Smuzhiyun            cmd_name = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_NAME'), self.fstype)
680*4882a593Smuzhiyun            cmd_link = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'), self.fstype)
681*4882a593Smuzhiyun            cmds = (cmd_name, cmd_link)
682*4882a593Smuzhiyun            self.rootfs = get_first_file(cmds)
683*4882a593Smuzhiyun            if not self.rootfs:
684*4882a593Smuzhiyun                raise RunQemuError("Failed to find rootfs: %s or %s" % cmds)
685*4882a593Smuzhiyun
686*4882a593Smuzhiyun        if not os.path.exists(self.rootfs):
687*4882a593Smuzhiyun            raise RunQemuError("Can't find rootfs: %s" % self.rootfs)
688*4882a593Smuzhiyun
689*4882a593Smuzhiyun    def setup_pkkek1(self):
690*4882a593Smuzhiyun        """
691*4882a593Smuzhiyun        Extract from PEM certificate the Platform Key and first Key
692*4882a593Smuzhiyun        Exchange Key certificate string. The hypervisor needs to provide
693*4882a593Smuzhiyun        it in the Type 11 SMBIOS table
694*4882a593Smuzhiyun        """
695*4882a593Smuzhiyun        pemcert = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), 'OvmfPkKek1.pem')
696*4882a593Smuzhiyun        try:
697*4882a593Smuzhiyun            with open(pemcert, 'r') as pemfile:
698*4882a593Smuzhiyun                key = pemfile.read().replace('\n', ''). \
699*4882a593Smuzhiyun                      replace('-----BEGIN CERTIFICATE-----', ''). \
700*4882a593Smuzhiyun                      replace('-----END CERTIFICATE-----', '')
701*4882a593Smuzhiyun                self.ovmf_secboot_pkkek1 = key
702*4882a593Smuzhiyun
703*4882a593Smuzhiyun        except FileNotFoundError:
704*4882a593Smuzhiyun            raise RunQemuError("Can't open PEM certificate %s " % pemcert)
705*4882a593Smuzhiyun
706*4882a593Smuzhiyun    def check_ovmf(self):
707*4882a593Smuzhiyun        """Check and set full path for OVMF firmware and variable file(s)."""
708*4882a593Smuzhiyun
709*4882a593Smuzhiyun        for index, ovmf in enumerate(self.ovmf_bios):
710*4882a593Smuzhiyun            if os.path.exists(ovmf):
711*4882a593Smuzhiyun                continue
712*4882a593Smuzhiyun            for suffix in ('qcow2', 'bin'):
713*4882a593Smuzhiyun                path = '%s/%s.%s' % (self.get('DEPLOY_DIR_IMAGE'), ovmf, suffix)
714*4882a593Smuzhiyun                if os.path.exists(path):
715*4882a593Smuzhiyun                    self.ovmf_bios[index] = path
716*4882a593Smuzhiyun                    if ovmf.endswith('secboot'):
717*4882a593Smuzhiyun                        self.setup_pkkek1()
718*4882a593Smuzhiyun                    break
719*4882a593Smuzhiyun            else:
720*4882a593Smuzhiyun                raise RunQemuError("Can't find OVMF firmware: %s" % ovmf)
721*4882a593Smuzhiyun
722*4882a593Smuzhiyun    def check_kernel(self):
723*4882a593Smuzhiyun        """Check and set kernel"""
724*4882a593Smuzhiyun        # The vm image doesn't need a kernel
725*4882a593Smuzhiyun        if self.fstype in self.vmtypes:
726*4882a593Smuzhiyun            return
727*4882a593Smuzhiyun
728*4882a593Smuzhiyun        # See if the user supplied a KERNEL option
729*4882a593Smuzhiyun        if self.get('KERNEL'):
730*4882a593Smuzhiyun            self.kernel = self.get('KERNEL')
731*4882a593Smuzhiyun
732*4882a593Smuzhiyun        # QB_DEFAULT_KERNEL is always a full file path
733*4882a593Smuzhiyun        kernel_name = os.path.basename(self.get('QB_DEFAULT_KERNEL'))
734*4882a593Smuzhiyun
735*4882a593Smuzhiyun        # The user didn't want a kernel to be loaded
736*4882a593Smuzhiyun        if kernel_name == "none" and not self.kernel:
737*4882a593Smuzhiyun            return
738*4882a593Smuzhiyun
739*4882a593Smuzhiyun        deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
740*4882a593Smuzhiyun        if not self.kernel:
741*4882a593Smuzhiyun            kernel_match_name = "%s/%s" % (deploy_dir_image, kernel_name)
742*4882a593Smuzhiyun            kernel_match_link = "%s/%s" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
743*4882a593Smuzhiyun            kernel_startswith = "%s/%s*" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
744*4882a593Smuzhiyun            cmds = (kernel_match_name, kernel_match_link, kernel_startswith)
745*4882a593Smuzhiyun            self.kernel = get_first_file(cmds)
746*4882a593Smuzhiyun            if not self.kernel:
747*4882a593Smuzhiyun                raise RunQemuError('KERNEL not found: %s, %s or %s' % cmds)
748*4882a593Smuzhiyun
749*4882a593Smuzhiyun        if not os.path.exists(self.kernel):
750*4882a593Smuzhiyun            raise RunQemuError("KERNEL %s not found" % self.kernel)
751*4882a593Smuzhiyun
752*4882a593Smuzhiyun    def check_dtb(self):
753*4882a593Smuzhiyun        """Check and set dtb"""
754*4882a593Smuzhiyun        # Did the user specify a device tree?
755*4882a593Smuzhiyun        if self.get('DEVICE_TREE'):
756*4882a593Smuzhiyun            self.dtb = self.get('DEVICE_TREE')
757*4882a593Smuzhiyun            if not os.path.exists(self.dtb):
758*4882a593Smuzhiyun                raise RunQemuError('Specified DTB not found: %s' % self.dtb)
759*4882a593Smuzhiyun            return
760*4882a593Smuzhiyun
761*4882a593Smuzhiyun        dtb = self.get('QB_DTB')
762*4882a593Smuzhiyun        if dtb:
763*4882a593Smuzhiyun            deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
764*4882a593Smuzhiyun            cmd_match = "%s/%s" % (deploy_dir_image, dtb)
765*4882a593Smuzhiyun            cmd_startswith = "%s/%s*" % (deploy_dir_image, dtb)
766*4882a593Smuzhiyun            cmd_wild = "%s/*.dtb" % deploy_dir_image
767*4882a593Smuzhiyun            cmds = (cmd_match, cmd_startswith, cmd_wild)
768*4882a593Smuzhiyun            self.dtb = get_first_file(cmds)
769*4882a593Smuzhiyun            if not os.path.exists(self.dtb):
770*4882a593Smuzhiyun                raise RunQemuError('DTB not found: %s, %s or %s' % cmds)
771*4882a593Smuzhiyun
772*4882a593Smuzhiyun    def check_bios(self):
773*4882a593Smuzhiyun        """Check and set bios"""
774*4882a593Smuzhiyun
775*4882a593Smuzhiyun        # See if the user supplied a BIOS option
776*4882a593Smuzhiyun        if self.get('BIOS'):
777*4882a593Smuzhiyun            self.bios = self.get('BIOS')
778*4882a593Smuzhiyun
779*4882a593Smuzhiyun        # QB_DEFAULT_BIOS is always a full file path
780*4882a593Smuzhiyun        bios_name = os.path.basename(self.get('QB_DEFAULT_BIOS'))
781*4882a593Smuzhiyun
782*4882a593Smuzhiyun        # The user didn't want a bios to be loaded
783*4882a593Smuzhiyun        if (bios_name == "" or bios_name == "none") and not self.bios:
784*4882a593Smuzhiyun            return
785*4882a593Smuzhiyun
786*4882a593Smuzhiyun        if not self.bios:
787*4882a593Smuzhiyun            deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
788*4882a593Smuzhiyun            self.bios = "%s/%s" % (deploy_dir_image, bios_name)
789*4882a593Smuzhiyun
790*4882a593Smuzhiyun            if not self.bios:
791*4882a593Smuzhiyun                raise RunQemuError('BIOS not found: %s' % bios_match_name)
792*4882a593Smuzhiyun
793*4882a593Smuzhiyun        if not os.path.exists(self.bios):
794*4882a593Smuzhiyun            raise RunQemuError("BIOS %s not found" % self.bios)
795*4882a593Smuzhiyun
796*4882a593Smuzhiyun
797*4882a593Smuzhiyun    def check_mem(self):
798*4882a593Smuzhiyun        """
799*4882a593Smuzhiyun        Both qemu and kernel needs memory settings, so check QB_MEM and set it
800*4882a593Smuzhiyun        for both.
801*4882a593Smuzhiyun        """
802*4882a593Smuzhiyun        s = re.search('-m +([0-9]+)', self.qemuparams)
803*4882a593Smuzhiyun        if s:
804*4882a593Smuzhiyun            self.set('QB_MEM', '-m %s' % s.group(1))
805*4882a593Smuzhiyun        elif not self.get('QB_MEM'):
806*4882a593Smuzhiyun            logger.info('QB_MEM is not set, use 256M by default')
807*4882a593Smuzhiyun            self.set('QB_MEM', '-m 256')
808*4882a593Smuzhiyun
809*4882a593Smuzhiyun        # Check and remove M or m suffix
810*4882a593Smuzhiyun        qb_mem = self.get('QB_MEM')
811*4882a593Smuzhiyun        if qb_mem.endswith('M') or qb_mem.endswith('m'):
812*4882a593Smuzhiyun            qb_mem = qb_mem[:-1]
813*4882a593Smuzhiyun
814*4882a593Smuzhiyun        # Add -m prefix it not present
815*4882a593Smuzhiyun        if not qb_mem.startswith('-m'):
816*4882a593Smuzhiyun            qb_mem = '-m %s' % qb_mem
817*4882a593Smuzhiyun
818*4882a593Smuzhiyun        self.set('QB_MEM', qb_mem)
819*4882a593Smuzhiyun
820*4882a593Smuzhiyun        mach = self.get('MACHINE')
821*4882a593Smuzhiyun        if not mach.startswith(('qemumips', 'qemux86')):
822*4882a593Smuzhiyun            self.kernel_cmdline_script += ' mem=%s' % self.get('QB_MEM').replace('-m','').strip() + 'M'
823*4882a593Smuzhiyun
824*4882a593Smuzhiyun        self.qemu_opt_script += ' %s' % self.get('QB_MEM')
825*4882a593Smuzhiyun
826*4882a593Smuzhiyun    def check_tcpserial(self):
827*4882a593Smuzhiyun        if self.tcpserial_portnum:
828*4882a593Smuzhiyun            ports = self.tcpserial_portnum.split(':')
829*4882a593Smuzhiyun            port = ports[0]
830*4882a593Smuzhiyun            if self.get('QB_TCPSERIAL_OPT'):
831*4882a593Smuzhiyun                self.qemu_opt_script += ' ' + self.get('QB_TCPSERIAL_OPT').replace('@PORT@', port)
832*4882a593Smuzhiyun            else:
833*4882a593Smuzhiyun                self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
834*4882a593Smuzhiyun
835*4882a593Smuzhiyun            if len(ports) > 1:
836*4882a593Smuzhiyun                for port in ports[1:]:
837*4882a593Smuzhiyun                    self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port
838*4882a593Smuzhiyun
839*4882a593Smuzhiyun    def check_and_set(self):
840*4882a593Smuzhiyun        """Check configs sanity and set when needed"""
841*4882a593Smuzhiyun        self.validate_paths()
842*4882a593Smuzhiyun        if not self.slirp_enabled and not self.net_bridge:
843*4882a593Smuzhiyun            check_tun()
844*4882a593Smuzhiyun        # Check audio
845*4882a593Smuzhiyun        if self.audio_enabled:
846*4882a593Smuzhiyun            if not self.get('QB_AUDIO_DRV'):
847*4882a593Smuzhiyun                raise RunQemuError("QB_AUDIO_DRV is NULL, this board doesn't support audio")
848*4882a593Smuzhiyun            if not self.get('QB_AUDIO_OPT'):
849*4882a593Smuzhiyun                logger.warning('QB_AUDIO_OPT is NULL, you may need define it to make audio work')
850*4882a593Smuzhiyun            else:
851*4882a593Smuzhiyun                self.qemu_opt_script += ' %s' % self.get('QB_AUDIO_OPT')
852*4882a593Smuzhiyun            os.putenv('QEMU_AUDIO_DRV', self.get('QB_AUDIO_DRV'))
853*4882a593Smuzhiyun        else:
854*4882a593Smuzhiyun            os.putenv('QEMU_AUDIO_DRV', 'none')
855*4882a593Smuzhiyun
856*4882a593Smuzhiyun        self.check_qemu_system()
857*4882a593Smuzhiyun        self.check_kvm()
858*4882a593Smuzhiyun        self.check_fstype()
859*4882a593Smuzhiyun        self.check_rootfs()
860*4882a593Smuzhiyun        self.check_ovmf()
861*4882a593Smuzhiyun        self.check_kernel()
862*4882a593Smuzhiyun        self.check_dtb()
863*4882a593Smuzhiyun        self.check_bios()
864*4882a593Smuzhiyun        self.check_mem()
865*4882a593Smuzhiyun        self.check_tcpserial()
866*4882a593Smuzhiyun
867*4882a593Smuzhiyun    def read_qemuboot(self):
868*4882a593Smuzhiyun        if not self.qemuboot:
869*4882a593Smuzhiyun            if self.get('DEPLOY_DIR_IMAGE'):
870*4882a593Smuzhiyun                deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
871*4882a593Smuzhiyun            else:
872*4882a593Smuzhiyun                logger.warning("Can't find qemuboot conf file, DEPLOY_DIR_IMAGE is NULL!")
873*4882a593Smuzhiyun                return
874*4882a593Smuzhiyun
875*4882a593Smuzhiyun            if self.rootfs and not os.path.exists(self.rootfs):
876*4882a593Smuzhiyun                # Lazy rootfs
877*4882a593Smuzhiyun                machine = self.get('MACHINE')
878*4882a593Smuzhiyun                if not machine:
879*4882a593Smuzhiyun                    machine = os.path.basename(deploy_dir_image)
880*4882a593Smuzhiyun                self.qemuboot = "%s/%s-%s.qemuboot.conf" % (deploy_dir_image,
881*4882a593Smuzhiyun                        self.rootfs, machine)
882*4882a593Smuzhiyun            else:
883*4882a593Smuzhiyun                cmd = 'ls -t %s/*.qemuboot.conf' %  deploy_dir_image
884*4882a593Smuzhiyun                logger.debug('Running %s...' % cmd)
885*4882a593Smuzhiyun                try:
886*4882a593Smuzhiyun                    qbs = subprocess.check_output(cmd, shell=True).decode('utf-8')
887*4882a593Smuzhiyun                except subprocess.CalledProcessError as err:
888*4882a593Smuzhiyun                    raise RunQemuError(err)
889*4882a593Smuzhiyun                if qbs:
890*4882a593Smuzhiyun                    for qb in qbs.split():
891*4882a593Smuzhiyun                        # Don't use initramfs when other choices unless fstype is ramfs
892*4882a593Smuzhiyun                        if '-initramfs-' in os.path.basename(qb) and self.fstype != 'cpio.gz':
893*4882a593Smuzhiyun                                continue
894*4882a593Smuzhiyun                        self.qemuboot = qb
895*4882a593Smuzhiyun                        break
896*4882a593Smuzhiyun                    if not self.qemuboot:
897*4882a593Smuzhiyun                        # Use the first one when no choice
898*4882a593Smuzhiyun                        self.qemuboot = qbs.split()[0]
899*4882a593Smuzhiyun                    self.qbconfload = True
900*4882a593Smuzhiyun
901*4882a593Smuzhiyun        if not self.qemuboot:
902*4882a593Smuzhiyun            # If we haven't found a .qemuboot.conf at this point it probably
903*4882a593Smuzhiyun            # doesn't exist, continue without
904*4882a593Smuzhiyun            return
905*4882a593Smuzhiyun
906*4882a593Smuzhiyun        if not os.path.exists(self.qemuboot):
907*4882a593Smuzhiyun            raise RunQemuError("Failed to find %s (wrong image name or BSP does not support running under qemu?)." % self.qemuboot)
908*4882a593Smuzhiyun
909*4882a593Smuzhiyun        logger.debug('CONFFILE: %s' % self.qemuboot)
910*4882a593Smuzhiyun
911*4882a593Smuzhiyun        cf = configparser.ConfigParser()
912*4882a593Smuzhiyun        cf.read(self.qemuboot)
913*4882a593Smuzhiyun        for k, v in cf.items('config_bsp'):
914*4882a593Smuzhiyun            k_upper = k.upper()
915*4882a593Smuzhiyun            if v.startswith("../"):
916*4882a593Smuzhiyun                v = os.path.abspath(os.path.dirname(self.qemuboot) + "/" + v)
917*4882a593Smuzhiyun            elif v == ".":
918*4882a593Smuzhiyun                v = os.path.dirname(self.qemuboot)
919*4882a593Smuzhiyun            self.set(k_upper, v)
920*4882a593Smuzhiyun
921*4882a593Smuzhiyun    def validate_paths(self):
922*4882a593Smuzhiyun        """Ensure all relevant path variables are set"""
923*4882a593Smuzhiyun        # When we're started with a *.qemuboot.conf arg assume that image
924*4882a593Smuzhiyun        # artefacts are relative to that file, rather than in whatever
925*4882a593Smuzhiyun        # directory DEPLOY_DIR_IMAGE in the conf file points to.
926*4882a593Smuzhiyun        if self.qbconfload:
927*4882a593Smuzhiyun            imgdir = os.path.realpath(os.path.dirname(self.qemuboot))
928*4882a593Smuzhiyun            if imgdir != os.path.realpath(self.get('DEPLOY_DIR_IMAGE')):
929*4882a593Smuzhiyun                logger.info('Setting DEPLOY_DIR_IMAGE to folder containing %s (%s)' % (self.qemuboot, imgdir))
930*4882a593Smuzhiyun                self.set('DEPLOY_DIR_IMAGE', imgdir)
931*4882a593Smuzhiyun
932*4882a593Smuzhiyun        # If the STAGING_*_NATIVE directories from the config file don't exist
933*4882a593Smuzhiyun        # and we're in a sourced OE build directory try to extract the paths
934*4882a593Smuzhiyun        # from `bitbake -e`
935*4882a593Smuzhiyun        havenative = os.path.exists(self.get('STAGING_DIR_NATIVE')) and \
936*4882a593Smuzhiyun            os.path.exists(self.get('STAGING_BINDIR_NATIVE'))
937*4882a593Smuzhiyun
938*4882a593Smuzhiyun        if not havenative:
939*4882a593Smuzhiyun            if not self.bitbake_e:
940*4882a593Smuzhiyun                self.load_bitbake_env()
941*4882a593Smuzhiyun
942*4882a593Smuzhiyun            if self.bitbake_e:
943*4882a593Smuzhiyun                native_vars = ['STAGING_DIR_NATIVE']
944*4882a593Smuzhiyun                for nv in native_vars:
945*4882a593Smuzhiyun                    s = re.search('^%s="(.*)"' % nv, self.bitbake_e, re.M)
946*4882a593Smuzhiyun                    if s and s.group(1) != self.get(nv):
947*4882a593Smuzhiyun                        logger.info('Overriding conf file setting of %s to %s from Bitbake environment' % (nv, s.group(1)))
948*4882a593Smuzhiyun                        self.set(nv, s.group(1))
949*4882a593Smuzhiyun            else:
950*4882a593Smuzhiyun                # when we're invoked from a running bitbake instance we won't
951*4882a593Smuzhiyun                # be able to call `bitbake -e`, then try:
952*4882a593Smuzhiyun                # - get OE_TMPDIR from environment and guess paths based on it
953*4882a593Smuzhiyun                # - get OECORE_NATIVE_SYSROOT from environment (for sdk)
954*4882a593Smuzhiyun                tmpdir = self.get('OE_TMPDIR')
955*4882a593Smuzhiyun                oecore_native_sysroot = self.get('OECORE_NATIVE_SYSROOT')
956*4882a593Smuzhiyun                if tmpdir:
957*4882a593Smuzhiyun                    logger.info('Setting STAGING_DIR_NATIVE and STAGING_BINDIR_NATIVE relative to OE_TMPDIR (%s)' % tmpdir)
958*4882a593Smuzhiyun                    hostos, _, _, _, machine = os.uname()
959*4882a593Smuzhiyun                    buildsys = '%s-%s' % (machine, hostos.lower())
960*4882a593Smuzhiyun                    staging_dir_native = '%s/sysroots/%s' % (tmpdir, buildsys)
961*4882a593Smuzhiyun                    self.set('STAGING_DIR_NATIVE', staging_dir_native)
962*4882a593Smuzhiyun                elif oecore_native_sysroot:
963*4882a593Smuzhiyun                    logger.info('Setting STAGING_DIR_NATIVE to OECORE_NATIVE_SYSROOT (%s)' % oecore_native_sysroot)
964*4882a593Smuzhiyun                    self.set('STAGING_DIR_NATIVE', oecore_native_sysroot)
965*4882a593Smuzhiyun                if self.get('STAGING_DIR_NATIVE'):
966*4882a593Smuzhiyun                    # we have to assume that STAGING_BINDIR_NATIVE is at usr/bin
967*4882a593Smuzhiyun                    staging_bindir_native = '%s/usr/bin' % self.get('STAGING_DIR_NATIVE')
968*4882a593Smuzhiyun                    logger.info('Setting STAGING_BINDIR_NATIVE to %s' % staging_bindir_native)
969*4882a593Smuzhiyun                    self.set('STAGING_BINDIR_NATIVE', '%s/usr/bin' % self.get('STAGING_DIR_NATIVE'))
970*4882a593Smuzhiyun
971*4882a593Smuzhiyun    def print_config(self):
972*4882a593Smuzhiyun        logoutput = ['Continuing with the following parameters:']
973*4882a593Smuzhiyun        if not self.fstype in self.vmtypes:
974*4882a593Smuzhiyun            logoutput.append('KERNEL: [%s]' % self.kernel)
975*4882a593Smuzhiyun            if self.bios:
976*4882a593Smuzhiyun                logoutput.append('BIOS: [%s]' % self.bios)
977*4882a593Smuzhiyun            if self.dtb:
978*4882a593Smuzhiyun                logoutput.append('DTB: [%s]' % self.dtb)
979*4882a593Smuzhiyun        logoutput.append('MACHINE: [%s]' % self.get('MACHINE'))
980*4882a593Smuzhiyun        try:
981*4882a593Smuzhiyun            fstype_flags = ' (' + ', '.join(self.fsinfo[self.fstype]) + ')'
982*4882a593Smuzhiyun        except KeyError:
983*4882a593Smuzhiyun            fstype_flags = ''
984*4882a593Smuzhiyun        logoutput.append('FSTYPE: [%s%s]' % (self.fstype, fstype_flags))
985*4882a593Smuzhiyun        if self.fstype  == 'nfs':
986*4882a593Smuzhiyun            logoutput.append('NFS_DIR: [%s]' % self.rootfs)
987*4882a593Smuzhiyun        else:
988*4882a593Smuzhiyun            logoutput.append('ROOTFS: [%s]' % self.rootfs)
989*4882a593Smuzhiyun        if self.ovmf_bios:
990*4882a593Smuzhiyun            logoutput.append('OVMF: %s' % self.ovmf_bios)
991*4882a593Smuzhiyun        if (self.ovmf_secboot_pkkek1):
992*4882a593Smuzhiyun            logoutput.append('SECBOOT PKKEK1: [%s...]' % self.ovmf_secboot_pkkek1[0:100])
993*4882a593Smuzhiyun        logoutput.append('CONFFILE: [%s]' % self.qemuboot)
994*4882a593Smuzhiyun        logoutput.append('')
995*4882a593Smuzhiyun        logger.info('\n'.join(logoutput))
996*4882a593Smuzhiyun
997*4882a593Smuzhiyun    def setup_nfs(self):
998*4882a593Smuzhiyun        if not self.nfs_server:
999*4882a593Smuzhiyun            if self.slirp_enabled:
1000*4882a593Smuzhiyun                self.nfs_server = '10.0.2.2'
1001*4882a593Smuzhiyun            else:
1002*4882a593Smuzhiyun                self.nfs_server = '192.168.7.1'
1003*4882a593Smuzhiyun
1004*4882a593Smuzhiyun        # Figure out a new nfs_instance to allow multiple qemus running.
1005*4882a593Smuzhiyun        ps = subprocess.check_output(("ps", "auxww")).decode('utf-8')
1006*4882a593Smuzhiyun        pattern = '/bin/unfsd .* -i .*\.pid -e .*/exports([0-9]+) '
1007*4882a593Smuzhiyun        all_instances = re.findall(pattern, ps, re.M)
1008*4882a593Smuzhiyun        if all_instances:
1009*4882a593Smuzhiyun            all_instances.sort(key=int)
1010*4882a593Smuzhiyun            self.nfs_instance = int(all_instances.pop()) + 1
1011*4882a593Smuzhiyun
1012*4882a593Smuzhiyun        nfsd_port = 3049 + 2 * self.nfs_instance
1013*4882a593Smuzhiyun        mountd_port = 3048 + 2 * self.nfs_instance
1014*4882a593Smuzhiyun
1015*4882a593Smuzhiyun        # Export vars for runqemu-export-rootfs
1016*4882a593Smuzhiyun        export_dict = {
1017*4882a593Smuzhiyun            'NFS_INSTANCE': self.nfs_instance,
1018*4882a593Smuzhiyun            'NFSD_PORT': nfsd_port,
1019*4882a593Smuzhiyun            'MOUNTD_PORT': mountd_port,
1020*4882a593Smuzhiyun        }
1021*4882a593Smuzhiyun        for k, v in export_dict.items():
1022*4882a593Smuzhiyun            # Use '%s' since they are integers
1023*4882a593Smuzhiyun            os.putenv(k, '%s' % v)
1024*4882a593Smuzhiyun
1025*4882a593Smuzhiyun        self.unfs_opts="nfsvers=3,port=%s,tcp,mountport=%s" % (nfsd_port, mountd_port)
1026*4882a593Smuzhiyun
1027*4882a593Smuzhiyun        # Extract .tar.bz2 or .tar.bz if no nfs dir
1028*4882a593Smuzhiyun        if not (self.rootfs and os.path.isdir(self.rootfs)):
1029*4882a593Smuzhiyun            src_prefix = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'))
1030*4882a593Smuzhiyun            dest = "%s-nfsroot" % src_prefix
1031*4882a593Smuzhiyun            if os.path.exists('%s.pseudo_state' % dest):
1032*4882a593Smuzhiyun                logger.info('Use %s as NFS_DIR' % dest)
1033*4882a593Smuzhiyun                self.rootfs = dest
1034*4882a593Smuzhiyun            else:
1035*4882a593Smuzhiyun                src = ""
1036*4882a593Smuzhiyun                src1 = '%s.tar.bz2' % src_prefix
1037*4882a593Smuzhiyun                src2 = '%s.tar.gz' % src_prefix
1038*4882a593Smuzhiyun                if os.path.exists(src1):
1039*4882a593Smuzhiyun                    src = src1
1040*4882a593Smuzhiyun                elif os.path.exists(src2):
1041*4882a593Smuzhiyun                    src = src2
1042*4882a593Smuzhiyun                if not src:
1043*4882a593Smuzhiyun                    raise RunQemuError("No NFS_DIR is set, and can't find %s or %s to extract" % (src1, src2))
1044*4882a593Smuzhiyun                logger.info('NFS_DIR not found, extracting %s to %s' % (src, dest))
1045*4882a593Smuzhiyun                cmd = ('runqemu-extract-sdk', src, dest)
1046*4882a593Smuzhiyun                logger.info('Running %s...' % str(cmd))
1047*4882a593Smuzhiyun                if subprocess.call(cmd) != 0:
1048*4882a593Smuzhiyun                    raise RunQemuError('Failed to run %s' % cmd)
1049*4882a593Smuzhiyun                self.rootfs = dest
1050*4882a593Smuzhiyun                self.cleanup_files.append(self.rootfs)
1051*4882a593Smuzhiyun                self.cleanup_files.append('%s.pseudo_state' % self.rootfs)
1052*4882a593Smuzhiyun
1053*4882a593Smuzhiyun        # Start the userspace NFS server
1054*4882a593Smuzhiyun        cmd = ('runqemu-export-rootfs', 'start', self.rootfs)
1055*4882a593Smuzhiyun        logger.info('Running %s...' % str(cmd))
1056*4882a593Smuzhiyun        if subprocess.call(cmd) != 0:
1057*4882a593Smuzhiyun            raise RunQemuError('Failed to run %s' % cmd)
1058*4882a593Smuzhiyun
1059*4882a593Smuzhiyun        self.nfs_running = True
1060*4882a593Smuzhiyun
1061*4882a593Smuzhiyun    def setup_net_bridge(self):
1062*4882a593Smuzhiyun        self.set('NETWORK_CMD', '-netdev bridge,br=%s,id=net0,helper=%s -device virtio-net-pci,netdev=net0 ' % (
1063*4882a593Smuzhiyun            self.net_bridge, os.path.join(self.bindir_native, 'qemu-oe-bridge-helper')))
1064*4882a593Smuzhiyun
1065*4882a593Smuzhiyun    def setup_slirp(self):
1066*4882a593Smuzhiyun        """Setup user networking"""
1067*4882a593Smuzhiyun
1068*4882a593Smuzhiyun        if self.fstype == 'nfs':
1069*4882a593Smuzhiyun            self.setup_nfs()
1070*4882a593Smuzhiyun        netconf = " " + self.cmdline_ip_slirp
1071*4882a593Smuzhiyun        logger.info("Network configuration:%s", netconf)
1072*4882a593Smuzhiyun        self.kernel_cmdline_script += netconf
1073*4882a593Smuzhiyun        # Port mapping
1074*4882a593Smuzhiyun        hostfwd = ",hostfwd=tcp::2222-:22,hostfwd=tcp::2323-:23"
1075*4882a593Smuzhiyun        qb_slirp_opt_default = "-netdev user,id=net0%s,tftp=%s" % (hostfwd, self.get('DEPLOY_DIR_IMAGE'))
1076*4882a593Smuzhiyun        qb_slirp_opt = self.get('QB_SLIRP_OPT') or qb_slirp_opt_default
1077*4882a593Smuzhiyun        # Figure out the port
1078*4882a593Smuzhiyun        ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt)
1079*4882a593Smuzhiyun        ports = [int(i) for i in ports]
1080*4882a593Smuzhiyun        mac = 2
1081*4882a593Smuzhiyun
1082*4882a593Smuzhiyun        lockdir = "/tmp/qemu-port-locks"
1083*4882a593Smuzhiyun        if not os.path.exists(lockdir):
1084*4882a593Smuzhiyun            # There might be a race issue when multi runqemu processess are
1085*4882a593Smuzhiyun            # running at the same time.
1086*4882a593Smuzhiyun            try:
1087*4882a593Smuzhiyun                os.mkdir(lockdir)
1088*4882a593Smuzhiyun                os.chmod(lockdir, 0o777)
1089*4882a593Smuzhiyun            except FileExistsError:
1090*4882a593Smuzhiyun                pass
1091*4882a593Smuzhiyun
1092*4882a593Smuzhiyun        # Find a free port to avoid conflicts
1093*4882a593Smuzhiyun        for p in ports[:]:
1094*4882a593Smuzhiyun            p_new = p
1095*4882a593Smuzhiyun            while not self.check_free_port('localhost', p_new, lockdir):
1096*4882a593Smuzhiyun                p_new += 1
1097*4882a593Smuzhiyun                mac += 1
1098*4882a593Smuzhiyun                while p_new in ports:
1099*4882a593Smuzhiyun                        p_new += 1
1100*4882a593Smuzhiyun                        mac += 1
1101*4882a593Smuzhiyun            if p != p_new:
1102*4882a593Smuzhiyun                ports.append(p_new)
1103*4882a593Smuzhiyun                qb_slirp_opt = re.sub(':%s-' % p, ':%s-' % p_new, qb_slirp_opt)
1104*4882a593Smuzhiyun                logger.info("Port forward changed: %s -> %s" % (p, p_new))
1105*4882a593Smuzhiyun        mac = "%s%02x" % (self.mac_slirp, mac)
1106*4882a593Smuzhiyun        self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qb_slirp_opt))
1107*4882a593Smuzhiyun        # Print out port foward
1108*4882a593Smuzhiyun        hostfwd = re.findall('(hostfwd=[^,]*)', qb_slirp_opt)
1109*4882a593Smuzhiyun        if hostfwd:
1110*4882a593Smuzhiyun            logger.info('Port forward: %s' % ' '.join(hostfwd))
1111*4882a593Smuzhiyun
1112*4882a593Smuzhiyun    def setup_tap(self):
1113*4882a593Smuzhiyun        """Setup tap"""
1114*4882a593Smuzhiyun
1115*4882a593Smuzhiyun        # This file is created when runqemu-gen-tapdevs creates a bank of tap
1116*4882a593Smuzhiyun        # devices, indicating that the user should not bring up new ones using
1117*4882a593Smuzhiyun        # sudo.
1118*4882a593Smuzhiyun        nosudo_flag = '/etc/runqemu-nosudo'
1119*4882a593Smuzhiyun        self.qemuifup = shutil.which('runqemu-ifup')
1120*4882a593Smuzhiyun        self.qemuifdown = shutil.which('runqemu-ifdown')
1121*4882a593Smuzhiyun        ip = shutil.which('ip')
1122*4882a593Smuzhiyun        lockdir = "/tmp/qemu-tap-locks"
1123*4882a593Smuzhiyun
1124*4882a593Smuzhiyun        if not (self.qemuifup and self.qemuifdown and ip):
1125*4882a593Smuzhiyun            logger.error("runqemu-ifup: %s" % self.qemuifup)
1126*4882a593Smuzhiyun            logger.error("runqemu-ifdown: %s" % self.qemuifdown)
1127*4882a593Smuzhiyun            logger.error("ip: %s" % ip)
1128*4882a593Smuzhiyun            raise OEPathError("runqemu-ifup, runqemu-ifdown or ip not found")
1129*4882a593Smuzhiyun
1130*4882a593Smuzhiyun        if not os.path.exists(lockdir):
1131*4882a593Smuzhiyun            # There might be a race issue when multi runqemu processess are
1132*4882a593Smuzhiyun            # running at the same time.
1133*4882a593Smuzhiyun            try:
1134*4882a593Smuzhiyun                os.mkdir(lockdir)
1135*4882a593Smuzhiyun                os.chmod(lockdir, 0o777)
1136*4882a593Smuzhiyun            except FileExistsError:
1137*4882a593Smuzhiyun                pass
1138*4882a593Smuzhiyun
1139*4882a593Smuzhiyun        cmd = (ip, 'link')
1140*4882a593Smuzhiyun        logger.debug('Running %s...' % str(cmd))
1141*4882a593Smuzhiyun        ip_link = subprocess.check_output(cmd).decode('utf-8')
1142*4882a593Smuzhiyun        # Matches line like: 6: tap0: <foo>
1143*4882a593Smuzhiyun        possibles = re.findall('^[0-9]+: +(tap[0-9]+): <.*', ip_link, re.M)
1144*4882a593Smuzhiyun        tap = ""
1145*4882a593Smuzhiyun        for p in possibles:
1146*4882a593Smuzhiyun            lockfile = os.path.join(lockdir, p)
1147*4882a593Smuzhiyun            if os.path.exists('%s.skip' % lockfile):
1148*4882a593Smuzhiyun                logger.info('Found %s.skip, skipping %s' % (lockfile, p))
1149*4882a593Smuzhiyun                continue
1150*4882a593Smuzhiyun            self.taplock = lockfile + '.lock'
1151*4882a593Smuzhiyun            if self.acquire_taplock(error=False):
1152*4882a593Smuzhiyun                tap = p
1153*4882a593Smuzhiyun                logger.info("Using preconfigured tap device %s" % tap)
1154*4882a593Smuzhiyun                logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap))
1155*4882a593Smuzhiyun                break
1156*4882a593Smuzhiyun
1157*4882a593Smuzhiyun        if not tap:
1158*4882a593Smuzhiyun            if os.path.exists(nosudo_flag):
1159*4882a593Smuzhiyun                logger.error("Error: There are no available tap devices to use for networking,")
1160*4882a593Smuzhiyun                logger.error("and I see %s exists, so I am not going to try creating" % nosudo_flag)
1161*4882a593Smuzhiyun                raise RunQemuError("a new one with sudo.")
1162*4882a593Smuzhiyun
1163*4882a593Smuzhiyun            gid = os.getgid()
1164*4882a593Smuzhiyun            uid = os.getuid()
1165*4882a593Smuzhiyun            logger.info("Setting up tap interface under sudo")
1166*4882a593Smuzhiyun            cmd = ('sudo', self.qemuifup, str(uid), str(gid), self.bindir_native)
1167*4882a593Smuzhiyun            try:
1168*4882a593Smuzhiyun                tap = subprocess.check_output(cmd).decode('utf-8').strip()
1169*4882a593Smuzhiyun            except subprocess.CalledProcessError as e:
1170*4882a593Smuzhiyun                logger.error('Setting up tap device failed:\n%s\nRun runqemu-gen-tapdevs to manually create one.' % str(e))
1171*4882a593Smuzhiyun                sys.exit(1)
1172*4882a593Smuzhiyun            lockfile = os.path.join(lockdir, tap)
1173*4882a593Smuzhiyun            self.taplock = lockfile + '.lock'
1174*4882a593Smuzhiyun            self.acquire_taplock()
1175*4882a593Smuzhiyun            self.cleantap = True
1176*4882a593Smuzhiyun            logger.debug('Created tap: %s' % tap)
1177*4882a593Smuzhiyun
1178*4882a593Smuzhiyun        if not tap:
1179*4882a593Smuzhiyun            logger.error("Failed to setup tap device. Run runqemu-gen-tapdevs to manually create.")
1180*4882a593Smuzhiyun            sys.exit(1)
1181*4882a593Smuzhiyun        self.tap = tap
1182*4882a593Smuzhiyun        tapnum = int(tap[3:])
1183*4882a593Smuzhiyun        gateway = tapnum * 2 + 1
1184*4882a593Smuzhiyun        client = gateway + 1
1185*4882a593Smuzhiyun        if self.fstype == 'nfs':
1186*4882a593Smuzhiyun            self.setup_nfs()
1187*4882a593Smuzhiyun        netconf = " " + self.cmdline_ip_tap
1188*4882a593Smuzhiyun        netconf = netconf.replace('@CLIENT@', str(client))
1189*4882a593Smuzhiyun        netconf = netconf.replace('@GATEWAY@', str(gateway))
1190*4882a593Smuzhiyun        logger.info("Network configuration:%s", netconf)
1191*4882a593Smuzhiyun        self.kernel_cmdline_script += netconf
1192*4882a593Smuzhiyun        mac = "%s%02x" % (self.mac_tap, client)
1193*4882a593Smuzhiyun        qb_tap_opt = self.get('QB_TAP_OPT')
1194*4882a593Smuzhiyun        if qb_tap_opt:
1195*4882a593Smuzhiyun            qemu_tap_opt = qb_tap_opt.replace('@TAP@', tap)
1196*4882a593Smuzhiyun        else:
1197*4882a593Smuzhiyun            qemu_tap_opt = "-netdev tap,id=net0,ifname=%s,script=no,downscript=no" % (self.tap)
1198*4882a593Smuzhiyun
1199*4882a593Smuzhiyun        if self.vhost_enabled:
1200*4882a593Smuzhiyun            qemu_tap_opt += ',vhost=on'
1201*4882a593Smuzhiyun
1202*4882a593Smuzhiyun        self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qemu_tap_opt))
1203*4882a593Smuzhiyun
1204*4882a593Smuzhiyun    def setup_network(self):
1205*4882a593Smuzhiyun        if self.get('QB_NET') == 'none':
1206*4882a593Smuzhiyun            return
1207*4882a593Smuzhiyun        if sys.stdin.isatty():
1208*4882a593Smuzhiyun            self.saved_stty = subprocess.check_output(("stty", "-g")).decode('utf-8').strip()
1209*4882a593Smuzhiyun        self.network_device = self.get('QB_NETWORK_DEVICE') or self.network_device
1210*4882a593Smuzhiyun        if self.net_bridge:
1211*4882a593Smuzhiyun            self.setup_net_bridge()
1212*4882a593Smuzhiyun        elif self.slirp_enabled:
1213*4882a593Smuzhiyun            self.cmdline_ip_slirp = self.get('QB_CMDLINE_IP_SLIRP') or self.cmdline_ip_slirp
1214*4882a593Smuzhiyun            self.setup_slirp()
1215*4882a593Smuzhiyun        else:
1216*4882a593Smuzhiyun            self.cmdline_ip_tap = self.get('QB_CMDLINE_IP_TAP') or self.cmdline_ip_tap
1217*4882a593Smuzhiyun            self.setup_tap()
1218*4882a593Smuzhiyun
1219*4882a593Smuzhiyun    def setup_rootfs(self):
1220*4882a593Smuzhiyun        if self.get('QB_ROOTFS') == 'none':
1221*4882a593Smuzhiyun            return
1222*4882a593Smuzhiyun        if 'wic.' in self.fstype:
1223*4882a593Smuzhiyun            self.fstype = self.fstype[4:]
1224*4882a593Smuzhiyun        rootfs_format = self.fstype if self.fstype in ('vmdk', 'vhd', 'vhdx', 'qcow2', 'vdi') else 'raw'
1225*4882a593Smuzhiyun
1226*4882a593Smuzhiyun        tmpfsdir = os.environ.get("RUNQEMU_TMPFS_DIR", None)
1227*4882a593Smuzhiyun        if self.snapshot and tmpfsdir:
1228*4882a593Smuzhiyun            newrootfs = os.path.join(tmpfsdir, os.path.basename(self.rootfs)) + "." + str(os.getpid())
1229*4882a593Smuzhiyun            logger.info("Copying rootfs to %s" % newrootfs)
1230*4882a593Smuzhiyun            copy_start = time.time()
1231*4882a593Smuzhiyun            shutil.copyfile(self.rootfs, newrootfs)
1232*4882a593Smuzhiyun            logger.info("Copy done in %s seconds" % (time.time() - copy_start))
1233*4882a593Smuzhiyun            self.rootfs = newrootfs
1234*4882a593Smuzhiyun            # Don't need a second copy now!
1235*4882a593Smuzhiyun            self.snapshot = False
1236*4882a593Smuzhiyun            self.cleanup_files.append(newrootfs)
1237*4882a593Smuzhiyun
1238*4882a593Smuzhiyun        qb_rootfs_opt = self.get('QB_ROOTFS_OPT')
1239*4882a593Smuzhiyun        if qb_rootfs_opt:
1240*4882a593Smuzhiyun            self.rootfs_options = qb_rootfs_opt.replace('@ROOTFS@', self.rootfs)
1241*4882a593Smuzhiyun        else:
1242*4882a593Smuzhiyun            self.rootfs_options = '-drive file=%s,if=virtio,format=%s' % (self.rootfs, rootfs_format)
1243*4882a593Smuzhiyun
1244*4882a593Smuzhiyun        qb_rootfs_extra_opt = self.get("QB_ROOTFS_EXTRA_OPT")
1245*4882a593Smuzhiyun        if qb_rootfs_extra_opt and not qb_rootfs_extra_opt.startswith(","):
1246*4882a593Smuzhiyun            qb_rootfs_extra_opt = "," + qb_rootfs_extra_opt
1247*4882a593Smuzhiyun
1248*4882a593Smuzhiyun        if self.fstype in ('cpio.gz', 'cpio'):
1249*4882a593Smuzhiyun            self.kernel_cmdline = 'root=/dev/ram0 rw debugshell'
1250*4882a593Smuzhiyun            self.rootfs_options = '-initrd %s' % self.rootfs
1251*4882a593Smuzhiyun        else:
1252*4882a593Smuzhiyun            vm_drive = ''
1253*4882a593Smuzhiyun            if self.fstype in self.vmtypes:
1254*4882a593Smuzhiyun                if self.fstype == 'iso':
1255*4882a593Smuzhiyun                    vm_drive = '-drive file=%s,if=virtio,media=cdrom' % self.rootfs
1256*4882a593Smuzhiyun                elif self.get('QB_DRIVE_TYPE'):
1257*4882a593Smuzhiyun                    drive_type = self.get('QB_DRIVE_TYPE')
1258*4882a593Smuzhiyun                    if drive_type.startswith("/dev/sd"):
1259*4882a593Smuzhiyun                        logger.info('Using scsi drive')
1260*4882a593Smuzhiyun                        vm_drive = '-drive if=none,id=hd,file=%s,format=%s -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd%s' \
1261*4882a593Smuzhiyun                                       % (self.rootfs, rootfs_format, qb_rootfs_extra_opt)
1262*4882a593Smuzhiyun                    elif drive_type.startswith("/dev/hd"):
1263*4882a593Smuzhiyun                        logger.info('Using ide drive')
1264*4882a593Smuzhiyun                        vm_drive = "-drive file=%s,format=%s" % (self.rootfs, rootfs_format)
1265*4882a593Smuzhiyun                    elif drive_type.startswith("/dev/vdb"):
1266*4882a593Smuzhiyun                        logger.info('Using block virtio drive');
1267*4882a593Smuzhiyun                        vm_drive = '-drive id=disk0,file=%s,if=none,format=%s -device virtio-blk-device,drive=disk0%s' \
1268*4882a593Smuzhiyun                                    % (self.rootfs, rootfs_format,qb_rootfs_extra_opt)
1269*4882a593Smuzhiyun                    else:
1270*4882a593Smuzhiyun                        # virtio might have been selected explicitly (just use it), or
1271*4882a593Smuzhiyun                        # is used as fallback (then warn about that).
1272*4882a593Smuzhiyun                        if not drive_type.startswith("/dev/vd"):
1273*4882a593Smuzhiyun                            logger.warning("Unknown QB_DRIVE_TYPE: %s" % drive_type)
1274*4882a593Smuzhiyun                            logger.warning("Failed to figure out drive type, consider define or fix QB_DRIVE_TYPE")
1275*4882a593Smuzhiyun                            logger.warning('Trying to use virtio block drive')
1276*4882a593Smuzhiyun                        vm_drive = '-drive if=virtio,file=%s,format=%s' % (self.rootfs, rootfs_format)
1277*4882a593Smuzhiyun
1278*4882a593Smuzhiyun                # All branches above set vm_drive.
1279*4882a593Smuzhiyun                self.rootfs_options = vm_drive
1280*4882a593Smuzhiyun                if not self.fstype in self.vmtypes:
1281*4882a593Smuzhiyun                    self.rootfs_options += ' -no-reboot'
1282*4882a593Smuzhiyun
1283*4882a593Smuzhiyun            # By default, ' rw' is appended to QB_KERNEL_ROOT unless either ro or rw is explicitly passed.
1284*4882a593Smuzhiyun            qb_kernel_root = self.get('QB_KERNEL_ROOT')
1285*4882a593Smuzhiyun            qb_kernel_root_l = qb_kernel_root.split()
1286*4882a593Smuzhiyun            if not ('ro' in qb_kernel_root_l or 'rw' in qb_kernel_root_l):
1287*4882a593Smuzhiyun                qb_kernel_root += ' rw'
1288*4882a593Smuzhiyun            self.kernel_cmdline = 'root=%s' % qb_kernel_root
1289*4882a593Smuzhiyun
1290*4882a593Smuzhiyun        if self.fstype == 'nfs':
1291*4882a593Smuzhiyun            self.rootfs_options = ''
1292*4882a593Smuzhiyun            k_root = '/dev/nfs nfsroot=%s:%s,%s' % (self.nfs_server, os.path.abspath(self.rootfs), self.unfs_opts)
1293*4882a593Smuzhiyun            self.kernel_cmdline = 'root=%s rw' % k_root
1294*4882a593Smuzhiyun
1295*4882a593Smuzhiyun        if self.fstype == 'none':
1296*4882a593Smuzhiyun            self.rootfs_options = ''
1297*4882a593Smuzhiyun
1298*4882a593Smuzhiyun        self.set('ROOTFS_OPTIONS', self.rootfs_options)
1299*4882a593Smuzhiyun
1300*4882a593Smuzhiyun    def guess_qb_system(self):
1301*4882a593Smuzhiyun        """attempt to determine the appropriate qemu-system binary"""
1302*4882a593Smuzhiyun        mach = self.get('MACHINE')
1303*4882a593Smuzhiyun        if not mach:
1304*4882a593Smuzhiyun            search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*'
1305*4882a593Smuzhiyun            if self.rootfs:
1306*4882a593Smuzhiyun                match = re.match(search, self.rootfs)
1307*4882a593Smuzhiyun                if match:
1308*4882a593Smuzhiyun                    mach = match.group(1)
1309*4882a593Smuzhiyun            elif self.kernel:
1310*4882a593Smuzhiyun                match = re.match(search, self.kernel)
1311*4882a593Smuzhiyun                if match:
1312*4882a593Smuzhiyun                    mach = match.group(1)
1313*4882a593Smuzhiyun
1314*4882a593Smuzhiyun        if not mach:
1315*4882a593Smuzhiyun            return None
1316*4882a593Smuzhiyun
1317*4882a593Smuzhiyun        if mach == 'qemuarm':
1318*4882a593Smuzhiyun            qbsys = 'arm'
1319*4882a593Smuzhiyun        elif mach == 'qemuarm64':
1320*4882a593Smuzhiyun            qbsys = 'aarch64'
1321*4882a593Smuzhiyun        elif mach == 'qemux86':
1322*4882a593Smuzhiyun            qbsys = 'i386'
1323*4882a593Smuzhiyun        elif mach == 'qemux86-64':
1324*4882a593Smuzhiyun            qbsys = 'x86_64'
1325*4882a593Smuzhiyun        elif mach == 'qemuppc':
1326*4882a593Smuzhiyun            qbsys = 'ppc'
1327*4882a593Smuzhiyun        elif mach == 'qemumips':
1328*4882a593Smuzhiyun            qbsys = 'mips'
1329*4882a593Smuzhiyun        elif mach == 'qemumips64':
1330*4882a593Smuzhiyun            qbsys = 'mips64'
1331*4882a593Smuzhiyun        elif mach == 'qemumipsel':
1332*4882a593Smuzhiyun            qbsys = 'mipsel'
1333*4882a593Smuzhiyun        elif mach == 'qemumips64el':
1334*4882a593Smuzhiyun            qbsys = 'mips64el'
1335*4882a593Smuzhiyun        elif mach == 'qemuriscv64':
1336*4882a593Smuzhiyun            qbsys = 'riscv64'
1337*4882a593Smuzhiyun        elif mach == 'qemuriscv32':
1338*4882a593Smuzhiyun            qbsys = 'riscv32'
1339*4882a593Smuzhiyun        else:
1340*4882a593Smuzhiyun            logger.error("Unable to determine QEMU PC System emulator for %s machine." % mach)
1341*4882a593Smuzhiyun            logger.error("As %s is not among valid QEMU machines such as," % mach)
1342*4882a593Smuzhiyun            logger.error("qemux86-64, qemux86, qemuarm64, qemuarm, qemumips64, qemumips64el, qemumipsel, qemumips, qemuppc")
1343*4882a593Smuzhiyun            raise RunQemuError("Set qb_system_name with suitable QEMU PC System emulator in .*qemuboot.conf.")
1344*4882a593Smuzhiyun
1345*4882a593Smuzhiyun        return 'qemu-system-%s' % qbsys
1346*4882a593Smuzhiyun
1347*4882a593Smuzhiyun    def check_qemu_system(self):
1348*4882a593Smuzhiyun        qemu_system = self.get('QB_SYSTEM_NAME')
1349*4882a593Smuzhiyun        if not qemu_system:
1350*4882a593Smuzhiyun            qemu_system = self.guess_qb_system()
1351*4882a593Smuzhiyun        if not qemu_system:
1352*4882a593Smuzhiyun            raise RunQemuError("Failed to boot, QB_SYSTEM_NAME is NULL!")
1353*4882a593Smuzhiyun        self.qemu_system = qemu_system
1354*4882a593Smuzhiyun
1355*4882a593Smuzhiyun    def setup_vga(self):
1356*4882a593Smuzhiyun        if self.nographic == True:
1357*4882a593Smuzhiyun            if self.sdl == True:
1358*4882a593Smuzhiyun                raise RunQemuError('Option nographic makes no sense alongside the sdl option.')
1359*4882a593Smuzhiyun            if self.gtk == True:
1360*4882a593Smuzhiyun                raise RunQemuError('Option nographic makes no sense alongside the gtk option.')
1361*4882a593Smuzhiyun            self.qemu_opt += ' -nographic'
1362*4882a593Smuzhiyun
1363*4882a593Smuzhiyun        if self.novga == True:
1364*4882a593Smuzhiyun            self.qemu_opt += ' -vga none'
1365*4882a593Smuzhiyun            return
1366*4882a593Smuzhiyun
1367*4882a593Smuzhiyun        if (self.gl_es == True or self.gl == True) and (self.sdl == False and self.gtk == False):
1368*4882a593Smuzhiyun            raise RunQemuError('Option gl/gl-es needs gtk or sdl option.')
1369*4882a593Smuzhiyun
1370*4882a593Smuzhiyun        # If we have no display option, we autodetect based upon what qemu supports. We
1371*4882a593Smuzhiyun        # need our font setup and show-cusor below so we need to see what qemu --help says
1372*4882a593Smuzhiyun        # is supported so we can pass our correct config in.
1373*4882a593Smuzhiyun        if not self.nographic and not self.sdl and not self.gtk and not self.publicvnc and not self.egl_headless == True:
1374*4882a593Smuzhiyun            output = subprocess.check_output([self.qemu_bin, "--help"], universal_newlines=True, env=self.qemu_environ)
1375*4882a593Smuzhiyun            if "-display gtk" in output:
1376*4882a593Smuzhiyun                self.gtk = True
1377*4882a593Smuzhiyun            elif "-display sdl" in output:
1378*4882a593Smuzhiyun                self.sdl = True
1379*4882a593Smuzhiyun            else:
1380*4882a593Smuzhiyun                self.qemu_opt += ' -display none'
1381*4882a593Smuzhiyun
1382*4882a593Smuzhiyun        if self.sdl == True or self.gtk == True or self.egl_headless == True:
1383*4882a593Smuzhiyun
1384*4882a593Smuzhiyun            if self.qemu_system.endswith(('i386', 'x86_64')):
1385*4882a593Smuzhiyun                if self.gl or self.gl_es or self.egl_headless:
1386*4882a593Smuzhiyun                    self.qemu_opt += ' -device virtio-vga-gl '
1387*4882a593Smuzhiyun                else:
1388*4882a593Smuzhiyun                    self.qemu_opt += ' -device virtio-vga '
1389*4882a593Smuzhiyun
1390*4882a593Smuzhiyun            self.qemu_opt += ' -display '
1391*4882a593Smuzhiyun            if self.egl_headless == True:
1392*4882a593Smuzhiyun                self.set_dri_path()
1393*4882a593Smuzhiyun                self.qemu_opt += 'egl-headless,'
1394*4882a593Smuzhiyun            else:
1395*4882a593Smuzhiyun                if self.sdl == True:
1396*4882a593Smuzhiyun                    self.qemu_opt += 'sdl,'
1397*4882a593Smuzhiyun                elif self.gtk == True:
1398*4882a593Smuzhiyun                    self.qemu_environ['FONTCONFIG_PATH'] = '/etc/fonts'
1399*4882a593Smuzhiyun                    self.qemu_opt += 'gtk,'
1400*4882a593Smuzhiyun
1401*4882a593Smuzhiyun                if self.gl == True:
1402*4882a593Smuzhiyun                    self.set_dri_path()
1403*4882a593Smuzhiyun                    self.qemu_opt += 'gl=on,'
1404*4882a593Smuzhiyun                elif self.gl_es == True:
1405*4882a593Smuzhiyun                    self.set_dri_path()
1406*4882a593Smuzhiyun                    self.qemu_opt += 'gl=es,'
1407*4882a593Smuzhiyun            self.qemu_opt += 'show-cursor=on'
1408*4882a593Smuzhiyun
1409*4882a593Smuzhiyun        self.qemu_opt += ' %s' %self.get('QB_GRAPHICS')
1410*4882a593Smuzhiyun
1411*4882a593Smuzhiyun    def setup_serial(self):
1412*4882a593Smuzhiyun        # Setup correct kernel command line for serial
1413*4882a593Smuzhiyun        if self.get('SERIAL_CONSOLES') and (self.serialstdio == True or self.serialconsole == True or self.nographic == True or self.tcpserial_portnum):
1414*4882a593Smuzhiyun            for entry in self.get('SERIAL_CONSOLES').split(' '):
1415*4882a593Smuzhiyun                self.kernel_cmdline_script += ' console=%s' %entry.split(';')[1]
1416*4882a593Smuzhiyun
1417*4882a593Smuzhiyun        if self.serialstdio == True or self.nographic == True:
1418*4882a593Smuzhiyun            self.qemu_opt += " -serial mon:stdio"
1419*4882a593Smuzhiyun        else:
1420*4882a593Smuzhiyun            self.qemu_opt += " -serial mon:vc"
1421*4882a593Smuzhiyun            if self.serialconsole:
1422*4882a593Smuzhiyun                if sys.stdin.isatty():
1423*4882a593Smuzhiyun                    subprocess.check_call(("stty", "intr", "^]"))
1424*4882a593Smuzhiyun                    logger.info("Interrupt character is '^]'")
1425*4882a593Smuzhiyun
1426*4882a593Smuzhiyun                self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT")
1427*4882a593Smuzhiyun
1428*4882a593Smuzhiyun        # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES).
1429*4882a593Smuzhiyun        # If no serial or serialtcp options were specified, only ttyS0 is created
1430*4882a593Smuzhiyun        # and sysvinit shows an error trying to enable ttyS1:
1431*4882a593Smuzhiyun        #     INIT: Id "S1" respawning too fast: disabled for 5 minutes
1432*4882a593Smuzhiyun        serial_num = len(re.findall("-serial", self.qemu_opt))
1433*4882a593Smuzhiyun        if serial_num < 2:
1434*4882a593Smuzhiyun            self.qemu_opt += " -serial null"
1435*4882a593Smuzhiyun
1436*4882a593Smuzhiyun    def find_qemu(self):
1437*4882a593Smuzhiyun        qemu_bin = os.path.join(self.bindir_native, self.qemu_system)
1438*4882a593Smuzhiyun
1439*4882a593Smuzhiyun        # It is possible to have qemu-native in ASSUME_PROVIDED, and it won't
1440*4882a593Smuzhiyun        # find QEMU in sysroot, it needs to use host's qemu.
1441*4882a593Smuzhiyun        if not os.path.exists(qemu_bin):
1442*4882a593Smuzhiyun            logger.info("QEMU binary not found in %s, trying host's QEMU" % qemu_bin)
1443*4882a593Smuzhiyun            for path in (os.environ['PATH'] or '').split(':'):
1444*4882a593Smuzhiyun                qemu_bin_tmp = os.path.join(path, self.qemu_system)
1445*4882a593Smuzhiyun                logger.info("Trying: %s" % qemu_bin_tmp)
1446*4882a593Smuzhiyun                if os.path.exists(qemu_bin_tmp):
1447*4882a593Smuzhiyun                    qemu_bin = qemu_bin_tmp
1448*4882a593Smuzhiyun                    if not os.path.isabs(qemu_bin):
1449*4882a593Smuzhiyun                        qemu_bin = os.path.abspath(qemu_bin)
1450*4882a593Smuzhiyun                    logger.info("Using host's QEMU: %s" % qemu_bin)
1451*4882a593Smuzhiyun                    break
1452*4882a593Smuzhiyun
1453*4882a593Smuzhiyun        if not os.access(qemu_bin, os.X_OK):
1454*4882a593Smuzhiyun            raise OEPathError("No QEMU binary '%s' could be found" % qemu_bin)
1455*4882a593Smuzhiyun        self.qemu_bin = qemu_bin
1456*4882a593Smuzhiyun
1457*4882a593Smuzhiyun    def setup_final(self):
1458*4882a593Smuzhiyun
1459*4882a593Smuzhiyun        self.find_qemu()
1460*4882a593Smuzhiyun
1461*4882a593Smuzhiyun        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')))
1462*4882a593Smuzhiyun
1463*4882a593Smuzhiyun        for ovmf in self.ovmf_bios:
1464*4882a593Smuzhiyun            format = ovmf.rsplit('.', 1)[-1]
1465*4882a593Smuzhiyun            if format == "bin":
1466*4882a593Smuzhiyun                format = "raw"
1467*4882a593Smuzhiyun            self.qemu_opt += ' -drive if=pflash,format=%s,file=%s' % (format, ovmf)
1468*4882a593Smuzhiyun
1469*4882a593Smuzhiyun        self.qemu_opt += ' ' + self.qemu_opt_script
1470*4882a593Smuzhiyun
1471*4882a593Smuzhiyun        if self.ovmf_secboot_pkkek1:
1472*4882a593Smuzhiyun			# Provide the Platform Key and first Key Exchange Key certificate as an
1473*4882a593Smuzhiyun			# OEM string in the SMBIOS Type 11 table. Prepend the certificate string
1474*4882a593Smuzhiyun			# with "application prefix" of the EnrollDefaultKeys.efi application
1475*4882a593Smuzhiyun            self.qemu_opt += ' -smbios type=11,value=4e32566d-8e9e-4f52-81d3-5bb9715f9727:' \
1476*4882a593Smuzhiyun                             + self.ovmf_secboot_pkkek1
1477*4882a593Smuzhiyun
1478*4882a593Smuzhiyun        # Append qemuparams to override previous settings
1479*4882a593Smuzhiyun        if self.qemuparams:
1480*4882a593Smuzhiyun            self.qemu_opt += ' ' + self.qemuparams
1481*4882a593Smuzhiyun
1482*4882a593Smuzhiyun        if self.snapshot:
1483*4882a593Smuzhiyun            self.qemu_opt += " -snapshot"
1484*4882a593Smuzhiyun
1485*4882a593Smuzhiyun        self.setup_serial()
1486*4882a593Smuzhiyun        self.setup_vga()
1487*4882a593Smuzhiyun
1488*4882a593Smuzhiyun    def start_qemu(self):
1489*4882a593Smuzhiyun        import shlex
1490*4882a593Smuzhiyun        if self.kernel:
1491*4882a593Smuzhiyun            kernel_opts = "-kernel %s -append '%s %s %s %s'" % (self.kernel, self.kernel_cmdline,
1492*4882a593Smuzhiyun                                                                self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'),
1493*4882a593Smuzhiyun                                                                self.bootparams)
1494*4882a593Smuzhiyun            if self.dtb:
1495*4882a593Smuzhiyun                kernel_opts += " -dtb %s" % self.dtb
1496*4882a593Smuzhiyun        else:
1497*4882a593Smuzhiyun            kernel_opts = ""
1498*4882a593Smuzhiyun
1499*4882a593Smuzhiyun        if self.bios:
1500*4882a593Smuzhiyun            self.qemu_opt += " -bios %s" % self.bios
1501*4882a593Smuzhiyun
1502*4882a593Smuzhiyun        cmd = "%s %s" % (self.qemu_opt, kernel_opts)
1503*4882a593Smuzhiyun        cmds = shlex.split(cmd)
1504*4882a593Smuzhiyun        logger.info('Running %s\n' % cmd)
1505*4882a593Smuzhiyun        with open('/proc/uptime', 'r') as f:
1506*4882a593Smuzhiyun            uptime_seconds = f.readline().split()[0]
1507*4882a593Smuzhiyun        logger.info('Host uptime: %s\n' % uptime_seconds)
1508*4882a593Smuzhiyun        pass_fds = []
1509*4882a593Smuzhiyun        if self.taplock_descriptor:
1510*4882a593Smuzhiyun            pass_fds = [self.taplock_descriptor.fileno()]
1511*4882a593Smuzhiyun        if len(self.portlocks):
1512*4882a593Smuzhiyun            for descriptor in self.portlocks.values():
1513*4882a593Smuzhiyun                pass_fds.append(descriptor.fileno())
1514*4882a593Smuzhiyun        process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds, env=self.qemu_environ)
1515*4882a593Smuzhiyun        self.qemuprocess = process
1516*4882a593Smuzhiyun        retcode = process.wait()
1517*4882a593Smuzhiyun        if retcode:
1518*4882a593Smuzhiyun            if retcode == -signal.SIGTERM:
1519*4882a593Smuzhiyun                logger.info("Qemu terminated by SIGTERM")
1520*4882a593Smuzhiyun            else:
1521*4882a593Smuzhiyun                logger.error("Failed to run qemu: %s", process.stderr.read().decode())
1522*4882a593Smuzhiyun
1523*4882a593Smuzhiyun    def cleanup(self):
1524*4882a593Smuzhiyun        if self.cleaned:
1525*4882a593Smuzhiyun            return
1526*4882a593Smuzhiyun
1527*4882a593Smuzhiyun        # avoid dealing with SIGTERM when cleanup function is running
1528*4882a593Smuzhiyun        signal.signal(signal.SIGTERM, signal.SIG_IGN)
1529*4882a593Smuzhiyun
1530*4882a593Smuzhiyun        logger.info("Cleaning up")
1531*4882a593Smuzhiyun
1532*4882a593Smuzhiyun        if self.qemuprocess:
1533*4882a593Smuzhiyun            try:
1534*4882a593Smuzhiyun                # give it some time to shut down, ignore return values and output
1535*4882a593Smuzhiyun                self.qemuprocess.send_signal(signal.SIGTERM)
1536*4882a593Smuzhiyun                self.qemuprocess.communicate(timeout=5)
1537*4882a593Smuzhiyun            except subprocess.TimeoutExpired:
1538*4882a593Smuzhiyun                self.qemuprocess.kill()
1539*4882a593Smuzhiyun
1540*4882a593Smuzhiyun        with open('/proc/uptime', 'r') as f:
1541*4882a593Smuzhiyun            uptime_seconds = f.readline().split()[0]
1542*4882a593Smuzhiyun        logger.info('Host uptime: %s\n' % uptime_seconds)
1543*4882a593Smuzhiyun        if self.cleantap:
1544*4882a593Smuzhiyun            cmd = ('sudo', self.qemuifdown, self.tap, self.bindir_native)
1545*4882a593Smuzhiyun            logger.debug('Running %s' % str(cmd))
1546*4882a593Smuzhiyun            subprocess.check_call(cmd)
1547*4882a593Smuzhiyun        self.release_taplock()
1548*4882a593Smuzhiyun        self.release_portlock()
1549*4882a593Smuzhiyun
1550*4882a593Smuzhiyun        if self.nfs_running:
1551*4882a593Smuzhiyun            logger.info("Shutting down the userspace NFS server...")
1552*4882a593Smuzhiyun            cmd = ("runqemu-export-rootfs", "stop", self.rootfs)
1553*4882a593Smuzhiyun            logger.debug('Running %s' % str(cmd))
1554*4882a593Smuzhiyun            subprocess.check_call(cmd)
1555*4882a593Smuzhiyun
1556*4882a593Smuzhiyun        if self.saved_stty:
1557*4882a593Smuzhiyun            subprocess.check_call(("stty", self.saved_stty))
1558*4882a593Smuzhiyun
1559*4882a593Smuzhiyun        if self.cleanup_files:
1560*4882a593Smuzhiyun            for ent in self.cleanup_files:
1561*4882a593Smuzhiyun                logger.info('Removing %s' % ent)
1562*4882a593Smuzhiyun                if os.path.isfile(ent):
1563*4882a593Smuzhiyun                    os.remove(ent)
1564*4882a593Smuzhiyun                else:
1565*4882a593Smuzhiyun                    shutil.rmtree(ent)
1566*4882a593Smuzhiyun
1567*4882a593Smuzhiyun        # Deliberately ignore the return code of 'tput smam'.
1568*4882a593Smuzhiyun        subprocess.call(["tput", "smam"])
1569*4882a593Smuzhiyun
1570*4882a593Smuzhiyun        self.cleaned = True
1571*4882a593Smuzhiyun
1572*4882a593Smuzhiyun    def run_bitbake_env(self, mach=None):
1573*4882a593Smuzhiyun        bitbake = shutil.which('bitbake')
1574*4882a593Smuzhiyun        if not bitbake:
1575*4882a593Smuzhiyun            return
1576*4882a593Smuzhiyun
1577*4882a593Smuzhiyun        if not mach:
1578*4882a593Smuzhiyun            mach = self.get('MACHINE')
1579*4882a593Smuzhiyun
1580*4882a593Smuzhiyun        multiconfig = self.get('MULTICONFIG')
1581*4882a593Smuzhiyun        if multiconfig:
1582*4882a593Smuzhiyun            multiconfig = "mc:%s" % multiconfig
1583*4882a593Smuzhiyun
1584*4882a593Smuzhiyun        if mach:
1585*4882a593Smuzhiyun            cmd = 'MACHINE=%s bitbake -e %s' % (mach, multiconfig)
1586*4882a593Smuzhiyun        else:
1587*4882a593Smuzhiyun            cmd = 'bitbake -e %s' % multiconfig
1588*4882a593Smuzhiyun
1589*4882a593Smuzhiyun        logger.info('Running %s...' % cmd)
1590*4882a593Smuzhiyun        return subprocess.check_output(cmd, shell=True).decode('utf-8')
1591*4882a593Smuzhiyun
1592*4882a593Smuzhiyun    def load_bitbake_env(self, mach=None):
1593*4882a593Smuzhiyun        if self.bitbake_e:
1594*4882a593Smuzhiyun            return
1595*4882a593Smuzhiyun
1596*4882a593Smuzhiyun        try:
1597*4882a593Smuzhiyun            self.bitbake_e = self.run_bitbake_env(mach=mach)
1598*4882a593Smuzhiyun        except subprocess.CalledProcessError as err:
1599*4882a593Smuzhiyun            self.bitbake_e = ''
1600*4882a593Smuzhiyun            logger.warning("Couldn't run 'bitbake -e' to gather environment information:\n%s" % err.output.decode('utf-8'))
1601*4882a593Smuzhiyun
1602*4882a593Smuzhiyun    def validate_combos(self):
1603*4882a593Smuzhiyun        if (self.fstype in self.vmtypes) and self.kernel:
1604*4882a593Smuzhiyun            raise RunQemuError("%s doesn't need kernel %s!" % (self.fstype, self.kernel))
1605*4882a593Smuzhiyun
1606*4882a593Smuzhiyun    @property
1607*4882a593Smuzhiyun    def bindir_native(self):
1608*4882a593Smuzhiyun        result = self.get('STAGING_BINDIR_NATIVE')
1609*4882a593Smuzhiyun        if result and os.path.exists(result):
1610*4882a593Smuzhiyun            return result
1611*4882a593Smuzhiyun
1612*4882a593Smuzhiyun        cmd = ['bitbake', '-e']
1613*4882a593Smuzhiyun        multiconfig = self.get('MULTICONFIG')
1614*4882a593Smuzhiyun        if multiconfig:
1615*4882a593Smuzhiyun            cmd.append('mc:%s:qemu-helper-native' % multiconfig)
1616*4882a593Smuzhiyun        else:
1617*4882a593Smuzhiyun            cmd.append('qemu-helper-native')
1618*4882a593Smuzhiyun
1619*4882a593Smuzhiyun        logger.info('Running %s...' % str(cmd))
1620*4882a593Smuzhiyun        out = subprocess.check_output(cmd).decode('utf-8')
1621*4882a593Smuzhiyun
1622*4882a593Smuzhiyun        match = re.search('^STAGING_BINDIR_NATIVE="(.*)"', out, re.M)
1623*4882a593Smuzhiyun        if match:
1624*4882a593Smuzhiyun            result = match.group(1)
1625*4882a593Smuzhiyun            if os.path.exists(result):
1626*4882a593Smuzhiyun                self.set('STAGING_BINDIR_NATIVE', result)
1627*4882a593Smuzhiyun                return result
1628*4882a593Smuzhiyun            raise RunQemuError("Native sysroot directory %s doesn't exist" % result)
1629*4882a593Smuzhiyun        else:
1630*4882a593Smuzhiyun            raise RunQemuError("Can't find STAGING_BINDIR_NATIVE in '%s' output" % cmd)
1631*4882a593Smuzhiyun
1632*4882a593Smuzhiyun
1633*4882a593Smuzhiyundef main():
1634*4882a593Smuzhiyun    if "help" in sys.argv or '-h' in sys.argv or '--help' in sys.argv:
1635*4882a593Smuzhiyun        print_usage()
1636*4882a593Smuzhiyun        return 0
1637*4882a593Smuzhiyun    try:
1638*4882a593Smuzhiyun        config = BaseConfig()
1639*4882a593Smuzhiyun
1640*4882a593Smuzhiyun        renice = os.path.expanduser("~/bin/runqemu-renice")
1641*4882a593Smuzhiyun        if os.path.exists(renice):
1642*4882a593Smuzhiyun            logger.info('Using %s to renice' % renice)
1643*4882a593Smuzhiyun            subprocess.check_call([renice, str(os.getpid())])
1644*4882a593Smuzhiyun
1645*4882a593Smuzhiyun        def sigterm_handler(signum, frame):
1646*4882a593Smuzhiyun            logger.info("Received signal: %s" % (signum))
1647*4882a593Smuzhiyun            config.cleanup()
1648*4882a593Smuzhiyun        signal.signal(signal.SIGTERM, sigterm_handler)
1649*4882a593Smuzhiyun
1650*4882a593Smuzhiyun        config.check_args()
1651*4882a593Smuzhiyun        config.read_qemuboot()
1652*4882a593Smuzhiyun        config.check_and_set()
1653*4882a593Smuzhiyun        # Check whether the combos is valid or not
1654*4882a593Smuzhiyun        config.validate_combos()
1655*4882a593Smuzhiyun        config.print_config()
1656*4882a593Smuzhiyun        config.setup_network()
1657*4882a593Smuzhiyun        config.setup_rootfs()
1658*4882a593Smuzhiyun        config.setup_final()
1659*4882a593Smuzhiyun        config.start_qemu()
1660*4882a593Smuzhiyun    except RunQemuError as err:
1661*4882a593Smuzhiyun        logger.error(err)
1662*4882a593Smuzhiyun        return 1
1663*4882a593Smuzhiyun    except Exception as err:
1664*4882a593Smuzhiyun        import traceback
1665*4882a593Smuzhiyun        traceback.print_exc()
1666*4882a593Smuzhiyun        return 1
1667*4882a593Smuzhiyun    finally:
1668*4882a593Smuzhiyun        config.cleanup()
1669*4882a593Smuzhiyun
1670*4882a593Smuzhiyunif __name__ == "__main__":
1671*4882a593Smuzhiyun    sys.exit(main())
1672