1 #include <assert.h>
2 #include <errno.h>
3 #include <stdbool.h>
4 #include <stdint.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <err.h>
9
10 #include <xen-tools/libs.h>
11 #include <xen/asm/x86-vendors.h>
12 #include <xen/lib/x86/cpu-policy.h>
13 #include <xen/domctl.h>
14
15 static unsigned int nr_failures;
16 #define fail(fmt, ...) \
17 ({ \
18 nr_failures++; \
19 (void)printf(fmt, ##__VA_ARGS__); \
20 })
21
22 #define memdup(ptr) \
23 ({ \
24 typeof(*(ptr)) *p_ = (ptr); \
25 void *n_ = malloc(sizeof(*p_)); \
26 \
27 if ( !n_ ) \
28 err(1, "%s malloc failure", __func__); \
29 \
30 memcpy(n_, p_, sizeof(*p_)); \
31 })
32
test_vendor_identification(void)33 static void test_vendor_identification(void)
34 {
35 static const struct test {
36 union {
37 char ident[12];
38 struct {
39 uint32_t b, d, c;
40 };
41 };
42 unsigned int vendor;
43 } tests[] = {
44 /* The 1st entry should remain here to work around gcc bug 91667. */
45 { { "" }, X86_VENDOR_UNKNOWN },
46 { { " " }, X86_VENDOR_UNKNOWN },
47 { { "xxxxxxxxxxxx" }, X86_VENDOR_UNKNOWN },
48
49 { { "GenuineIntel" }, X86_VENDOR_INTEL },
50 { { "AuthenticAMD" }, X86_VENDOR_AMD },
51 { { "CentaurHauls" }, X86_VENDOR_CENTAUR },
52 { { " Shanghai " }, X86_VENDOR_SHANGHAI },
53 { { "HygonGenuine" }, X86_VENDOR_HYGON },
54 };
55
56 printf("Testing CPU vendor identification:\n");
57
58 for ( size_t i = 0; i < ARRAY_SIZE(tests); ++i )
59 {
60 const struct test *t = &tests[i];
61 unsigned int vendor = x86_cpuid_lookup_vendor(t->b, t->c, t->d);
62
63 if ( vendor != t->vendor )
64 fail(" Test '%.12s', expected vendor %u, got %u\n",
65 t->ident, t->vendor, vendor);
66 }
67 }
68
leaves_are_sorted(const xen_cpuid_leaf_t * leaves,unsigned int nr)69 static bool leaves_are_sorted(const xen_cpuid_leaf_t *leaves, unsigned int nr)
70 {
71 for ( unsigned int i = 1; i < nr; ++i )
72 {
73 /* leaf index went backwards => not sorted. */
74 if ( leaves[i - 1].leaf > leaves[i].leaf )
75 return false;
76
77 /* leaf index went forwards => ok */
78 if ( leaves[i - 1].leaf < leaves[i].leaf )
79 continue;
80
81 /* leave index the same, subleaf didn't increase => not sorted. */
82 if ( leaves[i - 1].subleaf >= leaves[i].subleaf )
83 return false;
84 }
85
86 return true;
87 }
88
test_cpuid_current(void)89 static void test_cpuid_current(void)
90 {
91 struct cpuid_policy p;
92 xen_cpuid_leaf_t leaves[CPUID_MAX_SERIALISED_LEAVES];
93 unsigned int nr = ARRAY_SIZE(leaves);
94 int rc;
95
96 printf("Testing CPUID on current CPU\n");
97
98 x86_cpuid_policy_fill_native(&p);
99
100 rc = x86_cpuid_copy_to_buffer(&p, leaves, &nr);
101 if ( rc != 0 )
102 return fail(" Serialise, expected rc 0, got %d\n", rc);
103
104 if ( !leaves_are_sorted(leaves, nr) )
105 return fail(" Leaves not sorted\n");
106 }
107
test_cpuid_serialise_success(void)108 static void test_cpuid_serialise_success(void)
109 {
110 static const struct test {
111 struct cpuid_policy p;
112 const char *name;
113 unsigned int nr_leaves;
114 } tests[] = {
115 {
116 .name = "empty policy",
117 .nr_leaves = 4,
118 },
119
120 /* Leaf 4 serialisation stops at the first subleaf with type 0. */
121 {
122 .name = "empty leaf 4",
123 .p = {
124 .basic.max_leaf = 4,
125 },
126 .nr_leaves = 4 + 4,
127 },
128 {
129 .name = "partial leaf 4",
130 .p = {
131 .basic.max_leaf = 4,
132 .cache.subleaf[0].type = 1,
133 },
134 .nr_leaves = 4 + 4 + 1,
135 },
136
137 /* Leaf 7 serialisation stops at max_subleaf. */
138 {
139 .name = "empty leaf 7",
140 .p = {
141 .basic.max_leaf = 7,
142 },
143 .nr_leaves = 4 + 7,
144 },
145 {
146 .name = "partial leaf 7",
147 .p = {
148 .basic.max_leaf = 7,
149 .feat.max_subleaf = 1,
150 },
151 .nr_leaves = 4 + 7 + 1,
152 },
153
154 /* Leaf 0xb serialisation stops at the first subleaf with type 0. */
155 {
156 .name = "empty leaf 0xb",
157 .p = {
158 .basic.max_leaf = 0xb,
159 },
160 .nr_leaves = 4 + 0xb,
161 },
162 {
163 .name = "partial leaf 0xb",
164 .p = {
165 .basic.max_leaf = 0xb,
166 .topo.subleaf[0].type = 1,
167 },
168 .nr_leaves = 4 + 0xb + 1,
169 },
170
171 /*
172 * Leaf 0xd serialisation automatically has two leaves, and stops the
173 * highest bit set in {xcr0,xss}_{high,low}.
174 */
175 {
176 .name = "empty leaf 0xd",
177 .p = {
178 .basic.max_leaf = 0xd,
179 },
180 .nr_leaves = 4 + 0xd + 1,
181 },
182 {
183 .name = "partial 0xd",
184 .p = {
185 .basic.max_leaf = 0xd,
186 .xstate.xcr0_low = 7,
187 },
188 .nr_leaves = 4 + 0xd + 1 + 1,
189 },
190 };
191
192 printf("Testing CPUID serialise success:\n");
193
194 for ( size_t i = 0; i < ARRAY_SIZE(tests); ++i )
195 {
196 const struct test *t = &tests[i];
197 unsigned int nr = t->nr_leaves;
198 xen_cpuid_leaf_t *leaves = malloc(nr * sizeof(*leaves));
199 int rc;
200
201 if ( !leaves )
202 err(1, "%s() malloc failure", __func__);
203
204 rc = x86_cpuid_copy_to_buffer(&t->p, leaves, &nr);
205
206 if ( rc != 0 )
207 {
208 fail(" Test %s, expected rc 0, got %d\n",
209 t->name, rc);
210 goto test_done;
211 }
212
213 if ( nr != t->nr_leaves )
214 {
215 fail(" Test %s, expected %u leaves, got %u\n",
216 t->name, t->nr_leaves, nr);
217 goto test_done;
218 }
219
220 if ( !leaves_are_sorted(leaves, nr) )
221 {
222 fail(" Test %s, leaves not sorted\n",
223 t->name);
224 goto test_done;
225 }
226
227 test_done:
228 free(leaves);
229 }
230 }
231
test_msr_serialise_success(void)232 static void test_msr_serialise_success(void)
233 {
234 static const struct test {
235 struct msr_policy p;
236 const char *name;
237 unsigned int nr_msrs;
238 } tests[] = {
239 {
240 .name = "empty policy",
241 .nr_msrs = MSR_MAX_SERIALISED_ENTRIES,
242 },
243 };
244
245 printf("Testing MSR serialise success:\n");
246
247 for ( size_t i = 0; i < ARRAY_SIZE(tests); ++i )
248 {
249 const struct test *t = &tests[i];
250 unsigned int nr = t->nr_msrs;
251 xen_msr_entry_t *msrs = malloc(nr * sizeof(*msrs));
252 int rc;
253
254 if ( !msrs )
255 err(1, "%s() malloc failure", __func__);
256
257 rc = x86_msr_copy_to_buffer(&t->p, msrs, &nr);
258
259 if ( rc != 0 )
260 {
261 fail(" Test %s, expected rc 0, got %d\n",
262 t->name, rc);
263 goto test_done;
264 }
265
266 if ( nr != t->nr_msrs )
267 {
268 fail(" Test %s, expected %u msrs, got %u\n",
269 t->name, t->nr_msrs, nr);
270 goto test_done;
271 }
272
273 test_done:
274 free(msrs);
275 }
276 }
277
test_cpuid_deserialise_failure(void)278 static void test_cpuid_deserialise_failure(void)
279 {
280 static const struct test {
281 const char *name;
282 xen_cpuid_leaf_t leaf;
283 } tests[] = {
284 {
285 .name = "incorrect basic subleaf",
286 .leaf = { .leaf = 0, .subleaf = 0 },
287 },
288 {
289 .name = "incorrect hv1 subleaf",
290 .leaf = { .leaf = 0x40000000, .subleaf = 0 },
291 },
292 {
293 .name = "incorrect hv2 subleaf",
294 .leaf = { .leaf = 0x40000100, .subleaf = 0 },
295 },
296 {
297 .name = "incorrect extd subleaf",
298 .leaf = { .leaf = 0x80000000, .subleaf = 0 },
299 },
300 {
301 .name = "OoB basic leaf",
302 .leaf = { .leaf = CPUID_GUEST_NR_BASIC },
303 },
304 {
305 .name = "OoB cache leaf",
306 .leaf = { .leaf = 0x4, .subleaf = CPUID_GUEST_NR_CACHE },
307 },
308 {
309 .name = "OoB feat leaf",
310 .leaf = { .leaf = 0x7, .subleaf = CPUID_GUEST_NR_FEAT },
311 },
312 {
313 .name = "OoB topo leaf",
314 .leaf = { .leaf = 0xb, .subleaf = CPUID_GUEST_NR_TOPO },
315 },
316 {
317 .name = "OoB xstate leaf",
318 .leaf = { .leaf = 0xd, .subleaf = CPUID_GUEST_NR_XSTATE },
319 },
320 {
321 .name = "OoB extd leaf",
322 .leaf = { .leaf = 0x80000000 | CPUID_GUEST_NR_EXTD },
323 },
324 };
325
326 printf("Testing CPUID deserialise failure:\n");
327
328 for ( size_t i = 0; i < ARRAY_SIZE(tests); ++i )
329 {
330 const struct test *t = &tests[i];
331 uint32_t err_leaf, err_subleaf;
332 int rc;
333
334 /* No writes should occur. Use NULL to catch errors. */
335 rc = x86_cpuid_copy_from_buffer(NULL, &t->leaf, 1,
336 &err_leaf, &err_subleaf);
337
338 if ( rc != -ERANGE )
339 {
340 fail(" Test %s, expected rc %d, got %d\n",
341 t->name, -ERANGE, rc);
342 continue;
343 }
344
345 if ( err_leaf != t->leaf.leaf || err_subleaf != t->leaf.subleaf )
346 {
347 fail(" Test %s, expected err %08x:%08x, got %08x:%08x\n",
348 t->name, t->leaf.leaf, t->leaf.subleaf,
349 err_leaf, err_subleaf);
350 continue;
351 }
352 }
353 }
354
test_msr_deserialise_failure(void)355 static void test_msr_deserialise_failure(void)
356 {
357 static const struct test {
358 const char *name;
359 xen_msr_entry_t msr;
360 int rc;
361 } tests[] = {
362 {
363 .name = "bad msr index",
364 .msr = { .idx = 0xdeadc0de },
365 .rc = -ERANGE,
366 },
367 {
368 .name = "nonzero flags",
369 .msr = { .idx = 0xce, .flags = 1 },
370 .rc = -EINVAL,
371 },
372 {
373 .name = "truncated val",
374 .msr = { .idx = 0xce, .val = ~0ull },
375 .rc = -EOVERFLOW,
376 },
377 {
378 .name = "truncated val",
379 .msr = { .idx = 0x10a, .val = ~0ull },
380 .rc = -EOVERFLOW,
381 },
382 };
383
384 printf("Testing MSR deserialise failure:\n");
385
386 for ( size_t i = 0; i < ARRAY_SIZE(tests); ++i )
387 {
388 const struct test *t = &tests[i];
389 uint32_t err_msr;
390 int rc;
391
392 /* No writes should occur. Use NULL to catch errors. */
393 rc = x86_msr_copy_from_buffer(NULL, &t->msr, 1, &err_msr);
394
395 if ( rc != t->rc )
396 {
397 fail(" Test %s, expected rc %d, got %d\n",
398 t->name, t->rc, rc);
399 continue;
400 }
401
402 if ( err_msr != t->msr.idx )
403 {
404 fail(" Test %s, expected err_msr %#x, got %#x\n",
405 t->name, t->msr.idx, err_msr);
406 continue;
407 }
408 }
409 }
410
test_cpuid_out_of_range_clearing(void)411 static void test_cpuid_out_of_range_clearing(void)
412 {
413 static const struct test {
414 const char *name;
415 unsigned int nr_markers;
416 struct cpuid_policy p;
417 } tests[] = {
418 {
419 .name = "basic",
420 .nr_markers = 1,
421 .p = {
422 /* Retains marker in leaf 0. Clears others. */
423 .basic.max_leaf = 0,
424 .basic.vendor_ebx = 0xc2,
425
426 .basic.raw_fms = 0xc2,
427 .cache.raw[0].a = 0xc2,
428 .feat.raw[0].a = 0xc2,
429 .topo.raw[0].a = 0xc2,
430 .xstate.raw[0].a = 0xc2,
431 .xstate.raw[1].a = 0xc2,
432 },
433 },
434 {
435 .name = "cache",
436 .nr_markers = 1,
437 .p = {
438 /* Retains marker in subleaf 0. Clears others. */
439 .basic.max_leaf = 4,
440 .cache.raw[0] = { .a = 1, .b = 0xc2 },
441
442 .cache.raw[1].b = 0xc2,
443 .feat.raw[0].a = 0xc2,
444 .topo.raw[0].a = 0xc2,
445 .xstate.raw[0].a = 0xc2,
446 .xstate.raw[1].a = 0xc2,
447 },
448 },
449 {
450 .name = "feat",
451 .nr_markers = 1,
452 .p = {
453 /* Retains marker in subleaf 0. Clears others. */
454 .basic.max_leaf = 7,
455 .feat.raw[0].b = 0xc2,
456
457 .feat.raw[1].b = 0xc2,
458 .topo.raw[0].a = 0xc2,
459 .xstate.raw[0].a = 0xc2,
460 .xstate.raw[1].a = 0xc2,
461 },
462 },
463 {
464 .name = "topo",
465 .nr_markers = 1,
466 .p = {
467 /* Retains marker in subleaf 0. Clears others. */
468 .basic.max_leaf = 0xb,
469 .topo.raw[0] = { .b = 0xc2, .c = 0x0100 },
470
471 .topo.raw[1].b = 0xc2,
472 .xstate.raw[0].a = 0xc2,
473 .xstate.raw[1].a = 0xc2,
474 },
475 },
476 {
477 .name = "xstate x87",
478 .nr_markers = 2,
479 .p = {
480 /* First two subleaves always valid. Others cleared. */
481 .basic.max_leaf = 0xd,
482 .xstate.raw[0].a = 1,
483 .xstate.raw[0].b = 0xc2,
484 .xstate.raw[1].b = 0xc2,
485
486 .xstate.raw[2].b = 0xc2,
487 .xstate.raw[3].b = 0xc2,
488 },
489 },
490 {
491 .name = "xstate sse",
492 .nr_markers = 2,
493 .p = {
494 /* First two subleaves always valid. Others cleared. */
495 .basic.max_leaf = 0xd,
496 .xstate.raw[0].a = 2,
497 .xstate.raw[0].b = 0xc2,
498 .xstate.raw[1].b = 0xc2,
499
500 .xstate.raw[2].b = 0xc2,
501 .xstate.raw[3].b = 0xc2,
502 },
503 },
504 {
505 .name = "xstate avx",
506 .nr_markers = 3,
507 .p = {
508 /* Third subleaf also valid. Others cleared. */
509 .basic.max_leaf = 0xd,
510 .xstate.raw[0].a = 7,
511 .xstate.raw[0].b = 0xc2,
512 .xstate.raw[1].b = 0xc2,
513 .xstate.raw[2].b = 0xc2,
514
515 .xstate.raw[3].b = 0xc2,
516 },
517 },
518 {
519 .name = "extd",
520 .nr_markers = 1,
521 .p = {
522 /* Retains marker in leaf 0. Clears others. */
523 .extd.max_leaf = 0,
524 .extd.vendor_ebx = 0xc2,
525
526 .extd.raw_fms = 0xc2,
527 },
528 },
529 };
530
531 printf("Testing CPUID out-of-range clearing:\n");
532
533 for ( size_t i = 0; i < ARRAY_SIZE(tests); ++i )
534 {
535 const struct test *t = &tests[i];
536 struct cpuid_policy *p = memdup(&t->p);
537 void *ptr;
538 unsigned int nr_markers;
539
540 x86_cpuid_policy_clear_out_of_range_leaves(p);
541
542 /* Count the number of 0xc2's still remaining. */
543 for ( ptr = p, nr_markers = 0;
544 (ptr = memchr(ptr, 0xc2, (void *)p + sizeof(*p) - ptr));
545 ptr++, nr_markers++ )
546 ;
547
548 if ( nr_markers != t->nr_markers )
549 fail(" Test %s fail - expected %u markers, got %u\n",
550 t->name, t->nr_markers, nr_markers);
551
552 free(p);
553 }
554 }
555
test_is_compatible_success(void)556 static void test_is_compatible_success(void)
557 {
558 static struct test {
559 const char *name;
560 struct cpuid_policy host_cpuid;
561 struct cpuid_policy guest_cpuid;
562 struct msr_policy host_msr;
563 struct msr_policy guest_msr;
564 } tests[] = {
565 {
566 .name = "Host CPUID faulting, Guest not",
567 .host_msr = {
568 .platform_info.cpuid_faulting = true,
569 },
570 },
571 {
572 .name = "Host CPUID faulting, Guest wanted",
573 .host_msr = {
574 .platform_info.cpuid_faulting = true,
575 },
576 .guest_msr = {
577 .platform_info.cpuid_faulting = true,
578 },
579 },
580 };
581 struct cpu_policy_errors no_errors = INIT_CPU_POLICY_ERRORS;
582
583 printf("Testing policy compatibility success:\n");
584
585 for ( size_t i = 0; i < ARRAY_SIZE(tests); ++i )
586 {
587 struct test *t = &tests[i];
588 struct cpu_policy sys = {
589 &t->host_cpuid,
590 &t->host_msr,
591 }, new = {
592 &t->guest_cpuid,
593 &t->guest_msr,
594 };
595 struct cpu_policy_errors e;
596 int res = x86_cpu_policies_are_compatible(&sys, &new, &e);
597
598 /* Check the expected error output. */
599 if ( res != 0 || memcmp(&no_errors, &e, sizeof(no_errors)) )
600 fail(" Test '%s' expected no errors\n"
601 " got res %d { leaf %08x, subleaf %08x, msr %08x }\n",
602 t->name, res, e.leaf, e.subleaf, e.msr);
603 }
604 }
605
test_is_compatible_failure(void)606 static void test_is_compatible_failure(void)
607 {
608 static struct test {
609 const char *name;
610 struct cpuid_policy host_cpuid;
611 struct cpuid_policy guest_cpuid;
612 struct msr_policy host_msr;
613 struct msr_policy guest_msr;
614 struct cpu_policy_errors e;
615 } tests[] = {
616 {
617 .name = "Host basic.max_leaf out of range",
618 .guest_cpuid.basic.max_leaf = 1,
619 .e = { 0, -1, -1 },
620 },
621 {
622 .name = "Host extd.max_leaf out of range",
623 .guest_cpuid.extd.max_leaf = 1,
624 .e = { 0x80000000, -1, -1 },
625 },
626 {
627 .name = "Host no CPUID faulting, Guest wanted",
628 .guest_msr = {
629 .platform_info.cpuid_faulting = true,
630 },
631 .e = { -1, -1, 0xce },
632 },
633 };
634
635 printf("Testing policy compatibility failure:\n");
636
637 for ( size_t i = 0; i < ARRAY_SIZE(tests); ++i )
638 {
639 struct test *t = &tests[i];
640 struct cpu_policy sys = {
641 &t->host_cpuid,
642 &t->host_msr,
643 }, new = {
644 &t->guest_cpuid,
645 &t->guest_msr,
646 };
647 struct cpu_policy_errors e;
648 int res = x86_cpu_policies_are_compatible(&sys, &new, &e);
649
650 /* Check the expected error output. */
651 if ( res == 0 || memcmp(&t->e, &e, sizeof(t->e)) )
652 fail(" Test '%s' res %d\n"
653 " expected { leaf %08x, subleaf %08x, msr %08x }\n"
654 " got { leaf %08x, subleaf %08x, msr %08x }\n",
655 t->name, res,
656 t->e.leaf, t->e.subleaf, t->e.msr,
657 e.leaf, e.subleaf, e.msr);
658 }
659 }
660
main(int argc,char ** argv)661 int main(int argc, char **argv)
662 {
663 printf("CPU Policy unit tests\n");
664
665 test_vendor_identification();
666
667 test_cpuid_current();
668 test_cpuid_serialise_success();
669 test_cpuid_deserialise_failure();
670 test_cpuid_out_of_range_clearing();
671
672 test_msr_serialise_success();
673 test_msr_deserialise_failure();
674
675 test_is_compatible_success();
676 test_is_compatible_failure();
677
678 if ( nr_failures )
679 printf("Done: %u failures\n", nr_failures);
680 else
681 printf("Done: all ok\n");
682
683 return !!nr_failures;
684 }
685