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