From 2ecdf7e21d1ed7c9763cdf4067ec179a81455ccf Mon Sep 17 00:00:00 2001 From: Ross Burton Date: Mon, 18 Oct 2021 16:40:52 +0100 Subject: [PATCH] arm/oeqa: add FVP testimage controller target Add a new oeqa.core.target.OETarget subclass for testimage which starts a FVP using runfvp. This uses --console to connect to the console so telnet is needed on the host. Signed-off-by: Ross Burton Signed-off-by: Jon Mason --- .gitignore | 1 + meta-arm/lib/oeqa/controllers/__init__.py | 0 meta-arm/lib/oeqa/controllers/fvp.py | 87 +++++++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 .gitignore create mode 100644 meta-arm/lib/oeqa/controllers/__init__.py create mode 100644 meta-arm/lib/oeqa/controllers/fvp.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..bee8a64b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__ diff --git a/meta-arm/lib/oeqa/controllers/__init__.py b/meta-arm/lib/oeqa/controllers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/meta-arm/lib/oeqa/controllers/fvp.py b/meta-arm/lib/oeqa/controllers/fvp.py new file mode 100644 index 00000000..1e26a519 --- /dev/null +++ b/meta-arm/lib/oeqa/controllers/fvp.py @@ -0,0 +1,87 @@ +import asyncio +import os +import pathlib +import signal +import subprocess + +import oeqa.core.target.ssh + +class OEFVPTarget(oeqa.core.target.ssh.OESSHTarget): + + # meta-arm/scripts isn't on PATH, so work out where it is + metaarm = pathlib.Path(__file__).parents[4] + + def __init__(self, logger, target_ip, server_ip, timeout=300, user='root', + port=None, server_port=0, dir_image=None, rootfs=None, bootlog=None, + **kwargs): + super().__init__(logger, target_ip, server_ip, timeout, user, port) + image_dir = pathlib.Path(dir_image) + basename = pathlib.Path(rootfs).stem + self.fvpconf = image_dir / (basename + ".fvpconf") + + if not self.fvpconf.exists(): + raise FileNotFoundError(f"Cannot find {self.fvpconf}") + # FVPs boot slowly, so allow ten minutes + self.boot_timeout = 10 * 60 + + self.logfile = bootlog and open(bootlog, "wb") or None + + async def boot_fvp(self): + cmd = [OEFVPTarget.metaarm / "scripts" / "runfvp", "--console", "--verbose", self.fvpconf] + # Python 3.7 needs the command items to be str + cmd = [str(c) for c in cmd] + self.logger.debug(f"Starting {cmd}") + + # TODO: refactor runfvp so this can import it and directly hook to the + # console callback, then use telnetlib directly to access the console. + + # As we're using --console, telnet expects stdin to be readable too. + self.fvp = await asyncio.create_subprocess_exec(*cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.PIPE) + self.logger.debug(f"Started runfvp PID {self.fvp.pid}") + + async def wait_for_login(): + bootlog = bytearray() + while True: + line = await self.fvp.stdout.read(1024) + if not line: + self.logger.debug("runfvp terminated") + return False, bootlog + + self.logger.debug(f"Read line [{line}]") + + bootlog += line + if self.logfile: + self.logfile.write(line) + + if b" login:" in bootlog: + self.logger.debug("Found login prompt") + return True, bootlog + return False, bootlog + + try: + found, bootlog = await asyncio.wait_for(wait_for_login(), self.boot_timeout) + if found: + return + except asyncio.TimeoutError: + self.logger.info("Timed out waiting for login prompt.") + self.logger.info(b"".join(bootlog.splitlines()[-20:]).decode("utf-8", errors="replace")) + raise RuntimeError("Failed to start FVP.") + + def start(self, **kwargs): + # When we can assume Py3.7+, this can simply be asyncio.run() + loop = asyncio.get_event_loop() + loop.run_until_complete(asyncio.gather(self.boot_fvp())) + + def stop(self, **kwargs): + loop = asyncio.get_event_loop() + + # Kill the process group so that the telnet and FVP die too + gid = os.getpgid(self.fvp.pid) + + try: + self.logger.debug(f"Sending SIGTERM to {gid}") + os.killpg(gid, signal.SIGTERM) + loop.run_until_complete(asyncio.wait_for(self.fvp.wait(), 10)) + except TimeoutError: + self.logger.debug(f"Timed out, sending SIGKILL to {gid}") + os.killpg(gid, signal.SIGKILL)