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