* [PATCH v2 1/9] scripts: duplicate conftest.py as qemu_interactive.py
@ 2026-06-26 16:19 Ahmad Fatoum
2026-06-26 16:19 ` [PATCH v2 2/9] test: move interactive QEMU launcher out of pytest Ahmad Fatoum
` (7 more replies)
0 siblings, 8 replies; 9+ messages in thread
From: Ahmad Fatoum @ 2026-06-26 16:19 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
To make the change splitting pytest --interactive into its own script
easier to follow, duplicate the existing code under the new name.
No functional change.
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
Change can be squashed into follow-up commit. This doesn't yet work,
but doesn't break anything.
v1 -> v2:
- no change
---
scripts/qemu_interactive.py | 304 ++++++++++++++++++++++++++++++++++++
1 file changed, 304 insertions(+)
create mode 100644 scripts/qemu_interactive.py
diff --git a/scripts/qemu_interactive.py b/scripts/qemu_interactive.py
new file mode 100644
index 000000000000..6f76586e010b
--- /dev/null
+++ b/scripts/qemu_interactive.py
@@ -0,0 +1,304 @@
+# SPDX-License-Identifier: GPL-2.0-only
+import pytest
+import os
+import argparse
+from labgrid.exceptions import NoDriverFoundError
+from test.py import helper
+
+
+def transition_to_barebox(request, strategy):
+ try:
+ strategy.transition('barebox')
+ except Exception as e:
+ # If a normal strategy transition fails, there's no point in
+ # continuing the test. Let's print stderr and exit.
+ capmanager = request.config.pluginmanager.getplugin("capturemanager")
+ with capmanager.global_and_fixture_disabled():
+ _, stderr = capmanager.read_global_capture()
+ pytest.exit(f"{type(e).__name__}(\"{e}\"). Standard error:\n{stderr}",
+ returncode=3)
+
+
+@pytest.fixture(scope='function')
+def barebox(request, strategy, target):
+ transition_to_barebox(request, strategy)
+ return target.get_driver('BareboxDriver')
+
+
+@pytest.fixture(scope='function')
+def shell(strategy, target):
+ strategy.transition('shell')
+ return target.get_driver('ShellDriver')
+
+
+@pytest.fixture(scope="session")
+def barebox_config(request, strategy, target):
+ transition_to_barebox(request, strategy)
+ command = target.get_driver("BareboxDriver")
+ return helper.get_config(command)
+
+
+def get_enabled_arch(config):
+ # Get the absolute path to the directory containing this script
+ base_dir = os.path.dirname(os.path.abspath(__file__))
+
+ # Path to the 'arch' directory relative to this script
+ arch_dir = os.path.join(base_dir, "arch")
+
+ if not os.path.isdir(arch_dir):
+ return None
+
+ # Optional mapping from directory names to config key suffixes
+ arch_map = {"powerpc": "ppc"}
+
+ for entry in os.listdir(arch_dir):
+ path = os.path.join(arch_dir, entry)
+ if os.path.isdir(path):
+ key_suffix = arch_map.get(entry, entry).upper()
+ config_key = f"CONFIG_{key_suffix}"
+ if config.get(config_key):
+ return entry
+
+ return None
+
+
+def guess_lg_env():
+ config_file = helper.open_config_file(os.environ['LG_BUILDDIR'] + "/.config")
+ config = helper.parse_config(config_file)
+ if not config or not config.get('CONFIG_NAME'):
+ return None
+ arch = get_enabled_arch(config)
+ if not arch:
+ return None
+ filename = os.path.join("test", arch, f"{config['CONFIG_NAME']}.yaml")
+ if os.path.exists(filename):
+ return filename
+ return None
+
+
+lg_env = lg_builddir = None
+
+
+def pytest_configure(config):
+ if 'LG_BUILDDIR' not in os.environ:
+ if 'KBUILD_OUTPUT' in os.environ:
+ os.environ['LG_BUILDDIR'] = os.environ['KBUILD_OUTPUT']
+ elif os.path.isdir('build'):
+ os.environ['LG_BUILDDIR'] = os.path.realpath('build')
+ else:
+ os.environ['LG_BUILDDIR'] = os.getcwd()
+
+ if os.environ['LG_BUILDDIR'] is not None:
+ os.environ['LG_BUILDDIR'] = os.path.realpath(os.environ['LG_BUILDDIR'])
+
+ global lg_env
+ global lg_builddir
+
+ lg_builddir = os.environ['LG_BUILDDIR']
+ lg_env = config.option.lg_env
+ if lg_env is None:
+ lg_env = os.environ.get('LG_ENV')
+ if lg_env is None:
+ if lg_env := guess_lg_env():
+ os.environ['LG_ENV'] = lg_env
+
+
+def pytest_report_header(config):
+ report = []
+ if lg_builddir is not None:
+ report += [f"Build diectory: {lg_builddir}"]
+ if lg_env is not None:
+ report += [f"Labgrid Environment: {lg_env}"]
+ return "\n".join(report)
+
+
+def pytest_addoption(parser):
+ def assignment(arg):
+ return arg.split('=', 1)
+
+ parser.addoption('--interactive', action='store_const', const='qemu_interactive',
+ dest='lg_initial_state',
+ help=('(for debugging) skip tests and just start Qemu interactively'))
+ parser.addoption('--dry-run', action='store_const', const='qemu_dry_run',
+ dest='lg_initial_state',
+ help=('(for debugging) skip tests and just print Qemu command line'))
+ parser.addoption('--dump-dtb', action='store_const', const='qemu_dump_dtb',
+ dest='lg_initial_state',
+ help=('(for debugging) skip tests and just dump the Qemu device tree'))
+ parser.addoption('--graphic', '--graphics', action='store_true', dest='qemu_graphics',
+ help=('enable QEMU graphics output'))
+ parser.addoption('--rng', action='count', dest='qemu_rng',
+ help=('instantiate Virt I/O random number generator'))
+ parser.addoption('--console', action='count', dest='qemu_console', default=0,
+ help=('Pass an extra console (Virt I/O or ns16550_pci) to emulated barebox'))
+ parser.addoption('--fs', action='append', dest='qemu_fs',
+ default=[], metavar="[tag=]DIR", type=assignment,
+ help=('Pass directory trees to emulated barebox. Can be specified more than once')) # noqa
+ parser.addoption('--blk', action='append', dest='qemu_block',
+ default=[], metavar="FILE",
+ help=('Pass block device to emulated barebox. Can be specified more than once')) # noqa
+ parser.addoption('--env', action='append', dest='qemu_fw_cfg', type=assignment,
+ default=[], metavar="[envpath=]content | [envpath=]@filepath",
+ help=('Pass barebox environment files to barebox. Can be specified more than once')) # noqa
+ parser.addoption('--qemu', dest='qemu_arg', nargs=argparse.REMAINDER, default=[],
+ help=('Pass all remaining options to QEMU as is'))
+ parser.addoption('--bootarg', action='append', dest='bootarg', default=[],
+ help=('Pass boot arguments to barebox for debugging purposes'))
+ parser.addoption('--port-forward', metavar="PORT", action='append', dest='qemu_port', default=[],
+ help=('Forward incoming TCP or UDP connections on specified PORT'))
+
+
+@pytest.fixture(scope="session")
+def strategy(request, target, pytestconfig): # noqa: max-complexity=30
+ try:
+ strategy = target.get_driver("Strategy")
+ except NoDriverFoundError as e:
+ pytest.exit(e)
+
+ try:
+ main = target.env.config.data["targets"]["main"]
+ features = main["features"]
+ except KeyError:
+ features = []
+
+ try:
+ main = target.env.config.data["targets"]["main"]
+ yaml_env = main["env"]
+ except KeyError:
+ yaml_env = {}
+
+ try:
+ main = target.env.config.data["targets"]["main"]
+ qemu_bin = main["drivers"]["QEMUDriver"]["qemu_bin"]
+ features.append("qemu")
+ except KeyError:
+ pass
+
+ virtio = None
+
+ if "virtio-mmio" in features:
+ virtio = "device"
+ strategy.append_qemu_args('-global virtio-mmio.force-legacy=false')
+ if "virtio-pci" in features:
+ virtio = "pci,disable-modern=off"
+ features.append("pci")
+
+ if virtio and pytestconfig.option.qemu_rng:
+ for i in range(pytestconfig.option.qemu_rng):
+ strategy.append_qemu_args("-device", f"virtio-rng-{virtio}")
+
+ for i in range(pytestconfig.option.qemu_console):
+ if virtio and i == 0:
+ strategy.append_qemu_args(
+ "-device", f"virtio-serial-{virtio}",
+ "-chardev", f"pty,id=virtcon{i}",
+ "-device", f"virtconsole,chardev=virtcon{i},name=console.virtcon{i}"
+ )
+ continue
+
+ # ns16550 serial driver only works with x86 so far
+ if 'pci' in features:
+ strategy.append_qemu_args(
+ "-chardev", f"pty,id=pcicon{i}",
+ "-device", f"pci-serial,chardev=pcicon{i}"
+ )
+ else:
+ pytest.exit("barebox currently supports only a single extra virtio console\n", 1)
+
+ if "qemu" in features:
+ if not pytestconfig.option.qemu_graphics:
+ graphics = '-nographic'
+ elif qemu_bin == "qemu-system-x86_64":
+ graphics = '-device isa-vga'
+ elif 'pci' in features:
+ graphics = '-device VGA'
+ elif virtio:
+ graphics = '-vga none -device ramfb'
+ graphics += f' -device virtio-keyboard-{virtio}'
+ else:
+ pytest.exit("--graphics unsupported for target\n", 1)
+
+ if graphics is not None and \
+ pytestconfig.option.lg_initial_state != 'qemu_interactive':
+ graphics += ' -display none'
+
+ strategy.append_qemu_args(graphics)
+
+ for i, blk in enumerate(pytestconfig.option.qemu_block):
+ if virtio:
+ strategy.append_qemu_args(
+ "-drive", f"if=none,format=raw,id=hd{i},file={blk}",
+ "-device", f"virtio-blk-{virtio},drive=hd{i}"
+ )
+ else:
+ pytest.exit("--blk unsupported for target\n", 1)
+
+ envopts = {}
+
+ for i, fw_cfg in enumerate(pytestconfig.option.qemu_fw_cfg):
+ value = fw_cfg.pop()
+ envpath = fw_cfg.pop() if fw_cfg else f"data/fw_cfg{i}"
+
+ envopts[envpath] = value
+
+ for envpath, value in (yaml_env | envopts).items():
+ if virtio:
+ if isinstance(value, str) and value.startswith('@'):
+ source = f"file='{value[1:]}'"
+ else:
+ source = f"string='{value}'"
+
+ strategy.append_qemu_args(
+ '-fw_cfg', f'name=opt/org.barebox.env/{envpath},{source}'
+ )
+ else:
+ pytest.exit("env unsupported for target\n", 1)
+
+ if len(pytestconfig.option.bootarg) > 0:
+ strategy.append_qemu_bootargs(pytestconfig.option.bootarg)
+
+ for arg in pytestconfig.option.qemu_arg:
+ strategy.append_qemu_args(arg)
+
+ qemu_nic = "user,id=net0"
+
+ for port in pytestconfig.option.qemu_port:
+ qemu_nic += f",hostfwd=udp:127.0.0.2:{port}-:{port}"
+ qemu_nic += f",hostfwd=tcp:127.0.0.2:{port}-:{port}"
+
+ if "testfs" in features:
+ if not any(fs and fs[0] == "testfs" for fs in pytestconfig.option.qemu_fs):
+ testfs_path = os.path.join(os.environ["LG_BUILDDIR"], "testfs")
+ pytestconfig.option.qemu_fs.append(["testfs", testfs_path])
+ os.makedirs(testfs_path, exist_ok=True)
+ qemu_nic += f",tftp={testfs_path}"
+
+ if "qemu" in features:
+ strategy.append_qemu_args("-nic", qemu_nic)
+
+ for i, fs in enumerate(pytestconfig.option.qemu_fs):
+ if virtio:
+ path = fs.pop()
+ tag = fs.pop() if fs else f"fs{i}"
+
+ strategy.append_qemu_args(
+ "-fsdev", f"local,security_model=none,id=fs{i},path={path}",
+ "-device", f"virtio-9p-{virtio},id=fs{i},fsdev=fs{i},mount_tag={tag}"
+ )
+ else:
+ pytest.exit("--fs unsupported for target\n", 1)
+
+ state = request.config.option.lg_initial_state
+ if state is not None:
+ strategy.force(state)
+
+ return strategy
+
+
+@pytest.fixture(scope="session")
+def testfs(strategy, env):
+ if "testfs" not in env.get_target_features():
+ pytest.skip("testfs not supported on this platform")
+
+ path = os.path.join(os.environ["LG_BUILDDIR"], "testfs")
+ return path
--
2.47.3
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 2/9] test: move interactive QEMU launcher out of pytest
2026-06-26 16:19 [PATCH v2 1/9] scripts: duplicate conftest.py as qemu_interactive.py Ahmad Fatoum
@ 2026-06-26 16:19 ` Ahmad Fatoum
2026-06-26 16:19 ` [PATCH v2 3/9] usb: xhci-hcd: add XHCI over PCI driver Ahmad Fatoum
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Ahmad Fatoum @ 2026-06-26 16:19 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
pytest --interactive apparently starts two QEMUs, the interactive one in
addition to the one started directly by Labgrid. This leads to issues
when QEMU does something mutually exclusive like listening on a port or
taking a file lock.
Arguably, running an interactive QEMU with pytest as a runner was
a strange idea in the first place. Let's stop doing that and add a
dedicated qemu_interactive.py script.
The new script is imported by conftest.py to get support for custom
options like --blk or --rng. --interactive becomes an error.
To avoid cross-dependencies between test suite and the new script, the
CONFIG_NAME resolution is reimplemented without test/py/helper.py, but
apart from that it's mostly copy-paste and adding some boilerplate.
In the future[1], we can adapt this to use the new QEMUDriver.interact().
[1]: https://github.com/labgrid-project/labgrid/pull/1892
Assisted-by: Codex:gpt-5.5
Reported-by: Marc Kleine-Budde <mkl@pengutronix.de>
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
v1 -> v2:
- no change
---
Documentation/boards/emulated.rst | 10 +-
Documentation/devel/contributing.rst | 8 +-
Documentation/user/security-policies.rst | 2 +-
conftest.py | 243 ++---------
scripts/qemu_interactive.py | 534 +++++++++++++++--------
5 files changed, 375 insertions(+), 422 deletions(-)
mode change 100644 => 100755 scripts/qemu_interactive.py
diff --git a/Documentation/boards/emulated.rst b/Documentation/boards/emulated.rst
index 0d6dd85dc759..bc3e2566eaec 100644
--- a/Documentation/boards/emulated.rst
+++ b/Documentation/boards/emulated.rst
@@ -23,15 +23,15 @@ VirtIO over PCI.
Running in QEMU
---------------
-Emulated targets can be started interactively with ``pytest --interactive``::
+Emulated targets can be started interactively with ``scripts/qemu_interactive.py``::
# Run x86 VM runnig the EFI payload from efi_defconfig
- pytest --lg-env test/x86/efi_defconfig.yaml --interactive
+ scripts/qemu_interactive.py test/x86/efi_defconfig.yaml
# Identical to above, provided the CONFIG_NAME=efi_defconfig
- pytest --interactive
+ scripts/qemu_interactive.py
-The test suite can be run by omitting the ``--interactive``.
+The test suite can be run with ``pytest`` instead.
For more information, see the :ref:`labgrid` section in the
:ref:`contributing` guide.
@@ -43,7 +43,7 @@ be used in combination with QEMU. With user-mode networking (SLIRP),
guest-to-host UDP works via NAT out of the box,
but unsolicited host-to-guest UDP requires an explicit port forward::
- pytest --lg-env test/arm/multi_v8_defconfig.yaml --interactive \
+ scripts/qemu_interactive.py test/arm/multi_v8_defconfig.yaml \
--env nv/dev.netconsole.ip=10.0.2.2 \
--env nv/dev.netconsole.port=6666 \
--env init/netconsole="ifup -a1; netconsole.active=ioe" \
diff --git a/Documentation/devel/contributing.rst b/Documentation/devel/contributing.rst
index cb3820f23c63..8b92c6f483aa 100644
--- a/Documentation/devel/contributing.rst
+++ b/Documentation/devel/contributing.rst
@@ -134,7 +134,7 @@ it directly from PyPI instead of your distro's package repositories::
Example usage::
# Run x86 VM runnig the EFI payload from efi_defconfig
- pytest --lg-env test/x86/efi_defconfig.yaml --interactive
+ scripts/qemu_interactive.py test/x86/efi_defconfig.yaml
# Run the test suite against the same
pytest --lg-env test/x86/efi_defconfig.yaml
@@ -145,10 +145,10 @@ built out-of-tree, the build directory must be pointed at by
``LG_BUILDDIR``, ``KBUILD_OUTPUT`` or a ``build`` symlink.
Additional QEMU command-line options can be added by specifying
-them after the ``--qemu`` option::
+them after ``--qemu``::
# appends -device ? to the command line. Add --dry-run to see the final result
- pytest --lg-env test/riscv/rv64i_defconfig.yaml --interactive --qemu -device '?'
+ scripts/qemu_interactive.py test/riscv/rv64i_defconfig.yaml --qemu -device '?'
Some of the QEMU options that are used more often also have explicit
support in the test runner, so paravirtualized devices can be added
@@ -158,7 +158,7 @@ more easily::
pytest --lg-env test/arm/virt@multi_v8_defconfig.yaml --blk=rootfs.ext4
# Run interactively with graphics output
- pytest --lg-env test/mips/qemu-malta_defconfig.yaml --interactive --graphics
+ scripts/qemu_interactive.py test/mips/qemu-malta_defconfig.yaml --graphics
For testing, the QEMU fw_cfg and virtfs support is particularly useful::
diff --git a/Documentation/user/security-policies.rst b/Documentation/user/security-policies.rst
index 8d515508d106..296191c4f8e5 100644
--- a/Documentation/user/security-policies.rst
+++ b/Documentation/user/security-policies.rst
@@ -97,7 +97,7 @@ policy development and evaluation. ``images/barebox-dt-2nd.img`` that results
from building it can be passed as argument to ``qemu-system-arm -M virt -kernel``.
The easiest way to do this is probably installing labgrid and running
-``pytest --interactive`` after having built the config.
+``scripts/qemu_interactive.py`` after having built the config.
Differences from Kconfig
------------------------
diff --git a/conftest.py b/conftest.py
index 6f76586e010b..4ab76b295e59 100644
--- a/conftest.py
+++ b/conftest.py
@@ -3,6 +3,7 @@ import pytest
import os
import argparse
from labgrid.exceptions import NoDriverFoundError
+from scripts import qemu_interactive
from test.py import helper
@@ -38,68 +39,24 @@ def barebox_config(request, strategy, target):
return helper.get_config(command)
-def get_enabled_arch(config):
- # Get the absolute path to the directory containing this script
- base_dir = os.path.dirname(os.path.abspath(__file__))
-
- # Path to the 'arch' directory relative to this script
- arch_dir = os.path.join(base_dir, "arch")
-
- if not os.path.isdir(arch_dir):
- return None
-
- # Optional mapping from directory names to config key suffixes
- arch_map = {"powerpc": "ppc"}
-
- for entry in os.listdir(arch_dir):
- path = os.path.join(arch_dir, entry)
- if os.path.isdir(path):
- key_suffix = arch_map.get(entry, entry).upper()
- config_key = f"CONFIG_{key_suffix}"
- if config.get(config_key):
- return entry
-
- return None
-
-
-def guess_lg_env():
- config_file = helper.open_config_file(os.environ['LG_BUILDDIR'] + "/.config")
- config = helper.parse_config(config_file)
- if not config or not config.get('CONFIG_NAME'):
- return None
- arch = get_enabled_arch(config)
- if not arch:
- return None
- filename = os.path.join("test", arch, f"{config['CONFIG_NAME']}.yaml")
- if os.path.exists(filename):
- return filename
- return None
-
-
lg_env = lg_builddir = None
def pytest_configure(config):
- if 'LG_BUILDDIR' not in os.environ:
- if 'KBUILD_OUTPUT' in os.environ:
- os.environ['LG_BUILDDIR'] = os.environ['KBUILD_OUTPUT']
- elif os.path.isdir('build'):
- os.environ['LG_BUILDDIR'] = os.path.realpath('build')
- else:
- os.environ['LG_BUILDDIR'] = os.getcwd()
-
- if os.environ['LG_BUILDDIR'] is not None:
- os.environ['LG_BUILDDIR'] = os.path.realpath(os.environ['LG_BUILDDIR'])
+ removed_mode = getattr(config.option, 'qemu_interactive_removed', None)
+ if removed_mode is not None:
+ pytest.exit(qemu_interactive.replacement_message(removed_mode),
+ returncode=2)
global lg_env
global lg_builddir
- lg_builddir = os.environ['LG_BUILDDIR']
+ lg_builddir = qemu_interactive.resolve_lg_builddir()
lg_env = config.option.lg_env
if lg_env is None:
lg_env = os.environ.get('LG_ENV')
if lg_env is None:
- if lg_env := guess_lg_env():
+ if lg_env := qemu_interactive.guess_lg_env(lg_builddir):
os.environ['LG_ENV'] = lg_env
@@ -113,39 +70,22 @@ def pytest_report_header(config):
def pytest_addoption(parser):
- def assignment(arg):
- return arg.split('=', 1)
+ parser.addoption('--interactive', action='store_const',
+ const='--interactive', dest='qemu_interactive_removed',
+ help=argparse.SUPPRESS)
+ parser.addoption('--dry-run', action='store_const',
+ const='--dry-run', dest='qemu_interactive_removed',
+ help=argparse.SUPPRESS)
+ parser.addoption('--dump-dtb', action='store_const',
+ const='--dump-dtb', dest='qemu_interactive_removed',
+ help=argparse.SUPPRESS)
- parser.addoption('--interactive', action='store_const', const='qemu_interactive',
- dest='lg_initial_state',
- help=('(for debugging) skip tests and just start Qemu interactively'))
- parser.addoption('--dry-run', action='store_const', const='qemu_dry_run',
- dest='lg_initial_state',
- help=('(for debugging) skip tests and just print Qemu command line'))
- parser.addoption('--dump-dtb', action='store_const', const='qemu_dump_dtb',
- dest='lg_initial_state',
- help=('(for debugging) skip tests and just dump the Qemu device tree'))
- parser.addoption('--graphic', '--graphics', action='store_true', dest='qemu_graphics',
- help=('enable QEMU graphics output'))
- parser.addoption('--rng', action='count', dest='qemu_rng',
- help=('instantiate Virt I/O random number generator'))
- parser.addoption('--console', action='count', dest='qemu_console', default=0,
- help=('Pass an extra console (Virt I/O or ns16550_pci) to emulated barebox'))
- parser.addoption('--fs', action='append', dest='qemu_fs',
- default=[], metavar="[tag=]DIR", type=assignment,
- help=('Pass directory trees to emulated barebox. Can be specified more than once')) # noqa
- parser.addoption('--blk', action='append', dest='qemu_block',
- default=[], metavar="FILE",
- help=('Pass block device to emulated barebox. Can be specified more than once')) # noqa
- parser.addoption('--env', action='append', dest='qemu_fw_cfg', type=assignment,
- default=[], metavar="[envpath=]content | [envpath=]@filepath",
- help=('Pass barebox environment files to barebox. Can be specified more than once')) # noqa
- parser.addoption('--qemu', dest='qemu_arg', nargs=argparse.REMAINDER, default=[],
- help=('Pass all remaining options to QEMU as is'))
- parser.addoption('--bootarg', action='append', dest='bootarg', default=[],
- help=('Pass boot arguments to barebox for debugging purposes'))
- parser.addoption('--port-forward', metavar="PORT", action='append', dest='qemu_port', default=[],
- help=('Forward incoming TCP or UDP connections on specified PORT'))
+ group = parser.getgroup('barebox-qemu', 'barebox QEMU options')
+ qemu_interactive.register_shared_options(group)
+
+
+def _pytest_exit(message, returncode=1):
+ pytest.exit(message, returncode=returncode)
@pytest.fixture(scope="session")
@@ -155,142 +95,9 @@ def strategy(request, target, pytestconfig): # noqa: max-complexity=30
except NoDriverFoundError as e:
pytest.exit(e)
- try:
- main = target.env.config.data["targets"]["main"]
- features = main["features"]
- except KeyError:
- features = []
-
- try:
- main = target.env.config.data["targets"]["main"]
- yaml_env = main["env"]
- except KeyError:
- yaml_env = {}
-
- try:
- main = target.env.config.data["targets"]["main"]
- qemu_bin = main["drivers"]["QEMUDriver"]["qemu_bin"]
- features.append("qemu")
- except KeyError:
- pass
-
- virtio = None
-
- if "virtio-mmio" in features:
- virtio = "device"
- strategy.append_qemu_args('-global virtio-mmio.force-legacy=false')
- if "virtio-pci" in features:
- virtio = "pci,disable-modern=off"
- features.append("pci")
-
- if virtio and pytestconfig.option.qemu_rng:
- for i in range(pytestconfig.option.qemu_rng):
- strategy.append_qemu_args("-device", f"virtio-rng-{virtio}")
-
- for i in range(pytestconfig.option.qemu_console):
- if virtio and i == 0:
- strategy.append_qemu_args(
- "-device", f"virtio-serial-{virtio}",
- "-chardev", f"pty,id=virtcon{i}",
- "-device", f"virtconsole,chardev=virtcon{i},name=console.virtcon{i}"
- )
- continue
-
- # ns16550 serial driver only works with x86 so far
- if 'pci' in features:
- strategy.append_qemu_args(
- "-chardev", f"pty,id=pcicon{i}",
- "-device", f"pci-serial,chardev=pcicon{i}"
- )
- else:
- pytest.exit("barebox currently supports only a single extra virtio console\n", 1)
-
- if "qemu" in features:
- if not pytestconfig.option.qemu_graphics:
- graphics = '-nographic'
- elif qemu_bin == "qemu-system-x86_64":
- graphics = '-device isa-vga'
- elif 'pci' in features:
- graphics = '-device VGA'
- elif virtio:
- graphics = '-vga none -device ramfb'
- graphics += f' -device virtio-keyboard-{virtio}'
- else:
- pytest.exit("--graphics unsupported for target\n", 1)
-
- if graphics is not None and \
- pytestconfig.option.lg_initial_state != 'qemu_interactive':
- graphics += ' -display none'
-
- strategy.append_qemu_args(graphics)
-
- for i, blk in enumerate(pytestconfig.option.qemu_block):
- if virtio:
- strategy.append_qemu_args(
- "-drive", f"if=none,format=raw,id=hd{i},file={blk}",
- "-device", f"virtio-blk-{virtio},drive=hd{i}"
- )
- else:
- pytest.exit("--blk unsupported for target\n", 1)
-
- envopts = {}
-
- for i, fw_cfg in enumerate(pytestconfig.option.qemu_fw_cfg):
- value = fw_cfg.pop()
- envpath = fw_cfg.pop() if fw_cfg else f"data/fw_cfg{i}"
-
- envopts[envpath] = value
-
- for envpath, value in (yaml_env | envopts).items():
- if virtio:
- if isinstance(value, str) and value.startswith('@'):
- source = f"file='{value[1:]}'"
- else:
- source = f"string='{value}'"
-
- strategy.append_qemu_args(
- '-fw_cfg', f'name=opt/org.barebox.env/{envpath},{source}'
- )
- else:
- pytest.exit("env unsupported for target\n", 1)
-
- if len(pytestconfig.option.bootarg) > 0:
- strategy.append_qemu_bootargs(pytestconfig.option.bootarg)
-
- for arg in pytestconfig.option.qemu_arg:
- strategy.append_qemu_args(arg)
-
- qemu_nic = "user,id=net0"
-
- for port in pytestconfig.option.qemu_port:
- qemu_nic += f",hostfwd=udp:127.0.0.2:{port}-:{port}"
- qemu_nic += f",hostfwd=tcp:127.0.0.2:{port}-:{port}"
-
- if "testfs" in features:
- if not any(fs and fs[0] == "testfs" for fs in pytestconfig.option.qemu_fs):
- testfs_path = os.path.join(os.environ["LG_BUILDDIR"], "testfs")
- pytestconfig.option.qemu_fs.append(["testfs", testfs_path])
- os.makedirs(testfs_path, exist_ok=True)
- qemu_nic += f",tftp={testfs_path}"
-
- if "qemu" in features:
- strategy.append_qemu_args("-nic", qemu_nic)
-
- for i, fs in enumerate(pytestconfig.option.qemu_fs):
- if virtio:
- path = fs.pop()
- tag = fs.pop() if fs else f"fs{i}"
-
- strategy.append_qemu_args(
- "-fsdev", f"local,security_model=none,id=fs{i},path={path}",
- "-device", f"virtio-9p-{virtio},id=fs{i},fsdev=fs{i},mount_tag={tag}"
- )
- else:
- pytest.exit("--fs unsupported for target\n", 1)
-
- state = request.config.option.lg_initial_state
- if state is not None:
- strategy.force(state)
+ qemu_interactive.apply_shared_options(
+ strategy, target, pytestconfig.option, interactive=False,
+ fail=_pytest_exit)
return strategy
diff --git a/scripts/qemu_interactive.py b/scripts/qemu_interactive.py
old mode 100644
new mode 100755
index 6f76586e010b..a8605d23b132
--- a/scripts/qemu_interactive.py
+++ b/scripts/qemu_interactive.py
@@ -1,195 +1,218 @@
+#!/usr/bin/env python3
+# /// script
+# requires-python = ">=3.9"
+# dependencies = ["labgrid"]
+# ///
# SPDX-License-Identifier: GPL-2.0-only
-import pytest
-import os
+
import argparse
-from labgrid.exceptions import NoDriverFoundError
-from test.py import helper
+import glob
+import os
+import re
+import subprocess
+import sys
-def transition_to_barebox(request, strategy):
- try:
- strategy.transition('barebox')
- except Exception as e:
- # If a normal strategy transition fails, there's no point in
- # continuing the test. Let's print stderr and exit.
- capmanager = request.config.pluginmanager.getplugin("capturemanager")
- with capmanager.global_and_fixture_disabled():
- _, stderr = capmanager.read_global_capture()
- pytest.exit(f"{type(e).__name__}(\"{e}\"). Standard error:\n{stderr}",
- returncode=3)
+MODE_INTERACTIVE = "qemu_interactive"
+MODE_DRY_RUN = "qemu_dry_run"
+MODE_DUMP_DTB = "qemu_dump_dtb"
-@pytest.fixture(scope='function')
-def barebox(request, strategy, target):
- transition_to_barebox(request, strategy)
- return target.get_driver('BareboxDriver')
+class QemuInteractiveError(Exception):
+ def __init__(self, message, returncode=1):
+ super().__init__(message)
+ self.returncode = returncode
-@pytest.fixture(scope='function')
-def shell(strategy, target):
- strategy.transition('shell')
- return target.get_driver('ShellDriver')
+def _add_option(parser, *opts, **attrs):
+ if hasattr(parser, "addoption"):
+ return parser.addoption(*opts, **attrs)
+
+ return parser.add_argument(*opts, **attrs)
-@pytest.fixture(scope="session")
-def barebox_config(request, strategy, target):
- transition_to_barebox(request, strategy)
- command = target.get_driver("BareboxDriver")
- return helper.get_config(command)
+def _assignment(arg):
+ return arg.split("=", 1)
-def get_enabled_arch(config):
- # Get the absolute path to the directory containing this script
- base_dir = os.path.dirname(os.path.abspath(__file__))
-
- # Path to the 'arch' directory relative to this script
- arch_dir = os.path.join(base_dir, "arch")
-
- if not os.path.isdir(arch_dir):
- return None
-
- # Optional mapping from directory names to config key suffixes
- arch_map = {"powerpc": "ppc"}
-
- for entry in os.listdir(arch_dir):
- path = os.path.join(arch_dir, entry)
- if os.path.isdir(path):
- key_suffix = arch_map.get(entry, entry).upper()
- config_key = f"CONFIG_{key_suffix}"
- if config.get(config_key):
- return entry
-
- return None
+def register_shared_options(parser):
+ _add_option(parser, "--graphic", "--graphics", action="store_true",
+ dest="qemu_graphics", help="enable QEMU graphics output")
+ _add_option(parser, "--rng", action="count", dest="qemu_rng",
+ help="instantiate Virt I/O random number generator")
+ _add_option(parser, "--console", action="count", dest="qemu_console",
+ default=0,
+ help="Pass an extra console (Virt I/O or ns16550_pci) to emulated barebox")
+ _add_option(parser, "--fs", action="append", dest="qemu_fs",
+ default=[], metavar="[tag=]DIR", type=_assignment,
+ help="Pass directory trees to emulated barebox. Can be specified more than once")
+ _add_option(parser, "--blk", action="append", dest="qemu_block",
+ default=[], metavar="FILE",
+ help="Pass block device to emulated barebox. Can be specified more than once")
+ _add_option(parser, "--env", action="append", dest="qemu_fw_cfg",
+ type=_assignment, default=[],
+ metavar="[envpath=]content | [envpath=]@filepath",
+ help="Pass barebox environment files to barebox. Can be specified more than once")
+ _add_option(parser, "--qemu", dest="qemu_arg",
+ nargs=argparse.REMAINDER, default=[],
+ help="Pass all remaining options to QEMU as is")
+ _add_option(parser, "--bootarg", action="append", dest="bootarg",
+ default=[],
+ help="Pass boot arguments to barebox for debugging purposes")
+ _add_option(parser, "--port-forward", metavar="PORT", action="append",
+ dest="qemu_port", default=[],
+ help="Forward incoming TCP or UDP connections on specified PORT")
-def guess_lg_env():
- config_file = helper.open_config_file(os.environ['LG_BUILDDIR'] + "/.config")
- config = helper.parse_config(config_file)
- if not config or not config.get('CONFIG_NAME'):
- return None
- arch = get_enabled_arch(config)
- if not arch:
- return None
- filename = os.path.join("test", arch, f"{config['CONFIG_NAME']}.yaml")
- if os.path.exists(filename):
- return filename
- return None
+def register_interactive_options(parser):
+ _add_option(parser, "--lg-env", dest="lg_env", metavar="LG_ENV",
+ help="labgrid environment config file")
+
+ if hasattr(parser, "add_mutually_exclusive_group"):
+ mode_parser = parser.add_mutually_exclusive_group()
+ else:
+ mode_parser = parser
+
+ _add_option(mode_parser, "--dry-run", action="store_const",
+ const=MODE_DRY_RUN, default=MODE_INTERACTIVE,
+ dest="qemu_mode",
+ help="print the QEMU command line instead of executing it")
+ _add_option(mode_parser, "--dump-dtb", action="store_const",
+ const=MODE_DUMP_DTB, dest="qemu_mode",
+ help="run QEMU only to dump the device tree")
-lg_env = lg_builddir = None
+def repo_root():
+ return os.path.realpath(os.path.join(os.path.dirname(__file__), os.pardir))
-def pytest_configure(config):
- if 'LG_BUILDDIR' not in os.environ:
- if 'KBUILD_OUTPUT' in os.environ:
- os.environ['LG_BUILDDIR'] = os.environ['KBUILD_OUTPUT']
- elif os.path.isdir('build'):
- os.environ['LG_BUILDDIR'] = os.path.realpath('build')
+def resolve_lg_builddir():
+ if "LG_BUILDDIR" not in os.environ:
+ if "KBUILD_OUTPUT" in os.environ:
+ os.environ["LG_BUILDDIR"] = os.environ["KBUILD_OUTPUT"]
+ elif os.path.isdir("build"):
+ os.environ["LG_BUILDDIR"] = os.path.realpath("build")
else:
- os.environ['LG_BUILDDIR'] = os.getcwd()
+ os.environ["LG_BUILDDIR"] = os.getcwd()
- if os.environ['LG_BUILDDIR'] is not None:
- os.environ['LG_BUILDDIR'] = os.path.realpath(os.environ['LG_BUILDDIR'])
-
- global lg_env
- global lg_builddir
-
- lg_builddir = os.environ['LG_BUILDDIR']
- lg_env = config.option.lg_env
- if lg_env is None:
- lg_env = os.environ.get('LG_ENV')
- if lg_env is None:
- if lg_env := guess_lg_env():
- os.environ['LG_ENV'] = lg_env
+ os.environ["LG_BUILDDIR"] = os.path.realpath(os.environ["LG_BUILDDIR"])
+ return os.environ["LG_BUILDDIR"]
-def pytest_report_header(config):
- report = []
- if lg_builddir is not None:
- report += [f"Build diectory: {lg_builddir}"]
- if lg_env is not None:
- report += [f"Labgrid Environment: {lg_env}"]
- return "\n".join(report)
-
-
-def pytest_addoption(parser):
- def assignment(arg):
- return arg.split('=', 1)
-
- parser.addoption('--interactive', action='store_const', const='qemu_interactive',
- dest='lg_initial_state',
- help=('(for debugging) skip tests and just start Qemu interactively'))
- parser.addoption('--dry-run', action='store_const', const='qemu_dry_run',
- dest='lg_initial_state',
- help=('(for debugging) skip tests and just print Qemu command line'))
- parser.addoption('--dump-dtb', action='store_const', const='qemu_dump_dtb',
- dest='lg_initial_state',
- help=('(for debugging) skip tests and just dump the Qemu device tree'))
- parser.addoption('--graphic', '--graphics', action='store_true', dest='qemu_graphics',
- help=('enable QEMU graphics output'))
- parser.addoption('--rng', action='count', dest='qemu_rng',
- help=('instantiate Virt I/O random number generator'))
- parser.addoption('--console', action='count', dest='qemu_console', default=0,
- help=('Pass an extra console (Virt I/O or ns16550_pci) to emulated barebox'))
- parser.addoption('--fs', action='append', dest='qemu_fs',
- default=[], metavar="[tag=]DIR", type=assignment,
- help=('Pass directory trees to emulated barebox. Can be specified more than once')) # noqa
- parser.addoption('--blk', action='append', dest='qemu_block',
- default=[], metavar="FILE",
- help=('Pass block device to emulated barebox. Can be specified more than once')) # noqa
- parser.addoption('--env', action='append', dest='qemu_fw_cfg', type=assignment,
- default=[], metavar="[envpath=]content | [envpath=]@filepath",
- help=('Pass barebox environment files to barebox. Can be specified more than once')) # noqa
- parser.addoption('--qemu', dest='qemu_arg', nargs=argparse.REMAINDER, default=[],
- help=('Pass all remaining options to QEMU as is'))
- parser.addoption('--bootarg', action='append', dest='bootarg', default=[],
- help=('Pass boot arguments to barebox for debugging purposes'))
- parser.addoption('--port-forward', metavar="PORT", action='append', dest='qemu_port', default=[],
- help=('Forward incoming TCP or UDP connections on specified PORT'))
-
-
-@pytest.fixture(scope="session")
-def strategy(request, target, pytestconfig): # noqa: max-complexity=30
+def get_config_name(builddir):
try:
- strategy = target.get_driver("Strategy")
- except NoDriverFoundError as e:
- pytest.exit(e)
-
- try:
- main = target.env.config.data["targets"]["main"]
- features = main["features"]
- except KeyError:
- features = []
-
- try:
- main = target.env.config.data["targets"]["main"]
- yaml_env = main["env"]
- except KeyError:
- yaml_env = {}
-
- try:
- main = target.env.config.data["targets"]["main"]
- qemu_bin = main["drivers"]["QEMUDriver"]["qemu_bin"]
- features.append("qemu")
- except KeyError:
+ with open(os.path.join(builddir, ".config")) as config:
+ for line in config:
+ match = re.match(r'^CONFIG_NAME="(.*)"$', line)
+ if match:
+ return match.group(1)
+ except OSError:
pass
+ return None
+
+
+def guess_lg_env(builddir=None):
+ if builddir is None:
+ builddir = os.environ.get("LG_BUILDDIR")
+ if builddir is None:
+ return None
+
+ config_name = get_config_name(builddir)
+ if config_name is None:
+ return None
+
+ matches = glob.glob(os.path.join(repo_root(), "test", "*",
+ f"{config_name}.yaml"))
+ if matches:
+ return matches[0]
+
+ return None
+
+
+def resolve_lg_env(lg_env=None):
+ if lg_env is None:
+ lg_env = os.environ.get("LG_ENV")
+ if lg_env is None:
+ lg_env = guess_lg_env()
+
+ if lg_env is not None:
+ os.environ["LG_ENV"] = lg_env
+
+ return lg_env
+
+
+def _get_option(options, name, default=None):
+ if isinstance(options, dict):
+ return options.get(name, default)
+
+ return getattr(options, name, default)
+
+
+def _get_list_option(options, name):
+ value = _get_option(options, name, [])
+ if value is None:
+ return []
+ return list(value)
+
+
+def _fail(message, returncode=1):
+ raise QemuInteractiveError(message, returncode)
+
+
+def _append_qemu_args(strategy, fail, *args):
+ if strategy.qemu is None:
+ fail("Qemu option supplied for non-Qemu target")
+
+ for arg in args:
+ strategy.console.extra_args += " " + arg
+
+
+def _append_qemu_bootargs(strategy, fail, args):
+ if strategy.qemu is None:
+ fail("Qemu option supplied for non-Qemu target")
+ if strategy.console.boot_args is None:
+ strategy.console.boot_args = ""
+ strategy.console.boot_args += " ".join(args)
+
+
+def apply_shared_options(strategy, target, options, *, interactive, fail=None): # noqa: max-complexity=30
+ if fail is None:
+ fail = _fail
+
+ try:
+ main = target.env.config.data["targets"]["main"]
+ except KeyError:
+ main = {}
+
+ features = list(main.get("features", []))
+ yaml_env = dict(main.get("env", {}))
+ qemu_driver = main.get("drivers", {}).get("QEMUDriver")
+ qemu_bin = None
+
+ if qemu_driver is not None:
+ qemu_bin = qemu_driver.get("qemu_bin")
+ features.append("qemu")
+
virtio = None
if "virtio-mmio" in features:
virtio = "device"
- strategy.append_qemu_args('-global virtio-mmio.force-legacy=false')
+ _append_qemu_args(strategy, fail, '-global virtio-mmio.force-legacy=false')
if "virtio-pci" in features:
virtio = "pci,disable-modern=off"
features.append("pci")
- if virtio and pytestconfig.option.qemu_rng:
- for i in range(pytestconfig.option.qemu_rng):
- strategy.append_qemu_args("-device", f"virtio-rng-{virtio}")
+ qemu_rng = _get_option(options, "qemu_rng", 0) or 0
+ for _ in range(qemu_rng):
+ if virtio:
+ _append_qemu_args(strategy, fail, "-device", f"virtio-rng-{virtio}")
- for i in range(pytestconfig.option.qemu_console):
+ qemu_console = _get_option(options, "qemu_console", 0) or 0
+ for i in range(qemu_console):
if virtio and i == 0:
- strategy.append_qemu_args(
+ _append_qemu_args(
+ strategy, fail,
"-device", f"virtio-serial-{virtio}",
"-chardev", f"pty,id=virtcon{i}",
"-device", f"virtconsole,chardev=virtcon{i},name=console.virtcon{i}"
@@ -198,15 +221,16 @@ def strategy(request, target, pytestconfig): # noqa: max-complexity=30
# ns16550 serial driver only works with x86 so far
if 'pci' in features:
- strategy.append_qemu_args(
+ _append_qemu_args(
+ strategy, fail,
"-chardev", f"pty,id=pcicon{i}",
"-device", f"pci-serial,chardev=pcicon{i}"
)
else:
- pytest.exit("barebox currently supports only a single extra virtio console\n", 1)
+ fail("barebox currently supports only a single extra virtio console\n")
if "qemu" in features:
- if not pytestconfig.option.qemu_graphics:
+ if not _get_option(options, "qemu_graphics", False):
graphics = '-nographic'
elif qemu_bin == "qemu-system-x86_64":
graphics = '-device isa-vga'
@@ -216,89 +240,211 @@ def strategy(request, target, pytestconfig): # noqa: max-complexity=30
graphics = '-vga none -device ramfb'
graphics += f' -device virtio-keyboard-{virtio}'
else:
- pytest.exit("--graphics unsupported for target\n", 1)
+ fail("--graphics unsupported for target\n")
- if graphics is not None and \
- pytestconfig.option.lg_initial_state != 'qemu_interactive':
+ if graphics is not None and not interactive:
graphics += ' -display none'
- strategy.append_qemu_args(graphics)
+ _append_qemu_args(strategy, fail, graphics)
- for i, blk in enumerate(pytestconfig.option.qemu_block):
+ for i, blk in enumerate(_get_list_option(options, "qemu_block")):
if virtio:
- strategy.append_qemu_args(
+ _append_qemu_args(
+ strategy, fail,
"-drive", f"if=none,format=raw,id=hd{i},file={blk}",
"-device", f"virtio-blk-{virtio},drive=hd{i}"
)
else:
- pytest.exit("--blk unsupported for target\n", 1)
+ fail("--blk unsupported for target\n")
envopts = {}
- for i, fw_cfg in enumerate(pytestconfig.option.qemu_fw_cfg):
+ for i, fw_cfg in enumerate(_get_list_option(options, "qemu_fw_cfg")):
value = fw_cfg.pop()
envpath = fw_cfg.pop() if fw_cfg else f"data/fw_cfg{i}"
envopts[envpath] = value
- for envpath, value in (yaml_env | envopts).items():
+ for envpath, value in {**yaml_env, **envopts}.items():
if virtio:
if isinstance(value, str) and value.startswith('@'):
source = f"file='{value[1:]}'"
else:
source = f"string='{value}'"
- strategy.append_qemu_args(
+ _append_qemu_args(
+ strategy, fail,
'-fw_cfg', f'name=opt/org.barebox.env/{envpath},{source}'
)
else:
- pytest.exit("env unsupported for target\n", 1)
+ fail("env unsupported for target\n")
- if len(pytestconfig.option.bootarg) > 0:
- strategy.append_qemu_bootargs(pytestconfig.option.bootarg)
+ bootargs = _get_list_option(options, "bootarg")
+ if bootargs:
+ _append_qemu_bootargs(strategy, fail, bootargs)
- for arg in pytestconfig.option.qemu_arg:
- strategy.append_qemu_args(arg)
+ for arg in _get_list_option(options, "qemu_arg"):
+ _append_qemu_args(strategy, fail, arg)
qemu_nic = "user,id=net0"
- for port in pytestconfig.option.qemu_port:
+ for port in _get_list_option(options, "qemu_port"):
qemu_nic += f",hostfwd=udp:127.0.0.2:{port}-:{port}"
qemu_nic += f",hostfwd=tcp:127.0.0.2:{port}-:{port}"
+ qemu_fs = [list(fs) for fs in _get_list_option(options, "qemu_fs")]
+
if "testfs" in features:
- if not any(fs and fs[0] == "testfs" for fs in pytestconfig.option.qemu_fs):
+ if not any(fs and fs[0] == "testfs" for fs in qemu_fs):
testfs_path = os.path.join(os.environ["LG_BUILDDIR"], "testfs")
- pytestconfig.option.qemu_fs.append(["testfs", testfs_path])
+ qemu_fs.append(["testfs", testfs_path])
os.makedirs(testfs_path, exist_ok=True)
qemu_nic += f",tftp={testfs_path}"
if "qemu" in features:
- strategy.append_qemu_args("-nic", qemu_nic)
+ _append_qemu_args(strategy, fail, "-nic", qemu_nic)
- for i, fs in enumerate(pytestconfig.option.qemu_fs):
+ for i, fs in enumerate(qemu_fs):
if virtio:
path = fs.pop()
tag = fs.pop() if fs else f"fs{i}"
- strategy.append_qemu_args(
+ _append_qemu_args(
+ strategy, fail,
"-fsdev", f"local,security_model=none,id=fs{i},path={path}",
"-device", f"virtio-9p-{virtio},id=fs{i},fsdev=fs{i},mount_tag={tag}"
)
else:
- pytest.exit("--fs unsupported for target\n", 1)
-
- state = request.config.option.lg_initial_state
- if state is not None:
- strategy.force(state)
-
- return strategy
+ fail("--fs unsupported for target\n")
-@pytest.fixture(scope="session")
-def testfs(strategy, env):
- if "testfs" not in env.get_target_features():
- pytest.skip("testfs not supported on this platform")
+def replacement_message(mode):
+ return (f"pytest {mode} is no longer supported. Use "
+ "scripts/qemu_interactive.py instead.")
- path = os.path.join(os.environ["LG_BUILDDIR"], "testfs")
- return path
+
+def _split_qemu_args(argv):
+ for i, arg in enumerate(argv):
+ if arg in ("--qemu", "--"):
+ return argv[:i], argv[i + 1:]
+ if arg.startswith("--qemu="):
+ return argv[:i], [arg.split("=", 1)[1]] + argv[i + 1:]
+
+ return argv, []
+
+
+def build_arg_parser():
+ parser = argparse.ArgumentParser(
+ description="Start a barebox labgrid QEMU target interactively",
+ epilog="Raw QEMU arguments may follow either --qemu or --."
+ )
+
+ register_interactive_options(
+ parser.add_argument_group("qemu_interactive.py options"))
+ register_shared_options(parser.add_argument_group("QEMU options"))
+ parser.add_argument("lg_env_pos", nargs="*", metavar="LG_ENV",
+ help="labgrid environment config file")
+
+ return parser
+
+
+def parse_args(argv=None):
+ if argv is None:
+ argv = sys.argv[1:]
+
+ parser = build_arg_parser()
+ argv, qemu_args = _split_qemu_args(list(argv))
+ args = parser.parse_args(argv)
+
+ if len(args.lg_env_pos) > 1:
+ parser.error("only one positional labgrid environment may be specified")
+
+ if args.lg_env_pos:
+ if args.lg_env is not None:
+ parser.error("labgrid environment specified both positionally and with --lg-env")
+ args.lg_env = args.lg_env_pos[0]
+
+ del args.lg_env_pos
+ args.qemu_arg = qemu_args
+
+ return args
+
+
+def quote_cmd(cmd):
+ quoted = map(lambda s: s if s.find(" ") == -1 else "'" + s + "'", cmd)
+ return " ".join(quoted)
+
+
+def build_qemu_command(strategy, mode):
+ if strategy.qemu is None:
+ raise QemuInteractiveError(f"Can't enter {mode} for non-QEMU target")
+
+ if mode != MODE_DRY_RUN:
+ strategy.transition("off")
+
+ if mode == MODE_DUMP_DTB:
+ strategy.qemu.machine += f",dumpdtb={strategy.target.name}.dtb"
+
+ cmd = strategy.qemu.get_qemu_base_args()
+ cmd.extend(["-serial", "mon:stdio", "-trace", "file=/dev/null"])
+
+ return cmd
+
+
+def run_qemu(strategy, mode):
+ if mode not in (MODE_INTERACTIVE, MODE_DRY_RUN, MODE_DUMP_DTB):
+ raise QemuInteractiveError(
+ "Can only force to: qemu_dry_run, qemu_interactive, qemu_dump_dtb")
+
+ cmd = build_qemu_command(strategy, mode)
+ command = "running: \n{}\n".format(quote_cmd(cmd))
+
+ if mode == MODE_DRY_RUN:
+ print(command, end="")
+ return 0
+
+ with open("/dev/tty", "r+b", buffering=0) as tty:
+ tty.write(bytes(command, "UTF-8"))
+ return subprocess.run(cmd, stdin=tty, stdout=tty, stderr=tty).returncode
+
+
+def main(argv=None):
+ from labgrid import Environment
+ from labgrid.exceptions import NoDriverFoundError
+
+ args = parse_args(argv)
+ env = None
+
+ try:
+ resolve_lg_builddir()
+ lg_env = resolve_lg_env(args.lg_env)
+ if lg_env is None:
+ raise QemuInteractiveError(
+ "no labgrid environment specified; pass --lg-env, "
+ "a positional LG_ENV, set LG_ENV, or build a config with CONFIG_NAME",
+ 2)
+
+ env = Environment(lg_env)
+ target = env.get_target("main")
+ if target is None:
+ raise QemuInteractiveError("labgrid environment has no main target")
+
+ try:
+ strategy = target.get_driver("Strategy")
+ except NoDriverFoundError as e:
+ raise QemuInteractiveError(str(e)) from e
+
+ apply_shared_options(strategy, target, args,
+ interactive=args.qemu_mode != MODE_DUMP_DTB)
+
+ return run_qemu(strategy, args.qemu_mode)
+ except QemuInteractiveError as e:
+ print(f"error: {e}", file=sys.stderr)
+ return e.returncode
+ finally:
+ if env is not None:
+ env.cleanup()
+
+
+if __name__ == "__main__":
+ sys.exit(main())
--
2.47.3
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 3/9] usb: xhci-hcd: add XHCI over PCI driver
2026-06-26 16:19 [PATCH v2 1/9] scripts: duplicate conftest.py as qemu_interactive.py Ahmad Fatoum
2026-06-26 16:19 ` [PATCH v2 2/9] test: move interactive QEMU launcher out of pytest Ahmad Fatoum
@ 2026-06-26 16:19 ` Ahmad Fatoum
2026-06-26 16:19 ` [PATCH v2 4/9] scripts: qemu_interactive.py: add new --usbblk option Ahmad Fatoum
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Ahmad Fatoum @ 2026-06-26 16:19 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
In order to support USB in QEMU Virt, add support for registering over
PCI as well. The probe function looks nearly identical to the DT-probed
xhci_probe with the difference that we map a PCI MMIO region.
To make it readily usable, also enable it in multi_v8_defconfig.
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
v1 -> v2:
- use PCI_ANY_ID
- depend on PCI
- remove intermediate symbol CONFIG_USB_XHCI
---
arch/arm/configs/multi_v8_defconfig | 1 +
drivers/usb/host/Kconfig | 7 +++
drivers/usb/host/Makefile | 1 +
drivers/usb/host/xhci-pci.c | 97 +++++++++++++++++++++++++++++
drivers/usb/host/xhci.h | 1 +
5 files changed, 107 insertions(+)
create mode 100644 drivers/usb/host/xhci-pci.c
diff --git a/arch/arm/configs/multi_v8_defconfig b/arch/arm/configs/multi_v8_defconfig
index e62dbc96fd03..9712113bf1cd 100644
--- a/arch/arm/configs/multi_v8_defconfig
+++ b/arch/arm/configs/multi_v8_defconfig
@@ -213,6 +213,7 @@ CONFIG_USB_IMX_CHIPIDEA=y
CONFIG_USB_DWC3=y
CONFIG_USB_DWC3_DUAL_ROLE=y
CONFIG_USB_EHCI=y
+CONFIG_USB_XHCI_PCI=y
CONFIG_USB_STORAGE=y
CONFIG_USB_ONBOARD_DEV=y
CONFIG_USB_GADGET=y
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 58f276cdb45a..049a3831cd66 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -41,3 +41,10 @@ config USB_XHCI
This driver currently only supports virtual USB 2.0 ports, if you
plan to use USB 3.0 devices, use a USB 2.0 cable in between.
+
+config USB_XHCI_PCI
+ tristate "xHCI over PCI driver"
+ depends on USB_XHCI && PCI
+ help
+ Say y here if your USB 3.0 controller is connected via PCI and
+ you wish to access the USB devices on the bus from barebox.
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index cbddfbe9232e..ed446d0d801c 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_USB_EHCI_ZYNQ) += ehci-zynq.o
obj-$(CONFIG_USB_OHCI) += ohci-hcd.o
obj-$(CONFIG_USB_OHCI_AT91) += ohci-at91.o
obj-$(CONFIG_USB_XHCI) += xhci.o xhci-mem.o xhci-ring.o
+obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
new file mode 100644
index 000000000000..62aadbabf88d
--- /dev/null
+++ b/drivers/usb/host/xhci-pci.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/reset.h>
+#include <module.h>
+
+#include "xhci.h"
+
+static const char hcd_name[] = "xhci_hcd";
+
+static int xhci_pci_common_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct device *dev = &pdev->dev;
+ struct xhci_ctrl *ctrl;
+ void __iomem *base;
+ int ret;
+
+ (void)id;
+
+ ret = pci_enable_device(pdev);
+ if (ret)
+ return ret;
+
+ pci_set_master(pdev);
+
+ base = pci_iomap(pdev, 0);
+ if (!base) {
+ ret = dev_err_probe(dev, -EBUSY, "failed to map BAR0\n");
+ goto err_clear_master;
+ }
+
+ ctrl = xzalloc(sizeof(*ctrl));
+ ctrl->dev = dev;
+ ctrl->hccr = base;
+ ctrl->hcor = (struct xhci_hcor *)((uintptr_t)ctrl->hccr +
+ HC_LENGTH(xhci_readl(&(ctrl->hccr)->cr_capbase)));
+
+ dev->priv = ctrl;
+
+ ret = xhci_register(ctrl);
+ if (ret)
+ goto err_free_ctrl;
+
+ return 0;
+
+err_free_ctrl:
+ dev->priv = NULL;
+ free(ctrl);
+err_clear_master:
+ pci_clear_master(pdev);
+ return ret;
+}
+
+static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ return xhci_pci_common_probe(dev, id);
+}
+
+static void xhci_pci_remove(struct pci_dev *dev)
+{
+ struct xhci_ctrl *ctrl = dev->dev.priv;
+
+ xhci_deregister(ctrl);
+ pci_clear_master(dev);
+ free(ctrl);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* PCI driver selection metadata; PCI hotplugging uses this */
+static const struct pci_device_id pci_ids[] = {
+ /* handle any USB 3.0 xHCI controller */
+ { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_XHCI, PCI_ANY_ID),
+ },
+ { /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+/* pci driver glue; this is a "new style" PCI driver module */
+static struct pci_driver xhci_pci_driver = {
+ .name = hcd_name,
+ .id_table = pci_ids,
+
+ .probe = xhci_pci_probe,
+ .remove = xhci_pci_remove,
+};
+
+static int __init xhci_pci_init(void)
+{
+ return pci_register_driver(&xhci_pci_driver);
+}
+module_init(xhci_pci_init);
+
+MODULE_DESCRIPTION("xHCI PCI Host Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 37e8cee843cf..e5feb4f3d5a1 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -20,6 +20,7 @@
#include <io.h>
#include <io-64-nonatomic-lo-hi.h>
#include <linux/list.h>
+#include <linux/usb/usb.h>
#define MAX_EP_CTX_NUM 31
#define XHCI_ALIGNMENT 64
--
2.47.3
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 4/9] scripts: qemu_interactive.py: add new --usbblk option
2026-06-26 16:19 [PATCH v2 1/9] scripts: duplicate conftest.py as qemu_interactive.py Ahmad Fatoum
2026-06-26 16:19 ` [PATCH v2 2/9] test: move interactive QEMU launcher out of pytest Ahmad Fatoum
2026-06-26 16:19 ` [PATCH v2 3/9] usb: xhci-hcd: add XHCI over PCI driver Ahmad Fatoum
@ 2026-06-26 16:19 ` Ahmad Fatoum
2026-06-26 16:19 ` [PATCH v2 5/9] scripts: qemu_interactive.py: add new --nvmeblk option Ahmad Fatoum
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Ahmad Fatoum @ 2026-06-26 16:19 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
To be able to easily exercise the new xhci-pci support, add into
qemu_interactive.py a new --usbblk option that behaves like --blk, but
instead of creating a Virt I/O block device, creates a removable USB
storage.
As the ARM 64-bit Virt machine has no USB host by default, enable
xhci-pci there, so it can be readily used.
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
v1 -> v2:
- use > YAML block
---
scripts/qemu_interactive.py | 10 ++++++++++
test/arm/virt@multi_v8_defconfig.yaml | 2 ++
2 files changed, 12 insertions(+)
diff --git a/scripts/qemu_interactive.py b/scripts/qemu_interactive.py
index a8605d23b132..827a831725ba 100755
--- a/scripts/qemu_interactive.py
+++ b/scripts/qemu_interactive.py
@@ -49,6 +49,9 @@ def register_shared_options(parser):
_add_option(parser, "--blk", action="append", dest="qemu_block",
default=[], metavar="FILE",
help="Pass block device to emulated barebox. Can be specified more than once")
+ _add_option(parser, "--usbblk", action="append", dest="qemu_usbblock",
+ default=[], metavar="FILE",
+ help="Pass USB block device to emulated barebox. Can be specified more than once")
_add_option(parser, "--env", action="append", dest="qemu_fw_cfg",
type=_assignment, default=[],
metavar="[envpath=]content | [envpath=]@filepath",
@@ -257,6 +260,13 @@ def apply_shared_options(strategy, target, options, *, interactive, fail=None):
else:
fail("--blk unsupported for target\n")
+ for i, blk in enumerate(_get_list_option(options, "qemu_usbblock")):
+ _append_qemu_args(
+ strategy, fail,
+ "-drive", f"if=none,format=raw,id=usbstorage{i},file={blk}",
+ "-device", f"usb-storage,drive=usbstorage{i},bus=usb-bus.0,removable=on"
+ )
+
envopts = {}
for i, fw_cfg in enumerate(_get_list_option(options, "qemu_fw_cfg")):
diff --git a/test/arm/virt@multi_v8_defconfig.yaml b/test/arm/virt@multi_v8_defconfig.yaml
index 4eb75da4610e..209b59c8fff7 100644
--- a/test/arm/virt@multi_v8_defconfig.yaml
+++ b/test/arm/virt@multi_v8_defconfig.yaml
@@ -8,6 +8,8 @@ targets:
memory: 1024M
bios: barebox-qemu-virt.img
display: qemu-default
+ extra_args: >
+ -device qemu-xhci
BareboxDriver:
prompt: 'barebox@[^:]+:[^ ]+ '
bootstring: 'commandline:'
--
2.47.3
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 5/9] scripts: qemu_interactive.py: add new --nvmeblk option
2026-06-26 16:19 [PATCH v2 1/9] scripts: duplicate conftest.py as qemu_interactive.py Ahmad Fatoum
` (2 preceding siblings ...)
2026-06-26 16:19 ` [PATCH v2 4/9] scripts: qemu_interactive.py: add new --usbblk option Ahmad Fatoum
@ 2026-06-26 16:19 ` Ahmad Fatoum
2026-06-26 16:19 ` [PATCH v2 6/9] mci: sdhci: define same SDHCI_INT_* constants as Linux Ahmad Fatoum
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Ahmad Fatoum @ 2026-06-26 16:19 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
To be able to test the recent non-512-byte block support, add a
--nvmeblk option that behaves like --blk, but appears to the guest as
block device.
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
v1 -> v2:
- no change
---
arch/arm/configs/multi_v8_defconfig | 2 ++
scripts/qemu_interactive.py | 10 ++++++++++
2 files changed, 12 insertions(+)
diff --git a/arch/arm/configs/multi_v8_defconfig b/arch/arm/configs/multi_v8_defconfig
index 9712113bf1cd..5ea63372d050 100644
--- a/arch/arm/configs/multi_v8_defconfig
+++ b/arch/arm/configs/multi_v8_defconfig
@@ -103,6 +103,7 @@ CONFIG_CMD_MAGICVAR_HELP=y
CONFIG_CMD_SAVEENV=y
CONFIG_CMD_FILETYPE=y
CONFIG_CMD_LN=y
+CONFIG_CMD_STAT=y
CONFIG_CMD_MD5SUM=y
CONFIG_CMD_SHA1SUM=y
CONFIG_CMD_SHA224SUM=y
@@ -284,6 +285,7 @@ CONFIG_PHY_ROCKCHIP_USBDP=y
CONFIG_ROCKCHIP_IODOMAIN=y
CONFIG_ROCKCHIP_PM_DOMAINS=y
CONFIG_TI_SCI_PM_DOMAINS=y
+CONFIG_BLK_DEV_NVME=y
CONFIG_NVMEM_REBOOT_MODE=y
CONFIG_VIRTIO_MMIO=y
CONFIG_VIRTIO_PCI=y
diff --git a/scripts/qemu_interactive.py b/scripts/qemu_interactive.py
index 827a831725ba..77ff6f9fc8b4 100755
--- a/scripts/qemu_interactive.py
+++ b/scripts/qemu_interactive.py
@@ -49,6 +49,9 @@ def register_shared_options(parser):
_add_option(parser, "--blk", action="append", dest="qemu_block",
default=[], metavar="FILE",
help="Pass block device to emulated barebox. Can be specified more than once")
+ _add_option(parser, "--nvmeblk", action="append", dest="qemu_nvmeblock",
+ default=[], metavar="FILE",
+ help="Pass NVMe block device with 4K sector size to emulated barebox. Can be specified more than once")
_add_option(parser, "--usbblk", action="append", dest="qemu_usbblock",
default=[], metavar="FILE",
help="Pass USB block device to emulated barebox. Can be specified more than once")
@@ -260,6 +263,13 @@ def apply_shared_options(strategy, target, options, *, interactive, fail=None):
else:
fail("--blk unsupported for target\n")
+ for i, blk in enumerate(_get_list_option(options, "qemu_nvmeblock")):
+ _append_qemu_args(
+ strategy, fail,
+ "-drive", f"if=none,format=raw,id=nvme{i},file={blk}",
+ "-device", f"nvme,drive=nvme{i},serial=0ba2eb08,logical_block_size=4096,physical_block_size=4096"
+ )
+
for i, blk in enumerate(_get_list_option(options, "qemu_usbblock")):
_append_qemu_args(
strategy, fail,
--
2.47.3
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 6/9] mci: sdhci: define same SDHCI_INT_* constants as Linux
2026-06-26 16:19 [PATCH v2 1/9] scripts: duplicate conftest.py as qemu_interactive.py Ahmad Fatoum
` (3 preceding siblings ...)
2026-06-26 16:19 ` [PATCH v2 5/9] scripts: qemu_interactive.py: add new --nvmeblk option Ahmad Fatoum
@ 2026-06-26 16:19 ` Ahmad Fatoum
2026-06-26 16:19 ` [PATCH v2 7/9] mci: add PCI SDHCI controller support Ahmad Fatoum
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Ahmad Fatoum @ 2026-06-26 16:19 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
We already define a number of these bits and make use of them, but not
all of them are named identically to Linux.
To make code easier to port and avoid confusion, just define the Linux
bits as well.
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
v1 -> v2:
- new commit
---
drivers/mci/sdhci.h | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/drivers/mci/sdhci.h b/drivers/mci/sdhci.h
index d1f05ac96859..a3b8e65b55ee 100644
--- a/drivers/mci/sdhci.h
+++ b/drivers/mci/sdhci.h
@@ -111,6 +111,10 @@
#define SDHCI_RESET_DATA BIT(2)
#define SDHCI_INT_STATUS 0x30
#define SDHCI_INT_NORMAL_STATUS 0x30
+#define SDHCI_INT_TUNING_ERROR BIT(26)
+#define SDHCI_INT_ADMA_ERROR BIT(25)
+#define SDHCI_INT_AUTO_CMD_ERR BIT(24)
+#define SDHCI_INT_BUS_POWER BIT(23)
#define SDHCI_INT_DATA_END_BIT BIT(22)
#define SDHCI_INT_DATA_CRC BIT(21)
#define SDHCI_INT_DATA_TIMEOUT BIT(20)
@@ -119,13 +123,36 @@
#define SDHCI_INT_CRC BIT(17)
#define SDHCI_INT_TIMEOUT BIT(16)
#define SDHCI_INT_ERROR BIT(15)
+#define SDHCI_INT_CQE BIT(14)
+/* Host Version 4.10 */
+#define SDHCI_INT_FX_EVENT BIT(13)
+#define SDHCI_INT_RETUNE BIT(12)
#define SDHCI_INT_CARD_INT BIT(8)
#define SDHCI_INT_CARD_INSERT BIT(6)
#define SDHCI_INT_DATA_AVAIL BIT(5)
#define SDHCI_INT_SPACE_AVAIL BIT(4)
#define SDHCI_INT_DMA BIT(3)
+#define SDHCI_INT_BLK_GAP BIT(2)
#define SDHCI_INT_XFER_COMPLETE BIT(1)
#define SDHCI_INT_CMD_COMPLETE BIT(0)
+
+/* Linux-compatible constants */
+#define SDHCI_INT_RESPONSE SDHCI_INT_CMD_COMPLETE
+#define SDHCI_INT_DATA_END SDHCI_INT_XFER_COMPLETE
+#define SDHCI_INT_DMA_END SDHCI_INT_DMA
+#define SDHCI_INT_CARD_REMOVE SDHCI_INT_CARD_INT
+
+#define SDHCI_INT_CMD_MASK (SDHCI_INT_RESPONSE | SDHCI_INT_TIMEOUT | \
+ SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX | \
+ SDHCI_INT_AUTO_CMD_ERR)
+#define SDHCI_INT_DATA_MASK (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \
+ SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \
+ SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \
+ SDHCI_INT_DATA_END_BIT | SDHCI_INT_ADMA_ERROR | \
+ SDHCI_INT_BLK_GAP | SDHCI_INT_TUNING_ERROR)
+#define SDHCI_INT_ALL_MASK ((unsigned int)-1)
+
+
#define SDHCI_INT_ERROR_STATUS 0x32
#define SDHCI_INT_ENABLE 0x34
#define SDHCI_INT_ERROR_ENABLE 0x36
--
2.47.3
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 7/9] mci: add PCI SDHCI controller support
2026-06-26 16:19 [PATCH v2 1/9] scripts: duplicate conftest.py as qemu_interactive.py Ahmad Fatoum
` (4 preceding siblings ...)
2026-06-26 16:19 ` [PATCH v2 6/9] mci: sdhci: define same SDHCI_INT_* constants as Linux Ahmad Fatoum
@ 2026-06-26 16:19 ` Ahmad Fatoum
2026-06-26 16:19 ` [PATCH v2 8/9] ARM: multi_v8_defconfig: enable OP-TEE and RPMB support Ahmad Fatoum
2026-06-26 16:19 ` [PATCH v2 9/9] scripts: qemu_interactive.py: add new --sdblk and --emmcblk options Ahmad Fatoum
7 siblings, 0 replies; 9+ messages in thread
From: Ahmad Fatoum @ 2026-06-26 16:19 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
QEMU can expose eMMC through the class-compliant sdhci-pci device, but
barebox only had platform SDHCI glue. Add a PCI host driver that maps BAR0,
enables bus mastering, initializes the SDHCI core and registers an MCI host.
This is intentionally small and PIO-based, which is enough for the QEMU RPMB
test environment while reusing the common SDHCI helpers.
Assisted-by: Codex:gpt-5.5
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
v1 -> v2:
- new commit
---
drivers/mci/Kconfig | 8 ++
drivers/mci/Makefile | 1 +
drivers/mci/mci-core.c | 2 +-
drivers/mci/sdhci-pci.c | 203 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 213 insertions(+), 1 deletion(-)
create mode 100644 drivers/mci/sdhci-pci.c
diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
index b38f7a3bdf8b..55e63aad92d4 100644
--- a/drivers/mci/Kconfig
+++ b/drivers/mci/Kconfig
@@ -90,6 +90,14 @@ config MCI_MMC_RPMB
comment "--- MCI host drivers ---"
+config MCI_SDHCI_PCI
+ bool "PCI SDHCI controller"
+ depends on PCI && HAS_DMA
+ select MCI_SDHCI
+ help
+ Enable support for PCI-attached SDHCI controllers, such as QEMU's
+ sdhci-pci device used to attach emulated eMMC devices.
+
config MCI_DWC_MSHC
bool "Synopsys DesignWare Cores MSHC"
depends on HAS_DMA
diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile
index 67a1f7bf1724..761f4187c079 100644
--- a/drivers/mci/Makefile
+++ b/drivers/mci/Makefile
@@ -2,6 +2,7 @@
obj-$(CONFIG_MCI) += mci-core.o
pbl-$(CONFIG_MCI) += mci-pbl.o
obj-$(CONFIG_MCI_MMC_RPMB) += rpmb.o
+obj-$(CONFIG_MCI_SDHCI_PCI) += sdhci-pci.o
obj-$(CONFIG_MCI_AM654) += am654-sdhci.o
obj-$(CONFIG_MCI_ARASAN) += arasan-sdhci.o
obj-$(CONFIG_MCI_ATMEL) += atmel_mci.o atmel_mci_common.o
diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c
index 4413fd1655aa..d6746fbec28f 100644
--- a/drivers/mci/mci-core.c
+++ b/drivers/mci/mci-core.c
@@ -2942,7 +2942,7 @@ static struct device_node *mci_get_partition_node(struct device_node *hwnode,
struct device_node *np;
char partnodename[sizeof("bootx-partitions")];
- if (index > 8)
+ if (!hwnode || index > 8)
return NULL;
switch (type) {
diff --git a/drivers/mci/sdhci-pci.c b/drivers/mci/sdhci-pci.c
new file mode 100644
index 000000000000..1c8ad5435697
--- /dev/null
+++ b/drivers/mci/sdhci-pci.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <dma.h>
+#include <init.h>
+#include <io.h>
+#include <malloc.h>
+#include <mci.h>
+#include <module.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+
+#include "sdhci.h"
+
+struct sdhci_pci_host {
+ struct mci_host mci;
+ struct sdhci sdhci;
+};
+
+static inline struct sdhci_pci_host *to_sdhci_pci_host(struct mci_host *mci)
+{
+ return container_of(mci, struct sdhci_pci_host, mci);
+}
+
+#define PCI_CLASS_SYSTEM_SDHCI_DMA ((PCI_CLASS_SYSTEM_SDHCI << 8) | 0x01)
+
+static const struct pci_device_id pci_ids[] = {
+ /* Generic SD host controller */
+ {PCI_DEVICE_CLASS(PCI_CLASS_SYSTEM_SDHCI_DMA, PCI_ANY_ID)},
+ { /* end: all zeroes */ },
+};
+
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+/*****************************************************************************\
+ * *
+ * MCI core callbacks *
+ * *
+\*****************************************************************************/
+
+static int sdhci_pci_init(struct mci_host *mci, struct device *dev)
+{
+ struct sdhci_pci_host *host = to_sdhci_pci_host(mci);
+ int ret;
+
+ (void)dev;
+
+ ret = sdhci_reset(&host->sdhci, SDHCI_RESET_ALL);
+ if (ret)
+ return ret;
+
+ sdhci_write8(&host->sdhci, SDHCI_POWER_CONTROL,
+ SDHCI_BUS_VOLTAGE_330 | SDHCI_BUS_POWER_EN);
+ udelay(400);
+
+ sdhci_write32(&host->sdhci, SDHCI_INT_ENABLE,
+ SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK);
+ sdhci_write32(&host->sdhci, SDHCI_SIGNAL_ENABLE, 0);
+
+ return 0;
+}
+
+static void sdhci_pci_set_ios(struct mci_host *mci, struct mci_ios *ios)
+{
+ struct sdhci_pci_host *host = to_sdhci_pci_host(mci);
+ u8 val;
+
+ if (ios->clock)
+ sdhci_set_clock(&host->sdhci, ios->clock, host->sdhci.max_clk);
+
+ sdhci_set_bus_width(&host->sdhci, ios->bus_width);
+
+ val = sdhci_read8(&host->sdhci, SDHCI_HOST_CONTROL);
+ if (ios->clock > 26000000)
+ val |= SDHCI_CTRL_HISPD;
+ else
+ val &= ~SDHCI_CTRL_HISPD;
+ sdhci_write8(&host->sdhci, SDHCI_HOST_CONTROL, val);
+}
+
+static int sdhci_pci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd)
+{
+ struct sdhci_pci_host *host = to_sdhci_pci_host(mci);
+
+ return sdhci_send_command(&host->sdhci, cmd);
+}
+
+static int sdhci_pci_card_present(struct mci_host *mci)
+{
+ struct sdhci_pci_host *host = to_sdhci_pci_host(mci);
+
+ return !!(sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) &
+ SDHCI_CARD_PRESENT);
+}
+
+static int sdhci_pci_card_write_protected(struct mci_host *mci)
+{
+ struct sdhci_pci_host *host = to_sdhci_pci_host(mci);
+
+ return !(sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) &
+ SDHCI_WRITE_PROTECT);
+}
+
+static const struct mci_ops sdhci_pci_ops = {
+ .init = sdhci_pci_init,
+ .set_ios = sdhci_pci_set_ios,
+ .send_cmd = sdhci_pci_send_cmd,
+ .card_present = sdhci_pci_card_present,
+ .card_write_protected = sdhci_pci_card_write_protected,
+};
+
+/*****************************************************************************\
+ * *
+ * Device probing/removal *
+ * *
+\*****************************************************************************/
+
+static int sdhci_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct sdhci_pci_host *host;
+ struct mci_host *mci;
+ void __iomem *base;
+ int ret;
+
+ (void)ent;
+
+ dev_info(&pdev->dev, "SDHCI controller found [%04x:%04x] (rev %x)\n",
+ (int)pdev->vendor, (int)pdev->device, (int)pdev->revision);
+
+ ret = pci_enable_device(pdev);
+ if (ret)
+ return ret;
+
+ pci_set_master(pdev);
+
+ base = pci_iomap(pdev, 0);
+ if (!base)
+ return dev_err_probe(&pdev->dev, -EBUSY, "failed to map BAR0\n");
+
+ host = xzalloc(sizeof(*host));
+ mci = &host->mci;
+
+ host->sdhci.base = base;
+ host->sdhci.mci = mci;
+ host->sdhci.quirks2 = SDHCI_QUIRK2_BROKEN_HS200;
+
+ mci->hw_dev = &pdev->dev;
+ mci->ops = sdhci_pci_ops;
+ mci->max_req_size = 0x8000;
+
+ ret = sdhci_setup_host(&host->sdhci);
+ if (ret)
+ goto err;
+
+ if (host->sdhci.flags & SDHCI_USE_64_BIT_DMA)
+ dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
+ else
+ dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
+
+ ret = sdhci_setup_adma(&host->sdhci);
+ if (ret && ret != -ENOTSUPP)
+ dev_warn(&pdev->dev, "ADMA setup failed (%pe), falling back to SDMA\n",
+ ERR_PTR(ret));
+
+ mci->f_max = host->sdhci.max_clk;
+ if (!mci->f_max)
+ mci->f_max = 50000000;
+ mci->f_min = mci->f_max / SDHCI_MAX_DIV_SPEC_300;
+
+ pdev->dev.priv = host;
+
+ ret = mci_register(mci);
+ if (ret)
+ goto err;
+
+ return 0;
+
+err:
+ pdev->dev.priv = NULL;
+ free(host);
+ return ret;
+}
+
+static void sdhci_pci_remove(struct pci_dev *pdev)
+{
+ struct sdhci_pci_host *host = pdev->dev.priv;
+
+ sdhci_release_adma(&host->sdhci);
+ pci_clear_master(pdev);
+}
+
+static struct pci_driver sdhci_driver = {
+ .name = "sdhci-pci",
+ .id_table = pci_ids,
+ .probe = sdhci_pci_probe,
+ .remove = sdhci_pci_remove,
+};
+
+static int __init sdhci_pci_driver_init(void)
+{
+ return pci_register_driver(&sdhci_driver);
+}
+module_init(sdhci_pci_driver_init);
--
2.47.3
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 8/9] ARM: multi_v8_defconfig: enable OP-TEE and RPMB support
2026-06-26 16:19 [PATCH v2 1/9] scripts: duplicate conftest.py as qemu_interactive.py Ahmad Fatoum
` (5 preceding siblings ...)
2026-06-26 16:19 ` [PATCH v2 7/9] mci: add PCI SDHCI controller support Ahmad Fatoum
@ 2026-06-26 16:19 ` Ahmad Fatoum
2026-06-26 16:19 ` [PATCH v2 9/9] scripts: qemu_interactive.py: add new --sdblk and --emmcblk options Ahmad Fatoum
7 siblings, 0 replies; 9+ messages in thread
From: Ahmad Fatoum @ 2026-06-26 16:19 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
To allow testing the TEE + RPMB functionality in barebox, enable
everything the relevant barebox drivers and commands as well as command
for manipulating and partitioning the mmc.
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
v1 -> v2:
- new commit
---
arch/arm/configs/multi_v8_defconfig | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/arch/arm/configs/multi_v8_defconfig b/arch/arm/configs/multi_v8_defconfig
index 5ea63372d050..0ed2770d2d37 100644
--- a/arch/arm/configs/multi_v8_defconfig
+++ b/arch/arm/configs/multi_v8_defconfig
@@ -84,6 +84,7 @@ CONFIG_CMD_MEMINFO=y
CONFIG_CMD_ARM_MMUINFO=y
CONFIG_CMD_REGULATOR=y
CONFIG_CMD_BFETCH=y
+CONFIG_CMD_MMC=y
CONFIG_CMD_MMC_EXTCSD=y
CONFIG_CMD_POLLER=y
CONFIG_CMD_SLICE=y
@@ -94,6 +95,7 @@ CONFIG_CMD_UIMAGE=y
CONFIG_CMD_BOOTCHOOSER=y
CONFIG_CMD_PARTITION=y
CONFIG_CMD_FINDMNT=y
+CONFIG_CMD_PARTED=y
CONFIG_CMD_EXPORT=y
CONFIG_CMD_DEFAULTENV=y
CONFIG_CMD_LOADENV=y
@@ -141,6 +143,7 @@ CONFIG_CMD_SMC=y
CONFIG_CMD_LED_TRIGGER=y
CONFIG_CMD_USBGADGET=y
CONFIG_CMD_WD=y
+CONFIG_CMD_AVB_PVALUE=y
CONFIG_CMD_LOGIN=y
CONFIG_CMD_PASSWD=y
CONFIG_CMD_BAREBOX_UPDATE=y
@@ -228,6 +231,9 @@ CONFIG_DRIVER_VIDEO_RAMFB=y
CONFIG_SOUND=y
CONFIG_MCI=y
CONFIG_MCI_MMC_BOOT_PARTITIONS=y
+CONFIG_MCI_MMC_GPP_PARTITIONS=y
+CONFIG_MCI_MMC_RPMB=y
+CONFIG_MCI_SDHCI_PCI=y
CONFIG_MCI_DW=y
CONFIG_MCI_SUNXI_SMHC=y
CONFIG_MCI_ROCKCHIP_DWCMSHC=y
@@ -290,6 +296,9 @@ CONFIG_NVMEM_REBOOT_MODE=y
CONFIG_VIRTIO_MMIO=y
CONFIG_VIRTIO_PCI=y
CONFIG_MAILBOX=y
+CONFIG_TEE=y
+CONFIG_OPTEE=y
+CONFIG_OPTEE_AVB_PERSISTENT_VALUES=y
CONFIG_FS_CRAMFS=y
CONFIG_FS_EXT4=y
CONFIG_FS_TFTP=y
--
2.47.3
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 9/9] scripts: qemu_interactive.py: add new --sdblk and --emmcblk options
2026-06-26 16:19 [PATCH v2 1/9] scripts: duplicate conftest.py as qemu_interactive.py Ahmad Fatoum
` (6 preceding siblings ...)
2026-06-26 16:19 ` [PATCH v2 8/9] ARM: multi_v8_defconfig: enable OP-TEE and RPMB support Ahmad Fatoum
@ 2026-06-26 16:19 ` Ahmad Fatoum
7 siblings, 0 replies; 9+ messages in thread
From: Ahmad Fatoum @ 2026-06-26 16:19 UTC (permalink / raw)
To: barebox; +Cc: Ahmad Fatoum
These options will add a SD-Card or an eMMC block device, respectively.
The eMMC emulation is a recent addition to QEMU and isn't usable in our
Debian Trixie container yet, but it would be very useful to exercise the
RPMB supplicant code as well as EFI variables in future.
The eMMC image has optional BOOT1, BOOT2 and RPMB partitions, which if
available are located on that order in front of the user area.
To make it easy to use multiple separate images, we create a VMDK image
that references the supplied files.
Example usage:
scripts/qemu_interactive.py --emmcblk user=hd.img,boot1=boot1.img,rpmb=rpmb.img
or
scripts/qemu_interactive.py --sdblk hd.img
To make it readily usable on multi_v8_defconfig.yaml, also add a SDHCI
PCI device to the virtual machine.
Assisted-by: Codex:gpt-5.5
Signed-off-by: Ahmad Fatoum <a.fatoum@barebox.org>
---
v1 -> v2:
- new commit
---
scripts/qemu_interactive.py | 174 ++++++++++++++++++++++++++
test/arm/virt@multi_v8_defconfig.yaml | 1 +
2 files changed, 175 insertions(+)
diff --git a/scripts/qemu_interactive.py b/scripts/qemu_interactive.py
index 77ff6f9fc8b4..90b17e4f6422 100755
--- a/scripts/qemu_interactive.py
+++ b/scripts/qemu_interactive.py
@@ -6,17 +6,26 @@
# SPDX-License-Identifier: GPL-2.0-only
import argparse
+import atexit
import glob
import os
import re
import subprocess
import sys
+import tempfile
MODE_INTERACTIVE = "qemu_interactive"
MODE_DRY_RUN = "qemu_dry_run"
MODE_DUMP_DTB = "qemu_dump_dtb"
+EMMC_VMDK_SECTOR_SIZE = 512
+EMMC_BOOT_RPMB_ALIGNMENT = 128 * 1024
+EMMC_USER_ALIGNMENT = 1024 * 1024
+EMMC_PARTITION_HWPARTS = ("boot1", "boot2", "rpmb", "user")
+
+_temporary_emmc_vmdks = []
+
class QemuInteractiveError(Exception):
def __init__(self, message, returncode=1):
@@ -35,6 +44,18 @@ def _assignment(arg):
return arg.split("=", 1)
+def _cleanup_emmc_vmdks():
+ while _temporary_emmc_vmdks:
+ path = _temporary_emmc_vmdks.pop()
+ try:
+ os.unlink(path)
+ except FileNotFoundError:
+ pass
+
+
+atexit.register(_cleanup_emmc_vmdks)
+
+
def register_shared_options(parser):
_add_option(parser, "--graphic", "--graphics", action="store_true",
dest="qemu_graphics", help="enable QEMU graphics output")
@@ -49,6 +70,14 @@ def register_shared_options(parser):
_add_option(parser, "--blk", action="append", dest="qemu_block",
default=[], metavar="FILE",
help="Pass block device to emulated barebox. Can be specified more than once")
+ _add_option(parser, "--sdblk", action="append", dest="qemu_sdblock",
+ default=[], metavar="FILE",
+ help="Pass SD-Card block device to emulated barebox. Can be specified more than once")
+ _add_option(parser, "--emmcblk", action="append", dest="qemu_emmcblock",
+ default=[], metavar="FILE|HWPART=FILE[,HWPART=FILE...]",
+ help="Pass eMMC block device to emulated barebox. Can be "
+ "specified more than once. HWPART may be boot1, boot2, rpmb, "
+ "or user")
_add_option(parser, "--nvmeblk", action="append", dest="qemu_nvmeblock",
default=[], metavar="FILE",
help="Pass NVMe block device with 4K sector size to emulated barebox. Can be specified more than once")
@@ -182,6 +211,141 @@ def _append_qemu_bootargs(strategy, fail, args):
strategy.console.boot_args += " ".join(args)
+def _alignment_name(alignment):
+ if alignment % (1024 * 1024) == 0:
+ return f"{alignment // (1024 * 1024)} MiB"
+
+ return f"{alignment // 1024} KiB"
+
+
+def _emmc_partition_alignment(hwpart):
+ if hwpart == "user":
+ return EMMC_USER_ALIGNMENT
+
+ return EMMC_BOOT_RPMB_ALIGNMENT
+
+
+def _parse_emmc_partition_spec(spec, fail):
+ partitions = {}
+
+ for entry in spec.split(","):
+ if not entry:
+ fail("--emmcblk: empty partition entry")
+
+ hwpart, sep, value = entry.partition("=")
+ if not sep:
+ fail(f"--emmcblk: partition entry '{entry}' lacks '='")
+ if hwpart not in EMMC_PARTITION_HWPARTS:
+ fail(f"--emmcblk: unknown partition '{hwpart}'")
+ if hwpart in partitions:
+ fail(f"--emmcblk: duplicate partition '{hwpart}'")
+ if not value:
+ fail(f"--emmcblk: missing path for '{hwpart}'")
+ if '"' in value or "\n" in value or "\r" in value:
+ fail(f"--emmcblk: unsupported character in path for '{hwpart}'")
+ if not os.path.exists(value):
+ fail(f"--emmcblk: file for '{hwpart}' does not exist: {value}")
+ if not os.path.isfile(value):
+ fail(f"--emmcblk: path for '{hwpart}' is not a regular file: {value}")
+
+ size = os.path.getsize(value)
+ if size == 0:
+ fail(f"--emmcblk: file for '{hwpart}' is empty: {value}")
+
+ alignment = _emmc_partition_alignment(hwpart)
+ if size % alignment:
+ fail(f"--emmcblk: size of '{hwpart}' must be aligned to "
+ f"{_alignment_name(alignment)}: {value}")
+
+ partitions[hwpart] = {
+ "path": os.path.abspath(value),
+ "size": size,
+ }
+
+ if not partitions:
+ fail("--emmcblk: no partitions specified")
+
+ boot1 = partitions.get("boot1")
+ boot2 = partitions.get("boot2")
+
+ if boot1 and boot2 and boot1["size"] != boot2["size"]:
+ fail("--emmcblk: boot1 and boot2 must have the same size")
+
+ return partitions
+
+
+def _emmc_vmdk_escape_path(path):
+ if '"' in path or "\n" in path or "\r" in path:
+ raise QemuInteractiveError(
+ f"--emmcblk: unsupported character in VMDK path: {path}")
+
+ return path
+
+
+def _write_emmc_vmdk(extents):
+ with tempfile.NamedTemporaryFile(
+ mode="w", encoding="ascii", prefix="barebox-emmc-",
+ suffix=".vmdk", delete=False) as vmdk:
+ vmdk.write("# Disk DescriptorFile\n")
+ vmdk.write("version=1\n")
+ vmdk.write("CID=fffffffe\n")
+ vmdk.write("parentCID=ffffffff\n")
+ vmdk.write('createType="monolithicFlat"\n\n')
+ vmdk.write("# Extent description\n")
+
+ for path, size in extents:
+ sectors = size // EMMC_VMDK_SECTOR_SIZE
+ vmdk.write(
+ f'RW {sectors} FLAT "{_emmc_vmdk_escape_path(path)}" 0\n')
+
+ vmdk.write("\n# The Disk Data Base\n")
+ vmdk.write("#DDB\n")
+ vmdk.write('ddb.adapterType = "ide"\n')
+
+ _temporary_emmc_vmdks.append(vmdk.name)
+
+ return vmdk.name
+
+
+def _create_emmc_vmdk(partitions):
+ boot1 = partitions.get("boot1")
+ boot2 = partitions.get("boot2")
+ rpmb = partitions.get("rpmb")
+ user = partitions.get("user")
+ boot_size = 0
+ rpmb_size = 0
+ extents = []
+
+ if boot1 or boot2:
+ boot_size = (boot1 or boot2)["size"]
+ extents.append(((boot1 or {"path": "/dev/zero"})["path"], boot_size))
+ extents.append(((boot2 or {"path": "/dev/zero"})["path"], boot_size))
+
+ if rpmb:
+ rpmb_size = rpmb["size"]
+ extents.append((rpmb["path"], rpmb_size))
+
+ if user:
+ extents.append((user["path"], user["size"]))
+
+ return _write_emmc_vmdk(extents), boot_size, rpmb_size
+
+
+def _qemu_emmc_args(index, blk, fail):
+ if not ("=" in blk or "," in blk):
+ blk = f"user={blk}"
+
+ partitions = _parse_emmc_partition_spec(blk, fail)
+ vmdk, boot_size, rpmb_size = _create_emmc_vmdk(partitions)
+
+ return (
+ "-drive", f"if=none,format=vmdk,id=emmc{index},file={vmdk}",
+ "-device",
+ f"emmc,drive=emmc{index},boot-partition-size={boot_size},"
+ f"rpmb-partition-size={rpmb_size}",
+ )
+
+
def apply_shared_options(strategy, target, options, *, interactive, fail=None): # noqa: max-complexity=30
if fail is None:
fail = _fail
@@ -263,6 +427,16 @@ def apply_shared_options(strategy, target, options, *, interactive, fail=None):
else:
fail("--blk unsupported for target\n")
+ for i, blk in enumerate(_get_list_option(options, "qemu_sdblock")):
+ _append_qemu_args(
+ strategy, fail,
+ "-drive", f"if=none,format=raw,id=sdcard{i},file={blk}",
+ "-device", f"sd-card,drive=sdcard{i}"
+ )
+
+ for i, blk in enumerate(_get_list_option(options, "qemu_emmcblock")):
+ _append_qemu_args(strategy, fail, *_qemu_emmc_args(i, blk, fail))
+
for i, blk in enumerate(_get_list_option(options, "qemu_nvmeblock")):
_append_qemu_args(
strategy, fail,
diff --git a/test/arm/virt@multi_v8_defconfig.yaml b/test/arm/virt@multi_v8_defconfig.yaml
index 209b59c8fff7..ba430df8b3c2 100644
--- a/test/arm/virt@multi_v8_defconfig.yaml
+++ b/test/arm/virt@multi_v8_defconfig.yaml
@@ -10,6 +10,7 @@ targets:
display: qemu-default
extra_args: >
-device qemu-xhci
+ -device sdhci-pci
BareboxDriver:
prompt: 'barebox@[^:]+:[^ ]+ '
bootstring: 'commandline:'
--
2.47.3
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-06-26 16:27 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-06-26 16:19 [PATCH v2 1/9] scripts: duplicate conftest.py as qemu_interactive.py Ahmad Fatoum
2026-06-26 16:19 ` [PATCH v2 2/9] test: move interactive QEMU launcher out of pytest Ahmad Fatoum
2026-06-26 16:19 ` [PATCH v2 3/9] usb: xhci-hcd: add XHCI over PCI driver Ahmad Fatoum
2026-06-26 16:19 ` [PATCH v2 4/9] scripts: qemu_interactive.py: add new --usbblk option Ahmad Fatoum
2026-06-26 16:19 ` [PATCH v2 5/9] scripts: qemu_interactive.py: add new --nvmeblk option Ahmad Fatoum
2026-06-26 16:19 ` [PATCH v2 6/9] mci: sdhci: define same SDHCI_INT_* constants as Linux Ahmad Fatoum
2026-06-26 16:19 ` [PATCH v2 7/9] mci: add PCI SDHCI controller support Ahmad Fatoum
2026-06-26 16:19 ` [PATCH v2 8/9] ARM: multi_v8_defconfig: enable OP-TEE and RPMB support Ahmad Fatoum
2026-06-26 16:19 ` [PATCH v2 9/9] scripts: qemu_interactive.py: add new --sdblk and --emmcblk options Ahmad Fatoum
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox