From patchwork Thu Apr 23 15:37:19 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gyorgy Szing X-Patchwork-Id: 86757 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6D599FC035A for ; Thu, 23 Apr 2026 15:37:42 +0000 (UTC) Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.561.1776958656650431964 for ; Thu, 23 Apr 2026 08:37:36 -0700 Authentication-Results: mx.groups.io; dkim=fail reason="dkim: body hash did not verify" header.i=@arm.com header.s=foss header.b=jtK1UYwz; spf=pass (domain: arm.com, ip: 217.140.110.172, mailfrom: gyorgy.szing@arm.com) Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 3C1601A9A; Thu, 23 Apr 2026 08:37:30 -0700 (PDT) Received: from gyoszi01-yocto.budapest.arm.com (ubul2.budapest.arm.com [10.42.55.21]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 21A0A3F7B4; Thu, 23 Apr 2026 08:37:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1776958655; bh=XKYHuBRjo1yjGwUc2nSRHwDPS4fNvR5GNssAHycc8FU=; h=From:To:Cc:Subject:Date:From; b=jtK1UYwza9bMMQ6lwM5aINC3sCTrXjdShOrKdax4qYH+DMKW1v2JAmSQyeIRNACxx UKuSt0c8U+cM96rsEwNcsmjaMn+VDiRR4rNmfRL+XMc911FGTHPOyKCQ+2O8RtalWO eaEa28hC2wwcaMLiACVPSNZgj09U1jD3YiCGtLns= From: Gyorgy Szing To: meta-arm@lists.yoctoproject.org Cc: Gyorgy Szing Subject: [PATCH 1/3] scripts/runfvp: check available terminal types Date: Thu, 23 Apr 2026 17:37:19 +0200 Message-ID: <20260423153721.1275354-1-gyorgy.szing@arm.com> X-Mailer: git-send-email 2.43.0 MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Thu, 23 Apr 2026 15:37:42 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/meta-arm/message/7020 Improve usability by detecting which terminal types are available on the system. Extend the terminal abstraction to support checking whether a terminal can be executed, and add basic validation for all terminal types. Update the documentation to reflect the new behavior. Signed-off-by: Gyorgy Szing --- documentation/runfvp.md | 5 ++ meta-arm/lib/fvp/terminal.py | 95 +++++++++++++++++++++++++++++++----- scripts/runfvp | 8 ++- 3 files changed, 96 insertions(+), 12 deletions(-) diff --git a/documentation/runfvp.md b/documentation/runfvp.md index d1331101..aed99971 100644 --- a/documentation/runfvp.md +++ b/documentation/runfvp.md @@ -28,6 +28,11 @@ Note that currently meta-arm's `scripts` directory isn't in `PATH`, so a full pa `runfvp` will automatically start terminals connected to each of the serial ports that the machine specifies. This can be controlled by using the `--terminals` option, for example `--terminals=none` will mean no terminals are started, and `--terminals=tmux` will start the terminals in [`tmux`][tmux] sessions. Alternatively, passing `--console` will connect the serial port directly to the current session, without needing to open further windows. +The tool attempts to automatically select a suitable terminal type. To see which terminal type is selected by default in your environment, run `runfvp --help`. + +`runfvp` determines availability by checking for required executables in your PATH as well as environment variables specific to each terminal type. If any of these checks fail, the corresponding terminal type is disabled. +The --help output also lists all currently available terminal types. + The default terminal can also be configured by writing a [INI-style][INI] configuration file to `~/.config/runfvp.conf`: ``` diff --git a/meta-arm/lib/fvp/terminal.py b/meta-arm/lib/fvp/terminal.py index 280fb349..c0087fa1 100644 --- a/meta-arm/lib/fvp/terminal.py +++ b/meta-arm/lib/fvp/terminal.py @@ -3,9 +3,14 @@ import collections import pathlib import os +import logging +import configparser from typing import List, Optional +logger = logging.getLogger("Terminal") + + def get_config_dir() -> pathlib.Path: value = os.environ.get("XDG_CONFIG_HOME") if value and os.path.isabs(value): @@ -13,47 +18,115 @@ def get_config_dir() -> pathlib.Path: else: return pathlib.Path.home() / ".config" + +def check_executable(*cmd) -> bool: + import subprocess + + try: + result = subprocess.run( + cmd, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL + ) + + exitcode = result.returncode + + except FileNotFoundError: + exitcode = 127 + + return exitcode == 0 + + +def tmux_is_ready(*, silent: bool = False) -> bool: + log_print = (lambda *_args, **_kwargs: None) if silent else logger.error + + if not check_executable("tmux", "-V"): + log_print("--terminal tmux requires tmux to be available and runnable, but startup failed.") + return False + + return True + + +def is_display_available(log_print, terminal_name: str) -> bool: + if "DISPLAY" not in os.environ and "WAYLAND_DISPLAY" not in os.environ: + log_print(f"--terminal {terminal_name} requires a graphical display" + " but nor DISPLAY nor WAYLAND_DISPLAY is set.") + return False + return True + + +def gterm_is_ready(*, silent: bool = False) -> bool: + log_print = (lambda *_args, **_kwargs: None) if silent else logger.error + + if not is_display_available(log_print, "gnome-terminal"): + return False + + if not check_executable("gnome-terminal", "--version"): + log_print("--terminal gnome-terminal requires gnome-terminal to be available and runnable, but startup failed.") + return False + + return True + + +def xterm_is_ready(*, silent: bool = False) -> bool: + log_print = (lambda *_args, **_kwargs: None) if silent else logger.error + + if not is_display_available(log_print, "xterm"): + return False + + if not check_executable("xterm", "-version"): + log_print("--terminal xterm requires xterm to be available and runnable, but startup failed.") + return False + + return True + + class Terminals: - Terminal = collections.namedtuple("Terminal", ["priority", "name", "command"]) + Terminal = collections.namedtuple("Terminal", ["priority", "name", "command", "is_ready"]) def __init__(self): self.terminals = [] - def add_terminal(self, priority, name, command): - self.terminals.append(Terminals.Terminal(priority, name, command)) + def always_ready(self, *, silent: bool = False) -> bool: + return True + + def add_terminal(self, priority, name, command, is_ready=None): + if is_ready is None: + is_ready = self.always_ready + self.terminals.append(Terminals.Terminal(priority, name, command, is_ready)) # Keep this list sorted by priority self.terminals.sort(reverse=True, key=lambda t: t.priority) self.name_map = {t.name: t for t in self.terminals} def configured_terminal(self) -> Optional[str]: - import configparser - config = configparser.ConfigParser() config.read(get_config_dir() / "runfvp.conf") return config.get("RunFVP", "Terminal", fallback=None) def preferred_terminal(self) -> str: - import shlex - preferred = self.configured_terminal() if preferred: return preferred for t in self.terminals: - if t.command and shutil.which(shlex.split(t.command)[0]): + if t.command and t.is_ready(silent=True): return t.name return self.terminals[-1].name def all_terminals(self) -> List[str]: return self.name_map.keys() + def available_terminals(self) -> List[str]: + return [t for t in self.name_map if self.name_map[t].is_ready(silent=True)] + def __getitem__(self, name: str): return self.name_map[name] + terminals = Terminals() # TODO: option to switch between telnet and netcat connect_command = "telnet localhost %port" -terminals.add_terminal(2, "tmux", f"tmux new-window -n \"{{name}}\" \"{connect_command}\"") -terminals.add_terminal(2, "gnome-terminal", f"gnome-terminal --window --title \"{{name}} - %title\" --command \"{connect_command}\"") -terminals.add_terminal(1, "xterm", f"xterm -title \"{{name}} - %title\" -e {connect_command}") +terminals.add_terminal(2, "tmux", f'tmux new-window -n "{{name}}" "{connect_command}"', tmux_is_ready) +terminals.add_terminal(2, "gnome-terminal", f'gnome-terminal --window --title "{{name}} - %title" --command "{connect_command}"', gterm_is_ready) +terminals.add_terminal(1, "xterm", f'xterm -title "{{name}} - %title" -e {connect_command}', xterm_is_ready) terminals.add_terminal(0, "none", None) diff --git a/scripts/runfvp b/scripts/runfvp index ceae18ae..8e6fe655 100755 --- a/scripts/runfvp +++ b/scripts/runfvp @@ -23,7 +23,8 @@ def parse_args(arguments): 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)") + available_terminals=",".join(terminals.available_terminals()) + group.add_argument("-t", "--terminals", choices=terminals.all_terminals(), default=terminals.preferred_terminal(), help=f"Automatically start terminals (default: %(default)s). Available terminals are ({available_terminals})") 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 ]" @@ -52,6 +53,11 @@ def parse_args(arguments): def start_fvp(args, fvpconf, extra_args): fvp = runner.FVPRunner(logger) try: + + if args.terminals: + if not terminal.terminals[args.terminals].is_ready(): + return 1 + fvp.start(fvpconf, extra_args, args.terminals) if args.console: From patchwork Thu Apr 23 15:37:20 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gyorgy Szing X-Patchwork-Id: 86759 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 49FC6FC035A for ; Thu, 23 Apr 2026 15:38:02 +0000 (UTC) Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.566.1776958673027355854 for ; Thu, 23 Apr 2026 08:37:53 -0700 Authentication-Results: mx.groups.io; dkim=fail reason="dkim: body hash did not verify" header.i=@arm.com header.s=foss header.b=bvBxBMbp; spf=pass (domain: arm.com, ip: 217.140.110.172, mailfrom: gyorgy.szing@arm.com) Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id E79511A9A; Thu, 23 Apr 2026 08:37:46 -0700 (PDT) Received: from gyoszi01-yocto.budapest.arm.com (ubul2.budapest.arm.com [10.42.55.21]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id CF2163F7B4; Thu, 23 Apr 2026 08:37:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1776958672; bh=NSe7PXFLzD4hbJp/nHzKJICC0+EmkFJuUpes2eQfo/Q=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=bvBxBMbpEYjTI+9QXzrGBSfXY4lOYAvvMOqV4r7heQIxMGHX4iYC1hiN/sT1mHnym SH2xAQr8Sgozc09hfsy2mqwjMx8L60dth8oW9KuWMBUb96wlSWEQR9FrT5AhL+i3ZA gM7rea0LJuQjdt17rCBCCW6D4tlund00gukRiBFM= From: Gyorgy Szing To: meta-arm@lists.yoctoproject.org Cc: Gyorgy Szing Subject: [PATCH 2/3] scripts/runfvp: add Screen support Date: Thu, 23 Apr 2026 17:37:20 +0200 Message-ID: <20260423153721.1275354-2-gyorgy.szing@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260423153721.1275354-1-gyorgy.szing@arm.com> References: <20260423153721.1275354-1-gyorgy.szing@arm.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Thu, 23 Apr 2026 15:38:02 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/meta-arm/message/7021 Add support for Screen the GNU terminal multiplexer. The -t/--terminal option now accepts "screen" as a terminal type. When selected, the tool must be run from within an existing Screen session, and each FVP terminal is opened in a new Screen window. Signed-off-by: Gyorgy Szing --- documentation/runfvp.md | 3 +++ meta-arm/lib/fvp/terminal.py | 17 ++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/documentation/runfvp.md b/documentation/runfvp.md index aed99971..8c9b5f40 100644 --- a/documentation/runfvp.md +++ b/documentation/runfvp.md @@ -33,6 +33,8 @@ The tool attempts to automatically select a suitable terminal type. To see which `runfvp` determines availability by checking for required executables in your PATH as well as environment variables specific to each terminal type. If any of these checks fail, the corresponding terminal type is disabled. The --help output also lists all currently available terminal types. +When using `-terminals=screen`, `runfvp` must be launched from within an existing [`screen`][screen] session. Normally, screen sets the `STY` environment variable to reference the current session. However, if the session is renamed or if `kas` is started from within the screen session, this value may become invalid or be lost. In such cases, `STY` must be set manually. Use `screen -ls` to view the list of currently attached sessions. + The default terminal can also be configured by writing a [INI-style][INI] configuration file to `~/.config/runfvp.conf`: ``` @@ -144,3 +146,4 @@ FVP_ENV_PASSTHROUGH = "ARMLMD_LICENSE_FILE FM_TRACE_PLUGINS" [FVP]: https://developer.arm.com/tools-and-software/simulation-models/fixed-virtual-platforms [tmux]: https://tmux.github.io/ [INI]: https://docs.python.org/3/library/configparser.html +[screen]: https://www.gnu.org/software/screen/ diff --git a/meta-arm/lib/fvp/terminal.py b/meta-arm/lib/fvp/terminal.py index c0087fa1..fb6050d6 100644 --- a/meta-arm/lib/fvp/terminal.py +++ b/meta-arm/lib/fvp/terminal.py @@ -1,4 +1,3 @@ -import shutil import collections import pathlib import os @@ -37,6 +36,20 @@ def check_executable(*cmd) -> bool: return exitcode == 0 +def screen_is_ready(*, silent: bool = False) -> bool: + log_print = (lambda *_args, **_kwargs: None) if silent else logger.error + + if not check_executable("screen", "--version"): + log_print("--terminal screen requires screen to be available and runnable, but startup failed.") + return False + + if not os.environ.get("STY"): + log_print("--terminal screen requires runfvp to be started from a screen session.\n\tEnsure $STY is set in the environment.") + return False + + return True + + def tmux_is_ready(*, silent: bool = False) -> bool: log_print = (lambda *_args, **_kwargs: None) if silent else logger.error @@ -126,6 +139,8 @@ class Terminals: terminals = Terminals() # TODO: option to switch between telnet and netcat connect_command = "telnet localhost %port" +sty = os.environ.get("STY") +terminals.add_terminal(2, "screen", f'screen -S "{sty}" -X screen -t "{{name}} - %title" -L {connect_command}', screen_is_ready) terminals.add_terminal(2, "tmux", f'tmux new-window -n "{{name}}" "{connect_command}"', tmux_is_ready) terminals.add_terminal(2, "gnome-terminal", f'gnome-terminal --window --title "{{name}} - %title" --command "{connect_command}"', gterm_is_ready) terminals.add_terminal(1, "xterm", f'xterm -title "{{name}} - %title" -e {connect_command}', xterm_is_ready) From patchwork Thu Apr 23 15:37:21 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gyorgy Szing X-Patchwork-Id: 86758 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 54E61FC035D for ; Thu, 23 Apr 2026 15:38:02 +0000 (UTC) Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.570.1776958676668260694 for ; Thu, 23 Apr 2026 08:37:56 -0700 Authentication-Results: mx.groups.io; dkim=fail reason="dkim: body hash did not verify" header.i=@arm.com header.s=foss header.b=LNeu3n9u; spf=pass (domain: arm.com, ip: 217.140.110.172, mailfrom: gyorgy.szing@arm.com) Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 9C0631A9A; Thu, 23 Apr 2026 08:37:50 -0700 (PDT) Received: from gyoszi01-yocto.budapest.arm.com (ubul2.budapest.arm.com [10.42.55.21]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 7C5A63F7B4; Thu, 23 Apr 2026 08:37:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1776958676; bh=0SZXE+lNbElHoC4V813fZyTHPk8PpMux/6im5HT4Nfs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=LNeu3n9uJh+Ks0VAWYVqCs5zK6KJlNtVwW4zLPzaKi3nWAUXcz6jaw0xRQmsO/o0o 56Tr6LD6rc7P8XEKntXapyJyhilwGMUqcLotdTvVyMBSc7XJw20WTAd/k8Ek4ENXP9 vNaOzI94+v3diQ7memlxL9TQBf4vEyjjg+cTa37Q= From: Gyorgy Szing To: meta-arm@lists.yoctoproject.org Cc: Gyorgy Szing Subject: [PATCH 3/3] scripts/runfvp: fix exception handling Date: Thu, 23 Apr 2026 17:37:21 +0200 Message-ID: <20260423153721.1275354-3-gyorgy.szing@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260423153721.1275354-1-gyorgy.szing@arm.com> References: <20260423153721.1275354-1-gyorgy.szing@arm.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Thu, 23 Apr 2026 15:38:02 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/meta-arm/message/7022 Returning from the finally block at the end of the start_fvp() function is discarding exceptions. Since start_fvp() is near to the top off the call tree, this hides uncaught exceptions thrown by most of the code, which makes detecting and debugging issues hard. This is resolved by removing the return statement from the finally block, allowing exceptions to propagate normally. Signed-off-by: Gyorgy Szing --- scripts/runfvp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/runfvp b/scripts/runfvp index 8e6fe655..10a8ad1c 100755 --- a/scripts/runfvp +++ b/scripts/runfvp @@ -86,8 +86,9 @@ def start_fvp(args, fvpconf, extra_args): except FileNotFoundError as e: logger.error(f"FVP executable not found ({e})") finally: - return fvp.stop() + rv=fvp.stop() + return rv def runfvp(cli_args): args, extra_args = parse_args(cli_args)