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
19 static struct io_pgtable_cfg *cfg_cookie;
20
dummy_tlb_flush_all(void * cookie)21 static void dummy_tlb_flush_all(void *cookie)
22 {
23 WARN_ON(cookie != cfg_cookie);
24 }
25
dummy_tlb_flush(unsigned long iova,size_t size,size_t granule,void * cookie)26 static 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
dummy_tlb_add_page(struct iommu_iotlb_gather * gather,unsigned long iova,size_t granule,void * cookie)33 static 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
40 static 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
arm_lpae_run_tests(struct kunit * test,struct io_pgtable_cfg * cfg)51 static 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
arm_lpae_do_selftests(struct kunit * test)154 static 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
201 static struct kunit_case io_pgtable_arm_test_cases[] = {
202 KUNIT_CASE(arm_lpae_do_selftests),
203 {},
204 };
205
206 static struct kunit_suite io_pgtable_arm_test = {
207 .name = "io-pgtable-arm-test",
208 .test_cases = io_pgtable_arm_test_cases,
209 };
210
211 kunit_test_suite(io_pgtable_arm_test);
212
213 MODULE_DESCRIPTION("io-pgtable-arm library kunit tests");
214 MODULE_LICENSE("GPL");
215