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