blob: 9fb77d38d730556853830c133162659b7ab1b0b6 [file] [log] [blame]
#! /usr/bin/env python3
import asyncio
import os
import pathlib
import signal
import sys
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 terminal, runner, conffile
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)
# 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
async def start_fvp(args, config, extra_args):
fvp = runner.FVPRunner(logger)
try:
await fvp.start(config, extra_args, args.terminals)
if args.console:
fvp.add_line_callback(lambda line: logger.debug(f"FVP output: {line}"))
expected_terminal = config["consoles"]["default"]
if not expected_terminal:
logger.error("--console used but FVP_CONSOLE not set in machine configuration")
return 1
telnet = await fvp.create_telnet(expected_terminal)
await telnet.wait()
logger.debug(f"Telnet quit, cancelling tasks")
else:
fvp.add_line_callback(lambda line: print(line))
await fvp.run()
finally:
await 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)
logger.debug(f"Loading {config_file}")
config = conffile.load(config_file)
try:
# When we can assume Py3.7+, this can simply be asyncio.run()
loop = asyncio.get_event_loop()
return loop.run_until_complete(start_fvp(args, config, extra_args))
except asyncio.CancelledError:
# This means telnet exited, which isn't an error
return 0
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