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 is used by default.
6  *
7  * Authors: dd86k (dd@dax.moe)
8  * Copyright: © 2016-2021 dd86k
9  * License: MIT
10  */
11 module main;
12 
13 import core.stdc.errno : errno;
14 import core.stdc.stdio : printf, puts, sscanf, FILE, fopen, fread, fwrite;
15 import core.stdc.string : strcmp, strerror;
16 import ddcpuid;
17 
18 private:
19 @system:
20 extern (C):
21 
22 int putchar(int);
23 
24 /// Compiler version template for betterC usage
25 template CVER(int v) {
26 	enum CVER =
27 		cast(char)((v / 1000) + '0') ~
28 		"." ~
29 		cast(char)(((v % 1000) / 100) + '0') ~
30 		cast(char)(((v % 100) / 10) + '0') ~
31 		cast(char)((v % 10) + '0');
32 }
33 
34 /// Make a bit mask of one bit at n position
35 template BIT(int n) if (n <= 31) { enum uint BIT = 1 << n; }
36 
37 enum : uint {
38 	MAX_LEAF	= 0x20, /// Maximum leaf override
39 	MAX_VLEAF	= 0x4000_0000 + MAX_LEAF, /// Maximum virt leaf override
40 	MAX_ELEAF	= 0x8000_0000 + MAX_LEAF, /// Maximum extended leaf override
41 }
42 
43 /// Command-line options
44 struct options_t { align(1):
45 //	FILE *file;	/// Dump
46 	int maxLevel;	/// Maximum leaf for -r (-S)
47 	int maxSub;	/// Maximum subleaf for -r (-s)
48 	bool hasLevel;	/// If -S has been used
49 	bool table;	/// Raw table (-r)
50 	bool override_;	/// Override leaves (-o)
51 	bool getLevel;	/// Get x86-64 optimization feature level
52 	bool getDetails;	/// Get the boring details
53 	bool[3] reserved;	/// 
54 }
55 
56 immutable const(char) *secret = r"
57                             ############
58                      ######################
59                 ###########################
60             #################
61          ############              #######
62        #########     _      _     ### R #####
63      #######        | |    | |     ###########
64    #######        __| |  __| |          ########
65   #####          / _  | / _  |             ######
66  #####          | (_| || (_| |               #####
67 ####             \____| \____|                ####
68 ###                             _              ###
69 ###      [_]            [_]    | |              ##
70 ##        _  _ __   ___  _   __| | ____        ###
71 ###      | || '_  \/ __/| | / _  |/ __ \       ###
72 ###      | || | | |\__ \| || (_| ||  __/      ####
73  ###     |_||_| |_||___/|_| \____|\____|    #####
74  #####                                     #####
75   ######                               ######
76     #######                          #######
77       #########                ###########
78         ###############################
79             ######################
80 ";
81 
82 //TODO: Consider having a CPUINFO instance globally to avoid parameter spam
83 
84 /// print help page
85 void clih() {
86 	puts(
87 	"x86/AMD64 CPUID information tool\n"~
88 	"\n"~
89 	"USAGE\n"~
90 	" ddcpuid [OPTIONS...]\n"~
91 	"\n"~
92 	"OPTIONS\n"~
93 	" -d, --details  Show detailed processor information\n"~
94 	" -r, --table    Show raw CPUID data in a table\n"~
95 	" -S             Table: Set leaf (EAX) input value\n"~
96 	" -s             Table: Set subleaf (ECX) input value\n"~
97 //	" -D, --dump     Dump CPUID data into binary\n"~
98 	" -o             Override maximum leaves to 0x20, 0x4000_0020, and 0x8000_0020\n"~
99 	" -l, --level    Print the processor's feature level\n"~
100 	"\n"~
101 	"PAGES\n"~
102 	" --version    Print version screen and quit\n"~
103 	" --ver        Print version and quit\n"~
104 	" -h, --help   Print this help screen and quit"
105 	);
106 }
107 
108 /// print version page
109 void cliv() {
110 	puts(
111 	"ddcpuid-"~DDCPUID_PLATFORM~" "~DDCPUID_VERSION~" (built: "~__TIMESTAMP__~")\n"~
112 	"Copyright (c) 2016-2021 dd86k <dd@dax.moe>\n"~
113 	"License: MIT License <http://opensource.org/licenses/MIT>\n"~
114 	"Homepage: <https://github.com/dd86k/ddcpuid>\n"~
115 	"Compiler: "~__VENDOR__~" "~CVER!(__VERSION__)
116 	);
117 }
118 
119 void outcpuid(uint leaf, uint sub/*, FILE *file*/) {
120 	REGISTERS regs = void;
121 	asmcpuid(regs, leaf, sub);
122 /*	if (file) {
123 		dumpWrite(file, &leaf, leaf.sizeof);
124 		dumpWrite(file, &sub,  sub.sizeof);
125 		dumpWrite(file, &regs, regs.sizeof);
126 	} else {*/
127 		printcpuid(regs, leaf, sub);
128 //	}
129 }
130 
131 /// Print cpuid table entry into stdout.
132 /// Params:
133 /// 	leaf = EAX input
134 /// 	sub = ECX input
135 pragma(inline, false) // ldc optimization thing
136 void printcpuid(ref REGISTERS regs, uint leaf, uint sub) {
137 	with (regs)
138 	printf("| %8x | %8x | %8x | %8x | %8x | %8x |\n",
139 		leaf, sub, eax, ebx, ecx, edx);
140 }
141 
142 // NOTE: ddcpuid dump structure
143 //       char[4]: "ddcu"
144 //       ubyte  : file format version
145 //       ubyte  : reserved
146 //       ubyte  : reserved
147 //       ubyte  : reserved
148 //       uint[6]: leaf, subleaf, eax, ebx, ecx, edx
149 
150 /*int dumpOpen(FILE **file, const(char) *path) {
151 	return (*file = fopen(path, "w+b")) == null ? errno : 0;
152 }
153 
154 int dumpWrite(FILE *file, const(void) *data, size_t size) {
155 	return fwrite(data, size, 1, file) != 1;
156 }
157 
158 int dumpRead(FILE *file) {
159 	
160 }*/
161 
162 const(char) *classification(ref CPUINFO info) {
163 	// That's a story for another time
164 	if (info.extensions.x86_64 == false)	goto L_X86_64_NONE;
165 	
166 	// v4
167 	if (info.avx.avx512f && info.avx.avx512bw &&
168 		info.avx.avx512cd && info.avx.avx512dq &&
169 		info.avx.avx512vl) {
170 		return "x86-64-v4";
171 	}
172 	
173 	// v3
174 	if (info.avx.avx2 && info.avx.avx &&
175 		info.extensions.bmi2 && info.extensions.bmi1 &&
176 		info.extensions.f16c && info.extensions.fma3 &&
177 		info.extras.lzcnt && info.extras.movbe &&
178 		info.extras.osxsave) {
179 		return "x86-64-v3";
180 	}
181 	
182 	// v2
183 	if (info.sse.sse42 && info.sse.sse41 &&
184 		info.sse.ssse3 && info.sse.sse3 &&
185 		info.extensions.lahf64 && info.extras.popcnt &&
186 		info.extras.cmpxchg16b) {
187 		return "x86-64-v2";
188 	}
189 	
190 	// baseline
191 	if (info.sse.sse2 && info.sse.sse &&
192 		info.extensions.mmx && info.extras.fxsr &&
193 		info.extras.cmpxchg8b && info.extras.cmov &&
194 		info.extensions.fpu && info.extras.syscall) {
195 		return "x86-64"; // v1/baseline
196 	}
197 	
198 L_X86_64_NONE:
199 	return "i386";
200 }
201 
202 char adjust(ref float size) {
203 	version (Trace) trace("size=%u", size);
204 	if (size >= 1024.0) {
205 		size /= 1024;
206 		return 'M';
207 	}
208 	return 'K';
209 }
210 /// adjust
211 @system unittest {
212 	uint size = 1;
213 	assert(adjust(size) == 'K');
214 	assert(size == 1);
215 	size = 1024;
216 	assert(adjust(size) == 'M');
217 	assert(size == 1);
218 	size = 4096;
219 	assert(adjust(size) == 'M');
220 	assert(size == 4);
221 }
222 char adjustBits(ref uint size, int bitpos) {
223 	version (Trace) trace("size=%u bit=%d", size, bitpos);
224 	immutable char[8] SIZE = [ 0, 'K', 'M', 'G', 'T', 'P', 'E', 'Z' ];
225 	size_t s;
226 	while (bitpos >= 10) {
227 		bitpos -= 10;
228 		++s;
229 	}
230 	size = 1 << bitpos;
231 	return SIZE[s];
232 }
233 //TODO: Make DUB to include this main, somehow
234 /// adjustBits
235 @system unittest {
236 	float size;
237 	assert(adjustBits(size, 0) == 0);
238 	assert(size == 1);
239 	assert(adjustBits(size, 1) == 0);
240 	assert(size == 2);
241 	assert(adjustBits(size, 10) == 'K');
242 	assert(size == 1);
243 	assert(adjustBits(size, 11) == 'K');
244 	assert(size == 2);
245 	assert(adjustBits(size, 20) == 'M');
246 	assert(size == 1);
247 	assert(adjustBits(size, 36) == 'G');
248 	assert(size == 64);
249 	assert(adjustBits(size, 48) == 'T');
250 	assert(size == 256);
251 }
252 
253 void printLegacy(ref CPUINFO info) {
254 	if (info.extensions.fpu) {
255 		printf(" x87/FPU");
256 		if (info.extensions.f16c) printf(" +F16C");
257 	}
258 	if (info.extensions.mmx) {
259 		printf(" MMX");
260 		if (info.extensions.mmxExtended) printf(" ExtMMX");
261 	}
262 	if (info.extensions._3DNow) {
263 		printf(" 3DNow!");
264 		if (info.extensions._3DNowExtended) printf(" Ext3DNow!");
265 	}
266 }
267 void printTechs(ref CPUINFO info) {
268 	switch (info.vendorId) {
269 	case Vendor.Intel:
270 		if (info.tech.eist) printf(" EIST");
271 		if (info.tech.turboboost) {
272 			printf(" TurboBoost");
273 			if (info.tech.turboboost30) printf("-3.0");
274 		}
275 		if (info.memory.tsx) {
276 			printf(" TSX");
277 			if (info.memory.hle)
278 				printf(" +HLE");
279 			if (info.memory.rtm)
280 				printf(" +RTM");
281 			if (info.memory.tsxldtrk)
282 				printf(" +TSXLDTRK");
283 		}
284 		if (info.tech.smx) printf(" Intel-TXT/SMX");
285 		if (info.sgx.supported) {
286 			// NOTE: SGX system configuration
287 			//       "enabled" in BIOS: only CPUID.7h.EBX[2]
288 			//       "user controlled" in BIOS: SGX1/SGX2/size bits
289 			if (info.sgx.sgx1 && info.sgx.sgx2) {
290 				if (info.sgx.sgx1) printf(" SGX1");
291 				if (info.sgx.sgx2) printf(" SGX2");
292 			} else printf(" SGX"); // Fallback per-say
293 			if (info.sgx.maxSize) {
294 				uint s32 = void, s64 = void;
295 				char m32 = adjustBits(s32, info.sgx.maxSize);
296 				char m64 = adjustBits(s64, info.sgx.maxSize64);
297 				/*printf(" +maxSize=%u +maxSize64=%u",
298 					1 << info.sgx.maxSize,
299 					1 << info.sgx.maxSize64);*/
300 				printf(" +maxSize=%u%cB +maxSize64=%u%cB", s32, m32, s64, m64);
301 			}
302 		}
303 		break;
304 	case Vendor.AMD:
305 		if (info.tech.turboboost) printf(" Core-Performance-Boost");
306 		break;
307 	default:
308 	}
309 	if (info.tech.htt) printf(" HTT");
310 }
311 void printSSE(ref CPUINFO info) {
312 	printf(" SSE");
313 	if (info.sse.sse2) printf(" SSE2");
314 	if (info.sse.sse3) printf(" SSE3");
315 	if (info.sse.ssse3) printf(" SSSE3");
316 	if (info.sse.sse41) printf(" SSE4.1");
317 	if (info.sse.sse42) printf(" SSE4.2");
318 	if (info.sse.sse4a) printf(" SSE4a");
319 }
320 void printAVX(ref CPUINFO info) {
321 	printf(" AVX");
322 	if (info.avx.avx2) printf(" AVX2");
323 	if (info.avx.avx512f) {
324 		printf(" AVX512F");
325 		if (info.avx.avx512er) printf(" +ER");
326 		if (info.avx.avx512pf) printf(" +PF");
327 		if (info.avx.avx512cd) printf(" +CD");
328 		if (info.avx.avx512dq) printf(" +DQ");
329 		if (info.avx.avx512bw) printf(" +BW");
330 		if (info.avx.avx512vl) printf(" +VL");
331 		if (info.avx.avx512_ifma) printf(" +IFMA");
332 		if (info.avx.avx512_vbmi) printf(" +VBMI");
333 		if (info.avx.avx512_4vnniw) printf(" +4VNNIW");
334 		if (info.avx.avx512_4fmaps) printf(" +4FMAPS");
335 		if (info.avx.avx512_vbmi2) printf(" +VBMI2");
336 		if (info.avx.avx512_gfni) printf(" +GFNI");
337 		if (info.avx.avx512_vaes) printf(" +VAES");
338 		if (info.avx.avx512_vnni) printf(" +VNNI");
339 		if (info.avx.avx512_bitalg) printf(" +BITALG");
340 		if (info.avx.avx512_bf16) printf(" +BF16");
341 		if (info.avx.avx512_vp2intersect) printf(" +VP2INTERSECT");
342 	}
343 	if (info.extensions.xop) printf(" XOP");
344 }
345 void printFMA(ref CPUINFO info) {
346 	if (info.extensions.fma3) printf(" FMA3");
347 	if (info.extensions.fma4) printf(" FMA4");
348 }
349 void printAMX(ref CPUINFO info) {
350 	printf(" AMX");
351 	if (info.amx.bf16) printf(" +BF16");
352 	if (info.amx.int8) printf(" +INT8");
353 	if (info.amx.xtilecfg) printf(" +XTILECFG");
354 	if (info.amx.xtiledata) printf(" +XTILEDATA");
355 	if (info.amx.xfd) printf(" +XFD");
356 }
357 void printOthers(ref CPUINFO info) {
358 	if (info.extensions.aes_ni) printf(" AES-NI");
359 	if (info.extensions.adx) printf(" ADX");
360 	if (info.extensions.sha) printf(" SHA");
361 	if (info.extensions.tbm) printf(" TBM");
362 	if (info.extensions.bmi1) printf(" BMI1");
363 	if (info.extensions.bmi2) printf(" BMI2");
364 	if (info.extensions.waitpkg) printf(" WAITPKG");
365 }
366 void printSecurity(ref CPUINFO info) {
367 	if (info.security.ibpb) printf(" IBPB");
368 	if (info.security.ibrs) printf(" IBRS");
369 	if (info.security.ibrsAlwaysOn) printf(" IBRS_ON");	// AMD
370 	if (info.security.ibrsPreferred) printf(" IBRS_PREF");	// AMD
371 	if (info.security.stibp) printf(" STIBP");
372 	if (info.security.stibpAlwaysOn) printf(" STIBP_ON");	// AMD
373 	if (info.security.ssbd) printf(" SSBD");
374 	if (info.security.l1dFlush) printf(" L1D_FLUSH");	// Intel
375 	if (info.security.md_clear) printf(" MD_CLEAR");	// Intel
376 	if (info.security.cetIbt) printf(" CET_IBT");	// Intel
377 	if (info.security.cetSs) printf(" CET_SS");	// Intel
378 }
379 void printCacheFeats(ushort feats) {
380 	if (feats & BIT!(0)) printf(" SI"); // Self Initiative
381 	if (feats & BIT!(1)) printf(" FA"); // Fully Associative
382 	if (feats & BIT!(2)) printf(" NWBV"); // No Write-Back Validation
383 	if (feats & BIT!(3)) printf(" CI"); // Cache Inclusive
384 	if (feats & BIT!(4)) printf(" CCI"); // Complex Cache Indexing
385 }
386 
387 version (unittest) {} else
388 int main(int argc, const(char) **argv) {
389 	options_t options;	/// Command-line options
390 	
391 	const(char) *arg = void;
392 //	int e = void;
393 	for (int argi = 1; argi < argc; ++argi) {
394 		if (argv[argi][1] == '-') { // Long arguments
395 			arg = argv[argi] + 2;
396 /*			if (strcmp(arg, "dump") == 0) {
397 				if (++argi >= argc) {
398 					puts("Missing argument: path");
399 					return 1;
400 				}
401 				e = dumpOpen(&options.file, argv[argi]);
402 				if (e) {
403 					printf("Couldn't open file: %s\n", strerror(e));
404 					return 2;
405 				}
406 				continue;
407 			}*/
408 			if (strcmp(arg, "table") == 0) {
409 				options.table = true;
410 				continue;
411 			}
412 			if (strcmp(arg, "level") == 0) {
413 				options.getLevel = true;
414 				continue;
415 			}
416 			if (strcmp(arg, "details") == 0) {
417 				options.getDetails = true;
418 				continue;
419 			}
420 			if (strcmp(arg, "version") == 0) {
421 				cliv;
422 				return 0;
423 			}
424 			if (strcmp(arg, "ver") == 0) {
425 				puts(DDCPUID_VERSION);
426 				return 0;
427 			}
428 			if (strcmp(arg, "help") == 0) {
429 				clih;
430 				return 0;
431 			}
432 			if (strcmp(arg, "inside") == 0) {
433 				puts(secret);
434 				return 0;
435 			}
436 			printf("Unknown parameter: '%s'\n", arg);
437 			return 1;
438 		} else if (argv[argi][0] == '-') { // Short arguments
439 			arg = argv[argi] + 1;
440 			char o = void;
441 			while ((o = *arg) != 0) {
442 				++arg;
443 				switch (o) {
444 				case 'd': options.getDetails = true; continue;
445 				case 'l': options.getLevel = true; continue;
446 				case 'o': options.override_ = true; continue;
447 				case 'r': options.table = true; continue;
448 /*				case 'D':
449 					if (++argi >= argc) {
450 						puts("Missing parameter: file");
451 						return 1;
452 					}
453 					e = dumpOpen(&options.file, argv[argi]);
454 					if (e) {
455 						printf("Couldn't open file: %s\n", strerror(e));
456 						return 2;
457 					}
458 					continue;*/
459 				case 'S':
460 					if (++argi >= argc) {
461 						puts("Missing parameter: leaf");
462 						return 1;
463 					}
464 					options.hasLevel = sscanf(argv[argi], "%i", &options.maxLevel) == 1;
465 					if (options.hasLevel == false) {
466 						puts("Could not parse level (-S)");
467 						return 2;
468 					}
469 					continue;
470 				case 's':
471 					if (++argi >= argc) {
472 						puts("Missing parameter: sub-leaf (-s)");
473 						return 1;
474 					}
475 					if (sscanf(argv[argi], "%i", &options.maxSub) != 1) {
476 						puts("Could not parse sub-level (-s)");
477 						return 2;
478 					}
479 					continue;
480 				case 'h': clih; return 0;
481 				case 'V': cliv; return 0;
482 				default:
483 					printf("Unknown parameter: '-%c'\n", o);
484 					return 1;
485 				}
486 			} // while
487 		} // else if
488 	} // for
489 	
490 	CPUINFO info;
491 	
492 	if (options.override_ == false) {
493 		getLeaves(info);
494 	} else {
495 		info.maxLeaf = MAX_LEAF;
496 		info.maxLeafVirt = MAX_VLEAF;
497 		info.maxLeafExtended = MAX_ELEAF;
498 	}
499 	
500 	if (options.table/* || options.file*/) { // -r|-D
501 		uint l = void, s = void;
502 		//TODO: outcpuid should just accept ref options_t and s
503 
504 //		if (options.file) {
505 //			__gshared const(char) *HEADER = "ddcu\x01\x00\x00\x00";
506 //			dumpWrite(options.file, HEADER, 8);
507 //		} else {
508 			puts(
509 			"| Leaf     | Sub-leaf | EAX      | EBX      | ECX      | EDX      |\n"~
510 			"|----------|----------|----------|----------|----------|----------|"
511 			);
512 //		}
513 
514 		if (options.hasLevel) {
515 			for (s = 0; s <= options.maxSub; ++s)
516 				outcpuid(options.maxLevel, s/*, options.file*/);
517 			return 0;
518 		}
519 		
520 		// Normal
521 		for (l = 0; l <= info.maxLeaf; ++l)
522 			for (s = 0; s <= options.maxSub; ++s)
523 				outcpuid(l, s/*, options.file*/);
524 		
525 		// Paravirtualization
526 		if (info.maxLeafVirt > 0x4000_0000)
527 		for (l = 0x4000_0000; l <= info.maxLeafVirt; ++l)
528 			for (s = 0; s <= options.maxSub; ++s)
529 				outcpuid(l, s/*, options.file*/);
530 		
531 		// Extended
532 		for (l = 0x8000_0000; l <= info.maxLeafExtended; ++l)
533 			for (s = 0; s <= options.maxSub; ++s)
534 				outcpuid(l, s/*, options.file*/);
535 		return 0;
536 	}
537 	
538 	getInfo(info);
539 	
540 	if (options.getLevel) {
541 		puts(classification(info));
542 		return 0;
543 	}
544 	
545 	// NOTE: .ptr crash with GDC -O3
546 	//       glibc!__strlen_sse2 (in printf)
547 	char *vendor = cast(char*)info.vendorString;
548 	char *brand  = cast(char*)info.brandString;
549 	
550 	// Brand string left space trimming
551 	// Extremely common in Intel but let's also do it for others
552 	while (*brand == ' ') ++brand;
553 	
554 	CACHEINFO *cache = void;	/// Current cache level
555 	
556 	//
557 	// ANCHOR Summary
558 	//
559 	
560 	if (options.getDetails == false) {
561 		uint maxPhys = void;
562 		uint maxLine = void;
563 		char cphys = adjustBits(maxPhys, info.memory.physBits);
564 		char cline = adjustBits(maxLine, info.memory.lineBits);
565 		with (info) printf(
566 		"Name:        %.12s %.48s\n"~
567 		"Identifier:  Family 0x%x Model 0x%x Stepping 0x%x\n"~
568 		"Cores:       %u cores %u threads\n"~
569 		"Max. Memory: %u%cB physical %u%cB virtual\n"~
570 		"Techs:       %s",
571 		vendor, brand,
572 		family, model, stepping,
573 		cores.physical, cores.logical,
574 		maxPhys, cphys, maxLine, cline,
575 		classification(info)
576 		);
577 		
578 		printTechs(info);
579 		
580 		immutable const(char) *none = " None";
581 		
582 		printf("\nSSE:        ");
583 		if (info.sse.sse) {
584 			printSSE(info);
585 			putchar('\n');
586 		} else puts(none);
587 		
588 		printf("AVX:        ");
589 		if (info.avx.avx) {
590 			printAVX(info);
591 			putchar('\n');
592 		} else puts(none);
593 		
594 		printf("AMX:        ");
595 		if (info.amx.enabled) {
596 			printAMX(info);
597 			putchar('\n');
598 		} else puts(none);
599 		
600 		printf("Others:     ");
601 		printLegacy(info);
602 		printOthers(info);
603 		putchar('\n');
604 		
605 		printf("Mitigations:");
606 		printSecurity(info);
607 		putchar('\n');
608 		
609 		if (info.virt.vendorId) {
610 			const(char) *virtVendor = void;
611 			switch (info.virt.vendorId) with (VirtVendor) {
612 			case KVM:        virtVendor = "KVM"; break;
613 			case HyperV:     virtVendor = "Hyper-V"; break;
614 			case VBoxHyperV: virtVendor = "VirtualBox Hyper-V"; break;
615 			case VBoxMin:    virtVendor = "VirtualBox Minimal"; break;
616 			default:         virtVendor = "Unknown";
617 			}
618 			printf("ParaVirt.:   %s\n", virtVendor);
619 		}
620 		
621 		for (size_t i; i < info.cache.levels; ++i) {
622 			cache = &info.cache.level[i];
623 			float csize = cache.size;
624 			float tsize = csize * cache.sharedCores;
625 			char cc = adjust(csize);
626 			char ct = adjust(tsize);
627 			with (cache)
628 			printf("Cache L%u-%c:  %ux %g%cB\t(%g%cB)\t",
629 				level, type, sharedCores,
630 				csize, cc, tsize, ct);
631 			printCacheFeats(cache.features);
632 			putchar('\n');
633 		}
634 		
635 		return 0;
636 	}
637 	
638 	//
639 	// ANCHOR Detailed view
640 	//
641 	
642 	with (info) printf(
643 	"Vendor      : %.12s\n"~
644 	"Brand       : %.48s\n"~
645 	"Identifier  : 0x%x\n"~
646 	"Family      : 0x%x\n"~
647 	"BaseFamily  : 0x%x\n"~
648 	"ExtFamily   : 0x%x\n"~
649 	"Model       : 0x%x\n"~
650 	"BaseModel   : 0x%x\n"~
651 	"ExtModel    : 0x%x\n"~
652 	"Stepping    : 0x%x\n"~
653 	"Cores       : %u\n"~
654 	"Threads     : %u\n"~
655 	"Extensions  :",
656 	vendor, brand,
657 	identifier,
658 	family, familyBase, familyExtended,
659 	model, modelBase, modelExtended,
660 	stepping,
661 	cores.physical, cores.logical
662 	);
663 	
664 	printLegacy(info);
665 	if (info.sse.sse) printSSE(info);
666 	if (info.extensions.x86_64) {
667 		switch (info.vendorId) {
668 		case Vendor.Intel: printf(" Intel64/x86-64"); break;
669 		case Vendor.AMD: printf(" AMD64/x86-64"); break;
670 		default: printf(" x86-64");
671 		}
672 		if (info.extensions.lahf64)
673 			printf(" +LAHF64");
674 	}
675 	if (info.virt.available)
676 		switch (info.vendorId) {
677 		case Vendor.Intel: printf(" VT-x/VMX"); break;
678 		case Vendor.AMD: // SVM
679 			printf(" AMD-V/VMX");
680 			if (info.virt.version_)
681 				printf(":v%u", info.virt.version_);
682 			break;
683 		case Vendor.VIA: printf(" VIA-VT/VMX"); break;
684 		default: printf(" VMX");
685 		}
686 	if (info.avx.avx) printAVX(info);
687 	printFMA(info);
688 	printOthers(info);
689 	if (info.amx.enabled) printAMX(info);
690 	
691 	//
692 	// ANCHOR Extra/lone instructions
693 	//
694 	
695 	printf("\nExtra       :");
696 	if (info.extras.monitor) {
697 		printf(" MONITOR+MWAIT");
698 		if (info.extras.mwaitMin)
699 			printf(" +MIN=%u +MAX=%u",
700 				info.extras.mwaitMin, info.extras.mwaitMax);
701 		if (info.extras.monitorx) printf(" MONITORX+MWAITX");
702 	}
703 	if (info.extras.pclmulqdq) printf(" PCLMULQDQ");
704 	if (info.extras.cmpxchg8b) printf(" CMPXCHG8B");
705 	if (info.extras.cmpxchg16b) printf(" CMPXCHG16B");
706 	if (info.extras.movbe) printf(" MOVBE");
707 	if (info.extras.rdrand) printf(" RDRAND");
708 	if (info.extras.rdseed) printf(" RDSEED");
709 	if (info.extras.rdmsr) printf(" RDMSR+WRMSR");
710 	if (info.extras.sysenter) printf(" SYSENTER+SYSEXIT");
711 	if (info.extras.syscall) printf(" SYSCALL+SYSRET");
712 	if (info.extras.rdtsc) {
713 		printf(" RDTSC");
714 		if (info.extras.rdtscDeadline)
715 			printf(" +TSC-Deadline");
716 		if (info.extras.rdtscInvariant)
717 			printf(" +TSC-Invariant");
718 	}
719 	if (info.extras.rdtscp) printf(" RDTSCP");
720 	if (info.extras.rdpid) printf(" RDPID");
721 	if (info.extras.cmov) {
722 		printf(" CMOV");
723 		if (info.extensions.fpu) printf(" FCOMI+FCMOV");
724 	}
725 	if (info.extras.lzcnt) printf(" LZCNT");
726 	if (info.extras.popcnt) printf(" POPCNT");
727 	if (info.extras.xsave) printf(" XSAVE+XRSTOR");
728 	if (info.extras.osxsave) printf(" XSETBV+XGETBV");
729 	if (info.extras.fxsr) printf(" FXSAVE+FXRSTOR");
730 	if (info.extras.pconfig) printf(" PCONFIG");
731 	if (info.extras.cldemote) printf(" CLDEMOTE");
732 	if (info.extras.movdiri) printf(" MOVDIRI");
733 	if (info.extras.movdir64b) printf(" MOVDIR64B");
734 	if (info.extras.enqcmd) printf(" ENQCMD");
735 	if (info.extras.skinit) printf(" SKINIT+STGI");
736 	if (info.extras.serialize) printf(" SERIALIZE");
737 	
738 	//
739 	// ANCHOR Vendor specific technologies
740 	//
741 	
742 	printf("\nTechnologies:");
743 	printTechs(info);
744 	
745 	//
746 	// ANCHOR Cache information
747 	//
748 	
749 	printf("\nCache       :");
750 	if (info.cache.clflush)
751 		printf(" CLFLUSH=%uB", info.cache.clflushLinesize << 3);
752 	if (info.cache.clflushopt) printf(" CLFLUSHOPT");
753 	if (info.cache.cnxtId) printf(" CNXT-ID");
754 	if (info.cache.ss) printf(" SS");
755 	if (info.cache.prefetchw) printf(" PREFETCHW");
756 	if (info.cache.invpcid) printf(" INVPCID");
757 	if (info.cache.wbnoinvd) printf(" WBNOINVD");
758 	
759 	for (uint i; i < info.cache.levels; ++i) {
760 		cache = &info.cache.level[i];
761 		printf("\nLevel %u-%c   : %ux %5u KB, %u ways, %u parts, %u B, %u sets",
762 			cache.level, cache.type, cache.sharedCores, cache.size,
763 			cache.ways, cache.partitions, cache.lineSize, cache.sets
764 		);
765 		printCacheFeats(cache.features);
766 	}
767 	
768 	printf("\nSystem      :");
769 	if (info.sys.available) printf(" ACPI");
770 	if (info.sys.apic) printf(" APIC");
771 	if (info.sys.x2apic) printf(" x2APIC");
772 	if (info.sys.arat) printf(" ARAT");
773 	if (info.sys.tm) printf(" TM");
774 	if (info.sys.tm2) printf(" TM2");
775 	printf(" APIC-ID=%u", info.sys.apicId);
776 	if (info.sys.maxApicId) printf(" MAX-ID=%u", info.sys.maxApicId);
777 	
778 	printf("\nVirtual     :");
779 	if (info.virt.vme) printf(" VME");
780 	if (info.virt.apicv) printf(" APICv");
781 	
782 	// Paravirtualization
783 	if (info.virt.vendorId) {
784 		// See vendor string case
785 		char *virtvendor = cast(char*)info.virt.vendorString;
786 		printf(" HOST=%.12s", virtvendor);
787 	}
788 	switch (info.virt.vendorId) {
789 	case VirtVendor.VBoxMin:
790 		if (info.virt.vbox.tsc_freq_khz)
791 			printf(" TSC_FREQ_KHZ=%u", info.virt.vbox.tsc_freq_khz);
792 		if (info.virt.vbox.apic_freq_khz)
793 			printf(" APIC_FREQ_KHZ=%u", info.virt.vbox.apic_freq_khz);
794 		break;
795 	case VirtVendor.HyperV:
796 		printf(" OPENSOURCE=%d VENDOR_ID=%d OS=%d MAJOR=%d MINOR=%d SERVICE=%d BUILD=%d",
797 			info.virt.hv.guest_opensource,
798 			info.virt.hv.guest_vendor_id,
799 			info.virt.hv.guest_os,
800 			info.virt.hv.guest_major,
801 			info.virt.hv.guest_minor,
802 			info.virt.hv.guest_service,
803 			info.virt.hv.guest_build);
804 		if (info.virt.hv.base_feat_vp_runtime_msr) printf(" HV_BASE_FEAT_VP_RUNTIME_MSR");
805 		if (info.virt.hv.base_feat_part_time_ref_count_msr) printf(" HV_BASE_FEAT_PART_TIME_REF_COUNT_MSR");
806 		if (info.virt.hv.base_feat_basic_synic_msrs) printf(" HV_BASE_FEAT_BASIC_SYNIC_MSRS");
807 		if (info.virt.hv.base_feat_stimer_msrs) printf(" HV_BASE_FEAT_STIMER_MSRS");
808 		if (info.virt.hv.base_feat_apic_access_msrs) printf(" HV_BASE_FEAT_APIC_ACCESS_MSRS");
809 		if (info.virt.hv.base_feat_hypercall_msrs) printf(" HV_BASE_FEAT_HYPERCALL_MSRS");
810 		if (info.virt.hv.base_feat_vp_id_msr) printf(" HV_BASE_FEAT_VP_ID_MSR");
811 		if (info.virt.hv.base_feat_virt_sys_reset_msr) printf(" HV_BASE_FEAT_VIRT_SYS_RESET_MSR");
812 		if (info.virt.hv.base_feat_stat_pages_msr) printf(" HV_BASE_FEAT_STAT_PAGES_MSR");
813 		if (info.virt.hv.base_feat_part_ref_tsc_msr) printf(" HV_BASE_FEAT_PART_REF_TSC_MSR");
814 		if (info.virt.hv.base_feat_guest_idle_state_msr) printf(" HV_BASE_FEAT_GUEST_IDLE_STATE_MSR");
815 		if (info.virt.hv.base_feat_timer_freq_msrs) printf(" HV_BASE_FEAT_TIMER_FREQ_MSRS");
816 		if (info.virt.hv.base_feat_debug_msrs) printf(" HV_BASE_FEAT_DEBUG_MSRS");
817 		if (info.virt.hv.part_flags_create_part) printf(" HV_PART_FLAGS_CREATE_PART");
818 		if (info.virt.hv.part_flags_access_part_id) printf(" HV_PART_FLAGS_ACCESS_PART_ID");
819 		if (info.virt.hv.part_flags_access_memory_pool) printf(" HV_PART_FLAGS_ACCESS_MEMORY_POOL");
820 		if (info.virt.hv.part_flags_adjust_msg_buffers) printf(" HV_PART_FLAGS_ADJUST_MSG_BUFFERS");
821 		if (info.virt.hv.part_flags_post_msgs) printf(" HV_PART_FLAGS_POST_MSGS");
822 		if (info.virt.hv.part_flags_signal_events) printf(" HV_PART_FLAGS_SIGNAL_EVENTS");
823 		if (info.virt.hv.part_flags_create_port) printf(" HV_PART_FLAGS_CREATE_PORT");
824 		if (info.virt.hv.part_flags_connect_port) printf(" HV_PART_FLAGS_CONNECT_PORT");
825 		if (info.virt.hv.part_flags_access_stats) printf(" HV_PART_FLAGS_ACCESS_STATS");
826 		if (info.virt.hv.part_flags_debugging) printf(" HV_PART_FLAGS_DEBUGGING");
827 		if (info.virt.hv.part_flags_cpu_mgmt) printf(" HV_PART_FLAGS_CPU_MGMT");
828 		if (info.virt.hv.part_flags_cpu_profiler) printf(" HV_PART_FLAGS_CPU_PROFILER");
829 		if (info.virt.hv.part_flags_expanded_stack_walk) printf(" HV_PART_FLAGS_EXPANDED_STACK_WALK");
830 		if (info.virt.hv.part_flags_access_vsm) printf(" HV_PART_FLAGS_ACCESS_VSM");
831 		if (info.virt.hv.part_flags_access_vp_regs) printf(" HV_PART_FLAGS_ACCESS_VP_REGS");
832 		if (info.virt.hv.part_flags_extended_hypercalls) printf(" HV_PART_FLAGS_EXTENDED_HYPERCALLS");
833 		if (info.virt.hv.part_flags_start_vp) printf(" HV_PART_FLAGS_START_VP");
834 		if (info.virt.hv.pm_max_cpu_power_state_c0) printf(" HV_PM_MAX_CPU_POWER_STATE_C0");
835 		if (info.virt.hv.pm_max_cpu_power_state_c1) printf(" HV_PM_MAX_CPU_POWER_STATE_C1");
836 		if (info.virt.hv.pm_max_cpu_power_state_c2) printf(" HV_PM_MAX_CPU_POWER_STATE_C2");
837 		if (info.virt.hv.pm_max_cpu_power_state_c3) printf(" HV_PM_MAX_CPU_POWER_STATE_C3");
838 		if (info.virt.hv.pm_hpet_reqd_for_c3) printf(" HV_PM_HPET_REQD_FOR_C3");
839 		if (info.virt.hv.misc_feat_mwait) printf(" HV_MISC_FEAT_MWAIT");
840 		if (info.virt.hv.misc_feat_guest_debugging) printf(" HV_MISC_FEAT_GUEST_DEBUGGING");
841 		if (info.virt.hv.misc_feat_perf_mon) printf(" HV_MISC_FEAT_PERF_MON");
842 		if (info.virt.hv.misc_feat_pcpu_dyn_part_event) printf(" HV_MISC_FEAT_PCPU_DYN_PART_EVENT");
843 		if (info.virt.hv.misc_feat_xmm_hypercall_input) printf(" HV_MISC_FEAT_XMM_HYPERCALL_INPUT");
844 		if (info.virt.hv.misc_feat_guest_idle_state) printf(" HV_MISC_FEAT_GUEST_IDLE_STATE");
845 		if (info.virt.hv.misc_feat_hypervisor_sleep_state) printf(" HV_MISC_FEAT_HYPERVISOR_SLEEP_STATE");
846 		if (info.virt.hv.misc_feat_query_numa_distance) printf(" HV_MISC_FEAT_QUERY_NUMA_DISTANCE");
847 		if (info.virt.hv.misc_feat_timer_freq) printf(" HV_MISC_FEAT_TIMER_FREQ");
848 		if (info.virt.hv.misc_feat_inject_synmc_xcpt) printf(" HV_MISC_FEAT_INJECT_SYNMC_XCPT");
849 		if (info.virt.hv.misc_feat_guest_crash_msrs) printf(" HV_MISC_FEAT_GUEST_CRASH_MSRS");
850 		if (info.virt.hv.misc_feat_debug_msrs) printf(" HV_MISC_FEAT_DEBUG_MSRS");
851 		if (info.virt.hv.misc_feat_npiep1) printf(" HV_MISC_FEAT_NPIEP1");
852 		if (info.virt.hv.misc_feat_disable_hypervisor) printf(" HV_MISC_FEAT_DISABLE_HYPERVISOR");
853 		if (info.virt.hv.misc_feat_ext_gva_range_for_flush_va_list) printf(" HV_MISC_FEAT_EXT_GVA_RANGE_FOR_FLUSH_VA_LIST");
854 		if (info.virt.hv.misc_feat_hypercall_output_xmm) printf(" HV_MISC_FEAT_HYPERCALL_OUTPUT_XMM");
855 		if (info.virt.hv.misc_feat_sint_polling_mode) printf(" HV_MISC_FEAT_SINT_POLLING_MODE");
856 		if (info.virt.hv.misc_feat_hypercall_msr_lock) printf(" HV_MISC_FEAT_HYPERCALL_MSR_LOCK");
857 		if (info.virt.hv.misc_feat_use_direct_synth_msrs) printf(" HV_MISC_FEAT_USE_DIRECT_SYNTH_MSRS");
858 		if (info.virt.hv.hint_hypercall_for_process_switch) printf(" HV_HINT_HYPERCALL_FOR_PROCESS_SWITCH");
859 		if (info.virt.hv.hint_hypercall_for_tlb_flush) printf(" HV_HINT_HYPERCALL_FOR_TLB_FLUSH");
860 		if (info.virt.hv.hint_hypercall_for_tlb_shootdown) printf(" HV_HINT_HYPERCALL_FOR_TLB_SHOOTDOWN");
861 		if (info.virt.hv.hint_msr_for_apic_access) printf(" HV_HINT_MSR_FOR_APIC_ACCESS");
862 		if (info.virt.hv.hint_msr_for_sys_reset) printf(" HV_HINT_MSR_FOR_SYS_RESET");
863 		if (info.virt.hv.hint_relax_time_checks) printf(" HV_HINT_RELAX_TIME_CHECKS");
864 		if (info.virt.hv.hint_dma_remapping) printf(" HV_HINT_DMA_REMAPPING");
865 		if (info.virt.hv.hint_interrupt_remapping) printf(" HV_HINT_INTERRUPT_REMAPPING");
866 		if (info.virt.hv.hint_x2apic_msrs) printf(" HV_HINT_X2APIC_MSRS");
867 		if (info.virt.hv.hint_deprecate_auto_eoi) printf(" HV_HINT_DEPRECATE_AUTO_EOI");
868 		if (info.virt.hv.hint_synth_cluster_ipi_hypercall) printf(" HV_HINT_SYNTH_CLUSTER_IPI_HYPERCALL");
869 		if (info.virt.hv.hint_ex_proc_masks_interface) printf(" HV_HINT_EX_PROC_MASKS_INTERFACE");
870 		if (info.virt.hv.hint_nested_hyperv) printf(" HV_HINT_NESTED_HYPERV");
871 		if (info.virt.hv.hint_int_for_mbec_syscalls) printf(" HV_HINT_INT_FOR_MBEC_SYSCALLS");
872 		if (info.virt.hv.hint_nested_enlightened_vmcs_interface) printf(" HV_HINT_NESTED_ENLIGHTENED_VMCS_INTERFACE");
873 		if (info.virt.hv.host_feat_avic) printf(" HV_HOST_FEAT_AVIC");
874 		if (info.virt.hv.host_feat_msr_bitmap) printf(" HV_HOST_FEAT_MSR_BITMAP");
875 		if (info.virt.hv.host_feat_perf_counter) printf(" HV_HOST_FEAT_PERF_COUNTER");
876 		if (info.virt.hv.host_feat_nested_paging) printf(" HV_HOST_FEAT_NESTED_PAGING");
877 		if (info.virt.hv.host_feat_dma_remapping) printf(" HV_HOST_FEAT_DMA_REMAPPING");
878 		if (info.virt.hv.host_feat_interrupt_remapping) printf(" HV_HOST_FEAT_INTERRUPT_REMAPPING");
879 		if (info.virt.hv.host_feat_mem_patrol_scrubber) printf(" HV_HOST_FEAT_MEM_PATROL_SCRUBBER");
880 		if (info.virt.hv.host_feat_dma_prot_in_use) printf(" HV_HOST_FEAT_DMA_PROT_IN_USE");
881 		if (info.virt.hv.host_feat_hpet_requested) printf(" HV_HOST_FEAT_HPET_REQUESTED");
882 		if (info.virt.hv.host_feat_stimer_volatile) printf(" HV_HOST_FEAT_STIMER_VOLATILE");
883 		break;
884 	case VirtVendor.KVM:
885 		if (info.virt.kvm.feature_clocksource) printf(" KVM_FEATURE_CLOCKSOURCE");
886 		if (info.virt.kvm.feature_nop_io_delay) printf(" KVM_FEATURE_NOP_IO_DELAY");
887 		if (info.virt.kvm.feature_mmu_op) printf(" KVM_FEATURE_MMU_OP");
888 		if (info.virt.kvm.feature_clocksource2) printf(" KVM_FEATURE_CLOCKSOURCE2");
889 		if (info.virt.kvm.feature_async_pf) printf(" KVM_FEATURE_ASYNC_PF");
890 		if (info.virt.kvm.feature_steal_time) printf(" KVM_FEATURE_STEAL_TIME");
891 		if (info.virt.kvm.feature_pv_eoi) printf(" KVM_FEATURE_PV_EOI");
892 		if (info.virt.kvm.feature_pv_unhault) printf(" KVM_FEATURE_PV_UNHAULT");
893 		if (info.virt.kvm.feature_pv_tlb_flush) printf(" KVM_FEATURE_PV_TLB_FLUSH");
894 		if (info.virt.kvm.feature_async_pf_vmexit) printf(" KVM_FEATURE_ASYNC_PF_VMEXIT");
895 		if (info.virt.kvm.feature_pv_send_ipi) printf(" KVM_FEATURE_PV_SEND_IPI");
896 		if (info.virt.kvm.feature_pv_poll_control) printf(" KVM_FEATURE_PV_POLL_CONTROL");
897 		if (info.virt.kvm.feature_pv_sched_yield) printf(" KVM_FEATURE_PV_SCHED_YIELD");
898 		if (info.virt.kvm.feature_clocsource_stable_bit) printf(" KVM_FEATURE_CLOCSOURCE_STABLE_BIT");
899 		if (info.virt.kvm.hint_realtime) printf(" KVM_HINTS_REALTIME");
900 		break;
901 	default:
902 	}
903 	
904 	printf("\nMemory      :");
905 	
906 	if (info.memory.pae) printf(" PAE");
907 	if (info.memory.pse) printf(" PSE");
908 	if (info.memory.pse36) printf(" PSE-36");
909 	if (info.memory.page1gb) printf(" Page1GB");
910 	if (info.memory.nx)
911 		switch (info.vendorId) {
912 		case Vendor.Intel: printf(" Intel-XD/NX"); break;
913 		case Vendor.AMD: printf(" AMD-EVP/NX"); break;
914 		default: printf(" NX");
915 		}
916 	if (info.memory.dca) printf(" DCA");
917 	if (info.memory.pat) printf(" PAT");
918 	if (info.memory.mtrr) printf(" MTRR");
919 	if (info.memory.pge) printf(" PGE");
920 	if (info.memory.smep) printf(" SMEP");
921 	if (info.memory.smap) printf(" SMAP");
922 	if (info.memory.pku) printf(" PKU");
923 	if (info.memory._5pl) printf(" 5PL");
924 	if (info.memory.fsrepmov) printf(" FSRM");
925 	if (info.memory.lam) printf(" LAM");
926 	
927 	with (info.memory)
928 	printf("\nPhysicalBits: %u\nLinearBits  : %u\nDebugging   :",
929 		physBits, lineBits);
930 	
931 	if (info.debugging.mca) printf(" MCA");
932 	if (info.debugging.mce) printf(" MCE");
933 	if (info.debugging.de) printf(" DE");
934 	if (info.debugging.ds) printf(" DS");
935 	if (info.debugging.dsCpl) printf(" DS-CPL");
936 	if (info.debugging.dtes64) printf(" DTES64");
937 	if (info.debugging.pdcm) printf(" PDCM");
938 	if (info.debugging.sdbg) printf(" SDBG");
939 	if (info.debugging.pbe) printf(" PBE");
940 	
941 	printf("\nSecurity    :");
942 	if (info.security.ia32_arch_capabilities) printf(" IA32_ARCH_CAPABILITIES");
943 	printSecurity(info);
944 	
945 	with (info)
946 	printf(
947 	"\nMax. Leaf   : 0x%x\n"~
948 	"Max. V-Leaf : 0x%x\n"~
949 	"Max. E-Leaf : 0x%x\n"~
950 	"Type        : %s\n"~
951 	"Brand Index : %u\n"~
952 	"Misc.       :",
953 		maxLeaf, maxLeafVirt, maxLeafExtended, typeString, brandIndex);
954 	
955 	if (info.misc.xtpr) printf(" xTPR");
956 	if (info.misc.psn) printf(" PSN");
957 	if (info.misc.pcid) printf(" PCID");
958 	if (info.misc.fsgsbase) printf(" FSGSBASE");
959 	if (info.misc.uintr) printf(" UINTR");
960 	
961 	putchar('\n');
962 	
963 	return 0;
964 }