]> git.ipfire.org Git - thirdparty/openembedded/openembedded-core.git/blob - scripts/runqemu
build-compare: add date to PV
[thirdparty/openembedded/openembedded-core.git] / scripts / runqemu
1 #!/usr/bin/env python3
2
3 # Handle running OE images standalone with QEMU
4 #
5 # Copyright (C) 2006-2011 Linux Foundation
6 # Copyright (c) 2016 Wind River Systems, Inc.
7 #
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License version 2 as
10 # published by the Free Software Foundation.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License along
18 # with this program; if not, write to the Free Software Foundation, Inc.,
19 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21 import os
22 import sys
23 import logging
24 import subprocess
25 import re
26 import fcntl
27 import shutil
28 import glob
29 import configparser
30
31 class OEPathError(Exception):
32 """Custom Exception to give better guidance on missing binaries"""
33 def __init__(self, message):
34 self.message = "In order for this script to dynamically infer paths\n \
35 kernels or filesystem images, you either need bitbake in your PATH\n \
36 or to source oe-init-build-env before running this script.\n\n \
37 Dynamic path inference can be avoided by passing a *.qemuboot.conf to\n \
38 runqemu, i.e. `runqemu /path/to/my-image-name.qemuboot.conf`\n\n %s" % message
39
40
41 def create_logger():
42 logger = logging.getLogger('runqemu')
43 logger.setLevel(logging.INFO)
44
45 # create console handler and set level to debug
46 ch = logging.StreamHandler()
47 ch.setLevel(logging.INFO)
48
49 # create formatter
50 formatter = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
51
52 # add formatter to ch
53 ch.setFormatter(formatter)
54
55 # add ch to logger
56 logger.addHandler(ch)
57
58 return logger
59
60 logger = create_logger()
61
62 def print_usage():
63 print("""
64 Usage: you can run this script with any valid combination
65 of the following environment variables (in any order):
66 KERNEL - the kernel image file to use
67 ROOTFS - the rootfs image file or nfsroot directory to use
68 MACHINE - the machine name (optional, autodetected from KERNEL filename if unspecified)
69 Simplified QEMU command-line options can be passed with:
70 nographic - disable video console
71 serial - enable a serial console on /dev/ttyS0
72 slirp - enable user networking, no root privileges is required
73 kvm - enable KVM when running x86/x86_64 (VT-capable CPU required)
74 kvm-vhost - enable KVM with vhost when running x86/x86_64 (VT-capable CPU required)
75 publicvnc - enable a VNC server open to all hosts
76 audio - enable audio
77 [*/]ovmf* - OVMF firmware file or base name for booting with UEFI
78 tcpserial=<port> - specify tcp serial port number
79 biosdir=<dir> - specify custom bios dir
80 biosfilename=<filename> - specify bios filename
81 qemuparams=<xyz> - specify custom parameters to QEMU
82 bootparams=<xyz> - specify custom kernel parameters during boot
83 help, -h, --help: print this text
84
85 Examples:
86 runqemu qemuarm
87 runqemu tmp/deploy/images/qemuarm
88 runqemu tmp/deploy/images/qemux86/<qemuboot.conf>
89 runqemu qemux86-64 core-image-sato ext4
90 runqemu qemux86-64 wic-image-minimal wic
91 runqemu path/to/bzImage-qemux86.bin path/to/nfsrootdir/ serial
92 runqemu qemux86 iso/hddimg/vmdk/qcow2/vdi/ramfs/cpio.gz...
93 runqemu qemux86 qemuparams="-m 256"
94 runqemu qemux86 bootparams="psplash=false"
95 runqemu path/to/<image>-<machine>.vmdk
96 runqemu path/to/<image>-<machine>.wic
97 """)
98
99 def check_tun():
100 """Check /dev/net/tun"""
101 dev_tun = '/dev/net/tun'
102 if not os.path.exists(dev_tun):
103 raise Exception("TUN control device %s is unavailable; you may need to enable TUN (e.g. sudo modprobe tun)" % dev_tun)
104
105 if not os.access(dev_tun, os.W_OK):
106 raise Exception("TUN control device %s is not writable, please fix (e.g. sudo chmod 666 %s)" % (dev_tun, dev_tun))
107
108 def check_libgl(qemu_bin):
109 cmd = 'ldd %s' % qemu_bin
110 logger.info('Running %s...' % cmd)
111 need_gl = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8')
112 if re.search('libGLU', need_gl):
113 # We can't run without a libGL.so
114 libgl = False
115 check_files = (('/usr/lib/libGL.so', '/usr/lib/libGLU.so'), \
116 ('/usr/lib64/libGL.so', '/usr/lib64/libGLU.so'), \
117 ('/usr/lib/*-linux-gnu/libGL.so', '/usr/lib/*-linux-gnu/libGLU.so'))
118
119 for (f1, f2) in check_files:
120 if re.search('\*', f1):
121 for g1 in glob.glob(f1):
122 if libgl:
123 break
124 if os.path.exists(g1):
125 for g2 in glob.glob(f2):
126 if os.path.exists(g2):
127 libgl = True
128 break
129 if libgl:
130 break
131 else:
132 if os.path.exists(f1) and os.path.exists(f2):
133 libgl = True
134 break
135 if not libgl:
136 logger.error("You need libGL.so and libGLU.so to exist in your library path to run the QEMU emulator.")
137 logger.error("Ubuntu package names are: libgl1-mesa-dev and libglu1-mesa-dev.")
138 logger.error("Fedora package names are: mesa-libGL-devel mesa-libGLU-devel.")
139 raise Exception('%s requires libGLU, but not found' % qemu_bin)
140
141 def get_first_file(cmds):
142 """Return first file found in wildcard cmds"""
143 for cmd in cmds:
144 all_files = glob.glob(cmd)
145 if all_files:
146 for f in all_files:
147 if not os.path.isdir(f):
148 return f
149 return ''
150
151 def check_free_port(host, port):
152 """ Check whether the port is free or not """
153 import socket
154 from contextlib import closing
155
156 with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
157 if sock.connect_ex((host, port)) == 0:
158 # Port is open, so not free
159 return False
160 else:
161 # Port is not open, so free
162 return True
163
164 class BaseConfig(object):
165 def __init__(self):
166 # Vars can be merged with .qemuboot.conf, use a dict to manage them.
167 self.d = {
168 'MACHINE': '',
169 'DEPLOY_DIR_IMAGE': '',
170 'QB_KERNEL_ROOT': '/dev/vda',
171 }
172
173 self.qemu_opt = ''
174 self.qemu_opt_script = ''
175 self.nfs_dir = ''
176 self.clean_nfs_dir = False
177 self.nfs_server = ''
178 self.rootfs = ''
179 # File name(s) of a OVMF firmware file or variable store,
180 # to be added with -drive if=pflash.
181 # Found in the same places as the rootfs, with or without one of
182 # these suffices: qcow2, bin.
183 # Setting one also adds "-vga std" because that is all that
184 # OVMF supports.
185 self.ovmf_bios = []
186 self.qemuboot = ''
187 self.qbconfload = False
188 self.kernel = ''
189 self.kernel_cmdline = ''
190 self.kernel_cmdline_script = ''
191 self.bootparams = ''
192 self.dtb = ''
193 self.fstype = ''
194 self.kvm_enabled = False
195 self.vhost_enabled = False
196 self.slirp_enabled = False
197 self.nfs_instance = 0
198 self.nfs_running = False
199 self.serialstdio = False
200 self.cleantap = False
201 self.saved_stty = ''
202 self.audio_enabled = False
203 self.tcpserial_portnum = ''
204 self.custombiosdir = ''
205 self.lock = ''
206 self.lock_descriptor = ''
207 self.bitbake_e = ''
208 self.snapshot = False
209 self.fstypes = ('ext2', 'ext3', 'ext4', 'jffs2', 'nfs', 'btrfs', 'cpio.gz', 'cpio', 'ramfs')
210 self.vmtypes = ('hddimg', 'hdddirect', 'wic', 'vmdk', 'qcow2', 'vdi', 'iso')
211 self.network_device = "-device e1000,netdev=net0,mac=@MAC@"
212 # Use different mac section for tap and slirp to avoid
213 # conflicts, e.g., when one is running with tap, the other is
214 # running with slirp.
215 # The last section is dynamic, which is for avoiding conflicts,
216 # when multiple qemus are running, e.g., when multiple tap or
217 # slirp qemus are running.
218 self.mac_tap = "52:54:00:12:34:"
219 self.mac_slirp = "52:54:00:12:35:"
220
221 def acquire_lock(self):
222 logger.info("Acquiring lockfile %s..." % self.lock)
223 try:
224 self.lock_descriptor = open(self.lock, 'w')
225 fcntl.flock(self.lock_descriptor, fcntl.LOCK_EX|fcntl.LOCK_NB)
226 except Exception as e:
227 logger.info("Acquiring lockfile %s failed: %s" % (self.lock, e))
228 if self.lock_descriptor:
229 self.lock_descriptor.close()
230 return False
231 return True
232
233 def release_lock(self):
234 fcntl.flock(self.lock_descriptor, fcntl.LOCK_UN)
235 self.lock_descriptor.close()
236 os.remove(self.lock)
237
238 def get(self, key):
239 if key in self.d:
240 return self.d.get(key)
241 else:
242 return ''
243
244 def set(self, key, value):
245 self.d[key] = value
246
247 def is_deploy_dir_image(self, p):
248 if os.path.isdir(p):
249 if not re.search('.qemuboot.conf$', '\n'.join(os.listdir(p)), re.M):
250 logger.info("Can't find required *.qemuboot.conf in %s" % p)
251 return False
252 if not any(map(lambda name: '-image-' in name, os.listdir(p))):
253 logger.info("Can't find *-image-* in %s" % p)
254 return False
255 return True
256 else:
257 return False
258
259 def check_arg_fstype(self, fst):
260 """Check and set FSTYPE"""
261 if fst not in self.fstypes + self.vmtypes:
262 logger.warn("Maybe unsupported FSTYPE: %s" % fst)
263 if not self.fstype or self.fstype == fst:
264 if fst == 'ramfs':
265 fst = 'cpio.gz'
266 self.fstype = fst
267 else:
268 raise Exception("Conflicting: FSTYPE %s and %s" % (self.fstype, fst))
269
270 def set_machine_deploy_dir(self, machine, deploy_dir_image):
271 """Set MACHINE and DEPLOY_DIR_IMAGE"""
272 logger.info('MACHINE: %s' % machine)
273 self.set("MACHINE", machine)
274 logger.info('DEPLOY_DIR_IMAGE: %s' % deploy_dir_image)
275 self.set("DEPLOY_DIR_IMAGE", deploy_dir_image)
276
277 def check_arg_nfs(self, p):
278 if os.path.isdir(p):
279 self.nfs_dir = p
280 else:
281 m = re.match('(.*):(.*)', p)
282 self.nfs_server = m.group(1)
283 self.nfs_dir = m.group(2)
284 self.rootfs = ""
285 self.check_arg_fstype('nfs')
286
287 def check_arg_path(self, p):
288 """
289 - Check whether it is <image>.qemuboot.conf or contains <image>.qemuboot.conf
290 - Check whether is a kernel file
291 - Check whether is a image file
292 - Check whether it is a nfs dir
293 - Check whether it is a OVMF flash file
294 """
295 if p.endswith('.qemuboot.conf'):
296 self.qemuboot = p
297 self.qbconfload = True
298 elif re.search('\.bin$', p) or re.search('bzImage', p) or \
299 re.search('zImage', p) or re.search('vmlinux', p) or \
300 re.search('fitImage', p) or re.search('uImage', p):
301 self.kernel = p
302 elif os.path.exists(p) and (not os.path.isdir(p)) and '-image-' in os.path.basename(p):
303 self.rootfs = p
304 # Check filename against self.fstypes can hanlde <file>.cpio.gz,
305 # otherwise, its type would be "gz", which is incorrect.
306 fst = ""
307 for t in self.fstypes:
308 if p.endswith(t):
309 fst = t
310 break
311 if not fst:
312 m = re.search('.*\.(.*)$', self.rootfs)
313 if m:
314 fst = m.group(1)
315 if fst:
316 self.check_arg_fstype(fst)
317 qb = re.sub('\.' + fst + "$", '', self.rootfs)
318 qb = '%s%s' % (re.sub('\.rootfs$', '', qb), '.qemuboot.conf')
319 if os.path.exists(qb):
320 self.qemuboot = qb
321 self.qbconfload = True
322 else:
323 logger.warn("%s doesn't exist" % qb)
324 else:
325 raise Exception("Can't find FSTYPE from: %s" % p)
326
327 elif os.path.isdir(p) or re.search(':', p) and re.search('/', p):
328 if self.is_deploy_dir_image(p):
329 logger.info('DEPLOY_DIR_IMAGE: %s' % p)
330 self.set("DEPLOY_DIR_IMAGE", p)
331 else:
332 logger.info("Assuming %s is an nfs rootfs" % p)
333 self.check_arg_nfs(p)
334 elif os.path.basename(p).startswith('ovmf'):
335 self.ovmf_bios.append(p)
336 else:
337 raise Exception("Unknown path arg %s" % p)
338
339 def check_arg_machine(self, arg):
340 """Check whether it is a machine"""
341 if self.get('MACHINE') and self.get('MACHINE') != arg or re.search('/', arg):
342 raise Exception("Unknown arg: %s" % arg)
343 elif self.get('MACHINE') == arg:
344 return
345 logger.info('Assuming MACHINE = %s' % arg)
346
347 # if we're running under testimage, or similarly as a child
348 # of an existing bitbake invocation, we can't invoke bitbake
349 # to validate the MACHINE setting and must assume it's correct...
350 # FIXME: testimage.bbclass exports these two variables into env,
351 # are there other scenarios in which we need to support being
352 # invoked by bitbake?
353 deploy = os.environ.get('DEPLOY_DIR_IMAGE')
354 bbchild = deploy and os.environ.get('OE_TMPDIR')
355 if bbchild:
356 self.set_machine_deploy_dir(arg, deploy)
357 return
358 # also check whether we're running under a sourced toolchain
359 # environment file
360 if os.environ.get('OECORE_NATIVE_SYSROOT'):
361 self.set("MACHINE", arg)
362 return
363
364 cmd = 'MACHINE=%s bitbake -e' % arg
365 logger.info('Running %s...' % cmd)
366 self.bitbake_e = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8')
367 # bitbake -e doesn't report invalid MACHINE as an error, so
368 # let's check DEPLOY_DIR_IMAGE to make sure that it is a valid
369 # MACHINE.
370 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
371 if s:
372 deploy_dir_image = s.group(1)
373 else:
374 raise Exception("bitbake -e %s" % self.bitbake_e)
375 if self.is_deploy_dir_image(deploy_dir_image):
376 self.set_machine_deploy_dir(arg, deploy_dir_image)
377 else:
378 logger.error("%s not a directory valid DEPLOY_DIR_IMAGE" % deploy_dir_image)
379 self.set("MACHINE", arg)
380
381 def check_args(self):
382 unknown_arg = ""
383 for arg in sys.argv[1:]:
384 if arg in self.fstypes + self.vmtypes:
385 self.check_arg_fstype(arg)
386 elif arg == 'nographic':
387 self.qemu_opt_script += ' -nographic'
388 self.kernel_cmdline_script += ' console=ttyS0'
389 elif arg == 'serial':
390 self.kernel_cmdline_script += ' console=ttyS0'
391 self.serialstdio = True
392 elif arg == 'audio':
393 logger.info("Enabling audio in qemu")
394 logger.info("Please install sound drivers in linux host")
395 self.audio_enabled = True
396 elif arg == 'kvm':
397 self.kvm_enabled = True
398 elif arg == 'kvm-vhost':
399 self.vhost_enabled = True
400 elif arg == 'slirp':
401 self.slirp_enabled = True
402 elif arg == 'snapshot':
403 self.snapshot = True
404 elif arg == 'publicvnc':
405 self.qemu_opt_script += ' -vnc :0'
406 elif arg.startswith('tcpserial='):
407 self.tcpserial_portnum = arg[len('tcpserial='):]
408 elif arg.startswith('biosdir='):
409 self.custombiosdir = arg[len('biosdir='):]
410 elif arg.startswith('biosfilename='):
411 self.qemu_opt_script += ' -bios %s' % arg[len('biosfilename='):]
412 elif arg.startswith('qemuparams='):
413 self.qemu_opt_script += ' %s' % arg[len('qemuparams='):]
414 elif arg.startswith('bootparams='):
415 self.bootparams = arg[len('bootparams='):]
416 elif os.path.exists(arg) or (re.search(':', arg) and re.search('/', arg)):
417 self.check_arg_path(os.path.abspath(arg))
418 elif re.search(r'-image-|-image$', arg):
419 # Lazy rootfs
420 self.rootfs = arg
421 elif arg.startswith('ovmf'):
422 self.ovmf_bios.append(arg)
423 else:
424 # At last, assume is it the MACHINE
425 if (not unknown_arg) or unknown_arg == arg:
426 unknown_arg = arg
427 else:
428 raise Exception("Can't handle two unknown args: %s %s" % (unknown_arg, arg))
429 # Check to make sure it is a valid machine
430 if unknown_arg:
431 if self.get('MACHINE') == unknown_arg:
432 return
433 if not self.get('DEPLOY_DIR_IMAGE'):
434 # Trying to get DEPLOY_DIR_IMAGE from env.
435 p = os.getenv('DEPLOY_DIR_IMAGE')
436 if p and self.is_deploy_dir_image(p):
437 machine = os.path.basename(p)
438 if unknown_arg == machine:
439 self.set_machine_deploy_dir(machine, p)
440 return
441 else:
442 logger.info('DEPLOY_DIR_IMAGE: %s' % p)
443 self.set("DEPLOY_DIR_IMAGE", p)
444 self.check_arg_machine(unknown_arg)
445
446 if not (self.get('MACHINE') or self.get('DEPLOY_DIR_IMAGE')):
447 self.load_bitbake_env()
448 s = re.search('^DEPLOY_DIR_IMAGE="(.*)"', self.bitbake_e, re.M)
449 if s:
450 self.set("DEPLOY_DIR_IMAGE", s.group(1))
451
452 def check_kvm(self):
453 """Check kvm and kvm-host"""
454 if not (self.kvm_enabled or self.vhost_enabled):
455 self.qemu_opt_script += ' %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU'))
456 return
457
458 if not self.get('QB_CPU_KVM'):
459 raise Exception("QB_CPU_KVM is NULL, this board doesn't support kvm")
460
461 self.qemu_opt_script += ' %s %s' % (self.get('QB_MACHINE'), self.get('QB_CPU_KVM'))
462 yocto_kvm_wiki = "https://wiki.yoctoproject.org/wiki/How_to_enable_KVM_for_Poky_qemu"
463 yocto_paravirt_kvm_wiki = "https://wiki.yoctoproject.org/wiki/Running_an_x86_Yocto_Linux_image_under_QEMU_KVM"
464 dev_kvm = '/dev/kvm'
465 dev_vhost = '/dev/vhost-net'
466 with open('/proc/cpuinfo', 'r') as f:
467 kvm_cap = re.search('vmx|svm', "".join(f.readlines()))
468 if not kvm_cap:
469 logger.error("You are trying to enable KVM on a cpu without VT support.")
470 logger.error("Remove kvm from the command-line, or refer:")
471 raise Exception(yocto_kvm_wiki)
472
473 if not os.path.exists(dev_kvm):
474 logger.error("Missing KVM device. Have you inserted kvm modules?")
475 logger.error("For further help see:")
476 raise Exception(yocto_kvm_wiki)
477
478 if os.access(dev_kvm, os.W_OK|os.R_OK):
479 self.qemu_opt_script += ' -enable-kvm'
480 else:
481 logger.error("You have no read or write permission on /dev/kvm.")
482 logger.error("Please change the ownership of this file as described at:")
483 raise Exception(yocto_kvm_wiki)
484
485 if self.vhost_enabled:
486 if not os.path.exists(dev_vhost):
487 logger.error("Missing virtio net device. Have you inserted vhost-net module?")
488 logger.error("For further help see:")
489 raise Exception(yocto_paravirt_kvm_wiki)
490
491 if not os.access(dev_kvm, os.W_OK|os.R_OK):
492 logger.error("You have no read or write permission on /dev/vhost-net.")
493 logger.error("Please change the ownership of this file as described at:")
494 raise Exception(yocto_kvm_wiki)
495
496 def check_fstype(self):
497 """Check and setup FSTYPE"""
498 if not self.fstype:
499 fstype = self.get('QB_DEFAULT_FSTYPE')
500 if fstype:
501 self.fstype = fstype
502 else:
503 raise Exception("FSTYPE is NULL!")
504
505 def check_rootfs(self):
506 """Check and set rootfs"""
507
508 if self.fstype == 'nfs' or self.fstype == "none":
509 return
510
511 if self.rootfs and not os.path.exists(self.rootfs):
512 # Lazy rootfs
513 self.rootfs = "%s/%s-%s.%s" % (self.get('DEPLOY_DIR_IMAGE'),
514 self.rootfs, self.get('MACHINE'),
515 self.fstype)
516 elif not self.rootfs:
517 cmd_name = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_NAME'), self.fstype)
518 cmd_link = '%s/%s*.%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'), self.fstype)
519 cmds = (cmd_name, cmd_link)
520 self.rootfs = get_first_file(cmds)
521 if not self.rootfs:
522 raise Exception("Failed to find rootfs: %s or %s" % cmds)
523
524 if not os.path.exists(self.rootfs):
525 raise Exception("Can't find rootfs: %s" % self.rootfs)
526
527 def check_ovmf(self):
528 """Check and set full path for OVMF firmware and variable file(s)."""
529
530 for index, ovmf in enumerate(self.ovmf_bios):
531 if os.path.exists(ovmf):
532 continue
533 for suffix in ('qcow2', 'bin'):
534 path = '%s/%s.%s' % (self.get('DEPLOY_DIR_IMAGE'), ovmf, suffix)
535 if os.path.exists(path):
536 self.ovmf_bios[index] = path
537 break
538 else:
539 raise Exception("Can't find OVMF firmware: %s" % ovmf)
540
541 def check_kernel(self):
542 """Check and set kernel, dtb"""
543 # The vm image doesn't need a kernel
544 if self.fstype in self.vmtypes:
545 return
546
547 # QB_DEFAULT_KERNEL is always a full file path
548 kernel_name = os.path.basename(self.get('QB_DEFAULT_KERNEL'))
549
550 # The user didn't want a kernel to be loaded
551 if kernel_name == "none":
552 return
553
554 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
555 if not self.kernel:
556 kernel_match_name = "%s/%s" % (deploy_dir_image, kernel_name)
557 kernel_match_link = "%s/%s" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
558 kernel_startswith = "%s/%s*" % (deploy_dir_image, self.get('KERNEL_IMAGETYPE'))
559 cmds = (kernel_match_name, kernel_match_link, kernel_startswith)
560 self.kernel = get_first_file(cmds)
561 if not self.kernel:
562 raise Exception('KERNEL not found: %s, %s or %s' % cmds)
563
564 if not os.path.exists(self.kernel):
565 raise Exception("KERNEL %s not found" % self.kernel)
566
567 dtb = self.get('QB_DTB')
568 if dtb:
569 cmd_match = "%s/%s" % (deploy_dir_image, dtb)
570 cmd_startswith = "%s/%s*" % (deploy_dir_image, dtb)
571 cmd_wild = "%s/*.dtb" % deploy_dir_image
572 cmds = (cmd_match, cmd_startswith, cmd_wild)
573 self.dtb = get_first_file(cmds)
574 if not os.path.exists(self.dtb):
575 raise Exception('DTB not found: %s, %s or %s' % cmds)
576
577 def check_biosdir(self):
578 """Check custombiosdir"""
579 if not self.custombiosdir:
580 return
581
582 biosdir = ""
583 biosdir_native = "%s/%s" % (self.get('STAGING_DIR_NATIVE'), self.custombiosdir)
584 biosdir_host = "%s/%s" % (self.get('STAGING_DIR_HOST'), self.custombiosdir)
585 for i in (self.custombiosdir, biosdir_native, biosdir_host):
586 if os.path.isdir(i):
587 biosdir = i
588 break
589
590 if biosdir:
591 logger.info("Assuming biosdir is: %s" % biosdir)
592 self.qemu_opt_script += ' -L %s' % biosdir
593 else:
594 logger.error("Custom BIOS directory not found. Tried: %s, %s, and %s" % (self.custombiosdir, biosdir_native, biosdir_host))
595 raise Exception("Invalid custombiosdir: %s" % self.custombiosdir)
596
597 def check_mem(self):
598 s = re.search('-m +([0-9]+)', self.qemu_opt_script)
599 if s:
600 self.set('QB_MEM', '-m %s' % s.group(1))
601 elif not self.get('QB_MEM'):
602 logger.info('QB_MEM is not set, use 512M by default')
603 self.set('QB_MEM', '-m 512')
604
605 self.kernel_cmdline_script += ' mem=%s' % self.get('QB_MEM').replace('-m','').strip() + 'M'
606 self.qemu_opt_script += ' %s' % self.get('QB_MEM')
607
608 def check_tcpserial(self):
609 if self.tcpserial_portnum:
610 if self.get('QB_TCPSERIAL_OPT'):
611 self.qemu_opt_script += ' ' + self.get('QB_TCPSERIAL_OPT').replace('@PORT@', self.tcpserial_portnum)
612 else:
613 self.qemu_opt_script += ' -serial tcp:127.0.0.1:%s' % self.tcpserial_portnum
614
615 def check_and_set(self):
616 """Check configs sanity and set when needed"""
617 self.validate_paths()
618 if not self.slirp_enabled:
619 check_tun()
620 # Check audio
621 if self.audio_enabled:
622 if not self.get('QB_AUDIO_DRV'):
623 raise Exception("QB_AUDIO_DRV is NULL, this board doesn't support audio")
624 if not self.get('QB_AUDIO_OPT'):
625 logger.warn('QB_AUDIO_OPT is NULL, you may need define it to make audio work')
626 else:
627 self.qemu_opt_script += ' %s' % self.get('QB_AUDIO_OPT')
628 os.putenv('QEMU_AUDIO_DRV', self.get('QB_AUDIO_DRV'))
629 else:
630 os.putenv('QEMU_AUDIO_DRV', 'none')
631
632 self.check_kvm()
633 self.check_fstype()
634 self.check_rootfs()
635 self.check_ovmf()
636 self.check_kernel()
637 self.check_biosdir()
638 self.check_mem()
639 self.check_tcpserial()
640
641 def read_qemuboot(self):
642 if not self.qemuboot:
643 if self.get('DEPLOY_DIR_IMAGE'):
644 deploy_dir_image = self.get('DEPLOY_DIR_IMAGE')
645 elif os.getenv('DEPLOY_DIR_IMAGE'):
646 deploy_dir_image = os.getenv('DEPLOY_DIR_IMAGE')
647 else:
648 logger.info("Can't find qemuboot conf file, DEPLOY_DIR_IMAGE is NULL!")
649 return
650
651 if self.rootfs and not os.path.exists(self.rootfs):
652 # Lazy rootfs
653 machine = self.get('MACHINE')
654 if not machine:
655 machine = os.path.basename(deploy_dir_image)
656 self.qemuboot = "%s/%s-%s.qemuboot.conf" % (deploy_dir_image,
657 self.rootfs, machine)
658 else:
659 cmd = 'ls -t %s/*.qemuboot.conf' % deploy_dir_image
660 logger.info('Running %s...' % cmd)
661 qbs = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8')
662 if qbs:
663 for qb in qbs.split():
664 # Don't use initramfs when other choices unless fstype is ramfs
665 if '-initramfs-' in os.path.basename(qb) and self.fstype != 'cpio.gz':
666 continue
667 self.qemuboot = qb
668 break
669 if not self.qemuboot:
670 # Use the first one when no choice
671 self.qemuboot = qbs.split()[0]
672 self.qbconfload = True
673
674 if not self.qemuboot:
675 # If we haven't found a .qemuboot.conf at this point it probably
676 # doesn't exist, continue without
677 return
678
679 if not os.path.exists(self.qemuboot):
680 raise Exception("Failed to find <image>.qemuboot.conf = %s (wrong image name or BSP does not support running under qemu?)." % self.qemuboot)
681
682 logger.info('CONFFILE: %s' % self.qemuboot)
683
684 cf = configparser.ConfigParser()
685 cf.read(self.qemuboot)
686 for k, v in cf.items('config_bsp'):
687 k_upper = k.upper()
688 self.set(k_upper, v)
689
690 def validate_paths(self):
691 """Ensure all relevant path variables are set"""
692 # When we're started with a *.qemuboot.conf arg assume that image
693 # artefacts are relative to that file, rather than in whatever
694 # directory DEPLOY_DIR_IMAGE in the conf file points to.
695 if self.qbconfload:
696 imgdir = os.path.dirname(self.qemuboot)
697 if imgdir != self.get('DEPLOY_DIR_IMAGE'):
698 logger.info('Setting DEPLOY_DIR_IMAGE to folder containing %s (%s)' % (self.qemuboot, imgdir))
699 self.set('DEPLOY_DIR_IMAGE', imgdir)
700
701 # If the STAGING_*_NATIVE directories from the config file don't exist
702 # and we're in a sourced OE build directory try to extract the paths
703 # from `bitbake -e`
704 havenative = os.path.exists(self.get('STAGING_DIR_NATIVE')) and \
705 os.path.exists(self.get('STAGING_BINDIR_NATIVE'))
706
707 if not havenative:
708 if not self.bitbake_e:
709 self.load_bitbake_env()
710
711 if self.bitbake_e:
712 native_vars = ['STAGING_DIR_NATIVE', 'STAGING_BINDIR_NATIVE']
713 for nv in native_vars:
714 s = re.search('^%s="(.*)"' % nv, self.bitbake_e, re.M)
715 if s and s.group(1) != self.get(nv):
716 logger.info('Overriding conf file setting of %s to %s from Bitbake environment' % (nv, s.group(1)))
717 self.set(nv, s.group(1))
718 else:
719 # when we're invoked from a running bitbake instance we won't
720 # be able to call `bitbake -e`, then try:
721 # - get OE_TMPDIR from environment and guess paths based on it
722 # - get OECORE_NATIVE_SYSROOT from environment (for sdk)
723 tmpdir = os.environ.get('OE_TMPDIR', None)
724 oecore_native_sysroot = os.environ.get('OECORE_NATIVE_SYSROOT', None)
725 if tmpdir:
726 logger.info('Setting STAGING_DIR_NATIVE and STAGING_BINDIR_NATIVE relative to OE_TMPDIR (%s)' % tmpdir)
727 hostos, _, _, _, machine = os.uname()
728 buildsys = '%s-%s' % (machine, hostos.lower())
729 staging_dir_native = '%s/sysroots/%s' % (tmpdir, buildsys)
730 self.set('STAGING_DIR_NATIVE', staging_dir_native)
731 elif oecore_native_sysroot:
732 logger.info('Setting STAGING_DIR_NATIVE to OECORE_NATIVE_SYSROOT (%s)' % oecore_native_sysroot)
733 self.set('STAGING_DIR_NATIVE', oecore_native_sysroot)
734 if self.get('STAGING_DIR_NATIVE'):
735 # we have to assume that STAGING_BINDIR_NATIVE is at usr/bin
736 staging_bindir_native = '%s/usr/bin' % self.get('STAGING_DIR_NATIVE')
737 logger.info('Setting STAGING_BINDIR_NATIVE to %s' % staging_bindir_native)
738 self.set('STAGING_BINDIR_NATIVE', '%s/usr/bin' % self.get('STAGING_DIR_NATIVE'))
739
740 def print_config(self):
741 logger.info('Continuing with the following parameters:\n')
742 if not self.fstype in self.vmtypes:
743 print('KERNEL: [%s]' % self.kernel)
744 if self.dtb:
745 print('DTB: [%s]' % self.dtb)
746 print('MACHINE: [%s]' % self.get('MACHINE'))
747 print('FSTYPE: [%s]' % self.fstype)
748 if self.fstype == 'nfs':
749 print('NFS_DIR: [%s]' % self.nfs_dir)
750 else:
751 print('ROOTFS: [%s]' % self.rootfs)
752 if self.ovmf_bios:
753 print('OVMF: %s' % self.ovmf_bios)
754 print('CONFFILE: [%s]' % self.qemuboot)
755 print('')
756
757 def setup_nfs(self):
758 if not self.nfs_server:
759 if self.slirp_enabled:
760 self.nfs_server = '10.0.2.2'
761 else:
762 self.nfs_server = '192.168.7.1'
763
764 # Figure out a new nfs_instance to allow multiple qemus running.
765 # CentOS 7.1's ps doesn't print full command line without "ww"
766 # when invoke by subprocess.Popen().
767 cmd = "ps auxww"
768 ps = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8')
769 pattern = '/bin/unfsd .* -i .*\.pid -e .*/exports([0-9]+) '
770 all_instances = re.findall(pattern, ps, re.M)
771 if all_instances:
772 all_instances.sort(key=int)
773 self.nfs_instance = int(all_instances.pop()) + 1
774
775 mountd_rpcport = 21111 + self.nfs_instance
776 nfsd_rpcport = 11111 + self.nfs_instance
777 nfsd_port = 3049 + 2 * self.nfs_instance
778 mountd_port = 3048 + 2 * self.nfs_instance
779
780 # Export vars for runqemu-export-rootfs
781 export_dict = {
782 'NFS_INSTANCE': self.nfs_instance,
783 'MOUNTD_RPCPORT': mountd_rpcport,
784 'NFSD_RPCPORT': nfsd_rpcport,
785 'NFSD_PORT': nfsd_port,
786 'MOUNTD_PORT': mountd_port,
787 }
788 for k, v in export_dict.items():
789 # Use '%s' since they are integers
790 os.putenv(k, '%s' % v)
791
792 self.unfs_opts="nfsvers=3,port=%s,mountprog=%s,nfsprog=%s,udp,mountport=%s" % (nfsd_port, mountd_rpcport, nfsd_rpcport, mountd_port)
793
794 # Extract .tar.bz2 or .tar.bz if no self.nfs_dir
795 if not self.nfs_dir:
796 src_prefix = '%s/%s' % (self.get('DEPLOY_DIR_IMAGE'), self.get('IMAGE_LINK_NAME'))
797 dest = "%s-nfsroot" % src_prefix
798 if os.path.exists('%s.pseudo_state' % dest):
799 logger.info('Use %s as NFS_DIR' % dest)
800 self.nfs_dir = dest
801 else:
802 src = ""
803 src1 = '%s.tar.bz2' % src_prefix
804 src2 = '%s.tar.gz' % src_prefix
805 if os.path.exists(src1):
806 src = src1
807 elif os.path.exists(src2):
808 src = src2
809 if not src:
810 raise Exception("No NFS_DIR is set, and can't find %s or %s to extract" % (src1, src2))
811 logger.info('NFS_DIR not found, extracting %s to %s' % (src, dest))
812 cmd = 'runqemu-extract-sdk %s %s' % (src, dest)
813 logger.info('Running %s...' % cmd)
814 if subprocess.call(cmd, shell=True) != 0:
815 raise Exception('Failed to run %s' % cmd)
816 self.clean_nfs_dir = True
817 self.nfs_dir = dest
818
819 # Start the userspace NFS server
820 cmd = 'runqemu-export-rootfs start %s' % self.nfs_dir
821 logger.info('Running %s...' % cmd)
822 if subprocess.call(cmd, shell=True) != 0:
823 raise Exception('Failed to run %s' % cmd)
824
825 self.nfs_running = True
826
827 def setup_slirp(self):
828 """Setup user networking"""
829
830 if self.fstype == 'nfs':
831 self.setup_nfs()
832 self.kernel_cmdline_script += ' ip=dhcp'
833 # Port mapping
834 hostfwd = ",hostfwd=tcp::2222-:22,hostfwd=tcp::2323-:23"
835 qb_slirp_opt_default = "-netdev user,id=net0%s" % hostfwd
836 qb_slirp_opt = self.get('QB_SLIRP_OPT') or qb_slirp_opt_default
837 # Figure out the port
838 ports = re.findall('hostfwd=[^-]*:([0-9]+)-[^,-]*', qb_slirp_opt)
839 ports = [int(i) for i in ports]
840 mac = 2
841 # Find a free port to avoid conflicts
842 for p in ports[:]:
843 p_new = p
844 while not check_free_port('localhost', p_new):
845 p_new += 1
846 mac += 1
847 while p_new in ports:
848 p_new += 1
849 mac += 1
850 if p != p_new:
851 ports.append(p_new)
852 qb_slirp_opt = re.sub(':%s-' % p, ':%s-' % p_new, qb_slirp_opt)
853 logger.info("Port forward changed: %s -> %s" % (p, p_new))
854 mac = "%s%02x" % (self.mac_slirp, mac)
855 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qb_slirp_opt))
856 # Print out port foward
857 hostfwd = re.findall('(hostfwd=[^,]*)', qb_slirp_opt)
858 if hostfwd:
859 logger.info('Port forward: %s' % ' '.join(hostfwd))
860
861 def setup_tap(self):
862 """Setup tap"""
863
864 # This file is created when runqemu-gen-tapdevs creates a bank of tap
865 # devices, indicating that the user should not bring up new ones using
866 # sudo.
867 nosudo_flag = '/etc/runqemu-nosudo'
868 self.qemuifup = shutil.which('runqemu-ifup')
869 self.qemuifdown = shutil.which('runqemu-ifdown')
870 ip = shutil.which('ip')
871 lockdir = "/tmp/qemu-tap-locks"
872
873 if not (self.qemuifup and self.qemuifdown and ip):
874 raise OEPathError("runqemu-ifup, runqemu-ifdown or ip not found")
875
876 if not os.path.exists(lockdir):
877 # There might be a race issue when multi runqemu processess are
878 # running at the same time.
879 try:
880 os.mkdir(lockdir)
881 except FileExistsError:
882 pass
883
884 cmd = '%s link' % ip
885 logger.info('Running %s...' % cmd)
886 ip_link = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8')
887 # Matches line like: 6: tap0: <foo>
888 possibles = re.findall('^[1-9]+: +(tap[0-9]+): <.*', ip_link, re.M)
889 tap = ""
890 for p in possibles:
891 lockfile = os.path.join(lockdir, p)
892 if os.path.exists('%s.skip' % lockfile):
893 logger.info('Found %s.skip, skipping %s' % (lockfile, p))
894 continue
895 self.lock = lockfile + '.lock'
896 if self.acquire_lock():
897 tap = p
898 logger.info("Using preconfigured tap device %s" % tap)
899 logger.info("If this is not intended, touch %s.skip to make runqemu skip %s." %(lockfile, tap))
900 break
901
902 if not tap:
903 if os.path.exists(nosudo_flag):
904 logger.error("Error: There are no available tap devices to use for networking,")
905 logger.error("and I see %s exists, so I am not going to try creating" % nosudo_flag)
906 raise Exception("a new one with sudo.")
907
908 gid = os.getgid()
909 uid = os.getuid()
910 logger.info("Setting up tap interface under sudo")
911 cmd = 'sudo %s %s %s %s' % (self.qemuifup, uid, gid, self.get('STAGING_DIR_NATIVE'))
912 tap = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8').rstrip('\n')
913 lockfile = os.path.join(lockdir, tap)
914 self.lock = lockfile + '.lock'
915 self.acquire_lock()
916 self.cleantap = True
917 logger.info('Created tap: %s' % tap)
918
919 if not tap:
920 logger.error("Failed to setup tap device. Run runqemu-gen-tapdevs to manually create.")
921 return 1
922 self.tap = tap
923 tapnum = int(tap[3:])
924 gateway = tapnum * 2 + 1
925 client = gateway + 1
926 if self.fstype == 'nfs':
927 self.setup_nfs()
928 self.kernel_cmdline_script += " ip=192.168.7.%s::192.168.7.%s:255.255.255.0" % (client, gateway)
929 mac = "%s%02x" % (self.mac_tap, client)
930 qb_tap_opt = self.get('QB_TAP_OPT')
931 if qb_tap_opt:
932 qemu_tap_opt = qb_tap_opt.replace('@TAP@', tap)
933 else:
934 qemu_tap_opt = "-netdev tap,id=net0,ifname=%s,script=no,downscript=no" % (self.tap)
935
936 if self.vhost_enabled:
937 qemu_tap_opt += ',vhost=on'
938
939 self.set('NETWORK_CMD', '%s %s' % (self.network_device.replace('@MAC@', mac), qemu_tap_opt))
940
941 def setup_network(self):
942 if self.get('QB_NET') == 'none':
943 return
944 cmd = "stty -g"
945 self.saved_stty = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8')
946 self.network_device = self.get('QB_NETWORK_DEVICE') or self.network_device
947 if self.slirp_enabled:
948 self.setup_slirp()
949 else:
950 self.setup_tap()
951
952 def setup_rootfs(self):
953 if self.get('QB_ROOTFS') == 'none':
954 return
955 rootfs_format = self.fstype if self.fstype in ('vmdk', 'qcow2', 'vdi') else 'raw'
956
957 qb_rootfs_opt = self.get('QB_ROOTFS_OPT')
958 if qb_rootfs_opt:
959 self.rootfs_options = qb_rootfs_opt.replace('@ROOTFS@', self.rootfs)
960 else:
961 self.rootfs_options = '-drive file=%s,if=virtio,format=%s' % (self.rootfs, rootfs_format)
962
963 if self.fstype in ('cpio.gz', 'cpio'):
964 self.kernel_cmdline = 'root=/dev/ram0 rw debugshell'
965 self.rootfs_options = '-initrd %s' % self.rootfs
966 else:
967 if self.fstype in self.vmtypes:
968 if self.fstype == 'iso':
969 vm_drive = '-cdrom %s' % self.rootfs
970 else:
971 cmd1 = "grep -q 'root=/dev/sd' %s" % self.rootfs
972 cmd2 = "grep -q 'root=/dev/hd' %s" % self.rootfs
973 if subprocess.call(cmd1, shell=True) == 0:
974 logger.info('Using scsi drive')
975 vm_drive = '-drive if=none,id=hd,file=%s,format=%s -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd' \
976 % (self.rootfs, rootfs_format)
977 elif subprocess.call(cmd2, shell=True) == 0:
978 logger.info('Using ide drive')
979 vm_drive = "%s,format=%s" % (self.rootfs, rootfs_format)
980 else:
981 logger.warn("Can't detect drive type %s" % self.rootfs)
982 logger.warn('Trying to use virtio block drive')
983 vm_drive = '-drive if=virtio,file=%s,format=%s' % (self.rootfs, rootfs_format)
984 self.rootfs_options = '%s -no-reboot' % vm_drive
985 self.kernel_cmdline = 'root=%s rw highres=off' % (self.get('QB_KERNEL_ROOT'))
986
987 if self.fstype == 'nfs':
988 self.rootfs_options = ''
989 k_root = '/dev/nfs nfsroot=%s:%s,%s' % (self.nfs_server, self.nfs_dir, self.unfs_opts)
990 self.kernel_cmdline = 'root=%s rw highres=off' % k_root
991
992 if self.fstype == 'none':
993 self.rootfs_options = ''
994
995 self.set('ROOTFS_OPTIONS', self.rootfs_options)
996
997 def guess_qb_system(self):
998 """attempt to determine the appropriate qemu-system binary"""
999 mach = self.get('MACHINE')
1000 if not mach:
1001 search = '.*(qemux86-64|qemux86|qemuarm64|qemuarm|qemumips64|qemumips64el|qemumipsel|qemumips|qemuppc).*'
1002 if self.rootfs:
1003 match = re.match(search, self.rootfs)
1004 if match:
1005 mach = match.group(1)
1006 elif self.kernel:
1007 match = re.match(search, self.kernel)
1008 if match:
1009 mach = match.group(1)
1010
1011 if not mach:
1012 return None
1013
1014 if mach == 'qemuarm':
1015 qbsys = 'arm'
1016 elif mach == 'qemuarm64':
1017 qbsys = 'aarch64'
1018 elif mach == 'qemux86':
1019 qbsys = 'i386'
1020 elif mach == 'qemux86-64':
1021 qbsys = 'x86_64'
1022 elif mach == 'qemuppc':
1023 qbsys = 'ppc'
1024 elif mach == 'qemumips':
1025 qbsys = 'mips'
1026 elif mach == 'qemumips64':
1027 qbsys = 'mips64'
1028 elif mach == 'qemumipsel':
1029 qbsys = 'mipsel'
1030 elif mach == 'qemumips64el':
1031 qbsys = 'mips64el'
1032
1033 return 'qemu-system-%s' % qbsys
1034
1035 def setup_final(self):
1036 qemu_system = self.get('QB_SYSTEM_NAME')
1037 if not qemu_system:
1038 qemu_system = self.guess_qb_system()
1039 if not qemu_system:
1040 raise Exception("Failed to boot, QB_SYSTEM_NAME is NULL!")
1041
1042 qemu_bin = '%s/%s' % (self.get('STAGING_BINDIR_NATIVE'), qemu_system)
1043
1044 # It is possible to have qemu-native in ASSUME_PROVIDED, and it won't
1045 # find QEMU in sysroot, it needs to use host's qemu.
1046 if not os.path.exists(qemu_bin):
1047 logger.info("QEMU binary not found in %s, trying host's QEMU" % qemu_bin)
1048 for path in (os.environ['PATH'] or '').split(':'):
1049 qemu_bin_tmp = os.path.join(path, qemu_system)
1050 logger.info("Trying: %s" % qemu_bin_tmp)
1051 if os.path.exists(qemu_bin_tmp):
1052 qemu_bin = qemu_bin_tmp
1053 if not os.path.isabs(qemu_bin):
1054 qemu_bin = os.path.abspath(qemu_bin)
1055 logger.info("Using host's QEMU: %s" % qemu_bin)
1056 break
1057
1058 if not os.access(qemu_bin, os.X_OK):
1059 raise OEPathError("No QEMU binary '%s' could be found" % qemu_bin)
1060
1061 check_libgl(qemu_bin)
1062
1063 self.qemu_opt = "%s %s %s %s" % (qemu_bin, self.get('NETWORK_CMD'), self.get('ROOTFS_OPTIONS'), self.get('QB_OPT_APPEND'))
1064
1065 for ovmf in self.ovmf_bios:
1066 format = ovmf.rsplit('.', 1)[-1]
1067 self.qemu_opt += ' -drive if=pflash,format=%s,file=%s' % (format, ovmf)
1068 if self.ovmf_bios:
1069 # OVMF only supports normal VGA, i.e. we need to override a -vga vmware
1070 # that gets added for example for normal qemux86.
1071 self.qemu_opt += ' -vga std'
1072
1073 self.qemu_opt += ' ' + self.qemu_opt_script
1074
1075 if self.snapshot:
1076 self.qemu_opt += " -snapshot"
1077
1078 if self.serialstdio:
1079 logger.info("Interrupt character is '^]'")
1080 cmd = "stty intr ^]"
1081 subprocess.call(cmd, shell=True)
1082
1083 first_serial = ""
1084 if not re.search("-nographic", self.qemu_opt):
1085 first_serial = "-serial mon:vc"
1086 # We always want a ttyS1. Since qemu by default adds a serial
1087 # port when nodefaults is not specified, it seems that all that
1088 # would be needed is to make sure a "-serial" is there. However,
1089 # it appears that when "-serial" is specified, it ignores the
1090 # default serial port that is normally added. So here we make
1091 # sure to add two -serial if there are none. And only one if
1092 # there is one -serial already.
1093 serial_num = len(re.findall("-serial", self.qemu_opt))
1094 if serial_num == 0:
1095 self.qemu_opt += " %s %s" % (first_serial, self.get("QB_SERIAL_OPT"))
1096 elif serial_num == 1:
1097 self.qemu_opt += " %s" % self.get("QB_SERIAL_OPT")
1098
1099 # We always wants ttyS0 and ttyS1 in qemu machines (see SERIAL_CONSOLES),
1100 # if not serial or serialtcp options was specified only ttyS0 is created
1101 # and sysvinit shows an error trying to enable ttyS1:
1102 # INIT: Id "S1" respawning too fast: disabled for 5 minutes
1103 serial_num = len(re.findall("-serial", self.qemu_opt))
1104 if serial_num == 0:
1105 if re.search("-nographic", self.qemu_opt):
1106 self.qemu_opt += " -serial mon:stdio -serial null"
1107 else:
1108 self.qemu_opt += " -serial mon:vc -serial null"
1109
1110 def start_qemu(self):
1111 if self.kernel:
1112 kernel_opts = "-kernel %s -append '%s %s %s %s'" % (self.kernel, self.kernel_cmdline,
1113 self.kernel_cmdline_script, self.get('QB_KERNEL_CMDLINE_APPEND'),
1114 self.bootparams)
1115 if self.dtb:
1116 kernel_opts += " -dtb %s" % self.dtb
1117 else:
1118 kernel_opts = ""
1119 cmd = "%s %s" % (self.qemu_opt, kernel_opts)
1120 logger.info('Running %s' % cmd)
1121 if subprocess.call(cmd, shell=True) != 0:
1122 raise Exception('Failed to run %s' % cmd)
1123
1124 def cleanup(self):
1125 if self.cleantap:
1126 cmd = 'sudo %s %s %s' % (self.qemuifdown, self.tap, self.get('STAGING_DIR_NATIVE'))
1127 logger.info('Running %s' % cmd)
1128 subprocess.call(cmd, shell=True)
1129 if self.lock_descriptor:
1130 logger.info("Releasing lockfile for tap device '%s'" % self.tap)
1131 self.release_lock()
1132
1133 if self.nfs_running:
1134 logger.info("Shutting down the userspace NFS server...")
1135 cmd = "runqemu-export-rootfs stop %s" % self.nfs_dir
1136 logger.info('Running %s' % cmd)
1137 subprocess.call(cmd, shell=True)
1138
1139 if self.saved_stty:
1140 cmd = "stty %s" % self.saved_stty
1141 subprocess.call(cmd, shell=True)
1142
1143 if self.clean_nfs_dir:
1144 logger.info('Removing %s' % self.nfs_dir)
1145 shutil.rmtree(self.nfs_dir)
1146 shutil.rmtree('%s.pseudo_state' % self.nfs_dir)
1147
1148 def load_bitbake_env(self, mach=None):
1149 if self.bitbake_e:
1150 return
1151
1152 bitbake = shutil.which('bitbake')
1153 if not bitbake:
1154 return
1155
1156 if not mach:
1157 mach = self.get('MACHINE')
1158
1159 if mach:
1160 cmd = 'MACHINE=%s bitbake -e' % mach
1161 else:
1162 cmd = 'bitbake -e'
1163
1164 logger.info('Running %s...' % cmd)
1165 try:
1166 self.bitbake_e = subprocess.check_output(cmd, shell=True).decode('utf-8')
1167 except subprocess.CalledProcessError as err:
1168 self.bitbake_e = ''
1169 logger.warn("Couldn't run 'bitbake -e' to gather environment information:\n%s" % err.output.decode('utf-8'))
1170
1171 def main():
1172 if len(sys.argv) == 1 or "help" in sys.argv or \
1173 '-h' in sys.argv or '--help' in sys.argv:
1174 print_usage()
1175 return 0
1176 config = BaseConfig()
1177 try:
1178 config.check_args()
1179 except Exception as esc:
1180 logger.error(esc)
1181 logger.error("Try 'runqemu help' on how to use it")
1182 return 1
1183 config.read_qemuboot()
1184 config.check_and_set()
1185 config.print_config()
1186 try:
1187 config.setup_network()
1188 config.setup_rootfs()
1189 config.setup_final()
1190 config.start_qemu()
1191 finally:
1192 config.cleanup()
1193 return 0
1194
1195 if __name__ == "__main__":
1196 try:
1197 ret = main()
1198 except OEPathError as err:
1199 ret = 1
1200 logger.error(err.message)
1201 except Exception as esc:
1202 ret = 1
1203 import traceback
1204 traceback.print_exc()
1205 sys.exit(ret)