Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 1 | #! /usr/bin/env python3 |
| 2 | |
Patrick Williams | 7784c42 | 2022-11-17 07:29:11 -0600 | [diff] [blame] | 3 | import itertools |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 4 | import os |
| 5 | import pathlib |
| 6 | import signal |
| 7 | import sys |
Patrick Williams | 7784c42 | 2022-11-17 07:29:11 -0600 | [diff] [blame] | 8 | import threading |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 9 | |
| 10 | import logging |
| 11 | logger = logging.getLogger("RunFVP") |
| 12 | |
| 13 | # Add meta-arm/lib/ to path |
| 14 | libdir = pathlib.Path(__file__).parents[1] / "meta-arm" / "lib" |
| 15 | sys.path.insert(0, str(libdir)) |
| 16 | |
Andrew Geissler | 23e0279 | 2023-07-21 09:06:10 -0500 | [diff] [blame] | 17 | from fvp import conffile, terminal, runner |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 18 | |
| 19 | def parse_args(arguments): |
| 20 | import argparse |
| 21 | terminals = terminal.terminals |
| 22 | |
| 23 | parser = argparse.ArgumentParser(description="Run images in a FVP") |
| 24 | parser.add_argument("config", nargs="?", help="Machine name or path to .fvpconf file") |
| 25 | group = parser.add_mutually_exclusive_group() |
| 26 | group.add_argument("-t", "--terminals", choices=terminals.all_terminals(), default=terminals.preferred_terminal(), help="Automatically start terminals (default: %(default)s)") |
| 27 | group.add_argument("-c", "--console", action="store_true", help="Attach the first uart to stdin/stdout") |
| 28 | parser.add_argument("--verbose", action="store_true", help="Output verbose logging") |
| 29 | parser.usage = f"{parser.format_usage().strip()} -- [ arguments passed to FVP ]" |
| 30 | # TODO option for telnet vs netcat |
| 31 | |
| 32 | # If the arguments contains -- then everything after it should be passed to the FVP binary directly. |
| 33 | if "--" in arguments: |
| 34 | i = arguments.index("--") |
| 35 | fvp_args = arguments[i+1:] |
| 36 | arguments = arguments[:i] |
| 37 | else: |
| 38 | fvp_args = [] |
| 39 | |
| 40 | args = parser.parse_args(args=arguments) |
Patrick Williams | 7784c42 | 2022-11-17 07:29:11 -0600 | [diff] [blame] | 41 | logging.basicConfig(level=args.verbose and logging.DEBUG or logging.WARNING, |
| 42 | format='\033[G%(levelname)s: %(message)s') |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 43 | |
| 44 | # If we're hooking up the console, don't start any terminals |
| 45 | if args.console: |
| 46 | args.terminals = "none" |
| 47 | |
| 48 | logger.debug(f"Parsed arguments: {vars(args)}") |
| 49 | logger.debug(f"FVP arguments: {fvp_args}") |
| 50 | return args, fvp_args |
| 51 | |
Patrick Williams | e760df8 | 2023-05-26 11:10:49 -0500 | [diff] [blame] | 52 | def start_fvp(args, fvpconf, extra_args): |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 53 | fvp = runner.FVPRunner(logger) |
| 54 | try: |
Patrick Williams | e760df8 | 2023-05-26 11:10:49 -0500 | [diff] [blame] | 55 | fvp.start(fvpconf, extra_args, args.terminals) |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 56 | |
| 57 | if args.console: |
Patrick Williams | e760df8 | 2023-05-26 11:10:49 -0500 | [diff] [blame] | 58 | config = fvp.getConfig() |
| 59 | expected_terminal = config["consoles"].get("default") |
| 60 | if expected_terminal is None: |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 61 | logger.error("--console used but FVP_CONSOLE not set in machine configuration") |
| 62 | return 1 |
Patrick Williams | 7784c42 | 2022-11-17 07:29:11 -0600 | [diff] [blame] | 63 | port_stdout, log_stdout = itertools.tee(fvp.stdout, 2) |
| 64 | parser = runner.ConsolePortParser(port_stdout) |
| 65 | port = parser.parse_port(expected_terminal) |
| 66 | |
| 67 | def debug_log(): |
| 68 | for line in log_stdout: |
| 69 | line = line.strip().decode(errors='ignore') |
| 70 | logger.debug(f'FVP output: {line}') |
| 71 | log_thread = threading.Thread(None, debug_log) |
| 72 | log_thread.start() |
| 73 | |
| 74 | telnet = fvp.create_telnet(port) |
| 75 | telnet.wait() |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 76 | logger.debug(f"Telnet quit, cancelling tasks") |
| 77 | else: |
Patrick Williams | 7784c42 | 2022-11-17 07:29:11 -0600 | [diff] [blame] | 78 | for line in fvp.stdout: |
| 79 | print(line.strip().decode(errors='ignore')) |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 80 | |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 81 | finally: |
Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 82 | return fvp.stop() |
Patrick Williams | 7784c42 | 2022-11-17 07:29:11 -0600 | [diff] [blame] | 83 | |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 84 | |
| 85 | def runfvp(cli_args): |
| 86 | args, extra_args = parse_args(cli_args) |
| 87 | if args.config and pathlib.Path(args.config).exists(): |
| 88 | config_file = args.config |
| 89 | else: |
| 90 | config_file = conffile.find(args.config) |
Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 91 | return start_fvp(args, config_file, extra_args) |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 92 | |
Brad Bishop | bec4ebc | 2022-08-03 09:55:16 -0400 | [diff] [blame] | 93 | |
| 94 | if __name__ == "__main__": |
| 95 | try: |
| 96 | # Set the process group so that it's possible to kill runfvp and |
| 97 | # everything it spawns easily. |
| 98 | # Ignore permission errors happening when spawned from an other process |
| 99 | # for example run from except |
| 100 | try: |
| 101 | os.setpgid(0, 0) |
| 102 | except PermissionError: |
| 103 | pass |
| 104 | if sys.stdin.isatty(): |
| 105 | signal.signal(signal.SIGTTOU, signal.SIG_IGN) |
| 106 | os.tcsetpgrp(sys.stdin.fileno(), os.getpgrp()) |
| 107 | sys.exit(runfvp(sys.argv[1:])) |
| 108 | except KeyboardInterrupt: |
| 109 | pass |