blob: 939352b539af79a74e194ea84c8a4074b7c36e4f [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
17from fvp import terminal, runner, conffile
18
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 Williams7784c422022-11-17 07:29:11 -060052def start_fvp(args, config, extra_args):
Brad Bishopbec4ebc2022-08-03 09:55:16 -040053 fvp = runner.FVPRunner(logger)
54 try:
Patrick Williams7784c422022-11-17 07:29:11 -060055 fvp.start(config, extra_args, args.terminals)
Brad Bishopbec4ebc2022-08-03 09:55:16 -040056
57 if args.console:
Brad Bishopbec4ebc2022-08-03 09:55:16 -040058 expected_terminal = config["consoles"]["default"]
59 if not expected_terminal:
60 logger.error("--console used but FVP_CONSOLE not set in machine configuration")
61 return 1
Patrick Williams7784c422022-11-17 07:29:11 -060062 port_stdout, log_stdout = itertools.tee(fvp.stdout, 2)
63 parser = runner.ConsolePortParser(port_stdout)
64 port = parser.parse_port(expected_terminal)
65
66 def debug_log():
67 for line in log_stdout:
68 line = line.strip().decode(errors='ignore')
69 logger.debug(f'FVP output: {line}')
70 log_thread = threading.Thread(None, debug_log)
71 log_thread.start()
72
73 telnet = fvp.create_telnet(port)
74 telnet.wait()
Brad Bishopbec4ebc2022-08-03 09:55:16 -040075 logger.debug(f"Telnet quit, cancelling tasks")
76 else:
Patrick Williams7784c422022-11-17 07:29:11 -060077 for line in fvp.stdout:
78 print(line.strip().decode(errors='ignore'))
Brad Bishopbec4ebc2022-08-03 09:55:16 -040079
Brad Bishopbec4ebc2022-08-03 09:55:16 -040080 finally:
Patrick Williams7784c422022-11-17 07:29:11 -060081 fvp.stop()
82
Brad Bishopbec4ebc2022-08-03 09:55:16 -040083
84def runfvp(cli_args):
85 args, extra_args = parse_args(cli_args)
86 if args.config and pathlib.Path(args.config).exists():
87 config_file = args.config
88 else:
89 config_file = conffile.find(args.config)
90 logger.debug(f"Loading {config_file}")
91 config = conffile.load(config_file)
Patrick Williams7784c422022-11-17 07:29:11 -060092 start_fvp(args, config, extra_args)
Brad Bishopbec4ebc2022-08-03 09:55:16 -040093
Brad Bishopbec4ebc2022-08-03 09:55:16 -040094
95if __name__ == "__main__":
96 try:
97 # Set the process group so that it's possible to kill runfvp and
98 # everything it spawns easily.
99 # Ignore permission errors happening when spawned from an other process
100 # for example run from except
101 try:
102 os.setpgid(0, 0)
103 except PermissionError:
104 pass
105 if sys.stdin.isatty():
106 signal.signal(signal.SIGTTOU, signal.SIG_IGN)
107 os.tcsetpgrp(sys.stdin.fileno(), os.getpgrp())
108 sys.exit(runfvp(sys.argv[1:]))
109 except KeyboardInterrupt:
110 pass