1#!/usr/bin/env python3
2"""Run Trusty under QEMU in different configurations"""
3import enum
4import errno
5import fcntl
6import json
7import logging
8import os
9from textwrap import dedent
10import re
11import select
12import socket
13import subprocess
14import shutil
15import sys
16import tempfile
17import time
18import threading
19
20from typing import Optional, List
21
22import qemu_options
23from qemu_error import AdbFailure, ConfigError, RunnerGenericError, Timeout
24
25logger = logging.getLogger(__name__)
26
27# ADB expects its first console on 5554, and control on 5555
28ADB_BASE_PORT = 5554
29
30_TRUSTY_PRODUCT_PATH = "target/product/trusty"
31_ANDROID_HOST_PATH = "host/linux-x86"
32
33def find_android_build_dir(android):
34    if os.path.exists(os.path.join(android, _TRUSTY_PRODUCT_PATH)):
35        return android
36    if os.path.exists(os.path.join(android, "out", _TRUSTY_PRODUCT_PATH)):
37        return os.path.join(android, "out")
38
39    print(f"{android} not an Android source or build directory")
40    sys.exit(1)
41
42
43def find_android_image_dir(android):
44    return os.path.join(find_android_build_dir(android), _TRUSTY_PRODUCT_PATH)
45
46def find_adb_path(android):
47    return os.path.join(find_android_build_dir(android), _ANDROID_HOST_PATH, "bin/adb")
48
49
50class Config(object):
51    """Stores a QEMU configuration for use with the runner
52
53    Attributes:
54        boot_android:     Boolean indicating to boot Android. Setting the
55                          "android" config option to a path containing a
56                          built Android tree or prebuilt will implicitly
57                          set this attribute to true
58        android_image_dir: Path to directory containing Android images.
59                           Can be set by providing a built Android tree
60                           or prebuilt with the "android" config option.
61                           Implies booting android.
62        linux:            Path to a built Linux kernel tree or prebuilt
63                          kernel image.
64        linux_arch:       Architecture of Linux kernel.
65        atf:              Path to the ATF build to use.
66        qemu:             Path to the emulator to use.
67        arch:             Architecture definition.
68        rpmbd:            Path to the rpmb daemon to use.
69        adb:              Path to adb host tool.
70        extra_qemu_flags: Extra flags to pass to QEMU.
71    Setting android or linux to a false value will result in a QEMU which starts
72    without those components. Only one of android and android_image_dir may be provided
73    in the config.
74    """
75
76    def __init__(self, config=None):
77        """Qemu Configuration
78
79        If config is passed in, it should be a file containing a json
80        specification fields described in the docs.
81        Unspecified fields will be defaulted.
82
83        If you do not pass in a config, you will almost always need to
84        override these values; the default is not especially useful.
85        """
86        config_dict = {}
87        if config:
88            config_dict = json.load(config)
89
90        self.script_dir = os.path.dirname(os.path.realpath(__file__))
91
92        def abspath(config_key, default_value=None):
93            if config_value := config_dict.get(config_key, default_value):
94                return os.path.join(self.script_dir, config_value)
95            return None
96
97        if config_dict.get("android") and config_dict.get("android_image_dir"):
98            raise ConfigError("Config may only have one of android and android_image_dir")
99
100        self.adb = abspath("adb")
101        if android_path := abspath("android"):
102            logger.error("`android` config setting is deprecated. Please replace with "
103                         "`android_image_dir` and `adb` config entries.")
104            android_out = find_android_build_dir(android_path)
105            self.android_image_dir = os.path.join(android_out, _TRUSTY_PRODUCT_PATH)
106            if self.adb is None:
107                self.adb = os.path.join(android_out, _ANDROID_HOST_PATH, "bin/adb")
108        else:
109            self.android_image_dir = abspath("android_image_dir")
110        self.boot_android = self.android_image_dir is not None
111        self.linux = abspath("linux")
112        self.linux_arch = config_dict.get("linux_arch")
113        self.atf = abspath("atf")
114        self.qemu = abspath("qemu", "qemu-system-aarch64")
115        self.rpmbd = abspath("rpmbd")
116        self.arch = config_dict.get("arch")
117        self.extra_qemu_flags = config_dict.get("extra_qemu_flags", [])
118
119    def check_config(self, interactive: bool, boot_tests=(),
120                     android_tests=()):
121        """Checks the runner/qemu config to make sure they are compatible"""
122        # If we have any android tests, we need a linux dir and android dir
123        if android_tests:
124            if not self.linux:
125                raise ConfigError("Need Linux to run android tests")
126            if not self.boot_android:
127                raise ConfigError("Need Android to run android tests")
128
129        # For now, we can't run boot tests and android tests at the same time,
130        # because test-runner reports its exit code by terminating the
131        # emulator.
132        if android_tests:
133            if boot_tests:
134                raise ConfigError("Cannot run Android tests and boot"
135                                  " tests from same runner")
136
137        # Since boot test utilizes virtio serial console port for communication
138        # between QEMU guest and current process, it is not compatible with
139        # interactive mode.
140        if boot_tests:
141            if interactive:
142                raise ConfigError("Cannot run boot tests interactively")
143
144        if self.boot_android:
145            if not self.linux:
146                raise ConfigError("Cannot run Android without Linux")
147
148            if not self.android_image_dir:
149                raise ConfigError("Missing android_image_dir for Android")
150
151            if not self.adb:
152                raise ConfigError("Missing adb tool for Android")
153
154
155def alloc_ports():
156    """Allocates 2 sequential ports above 5554 for adb"""
157    # adb uses ports in pairs
158    port_width = 2
159
160    # We can't actually reserve ports atomically for QEMU, but we can at
161    # least scan and find two that are not currently in use.
162    min_port = ADB_BASE_PORT
163    while True:
164        alloced_ports = []
165        for port in range(min_port, min_port + port_width):
166            # If the port is already in use, don't hand it out
167            try:
168                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
169                sock.connect(("localhost", port))
170                break
171            except IOError:
172                alloced_ports += [port]
173        if len(alloced_ports) == port_width:
174            return alloced_ports
175
176        # We could increment by only 1, but if we are competing with other
177        # adb sessions for ports, this will be more polite
178        min_port += port_width
179
180
181def forward_ports(ports):
182    """Generates arguments to forward ports in QEMU on a virtio network"""
183    forwards = ""
184    remap_port = ADB_BASE_PORT
185    for port in ports:
186        forwards += f",hostfwd=tcp::{port}-:{remap_port}"
187        remap_port = remap_port + 1
188    return [
189        "-device", "virtio-net,netdev=adbnet0", "-netdev",
190        "user,id=adbnet0" + forwards
191    ]
192
193
194class QEMUCommandPipe(object):
195    """Communicate with QEMU."""
196
197    def __init__(self):
198        """Produces pipes for talking to QEMU and args to enable them."""
199        self.command_dir = tempfile.mkdtemp()
200        os.mkfifo(f"{self.command_dir}/com.in")
201        os.mkfifo(f"{self.command_dir}/com.out")
202        self.command_args = [
203            "-chardev",
204            f"pipe,id=command0,path={self.command_dir}/com", "-mon",
205            "chardev=command0,mode=control"
206        ]
207        self.com_pipe_in = None
208        self.com_pipe_out = None
209
210    def open(self):
211        # pylint: disable=consider-using-with
212        self.com_pipe_in = open(f"{self.command_dir}/com.in", "w",
213                                encoding="utf-8")
214        self.com_pipe_out = open(f"{self.command_dir}/com.out", "r",
215                                 encoding="utf-8")
216        self.qmp_command({"execute": "qmp_capabilities"})
217
218    def close(self):
219        """Close and clean up command pipes."""
220
221        def try_close(pipe):
222            try:
223                pipe.close()
224            except IOError as e:
225                print("close error ignored", e)
226
227        try_close(self.com_pipe_in)
228        try_close(self.com_pipe_out)
229
230        # Onerror callback function to handle errors when we try to remove
231        # command pipe directory, since we sleep one second if QEMU doesn't
232        # die immediately, command pipe directory might has been removed
233        # already during sleep period.
234        def cb_handle_error(func, path, exc_info):
235            if not os.access(path, os.F_OK):
236                # Command pipe directory already removed, this case is
237                # expected, pass this case.
238                pass
239            else:
240                raise RunnerGenericError("Failed to clean up command pipe.")
241
242        # Clean up our command pipe
243        shutil.rmtree(self.command_dir, onerror=cb_handle_error)
244
245    def qmp_command(self, qmp_command):
246        """Send a qmp command and return result."""
247
248        try:
249            json.dump(qmp_command, self.com_pipe_in)
250            self.com_pipe_in.flush()
251            for line in iter(self.com_pipe_out.readline, ""):
252                res = json.loads(line)
253
254                if err := res.get("error"):
255                    sys.stderr.write(f"Command {qmp_command} failed: {err}\n")
256                    return res
257
258                if "return" in res:
259                    return res
260
261                if "QMP" not in res and "event" not in res:
262                    # Print unexpected extra lines
263                    sys.stderr.write("ignored:" + line)
264        except IOError as e:
265            print("qmp_command error ignored", e)
266
267        return None
268
269    def qmp_execute(self, execute, arguments=None):
270        """Send a qmp execute command and return result."""
271        cmp_command = {"execute": execute}
272        if arguments:
273            cmp_command["arguments"] = arguments
274        return self.qmp_command(cmp_command)
275
276    def monitor_command(self, monitor_command, log_return=True):
277        """Send a monitor command and write result to stderr."""
278
279        res = self.qmp_execute("human-monitor-command",
280                               {"command-line": monitor_command})
281        if log_return and res and "return" in res:
282            sys.stderr.write(res["return"])
283
284
285def qemu_handle_error(command_pipe, debug_on_error):
286    """Dump registers and/or wait for debugger."""
287
288    sys.stdout.flush()
289
290    sys.stderr.write("QEMU register dump:\n")
291    command_pipe.monitor_command("info registers -a")
292    sys.stderr.write("\n")
293
294    if debug_on_error:
295        command_pipe.monitor_command("gdbserver")
296        print("Connect gdb, press enter when done ")
297        select.select([sys.stdin], [], [])
298        input("\n")  # pylint: disable=bad-builtin
299
300
301def qemu_exit(command_pipe, qemu_proc, has_error, debug_on_error):
302    """Ensures QEMU is terminated. Tries to write to drive image files."""
303    unclean_exit = False
304
305    if command_pipe:
306        # Ask QEMU to quit
307        if qemu_proc and (qemu_proc.poll() is None):
308            try:
309                if has_error:
310                    qemu_handle_error(command_pipe=command_pipe,
311                                      debug_on_error=debug_on_error)
312                command_pipe.qmp_execute("quit")
313            except OSError:
314                pass
315
316            # If it doesn't die immediately, wait a second
317            if qemu_proc.poll() is None:
318                time.sleep(1)
319                # If it's still not dead, take it out
320                if qemu_proc.poll() is None:
321                    qemu_proc.kill()
322                    print("QEMU refused quit")
323                    unclean_exit = True
324            qemu_proc.wait()
325
326        command_pipe.close()
327
328    elif qemu_proc and (qemu_proc.poll() is None):
329        # This was an interactive run or a boot test
330        # QEMU should not be running at this point
331        print("QEMU still running with no command channel")
332        qemu_proc.kill()
333        qemu_proc.wait()
334        unclean_exit = True
335    return unclean_exit
336
337class RunnerSession:
338    """Hold shared state between runner launch and shutdown."""
339
340    def __init__(self):
341        self.has_error = False
342        self.command_pipe = None
343        self.qemu_proc = None
344        self.ports = None
345        # stores the arguments used to start qemu iff performing a boot test
346        self.args = []
347        self.temp_files = []
348
349    def get_qemu_arg_temp_file(self):
350        """Returns a temp file that will be deleted after qemu exits."""
351        tmp = tempfile.NamedTemporaryFile(delete=False)  # pylint: disable=consider-using-with
352        self.temp_files.append(tmp.name)
353        return tmp
354
355
356class RunnerState(enum.Enum):
357    OFF = 0
358    BOOTLOADER = 1
359    ANDROID = 2
360
361
362class Runner(object):
363    """Executes tests in QEMU"""
364
365    def __init__(self,
366                 config,
367                 instance_dir: os.PathLike,
368                 interactive=False,
369                 verbose=False,
370                 rpmb=True,
371                 debug=False,
372                 debug_on_error=False):
373        """Initializes the runner with provided settings.
374
375        See .run() for the meanings of these.
376        """
377        self.config = config
378        self.interactive = interactive
379        self.debug = debug
380        self.verbose = verbose
381        self.adb_transport = None
382        self.use_rpmb = rpmb
383        self.rpmb_proc = None
384        self.rpmb_sock_dir = None
385        self.msg_sock = None
386        self.msg_sock_conn = None
387        self.msg_sock_dir = None
388        self.debug_on_error = debug_on_error
389        self.dump_stdout_on_error = False
390        self.default_timeout = 60 * 10  # 10 Minutes
391        self.session: Optional[RunnerSession] = None
392        self.state = RunnerState.OFF
393        self.instance_dir = instance_dir
394
395        # If we're not verbose or interactive, squelch command output
396        if verbose or self.interactive:
397            self.stdout = None
398            self.stderr = None
399        else:
400            self.stdout = tempfile.TemporaryFile()  # pylint: disable=consider-using-with
401            self.stderr = subprocess.STDOUT
402            self.dump_stdout_on_error = True
403
404        # If we're interactive connect stdin to the user
405        if self.interactive:
406            self.stdin = None
407        else:
408            self.stdin = subprocess.DEVNULL
409
410        if self.config.arch in ("arm64", "arm"):
411            self.qemu_arch_options = qemu_options.QemuArm64Options(
412                self.config, self.instance_dir)
413        elif self.config.arch == "x86_64":
414            # pylint: disable=no-member
415            self.qemu_arch_options = qemu_options.QemuX86_64Options(self.config) # type: ignore[attr-defined]
416        else:
417            raise ConfigError("Architecture unspecified or unsupported!")
418
419    def error_dump_output(self):
420        if self.dump_stdout_on_error:
421            sys.stdout.flush()
422            sys.stderr.write("System log:\n")
423            self.stdout.seek(0)
424            sys.stderr.buffer.write(self.stdout.read())
425
426    def get_qemu_arg_temp_file(self):
427        """Returns a temp file that will be deleted after qemu exits."""
428        # pylint: disable=consider-using-with
429        tmp = tempfile.NamedTemporaryFile(delete=False)
430        self.session.temp_files.append(tmp.name)
431        return tmp
432
433    def rpmb_up(self):
434        """Brings up the rpmb daemon, returning QEMU args to connect"""
435        rpmb_data = self.qemu_arch_options.rpmb_data_path()
436
437        self.rpmb_sock_dir = tempfile.mkdtemp()
438        rpmb_sock = f"{self.rpmb_sock_dir}/rpmb"
439        # pylint: disable=consider-using-with
440        rpmb_proc = subprocess.Popen([self.config.rpmbd,
441                                      "-d", rpmb_data,
442                                      "--sock", rpmb_sock])
443        self.rpmb_proc = rpmb_proc
444
445        # Wait for RPMB socket to appear to avoid a race with QEMU
446        test_sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
447        tries = 0
448        max_tries = 10
449        while True:
450            tries += 1
451            try:
452                test_sock.connect(rpmb_sock)
453                break
454            except socket.error as exn:
455                if tries >= max_tries:
456                    raise exn
457                time.sleep(1)
458
459        return self.qemu_arch_options.rpmb_options(rpmb_sock)
460
461    def rpmb_down(self):
462        """Kills the running rpmb daemon, cleaning up its socket directory"""
463        if self.rpmb_proc:
464            self.rpmb_proc.kill()
465            self.rpmb_proc = None
466        if self.rpmb_sock_dir:
467            shutil.rmtree(self.rpmb_sock_dir)
468            self.rpmb_sock_dir = None
469
470    def msg_channel_up(self):
471        """Create message channel between host and QEMU guest
472
473        Virtual serial console port 'testrunner0' is introduced as socket
474        communication channel for QEMU guest and current process. Testrunner
475        enumerates this port, reads test case which to be executed from
476        testrunner0 port, sends output log message and test result to
477        testrunner0 port.
478        """
479
480        self.msg_sock_dir = tempfile.mkdtemp()
481        msg_sock_file = f"{self.msg_sock_dir}/msg"
482        self.msg_sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
483        self.msg_sock.bind(msg_sock_file)
484
485        # Listen on message socket
486        self.msg_sock.listen(1)
487
488        return ["-device",
489                "virtserialport,chardev=testrunner0,name=testrunner0",
490                "-chardev", f"socket,id=testrunner0,path={msg_sock_file}"]
491
492    def msg_channel_down(self):
493        if self.msg_sock_conn:
494            self.msg_sock_conn.close()
495            self.msg_sock_conn = None
496        if self.msg_sock_dir:
497            shutil.rmtree(self.msg_sock_dir)
498            self.msg_sock_dir = None
499
500    def msg_channel_wait_for_connection(self):
501        """wait for testrunner to connect."""
502
503        # Accept testrunner's connection request
504        self.msg_sock_conn, _ = self.msg_sock.accept()
505
506    def msg_channel_send_msg(self, msg):
507        """Send message to testrunner via testrunner0 port
508
509        Testrunner tries to connect port while message with following format
510        "boottest your.port.here". Currently, we utilize this format to execute
511        cases in boot test.
512        If message does not comply above format, testrunner starts to launch
513        secondary OS.
514
515        """
516        if self.msg_sock_conn:
517            self.msg_sock_conn.send(msg.encode())
518        else:
519            sys.stderr.write("Connection has not been established yet!")
520
521    def msg_channel_recv(self):
522        if self.msg_sock_conn:
523            return self.msg_sock_conn.recv(64)
524
525        # error cases: channel not yet initialized or channel torn down
526        return bytes()
527
528    def msg_channel_close(self):
529        if self.msg_sock_conn:
530            self.msg_sock_conn.close()
531
532    def boottest_run(self, boot_tests, timeout=60 * 2):
533        """Run boot test cases"""
534        args = self.session.args
535        has_error = False
536        result = 2
537
538        if self.debug:
539            warning = """\
540                      Warning: Test selection does not work when --debug is set.
541                      To run a test in test runner, run in GDB:
542
543                      target remote :1234
544                      break host_get_cmdline
545                      c
546                      next 6
547                      set cmdline="boottest your.port.here"
548                      set cmdline_len=sizeof("boottest your.port.here")-1
549                      c
550                      """
551            print(dedent(warning))
552
553        if self.interactive:
554            args = ["-serial", "mon:stdio"] + args
555        else:
556            # This still leaves stdin connected, but doesn't connect a monitor
557            args = ["-serial", "stdio", "-monitor", "none"] + args
558
559        # Create command channel which used to quit QEMU after case execution
560        command_pipe = QEMUCommandPipe()
561        args += command_pipe.command_args
562        cmd = [self.config.qemu] + args
563
564        # pylint: disable=consider-using-with
565        qemu_proc = subprocess.Popen(cmd, cwd=self.config.atf,
566                                     stdin=self.stdin,
567                                     stdout=self.stdout,
568                                     stderr=self.stderr)
569
570        command_pipe.open()
571        self.msg_channel_wait_for_connection()
572
573        def kill_testrunner():
574            self.msg_channel_down()
575            qemu_exit(command_pipe, qemu_proc, has_error=True,
576                      debug_on_error=self.debug_on_error)
577            raise Timeout("Wait for boottest to complete", timeout)
578
579        kill_timer = threading.Timer(timeout, kill_testrunner)
580        if not self.debug:
581            kill_timer.start()
582
583        testcase = "boottest " + "".join(boot_tests)
584        try:
585            self.msg_channel_send_msg(testcase)
586
587            while True:
588                ret = self.msg_channel_recv()
589
590                # If connection is disconnected accidently by peer, for
591                # instance child QEMU process crashed, a message with length
592                # 0 would be received. We should drop this message, and
593                # indicate test framework that something abnormal happened.
594                if len(ret) == 0:
595                    has_error = True
596                    break
597
598                # Print message to STDOUT. Since we might meet EAGAIN IOError
599                # when writting to STDOUT, use try except loop to catch EAGAIN
600                # and waiting STDOUT to be available, then try to write again.
601                def print_msg(msg):
602                    while True:
603                        try:
604                            sys.stdout.write(msg)
605                            break
606                        except IOError as e:
607                            if e.errno != errno.EAGAIN:
608                                raise RunnerGenericError(
609                                    "Failed to print message") from e
610                            select.select([], [sys.stdout], [])
611
612                # Please align message structure definition in testrunner.
613                if ret[0] == 0:
614                    msg_len = ret[1]
615                    msg = ret[2 : 2 + msg_len].decode()
616                    print_msg(msg)
617                elif ret[0] == 1:
618                    result = ret[1]
619                    break
620                else:
621                    # Unexpected type, return test result:TEST_FAILED
622                    has_error = True
623                    result = 1
624                    break
625        finally:
626            kill_timer.cancel()
627            self.msg_channel_down()
628            self.session.has_error = has_error or result != 0
629            unclean_exit = qemu_exit(command_pipe, qemu_proc,
630                                     has_error=has_error,
631                                     debug_on_error=self.debug_on_error)
632
633        if unclean_exit:
634            raise RunnerGenericError("QEMU did not exit cleanly")
635
636        return result
637
638    def androidtest_run(self, cmd, test_timeout=None):
639        """Run android test cases"""
640        session: RunnerSession = self.session
641        assert session, "No session; must call launch before running any tests."
642
643        try:
644            if not test_timeout:
645                test_timeout = self.default_timeout
646
647            def on_adb_timeout():
648                print(f"adb Timed out ({test_timeout} s)")
649                qemu_handle_error(command_pipe=session.command_pipe,
650                                  debug_on_error=self.debug_on_error)
651
652            test_result = self.adb(["shell"] + cmd, timeout=test_timeout,
653                                   on_timeout=on_adb_timeout, force_output=True)
654            if test_result:
655                session.has_error = True
656
657            return test_result
658        except:
659            session.has_error = True
660            raise
661
662    def adb_bin(self):
663        """Returns location of adb"""
664        return self.config.adb
665
666    def adb(self,
667            args,
668            timeout=60,
669            on_timeout=lambda timeout: print(f"Timed out ({timeout} s)"),
670            force_output=False):
671        """Runs an adb command
672
673        If self.adb_transport is set, specializes the command to that
674        transport to allow for multiple simultaneous tests.
675
676        Timeout specifies a timeout for the command in seconds.
677
678        If force_output is set true, will send results to stdout and
679        stderr regardless of the runner's preferences.
680        """
681        if self.adb_transport:
682            args = ["-t", str(self.adb_transport)] + args
683
684        if force_output:
685            stdout = None
686            stderr = None
687        else:
688            stdout = self.stdout
689            stderr = self.stderr
690
691        adb_proc = subprocess.Popen(  # pylint: disable=consider-using-with
692            [self.adb_bin()] + args, stdin=self.stdin, stdout=stdout,
693            stderr=stderr)
694
695        status = 1
696        try:
697            status = adb_proc.wait(timeout)
698        except subprocess.TimeoutExpired:
699            if on_timeout:
700                on_timeout()
701
702            try:
703                adb_proc.kill()
704            except OSError:
705                pass
706
707        return status
708
709
710    def check_adb(self, args, **kwargs):
711        """As .adb(), but throws an exception if the command fails"""
712        code = self.adb(args, **kwargs)
713        if code != 0:
714            raise AdbFailure(args, code)
715
716    def adb_root(self):
717        """Restarts adbd with root permissions and waits until it's back up"""
718        max_tries = 10
719        num_tries = 0
720
721        # Ensure device is up else adb root can fail
722        self.adb(["wait-for-device"])
723        self.check_adb(["root"])
724
725        while True:
726            # adbd might not be down by this point yet
727            self.adb(["wait-for-device"])
728
729            # Check that adbd is up and running with root permissions
730            code = self.adb(["shell",
731                             "if [[ $(id -u) -ne 0 ]] ; then exit 1; fi"])
732            if code == 0:
733                return
734
735            num_tries += 1
736            if num_tries >= max_tries:
737                raise AdbFailure(["root"], code)
738            time.sleep(1)
739
740    def scan_transport(self, port, expect_none=False):
741        """Given a port and `adb devices -l`, find the transport id"""
742        output = subprocess.check_output([self.adb_bin(), "devices", "-l"],
743                                         universal_newlines=True)
744        match = re.search(fr"localhost:{port}.*transport_id:(\d+)", output)
745        if not match:
746            if expect_none:
747                self.adb_transport = None
748                return
749            raise RunnerGenericError(
750                f"Failed to find transport for port {port} in \n{output}")
751        self.adb_transport = int(match.group(1))
752
753    def adb_up(self, port):
754        """Ensures adb is connected to adbd on the selected port"""
755        # Wait until we can connect to the target port
756        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
757        connect_max_tries = 15
758        connect_tries = 0
759        while True:
760            try:
761                sock.connect(("localhost", port))
762                break
763            except IOError as ioe:
764                connect_tries += 1
765                if connect_tries >= connect_max_tries:
766                    raise Timeout("Wait for adbd socket",
767                                  connect_max_tries) from ioe
768                time.sleep(1)
769        sock.close()
770        self.check_adb(["connect", f"localhost:{port}"])
771        self.scan_transport(port)
772
773        # Sometimes adb can get stuck and will never connect. Using multiple
774        # shorter timeouts works better than one longer timeout in such cases.
775        adb_exception = None
776        for _ in range(10):
777            try:
778                self.check_adb(["wait-for-device"], timeout=30, on_timeout=None)
779                break
780            except AdbFailure as e:
781                adb_exception = e
782                continue
783        else:
784            print("'adb wait-for-device' Timed out")
785            raise adb_exception
786
787        self.adb_root()
788
789        # Files put onto the data partition in the Android build will not
790        # actually be populated into userdata.img when make dist is used.
791        # To work around this, we manually update /data once the device is
792        # booted by pushing it the files that would have been there.
793        userdata = self.qemu_arch_options.android_trusty_user_data()
794        self.check_adb(["push", userdata, "/"])
795
796    def adb_down(self, port):
797        """Cleans up after adb connection to adbd on selected port"""
798        self.check_adb(["disconnect", f"localhost:{port}"])
799
800        # Wait until QEMU's forward has expired
801        connect_max_tries = 300
802        connect_tries = 0
803        while True:
804            try:
805                self.scan_transport(port, expect_none=True)
806                if not self.adb_transport:
807                    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
808                    sock.connect(("localhost", port))
809                    sock.close()
810                connect_tries += 1
811                if connect_tries >= connect_max_tries:
812                    raise Timeout("Wait for port forward to go away",
813                                  connect_max_tries)
814                time.sleep(1)
815            except IOError:
816                break
817
818    def universal_args(self):
819        """Generates arguments used in all qemu invocations"""
820        args = self.qemu_arch_options.basic_options()
821        args += self.qemu_arch_options.bios_options()
822
823        if self.config.linux:
824            args += self.qemu_arch_options.linux_options()
825
826        if self.config.boot_android:
827            args += self.qemu_arch_options.android_drives_args()
828
829        # Append configured extra flags
830        args += self.config.extra_qemu_flags
831
832        return args
833
834    def launch(self, target_state):
835        """Launches the QEMU execution.
836
837        If interactive is specified, it will leave the user connected
838        to the serial console/monitor, and they are responsible for
839        terminating execution.
840
841        If debug is on, the main QEMU instance will be launched with -S and
842        -s, which pause the CPU rather than booting, and starts a gdb server
843        on port 1234 respectively.
844
845        It is the responsibility of callers to ensure that shutdown gets called
846        after launch - regardless of whether the launch succeeded or not.
847
848        Limitations:
849          If the adb port range is already in use, port forwarding may fail.
850
851        TODO: For boot tests, the emulator isn't actually launched here but in
852              boottest_run. Eventually, we want to unify the way boot tests and
853              android tests are launched. Specifically, we might stop execution
854              in the bootloader and have it wait for a command to boot android.
855        """
856        assert self.state == RunnerState.OFF
857        assert target_state in [RunnerState.BOOTLOADER,
858                                RunnerState.ANDROID], target_state
859
860        self.session = RunnerSession()
861        args = self.universal_args()
862
863        try:
864            if self.use_rpmb:
865                self.qemu_arch_options.create_rpmb_data()
866                args += self.rpmb_up()
867
868            if self.config.boot_android:
869                self.qemu_arch_options.create_drives_data()
870
871            if self.config.linux:
872                args += self.qemu_arch_options.gen_dtb(
873                    args,
874                    self.session.get_qemu_arg_temp_file())
875
876            # Prepend the machine since we don't need to edit it as in gen_dtb
877            args = self.qemu_arch_options.machine_options() + args
878
879            if self.debug:
880                args += ["-s", "-S"]
881
882            # Create socket for communication channel
883            args += self.msg_channel_up()
884
885            if target_state == RunnerState.BOOTLOADER:
886                self.session.args = args
887                self.state = target_state
888                return
889
890            # Logging and terminal monitor
891            # Prepend so that it is the *first* serial port and avoid
892            # conflicting with rpmb0.
893            args = ["-serial", "mon:stdio"] + args
894
895            # If we're noninteractive (e.g. testing) we need a command channel
896            # to tell the guest to exit
897            if not self.interactive:
898                self.session.command_pipe = QEMUCommandPipe()
899                args += self.session.command_pipe.command_args
900
901            # Reserve ADB ports
902            self.session.ports = alloc_ports()
903
904            # Write expected serial number (as given in adb) to stdout.
905            sys.stdout.write(
906                f"DEVICE_SERIAL: localhost:{self.session.ports[1]}\n")
907            sys.stdout.flush()
908
909            # Forward ADB ports in qemu
910            args += forward_ports(self.session.ports)
911
912            qemu_cmd = [self.config.qemu] + args
913            logger.info("qemu command: %s", qemu_cmd)
914            self.session.qemu_proc = subprocess.Popen(  # pylint: disable=consider-using-with
915                qemu_cmd,
916                cwd=self.config.atf,
917                stdin=self.stdin,
918                stdout=self.stdout,
919                stderr=self.stderr)
920
921            if self.session.command_pipe:
922                self.session.command_pipe.open()
923            self.msg_channel_wait_for_connection()
924
925            if self.debug:
926                script_dir = self.config.script_dir
927                if script_dir.endswith("/build-qemu-generic-arm64-test-debug"):
928                    print(f"Debug with: lldb --source {script_dir}/lldbinit")
929                else:
930                    print("Debug with: lldb --one-line 'gdb-remote 1234'")
931
932            # Send request to boot secondary OS
933            self.msg_channel_send_msg("Boot Secondary OS")
934
935            # Bring ADB up talking to the command port
936            self.adb_up(self.session.ports[1])
937
938            self.state = target_state
939        except:
940            self.session.has_error = True
941            raise
942
943    def shutdown(self, factory_reset: bool, full_wipe: bool):
944        """Shut down emulator after test cases have run
945
946        The launch and shutdown methods store shared state in a session object.
947        Calls to launch and shutdown must be correctly paired no matter whether
948        the launch steps and calls to adb succeed or fail.
949        """
950        if self.state == RunnerState.OFF:
951            return
952
953        assert self.session is not None
954
955        # Clean up generated device tree
956        for temp_file in self.session.temp_files:
957            try:
958                os.remove(temp_file)
959            except OSError:
960                pass
961
962        if self.session.has_error:
963            self.error_dump_output()
964
965        unclean_exit = qemu_exit(self.session.command_pipe,
966                                 self.session.qemu_proc,
967                                 has_error=self.session.has_error,
968                                 debug_on_error=self.debug_on_error)
969
970        fcntl.fcntl(0, fcntl.F_SETFL,
971                    fcntl.fcntl(0, fcntl.F_GETFL) & ~os.O_NONBLOCK)
972
973        self.rpmb_down()
974
975        self.msg_channel_down()
976
977        if self.adb_transport:
978            # Disconnect ADB and wait for our port to be released by qemu
979            self.adb_down(self.session.ports[1])
980
981        # Ideally, we'd clear on launch instead, but it doesn't know whether a
982        # clear should happen. We can't change launch to take factory_reset and
983        # full_wipe args because TrustyRebootCommand doesn't call launch, only
984        # shutdown. (The next test that runs after a reboot will re-launch when
985        # it notices the runner is down, but that test doesn't have the
986        # RebootMode info from the reboot.)
987        if factory_reset:
988            self.qemu_arch_options.delete_drives_data()
989        if full_wipe:
990            assert factory_reset, (
991                "Cannot perform a full wipe without factory resetting.")
992            self.qemu_arch_options.delete_rpmb_data()
993
994        self.session = None
995        self.state = RunnerState.OFF
996
997        if unclean_exit:
998            raise RunnerGenericError("QEMU did not exit cleanly")
999
1000    def reboot(self, target_state, factory_reset: bool, full_wipe: bool):
1001        self.shutdown(factory_reset, full_wipe)
1002
1003        try:
1004            self.launch(target_state)
1005        except:
1006            self.shutdown(factory_reset, full_wipe)
1007            raise
1008
1009    def run(self, boot_tests: Optional[List] = None,
1010            android_tests: Optional[List] = None,
1011            timeout: Optional[int] = None) -> List[int]:
1012        """Run boot or android tests.
1013
1014        Runs boot_tests through test_runner, android_tests through ADB,
1015        returning aggregated test return codes in a list.
1016
1017        Returns:
1018          A list of return codes for the provided tests.
1019          A negative return code indicates an internal tool failure.
1020
1021        Limitations:
1022          Until test_runner is updated, only one of android_tests or boot_tests
1023          may be provided.
1024          Similarly, while boot_tests is a list, test_runner only knows how to
1025          correctly run a single test at a time.
1026          Again due to test_runner's current state, if boot_tests are
1027          specified, interactive will be ignored since the machine will
1028          terminate itself.
1029
1030          If android_tests is provided, a Linux and Android dir must be
1031          provided in the config.
1032        """
1033        assert self.state == RunnerState.OFF
1034        self.config.check_config(self.interactive, boot_tests, android_tests)
1035
1036        if boot_tests and android_tests:
1037            raise RunnerGenericError(
1038                "Cannot run boot tests and android tests in the same "
1039                "QEMU instance")
1040
1041        if boot_tests and len(boot_tests) > 1:
1042            raise RunnerGenericError(
1043                "Can only run a single boot test at a time")
1044
1045        timeout = timeout if timeout else self.default_timeout
1046        try:
1047            self.launch(RunnerState.BOOTLOADER if boot_tests else
1048                        RunnerState.ANDROID)
1049            test_results = []
1050
1051            if boot_tests:
1052                test_results.append(self.boottest_run(boot_tests, timeout))
1053
1054            if android_tests:
1055                for android_test in android_tests:
1056                    test_result = self.androidtest_run([android_test], timeout)
1057                    test_results.append(test_result)
1058                    if test_result:
1059                        break
1060
1061            return test_results
1062        finally:
1063            # The wait on QEMU is done here to ensure that ADB failures do not
1064            # take away the user's serial console in interactive mode.
1065            if self.interactive and self.session:
1066                # The user is responsible for quitting QEMU
1067                self.session.qemu_proc.wait()
1068            self.shutdown(factory_reset=True, full_wipe=False)
1069