1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (C) 2025 Arm Ltd.
3 /* This file is intended to be included into mpam_devices.c */
4
5 #include <kunit/test.h>
6
7 /*
8 * This test catches fields that aren't being sanitised - but can't tell you
9 * which one...
10 */
test__props_mismatch(struct kunit * test)11 static void test__props_mismatch(struct kunit *test)
12 {
13 struct mpam_props parent = { 0 };
14 struct mpam_props child;
15
16 memset(&child, 0xff, sizeof(child));
17 __props_mismatch(&parent, &child, false);
18
19 memset(&child, 0, sizeof(child));
20 KUNIT_EXPECT_EQ(test, memcmp(&parent, &child, sizeof(child)), 0);
21
22 memset(&child, 0xff, sizeof(child));
23 __props_mismatch(&parent, &child, true);
24
25 KUNIT_EXPECT_EQ(test, memcmp(&parent, &child, sizeof(child)), 0);
26 }
27
28 static struct list_head fake_classes_list;
29 static struct mpam_class fake_class = { 0 };
30 static struct mpam_component fake_comp1 = { 0 };
31 static struct mpam_component fake_comp2 = { 0 };
32 static struct mpam_vmsc fake_vmsc1 = { 0 };
33 static struct mpam_vmsc fake_vmsc2 = { 0 };
34 static struct mpam_msc fake_msc1 = { 0 };
35 static struct mpam_msc fake_msc2 = { 0 };
36 static struct mpam_msc_ris fake_ris1 = { 0 };
37 static struct mpam_msc_ris fake_ris2 = { 0 };
38 static struct platform_device fake_pdev = { 0 };
39
reset_fake_hierarchy(void)40 static inline void reset_fake_hierarchy(void)
41 {
42 INIT_LIST_HEAD(&fake_classes_list);
43
44 memset(&fake_class, 0, sizeof(fake_class));
45 fake_class.level = 3;
46 fake_class.type = MPAM_CLASS_CACHE;
47 INIT_LIST_HEAD_RCU(&fake_class.components);
48 INIT_LIST_HEAD(&fake_class.classes_list);
49
50 memset(&fake_comp1, 0, sizeof(fake_comp1));
51 memset(&fake_comp2, 0, sizeof(fake_comp2));
52 fake_comp1.comp_id = 1;
53 fake_comp2.comp_id = 2;
54 INIT_LIST_HEAD(&fake_comp1.vmsc);
55 INIT_LIST_HEAD(&fake_comp1.class_list);
56 INIT_LIST_HEAD(&fake_comp2.vmsc);
57 INIT_LIST_HEAD(&fake_comp2.class_list);
58
59 memset(&fake_vmsc1, 0, sizeof(fake_vmsc1));
60 memset(&fake_vmsc2, 0, sizeof(fake_vmsc2));
61 INIT_LIST_HEAD(&fake_vmsc1.ris);
62 INIT_LIST_HEAD(&fake_vmsc1.comp_list);
63 fake_vmsc1.msc = &fake_msc1;
64 INIT_LIST_HEAD(&fake_vmsc2.ris);
65 INIT_LIST_HEAD(&fake_vmsc2.comp_list);
66 fake_vmsc2.msc = &fake_msc2;
67
68 memset(&fake_ris1, 0, sizeof(fake_ris1));
69 memset(&fake_ris2, 0, sizeof(fake_ris2));
70 fake_ris1.ris_idx = 1;
71 INIT_LIST_HEAD(&fake_ris1.msc_list);
72 fake_ris2.ris_idx = 2;
73 INIT_LIST_HEAD(&fake_ris2.msc_list);
74
75 fake_msc1.pdev = &fake_pdev;
76 fake_msc2.pdev = &fake_pdev;
77
78 list_add(&fake_class.classes_list, &fake_classes_list);
79 }
80
test_mpam_enable_merge_features(struct kunit * test)81 static void test_mpam_enable_merge_features(struct kunit *test)
82 {
83 reset_fake_hierarchy();
84
85 mutex_lock(&mpam_list_lock);
86
87 /* One Class+Comp, two RIS in one vMSC with common features */
88 fake_comp1.class = &fake_class;
89 list_add(&fake_comp1.class_list, &fake_class.components);
90 fake_comp2.class = NULL;
91 fake_vmsc1.comp = &fake_comp1;
92 list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc);
93 fake_vmsc2.comp = NULL;
94 fake_ris1.vmsc = &fake_vmsc1;
95 list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris);
96 fake_ris2.vmsc = &fake_vmsc1;
97 list_add(&fake_ris2.vmsc_list, &fake_vmsc1.ris);
98
99 mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props);
100 mpam_set_feature(mpam_feat_cpor_part, &fake_ris2.props);
101 fake_ris1.props.cpbm_wd = 4;
102 fake_ris2.props.cpbm_wd = 4;
103
104 mpam_enable_merge_features(&fake_classes_list);
105
106 KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_class.props));
107 KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 4);
108
109 reset_fake_hierarchy();
110
111 /* One Class+Comp, two RIS in one vMSC with non-overlapping features */
112 fake_comp1.class = &fake_class;
113 list_add(&fake_comp1.class_list, &fake_class.components);
114 fake_comp2.class = NULL;
115 fake_vmsc1.comp = &fake_comp1;
116 list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc);
117 fake_vmsc2.comp = NULL;
118 fake_ris1.vmsc = &fake_vmsc1;
119 list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris);
120 fake_ris2.vmsc = &fake_vmsc1;
121 list_add(&fake_ris2.vmsc_list, &fake_vmsc1.ris);
122
123 mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props);
124 mpam_set_feature(mpam_feat_cmax_cmin, &fake_ris2.props);
125 fake_ris1.props.cpbm_wd = 4;
126 fake_ris2.props.cmax_wd = 4;
127
128 mpam_enable_merge_features(&fake_classes_list);
129
130 /* Multiple RIS within one MSC controlling the same resource can be mismatched */
131 KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_class.props));
132 KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cmax_cmin, &fake_class.props));
133 KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cmax_cmin, &fake_vmsc1.props));
134 KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 4);
135 KUNIT_EXPECT_EQ(test, fake_vmsc1.props.cmax_wd, 4);
136 KUNIT_EXPECT_EQ(test, fake_class.props.cmax_wd, 4);
137
138 reset_fake_hierarchy();
139
140 /* One Class+Comp, two MSC with overlapping features */
141 fake_comp1.class = &fake_class;
142 list_add(&fake_comp1.class_list, &fake_class.components);
143 fake_comp2.class = NULL;
144 fake_vmsc1.comp = &fake_comp1;
145 list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc);
146 fake_vmsc2.comp = &fake_comp1;
147 list_add(&fake_vmsc2.comp_list, &fake_comp1.vmsc);
148 fake_ris1.vmsc = &fake_vmsc1;
149 list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris);
150 fake_ris2.vmsc = &fake_vmsc2;
151 list_add(&fake_ris2.vmsc_list, &fake_vmsc2.ris);
152
153 mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props);
154 mpam_set_feature(mpam_feat_cpor_part, &fake_ris2.props);
155 fake_ris1.props.cpbm_wd = 4;
156 fake_ris2.props.cpbm_wd = 4;
157
158 mpam_enable_merge_features(&fake_classes_list);
159
160 KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_class.props));
161 KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 4);
162
163 reset_fake_hierarchy();
164
165 /* One Class+Comp, two MSC with non-overlapping features */
166 fake_comp1.class = &fake_class;
167 list_add(&fake_comp1.class_list, &fake_class.components);
168 fake_comp2.class = NULL;
169 fake_vmsc1.comp = &fake_comp1;
170 list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc);
171 fake_vmsc2.comp = &fake_comp1;
172 list_add(&fake_vmsc2.comp_list, &fake_comp1.vmsc);
173 fake_ris1.vmsc = &fake_vmsc1;
174 list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris);
175 fake_ris2.vmsc = &fake_vmsc2;
176 list_add(&fake_ris2.vmsc_list, &fake_vmsc2.ris);
177
178 mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props);
179 mpam_set_feature(mpam_feat_cmax_cmin, &fake_ris2.props);
180 fake_ris1.props.cpbm_wd = 4;
181 fake_ris2.props.cmax_wd = 4;
182
183 mpam_enable_merge_features(&fake_classes_list);
184
185 /*
186 * Multiple RIS in different MSC can't control the same resource,
187 * mismatched features can not be supported.
188 */
189 KUNIT_EXPECT_FALSE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_class.props));
190 KUNIT_EXPECT_FALSE(test, mpam_has_feature(mpam_feat_cmax_cmin, &fake_class.props));
191 KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 0);
192 KUNIT_EXPECT_EQ(test, fake_class.props.cmax_wd, 0);
193
194 reset_fake_hierarchy();
195
196 /* One Class+Comp, two MSC with incompatible overlapping features */
197 fake_comp1.class = &fake_class;
198 list_add(&fake_comp1.class_list, &fake_class.components);
199 fake_comp2.class = NULL;
200 fake_vmsc1.comp = &fake_comp1;
201 list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc);
202 fake_vmsc2.comp = &fake_comp1;
203 list_add(&fake_vmsc2.comp_list, &fake_comp1.vmsc);
204 fake_ris1.vmsc = &fake_vmsc1;
205 list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris);
206 fake_ris2.vmsc = &fake_vmsc2;
207 list_add(&fake_ris2.vmsc_list, &fake_vmsc2.ris);
208
209 mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props);
210 mpam_set_feature(mpam_feat_cpor_part, &fake_ris2.props);
211 mpam_set_feature(mpam_feat_mbw_part, &fake_ris1.props);
212 mpam_set_feature(mpam_feat_mbw_part, &fake_ris2.props);
213 fake_ris1.props.cpbm_wd = 5;
214 fake_ris2.props.cpbm_wd = 3;
215 fake_ris1.props.mbw_pbm_bits = 5;
216 fake_ris2.props.mbw_pbm_bits = 3;
217
218 mpam_enable_merge_features(&fake_classes_list);
219
220 /*
221 * Multiple RIS in different MSC can't control the same resource,
222 * mismatched features can not be supported.
223 */
224 KUNIT_EXPECT_FALSE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_class.props));
225 KUNIT_EXPECT_FALSE(test, mpam_has_feature(mpam_feat_mbw_part, &fake_class.props));
226 KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 0);
227 KUNIT_EXPECT_EQ(test, fake_class.props.mbw_pbm_bits, 0);
228
229 reset_fake_hierarchy();
230
231 /* One Class+Comp, two MSC with overlapping features that need tweaking */
232 fake_comp1.class = &fake_class;
233 list_add(&fake_comp1.class_list, &fake_class.components);
234 fake_comp2.class = NULL;
235 fake_vmsc1.comp = &fake_comp1;
236 list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc);
237 fake_vmsc2.comp = &fake_comp1;
238 list_add(&fake_vmsc2.comp_list, &fake_comp1.vmsc);
239 fake_ris1.vmsc = &fake_vmsc1;
240 list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris);
241 fake_ris2.vmsc = &fake_vmsc2;
242 list_add(&fake_ris2.vmsc_list, &fake_vmsc2.ris);
243
244 mpam_set_feature(mpam_feat_mbw_min, &fake_ris1.props);
245 mpam_set_feature(mpam_feat_mbw_min, &fake_ris2.props);
246 mpam_set_feature(mpam_feat_cmax_cmax, &fake_ris1.props);
247 mpam_set_feature(mpam_feat_cmax_cmax, &fake_ris2.props);
248 fake_ris1.props.bwa_wd = 5;
249 fake_ris2.props.bwa_wd = 3;
250 fake_ris1.props.cmax_wd = 5;
251 fake_ris2.props.cmax_wd = 3;
252
253 mpam_enable_merge_features(&fake_classes_list);
254
255 /*
256 * RIS with different control properties need to be sanitised so the
257 * class has the common set of properties.
258 */
259 KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_mbw_min, &fake_class.props));
260 KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cmax_cmax, &fake_class.props));
261 KUNIT_EXPECT_EQ(test, fake_class.props.bwa_wd, 3);
262 KUNIT_EXPECT_EQ(test, fake_class.props.cmax_wd, 3);
263
264 reset_fake_hierarchy();
265
266 /* One Class Two Comp with overlapping features */
267 fake_comp1.class = &fake_class;
268 list_add(&fake_comp1.class_list, &fake_class.components);
269 fake_comp2.class = &fake_class;
270 list_add(&fake_comp2.class_list, &fake_class.components);
271 fake_vmsc1.comp = &fake_comp1;
272 list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc);
273 fake_vmsc2.comp = &fake_comp2;
274 list_add(&fake_vmsc2.comp_list, &fake_comp2.vmsc);
275 fake_ris1.vmsc = &fake_vmsc1;
276 list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris);
277 fake_ris2.vmsc = &fake_vmsc2;
278 list_add(&fake_ris2.vmsc_list, &fake_vmsc2.ris);
279
280 mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props);
281 mpam_set_feature(mpam_feat_cpor_part, &fake_ris2.props);
282 fake_ris1.props.cpbm_wd = 4;
283 fake_ris2.props.cpbm_wd = 4;
284
285 mpam_enable_merge_features(&fake_classes_list);
286
287 KUNIT_EXPECT_TRUE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_class.props));
288 KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 4);
289
290 reset_fake_hierarchy();
291
292 /* One Class Two Comp with non-overlapping features */
293 fake_comp1.class = &fake_class;
294 list_add(&fake_comp1.class_list, &fake_class.components);
295 fake_comp2.class = &fake_class;
296 list_add(&fake_comp2.class_list, &fake_class.components);
297 fake_vmsc1.comp = &fake_comp1;
298 list_add(&fake_vmsc1.comp_list, &fake_comp1.vmsc);
299 fake_vmsc2.comp = &fake_comp2;
300 list_add(&fake_vmsc2.comp_list, &fake_comp2.vmsc);
301 fake_ris1.vmsc = &fake_vmsc1;
302 list_add(&fake_ris1.vmsc_list, &fake_vmsc1.ris);
303 fake_ris2.vmsc = &fake_vmsc2;
304 list_add(&fake_ris2.vmsc_list, &fake_vmsc2.ris);
305
306 mpam_set_feature(mpam_feat_cpor_part, &fake_ris1.props);
307 mpam_set_feature(mpam_feat_cmax_cmin, &fake_ris2.props);
308 fake_ris1.props.cpbm_wd = 4;
309 fake_ris2.props.cmax_wd = 4;
310
311 mpam_enable_merge_features(&fake_classes_list);
312
313 /*
314 * Multiple components can't control the same resource, mismatched features can
315 * not be supported.
316 */
317 KUNIT_EXPECT_FALSE(test, mpam_has_feature(mpam_feat_cpor_part, &fake_class.props));
318 KUNIT_EXPECT_FALSE(test, mpam_has_feature(mpam_feat_cmax_cmin, &fake_class.props));
319 KUNIT_EXPECT_EQ(test, fake_class.props.cpbm_wd, 0);
320 KUNIT_EXPECT_EQ(test, fake_class.props.cmax_wd, 0);
321
322 mutex_unlock(&mpam_list_lock);
323 }
324
__test_mpam_reset_msc_bitmap(struct mpam_msc * msc,u16 reg,u16 wd)325 static void __test_mpam_reset_msc_bitmap(struct mpam_msc *msc, u16 reg, u16 wd)
326 {
327 /* Avoid warnings when running with CONFIG_DEBUG_PREEMPT */
328 guard(preempt)();
329
330 mpam_reset_msc_bitmap(msc, reg, wd);
331 }
332
test_mpam_reset_msc_bitmap(struct kunit * test)333 static void test_mpam_reset_msc_bitmap(struct kunit *test)
334 {
335 char __iomem *buf = (__force char __iomem *)kunit_kzalloc(test, SZ_16K, GFP_KERNEL);
336 struct mpam_msc fake_msc = {};
337 u32 *test_result;
338
339 if (!buf)
340 return;
341
342 fake_msc.mapped_hwpage = buf;
343 fake_msc.mapped_hwpage_sz = SZ_16K;
344 cpumask_copy(&fake_msc.accessibility, cpu_possible_mask);
345
346 /* Satisfy lockdep checks */
347 mutex_init(&fake_msc.part_sel_lock);
348 mutex_lock(&fake_msc.part_sel_lock);
349
350 test_result = (__force u32 *)(buf + MPAMCFG_CPBM);
351
352 __test_mpam_reset_msc_bitmap(&fake_msc, MPAMCFG_CPBM, 0);
353 KUNIT_EXPECT_EQ(test, test_result[0], 0);
354 KUNIT_EXPECT_EQ(test, test_result[1], 0);
355 test_result[0] = 0;
356 test_result[1] = 0;
357
358 __test_mpam_reset_msc_bitmap(&fake_msc, MPAMCFG_CPBM, 1);
359 KUNIT_EXPECT_EQ(test, test_result[0], 1);
360 KUNIT_EXPECT_EQ(test, test_result[1], 0);
361 test_result[0] = 0;
362 test_result[1] = 0;
363
364 __test_mpam_reset_msc_bitmap(&fake_msc, MPAMCFG_CPBM, 16);
365 KUNIT_EXPECT_EQ(test, test_result[0], 0xffff);
366 KUNIT_EXPECT_EQ(test, test_result[1], 0);
367 test_result[0] = 0;
368 test_result[1] = 0;
369
370 __test_mpam_reset_msc_bitmap(&fake_msc, MPAMCFG_CPBM, 32);
371 KUNIT_EXPECT_EQ(test, test_result[0], 0xffffffff);
372 KUNIT_EXPECT_EQ(test, test_result[1], 0);
373 test_result[0] = 0;
374 test_result[1] = 0;
375
376 __test_mpam_reset_msc_bitmap(&fake_msc, MPAMCFG_CPBM, 33);
377 KUNIT_EXPECT_EQ(test, test_result[0], 0xffffffff);
378 KUNIT_EXPECT_EQ(test, test_result[1], 1);
379 test_result[0] = 0;
380 test_result[1] = 0;
381
382 mutex_unlock(&fake_msc.part_sel_lock);
383 }
384
385 static struct kunit_case mpam_devices_test_cases[] = {
386 KUNIT_CASE(test_mpam_reset_msc_bitmap),
387 KUNIT_CASE(test_mpam_enable_merge_features),
388 KUNIT_CASE(test__props_mismatch),
389 {}
390 };
391
392 static struct kunit_suite mpam_devices_test_suite = {
393 .name = "mpam_devices_test_suite",
394 .test_cases = mpam_devices_test_cases,
395 };
396
397 kunit_test_suites(&mpam_devices_test_suite);
398