#! /usr/bin/env python3 import itertools import os import pathlib import signal import sys import threading import logging logger = logging.getLogger("RunFVP") # Add meta-arm/lib/ to path libdir = pathlib.Path(__file__).parents[1] / "meta-arm" / "lib" sys.path.insert(0, str(libdir)) from fvp import conffile, terminal, runner def parse_args(arguments): import argparse terminals = terminal.terminals parser = argparse.ArgumentParser(description="Run images in a FVP") parser.add_argument("config", nargs="?", help="Machine name or path to .fvpconf file") group = parser.add_mutually_exclusive_group() group.add_argument("-t", "--terminals", choices=terminals.all_terminals(), default=terminals.preferred_terminal(), help="Automatically start terminals (default: %(default)s)") group.add_argument("-c", "--console", action="store_true", help="Attach the first uart to stdin/stdout") parser.add_argument("--verbose", action="store_true", help="Output verbose logging") parser.usage = f"{parser.format_usage().strip()} -- [ arguments passed to FVP ]" # TODO option for telnet vs netcat # If the arguments contains -- then everything after it should be passed to the FVP binary directly. if "--" in arguments: i = arguments.index("--") fvp_args = arguments[i+1:] arguments = arguments[:i] else: fvp_args = [] args = parser.parse_args(args=arguments) logging.basicConfig(level=args.verbose and logging.DEBUG or logging.WARNING, format='\033[G%(levelname)s: %(message)s') # If we're hooking up the console, don't start any terminals if args.console: args.terminals = "none" logger.debug(f"Parsed arguments: {vars(args)}") logger.debug(f"FVP arguments: {fvp_args}") return args, fvp_args def start_fvp(args, fvpconf, extra_args): fvp = runner.FVPRunner(logger) try: fvp.start(fvpconf, extra_args, args.terminals) if args.console: config = fvp.getConfig() expected_terminal = config["consoles"].get("default") if expected_terminal is None: logger.error("--console used but FVP_CONSOLE not set in machine configuration") return 1 port_stdout, log_stdout = itertools.tee(fvp.stdout, 2) parser = runner.ConsolePortParser(port_stdout) port = parser.parse_port(expected_terminal) def debug_log(): for line in log_stdout: line = line.strip().decode(errors='ignore') logger.debug(f'FVP output: {line}') log_thread = threading.Thread(None, debug_log) log_thread.start() telnet = fvp.create_telnet(port) telnet.wait() logger.debug(f"Telnet quit, cancelling tasks") else: for line in fvp.stdout: print(line.strip().decode(errors='ignore')) finally: fvp.stop() def runfvp(cli_args): args, extra_args = parse_args(cli_args) if args.config and pathlib.Path(args.config).exists(): config_file = args.config else: config_file = conffile.find(args.config) start_fvp(args, config_file, extra_args) if __name__ == "__main__": try: # Set the process group so that it's possible to kill runfvp and # everything it spawns easily. # Ignore permission errors happening when spawned from an other process # for example run from except try: os.setpgid(0, 0) except PermissionError: pass if sys.stdin.isatty(): signal.signal(signal.SIGTTOU, signal.SIG_IGN) os.tcsetpgrp(sys.stdin.fileno(), os.getpgrp()) sys.exit(runfvp(sys.argv[1:])) except KeyboardInterrupt: pass