• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2021 - Google LLC
4  * Author: David Brazdil <dbrazdil@google.com>
5  */
6 
7 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
8 
9 #include "../../../tools/testing/selftests/kselftest_module.h"
10 
11 #include <linux/slab.h>
12 
13 #include <asm/kvm_s2mpu.h>
14 
15 KSTM_MODULE_GLOBALS();
16 
17 #define ASSERT(cond)							\
18 	do {								\
19 		if (!(cond)) {						\
20 			pr_err("line %d: assertion failed: %s\n",	\
21 			       __LINE__, #cond);			\
22 			return -1;					\
23 		}							\
24 	} while (0)
25 
26 static struct fmpt g_fmpt;
27 static u32 g_smpt[SMPT_NUM_WORDS];
28 
init_smpt(enum mpt_prot prot)29 static void __init init_smpt(enum mpt_prot prot)
30 {
31 	memset(g_smpt, (char)mpt_prot_doubleword[prot], SMPT_SIZE);
32 }
33 
init_fmpt(enum mpt_prot prot,bool gran_1g)34 static void __init init_fmpt(enum mpt_prot prot, bool gran_1g)
35 {
36 	init_smpt(prot);
37 	g_fmpt = (struct fmpt){
38 		.gran_1g = gran_1g,
39 		.prot = prot,
40 		.smpt = g_smpt,
41 	};
42 }
43 
get_prot_at(size_t gb_byte_off)44 static enum mpt_prot __init get_prot_at(size_t gb_byte_off)
45 {
46 	size_t page_idx = gb_byte_off / SMPT_GRAN;
47 	size_t word_idx = page_idx / SMPT_ELEMS_PER_WORD;
48 	size_t bit_shift = (page_idx % SMPT_ELEMS_PER_WORD) * MPT_PROT_BITS;
49 
50 	return (g_smpt[word_idx] >> bit_shift) & MPT_PROT_MASK;
51 }
52 
check_smpt(size_t start_byte,size_t end_byte,enum mpt_prot prot_out,enum mpt_prot prot_in)53 static bool __init check_smpt(size_t start_byte, size_t end_byte,
54 			      enum mpt_prot prot_out, enum mpt_prot prot_in)
55 {
56 	size_t off;
57 
58 	for (off = 0; off < start_byte; off += PAGE_SIZE) {
59 		if (get_prot_at(off) != prot_out)
60 			return false;
61 	}
62 	for (off = start_byte; off < end_byte; off += PAGE_SIZE) {
63 		if (get_prot_at(off) != prot_in)
64 			return false;
65 	}
66 	for (off = end_byte; off < SZ_1G; off += PAGE_SIZE) {
67 		if (get_prot_at(off) != prot_out)
68 			return false;
69 	}
70 	return true;
71 }
72 
73 /* Start with 1G granule, overwrite the whole 1G. */
test_set_fmpt__fmpt_to_fmpt_whole(void)74 static int __init test_set_fmpt__fmpt_to_fmpt_whole(void)
75 {
76 	init_fmpt(MPT_PROT_NONE, /*gran_1g*/ true);
77 	__set_fmpt_range(&g_fmpt, 0, SZ_1G, MPT_PROT_R);
78 	ASSERT(g_fmpt.flags == MPT_UPDATE_L1);
79 	ASSERT(g_fmpt.gran_1g);
80 	ASSERT(g_fmpt.prot == MPT_PROT_R);
81 	return 0;
82 }
83 
84 /* Start with 1G granule, overwrite the whole 1G with the same prot. */
test_set_fmpt__fmpt_no_change_whole(void)85 static int __init test_set_fmpt__fmpt_no_change_whole(void)
86 {
87 	init_fmpt(MPT_PROT_R, /*gran_1g*/ true);
88 	__set_fmpt_range(&g_fmpt, 0, SZ_1G, MPT_PROT_R);
89 	ASSERT(g_fmpt.flags == 0);
90 	ASSERT(g_fmpt.gran_1g);
91 	ASSERT(g_fmpt.prot == MPT_PROT_R);
92 	return 0;
93 }
94 
95 /* Start with 1G granule, partially overwrite with the same prot. */
test_set_fmpt__fmpt_no_change_partial(void)96 static int __init test_set_fmpt__fmpt_no_change_partial(void)
97 {
98 	init_fmpt(MPT_PROT_R, /*gran_1g*/ true);
99 	__set_fmpt_range(&g_fmpt, 0, PAGE_SIZE, MPT_PROT_R);
100 	ASSERT(g_fmpt.flags == 0);
101 	ASSERT(g_fmpt.gran_1g);
102 	ASSERT(g_fmpt.prot == MPT_PROT_R);
103 	return 0;
104 }
105 
106 /* Convert from 1G to PAGE_SIZE granule. */
test_set_fmpt__fmpt_to_smpt(void)107 static int __init test_set_fmpt__fmpt_to_smpt(void)
108 {
109 	size_t start = 5 * SMPT_WORD_BYTE_RANGE / 2;
110 	size_t end = 20 * SMPT_WORD_BYTE_RANGE;
111 
112 	init_fmpt(MPT_PROT_R, /*gran_1g*/ true);
113 	__set_fmpt_range(&g_fmpt, start, end, MPT_PROT_RW);
114 	ASSERT(g_fmpt.flags == (MPT_UPDATE_L1 | MPT_UPDATE_L2));
115 	ASSERT(!g_fmpt.gran_1g);
116 	return check_smpt(start, end, MPT_PROT_R, MPT_PROT_RW) ? 0 : 1;
117 }
118 
119 /* Convert from PAGE_SIZE to 1G granule by overwriting the whole 1G. */
test_set_fmpt__smpt_to_fmpt_whole(void)120 static int __init test_set_fmpt__smpt_to_fmpt_whole(void)
121 {
122 	init_fmpt(MPT_PROT_NONE, /*gran_1g*/ false);
123 	__set_fmpt_range(&g_fmpt, 0, SZ_1G, MPT_PROT_R);
124 	ASSERT(g_fmpt.flags == MPT_UPDATE_L1);
125 	ASSERT(g_fmpt.gran_1g);
126 	ASSERT(g_fmpt.prot == MPT_PROT_R);
127 	return 0;
128 }
129 
130 /* Convert from PAGE_SIZE to 1G granule by making the SMPT uniform. */
test_set_fmpt__smpt_to_fmpt_partial(void)131 static int __init test_set_fmpt__smpt_to_fmpt_partial(void)
132 {
133 	size_t start = 5 * SMPT_WORD_BYTE_RANGE / 2;
134 	size_t end = 20 * SMPT_WORD_BYTE_RANGE;
135 
136 	/* Create SMPT with all PROT_W except a small subrange. */
137 	init_fmpt(MPT_PROT_W, /*gran_1g*/ false);
138 	__set_smpt_range(g_smpt, start, end, MPT_PROT_RW);
139 
140 	/* Fill the subrange with PROT_W to make the SMPT uniform. */
141 	__set_fmpt_range(&g_fmpt, start, end, MPT_PROT_W);
142 	ASSERT(g_fmpt.flags == MPT_UPDATE_L1);
143 	ASSERT(g_fmpt.gran_1g);
144 	ASSERT(g_fmpt.prot == MPT_PROT_W);
145 	return 0;
146 }
147 
148 /* Keep PAGE_SIZE granule when SMPT not uniform after update. */
test_set_fmpt__smpt_to_smpt(void)149 static int __init test_set_fmpt__smpt_to_smpt(void)
150 {
151 	size_t start = SZ_1G - SMPT_GRAN;
152 	size_t end = SZ_1G;
153 
154 	init_fmpt(MPT_PROT_NONE, /*gran_1g*/ false);
155 	ASSERT(__is_smpt_uniform(g_smpt, MPT_PROT_NONE));
156 
157 	/* Fill the subrange with PROT_W to make the SMPT uniform. */
158 	__set_fmpt_range(&g_fmpt, start, end, MPT_PROT_RW);
159 	ASSERT(g_fmpt.flags == MPT_UPDATE_L2);
160 	ASSERT(!g_fmpt.gran_1g);
161 	ASSERT(!__is_smpt_uniform(g_smpt, MPT_PROT_NONE));
162 	return 0;
163 }
164 
__test_set_smpt(size_t start_byte,size_t end_byte)165 static int __init __test_set_smpt(size_t start_byte, size_t end_byte)
166 {
167 	init_smpt(MPT_PROT_NONE);
168 	__set_smpt_range(g_smpt, start_byte, end_byte, MPT_PROT_W);
169 	return check_smpt(start_byte, end_byte, MPT_PROT_NONE, MPT_PROT_W) ? 0 : 1;
170 }
171 
172 /* Range within one SMPT word, force a fallback to __set_smpt_range_slow. */
test_set_smpt__within_one_word(void)173 static int __init test_set_smpt__within_one_word(void)
174 {
175 	return __test_set_smpt(3 * SMPT_WORD_BYTE_RANGE + 5 * PAGE_SIZE,
176 			       3 * SMPT_WORD_BYTE_RANGE + 6 * PAGE_SIZE);
177 }
178 
179 /* No whole SMPT word, force a fallback to __set_smpt_range_slow. */
test_set_smpt__no_whole_word(void)180 static int __init test_set_smpt__no_whole_word(void)
181 {
182 	return __test_set_smpt(3 * SMPT_WORD_BYTE_RANGE + 5 * PAGE_SIZE,
183 			       4 * SMPT_WORD_BYTE_RANGE + 2 * PAGE_SIZE);
184 }
185 
186 /* Both start and end aligned to SMPT word. */
test_set_smpt__no_prologue_or_epilogue(void)187 static int __init test_set_smpt__no_prologue_or_epilogue(void)
188 {
189 	return __test_set_smpt(10 * SMPT_WORD_BYTE_RANGE,
190 			       20 * SMPT_WORD_BYTE_RANGE);
191 }
192 
193 /* Start not aligned to SMPT word. */
test_set_smpt__prologue(void)194 static int __init test_set_smpt__prologue(void)
195 {
196 	return __test_set_smpt(17 * SMPT_WORD_BYTE_RANGE / 2,
197 			       20 * SMPT_WORD_BYTE_RANGE);
198 }
199 
200 /* End not aligned to SMPT word. */
test_set_smpt__epilogue(void)201 static int __init test_set_smpt__epilogue(void)
202 {
203 	return __test_set_smpt(0, 17 * SMPT_WORD_BYTE_RANGE / 2);
204 }
205 
206 /* Neither start nor end aligned to SMPT word. */
test_set_smpt__prologue_and_epilogue(void)207 static int __init test_set_smpt__prologue_and_epilogue(void)
208 {
209 	return __test_set_smpt(17 * SMPT_WORD_BYTE_RANGE / 2,
210 			       31 * SMPT_WORD_BYTE_RANGE / 2);
211 }
212 
__test_set_smpt_slow(size_t start_byte,size_t end_byte)213 static int __init __test_set_smpt_slow(size_t start_byte, size_t end_byte)
214 {
215 	init_smpt(MPT_PROT_NONE);
216 	__set_smpt_range_slow(g_smpt, start_byte, end_byte, MPT_PROT_RW);
217 	return check_smpt(start_byte, end_byte, MPT_PROT_NONE, MPT_PROT_RW) ? 0 : 1;
218 }
219 
test_set_smpt_slow__empty_word_align(void)220 static int __init test_set_smpt_slow__empty_word_align(void)
221 {
222 	return __test_set_smpt_slow(3 * SMPT_WORD_BYTE_RANGE,
223 				    3 * SMPT_WORD_BYTE_RANGE);
224 }
225 
test_set_smpt_slow__empty_page_align(void)226 static int __init test_set_smpt_slow__empty_page_align(void)
227 {
228 	return __test_set_smpt_slow(3 * SMPT_WORD_BYTE_RANGE + PAGE_SIZE,
229 				    3 * SMPT_WORD_BYTE_RANGE + PAGE_SIZE);
230 }
231 
test_set_smpt_slow__one_whole_word(void)232 static int __init test_set_smpt_slow__one_whole_word(void)
233 {
234 	return __test_set_smpt_slow(3 * SMPT_WORD_BYTE_RANGE,
235 				    4 * SMPT_WORD_BYTE_RANGE);
236 }
237 
test_set_smpt_slow__one_partial_word(void)238 static int __init test_set_smpt_slow__one_partial_word(void)
239 {
240 	return __test_set_smpt_slow(3 * SMPT_WORD_BYTE_RANGE + PAGE_SIZE,
241 				    4 * SMPT_WORD_BYTE_RANGE - PAGE_SIZE);
242 }
243 
test_set_smpt_slow__multiple_whole_words(void)244 static int __init test_set_smpt_slow__multiple_whole_words(void)
245 {
246 	return __test_set_smpt_slow(13 * SMPT_WORD_BYTE_RANGE,
247 				    17 * SMPT_WORD_BYTE_RANGE);
248 }
249 
test_set_smpt_slow__multiple_partial_words(void)250 static int __init test_set_smpt_slow__multiple_partial_words(void)
251 {
252 	return __test_set_smpt_slow((13 * 2 + 1) * SMPT_WORD_BYTE_RANGE / 2,
253 				    (17 * 4 + 1) * SMPT_WORD_BYTE_RANGE / 4);
254 }
255 
selftest(void)256 static void __init selftest(void)
257 {
258 	KSTM_CHECK_ZERO(test_set_fmpt__fmpt_to_fmpt_whole());
259 	KSTM_CHECK_ZERO(test_set_fmpt__fmpt_no_change_whole());
260 	KSTM_CHECK_ZERO(test_set_fmpt__fmpt_no_change_partial());
261 	KSTM_CHECK_ZERO(test_set_fmpt__fmpt_to_smpt());
262 	KSTM_CHECK_ZERO(test_set_fmpt__smpt_to_fmpt_whole());
263 	KSTM_CHECK_ZERO(test_set_fmpt__smpt_to_fmpt_partial());
264 	KSTM_CHECK_ZERO(test_set_fmpt__smpt_to_smpt());
265 
266 	KSTM_CHECK_ZERO(test_set_smpt__within_one_word());
267 	KSTM_CHECK_ZERO(test_set_smpt__no_whole_word());
268 	KSTM_CHECK_ZERO(test_set_smpt__no_prologue_or_epilogue());
269 	KSTM_CHECK_ZERO(test_set_smpt__prologue());
270 	KSTM_CHECK_ZERO(test_set_smpt__epilogue());
271 	KSTM_CHECK_ZERO(test_set_smpt__prologue_and_epilogue());
272 
273 	KSTM_CHECK_ZERO(test_set_smpt_slow__empty_word_align());
274 	KSTM_CHECK_ZERO(test_set_smpt_slow__empty_page_align());
275 	KSTM_CHECK_ZERO(test_set_smpt_slow__one_whole_word());
276 	KSTM_CHECK_ZERO(test_set_smpt_slow__one_partial_word());
277 	KSTM_CHECK_ZERO(test_set_smpt_slow__multiple_whole_words());
278 	KSTM_CHECK_ZERO(test_set_smpt_slow__multiple_partial_words());
279 }
280 
281 KSTM_MODULE_LOADERS(test_kvm_s2mpu);
282 MODULE_AUTHOR("David Brazdil <dbrazdil@google.com>");
283 MODULE_LICENSE("GPL v2");
284