summaryrefslogtreecommitdiffstats
path: root/scripts/runqemu
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/runqemu')
-rwxr-xr-xscripts/runqemu664
1 files changed, 443 insertions, 221 deletions
diff --git a/scripts/runqemu b/scripts/runqemu
index 532f2e338d..3d77046972 100755
--- a/scripts/runqemu
+++ b/scripts/runqemu
@@ -18,6 +18,7 @@ import shutil
18import glob 18import glob
19import configparser 19import configparser
20import signal 20import signal
21import time
21 22
22class RunQemuError(Exception): 23class RunQemuError(Exception):
23 """Custom exception to raise on known errors.""" 24 """Custom exception to raise on known errors."""
@@ -65,20 +66,25 @@ of the following environment variables (in any order):
65 MACHINE - the machine name (optional, autodetected from KERNEL filename if unspecified) 66 MACHINE - the machine name (optional, autodetected from KERNEL filename if unspecified)
66 Simplified QEMU command-line options can be passed with: 67 Simplified QEMU command-line options can be passed with:
67 nographic - disable video console 68 nographic - disable video console
69 nonetwork - disable network connectivity
68 novga - Disable VGA emulation completely 70 novga - Disable VGA emulation completely
69 sdl - choose the SDL UI frontend 71 sdl - choose the SDL UI frontend
70 gtk - choose the Gtk UI frontend 72 gtk - choose the Gtk UI frontend
71 gl - enable virgl-based GL acceleration (also needs gtk or sdl options) 73 gl - enable virgl-based GL acceleration (also needs gtk or sdl options)
72 gl-es - enable virgl-based GL acceleration, using OpenGL ES (also needs gtk or sdl options) 74 gl-es - enable virgl-based GL acceleration, using OpenGL ES (also needs gtk or sdl options)
73 egl-headless - enable headless EGL output; use vnc (via publicvnc option) or spice to see it 75 egl-headless - enable headless EGL output; use vnc (via publicvnc option) or spice to see it
76 (hint: if /dev/dri/renderD* is absent due to lack of suitable GPU, 'modprobe vgem' will create
77 one suitable for mesa llvmpipe software renderer)
74 serial - enable a serial console on /dev/ttyS0 78 serial - enable a serial console on /dev/ttyS0
75 serialstdio - enable a serial console on the console (regardless of graphics mode) 79 serialstdio - enable a serial console on the console (regardless of graphics mode)
76 slirp - enable user networking, no root privileges is required 80 slirp - enable user networking, no root privilege is required
77 snapshot - don't write changes to back to images 81 snapshot - don't write changes back to images
78 kvm - enable KVM when running x86/x86_64 (VT-capable CPU required) 82 kvm - enable KVM when running x86/x86_64 (VT-capable CPU required)
79 kvm-vhost - enable KVM with vhost when running x86/x86_64 (VT-capable CPU required) 83 kvm-vhost - enable KVM with vhost when running x86/x86_64 (VT-capable CPU required)
80 publicvnc - enable a VNC server open to all hosts 84 publicvnc - enable a VNC server open to all hosts
81 audio - enable audio 85 audio - enable audio
86 guestagent - enable guest agent communication
87 qmp=<path> - create a QMP socket (defaults to unix:qmp.sock if unspecified)
82 [*/]ovmf* - OVMF firmware file or base name for booting with UEFI 88 [*/]ovmf* - OVMF firmware file or base name for booting with UEFI
83 tcpserial=<port> - specify tcp serial port number 89 tcpserial=<port> - specify tcp serial port number
84 qemuparams=<xyz> - specify custom parameters to QEMU 90 qemuparams=<xyz> - specify custom parameters to QEMU
@@ -113,10 +119,10 @@ def check_tun():
113 if not os.access(dev_tun, os.W_OK): 119 if not os.access(dev_tun, os.W_OK):
114 raise RunQemuError("TUN control device %s is not writable, please fix (e.g. sudo chmod 666 %s)" % (dev_tun, dev_tun)) 120 raise RunQemuError("TUN control device %s is not writable, please fix (e.g. sudo chmod 666 %s)" % (dev_tun, dev_tun))
115 121
116def get_first_file(cmds): 122def get_first_file(globs):
117 """Return first file found in wildcard cmds""" 123 """Return first file found in wildcard globs"""
118 for cmd in cmds: 124 for g in globs:
119 all_files = glob.glob(cmd) 125 all_files = glob.glob(g)
120 if all_files: 126 if all_files:
121 for f in all_files: 127 for f in all_files:
122 if not os.path.isdir(f): 128 if not os.path.isdir(f):
@@ -139,12 +145,12 @@ class BaseConfig(object):
139 'OE_TMPDIR', 145 'OE_TMPDIR',
140 'OECORE_NATIVE_SYSROOT', 146 'OECORE_NATIVE_SYSROOT',
141 'MULTICONFIG', 147 'MULTICONFIG',
148 'SERIAL_CONSOLES',
142 ) 149 )
143 150
144 self.qemu_opt = '' 151 self.qemu_opt = ''
145 self.qemu_opt_script = '' 152 self.qemu_opt_script = ''
146 self.qemuparams = '' 153 self.qemuparams = ''
147 self.clean_nfs_dir = False
148 self.nfs_server = '' 154 self.nfs_server = ''
149 self.rootfs = '' 155 self.rootfs = ''
150 # File name(s) of a OVMF firmware file or variable store, 156 # File name(s) of a OVMF firmware file or variable store,
@@ -173,6 +179,15 @@ class BaseConfig(object):
173 self.nfs_running = False 179 self.nfs_running = False
174 self.serialconsole = False 180 self.serialconsole = False
175 self.serialstdio = False 181 self.serialstdio = False
182 self.nographic = False
183 self.nonetwork = False
184 self.sdl = False
185 self.gtk = False
186 self.gl = False
187 self.gl_es = False
188 self.egl_headless = False
189 self.publicvnc = False
190 self.novga = False
176 self.cleantap = False 191 self.cleantap = False
177 self.saved_stty = '' 192 self.saved_stty = ''
178 self.audio_enabled = False 193 self.audio_enabled = False
@@ -184,12 +199,14 @@ class BaseConfig(object):
184 self.snapshot = False 199 self.snapshot = False
185 self.wictypes = ('wic', 'wic.vmdk', 'wic.qcow2', 'wic.vdi', "wic.vhd", "wic.vhdx") 200 self.wictypes = ('wic', 'wic.vmdk', 'wic.qcow2', 'wic.vdi', "wic.vhd", "wic.vhdx")
186 self.fstypes = ('ext2', 'ext3', 'ext4', 'jffs2', 'nfs', 'btrfs', 201 self.fstypes = ('ext2', 'ext3', 'ext4', 'jffs2', 'nfs', 'btrfs',
187 'cpio.gz', 'cpio', 'ramfs', 'tar.bz2', 'tar.gz') 202 'cpio.gz', 'cpio', 'ramfs', 'tar.bz2', 'tar.gz',
203 'squashfs', 'squashfs-xz', 'squashfs-lzo',
204 'squashfs-lz4', 'squashfs-zst')
188 self.vmtypes = ('hddimg', 'iso') 205 self.vmtypes = ('hddimg', 'iso')
189 self.fsinfo = {} 206 self.fsinfo = {}
190 self.network_device = "-device e1000,netdev=net0,mac=@MAC@" 207 self.network_device = "-device e1000,netdev=net0,mac=@MAC@"
191 self.cmdline_ip_slirp = "ip=dhcp" 208 self.cmdline_ip_slirp = "ip=dhcp"
192 self.cmdline_ip_tap = "ip=192.168.7.@CLIENT@::192.168.7.@GATEWAY@:255.255.255.0" 209 self.cmdline_ip_tap = "ip=192.168.7.@CLIENT@::192.168.7.@GATEWAY@:255.255.255.0::eth0:off:8.8.8.8 net.ifnames=0"
193 # Use different mac section for tap and slirp to avoid 210 # Use different mac section for tap and slirp to avoid
194 # conflicts, e.g., when one is running with tap, the other is 211 # conflicts, e.g., when one is running with tap, the other is
195 # running with slirp. 212 # running with slirp.
@@ -199,9 +216,15 @@ class BaseConfig(object):
199 self.mac_tap = "52:54:00:12:34:" 216 self.mac_tap = "52:54:00:12:34:"
200 self.mac_slirp = "52:54:00:12:35:" 217 self.mac_slirp = "52:54:00:12:35:"
201 # pid of the actual qemu process 218 # pid of the actual qemu process
202 self.qemupid = None 219 self.qemu_environ = os.environ.copy()
220 self.qemuprocess = None
203 # avoid cleanup twice 221 # avoid cleanup twice
204 self.cleaned = False 222 self.cleaned = False
223 # Files to cleanup after run
224 self.cleanup_files = []
225 self.qmp = None
226 self.guest_agent = False
227 self.guest_agent_sockpath = '/tmp/qga.sock'
205 228
206 def acquire_taplock(self, error=True): 229 def acquire_taplock(self, error=True):
207 logger.debug("Acquiring lockfile %s..." % self.taplock) 230 logger.debug("Acquiring lockfile %s..." % self.taplock)
@@ -223,9 +246,12 @@ class BaseConfig(object):
223 def release_taplock(self): 246 def release_taplock(self):
224 if self.taplock_descriptor: 247 if self.taplock_descriptor:
225 logger.debug("Releasing lockfile for tap device '%s'" % self.tap) 248 logger.debug("Releasing lockfile for tap device '%s'" % self.tap)
226 fcntl.flock(self.taplock_descriptor, fcntl.LOCK_UN) 249 # We pass the fd to the qemu process and if we unlock here, it would unlock for
250 # that too. Therefore don't unlock, just close
251 # fcntl.flock(self.taplock_descriptor, fcntl.LOCK_UN)
227 self.taplock_descriptor.close() 252 self.taplock_descriptor.close()
228 os.remove(self.taplock) 253 # Removing the file is a potential race, don't do that either
254 # os.remove(self.taplock)
229 self.taplock_descriptor = None 255 self.taplock_descriptor = None
230 256
231 def check_free_port(self, host, port, lockdir): 257 def check_free_port(self, host, port, lockdir):
@@ -263,17 +289,23 @@ class BaseConfig(object):
263 289
264 def release_portlock(self, lockfile=None): 290 def release_portlock(self, lockfile=None):
265 if lockfile != None: 291 if lockfile != None:
266 logger.debug("Releasing lockfile '%s'" % lockfile) 292 logger.debug("Releasing lockfile '%s'" % lockfile)
267 fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_UN) 293 # We pass the fd to the qemu process and if we unlock here, it would unlock for
268 self.portlocks[lockfile].close() 294 # that too. Therefore don't unlock, just close
269 os.remove(lockfile) 295 # fcntl.flock(self.portlocks[lockfile], fcntl.LOCK_UN)
270 del self.portlocks[lockfile] 296 self.portlocks[lockfile].close()
297 # Removing the file is a potential race, don't do that either
298 # os.remove(lockfile)
299 del self.portlocks[lockfile]
271 elif len(self.portlocks): 300 elif len(self.portlocks):
272 for lockfile, descriptor in self.portlocks.items(): 301 for lockfile, descriptor in self.portlocks.items():
273 logger.debug("Releasing lockfile '%s'" % lockfile) 302 logger.debug("Releasing lockfile '%s'" % lockfile)
274 fcntl.flock(descriptor, fcntl.LOCK_UN) 303 # We pass the fd to the qemu process and if we unlock here, it would unlock for
304 # that too. Therefore don't unlock, just close
305 # fcntl.flock(descriptor, fcntl.LOCK_UN)
275 descriptor.close() 306 descriptor.close()
276 os.remove(lockfile) 307 # Removing the file is a potential race, don't do that either
308 # os.remove(lockfile)
277 self.portlocks = {} 309 self.portlocks = {}
278 310
279 def get(self, key): 311 def get(self, key):
@@ -331,21 +363,21 @@ class BaseConfig(object):
331 def check_arg_path(self, p): 363 def check_arg_path(self, p):
332 """ 364 """
333 - Check whether it is <image>.qemuboot.conf or contains <image>.qemuboot.conf 365 - Check whether it is <image>.qemuboot.conf or contains <image>.qemuboot.conf
334 - Check whether is a kernel file 366 - Check whether it is a kernel file
335 - Check whether is a image file 367 - Check whether it is an image file
336 - Check whether it is a nfs dir 368 - Check whether it is an NFS dir
337 - Check whether it is a OVMF flash file 369 - Check whether it is an OVMF flash file
338 """ 370 """
339 if p.endswith('.qemuboot.conf'): 371 if p.endswith('.qemuboot.conf'):
340 self.qemuboot = p 372 self.qemuboot = p
341 self.qbconfload = True 373 self.qbconfload = True
342 elif re.search('\.bin$', p) or re.search('bzImage', p) or \ 374 elif re.search('\\.bin$', p) or re.search('bzImage', p) or \
343 re.search('zImage', p) or re.search('vmlinux', p) or \ 375 re.search('zImage', p) or re.search('vmlinux', p) or \
344 re.search('fitImage', p) or re.search('uImage', p): 376 re.search('fitImage', p) or re.search('uImage', p):
345 self.kernel = p 377 self.kernel = p
346 elif os.path.exists(p) and (not os.path.isdir(p)) and '-image-' in os.path.basename(p): 378 elif os.path.isfile(p) and ('-image-' in os.path.basename(p) or '.rootfs.' in os.path.basename(p)):
347 self.rootfs = p 379 self.rootfs = p
348 # Check filename against self.fstypes can hanlde <file>.cpio.gz, 380 # Check filename against self.fstypes can handle <file>.cpio.gz,
349 # otherwise, its type would be "gz", which is incorrect. 381 # otherwise, its type would be "gz", which is incorrect.
350 fst = "" 382 fst = ""
351 for t in self.fstypes: 383 for t in self.fstypes:
@@ -353,18 +385,24 @@ class BaseConfig(object):
353 fst = t 385 fst = t
354 break 386 break
355 if not fst: 387 if not fst:
356 m = re.search('.*\.(.*)$', self.rootfs) 388 m = re.search('.*\\.(.*)$', self.rootfs)
357 if m: 389 if m:
358 fst = m.group(1) 390 fst = m.group(1)
359 if fst: 391 if fst:
360 self.check_arg_fstype(fst) 392 self.check_arg_fstype(fst)
361 qb = re.sub('\.' + fst + "$", '', self.rootfs) 393 qb = re.sub('\\.' + fst + "$", '.qemuboot.conf', self.rootfs)
362 qb = '%s%s' % (re.sub('\.rootfs$', '', qb), '.qemuboot.conf')
363 if os.path.exists(qb): 394 if os.path.exists(qb):
364 self.qemuboot = qb 395 self.qemuboot = qb
365 self.qbconfload = True 396 self.qbconfload = True
366 else: 397 else:
367 logger.warning("%s doesn't exist" % qb) 398 logger.warning("%s doesn't exist, will try to remove '.rootfs' from filename" % qb)
399 # They to remove .rootfs (IMAGE_NAME_SUFFIX) as well
400 qb = re.sub('\\.rootfs.qemuboot.conf$', '.qemuboot.conf', qb)
401 if os.path.exists(qb):
402 self.qemuboot = qb
403 self.qbconfload = True
404 else:
405 logger.warning("%s doesn't exist" % qb)
368 else: 406 else:
369 raise RunQemuError("Can't find FSTYPE from: %s" % p) 407 raise RunQemuError("Can't find FSTYPE from: %s" % p)
370 408
@@ -398,6 +436,7 @@ class BaseConfig(object):
398 # are there other scenarios in which we need to support being 436 # are there other scenarios in which we need to support being
399 # invoked by bitbake? 437 # invoked by bitbake?
400 deploy = self.get('DEPLOY_DIR_IMAGE') 438 deploy = self.get('DEPLOY_DIR_IMAGE')
439 image_link_name = self.get('IMAGE_LINK_NAME')
401 bbchild = deploy and self.get('OE_TMPDIR') 440 bbchild = deploy and self.get('OE_TMPDIR')
402 if bbchild: 441 if bbchild:
403 self.set_machine_deploy_dir(arg, deploy) 442 self.set_machine_deploy_dir(arg, deploy)
@@ -422,23 +461,27 @@ class BaseConfig(object):
422 else: 461 else:
423 logger.error("%s not a directory valid DEPLOY_DIR_IMAGE" % deploy_dir_image) 462 logger.error("%s not a directory valid DEPLOY_DIR_IMAGE" % deploy_dir_image)
424 self.set("MACHINE", arg) 463 self.set("MACHINE", arg)
425 464 if not image_link_name:
426 def set_dri_path(self): 465 s = re.search('^IMAGE_LINK_NAME="(.*)"', self.bitbake_e, re.M)
427 # As runqemu can be run within bitbake (when using testimage, for example), 466 if s:
428 # we need to ensure that we run host pkg-config, and that it does not 467 image_link_name = s.group(1)
429 # get mis-directed to native build paths set by bitbake. 468 self.set("IMAGE_LINK_NAME", image_link_name)
430 try: 469 logger.debug('Using IMAGE_LINK_NAME = "%s"' % image_link_name)
431 del os.environ['PKG_CONFIG_PATH'] 470
432 del os.environ['PKG_CONFIG_DIR'] 471 def set_mesa_paths(self):
433 del os.environ['PKG_CONFIG_LIBDIR'] 472 drivers_path = os.path.join(self.bindir_native, '../lib/dri')
434 del os.environ['PKG_CONFIG_SYSROOT_DIR'] 473 gbm_path = os.path.join(self.bindir_native, '../lib/gbm')
435 except KeyError: 474 if not os.path.exists(drivers_path) or not os.listdir(drivers_path) \
436 pass 475 or not os.path.exists(gbm_path) or not os.listdir(gbm_path):
437 try: 476 raise RunQemuError("""
438 dripath = subprocess.check_output("PATH=/bin:/usr/bin:$PATH pkg-config --variable=dridriverdir dri", shell=True) 477qemu has been built without opengl support and accelerated graphics support is not available.
439 except subprocess.CalledProcessError as e: 478To enable it, add:
440 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.") 479DISTRO_FEATURES_NATIVE:append = " opengl"
441 os.environ['LIBGL_DRIVERS_PATH'] = dripath.decode('utf-8').strip() 480DISTRO_FEATURES_NATIVESDK:append = " opengl"
481to your build configuration.
482""")
483 self.qemu_environ['LIBGL_DRIVERS_PATH'] = drivers_path
484 self.qemu_environ['GBM_BACKENDS_PATH'] = gbm_path
442 485
443 def check_args(self): 486 def check_args(self):
444 for debug in ("-d", "--debug"): 487 for debug in ("-d", "--debug"):
@@ -452,51 +495,32 @@ class BaseConfig(object):
452 sys.argv.remove(quiet) 495 sys.argv.remove(quiet)
453 496
454 if 'gl' not in sys.argv[1:] and 'gl-es' not in sys.argv[1:]: 497 if 'gl' not in sys.argv[1:] and 'gl-es' not in sys.argv[1:]:
455 os.environ['SDL_RENDER_DRIVER'] = 'software' 498 self.qemu_environ['SDL_RENDER_DRIVER'] = 'software'
499 self.qemu_environ['SDL_FRAMEBUFFER_ACCELERATION'] = 'false'
456 500
457 unknown_arg = "" 501 unknown_arg = ""
458 for arg in sys.argv[1:]: 502 for arg in sys.argv[1:]:
459 if arg in self.fstypes + self.vmtypes + self.wictypes: 503 if arg in self.fstypes + self.vmtypes + self.wictypes:
460 self.check_arg_fstype(arg) 504 self.check_arg_fstype(arg)
461 elif arg == 'nographic': 505 elif arg == 'nographic':
462 if ('sdl' in sys.argv): 506 self.nographic = True
463 raise RunQemuError('Option nographic makes no sense alongside the sdl option.' % (arg)) 507 elif arg == "nonetwork":
464 if ('gtk' in sys.argv): 508 self.nonetwork = True
465 raise RunQemuError('Option nographic makes no sense alongside the gtk option.' % (arg))
466 self.qemu_opt_script += ' -nographic'
467 self.kernel_cmdline_script += ' console=ttyS0'
468 elif arg == 'sdl': 509 elif arg == 'sdl':
469 if 'gl' in sys.argv[1:]: 510 self.sdl = True
470 self.set_dri_path()
471 self.qemu_opt_script += ' -vga virtio -display sdl,gl=on,show-cursor=on'
472 elif 'gl-es' in sys.argv[1:]:
473 self.set_dri_path()
474 self.qemu_opt_script += ' -vga virtio -display sdl,gl=es,show-cursor=on'
475 else:
476 self.qemu_opt_script += ' -display sdl,show-cursor=on'
477 elif arg == 'gtk': 511 elif arg == 'gtk':
478 if 'gl' in sys.argv[1:]: 512 self.gtk = True
479 self.set_dri_path() 513 elif arg == 'gl':
480 self.qemu_opt_script += ' -vga virtio -display gtk,gl=on,show-cursor=on' 514 self.gl = True
481 elif 'gl-es' in sys.argv[1:]: 515 elif arg == 'gl-es':
482 self.set_dri_path() 516 self.gl_es = True
483 self.qemu_opt_script += ' -vga virtio -display gtk,gl=es,show-cursor=on'
484 else:
485 self.qemu_opt_script += ' -display gtk,show-cursor=on'
486 elif arg == 'gl' or arg == 'gl-es':
487 # These args are handled inside sdl or gtk blocks above
488 if ('gtk' not in sys.argv) and ('sdl' not in sys.argv):
489 raise RunQemuError('Option %s also needs gtk or sdl option.' % (arg))
490 elif arg == 'egl-headless': 517 elif arg == 'egl-headless':
491 self.set_dri_path() 518 self.egl_headless = True
492 self.qemu_opt_script += ' -vga virtio -display egl-headless,show-cursor=on'
493 elif arg == 'novga': 519 elif arg == 'novga':
494 self.qemu_opt_script += ' -vga none' 520 self.novga = True
495 elif arg == 'serial': 521 elif arg == 'serial':
496 self.kernel_cmdline_script += ' console=ttyS0'
497 self.serialconsole = True 522 self.serialconsole = True
498 elif arg == "serialstdio": 523 elif arg == "serialstdio":
499 self.kernel_cmdline_script += ' console=ttyS0'
500 self.serialstdio = True 524 self.serialstdio = True
501 elif arg == 'audio': 525 elif arg == 'audio':
502 logger.info("Enabling audio in qemu") 526 logger.info("Enabling audio in qemu")
@@ -513,7 +537,16 @@ class BaseConfig(object):
513 elif arg == 'snapshot': 537 elif arg == 'snapshot':
514 self.snapshot = True 538 self.snapshot = True
515 elif arg == 'publicvnc': 539 elif arg == 'publicvnc':
540 self.publicvnc = True
516 self.qemu_opt_script += ' -vnc :0' 541 self.qemu_opt_script += ' -vnc :0'
542 elif arg == 'guestagent':
543 self.guest_agent = True
544 elif arg == "qmp":
545 self.qmp = "unix:qmp.sock"
546 elif arg.startswith("qmp="):
547 self.qmp = arg[len('qmp='):]
548 elif arg.startswith('guestagent-sockpath='):
549 self.guest_agent_sockpath = '%s' % arg[len('guestagent-sockpath='):]
517 elif arg.startswith('tcpserial='): 550 elif arg.startswith('tcpserial='):
518 self.tcpserial_portnum = '%s' % arg[len('tcpserial='):] 551 self.tcpserial_portnum = '%s' % arg[len('tcpserial='):]
519 elif arg.startswith('qemuparams='): 552 elif arg.startswith('qemuparams='):
@@ -545,21 +578,28 @@ class BaseConfig(object):
545 self.check_arg_machine(unknown_arg) 578 self.check_arg_machine(unknown_arg)
546 579
547 if not (self.get('DEPLOY_DIR_IMAGE') or self.qbconfload): 580 if not (self.get('DEPLOY_DIR_IMAGE') or self.qbconfload):
548 self.load_bitbake_env() 581 self.load_bitbake_env(target=self.rootfs)
549 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M) 582 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
550 if s: 583 if s:
551 self.set("DEPLOY_DIR_IMAGE", s.group(1)) 584 self.set("DEPLOY_DIR_IMAGE", s.group(1))
552 585
586 if not self.get('IMAGE_LINK_NAME') and self.rootfs:
587 s = re.search('^IMAGE_LINK_NAME="(.*)"', self.bitbake_e, re.M)
588 if s:
589 image_link_name = s.group(1)
590 self.set("IMAGE_LINK_NAME", image_link_name)
591 logger.debug('Using IMAGE_LINK_NAME = "%s"' % image_link_name)
592
553 def check_kvm(self): 593 def check_kvm(self):
554 """Check kvm and kvm-host""" 594 """Check kvm and kvm-host"""
555 if not (self.kvm_enabled or self.vhost_enabled): 595 if not (self.kvm_enabled or self.vhost_enabled):
556 self.qemu_opt_script += ' %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU')) 596 self.qemu_opt_script += ' %s %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU'), self.get('QB_SMP'))
557 return 597 return
558 598
559 if not self.get('QB_CPU_KVM'): 599 if not self.get('QB_CPU_KVM'):
560 raise RunQemuError("QB_CPU_KVM is NULL, this board doesn't support kvm") 600 raise RunQemuError("QB_CPU_KVM is NULL, this board doesn't support kvm")
561 601
562 self.qemu_opt_script += ' %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU_KVM')) 602 self.qemu_opt_script += ' %s %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU_KVM'), self.get('QB_SMP'))
563 yocto_kvm_wiki = "https://wiki.yoctoproject.org/wiki/How_to_enable_KVM_for_Poky_qemu" 603 yocto_kvm_wiki = "https://wiki.yoctoproject.org/wiki/How_to_enable_KVM_for_Poky_qemu"
564 yocto_paravirt_kvm_wiki = "https://wiki.yoctoproject.org/wiki/Running_an_x86_Yocto_Linux_image_under_QEMU_KVM" 604 yocto_paravirt_kvm_wiki = "https://wiki.yoctoproject.org/wiki/Running_an_x86_Yocto_Linux_image_under_QEMU_KVM"
565 dev_kvm = '/dev/kvm' 605 dev_kvm = '/dev/kvm'
@@ -579,11 +619,6 @@ class BaseConfig(object):
579 619
580 if os.access(dev_kvm, os.W_OK|os.R_OK): 620 if os.access(dev_kvm, os.W_OK|os.R_OK):
581 self.qemu_opt_script += ' -enable-kvm' 621 self.qemu_opt_script += ' -enable-kvm'
582 if self.get('MACHINE') == "qemux86":
583 # Workaround for broken APIC window on pre 4.15 host kernels which causes boot hangs
584 # See YOCTO #12301
585 # On 64 bit we use x2apic
586 self.kernel_cmdline_script += " clocksource=kvm-clock hpet=disable noapic nolapic"
587 else: 622 else:
588 logger.error("You have no read or write permission on /dev/kvm.") 623 logger.error("You have no read or write permission on /dev/kvm.")
589 logger.error("Please change the ownership of this file as described at:") 624 logger.error("Please change the ownership of this file as described at:")
@@ -624,10 +659,10 @@ class BaseConfig(object):
624 elif fsflag == 'kernel-in-fs': 659 elif fsflag == 'kernel-in-fs':
625 wic_fs = False 660 wic_fs = False
626 else: 661 else:
627 logger.warn('Unknown flag "%s:%s" in QB_FSINFO', fstype, fsflag) 662 logger.warning('Unknown flag "%s:%s" in QB_FSINFO', fstype, fsflag)
628 continue 663 continue
629 else: 664 else:
630 logger.warn('QB_FSINFO is not supported for image type "%s"', fstype) 665 logger.warning('QB_FSINFO is not supported for image type "%s"', fstype)
631 continue 666 continue
632 667
633 if fstype in self.fsinfo: 668 if fstype in self.fsinfo:
@@ -660,16 +695,16 @@ class BaseConfig(object):
660 695
661 if self.rootfs and not os.path.exists(self.rootfs): 696 if self.rootfs and not os.path.exists(self.rootfs):
662 # Lazy rootfs 697 # Lazy rootfs
663 self.rootfs = "%s/%s-%s.%s" % (self.get('DEPLOY_DIR_IMAGE'), 698 self.rootfs = "%s/%s.%s" % (self.get('DEPLOY_DIR_IMAGE'),
664 self.rootfs, self.get('MACHINE'), 699 self.get('IMAGE_LINK_NAME'),
665 self.fstype) 700 self.fstype)
666 elif not self.rootfs: 701 elif not self.rootfs:
667 cmd_name = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_NAME'), self.fstype) 702 glob_name = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_NAME'), self.fstype)
668 cmd_link = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'), self.fstype) 703 glob_link = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'), self.fstype)
669 cmds = (cmd_name, cmd_link) 704 globs = (glob_name, glob_link)
670 self.rootfs = get_first_file(cmds) 705 self.rootfs = get_first_file(globs)
671 if not self.rootfs: 706 if not self.rootfs:
672 raise RunQemuError("Failed to find rootfs: %s or %s" % cmds) 707 raise RunQemuError("Failed to find rootfs: %s or %s" % globs)
673 708
674 if not os.path.exists(self.rootfs): 709 if not os.path.exists(self.rootfs):
675 raise RunQemuError("Can't find rootfs: %s" % self.rootfs) 710 raise RunQemuError("Can't find rootfs: %s" % self.rootfs)
@@ -729,10 +764,10 @@ class BaseConfig(object):
729 kernel_match_name = "%s/%s" % (deploy_dir_image, kernel_name) 764 kernel_match_name = "%s/%s" % (deploy_dir_image, kernel_name)
730 kernel_match_link = "%s/%s" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE')) 765 kernel_match_link = "%s/%s" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
731 kernel_startswith = "%s/%s*" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE')) 766 kernel_startswith = "%s/%s*" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
732 cmds = (kernel_match_name, kernel_match_link, kernel_startswith) 767 globs = (kernel_match_name, kernel_match_link, kernel_startswith)
733 self.kernel = get_first_file(cmds) 768 self.kernel = get_first_file(globs)
734 if not self.kernel: 769 if not self.kernel:
735 raise RunQemuError('KERNEL not found: %s, %s or %s' % cmds) 770 raise RunQemuError('KERNEL not found: %s, %s or %s' % globs)
736 771
737 if not os.path.exists(self.kernel): 772 if not os.path.exists(self.kernel):
738 raise RunQemuError("KERNEL %s not found" % self.kernel) 773 raise RunQemuError("KERNEL %s not found" % self.kernel)
@@ -749,13 +784,13 @@ class BaseConfig(object):
749 dtb = self.get('QB_DTB') 784 dtb = self.get('QB_DTB')
750 if dtb: 785 if dtb:
751 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE') 786 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
752 cmd_match = "%s/%s" % (deploy_dir_image, dtb) 787 glob_match = "%s/%s" % (deploy_dir_image, dtb)
753 cmd_startswith = "%s/%s*" % (deploy_dir_image, dtb) 788 glob_startswith = "%s/%s*" % (deploy_dir_image, dtb)
754 cmd_wild = "%s/*.dtb" % deploy_dir_image 789 glob_wild = "%s/*.dtb" % deploy_dir_image
755 cmds = (cmd_match, cmd_startswith, cmd_wild) 790 globs = (glob_match, glob_startswith, glob_wild)
756 self.dtb = get_first_file(cmds) 791 self.dtb = get_first_file(globs)
757 if not os.path.exists(self.dtb): 792 if not os.path.exists(self.dtb):
758 raise RunQemuError('DTB not found: %s, %s or %s' % cmds) 793 raise RunQemuError('DTB not found: %s, %s or %s' % globs)
759 794
760 def check_bios(self): 795 def check_bios(self):
761 """Check and set bios""" 796 """Check and set bios"""
@@ -779,7 +814,7 @@ class BaseConfig(object):
779 raise RunQemuError('BIOS not found: %s' % bios_match_name) 814 raise RunQemuError('BIOS not found: %s' % bios_match_name)
780 815
781 if not os.path.exists(self.bios): 816 if not os.path.exists(self.bios):
782 raise RunQemuError("KERNEL %s not found" % self.bios) 817 raise RunQemuError("BIOS %s not found" % self.bios)
783 818
784 819
785 def check_mem(self): 820 def check_mem(self):
@@ -806,7 +841,7 @@ class BaseConfig(object):
806 self.set('QB_MEM', qb_mem) 841 self.set('QB_MEM', qb_mem)
807 842
808 mach = self.get('MACHINE') 843 mach = self.get('MACHINE')
809 if not mach.startswith('qemumips'): 844 if not mach.startswith(('qemumips', 'qemux86', 'qemuloongarch64')):
810 self.kernel_cmdline_script += ' mem=%s' % self.get('QB_MEM').replace('-m','').strip() + 'M' 845 self.kernel_cmdline_script += ' mem=%s' % self.get('QB_MEM').replace('-m','').strip() + 'M'
811 846
812 self.qemu_opt_script += ' %s' % self.get('QB_MEM') 847 self.qemu_opt_script += ' %s' % self.get('QB_MEM')
@@ -818,11 +853,11 @@ class BaseConfig(object):
818 if self.get('QB_TCPSERIAL_OPT'): 853 if self.get('QB_TCPSERIAL_OPT'):
819 self.qemu_opt_script += ' ' + self.get('QB_TCPSERIAL_OPT').replace('@PORT@', port) 854 self.qemu_opt_script += ' ' + self.get('QB_TCPSERIAL_OPT').replace('@PORT@', port)
820 else: 855 else:
821 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port 856 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s,nodelay=on' % port
822 857
823 if len(ports) > 1: 858 if len(ports) > 1:
824 for port in ports[1:]: 859 for port in ports[1:]:
825 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % port 860 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s,nodelay=on' % port
826 861
827 def check_and_set(self): 862 def check_and_set(self):
828 """Check configs sanity and set when needed""" 863 """Check configs sanity and set when needed"""
@@ -865,8 +900,10 @@ class BaseConfig(object):
865 machine = self.get('MACHINE') 900 machine = self.get('MACHINE')
866 if not machine: 901 if not machine:
867 machine = os.path.basename(deploy_dir_image) 902 machine = os.path.basename(deploy_dir_image)
868 self.qemuboot = "%s/%s-%s.qemuboot.conf" % (deploy_dir_image, 903 if not self.get('IMAGE_LINK_NAME'):
869 self.rootfs, machine) 904 raise RunQemuError("IMAGE_LINK_NAME wasn't set to find corresponding .qemuboot.conf file")
905 self.qemuboot = "%s/%s.qemuboot.conf" % (deploy_dir_image,
906 self.get('IMAGE_LINK_NAME'))
870 else: 907 else:
871 cmd = 'ls -t %s/*.qemuboot.conf' % deploy_dir_image 908 cmd = 'ls -t %s/*.qemuboot.conf' % deploy_dir_image
872 logger.debug('Running %s...' % cmd) 909 logger.debug('Running %s...' % cmd)
@@ -987,19 +1024,16 @@ class BaseConfig(object):
987 if self.slirp_enabled: 1024 if self.slirp_enabled:
988 self.nfs_server = '10.0.2.2' 1025 self.nfs_server = '10.0.2.2'
989 else: 1026 else:
990 self.nfs_server = '192.168.7.1' 1027 self.nfs_server = '192.168.7.@GATEWAY@'
991
992 # Figure out a new nfs_instance to allow multiple qemus running.
993 ps = subprocess.check_output(("ps", "auxww")).decode('utf-8')
994 pattern = '/bin/unfsd .* -i .*\.pid -e .*/exports([0-9]+) '
995 all_instances = re.findall(pattern, ps, re.M)
996 if all_instances:
997 all_instances.sort(key=int)
998 self.nfs_instance = int(all_instances.pop()) + 1
999 1028
1000 nfsd_port = 3049 + 2 * self.nfs_instance 1029 nfsd_port = 3048 + self.nfs_instance
1001 mountd_port = 3048 + 2 * self.nfs_instance 1030 lockdir = "/tmp/qemu-port-locks"
1031 self.make_lock_dir(lockdir)
1032 while not self.check_free_port('localhost', nfsd_port, lockdir):
1033 self.nfs_instance += 1
1034 nfsd_port += 1
1002 1035
1036 mountd_port = nfsd_port
1003 # Export vars for runqemu-export-rootfs 1037 # Export vars for runqemu-export-rootfs
1004 export_dict = { 1038 export_dict = {
1005 'NFS_INSTANCE': self.nfs_instance, 1039 'NFS_INSTANCE': self.nfs_instance,
@@ -1010,7 +1044,11 @@ class BaseConfig(object):
1010 # Use '%s' since they are integers 1044 # Use '%s' since they are integers
1011 os.putenv(k, '%s' % v) 1045 os.putenv(k, '%s' % v)
1012 1046
1013 self.unfs_opts="nfsvers=3,port=%s,tcp,mountport=%s" % (nfsd_port, mountd_port) 1047 qb_nfsrootfs_extra_opt = self.get("QB_NFSROOTFS_EXTRA_OPT")
1048 if qb_nfsrootfs_extra_opt and not qb_nfsrootfs_extra_opt.startswith(","):
1049 qb_nfsrootfs_extra_opt = "," + qb_nfsrootfs_extra_opt
1050
1051 self.unfs_opts="nfsvers=3,port=%s,tcp,mountport=%s%s" % (nfsd_port, mountd_port, qb_nfsrootfs_extra_opt)
1014 1052
1015 # Extract .tar.bz2 or .tar.bz if no nfs dir 1053 # Extract .tar.bz2 or .tar.bz if no nfs dir
1016 if not (self.rootfs and os.path.isdir(self.rootfs)): 1054 if not (self.rootfs and os.path.isdir(self.rootfs)):
@@ -1033,22 +1071,41 @@ class BaseConfig(object):
1033 cmd = ('runqemu-extract-sdk', src, dest) 1071 cmd = ('runqemu-extract-sdk', src, dest)
1034 logger.info('Running %s...' % str(cmd)) 1072 logger.info('Running %s...' % str(cmd))
1035 if subprocess.call(cmd) != 0: 1073 if subprocess.call(cmd) != 0:
1036 raise RunQemuError('Failed to run %s' % cmd) 1074 raise RunQemuError('Failed to run %s' % str(cmd))
1037 self.clean_nfs_dir = True
1038 self.rootfs = dest 1075 self.rootfs = dest
1076 self.cleanup_files.append(self.rootfs)
1077 self.cleanup_files.append('%s.pseudo_state' % self.rootfs)
1039 1078
1040 # Start the userspace NFS server 1079 # Start the userspace NFS server
1041 cmd = ('runqemu-export-rootfs', 'start', self.rootfs) 1080 cmd = ('runqemu-export-rootfs', 'start', self.rootfs)
1042 logger.info('Running %s...' % str(cmd)) 1081 logger.info('Running %s...' % str(cmd))
1043 if subprocess.call(cmd) != 0: 1082 if subprocess.call(cmd) != 0:
1044 raise RunQemuError('Failed to run %s' % cmd) 1083 raise RunQemuError('Failed to run %s' % str(cmd))
1045 1084
1046 self.nfs_running = True 1085 self.nfs_running = True
1047 1086
1087 def setup_cmd(self):
1088 cmd = self.get('QB_SETUP_CMD')
1089 if cmd != '':
1090 logger.info('Running setup command %s' % str(cmd))
1091 if subprocess.call(cmd, shell=True) != 0:
1092 raise RunQemuError('Failed to run %s' % str(cmd))
1093
1048 def setup_net_bridge(self): 1094 def setup_net_bridge(self):
1049 self.set('NETWORK_CMD', '-netdev bridge,br=%s,id=net0,helper=%s -device virtio-net-pci,netdev=net0 ' % ( 1095 self.set('NETWORK_CMD', '-netdev bridge,br=%s,id=net0,helper=%s -device virtio-net-pci,netdev=net0 ' % (
1050 self.net_bridge, os.path.join(self.bindir_native, 'qemu-oe-bridge-helper'))) 1096 self.net_bridge, os.path.join(self.bindir_native, 'qemu-oe-bridge-helper')))
1051 1097
1098 def make_lock_dir(self, lockdir):
1099 if not os.path.exists(lockdir):
1100 # There might be a race issue when multi runqemu processess are
1101 # running at the same time.
1102 try:
1103 os.mkdir(lockdir)
1104 os.chmod(lockdir, 0o777)
1105 except FileExistsError:
1106 pass
1107 return
1108
1052 def setup_slirp(self): 1109 def setup_slirp(self):
1053 """Setup user networking""" 1110 """Setup user networking"""
1054 1111
@@ -1058,7 +1115,7 @@ class BaseConfig(object):
1058 logger.info("Network configuration:%s", netconf) 1115 logger.info("Network configuration:%s", netconf)
1059 self.kernel_cmdline_script += netconf 1116 self.kernel_cmdline_script += netconf
1060 # Port mapping 1117 # Port mapping
1061 hostfwd = ",hostfwd=tcp::2222-:22,hostfwd=tcp::2323-:23" 1118 hostfwd = ",hostfwd=tcp:127.0.0.1:2222-:22,hostfwd=tcp:127.0.0.1:2323-:23"
1062 qb_slirp_opt_default = "-netdev user,id=net0%s,tftp=%s" % (hostfwd, self.get('DEPLOY_DIR_IMAGE')) 1119 qb_slirp_opt_default = "-netdev user,id=net0%s,tftp=%s" % (hostfwd, self.get('DEPLOY_DIR_IMAGE'))
1063 qb_slirp_opt = self.get('QB_SLIRP_OPT') or qb_slirp_opt_default 1120 qb_slirp_opt = self.get('QB_SLIRP_OPT') or qb_slirp_opt_default
1064 # Figure out the port 1121 # Figure out the port
@@ -1067,14 +1124,7 @@ class BaseConfig(object):
1067 mac = 2 1124 mac = 2
1068 1125
1069 lockdir = "/tmp/qemu-port-locks" 1126 lockdir = "/tmp/qemu-port-locks"
1070 if not os.path.exists(lockdir): 1127 self.make_lock_dir(lockdir)
1071 # There might be a race issue when multi runqemu processess are
1072 # running at the same time.
1073 try:
1074 os.mkdir(lockdir)
1075 os.chmod(lockdir, 0o777)
1076 except FileExistsError:
1077 pass
1078 1128
1079 # Find a free port to avoid conflicts 1129 # Find a free port to avoid conflicts
1080 for p in ports[:]: 1130 for p in ports[:]:
@@ -1114,20 +1164,17 @@ class BaseConfig(object):
1114 logger.error("ip: %s" % ip) 1164 logger.error("ip: %s" % ip)
1115 raise OEPathError("runqemu-ifup, runqemu-ifdown or ip not found") 1165 raise OEPathError("runqemu-ifup, runqemu-ifdown or ip not found")
1116 1166
1117 if not os.path.exists(lockdir): 1167 self.make_lock_dir(lockdir)
1118 # There might be a race issue when multi runqemu processess are
1119 # running at the same time.
1120 try:
1121 os.mkdir(lockdir)
1122 os.chmod(lockdir, 0o777)
1123 except FileExistsError:
1124 pass
1125 1168
1126 cmd = (ip, 'link') 1169 cmd = (ip, 'link')
1127 logger.debug('Running %s...' % str(cmd)) 1170 logger.debug('Running %s...' % str(cmd))
1128 ip_link = subprocess.check_output(cmd).decode('utf-8') 1171 ip_link = subprocess.check_output(cmd).decode('utf-8')
1129 # Matches line like: 6: tap0: <foo> 1172 # Matches line like: 6: tap0: <foo>
1130 possibles = re.findall('^[0-9]+: +(tap[0-9]+): <.*', ip_link, re.M) 1173 oe_tap_name = 'tap'
1174 if 'OE_TAP_NAME' in os.environ:
1175 oe_tap_name = os.environ['OE_TAP_NAME']
1176 tap_re = '^[0-9]+: +(' + oe_tap_name + '[0-9]+): <.*'
1177 possibles = re.findall(tap_re, ip_link, re.M)
1131 tap = "" 1178 tap = ""
1132 for p in possibles: 1179 for p in possibles:
1133 lockfile = os.path.join(lockdir, p) 1180 lockfile = os.path.join(lockdir, p)
@@ -1148,25 +1195,28 @@ class BaseConfig(object):
1148 raise RunQemuError("a new one with sudo.") 1195 raise RunQemuError("a new one with sudo.")
1149 1196
1150 gid = os.getgid() 1197 gid = os.getgid()
1151 uid = os.getuid()
1152 logger.info("Setting up tap interface under sudo") 1198 logger.info("Setting up tap interface under sudo")
1153 cmd = ('sudo', self.qemuifup, str(uid), str(gid), self.bindir_native) 1199 cmd = ('sudo', self.qemuifup, str(gid))
1154 try: 1200 for _ in range(5):
1155 tap = subprocess.check_output(cmd).decode('utf-8').strip() 1201 try:
1156 except subprocess.CalledProcessError as e: 1202 tap = subprocess.check_output(cmd).decode('utf-8').strip()
1157 logger.error('Setting up tap device failed:\n%s\nRun runqemu-gen-tapdevs to manually create one.' % str(e)) 1203 except subprocess.CalledProcessError as e:
1158 sys.exit(1) 1204 logger.error('Setting up tap device failed:\n%s\nRun runqemu-gen-tapdevs to manually create one.' % str(e))
1159 lockfile = os.path.join(lockdir, tap) 1205 sys.exit(1)
1160 self.taplock = lockfile + '.lock' 1206 lockfile = os.path.join(lockdir, tap)
1161 self.acquire_taplock() 1207 self.taplock = lockfile + '.lock'
1162 self.cleantap = True 1208 if self.acquire_taplock():
1163 logger.debug('Created tap: %s' % tap) 1209 self.cleantap = True
1210 logger.debug('Created tap: %s' % tap)
1211 break
1212 else:
1213 tap = None
1164 1214
1165 if not tap: 1215 if not tap:
1166 logger.error("Failed to setup tap device. Run runqemu-gen-tapdevs to manually create.") 1216 logger.error("Failed to setup tap device. Run runqemu-gen-tapdevs to manually create.")
1167 sys.exit(1) 1217 sys.exit(1)
1168 self.tap = tap 1218 self.tap = tap
1169 tapnum = int(tap[3:]) 1219 tapnum = int(tap[len(oe_tap_name):])
1170 gateway = tapnum * 2 + 1 1220 gateway = tapnum * 2 + 1
1171 client = gateway + 1 1221 client = gateway + 1
1172 if self.fstype == 'nfs': 1222 if self.fstype == 'nfs':
@@ -1174,6 +1224,7 @@ class BaseConfig(object):
1174 netconf = " " + self.cmdline_ip_tap 1224 netconf = " " + self.cmdline_ip_tap
1175 netconf = netconf.replace('@CLIENT@', str(client)) 1225 netconf = netconf.replace('@CLIENT@', str(client))
1176 netconf = netconf.replace('@GATEWAY@', str(gateway)) 1226 netconf = netconf.replace('@GATEWAY@', str(gateway))
1227 self.nfs_server = self.nfs_server.replace('@GATEWAY@', str(gateway))
1177 logger.info("Network configuration:%s", netconf) 1228 logger.info("Network configuration:%s", netconf)
1178 self.kernel_cmdline_script += netconf 1229 self.kernel_cmdline_script += netconf
1179 mac = "%s%02x" % (self.mac_tap, client) 1230 mac = "%s%02x" % (self.mac_tap, client)
@@ -1189,7 +1240,8 @@ class BaseConfig(object):
1189 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qemu_tap_opt)) 1240 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qemu_tap_opt))
1190 1241
1191 def setup_network(self): 1242 def setup_network(self):
1192 if self.get('QB_NET') == 'none': 1243 if self.nonetwork or self.get('QB_NET') == 'none':
1244 self.set('NETWORK_CMD', '-nic none')
1193 return 1245 return
1194 if sys.stdin.isatty(): 1246 if sys.stdin.isatty():
1195 self.saved_stty = subprocess.check_output(("stty", "-g")).decode('utf-8').strip() 1247 self.saved_stty = subprocess.check_output(("stty", "-g")).decode('utf-8').strip()
@@ -1210,6 +1262,18 @@ class BaseConfig(object):
1210 self.fstype = self.fstype[4:] 1262 self.fstype = self.fstype[4:]
1211 rootfs_format = self.fstype if self.fstype in ('vmdk', 'vhd', 'vhdx', 'qcow2', 'vdi') else 'raw' 1263 rootfs_format = self.fstype if self.fstype in ('vmdk', 'vhd', 'vhdx', 'qcow2', 'vdi') else 'raw'
1212 1264
1265 tmpfsdir = os.environ.get("RUNQEMU_TMPFS_DIR", None)
1266 if self.snapshot and tmpfsdir:
1267 newrootfs = os.path.join(tmpfsdir, os.path.basename(self.rootfs)) + "." + str(os.getpid())
1268 logger.info("Copying rootfs to %s" % newrootfs)
1269 copy_start = time.time()
1270 shutil.copyfile(self.rootfs, newrootfs)
1271 logger.info("Copy done in %s seconds" % (time.time() - copy_start))
1272 self.rootfs = newrootfs
1273 # Don't need a second copy now!
1274 self.snapshot = False
1275 self.cleanup_files.append(newrootfs)
1276
1213 qb_rootfs_opt = self.get('QB_ROOTFS_OPT') 1277 qb_rootfs_opt = self.get('QB_ROOTFS_OPT')
1214 if qb_rootfs_opt: 1278 if qb_rootfs_opt:
1215 self.rootfs_options = qb_rootfs_opt.replace('@ROOTFS@', self.rootfs) 1279 self.rootfs_options = qb_rootfs_opt.replace('@ROOTFS@', self.rootfs)
@@ -1237,6 +1301,10 @@ class BaseConfig(object):
1237 elif drive_type.startswith("/dev/hd"): 1301 elif drive_type.startswith("/dev/hd"):
1238 logger.info('Using ide drive') 1302 logger.info('Using ide drive')
1239 vm_drive = "-drive file=%s,format=%s" % (self.rootfs, rootfs_format) 1303 vm_drive = "-drive file=%s,format=%s" % (self.rootfs, rootfs_format)
1304 elif drive_type.startswith("/dev/mmcblk"):
1305 logger.info('Using sdcard drive')
1306 vm_drive = '-drive id=sdcard0,if=none,file=%s,format=%s -device sdhci-pci -device sd-card,drive=sdcard0' \
1307 % (self.rootfs, rootfs_format)
1240 elif drive_type.startswith("/dev/vdb"): 1308 elif drive_type.startswith("/dev/vdb"):
1241 logger.info('Using block virtio drive'); 1309 logger.info('Using block virtio drive');
1242 vm_drive = '-drive id=disk0,file=%s,if=none,format=%s -device virtio-blk-device,drive=disk0%s' \ 1310 vm_drive = '-drive id=disk0,file=%s,if=none,format=%s -device virtio-blk-device,drive=disk0%s' \
@@ -1254,7 +1322,13 @@ class BaseConfig(object):
1254 self.rootfs_options = vm_drive 1322 self.rootfs_options = vm_drive
1255 if not self.fstype in self.vmtypes: 1323 if not self.fstype in self.vmtypes:
1256 self.rootfs_options += ' -no-reboot' 1324 self.rootfs_options += ' -no-reboot'
1257 self.kernel_cmdline = 'root=%s rw' % (self.get('QB_KERNEL_ROOT')) 1325
1326 # By default, ' rw' is appended to QB_KERNEL_ROOT unless either ro or rw is explicitly passed.
1327 qb_kernel_root = self.get('QB_KERNEL_ROOT')
1328 qb_kernel_root_l = qb_kernel_root.split()
1329 if not ('ro' in qb_kernel_root_l or 'rw' in qb_kernel_root_l):
1330 qb_kernel_root += ' rw'
1331 self.kernel_cmdline = 'root=%s' % qb_kernel_root
1258 1332
1259 if self.fstype == 'nfs': 1333 if self.fstype == 'nfs':
1260 self.rootfs_options = '' 1334 self.rootfs_options = ''
@@ -1270,7 +1344,7 @@ class BaseConfig(object):
1270 """attempt to determine the appropriate qemu-system binary""" 1344 """attempt to determine the appropriate qemu-system binary"""
1271 mach = self.get('MACHINE') 1345 mach = self.get('MACHINE')
1272 if not mach: 1346 if not mach:
1273 search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*' 1347 search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemuloongarch64|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*'
1274 if self.rootfs: 1348 if self.rootfs:
1275 match = re.match(search, self.rootfs) 1349 match = re.match(search, self.rootfs)
1276 if match: 1350 if match:
@@ -1293,6 +1367,8 @@ class BaseConfig(object):
1293 qbsys = 'x86_64' 1367 qbsys = 'x86_64'
1294 elif mach == 'qemuppc': 1368 elif mach == 'qemuppc':
1295 qbsys = 'ppc' 1369 qbsys = 'ppc'
1370 elif mach == 'qemuloongarch64':
1371 qbsys = 'loongarch64'
1296 elif mach == 'qemumips': 1372 elif mach == 'qemumips':
1297 qbsys = 'mips' 1373 qbsys = 'mips'
1298 elif mach == 'qemumips64': 1374 elif mach == 'qemumips64':
@@ -1321,7 +1397,127 @@ class BaseConfig(object):
1321 raise RunQemuError("Failed to boot, QB_SYSTEM_NAME is NULL!") 1397 raise RunQemuError("Failed to boot, QB_SYSTEM_NAME is NULL!")
1322 self.qemu_system = qemu_system 1398 self.qemu_system = qemu_system
1323 1399
1324 def setup_final(self): 1400 def check_render_nodes(self):
1401 render_hint = """If /dev/dri/renderD* is absent due to lack of suitable GPU, 'modprobe vgem' will create one suitable for mesa llvmpipe software renderer."""
1402 try:
1403 content = os.listdir("/dev/dri")
1404 nodes = [i for i in content if i.startswith('renderD')]
1405 if len(nodes) == 0:
1406 raise RunQemuError("No render nodes found in /dev/dri/: %s. %s" %(content, render_hint))
1407 for n in nodes:
1408 try:
1409 with open(os.path.join("/dev/dri", n), "w") as f:
1410 f.close()
1411 break
1412 except IOError:
1413 pass
1414 else:
1415 raise RunQemuError("None of the render nodes in /dev/dri/ are accessible: %s; you may need to add yourself to 'render' group or otherwise ensure you have read-write permissions on one of them." %(nodes))
1416 except FileNotFoundError:
1417 raise RunQemuError("/dev/dri directory does not exist; no render nodes available on this machine. %s" %(render_hint))
1418
1419 def setup_guest_agent(self):
1420 if self.guest_agent == True:
1421 self.qemu_opt += ' -chardev socket,path=' + self.guest_agent_sockpath + ',server,nowait,id=qga0 '
1422 self.qemu_opt += ' -device virtio-serial '
1423 self.qemu_opt += ' -device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0 '
1424
1425 def setup_qmp(self):
1426 if self.qmp:
1427 self.qemu_opt += " -qmp %s,server,nowait" % self.qmp
1428
1429 def setup_vga(self):
1430 if self.nographic == True:
1431 if self.sdl == True:
1432 raise RunQemuError('Option nographic makes no sense alongside the sdl option.')
1433 if self.gtk == True:
1434 raise RunQemuError('Option nographic makes no sense alongside the gtk option.')
1435 self.qemu_opt += ' -nographic'
1436
1437 if self.novga == True:
1438 self.qemu_opt += ' -vga none'
1439 return
1440
1441 if (self.gl_es == True or self.gl == True) and (self.sdl == False and self.gtk == False):
1442 raise RunQemuError('Option gl/gl-es needs gtk or sdl option.')
1443
1444 # If we have no display option, we autodetect based upon what qemu supports. We
1445 # need our font setup and show-cusor below so we need to see what qemu --help says
1446 # is supported so we can pass our correct config in.
1447 if not self.nographic and not self.sdl and not self.gtk and not self.publicvnc and not self.egl_headless == True:
1448 output = subprocess.check_output([self.qemu_bin, "--help"], universal_newlines=True, env=self.qemu_environ)
1449 if "-display gtk" in output:
1450 self.gtk = True
1451 elif "-display sdl" in output:
1452 self.sdl = True
1453 else:
1454 self.qemu_opt += ' -display none'
1455
1456 if self.sdl == True or self.gtk == True or self.egl_headless == True:
1457
1458 if self.qemu_system.endswith(('i386', 'x86_64')):
1459 if self.gl or self.gl_es or self.egl_headless:
1460 self.qemu_opt += ' -device virtio-vga-gl '
1461 else:
1462 self.qemu_opt += ' -device virtio-vga '
1463
1464 self.qemu_opt += ' -display '
1465 if self.egl_headless == True:
1466 self.check_render_nodes()
1467 self.set_mesa_paths()
1468 self.qemu_opt += 'egl-headless,'
1469 else:
1470 if self.sdl == True:
1471 self.qemu_opt += 'sdl,'
1472 elif self.gtk == True:
1473 self.qemu_environ['FONTCONFIG_PATH'] = '/etc/fonts'
1474 self.qemu_opt += 'gtk,'
1475
1476 if self.gl == True:
1477 self.set_mesa_paths()
1478 self.qemu_opt += 'gl=on,'
1479 elif self.gl_es == True:
1480 self.set_mesa_paths()
1481 self.qemu_opt += 'gl=es,'
1482 self.qemu_opt += 'show-cursor=on'
1483
1484 self.qemu_opt += ' %s' %self.get('QB_GRAPHICS')
1485
1486 def setup_serial(self):
1487 # Setup correct kernel command line for serial
1488 if self.get('SERIAL_CONSOLES') and (self.serialstdio == True or self.serialconsole == True or self.nographic == True or self.tcpserial_portnum):
1489 for entry in self.get('SERIAL_CONSOLES').split(' '):
1490 self.kernel_cmdline_script += ' console=%s' %entry.split(';')[1]
1491
1492 # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES).
1493 # If no serial or serialtcp options were specified, only ttyS0 is created
1494 # and sysvinit shows an error trying to enable ttyS1:
1495 # INIT: Id "S1" respawning too fast: disabled for 5 minutes
1496 serial_num = len(re.findall("(^| )-serial ", self.qemu_opt))
1497
1498 # Assume if the user passed serial options, they know what they want
1499 # and pad to two devices
1500 if serial_num == 1:
1501 self.qemu_opt += " -serial null"
1502 elif serial_num >= 2:
1503 return
1504
1505 if self.serialstdio == True or self.nographic == True:
1506 self.qemu_opt += " -serial mon:stdio"
1507 else:
1508 self.qemu_opt += " -serial mon:vc"
1509 if self.serialconsole:
1510 if sys.stdin.isatty():
1511 subprocess.check_call(("stty", "intr", "^]"))
1512 logger.info("Interrupt character is '^]'")
1513
1514 self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT")
1515
1516 serial_num = len(re.findall("(^| )-serial ", self.qemu_opt))
1517 if serial_num < 2:
1518 self.qemu_opt += " -serial null"
1519
1520 def find_qemu(self):
1325 qemu_bin = os.path.join(self.bindir_native, self.qemu_system) 1521 qemu_bin = os.path.join(self.bindir_native, self.qemu_system)
1326 1522
1327 # It is possible to have qemu-native in ASSUME_PROVIDED, and it won't 1523 # It is possible to have qemu-native in ASSUME_PROVIDED, and it won't
@@ -1340,11 +1536,18 @@ class BaseConfig(object):
1340 1536
1341 if not os.access(qemu_bin, os.X_OK): 1537 if not os.access(qemu_bin, os.X_OK):
1342 raise OEPathError("No QEMU binary '%s' could be found" % qemu_bin) 1538 raise OEPathError("No QEMU binary '%s' could be found" % qemu_bin)
1539 self.qemu_bin = qemu_bin
1343 1540
1344 self.qemu_opt = "%s %s %s %s %s" % (qemu_bin, self.get('NETWORK_CMD'), self.get('QB_RNG'), self.get('ROOTFS_OPTIONS'), self.get('QB_OPT_APPEND')) 1541 def setup_final(self):
1542
1543 self.find_qemu()
1544
1545 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')))
1345 1546
1346 for ovmf in self.ovmf_bios: 1547 for ovmf in self.ovmf_bios:
1347 format = ovmf.rsplit('.', 1)[-1] 1548 format = ovmf.rsplit('.', 1)[-1]
1549 if format == "bin":
1550 format = "raw"
1348 self.qemu_opt += ' -drive if=pflash,format=%s,file=%s' % (format, ovmf) 1551 self.qemu_opt += ' -drive if=pflash,format=%s,file=%s' % (format, ovmf)
1349 1552
1350 self.qemu_opt += ' ' + self.qemu_opt_script 1553 self.qemu_opt += ' ' + self.qemu_opt_script
@@ -1363,61 +1566,44 @@ class BaseConfig(object):
1363 if self.snapshot: 1566 if self.snapshot:
1364 self.qemu_opt += " -snapshot" 1567 self.qemu_opt += " -snapshot"
1365 1568
1366 if self.serialconsole: 1569 self.setup_guest_agent()
1367 if sys.stdin.isatty(): 1570 self.setup_qmp()
1368 subprocess.check_call(("stty", "intr", "^]")) 1571 self.setup_serial()
1369 logger.info("Interrupt character is '^]'") 1572 self.setup_vga()
1370
1371 first_serial = ""
1372 if not re.search("-nographic", self.qemu_opt):
1373 first_serial = "-serial mon:vc"
1374 # We always want a ttyS1. Since qemu by default adds a serial
1375 # port when nodefaults is not specified, it seems that all that
1376 # would be needed is to make sure a "-serial" is there. However,
1377 # it appears that when "-serial" is specified, it ignores the
1378 # default serial port that is normally added. So here we make
1379 # sure to add two -serial if there are none. And only one if
1380 # there is one -serial already.
1381 serial_num = len(re.findall("-serial", self.qemu_opt))
1382 if serial_num == 0:
1383 self.qemu_opt += " %s %s" % (first_serial, self.get("QB_SERIAL_OPT"))
1384 elif serial_num == 1:
1385 self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT")
1386
1387 # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES),
1388 # if not serial or serialtcp options was specified only ttyS0 is created
1389 # and sysvinit shows an error trying to enable ttyS1:
1390 # INIT: Id "S1" respawning too fast: disabled for 5 minutes
1391 serial_num = len(re.findall("-serial", self.qemu_opt))
1392 if serial_num == 0:
1393 if re.search("-nographic", self.qemu_opt) or self.serialstdio:
1394 self.qemu_opt += " -serial mon:stdio -serial null"
1395 else:
1396 self.qemu_opt += " -serial mon:vc -serial null"
1397 1573
1398 def start_qemu(self): 1574 def start_qemu(self):
1399 import shlex 1575 import shlex
1400 if self.kernel: 1576 if self.kernel:
1401 kernel_opts = "-kernel %s -append '%s %s %s %s'" % (self.kernel, self.kernel_cmdline, 1577 kernel_opts = "-kernel %s" % (self.kernel)
1578 if self.get('QB_KERNEL_CMDLINE') == "none":
1579 if self.bootparams:
1580 kernel_opts += " -append '%s'" % (self.bootparams)
1581 else:
1582 kernel_opts += " -append '%s %s %s %s'" % (self.kernel_cmdline,
1402 self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'), 1583 self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'),
1403 self.bootparams) 1584 self.bootparams)
1404 if self.bios:
1405 kernel_opts += " -bios %s" % self.bios
1406 if self.dtb: 1585 if self.dtb:
1407 kernel_opts += " -dtb %s" % self.dtb 1586 kernel_opts += " -dtb %s" % self.dtb
1408 else: 1587 else:
1409 kernel_opts = "" 1588 kernel_opts = ""
1589
1590 if self.bios:
1591 self.qemu_opt += " -bios %s" % self.bios
1592
1410 cmd = "%s %s" % (self.qemu_opt, kernel_opts) 1593 cmd = "%s %s" % (self.qemu_opt, kernel_opts)
1411 cmds = shlex.split(cmd) 1594 cmds = shlex.split(cmd)
1412 logger.info('Running %s\n' % cmd) 1595 logger.info('Running %s\n' % cmd)
1596 with open('/proc/uptime', 'r') as f:
1597 uptime_seconds = f.readline().split()[0]
1598 logger.info('Host uptime: %s\n' % uptime_seconds)
1413 pass_fds = [] 1599 pass_fds = []
1414 if self.taplock_descriptor: 1600 if self.taplock_descriptor:
1415 pass_fds = [self.taplock_descriptor.fileno()] 1601 pass_fds = [self.taplock_descriptor.fileno()]
1416 if len(self.portlocks): 1602 if len(self.portlocks):
1417 for descriptor in self.portlocks.values(): 1603 for descriptor in self.portlocks.values():
1418 pass_fds.append(descriptor.fileno()) 1604 pass_fds.append(descriptor.fileno())
1419 process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds) 1605 process = subprocess.Popen(cmds, stderr=subprocess.PIPE, pass_fds=pass_fds, env=self.qemu_environ)
1420 self.qemupid = process.pid 1606 self.qemuprocess = process
1421 retcode = process.wait() 1607 retcode = process.wait()
1422 if retcode: 1608 if retcode:
1423 if retcode == -signal.SIGTERM: 1609 if retcode == -signal.SIGTERM:
@@ -1425,6 +1611,13 @@ class BaseConfig(object):
1425 else: 1611 else:
1426 logger.error("Failed to run qemu: %s", process.stderr.read().decode()) 1612 logger.error("Failed to run qemu: %s", process.stderr.read().decode())
1427 1613
1614 def cleanup_cmd(self):
1615 cmd = self.get('QB_CLEANUP_CMD')
1616 if cmd != '':
1617 logger.info('Running cleanup command %s' % str(cmd))
1618 if subprocess.call(cmd, shell=True) != 0:
1619 raise RunQemuError('Failed to run %s' % str(cmd))
1620
1428 def cleanup(self): 1621 def cleanup(self):
1429 if self.cleaned: 1622 if self.cleaned:
1430 return 1623 return
@@ -1433,30 +1626,48 @@ class BaseConfig(object):
1433 signal.signal(signal.SIGTERM, signal.SIG_IGN) 1626 signal.signal(signal.SIGTERM, signal.SIG_IGN)
1434 1627
1435 logger.info("Cleaning up") 1628 logger.info("Cleaning up")
1629
1630 if self.qemuprocess:
1631 try:
1632 # give it some time to shut down, ignore return values and output
1633 self.qemuprocess.send_signal(signal.SIGTERM)
1634 self.qemuprocess.communicate(timeout=5)
1635 except subprocess.TimeoutExpired:
1636 self.qemuprocess.kill()
1637
1638 with open('/proc/uptime', 'r') as f:
1639 uptime_seconds = f.readline().split()[0]
1640 logger.info('Host uptime: %s\n' % uptime_seconds)
1436 if self.cleantap: 1641 if self.cleantap:
1437 cmd = ('sudo', self.qemuifdown, self.tap, self.bindir_native) 1642 cmd = ('sudo', self.qemuifdown, self.tap)
1438 logger.debug('Running %s' % str(cmd)) 1643 logger.debug('Running %s' % str(cmd))
1439 subprocess.check_call(cmd) 1644 subprocess.check_call(cmd)
1440 self.release_taplock() 1645 self.release_taplock()
1441 self.release_portlock()
1442 1646
1443 if self.nfs_running: 1647 if self.nfs_running:
1444 logger.info("Shutting down the userspace NFS server...") 1648 logger.info("Shutting down the userspace NFS server...")
1445 cmd = ("runqemu-export-rootfs", "stop", self.rootfs) 1649 cmd = ("runqemu-export-rootfs", "stop", self.rootfs)
1446 logger.debug('Running %s' % str(cmd)) 1650 logger.debug('Running %s' % str(cmd))
1447 subprocess.check_call(cmd) 1651 subprocess.check_call(cmd)
1652 self.release_portlock()
1448 1653
1449 if self.saved_stty: 1654 if self.saved_stty:
1450 subprocess.check_call(("stty", self.saved_stty)) 1655 subprocess.check_call(("stty", self.saved_stty))
1451 1656
1452 if self.clean_nfs_dir: 1657 if self.cleanup_files:
1453 logger.info('Removing %s' % self.rootfs) 1658 for ent in self.cleanup_files:
1454 shutil.rmtree(self.rootfs) 1659 logger.info('Removing %s' % ent)
1455 shutil.rmtree('%s.pseudo_state' % self.rootfs) 1660 if os.path.isfile(ent):
1661 os.remove(ent)
1662 else:
1663 shutil.rmtree(ent)
1664
1665 # Deliberately ignore the return code of 'tput smam'.
1666 subprocess.call(["tput", "smam"])
1456 1667
1457 self.cleaned = True 1668 self.cleaned = True
1458 1669
1459 def run_bitbake_env(self, mach=None): 1670 def run_bitbake_env(self, mach=None, target=''):
1460 bitbake = shutil.which('bitbake') 1671 bitbake = shutil.which('bitbake')
1461 if not bitbake: 1672 if not bitbake:
1462 return 1673 return
@@ -1468,23 +1679,37 @@ class BaseConfig(object):
1468 if multiconfig: 1679 if multiconfig:
1469 multiconfig = "mc:%s" % multiconfig 1680 multiconfig = "mc:%s" % multiconfig
1470 1681
1682 if self.rootfs and not target:
1683 target = self.rootfs
1684
1471 if mach: 1685 if mach:
1472 cmd = 'MACHINE=%s bitbake -e %s' % (mach, multiconfig) 1686 cmd = 'MACHINE=%s bitbake -e %s %s' % (mach, multiconfig, target)
1473 else: 1687 else:
1474 cmd = 'bitbake -e %s' % multiconfig 1688 cmd = 'bitbake -e %s %s' % (multiconfig, target)
1475 1689
1476 logger.info('Running %s...' % cmd) 1690 logger.info('Running %s...' % cmd)
1477 return subprocess.check_output(cmd, shell=True).decode('utf-8') 1691 try:
1692 return subprocess.check_output(cmd, shell=True).decode('utf-8')
1693 except subprocess.CalledProcessError as err:
1694 logger.warning("Couldn't run '%s' to gather environment information, maybe the target wasn't an image name, will retry with virtual/kernel as a target:\n%s" % (cmd, err.output.decode('utf-8')))
1695 # need something with IMAGE_NAME_SUFFIX/IMAGE_LINK_NAME defined (kernel also inherits image-artifact-names.bbclass)
1696 target = 'virtual/kernel'
1697 if mach:
1698 cmd = 'MACHINE=%s bitbake -e %s %s' % (mach, multiconfig, target)
1699 else:
1700 cmd = 'bitbake -e %s %s' % (multiconfig, target)
1701 try:
1702 return subprocess.check_output(cmd, shell=True).decode('utf-8')
1703 except subprocess.CalledProcessError as err:
1704 logger.warning("Couldn't run '%s' to gather environment information, giving up with 'bitbake -e':\n%s" % (cmd, err.output.decode('utf-8')))
1705 return ''
1706
1478 1707
1479 def load_bitbake_env(self, mach=None): 1708 def load_bitbake_env(self, mach=None, target=None):
1480 if self.bitbake_e: 1709 if self.bitbake_e:
1481 return 1710 return
1482 1711
1483 try: 1712 self.bitbake_e = self.run_bitbake_env(mach=mach, target=target)
1484 self.bitbake_e = self.run_bitbake_env(mach=mach)
1485 except subprocess.CalledProcessError as err:
1486 self.bitbake_e = ''
1487 logger.warning("Couldn't run 'bitbake -e' to gather environment information:\n%s" % err.output.decode('utf-8'))
1488 1713
1489 def validate_combos(self): 1714 def validate_combos(self):
1490 if (self.fstype in self.vmtypes) and self.kernel: 1715 if (self.fstype in self.vmtypes) and self.kernel:
@@ -1514,7 +1739,7 @@ class BaseConfig(object):
1514 return result 1739 return result
1515 raise RunQemuError("Native sysroot directory %s doesn't exist" % result) 1740 raise RunQemuError("Native sysroot directory %s doesn't exist" % result)
1516 else: 1741 else:
1517 raise RunQemuError("Can't find STAGING_BINDIR_NATIVE in '%s' output" % cmd) 1742 raise RunQemuError("Can't find STAGING_BINDIR_NATIVE in '%s' output" % str(cmd))
1518 1743
1519 1744
1520def main(): 1745def main():
@@ -1530,11 +1755,8 @@ def main():
1530 subprocess.check_call([renice, str(os.getpid())]) 1755 subprocess.check_call([renice, str(os.getpid())])
1531 1756
1532 def sigterm_handler(signum, frame): 1757 def sigterm_handler(signum, frame):
1533 logger.info("SIGTERM received") 1758 logger.info("Received signal: %s" % (signum))
1534 os.kill(config.qemupid, signal.SIGTERM)
1535 config.cleanup() 1759 config.cleanup()
1536 # Deliberately ignore the return code of 'tput smam'.
1537 subprocess.call(["tput", "smam"])
1538 signal.signal(signal.SIGTERM, sigterm_handler) 1760 signal.signal(signal.SIGTERM, sigterm_handler)
1539 1761
1540 config.check_args() 1762 config.check_args()
@@ -1546,6 +1768,7 @@ def main():
1546 config.setup_network() 1768 config.setup_network()
1547 config.setup_rootfs() 1769 config.setup_rootfs()
1548 config.setup_final() 1770 config.setup_final()
1771 config.setup_cmd()
1549 config.start_qemu() 1772 config.start_qemu()
1550 except RunQemuError as err: 1773 except RunQemuError as err:
1551 logger.error(err) 1774 logger.error(err)
@@ -1555,9 +1778,8 @@ def main():
1555 traceback.print_exc() 1778 traceback.print_exc()
1556 return 1 1779 return 1
1557 finally: 1780 finally:
1781 config.cleanup_cmd()
1558 config.cleanup() 1782 config.cleanup()
1559 # Deliberately ignore the return code of 'tput smam'.
1560 subprocess.call(["tput", "smam"])
1561 1783
1562if __name__ == "__main__": 1784if __name__ == "__main__":
1563 sys.exit(main()) 1785 sys.exit(main())