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