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