1bec4ebc2SBrad Bishop#! /usr/bin/env python3 2bec4ebc2SBrad Bishop 37784c429SPatrick Williamsimport itertools 4bec4ebc2SBrad Bishopimport os 5bec4ebc2SBrad Bishopimport pathlib 6bec4ebc2SBrad Bishopimport signal 7bec4ebc2SBrad Bishopimport sys 87784c429SPatrick Williamsimport threading 9bec4ebc2SBrad Bishop 10bec4ebc2SBrad Bishopimport logging 11bec4ebc2SBrad Bishoplogger = logging.getLogger("RunFVP") 12bec4ebc2SBrad Bishop 13bec4ebc2SBrad Bishop# Add meta-arm/lib/ to path 14bec4ebc2SBrad Bishoplibdir = pathlib.Path(__file__).parents[1] / "meta-arm" / "lib" 15bec4ebc2SBrad Bishopsys.path.insert(0, str(libdir)) 16bec4ebc2SBrad Bishop 1723e02799SAndrew Geisslerfrom fvp import conffile, terminal, runner 18bec4ebc2SBrad Bishop 19bec4ebc2SBrad Bishopdef parse_args(arguments): 20bec4ebc2SBrad Bishop import argparse 21bec4ebc2SBrad Bishop terminals = terminal.terminals 22bec4ebc2SBrad Bishop 23bec4ebc2SBrad Bishop parser = argparse.ArgumentParser(description="Run images in a FVP") 24bec4ebc2SBrad Bishop parser.add_argument("config", nargs="?", help="Machine name or path to .fvpconf file") 25bec4ebc2SBrad Bishop group = parser.add_mutually_exclusive_group() 26bec4ebc2SBrad Bishop group.add_argument("-t", "--terminals", choices=terminals.all_terminals(), default=terminals.preferred_terminal(), help="Automatically start terminals (default: %(default)s)") 27bec4ebc2SBrad Bishop group.add_argument("-c", "--console", action="store_true", help="Attach the first uart to stdin/stdout") 28bec4ebc2SBrad Bishop parser.add_argument("--verbose", action="store_true", help="Output verbose logging") 29bec4ebc2SBrad Bishop parser.usage = f"{parser.format_usage().strip()} -- [ arguments passed to FVP ]" 30bec4ebc2SBrad Bishop # TODO option for telnet vs netcat 31bec4ebc2SBrad Bishop 32bec4ebc2SBrad Bishop # If the arguments contains -- then everything after it should be passed to the FVP binary directly. 33bec4ebc2SBrad Bishop if "--" in arguments: 34bec4ebc2SBrad Bishop i = arguments.index("--") 35bec4ebc2SBrad Bishop fvp_args = arguments[i+1:] 36bec4ebc2SBrad Bishop arguments = arguments[:i] 37bec4ebc2SBrad Bishop else: 38bec4ebc2SBrad Bishop fvp_args = [] 39bec4ebc2SBrad Bishop 40bec4ebc2SBrad Bishop args = parser.parse_args(args=arguments) 417784c429SPatrick Williams logging.basicConfig(level=args.verbose and logging.DEBUG or logging.WARNING, 427784c429SPatrick Williams format='\033[G%(levelname)s: %(message)s') 43bec4ebc2SBrad Bishop 44bec4ebc2SBrad Bishop # If we're hooking up the console, don't start any terminals 45bec4ebc2SBrad Bishop if args.console: 46bec4ebc2SBrad Bishop args.terminals = "none" 47bec4ebc2SBrad Bishop 48bec4ebc2SBrad Bishop logger.debug(f"Parsed arguments: {vars(args)}") 49bec4ebc2SBrad Bishop logger.debug(f"FVP arguments: {fvp_args}") 50bec4ebc2SBrad Bishop return args, fvp_args 51bec4ebc2SBrad Bishop 52e760df85SPatrick Williamsdef start_fvp(args, fvpconf, extra_args): 53bec4ebc2SBrad Bishop fvp = runner.FVPRunner(logger) 54bec4ebc2SBrad Bishop try: 55e760df85SPatrick Williams fvp.start(fvpconf, extra_args, args.terminals) 56bec4ebc2SBrad Bishop 57bec4ebc2SBrad Bishop if args.console: 58e760df85SPatrick Williams config = fvp.getConfig() 59e760df85SPatrick Williams expected_terminal = config["consoles"].get("default") 60e760df85SPatrick Williams if expected_terminal is None: 61bec4ebc2SBrad Bishop logger.error("--console used but FVP_CONSOLE not set in machine configuration") 62bec4ebc2SBrad Bishop return 1 637784c429SPatrick Williams port_stdout, log_stdout = itertools.tee(fvp.stdout, 2) 647784c429SPatrick Williams parser = runner.ConsolePortParser(port_stdout) 657784c429SPatrick Williams port = parser.parse_port(expected_terminal) 667784c429SPatrick Williams 677784c429SPatrick Williams def debug_log(): 687784c429SPatrick Williams for line in log_stdout: 697784c429SPatrick Williams line = line.strip().decode(errors='ignore') 707784c429SPatrick Williams logger.debug(f'FVP output: {line}') 717784c429SPatrick Williams log_thread = threading.Thread(None, debug_log) 727784c429SPatrick Williams log_thread.start() 737784c429SPatrick Williams 747784c429SPatrick Williams telnet = fvp.create_telnet(port) 757784c429SPatrick Williams telnet.wait() 76bec4ebc2SBrad Bishop logger.debug(f"Telnet quit, cancelling tasks") 77bec4ebc2SBrad Bishop else: 787784c429SPatrick Williams for line in fvp.stdout: 797784c429SPatrick Williams print(line.strip().decode(errors='ignore')) 80db4c27eeSPatrick Williams 81bec4ebc2SBrad Bishop finally: 82*ac13d5f3SPatrick Williams return fvp.stop() 837784c429SPatrick Williams 84bec4ebc2SBrad Bishop 85bec4ebc2SBrad Bishopdef runfvp(cli_args): 86bec4ebc2SBrad Bishop args, extra_args = parse_args(cli_args) 87bec4ebc2SBrad Bishop if args.config and pathlib.Path(args.config).exists(): 88bec4ebc2SBrad Bishop config_file = args.config 89bec4ebc2SBrad Bishop else: 90bec4ebc2SBrad Bishop config_file = conffile.find(args.config) 91*ac13d5f3SPatrick Williams return start_fvp(args, config_file, extra_args) 92bec4ebc2SBrad Bishop 93bec4ebc2SBrad Bishop 94bec4ebc2SBrad Bishopif __name__ == "__main__": 95bec4ebc2SBrad Bishop try: 96bec4ebc2SBrad Bishop # Set the process group so that it's possible to kill runfvp and 97bec4ebc2SBrad Bishop # everything it spawns easily. 98bec4ebc2SBrad Bishop # Ignore permission errors happening when spawned from an other process 99bec4ebc2SBrad Bishop # for example run from except 100bec4ebc2SBrad Bishop try: 101bec4ebc2SBrad Bishop os.setpgid(0, 0) 102bec4ebc2SBrad Bishop except PermissionError: 103bec4ebc2SBrad Bishop pass 104bec4ebc2SBrad Bishop if sys.stdin.isatty(): 105bec4ebc2SBrad Bishop signal.signal(signal.SIGTTOU, signal.SIG_IGN) 106bec4ebc2SBrad Bishop os.tcsetpgrp(sys.stdin.fileno(), os.getpgrp()) 107bec4ebc2SBrad Bishop sys.exit(runfvp(sys.argv[1:])) 108bec4ebc2SBrad Bishop except KeyboardInterrupt: 109bec4ebc2SBrad Bishop pass 110