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