| 1 | /* |
| 2 | * QTest for SMMUv3 with iommu-testdev |
| 3 | * |
| 4 | * This QTest file is used to test the SMMUv3 with iommu-testdev so that we can |
| 5 | * test SMMUv3 without any guest kernel or firmware. |
| 6 | * |
| 7 | * Copyright (c) 2026 Phytium Technology |
| 8 | * |
| 9 | * Author: |
| 10 | * Tao Tang <tangtao1634@phytium.com.cn> |
| 11 | * |
| 12 | * SPDX-License-Identifier: GPL-2.0-or-later |
| 13 | */ |
| 14 | |
| 15 | #include "qemu/osdep.h" |
| 16 | #include "libqtest.h" |
| 17 | #include "libqos/pci.h" |
| 18 | #include "libqos/generic-pcihost.h" |
| 19 | #include "hw/pci/pci_regs.h" |
| 20 | #include "hw/misc/iommu-testdev.h" |
| 21 | #include "libqos/qos-smmuv3.h" |
| 22 | |
| 23 | #define DMA_LEN 4 |
| 24 | |
| 25 | static uint64_t smmuv3_expected_gpa(uint64_t iova) |
| 26 | { |
| 27 | return qsmmu_space_offset(QSMMU_SPACE_NONSECURE) + |
| 28 | QSMMU_L3_PTE_VAL + (iova & 0xfff); |
| 29 | } |
| 30 | |
| 31 | static void save_fn(QPCIDevice *dev, int devfn, void *data) |
| 32 | { |
| 33 | QPCIDevice **pdev = (QPCIDevice **) data; |
| 34 | |
| 35 | *pdev = dev; |
| 36 | } |
| 37 | |
| 38 | static QPCIDevice *setup_qtest_pci_device(QTestState *qts, QGenericPCIBus *gbus, |
| 39 | QPCIBar *bar) |
| 40 | { |
| 41 | QPCIDevice *dev = NULL; |
| 42 | |
| 43 | qpci_init_generic(gbus, qts, NULL, false); |
| 44 | |
| 45 | qpci_device_foreach(&gbus->bus, IOMMU_TESTDEV_VENDOR_ID, |
| 46 | IOMMU_TESTDEV_DEVICE_ID, save_fn, &dev); |
| 47 | g_assert(dev); |
| 48 | |
| 49 | qpci_device_enable(dev); |
| 50 | *bar = qpci_iomap(dev, 0, NULL); |
| 51 | g_assert_false(bar->is_io); |
| 52 | |
| 53 | return dev; |
| 54 | } |
| 55 | |
| 56 | static void run_smmuv3_translation(const QSMMUTestConfig *cfg) |
| 57 | { |
| 58 | QTestState *qts; |
| 59 | QGenericPCIBus gbus; |
| 60 | QPCIDevice *dev; |
| 61 | QPCIBar bar; |
| 62 | |
| 63 | if (!qtest_has_machine("virt")) { |
| 64 | g_test_skip("virt machine not available"); |
| 65 | return; |
| 66 | } |
| 67 | if (!qtest_has_device("arm-gicv3")) { |
| 68 | /* This can happen for a KVM-only build: qtest uses the TCG GICv3 */ |
| 69 | g_test_skip("gicv3 not available"); |
| 70 | return; |
| 71 | } |
| 72 | |
| 73 | /* Initialize QEMU environment for SMMU testing */ |
| 74 | qts = qtest_init("-machine virt,acpi=off,gic-version=3,iommu=smmuv3 " |
| 75 | "-smp 1 -m 512 -cpu max -net none " |
| 76 | "-device iommu-testdev"); |
| 77 | |
| 78 | /* Setup and configure PCI device */ |
| 79 | dev = setup_qtest_pci_device(qts, &gbus, &bar); |
| 80 | g_assert(dev); |
| 81 | |
| 82 | g_test_message("### SMMUv3 translation mode=%d sec_sid=%d ###", |
| 83 | cfg->trans_mode, cfg->sec_sid); |
| 84 | qsmmu_run_translation_case(qts, dev, bar, VIRT_SMMU_BASE, cfg); |
| 85 | g_free(dev); |
| 86 | qtest_quit(qts); |
| 87 | } |
| 88 | |
| 89 | static void test_smmuv3_ns_s1_only(void) |
| 90 | { |
| 91 | QSMMUTestConfig cfg = { |
| 92 | .trans_mode = QSMMU_TM_S1_ONLY, |
| 93 | .sec_sid = QSMMU_SEC_SID_NONSECURE, |
| 94 | .dma_gpa = smmuv3_expected_gpa(QSMMU_IOVA), |
| 95 | .dma_len = DMA_LEN, |
| 96 | .expected_result = 0, |
| 97 | }; |
| 98 | |
| 99 | run_smmuv3_translation(&cfg); |
| 100 | } |
| 101 | |
| 102 | static void test_smmuv3_ns_s2_only(void) |
| 103 | { |
| 104 | QSMMUTestConfig cfg = { |
| 105 | .trans_mode = QSMMU_TM_S2_ONLY, |
| 106 | .sec_sid = QSMMU_SEC_SID_NONSECURE, |
| 107 | .dma_gpa = smmuv3_expected_gpa(QSMMU_IOVA), |
| 108 | .dma_len = DMA_LEN, |
| 109 | .expected_result = 0, |
| 110 | }; |
| 111 | |
| 112 | run_smmuv3_translation(&cfg); |
| 113 | } |
| 114 | |
| 115 | static void test_smmuv3_ns_nested(void) |
| 116 | { |
| 117 | QSMMUTestConfig cfg = { |
| 118 | .trans_mode = QSMMU_TM_NESTED, |
| 119 | .sec_sid = QSMMU_SEC_SID_NONSECURE, |
| 120 | .dma_gpa = smmuv3_expected_gpa(QSMMU_IOVA), |
| 121 | .dma_len = DMA_LEN, |
| 122 | .expected_result = 0, |
| 123 | }; |
| 124 | |
| 125 | run_smmuv3_translation(&cfg); |
| 126 | } |
| 127 | |
| 128 | int main(int argc, char **argv) |
| 129 | { |
| 130 | g_test_init(&argc, &argv, NULL); |
| 131 | qtest_add_func("/iommu-testdev/translation/ns-s1-only", |
| 132 | test_smmuv3_ns_s1_only); |
| 133 | qtest_add_func("/iommu-testdev/translation/ns-s2-only", |
| 134 | test_smmuv3_ns_s2_only); |
| 135 | qtest_add_func("/iommu-testdev/translation/ns-nested", |
| 136 | test_smmuv3_ns_nested); |
| 137 | return g_test_run(); |
| 138 | } |