diff options
Diffstat (limited to 'meta-zephyr-core/lib/oeqa/utils')
-rw-r--r-- | meta-zephyr-core/lib/oeqa/utils/qemuzephyrrunner.py | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/meta-zephyr-core/lib/oeqa/utils/qemuzephyrrunner.py b/meta-zephyr-core/lib/oeqa/utils/qemuzephyrrunner.py new file mode 100644 index 0000000..7b486ce --- /dev/null +++ b/meta-zephyr-core/lib/oeqa/utils/qemuzephyrrunner.py @@ -0,0 +1,226 @@ +# Copyright (C) 2015-2017 Intel Corporation +# +# Released under the MIT license (see COPYING.MIT) + +# This module provides a class for starting qemu images. +# It's used by testimage.bbclass. + +import subprocess +import os +import time +import signal +import socket +import select +import bb +import tempfile +import sys +import configparser +from oeqa.utils.qemurunner import QemuRunner + +class QemuZephyrRunner(QemuRunner): + + def __init__(self, machine, rootfs, display, tmpdir, deploy_dir_image, logfile, boottime, dump_dir, dump_host_cmds, use_kvm, logger, tmpfsdir): + + + QemuRunner.__init__(self, machine, rootfs, display, tmpdir, + deploy_dir_image, logfile, boottime, None, + None, use_kvm, logger, tmpfsdir) + + # Popen object for runqemu + self.socketfile = tempfile.NamedTemporaryFile() + self.runqemu = None + self.socketname = self.socketfile.name + self.server_socket = None + + self.kernel = rootfs + self.deploy_dir_image = deploy_dir_image + self.tmpfsdir = tmpfsdir + self.logfile = logfile + self.use_kvm = use_kvm + + self.buffers = b'' + self._rbufsize = 4096 + # 5 minutes timeout... + self.endtime = time.time() + 60*5 + + self.qemuboot = False + self.d = {'QB_KERNEL_ROOT': '/dev/vda'} + + def get(self, key): + if key in self.d: + return self.d.get(key) + elif os.getenv(key): + return os.getenv(key) + else: + return '' + + def set(self, key, value): + self.d[key] = value + + def read_qemuboot(self): + if not self.qemuboot: + if self.get('DEPLOY_DIR_IMAGE'): + deploy_dir_image = self.get('DEPLOY_DIR_IMAGE') + else: + bb.warning("Can't find qemuboot conf file, DEPLOY_DIR_IMAGE is NULL!") + return + + if self.rootfs and not os.path.exists(self.rootfs): + # Lazy rootfs + machine = self.get('MACHINE') + if not machine: + machine = os.path.basename(deploy_dir_image) + self.qemuboot = "%s/%s-%s.qemuboot.conf" % (deploy_dir_image, + self.rootfs, machine) + else: + cmd = 'ls -t %s/*.qemuboot.conf' % deploy_dir_image + try: + qbs = subprocess.check_output(cmd, shell=True).decode('utf-8') + except subprocess.CalledProcessError as err: + raise RunQemuError(err) + if qbs: + for qb in qbs.split(): + # Don't use initramfs when other choices unless fstype is ramfs + if '-initramfs-' in os.path.basename(qb) and self.fstype != 'cpio.gz': + continue + self.qemuboot = qb + break + if not self.qemuboot: + # Use the first one when no choice + self.qemuboot = qbs.split()[0] + self.qbconfload = True + + if not self.qemuboot: + # If we haven't found a .qemuboot.conf at this point it probably + # doesn't exist, continue without + return + + if not os.path.exists(self.qemuboot): + raise RunQemuError("Failed to find %s (wrong image name or BSP does not support running under qemu?)." % self.qemuboot) + + cf = configparser.ConfigParser() + cf.read(self.qemuboot) + for k, v in cf.items('config_bsp'): + k_upper = k.upper() + if v.startswith("../"): + v = os.path.abspath(os.path.dirname(self.qemuboot) + "/" + v) + elif v == ".": + v = os.path.dirname(self.qemuboot) + self.set(k_upper, v) + + + def create_socket(self): + bb.note("waiting at most %s seconds for qemu pid" % self.runqemutime) + tries = self.runqemutime + while tries > 0: + time.sleep(1) + try: + self.server_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.server_socket.connect(self.socketname) + bb.note("Created listening socket for qemu serial console.") + break + + except socket.error: + self.server_socket.close() + tries -= 1 + + if tries == 0: + bb.error("Failed to create listening socket %s: " % (self.socketname)) + return False + return True + + def start(self, params=None,runqemuparams=None, extra_bootparams=None): + + if not os.path.exists(self.tmpdir): + bb.error("Invalid TMPDIR path %s" % self.tmpdir) + return False + else: + os.environ["OE_TMPDIR"] = self.tmpdir + if not os.path.exists(self.deploy_dir_image): + bb.error("Invalid DEPLOY_DIR_IMAGE path %s" % self.deploy_dir_image) + return False + else: + os.environ["DEPLOY_DIR_IMAGE"] = self.deploy_dir_image + if self.tmpfsdir: + env["RUNQEMU_TMPFS_DIR"] = self.tmpfsdir + + if not os.path.exists(self.kernel): + bb.error("Invalid kernel path: %s" % self.kernel) + return False + + self.qemuparams = '-serial unix:%s,server' % (self.socketname) + + self.read_qemuboot() + qemu_binary = self.get('QB_SYSTEM_NAME') + qemu_machine_args = self.get('QB_MACHINE') + if qemu_binary == "" or qemu_machine_args == "": + bb.error("Unsupported QEMU: %s" % self.machine) + return False + + self.qemuparams += " %s " %self.get('QB_OPT_APPEND') + self.qemuparams += " %s " %self.get('QB_CPU') + + self.origchldhandler = signal.getsignal(signal.SIGCHLD) + signal.signal(signal.SIGCHLD, self.handleSIGCHLD) + + launch_cmd = '%s -kernel %s %s %s' % (qemu_binary, self.kernel, self.qemuparams, qemu_machine_args) + bb.note(launch_cmd) + self.runqemu = subprocess.Popen(launch_cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,preexec_fn=os.setpgrp) + + # + # We need the preexec_fn above so that all runqemu processes can easily be killed + # (by killing their process group). This presents a problem if this controlling + # process itself is killed however since those processes don't notice the death + # of the parent and merrily continue on. + # + # Rather than hack runqemu to deal with this, we add something here instead. + # Basically we fork off another process which holds an open pipe to the parent + # and also is setpgrp. If/when the pipe sees EOF from the parent dieing, it kills + # the process group. This is like pctrl's PDEATHSIG but for a process group + # rather than a single process. + # + r, w = os.pipe() + self.monitorpid = os.fork() + if self.monitorpid: + os.close(r) + self.monitorpipe = os.fdopen(w, "w") + else: + # child process + os.setpgrp() + os.close(w) + r = os.fdopen(r) + x = r.read() + os.killpg(os.getpgid(self.runqemu.pid), signal.SIGTERM) + os._exit(0) + + bb.note("qemu started, pid is %s" % self.runqemu.pid) + return self.create_socket() + + def _readline(self): + nl = self.buffers.find(b'\n') + if nl >= 0: + nl += 1 + line = self.buffers[:nl] + newbuf = self.buffers[nl:] + self.buffers = newbuf + return line + return None + + def serial_readline(self): + line = self._readline() + if line is None: + while True: + if time.time() >= self.endtime: + bb.warn("Timeout!") + raise Exception("Timeout") + data = self.server_socket.recv(self._rbufsize) + if data is None: + raise Exception("No data on read ready socket") + + self.buffers = self.buffers + data + line = self._readline() + if line is not None: + break + + self.log(line) + return line |