mirror of
https://git.yoctoproject.org/meta-zephyr
synced 2026-01-29 21:58:41 +01:00
This commit restructures meta-zephyr into meta-zephyr-core and meta-zephyr-bsp. It moves machine definitions into meta-zephyr-bsp in preparation for adding the autogenerated zephyr machines. Signed-off-by: Eilís Ní Fhlannagáin <elizabeth.flanagan@huawei.com> Signed-off-by: Naveen Saini <naveen.kumar.saini@intel.com>
227 lines
8.0 KiB
Python
227 lines
8.0 KiB
Python
# 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
|