summaryrefslogtreecommitdiffstats
path: root/scripts/runqemu
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/runqemu')
-rwxr-xr-xscripts/runqemu466
1 files changed, 313 insertions, 153 deletions
diff --git a/scripts/runqemu b/scripts/runqemu
index efb98ab9e0..3d77046972 100755
--- a/scripts/runqemu
+++ b/scripts/runqemu
@@ -66,6 +66,7 @@ of the following environment variables (in any order):
66 MACHINE - the machine name (optional, autodetected from KERNEL filename if unspecified) 66 MACHINE - the machine name (optional, autodetected from KERNEL filename if unspecified)
67 Simplified QEMU command-line options can be passed with: 67 Simplified QEMU command-line options can be passed with:
68 nographic - disable video console 68 nographic - disable video console
69 nonetwork - disable network connectivity
69 novga - Disable VGA emulation completely 70 novga - Disable VGA emulation completely
70 sdl - choose the SDL UI frontend 71 sdl - choose the SDL UI frontend
71 gtk - choose the Gtk UI frontend 72 gtk - choose the Gtk UI frontend
@@ -73,15 +74,17 @@ of the following environment variables (in any order):
73 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)
74 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
75 (hint: if /dev/dri/renderD* is absent due to lack of suitable GPU, 'modprobe vgem' will create 76 (hint: if /dev/dri/renderD* is absent due to lack of suitable GPU, 'modprobe vgem' will create
76 one sutable for mesa llvmpipe sofware renderer) 77 one suitable for mesa llvmpipe software renderer)
77 serial - enable a serial console on /dev/ttyS0 78 serial - enable a serial console on /dev/ttyS0
78 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)
79 slirp - enable user networking, no root privileges is required 80 slirp - enable user networking, no root privilege is required
80 snapshot - don't write changes to back to images 81 snapshot - don't write changes back to images
81 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)
82 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)
83 publicvnc - enable a VNC server open to all hosts 84 publicvnc - enable a VNC server open to all hosts
84 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)
85 [*/]ovmf* - OVMF firmware file or base name for booting with UEFI 88 [*/]ovmf* - OVMF firmware file or base name for booting with UEFI
86 tcpserial=<port> - specify tcp serial port number 89 tcpserial=<port> - specify tcp serial port number
87 qemuparams=<xyz> - specify custom parameters to QEMU 90 qemuparams=<xyz> - specify custom parameters to QEMU
@@ -116,10 +119,10 @@ def check_tun():
116 if not os.access(dev_tun, os.W_OK): 119 if not os.access(dev_tun, os.W_OK):
117 raise RunQemuError("TUN control device %s is not writable, please fix (e.g. sudo chmod 666 %s)" % (dev_tun, dev_tun)) 120 raise RunQemuError("TUN control device %s is not writable, please fix (e.g. sudo chmod 666 %s)" % (dev_tun, dev_tun))
118 121
119def get_first_file(cmds): 122def get_first_file(globs):
120 """Return first file found in wildcard cmds""" 123 """Return first file found in wildcard globs"""
121 for cmd in cmds: 124 for g in globs:
122 all_files = glob.glob(cmd) 125 all_files = glob.glob(g)
123 if all_files: 126 if all_files:
124 for f in all_files: 127 for f in all_files:
125 if not os.path.isdir(f): 128 if not os.path.isdir(f):
@@ -177,11 +180,13 @@ class BaseConfig(object):
177 self.serialconsole = False 180 self.serialconsole = False
178 self.serialstdio = False 181 self.serialstdio = False
179 self.nographic = False 182 self.nographic = False
183 self.nonetwork = False
180 self.sdl = False 184 self.sdl = False
181 self.gtk = False 185 self.gtk = False
182 self.gl = False 186 self.gl = False
183 self.gl_es = False 187 self.gl_es = False
184 self.egl_headless = False 188 self.egl_headless = False
189 self.publicvnc = False
185 self.novga = False 190 self.novga = False
186 self.cleantap = False 191 self.cleantap = False
187 self.saved_stty = '' 192 self.saved_stty = ''
@@ -194,12 +199,14 @@ class BaseConfig(object):
194 self.snapshot = False 199 self.snapshot = False
195 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")
196 self.fstypes = ('ext2', 'ext3', 'ext4', 'jffs2', 'nfs', 'btrfs', 201 self.fstypes = ('ext2', 'ext3', 'ext4', 'jffs2', 'nfs', 'btrfs',
197 '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')
198 self.vmtypes = ('hddimg', 'iso') 205 self.vmtypes = ('hddimg', 'iso')
199 self.fsinfo = {} 206 self.fsinfo = {}
200 self.network_device = "-device e1000,netdev=net0,mac=@MAC@" 207 self.network_device = "-device e1000,netdev=net0,mac=@MAC@"
201 self.cmdline_ip_slirp = "ip=dhcp" 208 self.cmdline_ip_slirp = "ip=dhcp"
202 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"
203 # Use different mac section for tap and slirp to avoid 210 # Use different mac section for tap and slirp to avoid
204 # 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
205 # running with slirp. 212 # running with slirp.
@@ -209,11 +216,15 @@ class BaseConfig(object):
209 self.mac_tap = "52:54:00:12:34:" 216 self.mac_tap = "52:54:00:12:34:"
210 self.mac_slirp = "52:54:00:12:35:" 217 self.mac_slirp = "52:54:00:12:35:"
211 # pid of the actual qemu process 218 # pid of the actual qemu process
212 self.qemupid = None 219 self.qemu_environ = os.environ.copy()
220 self.qemuprocess = None
213 # avoid cleanup twice 221 # avoid cleanup twice
214 self.cleaned = False 222 self.cleaned = False
215 # Files to cleanup after run 223 # Files to cleanup after run
216 self.cleanup_files = [] 224 self.cleanup_files = []
225 self.qmp = None
226 self.guest_agent = False
227 self.guest_agent_sockpath = '/tmp/qga.sock'
217 228
218 def acquire_taplock(self, error=True): 229 def acquire_taplock(self, error=True):
219 logger.debug("Acquiring lockfile %s..." % self.taplock) 230 logger.debug("Acquiring lockfile %s..." % self.taplock)
@@ -352,21 +363,21 @@ class BaseConfig(object):
352 def check_arg_path(self, p): 363 def check_arg_path(self, p):
353 """ 364 """
354 - 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
355 - Check whether is a kernel file 366 - Check whether it is a kernel file
356 - Check whether is a image file 367 - Check whether it is an image file
357 - Check whether it is a nfs dir 368 - Check whether it is an NFS dir
358 - Check whether it is a OVMF flash file 369 - Check whether it is an OVMF flash file
359 """ 370 """
360 if p.endswith('.qemuboot.conf'): 371 if p.endswith('.qemuboot.conf'):
361 self.qemuboot = p 372 self.qemuboot = p
362 self.qbconfload = True 373 self.qbconfload = True
363 elif re.search('\.bin$', p) or re.search('bzImage', p) or \ 374 elif re.search('\\.bin$', p) or re.search('bzImage', p) or \
364 re.search('zImage', p) or re.search('vmlinux', p) or \ 375 re.search('zImage', p) or re.search('vmlinux', p) or \
365 re.search('fitImage', p) or re.search('uImage', p): 376 re.search('fitImage', p) or re.search('uImage', p):
366 self.kernel = p 377 self.kernel = p
367 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)):
368 self.rootfs = p 379 self.rootfs = p
369 # Check filename against self.fstypes can hanlde <file>.cpio.gz, 380 # Check filename against self.fstypes can handle <file>.cpio.gz,
370 # otherwise, its type would be "gz", which is incorrect. 381 # otherwise, its type would be "gz", which is incorrect.
371 fst = "" 382 fst = ""
372 for t in self.fstypes: 383 for t in self.fstypes:
@@ -374,18 +385,24 @@ class BaseConfig(object):
374 fst = t 385 fst = t
375 break 386 break
376 if not fst: 387 if not fst:
377 m = re.search('.*\.(.*)$', self.rootfs) 388 m = re.search('.*\\.(.*)$', self.rootfs)
378 if m: 389 if m:
379 fst = m.group(1) 390 fst = m.group(1)
380 if fst: 391 if fst:
381 self.check_arg_fstype(fst) 392 self.check_arg_fstype(fst)
382 qb = re.sub('\.' + fst + "$", '', self.rootfs) 393 qb = re.sub('\\.' + fst + "$", '.qemuboot.conf', self.rootfs)
383 qb = '%s%s' % (re.sub('\.rootfs$', '', qb), '.qemuboot.conf')
384 if os.path.exists(qb): 394 if os.path.exists(qb):
385 self.qemuboot = qb 395 self.qemuboot = qb
386 self.qbconfload = True 396 self.qbconfload = True
387 else: 397 else:
388 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)
389 else: 406 else:
390 raise RunQemuError("Can't find FSTYPE from: %s" % p) 407 raise RunQemuError("Can't find FSTYPE from: %s" % p)
391 408
@@ -419,6 +436,7 @@ class BaseConfig(object):
419 # are there other scenarios in which we need to support being 436 # are there other scenarios in which we need to support being
420 # invoked by bitbake? 437 # invoked by bitbake?
421 deploy = self.get('DEPLOY_DIR_IMAGE') 438 deploy = self.get('DEPLOY_DIR_IMAGE')
439 image_link_name = self.get('IMAGE_LINK_NAME')
422 bbchild = deploy and self.get('OE_TMPDIR') 440 bbchild = deploy and self.get('OE_TMPDIR')
423 if bbchild: 441 if bbchild:
424 self.set_machine_deploy_dir(arg, deploy) 442 self.set_machine_deploy_dir(arg, deploy)
@@ -443,23 +461,27 @@ class BaseConfig(object):
443 else: 461 else:
444 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)
445 self.set("MACHINE", arg) 463 self.set("MACHINE", arg)
446 464 if not image_link_name:
447 def set_dri_path(self): 465 s = re.search('^IMAGE_LINK_NAME="(.*)"', self.bitbake_e, re.M)
448 # As runqemu can be run within bitbake (when using testimage, for example), 466 if s:
449 # we need to ensure that we run host pkg-config, and that it does not 467 image_link_name = s.group(1)
450 # get mis-directed to native build paths set by bitbake. 468 self.set("IMAGE_LINK_NAME", image_link_name)
451 try: 469 logger.debug('Using IMAGE_LINK_NAME = "%s"' % image_link_name)
452 del os.environ['PKG_CONFIG_PATH'] 470
453 del os.environ['PKG_CONFIG_DIR'] 471 def set_mesa_paths(self):
454 del os.environ['PKG_CONFIG_LIBDIR'] 472 drivers_path = os.path.join(self.bindir_native, '../lib/dri')
455 del os.environ['PKG_CONFIG_SYSROOT_DIR'] 473 gbm_path = os.path.join(self.bindir_native, '../lib/gbm')
456 except KeyError: 474 if not os.path.exists(drivers_path) or not os.listdir(drivers_path) \
457 pass 475 or not os.path.exists(gbm_path) or not os.listdir(gbm_path):
458 try: 476 raise RunQemuError("""
459 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.
460 except subprocess.CalledProcessError as e: 478To enable it, add:
461 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"
462 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
463 485
464 def check_args(self): 486 def check_args(self):
465 for debug in ("-d", "--debug"): 487 for debug in ("-d", "--debug"):
@@ -473,7 +495,8 @@ class BaseConfig(object):
473 sys.argv.remove(quiet) 495 sys.argv.remove(quiet)
474 496
475 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:]:
476 os.environ['SDL_RENDER_DRIVER'] = 'software' 498 self.qemu_environ['SDL_RENDER_DRIVER'] = 'software'
499 self.qemu_environ['SDL_FRAMEBUFFER_ACCELERATION'] = 'false'
477 500
478 unknown_arg = "" 501 unknown_arg = ""
479 for arg in sys.argv[1:]: 502 for arg in sys.argv[1:]:
@@ -481,13 +504,15 @@ class BaseConfig(object):
481 self.check_arg_fstype(arg) 504 self.check_arg_fstype(arg)
482 elif arg == 'nographic': 505 elif arg == 'nographic':
483 self.nographic = True 506 self.nographic = True
507 elif arg == "nonetwork":
508 self.nonetwork = True
484 elif arg == 'sdl': 509 elif arg == 'sdl':
485 self.sdl = True 510 self.sdl = True
486 elif arg == 'gtk': 511 elif arg == 'gtk':
487 self.gtk = True 512 self.gtk = True
488 elif arg == 'gl': 513 elif arg == 'gl':
489 self.gl = True 514 self.gl = True
490 elif 'gl-es' in sys.argv[1:]: 515 elif arg == 'gl-es':
491 self.gl_es = True 516 self.gl_es = True
492 elif arg == 'egl-headless': 517 elif arg == 'egl-headless':
493 self.egl_headless = True 518 self.egl_headless = True
@@ -512,7 +537,16 @@ class BaseConfig(object):
512 elif arg == 'snapshot': 537 elif arg == 'snapshot':
513 self.snapshot = True 538 self.snapshot = True
514 elif arg == 'publicvnc': 539 elif arg == 'publicvnc':
540 self.publicvnc = True
515 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='):]
516 elif arg.startswith('tcpserial='): 550 elif arg.startswith('tcpserial='):
517 self.tcpserial_portnum = '%s' % arg[len('tcpserial='):] 551 self.tcpserial_portnum = '%s' % arg[len('tcpserial='):]
518 elif arg.startswith('qemuparams='): 552 elif arg.startswith('qemuparams='):
@@ -544,11 +578,18 @@ class BaseConfig(object):
544 self.check_arg_machine(unknown_arg) 578 self.check_arg_machine(unknown_arg)
545 579
546 if not (self.get('DEPLOY_DIR_IMAGE') or self.qbconfload): 580 if not (self.get('DEPLOY_DIR_IMAGE') or self.qbconfload):
547 self.load_bitbake_env() 581 self.load_bitbake_env(target=self.rootfs)
548 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M) 582 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
549 if s: 583 if s:
550 self.set("DEPLOY_DIR_IMAGE", s.group(1)) 584 self.set("DEPLOY_DIR_IMAGE", s.group(1))
551 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
552 def check_kvm(self): 593 def check_kvm(self):
553 """Check kvm and kvm-host""" 594 """Check kvm and kvm-host"""
554 if not (self.kvm_enabled or self.vhost_enabled): 595 if not (self.kvm_enabled or self.vhost_enabled):
@@ -578,11 +619,6 @@ class BaseConfig(object):
578 619
579 if os.access(dev_kvm, os.W_OK|os.R_OK): 620 if os.access(dev_kvm, os.W_OK|os.R_OK):
580 self.qemu_opt_script += ' -enable-kvm' 621 self.qemu_opt_script += ' -enable-kvm'
581 if self.get('MACHINE') == "qemux86":
582 # Workaround for broken APIC window on pre 4.15 host kernels which causes boot hangs
583 # See YOCTO #12301
584 # On 64 bit we use x2apic
585 self.kernel_cmdline_script += " clocksource=kvm-clock hpet=disable noapic nolapic"
586 else: 622 else:
587 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.")
588 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:")
@@ -623,10 +659,10 @@ class BaseConfig(object):
623 elif fsflag == 'kernel-in-fs': 659 elif fsflag == 'kernel-in-fs':
624 wic_fs = False 660 wic_fs = False
625 else: 661 else:
626 logger.warn('Unknown flag "%s:%s" in QB_FSINFO', fstype, fsflag) 662 logger.warning('Unknown flag "%s:%s" in QB_FSINFO', fstype, fsflag)
627 continue 663 continue
628 else: 664 else:
629 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)
630 continue 666 continue
631 667
632 if fstype in self.fsinfo: 668 if fstype in self.fsinfo:
@@ -659,16 +695,16 @@ class BaseConfig(object):
659 695
660 if self.rootfs and not os.path.exists(self.rootfs): 696 if self.rootfs and not os.path.exists(self.rootfs):
661 # Lazy rootfs 697 # Lazy rootfs
662 self.rootfs = "%s/%s-%s.%s" % (self.get('DEPLOY_DIR_IMAGE'), 698 self.rootfs = "%s/%s.%s" % (self.get('DEPLOY_DIR_IMAGE'),
663 self.rootfs, self.get('MACHINE'), 699 self.get('IMAGE_LINK_NAME'),
664 self.fstype) 700 self.fstype)
665 elif not self.rootfs: 701 elif not self.rootfs:
666 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)
667 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)
668 cmds = (cmd_name, cmd_link) 704 globs = (glob_name, glob_link)
669 self.rootfs = get_first_file(cmds) 705 self.rootfs = get_first_file(globs)
670 if not self.rootfs: 706 if not self.rootfs:
671 raise RunQemuError("Failed to find rootfs: %s or %s" % cmds) 707 raise RunQemuError("Failed to find rootfs: %s or %s" % globs)
672 708
673 if not os.path.exists(self.rootfs): 709 if not os.path.exists(self.rootfs):
674 raise RunQemuError("Can't find rootfs: %s" % self.rootfs) 710 raise RunQemuError("Can't find rootfs: %s" % self.rootfs)
@@ -728,10 +764,10 @@ class BaseConfig(object):
728 kernel_match_name = "%s/%s" % (deploy_dir_image, kernel_name) 764 kernel_match_name = "%s/%s" % (deploy_dir_image, kernel_name)
729 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'))
730 kernel_startswith = "%s/%s*" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE')) 766 kernel_startswith = "%s/%s*" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
731 cmds = (kernel_match_name, kernel_match_link, kernel_startswith) 767 globs = (kernel_match_name, kernel_match_link, kernel_startswith)
732 self.kernel = get_first_file(cmds) 768 self.kernel = get_first_file(globs)
733 if not self.kernel: 769 if not self.kernel:
734 raise RunQemuError('KERNEL not found: %s, %s or %s' % cmds) 770 raise RunQemuError('KERNEL not found: %s, %s or %s' % globs)
735 771
736 if not os.path.exists(self.kernel): 772 if not os.path.exists(self.kernel):
737 raise RunQemuError("KERNEL %s not found" % self.kernel) 773 raise RunQemuError("KERNEL %s not found" % self.kernel)
@@ -748,13 +784,13 @@ class BaseConfig(object):
748 dtb = self.get('QB_DTB') 784 dtb = self.get('QB_DTB')
749 if dtb: 785 if dtb:
750 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE') 786 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
751 cmd_match = "%s/%s" % (deploy_dir_image, dtb) 787 glob_match = "%s/%s" % (deploy_dir_image, dtb)
752 cmd_startswith = "%s/%s*" % (deploy_dir_image, dtb) 788 glob_startswith = "%s/%s*" % (deploy_dir_image, dtb)
753 cmd_wild = "%s/*.dtb" % deploy_dir_image 789 glob_wild = "%s/*.dtb" % deploy_dir_image
754 cmds = (cmd_match, cmd_startswith, cmd_wild) 790 globs = (glob_match, glob_startswith, glob_wild)
755 self.dtb = get_first_file(cmds) 791 self.dtb = get_first_file(globs)
756 if not os.path.exists(self.dtb): 792 if not os.path.exists(self.dtb):
757 raise RunQemuError('DTB not found: %s, %s or %s' % cmds) 793 raise RunQemuError('DTB not found: %s, %s or %s' % globs)
758 794
759 def check_bios(self): 795 def check_bios(self):
760 """Check and set bios""" 796 """Check and set bios"""
@@ -805,7 +841,7 @@ class BaseConfig(object):
805 self.set('QB_MEM', qb_mem) 841 self.set('QB_MEM', qb_mem)
806 842
807 mach = self.get('MACHINE') 843 mach = self.get('MACHINE')
808 if not mach.startswith('qemumips'): 844 if not mach.startswith(('qemumips', 'qemux86', 'qemuloongarch64')):
809 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'
810 846
811 self.qemu_opt_script += ' %s' % self.get('QB_MEM') 847 self.qemu_opt_script += ' %s' % self.get('QB_MEM')
@@ -817,11 +853,11 @@ class BaseConfig(object):
817 if self.get('QB_TCPSERIAL_OPT'): 853 if self.get('QB_TCPSERIAL_OPT'):
818 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)
819 else: 855 else:
820 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
821 857
822 if len(ports) > 1: 858 if len(ports) > 1:
823 for port in ports[1:]: 859 for port in ports[1:]:
824 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
825 861
826 def check_and_set(self): 862 def check_and_set(self):
827 """Check configs sanity and set when needed""" 863 """Check configs sanity and set when needed"""
@@ -864,8 +900,10 @@ class BaseConfig(object):
864 machine = self.get('MACHINE') 900 machine = self.get('MACHINE')
865 if not machine: 901 if not machine:
866 machine = os.path.basename(deploy_dir_image) 902 machine = os.path.basename(deploy_dir_image)
867 self.qemuboot = "%s/%s-%s.qemuboot.conf" % (deploy_dir_image, 903 if not self.get('IMAGE_LINK_NAME'):
868 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'))
869 else: 907 else:
870 cmd = 'ls -t %s/*.qemuboot.conf' % deploy_dir_image 908 cmd = 'ls -t %s/*.qemuboot.conf' % deploy_dir_image
871 logger.debug('Running %s...' % cmd) 909 logger.debug('Running %s...' % cmd)
@@ -986,19 +1024,16 @@ class BaseConfig(object):
986 if self.slirp_enabled: 1024 if self.slirp_enabled:
987 self.nfs_server = '10.0.2.2' 1025 self.nfs_server = '10.0.2.2'
988 else: 1026 else:
989 self.nfs_server = '192.168.7.1' 1027 self.nfs_server = '192.168.7.@GATEWAY@'
990
991 # Figure out a new nfs_instance to allow multiple qemus running.
992 ps = subprocess.check_output(("ps", "auxww")).decode('utf-8')
993 pattern = '/bin/unfsd .* -i .*\.pid -e .*/exports([0-9]+) '
994 all_instances = re.findall(pattern, ps, re.M)
995 if all_instances:
996 all_instances.sort(key=int)
997 self.nfs_instance = int(all_instances.pop()) + 1
998 1028
999 nfsd_port = 3049 + 2 * self.nfs_instance 1029 nfsd_port = 3048 + self.nfs_instance
1000 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
1001 1035
1036 mountd_port = nfsd_port
1002 # Export vars for runqemu-export-rootfs 1037 # Export vars for runqemu-export-rootfs
1003 export_dict = { 1038 export_dict = {
1004 'NFS_INSTANCE': self.nfs_instance, 1039 'NFS_INSTANCE': self.nfs_instance,
@@ -1009,7 +1044,11 @@ class BaseConfig(object):
1009 # Use '%s' since they are integers 1044 # Use '%s' since they are integers
1010 os.putenv(k, '%s' % v) 1045 os.putenv(k, '%s' % v)
1011 1046
1012 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)
1013 1052
1014 # Extract .tar.bz2 or .tar.bz if no nfs dir 1053 # Extract .tar.bz2 or .tar.bz if no nfs dir
1015 if not (self.rootfs and os.path.isdir(self.rootfs)): 1054 if not (self.rootfs and os.path.isdir(self.rootfs)):
@@ -1032,7 +1071,7 @@ class BaseConfig(object):
1032 cmd = ('runqemu-extract-sdk', src, dest) 1071 cmd = ('runqemu-extract-sdk', src, dest)
1033 logger.info('Running %s...' % str(cmd)) 1072 logger.info('Running %s...' % str(cmd))
1034 if subprocess.call(cmd) != 0: 1073 if subprocess.call(cmd) != 0:
1035 raise RunQemuError('Failed to run %s' % cmd) 1074 raise RunQemuError('Failed to run %s' % str(cmd))
1036 self.rootfs = dest 1075 self.rootfs = dest
1037 self.cleanup_files.append(self.rootfs) 1076 self.cleanup_files.append(self.rootfs)
1038 self.cleanup_files.append('%s.pseudo_state' % self.rootfs) 1077 self.cleanup_files.append('%s.pseudo_state' % self.rootfs)
@@ -1041,14 +1080,32 @@ class BaseConfig(object):
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()
@@ -1249,6 +1301,10 @@ class BaseConfig(object):
1249 elif drive_type.startswith("/dev/hd"): 1301 elif drive_type.startswith("/dev/hd"):
1250 logger.info('Using ide drive') 1302 logger.info('Using ide drive')
1251 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)
1252 elif drive_type.startswith("/dev/vdb"): 1308 elif drive_type.startswith("/dev/vdb"):
1253 logger.info('Using block virtio drive'); 1309 logger.info('Using block virtio drive');
1254 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' \
@@ -1288,7 +1344,7 @@ class BaseConfig(object):
1288 """attempt to determine the appropriate qemu-system binary""" 1344 """attempt to determine the appropriate qemu-system binary"""
1289 mach = self.get('MACHINE') 1345 mach = self.get('MACHINE')
1290 if not mach: 1346 if not mach:
1291 search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*' 1347 search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemuloongarch64|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*'
1292 if self.rootfs: 1348 if self.rootfs:
1293 match = re.match(search, self.rootfs) 1349 match = re.match(search, self.rootfs)
1294 if match: 1350 if match:
@@ -1311,6 +1367,8 @@ class BaseConfig(object):
1311 qbsys = 'x86_64' 1367 qbsys = 'x86_64'
1312 elif mach == 'qemuppc': 1368 elif mach == 'qemuppc':
1313 qbsys = 'ppc' 1369 qbsys = 'ppc'
1370 elif mach == 'qemuloongarch64':
1371 qbsys = 'loongarch64'
1314 elif mach == 'qemumips': 1372 elif mach == 'qemumips':
1315 qbsys = 'mips' 1373 qbsys = 'mips'
1316 elif mach == 'qemumips64': 1374 elif mach == 'qemumips64':
@@ -1339,6 +1397,35 @@ class BaseConfig(object):
1339 raise RunQemuError("Failed to boot, QB_SYSTEM_NAME is NULL!") 1397 raise RunQemuError("Failed to boot, QB_SYSTEM_NAME is NULL!")
1340 self.qemu_system = qemu_system 1398 self.qemu_system = qemu_system
1341 1399
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
1342 def setup_vga(self): 1429 def setup_vga(self):
1343 if self.nographic == True: 1430 if self.nographic == True:
1344 if self.sdl == True: 1431 if self.sdl == True:
@@ -1354,27 +1441,43 @@ class BaseConfig(object):
1354 if (self.gl_es == True or self.gl == True) and (self.sdl == False and self.gtk == False): 1441 if (self.gl_es == True or self.gl == True) and (self.sdl == False and self.gtk == False):
1355 raise RunQemuError('Option gl/gl-es needs gtk or sdl option.') 1442 raise RunQemuError('Option gl/gl-es needs gtk or sdl option.')
1356 1443
1357 if self.sdl == True or self.gtk == True or self.egl_headless == True: 1444 # If we have no display option, we autodetect based upon what qemu supports. We
1358 if self.gl or self.gl_es or self.egl_headless: 1445 # need our font setup and show-cusor below so we need to see what qemu --help says
1359 self.qemu_opt += ' -device virtio-vga-gl ' 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
1360 else: 1453 else:
1361 self.qemu_opt += ' -device virtio-vga ' 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 '
1362 1463
1363 self.qemu_opt += '-display ' 1464 self.qemu_opt += ' -display '
1364 if self.egl_headless == True: 1465 if self.egl_headless == True:
1365 self.set_dri_path() 1466 self.check_render_nodes()
1467 self.set_mesa_paths()
1366 self.qemu_opt += 'egl-headless,' 1468 self.qemu_opt += 'egl-headless,'
1367 else: 1469 else:
1368 if self.sdl == True: 1470 if self.sdl == True:
1369 self.qemu_opt += 'sdl,' 1471 self.qemu_opt += 'sdl,'
1370 elif self.gtk == True: 1472 elif self.gtk == True:
1473 self.qemu_environ['FONTCONFIG_PATH'] = '/etc/fonts'
1371 self.qemu_opt += 'gtk,' 1474 self.qemu_opt += 'gtk,'
1372 1475
1373 if self.gl == True: 1476 if self.gl == True:
1374 self.set_dri_path() 1477 self.set_mesa_paths()
1375 self.qemu_opt += 'gl=on,' 1478 self.qemu_opt += 'gl=on,'
1376 elif self.gl_es == True: 1479 elif self.gl_es == True:
1377 self.set_dri_path() 1480 self.set_mesa_paths()
1378 self.qemu_opt += 'gl=es,' 1481 self.qemu_opt += 'gl=es,'
1379 self.qemu_opt += 'show-cursor=on' 1482 self.qemu_opt += 'show-cursor=on'
1380 1483
@@ -1386,6 +1489,19 @@ class BaseConfig(object):
1386 for entry in self.get('SERIAL_CONSOLES').split(' '): 1489 for entry in self.get('SERIAL_CONSOLES').split(' '):
1387 self.kernel_cmdline_script += ' console=%s' %entry.split(';')[1] 1490 self.kernel_cmdline_script += ' console=%s' %entry.split(';')[1]
1388 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
1389 if self.serialstdio == True or self.nographic == True: 1505 if self.serialstdio == True or self.nographic == True:
1390 self.qemu_opt += " -serial mon:stdio" 1506 self.qemu_opt += " -serial mon:stdio"
1391 else: 1507 else:
@@ -1397,15 +1513,11 @@ class BaseConfig(object):
1397 1513
1398 self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT") 1514 self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT")
1399 1515
1400 # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES). 1516 serial_num = len(re.findall("(^| )-serial ", self.qemu_opt))
1401 # If no serial or serialtcp options were specified, only ttyS0 is created
1402 # and sysvinit shows an error trying to enable ttyS1:
1403 # INIT: Id "S1" respawning too fast: disabled for 5 minutes
1404 serial_num = len(re.findall("-serial", self.qemu_opt))
1405 if serial_num < 2: 1517 if serial_num < 2:
1406 self.qemu_opt += " -serial null" 1518 self.qemu_opt += " -serial null"
1407 1519
1408 def setup_final(self): 1520 def find_qemu(self):
1409 qemu_bin = os.path.join(self.bindir_native, self.qemu_system) 1521 qemu_bin = os.path.join(self.bindir_native, self.qemu_system)
1410 1522
1411 # 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
@@ -1424,8 +1536,13 @@ class BaseConfig(object):
1424 1536
1425 if not os.access(qemu_bin, os.X_OK): 1537 if not os.access(qemu_bin, os.X_OK):
1426 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
1540
1541 def setup_final(self):
1427 1542
1428 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').replace('@DEPLOY_DIR_IMAGE@', self.get('DEPLOY_DIR_IMAGE'))) 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')))
1429 1546
1430 for ovmf in self.ovmf_bios: 1547 for ovmf in self.ovmf_bios:
1431 format = ovmf.rsplit('.', 1)[-1] 1548 format = ovmf.rsplit('.', 1)[-1]
@@ -1449,13 +1566,20 @@ class BaseConfig(object):
1449 if self.snapshot: 1566 if self.snapshot:
1450 self.qemu_opt += " -snapshot" 1567 self.qemu_opt += " -snapshot"
1451 1568
1569 self.setup_guest_agent()
1570 self.setup_qmp()
1452 self.setup_serial() 1571 self.setup_serial()
1453 self.setup_vga() 1572 self.setup_vga()
1454 1573
1455 def start_qemu(self): 1574 def start_qemu(self):
1456 import shlex 1575 import shlex
1457 if self.kernel: 1576 if self.kernel:
1458 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,
1459 self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'), 1583 self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'),
1460 self.bootparams) 1584 self.bootparams)
1461 if self.dtb: 1585 if self.dtb:
@@ -1469,14 +1593,17 @@ class BaseConfig(object):
1469 cmd = "%s %s" % (self.qemu_opt, kernel_opts) 1593 cmd = "%s %s" % (self.qemu_opt, kernel_opts)
1470 cmds = shlex.split(cmd) 1594 cmds = shlex.split(cmd)
1471 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)
1472 pass_fds = [] 1599 pass_fds = []
1473 if self.taplock_descriptor: 1600 if self.taplock_descriptor:
1474 pass_fds = [self.taplock_descriptor.fileno()] 1601 pass_fds = [self.taplock_descriptor.fileno()]
1475 if len(self.portlocks): 1602 if len(self.portlocks):
1476 for descriptor in self.portlocks.values(): 1603 for descriptor in self.portlocks.values():
1477 pass_fds.append(descriptor.fileno()) 1604 pass_fds.append(descriptor.fileno())
1478 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)
1479 self.qemupid = process.pid 1606 self.qemuprocess = process
1480 retcode = process.wait() 1607 retcode = process.wait()
1481 if retcode: 1608 if retcode:
1482 if retcode == -signal.SIGTERM: 1609 if retcode == -signal.SIGTERM:
@@ -1484,6 +1611,13 @@ class BaseConfig(object):
1484 else: 1611 else:
1485 logger.error("Failed to run qemu: %s", process.stderr.read().decode()) 1612 logger.error("Failed to run qemu: %s", process.stderr.read().decode())
1486 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
1487 def cleanup(self): 1621 def cleanup(self):
1488 if self.cleaned: 1622 if self.cleaned:
1489 return 1623 return
@@ -1492,18 +1626,30 @@ class BaseConfig(object):
1492 signal.signal(signal.SIGTERM, signal.SIG_IGN) 1626 signal.signal(signal.SIGTERM, signal.SIG_IGN)
1493 1627
1494 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)
1495 if self.cleantap: 1641 if self.cleantap:
1496 cmd = ('sudo', self.qemuifdown, self.tap, self.bindir_native) 1642 cmd = ('sudo', self.qemuifdown, self.tap)
1497 logger.debug('Running %s' % str(cmd)) 1643 logger.debug('Running %s' % str(cmd))
1498 subprocess.check_call(cmd) 1644 subprocess.check_call(cmd)
1499 self.release_taplock() 1645 self.release_taplock()
1500 self.release_portlock()
1501 1646
1502 if self.nfs_running: 1647 if self.nfs_running:
1503 logger.info("Shutting down the userspace NFS server...") 1648 logger.info("Shutting down the userspace NFS server...")
1504 cmd = ("runqemu-export-rootfs", "stop", self.rootfs) 1649 cmd = ("runqemu-export-rootfs", "stop", self.rootfs)
1505 logger.debug('Running %s' % str(cmd)) 1650 logger.debug('Running %s' % str(cmd))
1506 subprocess.check_call(cmd) 1651 subprocess.check_call(cmd)
1652 self.release_portlock()
1507 1653
1508 if self.saved_stty: 1654 if self.saved_stty:
1509 subprocess.check_call(("stty", self.saved_stty)) 1655 subprocess.check_call(("stty", self.saved_stty))
@@ -1516,9 +1662,12 @@ class BaseConfig(object):
1516 else: 1662 else:
1517 shutil.rmtree(ent) 1663 shutil.rmtree(ent)
1518 1664
1665 # Deliberately ignore the return code of 'tput smam'.
1666 subprocess.call(["tput", "smam"])
1667
1519 self.cleaned = True 1668 self.cleaned = True
1520 1669
1521 def run_bitbake_env(self, mach=None): 1670 def run_bitbake_env(self, mach=None, target=''):
1522 bitbake = shutil.which('bitbake') 1671 bitbake = shutil.which('bitbake')
1523 if not bitbake: 1672 if not bitbake:
1524 return 1673 return
@@ -1530,23 +1679,37 @@ class BaseConfig(object):
1530 if multiconfig: 1679 if multiconfig:
1531 multiconfig = "mc:%s" % multiconfig 1680 multiconfig = "mc:%s" % multiconfig
1532 1681
1682 if self.rootfs and not target:
1683 target = self.rootfs
1684
1533 if mach: 1685 if mach:
1534 cmd = 'MACHINE=%s bitbake -e %s' % (mach, multiconfig) 1686 cmd = 'MACHINE=%s bitbake -e %s %s' % (mach, multiconfig, target)
1535 else: 1687 else:
1536 cmd = 'bitbake -e %s' % multiconfig 1688 cmd = 'bitbake -e %s %s' % (multiconfig, target)
1537 1689
1538 logger.info('Running %s...' % cmd) 1690 logger.info('Running %s...' % cmd)
1539 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 ''
1540 1706
1541 def load_bitbake_env(self, mach=None): 1707
1708 def load_bitbake_env(self, mach=None, target=None):
1542 if self.bitbake_e: 1709 if self.bitbake_e:
1543 return 1710 return
1544 1711
1545 try: 1712 self.bitbake_e = self.run_bitbake_env(mach=mach, target=target)
1546 self.bitbake_e = self.run_bitbake_env(mach=mach)
1547 except subprocess.CalledProcessError as err:
1548 self.bitbake_e = ''
1549 logger.warning("Couldn't run 'bitbake -e' to gather environment information:\n%s" % err.output.decode('utf-8'))
1550 1713
1551 def validate_combos(self): 1714 def validate_combos(self):
1552 if (self.fstype in self.vmtypes) and self.kernel: 1715 if (self.fstype in self.vmtypes) and self.kernel:
@@ -1576,7 +1739,7 @@ class BaseConfig(object):
1576 return result 1739 return result
1577 raise RunQemuError("Native sysroot directory %s doesn't exist" % result) 1740 raise RunQemuError("Native sysroot directory %s doesn't exist" % result)
1578 else: 1741 else:
1579 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))
1580 1743
1581 1744
1582def main(): 1745def main():
@@ -1592,11 +1755,8 @@ def main():
1592 subprocess.check_call([renice, str(os.getpid())]) 1755 subprocess.check_call([renice, str(os.getpid())])
1593 1756
1594 def sigterm_handler(signum, frame): 1757 def sigterm_handler(signum, frame):
1595 logger.info("SIGTERM received") 1758 logger.info("Received signal: %s" % (signum))
1596 os.kill(config.qemupid, signal.SIGTERM)
1597 config.cleanup() 1759 config.cleanup()
1598 # Deliberately ignore the return code of 'tput smam'.
1599 subprocess.call(["tput", "smam"])
1600 signal.signal(signal.SIGTERM, sigterm_handler) 1760 signal.signal(signal.SIGTERM, sigterm_handler)
1601 1761
1602 config.check_args() 1762 config.check_args()
@@ -1608,6 +1768,7 @@ def main():
1608 config.setup_network() 1768 config.setup_network()
1609 config.setup_rootfs() 1769 config.setup_rootfs()
1610 config.setup_final() 1770 config.setup_final()
1771 config.setup_cmd()
1611 config.start_qemu() 1772 config.start_qemu()
1612 except RunQemuError as err: 1773 except RunQemuError as err:
1613 logger.error(err) 1774 logger.error(err)
@@ -1617,9 +1778,8 @@ def main():
1617 traceback.print_exc() 1778 traceback.print_exc()
1618 return 1 1779 return 1
1619 finally: 1780 finally:
1781 config.cleanup_cmd()
1620 config.cleanup() 1782 config.cleanup()
1621 # Deliberately ignore the return code of 'tput smam'.
1622 subprocess.call(["tput", "smam"])
1623 1783
1624if __name__ == "__main__": 1784if __name__ == "__main__":
1625 sys.exit(main()) 1785 sys.exit(main())