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