From: "Alex Bennée" <alex.bennee@linaro.org>
To: tangtao1634 <tangtao1634@phytium.com.cn>
Cc: pbonzini@redhat.com, farosas@suse.de, lvivier@redhat.com,
"Eric Auger" <eric.auger@redhat.com>,
"Peter Maydell" <peter.maydell@linaro.org>,
qemu-devel@nongnu.org, qemu-arm@nongnu.org,
"Chen Baozi" <chenbaozi@phytium.com.cn>,
"Pierrick Bouvier" <pierrick.bouvier@linaro.org>,
"Philippe Mathieu-Daudé" <philmd@linaro.org>,
"Jean-Philippe Brucker" <jean-philippe@linaro.org>,
"Mostafa Saleh" <smostafa@google.com>
Subject: Re: [RFC v2 2/2] tests/qtest: add SMMUv3 smoke test using smmu-testdev DMA source
Date: Thu, 23 Oct 2025 12:02:58 +0100 [thread overview]
Message-ID: <87jz0lnan1.fsf@draig.linaro.org> (raw)
In-Reply-To: <20250930165340.42788-3-tangtao1634@phytium.com.cn> (tangtao's message of "Wed, 1 Oct 2025 00:53:40 +0800")
tangtao1634 <tangtao1634@phytium.com.cn> writes:
> From: Tao Tang <tangtao1634@phytium.com.cn>
>
> Introduce a bare-metal qtest that drives the new smmu-testdev to exercise
> the SMMUv3 emulation without guest firmware or drivers. The test programs
> a minimal Non-Secure context (STE/CD/PTE), triggers a DMA, and asserts
> translation results.
>
> Motivation
> ----------
> SMMU testing in emulation often requires a large software stack and a
> realistic PCIe fabric, which adds flakiness and obscures failures. This
> qtest keeps the surface small and deterministic by using a hermetic DMA
> source that feeds the SMMU directly.
>
> What the test covers
> --------------------
> * Builds a Non-Secure STE/CD/PTE for a chosen stream_id/ssid.
> * Primes source and destination host buffers.
> * Kicks a DMA via smmu-testdev and waits for completion.
> * Verifies translated access and payload equality.
>
> Non-goals and scope limits
> --------------------------
> * Secure bank flows are omitted because Secure SMMU support is still RFC.
> A local Secure test exists and can be posted once the upstream series
> lands.
> * PCIe discovery, MSI/INTx, ATS/PRI, and driver bring-up are out of scope
> as smmu-testdev is not a realistic PCIe Endpoint nor a platform device.
>
> Rationale for a dedicated path
> ------------------------------
> Using a generic PCI or virtio device would still require driver init and a
> richer bus model, undermining determinism for this focused purpose. This
> qtest, paired with smmu-testdev, keeps failures attributable to the SMMU
> translation path.
>
> Finally we document the smmu-testdev device in docs/specs.
>
> Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
> ---
> docs/specs/index.rst | 1 +
> docs/specs/smmu-testdev.rst | 45 ++++++
> tests/qtest/meson.build | 1 +
> tests/qtest/smmu-testdev-qtest.c | 238 +++++++++++++++++++++++++++++++
> 4 files changed, 285 insertions(+)
> create mode 100644 docs/specs/smmu-testdev.rst
> create mode 100644 tests/qtest/smmu-testdev-qtest.c
>
> diff --git a/docs/specs/index.rst b/docs/specs/index.rst
> index f19d73c9f6..47a18c48f1 100644
> --- a/docs/specs/index.rst
> +++ b/docs/specs/index.rst
> @@ -39,3 +39,4 @@ guest hardware that is specific to QEMU.
> riscv-iommu
> riscv-aia
> aspeed-intc
> + smmu-testdev
> \ No newline at end of file
> diff --git a/docs/specs/smmu-testdev.rst b/docs/specs/smmu-testdev.rst
> new file mode 100644
> index 0000000000..2599b46e4f
> --- /dev/null
> +++ b/docs/specs/smmu-testdev.rst
> @@ -0,0 +1,45 @@
> +smmu-testdev — Minimal SMMUv3 DMA test device
> +=============================================
> +
> +Overview
> +--------
> +``smmu-testdev`` is a tiny, test-only DMA source intended to exercise the
> +SMMUv3 emulation without booting firmware or a guest OS. It lets tests
> +populate STE/CD/PTE with known values and trigger a DMA that flows through
> +the SMMU translation path. It is **not** a faithful PCIe endpoint nor a
> +platform device and must be considered a QEMU-internal test vehicle.
> +
> +Status
> +------
> +* Location: ``hw/misc/smmu-testdev.c``
> +* Build guard: ``CONFIG_SMMU_TESTDEV``
> +* Default machines: none (tests instantiate it explicitly)
> +* Intended use: qtests under ``tests/qtest/smmu-testdev-qtest.c``
> +
> +Running the qtest
> +-----------------
> +The smoke test ships with this device and is the recommended entry point::
> +
> + QTEST_QEMU_BINARY=qemu-system-aarch64 ./tests/qtest/smmu-testdev-qtest
> + --tap -k
> +
> +This programs a minimal Non-Secure SMMU context, kicks a DMA, and verifies
> +translation + data integrity.
> +
> +Instantiation (advanced)
> +------------------------
> +The device is not wired into any board by default. For ad-hoc experiments,
> +tests (or developers) can create it dynamically via qtest or the QEMU
> +monitor. It exposes a single MMIO window that the test drives directly.
> +
> +Limitations
> +-----------
> +* Non-Secure bank only in this version; Secure SMMU tests are planned once
> + upstream Secure support lands.
> +* No PCIe discovery, MSI, ATS/PRI, or driver bring-up is modeled.
> +* The device is test-only; do not rely on it for machine realism.
> +
> +See also
> +--------
> +* ``tests/qtest/smmu-testdev-qtest.c`` — the companion smoke test
> +* SMMUv3 emulation and documentation under ``hw/arm/smmu*``
> diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
> index 669d07c06b..bcdb51e141 100644
> --- a/tests/qtest/meson.build
> +++ b/tests/qtest/meson.build
> @@ -263,6 +263,7 @@ qtests_aarch64 = \
> config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? ['tpm-tis-i2c-test'] : []) + \
> (config_all_devices.has_key('CONFIG_ASPEED_SOC') ? qtests_aspeed64 : []) + \
> (config_all_devices.has_key('CONFIG_NPCM8XX') ? qtests_npcm8xx : []) + \
> + (config_all_devices.has_key('CONFIG_SMMU_TESTDEV') ? ['smmu-testdev-qtest'] : []) + \
> qtests_cxl + \
> ['arm-cpu-features',
> 'numa-test',
> diff --git a/tests/qtest/smmu-testdev-qtest.c b/tests/qtest/smmu-testdev-qtest.c
> new file mode 100644
> index 0000000000..d89e45757b
> --- /dev/null
> +++ b/tests/qtest/smmu-testdev-qtest.c
> @@ -0,0 +1,238 @@
> +/*
> + * QTest for smmu-testdev
> + *
> + * This QTest file is used to test the smmu-testdev so that we can test SMMU
> + * without any guest kernel or firmware.
> + *
> + * Copyright (c) 2025 Phytium Technology
> + *
> + * Author:
> + * Tao Tang <tangtao1634@phytium.com.cn>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "libqtest.h"
> +#include "libqos/pci.h"
> +#include "libqos/generic-pcihost.h"
> +#include "hw/pci/pci_regs.h"
> +#include "hw/misc/smmu-testdev.h"
> +
> +#define VIRT_SMMU_BASE 0x0000000009050000ULL
> +#define DMA_LEN 0x20U
> +
> +static inline uint64_t smmu_bank_base(uint64_t base, SMMUTestDevSpace sp)
> +{
> + /* Map only the Non-Secure bank for now; future domains may offset. */
> + (void)sp;
> + return base;
> +}
> +
> +static uint32_t expected_dma_result(uint32_t mode,
> + SMMUTestDevSpace s1_space,
> + SMMUTestDevSpace s2_space)
> +{
> + (void)mode;
> + if (s1_space != STD_SPACE_NONSECURE || s2_space != STD_SPACE_NONSECURE) {
> + return STD_DMA_ERR_TX_FAIL;
> + }
> + return 0u;
> +}
> +
> +static void smmu_prog_bank(QTestState *qts, uint64_t B, SMMUTestDevSpace sp)
> +{
> + g_assert_cmpuint(sp, ==, STD_SPACE_NONSECURE);
> + /* Program minimal SMMUv3 state in a given control bank. */
> + qtest_writel(qts, B + 0x0044, 0x80000000); /* GBPA UPDATE */
> + qtest_writel(qts, B + 0x0020, 0x0); /* CR0 */
> + qtest_writel(qts, B + 0x0028, 0x0d75); /* CR1 */
> + {
> + /* CMDQ_BASE: add address-space offset (S/NS/Root/Realm). */
> + uint64_t v = 0x400000000e16b00aULL + std_space_offset(sp);
> + qtest_writeq(qts, B + 0x0090, v);
> + }
> + qtest_writel(qts, B + 0x009c, 0x0); /* CMDQ_CONS */
> + qtest_writel(qts, B + 0x0098, 0x0); /* CMDQ_PROD */
> + {
> + /* EVENTQ_BASE: add address-space offset (S/NS/Root/Realm). */
> + uint64_t v = 0x400000000e17000aULL + std_space_offset(sp);
> + qtest_writeq(qts, B + 0x00a0, v);
> + }
> + qtest_writel(qts, B + 0x00a8, 0x0); /* EVENTQ_PROD */
> + qtest_writel(qts, B + 0x00ac, 0x0); /* EVENTQ_CONS */
> + qtest_writel(qts, B + 0x0088, 0x5); /* STRTAB_BASE_CFG */
> + {
> + /* STRTAB_BASE: add address-space offset (S/NS/Root/Realm). */
> + uint64_t v = 0x400000000e179000ULL + std_space_offset(sp);
> + qtest_writeq(qts, B + 0x0080, v);
> + }
> + qtest_writel(qts, B + 0x003C, 0x1); /* INIT */
> + qtest_writel(qts, B + 0x0020, 0xD); /* CR0 */
> +}
> +
> +static void smmu_prog_minimal(QTestState *qts, SMMUTestDevSpace space)
> +{
> + /* Always program Non-Secure bank, then the requested space. */
> + uint64_t ns_base = smmu_bank_base(VIRT_SMMU_BASE, STD_SPACE_NONSECURE);
> + smmu_prog_bank(qts, ns_base, STD_SPACE_NONSECURE);
> +
> + uint64_t sp_base = smmu_bank_base(VIRT_SMMU_BASE, space);
> + if (sp_base != ns_base) {
> + smmu_prog_bank(qts, sp_base, space);
> + }
> +}
> +
> +static uint32_t poll_dma_result(QPCIDevice *dev, QPCIBar bar,
> + QTestState *qts)
> +{
> + /* Trigger side effects (DMA) via REG_ID read once. */
> + (void)qpci_io_readl(dev, bar, STD_REG_ID);
> +
> + /* Poll until not BUSY, then return the result. */
> + for (int i = 0; i < 1000; i++) {
> + uint32_t r = qpci_io_readl(dev, bar, STD_REG_DMA_RESULT);
> + if (r != STD_DMA_RESULT_BUSY) {
> + return r;
> + }
> + /* Small backoff to avoid busy spinning. */
> + g_usleep(1000);
> + }
> + /* Timeout treated as failure-like non-zero. */
> + return STD_DMA_RESULT_BUSY;
> +}
> +
> +static void test_mmio_access(void)
> +{
> + QTestState *qts;
> + QGenericPCIBus gbus;
> + QPCIDevice *dev;
> + QPCIBar bar;
> + uint8_t buf[DMA_LEN];
> + uint32_t attr_ns;
> + qts = qtest_init("-machine virt,acpi=off,gic-version=3,iommu=smmuv3 " \
> + "-display none -smp 1 -m 512 -cpu max -net none "
> + "-device smmu-testdev,device=0x0,function=0x1 ");
> +
> + qpci_init_generic(&gbus, qts, NULL, false);
> +
> + /* Find device by vendor/device ID to avoid slot surprises. */
> + dev = NULL;
might as well init when you declare.
> + for (int slot = 0; slot < 32 && !dev; slot++) {
> + for (int fn = 0; fn < 8 && !dev; fn++) {
> + QPCIDevice *cand = qpci_device_find(&gbus.bus,
> + QPCI_DEVFN(slot, fn));
> + if (!cand) {
> + continue;
> + }
> + uint16_t vid = qpci_config_readw(cand, PCI_VENDOR_ID);
> + uint16_t did = qpci_config_readw(cand, PCI_DEVICE_ID);
> + if (vid == 0x1b36 && did == 0x0005) {
> + dev = cand;
> + } else {
> + g_free(cand);
> + }
> + }
> + }
> + g_assert_nonnull(dev);
surely g_assert(dev) would do.
> +
> + qpci_device_enable(dev);
> + bar = qpci_iomap(dev, 0, NULL);
> + g_assert_false(bar.is_io);
> +
> + /* Baseline attribute reads. */
> + attr_ns = qpci_io_readl(dev, bar, STD_REG_ATTR_NS);
> + g_assert_cmpuint(attr_ns, ==, 0x2);
> +
> + /* Program SMMU base and DMA parameters. */
> + qpci_io_writel(dev, bar, STD_REG_SMMU_BASE_LO, (uint32_t)VIRT_SMMU_BASE);
> + qpci_io_writel(dev, bar, STD_REG_SMMU_BASE_HI,
> + (uint32_t)(VIRT_SMMU_BASE >> 32));
> + qpci_io_writel(dev, bar, STD_REG_DMA_IOVA_LO, (uint32_t)STD_IOVA);
> + qpci_io_writel(dev, bar, STD_REG_DMA_IOVA_HI,
> + (uint32_t)(STD_IOVA >> 32));
> + qpci_io_writel(dev, bar, STD_REG_DMA_LEN, DMA_LEN);
> + qpci_io_writel(dev, bar, STD_REG_DMA_DIR, 0); /* device -> host */
> +
> + qtest_memset(qts, STD_IOVA, 0x00, DMA_LEN);
> + qtest_memread(qts, STD_IOVA, buf, DMA_LEN);
> +
> + /* Refresh attrs via write to ensure legacy functionality still works. */
> + qpci_io_writel(dev, bar, STD_REG_ID, 0x1);
> + /*
> + * invoke translation builder for multiple
> + * stage/security-space combinations (readable/refactored).
> + */
> + const uint32_t modes[] = { 0u, 1u, 2u }; /* Stage1, Stage2, Nested stage */
> + const SMMUTestDevSpace spaces[] = { STD_SPACE_NONSECURE };
top of block.
> + /* Use attrs-DMA path for end-to-end */
> + qpci_io_writel(dev, bar, STD_REG_DMA_MODE, 1);
> + for (size_t mi = 0; mi < sizeof(modes) / sizeof(modes[0]); mi++) {
> + const SMMUTestDevSpace *s1_set = NULL;
> + size_t s1_count = 0;
> + const SMMUTestDevSpace *s2_set = NULL;
> + size_t s2_count = 0;
> +
> + switch (modes[mi]) {
> + case 0u:
> + case 1u:
> + case 2u:
> + s1_set = spaces;
> + s1_count = sizeof(spaces) / sizeof(spaces[0]);
> + s2_set = spaces;
> + s2_count = sizeof(spaces) / sizeof(spaces[0]);
> + break;
> + default:
> + g_assert_not_reached();
> + }
> +
> + for (size_t si = 0; si < s1_count; si++) {
> + for (size_t sj = 0; sj < s2_count; sj++) {
> + qpci_io_writel(dev, bar, STD_REG_TRANS_MODE, modes[mi]);
> + qpci_io_writel(dev, bar, STD_REG_S1_SPACE, s1_set[si]);
> + qpci_io_writel(dev, bar, STD_REG_S2_SPACE, s2_set[sj]);
> + qpci_io_writel(dev, bar, STD_REG_TRANS_DBELL, 0x2);
> + qpci_io_writel(dev, bar, STD_REG_TRANS_DBELL, 0x1);
> +
> + uint32_t st = qpci_io_readl(dev, bar,
> + STD_REG_TRANS_STATUS);
> + g_test_message("build: stage=%s s1=%s s2=%s status=0x%x",
> + std_mode_to_str(modes[mi]),
> + std_space_to_str(s1_set[si]),
> + std_space_to_str(s2_set[sj]), st);
> + /* Program SMMU registers in selected control bank. */
> + smmu_prog_minimal(qts, s1_set[si]);
> +
> + /* End-to-end DMA using tx_space per mode. */
> + SMMUTestDevSpace tx_space =
> + (modes[mi] == 0u) ? s1_set[si] : s2_set[sj];
> + uint32_t dma_attrs = ((uint32_t)tx_space << 1);
> + qpci_io_writel(dev, bar, STD_REG_DMA_ATTRS,
> + dma_attrs);
> + qpci_io_writel(dev, bar, STD_REG_DMA_DBELL, 1);
> + /* Wait for DMA completion and assert success. */
> + {
> + uint32_t dr = poll_dma_result(dev, bar, qts);
> + uint32_t exp = expected_dma_result(modes[mi],
> + spaces[si],
> + spaces[sj]);
> + g_assert_cmpuint(dr, ==, exp);
> + g_test_message("polling end. attrs=0x%x res=0x%x",
> + dma_attrs, dr);
> + }
> + /* Clear CD/STE/PTE built by the device for next round. */
> + qpci_io_writel(dev, bar, STD_REG_TRANS_CLEAR, 1);
> + g_test_message("clear cache end.");
> + }
> + }
> + }
I suspect this function could be broken up a bit as new tests are added
and functionality shared?
> +
> + qtest_quit(qts);
> +}
> +
> +int main(int argc, char **argv)
> +{
> + g_test_init(&argc, &argv, NULL);
> + qtest_add_func("/smmu-testdev/mmio", test_mmio_access);
> + return g_test_run();
> +}
--
Alex Bennée
Virtualisation Tech Lead @ Linaro
next prev parent reply other threads:[~2025-10-23 11:03 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-09-30 16:53 [RFC v2 0/2] hw/misc: Introduce a new SMMUv3 test framework tangtao1634
2025-09-30 16:53 ` [RFC v2 1/2] hw/misc/smmu-testdev: introduce minimal SMMUv3 test device tangtao1634
2025-10-23 10:31 ` Alex Bennée
2025-10-27 14:54 ` Tao Tang
2025-09-30 16:53 ` [RFC v2 2/2] tests/qtest: add SMMUv3 smoke test using smmu-testdev DMA source tangtao1634
2025-10-23 11:02 ` Alex Bennée [this message]
2025-10-27 15:26 ` Tao Tang
2025-10-27 15:52 ` Pierrick Bouvier
2025-10-27 17:07 ` Alex Bennée
2025-10-23 9:04 ` [RFC v2 0/2] hw/misc: Introduce a new SMMUv3 test framework Tao Tang
2025-10-23 10:06 ` Alex Bennée
2025-10-23 10:11 ` Peter Maydell
2025-10-27 13:32 ` Tao Tang
2025-10-27 14:25 ` Alex Bennée
2025-10-27 13:58 ` Peter Maydell
2025-10-28 14:05 ` Tao Tang
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87jz0lnan1.fsf@draig.linaro.org \
--to=alex.bennee@linaro.org \
--cc=chenbaozi@phytium.com.cn \
--cc=eric.auger@redhat.com \
--cc=farosas@suse.de \
--cc=jean-philippe@linaro.org \
--cc=lvivier@redhat.com \
--cc=pbonzini@redhat.com \
--cc=peter.maydell@linaro.org \
--cc=philmd@linaro.org \
--cc=pierrick.bouvier@linaro.org \
--cc=qemu-arm@nongnu.org \
--cc=qemu-devel@nongnu.org \
--cc=smostafa@google.com \
--cc=tangtao1634@phytium.com.cn \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.