1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <err.h>
4 #include <getopt.h>
5 #include <string.h>
6 #include <errno.h>
7 #include <limits.h>
8 #include <inttypes.h>
9 
10 #include <xenctrl.h>
11 
12 #include <xen-tools/libs.h>
13 
14 static uint32_t nr_features;
15 
16 static const char *const str_1d[32] =
17 {
18     [ 0] = "fpu",  [ 1] = "vme",
19     [ 2] = "de",   [ 3] = "pse",
20     [ 4] = "tsc",  [ 5] = "msr",
21     [ 6] = "pae",  [ 7] = "mce",
22     [ 8] = "cx8",  [ 9] = "apic",
23     /* [10] */     [11] = "sysenter",
24     [12] = "mtrr", [13] = "pge",
25     [14] = "mca",  [15] = "cmov",
26     [16] = "pat",  [17] = "pse36",
27     [18] = "psn",  [19] = "clflush",
28     /* [20] */     [21] = "ds",
29     [22] = "acpi", [23] = "mmx",
30     [24] = "fxsr", [25] = "sse",
31     [26] = "sse2", [27] = "ss",
32     [28] = "htt",  [29] = "tm",
33     [30] = "ia64", [31] = "pbe",
34 };
35 
36 static const char *const str_1c[32] =
37 {
38     [ 0] = "sse3",    [ 1] = "pclmulqdq",
39     [ 2] = "dtes64",  [ 3] = "monitor",
40     [ 4] = "ds-cpl",  [ 5] = "vmx",
41     [ 6] = "smx",     [ 7] = "est",
42     [ 8] = "tm2",     [ 9] = "ssse3",
43     [10] = "cntx-id", [11] = "sdgb",
44     [12] = "fma",     [13] = "cx16",
45     [14] = "xtpr",    [15] = "pdcm",
46     /* [16] */        [17] = "pcid",
47     [18] = "dca",     [19] = "sse41",
48     [20] = "sse42",   [21] = "x2apic",
49     [22] = "movebe",  [23] = "popcnt",
50     [24] = "tsc-dl",  [25] = "aesni",
51     [26] = "xsave",   [27] = "osxsave",
52     [28] = "avx",     [29] = "f16c",
53     [30] = "rdrnd",   [31] = "hyper",
54 };
55 
56 static const char *const str_e1d[32] =
57 {
58     [ 0] = "fpu",    [ 1] = "vme",
59     [ 2] = "de",     [ 3] = "pse",
60     [ 4] = "tsc",    [ 5] = "msr",
61     [ 6] = "pae",    [ 7] = "mce",
62     [ 8] = "cx8",    [ 9] = "apic",
63     /* [10] */       [11] = "syscall",
64     [12] = "mtrr",   [13] = "pge",
65     [14] = "mca",    [15] = "cmov",
66     [16] = "fcmov",  [17] = "pse36",
67     /* [18] */       [19] = "mp",
68     [20] = "nx",     /* [21] */
69     [22] = "mmx+",   [23] = "mmx",
70     [24] = "fxsr",   [25] = "fxsr+",
71     [26] = "pg1g",   [27] = "rdtscp",
72     /* [28] */       [29] = "lm",
73     [30] = "3dnow+", [31] = "3dnow",
74 };
75 
76 static const char *const str_e1c[32] =
77 {
78     [ 0] = "lahf_lm",    [ 1] = "cmp",
79     [ 2] = "svm",        [ 3] = "extapic",
80     [ 4] = "cr8d",       [ 5] = "lzcnt",
81     [ 6] = "sse4a",      [ 7] = "msse",
82     [ 8] = "3dnowpf",    [ 9] = "osvw",
83     [10] = "ibs",        [11] = "xop",
84     [12] = "skinit",     [13] = "wdt",
85     /* [14] */           [15] = "lwp",
86     [16] = "fma4",       [17] = "tce",
87     /* [18] */           [19] = "nodeid",
88     /* [20] */           [21] = "tbm",
89     [22] = "topoext",    [23] = "perfctr_core",
90     [24] = "perfctr_nb", /* [25] */
91     [26] = "dbx",        [27] = "perftsc",
92     [28] = "pcx_l2i",    [29] = "monitorx",
93 };
94 
95 static const char *const str_7b0[32] =
96 {
97     [ 0] = "fsgsbase", [ 1] = "tsc-adj",
98     [ 2] = "sgx",      [ 3] = "bmi1",
99     [ 4] = "hle",      [ 5] = "avx2",
100     [ 6] = "fdp_exn",  [ 7] = "smep",
101     [ 8] = "bmi2",     [ 9] = "erms",
102     [10] = "invpcid",  [11] = "rtm",
103     [12] = "pqm",      [13] = "depfpp",
104     [14] = "mpx",      [15] = "pqe",
105     [16] = "avx512f",  [17] = "avx512dq",
106     [18] = "rdseed",   [19] = "adx",
107     [20] = "smap",     [21] = "avx512-ifma",
108     [22] = "pcommit",  [23] = "clflushopt",
109     [24] = "clwb",     [25] = "pt",
110     [26] = "avx512pf", [27] = "avx512er",
111     [28] = "avx512cd", [29] = "sha",
112     [30] = "avx512bw", [31] = "avx512vl",
113 };
114 
115 static const char *const str_Da1[32] =
116 {
117     [ 0] = "xsaveopt", [ 1] = "xsavec",
118     [ 2] = "xgetbv1",  [ 3] = "xsaves",
119 };
120 
121 static const char *const str_7c0[32] =
122 {
123     [ 0] = "prefetchwt1",      [ 1] = "avx512_vbmi",
124     [ 2] = "umip",             [ 3] = "pku",
125     [ 4] = "ospke",            [ 5] = "waitpkg",
126     [ 6] = "avx512_vbmi2",     [ 7] = "cet-ss",
127     [ 8] = "gfni",             [ 9] = "vaes",
128     [10] = "vpclmulqdq",       [11] = "avx512_vnni",
129     [12] = "avx512_bitalg",
130     [14] = "avx512_vpopcntdq",
131     [16] = "tsxldtrk",
132 
133     [22] = "rdpid",
134     /* 24 */                   [25] = "cldemote",
135     /* 26 */                   [27] = "movdiri",
136     [28] = "movdir64b",
137     [30] = "sgx_lc",
138 };
139 
140 static const char *const str_e7d[32] =
141 {
142     [ 8] = "itsc",
143     [10] = "efro",
144 };
145 
146 static const char *const str_e8b[32] =
147 {
148     [ 0] = "clzero",
149     [ 2] = "rstr-fp-err-ptrs",
150 
151     /* [ 8] */            [ 9] = "wbnoinvd",
152 
153     [12] = "ibpb",
154 
155     /* [22] */                 [23] = "ppin",
156 };
157 
158 static const char *const str_7d0[32] =
159 {
160     [ 2] = "avx512_4vnniw", [ 3] = "avx512_4fmaps",
161     [ 4] = "fsrm",
162 
163     /*  8 */                [ 9] = "srbds-ctrl",
164     [10] = "md-clear",
165     /* 12 */                [13] = "tsx-force-abort",
166     [14] = "serialize",
167 
168     [18] = "pconfig",
169     [20] = "cet-ibt",
170 
171     [26] = "ibrsb",         [27] = "stibp",
172     [28] = "l1d_flush",     [29] = "arch_caps",
173     [30] = "core_caps",     [31] = "ssbd",
174 };
175 
176 static const char *const str_7a1[32] =
177 {
178     /* 4 */                 [ 5] = "avx512_bf16",
179 };
180 
181 static const struct {
182     const char *name;
183     const char *abbr;
184     const char *const *strs;
185 } decodes[] =
186 {
187     { "0x00000001.edx",   "1d",  str_1d },
188     { "0x00000001.ecx",   "1c",  str_1c },
189     { "0x80000001.edx",   "e1d", str_e1d },
190     { "0x80000001.ecx",   "e1c", str_e1c },
191     { "0x0000000d:1.eax", "Da1", str_Da1 },
192     { "0x00000007:0.ebx", "7b0", str_7b0 },
193     { "0x00000007:0.ecx", "7c0", str_7c0 },
194     { "0x80000007.edx",   "e7d", str_e7d },
195     { "0x80000008.ebx",   "e8b", str_e8b },
196     { "0x00000007:0.edx", "7d0", str_7d0 },
197     { "0x00000007:1.eax", "7a1", str_7a1 },
198 };
199 
200 #define COL_ALIGN "18"
201 
202 static struct fsinfo {
203     const char *name;
204     uint32_t len;
205     uint32_t *fs;
206 } featuresets[] =
207 {
208     [XEN_SYSCTL_cpu_featureset_host] = { "Host", 0, NULL },
209     [XEN_SYSCTL_cpu_featureset_raw]  = { "Raw",  0, NULL },
210     [XEN_SYSCTL_cpu_featureset_pv]   = { "PV",   0, NULL },
211     [XEN_SYSCTL_cpu_featureset_hvm]  = { "HVM",  0, NULL },
212 };
213 
dump_leaf(uint32_t leaf,const char * const * strs)214 static void dump_leaf(uint32_t leaf, const char *const *strs)
215 {
216     unsigned i;
217 
218     if ( !strs )
219     {
220         printf(" ???");
221         return;
222     }
223 
224     for ( i = 0; i < 32; ++i )
225         if ( leaf & (1u << i) )
226         {
227             if ( strs[i] )
228                 printf(" %s", strs[i]);
229             else
230                 printf(" <%u>", i);
231         }
232 }
233 
decode_featureset(const uint32_t * features,const uint32_t length,const char * name,bool detail)234 static void decode_featureset(const uint32_t *features,
235                               const uint32_t length,
236                               const char *name,
237                               bool detail)
238 {
239     unsigned int i;
240 
241     printf("%-"COL_ALIGN"s        ", name);
242     for ( i = 0; i < length; ++i )
243         printf("%08x%c", features[i],
244                i < length - 1 ? ':' : '\n');
245 
246     if ( !detail )
247         return;
248 
249     for ( i = 0; i < length && i < ARRAY_SIZE(decodes); ++i )
250     {
251         printf("  [%02u] %-"COL_ALIGN"s", i, decodes[i].name ?: "<UNKNOWN>");
252         if ( decodes[i].name )
253             dump_leaf(features[i], decodes[i].strs);
254         printf("\n");
255     }
256 }
257 
get_featureset(xc_interface * xch,unsigned int idx)258 static int get_featureset(xc_interface *xch, unsigned int idx)
259 {
260     struct fsinfo *f = &featuresets[idx];
261 
262     f->len = nr_features;
263     f->fs = calloc(nr_features, sizeof(*f->fs));
264 
265     if ( !f->fs )
266         err(1, "calloc(, featureset)");
267 
268     return xc_get_cpu_featureset(xch, idx, &f->len, f->fs);
269 }
270 
dump_info(xc_interface * xch,bool detail)271 static void dump_info(xc_interface *xch, bool detail)
272 {
273     unsigned int i;
274 
275     printf("nr_features: %u\n", nr_features);
276 
277     if ( !detail )
278     {
279         printf("       %"COL_ALIGN"s ", "KEY");
280         for ( i = 0; i < ARRAY_SIZE(decodes); ++i )
281             printf("%-8s ", decodes[i].abbr ?: "???");
282         printf("\n");
283     }
284 
285     printf("\nStatic sets:\n");
286     decode_featureset(xc_get_static_cpu_featuremask(XC_FEATUREMASK_KNOWN),
287                       nr_features, "Known", detail);
288     decode_featureset(xc_get_static_cpu_featuremask(XC_FEATUREMASK_SPECIAL),
289                       nr_features, "Special", detail);
290     decode_featureset(xc_get_static_cpu_featuremask(XC_FEATUREMASK_PV_MAX),
291                       nr_features, "PV Max", detail);
292     decode_featureset(xc_get_static_cpu_featuremask(XC_FEATUREMASK_PV_DEF),
293                       nr_features, "PV Default", detail);
294     decode_featureset(xc_get_static_cpu_featuremask(XC_FEATUREMASK_HVM_SHADOW_MAX),
295                       nr_features, "HVM Shadow Max", detail);
296     decode_featureset(xc_get_static_cpu_featuremask(XC_FEATUREMASK_HVM_SHADOW_DEF),
297                       nr_features, "HVM Shadow Default", detail);
298     decode_featureset(xc_get_static_cpu_featuremask(XC_FEATUREMASK_HVM_HAP_MAX),
299                       nr_features, "HVM Hap Max", detail);
300     decode_featureset(xc_get_static_cpu_featuremask(XC_FEATUREMASK_HVM_HAP_DEF),
301                       nr_features, "HVM Hap Default", detail);
302 
303     printf("\nDynamic sets:\n");
304     for ( i = 0; i < ARRAY_SIZE(featuresets); ++i )
305     {
306         if ( get_featureset(xch, i) )
307         {
308             if ( errno == EOPNOTSUPP )
309             {
310                 printf("%s featureset not supported by Xen\n",
311                        featuresets[i].name);
312                 continue;
313             }
314 
315             err(1, "xc_get_featureset()");
316         }
317 
318         decode_featureset(featuresets[i].fs, featuresets[i].len,
319                           featuresets[i].name, detail);
320     }
321 
322     for ( i = 0; i < ARRAY_SIZE(featuresets); ++i )
323         free(featuresets[i].fs);
324 }
325 
print_policy(const char * name,xen_cpuid_leaf_t * leaves,uint32_t nr_leaves,xen_msr_entry_t * msrs,uint32_t nr_msrs)326 static void print_policy(const char *name,
327                          xen_cpuid_leaf_t *leaves, uint32_t nr_leaves,
328                          xen_msr_entry_t *msrs, uint32_t nr_msrs)
329 {
330     unsigned int l;
331 
332     printf("%s policy: %u leaves, %u MSRs\n", name, nr_leaves, nr_msrs);
333     printf(" CPUID:\n");
334     printf("  %-8s %-8s -> %-8s %-8s %-8s %-8s\n",
335            "leaf", "subleaf", "eax", "ebx", "ecx", "edx");
336     for ( l = 0; l < nr_leaves; ++l )
337     {
338         /* Skip empty leaves. */
339         if ( !leaves[l].a && !leaves[l].b && !leaves[l].c && !leaves[l].d )
340             continue;
341 
342         printf("  %08x:%08x -> %08x:%08x:%08x:%08x\n",
343                leaves[l].leaf, leaves[l].subleaf,
344                leaves[l].a, leaves[l].b, leaves[l].c, leaves[l].d);
345     }
346 
347     printf(" MSRs:\n");
348     printf("  %-8s -> %-16s\n", "index", "value");
349     for ( l = 0; l < nr_msrs; ++l )
350         printf("  %08x -> %016"PRIx64"\n",
351                msrs[l].idx, msrs[l].val);
352 }
353 
main(int argc,char ** argv)354 int main(int argc, char **argv)
355 {
356     enum { MODE_UNKNOWN, MODE_INFO, MODE_DETAIL, MODE_INTERPRET, MODE_POLICY }
357     mode = MODE_UNKNOWN;
358     int domid = -1;
359 
360     nr_features = xc_get_cpu_featureset_size();
361 
362     for ( ;; )
363     {
364         const char *tmp_optarg;
365         int option_index = 0, c;
366         static const struct option long_options[] =
367         {
368             { "help", no_argument, NULL, 'h' },
369             { "info", no_argument, NULL, 'i' },
370             { "detail", no_argument, NULL, 'd' },
371             { "verbose", no_argument, NULL, 'v' },
372             { "policy", optional_argument, NULL, 'p' },
373             { NULL, 0, NULL, 0 },
374         };
375 
376         c = getopt_long(argc, argv, "hidvp::", long_options, &option_index);
377 
378         if ( c == -1 )
379             break;
380 
381         switch ( c )
382         {
383         default:
384             printf("Bad option '%c'\n", c);
385             /* Fallthough */
386         case 'h':
387             printf("Usage: %s [ info | detail | <featureset>* ]\n", argv[0]);
388             return 0;
389 
390         case 'i':
391             mode = MODE_INFO;
392             break;
393 
394         case 'p':
395             mode = MODE_POLICY;
396 
397             tmp_optarg = optarg;
398 
399             /* Make "--policy $DOMID" and "-p $DOMID" work. */
400             if ( !optarg && optind < argc &&
401                  argv[optind] != NULL && argv[optind][0] != '\0' &&
402                  argv[optind][0] != '-' )
403                 tmp_optarg = argv[optind++];
404 
405             if ( tmp_optarg )
406             {
407                 char *endptr;
408 
409                 errno = 0;
410                 domid = strtol(tmp_optarg, &endptr, 0);
411                 if ( errno || endptr == tmp_optarg )
412                     err(1, "strtol(%s,,)", tmp_optarg);
413             }
414             break;
415 
416         case 'd':
417         case 'v':
418             mode = MODE_DETAIL;
419             break;
420         }
421     }
422 
423     if ( mode == MODE_UNKNOWN )
424     {
425         if ( optind == argc )
426             mode = MODE_INFO;
427         else if ( optind < argc )
428         {
429             if ( !strcmp(argv[optind], "info") )
430             {
431                 mode = MODE_INFO;
432                 optind++;
433             }
434             else if ( !strcmp(argv[optind], "detail") )
435             {
436                 mode = MODE_DETAIL;
437                 optind++;
438             }
439             else
440                 mode = MODE_INTERPRET;
441         }
442         else
443             mode = MODE_INTERPRET;
444     }
445 
446     if ( mode == MODE_POLICY )
447     {
448         static const char *const sys_policies[] = {
449             [ XEN_SYSCTL_cpu_policy_raw ]          = "Raw",
450             [ XEN_SYSCTL_cpu_policy_host ]         = "Host",
451             [ XEN_SYSCTL_cpu_policy_pv_max ]       = "PV Max",
452             [ XEN_SYSCTL_cpu_policy_hvm_max ]      = "HVM Max",
453             [ XEN_SYSCTL_cpu_policy_pv_default ]   = "PV Default",
454             [ XEN_SYSCTL_cpu_policy_hvm_default ]  = "HVM Default",
455         };
456         xen_cpuid_leaf_t *leaves;
457         xen_msr_entry_t *msrs;
458         uint32_t i, max_leaves, max_msrs;
459 
460         xc_interface *xch = xc_interface_open(0, 0, 0);
461 
462         if ( !xch )
463             err(1, "xc_interface_open");
464 
465         if ( xc_get_cpu_policy_size(xch, &max_leaves, &max_msrs) )
466             err(1, "xc_get_cpu_policy_size(...)");
467         if ( domid == -1 )
468             printf("Xen reports there are maximum %u leaves and %u MSRs\n",
469                    max_leaves, max_msrs);
470 
471         leaves = calloc(max_leaves, sizeof(xen_cpuid_leaf_t));
472         if ( !leaves )
473             err(1, "calloc(max_leaves)");
474         msrs = calloc(max_msrs, sizeof(xen_msr_entry_t));
475         if ( !msrs )
476             err(1, "calloc(max_msrs)");
477 
478         if ( domid != -1 )
479         {
480             char name[20];
481             uint32_t nr_leaves = max_leaves;
482             uint32_t nr_msrs = max_msrs;
483 
484             if ( xc_get_domain_cpu_policy(xch, domid, &nr_leaves, leaves,
485                                           &nr_msrs, msrs) )
486                 err(1, "xc_get_domain_cpu_policy(, %d, %d,, %d,)",
487                     domid, nr_leaves, nr_msrs);
488 
489             snprintf(name, sizeof(name), "Domain %d", domid);
490             print_policy(name, leaves, nr_leaves, msrs, nr_msrs);
491         }
492         else
493         {
494             /* Get system policies */
495             for ( i = 0; i < ARRAY_SIZE(sys_policies); ++i )
496             {
497                 uint32_t nr_leaves = max_leaves;
498                 uint32_t nr_msrs = max_msrs;
499 
500                 if ( xc_get_system_cpu_policy(xch, i, &nr_leaves, leaves,
501                                               &nr_msrs, msrs) )
502                 {
503                     if ( errno == EOPNOTSUPP )
504                     {
505                         printf("%s policy not supported by Xen\n",
506                                sys_policies[i]);
507                         continue;
508                     }
509 
510                     err(1, "xc_get_system_cpu_policy(, %s,,)", sys_policies[i]);
511                 }
512 
513                 print_policy(sys_policies[i], leaves, nr_leaves,
514                              msrs, nr_msrs);
515             }
516         }
517 
518         free(leaves);
519         free(msrs);
520         xc_interface_close(xch);
521     }
522     else if ( mode == MODE_INFO || mode == MODE_DETAIL )
523     {
524         xc_interface *xch = xc_interface_open(0, 0, 0);
525 
526         if ( !xch )
527             err(1, "xc_interface_open");
528 
529         if ( xc_get_cpu_featureset(xch, 0, &nr_features, NULL) )
530             err(1, "xc_get_featureset(, NULL)");
531 
532         dump_info(xch, mode == MODE_DETAIL);
533 
534         xc_interface_close(xch);
535     }
536     else
537     {
538         uint32_t fs[nr_features + 1];
539 
540         while ( optind < argc )
541         {
542             char *ptr = argv[optind++];
543             unsigned int i = 0;
544             int offset;
545 
546             memset(fs, 0, sizeof(fs));
547 
548             while ( sscanf(ptr, "%x%n", &fs[i], &offset) == 1 )
549             {
550                 i++;
551                 ptr += offset;
552 
553                 if ( i == nr_features )
554                     break;
555 
556                 if ( *ptr == ':' )
557                 {
558                     ptr++; continue;
559                 }
560                 break;
561             }
562 
563             if ( !i )
564             {
565                 fprintf(stderr, "'%s' unrecognized - skipping\n", ptr);
566                 continue;
567             }
568 
569             if ( *ptr )
570                 fprintf(stderr, "'%s' unrecognized - ignoring\n", ptr);
571 
572             decode_featureset(fs, i, "Raw", true);
573         }
574     }
575 
576     return 0;
577 }
578 
579 /*
580  * Local variables:
581  * mode: C
582  * c-file-style: "BSD"
583  * c-basic-offset: 4
584  * tab-width: 4
585  * indent-tabs-mode: nil
586  * End:
587  */
588