runfvp 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. #! /usr/bin/env python3
  2. import itertools
  3. import os
  4. import pathlib
  5. import signal
  6. import sys
  7. import threading
  8. import logging
  9. logger = logging.getLogger("RunFVP")
  10. # Add meta-arm/lib/ to path
  11. libdir = pathlib.Path(__file__).parents[1] / "meta-arm" / "lib"
  12. sys.path.insert(0, str(libdir))
  13. from fvp import conffile, terminal, runner
  14. def parse_args(arguments):
  15. import argparse
  16. terminals = terminal.terminals
  17. parser = argparse.ArgumentParser(description="Run images in a FVP")
  18. parser.add_argument("config", nargs="?", help="Machine name or path to .fvpconf file")
  19. group = parser.add_mutually_exclusive_group()
  20. group.add_argument("-t", "--terminals", choices=terminals.all_terminals(), default=terminals.preferred_terminal(), help="Automatically start terminals (default: %(default)s)")
  21. group.add_argument("-c", "--console", action="store_true", help="Attach the first uart to stdin/stdout")
  22. parser.add_argument("--verbose", action="store_true", help="Output verbose logging")
  23. parser.usage = f"{parser.format_usage().strip()} -- [ arguments passed to FVP ]"
  24. # TODO option for telnet vs netcat
  25. # If the arguments contains -- then everything after it should be passed to the FVP binary directly.
  26. if "--" in arguments:
  27. i = arguments.index("--")
  28. fvp_args = arguments[i+1:]
  29. arguments = arguments[:i]
  30. else:
  31. fvp_args = []
  32. args = parser.parse_args(args=arguments)
  33. logging.basicConfig(level=args.verbose and logging.DEBUG or logging.WARNING,
  34. format='\033[G%(levelname)s: %(message)s')
  35. # If we're hooking up the console, don't start any terminals
  36. if args.console:
  37. args.terminals = "none"
  38. logger.debug(f"Parsed arguments: {vars(args)}")
  39. logger.debug(f"FVP arguments: {fvp_args}")
  40. return args, fvp_args
  41. def start_fvp(args, fvpconf, extra_args):
  42. fvp = runner.FVPRunner(logger)
  43. try:
  44. fvp.start(fvpconf, extra_args, args.terminals)
  45. if args.console:
  46. config = fvp.getConfig()
  47. expected_terminal = config["consoles"].get("default")
  48. if expected_terminal is None:
  49. logger.error("--console used but FVP_CONSOLE not set in machine configuration")
  50. return 1
  51. port_stdout, log_stdout = itertools.tee(fvp.stdout, 2)
  52. parser = runner.ConsolePortParser(port_stdout)
  53. port = parser.parse_port(expected_terminal)
  54. def debug_log():
  55. for line in log_stdout:
  56. line = line.strip().decode(errors='ignore')
  57. logger.debug(f'FVP output: {line}')
  58. log_thread = threading.Thread(None, debug_log)
  59. log_thread.start()
  60. telnet = fvp.create_telnet(port)
  61. telnet.wait()
  62. logger.debug(f"Telnet quit, cancelling tasks")
  63. else:
  64. for line in fvp.stdout:
  65. print(line.strip().decode(errors='ignore'))
  66. finally:
  67. return fvp.stop()
  68. def runfvp(cli_args):
  69. args, extra_args = parse_args(cli_args)
  70. if args.config and pathlib.Path(args.config).exists():
  71. config_file = args.config
  72. else:
  73. config_file = conffile.find(args.config)
  74. return start_fvp(args, config_file, extra_args)
  75. if __name__ == "__main__":
  76. try:
  77. # Set the process group so that it's possible to kill runfvp and
  78. # everything it spawns easily.
  79. # Ignore permission errors happening when spawned from an other process
  80. # for example run from except
  81. try:
  82. os.setpgid(0, 0)
  83. except PermissionError:
  84. pass
  85. if sys.stdin.isatty():
  86. signal.signal(signal.SIGTTOU, signal.SIG_IGN)
  87. os.tcsetpgrp(sys.stdin.fileno(), os.getpgrp())
  88. sys.exit(runfvp(sys.argv[1:]))
  89. except KeyboardInterrupt:
  90. pass