1 /**
2  * x86 CPU Identification tool
3  *
4  * This was initially used internally, so it's pretty unfriendly.
5  *
6  * The best way to use this module would be:
7  * ---
8  * CPUINFO info;     // Important to let the struct init to zero!
9  * getLeaves(info);  // Get maximum CPUID leaves (mandatory step before info)
10  * getInfo(info);    // Fill CPUINFO structure (optional)
11  * ---
12  *
13  * Then checking the corresponding field:
14  * ---
15  * if (info.extensions.amx.xfd) {
16  *   // Intel AMX with AMX_XFD is available
17  * } else {
18  *   // Feature unavailable
19  * }
20  * ---
21  *
22  * See the CPUINFO structure for available fields.
23  *
24  * To further understand these fields, it's encouraged to consult the technical manual.
25  *
26  * Authors: dd86k (dd@dax.moe)
27  * Copyright: © 2016-2022 dd86k
28  * License: MIT
29  */
30 module ddcpuid;
31 
32 // NOTE: GAS syntax reminder
33 //       asm { "asm;\n\t" : "constraint" output : "constraint" input : clobbers }
34 
35 @system:
36 extern (C):
37 
38 //TODO: Consider ddcpuid_* function prefix since we extern to C
39 
40 version (X86)
41 	enum DDCPUID_PLATFORM = "i686"; /// Target platform
42 else version (X86_64)
43 	enum DDCPUID_PLATFORM = "amd64"; /// Target platform
44 else static assert(0, "Unsupported platform");
45 
46 version (DigitalMars) {
47 	version = DMD;	// DMD compiler
48 	version = DMDLDC;	// DMD or LDC compilers
49 } else version (GNU) {
50 	version = GDC;	// GDC compiler
51 } else version (LDC) {
52 	version = DMDLDC;	// DMD or LDC compilers
53 } else static assert(0, "Unsupported compiler");
54 
55 enum DDCPUID_VERSION   = "0.20.0";	/// Library version
56 private enum CACHE_LEVELS = 6;	/// For buffer
57 private enum CACHE_MAX_LEVEL = CACHE_LEVELS - 1;
58 
59 version (PrintInfo) {
60 	pragma(msg, "CPUINFO.sizeof\t", CPUINFO.sizeof);
61 	pragma(msg, "CACHE.sizeof\t", CACHEINFO.sizeof);
62 }
63 
64 /// Make a bit mask of one bit at n position
65 private
66 template BIT(int n) if (n <= 31) { enum uint BIT = 1 << n; }
67 
68 /// Vendor ID template
69 // Little-endian only, unless x86 gets any crazier
70 private
71 template ID(char[4] c) {
72 	enum uint ID = c[0] | c[1] << 8 | c[2] << 16 | c[3] << 24;
73 }
74 
75 /// Vendor ID.
76 ///
77 /// The CPUINFO.vendor_id field is set according to the Vendor String.
78 /// They are validated in the getVendor function, so they are safe to use.
79 enum Vendor {
80 	Other = 0,
81 	Intel = ID!"Genu",	/// `"GenuineIntel"`: Intel
82 	AMD   = ID!"Auth",	/// `"AuthenticAMD"`: AMD
83 	VIA   = ID!"VIA ",	/// `"VIA VIA VIA "`: VIA
84 }
85 
86 /// Virtual Vendor ID, used as the interface type.
87 ///
88 /// The CPUINFO.virt.vendor_id field is set according to the Vendor String.
89 /// They are validated in the getVendor function, so they are safe to use.
90 /// The VBoxHyperV ID will be adjusted for HyperV since it's the same interface,
91 /// but simply a different implementation.
92 // NOTE: bhyve doesn't not emit cpuid bits within 0x40000000, so not supported
93 enum VirtVendor {
94 	Other = 0,
95 	KVM        = ID!"KVMK",	/// `"KVMKVMKVM\0\0\0"`: KVM
96 	HyperV     = ID!"Micr",	/// `"Microsoft Hv"`: Hyper-V interface
97 	VBoxHyperV = ID!"VBox",	/// `"VBoxVBoxVBox"`: VirtualBox's Hyper-V interface
98 	VBoxMin    = 0,	/// Unset: VirtualBox minimal interface
99 }
100 
101 /// Registers structure used with the ddcpuid function.
102 struct REGISTERS {
103 	union {
104 		uint eax;
105 		ushort ax;
106 		struct { ubyte al, ah; }
107 	}
108 	union {
109 		uint ebx;
110 		ushort bx;
111 		struct { ubyte bl, bh; }
112 	}
113 	union {
114 		uint ecx;
115 		ushort cx;
116 		struct { ubyte cl, ch; }
117 	}
118 	union {
119 		uint edx;
120 		ushort dx;
121 		struct { ubyte dl, dh; }
122 	}
123 }
124 ///
125 @system unittest {
126 	REGISTERS regs = void;
127 	regs.eax = 0xaabbccdd;
128 	assert(regs.eax == 0xaabbccdd);
129 	assert(regs.ax  == 0xccdd);
130 	assert(regs.al  == 0xdd);
131 	assert(regs.ah  == 0xcc);
132 }
133 
134 /// CPU cache entry
135 struct CACHEINFO { align(1):
136 	this(ubyte level_, char type_, uint kbsize_, ushort shared_,
137 		ushort ways_, ushort parts_, ushort lineSize_, uint sets_) {
138 		level = level_;
139 		type = type_;
140 		size = kbsize_;
141 		sharedCores = shared_;
142 		ways = ways_;
143 		partitions = parts_;
144 		lineSize = lineSize_;
145 		sets = sets_;
146 		features = 0;
147 	}
148 	//TODO: Sort fields (totalSize, coresShared, ways, partitions, lineSize, sets)
149 	ushort lineSize;	/// Size of the line in bytes.
150 	union {
151 		ushort partitions;	/// Number of partitions.
152 		ushort lines;	/// Legacy name of partitions.
153 	}
154 	ushort ways;	/// Number of ways per line.
155 	uint sets; /// Number of cache sets. (Entries)
156 	/// Cache size in kilobytes.
157 	// (Ways + 1) * (Partitions + 1) * (LineSize + 1) * (Sets + 1)
158 	// (EBX[31:22] + 1) * (EBX[21:12] + 1) * (EBX[11:0] + 1) * (ECX + 1)
159 	uint size;
160 	/// Number of CPU cores sharing this cache.
161 	ushort sharedCores;
162 	/// Cache feature, bit flags.
163 	/// - Bit 0: Self Initializing cache
164 	/// - Bit 1: Fully Associative cache
165 	/// - Bit 2: No Write-Back Invalidation (toggle)
166 	/// - Bit 3:  Cache Inclusiveness (toggle)
167 	/// - Bit 4: Complex Cache Indexing (toggle)
168 	ushort features;
169 	ubyte level;	/// Cache level: L1, L2, etc.
170 	char type = 0;	/// Type entry character: 'D'=Data, 'I'=Instructions, 'U'=Unified
171 }
172 
173 struct VendorString { align(1):
174 	union {
175 		struct { uint ebx, edx, ecx; }
176 		char[12] string_;
177 	}
178 	Vendor id;	/// Validated vendor ID
179 }
180 
181 @system unittest {
182 	VendorString s;
183 	s.string_ = "AuthenticAMD";
184 	assert(s.ebx == ID!"Auth");
185 	assert(s.edx == ID!"enti");
186 	assert(s.ecx == ID!"cAMD");
187 }
188 
189 struct VirtVendorString { align(1):
190 	union {
191 		struct { uint ebx, ecx, edx; }
192 		char[12] string_;
193 	}
194 	VirtVendor id;	/// Validated vendor ID
195 }
196 
197 @system unittest {
198 	VirtVendorString s;
199 	s.string_ = "AuthenticAMD";
200 	assert(s.ebx == ID!"Auth");
201 	assert(s.ecx == ID!"enti");
202 	assert(s.edx == ID!"cAMD");
203 }
204 
205 /// CPU information structure
206 struct CPUINFO { align(1):
207 	uint maxLeaf;	/// Highest cpuid leaf
208 	uint maxLeafVirt;	/// Highest cpuid virtualization leaf
209 	uint maxLeafExtended;	/// Highest cpuid extended leaf
210 	
211 	// Vendor/brand strings
212 	
213 	VendorString vendor;
214 	
215 	union {
216 		private uint[12] brand32;	// For init only
217 		char[48] brandString;	/// Processor Brand String
218 	}
219 	ubyte brandIndex;	/// Brand string index
220 	
221 	// Core
222 	
223 	/// Contains the information on the number of cores.
224 	struct Cores {
225 		ushort logical;	/// Logical cores in this processor
226 		ushort physical;	/// Physical cores in this processor
227 	}
228 	align(2) Cores cores;	/// Processor package cores
229 	
230 	// Identifier
231 	
232 	uint identifier;	/// Raw identifier (CPUID.01h.EAX)
233 	ushort family;	/// Effective family identifier
234 	ushort model;	/// Effective model identifier
235 //	const(char) *microArchitecture;	/// Microarchitecture name string
236 	ubyte familyBase;	/// Base family identifier
237 	ubyte familyExtended;	/// Extended family identifier
238 	ubyte modelBase;	/// Base model identifier
239 	ubyte modelExtended;	/// Extended model identifier
240 	ubyte stepping;	/// Stepping revision
241 	ubyte type;	/// Processor type number
242 	const(char) *typeString;	/// Processor type string.
243 	
244 	/// Contains processor extensions.
245 	/// Extensions contain a variety of instructions to aid particular
246 	/// tasks.
247 	struct Extensions {
248 		bool fpu;	/// On-Chip x87 FPU
249 		bool f16c;	/// Float16 Conversions
250 		bool mmx;	/// MMX
251 		bool mmxExtended;	/// MMX Extended
252 		bool _3DNow;	/// 3DNow!
253 		bool _3DNowExtended;	/// 3DNow! Extended
254 		bool aes_ni;	/// Advanced Encryption Standard New Instructions
255 		bool sha;	/// SHA-1
256 		bool fma3;	/// Fused Multiply-Add
257 		bool fma4;	/// FMA4
258 		bool bmi1;	/// BMI1
259 		bool bmi2;	/// BMI2
260 		bool x86_64;	/// 64-bit mode (Long mode)
261 		bool lahf64;	/// LAHF+SAHF in 64-bit mode
262 		bool waitpkg;	/// User Level Monitor Wait (UMWAIT)
263 		bool xop;	/// AMD eXtended OPerations
264 		bool tbm;	/// Trailing Bit Manipulation
265 		bool adx;	/// Multi-precision Add-Carry (ADCX+ADOX)
266 	}
267 	align(2) Extensions extensions;	/// Extensions
268 		
269 	struct SSE {
270 		bool sse;	/// Streaming SIMD Extensions
271 		bool sse2;	/// SSE2
272 		bool sse3;	/// SSE3
273 		bool ssse3;	/// SSSE3
274 		bool sse41;	/// SSE4.1
275 		bool sse42;	/// SSE4.2
276 		bool sse4a;	/// SSE4a
277 	}
278 	align(2) SSE sse;	/// Streaming SIMD Extensions
279 	
280 	struct AVX {
281 		bool avx;	/// Advanced Vector eXtension
282 		bool avx2;	/// AVX2
283 		bool avx512f;	/// AVX512
284 		bool avx512er;	/// AVX512_ER
285 		bool avx512pf;	/// AVX512_PF
286 		bool avx512cd;	/// AVX512_CD
287 		bool avx512dq;	/// AVX512_DQ
288 		bool avx512bw;	/// AVX512_BW
289 		bool avx512vl;	/// AVX512_VL
290 		bool avx512_ifma;	/// AVX512_IFMA
291 		bool avx512_vbmi;	/// AVX512_VBMI
292 		bool avx512_vbmi2;	/// AVX512_VBMI2
293 		bool avx512_gfni;	/// AVX512_GFNI
294 		bool avx512_vaes;	/// AVX512_VAES
295 		bool avx512_vnni;	/// AVX512_VNNI
296 		bool avx512_bitalg;	/// AVX512_BITALG
297 		bool avx512_vpopcntdq;	/// AVX512_VPOPCNTDQ
298 		bool avx512_4vnniw;	/// AVX512_4VNNIW
299 		bool avx512_4fmaps;	/// AVX512_4FMAPS
300 		bool avx512_bf16;	/// AVX512_BF16
301 		bool avx512_vp2intersect;	/// AVX512_VP2INTERSECT
302 	}
303 	align(2) AVX avx;	/// Advanced Vector eXtension
304 	
305 	struct AMX {
306 		bool enabled;	/// Advanced Matrix eXtension
307 		bool bf16;	/// AMX_BF16
308 		bool int8;	/// AMX_INT8
309 		bool xtilecfg;	/// AMX_XTILECFG
310 		bool xtiledata;	/// AMX_XTILEDATA
311 		bool xfd;	/// AMX_XFD
312 	}
313 	align(2) AMX amx;	/// Intel AMX
314 	
315 	struct SGX {
316 		bool supported;	/// If SGX is supported (and enabled)
317 		bool sgx1;	/// SGX1
318 		bool sgx2;	/// SGX2
319 		ubyte maxSize;	/// 2^n maximum enclave size in non-64-bit
320 		ubyte maxSize64;	/// 2^n maximum enclave size in 64-bit
321 	}
322 	align(2) SGX sgx;	/// Intel SGX
323 	
324 	/// Additional instructions. Often not part of extensions.
325 	struct Extras {
326 		bool pclmulqdq;	/// PCLMULQDQ instruction
327 		bool monitor;	/// MONITOR and MWAIT instructions
328 		ushort mwaitMin;	/// (With MONITOR+MWAIT) MWAIT minimum size in bytes
329 		ushort mwaitMax;	/// (With MONITOR+MWAIT) MWAIT maximum size in bytes
330 		bool cmpxchg8b;	/// CMPXCHG8B
331 		bool cmpxchg16b;	/// CMPXCHG16B instruction
332 		bool movbe;	/// MOVBE instruction
333 		bool rdrand;	/// RDRAND instruction
334 		bool rdseed;	/// RDSEED instruction
335 		bool rdmsr;	/// RDMSR instruction
336 		bool sysenter;	/// SYSENTER and SYSEXIT instructions
337 		bool rdtsc;	/// RDTSC instruction
338 		bool rdtscDeadline;	/// (With RDTSC) IA32_TSC_DEADLINE MSR
339 		bool rdtscInvariant;	/// (With RDTSC) Timestamp counter invariant of C/P/T-state
340 		bool rdtscp;	/// RDTSCP instruction
341 		bool rdpid;	/// RDPID instruction
342 		bool cmov;	/// CMOVcc instruction
343 		bool lzcnt;	/// LZCNT instruction
344 		bool popcnt;	/// POPCNT instruction
345 		bool xsave;	/// XSAVE and XRSTOR instructions
346 		bool osxsave;	/// OSXSAVE and XGETBV instructions
347 		bool fxsr;	/// FXSAVE and FXRSTOR instructions
348 		bool pconfig;	/// PCONFIG instruction
349 		bool cldemote;	/// CLDEMOTE instruction
350 		bool movdiri;	/// MOVDIRI instruction
351 		bool movdir64b;	/// MOVDIR64B instruction
352 		bool enqcmd;	/// ENQCMD instruction
353 		bool syscall;	/// SYSCALL and SYSRET instructions
354 		bool monitorx;	/// MONITORX and MWAITX instructions
355 		bool skinit;	/// SKINIT instruction
356 		bool serialize;	/// SERIALIZE instruction
357 	}
358 	align(2) Extras extras;	/// Additional instructions
359 	
360 	/// Processor technologies.
361 	struct Technologies {
362 		bool eist;	/// Intel SpeedStep/AMD PowerNow/AMD Cool'n'Quiet
363 		bool turboboost;	/// Intel TurboBoost/AMD CorePerformanceBoost
364 		bool turboboost30;	/// Intel TurboBoost 3.0
365 		bool smx;	/// Intel TXT
366 		bool htt;	/// (HTT) HyperThreading Technology
367 	}
368 	align(2) Technologies tech;	/// Processor technologies
369 	
370 	/// Cache information.
371 	struct CacheInfo {
372 		uint levels;
373 		CACHEINFO[CACHE_LEVELS] level;
374 		bool clflush;	/// CLFLUSH instruction
375 		ubyte clflushLinesize;	/// Linesize of CLFLUSH in bytes
376 		bool clflushopt;	/// CLFLUSH instruction
377 		bool cnxtId;	/// L1 Context ID
378 		bool ss;	/// SelfSnoop
379 		bool prefetchw;	/// PREFETCHW instruction
380 		bool invpcid;	/// INVPCID instruction
381 		bool wbnoinvd;	/// WBNOINVD instruction
382 	}
383 	align(2) CacheInfo cache;	/// Cache information
384 	
385 	/// ACPI information.
386 	struct SysInfo {
387 		bool available;	/// ACPI
388 		bool apic;	/// APIC
389 		bool x2apic;	/// x2APIC
390 		bool arat;	/// Always-Running-APIC-Timer
391 		bool tm;	/// Thermal Monitor
392 		bool tm2;	/// Thermal Monitor 2
393 		ubyte maxApicId;	/// Maximum APIC ID
394 		ubyte apicId;	/// Initial APIC ID (running core where CPUID was called)
395 	}
396 	align(2) SysInfo sys;	/// System features
397 	
398 	/// Virtualization features. If a paravirtual interface is available,
399 	/// its information will be found here.
400 	struct Virtualization {
401 		bool available;	/// Intel VT-x/AMD-V
402 		ubyte version_;	/// (AMD) Virtualization platform version
403 		bool vme;	/// Enhanced vm8086
404 		bool apicv;	/// (AMD) APICv. Intel's is available via a MSR.
405 		VirtVendorString vendor;
406 		
407 		struct VBox {
408 			uint tsc_freq_khz;	/// (VBox) Timestamp counter frequency in KHz
409 			uint apic_freq_khz;	/// (VBox) Paravirtualization API KHz frequency
410 		}
411 		VBox vbox;
412 		
413 		struct KVM {
414 			bool feature_clocksource;	/// (KVM) kvmclock interface
415 			bool feature_nop_io_delay;	/// (KVM) No delays required on I/O operations
416 			bool feature_mmu_op;	/// (KVM) Deprecated
417 			bool feature_clocksource2;	/// (KVM) Remapped kvmclock interface
418 			bool feature_async_pf;	/// (KVM) Asynchronous Page Fault
419 			bool feature_steal_time;	/// (KVM) Steal time
420 			bool feature_pv_eoi;	/// (KVM) Paravirtualized End Of the Interrupt handler
421 			bool feature_pv_unhault;	/// (KVM) Paravirtualized spinlock
422 			bool feature_pv_tlb_flush;	/// (KVM) Paravirtualized TLB flush
423 			bool feature_async_pf_vmexit;	/// (KVM) Asynchronous Page Fault at VM exit
424 			bool feature_pv_send_ipi;	/// (KVM) Paravirtualized SEBD inter-processor-interrupt
425 			bool feature_pv_poll_control;	/// (KVM) Host-side polling on HLT
426 			bool feature_pv_sched_yield;	/// (KVM) paravirtualized scheduler yield
427 			bool feature_clocsource_stable_bit;	/// (KVM) kvmclock warning
428 			bool hint_realtime;	/// (KVM) vCPUs are never preempted for an unlimited amount of time
429 		}
430 		KVM kvm;
431 		
432 		struct HyperV {
433 			ushort guest_vendor_id;	/// (Hyper-V) Paravirtualization Guest Vendor ID
434 			ushort guest_build;	/// (Hyper-V) Paravirtualization Guest Build number
435 			ubyte guest_os;	/// (Hyper-V) Paravirtualization Guest OS ID
436 			ubyte guest_major;	/// (Hyper-V) Paravirtualization Guest OS Major version
437 			ubyte guest_minor;	/// (Hyper-V) Paravirtualization Guest OS Minor version
438 			ubyte guest_service;	/// (Hyper-V) Paravirtualization Guest Service ID
439 			bool guest_opensource;	/// (Hyper-V) Paravirtualization Guest additions open-source
440 			bool base_feat_vp_runtime_msr;	/// (Hyper-V) Virtual processor runtime MSR
441 			bool base_feat_part_time_ref_count_msr;	/// (Hyper-V) Partition reference counter MSR
442 			bool base_feat_basic_synic_msrs;	/// (Hyper-V) Basic Synthetic Interrupt Controller MSRs
443 			bool base_feat_stimer_msrs;	/// (Hyper-V) Synthetic Timer MSRs
444 			bool base_feat_apic_access_msrs;	/// (Hyper-V) APIC access MSRs (EOI, ICR, TPR)
445 			bool base_feat_hypercall_msrs;	/// (Hyper-V) Hypercalls API MSRs
446 			bool base_feat_vp_id_msr;	/// (Hyper-V) vCPU index MSR
447 			bool base_feat_virt_sys_reset_msr;	/// (Hyper-V) Virtual system reset MSR
448 			bool base_feat_stat_pages_msr;	/// (Hyper-V) Statistic pages MSRs
449 			bool base_feat_part_ref_tsc_msr;	/// (Hyper-V) Partition reference timestamp counter MSR
450 			bool base_feat_guest_idle_state_msr;	/// (Hyper-V) Virtual guest idle state MSR
451 			bool base_feat_timer_freq_msrs;	/// (Hyper-V) Timer frequency MSRs (TSC and APIC)
452 			bool base_feat_debug_msrs;	/// (Hyper-V) Debug MSRs
453 			bool part_flags_create_part;	/// (Hyper-V) Partitions can be created
454 			bool part_flags_access_part_id;	/// (Hyper-V) Partitions IDs can be accessed
455 			bool part_flags_access_memory_pool;	/// (Hyper-V) Memory pool can be accessed
456 			bool part_flags_adjust_msg_buffers;	/// (Hyper-V) Possible to adjust message buffers
457 			bool part_flags_post_msgs;	/// (Hyper-V) Possible to send messages
458 			bool part_flags_signal_events;	/// (Hyper-V) Possible to signal events
459 			bool part_flags_create_port;	/// (Hyper-V) Possible to create ports
460 			bool part_flags_connect_port;	/// (Hyper-V) Possible to connect to ports
461 			bool part_flags_access_stats;	/// (Hyper-V) Can access statistics
462 			bool part_flags_debugging;	/// (Hyper-V) Debugging features available
463 			bool part_flags_cpu_mgmt;	/// (Hyper-V) Processor management available
464 			bool part_flags_cpu_profiler;	/// (Hyper-V) Processor profiler available
465 			bool part_flags_expanded_stack_walk;	/// (Hyper-V) Extended stack walking available
466 			bool part_flags_access_vsm;	/// (Hyper-V) Virtual system monitor available
467 			bool part_flags_access_vp_regs;	/// (Hyper-V) Virtual private registers available
468 			bool part_flags_extended_hypercalls;	/// (Hyper-V) Extended hypercalls API available
469 			bool part_flags_start_vp;	/// (Hyper-V) Virtual processor has started
470 			bool pm_max_cpu_power_state_c0;	/// (Hyper-V) Processor C0 is maximum state
471 			bool pm_max_cpu_power_state_c1;	/// (Hyper-V) Processor C1 is maximum state
472 			bool pm_max_cpu_power_state_c2;	/// (Hyper-V) Processor C2 is maximum state
473 			bool pm_max_cpu_power_state_c3;	/// (Hyper-V) Processor C3 is maximum state
474 			bool pm_hpet_reqd_for_c3;	/// (Hyper-V) High-precision event timer required for C3 state
475 			bool misc_feat_mwait;	/// (Hyper-V) MWAIT instruction available for guest
476 			bool misc_feat_guest_debugging;	/// (Hyper-V) Guest supports debugging
477 			bool misc_feat_perf_mon;	/// (Hyper-V) Performance monitor support available
478 			bool misc_feat_pcpu_dyn_part_event;	/// (Hyper-V) Physicap CPU dynamic partitioning event available
479 			bool misc_feat_xmm_hypercall_input;	/// (Hyper-V) Hypercalls via XMM registers available
480 			bool misc_feat_guest_idle_state;	/// (Hyper-V) Virtual guest supports idle state
481 			bool misc_feat_hypervisor_sleep_state;	/// (Hyper-V) Hypervisor supports sleep
482 			bool misc_feat_query_numa_distance;	/// (Hyper-V) NUMA distance query available
483 			bool misc_feat_timer_freq;	/// (Hyper-V) Determining timer frequencies available
484 			bool misc_feat_inject_synmc_xcpt;	/// (Hyper-V) Support for injecting synthetic machine checks
485 			bool misc_feat_guest_crash_msrs;	/// (Hyper-V) Guest crash MSR available
486 			bool misc_feat_debug_msrs;	/// (Hyper-V) Debug MSR available
487 			bool misc_feat_npiep1;	/// (Hyper-V) Documentation unavailable
488 			bool misc_feat_disable_hypervisor;	/// (Hyper-V) Hypervisor can be disabled
489 			bool misc_feat_ext_gva_range_for_flush_va_list;	/// (Hyper-V) Extended guest virtual address (GVA) ranges for FlushVirtualAddressList available
490 			bool misc_feat_hypercall_output_xmm;	/// (Hyper-V) Returning hypercall output via XMM registers available
491 			bool misc_feat_sint_polling_mode;	/// (Hyper-V) Synthetic interrupt source polling mode available
492 			bool misc_feat_hypercall_msr_lock;	/// (Hyper-V) Hypercall MISR lock feature available
493 			bool misc_feat_use_direct_synth_msrs;	/// (Hyper-V) Possible to directly use synthetic MSRs
494 			bool hint_hypercall_for_process_switch;	/// (Hyper-V) Guest should use the Hypercall API for address space switches rather than MOV CR3
495 			bool hint_hypercall_for_tlb_flush;	/// (Hyper-V) Guest should use the Hypercall API for local TLB flushes rather than INVLPG/MOV CR3
496 			bool hint_hypercall_for_tlb_shootdown;	/// (Hyper-V) Guest should use the Hypercall API for inter-CPU TLB flushes rather than inter-processor-interrupts (IPI)
497 			bool hint_msr_for_apic_access;	/// (Hyper-V) Guest should use the MSRs for APIC access (EOI, ICR, TPR) rather than memory-mapped input/output (MMIO)
498 			bool hint_msr_for_sys_reset;	/// (Hyper-V) Guest should use the hypervisor-provided MSR for a system reset instead of traditional methods
499 			bool hint_relax_time_checks;	/// (Hyper-V) Guest should relax timer-related checks (watchdogs/deadman timeouts) that rely on timely deliver of external interrupts
500 			bool hint_dma_remapping;	/// (Hyper-V) Guest should use the direct memory access (DMA) remapping
501 			bool hint_interrupt_remapping;	/// (Hyper-V) Guest should use the interrupt remapping
502 			bool hint_x2apic_msrs;	/// (Hyper-V) Guest should use the X2APIC MSRs rather than memory mapped input/output (MMIO)
503 			bool hint_deprecate_auto_eoi;	/// (Hyper-V) Guest should deprecate Auto EOI (End Of Interrupt) features
504 			bool hint_synth_cluster_ipi_hypercall;	/// (Hyper-V) Guest should use the SyntheticClusterIpi Hypercall
505 			bool hint_ex_proc_masks_interface;	/// (Hyper-V) Guest should use the newer ExProcessMasks interface over ProcessMasks
506 			bool hint_nested_hyperv;	/// (Hyper-V) Hyper-V instance is nested within a Hyper-V partition
507 			bool hint_int_for_mbec_syscalls;	/// (Hyper-V) Guest should use the INT instruction for Mode Based Execution Control (MBEC) system calls
508 			bool hint_nested_enlightened_vmcs_interface;	/// (Hyper-V) Guest should use enlightened Virtual Machine Control Structure (VMCS) interfaces and nested enlightenment
509 			bool host_feat_avic;	/// (Hyper-V) Hypervisor is using the Advanced Virtual Interrupt Controller (AVIC) overlay
510 			bool host_feat_msr_bitmap;	/// (Hyper-V) Hypervisor is using MSR bitmaps
511 			bool host_feat_perf_counter;	/// (Hyper-V) Hypervisor supports the architectural performance counter
512 			bool host_feat_nested_paging;	/// (Hyper-V) Hypervisor is using nested paging
513 			bool host_feat_dma_remapping;	/// (Hyper-V) Hypervisor is using direct memory access (DMA) remapping
514 			bool host_feat_interrupt_remapping;	/// (Hyper-V) Hypervisor is using interrupt remapping
515 			bool host_feat_mem_patrol_scrubber;	/// (Hyper-V) Hypervisor's memory patrol scrubber is present
516 			bool host_feat_dma_prot_in_use;	/// (Hyper-V) Hypervisor is using direct memory access (DMA) protection
517 			bool host_feat_hpet_requested;	/// (Hyper-V) Hypervisor requires a High Precision Event Timer (HPET)
518 			bool host_feat_stimer_volatile;	/// (Hyper-V) Hypervisor's synthetic timers are volatile
519 		}
520 		HyperV hv;
521 	}
522 	align(2) Virtualization virt;	/// Virtualization features
523 	
524 	/// Memory features.
525 	struct Memory {
526 		bool pae;	/// Physical Address Extension 
527 		bool pse;	/// Page Size Extension
528 		bool pse36;	/// 36-bit PSE
529 		bool page1gb;	/// 1GiB pages in 4-level paging and higher
530 		bool mtrr;	/// Memory Type Range Registers
531 		bool pat;	/// Page Attribute Table
532 		bool pge;	/// Page Global Bit
533 		bool dca;	/// Direct Cache Access
534 		bool nx;	/// Intel XD (No eXecute bit)
535 		union {
536 			uint tsx;	/// Intel TSX. If set, has one of HLE, RTM, or TSXLDTRK.
537 			struct {
538 				bool hle;	/// (TSX) Hardware Lock Elision
539 				bool rtm;	/// (TSX) Restricted Transactional Memory
540 				bool tsxldtrk;	/// (TSX) Suspend Load Address Tracking
541 			}
542 		}
543 		bool smep;	/// Supervisor Mode Execution Protection
544 		bool smap;	/// Supervisor Mode Access Protection
545 		bool pku;	/// Protection Key Units
546 		bool _5pl;	/// 5-level paging
547 		bool fsrepmov;	/// Fast Short REP MOVSB optimization
548 		bool lam;	/// Linear Address Masking
549 		ubyte physBits;	/// Memory physical bits
550 		ubyte lineBits;	/// Memory linear bits
551 	}
552 	align (2) Memory memory;	/// Memory features
553 	
554 	/// Debugging features.
555 	struct Debugging {
556 		bool mca;	/// Machine Check Architecture
557 		bool mce;	/// Machine Check Exception
558 		bool de;	/// Degging Extensions
559 		bool ds;	/// Debug Store
560 		bool ds_cpl;	/// Debug Store for Current Privilege Level
561 		bool dtes64;	/// 64-bit Debug Store area
562 		bool pdcm;	/// Perfmon And Debug Capability
563 		bool sdbg;	/// Silicon Debug
564 		bool pbe;	/// Pending Break Enable
565 	}
566 	align(2) Debugging debugging;	/// Debugging feature
567 	
568 	/// Security features and mitigations.
569 	struct Security {
570 		bool ia32_arch_capabilities;	/// IA32_ARCH_CAPABILITIES MSR
571 		// NOTE: IA32_CORE_CAPABILITIES is currently empty
572 		bool ibpb;	/// Indirect Branch Predictor Barrier
573 		bool ibrs;	/// Indirect Branch Restricted Speculation
574 		bool ibrsAlwaysOn;	/// IBRS always enabled
575 		bool ibrsPreferred;	/// IBRS preferred over software solution
576 		bool stibp;	/// Single Thread Indirect Branch Predictors
577 		bool stibpAlwaysOn;	/// STIBP always enabled
578 		bool ssbd;	/// Speculative Store Bypass Disable
579 		bool l1dFlush;	/// L1D Cache Flush
580 		bool md_clear;	/// MDS mitigation
581 		bool cetIbt;	/// (Control-flow Enforcement Technology) Indirect Branch Tracking 
582 		bool cetSs;	/// (Control-flow Enforcement Technology) Shadow Stack
583 	}
584 	align(2) Security security;	/// Security features
585 	
586 	/// Miscellaneous features.
587 	struct Miscellaneous {
588 		bool psn;	/// Processor Serial Number (Pentium III only)
589 		bool pcid;	/// PCID
590 		bool xtpr;	/// xTPR
591 		bool fsgsbase;	/// FS and GS register base
592 		bool uintr;	/// User Interrupts
593 	}
594 	align(2) Miscellaneous misc;	/// Miscellaneous features
595 }
596 
597 // EAX[4:0], 0-31, but there aren't that many
598 // So we limit it to 0-7
599 private enum CACHE_MASK = 7; // Max 31
600 private immutable const(char)* CACHE_TYPE = "?DIU????";
601 
602 private
603 immutable const(char)*[4] PROCESSOR_TYPE = [ "Original", "OverDrive", "Dual", "Reserved" ];
604 
605 version (Trace) {
606 	import core.stdc.stdio;
607 	import core.stdc.stdarg;
608 	
609 	private extern (C) int putchar(int);
610 	
611 	/// Trace application
612 	void trace(string func = __FUNCTION__)(const(char) *fmt, ...) {
613 		va_list va;
614 		va_start(va, fmt);
615 		printf("TRACE:%s: ", func.ptr);
616 		vprintf(fmt, va);
617 		putchar('\n');
618 	}
619 }
620 
621 /// Test if a bit is set.
622 /// Params:
623 /// 	val = 32-bit content.
624 /// 	pos = Bit position 
625 /// Returns: True if bit set.
626 pragma(inline, true)
627 private bool bit(uint val, int pos) pure @safe {
628 	return (val & (1 << pos)) != 0;
629 }
630 
631 @safe unittest {
632 	assert(bit(2, 1)); // bit 1 of 2 is set (2[1], so 0b11[1])
633 }
634 
635 /// Query processor with CPUID.
636 /// Params:
637 ///   regs = REGISTERS structure
638 ///   level = Leaf (EAX)
639 ///   sublevel = Sub-leaf (ECX)
640 pragma(inline, false)
641 void ddcpuid_id(ref REGISTERS regs, uint level, uint sublevel = 0) {
642 	version (DMD) {
643 		version (X86) asm {
644 			mov EDI, regs;
645 			mov EAX, level;
646 			mov ECX, sublevel;
647 			cpuid;
648 			mov [EDI + regs.eax.offsetof], EAX;
649 			mov [EDI + regs.ebx.offsetof], EBX;
650 			mov [EDI + regs.ecx.offsetof], ECX;
651 			mov [EDI + regs.edx.offsetof], EDX;
652 		} else version (X86_64) asm {
653 			mov RDI, regs;
654 			mov EAX, level;
655 			mov ECX, sublevel;
656 			cpuid;
657 			mov [RDI + regs.eax.offsetof], EAX;
658 			mov [RDI + regs.ebx.offsetof], EBX;
659 			mov [RDI + regs.ecx.offsetof], ECX;
660 			mov [RDI + regs.edx.offsetof], EDX;
661 		}
662 	} else version (GDC) {
663 		asm {
664 			"cpuid"
665 			: "=a" (regs.eax), "=b" (regs.ebx), "=c" (regs.ecx), "=d" (regs.edx)
666 			: "a" (level), "c" (sublevel);
667 		}
668 	} else version (LDC) {
669 		version (X86) asm {
670 			lea EDI, regs;
671 			mov EAX, level;
672 			mov ECX, sublevel;
673 			cpuid;
674 			mov [EDI + regs.eax.offsetof], EAX;
675 			mov [EDI + regs.ebx.offsetof], EBX;
676 			mov [EDI + regs.ecx.offsetof], ECX;
677 			mov [EDI + regs.edx.offsetof], EDX;
678 		} else version (X86_64) asm {
679 			lea RDI, regs;
680 			mov EAX, level;
681 			mov ECX, sublevel;
682 			cpuid;
683 			mov [RDI + regs.eax.offsetof], EAX;
684 			mov [RDI + regs.ebx.offsetof], EBX;
685 			mov [RDI + regs.ecx.offsetof], ECX;
686 			mov [RDI + regs.edx.offsetof], EDX;
687 		}
688 	}
689 	version (Trace) with (regs) trace(
690 		"level=%x sub=%x -> eax=%x ebx=%x ecx=%x edx=%x",
691 		level, sublevel, eax, ebx, ecx, edx);
692 }
693 /// Typically these tests are done on Pentium 4 and later processors
694 @system unittest {
695 	REGISTERS regs;
696 	ddcpuid_id(regs, 0);
697 	assert(regs.eax > 0 && regs.eax < 0x4000_0000);
698 	ddcpuid_id(regs, 0x8000_0000);
699 	assert(regs.eax > 0x8000_0000);
700 }
701 
702 private uint ddcpuid_max_leaf() {
703 	version (DMDLDC) {
704 		asm {
705 			xor EAX,EAX;
706 			cpuid;
707 		}
708 	} else version (GDC) {
709 		asm {
710 			"xor %eax,%eax\t\n"~
711 			"cpuid";
712 		}
713 	}
714 }
715 
716 private uint ddcpuid_max_leaf_virt() {
717 	version (DMDLDC) {
718 		asm {
719 			mov EAX,0x4000_0000;
720 			cpuid;
721 		}
722 	} else version (GDC) {
723 		asm {
724 			"mov 0x40000000,%eax\t\n"~
725 			"cpuid";
726 		}
727 	}
728 }
729 
730 private uint ddcpuid_max_leaf_ext() {
731 	version (DMDLDC) {
732 		asm {
733 			mov EAX,0x8000_0000;
734 			cpuid;
735 		}
736 	} else version (GDC) {
737 		asm {
738 			"mov 0x80000000,%eax\t\n"~
739 			"cpuid";
740 		}
741 	}
742 }
743 
744 /// Get CPU leaf levels.
745 /// Params: info = CPUINFO structure
746 pragma(inline, false)
747 void ddcpuid_leaves(ref CPUINFO info) {
748 	info.maxLeaf = ddcpuid_max_leaf;
749 	info.maxLeafVirt = ddcpuid_max_leaf_virt;
750 	info.maxLeafExtended = ddcpuid_max_leaf_ext;
751 }
752 
753 pragma(inline, false)
754 private
755 void ddcpuid_vendor(ref char[12] string_) {
756 	version (DMD) {
757 		version (X86) asm {
758 			mov EDI, string_;
759 			mov EAX, 0;
760 			cpuid;
761 			mov [EDI], EBX;
762 			mov [EDI + 4], EDX;
763 			mov [EDI + 8], ECX;
764 		} else asm { // x86-64
765 			mov RDI, string_;
766 			mov EAX, 0;
767 			cpuid;
768 			mov [RDI], EBX;
769 			mov [RDI + 4], EDX;
770 			mov [RDI + 8], ECX;
771 		}
772 	} else version (GDC) {
773 		version (X86) asm {
774 			"lea %0, %%edi\n\t"~
775 			"mov $0, %%eax\n\t"~
776 			"cpuid\n"~
777 			"mov %%ebx, (%%edi)\n\t"~
778 			"mov %%edx, 4(%%edi)\n\t"~
779 			"mov %%ecx, 8(%%edi)"
780 			:
781 			: "m" (string_)
782 			: "edi", "eax", "ebx", "ecx", "edx";
783 		} else asm { // x86-64
784 			"lea %0, %%rdi\n\t"~
785 			"mov $0, %%eax\n\t"~
786 			"cpuid\n"~
787 			"mov %%ebx, (%%rdi)\n\t"~
788 			"mov %%edx, 4(%%rdi)\n\t"~
789 			"mov %%ecx, 8(%%rdi)"
790 			:
791 			: "m" (string_)
792 			: "rdi", "rax", "rbx", "rcx", "rdx";
793 		}
794 	} else version (LDC) {
795 		version (X86) asm {
796 			lea EDI, string_;
797 			mov EAX, 0;
798 			cpuid;
799 			mov [EDI], EBX;
800 			mov [EDI + 4], EDX;
801 			mov [EDI + 8], ECX;
802 		} else asm { // x86-64
803 			lea RDI, string_;
804 			mov EAX, 0;
805 			cpuid;
806 			mov [RDI], EBX;
807 			mov [RDI + 4], EDX;
808 			mov [RDI + 8], ECX;
809 		}
810 	}
811 }
812 
813 private
814 Vendor ddcpuid_vendor_id(ref VendorString vendor) {
815 	// Vendor string verification
816 	// If the rest of the string doesn't correspond, the id is unset
817 	switch (vendor.ebx) with (Vendor) {
818 	case Intel:	// "GenuineIntel"
819 		if (vendor.edx != ID!("ineI")) break;
820 		if (vendor.ecx != ID!("ntel")) break;
821 		return Vendor.Intel;
822 	case AMD:	// "AuthenticAMD"
823 		if (vendor.edx != ID!("enti")) break;
824 		if (vendor.ecx != ID!("cAMD")) break;
825 		return Vendor.AMD;
826 	case VIA:	// "VIA VIA VIA "
827 		if (vendor.edx != ID!("VIA ")) break;
828 		if (vendor.ecx != ID!("VIA ")) break;
829 		return Vendor.VIA;
830 	default: // Unknown
831 	}
832 	return Vendor.Other;
833 }
834 
835 pragma(inline, false)
836 private
837 void ddcpuid_extended_brand(ref char[48] string_) {
838 	version (DMD) {
839 		version (X86) asm {
840 			mov EDI, string_;
841 			mov EAX, 0x8000_0002;
842 			cpuid;
843 			mov [EDI], EAX;
844 			mov [EDI +  4], EBX;
845 			mov [EDI +  8], ECX;
846 			mov [EDI + 12], EDX;
847 			mov EAX, 0x8000_0003;
848 			cpuid;
849 			mov [EDI + 16], EAX;
850 			mov [EDI + 20], EBX;
851 			mov [EDI + 24], ECX;
852 			mov [EDI + 28], EDX;
853 			mov EAX, 0x8000_0004;
854 			cpuid;
855 			mov [EDI + 32], EAX;
856 			mov [EDI + 36], EBX;
857 			mov [EDI + 40], ECX;
858 			mov [EDI + 44], EDX;
859 		} else version (X86_64) asm {
860 			mov RDI, string_;
861 			mov EAX, 0x8000_0002;
862 			cpuid;
863 			mov [RDI], EAX;
864 			mov [RDI +  4], EBX;
865 			mov [RDI +  8], ECX;
866 			mov [RDI + 12], EDX;
867 			mov EAX, 0x8000_0003;
868 			cpuid;
869 			mov [RDI + 16], EAX;
870 			mov [RDI + 20], EBX;
871 			mov [RDI + 24], ECX;
872 			mov [RDI + 28], EDX;
873 			mov EAX, 0x8000_0004;
874 			cpuid;
875 			mov [RDI + 32], EAX;
876 			mov [RDI + 36], EBX;
877 			mov [RDI + 40], ECX;
878 			mov [RDI + 44], EDX;
879 		}
880 	} else version (GDC) {
881 		version (X86) asm {
882 			"lea %0, %%edi\n\t"~
883 			"mov $0x80000002, %%eax\n\t"~
884 			"cpuid\n\t"~
885 			"mov %%eax, (%%rdi)\n\t"~
886 			"mov %%ebx, 4(%%rdi)\n\t"~
887 			"mov %%ecx, 8(%%rdi)\n\t"~
888 			"mov %%edx, 12(%%rdi)\n\t"~
889 			"mov $0x80000003, %%eax\n\t"~
890 			"cpuid\n\t"~
891 			"mov %%eax, 16(%%rdi)\n\t"~
892 			"mov %%ebx, 20(%%rdi)\n\t"~
893 			"mov %%ecx, 24(%%rdi)\n\t"~
894 			"mov %%edx, 28(%%rdi)\n\t"~
895 			"mov $0x80000004, %%eax\n\t"~
896 			"cpuid\n\t"~
897 			"mov %%eax, 32(%%rdi)\n\t"~
898 			"mov %%ebx, 36(%%rdi)\n\t"~
899 			"mov %%ecx, 40(%%rdi)\n\t"~
900 			"mov %%edx, 44(%%rdi)"
901 			:
902 			: "m" (string_)
903 			: "edi", "eax", "ebx", "ecx", "edx";
904 		} else version (X86_64) asm {
905 			"lea %0, %%rdi\n\t"~
906 			"mov $0x80000002, %%eax\n\t"~
907 			"cpuid\n\t"~
908 			"mov %%eax, (%%rdi)\n\t"~
909 			"mov %%ebx, 4(%%rdi)\n\t"~
910 			"mov %%ecx, 8(%%rdi)\n\t"~
911 			"mov %%edx, 12(%%rdi)\n\t"~
912 			"mov $0x80000003, %%eax\n\t"~
913 			"cpuid\n\t"~
914 			"mov %%eax, 16(%%rdi)\n\t"~
915 			"mov %%ebx, 20(%%rdi)\n\t"~
916 			"mov %%ecx, 24(%%rdi)\n\t"~
917 			"mov %%edx, 28(%%rdi)\n\t"~
918 			"mov $0x80000004, %%eax\n\t"~
919 			"cpuid\n\t"~
920 			"mov %%eax, 32(%%rdi)\n\t"~
921 			"mov %%ebx, 36(%%rdi)\n\t"~
922 			"mov %%ecx, 40(%%rdi)\n\t"~
923 			"mov %%edx, 44(%%rdi)"
924 			:
925 			: "m" (string_)
926 			: "rdi", "rax", "rbx", "rcx", "rdx";
927 		}
928 	} else version (LDC) {
929 		version (X86) asm {
930 			lea EDI, string_;
931 			mov EAX, 0x8000_0002;
932 			cpuid;
933 			mov [EDI], EAX;
934 			mov [EDI +  4], EBX;
935 			mov [EDI +  8], ECX;
936 			mov [EDI + 12], EDX;
937 			mov EAX, 0x8000_0003;
938 			cpuid;
939 			mov [EDI + 16], EAX;
940 			mov [EDI + 20], EBX;
941 			mov [EDI + 24], ECX;
942 			mov [EDI + 28], EDX;
943 			mov EAX, 0x8000_0004;
944 			cpuid;
945 			mov [EDI + 32], EAX;
946 			mov [EDI + 36], EBX;
947 			mov [EDI + 40], ECX;
948 			mov [EDI + 44], EDX;
949 		} else version (X86_64) asm {
950 			lea RDI, string_;
951 			mov EAX, 0x8000_0002;
952 			cpuid;
953 			mov [RDI], EAX;
954 			mov [RDI +  4], EBX;
955 			mov [RDI +  8], ECX;
956 			mov [RDI + 12], EDX;
957 			mov EAX, 0x8000_0003;
958 			cpuid;
959 			mov [RDI + 16], EAX;
960 			mov [RDI + 20], EBX;
961 			mov [RDI + 24], ECX;
962 			mov [RDI + 28], EDX;
963 			mov EAX, 0x8000_0004;
964 			cpuid;
965 			mov [RDI + 32], EAX;
966 			mov [RDI + 36], EBX;
967 			mov [RDI + 40], ECX;
968 			mov [RDI + 44], EDX;
969 		}
970 	}
971 }
972 
973 // Avoids depending on C runtime for library.
974 /// Copy brand string
975 /// Params:
976 /// 	dst = Destination buffer
977 /// 	src = Source constant string
978 pragma(inline, false)
979 private
980 void ddcpuid_strcpy48(ref char[48] dst, const(char) *src) {
981 	for (size_t i; i < 48; ++i) {
982 		char c = src[i];
983 		dst[i] = c;
984 		if (c == 0) break;
985 	}
986 }
987 private alias strcpy48 = ddcpuid_strcpy48;
988 
989 @system unittest {
990 	char[48] buffer = void;
991 	strcpy48(buffer, "ea");
992 	assert(buffer[0] == 'e');
993 	assert(buffer[1] == 'a');
994 	assert(buffer[2] == 0);
995 }
996 
997 /// Get the legacy processor brand string.
998 /// These indexes/tables were introduced in Intel's Pentium III.
999 /// AMD does not use them.
1000 /// Params:
1001 /// 	info = CPUINFO structure.
1002 /// 	index = CPUID.01h.BL value.
1003 pragma(inline, false)
1004 private
1005 void ddcpuid_intel_brand_index(ref CPUINFO info, ubyte index) {
1006 	switch (index) {
1007 	case 1, 0xA, 0xF, 0x14:
1008 		strcpy48(info.brandString, "Intel(R) Celeron(R)");
1009 		return;
1010 	case 2, 4:
1011 		strcpy48(info.brandString, "Intel(R) Pentium(R) III");
1012 		return;
1013 	case 3:
1014 		if (info.identifier == 0x6b1) goto case 1;
1015 		strcpy48(info.brandString, "Intel(R) Pentium(R) III Xeon(R)");
1016 		return;
1017 	case 6:
1018 		strcpy48(info.brandString, "Mobile Intel(R) Pentium(R) III");
1019 		return;
1020 	case 7, 0x13, 0x17: // Same as Intel(R) Celeron(R) M?
1021 		strcpy48(info.brandString, "Mobile Intel(R) Celeron(R)");
1022 		return;
1023 	case 8, 9:
1024 		strcpy48(info.brandString, "Intel(R) Pentium(R) 4");
1025 		return;
1026 	case 0xB:
1027 		if (info.identifier == 0xf13) goto case 0xC;
1028 	L_XEON: // Needed to avoid loop
1029 		strcpy48(info.brandString, "Intel(R) Xeon(R)");
1030 		return;
1031 	case 0xC:
1032 		strcpy48(info.brandString, "Intel(R) Xeon(R) MP");
1033 		return;
1034 	case 0xE:
1035 		if (info.identifier == 0xf13) goto L_XEON;
1036 		strcpy48(info.brandString, "Mobile Intel(R) Pentium(R) 4");
1037 		return;
1038 	case 0x11, 0x15: // Yes, really.
1039 		strcpy48(info.brandString, "Mobile Genuine Intel(R)");
1040 		return;
1041 	case 0x12: strcpy48(info.brandString, "Intel(R) Celeron(R) M"); return;
1042 	case 0x16: strcpy48(info.brandString, "Intel(R) Pentium(R) M"); return;
1043 	default:   strcpy48(info.brandString, "Unknown"); return;
1044 	}
1045 }
1046 
1047 pragma(inline, false)
1048 private
1049 void ddcpuid_intel_brand_family(ref CPUINFO info) {
1050 	// This function exist for processors that does not support the
1051 	// brand name table.
1052 	// At least do from Pentium to late Pentium II processors.
1053 	switch (info.family) {
1054 	case 5: // i586, Pentium
1055 		if (info.model >= 4) {
1056 			strcpy48(info.brandString, "Intel(R) Pentium(R) MMX");
1057 			return;
1058 		}
1059 		strcpy48(info.brandString, "Intel(R) Pentium(R)");
1060 		return;
1061 	case 6: // i686, Pentium Pro
1062 		if (info.model >= 3) {
1063 			strcpy48(info.brandString, "Intel(R) Pentium(R) II");
1064 			return;
1065 		}
1066 		strcpy48(info.brandString, "Intel(R) Pentium(R) Pro");
1067 		return;
1068 	default:
1069 		strcpy48(info.brandString, "Unknown");
1070 		return;
1071 	}
1072 }
1073 
1074 pragma(inline, false)
1075 private
1076 void ddcpuid_amd_brand_family(ref CPUINFO info) {
1077 	// This function exist for processors that does not support the
1078 	// extended brand string which is the Am5x86 and AMD K-5 model 0.
1079 	// K-5 model 1 has extended brand string so case 5 is only model 0.
1080 	// AMD has no official names for these.
1081 	switch (info.family) {
1082 	case 4:  strcpy48(info.brandString, "AMD Am5x86"); return;
1083 	case 5:  strcpy48(info.brandString, "AMD K5"); return;
1084 	default: strcpy48(info.brandString, "Unknown"); return;
1085 	}
1086 }
1087 
1088 pragma(inline, false)
1089 private
1090 void ddcpuid_virt_vendor(ref char[12] string_) {
1091 	version (DMD) {
1092 		version (X86) asm {
1093 			mov EDI, string_;
1094 			mov EAX, 0x40000000;
1095 			cpuid;
1096 			mov [EDI], EBX;
1097 			mov [EDI + 4], ECX;
1098 			mov [EDI + 8], EDX;
1099 		} else asm { // x86-64
1100 			mov RDI, string_;
1101 			mov EAX, 0x40000000;
1102 			cpuid;
1103 			mov [RDI], EBX;
1104 			mov [RDI + 4], ECX;
1105 			mov [RDI + 8], EDX;
1106 		}
1107 	} else version (GDC) {
1108 		version (X86) asm {
1109 			"lea %0, %%edi\n\t"~
1110 			"mov $0x40000000, %%eax\n\t"~
1111 			"cpuid\n"~
1112 			"mov %%ebx, (%%edi)\n\t"~
1113 			"mov %%ecx, 4(%%edi)\n\t"~
1114 			"mov %%edx, 8(%%edi)"
1115 			:
1116 			: "m" (string_)
1117 			: "edi", "eax", "ebx", "ecx", "edx";
1118 		} else asm { // x86-64
1119 			"lea %0, %%rdi\n\t"~
1120 			"mov $0x40000000, %%eax\n\t"~
1121 			"cpuid\n"~
1122 			"mov %%ebx, (%%rdi)\n\t"~
1123 			"mov %%ecx, 4(%%rdi)\n\t"~
1124 			"mov %%edx, 8(%%rdi)"
1125 			:
1126 			: "m" (string_)
1127 			: "rdi", "rax", "rbx", "rcx", "rdx";
1128 		}
1129 	} else version (LDC) {
1130 		version (X86) asm {
1131 			lea EDI, string_;
1132 			mov EAX, 0x40000000;
1133 			cpuid;
1134 			mov [EDI], EBX;
1135 			mov [EDI + 4], ECX;
1136 			mov [EDI + 8], EDX;
1137 		} else asm { // x86-64
1138 			lea RDI, string_;
1139 			mov EAX, 0x40000000;
1140 			cpuid;
1141 			mov [RDI], EBX;
1142 			mov [RDI + 4], ECX;
1143 			mov [RDI + 8], EDX;
1144 		}
1145 	}
1146 }
1147 
1148 pragma(inline, false)
1149 private
1150 VirtVendor ddcpuid_virt_vendor_id(ref VirtVendorString vendor) {
1151 	// Paravirtual vendor string verification
1152 	// If the rest of the string doesn't correspond, the id is unset
1153 	switch (vendor.ebx) {
1154 	case VirtVendor.KVM:	// "KVMKVMKVM\0\0\0"
1155 		if (vendor.ecx != ID!("VMKV")) goto default;
1156 		if (vendor.edx != ID!("M\0\0\0")) goto default;
1157 		return VirtVendor.KVM;
1158 	case VirtVendor.HyperV:	// "Microsoft Hv"
1159 		if (vendor.ecx != ID!("osof")) goto default;
1160 		if (vendor.edx != ID!("t Hv")) goto default;
1161 		return VirtVendor.HyperV;
1162 	case VirtVendor.VBoxHyperV:	// "VBoxVBoxVBox"
1163 		if (vendor.ecx != ID!("VBox")) goto default;
1164 		if (vendor.edx != ID!("VBox")) goto default;
1165 		return VirtVendor.HyperV; // Bug according to VBox
1166 	default:
1167 		return VirtVendor.Other;
1168 	}
1169 }
1170 
1171 @system unittest {
1172 	VirtVendorString vendor;
1173 	vendor.string_ = "KVMKVMKVM\0\0\0";
1174 	assert(ddcpuid_virt_vendor_id(vendor) == VirtVendor.KVM);
1175 }
1176 
1177 pragma(inline, false)
1178 private
1179 void ddcpuid_model_string(ref CPUINFO info) {
1180 	switch (info.vendor.id) with (Vendor) {
1181 	case Intel:
1182 		// Brand string
1183 		if (info.maxLeafExtended >= 0x8000_0004)
1184 			ddcpuid_extended_brand(info.brandString);
1185 		else if (info.brandIndex)
1186 			ddcpuid_intel_brand_index(info, info.brandIndex);
1187 		else
1188 			ddcpuid_intel_brand_family(info);
1189 		return;
1190 	case AMD, VIA:
1191 		// Brand string
1192 		// NOTE: AMD processor never supported the string table.
1193 		//       The Am486DX4 and Am5x86 processors do not support the extended brand string.
1194 		//       The K5 model 0 does not support the extended brand string.
1195 		//       The K5 model 1, 2, and 3 support the extended brand string.
1196 		if (info.maxLeafExtended >= 0x8000_0004)
1197 			ddcpuid_extended_brand(info.brandString);
1198 		else
1199 			ddcpuid_amd_brand_family(info);
1200 		return;
1201 	default:
1202 		strcpy48(info.brandString, "Unknown");
1203 		return;
1204 	}
1205 }
1206 
1207 pragma(inline, false)
1208 private
1209 void ddcpuid_leaf1(ref CPUINFO info, ref REGISTERS regs) {
1210 	// EAX
1211 	info.identifier = regs.eax;
1212 	info.stepping   = regs.eax & 15;       // EAX[3:0]
1213 	info.modelBase  = regs.eax >>  4 & 15; // EAX[7:4]
1214 	info.familyBase = regs.eax >>  8 & 15; // EAX[11:8]
1215 	info.type       = regs.eax >> 12 & 3;  // EAX[13:12]
1216 	info.typeString = PROCESSOR_TYPE[info.type];
1217 	info.modelExtended   = regs.eax >> 16 & 15; // EAX[19:16]
1218 	info.familyExtended  = cast(ubyte)(regs.eax >> 20); // EAX[27:20]
1219 	
1220 	switch (info.vendor.id) with (Vendor) {
1221 	case Intel:
1222 		info.family = info.familyBase != 15 ?
1223 			cast(ushort)info.familyBase :
1224 			cast(ushort)(info.familyExtended + info.familyBase);
1225 		
1226 		info.model = info.familyBase == 6 || info.familyBase == 0 ?
1227 			cast(ushort)((info.modelExtended << 4) + info.modelBase) :
1228 			cast(ushort)info.modelBase; // DisplayModel = Model_ID;
1229 		
1230 		// ECX
1231 		info.debugging.dtes64	= bit(regs.ecx, 2);
1232 		info.debugging.ds_cpl	= bit(regs.ecx, 4);
1233 		info.virt.available	= bit(regs.ecx, 5);
1234 		info.tech.smx	= bit(regs.ecx, 6);
1235 		info.tech.eist	= bit(regs.ecx, 7);
1236 		info.sys.tm2	= bit(regs.ecx, 8);
1237 		info.cache.cnxtId	= bit(regs.ecx, 10);
1238 		info.debugging.sdbg	= bit(regs.ecx, 11);
1239 		info.misc.xtpr	= bit(regs.ecx, 14);
1240 		info.debugging.pdcm	= bit(regs.ecx, 15);
1241 		info.misc.pcid	= bit(regs.ecx, 17);
1242 		info.debugging.mca	= bit(regs.ecx, 18);
1243 		info.sys.x2apic	= bit(regs.ecx, 21);
1244 		info.extras.rdtscDeadline	= bit(regs.ecx, 24);
1245 		
1246 		// EDX
1247 		info.misc.psn	= bit(regs.edx, 18);
1248 		info.debugging.ds	= bit(regs.edx, 21);
1249 		info.sys.available	= bit(regs.edx, 22);
1250 		info.cache.ss	= bit(regs.edx, 27);
1251 		info.sys.tm	= bit(regs.edx, 29);
1252 		info.debugging.pbe	= regs.edx >= BIT!(31);
1253 		break;
1254 	case AMD:
1255 		if (info.familyBase < 15) {
1256 			info.family = info.familyBase;
1257 			info.model = info.modelBase;
1258 		} else {
1259 			info.family = cast(ushort)(info.familyExtended + info.familyBase);
1260 			info.model = cast(ushort)((info.modelExtended << 4) + info.modelBase);
1261 		}
1262 		break;
1263 	default:
1264 	}
1265 	
1266 	// EBX
1267 	info.sys.apicId = regs.ebx >> 24;
1268 	info.sys.maxApicId = cast(ubyte)(regs.ebx >> 16);
1269 	info.cache.clflushLinesize = regs.bh;
1270 	info.brandIndex = regs.bl;
1271 	
1272 	// ECX
1273 	info.sse.sse3	= bit(regs.ecx, 0);
1274 	info.extras.pclmulqdq	= bit(regs.ecx, 1);
1275 	info.extras.monitor	= bit(regs.ecx, 3);
1276 	info.sse.ssse3	= bit(regs.ecx, 9);
1277 	info.extensions.fma3	= bit(regs.ecx, 12);
1278 	info.extras.cmpxchg16b	= bit(regs.ecx, 13);
1279 	info.sse.sse41	= bit(regs.ecx, 15);
1280 	info.sse.sse42	= bit(regs.ecx, 20);
1281 	info.extras.movbe	= bit(regs.ecx, 22);
1282 	info.extras.popcnt	= bit(regs.ecx, 23);
1283 	info.extensions.aes_ni	= bit(regs.ecx, 25);
1284 	info.extras.xsave	= bit(regs.ecx, 26);
1285 	info.extras.osxsave	= bit(regs.ecx, 27);
1286 	info.avx.avx	= bit(regs.ecx, 28);
1287 	info.extensions.f16c	= bit(regs.ecx, 29);
1288 	info.extras.rdrand	= bit(regs.ecx, 30);
1289 	
1290 	// EDX
1291 	info.extensions.fpu	= bit(regs.edx, 0);
1292 	info.virt.vme	= bit(regs.edx, 1);
1293 	info.debugging.de	= bit(regs.edx, 2);
1294 	info.memory.pse	= bit(regs.edx, 3);
1295 	info.extras.rdtsc	= bit(regs.edx, 4);
1296 	info.extras.rdmsr	= bit(regs.edx, 5);
1297 	info.memory.pae	= bit(regs.edx, 6);
1298 	info.debugging.mce	= bit(regs.edx, 7);
1299 	info.extras.cmpxchg8b	= bit(regs.edx, 8);
1300 	info.sys.apic	= bit(regs.edx, 9);
1301 	info.extras.sysenter	= bit(regs.edx, 11);
1302 	info.memory.mtrr	= bit(regs.edx, 12);
1303 	info.memory.pge	= bit(regs.edx, 13);
1304 	info.debugging.mca	= bit(regs.edx, 14);
1305 	info.extras.cmov	= bit(regs.edx, 15);
1306 	info.memory.pat	= bit(regs.edx, 16);
1307 	info.memory.pse36	= bit(regs.edx, 17);
1308 	info.cache.clflush	= bit(regs.edx, 19);
1309 	info.extensions.mmx	= bit(regs.edx, 23);
1310 	info.extras.fxsr	= bit(regs.edx, 24);
1311 	info.sse.sse	= bit(regs.edx, 25);
1312 	info.sse.sse2	= bit(regs.edx, 26);
1313 	info.tech.htt	= bit(regs.edx, 28);
1314 }
1315 
1316 //NOTE: Only Intel officially supports CPUID.02h
1317 //      No dedicated functions to a cache descriptor to avoid a definition.
1318 pragma(inline, false)
1319 private
1320 void ddcpuid_leaf2(ref CPUINFO info, ref REGISTERS regs) {
1321 	struct leaf2_t {
1322 		union {
1323 			REGISTERS registers;
1324 			ubyte[16] values;
1325 		}
1326 	}
1327 	leaf2_t data = void;
1328 	
1329 	data.registers = regs;
1330 	
1331 	enum L1I = 0;
1332 	enum L1D = 1;
1333 	enum L2 = 2;
1334 	enum L3 = 3;
1335 	// Skips value in AL
1336 	with (info.cache) for (size_t index = 1; index < 16; ++index) {
1337 		ubyte value = data.values[index];
1338 		
1339 		// Cache entries only, the rest is "don't care".
1340 		// Unless if one day I support looking up TLB data, but AMD does not support this.
1341 		// continue: Explicitly skip cache, this includes 0x00 (null), 0x40 (no L2 or L3).
1342 		// break: Valid cache descriptor, increment cache level.
1343 		//TODO: table + foreach loop
1344 		switch (value) {
1345 		case 0x06: // 1st-level instruction cache: 8 KBytes, 4-way set associative, 32 byte line size
1346 			level[L1I] = CACHEINFO(1, 'I', 8, 1, 4, 1, 32, 64);
1347 			break;
1348 		case 0x08: // 1st-level instruction cache: 16 KBytes, 4-way set associative, 32 byte line size
1349 			level[L1I] = CACHEINFO(1, 'I', 16, 1, 4, 1, 32, 128);
1350 			break;
1351 		case 0x09: // 1st-level instruction cache: 32 KBytes, 4-way set associative, 64 byte line size
1352 			level[L1I] = CACHEINFO(1, 'I', 32, 1, 4, 1, 64, 128);
1353 			break;
1354 		case 0x0A: // 1st-level data cache: 8 KBytes, 2-way set associative, 32 byte line size
1355 			level[L1D] = CACHEINFO(1, 'D', 8, 1, 2, 1, 32, 128);
1356 			break;
1357 		case 0x0C: // 1st-level data cache: 16 KBytes, 4-way set associative, 32 byte line size
1358 			level[L1D] = CACHEINFO(1, 'D', 16, 1, 4, 1, 32, 128);
1359 			break;
1360 		case 0x0D: // 1st-level data cache: 16 KBytes, 4-way set associative, 64 byte line size (ECC?)
1361 			level[L1D] = CACHEINFO(1, 'D', 16, 1, 4, 1, 64, 64);
1362 			break;
1363 		case 0x0E: // 1st-level data cache: 24 KBytes, 6-way set associative, 64 byte line size
1364 			level[L1D] = CACHEINFO(1, 'D', 24, 1, 6, 1, 64, 64);
1365 			break;
1366 		case 0x10: // (sandpile) data L1 cache, 16 KB, 4 ways, 32 byte lines (IA-64)
1367 			level[L1D] = CACHEINFO(1, 'D', 16, 1, 4, 1, 32, 64);
1368 			break;
1369 		case 0x15: // (sandpile) code L1 cache, 16 KB, 4 ways, 32 byte lines (IA-64)
1370 			level[L1I] = CACHEINFO(1, 'I', 16, 1, 4, 1, 32, 64);
1371 			break;
1372 		case 0x1a: // (sandpile) code and data L2 cache, 96 KB, 6 ways, 64 byte lines (IA-64)
1373 			level[L2] = CACHEINFO(2, 'I', 96, 1, 6, 1, 64, 256);
1374 			break;
1375 		case 0x1D: // 2nd-level cache: 128 KBytes, 2-way set associative, 64 byte line size
1376 			level[L2] = CACHEINFO(2, 'U', 128, 1, 2, 1, 64, 1024);
1377 			break;
1378 		case 0x21: // 2nd-level cache: 256 KBytes, 8-way set associative, 64 byte line size
1379 			level[L2] = CACHEINFO(2, 'U', 256, 1, 8, 1, 64, 512);
1380 			break;
1381 		case 0x22: // 3rd-level cache: 512 KBytes, 4-way set associative, 64 byte line size, 2 lines per sector
1382 			level[L3] = CACHEINFO(3, 'U', 512, 1, 4, 2, 64, 1024);
1383 			break;
1384 		case 0x23: // 3rd-level cache: 1 MBytes, 8-way set associative, 64 byte line size, 2 lines per sector
1385 			level[L3] = CACHEINFO(3, 'U', 1024, 1, 8, 2, 64, 1024);
1386 			break;
1387 		case 0x24: // 2nd-level cache: 1 MBytes, 16-way set associative, 64 byte line size
1388 			level[L2] = CACHEINFO(2, 'U', 1024, 1, 16, 1, 64, 1024);
1389 			break;
1390 		case 0x25: // 3rd-level cache: 2 MBytes, 8-way set associative, 64 byte line size, 2 lines per sector
1391 			level[L3] = CACHEINFO(3, 'U', 2048, 1, 8, 2, 64, 2048);
1392 			break;
1393 		case 0x29: // 3rd-level cache: 4 MBytes, 8-way set associative, 64 byte line size, 2 lines per sector
1394 			level[L3] = CACHEINFO(3, 'U', 4096, 1, 8, 2, 64, 4096);
1395 			break;
1396 		case 0x2C: // 1st-level data cache: 32 KBytes, 8-way set associative, 64 byte line size
1397 			level[L1D] = CACHEINFO(1, 'D', 32, 1, 8, 1, 64, 64);
1398 			break;
1399 		case 0x30: // 1st-level instruction cache: 32 KBytes, 8-way set associative, 64 byte line size
1400 			level[L1I] = CACHEINFO(1, 'I', 32, 1, 8, 1, 64, 64);
1401 			break;
1402 		case 0x39: // (sandpile) code and data L2 cache, 128 KB, 4 ways, 64 byte lines, sectored (htt?)
1403 			level[L2] = CACHEINFO(2, 'U', 128, 1, 4, 1, 64, 512);
1404 			break;
1405 		case 0x3A: // (sandpile) code and data L2 cache, 192 KB, 6 ways, 64 byte lines, sectored (htt?)
1406 			level[L2] = CACHEINFO(2, 'U', 192, 1, 6, 1, 64, 512);
1407 			break;
1408 		case 0x3B: // (sandpile) code and data L2 cache, 128 KB, 2 ways, 64 byte lines, sectored (htt?)
1409 			level[L2] = CACHEINFO(2, 'U', 128, 1, 2, 1, 64, 1024);
1410 			break;
1411 		case 0x3C: // (sandpile) code and data L2 cache, 256 KB, 4 ways, 64 byte lines, sectored
1412 			level[L2] = CACHEINFO(2, 'U', 256, 1, 4, 1, 64, 1024);
1413 			break;
1414 		case 0x3D: // (sandpile) code and data L2 cache, 384 KB, 6 ways, 64 byte lines, sectored (htt?)
1415 			level[L2] = CACHEINFO(2, 'U', 384, 1, 6, 1, 64, 1024);
1416 			break;
1417 		case 0x3E: // (sandpile) code and data L2 cache, 512 KB, 4 ways, 64 byte lines, sectored (htt?)
1418 			level[L2] = CACHEINFO(2, 'U', 512, 1, 4, 1, 64, 2048);
1419 			break;
1420 		case 0x41: // 2nd-level cache: 128 KBytes, 4-way set associative, 32 byte line size
1421 			level[L2] = CACHEINFO(2, 'U', 128, 1, 4, 1, 32, 1024);
1422 			break;
1423 		case 0x42: // 2nd-level cache: 256 KBytes, 4-way set associative, 32 byte line size
1424 			level[L2] = CACHEINFO(2, 'U', 256, 1, 4, 1, 32, 2048);
1425 			break;
1426 		case 0x43: // 2nd-level cache: 512 KBytes, 4-way set associative, 32 byte line size
1427 			level[L2] = CACHEINFO(2, 'U', 512, 1, 4, 1, 32, 4096);
1428 			break;
1429 		case 0x44: // 2nd-level cache: 1 MByte, 4-way set associative, 32 byte line size
1430 			level[L2] = CACHEINFO(2, 'U', 1024, 1, 4, 1, 32, 8192);
1431 			break;
1432 		case 0x45: // 2nd-level cache: 2 MByte, 4-way set associative, 32 byte line size
1433 			level[L2] = CACHEINFO(2, 'U', 2048, 1, 4, 1, 32, 16384);
1434 			break;
1435 		case 0x46: // 3rd-level cache: 4 MByte, 4-way set associative, 64 byte line size
1436 			level[L2] = CACHEINFO(2, 'U', 4096, 1, 4, 1, 64, 16384);
1437 			break;
1438 		case 0x47: // 3rd-level cache: 8 MByte, 8-way set associative, 64 byte line size
1439 			level[L3] = CACHEINFO(3, 'U', 8192, 1, 8, 1, 64, 16384);
1440 			break;
1441 		case 0x48: // 2nd-level cache: 3 MByte, 12-way set associative, 64 byte line size
1442 			level[L2] = CACHEINFO(2, 'U', 3072, 1, 12, 1, 64, 4096);
1443 			break;
1444 		// 3rd-level cache: 4 MByte, 16-way set associative, 64-byte line size (Intel Xeon processor MP, Family 0FH, Model 06H);			
1445 		// 2nd-level cache: 4 MByte, 16-way set associative, 64 byte line size
1446 		case 0x49:
1447 			if (info.family == 0xf && info.family == 6)
1448 				level[L3] = CACHEINFO(3, 'U', 4096, 1, 16, 1, 64, 4096);
1449 			else
1450 				level[L2] = CACHEINFO(2, 'U', 4096, 1, 16, 1, 64, 4096);
1451 			break;
1452 		case 0x4A: // 3rd-level cache: 6 MByte, 12-way set associative, 64 byte line size
1453 			level[L3] = CACHEINFO(3, 'U', 6144, 1, 12, 1, 64, 6144);
1454 			break;
1455 		case 0x4B: // 3rd-level cache: 8 MByte, 16-way set associative, 64 byte line size
1456 			level[L3] = CACHEINFO(3, 'U', 8192, 1, 16, 1, 64, 8192);
1457 			break;
1458 		case 0x4C: // 3rd-level cache: 12 MByte, 12-way set associative, 64 byte line size
1459 			level[L3] = CACHEINFO(3, 'U', 8192, 1, 12, 1, 64, 16384);
1460 			break;
1461 		case 0x4D: // 3rd-level cache: 16 MByte, 16-way set associative, 64 byte line size
1462 			level[L3] = CACHEINFO(3, 'U', 16384, 1, 16, 1, 64, 16384);
1463 			break;
1464 		case 0x4E: // 2nd-level cache: 6MByte, 24-way set associative, 64 byte line size
1465 			level[L2] = CACHEINFO(2, 'U', 6144, 1, 24, 1, 64, 4096);
1466 			break;
1467 		case 0x60: // 1st-level data cache: 16 KByte, 8-way set associative, 64 byte line size
1468 			level[L1D] = CACHEINFO(1, 'D', 16, 1, 8, 1, 64, 32);
1469 			break;
1470 		case 0x66: // 1st-level data cache: 8 KByte, 4-way set associative, 64 byte line size
1471 			level[L1D] = CACHEINFO(1, 'D', 8, 1, 4, 1, 64, 32);
1472 			break;
1473 		case 0x67: // 1st-level data cache: 16 KByte, 4-way set associative, 64 byte line size
1474 			level[L1D] = CACHEINFO(1, 'D', 16, 1, 4, 1, 64, 64);
1475 			break;
1476 		case 0x68: // 1st-level data cache: 32 KByte, 4-way set associative, 64 byte line size
1477 			level[L1D] = CACHEINFO(1, 'D', 32, 1, 4, 1, 64, 128);
1478 			break;
1479 		case 0x77: // (sandpile) code L1 cache, 16 KB, 4 ways, 64 byte lines, sectored (IA-64)
1480 			level[L1I] = CACHEINFO(1, 'I', 16, 1, 4, 1, 64, 64);
1481 			break;
1482 		case 0x78: // 2nd-level cache: 1 MByte, 4-way set associative, 64byte line size
1483 			level[L2] = CACHEINFO(2, 'U', 1024, 1, 4, 1, 64, 4096);
1484 			break;
1485 		case 0x79: // 2nd-level cache: 128 KByte, 8-way set associative, 64 byte line size, 2 lines per sector
1486 			level[L2] = CACHEINFO(2, 'U', 128, 1, 8, 2, 64, 128);
1487 			break;
1488 		case 0x7A: // 2nd-level cache: 256 KByte, 8-way set associative, 64 byte line size, 2 lines per sector
1489 			level[L2] = CACHEINFO(2, 'U', 256, 1, 8, 2, 64, 256);
1490 			break;
1491 		case 0x7B: // 2nd-level cache: 512 KByte, 8-way set associative, 64 byte line size, 2 lines per sector
1492 			level[L2] = CACHEINFO(2, 'U', 512, 1, 8, 2, 64, 512);
1493 			break;
1494 		case 0x7C: // 2nd-level cache: 1 MByte, 8-way set associative, 64 byte line size, 2 lines per sector
1495 			level[L2] = CACHEINFO(2, 'U', 1024, 1, 8, 2, 64, 1024);
1496 			break;
1497 		case 0x7D: // 2nd-level cache: 2 MByte, 8-way set associative, 64 byte line size
1498 			level[L2] = CACHEINFO(2, 'U', 2048, 1, 8, 1, 64, 4096);
1499 			break;
1500 		case 0x7E: // (sandpile) code and data L2 cache, 256 KB, 8 ways, 128 byte lines, sect. (IA-64)
1501 			level[L2] = CACHEINFO(2, 'U', 256, 1, 8, 1, 128, 256);
1502 			break;
1503 		case 0x7F: // 2nd-level cache: 512 KByte, 2-way set associative, 64-byte line size
1504 			level[L2] = CACHEINFO(2, 'U', 512, 1, 2, 1, 64, 4096);
1505 			break;
1506 		case 0x80: // 2nd-level cache: 512 KByte, 8-way set associative, 64-byte line size
1507 			level[L2] = CACHEINFO(2, 'U', 512, 1, 8, 1, 64, 1024);
1508 			break;
1509 		case 0x81: // (sandpile) code and data L2 cache, 128 KB, 8 ways, 32 byte lines
1510 			level[L2] = CACHEINFO(2, 'U', 128, 1, 8, 1, 32, 512);
1511 			break;
1512 		case 0x82: // 2nd-level cache: 256 KByte, 8-way set associative, 32 byte line size
1513 			level[L2] = CACHEINFO(2, 'U', 256, 1, 8, 1, 32, 1024);
1514 			break;
1515 		case 0x83: // 2nd-level cache: 512 KByte, 8-way set associative, 32 byte line size
1516 			level[L2] = CACHEINFO(2, 'U', 512, 1, 8, 1, 32, 2048);
1517 			break;
1518 		case 0x84: // 2nd-level cache: 1 MByte, 8-way set associative, 32 byte line size
1519 			level[L2] = CACHEINFO(2, 'U', 1024, 1, 8, 1, 32, 4096);
1520 			break;
1521 		case 0x85: // 2nd-level cache: 2 MByte, 8-way set associative, 32 byte line size
1522 			level[L2] = CACHEINFO(2, 'U', 2048, 1, 8, 1, 32, 8192);
1523 			break;
1524 		case 0x86: // 2nd-level cache: 512 KByte, 4-way set associative, 64 byte line size
1525 			level[L2] = CACHEINFO(2, 'U', 512, 1, 4, 1, 64, 2048);
1526 			break;
1527 		case 0x87: // 2nd-level cache: 1 MByte, 8-way set associative, 64 byte line size
1528 			level[L2] = CACHEINFO(2, 'U', 1024, 1, 8, 1, 64, 2048);
1529 			break;
1530 		case 0xD0: // 3rd-level cache: 512 KByte, 4-way set associative, 64 byte line size
1531 			level[L3] = CACHEINFO(3, 'U', 512, 1, 4, 1, 64, 2048);
1532 			break;
1533 		case 0xD1: // 3rd-level cache: 1 MByte, 4-way set associative, 64 byte line size
1534 			level[L3] = CACHEINFO(3, 'U', 1024, 1, 4, 1, 64, 4096);
1535 			break;
1536 		case 0xD2: // 3rd-level cache: 2 MByte, 4-way set associative, 64 byte line size
1537 			level[L3] = CACHEINFO(3, 'U', 2048, 1, 4, 1, 64, 8192);
1538 			break;
1539 		case 0xD6: // 3rd-level cache: 1 MByte, 8-way set associative, 64 byte line size
1540 			level[L3] = CACHEINFO(3, 'U', 1024, 1, 8, 1, 64, 2048);
1541 			break;
1542 		case 0xD7: // 3rd-level cache: 2 MByte, 8-way set associative, 64 byte line size
1543 			level[L3] = CACHEINFO(3, 'U', 2048, 1, 8, 1, 64, 4096);
1544 			break;
1545 		case 0xD8: // 3rd-level cache: 4 MByte, 8-way set associative, 64 byte line size
1546 			level[L3] = CACHEINFO(3, 'U', 4096, 1, 8, 1, 64, 8192);
1547 			break;
1548 		case 0xDC: // 3rd-level cache: 1.5 MByte, 12-way set associative, 64 byte line size
1549 			level[L3] = CACHEINFO(3, 'U', 1536, 1, 12, 1, 64, 2048);
1550 			break;
1551 		case 0xDD: // 3rd-level cache: 3 MByte, 12-way set associative, 64 byte line size
1552 			level[L3] = CACHEINFO(3, 'U', 3072, 1, 12, 1, 64, 4096);
1553 			break;
1554 		case 0xDE: // 3rd-level cache: 6 MByte, 12-way set associative, 64 byte line size
1555 			level[L3] = CACHEINFO(3, 'U', 6144, 1, 12, 1, 64, 8192);
1556 			break;
1557 		case 0xE2: // 3rd-level cache: 2 MByte, 16-way set associative, 64 byte line size
1558 			level[L3] = CACHEINFO(3, 'U', 2048, 1, 16, 1, 64, 2048);
1559 			break;
1560 		case 0xE3: // 3rd-level cache: 4 MByte, 16-way set associative, 64 byte line size
1561 			level[L3] = CACHEINFO(3, 'U', 4096, 1, 16, 1, 64, 4096);
1562 			break;
1563 		case 0xE4: // 3rd-level cache: 8 MByte, 16-way set associative, 64 byte line size
1564 			level[L3] = CACHEINFO(3, 'U', 8192, 1, 16, 1, 64, 8192);
1565 			break;
1566 		case 0xEA: // 3rd-level cache: 12MByte, 24-way set associative, 64 byte line size
1567 			level[L3] = CACHEINFO(3, 'U', 12288, 1, 24, 1, 64, 8192);
1568 			break;
1569 		case 0xEB: // 3rd-level cache: 18MByte, 24-way set associative, 64 byte line size
1570 			level[L3] = CACHEINFO(3, 'U', 18432, 1, 24, 1, 64, 12288);
1571 			break;
1572 		case 0xEC: // 3rd-level cache: 24MByte, 24-way set associative, 64 byte line size
1573 			level[L3] = CACHEINFO(3, 'U', 24576, 1, 24, 1, 64, 16384);
1574 			break;
1575 		default: continue;
1576 		}
1577 		
1578 		++levels;
1579 	}
1580 	with (info.cache) { // Some do not have L1I
1581 		if (level[0].level == 0) {
1582 			for (size_t i; i < levels; ++i) {
1583 				level[i] = level[i+1];
1584 			}
1585 			level[levels] = CACHEINFO.init;
1586 		}
1587 	}
1588 }
1589 
1590 version (TestCPUID02h) @system unittest {
1591 	import std.stdio : write, writeln, writef;
1592 	REGISTERS regs; // Celeron 0xf34
1593 	regs.eax = 0x605b5101;
1594 	regs.ebx = 0;
1595 	regs.ecx = 0;
1596 	regs.edx = 0x3c7040;
1597 	
1598 	CPUINFO info;
1599 	ddcpuid_leaf2(info, regs);
1600 	
1601 	writeln("TEST: CPUID.02h");
1602 	CACHEINFO *cache = void;
1603 	for (uint i; i < CACHE_MAX_LEVEL; ++i) {
1604 		cache = &info.cache.level[i];
1605 		writef("Level %u-%c   : %2ux %6u KiB, %u ways, %u parts, %u B, %u sets",
1606 			cache.level, cache.type, cache.sharedCores, cache.size,
1607 			cache.ways, cache.partitions, cache.lineSize, cache.sets
1608 		);
1609 		if (cache.features) {
1610 			write(',');
1611 			if (cache.features & BIT!(0)) write(" si"); // Self Initiative
1612 			if (cache.features & BIT!(1)) write(" fa"); // Fully Associative
1613 			if (cache.features & BIT!(2)) write(" nwbv"); // No Write-Back Validation
1614 			if (cache.features & BIT!(3)) write(" ci"); // Cache Inclusive
1615 			if (cache.features & BIT!(4)) write(" cci"); // Complex Cache Indexing
1616 		}
1617 		writeln;
1618 	}
1619 }
1620 
1621 pragma(inline, false)
1622 private
1623 void ddcpuid_leaf5(ref CPUINFO info, ref REGISTERS regs) {
1624 	info.extras.mwaitMin = regs.ax;
1625 	info.extras.mwaitMax = regs.bx;
1626 }
1627 
1628 pragma(inline, false)
1629 private
1630 void ddcpuid_leaf6(ref CPUINFO info, ref REGISTERS regs) {
1631 	switch (info.vendor.id) with (Vendor) {
1632 	case Intel:
1633 		info.tech.turboboost	= bit(regs.eax, 1);
1634 		info.tech.turboboost30	= bit(regs.eax, 14);
1635 		break;
1636 	default:
1637 	}
1638 	
1639 	info.sys.arat = bit(regs.eax, 2);
1640 }
1641 
1642 pragma(inline, false)
1643 private
1644 void ddcpuid_leaf7(ref CPUINFO info, ref REGISTERS regs) {
1645 	switch (info.vendor.id) with (Vendor) {
1646 	case Intel:
1647 		// EBX
1648 		info.sgx.supported	= bit(regs.ebx, 2);
1649 		info.memory.hle	= bit(regs.ebx, 4);
1650 		info.cache.invpcid	= bit(regs.ebx, 10);
1651 		info.memory.rtm	= bit(regs.ebx, 11);
1652 		info.avx.avx512f	= bit(regs.ebx, 16);
1653 		info.memory.smap	= bit(regs.ebx, 20);
1654 		info.avx.avx512er	= bit(regs.ebx, 27);
1655 		info.avx.avx512pf	= bit(regs.ebx, 26);
1656 		info.avx.avx512cd	= bit(regs.ebx, 28);
1657 		info.avx.avx512dq	= bit(regs.ebx, 17);
1658 		info.avx.avx512bw	= bit(regs.ebx, 30);
1659 		info.avx.avx512_ifma	= bit(regs.ebx, 21);
1660 		info.avx.avx512_vbmi	= regs.ebx >= BIT!(31);
1661 		// ECX
1662 		info.avx.avx512vl	= bit(regs.ecx, 1);
1663 		info.memory.pku	= bit(regs.ecx, 3);
1664 		info.memory.fsrepmov	= bit(regs.ecx, 4);
1665 		info.extensions.waitpkg	= bit(regs.ecx, 5);
1666 		info.avx.avx512_vbmi2	= bit(regs.ecx, 6);
1667 		info.security.cetSs	= bit(regs.ecx, 7);
1668 		info.avx.avx512_gfni	= bit(regs.ecx, 8);
1669 		info.avx.avx512_vaes	= bit(regs.ecx, 9);
1670 		info.avx.avx512_vnni	= bit(regs.ecx, 11);
1671 		info.avx.avx512_bitalg	= bit(regs.ecx, 12);
1672 		info.avx.avx512_vpopcntdq	= bit(regs.ecx, 14);
1673 		info.memory._5pl	= bit(regs.ecx, 16);
1674 		info.extras.cldemote	= bit(regs.ecx, 25);
1675 		info.extras.movdiri	= bit(regs.ecx, 27);
1676 		info.extras.movdir64b	= bit(regs.ecx, 28);
1677 		info.extras.enqcmd	= bit(regs.ecx, 29);
1678 		// EDX
1679 		info.avx.avx512_4vnniw	= bit(regs.edx, 2);
1680 		info.avx.avx512_4fmaps	= bit(regs.edx, 3);
1681 		info.misc.uintr	= bit(regs.edx, 5);
1682 		info.avx.avx512_vp2intersect	= bit(regs.edx, 8);
1683 		info.security.md_clear	= bit(regs.edx, 10);
1684 		info.extras.serialize	= bit(regs.edx, 14);
1685 		info.memory.tsxldtrk	= bit(regs.edx, 16);
1686 		info.extras.pconfig	= bit(regs.edx, 18);
1687 		info.security.cetIbt	= bit(regs.edx, 20);
1688 		info.amx.bf16	= bit(regs.edx, 22);
1689 		info.amx.enabled	= bit(regs.edx, 24);
1690 		info.amx.int8	= bit(regs.edx, 25);
1691 		info.security.ibrs = bit(regs.edx, 26);
1692 		info.security.stibp	= bit(regs.edx, 27);
1693 		info.security.l1dFlush	= bit(regs.edx, 28);
1694 		info.security.ia32_arch_capabilities	= bit(regs.edx, 29);
1695 		info.security.ssbd	= regs.edx >= BIT!(31);
1696 		break;
1697 	default:
1698 	}
1699 
1700 	// ebx
1701 	info.misc.fsgsbase	= bit(regs.ebx, 0);
1702 	info.extensions.bmi1	= bit(regs.ebx, 3);
1703 	info.avx.avx2	= bit(regs.ebx, 5);
1704 	info.memory.smep	= bit(regs.ebx, 7);
1705 	info.extensions.bmi2	= bit(regs.ebx, 8);
1706 	info.extras.rdseed	= bit(regs.ebx, 18);
1707 	info.extensions.adx	= bit(regs.ebx, 19);
1708 	info.cache.clflushopt	= bit(regs.ebx, 23);
1709 	info.extensions.sha	= bit(regs.ebx, 29);
1710 	// ecx
1711 	info.extras.rdpid	= bit(regs.ecx, 22);
1712 }
1713 
1714 pragma(inline, false)
1715 private
1716 void ddcpuid_leaf7sub1(ref CPUINFO info, ref REGISTERS regs) {
1717 	switch (info.vendor.id) with (Vendor) {
1718 	case Intel:
1719 		// a
1720 		info.avx.avx512_bf16	= bit(regs.eax, 5);
1721 		info.memory.lam	= bit(regs.eax, 26);
1722 		break;
1723 	default:
1724 	}
1725 }
1726 
1727 pragma(inline, false)
1728 private
1729 void ddcpuid_leafD(ref CPUINFO info, ref REGISTERS regs) {
1730 	switch (info.vendor.id) with (Vendor) {
1731 	case Intel:
1732 		info.amx.xtilecfg	= bit(regs.eax, 17);
1733 		info.amx.xtiledata	= bit(regs.eax, 18);
1734 		break;
1735 	default:
1736 	}
1737 }
1738 
1739 pragma(inline, false)
1740 private
1741 void ddcpuid_leafDsub1(ref CPUINFO info, ref REGISTERS regs) {
1742 	switch (info.vendor.id) with (Vendor) {
1743 	case Intel:
1744 		info.amx.xfd	= bit(regs.eax, 18);
1745 		break;
1746 	default:
1747 	}
1748 }
1749 
1750 pragma(inline, false)
1751 private
1752 void ddcpuid_leaf12(ref CPUINFO info, ref REGISTERS regs) {
1753 	switch (info.vendor.id) with (Vendor) {
1754 	case Intel:
1755 		info.sgx.sgx1 = bit(regs.al, 0);
1756 		info.sgx.sgx2 = bit(regs.al, 1);
1757 		info.sgx.maxSize   = regs.dl;
1758 		info.sgx.maxSize64 = regs.dh;
1759 		break;
1760 	default:
1761 	}
1762 }
1763 
1764 pragma(inline, false)
1765 private
1766 void ddcpuid_leaf4000_0001(ref CPUINFO info, ref REGISTERS regs) {
1767 //	switch (info.virt.vendor.id) with (VirtVendor) {
1768 //	case KVM:
1769 		info.virt.kvm.feature_clocksource	= bit(regs.eax, 0);
1770 		info.virt.kvm.feature_nop_io_delay	= bit(regs.eax, 1);
1771 		info.virt.kvm.feature_mmu_op	= bit(regs.eax, 2);
1772 		info.virt.kvm.feature_clocksource2	= bit(regs.eax, 3);
1773 		info.virt.kvm.feature_async_pf	= bit(regs.eax, 4);
1774 		info.virt.kvm.feature_steal_time	= bit(regs.eax, 5);
1775 		info.virt.kvm.feature_pv_eoi	= bit(regs.eax, 6);
1776 		info.virt.kvm.feature_pv_unhault	= bit(regs.eax, 7);
1777 		info.virt.kvm.feature_pv_tlb_flush	= bit(regs.eax, 9);
1778 		info.virt.kvm.feature_async_pf_vmexit	= bit(regs.eax, 10);
1779 		info.virt.kvm.feature_pv_send_ipi	= bit(regs.eax, 11);
1780 		info.virt.kvm.feature_pv_poll_control	= bit(regs.eax, 12);
1781 		info.virt.kvm.feature_pv_sched_yield	= bit(regs.eax, 13);
1782 		info.virt.kvm.feature_clocsource_stable_bit	= bit(regs.eax, 24);
1783 		info.virt.kvm.hint_realtime	= bit(regs.edx, 0);
1784 //		break;
1785 //	default:
1786 //	}
1787 }
1788 
1789 pragma(inline, false)
1790 private
1791 void ddcpuid_leaf4000_0002(ref CPUINFO info, ref REGISTERS regs) {
1792 //	switch (info.virt.vendor.id) with (VirtVendor) {
1793 //	case HyperV:
1794 		info.virt.hv.guest_minor	= cast(ubyte)(regs.eax >> 24);
1795 		info.virt.hv.guest_service	= cast(ubyte)(regs.eax >> 16);
1796 		info.virt.hv.guest_build	= regs.ax;
1797 		info.virt.hv.guest_opensource	= regs.edx >= BIT!(31);
1798 		info.virt.hv.guest_vendor_id	= (regs.edx >> 16) & 0xFFF;
1799 		info.virt.hv.guest_os	= regs.dh;
1800 		info.virt.hv.guest_major	= regs.dl;
1801 //		break;
1802 //	default:
1803 //	}
1804 }
1805 
1806 pragma(inline, false)
1807 private
1808 void ddcpuid_leaf4000_0003(ref CPUINFO info, ref REGISTERS regs) {
1809 //	switch (info.virt.vendor.id) with (VirtVendor) {
1810 //	case HyperV:
1811 		info.virt.hv.base_feat_vp_runtime_msr	= bit(regs.eax, 0);
1812 		info.virt.hv.base_feat_part_time_ref_count_msr	= bit(regs.eax, 1);
1813 		info.virt.hv.base_feat_basic_synic_msrs	= bit(regs.eax, 2);
1814 		info.virt.hv.base_feat_stimer_msrs	= bit(regs.eax, 3);
1815 		info.virt.hv.base_feat_apic_access_msrs	= bit(regs.eax, 4);
1816 		info.virt.hv.base_feat_hypercall_msrs	= bit(regs.eax, 5);
1817 		info.virt.hv.base_feat_vp_id_msr	= bit(regs.eax, 6);
1818 		info.virt.hv.base_feat_virt_sys_reset_msr	= bit(regs.eax, 7);
1819 		info.virt.hv.base_feat_stat_pages_msr	= bit(regs.eax, 8);
1820 		info.virt.hv.base_feat_part_ref_tsc_msr	= bit(regs.eax, 9);
1821 		info.virt.hv.base_feat_guest_idle_state_msr	= bit(regs.eax, 10);
1822 		info.virt.hv.base_feat_timer_freq_msrs	= bit(regs.eax, 11);
1823 		info.virt.hv.base_feat_debug_msrs	= bit(regs.eax, 12);
1824 		info.virt.hv.part_flags_create_part	= bit(regs.ebx, 0);
1825 		info.virt.hv.part_flags_access_part_id	= bit(regs.ebx, 1);
1826 		info.virt.hv.part_flags_access_memory_pool	= bit(regs.ebx, 2);
1827 		info.virt.hv.part_flags_adjust_msg_buffers	= bit(regs.ebx, 3);
1828 		info.virt.hv.part_flags_post_msgs	= bit(regs.ebx, 4);
1829 		info.virt.hv.part_flags_signal_events	= bit(regs.ebx, 5);
1830 		info.virt.hv.part_flags_create_port	= bit(regs.ebx, 6);
1831 		info.virt.hv.part_flags_connect_port	= bit(regs.ebx, 7);
1832 		info.virt.hv.part_flags_access_stats	= bit(regs.ebx, 8);
1833 		info.virt.hv.part_flags_debugging	= bit(regs.ebx, 11);
1834 		info.virt.hv.part_flags_cpu_mgmt	= bit(regs.ebx, 12);
1835 		info.virt.hv.part_flags_cpu_profiler	= bit(regs.ebx, 13);
1836 		info.virt.hv.part_flags_expanded_stack_walk	= bit(regs.ebx, 14);
1837 		info.virt.hv.part_flags_access_vsm	= bit(regs.ebx, 16);
1838 		info.virt.hv.part_flags_access_vp_regs	= bit(regs.ebx, 17);
1839 		info.virt.hv.part_flags_extended_hypercalls	= bit(regs.ebx, 20);
1840 		info.virt.hv.part_flags_start_vp	= bit(regs.ebx, 21);
1841 		info.virt.hv.pm_max_cpu_power_state_c0	= bit(regs.ecx, 0);
1842 		info.virt.hv.pm_max_cpu_power_state_c1	= bit(regs.ecx, 1);
1843 		info.virt.hv.pm_max_cpu_power_state_c2	= bit(regs.ecx, 2);
1844 		info.virt.hv.pm_max_cpu_power_state_c3	= bit(regs.ecx, 3);
1845 		info.virt.hv.pm_hpet_reqd_for_c3	= bit(regs.ecx, 4);
1846 		info.virt.hv.misc_feat_mwait	= bit(regs.eax, 0);
1847 		info.virt.hv.misc_feat_guest_debugging	= bit(regs.eax, 1);
1848 		info.virt.hv.misc_feat_perf_mon	= bit(regs.eax, 2);
1849 		info.virt.hv.misc_feat_pcpu_dyn_part_event	= bit(regs.eax, 3);
1850 		info.virt.hv.misc_feat_xmm_hypercall_input	= bit(regs.eax, 4);
1851 		info.virt.hv.misc_feat_guest_idle_state	= bit(regs.eax, 5);
1852 		info.virt.hv.misc_feat_hypervisor_sleep_state	= bit(regs.eax, 6);
1853 		info.virt.hv.misc_feat_query_numa_distance	= bit(regs.eax, 7);
1854 		info.virt.hv.misc_feat_timer_freq	= bit(regs.eax, 8);
1855 		info.virt.hv.misc_feat_inject_synmc_xcpt	= bit(regs.eax, 9);
1856 		info.virt.hv.misc_feat_guest_crash_msrs	= bit(regs.eax, 10);
1857 		info.virt.hv.misc_feat_debug_msrs	= bit(regs.eax, 11);
1858 		info.virt.hv.misc_feat_npiep1	= bit(regs.eax, 12);
1859 		info.virt.hv.misc_feat_disable_hypervisor	= bit(regs.eax, 13);
1860 		info.virt.hv.misc_feat_ext_gva_range_for_flush_va_list	= bit(regs.eax, 14);
1861 		info.virt.hv.misc_feat_hypercall_output_xmm	= bit(regs.eax, 15);
1862 		info.virt.hv.misc_feat_sint_polling_mode	= bit(regs.eax, 17);
1863 		info.virt.hv.misc_feat_hypercall_msr_lock	= bit(regs.eax, 18);
1864 		info.virt.hv.misc_feat_use_direct_synth_msrs	= bit(regs.eax, 19);
1865 //		break;
1866 //	default:
1867 //	}
1868 }
1869 
1870 pragma(inline, false)
1871 private
1872 void ddcpuid_leaf4000_0004(ref CPUINFO info, ref REGISTERS regs) {
1873 //	switch (info.virt.vendor.id) with (VirtVendor) {
1874 //	case HyperV:
1875 		info.virt.hv.hint_hypercall_for_process_switch	= bit(regs.eax, 0);
1876 		info.virt.hv.hint_hypercall_for_tlb_flush	= bit(regs.eax, 1);
1877 		info.virt.hv.hint_hypercall_for_tlb_shootdown	= bit(regs.eax, 2);
1878 		info.virt.hv.hint_msr_for_apic_access	= bit(regs.eax, 3);
1879 		info.virt.hv.hint_msr_for_sys_reset	= bit(regs.eax, 4);
1880 		info.virt.hv.hint_relax_time_checks	= bit(regs.eax, 5);
1881 		info.virt.hv.hint_dma_remapping	= bit(regs.eax, 6);
1882 		info.virt.hv.hint_interrupt_remapping	= bit(regs.eax, 7);
1883 		info.virt.hv.hint_x2apic_msrs	= bit(regs.eax, 8);
1884 		info.virt.hv.hint_deprecate_auto_eoi	= bit(regs.eax, 9);
1885 		info.virt.hv.hint_synth_cluster_ipi_hypercall	= bit(regs.eax, 10);
1886 		info.virt.hv.hint_ex_proc_masks_interface	= bit(regs.eax, 11);
1887 		info.virt.hv.hint_nested_hyperv	= bit(regs.eax, 12);
1888 		info.virt.hv.hint_int_for_mbec_syscalls	= bit(regs.eax, 13);
1889 		info.virt.hv.hint_nested_enlightened_vmcs_interface	= bit(regs.eax, 14);
1890 //		break;
1891 //	default:
1892 //	}
1893 }
1894 
1895 pragma(inline, false)
1896 private
1897 void ddcpuid_leaf4000_0006(ref CPUINFO info, ref REGISTERS regs) {
1898 //	switch (info.virt.vendor.id) with (VirtVendor) {
1899 //	case HyperV:
1900 		info.virt.hv.host_feat_avic	= bit(regs.eax, 0);
1901 		info.virt.hv.host_feat_msr_bitmap	= bit(regs.eax, 1);
1902 		info.virt.hv.host_feat_perf_counter	= bit(regs.eax, 2);
1903 		info.virt.hv.host_feat_nested_paging	= bit(regs.eax, 3);
1904 		info.virt.hv.host_feat_dma_remapping	= bit(regs.eax, 4);
1905 		info.virt.hv.host_feat_interrupt_remapping	= bit(regs.eax, 5);
1906 		info.virt.hv.host_feat_mem_patrol_scrubber	= bit(regs.eax, 6);
1907 		info.virt.hv.host_feat_dma_prot_in_use	= bit(regs.eax, 7);
1908 		info.virt.hv.host_feat_hpet_requested	= bit(regs.eax, 8);
1909 		info.virt.hv.host_feat_stimer_volatile	= bit(regs.eax, 9);
1910 //		break;
1911 //	default:
1912 //	}
1913 }
1914 
1915 pragma(inline, false)
1916 private
1917 void ddcpuid_leaf4000_0010(ref CPUINFO info, ref REGISTERS regs) {
1918 //	switch (info.virt.vendor.id) with (VirtVendor) {
1919 //	case VBoxMin: // VBox Minimal
1920 		info.virt.vbox.tsc_freq_khz = regs.eax;
1921 		info.virt.vbox.apic_freq_khz = regs.ebx;
1922 //		break;
1923 //	default:
1924 //	}
1925 }
1926 
1927 pragma(inline, false)
1928 private
1929 void ddcpuid_leaf8000_0001(ref CPUINFO info, ref REGISTERS regs) {
1930 	switch (info.vendor.id) with (Vendor) {
1931 	case AMD:
1932 		// ecx
1933 		info.virt.available	= bit(regs.ecx, 2);
1934 		info.sys.x2apic	= bit(regs.ecx, 3);
1935 		info.sse.sse4a	= bit(regs.ecx, 6);
1936 		info.extensions.xop	= bit(regs.ecx, 11);
1937 		info.extras.skinit	= bit(regs.ecx, 12);
1938 		info.extensions.fma4	= bit(regs.ecx, 16);
1939 		info.extensions.tbm	= bit(regs.ecx, 21);
1940 		// edx
1941 		info.extensions.mmxExtended	= bit(regs.edx, 22);
1942 		info.extensions._3DNowExtended	= bit(regs.edx, 30);
1943 		info.extensions._3DNow	= regs.edx >= BIT!(31);
1944 		break;
1945 	default:
1946 	}
1947 	
1948 	// ecx
1949 	info.extensions.lahf64	= bit(regs.ecx, 0);
1950 	info.extras.lzcnt	= bit(regs.ecx, 5);
1951 	info.cache.prefetchw	= bit(regs.ecx, 8);
1952 	info.extras.monitorx	= bit(regs.ecx, 29);
1953 	// edx
1954 	info.extras.syscall	= bit(regs.edx, 11);
1955 	info.memory.nx	= bit(regs.edx, 20);
1956 	info.memory.page1gb	= bit(regs.edx, 26);
1957 	info.extras.rdtscp	= bit(regs.edx, 27);
1958 	info.extensions.x86_64	= bit(regs.edx, 29);
1959 }
1960 
1961 pragma(inline, false)
1962 private
1963 void ddcpuid_leaf8000_0007(ref CPUINFO info, ref REGISTERS regs) {
1964 	switch (info.vendor.id) with (Vendor) {
1965 	case Intel:
1966 		info.extras.rdseed	= bit(regs.ebx, 28);
1967 		break;
1968 	case AMD:
1969 		info.sys.tm	= bit(regs.edx, 4);
1970 		info.tech.turboboost	= bit(regs.edx, 9);
1971 		break;
1972 	default:
1973 	}
1974 	
1975 	info.extras.rdtscInvariant	= bit(regs.edx, 8);
1976 }
1977 
1978 pragma(inline, false)
1979 private
1980 void ddcpuid_leaf8000_0008(ref CPUINFO info, ref REGISTERS regs) {
1981 	switch (info.vendor.id) with (Vendor) {
1982 	case Intel:
1983 		info.cache.wbnoinvd	= bit(regs.ebx, 9);
1984 		break;
1985 	case AMD:
1986 		info.security.ibpb	= bit(regs.ebx, 12);
1987 		info.security.ibrs	= bit(regs.ebx, 14);
1988 		info.security.stibp	= bit(regs.ebx, 15);
1989 		info.security.ibrsAlwaysOn	= bit(regs.ebx, 16);
1990 		info.security.stibpAlwaysOn	= bit(regs.ebx, 17);
1991 		info.security.ibrsPreferred	= bit(regs.ebx, 18);
1992 		info.security.ssbd	= bit(regs.ebx, 24);
1993 		break;
1994 	default:
1995 	}
1996 	
1997 	info.memory.physBits = regs.al;
1998 	info.memory.lineBits = regs.ah;
1999 }
2000 
2001 pragma(inline, false)
2002 private
2003 void ddcpuid_leaf8000_000A(ref CPUINFO info, ref REGISTERS regs) {
2004 	switch (info.vendor.id) {
2005 	case Vendor.AMD:
2006 		info.virt.version_	= regs.al; // EAX[7:0]
2007 		info.virt.apicv	= bit(regs.edx, 13);
2008 		break;
2009 	default:
2010 	}
2011 }
2012 
2013 pragma(inline, false)
2014 private
2015 void ddcpuid_topology(ref CPUINFO info) {
2016 	ushort sc = void;	/// raw cores shared across cache level
2017 	ushort crshrd = void;	/// actual count of shared cores
2018 	ubyte type = void;	/// cache type
2019 	ubyte mids = void;	/// maximum IDs to this cache
2020 	REGISTERS regs = void;	/// registers
2021 	
2022 	info.cache.levels = 0;
2023 	CACHEINFO *ca = cast(CACHEINFO*)info.cache.level;
2024 	
2025 	//TODO: Make 1FH/BH/4H/etc. functions.
2026 	switch (info.vendor.id) with (Vendor) {
2027 	case Intel:
2028 		if (info.maxLeaf >= 0x1f) goto L_CACHE_INTEL_1FH;
2029 		if (info.maxLeaf >= 0xb)  goto L_CACHE_INTEL_BH;
2030 		if (info.maxLeaf >= 4)    goto L_CACHE_INTEL_4H;
2031 		// Celeron 0xf34 has maxLeaf=03h and ext=8000_0008h
2032 		if (info.maxLeaf >= 2)    goto L_CACHE_INTEL_2H;
2033 		if (info.maxLeafExtended >= 0x8000_0005) goto L_CACHE_AMD_EXT_5H;
2034 		break;
2035 		
2036 L_CACHE_INTEL_1FH:
2037 		//TODO: Support levels 3,4,5 in CPUID.1FH
2038 		//      (Module, Tile, and Die)
2039 		ddcpuid_id(regs, 0x1f, 1); // Cores (logical)
2040 		info.cores.logical = regs.bx;
2041 		
2042 		ddcpuid_id(regs, 0x1f, 0); // SMT (architectural states per core)
2043 		info.cores.physical = cast(ushort)(info.cores.logical / regs.bx);
2044 		
2045 		goto L_CACHE_INTEL_4H;
2046 		
2047 L_CACHE_INTEL_BH:
2048 		ddcpuid_id(regs, 0xb, 1); // Cores (logical)
2049 		info.cores.logical = regs.bx;
2050 		
2051 		ddcpuid_id(regs, 0xb, 0); // SMT (architectural states per core)
2052 		info.cores.physical = cast(ushort)(info.cores.logical / regs.bx);
2053 		
2054 L_CACHE_INTEL_4H:
2055 		ddcpuid_id(regs, 4, info.cache.levels);
2056 		
2057 		type = regs.eax & CACHE_MASK; // EAX[4:0]
2058 		if (type == 0 || info.cache.levels >= CACHE_MAX_LEVEL) return;
2059 		
2060 		ca.type = CACHE_TYPE[type];
2061 		ca.level = regs.al >> 5;
2062 		ca.lineSize = (regs.bx & 0xfff) + 1; // bits 11-0
2063 		ca.partitions = ((regs.ebx >> 12) & 0x3ff) + 1; // bits 21-12
2064 		ca.ways = ((regs.ebx >> 22) + 1); // bits 31-22
2065 		ca.sets = regs.ecx + 1;
2066 		if (regs.eax & BIT!(8)) ca.features = 1;
2067 		if (regs.eax & BIT!(9)) ca.features |= BIT!(1);
2068 		if (regs.edx & BIT!(0)) ca.features |= BIT!(2);
2069 		if (regs.edx & BIT!(1)) ca.features |= BIT!(3);
2070 		if (regs.edx & BIT!(2)) ca.features |= BIT!(4);
2071 		ca.size = (ca.sets * ca.lineSize * ca.partitions * ca.ways) >> 10;
2072 		
2073 		mids = (regs.eax >> 26) + 1;	// EAX[31:26]
2074 		
2075 		if (info.cores.logical == 0) { // skip if already populated
2076 			info.cores.logical = mids;
2077 			info.cores.physical = info.tech.htt ? mids >> 1 : mids;
2078 		}
2079 		
2080 		crshrd = (((regs.eax >> 14) & 2047) + 1);	// EAX[25:14]
2081 		sc = cast(ushort)(info.cores.logical / crshrd); // cast for ldc 0.17.1
2082 		ca.sharedCores = sc ? sc : 1;
2083 		version (Trace) trace("intel.4h mids=%u shared=%u crshrd=%u sc=%u",
2084 			mids, ca.sharedCores, crshrd, sc);
2085 		
2086 		++info.cache.levels; ++ca;
2087 		goto L_CACHE_INTEL_4H;
2088 
2089 L_CACHE_INTEL_2H:
2090 		ddcpuid_id(regs, 2);
2091 		ddcpuid_leaf2(info, regs);
2092 		break;
2093 	case AMD:
2094 		if (info.maxLeafExtended >= 0x8000_001D) goto L_CACHE_AMD_EXT_1DH;
2095 		if (info.maxLeafExtended >= 0x8000_0005) goto L_CACHE_AMD_EXT_5H;
2096 		break;
2097 		
2098 		/*if (info.maxLeafExtended < 0x8000_001e) goto L_AMD_TOPOLOGY_EXT_8H;
2099 		
2100 		ddcpuid_id(regs, 0x8000_0001);
2101 		
2102 		if (regs.ecx & BIT!(22)) { // Topology extensions support
2103 			ddcpuid_id(regs, 0x8000_001e);
2104 			
2105 			info.cores.logical = regs.ch + 1;
2106 			info.cores.physical = regs.dh & 7;
2107 			goto L_AMD_CACHE;
2108 		}*/
2109 		
2110 /*L_AMD_TOPOLOGY_EXT_8H:
2111 		// See APM Volume 3 Appendix E.5
2112 		// For some reason, CPUID Fn8000_001E_EBX is not mentioned there
2113 		ddcpuid_id(regs, 0x8000_0008);
2114 		
2115 		type = regs.cx >> 12; // ApicIdSize
2116 		
2117 		if (type) { // Extended
2118 			info.cores.physical = regs.cl + 1;
2119 			info.cores.logical = cast(ushort)(1 << type);
2120 		} else { // Legacy
2121 			info.cores.logical = info.cores.physical = regs.cl + 1;
2122 		}*/
2123 		
2124 		//
2125 		// AMD newer cache method
2126 		//
2127 		
2128 L_CACHE_AMD_EXT_1DH: // Almost the same as Intel's
2129 		ddcpuid_id(regs, 0x8000_001d, info.cache.levels);
2130 		
2131 		type = regs.eax & CACHE_MASK; // EAX[4:0]
2132 		if (type == 0 || info.cache.levels >= CACHE_MAX_LEVEL) return;
2133 		
2134 		ca.type = CACHE_TYPE[type];
2135 		ca.level = (regs.eax >> 5) & 7;
2136 		ca.lineSize = (regs.ebx & 0xfff) + 1;
2137 		ca.partitions = ((regs.ebx >> 12) & 0x3ff) + 1;
2138 		ca.ways = (regs.ebx >> 22) + 1;
2139 		ca.sets = regs.ecx + 1;
2140 		if (regs.eax & BIT!(8)) ca.features = 1;
2141 		if (regs.eax & BIT!(9)) ca.features |= BIT!(1);
2142 		if (regs.edx & BIT!(0)) ca.features |= BIT!(2);
2143 		if (regs.edx & BIT!(1)) ca.features |= BIT!(3);
2144 		ca.size = (ca.sets * ca.lineSize * ca.partitions * ca.ways) >> 10;
2145 		
2146 		crshrd = (((regs.eax >> 14) & 0xfff) + 1); // bits 25-14
2147 		sc = cast(ushort)(info.sys.maxApicId / crshrd); // cast for ldc 0.17.1
2148 		ca.sharedCores = sc ? sc : 1;
2149 		
2150 		if (info.cores.logical == 0) with (info.cores) { // skip if already populated
2151 			logical = info.sys.maxApicId;
2152 			physical = info.tech.htt ? logical >> 1 : info.sys.maxApicId;
2153 		}
2154 		
2155 		version (Trace) trace("amd.8000_001Dh mids=%u shared=%u crshrd=%u sc=%u",
2156 			mids, ca.sharedCores, crshrd, sc);
2157 		
2158 		++info.cache.levels; ++ca;
2159 		goto L_CACHE_AMD_EXT_1DH;
2160 		
2161 		//
2162 		// AMD legacy cache
2163 		//
2164 		
2165 L_CACHE_AMD_EXT_5H:
2166 		ddcpuid_id(regs, 0x8000_0005);
2167 		
2168 		info.cache.level[0].level = 1; // L1-D
2169 		info.cache.level[0].type = 'D'; // data
2170 		info.cache.level[0].size = regs.ecx >> 24;
2171 		info.cache.level[0].ways = cast(ubyte)(regs.ecx >> 16);
2172 		info.cache.level[0].lines = regs.ch;
2173 		info.cache.level[0].lineSize = regs.cl;
2174 		info.cache.level[0].sets = 1;
2175 		
2176 		info.cache.level[1].level = 1; // L1-I
2177 		info.cache.level[1].type = 'I'; // instructions
2178 		info.cache.level[1].size = regs.edx >> 24;
2179 		info.cache.level[1].ways = cast(ubyte)(regs.edx >> 16);
2180 		info.cache.level[1].lines = regs.dh;
2181 		info.cache.level[1].lineSize = regs.dl;
2182 		info.cache.level[1].sets = 1;
2183 		
2184 		info.cache.levels = 2;
2185 		
2186 		if (info.maxLeafExtended < 0x8000_0006)
2187 			return; // No L2/L3
2188 		
2189 		// See Table E-4. L2/L3 Cache and TLB Associativity Field Encoding
2190 		static immutable ubyte[16] _amd_cache_ways = [
2191 			// 7h is reserved
2192 			// 9h mentions 8000_001D but that's already supported
2193 			0, 1, 2, 3, 4, 6, 8, 0, 16, 0, 32, 48, 64, 96, 128, 255
2194 		];
2195 		
2196 		ddcpuid_id(regs, 0x8000_0006);
2197 		
2198 		type = regs.cx >> 12; // amd_ways_l2
2199 		if (type) {
2200 			info.cache.level[2].level = 2;  // L2
2201 			info.cache.level[2].type = 'U'; // unified
2202 			info.cache.level[2].size = regs.ecx >> 16;
2203 			info.cache.level[2].ways = _amd_cache_ways[type];
2204 			info.cache.level[2].lines = regs.ch & 0xf;
2205 			info.cache.level[2].lineSize = regs.cl;
2206 			info.cache.level[2].sets = 1;
2207 			info.cache.levels = 3;
2208 			
2209 			type = regs.dx >> 12; // amd_ways_l3
2210 			if (type) {
2211 				info.cache.level[3].level = 3;  // L3
2212 				info.cache.level[3].type = 'U'; // unified
2213 				info.cache.level[3].size = ((regs.edx >> 18) + 1) << 9;
2214 				info.cache.level[3].ways = _amd_cache_ways[type];
2215 				info.cache.level[3].lines = regs.dh & 0xf;
2216 				info.cache.level[3].lineSize = regs.dl & 0x7F;
2217 				info.cache.level[3].sets = 1;
2218 				info.cache.levels = 4;
2219 			}
2220 		}
2221 		return;
2222 	default:
2223 	}
2224 	
2225 	with (info) cores.physical = cores.logical = 1;
2226 }
2227 
2228 private struct LeafInfo {
2229 	uint leaf;
2230 	uint sub;
2231 	void function(ref CPUINFO, ref REGISTERS) func;
2232 }
2233 private struct LeafExtInfo {
2234 	uint leaf;
2235 	void function(ref CPUINFO, ref REGISTERS) func;
2236 }
2237 
2238 /// Fetch CPU information.
2239 /// Params: info = CPUINFO structure
2240 pragma(inline, false)
2241 void ddcpuid_cpuinfo(ref CPUINFO info) {
2242 	static immutable LeafInfo[] regulars = [
2243 		{ 0x1,	0,	&ddcpuid_leaf1 },	// Sets brand index
2244 		{ 0x5,	0,	&ddcpuid_leaf5 },
2245 		{ 0x6,	0,	&ddcpuid_leaf6 },
2246 		{ 0x7,	0,	&ddcpuid_leaf7 },
2247 		{ 0x7,	1,	&ddcpuid_leaf7sub1 },
2248 		{ 0xd,	0,	&ddcpuid_leafD },
2249 		{ 0xd,	1,	&ddcpuid_leafDsub1 },
2250 		{ 0x12,	0,	&ddcpuid_leaf12 },
2251 	];
2252 	static immutable LeafExtInfo[] extended = [
2253 		{ 0x8000_0001,	&ddcpuid_leaf8000_0001 },
2254 		{ 0x8000_0007,	&ddcpuid_leaf8000_0007 },
2255 		{ 0x8000_0008,	&ddcpuid_leaf8000_0008 },
2256 		{ 0x8000_000a,	&ddcpuid_leaf8000_000A },
2257 	];
2258 	REGISTERS regs = void;	/// registers
2259 	
2260 	ddcpuid_vendor(info.vendor.string_);
2261 	info.vendor.id = ddcpuid_vendor_id(info.vendor);
2262 	
2263 	foreach (ref immutable(LeafInfo) l; regulars) {
2264 		if (l.leaf > info.maxLeaf) break;
2265 		
2266 		ddcpuid_id(regs, l.leaf, l.sub);
2267 		l.func(info, regs);
2268 	}
2269 	
2270 	// Paravirtualization leaves
2271 	if (info.maxLeafVirt >= 0x4000_0000) {
2272 		ddcpuid_virt_vendor(info.virt.vendor.string_);
2273 		info.virt.vendor.id = ddcpuid_virt_vendor_id(info.virt.vendor);
2274 		
2275 		switch (info.virt.vendor.id) with (VirtVendor) {
2276 		case KVM:
2277 			ddcpuid_id(regs, 0x4000_0001);
2278 			ddcpuid_leaf4000_0001(info, regs);
2279 			break;
2280 		case HyperV:
2281 			ddcpuid_id(regs, 0x4000_0002);
2282 			ddcpuid_leaf4000_0002(info, regs);
2283 			ddcpuid_id(regs, 0x4000_0003);
2284 			ddcpuid_leaf4000_0003(info, regs);
2285 			ddcpuid_id(regs, 0x4000_0004);
2286 			ddcpuid_leaf4000_0004(info, regs);
2287 			ddcpuid_id(regs, 0x4000_0006);
2288 			ddcpuid_leaf4000_0006(info, regs);
2289 			break;
2290 		case VBoxMin:
2291 			ddcpuid_id(regs, 0x4000_0010);
2292 			ddcpuid_leaf4000_0010(info, regs);
2293 			break;
2294 		default:
2295 		}
2296 	}
2297 	
2298 	// Extended leaves
2299 	if (info.maxLeafExtended >= 0x8000_0000) {
2300 		foreach (ref immutable(LeafExtInfo) l; extended) {
2301 			if (l.leaf > info.maxLeafExtended) break;
2302 			
2303 			ddcpuid_id(regs, l.leaf);
2304 			l.func(info, regs);
2305 		}
2306 	}
2307 	
2308 	ddcpuid_model_string(info); // Sets brand string
2309 	ddcpuid_topology(info);	 // Sets core/thread/cache topology
2310 }
2311 
2312 const(char) *ddcpuid_baseline(ref CPUINFO info) {
2313 	if (info.extensions.x86_64 == false) {
2314 		if (info.family >= 6) // Pentium Pro / II
2315 			return "i686";
2316 		// NOTE: K7 is still family 5 and didn't have SSE2.
2317 		return info.family == 5 ? "i586" : "i486"; // Pentium / MMX
2318 	}
2319 	
2320 	// v4
2321 	if (info.avx.avx512f && info.avx.avx512bw &&
2322 		info.avx.avx512cd && info.avx.avx512dq &&
2323 		info.avx.avx512vl) {
2324 		return "x86-64-v4";
2325 	}
2326 	
2327 	// v3
2328 	if (info.avx.avx2 && info.avx.avx &&
2329 		info.extensions.bmi2 && info.extensions.bmi1 &&
2330 		info.extensions.f16c && info.extensions.fma3 &&
2331 		info.extras.lzcnt && info.extras.movbe &&
2332 		info.extras.osxsave) {
2333 		return "x86-64-v3";
2334 	}
2335 	
2336 	// v2
2337 	if (info.sse.sse42 && info.sse.sse41 &&
2338 		info.sse.ssse3 && info.sse.sse3 &&
2339 		info.extensions.lahf64 && info.extras.popcnt &&
2340 		info.extras.cmpxchg16b) {
2341 		return "x86-64-v2";
2342 	}
2343 	
2344 	// baseline (v1)
2345 	/*if (info.sse.sse2 && info.sse.sse &&
2346 		info.extensions.mmx && info.extras.fxsr &&
2347 		info.extras.cmpxchg8b && info.extras.cmov &&
2348 		info.extensions.fpu && info.extras.syscall) {
2349 		return "x86-64";
2350 	}*/
2351 	
2352 	return "x86-64"; // v1 anyway
2353 }