blob: c2e536c88fe7c95d327ae0dea347065f2b9bd221 [file] [log] [blame]
Brad Bishopbec4ebc2022-08-03 09:55:16 -04001#! /usr/bin/env python3
2
Patrick Williams7784c422022-11-17 07:29:11 -06003import itertools
Brad Bishopbec4ebc2022-08-03 09:55:16 -04004import os
5import pathlib
6import signal
7import sys
Patrick Williams7784c422022-11-17 07:29:11 -06008import threading
Brad Bishopbec4ebc2022-08-03 09:55:16 -04009
10import logging
11logger = logging.getLogger("RunFVP")
12
13# Add meta-arm/lib/ to path
14libdir = pathlib.Path(__file__).parents[1] / "meta-arm" / "lib"
15sys.path.insert(0, str(libdir))
16
Patrick Williamse760df82023-05-26 11:10:49 -050017from fvp import terminal, runner
Brad Bishopbec4ebc2022-08-03 09:55:16 -040018
19def 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 Williams7784c422022-11-17 07:29:11 -060041 logging.basicConfig(level=args.verbose and logging.DEBUG or logging.WARNING,
42 format='\033[G%(levelname)s: %(message)s')
Brad Bishopbec4ebc2022-08-03 09:55:16 -040043
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 Williamse760df82023-05-26 11:10:49 -050052def start_fvp(args, fvpconf, extra_args):
Brad Bishopbec4ebc2022-08-03 09:55:16 -040053 fvp = runner.FVPRunner(logger)
54 try:
Patrick Williamse760df82023-05-26 11:10:49 -050055 fvp.start(fvpconf, extra_args, args.terminals)
Brad Bishopbec4ebc2022-08-03 09:55:16 -040056
57 if args.console:
Patrick Williamse760df82023-05-26 11:10:49 -050058 config = fvp.getConfig()
59 expected_terminal = config["consoles"].get("default")
60 if expected_terminal is None:
Brad Bishopbec4ebc2022-08-03 09:55:16 -040061 logger.error("--console used but FVP_CONSOLE not set in machine configuration")
62 return 1
Patrick Williams7784c422022-11-17 07:29:11 -060063 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 Bishopbec4ebc2022-08-03 09:55:16 -040076 logger.debug(f"Telnet quit, cancelling tasks")
77 else:
Patrick Williams7784c422022-11-17 07:29:11 -060078 for line in fvp.stdout:
79 print(line.strip().decode(errors='ignore'))
Brad Bishopbec4ebc2022-08-03 09:55:16 -040080
Brad Bishopbec4ebc2022-08-03 09:55:16 -040081 finally:
Patrick Williams7784c422022-11-17 07:29:11 -060082 fvp.stop()
83
Brad Bishopbec4ebc2022-08-03 09:55:16 -040084
85def 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 Williamse760df82023-05-26 11:10:49 -050091 start_fvp(args, config_file, extra_args)
Brad Bishopbec4ebc2022-08-03 09:55:16 -040092
Brad Bishopbec4ebc2022-08-03 09:55:16 -040093
94if __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