File Index
Files and directories
Sign in
FILE

io-pgtable-arm-selftests.c

File preview with syntax highlighting.

linux-6.19.14/drivers/iommu/io-pgtable-arm-selftests.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * CPU-agnostic ARM page table allocator.
  4 *
  5 * Copyright (C) 2014 ARM Limited
  6 *
  7 * Author: Will Deacon <will.deacon@arm.com>
  8 */
  9
 10#define pr_fmt(fmt)	"arm-lpae io-pgtable: " fmt
 11
 12#include <kunit/device.h>
 13#include <kunit/test.h>
 14#include <linux/io-pgtable.h>
 15#include <linux/kernel.h>
 16
 17#include "io-pgtable-arm.h"
 18
 19static struct io_pgtable_cfg *cfg_cookie;
 20
 21static void dummy_tlb_flush_all(void *cookie)
 22{
 23	WARN_ON(cookie != cfg_cookie);
 24}
 25
 26static void dummy_tlb_flush(unsigned long iova, size_t size,
 27			    size_t granule, void *cookie)
 28{
 29	WARN_ON(cookie != cfg_cookie);
 30	WARN_ON(!(size & cfg_cookie->pgsize_bitmap));
 31}
 32
 33static void dummy_tlb_add_page(struct iommu_iotlb_gather *gather,
 34			       unsigned long iova, size_t granule,
 35			       void *cookie)
 36{
 37	dummy_tlb_flush(iova, granule, granule, cookie);
 38}
 39
 40static const struct iommu_flush_ops dummy_tlb_ops = {
 41	.tlb_flush_all	= dummy_tlb_flush_all,
 42	.tlb_flush_walk	= dummy_tlb_flush,
 43	.tlb_add_page	= dummy_tlb_add_page,
 44};
 45
 46#define __FAIL(test, i) ({							\
 47		KUNIT_FAIL(test, "test failed for fmt idx %d\n", (i));		\
 48		-EFAULT;							\
 49})
 50
 51static int arm_lpae_run_tests(struct kunit *test, struct io_pgtable_cfg *cfg)
 52{
 53	static const enum io_pgtable_fmt fmts[] = {
 54		ARM_64_LPAE_S1,
 55		ARM_64_LPAE_S2,
 56	};
 57
 58	int i, j;
 59	unsigned long iova;
 60	size_t size, mapped;
 61	struct io_pgtable_ops *ops;
 62
 63	for (i = 0; i < ARRAY_SIZE(fmts); ++i) {
 64		cfg_cookie = cfg;
 65		ops = alloc_io_pgtable_ops(fmts[i], cfg, cfg);
 66		if (!ops) {
 67			kunit_err(test, "failed to allocate io pgtable ops\n");
 68			return -ENOMEM;
 69		}
 70
 71		/*
 72		 * Initial sanity checks.
 73		 * Empty page tables shouldn't provide any translations.
 74		 */
 75		if (ops->iova_to_phys(ops, 42))
 76			return __FAIL(test, i);
 77
 78		if (ops->iova_to_phys(ops, SZ_1G + 42))
 79			return __FAIL(test, i);
 80
 81		if (ops->iova_to_phys(ops, SZ_2G + 42))
 82			return __FAIL(test, i);
 83
 84		/*
 85		 * Distinct mappings of different granule sizes.
 86		 */
 87		iova = 0;
 88		for_each_set_bit(j, &cfg->pgsize_bitmap, BITS_PER_LONG) {
 89			size = 1UL << j;
 90
 91			if (ops->map_pages(ops, iova, iova, size, 1,
 92					   IOMMU_READ | IOMMU_WRITE |
 93					   IOMMU_NOEXEC | IOMMU_CACHE,
 94					   GFP_KERNEL, &mapped))
 95				return __FAIL(test, i);
 96
 97			/* Overlapping mappings */
 98			if (!ops->map_pages(ops, iova, iova + size, size, 1,
 99					    IOMMU_READ | IOMMU_NOEXEC,
100					    GFP_KERNEL, &mapped))
101				return __FAIL(test, i);
102
103			if (ops->iova_to_phys(ops, iova + 42) != (iova + 42))
104				return __FAIL(test, i);
105
106			iova += SZ_1G;
107		}
108
109		/* Full unmap */
110		iova = 0;
111		for_each_set_bit(j, &cfg->pgsize_bitmap, BITS_PER_LONG) {
112			size = 1UL << j;
113
114			if (ops->unmap_pages(ops, iova, size, 1, NULL) != size)
115				return __FAIL(test, i);
116
117			if (ops->iova_to_phys(ops, iova + 42))
118				return __FAIL(test, i);
119
120			/* Remap full block */
121			if (ops->map_pages(ops, iova, iova, size, 1,
122					   IOMMU_WRITE, GFP_KERNEL, &mapped))
123				return __FAIL(test, i);
124
125			if (ops->iova_to_phys(ops, iova + 42) != (iova + 42))
126				return __FAIL(test, i);
127
128			iova += SZ_1G;
129		}
130
131		/*
132		 * Map/unmap the last largest supported page of the IAS, this can
133		 * trigger corner cases in the concatednated page tables.
134		 */
135		mapped = 0;
136		size = 1UL << __fls(cfg->pgsize_bitmap);
137		iova = (1UL << cfg->ias) - size;
138		if (ops->map_pages(ops, iova, iova, size, 1,
139				   IOMMU_READ | IOMMU_WRITE |
140				   IOMMU_NOEXEC | IOMMU_CACHE,
141				   GFP_KERNEL, &mapped))
142			return __FAIL(test, i);
143		if (mapped != size)
144			return __FAIL(test, i);
145		if (ops->unmap_pages(ops, iova, size, 1, NULL) != size)
146			return __FAIL(test, i);
147
148		free_io_pgtable_ops(ops);
149	}
150
151	return 0;
152}
153
154static void arm_lpae_do_selftests(struct kunit *test)
155{
156	static const unsigned long pgsize[] = {
157		SZ_4K | SZ_2M | SZ_1G,
158		SZ_16K | SZ_32M,
159		SZ_64K | SZ_512M,
160	};
161
162	static const unsigned int address_size[] = {
163		32, 36, 40, 42, 44, 48,
164	};
165
166	int i, j, k, pass = 0, fail = 0;
167	struct device *dev;
168	struct io_pgtable_cfg cfg = {
169		.tlb = &dummy_tlb_ops,
170		.coherent_walk = true,
171		.quirks = IO_PGTABLE_QUIRK_NO_WARN,
172	};
173
174	dev = kunit_device_register(test, "io-pgtable-test");
175	KUNIT_EXPECT_NOT_ERR_OR_NULL(test, dev);
176	if (IS_ERR_OR_NULL(dev))
177		return;
178
179	cfg.iommu_dev = dev;
180
181	for (i = 0; i < ARRAY_SIZE(pgsize); ++i) {
182		for (j = 0; j < ARRAY_SIZE(address_size); ++j) {
183			/* Don't use ias > oas as it is not valid for stage-2. */
184			for (k = 0; k <= j; ++k) {
185				cfg.pgsize_bitmap = pgsize[i];
186				cfg.ias = address_size[k];
187				cfg.oas = address_size[j];
188				kunit_info(test, "pgsize_bitmap 0x%08lx, IAS %u OAS %u\n",
189					   pgsize[i], cfg.ias, cfg.oas);
190				if (arm_lpae_run_tests(test, &cfg))
191					fail++;
192				else
193					pass++;
194			}
195		}
196	}
197
198	kunit_info(test, "completed with %d PASS %d FAIL\n", pass, fail);
199}
200
201static struct kunit_case io_pgtable_arm_test_cases[] = {
202	KUNIT_CASE(arm_lpae_do_selftests),
203	{},
204};
205
206static struct kunit_suite io_pgtable_arm_test = {
207	.name = "io-pgtable-arm-test",
208	.test_cases = io_pgtable_arm_test_cases,
209};
210
211kunit_test_suite(io_pgtable_arm_test);
212
213MODULE_DESCRIPTION("io-pgtable-arm library kunit tests");
214MODULE_LICENSE("GPL");
~/ Read-only file preview.