فهرست منبع

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 <ross.burton@arm.com>
Signed-off-by: Jon Mason <jon.mason@arm.com>
Ross Burton 3 سال پیش
والد
کامیت
2ecdf7e21d
3فایلهای تغییر یافته به همراه88 افزوده شده و 0 حذف شده
  1. 1 0
      .gitignore
  2. 0 0
      meta-arm/lib/oeqa/controllers/__init__.py
  3. 87 0
      meta-arm/lib/oeqa/controllers/fvp.py

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+__pycache__

+ 0 - 0
meta-arm/lib/oeqa/controllers/__init__.py


+ 87 - 0
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)