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