1 /**
2  * Program entry point.
3  *
4  * NOTE: printf is mainly used for two reasons. First, fputs with stdout
5  *       crashes on Windows. Secondly, line buffering.
6  *
7  * Authors: dd86k (dd@dax.moe)
8  * Copyright: © 2016-2021 dd86k
9  * License: MIT
10  */
11 module main;
12 
13 import ddcpuid;
14 
15 private:
16 @system:
17 extern (C):
18 
19 int strcmp(scope const char*, scope const char*);
20 int puts(scope const char*);
21 int putchar(int);
22 int atoi(scope const char*);
23 
24 static if (__VERSION__ >= 2092) {
25 	pragma(printf)
26 	int printf(scope const char*, ...);
27 } else {
28 	int printf(scope const char*, ...);
29 }
30 
31 /// Compiler version template for betterC usage
32 template CVER(int v) {
33 	enum CVER =
34 		cast(char)((v / 1000) + '0') ~
35 		"." ~
36 		cast(char)(((v % 1000) / 100) + '0') ~
37 		cast(char)(((v % 100) / 10) + '0') ~
38 		cast(char)((v % 10) + '0');
39 }
40 
41 /// Make a bit mask of one bit at n position
42 template BIT(int n) if (n <= 31) { enum uint BIT = 1 << n; }
43 
44 enum : uint {
45 	MAX_LEAF	= 0x20, /// Maximum leaf override
46 	MAX_VLEAF	= 0x4000_0020, /// Maximum virt leaf override
47 	MAX_ELEAF	= 0x8000_0020, /// Maximum extended leaf override
48 }
49 
50 /// Command-line options
51 struct options_t {
52 	uint maxsub;	/// Maximum subleaf for -r (-s)
53 	bool table;	/// Raw table (-r)
54 	bool override_;	/// Override leaves (-o)
55 	bool optlevel;	/// Get x86-64 optimization feature level
56 }
57 
58 /// print help page
59 void clih() {
60 	puts(
61 	"x86/AMD64 CPUID information tool\n"~
62 	"\n"~
63 	"USAGE\n"~
64 	"  ddcpuid [OPTIONS...]\n"~
65 	"\n"~
66 	"OPTIONS\n"~
67 	"  -r, --table   Show raw CPUID data in a table\n"~
68 	"  -s            Set subleaf (ECX) input value with -r\n"~
69 	"  -o            Override maximum leaves to 20h, 4000_0020h, and 8000_0020h\n"~
70 	"  -l, --level   Print the processor's feature level\n"~
71 	"\n"~
72 	"PAGES\n"~
73 	"  --version    Print version screen and quit\n"~
74 	"  --ver        Print version and quit\n"~
75 	"  -h, --help   Print this help screen and quit"
76 	);
77 }
78 
79 /// print version page
80 void cliv() {
81 	puts(
82 	"ddcpuid-"~DDCPUID_PLATFORM~" v"~DDCPUID_VERSION~" ("~__TIMESTAMP__~")\n"~
83 	"Copyright (c) 2016-2021 dd86k <dd@dax.moe>\n"~
84 	"License: MIT License <http://opensource.org/licenses/MIT>\n"~
85 	"Project page: <https://github.com/dd86k/ddcpuid>\n"~
86 	"Compiler: "~__VENDOR__~" v"~CVER!(__VERSION__)
87 	);
88 }
89 
90 /// Print cpuid table entry into stdout.
91 /// Params:
92 /// 	leaf = EAX input
93 /// 	sub = ECX input
94 pragma(inline, false) // ldc optimization thing
95 void printcpuid(uint leaf, uint sub) {
96 	REGISTERS regs = void;
97 	asmcpuid(regs, leaf, sub);
98 	with (regs)
99 	printf("| %8x | %8x | %8x | %8x | %8x | %8x |\n",
100 		leaf, sub, eax, ebx, ecx, edx);
101 }
102 
103 int main(int argc, const(char) **argv) {
104 	options_t opts;	/// Command-line options
105 	
106 	const(char) *arg = void;
107 	for (int argi = 1; argi < argc; ++argi) {
108 		if (argv[argi][1] == '-') { // Long arguments
109 			arg = argv[argi] + 2;
110 			if (strcmp(arg, "level") == 0) { opts.optlevel = true; continue; }
111 			if (strcmp(arg, "version") == 0) { cliv; return 0; }
112 			if (strcmp(arg, "ver") == 0) { puts(DDCPUID_VERSION); return 0; }
113 			if (strcmp(arg, "help") == 0) { clih; return 0; }
114 			printf("Unknown parameter: '%s'\n", arg);
115 			return 1;
116 		} else if (argv[argi][0] == '-') { // Short arguments
117 			arg = argv[argi] + 1;
118 			char o = void;
119 			while ((o = *arg) != 0) {
120 				++arg;
121 				switch (o) {
122 				case 'l': opts.optlevel = true; continue;
123 				case 'o': opts.override_ = true; continue;
124 				case 'r': opts.table = true; continue;
125 				case 's': //TODO: Consider supporting -sN syntax
126 					if (++argi >= argc) {
127 						puts("Missing parameter: sub-leaf (-s)");
128 						return 1;
129 					}
130 					opts.maxsub = atoi(argv[argi]);
131 					continue;
132 				case 'h': clih; return 0;
133 				case 'V': cliv; return 0;
134 				default:
135 					printf("Unknown parameter: '-%c'\n", o);
136 					return 1;
137 				}
138 			}
139 		} // else if
140 	} // for
141 	
142 	CPUINFO info;
143 	
144 	if (opts.override_ == false) {
145 		getLeaves(info);
146 	} else {
147 		info.max_leaf = MAX_LEAF;
148 		info.max_virt_leaf = MAX_VLEAF;
149 		info.max_ext_leaf = MAX_ELEAF;
150 	}
151 	
152 	if (opts.table) { // -r
153 		puts(
154 		"| Leaf     | Sub-leaf | EAX      | EBX      | ECX      | EDX      |\n"~
155 		"|----------|----------|----------|----------|----------|----------|"
156 		);
157 		
158 		// Normal
159 		uint l = void, s = void;
160 		for (l = 0; l <= info.max_leaf; ++l)
161 			for (s = 0; s <= opts.maxsub; ++s)
162 				printcpuid(l, s);
163 		
164 		// Paravirtualization
165 		if (info.max_virt_leaf > 0x4000_0000)
166 		for (l = 0x4000_0000; l <= info.max_virt_leaf; ++l)
167 			for (s = 0; s <= opts.maxsub; ++s)
168 				printcpuid(l, s);
169 		
170 		// Extended
171 		for (l = 0x8000_0000; l <= info.max_ext_leaf; ++l)
172 			for (s = 0; s <= opts.maxsub; ++s)
173 				printcpuid(l, s);
174 		return 0;
175 	}
176 	
177 	getInfo(info);
178 	
179 	if (opts.optlevel) {
180 		// That's a story for another time
181 		if (info.ext.x86_64 == false)	goto L_X86_64_NONE;
182 		
183 		// v4
184 		if (info.ext.avx512f && info.ext.avx512bw &&
185 			info.ext.avx512cd && info.ext.avx512dq &&
186 			info.ext.avx512vl) {
187 			puts("x86-64-v4");
188 			return 0;
189 		}
190 		
191 		// v3
192 		if (info.ext.avx2 && info.ext.avx &&
193 			info.ext.bmi2 && info.ext.bmi1 &&
194 			info.ext.f16c && info.ext.fma3 &&
195 			info.extras.lzcnt && info.extras.movbe &&
196 			info.extras.osxsave) {
197 			puts("x86-64-v3");
198 			return 0;
199 		}
200 		
201 		// v2
202 		if (info.ext.sse42 && info.ext.sse41 &&
203 			info.ext.ssse3 && info.ext.sse3 &&
204 			info.ext.lahf64 && info.extras.popcnt &&
205 			info.extras.cmpxchg16b) {
206 			puts("x86-64-v2");
207 			return 0;
208 		}
209 		
210 		// baseline
211 		if (info.ext.sse2 && info.ext.sse &&
212 			info.ext.mmx && info.extras.fxsr &&
213 			info.extras.cmpxchg8b && info.extras.cmov &&
214 			info.ext.fpu && info.extras.syscall) {
215 			puts("x86-64"); // v1/base
216 			return 0;
217 		}
218 		
219 L_X86_64_NONE:
220 		puts("none");
221 		return 0;
222 	}
223 	
224 	// NOTE: .ptr crash with GDC -O3
225 	//       glibc!__strlen_sse2 (in printf)
226 	char *vendor = cast(char*)info.vendor;
227 	char *brand  = cast(char*)info.brand;
228 	
229 	// Brand string left space trimming
230 	// Extremely common in Intel but let's also do it for others
231 	while (*brand == ' ') ++brand;
232 	
233 	//
234 	// ANCHOR Processor basic information
235 	//
236 	
237 	printf(
238 	"Vendor      : %.12s\n"~
239 	"Brand       : %.48s\n"~
240 	"Identifier  : Family %u (0x%x) [0x%x:0x%x] Model %u (0x%x) [0x%x:0x%x] Stepping %u\n"~
241 	"Cores       : %u threads\n"~
242 	"Extensions  :",
243 	vendor, brand,
244 	info.family, info.family, info.family_base, info.family_ext,
245 	info.model, info.model, info.model_base, info.model_ext,
246 	info.stepping,
247 	info.cores.logical
248 	);
249 	
250 	if (info.ext.fpu) {
251 		printf(" x87/FPU");
252 		if (info.ext.f16c) printf(" +F16C");
253 	}
254 	if (info.ext.mmx) {
255 		printf(" MMX");
256 		if (info.ext.mmxext) printf(" ExtMMX");
257 	}
258 	if (info.ext._3dnow) {
259 		printf(" 3DNow!");
260 		if (info.ext._3dnowext) printf(" Ext3DNow!");
261 	}
262 	if (info.ext.sse) {
263 		printf(" SSE");
264 		if (info.ext.sse2) printf(" SSE2");
265 		if (info.ext.sse3) printf(" SSE3");
266 		if (info.ext.ssse3) printf(" SSSE3");
267 		if (info.ext.sse41) printf(" SSE4.1");
268 		if (info.ext.sse42) printf(" SSE4.2");
269 		if (info.ext.sse4a) printf(" SSE4a");
270 		if (info.ext.xop) printf(" XOP");
271 	}
272 	if (info.ext.x86_64) {
273 		switch (info.vendor_id) {
274 		case Vendor.Intel: printf(" Intel64/x86-64"); break;
275 		case Vendor.AMD: printf(" AMD64/x86-64"); break;
276 		default: printf(" x86-64");
277 		}
278 		if (info.ext.lahf64)
279 			printf(" +LAHF64");
280 	}
281 	if (info.virt.available)
282 		switch (info.vendor_id) {
283 		case Vendor.Intel: printf(" VT-x/VMX"); break;
284 		case Vendor.AMD: // SVM
285 			printf(" AMD-V/VMX");
286 			if (info.virt.version_)
287 				printf(":v%u", info.virt.version_);
288 			break;
289 		case Vendor.VIA: printf(" VIA-VT/VMX"); break;
290 		default: printf(" VMX");
291 		}
292 	if (info.ext.aes_ni) printf(" AES-NI");
293 	if (info.ext.avx) printf(" AVX");
294 	if (info.ext.avx2) printf(" AVX2");
295 	if (info.ext.avx512f) {
296 		printf(" AVX512F");
297 		if (info.ext.avx512er) printf(" AVX512ER");
298 		if (info.ext.avx512pf) printf(" AVX512PF");
299 		if (info.ext.avx512cd) printf(" AVX512CD");
300 		if (info.ext.avx512dq) printf(" AVX512DQ");
301 		if (info.ext.avx512bw) printf(" AVX512BW");
302 		if (info.ext.avx512vl) printf(" AVX512VL");
303 		if (info.ext.avx512_ifma) printf(" AVX512_IFMA");
304 		if (info.ext.avx512_vbmi) printf(" AVX512_VBMI");
305 		if (info.ext.avx512_4vnniw) printf(" AVX512_4VNNIW");
306 		if (info.ext.avx512_4fmaps) printf(" AVX512_4FMAPS");
307 		if (info.ext.avx512_vbmi2) printf(" AVX512_VBMI2");
308 		if (info.ext.avx512_gfni) printf(" AVX512_GFNI");
309 		if (info.ext.avx512_vaes) printf(" AVX512_VAES");
310 		if (info.ext.avx512_vnni) printf(" AVX512_VNNI");
311 		if (info.ext.avx512_bitalg) printf(" AVX512_BITALG");
312 		if (info.ext.avx512_bf16) printf(" AVX512_BF16");
313 		if (info.ext.avx512_vp2intersect) printf(" AVX512_VP2INTERSECT");
314 	}
315 	if (info.ext.adx) printf(" ADX");
316 	if (info.ext.sha) printf(" SHA");
317 	if (info.ext.fma3) printf(" FMA3");
318 	if (info.ext.fma4) printf(" FMA4");
319 	if (info.ext.tbm) printf(" TBM");
320 	if (info.ext.bmi1) printf(" BMI1");
321 	if (info.ext.bmi2) printf(" BMI2");
322 	if (info.ext.waitpkg) printf(" WAITPKG");
323 	if (info.ext.amx) printf(" AMX");
324 	if (info.ext.amx_bf16) printf(" +BF16");
325 	if (info.ext.amx_int8) printf(" +INT8");
326 	if (info.ext.amx_xtilecfg) printf(" +XTILECFG");
327 	if (info.ext.amx_xtiledata) printf(" +XTILEDATA");
328 	if (info.ext.amx_xfd) printf(" +XFD");
329 	
330 	//
331 	// ANCHOR Extra/lone instructions
332 	//
333 	
334 	printf("\nExtra       :");
335 	if (info.extras.monitor) {
336 		printf(" MONITOR+MWAIT");
337 		if (info.extras.mwait_min)
338 			printf(" +MIN=%u +MAX=%u",
339 				info.extras.mwait_min, info.extras.mwait_max);
340 		if (info.extras.monitorx) printf(" MONITORX+MWAITX");
341 	}
342 	if (info.extras.pclmulqdq) printf(" PCLMULQDQ");
343 	if (info.extras.cmpxchg8b) printf(" CMPXCHG8B");
344 	if (info.extras.cmpxchg16b) printf(" CMPXCHG16B");
345 	if (info.extras.movbe) printf(" MOVBE");
346 	if (info.extras.rdrand) printf(" RDRAND");
347 	if (info.extras.rdseed) printf(" RDSEED");
348 	if (info.extras.rdmsr) printf(" RDMSR+WRMSR");
349 	if (info.extras.sysenter) printf(" SYSENTER+SYSEXIT");
350 	if (info.extras.syscall) printf(" SYSCALL+SYSRET");
351 	if (info.extras.rdtsc) {
352 		printf(" RDTSC");
353 		if (info.extras.rdtsc_deadline)
354 			printf(" +TSC-Deadline");
355 		if (info.extras.rdtsc_invariant)
356 			printf(" +TSC-Invariant");
357 	}
358 	if (info.extras.rdtscp) printf(" RDTSCP");
359 	if (info.extras.rdpid) printf(" RDPID");
360 	if (info.extras.cmov) {
361 		printf(" CMOV");
362 		if (info.ext.fpu) printf(" FCOMI+FCMOV");
363 	}
364 	if (info.extras.lzcnt) printf(" LZCNT");
365 	if (info.extras.popcnt) printf(" POPCNT");
366 	if (info.extras.xsave) printf(" XSAVE+XRSTOR");
367 	if (info.extras.osxsave) printf(" XSETBV+XGETBV");
368 	if (info.extras.fxsr) printf(" FXSAVE+FXRSTOR");
369 	if (info.extras.pconfig) printf(" PCONFIG");
370 	if (info.extras.cldemote) printf(" CLDEMOTE");
371 	if (info.extras.movdiri) printf(" MOVDIRI");
372 	if (info.extras.movdir64b) printf(" MOVDIR64B");
373 	if (info.extras.enqcmd) printf(" ENQCMD");
374 	if (info.extras.skinit) printf(" SKINIT+STGI");
375 	if (info.extras.serialize) printf(" SERIALIZE");
376 	
377 	//
378 	// ANCHOR Vendor specific technologies
379 	//
380 	
381 	printf("\nTechnologies:");
382 	
383 	switch (info.vendor_id) {
384 	case Vendor.Intel:
385 		if (info.tech.eist) printf(" EIST");
386 		if (info.tech.turboboost)
387 			printf(info.tech.turboboost30 ?
388 				" TurboBoot-3.0" : " TurboBoost");
389 		if (info.mem.tsx) {
390 			printf(" TSX");
391 			if (info.mem.hle)
392 				printf(" +HLE");
393 			if (info.mem.rtm)
394 				printf(" +RTM");
395 			if (info.mem.tsxldtrk)
396 				printf(" +TSXLDTRK");
397 		}
398 		if (info.tech.smx) printf(" Intel-TXT/SMX");
399 		if (info.tech.sgx) printf(" SGX");
400 		break;
401 	case Vendor.AMD:
402 		if (info.tech.turboboost) printf(" Core-Performance-Boost");
403 		break;
404 	default:
405 	}
406 	if (info.tech.htt) printf(" HTT");
407 	
408 	//
409 	// ANCHOR Cache information
410 	//
411 	
412 	printf("\nCache       :");
413 	if (info.cache.clflush)
414 		printf(" CLFLUSH=%uB", info.cache.clflush_linesize << 3);
415 	if (info.cache.clflushopt) printf(" CLFLUSHOPT");
416 	if (info.cache.cnxt_id) printf(" CNXT_ID");
417 	if (info.cache.ss) printf(" SS");
418 	if (info.cache.prefetchw) printf(" PREFETCHW");
419 	if (info.cache.invpcid) printf(" INVPCID");
420 	if (info.cache.wbnoinvd) printf(" WBNOINVD");
421 	
422 	for (uint i; i < info.cache.levels; ++i) {
423 		CACHEINFO *cache = &info.cache.level[i];
424 		char c = 'K';
425 		if (cache.size >= 1024) {
426 			cache.size >>= 10;
427 			c = 'M';
428 		}
429 		printf("\n\tL%u-%c: %ux %4u %ciB, %u ways, %u parts, %u B, %u sets",
430 			cache.level, cache.type, cache.sharedCores, cache.size, c,
431 			cache.ways, cache.partitions, cache.linesize, cache.sets
432 		);
433 		if (cache.feat & BIT!(0)) printf(" +SI"); // Self Initiative
434 		if (cache.feat & BIT!(1)) printf(" +FA"); // Fully Associative
435 		if (cache.feat & BIT!(2)) printf(" +NWBV"); // No Write-Back Validation
436 		if (cache.feat & BIT!(3)) printf(" +CI"); // Cache Inclusive
437 		if (cache.feat & BIT!(4)) printf(" +CCI"); // Complex Cache Indexing
438 	}
439 	
440 	printf("\nACPI        :");
441 	if (info.acpi.available) printf(" ACPI");
442 	if (info.acpi.apic) printf(" APIC");
443 	if (info.acpi.x2apic) printf(" x2APIC");
444 	if (info.acpi.arat) printf(" ARAT");
445 	if (info.acpi.tm) printf(" TM");
446 	if (info.acpi.tm2) printf(" TM2");
447 	printf(" APIC-ID=%u", info.acpi.apic_id);
448 	if (info.acpi.max_apic_id) printf(" MAX-ID=%u", info.acpi.max_apic_id);
449 	
450 	printf("\nVirtual     :");
451 	if (info.virt.vme) printf(" VME");
452 	if (info.virt.apivc) printf(" APICv");
453 	
454 	// Paravirtualization
455 	if (info.virt.vendor_id) {
456 		// See earlier NOTE
457 		char *virtvendor = cast(char*)info.virt.vendor;
458 		printf(" HOST=%.12s", virtvendor);
459 	}
460 	switch (info.virt.vendor_id) {
461 	case VirtVendor.VBoxMin:
462 		if (info.virt.vbox_tsc_freq_khz)
463 			printf(" TSC_FREQ_KHZ=%u", info.virt.vbox_tsc_freq_khz);
464 		if (info.virt.vbox_apic_freq_khz)
465 			printf(" APIC_FREQ_KHZ=%u", info.virt.vbox_apic_freq_khz);
466 		break;
467 	case VirtVendor.HyperV:
468 		printf(" OPENSOURCE=%d VENDOR_ID=%d OS=%d MAJOR=%d MINOR=%d SERVICE=%d BUILD=%d",
469 			info.virt.hv_guest_opensource,
470 			info.virt.hv_guest_vendor_id,
471 			info.virt.hv_guest_os,
472 			info.virt.hv_guest_major,
473 			info.virt.hv_guest_minor,
474 			info.virt.hv_guest_service,
475 			info.virt.hv_guest_build);
476 		if (info.virt.hv_base_feat_vp_runtime_msr) printf(" HV_BASE_FEAT_VP_RUNTIME_MSR");
477 		if (info.virt.hv_base_feat_part_time_ref_count_msr) printf(" HV_BASE_FEAT_PART_TIME_REF_COUNT_MSR");
478 		if (info.virt.hv_base_feat_basic_synic_msrs) printf(" HV_BASE_FEAT_BASIC_SYNIC_MSRS");
479 		if (info.virt.hv_base_feat_stimer_msrs) printf(" HV_BASE_FEAT_STIMER_MSRS");
480 		if (info.virt.hv_base_feat_apic_access_msrs) printf(" HV_BASE_FEAT_APIC_ACCESS_MSRS");
481 		if (info.virt.hv_base_feat_hypercall_msrs) printf(" HV_BASE_FEAT_HYPERCALL_MSRS");
482 		if (info.virt.hv_base_feat_vp_id_msr) printf(" HV_BASE_FEAT_VP_ID_MSR");
483 		if (info.virt.hv_base_feat_virt_sys_reset_msr) printf(" HV_BASE_FEAT_VIRT_SYS_RESET_MSR");
484 		if (info.virt.hv_base_feat_stat_pages_msr) printf(" HV_BASE_FEAT_STAT_PAGES_MSR");
485 		if (info.virt.hv_base_feat_part_ref_tsc_msr) printf(" HV_BASE_FEAT_PART_REF_TSC_MSR");
486 		if (info.virt.hv_base_feat_guest_idle_state_msr) printf(" HV_BASE_FEAT_GUEST_IDLE_STATE_MSR");
487 		if (info.virt.hv_base_feat_timer_freq_msrs) printf(" HV_BASE_FEAT_TIMER_FREQ_MSRS");
488 		if (info.virt.hv_base_feat_debug_msrs) printf(" HV_BASE_FEAT_DEBUG_MSRS");
489 		if (info.virt.hv_part_flags_create_part) printf(" HV_PART_FLAGS_CREATE_PART");
490 		if (info.virt.hv_part_flags_access_part_id) printf(" HV_PART_FLAGS_ACCESS_PART_ID");
491 		if (info.virt.hv_part_flags_access_memory_pool) printf(" HV_PART_FLAGS_ACCESS_MEMORY_POOL");
492 		if (info.virt.hv_part_flags_adjust_msg_buffers) printf(" HV_PART_FLAGS_ADJUST_MSG_BUFFERS");
493 		if (info.virt.hv_part_flags_post_msgs) printf(" HV_PART_FLAGS_POST_MSGS");
494 		if (info.virt.hv_part_flags_signal_events) printf(" HV_PART_FLAGS_SIGNAL_EVENTS");
495 		if (info.virt.hv_part_flags_create_port) printf(" HV_PART_FLAGS_CREATE_PORT");
496 		if (info.virt.hv_part_flags_connect_port) printf(" HV_PART_FLAGS_CONNECT_PORT");
497 		if (info.virt.hv_part_flags_access_stats) printf(" HV_PART_FLAGS_ACCESS_STATS");
498 		if (info.virt.hv_part_flags_debugging) printf(" HV_PART_FLAGS_DEBUGGING");
499 		if (info.virt.hv_part_flags_cpu_mgmt) printf(" HV_PART_FLAGS_CPU_MGMT");
500 		if (info.virt.hv_part_flags_cpu_profiler) printf(" HV_PART_FLAGS_CPU_PROFILER");
501 		if (info.virt.hv_part_flags_expanded_stack_walk) printf(" HV_PART_FLAGS_EXPANDED_STACK_WALK");
502 		if (info.virt.hv_part_flags_access_vsm) printf(" HV_PART_FLAGS_ACCESS_VSM");
503 		if (info.virt.hv_part_flags_access_vp_regs) printf(" HV_PART_FLAGS_ACCESS_VP_REGS");
504 		if (info.virt.hv_part_flags_extended_hypercalls) printf(" HV_PART_FLAGS_EXTENDED_HYPERCALLS");
505 		if (info.virt.hv_part_flags_start_vp) printf(" HV_PART_FLAGS_START_VP");
506 		if (info.virt.hv_pm_max_cpu_power_state_c0) printf(" HV_PM_MAX_CPU_POWER_STATE_C0");
507 		if (info.virt.hv_pm_max_cpu_power_state_c1) printf(" HV_PM_MAX_CPU_POWER_STATE_C1");
508 		if (info.virt.hv_pm_max_cpu_power_state_c2) printf(" HV_PM_MAX_CPU_POWER_STATE_C2");
509 		if (info.virt.hv_pm_max_cpu_power_state_c3) printf(" HV_PM_MAX_CPU_POWER_STATE_C3");
510 		if (info.virt.hv_pm_hpet_reqd_for_c3) printf(" HV_PM_HPET_REQD_FOR_C3");
511 		if (info.virt.hv_misc_feat_mwait) printf(" HV_MISC_FEAT_MWAIT");
512 		if (info.virt.hv_misc_feat_guest_debugging) printf(" HV_MISC_FEAT_GUEST_DEBUGGING");
513 		if (info.virt.hv_misc_feat_perf_mon) printf(" HV_MISC_FEAT_PERF_MON");
514 		if (info.virt.hv_misc_feat_pcpu_dyn_part_event) printf(" HV_MISC_FEAT_PCPU_DYN_PART_EVENT");
515 		if (info.virt.hv_misc_feat_xmm_hypercall_input) printf(" HV_MISC_FEAT_XMM_HYPERCALL_INPUT");
516 		if (info.virt.hv_misc_feat_guest_idle_state) printf(" HV_MISC_FEAT_GUEST_IDLE_STATE");
517 		if (info.virt.hv_misc_feat_hypervisor_sleep_state) printf(" HV_MISC_FEAT_HYPERVISOR_SLEEP_STATE");
518 		if (info.virt.hv_misc_feat_query_numa_distance) printf(" HV_MISC_FEAT_QUERY_NUMA_DISTANCE");
519 		if (info.virt.hv_misc_feat_timer_freq) printf(" HV_MISC_FEAT_TIMER_FREQ");
520 		if (info.virt.hv_misc_feat_inject_synmc_xcpt) printf(" HV_MISC_FEAT_INJECT_SYNMC_XCPT");
521 		if (info.virt.hv_misc_feat_guest_crash_msrs) printf(" HV_MISC_FEAT_GUEST_CRASH_MSRS");
522 		if (info.virt.hv_misc_feat_debug_msrs) printf(" HV_MISC_FEAT_DEBUG_MSRS");
523 		if (info.virt.hv_misc_feat_npiep1) printf(" HV_MISC_FEAT_NPIEP1");
524 		if (info.virt.hv_misc_feat_disable_hypervisor) printf(" HV_MISC_FEAT_DISABLE_HYPERVISOR");
525 		if (info.virt.hv_misc_feat_ext_gva_range_for_flush_va_list) printf(" HV_MISC_FEAT_EXT_GVA_RANGE_FOR_FLUSH_VA_LIST");
526 		if (info.virt.hv_misc_feat_hypercall_output_xmm) printf(" HV_MISC_FEAT_HYPERCALL_OUTPUT_XMM");
527 		if (info.virt.hv_misc_feat_sint_polling_mode) printf(" HV_MISC_FEAT_SINT_POLLING_MODE");
528 		if (info.virt.hv_misc_feat_hypercall_msr_lock) printf(" HV_MISC_FEAT_HYPERCALL_MSR_LOCK");
529 		if (info.virt.hv_misc_feat_use_direct_synth_msrs) printf(" HV_MISC_FEAT_USE_DIRECT_SYNTH_MSRS");
530 		if (info.virt.hv_hint_hypercall_for_process_switch) printf(" HV_HINT_HYPERCALL_FOR_PROCESS_SWITCH");
531 		if (info.virt.hv_hint_hypercall_for_tlb_flush) printf(" HV_HINT_HYPERCALL_FOR_TLB_FLUSH");
532 		if (info.virt.hv_hint_hypercall_for_tlb_shootdown) printf(" HV_HINT_HYPERCALL_FOR_TLB_SHOOTDOWN");
533 		if (info.virt.hv_hint_msr_for_apic_access) printf(" HV_HINT_MSR_FOR_APIC_ACCESS");
534 		if (info.virt.hv_hint_msr_for_sys_reset) printf(" HV_HINT_MSR_FOR_SYS_RESET");
535 		if (info.virt.hv_hint_relax_time_checks) printf(" HV_HINT_RELAX_TIME_CHECKS");
536 		if (info.virt.hv_hint_dma_remapping) printf(" HV_HINT_DMA_REMAPPING");
537 		if (info.virt.hv_hint_interrupt_remapping) printf(" HV_HINT_INTERRUPT_REMAPPING");
538 		if (info.virt.hv_hint_x2apic_msrs) printf(" HV_HINT_X2APIC_MSRS");
539 		if (info.virt.hv_hint_deprecate_auto_eoi) printf(" HV_HINT_DEPRECATE_AUTO_EOI");
540 		if (info.virt.hv_hint_synth_cluster_ipi_hypercall) printf(" HV_HINT_SYNTH_CLUSTER_IPI_HYPERCALL");
541 		if (info.virt.hv_hint_ex_proc_masks_interface) printf(" HV_HINT_EX_PROC_MASKS_INTERFACE");
542 		if (info.virt.hv_hint_nested_hyperv) printf(" HV_HINT_NESTED_HYPERV");
543 		if (info.virt.hv_hint_int_for_mbec_syscalls) printf(" HV_HINT_INT_FOR_MBEC_SYSCALLS");
544 		if (info.virt.hv_hint_nested_enlightened_vmcs_interface) printf(" HV_HINT_NESTED_ENLIGHTENED_VMCS_INTERFACE");
545 		if (info.virt.hv_host_feat_avic) printf(" HV_HOST_FEAT_AVIC");
546 		if (info.virt.hv_host_feat_msr_bitmap) printf(" HV_HOST_FEAT_MSR_BITMAP");
547 		if (info.virt.hv_host_feat_perf_counter) printf(" HV_HOST_FEAT_PERF_COUNTER");
548 		if (info.virt.hv_host_feat_nested_paging) printf(" HV_HOST_FEAT_NESTED_PAGING");
549 		if (info.virt.hv_host_feat_dma_remapping) printf(" HV_HOST_FEAT_DMA_REMAPPING");
550 		if (info.virt.hv_host_feat_interrupt_remapping) printf(" HV_HOST_FEAT_INTERRUPT_REMAPPING");
551 		if (info.virt.hv_host_feat_mem_patrol_scrubber) printf(" HV_HOST_FEAT_MEM_PATROL_SCRUBBER");
552 		if (info.virt.hv_host_feat_dma_prot_in_use) printf(" HV_HOST_FEAT_DMA_PROT_IN_USE");
553 		if (info.virt.hv_host_feat_hpet_requested) printf(" HV_HOST_FEAT_HPET_REQUESTED");
554 		if (info.virt.hv_host_feat_stimer_volatile) printf(" HV_HOST_FEAT_STIMER_VOLATILE");
555 		break;
556 	case VirtVendor.KVM:
557 		if (info.virt.kvm_feature_clocksource) printf(" KVM_FEATURE_CLOCKSOURCE");
558 		if (info.virt.kvm_feature_nop_io_delay) printf(" KVM_FEATURE_NOP_IO_DELAY");
559 		if (info.virt.kvm_feature_mmu_op) printf(" KVM_FEATURE_MMU_OP");
560 		if (info.virt.kvm_feature_clocksource2) printf(" KVM_FEATURE_CLOCKSOURCE2");
561 		if (info.virt.kvm_feature_async_pf) printf(" KVM_FEATURE_ASYNC_PF");
562 		if (info.virt.kvm_feature_steal_time) printf(" KVM_FEATURE_STEAL_TIME");
563 		if (info.virt.kvm_feature_pv_eoi) printf(" KVM_FEATURE_PV_EOI");
564 		if (info.virt.kvm_feature_pv_unhault) printf(" KVM_FEATURE_PV_UNHAULT");
565 		if (info.virt.kvm_feature_pv_tlb_flush) printf(" KVM_FEATURE_PV_TLB_FLUSH");
566 		if (info.virt.kvm_feature_async_pf_vmexit) printf(" KVM_FEATURE_ASYNC_PF_VMEXIT");
567 		if (info.virt.kvm_feature_pv_send_ipi) printf(" KVM_FEATURE_PV_SEND_IPI");
568 		if (info.virt.kvm_feature_pv_poll_control) printf(" KVM_FEATURE_PV_POLL_CONTROL");
569 		if (info.virt.kvm_feature_pv_sched_yield) printf(" KVM_FEATURE_PV_SCHED_YIELD");
570 		if (info.virt.kvm_feature_clocsource_stable_bit) printf(" KVM_FEATURE_CLOCSOURCE_STABLE_BIT");
571 		if (info.virt.kvm_hint_realtime) printf(" KVM_HINTS_REALTIME");
572 		break;
573 	default:
574 	}
575 	
576 	printf("\nMemory      :");
577 	if (info.mem.phys_bits) printf(" P-Bits=%u", info.mem.phys_bits);
578 	if (info.mem.line_bits) printf(" L-Bits=%u", info.mem.line_bits);
579 	if (info.mem.pae) printf(" PAE");
580 	if (info.mem.pse) printf(" PSE");
581 	if (info.mem.pse_36) printf(" PSE-36");
582 	if (info.mem.page1gb) printf(" Page1GB");
583 	if (info.mem.nx)
584 		switch (info.vendor_id) {
585 		case Vendor.Intel: printf(" Intel-XD/NX"); break;
586 		case Vendor.AMD: printf(" AMD-EVP/NX"); break;
587 		default: printf(" NX");
588 		}
589 	if (info.mem.dca) printf(" DCA");
590 	if (info.mem.pat) printf(" PAT");
591 	if (info.mem.mtrr) printf(" MTRR");
592 	if (info.mem.pge) printf(" PGE");
593 	if (info.mem.smep) printf(" SMEP");
594 	if (info.mem.smap) printf(" SMAP");
595 	if (info.mem.pku) printf(" PKU");
596 	if (info.mem._5pl) printf(" 5PL");
597 	if (info.mem.fsrepmov) printf(" FSRM");
598 	if (info.mem.lam) printf(" LAM");
599 	
600 	printf("\nDebugging   :");
601 	if (info.dbg.mca) printf(" MCA");
602 	if (info.dbg.mce) printf(" MCE");
603 	if (info.dbg.de) printf(" DE");
604 	if (info.dbg.ds) printf(" DS");
605 	if (info.dbg.ds_cpl) printf(" DS-CPL");
606 	if (info.dbg.dtes64) printf(" DTES64");
607 	if (info.dbg.pdcm) printf(" PDCM");
608 	if (info.dbg.sdbg) printf(" SDBG");
609 	if (info.dbg.pbe) printf(" PBE");
610 	
611 	printf("\nSecurity    :");
612 	if (info.sec.ia32_arch_capabilities) printf(" IA32_ARCH_CAPABILITIES");
613 	if (info.sec.ibpb) printf(" IBPB");
614 	if (info.sec.ibrs) printf(" IBRS");
615 	if (info.sec.ibrs_on) printf(" IBRS_ON");	// AMD
616 	if (info.sec.ibrs_pref) printf(" IBRS_PREF");	// AMD
617 	if (info.sec.stibp) printf(" STIBP");
618 	if (info.sec.stibp_on) printf(" STIBP_ON");	// AMD
619 	if (info.sec.ssbd) printf(" SSBD");
620 	if (info.sec.l1d_flush) printf(" L1D_FLUSH");	// Intel
621 	if (info.sec.md_clear) printf(" MD_CLEAR");	// Intel
622 	if (info.sec.cet_ibt) printf(" CET_IBT");	// Intel
623 	if (info.sec.cet_ss) printf(" CET_SS");	// Intel
624 	
625 	printf("\nMisc.       : HLeaf=0x%x HVLeaf=0x%x HELeaf=0x%x Type=%s Index=%u",
626 		info.max_leaf, info.max_virt_leaf, info.max_ext_leaf,
627 		info.type_string, info.brand_index);
628 	if (info.misc.xtpr) printf(" xTPR");
629 	if (info.misc.psn) printf(" PSN");
630 	if (info.misc.pcid) printf(" PCID");
631 	if (info.misc.fsgsbase) printf(" FSGSBASE");
632 	if (info.misc.uintr) printf(" UINTR");
633 	
634 	putchar('\n');
635 	
636 	return 0;
637 }