]>
git.ipfire.org Git - thirdparty/openembedded/openembedded-core.git/blob - scripts/runqemu
3 # Handle running OE images standalone with QEMU
5 # Copyright (C) 2006-2011 Linux Foundation
6 # Copyright (c) 2016 Wind River Systems, Inc.
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License version 2 as
10 # published by the Free Software Foundation.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License along
18 # with this program; if not, write to the Free Software Foundation, Inc.,
19 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
31 class OEPathError(Exception):
32 """Custom Exception to give better guidance on missing binaries"""
33 def __init__(self
, message
):
34 self
.message
= "In order for this script to dynamically infer paths\n \
35 kernels or filesystem images, you either need bitbake in your PATH\n \
36 or to source oe-init-build-env before running this script.\n\n \
37 Dynamic path inference can be avoided by passing a *.qemuboot.conf to\n \
38 runqemu, i.e. `runqemu /path/to/my-image-name.qemuboot.conf`\n\n %s" % message
42 logger
= logging
.getLogger('runqemu')
43 logger
.setLevel(logging
.INFO
)
45 # create console handler and set level to debug
46 ch
= logging
.StreamHandler()
47 ch
.setLevel(logging
.INFO
)
50 formatter
= logging
.Formatter('%(name)s - %(levelname)s - %(message)s')
53 ch
.setFormatter(formatter
)
60 logger
= create_logger()
64 Usage: you can run this script with any valid combination
65 of the following environment variables (in any order):
66 KERNEL - the kernel image file to use
67 ROOTFS - the rootfs image file or nfsroot directory to use
68 MACHINE - the machine name (optional, autodetected from KERNEL filename if unspecified)
69 Simplified QEMU command-line options can be passed with:
70 nographic - disable video console
71 serial - enable a serial console on /dev/ttyS0
72 slirp - enable user networking, no root privileges is required
73 kvm - enable KVM when running x86/x86_64 (VT-capable CPU required)
74 kvm-vhost - enable KVM with vhost when running x86/x86_64 (VT-capable CPU required)
75 publicvnc - enable a VNC server open to all hosts
77 [*/]ovmf* - OVMF firmware file or base name for booting with UEFI
78 tcpserial=<port> - specify tcp serial port number
79 biosdir=<dir> - specify custom bios dir
80 biosfilename=<filename> - specify bios filename
81 qemuparams=<xyz> - specify custom parameters to QEMU
82 bootparams=<xyz> - specify custom kernel parameters during boot
83 help, -h, --help: print this text
87 runqemu tmp/deploy/images/qemuarm
88 runqemu tmp/deploy/images/qemux86/<qemuboot.conf>
89 runqemu qemux86-64 core-image-sato ext4
90 runqemu qemux86-64 wic-image-minimal wic
91 runqemu path/to/bzImage-qemux86.bin path/to/nfsrootdir/ serial
92 runqemu qemux86 iso/hddimg/vmdk/qcow2/vdi/ramfs/cpio.gz...
93 runqemu qemux86 qemuparams="-m 256"
94 runqemu qemux86 bootparams="psplash=false"
95 runqemu path/to/<image>-<machine>.vmdk
96 runqemu path/to/<image>-<machine>.wic
100 """Check /dev/net/tun"""
101 dev_tun
= '/dev/net/tun'
102 if not os
.path
.exists(dev_tun
):
103 raise Exception("TUN control device %s is unavailable; you may need to enable TUN (e.g. sudo modprobe tun)" % dev_tun
)
105 if not os
.access(dev_tun
, os
.W_OK
):
106 raise Exception("TUN control device %s is not writable, please fix (e.g. sudo chmod 666 %s)" % (dev_tun
, dev_tun
))
108 def check_libgl(qemu_bin
):
109 cmd
= 'ldd %s' % qemu_bin
110 logger
.info('Running %s...' % cmd
)
111 need_gl
= subprocess
.Popen(cmd
, shell
=True, stdout
=subprocess
.PIPE
).stdout
.read().decode('utf-8')
112 if re
.search('libGLU', need_gl
):
113 # We can't run without a libGL.so
115 check_files
= (('/usr/lib/libGL.so', '/usr/lib/libGLU.so'), \
116 ('/usr/lib64/libGL.so', '/usr/lib64/libGLU.so'), \
117 ('/usr/lib/*-linux-gnu/libGL.so', '/usr/lib/*-linux-gnu/libGLU.so'))
119 for (f1
, f2
) in check_files
:
120 if re
.search('\*', f1
):
121 for g1
in glob
.glob(f1
):
124 if os
.path
.exists(g1
):
125 for g2
in glob
.glob(f2
):
126 if os
.path
.exists(g2
):
132 if os
.path
.exists(f1
) and os
.path
.exists(f2
):
136 logger
.error("You need libGL.so and libGLU.so to exist in your library path to run the QEMU emulator.")
137 logger
.error("Ubuntu package names are: libgl1-mesa-dev and libglu1-mesa-dev.")
138 logger
.error("Fedora package names are: mesa-libGL-devel mesa-libGLU-devel.")
139 raise Exception('%s requires libGLU, but not found' % qemu_bin
)
141 def get_first_file(cmds
):
142 """Return first file found in wildcard cmds"""
144 all_files
= glob
.glob(cmd
)
147 if not os
.path
.isdir(f
):
151 def check_free_port(host
, port
):
152 """ Check whether the port is free or not """
154 from contextlib
import closing
156 with
closing(socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)) as sock
:
157 if sock
.connect_ex((host
, port
)) == 0:
158 # Port is open, so not free
161 # Port is not open, so free
164 class BaseConfig(object):
166 # Vars can be merged with .qemuboot.conf, use a dict to manage them.
169 'DEPLOY_DIR_IMAGE': '',
170 'QB_KERNEL_ROOT': '/dev/vda',
174 self
.qemu_opt_script
= ''
176 self
.clean_nfs_dir
= False
179 # File name(s) of a OVMF firmware file or variable store,
180 # to be added with -drive if=pflash.
181 # Found in the same places as the rootfs, with or without one of
182 # these suffices: qcow2, bin.
183 # Setting one also adds "-vga std" because that is all that
187 self
.qbconfload
= False
189 self
.kernel_cmdline
= ''
190 self
.kernel_cmdline_script
= ''
194 self
.kvm_enabled
= False
195 self
.vhost_enabled
= False
196 self
.slirp_enabled
= False
197 self
.nfs_instance
= 0
198 self
.nfs_running
= False
199 self
.serialstdio
= False
200 self
.cleantap
= False
202 self
.audio_enabled
= False
203 self
.tcpserial_portnum
= ''
204 self
.custombiosdir
= ''
206 self
.lock_descriptor
= ''
208 self
.snapshot
= False
209 self
.fstypes
= ('ext2', 'ext3', 'ext4', 'jffs2', 'nfs', 'btrfs', 'cpio.gz', 'cpio', 'ramfs')
210 self
.vmtypes
= ('hddimg', 'hdddirect', 'wic', 'vmdk', 'qcow2', 'vdi', 'iso')
211 self
.network_device
= "-device e1000,netdev=net0,mac=@MAC@"
212 # Use different mac section for tap and slirp to avoid
213 # conflicts, e.g., when one is running with tap, the other is
214 # running with slirp.
215 # The last section is dynamic, which is for avoiding conflicts,
216 # when multiple qemus are running, e.g., when multiple tap or
217 # slirp qemus are running.
218 self
.mac_tap
= "52:54:00:12:34:"
219 self
.mac_slirp
= "52:54:00:12:35:"
221 def acquire_lock(self
):
222 logger
.info("Acquiring lockfile %s..." % self
.lock
)
224 self
.lock_descriptor
= open(self
.lock
, 'w')
225 fcntl
.flock(self
.lock_descriptor
, fcntl
.LOCK_EX|fcntl
.LOCK_NB
)
226 except Exception as e
:
227 logger
.info("Acquiring lockfile %s failed: %s" % (self
.lock
, e
))
228 if self
.lock_descriptor
:
229 self
.lock_descriptor
.close()
233 def release_lock(self
):
234 fcntl
.flock(self
.lock_descriptor
, fcntl
.LOCK_UN
)
235 self
.lock_descriptor
.close()
240 return self
.d
.get(key
)
244 def set(self
, key
, value
):
247 def is_deploy_dir_image(self
, p
):
249 if not re
.search('.qemuboot.conf$', '\n'.join(os
.listdir(p
)), re
.M
):
250 logger
.info("Can't find required *.qemuboot.conf in %s" % p
)
252 if not any(map(lambda name
: '-image-' in name
, os
.listdir(p
))):
253 logger
.info("Can't find *-image-* in %s" % p
)
259 def check_arg_fstype(self
, fst
):
260 """Check and set FSTYPE"""
261 if fst
not in self
.fstypes
+ self
.vmtypes
:
262 logger
.warn("Maybe unsupported FSTYPE: %s" % fst
)
263 if not self
.fstype
or self
.fstype
== fst
:
268 raise Exception("Conflicting: FSTYPE %s and %s" % (self
.fstype
, fst
))
270 def set_machine_deploy_dir(self
, machine
, deploy_dir_image
):
271 """Set MACHINE and DEPLOY_DIR_IMAGE"""
272 logger
.info('MACHINE: %s' % machine
)
273 self
.set("MACHINE", machine
)
274 logger
.info('DEPLOY_DIR_IMAGE: %s' % deploy_dir_image
)
275 self
.set("DEPLOY_DIR_IMAGE", deploy_dir_image
)
277 def check_arg_nfs(self
, p
):
281 m
= re
.match('(.*):(.*)', p
)
282 self
.nfs_server
= m
.group(1)
283 self
.nfs_dir
= m
.group(2)
285 self
.check_arg_fstype('nfs')
287 def check_arg_path(self
, p
):
289 - Check whether it is <image>.qemuboot.conf or contains <image>.qemuboot.conf
290 - Check whether is a kernel file
291 - Check whether is a image file
292 - Check whether it is a nfs dir
293 - Check whether it is a OVMF flash file
295 if p
.endswith('.qemuboot.conf'):
297 self
.qbconfload
= True
298 elif re
.search('\.bin$', p
) or re
.search('bzImage', p
) or \
299 re
.search('zImage', p
) or re
.search('vmlinux', p
) or \
300 re
.search('fitImage', p
) or re
.search('uImage', p
):
302 elif os
.path
.exists(p
) and (not os
.path
.isdir(p
)) and '-image-' in os
.path
.basename(p
):
304 # Check filename against self.fstypes can hanlde <file>.cpio.gz,
305 # otherwise, its type would be "gz", which is incorrect.
307 for t
in self
.fstypes
:
312 m
= re
.search('.*\.(.*)$', self
.rootfs
)
316 self
.check_arg_fstype(fst
)
317 qb
= re
.sub('\.' + fst
+ "$", '', self
.rootfs
)
318 qb
= '%s%s' % (re
.sub('\.rootfs$', '', qb
), '.qemuboot.conf')
319 if os
.path
.exists(qb
):
321 self
.qbconfload
= True
323 logger
.warn("%s doesn't exist" % qb
)
325 raise Exception("Can't find FSTYPE from: %s" % p
)
327 elif os
.path
.isdir(p
) or re
.search(':', p
) and re
.search('/', p
):
328 if self
.is_deploy_dir_image(p
):
329 logger
.info('DEPLOY_DIR_IMAGE: %s' % p
)
330 self
.set("DEPLOY_DIR_IMAGE", p
)
332 logger
.info("Assuming %s is an nfs rootfs" % p
)
333 self
.check_arg_nfs(p
)
334 elif os
.path
.basename(p
).startswith('ovmf'):
335 self
.ovmf_bios
.append(p
)
337 raise Exception("Unknown path arg %s" % p
)
339 def check_arg_machine(self
, arg
):
340 """Check whether it is a machine"""
341 if self
.get('MACHINE') and self
.get('MACHINE') != arg
or re
.search('/', arg
):
342 raise Exception("Unknown arg: %s" % arg
)
343 elif self
.get('MACHINE') == arg
:
345 logger
.info('Assuming MACHINE = %s' % arg
)
347 # if we're running under testimage, or similarly as a child
348 # of an existing bitbake invocation, we can't invoke bitbake
349 # to validate the MACHINE setting and must assume it's correct...
350 # FIXME: testimage.bbclass exports these two variables into env,
351 # are there other scenarios in which we need to support being
352 # invoked by bitbake?
353 deploy
= os
.environ
.get('DEPLOY_DIR_IMAGE')
354 bbchild
= deploy
and os
.environ
.get('OE_TMPDIR')
356 self
.set_machine_deploy_dir(arg
, deploy
)
358 # also check whether we're running under a sourced toolchain
360 if os
.environ
.get('OECORE_NATIVE_SYSROOT'):
361 self
.set("MACHINE", arg
)
364 cmd
= 'MACHINE=%s bitbake -e' % arg
365 logger
.info('Running %s...' % cmd
)
366 self
.bitbake_e
= subprocess
.Popen(cmd
, shell
=True, stdout
=subprocess
.PIPE
).stdout
.read().decode('utf-8')
367 # bitbake -e doesn't report invalid MACHINE as an error, so
368 # let's check DEPLOY_DIR_IMAGE to make sure that it is a valid
370 s
= re
.search('^DEPLOY_DIR_IMAGE="(.*)"', self
.bitbake_e
, re
.M
)
372 deploy_dir_image
= s
.group(1)
374 raise Exception("bitbake -e %s" % self
.bitbake_e
)
375 if self
.is_deploy_dir_image(deploy_dir_image
):
376 self
.set_machine_deploy_dir(arg
, deploy_dir_image
)
378 logger
.error("%s not a directory valid DEPLOY_DIR_IMAGE" % deploy_dir_image
)
379 self
.set("MACHINE", arg
)
381 def check_args(self
):
383 for arg
in sys
.argv
[1:]:
384 if arg
in self
.fstypes
+ self
.vmtypes
:
385 self
.check_arg_fstype(arg
)
386 elif arg
== 'nographic':
387 self
.qemu_opt_script
+= ' -nographic'
388 self
.kernel_cmdline_script
+= ' console=ttyS0'
389 elif arg
== 'serial':
390 self
.kernel_cmdline_script
+= ' console=ttyS0'
391 self
.serialstdio
= True
393 logger
.info("Enabling audio in qemu")
394 logger
.info("Please install sound drivers in linux host")
395 self
.audio_enabled
= True
397 self
.kvm_enabled
= True
398 elif arg
== 'kvm-vhost':
399 self
.vhost_enabled
= True
401 self
.slirp_enabled
= True
402 elif arg
== 'snapshot':
404 elif arg
== 'publicvnc':
405 self
.qemu_opt_script
+= ' -vnc :0'
406 elif arg
.startswith('tcpserial='):
407 self
.tcpserial_portnum
= arg
[len('tcpserial='):]
408 elif arg
.startswith('biosdir='):
409 self
.custombiosdir
= arg
[len('biosdir='):]
410 elif arg
.startswith('biosfilename='):
411 self
.qemu_opt_script
+= ' -bios %s' % arg
[len('biosfilename='):]
412 elif arg
.startswith('qemuparams='):
413 self
.qemu_opt_script
+= ' %s' % arg
[len('qemuparams='):]
414 elif arg
.startswith('bootparams='):
415 self
.bootparams
= arg
[len('bootparams='):]
416 elif os
.path
.exists(arg
) or (re
.search(':', arg
) and re
.search('/', arg
)):
417 self
.check_arg_path(os
.path
.abspath(arg
))
418 elif re
.search(r
'-image-|-image$', arg
):
421 elif arg
.startswith('ovmf'):
422 self
.ovmf_bios
.append(arg
)
424 # At last, assume is it the MACHINE
425 if (not unknown_arg
) or unknown_arg
== arg
:
428 raise Exception("Can't handle two unknown args: %s %s" % (unknown_arg
, arg
))
429 # Check to make sure it is a valid machine
431 if self
.get('MACHINE') == unknown_arg
:
433 if not self
.get('DEPLOY_DIR_IMAGE'):
434 # Trying to get DEPLOY_DIR_IMAGE from env.
435 p
= os
.getenv('DEPLOY_DIR_IMAGE')
436 if p
and self
.is_deploy_dir_image(p
):
437 machine
= os
.path
.basename(p
)
438 if unknown_arg
== machine
:
439 self
.set_machine_deploy_dir(machine
, p
)
442 logger
.info('DEPLOY_DIR_IMAGE: %s' % p
)
443 self
.set("DEPLOY_DIR_IMAGE", p
)
444 self
.check_arg_machine(unknown_arg
)
446 if not (self
.get('MACHINE') or self
.get('DEPLOY_DIR_IMAGE')):
447 self
.load_bitbake_env()
448 s
= re
.search('^DEPLOY_DIR_IMAGE="(.*)"', self
.bitbake_e
, re
.M
)
450 self
.set("DEPLOY_DIR_IMAGE", s
.group(1))
453 """Check kvm and kvm-host"""
454 if not (self
.kvm_enabled
or self
.vhost_enabled
):
455 self
.qemu_opt_script
+= ' %s %s' % (self
.get('QB_MACHINE'), self
.get('QB_CPU'))
458 if not self
.get('QB_CPU_KVM'):
459 raise Exception("QB_CPU_KVM is NULL, this board doesn't support kvm")
461 self
.qemu_opt_script
+= ' %s %s' % (self
.get('QB_MACHINE'), self
.get('QB_CPU_KVM'))
462 yocto_kvm_wiki
= "https://wiki.yoctoproject.org/wiki/How_to_enable_KVM_for_Poky_qemu"
463 yocto_paravirt_kvm_wiki
= "https://wiki.yoctoproject.org/wiki/Running_an_x86_Yocto_Linux_image_under_QEMU_KVM"
465 dev_vhost
= '/dev/vhost-net'
466 with
open('/proc/cpuinfo', 'r') as f
:
467 kvm_cap
= re
.search('vmx|svm', "".join(f
.readlines()))
469 logger
.error("You are trying to enable KVM on a cpu without VT support.")
470 logger
.error("Remove kvm from the command-line, or refer:")
471 raise Exception(yocto_kvm_wiki
)
473 if not os
.path
.exists(dev_kvm
):
474 logger
.error("Missing KVM device. Have you inserted kvm modules?")
475 logger
.error("For further help see:")
476 raise Exception(yocto_kvm_wiki
)
478 if os
.access(dev_kvm
, os
.W_OK|os
.R_OK
):
479 self
.qemu_opt_script
+= ' -enable-kvm'
481 logger
.error("You have no read or write permission on /dev/kvm.")
482 logger
.error("Please change the ownership of this file as described at:")
483 raise Exception(yocto_kvm_wiki
)
485 if self
.vhost_enabled
:
486 if not os
.path
.exists(dev_vhost
):
487 logger
.error("Missing virtio net device. Have you inserted vhost-net module?")
488 logger
.error("For further help see:")
489 raise Exception(yocto_paravirt_kvm_wiki
)
491 if not os
.access(dev_kvm
, os
.W_OK|os
.R_OK
):
492 logger
.error("You have no read or write permission on /dev/vhost-net.")
493 logger
.error("Please change the ownership of this file as described at:")
494 raise Exception(yocto_kvm_wiki
)
496 def check_fstype(self
):
497 """Check and setup FSTYPE"""
499 fstype
= self
.get('QB_DEFAULT_FSTYPE')
503 raise Exception("FSTYPE is NULL!")
505 def check_rootfs(self
):
506 """Check and set rootfs"""
508 if self
.fstype
== 'nfs' or self
.fstype
== "none":
511 if self
.rootfs
and not os
.path
.exists(self
.rootfs
):
513 self
.rootfs
= "%s/%s-%s.%s" % (self
.get('DEPLOY_DIR_IMAGE'),
514 self
.rootfs
, self
.get('MACHINE'),
516 elif not self
.rootfs
:
517 cmd_name
= '%s/%s*.%s' % (self
.get('DEPLOY_DIR_IMAGE'), self
.get('IMAGE_NAME'), self
.fstype
)
518 cmd_link
= '%s/%s*.%s' % (self
.get('DEPLOY_DIR_IMAGE'), self
.get('IMAGE_LINK_NAME'), self
.fstype
)
519 cmds
= (cmd_name
, cmd_link
)
520 self
.rootfs
= get_first_file(cmds
)
522 raise Exception("Failed to find rootfs: %s or %s" % cmds
)
524 if not os
.path
.exists(self
.rootfs
):
525 raise Exception("Can't find rootfs: %s" % self
.rootfs
)
527 def check_ovmf(self
):
528 """Check and set full path for OVMF firmware and variable file(s)."""
530 for index
, ovmf
in enumerate(self
.ovmf_bios
):
531 if os
.path
.exists(ovmf
):
533 for suffix
in ('qcow2', 'bin'):
534 path
= '%s/%s.%s' % (self
.get('DEPLOY_DIR_IMAGE'), ovmf
, suffix
)
535 if os
.path
.exists(path
):
536 self
.ovmf_bios
[index
] = path
539 raise Exception("Can't find OVMF firmware: %s" % ovmf
)
541 def check_kernel(self
):
542 """Check and set kernel, dtb"""
543 # The vm image doesn't need a kernel
544 if self
.fstype
in self
.vmtypes
:
547 # QB_DEFAULT_KERNEL is always a full file path
548 kernel_name
= os
.path
.basename(self
.get('QB_DEFAULT_KERNEL'))
550 # The user didn't want a kernel to be loaded
551 if kernel_name
== "none":
554 deploy_dir_image
= self
.get('DEPLOY_DIR_IMAGE')
556 kernel_match_name
= "%s/%s" % (deploy_dir_image
, kernel_name
)
557 kernel_match_link
= "%s/%s" % (deploy_dir_image
, self
.get('KERNEL_IMAGETYPE'))
558 kernel_startswith
= "%s/%s*" % (deploy_dir_image
, self
.get('KERNEL_IMAGETYPE'))
559 cmds
= (kernel_match_name
, kernel_match_link
, kernel_startswith
)
560 self
.kernel
= get_first_file(cmds
)
562 raise Exception('KERNEL not found: %s, %s or %s' % cmds
)
564 if not os
.path
.exists(self
.kernel
):
565 raise Exception("KERNEL %s not found" % self
.kernel
)
567 dtb
= self
.get('QB_DTB')
569 cmd_match
= "%s/%s" % (deploy_dir_image
, dtb
)
570 cmd_startswith
= "%s/%s*" % (deploy_dir_image
, dtb
)
571 cmd_wild
= "%s/*.dtb" % deploy_dir_image
572 cmds
= (cmd_match
, cmd_startswith
, cmd_wild
)
573 self
.dtb
= get_first_file(cmds
)
574 if not os
.path
.exists(self
.dtb
):
575 raise Exception('DTB not found: %s, %s or %s' % cmds
)
577 def check_biosdir(self
):
578 """Check custombiosdir"""
579 if not self
.custombiosdir
:
583 biosdir_native
= "%s/%s" % (self
.get('STAGING_DIR_NATIVE'), self
.custombiosdir
)
584 biosdir_host
= "%s/%s" % (self
.get('STAGING_DIR_HOST'), self
.custombiosdir
)
585 for i
in (self
.custombiosdir
, biosdir_native
, biosdir_host
):
591 logger
.info("Assuming biosdir is: %s" % biosdir
)
592 self
.qemu_opt_script
+= ' -L %s' % biosdir
594 logger
.error("Custom BIOS directory not found. Tried: %s, %s, and %s" % (self
.custombiosdir
, biosdir_native
, biosdir_host
))
595 raise Exception("Invalid custombiosdir: %s" % self
.custombiosdir
)
598 s
= re
.search('-m +([0-9]+)', self
.qemu_opt_script
)
600 self
.set('QB_MEM', '-m %s' % s
.group(1))
601 elif not self
.get('QB_MEM'):
602 logger
.info('QB_MEM is not set, use 512M by default')
603 self
.set('QB_MEM', '-m 512')
605 self
.kernel_cmdline_script
+= ' mem=%s' % self
.get('QB_MEM').replace('-m','').strip() + 'M'
606 self
.qemu_opt_script
+= ' %s' % self
.get('QB_MEM')
608 def check_tcpserial(self
):
609 if self
.tcpserial_portnum
:
610 if self
.get('QB_TCPSERIAL_OPT'):
611 self
.qemu_opt_script
+= ' ' + self
.get('QB_TCPSERIAL_OPT').replace('@PORT@', self
.tcpserial_portnum
)
613 self
.qemu_opt_script
+= ' -serial tcp:127.0.0.1:%s' % self
.tcpserial_portnum
615 def check_and_set(self
):
616 """Check configs sanity and set when needed"""
617 self
.validate_paths()
618 if not self
.slirp_enabled
:
621 if self
.audio_enabled
:
622 if not self
.get('QB_AUDIO_DRV'):
623 raise Exception("QB_AUDIO_DRV is NULL, this board doesn't support audio")
624 if not self
.get('QB_AUDIO_OPT'):
625 logger
.warn('QB_AUDIO_OPT is NULL, you may need define it to make audio work')
627 self
.qemu_opt_script
+= ' %s' % self
.get('QB_AUDIO_OPT')
628 os
.putenv('QEMU_AUDIO_DRV', self
.get('QB_AUDIO_DRV'))
630 os
.putenv('QEMU_AUDIO_DRV', 'none')
639 self
.check_tcpserial()
641 def read_qemuboot(self
):
642 if not self
.qemuboot
:
643 if self
.get('DEPLOY_DIR_IMAGE'):
644 deploy_dir_image
= self
.get('DEPLOY_DIR_IMAGE')
645 elif os
.getenv('DEPLOY_DIR_IMAGE'):
646 deploy_dir_image
= os
.getenv('DEPLOY_DIR_IMAGE')
648 logger
.info("Can't find qemuboot conf file, DEPLOY_DIR_IMAGE is NULL!")
651 if self
.rootfs
and not os
.path
.exists(self
.rootfs
):
653 machine
= self
.get('MACHINE')
655 machine
= os
.path
.basename(deploy_dir_image
)
656 self
.qemuboot
= "%s/%s-%s.qemuboot.conf" % (deploy_dir_image
,
657 self
.rootfs
, machine
)
659 cmd
= 'ls -t %s/*.qemuboot.conf' % deploy_dir_image
660 logger
.info('Running %s...' % cmd
)
661 qbs
= subprocess
.Popen(cmd
, shell
=True, stdout
=subprocess
.PIPE
).stdout
.read().decode('utf-8')
663 for qb
in qbs
.split():
664 # Don't use initramfs when other choices unless fstype is ramfs
665 if '-initramfs-' in os
.path
.basename(qb
) and self
.fstype
!= 'cpio.gz':
669 if not self
.qemuboot
:
670 # Use the first one when no choice
671 self
.qemuboot
= qbs
.split()[0]
672 self
.qbconfload
= True
674 if not self
.qemuboot
:
675 # If we haven't found a .qemuboot.conf at this point it probably
676 # doesn't exist, continue without
679 if not os
.path
.exists(self
.qemuboot
):
680 raise Exception("Failed to find <image>.qemuboot.conf = %s (wrong image name or BSP does not support running under qemu?)." % self
.qemuboot
)
682 logger
.info('CONFFILE: %s' % self
.qemuboot
)
684 cf
= configparser
.ConfigParser()
685 cf
.read(self
.qemuboot
)
686 for k
, v
in cf
.items('config_bsp'):
690 def validate_paths(self
):
691 """Ensure all relevant path variables are set"""
692 # When we're started with a *.qemuboot.conf arg assume that image
693 # artefacts are relative to that file, rather than in whatever
694 # directory DEPLOY_DIR_IMAGE in the conf file points to.
696 imgdir
= os
.path
.dirname(self
.qemuboot
)
697 if imgdir
!= self
.get('DEPLOY_DIR_IMAGE'):
698 logger
.info('Setting DEPLOY_DIR_IMAGE to folder containing %s (%s)' % (self
.qemuboot
, imgdir
))
699 self
.set('DEPLOY_DIR_IMAGE', imgdir
)
701 # If the STAGING_*_NATIVE directories from the config file don't exist
702 # and we're in a sourced OE build directory try to extract the paths
704 havenative
= os
.path
.exists(self
.get('STAGING_DIR_NATIVE')) and \
705 os
.path
.exists(self
.get('STAGING_BINDIR_NATIVE'))
708 if not self
.bitbake_e
:
709 self
.load_bitbake_env()
712 native_vars
= ['STAGING_DIR_NATIVE', 'STAGING_BINDIR_NATIVE']
713 for nv
in native_vars
:
714 s
= re
.search('^%s="(.*)"' % nv
, self
.bitbake_e
, re
.M
)
715 if s
and s
.group(1) != self
.get(nv
):
716 logger
.info('Overriding conf file setting of %s to %s from Bitbake environment' % (nv
, s
.group(1)))
717 self
.set(nv
, s
.group(1))
719 # when we're invoked from a running bitbake instance we won't
720 # be able to call `bitbake -e`, then try:
721 # - get OE_TMPDIR from environment and guess paths based on it
722 # - get OECORE_NATIVE_SYSROOT from environment (for sdk)
723 tmpdir
= os
.environ
.get('OE_TMPDIR', None)
724 oecore_native_sysroot
= os
.environ
.get('OECORE_NATIVE_SYSROOT', None)
726 logger
.info('Setting STAGING_DIR_NATIVE and STAGING_BINDIR_NATIVE relative to OE_TMPDIR (%s)' % tmpdir
)
727 hostos
, _
, _
, _
, machine
= os
.uname()
728 buildsys
= '%s-%s' % (machine
, hostos
.lower())
729 staging_dir_native
= '%s/sysroots/%s' % (tmpdir
, buildsys
)
730 self
.set('STAGING_DIR_NATIVE', staging_dir_native
)
731 elif oecore_native_sysroot
:
732 logger
.info('Setting STAGING_DIR_NATIVE to OECORE_NATIVE_SYSROOT (%s)' % oecore_native_sysroot
)
733 self
.set('STAGING_DIR_NATIVE', oecore_native_sysroot
)
734 if self
.get('STAGING_DIR_NATIVE'):
735 # we have to assume that STAGING_BINDIR_NATIVE is at usr/bin
736 staging_bindir_native
= '%s/usr/bin' % self
.get('STAGING_DIR_NATIVE')
737 logger
.info('Setting STAGING_BINDIR_NATIVE to %s' % staging_bindir_native
)
738 self
.set('STAGING_BINDIR_NATIVE', '%s/usr/bin' % self
.get('STAGING_DIR_NATIVE'))
740 def print_config(self
):
741 logger
.info('Continuing with the following parameters:\n')
742 if not self
.fstype
in self
.vmtypes
:
743 print('KERNEL: [%s]' % self
.kernel
)
745 print('DTB: [%s]' % self
.dtb
)
746 print('MACHINE: [%s]' % self
.get('MACHINE'))
747 print('FSTYPE: [%s]' % self
.fstype
)
748 if self
.fstype
== 'nfs':
749 print('NFS_DIR: [%s]' % self
.nfs_dir
)
751 print('ROOTFS: [%s]' % self
.rootfs
)
753 print('OVMF: %s' % self
.ovmf_bios
)
754 print('CONFFILE: [%s]' % self
.qemuboot
)
758 if not self
.nfs_server
:
759 if self
.slirp_enabled
:
760 self
.nfs_server
= '10.0.2.2'
762 self
.nfs_server
= '192.168.7.1'
764 # Figure out a new nfs_instance to allow multiple qemus running.
765 # CentOS 7.1's ps doesn't print full command line without "ww"
766 # when invoke by subprocess.Popen().
768 ps
= subprocess
.Popen(cmd
, shell
=True, stdout
=subprocess
.PIPE
).stdout
.read().decode('utf-8')
769 pattern
= '/bin/unfsd .* -i .*\.pid -e .*/exports([0-9]+) '
770 all_instances
= re
.findall(pattern
, ps
, re
.M
)
772 all_instances
.sort(key
=int)
773 self
.nfs_instance
= int(all_instances
.pop()) + 1
775 mountd_rpcport
= 21111 + self
.nfs_instance
776 nfsd_rpcport
= 11111 + self
.nfs_instance
777 nfsd_port
= 3049 + 2 * self
.nfs_instance
778 mountd_port
= 3048 + 2 * self
.nfs_instance
780 # Export vars for runqemu-export-rootfs
782 'NFS_INSTANCE': self
.nfs_instance
,
783 'MOUNTD_RPCPORT': mountd_rpcport
,
784 'NFSD_RPCPORT': nfsd_rpcport
,
785 'NFSD_PORT': nfsd_port
,
786 'MOUNTD_PORT': mountd_port
,
788 for k
, v
in export_dict
.items():
789 # Use '%s' since they are integers
790 os
.putenv(k
, '%s' % v
)
792 self
.unfs_opts
="nfsvers=3,port=%s,mountprog=%s,nfsprog=%s,udp,mountport=%s" % (nfsd_port
, mountd_rpcport
, nfsd_rpcport
, mountd_port
)
794 # Extract .tar.bz2 or .tar.bz if no self.nfs_dir
796 src_prefix
= '%s/%s' % (self
.get('DEPLOY_DIR_IMAGE'), self
.get('IMAGE_LINK_NAME'))
797 dest
= "%s-nfsroot" % src_prefix
798 if os
.path
.exists('%s.pseudo_state' % dest
):
799 logger
.info('Use %s as NFS_DIR' % dest
)
803 src1
= '%s.tar.bz2' % src_prefix
804 src2
= '%s.tar.gz' % src_prefix
805 if os
.path
.exists(src1
):
807 elif os
.path
.exists(src2
):
810 raise Exception("No NFS_DIR is set, and can't find %s or %s to extract" % (src1
, src2
))
811 logger
.info('NFS_DIR not found, extracting %s to %s' % (src
, dest
))
812 cmd
= 'runqemu-extract-sdk %s %s' % (src
, dest
)
813 logger
.info('Running %s...' % cmd
)
814 if subprocess
.call(cmd
, shell
=True) != 0:
815 raise Exception('Failed to run %s' % cmd
)
816 self
.clean_nfs_dir
= True
819 # Start the userspace NFS server
820 cmd
= 'runqemu-export-rootfs start %s' % self
.nfs_dir
821 logger
.info('Running %s...' % cmd
)
822 if subprocess
.call(cmd
, shell
=True) != 0:
823 raise Exception('Failed to run %s' % cmd
)
825 self
.nfs_running
= True
827 def setup_slirp(self
):
828 """Setup user networking"""
830 if self
.fstype
== 'nfs':
832 self
.kernel_cmdline_script
+= ' ip=dhcp'
834 hostfwd
= ",hostfwd=tcp::2222-:22,hostfwd=tcp::2323-:23"
835 qb_slirp_opt_default
= "-netdev user,id=net0%s" % hostfwd
836 qb_slirp_opt
= self
.get('QB_SLIRP_OPT') or qb_slirp_opt_default
837 # Figure out the port
838 ports
= re
.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt
)
839 ports
= [int(i
) for i
in ports
]
841 # Find a free port to avoid conflicts
844 while not check_free_port('localhost', p_new
):
847 while p_new
in ports
:
852 qb_slirp_opt
= re
.sub(':%s-' % p
, ':%s-' % p_new
, qb_slirp_opt
)
853 logger
.info("Port forward changed: %s -> %s" % (p
, p_new
))
854 mac
= "%s%02x" % (self
.mac_slirp
, mac
)
855 self
.set('NETWORK_CMD', '%s %s' % (self
.network_device
.replace('@MAC@', mac
), qb_slirp_opt
))
856 # Print out port foward
857 hostfwd
= re
.findall('(hostfwd=[^,]*)', qb_slirp_opt
)
859 logger
.info('Port forward: %s' % ' '.join(hostfwd
))
864 # This file is created when runqemu-gen-tapdevs creates a bank of tap
865 # devices, indicating that the user should not bring up new ones using
867 nosudo_flag
= '/etc/runqemu-nosudo'
868 self
.qemuifup
= shutil
.which('runqemu-ifup')
869 self
.qemuifdown
= shutil
.which('runqemu-ifdown')
870 ip
= shutil
.which('ip')
871 lockdir
= "/tmp/qemu-tap-locks"
873 if not (self
.qemuifup
and self
.qemuifdown
and ip
):
874 raise OEPathError("runqemu-ifup, runqemu-ifdown or ip not found")
876 if not os
.path
.exists(lockdir
):
877 # There might be a race issue when multi runqemu processess are
878 # running at the same time.
881 except FileExistsError
:
885 logger
.info('Running %s...' % cmd
)
886 ip_link
= subprocess
.Popen(cmd
, shell
=True, stdout
=subprocess
.PIPE
).stdout
.read().decode('utf-8')
887 # Matches line like: 6: tap0: <foo>
888 possibles
= re
.findall('^[1-9]+: +(tap[0-9]+): <.*', ip_link
, re
.M
)
891 lockfile
= os
.path
.join(lockdir
, p
)
892 if os
.path
.exists('%s.skip' % lockfile
):
893 logger
.info('Found %s.skip, skipping %s' % (lockfile
, p
))
895 self
.lock
= lockfile
+ '.lock'
896 if self
.acquire_lock():
898 logger
.info("Using preconfigured tap device %s" % tap
)
899 logger
.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile
, tap
))
903 if os
.path
.exists(nosudo_flag
):
904 logger
.error("Error: There are no available tap devices to use for networking,")
905 logger
.error("and I see %s exists, so I am not going to try creating" % nosudo_flag
)
906 raise Exception("a new one with sudo.")
910 logger
.info("Setting up tap interface under sudo")
911 cmd
= 'sudo %s %s %s %s' % (self
.qemuifup
, uid
, gid
, self
.get('STAGING_DIR_NATIVE'))
912 tap
= subprocess
.Popen(cmd
, shell
=True, stdout
=subprocess
.PIPE
).stdout
.read().decode('utf-8').rstrip('\n')
913 lockfile
= os
.path
.join(lockdir
, tap
)
914 self
.lock
= lockfile
+ '.lock'
917 logger
.info('Created tap: %s' % tap
)
920 logger
.error("Failed to setup tap device. Run runqemu-gen-tapdevs to manually create.")
923 tapnum
= int(tap
[3:])
924 gateway
= tapnum
* 2 + 1
926 if self
.fstype
== 'nfs':
928 self
.kernel_cmdline_script
+= " ip=192.168.7.%s::192.168.7.%s:255.255.255.0" % (client
, gateway
)
929 mac
= "%s%02x" % (self
.mac_tap
, client
)
930 qb_tap_opt
= self
.get('QB_TAP_OPT')
932 qemu_tap_opt
= qb_tap_opt
.replace('@TAP@', tap
)
934 qemu_tap_opt
= "-netdev tap,id=net0,ifname=%s,script=no,downscript=no" % (self
.tap
)
936 if self
.vhost_enabled
:
937 qemu_tap_opt
+= ',vhost=on'
939 self
.set('NETWORK_CMD', '%s %s' % (self
.network_device
.replace('@MAC@', mac
), qemu_tap_opt
))
941 def setup_network(self
):
942 if self
.get('QB_NET') == 'none':
945 self
.saved_stty
= subprocess
.Popen(cmd
, shell
=True, stdout
=subprocess
.PIPE
).stdout
.read().decode('utf-8')
946 self
.network_device
= self
.get('QB_NETWORK_DEVICE') or self
.network_device
947 if self
.slirp_enabled
:
952 def setup_rootfs(self
):
953 if self
.get('QB_ROOTFS') == 'none':
955 rootfs_format
= self
.fstype
if self
.fstype
in ('vmdk', 'qcow2', 'vdi') else 'raw'
957 qb_rootfs_opt
= self
.get('QB_ROOTFS_OPT')
959 self
.rootfs_options
= qb_rootfs_opt
.replace('@ROOTFS@', self
.rootfs
)
961 self
.rootfs_options
= '-drive file=%s,if=virtio,format=%s' % (self
.rootfs
, rootfs_format
)
963 if self
.fstype
in ('cpio.gz', 'cpio'):
964 self
.kernel_cmdline
= 'root=/dev/ram0 rw debugshell'
965 self
.rootfs_options
= '-initrd %s' % self
.rootfs
967 if self
.fstype
in self
.vmtypes
:
968 if self
.fstype
== 'iso':
969 vm_drive
= '-cdrom %s' % self
.rootfs
971 cmd1
= "grep -q 'root=/dev/sd' %s" % self
.rootfs
972 cmd2
= "grep -q 'root=/dev/hd' %s" % self
.rootfs
973 if subprocess
.call(cmd1
, shell
=True) == 0:
974 logger
.info('Using scsi drive')
975 vm_drive
= '-drive if=none,id=hd,file=%s,format=%s -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd' \
976 % (self
.rootfs
, rootfs_format
)
977 elif subprocess
.call(cmd2
, shell
=True) == 0:
978 logger
.info('Using ide drive')
979 vm_drive
= "%s,format=%s" % (self
.rootfs
, rootfs_format
)
981 logger
.warn("Can't detect drive type %s" % self
.rootfs
)
982 logger
.warn('Trying to use virtio block drive')
983 vm_drive
= '-drive if=virtio,file=%s,format=%s' % (self
.rootfs
, rootfs_format
)
984 self
.rootfs_options
= '%s -no-reboot' % vm_drive
985 self
.kernel_cmdline
= 'root=%s rw highres=off' % (self
.get('QB_KERNEL_ROOT'))
987 if self
.fstype
== 'nfs':
988 self
.rootfs_options
= ''
989 k_root
= '/dev/nfs nfsroot=%s:%s,%s' % (self
.nfs_server
, self
.nfs_dir
, self
.unfs_opts
)
990 self
.kernel_cmdline
= 'root=%s rw highres=off' % k_root
992 if self
.fstype
== 'none':
993 self
.rootfs_options
= ''
995 self
.set('ROOTFS_OPTIONS', self
.rootfs_options
)
997 def guess_qb_system(self
):
998 """attempt to determine the appropriate qemu-system binary"""
999 mach
= self
.get('MACHINE')
1001 search
= '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*'
1003 match
= re
.match(search
, self
.rootfs
)
1005 mach
= match
.group(1)
1007 match
= re
.match(search
, self
.kernel
)
1009 mach
= match
.group(1)
1014 if mach
== 'qemuarm':
1016 elif mach
== 'qemuarm64':
1018 elif mach
== 'qemux86':
1020 elif mach
== 'qemux86-64':
1022 elif mach
== 'qemuppc':
1024 elif mach
== 'qemumips':
1026 elif mach
== 'qemumips64':
1028 elif mach
== 'qemumipsel':
1030 elif mach
== 'qemumips64el':
1033 return 'qemu-system-%s' % qbsys
1035 def setup_final(self
):
1036 qemu_system
= self
.get('QB_SYSTEM_NAME')
1038 qemu_system
= self
.guess_qb_system()
1040 raise Exception("Failed to boot, QB_SYSTEM_NAME is NULL!")
1042 qemu_bin
= '%s/%s' % (self
.get('STAGING_BINDIR_NATIVE'), qemu_system
)
1044 # It is possible to have qemu-native in ASSUME_PROVIDED, and it won't
1045 # find QEMU in sysroot, it needs to use host's qemu.
1046 if not os
.path
.exists(qemu_bin
):
1047 logger
.info("QEMU binary not found in %s, trying host's QEMU" % qemu_bin
)
1048 for path
in (os
.environ
['PATH'] or '').split(':'):
1049 qemu_bin_tmp
= os
.path
.join(path
, qemu_system
)
1050 logger
.info("Trying: %s" % qemu_bin_tmp
)
1051 if os
.path
.exists(qemu_bin_tmp
):
1052 qemu_bin
= qemu_bin_tmp
1053 if not os
.path
.isabs(qemu_bin
):
1054 qemu_bin
= os
.path
.abspath(qemu_bin
)
1055 logger
.info("Using host's QEMU: %s" % qemu_bin
)
1058 if not os
.access(qemu_bin
, os
.X_OK
):
1059 raise OEPathError("No QEMU binary '%s' could be found" % qemu_bin
)
1061 check_libgl(qemu_bin
)
1063 self
.qemu_opt
= "%s %s %s %s" % (qemu_bin
, self
.get('NETWORK_CMD'), self
.get('ROOTFS_OPTIONS'), self
.get('QB_OPT_APPEND'))
1065 for ovmf
in self
.ovmf_bios
:
1066 format
= ovmf
.rsplit('.', 1)[-1]
1067 self
.qemu_opt
+= ' -drive if=pflash,format=%s,file=%s' % (format
, ovmf
)
1069 # OVMF only supports normal VGA, i.e. we need to override a -vga vmware
1070 # that gets added for example for normal qemux86.
1071 self
.qemu_opt
+= ' -vga std'
1073 self
.qemu_opt
+= ' ' + self
.qemu_opt_script
1076 self
.qemu_opt
+= " -snapshot"
1078 if self
.serialstdio
:
1079 logger
.info("Interrupt character is '^]'")
1080 cmd
= "stty intr ^]"
1081 subprocess
.call(cmd
, shell
=True)
1084 if not re
.search("-nographic", self
.qemu_opt
):
1085 first_serial
= "-serial mon:vc"
1086 # We always want a ttyS1. Since qemu by default adds a serial
1087 # port when nodefaults is not specified, it seems that all that
1088 # would be needed is to make sure a "-serial" is there. However,
1089 # it appears that when "-serial" is specified, it ignores the
1090 # default serial port that is normally added. So here we make
1091 # sure to add two -serial if there are none. And only one if
1092 # there is one -serial already.
1093 serial_num
= len(re
.findall("-serial", self
.qemu_opt
))
1095 self
.qemu_opt
+= " %s %s" % (first_serial
, self
.get("QB_SERIAL_OPT"))
1096 elif serial_num
== 1:
1097 self
.qemu_opt
+= " %s" % self
.get("QB_SERIAL_OPT")
1099 # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES),
1100 # if not serial or serialtcp options was specified only ttyS0 is created
1101 # and sysvinit shows an error trying to enable ttyS1:
1102 # INIT: Id "S1" respawning too fast: disabled for 5 minutes
1103 serial_num
= len(re
.findall("-serial", self
.qemu_opt
))
1105 if re
.search("-nographic", self
.qemu_opt
):
1106 self
.qemu_opt
+= " -serial mon:stdio -serial null"
1108 self
.qemu_opt
+= " -serial mon:vc -serial null"
1110 def start_qemu(self
):
1112 kernel_opts
= "-kernel %s -append '%s %s %s %s'" % (self
.kernel
, self
.kernel_cmdline
,
1113 self
.kernel_cmdline_script
, self
.get('QB_KERNEL_CMDLINE_APPEND'),
1116 kernel_opts
+= " -dtb %s" % self
.dtb
1119 cmd
= "%s %s" % (self
.qemu_opt
, kernel_opts
)
1120 logger
.info('Running %s' % cmd
)
1121 if subprocess
.call(cmd
, shell
=True) != 0:
1122 raise Exception('Failed to run %s' % cmd
)
1126 cmd
= 'sudo %s %s %s' % (self
.qemuifdown
, self
.tap
, self
.get('STAGING_DIR_NATIVE'))
1127 logger
.info('Running %s' % cmd
)
1128 subprocess
.call(cmd
, shell
=True)
1129 if self
.lock_descriptor
:
1130 logger
.info("Releasing lockfile for tap device '%s'" % self
.tap
)
1133 if self
.nfs_running
:
1134 logger
.info("Shutting down the userspace NFS server...")
1135 cmd
= "runqemu-export-rootfs stop %s" % self
.nfs_dir
1136 logger
.info('Running %s' % cmd
)
1137 subprocess
.call(cmd
, shell
=True)
1140 cmd
= "stty %s" % self
.saved_stty
1141 subprocess
.call(cmd
, shell
=True)
1143 if self
.clean_nfs_dir
:
1144 logger
.info('Removing %s' % self
.nfs_dir
)
1145 shutil
.rmtree(self
.nfs_dir
)
1146 shutil
.rmtree('%s.pseudo_state' % self
.nfs_dir
)
1148 def load_bitbake_env(self
, mach
=None):
1152 bitbake
= shutil
.which('bitbake')
1157 mach
= self
.get('MACHINE')
1160 cmd
= 'MACHINE=%s bitbake -e' % mach
1164 logger
.info('Running %s...' % cmd
)
1166 self
.bitbake_e
= subprocess
.check_output(cmd
, shell
=True).decode('utf-8')
1167 except subprocess
.CalledProcessError
as err
:
1169 logger
.warn("Couldn't run 'bitbake -e' to gather environment information:\n%s" % err
.output
.decode('utf-8'))
1172 if len(sys
.argv
) == 1 or "help" in sys
.argv
or \
1173 '-h' in sys
.argv
or '--help' in sys
.argv
:
1176 config
= BaseConfig()
1179 except Exception as esc
:
1181 logger
.error("Try 'runqemu help' on how to use it")
1183 config
.read_qemuboot()
1184 config
.check_and_set()
1185 config
.print_config()
1187 config
.setup_network()
1188 config
.setup_rootfs()
1189 config
.setup_final()
1195 if __name__
== "__main__":
1198 except OEPathError
as err
:
1200 logger
.error(err
.message
)
1201 except Exception as esc
:
1204 traceback
.print_exc()