Browse Source
Add a qtest suite that validates ARM SMMUv3 translation without guest
firmware or OS. The tests leverage iommu-testdev to trigger DMA
operations and the qos-smmuv3 library to configure IOMMU translation
structures.
This test suite targets the virt machine and covers:
- Stage 1 only translation (VA -> PA via CD page tables)
- Stage 2 only translation (IPA -> PA via STE S2 tables)
- Nested translation (VA -> IPA -> PA, Stage 1 + Stage 2)
- Design to extended to support multiple security spaces
(Non-Secure, Secure, Root, Realm)
Each test case follows this sequence:
1. Initialize SMMUv3 with appropriate command/event queues
2. Build translation tables (STE/CD/PTE) for the target scenario
3. Configure iommu-testdev with IOVA and DMA attributes via MMIO
4. Trigger DMA and validate successful translation
5. Verify data integrity through a deterministic write-read pattern
This bare-metal approach provides deterministic IOMMU testing with
minimal dependencies, making failures directly attributable to the SMMU
translation path.
Signed-off-by: Tao Tang <tangtao1634@phytium.com.cn>
Tested-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
Message-ID: <20260119161112.3841386-9-tangtao1634@phytium.com.cn>
[PMD: Cover tests/qtest/iommu-smmuv3-test.c in MAINTAINERS]
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
pull/316/head
committed by
Philippe Mathieu-Daudé
3 changed files with 136 additions and 0 deletions
@ -0,0 +1,132 @@ |
|||
/*
|
|||
* QTest for SMMUv3 with iommu-testdev |
|||
* |
|||
* This QTest file is used to test the SMMUv3 with iommu-testdev so that we can |
|||
* test SMMUv3 without any guest kernel or firmware. |
|||
* |
|||
* Copyright (c) 2026 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/iommu-testdev.h" |
|||
#include "libqos/qos-smmuv3.h" |
|||
|
|||
#define DMA_LEN 4 |
|||
|
|||
static uint64_t smmuv3_expected_gpa(uint64_t iova) |
|||
{ |
|||
return qsmmu_space_offset(QSMMU_SPACE_NONSECURE) + |
|||
QSMMU_L3_PTE_VAL + (iova & 0xfff); |
|||
} |
|||
|
|||
static void save_fn(QPCIDevice *dev, int devfn, void *data) |
|||
{ |
|||
QPCIDevice **pdev = (QPCIDevice **) data; |
|||
|
|||
*pdev = dev; |
|||
} |
|||
|
|||
static QPCIDevice *setup_qtest_pci_device(QTestState *qts, QGenericPCIBus *gbus, |
|||
QPCIBar *bar) |
|||
{ |
|||
QPCIDevice *dev = NULL; |
|||
|
|||
qpci_init_generic(gbus, qts, NULL, false); |
|||
|
|||
qpci_device_foreach(&gbus->bus, IOMMU_TESTDEV_VENDOR_ID, |
|||
IOMMU_TESTDEV_DEVICE_ID, save_fn, &dev); |
|||
g_assert(dev); |
|||
|
|||
qpci_device_enable(dev); |
|||
*bar = qpci_iomap(dev, 0, NULL); |
|||
g_assert_false(bar->is_io); |
|||
|
|||
return dev; |
|||
} |
|||
|
|||
static void run_smmuv3_translation(const QSMMUTestConfig *cfg) |
|||
{ |
|||
QTestState *qts; |
|||
QGenericPCIBus gbus; |
|||
QPCIDevice *dev; |
|||
QPCIBar bar; |
|||
|
|||
if (!qtest_has_machine("virt")) { |
|||
g_test_skip("virt machine not available"); |
|||
return; |
|||
} |
|||
|
|||
/* Initialize QEMU environment for SMMU testing */ |
|||
qts = qtest_init("-machine virt,acpi=off,gic-version=3,iommu=smmuv3 " |
|||
"-smp 1 -m 512 -cpu max -net none " |
|||
"-device iommu-testdev"); |
|||
|
|||
/* Setup and configure PCI device */ |
|||
dev = setup_qtest_pci_device(qts, &gbus, &bar); |
|||
g_assert(dev); |
|||
|
|||
g_test_message("### SMMUv3 translation mode=%d sec_sid=%d ###", |
|||
cfg->trans_mode, cfg->sec_sid); |
|||
qsmmu_run_translation_case(qts, dev, bar, VIRT_SMMU_BASE, cfg); |
|||
qtest_quit(qts); |
|||
} |
|||
|
|||
static void test_smmuv3_ns_s1_only(void) |
|||
{ |
|||
QSMMUTestConfig cfg = { |
|||
.trans_mode = QSMMU_TM_S1_ONLY, |
|||
.sec_sid = QSMMU_SEC_SID_NONSECURE, |
|||
.dma_gpa = smmuv3_expected_gpa(QSMMU_IOVA), |
|||
.dma_len = DMA_LEN, |
|||
.expected_result = 0, |
|||
}; |
|||
|
|||
run_smmuv3_translation(&cfg); |
|||
} |
|||
|
|||
static void test_smmuv3_ns_s2_only(void) |
|||
{ |
|||
QSMMUTestConfig cfg = { |
|||
.trans_mode = QSMMU_TM_S2_ONLY, |
|||
.sec_sid = QSMMU_SEC_SID_NONSECURE, |
|||
.dma_gpa = smmuv3_expected_gpa(QSMMU_IOVA), |
|||
.dma_len = DMA_LEN, |
|||
.expected_result = 0, |
|||
}; |
|||
|
|||
run_smmuv3_translation(&cfg); |
|||
} |
|||
|
|||
static void test_smmuv3_ns_nested(void) |
|||
{ |
|||
QSMMUTestConfig cfg = { |
|||
.trans_mode = QSMMU_TM_NESTED, |
|||
.sec_sid = QSMMU_SEC_SID_NONSECURE, |
|||
.dma_gpa = smmuv3_expected_gpa(QSMMU_IOVA), |
|||
.dma_len = DMA_LEN, |
|||
.expected_result = 0, |
|||
}; |
|||
|
|||
run_smmuv3_translation(&cfg); |
|||
} |
|||
|
|||
int main(int argc, char **argv) |
|||
{ |
|||
g_test_init(&argc, &argv, NULL); |
|||
qtest_add_func("/iommu-testdev/translation/ns-s1-only", |
|||
test_smmuv3_ns_s1_only); |
|||
qtest_add_func("/iommu-testdev/translation/ns-s2-only", |
|||
test_smmuv3_ns_s2_only); |
|||
qtest_add_func("/iommu-testdev/translation/ns-nested", |
|||
test_smmuv3_ns_nested); |
|||
return g_test_run(); |
|||
} |
|||
Loading…
Reference in new issue