1 /**
2  * Program entry point.
3  *
4  *
5  * Authors: dd86k (dd@dax.moe)
6  * Copyright: © 2016-2022 dd86k
7  * License: MIT
8  */
9 module main;
10 
11 import core.stdc.stdio : printf, sscanf;
12 import core.stdc.errno : errno;
13 import core.stdc.stdlib : malloc, free;
14 import core.stdc.string : strcmp, strtok, strncpy, strerror;
15 import ddcpuid;
16 
17 // NOTE: printf is used for a few reasons:
18 //       - fputs with stdout crashes on Windows due to improper externs.
19 //       - line buffering is used by default, which can be an advantage.
20 //TODO: Consider using WriteFile+STD_OUTPUT_HANDLE and write(2)+STDOUT_FILENO?
21 //      No real benefit than maybe save some instructions
22 
23 private:
24 @system:
25 extern (C):
26 
27 int putchar(int);	// because wrong extern
28 int puts(scope const char* s);	// because wrong extern
29 
30 /// Compiler version template for betterC usage
31 template CVER(int v) {
32 	enum CVER =
33 		cast(char)((v / 1000) + '0') ~
34 		"." ~
35 		cast(char)(((v % 1000) / 100) + '0') ~
36 		cast(char)(((v % 100) / 10) + '0') ~
37 		cast(char)((v % 10) + '0');
38 }
39 
40 /// Make a bit mask of one bit at n position
41 template BIT(int n) if (n <= 31) { enum uint BIT = 1 << n; }
42 
43 enum : uint {
44 	MAX_LEAF	= 0x30, /// Maximum leaf override
45 	MAX_VLEAF	= 0x4000_0000 + MAX_LEAF, /// Maximum virt leaf override
46 	MAX_ELEAF	= 0x8000_0000 + MAX_LEAF, /// Maximum extended leaf override
47 }
48 
49 /// Command-line options
50 struct options_t { align(1):
51 	int maxLevel;	/// Maximum leaf for -r (-S)
52 	int maxSubLevel;	/// Maximum subleaf for -r (-s)
53 	bool hasLevel;	/// If -S has been used
54 	bool table;	/// To be deprecated
55 	bool override_;	/// Override leaves (-o)
56 	bool baseline;	/// Get x86-64 optimization feature level or baseline
57 	bool all;	/// Get all processor details
58 	bool raw;	/// Raw CPUID value table (-r/--raw)
59 	bool rawInput;	/// Raw values were supplied, avoid fetching
60 	bool[1] reserved;	/// 
61 }
62 
63 // One day I'll make this italics.
64 immutable const(char) *secret = r"
65                             ############
66                      ######################
67                 ###########################
68             #################
69          ############              #######
70        #########     _      _     ### R #####
71      #######        | |    | |     ###########
72    #######        __| |  __| |          ########
73   #####          / _  | / _  |             ######
74  #####          | (_| || (_| |               #####
75 ####             \____| \____|                ####
76 ###                             _              ###
77 ###      [_]            [_]    | |              ##
78 ##        _  _ __   ___  _   __| | ____        ###
79 ###      | || '_  \/ __/| | / _  |/ __ \       ###
80 ###      | || | | |\__ \| || (_| ||  __/      ####
81  ###     |_||_| |_||___/|_| \____|\____|    #####
82  #####                                     #####
83   ######                               ######
84     #######                          #######
85       #########                ###########
86         ###############################
87             ######################
88 ";
89 
90 /// print help page
91 void clih() {
92 	puts(
93 	"x86/AMD64 CPUID information tool\n"~
94 	"\n"~
95 	"USAGE\n"~
96 	" ddcpuid [OPTIONS...]\n"~
97 	"\n"~
98 	"OPTIONS\n"~
99 	" -a, --all        Show all processor information\n"~
100 	" -b, --baseline   Print the processor's feature level\n"~
101 	" -d, --details    (Deprecated) Alias of --all\n"~
102 	" -l, --level      (Deprecated) Alias of --baseline\n"~
103 	" -o               Override maximum leaves to 0x20, 0x4000_0020, and 0x8000_0020\n"~
104 	" -r, --raw        Display raw CPUID values. Takes optional leaf,subleaf values.\n"~
105 	" -S               (Deprecated) Alias of --raw eax, requires --table\n"~
106 	" -s               (Deprecated) Alias of --raw eax,ecx, requires --table\n"~
107 	"     --table      (Deprecated) Alias of --raw\n"~
108 	"\n"~
109 	"PAGES\n"~
110 	" -h, --help   Print this help screen and quit\n"~
111 	" --version    Print version screen and quit\n"~
112 	" --ver        Print only version string and quit\n"
113 	);
114 }
115 
116 /// print version page
117 void cliv() {
118 	puts(
119 	"ddcpuid-"~DDCPUID_PLATFORM~" "~DDCPUID_VERSION~" (built: "~__TIMESTAMP__~")\n"~
120 	"Copyright (c) 2016-2022 dd86k <dd@dax.moe>\n"~
121 	"License: MIT License <http://opensource.org/licenses/MIT>\n"~
122 	"Homepage: <https://github.com/dd86k/ddcpuid>\n"~
123 	"Compiler: "~__VENDOR__~" "~CVER!(__VERSION__)
124 	);
125 }
126 
127 void outcpuid(uint leaf, uint sub) {
128 	REGISTERS regs = void;
129 	ddcpuid_id(regs, leaf, sub);
130 	printcpuid(regs, leaf, sub);
131 }
132 
133 /// Print cpuid table entry into stdout.
134 /// Params:
135 /// 	leaf = EAX input
136 /// 	sub = ECX input
137 pragma(inline, false) // ldc optimization thing
138 void printcpuid(ref REGISTERS regs, uint leaf, uint sub) {
139 	with (regs)
140 	printf("| %8x | %8x | %8x | %8x | %8x | %8x |\n",
141 		leaf, sub, eax, ebx, ecx, edx);
142 }
143 
144 char adjust(ref float size) {
145 	version (Trace) trace("size=%u", size);
146 	if (size >= 1024.0) {
147 		size /= 1024;
148 		return 'M';
149 	}
150 	return 'K';
151 }
152 /// adjust
153 @system unittest {
154 	uint size = 1;
155 	assert(adjust(size) == 'K');
156 	assert(size == 1);
157 	size = 1024;
158 	assert(adjust(size) == 'M');
159 	assert(size == 1);
160 	size = 4096;
161 	assert(adjust(size) == 'M');
162 	assert(size == 4);
163 }
164 const(char)* adjustBits(ref uint size, int bitpos) {
165 	version (Trace) trace("size=%u bit=%d", size, bitpos);
166 	static immutable const(char)*[8] SIZES = [
167 		"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB"
168 	];
169 	size_t s;
170 	while (bitpos >= 10) { bitpos -= 10; ++s; }
171 	size = 1 << bitpos;
172 	return SIZES[s];
173 }
174 //TODO: Make DUB to include this main, somehow
175 /// adjustBits
176 @system unittest {
177 	float size;
178 	assert(adjustBits(size, 0) == "B");
179 	assert(size == 1);
180 	assert(adjustBits(size, 1) == "B");
181 	assert(size == 2);
182 	assert(adjustBits(size, 10) == "KiB");
183 	assert(size == 1);
184 	assert(adjustBits(size, 11) == "KiB");
185 	assert(size == 2);
186 	assert(adjustBits(size, 20) == "MiB");
187 	assert(size == 1);
188 	assert(adjustBits(size, 36) == "GiB");
189 	assert(size == 64);
190 	assert(adjustBits(size, 48) == "TiB");
191 	assert(size == 256);
192 }
193 
194 void printLegacy(ref CPUINFO info) {
195 	if (info.extensions.fpu) {
196 		printf(" x87/fpu");
197 		if (info.extensions.f16c) printf(" +f16c");
198 	}
199 	if (info.extensions.mmx) {
200 		printf(" mmx");
201 		if (info.extensions.mmxExtended) printf(" extmmx");
202 	}
203 	if (info.extensions._3DNow) {
204 		printf(" 3dnow!");
205 		if (info.extensions._3DNowExtended) printf(" ext3dnow!");
206 	}
207 }
208 void printExtensions(ref CPUINFO info) {
209 	
210 }
211 void printTechs(ref CPUINFO info) {
212 	switch (info.vendor.id) with (Vendor) {
213 	case Intel:
214 		if (info.tech.eist) printf(" eist");
215 		if (info.tech.turboboost) {
216 			printf(" turboboost");
217 			if (info.tech.turboboost30) printf("-3.0");
218 		}
219 		if (info.memory.tsx) {
220 			printf(" tsx");
221 			if (info.memory.hle)
222 				printf(" +hle");
223 			if (info.memory.rtm)
224 				printf(" +rtm");
225 			if (info.memory.tsxldtrk)
226 				printf(" +tsxldtrk");
227 		}
228 		if (info.tech.smx) printf(" intel-txt/smx");
229 		if (info.sgx.supported) {
230 			// NOTE: SGX system configuration
231 			//       "enabled" in BIOS: only CPUID.7h.EBX[2]
232 			//       "user controlled" in BIOS: SGX1/SGX2/size bits
233 			if (info.sgx.sgx1 && info.sgx.sgx2) {
234 				if (info.sgx.sgx1) printf(" sgx1");
235 				if (info.sgx.sgx2) printf(" sgx2");
236 			} else printf(" sgx"); // Fallback per-say
237 			if (info.sgx.maxSize) {
238 				uint s32 = void, s64 = void;
239 				const(char) *m32 = adjustBits(s32, info.sgx.maxSize);
240 				const(char) *m64 = adjustBits(s64, info.sgx.maxSize64);
241 				printf(" +maxsize=%u%s +maxsize64=%u%s", s32, m32, s64, m64);
242 			}
243 		}
244 		break;
245 	case AMD:
246 		if (info.tech.turboboost) printf(" core-performance-boost");
247 		break;
248 	default:
249 	}
250 	if (info.tech.htt) printf(" htt");
251 }
252 void printSSE(ref CPUINFO info) {
253 	printf(" sse");
254 	if (info.sse.sse2) printf(" sse2");
255 	if (info.sse.sse3) printf(" sse3");
256 	if (info.sse.ssse3) printf(" ssse3");
257 	if (info.sse.sse41) printf(" sse4.1");
258 	if (info.sse.sse42) printf(" sse4.2");
259 	if (info.sse.sse4a) printf(" sse4a");
260 }
261 void printAVX(ref CPUINFO info) {
262 	printf(" avx");
263 	if (info.avx.avx2) printf(" avx2");
264 	if (info.avx.avx512f) {
265 		printf(" avx512f");
266 		if (info.avx.avx512er) printf(" +er");
267 		if (info.avx.avx512pf) printf(" +pf");
268 		if (info.avx.avx512cd) printf(" +cd");
269 		if (info.avx.avx512dq) printf(" +dq");
270 		if (info.avx.avx512bw) printf(" +bw");
271 		if (info.avx.avx512vl) printf(" +vl");
272 		if (info.avx.avx512_ifma) printf(" +ifma");
273 		if (info.avx.avx512_vbmi) printf(" +vbmi");
274 		if (info.avx.avx512_4vnniw) printf(" +4vnniw");
275 		if (info.avx.avx512_4fmaps) printf(" +4fmaps");
276 		if (info.avx.avx512_vbmi2) printf(" +vbmi2");
277 		if (info.avx.avx512_gfni) printf(" +gfni");
278 		if (info.avx.avx512_vaes) printf(" +vaes");
279 		if (info.avx.avx512_vnni) printf(" +vnni");
280 		if (info.avx.avx512_bitalg) printf(" +bitalg");
281 		if (info.avx.avx512_bf16) printf(" +bf16");
282 		if (info.avx.avx512_vp2intersect) printf(" +vp2intersect");
283 	}
284 	if (info.extensions.xop) printf(" xop");
285 }
286 void printFMA(ref CPUINFO info) {
287 	if (info.extensions.fma3) printf(" fma3");
288 	if (info.extensions.fma4) printf(" fma4");
289 }
290 void printAMX(ref CPUINFO info) {
291 	printf(" amx");
292 	if (info.amx.bf16) printf(" +bf16");
293 	if (info.amx.int8) printf(" +int8");
294 	if (info.amx.xtilecfg) printf(" +xtilecfg");
295 	if (info.amx.xtiledata) printf(" +xtiledata");
296 	if (info.amx.xfd) printf(" +xfd");
297 }
298 void printOthers(ref CPUINFO info) {
299 	const(char) *tstr = void;
300 	if (info.extensions.x86_64) {
301 		switch (info.vendor.id) with (Vendor) {
302 		case Intel:	tstr = " intel64/x86-64"; break;
303 		case AMD:	tstr = " amd64/x86-64"; break;
304 		default:	tstr = " x86-64";
305 		}
306 		printf(tstr);
307 		if (info.extensions.lahf64)
308 			printf(" +lahf64");
309 	}
310 	if (info.virt.available)
311 		switch (info.vendor.id) with (Vendor) {
312 		case Intel: printf(" vt-x/vmx"); break;
313 		case AMD: // SVM
314 			printf(" amd-v/vmx");
315 			if (info.virt.version_)
316 				printf(" +svm=v%u", info.virt.version_);
317 			break;
318 		case VIA: printf(" via-vt/vmx"); break;
319 		default: printf(" vmx");
320 		}
321 	if (info.extensions.aes_ni) printf(" aes-ni");
322 	if (info.extensions.adx) printf(" adx");
323 	if (info.extensions.sha) printf(" sha");
324 	if (info.extensions.tbm) printf(" tbm");
325 	if (info.extensions.bmi1) printf(" bmi1");
326 	if (info.extensions.bmi2) printf(" bmi2");
327 	if (info.extensions.waitpkg) printf(" waitpkg");
328 }
329 void printSecurity(ref CPUINFO info) {
330 	if (info.security.ibpb) printf(" ibpb");
331 	if (info.security.ibrs) printf(" ibrs");
332 	if (info.security.ibrsAlwaysOn) printf(" ibrs_on");	// AMD
333 	if (info.security.ibrsPreferred) printf(" ibrs_pref");	// AMD
334 	if (info.security.stibp) printf(" stibp");
335 	if (info.security.stibpAlwaysOn) printf(" stibp_on");	// AMD
336 	if (info.security.ssbd) printf(" ssbd");
337 	if (info.security.l1dFlush) printf(" l1d_flush");	// Intel
338 	if (info.security.md_clear) printf(" md_clear");	// Intel
339 	if (info.security.cetIbt) printf(" cet_ibt");	// Intel
340 	if (info.security.cetSs) printf(" cet_ss");	// Intel
341 }
342 void printCacheFeats(ushort feats) {
343 	if (feats == 0) return;
344 	putchar(',');
345 	if (feats & BIT!(0)) printf(" si"); // Self Initiative
346 	if (feats & BIT!(1)) printf(" fa"); // Fully Associative
347 	if (feats & BIT!(2)) printf(" nwbv"); // No Write-Back Validation
348 	if (feats & BIT!(3)) printf(" ci"); // Cache Inclusive
349 	if (feats & BIT!(4)) printf(" cci"); // Complex Cache Indexing
350 }
351 
352 int optionRaw(ref options_t options, const(char) *arg) {
353 	enum MAX = 512;
354 	
355 	version (Trace) trace("arg=%s", arg);
356 	
357 	options.raw = true;
358 	if (arg == null) return 0;
359 	
360 	char *s = cast(char*)malloc(MAX);
361 	if (s == null) {
362 		puts(strerror(errno));
363 		return 1;
364 	}
365 	
366 	options.rawInput = true;
367 	strncpy(s, arg, MAX);
368 	arg = strtok(s, ",");
369 	version (Trace) trace("token=%s", arg);
370 	if (arg == null) {
371 		puts("Empty value for leaf");
372 		return 1;
373 	}
374 	sscanf(arg, "%i", &options.maxLevel);
375 	arg = strtok(null, ",");
376 	version (Trace) trace("token=%s", arg);
377 	if (arg)
378 		sscanf(arg, "%i", &options.maxSubLevel);
379 	free(s);
380 	return 0;
381 }
382 
383 //TODO: --no-header for -c/--cpuid
384 version (unittest) {} else
385 int main(int argc, const(char) **argv) {
386 	options_t options; /// Command-line options
387 	int error = void;
388 	
389 	const(char) *arg = void; /// Temp argument holder
390 	for (int argi = 1; argi < argc; ++argi) {
391 		if (argv[argi][1] == '-') { // Long arguments
392 			arg = argv[argi] + 2;
393 			if (strcmp(arg, "raw") == 0) {
394 				arg = argi + 1 >= argc ? null : argv[argi + 1];
395 				if (arg && arg[0] == '-') arg = null;
396 				error = optionRaw(options, arg);
397 				if (error) return error;
398 				continue;
399 			}
400 			if (strcmp(arg, "table") == 0) {
401 				options.table = true;
402 				continue;
403 			}
404 			if (strcmp(arg, "level") == 0 || strcmp(arg, "baseline") == 0) {
405 				options.baseline = true;
406 				continue;
407 			}
408 			if (strcmp(arg, "details") == 0 || strcmp(arg, "all") == 0) {
409 				options.all = true;
410 				continue;
411 			}
412 			if (strcmp(arg, "version") == 0) {
413 				cliv;
414 				return 0;
415 			}
416 			if (strcmp(arg, "ver") == 0) {
417 				puts(DDCPUID_VERSION);
418 				return 0;
419 			}
420 			if (strcmp(arg, "help") == 0) {
421 				clih;
422 				return 0;
423 			}
424 			if (strcmp(arg, "inside") == 0) {
425 				puts(secret);
426 				return 0;
427 			}
428 			printf("Unknown parameter: '%s'\n", arg);
429 			return 1;
430 		} else if (argv[argi][0] == '-') { // Short arguments
431 			arg = argv[argi] + 1;
432 			char o = void;
433 			while ((o = *arg) != 0) {
434 				++arg;
435 				switch (o) {
436 				case 'a', 'd': options.all = true; continue;
437 				case 'b', 'l': options.baseline = true; continue;
438 				case 'o': options.override_ = true; continue;
439 				case 'r':
440 					arg = argi + 1 >= argc ? null : argv[argi + 1];
441 					if (arg && arg[0] == '-') arg = null;
442 					error = optionRaw(options, arg);
443 					if (error) return error;
444 					continue;
445 				case 'S':
446 					if (++argi >= argc) {
447 						puts("Missing parameter: leaf");
448 						return 1;
449 					}
450 					options.hasLevel = sscanf(argv[argi], "%i", &options.maxLevel) == 1;
451 					options.rawInput = true;
452 					if (options.hasLevel == false) {
453 						puts("Could not parse level (-S)");
454 						return 2;
455 					}
456 					continue;
457 				case 's':
458 					if (++argi >= argc) {
459 						puts("Missing parameter: sub-leaf (-s)");
460 						return 1;
461 					}
462 					if (sscanf(argv[argi], "%i", &options.maxSubLevel) != 1) {
463 						puts("Could not parse sub-level (-s)");
464 						return 2;
465 					}
466 					options.rawInput = true;
467 					continue;
468 				case 'h': clih; return 0;
469 				case 'V': cliv; return 0;
470 				default:
471 					printf("Unknown parameter: '-%c'\n", o);
472 					return 1;
473 				}
474 			} // while
475 		} // else if
476 	} // for
477 	
478 	CPUINFO info;
479 	
480 	if (options.override_) {
481 		info.maxLeaf = MAX_LEAF;
482 		info.maxLeafVirt = MAX_VLEAF;
483 		info.maxLeafExtended = MAX_ELEAF;
484 	} else if (options.rawInput == false) {
485 		ddcpuid_leaves(info);
486 	}
487 	
488 	if (options.raw || options.table) {
489 		uint l = void, s = void;
490 		
491 		puts(
492 		"| Leaf     | Sub-leaf | EAX      | EBX      | ECX      | EDX      |\n"~
493 		"|----------|----------|----------|----------|----------|----------|"
494 		);
495 		
496 		if (options.rawInput) {
497 			outcpuid(options.maxLevel, options.maxSubLevel);
498 			return 0;
499 		}
500 		
501 		// Normal
502 		for (l = 0; l <= info.maxLeaf; ++l)
503 			for (s = 0; s <= options.maxSubLevel; ++s)
504 				outcpuid(l, s);
505 		
506 		// Paravirtualization
507 		if (info.maxLeafVirt > 0x4000_0000)
508 		for (l = 0x4000_0000; l <= info.maxLeafVirt; ++l)
509 			for (s = 0; s <= options.maxSubLevel; ++s)
510 				outcpuid(l, s);
511 		
512 		// Extended
513 		for (l = 0x8000_0000; l <= info.maxLeafExtended; ++l)
514 			for (s = 0; s <= options.maxSubLevel; ++s)
515 				outcpuid(l, s);
516 		return 0;
517 	}
518 	
519 	ddcpuid_cpuinfo(info);
520 	
521 	if (options.baseline) {
522 		puts(ddcpuid_baseline(info));
523 		return 0;
524 	}
525 	
526 	// NOTE: .ptr crash with GDC -O3
527 	//       glibc!__strlen_sse2 (in printf)
528 	char *vendorstr = cast(char*)info.vendor.string_;
529 	char *brandstr  = cast(char*)info.brandString;
530 	
531 	// Brand string left space trimming
532 	// While very common in Intel, let's also do it for others (in case of)
533 	while (*brandstr == ' ') ++brandstr;
534 	
535 	CACHEINFO *cache = void;	/// Current cache level
536 	
537 	//
538 	// ANCHOR Summary
539 	//
540 	
541 	if (options.all == false) {
542 		with (info) printf(
543 		"Name:        %.12s %.48s\n"~
544 		"Identifier:  Family 0x%x Model 0x%x Stepping 0x%x\n"~
545 		"Cores:       %u cores, %u threads\n",
546 		vendorstr, brandstr,
547 		family, model, stepping,
548 		cores.physical, cores.logical,
549 		);
550 		
551 		if (info.memory.physBits || info.memory.lineBits) {
552 			uint maxPhys = void, maxLine = void;
553 			const(char) *cphys = adjustBits(maxPhys, info.memory.physBits);
554 			const(char) *cline = adjustBits(maxLine, info.memory.lineBits);
555 			with (info) printf(
556 			"Max. Memory: %u %s physical, %u %s virtual\n",
557 			maxPhys, cphys, maxLine, cline,
558 			);
559 		}
560 		
561 		with (info) printf(
562 		"Baseline:    %s\n"~
563 		"Techs:      ",
564 		ddcpuid_baseline(info)
565 		);
566 		
567 		printTechs(info);
568 		
569 		immutable const(char) *none = " None";
570 		
571 		printf("\nExtensions: ");
572 		printLegacy(info);
573 		printOthers(info);
574 		
575 		printf("\nSSE:        ");
576 		if (info.sse.sse) {
577 			printSSE(info);
578 			putchar('\n');
579 		} else puts(none);
580 		
581 		printf("AVX:        ");
582 		if (info.avx.avx) {
583 			printAVX(info);
584 			putchar('\n');
585 		} else puts(none);
586 		
587 		printf("AMX:        ");
588 		if (info.amx.enabled) {
589 			printAMX(info);
590 			putchar('\n');
591 		} else puts(none);
592 		
593 		printf("Mitigations:");
594 		printSecurity(info);
595 		putchar('\n');
596 		
597 		// NOTE: id=0 would be vboxmin, so using this is more reliable
598 		if (info.maxLeafVirt) {
599 			const(char) *virtVendor = void;
600 			switch (info.virt.vendor.id) with (VirtVendor) {
601 			case KVM:        virtVendor = "KVM"; break;
602 			case HyperV:     virtVendor = "Hyper-V"; break;
603 			case VBoxHyperV: virtVendor = "VirtualBox Hyper-V"; break;
604 			case VBoxMin:    virtVendor = "VirtualBox Minimal"; break;
605 			default:         virtVendor = "Unknown";
606 			}
607 			printf("ParaVirt.:   %s\n", virtVendor);
608 		}
609 		
610 		for (size_t i; i < info.cache.levels; ++i) {
611 			cache = &info.cache.level[i];
612 			float csize = cache.size;
613 			float tsize = csize * cache.sharedCores;
614 			char cc = adjust(csize);
615 			char ct = adjust(tsize);
616 			with (cache)
617 			printf("Cache L%u-%c:  %3ux %4g %ciB, %4g %ciB total",
618 				level, type, sharedCores, csize, cc, tsize, ct);
619 			printCacheFeats(cache.features);
620 			putchar('\n');
621 		}
622 		
623 		return 0;
624 	}
625 	
626 	//
627 	// ANCHOR Detailed view
628 	//
629 	
630 	with (info) printf(
631 	"Vendor      : %.12s\n"~
632 	"Brand       : %.48s\n"~
633 	"Identifier  : 0x%x\n"~
634 	"Family      : 0x%x\n"~
635 	"BaseFamily  : 0x%x\n"~
636 	"ExtFamily   : 0x%x\n"~
637 	"Model       : 0x%x\n"~
638 	"BaseModel   : 0x%x\n"~
639 	"ExtModel    : 0x%x\n"~
640 	"Stepping    : 0x%x\n"~
641 	"Cores       : %u\n"~
642 	"Threads     : %u\n"~
643 	"Extensions  :",
644 	vendorstr, brandstr,
645 	identifier,
646 	family, familyBase, familyExtended,
647 	model, modelBase, modelExtended,
648 	stepping,
649 	cores.physical, cores.logical
650 	);
651 	
652 	// Extensions
653 	
654 	const(char) *tstr = void;
655 	printLegacy(info);
656 	if (info.sse.sse) printSSE(info);
657 	if (info.avx.avx) printAVX(info);
658 	printFMA(info);
659 	printOthers(info);
660 	if (info.amx.enabled) printAMX(info);
661 	
662 	//
663 	// ANCHOR Extra/lone instructions
664 	//
665 	
666 	printf("\nExtra       :");
667 	if (info.extras.monitor) {
668 		printf(" monitor+mwait");
669 		if (info.extras.mwaitMin)
670 			printf(" +min=%u +max=%u",
671 				info.extras.mwaitMin, info.extras.mwaitMax);
672 		if (info.extras.monitorx) printf(" monitorx+mwaitx");
673 	}
674 	if (info.extras.pclmulqdq) printf(" pclmulqdq");
675 	if (info.extras.cmpxchg8b) printf(" cmpxchg8b");
676 	if (info.extras.cmpxchg16b) printf(" cmpxchg16b");
677 	if (info.extras.movbe) printf(" movbe");
678 	if (info.extras.rdrand) printf(" rdrand");
679 	if (info.extras.rdseed) printf(" rdseed");
680 	if (info.extras.rdmsr) printf(" rdmsr+wrmsr");
681 	if (info.extras.sysenter) printf(" sysenter+sysexit");
682 	if (info.extras.syscall) printf(" syscall+sysret");
683 	if (info.extras.rdtsc) {
684 		printf(" rdtsc");
685 		if (info.extras.rdtscDeadline)
686 			printf(" +tsc-deadline");
687 		if (info.extras.rdtscInvariant)
688 			printf(" +tsc-invariant");
689 	}
690 	if (info.extras.rdtscp) printf(" rdtscp");
691 	if (info.extras.rdpid) printf(" rdpid");
692 	if (info.extras.cmov) {
693 		printf(" cmov");
694 		if (info.extensions.fpu) printf(" fcomi+fcmov");
695 	}
696 	if (info.extras.lzcnt) printf(" lzcnt");
697 	if (info.extras.popcnt) printf(" popcnt");
698 	if (info.extras.xsave) printf(" xsave+xrstor");
699 	if (info.extras.osxsave) printf(" xsetbv+xgetbv");
700 	if (info.extras.fxsr) printf(" fxsave+fxrstor");
701 	if (info.extras.pconfig) printf(" pconfig");
702 	if (info.extras.cldemote) printf(" cldemote");
703 	if (info.extras.movdiri) printf(" movdiri");
704 	if (info.extras.movdir64b) printf(" movdir64b");
705 	if (info.extras.enqcmd) printf(" enqcmd");
706 	if (info.extras.skinit) printf(" skinit+stgi");
707 	if (info.extras.serialize) printf(" serialize");
708 	
709 	//
710 	// ANCHOR Vendor specific technologies
711 	//
712 	
713 	printf("\nTechnologies:");
714 	printTechs(info);
715 	
716 	//
717 	// ANCHOR Cache information
718 	//
719 	
720 	printf("\nCache       :");
721 	if (info.cache.clflush)
722 		printf(" clflush=%uB", info.cache.clflushLinesize << 3);
723 	if (info.cache.clflushopt) printf(" clflushopt");
724 	if (info.cache.cnxtId) printf(" cnxt-id");
725 	if (info.cache.ss) printf(" ss");
726 	if (info.cache.prefetchw) printf(" prefetchw");
727 	if (info.cache.invpcid) printf(" invpcid");
728 	if (info.cache.wbnoinvd) printf(" wbnoinvd");
729 	
730 	for (uint i; i < info.cache.levels; ++i) {
731 		cache = &info.cache.level[i];
732 		printf("\nLevel %u-%c   : %2ux %6u KiB, %u ways, %u parts, %u B, %u sets",
733 			cache.level, cache.type, cache.sharedCores, cache.size,
734 			cache.ways, cache.partitions, cache.lineSize, cache.sets
735 		);
736 		printCacheFeats(cache.features);
737 	}
738 	
739 	printf("\nSystem      :");
740 	if (info.sys.available) printf(" acpi");
741 	if (info.sys.apic) printf(" apic");
742 	if (info.sys.x2apic) printf(" x2apic");
743 	if (info.sys.arat) printf(" arat");
744 	if (info.sys.tm) printf(" tm");
745 	if (info.sys.tm2) printf(" tm2");
746 	printf(" apic-id=%u", info.sys.apicId);
747 	if (info.sys.maxApicId) printf(" max-id=%u", info.sys.maxApicId);
748 	
749 	printf("\nVirtual     :");
750 	if (info.virt.vme) printf(" vme");
751 	if (info.virt.apicv) printf(" apicv");
752 	
753 	// Paravirtualization
754 	if (info.virt.vendor.id) {
755 		// See vendor string case
756 		char *virtvendor = cast(char*)info.virt.vendor.string_;
757 		printf(" host=%.12s", virtvendor);
758 	}
759 	switch (info.virt.vendor.id) with (VirtVendor) {
760 	case VBoxMin:
761 		if (info.virt.vbox.tsc_freq_khz)
762 			printf(" tsc_freq_khz=%u", info.virt.vbox.tsc_freq_khz);
763 		if (info.virt.vbox.apic_freq_khz)
764 			printf(" apic_freq_khz=%u", info.virt.vbox.apic_freq_khz);
765 		break;
766 	case HyperV:
767 		printf(" opensource=%d vendor_id=%d os=%d major=%d minor=%d service=%d build=%d",
768 			info.virt.hv.guest_opensource,
769 			info.virt.hv.guest_vendor_id,
770 			info.virt.hv.guest_os,
771 			info.virt.hv.guest_major,
772 			info.virt.hv.guest_minor,
773 			info.virt.hv.guest_service,
774 			info.virt.hv.guest_build);
775 		if (info.virt.hv.base_feat_vp_runtime_msr) printf(" hv_base_feat_vp_runtime_msr");
776 		if (info.virt.hv.base_feat_part_time_ref_count_msr) printf(" hv_base_feat_part_time_ref_count_msr");
777 		if (info.virt.hv.base_feat_basic_synic_msrs) printf(" hv_base_feat_basic_synic_msrs");
778 		if (info.virt.hv.base_feat_stimer_msrs) printf(" hv_base_feat_stimer_msrs");
779 		if (info.virt.hv.base_feat_apic_access_msrs) printf(" hv_base_feat_apic_access_msrs");
780 		if (info.virt.hv.base_feat_hypercall_msrs) printf(" hv_base_feat_hypercall_msrs");
781 		if (info.virt.hv.base_feat_vp_id_msr) printf(" hv_base_feat_vp_id_msr");
782 		if (info.virt.hv.base_feat_virt_sys_reset_msr) printf(" hv_base_feat_virt_sys_reset_msr");
783 		if (info.virt.hv.base_feat_stat_pages_msr) printf(" hv_base_feat_stat_pages_msr");
784 		if (info.virt.hv.base_feat_part_ref_tsc_msr) printf(" hv_base_feat_part_ref_tsc_msr");
785 		if (info.virt.hv.base_feat_guest_idle_state_msr) printf(" hv_base_feat_guest_idle_state_msr");
786 		if (info.virt.hv.base_feat_timer_freq_msrs) printf(" hv_base_feat_timer_freq_msrs");
787 		if (info.virt.hv.base_feat_debug_msrs) printf(" hv_base_feat_debug_msrs");
788 		if (info.virt.hv.part_flags_create_part) printf(" hv_part_flags_create_part");
789 		if (info.virt.hv.part_flags_access_part_id) printf(" hv_part_flags_access_part_id");
790 		if (info.virt.hv.part_flags_access_memory_pool) printf(" hv_part_flags_access_memory_pool");
791 		if (info.virt.hv.part_flags_adjust_msg_buffers) printf(" hv_part_flags_adjust_msg_buffers");
792 		if (info.virt.hv.part_flags_post_msgs) printf(" hv_part_flags_post_msgs");
793 		if (info.virt.hv.part_flags_signal_events) printf(" hv_part_flags_signal_events");
794 		if (info.virt.hv.part_flags_create_port) printf(" hv_part_flags_create_port");
795 		if (info.virt.hv.part_flags_connect_port) printf(" hv_part_flags_connect_port");
796 		if (info.virt.hv.part_flags_access_stats) printf(" hv_part_flags_access_stats");
797 		if (info.virt.hv.part_flags_debugging) printf(" hv_part_flags_debugging");
798 		if (info.virt.hv.part_flags_cpu_mgmt) printf(" hv_part_flags_cpu_mgmt");
799 		if (info.virt.hv.part_flags_cpu_profiler) printf(" hv_part_flags_cpu_profiler");
800 		if (info.virt.hv.part_flags_expanded_stack_walk) printf(" hv_part_flags_expanded_stack_walk");
801 		if (info.virt.hv.part_flags_access_vsm) printf(" hv_part_flags_access_vsm");
802 		if (info.virt.hv.part_flags_access_vp_regs) printf(" hv_part_flags_access_vp_regs");
803 		if (info.virt.hv.part_flags_extended_hypercalls) printf(" hv_part_flags_extended_hypercalls");
804 		if (info.virt.hv.part_flags_start_vp) printf(" hv_part_flags_start_vp");
805 		if (info.virt.hv.pm_max_cpu_power_state_c0) printf(" hv_pm_max_cpu_power_state_c0");
806 		if (info.virt.hv.pm_max_cpu_power_state_c1) printf(" hv_pm_max_cpu_power_state_c1");
807 		if (info.virt.hv.pm_max_cpu_power_state_c2) printf(" hv_pm_max_cpu_power_state_c2");
808 		if (info.virt.hv.pm_max_cpu_power_state_c3) printf(" hv_pm_max_cpu_power_state_c3");
809 		if (info.virt.hv.pm_hpet_reqd_for_c3) printf(" hv_pm_hpet_reqd_for_c3");
810 		if (info.virt.hv.misc_feat_mwait) printf(" hv_misc_feat_mwait");
811 		if (info.virt.hv.misc_feat_guest_debugging) printf(" hv_misc_feat_guest_debugging");
812 		if (info.virt.hv.misc_feat_perf_mon) printf(" hv_misc_feat_perf_mon");
813 		if (info.virt.hv.misc_feat_pcpu_dyn_part_event) printf(" hv_misc_feat_pcpu_dyn_part_event");
814 		if (info.virt.hv.misc_feat_xmm_hypercall_input) printf(" hv_misc_feat_xmm_hypercall_input");
815 		if (info.virt.hv.misc_feat_guest_idle_state) printf(" hv_misc_feat_guest_idle_state");
816 		if (info.virt.hv.misc_feat_hypervisor_sleep_state) printf(" hv_misc_feat_hypervisor_sleep_state");
817 		if (info.virt.hv.misc_feat_query_numa_distance) printf(" hv_misc_feat_query_numa_distance");
818 		if (info.virt.hv.misc_feat_timer_freq) printf(" hv_misc_feat_timer_freq");
819 		if (info.virt.hv.misc_feat_inject_synmc_xcpt) printf(" hv_misc_feat_inject_synmc_xcpt");
820 		if (info.virt.hv.misc_feat_guest_crash_msrs) printf(" hv_misc_feat_guest_crash_msrs");
821 		if (info.virt.hv.misc_feat_debug_msrs) printf(" hv_misc_feat_debug_msrs");
822 		if (info.virt.hv.misc_feat_npiep1) printf(" hv_misc_feat_npiep1");
823 		if (info.virt.hv.misc_feat_disable_hypervisor) printf(" hv_misc_feat_disable_hypervisor");
824 		if (info.virt.hv.misc_feat_ext_gva_range_for_flush_va_list) printf(" hv_misc_feat_ext_gva_range_for_flush_va_list");
825 		if (info.virt.hv.misc_feat_hypercall_output_xmm) printf(" hv_misc_feat_hypercall_output_xmm");
826 		if (info.virt.hv.misc_feat_sint_polling_mode) printf(" hv_misc_feat_sint_polling_mode");
827 		if (info.virt.hv.misc_feat_hypercall_msr_lock) printf(" hv_misc_feat_hypercall_msr_lock");
828 		if (info.virt.hv.misc_feat_use_direct_synth_msrs) printf(" hv_misc_feat_use_direct_synth_msrs");
829 		if (info.virt.hv.hint_hypercall_for_process_switch) printf(" hv_hint_hypercall_for_process_switch");
830 		if (info.virt.hv.hint_hypercall_for_tlb_flush) printf(" hv_hint_hypercall_for_tlb_flush");
831 		if (info.virt.hv.hint_hypercall_for_tlb_shootdown) printf(" hv_hint_hypercall_for_tlb_shootdown");
832 		if (info.virt.hv.hint_msr_for_apic_access) printf(" hv_hint_msr_for_apic_access");
833 		if (info.virt.hv.hint_msr_for_sys_reset) printf(" hv_hint_msr_for_sys_reset");
834 		if (info.virt.hv.hint_relax_time_checks) printf(" hv_hint_relax_time_checks");
835 		if (info.virt.hv.hint_dma_remapping) printf(" hv_hint_dma_remapping");
836 		if (info.virt.hv.hint_interrupt_remapping) printf(" hv_hint_interrupt_remapping");
837 		if (info.virt.hv.hint_x2apic_msrs) printf(" hv_hint_x2apic_msrs");
838 		if (info.virt.hv.hint_deprecate_auto_eoi) printf(" hv_hint_deprecate_auto_eoi");
839 		if (info.virt.hv.hint_synth_cluster_ipi_hypercall) printf(" hv_hint_synth_cluster_ipi_hypercall");
840 		if (info.virt.hv.hint_ex_proc_masks_interface) printf(" hv_hint_ex_proc_masks_interface");
841 		if (info.virt.hv.hint_nested_hyperv) printf(" hv_hint_nested_hyperv");
842 		if (info.virt.hv.hint_int_for_mbec_syscalls) printf(" hv_hint_int_for_mbec_syscalls");
843 		if (info.virt.hv.hint_nested_enlightened_vmcs_interface) printf(" hv_hint_nested_enlightened_vmcs_interface");
844 		if (info.virt.hv.host_feat_avic) printf(" hv_host_feat_avic");
845 		if (info.virt.hv.host_feat_msr_bitmap) printf(" hv_host_feat_msr_bitmap");
846 		if (info.virt.hv.host_feat_perf_counter) printf(" hv_host_feat_perf_counter");
847 		if (info.virt.hv.host_feat_nested_paging) printf(" hv_host_feat_nested_paging");
848 		if (info.virt.hv.host_feat_dma_remapping) printf(" hv_host_feat_dma_remapping");
849 		if (info.virt.hv.host_feat_interrupt_remapping) printf(" hv_host_feat_interrupt_remapping");
850 		if (info.virt.hv.host_feat_mem_patrol_scrubber) printf(" hv_host_feat_mem_patrol_scrubber");
851 		if (info.virt.hv.host_feat_dma_prot_in_use) printf(" hv_host_feat_dma_prot_in_use");
852 		if (info.virt.hv.host_feat_hpet_requested) printf(" hv_host_feat_hpet_requested");
853 		if (info.virt.hv.host_feat_stimer_volatile) printf(" hv_host_feat_stimer_volatile");
854 		break;
855 	case VirtVendor.KVM:
856 		if (info.virt.kvm.feature_clocksource) printf(" kvm_feature_clocksource");
857 		if (info.virt.kvm.feature_nop_io_delay) printf(" kvm_feature_nop_io_delay");
858 		if (info.virt.kvm.feature_mmu_op) printf(" kvm_feature_mmu_op");
859 		if (info.virt.kvm.feature_clocksource2) printf(" kvm_feature_clocksource2");
860 		if (info.virt.kvm.feature_async_pf) printf(" kvm_feature_async_pf");
861 		if (info.virt.kvm.feature_steal_time) printf(" kvm_feature_steal_time");
862 		if (info.virt.kvm.feature_pv_eoi) printf(" kvm_feature_pv_eoi");
863 		if (info.virt.kvm.feature_pv_unhault) printf(" kvm_feature_pv_unhault");
864 		if (info.virt.kvm.feature_pv_tlb_flush) printf(" kvm_feature_pv_tlb_flush");
865 		if (info.virt.kvm.feature_async_pf_vmexit) printf(" kvm_feature_async_pf_vmexit");
866 		if (info.virt.kvm.feature_pv_send_ipi) printf(" kvm_feature_pv_send_ipi");
867 		if (info.virt.kvm.feature_pv_poll_control) printf(" kvm_feature_pv_poll_control");
868 		if (info.virt.kvm.feature_pv_sched_yield) printf(" kvm_feature_pv_sched_yield");
869 		if (info.virt.kvm.feature_clocsource_stable_bit) printf(" kvm_feature_clocsource_stable_bit");
870 		if (info.virt.kvm.hint_realtime) printf(" kvm_hints_realtime");
871 		break;
872 	default:
873 	}
874 	
875 	printf("\nMemory      :");
876 	
877 	if (info.memory.pae) printf(" pae");
878 	if (info.memory.pse) printf(" pse");
879 	if (info.memory.pse36) printf(" pse-36");
880 	if (info.memory.page1gb) printf(" page1gb");
881 	if (info.memory.nx) {
882 		switch (info.vendor.id) with (Vendor) {
883 		case Intel:	tstr = " intel-xd/nx"; break;
884 		case AMD:	tstr = " amd-evp/nx"; break;
885 		default:	tstr = " nx";
886 		}
887 		printf(tstr);
888 	}
889 	if (info.memory.dca) printf(" dca");
890 	if (info.memory.pat) printf(" pat");
891 	if (info.memory.mtrr) printf(" mtrr");
892 	if (info.memory.pge) printf(" pge");
893 	if (info.memory.smep) printf(" smep");
894 	if (info.memory.smap) printf(" smap");
895 	if (info.memory.pku) printf(" pku");
896 	if (info.memory._5pl) printf(" 5pl");
897 	if (info.memory.fsrepmov) printf(" fsrm");
898 	if (info.memory.lam) printf(" lam");
899 	
900 	with (info.memory)
901 	printf("\nPhysicalBits: %u\nLinearBits  : %u\nDebugging   :",
902 		physBits, lineBits);
903 	
904 	if (info.debugging.mca) printf(" mca");
905 	if (info.debugging.mce) printf(" mce");
906 	if (info.debugging.de) printf(" de");
907 	if (info.debugging.ds) printf(" ds");
908 	if (info.debugging.ds_cpl) printf(" ds-cpl");
909 	if (info.debugging.dtes64) printf(" dtes64");
910 	if (info.debugging.pdcm) printf(" pdcm");
911 	if (info.debugging.sdbg) printf(" sdbg");
912 	if (info.debugging.pbe) printf(" pbe");
913 	
914 	printf("\nSecurity    :");
915 	if (info.security.ia32_arch_capabilities) printf(" ia32_arch_capabilities");
916 	printSecurity(info);
917 	
918 	with (info)
919 	printf(
920 	"\nMax. Leaf   : 0x%x\n"~
921 	"Max. V-Leaf : 0x%x\n"~
922 	"Max. E-Leaf : 0x%x\n"~
923 	"Type        : %s\n"~
924 	"Brand Index : %u\n"~
925 	"Misc.       :",
926 		maxLeaf, maxLeafVirt, maxLeafExtended, typeString, brandIndex);
927 	
928 	if (info.misc.xtpr) printf(" xtpr");
929 	if (info.misc.psn) printf(" psn");
930 	if (info.misc.pcid) printf(" pcid");
931 	if (info.misc.fsgsbase) printf(" fsgsbase");
932 	if (info.misc.uintr) printf(" uintr");
933 	
934 	putchar('\n');
935 	
936 	return 0;
937 }