| 1 | /* |
| 2 | * QTest testcase for intel-iommu |
| 3 | * |
| 4 | * Copyright (c) 2024 Intel, Inc. |
| 5 | * |
| 6 | * Author: Zhenzhong Duan <zhenzhong.duan@intel.com> |
| 7 | * |
| 8 | * SPDX-License-Identifier: GPL-2.0-or-later |
| 9 | */ |
| 10 | |
| 11 | #include "qemu/osdep.h" |
| 12 | #include "libqtest.h" |
| 13 | #include "hw/i386/intel_iommu_internal.h" |
| 14 | |
| 15 | #define CAP_STAGE_1_FIXED1 (VTD_CAP_FRO | VTD_CAP_NFR | VTD_CAP_ND | \ |
| 16 | VTD_CAP_MAMV | VTD_CAP_PSI | VTD_CAP_SSLPS) |
| 17 | #define ECAP_STAGE_1_FIXED1 (VTD_ECAP_QI | VTD_ECAP_IR | VTD_ECAP_IRO | \ |
| 18 | VTD_ECAP_MHMV | VTD_ECAP_SMTS | VTD_ECAP_FSTS) |
| 19 | |
| 20 | static inline uint32_t vtd_reg_readl(QTestState *s, uint64_t offset) |
| 21 | { |
| 22 | return qtest_readl(s, Q35_HOST_BRIDGE_IOMMU_ADDR + offset); |
| 23 | } |
| 24 | |
| 25 | static inline uint64_t vtd_reg_readq(QTestState *s, uint64_t offset) |
| 26 | { |
| 27 | return qtest_readq(s, Q35_HOST_BRIDGE_IOMMU_ADDR + offset); |
| 28 | } |
| 29 | |
| 30 | static inline void vtd_reg_writeq(QTestState *s, uint64_t offset, |
| 31 | uint64_t value) |
| 32 | { |
| 33 | qtest_writeq(s, Q35_HOST_BRIDGE_IOMMU_ADDR + offset, value); |
| 34 | } |
| 35 | |
| 36 | static void test_intel_iommu_8byte_access(void) |
| 37 | { |
| 38 | QTestState *s; |
| 39 | uint64_t off; |
| 40 | |
| 41 | s = qtest_init("-M q35 -device intel-iommu"); |
| 42 | |
| 43 | for (off = 0; off < DMAR_REG_SIZE; off += 4) { |
| 44 | vtd_reg_readq(s, off); |
| 45 | vtd_reg_writeq(s, off, 0); |
| 46 | } |
| 47 | |
| 48 | g_assert_cmpuint(vtd_reg_readl(s, DMAR_VER_REG), !=, 0); |
| 49 | |
| 50 | qtest_quit(s); |
| 51 | } |
| 52 | |
| 53 | static void test_intel_iommu_stage_1(void) |
| 54 | { |
| 55 | uint8_t init_csr[DMAR_REG_SIZE]; /* register values */ |
| 56 | uint8_t post_reset_csr[DMAR_REG_SIZE]; /* register values */ |
| 57 | uint64_t cap, ecap, tmp; |
| 58 | QTestState *s; |
| 59 | |
| 60 | s = qtest_init("-M q35 -device intel-iommu,scalable-mode=on,fsts=on"); |
| 61 | |
| 62 | cap = vtd_reg_readq(s, DMAR_CAP_REG); |
| 63 | g_assert((cap & CAP_STAGE_1_FIXED1) == CAP_STAGE_1_FIXED1); |
| 64 | |
| 65 | tmp = cap & VTD_CAP_SAGAW_MASK; |
| 66 | g_assert(tmp == (VTD_CAP_SAGAW_39bit | VTD_CAP_SAGAW_48bit)); |
| 67 | |
| 68 | tmp = VTD_MGAW_FROM_CAP(cap); |
| 69 | g_assert(tmp == VTD_HOST_AW_48BIT - 1); |
| 70 | |
| 71 | ecap = vtd_reg_readq(s, DMAR_ECAP_REG); |
| 72 | g_assert((ecap & ECAP_STAGE_1_FIXED1) == ECAP_STAGE_1_FIXED1); |
| 73 | |
| 74 | qtest_memread(s, Q35_HOST_BRIDGE_IOMMU_ADDR, init_csr, DMAR_REG_SIZE); |
| 75 | |
| 76 | qobject_unref(qtest_qmp(s, "{ 'execute': 'system_reset' }")); |
| 77 | qtest_qmp_eventwait(s, "RESET"); |
| 78 | |
| 79 | qtest_memread(s, Q35_HOST_BRIDGE_IOMMU_ADDR, post_reset_csr, DMAR_REG_SIZE); |
| 80 | /* Ensure registers are consistent after hard reset */ |
| 81 | g_assert(!memcmp(init_csr, post_reset_csr, DMAR_REG_SIZE)); |
| 82 | |
| 83 | qtest_quit(s); |
| 84 | } |
| 85 | |
| 86 | int main(int argc, char **argv) |
| 87 | { |
| 88 | g_test_init(&argc, &argv, NULL); |
| 89 | qtest_add_func("/q35/intel-iommu/8byte-access", |
| 90 | test_intel_iommu_8byte_access); |
| 91 | qtest_add_func("/q35/intel-iommu/stage-1", test_intel_iommu_stage_1); |
| 92 | |
| 93 | return g_test_run(); |
| 94 | } |