/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES */ #ifndef __GENERIC_PT_KUNIT_IOMMU_H #define __GENERIC_PT_KUNIT_IOMMU_H #define GENERIC_PT_KUNIT 1 #include #include #include "../iommu-pages.h" #include "pt_iter.h" #define pt_iommu_table_cfg CONCATENATE(pt_iommu_table, _cfg) #define pt_iommu_init CONCATENATE(CONCATENATE(pt_iommu_, PTPFX), init) int pt_iommu_init(struct pt_iommu_table *fmt_table, const struct pt_iommu_table_cfg *cfg, gfp_t gfp); /* The format can provide a list of configurations it would like to test */ #ifdef kunit_fmt_cfgs static const void *kunit_pt_gen_params_cfg(struct kunit *test, const void *prev, char *desc) { uintptr_t cfg_id = (uintptr_t)prev; cfg_id++; if (cfg_id >= ARRAY_SIZE(kunit_fmt_cfgs) + 1) return NULL; snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s_cfg_%u", __stringify(PTPFX_RAW), (unsigned int)(cfg_id - 1)); return (void *)cfg_id; } #define KUNIT_CASE_FMT(test_name) \ KUNIT_CASE_PARAM(test_name, kunit_pt_gen_params_cfg) #else #define KUNIT_CASE_FMT(test_name) KUNIT_CASE(test_name) #endif #define KUNIT_ASSERT_NO_ERRNO(test, ret) \ KUNIT_ASSERT_EQ_MSG(test, ret, 0, KUNIT_SUBSUBTEST_INDENT "errno %pe", \ ERR_PTR(ret)) #define KUNIT_ASSERT_NO_ERRNO_FN(test, fn, ret) \ KUNIT_ASSERT_EQ_MSG(test, ret, 0, \ KUNIT_SUBSUBTEST_INDENT "errno %pe from %s", \ ERR_PTR(ret), fn) /* * When the test is run on a 32 bit system unsigned long can be 32 bits. This * cause the iommu op signatures to be restricted to 32 bits. Meaning the test * has to be mindful not to create any VA's over the 32 bit limit. Reduce the * scope of the testing as the main purpose of checking on full 32 bit is to * look for 32bitism in the core code. Run the test on i386 with X86_PAE=y to * get the full coverage when dma_addr_t & phys_addr_t are 8 bytes */ #define IS_32BIT (sizeof(unsigned long) == 4) struct kunit_iommu_priv { union { struct iommu_domain domain; struct pt_iommu_table fmt_table; }; spinlock_t top_lock; struct device *dummy_dev; struct pt_iommu *iommu; struct pt_common *common; struct pt_iommu_table_cfg cfg; struct pt_iommu_info info; unsigned int smallest_pgsz_lg2; pt_vaddr_t smallest_pgsz; unsigned int largest_pgsz_lg2; pt_oaddr_t test_oa; pt_vaddr_t safe_pgsize_bitmap; unsigned long orig_nr_secondary_pagetable; }; PT_IOMMU_CHECK_DOMAIN(struct kunit_iommu_priv, fmt_table.iommu, domain); static void pt_kunit_iotlb_sync(struct iommu_domain *domain, struct iommu_iotlb_gather *gather) { iommu_put_pages_list(&gather->freelist); } #define IOMMU_PT_DOMAIN_OPS1(x) IOMMU_PT_DOMAIN_OPS(x) static const struct iommu_domain_ops kunit_pt_ops = { IOMMU_PT_DOMAIN_OPS1(PTPFX_RAW), .iotlb_sync = &pt_kunit_iotlb_sync, }; static void pt_kunit_change_top(struct pt_iommu *iommu_table, phys_addr_t top_paddr, unsigned int top_level) { } static spinlock_t *pt_kunit_get_top_lock(struct pt_iommu *iommu_table) { struct kunit_iommu_priv *priv = container_of( iommu_table, struct kunit_iommu_priv, fmt_table.iommu); return &priv->top_lock; } static const struct pt_iommu_driver_ops pt_kunit_driver_ops = { .change_top = &pt_kunit_change_top, .get_top_lock = &pt_kunit_get_top_lock, }; static int pt_kunit_priv_init(struct kunit *test, struct kunit_iommu_priv *priv) { unsigned int va_lg2sz; int ret; /* Enough so the memory allocator works */ priv->dummy_dev = kunit_device_register(test, "pt_kunit_dev"); if (IS_ERR(priv->dummy_dev)) return PTR_ERR(priv->dummy_dev); set_dev_node(priv->dummy_dev, NUMA_NO_NODE); spin_lock_init(&priv->top_lock); #ifdef kunit_fmt_cfgs priv->cfg = kunit_fmt_cfgs[((uintptr_t)test->param_value) - 1]; /* * The format can set a list of features that the kunit_fmt_cfgs * controls, other features are default to on. */ priv->cfg.common.features |= PT_SUPPORTED_FEATURES & (~KUNIT_FMT_FEATURES); #else priv->cfg.common.features = PT_SUPPORTED_FEATURES; #endif /* Defaults, for the kunit */ if (!priv->cfg.common.hw_max_vasz_lg2) priv->cfg.common.hw_max_vasz_lg2 = PT_MAX_VA_ADDRESS_LG2; if (!priv->cfg.common.hw_max_oasz_lg2) priv->cfg.common.hw_max_oasz_lg2 = pt_max_oa_lg2(NULL); priv->fmt_table.iommu.nid = NUMA_NO_NODE; priv->fmt_table.iommu.driver_ops = &pt_kunit_driver_ops; priv->fmt_table.iommu.iommu_device = priv->dummy_dev; priv->domain.ops = &kunit_pt_ops; ret = pt_iommu_init(&priv->fmt_table, &priv->cfg, GFP_KERNEL); if (ret) { if (ret == -EOVERFLOW) kunit_skip( test, "This configuration cannot be tested on 32 bit"); return ret; } priv->iommu = &priv->fmt_table.iommu; priv->common = common_from_iommu(&priv->fmt_table.iommu); priv->iommu->ops->get_info(priv->iommu, &priv->info); /* * size_t is used to pass the mapping length, it can be 32 bit, truncate * the pagesizes so we don't use large sizes. */ priv->info.pgsize_bitmap = (size_t)priv->info.pgsize_bitmap; priv->smallest_pgsz_lg2 = vaffs(priv->info.pgsize_bitmap); priv->smallest_pgsz = log2_to_int(priv->smallest_pgsz_lg2); priv->largest_pgsz_lg2 = vafls((dma_addr_t)priv->info.pgsize_bitmap) - 1; priv->test_oa = oalog2_mod(0x74a71445deadbeef, priv->common->max_oasz_lg2); /* * We run out of VA space if the mappings get too big, make something * smaller that can safely pass through dma_addr_t API. */ va_lg2sz = priv->common->max_vasz_lg2; if (IS_32BIT && va_lg2sz > 32) va_lg2sz = 32; priv->safe_pgsize_bitmap = log2_mod(priv->info.pgsize_bitmap, va_lg2sz - 1); return 0; } #endif