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-rc";	/// 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 pragma(inline, false)
1172 private
1173 void ddcpuid_model_string(ref CPUINFO info) {
1174 	switch (info.vendor.id) with (Vendor) {
1175 	case Intel:
1176 		// Brand string
1177 		if (info.maxLeafExtended >= 0x8000_0004)
1178 			ddcpuid_extended_brand(info.brandString);
1179 		else if (info.brandIndex)
1180 			ddcpuid_intel_brand_index(info, info.brandIndex);
1181 		else
1182 			ddcpuid_intel_brand_family(info);
1183 		return;
1184 	case AMD, VIA:
1185 		// Brand string
1186 		// NOTE: AMD processor never supported the string table.
1187 		//       The Am486DX4 and Am5x86 processors do not support the extended brand string.
1188 		//       The K5 model 0 does not support the extended brand string.
1189 		//       The K5 model 1, 2, and 3 support the extended brand string.
1190 		if (info.maxLeafExtended >= 0x8000_0004)
1191 			ddcpuid_extended_brand(info.brandString);
1192 		else
1193 			ddcpuid_amd_brand_family(info);
1194 		return;
1195 	default:
1196 		strcpy48(info.brandString, "Unknown");
1197 		return;
1198 	}
1199 }
1200 
1201 pragma(inline, false)
1202 private
1203 void ddcpuid_leaf1(ref CPUINFO info, ref REGISTERS regs) {
1204 	// EAX
1205 	info.identifier = regs.eax;
1206 	info.stepping   = regs.eax & 15;       // EAX[3:0]
1207 	info.modelBase  = regs.eax >>  4 & 15; // EAX[7:4]
1208 	info.familyBase = regs.eax >>  8 & 15; // EAX[11:8]
1209 	info.type       = regs.eax >> 12 & 3;  // EAX[13:12]
1210 	info.typeString = PROCESSOR_TYPE[info.type];
1211 	info.modelExtended   = regs.eax >> 16 & 15; // EAX[19:16]
1212 	info.familyExtended  = cast(ubyte)(regs.eax >> 20); // EAX[27:20]
1213 	
1214 	switch (info.vendor.id) with (Vendor) {
1215 	case Intel:
1216 		info.family = info.familyBase != 15 ?
1217 			cast(ushort)info.familyBase :
1218 			cast(ushort)(info.familyExtended + info.familyBase);
1219 		
1220 		info.model = info.familyBase == 6 || info.familyBase == 0 ?
1221 			cast(ushort)((info.modelExtended << 4) + info.modelBase) :
1222 			cast(ushort)info.modelBase; // DisplayModel = Model_ID;
1223 		
1224 		// ECX
1225 		info.debugging.dtes64	= bit(regs.ecx, 2);
1226 		info.debugging.ds_cpl	= bit(regs.ecx, 4);
1227 		info.virt.available	= bit(regs.ecx, 5);
1228 		info.tech.smx	= bit(regs.ecx, 6);
1229 		info.tech.eist	= bit(regs.ecx, 7);
1230 		info.sys.tm2	= bit(regs.ecx, 8);
1231 		info.cache.cnxtId	= bit(regs.ecx, 10);
1232 		info.debugging.sdbg	= bit(regs.ecx, 11);
1233 		info.misc.xtpr	= bit(regs.ecx, 14);
1234 		info.debugging.pdcm	= bit(regs.ecx, 15);
1235 		info.misc.pcid	= bit(regs.ecx, 17);
1236 		info.debugging.mca	= bit(regs.ecx, 18);
1237 		info.sys.x2apic	= bit(regs.ecx, 21);
1238 		info.extras.rdtscDeadline	= bit(regs.ecx, 24);
1239 		
1240 		// EDX
1241 		info.misc.psn	= bit(regs.edx, 18);
1242 		info.debugging.ds	= bit(regs.edx, 21);
1243 		info.sys.available	= bit(regs.edx, 22);
1244 		info.cache.ss	= bit(regs.edx, 27);
1245 		info.sys.tm	= bit(regs.edx, 29);
1246 		info.debugging.pbe	= regs.edx >= BIT!(31);
1247 		break;
1248 	case AMD:
1249 		if (info.familyBase < 15) {
1250 			info.family = info.familyBase;
1251 			info.model = info.modelBase;
1252 		} else {
1253 			info.family = cast(ushort)(info.familyExtended + info.familyBase);
1254 			info.model = cast(ushort)((info.modelExtended << 4) + info.modelBase);
1255 		}
1256 		break;
1257 	default:
1258 	}
1259 	
1260 	// EBX
1261 	info.sys.apicId = regs.ebx >> 24;
1262 	info.sys.maxApicId = cast(ubyte)(regs.ebx >> 16);
1263 	info.cache.clflushLinesize = regs.bh;
1264 	info.brandIndex = regs.bl;
1265 	
1266 	// ECX
1267 	info.sse.sse3	= bit(regs.ecx, 0);
1268 	info.extras.pclmulqdq	= bit(regs.ecx, 1);
1269 	info.extras.monitor	= bit(regs.ecx, 3);
1270 	info.sse.ssse3	= bit(regs.ecx, 9);
1271 	info.extensions.fma3	= bit(regs.ecx, 12);
1272 	info.extras.cmpxchg16b	= bit(regs.ecx, 13);
1273 	info.sse.sse41	= bit(regs.ecx, 15);
1274 	info.sse.sse42	= bit(regs.ecx, 20);
1275 	info.extras.movbe	= bit(regs.ecx, 22);
1276 	info.extras.popcnt	= bit(regs.ecx, 23);
1277 	info.extensions.aes_ni	= bit(regs.ecx, 25);
1278 	info.extras.xsave	= bit(regs.ecx, 26);
1279 	info.extras.osxsave	= bit(regs.ecx, 27);
1280 	info.avx.avx	= bit(regs.ecx, 28);
1281 	info.extensions.f16c	= bit(regs.ecx, 29);
1282 	info.extras.rdrand	= bit(regs.ecx, 30);
1283 	
1284 	// EDX
1285 	info.extensions.fpu	= bit(regs.edx, 0);
1286 	info.virt.vme	= bit(regs.edx, 1);
1287 	info.debugging.de	= bit(regs.edx, 2);
1288 	info.memory.pse	= bit(regs.edx, 3);
1289 	info.extras.rdtsc	= bit(regs.edx, 4);
1290 	info.extras.rdmsr	= bit(regs.edx, 5);
1291 	info.memory.pae	= bit(regs.edx, 6);
1292 	info.debugging.mce	= bit(regs.edx, 7);
1293 	info.extras.cmpxchg8b	= bit(regs.edx, 8);
1294 	info.sys.apic	= bit(regs.edx, 9);
1295 	info.extras.sysenter	= bit(regs.edx, 11);
1296 	info.memory.mtrr	= bit(regs.edx, 12);
1297 	info.memory.pge	= bit(regs.edx, 13);
1298 	info.debugging.mca	= bit(regs.edx, 14);
1299 	info.extras.cmov	= bit(regs.edx, 15);
1300 	info.memory.pat	= bit(regs.edx, 16);
1301 	info.memory.pse36	= bit(regs.edx, 17);
1302 	info.cache.clflush	= bit(regs.edx, 19);
1303 	info.extensions.mmx	= bit(regs.edx, 23);
1304 	info.extras.fxsr	= bit(regs.edx, 24);
1305 	info.sse.sse	= bit(regs.edx, 25);
1306 	info.sse.sse2	= bit(regs.edx, 26);
1307 	info.tech.htt	= bit(regs.edx, 28);
1308 }
1309 
1310 //NOTE: Only Intel officially supports CPUID.02h
1311 //      No dedicated functions to a cache descriptor to avoid a definition.
1312 pragma(inline, false)
1313 private
1314 void ddcpuid_leaf2(ref CPUINFO info, ref REGISTERS regs) {
1315 	struct leaf2_t {
1316 		union {
1317 			REGISTERS registers;
1318 			ubyte[16] values;
1319 		}
1320 	}
1321 	leaf2_t data = void;
1322 	
1323 	if (regs.eax < 0x8000_0000) { // valid bit, adjust byte positions
1324 		data.values[3] = data.values[2];
1325 		data.values[2] = data.values[1];
1326 		data.values[1] = data.values[0];
1327 	}
1328 	
1329 	// Simulate CPUID.04h
1330 	enum L1I = 0;
1331 	enum L1D = 1;
1332 	enum L2 = 2;
1333 	enum L3 = 3;
1334 	for (size_t index = 1; index < 16; ++index) {
1335 		ubyte value = data.values[index];
1336 		
1337 		// Cache entries only, the rest is "don't care".
1338 		// Unless if one day I support looking up TLB data, but AMD does not support this.
1339 		// continue: Explicitly skip cache, this includes 0x00 (null), 0x40 (no L2 or L3).
1340 		// break: Valid cache descriptor, increment cache level.
1341 		//TODO: table + foreach loop
1342 		with (info.cache) switch (value) {
1343 		case 0x06: // 1st-level instruction cache: 8 KBytes, 4-way set associative, 32 byte line size
1344 			level[L1I] = CACHEINFO(1, 'I', 8, 1, 4, 1, 32, 64);
1345 			break;
1346 		case 0x08: // 1st-level instruction cache: 16 KBytes, 4-way set associative, 32 byte line size
1347 			level[L1I] = CACHEINFO(1, 'I', 16, 1, 4, 1, 32, 128);
1348 			break;
1349 		case 0x09: // 1st-level instruction cache: 32 KBytes, 4-way set associative, 64 byte line size
1350 			level[L1I] = CACHEINFO(1, 'I', 32, 1, 4, 1, 64, 128);
1351 			break;
1352 		case 0x0A: // 1st-level data cache: 8 KBytes, 2-way set associative, 32 byte line size
1353 			level[L1D] = CACHEINFO(1, 'D', 8, 1, 2, 1, 32, 128);
1354 			break;
1355 		case 0x0C: // 1st-level data cache: 16 KBytes, 4-way set associative, 32 byte line size
1356 			level[L1D] = CACHEINFO(1, 'D', 16, 1, 4, 1, 32, 128);
1357 			break;
1358 		case 0x0D: // 1st-level data cache: 16 KBytes, 4-way set associative, 64 byte line size (ECC?)
1359 			level[L1D] = CACHEINFO(1, 'D', 16, 1, 4, 1, 64, 64);
1360 			break;
1361 		case 0x0E: // 1st-level data cache: 24 KBytes, 6-way set associative, 64 byte line size
1362 			level[L1D] = CACHEINFO(1, 'D', 24, 1, 6, 1, 64, 64);
1363 			break;
1364 		case 0x10: // (sandpile) data L1 cache, 16 KB, 4 ways, 32 byte lines (IA-64)
1365 			level[L1D] = CACHEINFO(1, 'D', 16, 1, 4, 1, 32, 64);
1366 			break;
1367 		case 0x15: // (sandpile) code L1 cache, 16 KB, 4 ways, 32 byte lines (IA-64)
1368 			level[L1I] = CACHEINFO(1, 'I', 16, 1, 4, 1, 32, 64);
1369 			break;
1370 		case 0x1a: // (sandpile) code and data L2 cache, 96 KB, 6 ways, 64 byte lines (IA-64)
1371 			level[L2] = CACHEINFO(2, 'I', 96, 1, 6, 1, 64, 256);
1372 			break;
1373 		case 0x1D: // 2nd-level cache: 128 KBytes, 2-way set associative, 64 byte line size
1374 			level[L2] = CACHEINFO(2, 'U', 128, 1, 2, 1, 64, 1024);
1375 			break;
1376 		case 0x21: // 2nd-level cache: 256 KBytes, 8-way set associative, 64 byte line size
1377 			level[L2] = CACHEINFO(2, 'U', 256, 1, 8, 1, 64, 512);
1378 			break;
1379 		case 0x22: // 3rd-level cache: 512 KBytes, 4-way set associative, 64 byte line size, 2 lines per sector
1380 			level[L3] = CACHEINFO(3, 'U', 512, 1, 4, 2, 64, 1024);
1381 			break;
1382 		case 0x23: // 3rd-level cache: 1 MBytes, 8-way set associative, 64 byte line size, 2 lines per sector
1383 			level[L3] = CACHEINFO(3, 'U', 1024, 1, 8, 2, 64, 1024);
1384 			break;
1385 		case 0x24: // 2nd-level cache: 1 MBytes, 16-way set associative, 64 byte line size
1386 			level[L2] = CACHEINFO(2, 'U', 1024, 1, 16, 1, 64, 1024);
1387 			break;
1388 		case 0x25: // 3rd-level cache: 2 MBytes, 8-way set associative, 64 byte line size, 2 lines per sector
1389 			level[L3] = CACHEINFO(3, 'U', 2048, 1, 8, 2, 64, 2048);
1390 			break;
1391 		case 0x29: // 3rd-level cache: 4 MBytes, 8-way set associative, 64 byte line size, 2 lines per sector
1392 			level[L3] = CACHEINFO(3, 'U', 4096, 1, 8, 2, 64, 4096);
1393 			break;
1394 		case 0x2C: // 1st-level data cache: 32 KBytes, 8-way set associative, 64 byte line size
1395 			level[L1D] = CACHEINFO(1, 'D', 32, 1, 8, 1, 64, 64);
1396 			break;
1397 		case 0x30: // 1st-level instruction cache: 32 KBytes, 8-way set associative, 64 byte line size
1398 			level[L1I] = CACHEINFO(1, 'I', 32, 1, 8, 1, 64, 64);
1399 			break;
1400 		case 0x39: // (sandpile) code and data L2 cache, 128 KB, 4 ways, 64 byte lines, sectored (htt?)
1401 			level[L2] = CACHEINFO(2, 'U', 128, 1, 4, 1, 64, 512);
1402 			break;
1403 		case 0x3A: // (sandpile) code and data L2 cache, 192 KB, 6 ways, 64 byte lines, sectored (htt?)
1404 			level[L2] = CACHEINFO(2, 'U', 192, 1, 6, 1, 64, 512);
1405 			break;
1406 		case 0x3B: // (sandpile) code and data L2 cache, 128 KB, 2 ways, 64 byte lines, sectored (htt?)
1407 			level[L2] = CACHEINFO(2, 'U', 128, 1, 2, 1, 64, 1024);
1408 			break;
1409 		case 0x3C: // (sandpile) code and data L2 cache, 256 KB, 4 ways, 64 byte lines, sectored
1410 			level[L2] = CACHEINFO(2, 'U', 256, 1, 4, 1, 64, 1024);
1411 			break;
1412 		case 0x3D: // (sandpile) code and data L2 cache, 384 KB, 6 ways, 64 byte lines, sectored (htt?)
1413 			level[L2] = CACHEINFO(2, 'U', 384, 1, 6, 1, 64, 1024);
1414 			break;
1415 		case 0x3E: // (sandpile) code and data L2 cache, 512 KB, 4 ways, 64 byte lines, sectored (htt?)
1416 			level[L2] = CACHEINFO(2, 'U', 512, 1, 4, 1, 64, 2048);
1417 			break;
1418 		case 0x41: // 2nd-level cache: 128 KBytes, 4-way set associative, 32 byte line size
1419 			level[L2] = CACHEINFO(2, 'U', 128, 1, 4, 1, 32, 1024);
1420 			break;
1421 		case 0x42: // 2nd-level cache: 256 KBytes, 4-way set associative, 32 byte line size
1422 			level[L2] = CACHEINFO(2, 'U', 256, 1, 4, 1, 32, 2048);
1423 			break;
1424 		case 0x43: // 2nd-level cache: 512 KBytes, 4-way set associative, 32 byte line size
1425 			level[L2] = CACHEINFO(2, 'U', 512, 1, 4, 1, 32, 4096);
1426 			break;
1427 		case 0x44: // 2nd-level cache: 1 MByte, 4-way set associative, 32 byte line size
1428 			level[L2] = CACHEINFO(2, 'U', 1024, 1, 4, 1, 32, 8192);
1429 			break;
1430 		case 0x45: // 2nd-level cache: 2 MByte, 4-way set associative, 32 byte line size
1431 			level[L2] = CACHEINFO(2, 'U', 2048, 1, 4, 1, 32, 16384);
1432 			break;
1433 		case 0x46: // 3rd-level cache: 4 MByte, 4-way set associative, 64 byte line size
1434 			level[L2] = CACHEINFO(2, 'U', 4096, 1, 4, 1, 64, 16384);
1435 			break;
1436 		case 0x47: // 3rd-level cache: 8 MByte, 8-way set associative, 64 byte line size
1437 			level[L3] = CACHEINFO(3, 'U', 8192, 1, 8, 1, 64, 16384);
1438 			break;
1439 		case 0x48: // 2nd-level cache: 3 MByte, 12-way set associative, 64 byte line size
1440 			level[L2] = CACHEINFO(2, 'U', 3072, 1, 12, 1, 64, 4096);
1441 			break;
1442 		// 3rd-level cache: 4 MByte, 16-way set associative, 64-byte line size (Intel Xeon processor MP, Family 0FH, Model 06H);			
1443 		// 2nd-level cache: 4 MByte, 16-way set associative, 64 byte line size
1444 		case 0x49:
1445 			if (info.family == 0xf && info.family == 6)
1446 				level[L3] = CACHEINFO(3, 'U', 4096, 1, 16, 1, 64, 4096);
1447 			else
1448 				level[L2] = CACHEINFO(2, 'U', 4096, 1, 16, 1, 64, 4096);
1449 			break;
1450 		case 0x4A: // 3rd-level cache: 6 MByte, 12-way set associative, 64 byte line size
1451 			level[L3] = CACHEINFO(3, 'U', 6144, 1, 12, 1, 64, 6144);
1452 			break;
1453 		case 0x4B: // 3rd-level cache: 8 MByte, 16-way set associative, 64 byte line size
1454 			level[L3] = CACHEINFO(3, 'U', 8192, 1, 16, 1, 64, 8192);
1455 			break;
1456 		case 0x4C: // 3rd-level cache: 12 MByte, 12-way set associative, 64 byte line size
1457 			level[L3] = CACHEINFO(3, 'U', 8192, 1, 12, 1, 64, 16384);
1458 			break;
1459 		case 0x4D: // 3rd-level cache: 16 MByte, 16-way set associative, 64 byte line size
1460 			level[L3] = CACHEINFO(3, 'U', 16384, 1, 16, 1, 64, 16384);
1461 			break;
1462 		case 0x4E: // 2nd-level cache: 6MByte, 24-way set associative, 64 byte line size
1463 			level[L2] = CACHEINFO(2, 'U', 6144, 1, 24, 1, 64, 4096);
1464 			break;
1465 		case 0x60: // 1st-level data cache: 16 KByte, 8-way set associative, 64 byte line size
1466 			level[L1D] = CACHEINFO(1, 'D', 16, 1, 8, 1, 64, 32);
1467 			break;
1468 		case 0x66: // 1st-level data cache: 8 KByte, 4-way set associative, 64 byte line size
1469 			level[L1D] = CACHEINFO(1, 'D', 8, 1, 4, 1, 64, 32);
1470 			break;
1471 		case 0x67: // 1st-level data cache: 16 KByte, 4-way set associative, 64 byte line size
1472 			level[L1D] = CACHEINFO(1, 'D', 16, 1, 4, 1, 64, 64);
1473 			break;
1474 		case 0x68: // 1st-level data cache: 32 KByte, 4-way set associative, 64 byte line size
1475 			level[L1D] = CACHEINFO(1, 'D', 32, 1, 4, 1, 64, 128);
1476 			break;
1477 		case 0x77: // (sandpile) code L1 cache, 16 KB, 4 ways, 64 byte lines, sectored (IA-64)
1478 			level[L1I] = CACHEINFO(1, 'I', 16, 1, 4, 1, 64, 64);
1479 			break;
1480 		case 0x78: // 2nd-level cache: 1 MByte, 4-way set associative, 64byte line size
1481 			level[L2] = CACHEINFO(2, 'U', 1024, 1, 4, 1, 64, 4096);
1482 			break;
1483 		case 0x79: // 2nd-level cache: 128 KByte, 8-way set associative, 64 byte line size, 2 lines per sector
1484 			level[L2] = CACHEINFO(2, 'U', 128, 1, 8, 2, 64, 128);
1485 			break;
1486 		case 0x7A: // 2nd-level cache: 256 KByte, 8-way set associative, 64 byte line size, 2 lines per sector
1487 			level[L2] = CACHEINFO(2, 'U', 256, 1, 8, 2, 64, 256);
1488 			break;
1489 		case 0x7B: // 2nd-level cache: 512 KByte, 8-way set associative, 64 byte line size, 2 lines per sector
1490 			level[L2] = CACHEINFO(2, 'U', 512, 1, 8, 2, 64, 512);
1491 			break;
1492 		case 0x7C: // 2nd-level cache: 1 MByte, 8-way set associative, 64 byte line size, 2 lines per sector
1493 			level[L2] = CACHEINFO(2, 'U', 1024, 1, 8, 2, 64, 1024);
1494 			break;
1495 		case 0x7D: // 2nd-level cache: 2 MByte, 8-way set associative, 64 byte line size
1496 			level[L2] = CACHEINFO(2, 'U', 2048, 1, 8, 1, 64, 4096);
1497 			break;
1498 		case 0x7E: // (sandpile) code and data L2 cache, 256 KB, 8 ways, 128 byte lines, sect. (IA-64)
1499 			level[L2] = CACHEINFO(2, 'U', 256, 1, 8, 1, 128, 256);
1500 			break;
1501 		case 0x7F: // 2nd-level cache: 512 KByte, 2-way set associative, 64-byte line size
1502 			level[L2] = CACHEINFO(2, 'U', 512, 1, 2, 1, 64, 4096);
1503 			break;
1504 		case 0x80: // 2nd-level cache: 512 KByte, 8-way set associative, 64-byte line size
1505 			level[L2] = CACHEINFO(2, 'U', 512, 1, 8, 1, 64, 1024);
1506 			break;
1507 		case 0x81: // (sandpile) code and data L2 cache, 128 KB, 8 ways, 32 byte lines
1508 			level[L2] = CACHEINFO(2, 'U', 128, 1, 8, 1, 32, 512);
1509 			break;
1510 		case 0x82: // 2nd-level cache: 256 KByte, 8-way set associative, 32 byte line size
1511 			level[L2] = CACHEINFO(2, 'U', 256, 1, 8, 1, 32, 1024);
1512 			break;
1513 		case 0x83: // 2nd-level cache: 512 KByte, 8-way set associative, 32 byte line size
1514 			level[L2] = CACHEINFO(2, 'U', 512, 1, 8, 1, 32, 2048);
1515 			break;
1516 		case 0x84: // 2nd-level cache: 1 MByte, 8-way set associative, 32 byte line size
1517 			level[L2] = CACHEINFO(2, 'U', 1024, 1, 8, 1, 32, 4096);
1518 			break;
1519 		case 0x85: // 2nd-level cache: 2 MByte, 8-way set associative, 32 byte line size
1520 			level[L2] = CACHEINFO(2, 'U', 2048, 1, 8, 1, 32, 8192);
1521 			break;
1522 		case 0x86: // 2nd-level cache: 512 KByte, 4-way set associative, 64 byte line size
1523 			level[L2] = CACHEINFO(2, 'U', 512, 1, 4, 1, 64, 2048);
1524 			break;
1525 		case 0x87: // 2nd-level cache: 1 MByte, 8-way set associative, 64 byte line size
1526 			level[L2] = CACHEINFO(2, 'U', 1024, 1, 8, 1, 64, 2048);
1527 			break;
1528 		case 0xD0: // 3rd-level cache: 512 KByte, 4-way set associative, 64 byte line size
1529 			level[L3] = CACHEINFO(3, 'U', 512, 1, 4, 1, 64, 2048);
1530 			break;
1531 		case 0xD1: // 3rd-level cache: 1 MByte, 4-way set associative, 64 byte line size
1532 			level[L3] = CACHEINFO(3, 'U', 1024, 1, 4, 1, 64, 4096);
1533 			break;
1534 		case 0xD2: // 3rd-level cache: 2 MByte, 4-way set associative, 64 byte line size
1535 			level[L3] = CACHEINFO(3, 'U', 2048, 1, 4, 1, 64, 8192);
1536 			break;
1537 		case 0xD6: // 3rd-level cache: 1 MByte, 8-way set associative, 64 byte line size
1538 			level[L3] = CACHEINFO(3, 'U', 1024, 1, 8, 1, 64, 2048);
1539 			break;
1540 		case 0xD7: // 3rd-level cache: 2 MByte, 8-way set associative, 64 byte line size
1541 			level[L3] = CACHEINFO(3, 'U', 2048, 1, 8, 1, 64, 4096);
1542 			break;
1543 		case 0xD8: // 3rd-level cache: 4 MByte, 8-way set associative, 64 byte line size
1544 			level[L3] = CACHEINFO(3, 'U', 4096, 1, 8, 1, 64, 8192);
1545 			break;
1546 		case 0xDC: // 3rd-level cache: 1.5 MByte, 12-way set associative, 64 byte line size
1547 			level[L3] = CACHEINFO(3, 'U', 1536, 1, 12, 1, 64, 2048);
1548 			break;
1549 		case 0xDD: // 3rd-level cache: 3 MByte, 12-way set associative, 64 byte line size
1550 			level[L3] = CACHEINFO(3, 'U', 3072, 1, 12, 1, 64, 4096);
1551 			break;
1552 		case 0xDE: // 3rd-level cache: 6 MByte, 12-way set associative, 64 byte line size
1553 			level[L3] = CACHEINFO(3, 'U', 6144, 1, 12, 1, 64, 8192);
1554 			break;
1555 		case 0xE2: // 3rd-level cache: 2 MByte, 16-way set associative, 64 byte line size
1556 			level[L3] = CACHEINFO(3, 'U', 2048, 1, 16, 1, 64, 2048);
1557 			break;
1558 		case 0xE3: // 3rd-level cache: 4 MByte, 16-way set associative, 64 byte line size
1559 			level[L3] = CACHEINFO(3, 'U', 4096, 1, 16, 1, 64, 4096);
1560 			break;
1561 		case 0xE4: // 3rd-level cache: 8 MByte, 16-way set associative, 64 byte line size
1562 			level[L3] = CACHEINFO(3, 'U', 8192, 1, 16, 1, 64, 8192);
1563 			break;
1564 		case 0xEA: // 3rd-level cache: 12MByte, 24-way set associative, 64 byte line size
1565 			level[L3] = CACHEINFO(3, 'U', 12288, 1, 24, 1, 64, 8192);
1566 			break;
1567 		case 0xEB: // 3rd-level cache: 18MByte, 24-way set associative, 64 byte line size
1568 			level[L3] = CACHEINFO(3, 'U', 18432, 1, 24, 1, 64, 12288);
1569 			break;
1570 		case 0xEC: // 3rd-level cache: 24MByte, 24-way set associative, 64 byte line size
1571 			level[L3] = CACHEINFO(3, 'U', 24576, 1, 24, 1, 64, 16384);
1572 			break;
1573 		default: continue;
1574 		}
1575 		
1576 		++info.cache.levels;
1577 	}
1578 }
1579 
1580 pragma(inline, false)
1581 private
1582 void ddcpuid_leaf5(ref CPUINFO info, ref REGISTERS regs) {
1583 	info.extras.mwaitMin = regs.ax;
1584 	info.extras.mwaitMax = regs.bx;
1585 }
1586 
1587 pragma(inline, false)
1588 private
1589 void ddcpuid_leaf6(ref CPUINFO info, ref REGISTERS regs) {
1590 	switch (info.vendor.id) with (Vendor) {
1591 	case Intel:
1592 		info.tech.turboboost	= bit(regs.eax, 1);
1593 		info.tech.turboboost30	= bit(regs.eax, 14);
1594 		break;
1595 	default:
1596 	}
1597 	
1598 	info.sys.arat = bit(regs.eax, 2);
1599 }
1600 
1601 pragma(inline, false)
1602 private
1603 void ddcpuid_leaf7(ref CPUINFO info, ref REGISTERS regs) {
1604 	switch (info.vendor.id) with (Vendor) {
1605 	case Intel:
1606 		// EBX
1607 		info.sgx.supported	= bit(regs.ebx, 2);
1608 		info.memory.hle	= bit(regs.ebx, 4);
1609 		info.cache.invpcid	= bit(regs.ebx, 10);
1610 		info.memory.rtm	= bit(regs.ebx, 11);
1611 		info.avx.avx512f	= bit(regs.ebx, 16);
1612 		info.memory.smap	= bit(regs.ebx, 20);
1613 		info.avx.avx512er	= bit(regs.ebx, 27);
1614 		info.avx.avx512pf	= bit(regs.ebx, 26);
1615 		info.avx.avx512cd	= bit(regs.ebx, 28);
1616 		info.avx.avx512dq	= bit(regs.ebx, 17);
1617 		info.avx.avx512bw	= bit(regs.ebx, 30);
1618 		info.avx.avx512_ifma	= bit(regs.ebx, 21);
1619 		info.avx.avx512_vbmi	= regs.ebx >= BIT!(31);
1620 		// ECX
1621 		info.avx.avx512vl	= bit(regs.ecx, 1);
1622 		info.memory.pku	= bit(regs.ecx, 3);
1623 		info.memory.fsrepmov	= bit(regs.ecx, 4);
1624 		info.extensions.waitpkg	= bit(regs.ecx, 5);
1625 		info.avx.avx512_vbmi2	= bit(regs.ecx, 6);
1626 		info.security.cetSs	= bit(regs.ecx, 7);
1627 		info.avx.avx512_gfni	= bit(regs.ecx, 8);
1628 		info.avx.avx512_vaes	= bit(regs.ecx, 9);
1629 		info.avx.avx512_vnni	= bit(regs.ecx, 11);
1630 		info.avx.avx512_bitalg	= bit(regs.ecx, 12);
1631 		info.avx.avx512_vpopcntdq	= bit(regs.ecx, 14);
1632 		info.memory._5pl	= bit(regs.ecx, 16);
1633 		info.extras.cldemote	= bit(regs.ecx, 25);
1634 		info.extras.movdiri	= bit(regs.ecx, 27);
1635 		info.extras.movdir64b	= bit(regs.ecx, 28);
1636 		info.extras.enqcmd	= bit(regs.ecx, 29);
1637 		// EDX
1638 		info.avx.avx512_4vnniw	= bit(regs.edx, 2);
1639 		info.avx.avx512_4fmaps	= bit(regs.edx, 3);
1640 		info.misc.uintr	= bit(regs.edx, 5);
1641 		info.avx.avx512_vp2intersect	= bit(regs.edx, 8);
1642 		info.security.md_clear	= bit(regs.edx, 10);
1643 		info.extras.serialize	= bit(regs.edx, 14);
1644 		info.memory.tsxldtrk	= bit(regs.edx, 16);
1645 		info.extras.pconfig	= bit(regs.edx, 18);
1646 		info.security.cetIbt	= bit(regs.edx, 20);
1647 		info.amx.bf16	= bit(regs.edx, 22);
1648 		info.amx.enabled	= bit(regs.edx, 24);
1649 		info.amx.int8	= bit(regs.edx, 25);
1650 		info.security.ibrs = bit(regs.edx, 26);
1651 		info.security.stibp	= bit(regs.edx, 27);
1652 		info.security.l1dFlush	= bit(regs.edx, 28);
1653 		info.security.ia32_arch_capabilities	= bit(regs.edx, 29);
1654 		info.security.ssbd	= regs.edx >= BIT!(31);
1655 		break;
1656 	default:
1657 	}
1658 
1659 	// ebx
1660 	info.misc.fsgsbase	= bit(regs.ebx, 0);
1661 	info.extensions.bmi1	= bit(regs.ebx, 3);
1662 	info.avx.avx2	= bit(regs.ebx, 5);
1663 	info.memory.smep	= bit(regs.ebx, 7);
1664 	info.extensions.bmi2	= bit(regs.ebx, 8);
1665 	info.extras.rdseed	= bit(regs.ebx, 18);
1666 	info.extensions.adx	= bit(regs.ebx, 19);
1667 	info.cache.clflushopt	= bit(regs.ebx, 23);
1668 	info.extensions.sha	= bit(regs.ebx, 29);
1669 	// ecx
1670 	info.extras.rdpid	= bit(regs.ecx, 22);
1671 }
1672 
1673 pragma(inline, false)
1674 private
1675 void ddcpuid_leaf7sub1(ref CPUINFO info, ref REGISTERS regs) {
1676 	switch (info.vendor.id) with (Vendor) {
1677 	case Intel:
1678 		// a
1679 		info.avx.avx512_bf16	= bit(regs.eax, 5);
1680 		info.memory.lam	= bit(regs.eax, 26);
1681 		break;
1682 	default:
1683 	}
1684 }
1685 
1686 pragma(inline, false)
1687 private
1688 void ddcpuid_leafD(ref CPUINFO info, ref REGISTERS regs) {
1689 	switch (info.vendor.id) with (Vendor) {
1690 	case Intel:
1691 		info.amx.xtilecfg	= bit(regs.eax, 17);
1692 		info.amx.xtiledata	= bit(regs.eax, 18);
1693 		break;
1694 	default:
1695 	}
1696 }
1697 
1698 pragma(inline, false)
1699 private
1700 void ddcpuid_leafDsub1(ref CPUINFO info, ref REGISTERS regs) {
1701 	switch (info.vendor.id) with (Vendor) {
1702 	case Intel:
1703 		info.amx.xfd	= bit(regs.eax, 18);
1704 		break;
1705 	default:
1706 	}
1707 }
1708 
1709 pragma(inline, false)
1710 private
1711 void ddcpuid_leaf12(ref CPUINFO info, ref REGISTERS regs) {
1712 	switch (info.vendor.id) with (Vendor) {
1713 	case Intel:
1714 		info.sgx.sgx1 = bit(regs.al, 0);
1715 		info.sgx.sgx2 = bit(regs.al, 1);
1716 		info.sgx.maxSize   = regs.dl;
1717 		info.sgx.maxSize64 = regs.dh;
1718 		break;
1719 	default:
1720 	}
1721 }
1722 
1723 pragma(inline, false)
1724 private
1725 void ddcpuid_leaf4000_0001(ref CPUINFO info, ref REGISTERS regs) {
1726 //	switch (info.virt.vendor.id) with (VirtVendor) {
1727 //	case KVM:
1728 		info.virt.kvm.feature_clocksource	= bit(regs.eax, 0);
1729 		info.virt.kvm.feature_nop_io_delay	= bit(regs.eax, 1);
1730 		info.virt.kvm.feature_mmu_op	= bit(regs.eax, 2);
1731 		info.virt.kvm.feature_clocksource2	= bit(regs.eax, 3);
1732 		info.virt.kvm.feature_async_pf	= bit(regs.eax, 4);
1733 		info.virt.kvm.feature_steal_time	= bit(regs.eax, 5);
1734 		info.virt.kvm.feature_pv_eoi	= bit(regs.eax, 6);
1735 		info.virt.kvm.feature_pv_unhault	= bit(regs.eax, 7);
1736 		info.virt.kvm.feature_pv_tlb_flush	= bit(regs.eax, 9);
1737 		info.virt.kvm.feature_async_pf_vmexit	= bit(regs.eax, 10);
1738 		info.virt.kvm.feature_pv_send_ipi	= bit(regs.eax, 11);
1739 		info.virt.kvm.feature_pv_poll_control	= bit(regs.eax, 12);
1740 		info.virt.kvm.feature_pv_sched_yield	= bit(regs.eax, 13);
1741 		info.virt.kvm.feature_clocsource_stable_bit	= bit(regs.eax, 24);
1742 		info.virt.kvm.hint_realtime	= bit(regs.edx, 0);
1743 //		break;
1744 //	default:
1745 //	}
1746 }
1747 
1748 pragma(inline, false)
1749 private
1750 void ddcpuid_leaf4000_0002(ref CPUINFO info, ref REGISTERS regs) {
1751 //	switch (info.virt.vendor.id) with (VirtVendor) {
1752 //	case HyperV:
1753 		info.virt.hv.guest_minor	= cast(ubyte)(regs.eax >> 24);
1754 		info.virt.hv.guest_service	= cast(ubyte)(regs.eax >> 16);
1755 		info.virt.hv.guest_build	= regs.ax;
1756 		info.virt.hv.guest_opensource	= regs.edx >= BIT!(31);
1757 		info.virt.hv.guest_vendor_id	= (regs.edx >> 16) & 0xFFF;
1758 		info.virt.hv.guest_os	= regs.dh;
1759 		info.virt.hv.guest_major	= regs.dl;
1760 //		break;
1761 //	default:
1762 //	}
1763 }
1764 
1765 pragma(inline, false)
1766 private
1767 void ddcpuid_leaf4000_0003(ref CPUINFO info, ref REGISTERS regs) {
1768 //	switch (info.virt.vendor.id) with (VirtVendor) {
1769 //	case HyperV:
1770 		info.virt.hv.base_feat_vp_runtime_msr	= bit(regs.eax, 0);
1771 		info.virt.hv.base_feat_part_time_ref_count_msr	= bit(regs.eax, 1);
1772 		info.virt.hv.base_feat_basic_synic_msrs	= bit(regs.eax, 2);
1773 		info.virt.hv.base_feat_stimer_msrs	= bit(regs.eax, 3);
1774 		info.virt.hv.base_feat_apic_access_msrs	= bit(regs.eax, 4);
1775 		info.virt.hv.base_feat_hypercall_msrs	= bit(regs.eax, 5);
1776 		info.virt.hv.base_feat_vp_id_msr	= bit(regs.eax, 6);
1777 		info.virt.hv.base_feat_virt_sys_reset_msr	= bit(regs.eax, 7);
1778 		info.virt.hv.base_feat_stat_pages_msr	= bit(regs.eax, 8);
1779 		info.virt.hv.base_feat_part_ref_tsc_msr	= bit(regs.eax, 9);
1780 		info.virt.hv.base_feat_guest_idle_state_msr	= bit(regs.eax, 10);
1781 		info.virt.hv.base_feat_timer_freq_msrs	= bit(regs.eax, 11);
1782 		info.virt.hv.base_feat_debug_msrs	= bit(regs.eax, 12);
1783 		info.virt.hv.part_flags_create_part	= bit(regs.ebx, 0);
1784 		info.virt.hv.part_flags_access_part_id	= bit(regs.ebx, 1);
1785 		info.virt.hv.part_flags_access_memory_pool	= bit(regs.ebx, 2);
1786 		info.virt.hv.part_flags_adjust_msg_buffers	= bit(regs.ebx, 3);
1787 		info.virt.hv.part_flags_post_msgs	= bit(regs.ebx, 4);
1788 		info.virt.hv.part_flags_signal_events	= bit(regs.ebx, 5);
1789 		info.virt.hv.part_flags_create_port	= bit(regs.ebx, 6);
1790 		info.virt.hv.part_flags_connect_port	= bit(regs.ebx, 7);
1791 		info.virt.hv.part_flags_access_stats	= bit(regs.ebx, 8);
1792 		info.virt.hv.part_flags_debugging	= bit(regs.ebx, 11);
1793 		info.virt.hv.part_flags_cpu_mgmt	= bit(regs.ebx, 12);
1794 		info.virt.hv.part_flags_cpu_profiler	= bit(regs.ebx, 13);
1795 		info.virt.hv.part_flags_expanded_stack_walk	= bit(regs.ebx, 14);
1796 		info.virt.hv.part_flags_access_vsm	= bit(regs.ebx, 16);
1797 		info.virt.hv.part_flags_access_vp_regs	= bit(regs.ebx, 17);
1798 		info.virt.hv.part_flags_extended_hypercalls	= bit(regs.ebx, 20);
1799 		info.virt.hv.part_flags_start_vp	= bit(regs.ebx, 21);
1800 		info.virt.hv.pm_max_cpu_power_state_c0	= bit(regs.ecx, 0);
1801 		info.virt.hv.pm_max_cpu_power_state_c1	= bit(regs.ecx, 1);
1802 		info.virt.hv.pm_max_cpu_power_state_c2	= bit(regs.ecx, 2);
1803 		info.virt.hv.pm_max_cpu_power_state_c3	= bit(regs.ecx, 3);
1804 		info.virt.hv.pm_hpet_reqd_for_c3	= bit(regs.ecx, 4);
1805 		info.virt.hv.misc_feat_mwait	= bit(regs.eax, 0);
1806 		info.virt.hv.misc_feat_guest_debugging	= bit(regs.eax, 1);
1807 		info.virt.hv.misc_feat_perf_mon	= bit(regs.eax, 2);
1808 		info.virt.hv.misc_feat_pcpu_dyn_part_event	= bit(regs.eax, 3);
1809 		info.virt.hv.misc_feat_xmm_hypercall_input	= bit(regs.eax, 4);
1810 		info.virt.hv.misc_feat_guest_idle_state	= bit(regs.eax, 5);
1811 		info.virt.hv.misc_feat_hypervisor_sleep_state	= bit(regs.eax, 6);
1812 		info.virt.hv.misc_feat_query_numa_distance	= bit(regs.eax, 7);
1813 		info.virt.hv.misc_feat_timer_freq	= bit(regs.eax, 8);
1814 		info.virt.hv.misc_feat_inject_synmc_xcpt	= bit(regs.eax, 9);
1815 		info.virt.hv.misc_feat_guest_crash_msrs	= bit(regs.eax, 10);
1816 		info.virt.hv.misc_feat_debug_msrs	= bit(regs.eax, 11);
1817 		info.virt.hv.misc_feat_npiep1	= bit(regs.eax, 12);
1818 		info.virt.hv.misc_feat_disable_hypervisor	= bit(regs.eax, 13);
1819 		info.virt.hv.misc_feat_ext_gva_range_for_flush_va_list	= bit(regs.eax, 14);
1820 		info.virt.hv.misc_feat_hypercall_output_xmm	= bit(regs.eax, 15);
1821 		info.virt.hv.misc_feat_sint_polling_mode	= bit(regs.eax, 17);
1822 		info.virt.hv.misc_feat_hypercall_msr_lock	= bit(regs.eax, 18);
1823 		info.virt.hv.misc_feat_use_direct_synth_msrs	= bit(regs.eax, 19);
1824 //		break;
1825 //	default:
1826 //	}
1827 }
1828 
1829 pragma(inline, false)
1830 private
1831 void ddcpuid_leaf4000_0004(ref CPUINFO info, ref REGISTERS regs) {
1832 //	switch (info.virt.vendor.id) with (VirtVendor) {
1833 //	case HyperV:
1834 		info.virt.hv.hint_hypercall_for_process_switch	= bit(regs.eax, 0);
1835 		info.virt.hv.hint_hypercall_for_tlb_flush	= bit(regs.eax, 1);
1836 		info.virt.hv.hint_hypercall_for_tlb_shootdown	= bit(regs.eax, 2);
1837 		info.virt.hv.hint_msr_for_apic_access	= bit(regs.eax, 3);
1838 		info.virt.hv.hint_msr_for_sys_reset	= bit(regs.eax, 4);
1839 		info.virt.hv.hint_relax_time_checks	= bit(regs.eax, 5);
1840 		info.virt.hv.hint_dma_remapping	= bit(regs.eax, 6);
1841 		info.virt.hv.hint_interrupt_remapping	= bit(regs.eax, 7);
1842 		info.virt.hv.hint_x2apic_msrs	= bit(regs.eax, 8);
1843 		info.virt.hv.hint_deprecate_auto_eoi	= bit(regs.eax, 9);
1844 		info.virt.hv.hint_synth_cluster_ipi_hypercall	= bit(regs.eax, 10);
1845 		info.virt.hv.hint_ex_proc_masks_interface	= bit(regs.eax, 11);
1846 		info.virt.hv.hint_nested_hyperv	= bit(regs.eax, 12);
1847 		info.virt.hv.hint_int_for_mbec_syscalls	= bit(regs.eax, 13);
1848 		info.virt.hv.hint_nested_enlightened_vmcs_interface	= bit(regs.eax, 14);
1849 //		break;
1850 //	default:
1851 //	}
1852 }
1853 
1854 pragma(inline, false)
1855 private
1856 void ddcpuid_leaf4000_0006(ref CPUINFO info, ref REGISTERS regs) {
1857 //	switch (info.virt.vendor.id) with (VirtVendor) {
1858 //	case HyperV:
1859 		info.virt.hv.host_feat_avic	= bit(regs.eax, 0);
1860 		info.virt.hv.host_feat_msr_bitmap	= bit(regs.eax, 1);
1861 		info.virt.hv.host_feat_perf_counter	= bit(regs.eax, 2);
1862 		info.virt.hv.host_feat_nested_paging	= bit(regs.eax, 3);
1863 		info.virt.hv.host_feat_dma_remapping	= bit(regs.eax, 4);
1864 		info.virt.hv.host_feat_interrupt_remapping	= bit(regs.eax, 5);
1865 		info.virt.hv.host_feat_mem_patrol_scrubber	= bit(regs.eax, 6);
1866 		info.virt.hv.host_feat_dma_prot_in_use	= bit(regs.eax, 7);
1867 		info.virt.hv.host_feat_hpet_requested	= bit(regs.eax, 8);
1868 		info.virt.hv.host_feat_stimer_volatile	= bit(regs.eax, 9);
1869 //		break;
1870 //	default:
1871 //	}
1872 }
1873 
1874 pragma(inline, false)
1875 private
1876 void ddcpuid_leaf4000_0010(ref CPUINFO info, ref REGISTERS regs) {
1877 //	switch (info.virt.vendor.id) with (VirtVendor) {
1878 //	case VBoxMin: // VBox Minimal
1879 		info.virt.vbox.tsc_freq_khz = regs.eax;
1880 		info.virt.vbox.apic_freq_khz = regs.ebx;
1881 //		break;
1882 //	default:
1883 //	}
1884 }
1885 
1886 pragma(inline, false)
1887 private
1888 void ddcpuid_leaf8000_0001(ref CPUINFO info, ref REGISTERS regs) {
1889 	switch (info.vendor.id) with (Vendor) {
1890 	case AMD:
1891 		// ecx
1892 		info.virt.available	= bit(regs.ecx, 2);
1893 		info.sys.x2apic	= bit(regs.ecx, 3);
1894 		info.sse.sse4a	= bit(regs.ecx, 6);
1895 		info.extensions.xop	= bit(regs.ecx, 11);
1896 		info.extras.skinit	= bit(regs.ecx, 12);
1897 		info.extensions.fma4	= bit(regs.ecx, 16);
1898 		info.extensions.tbm	= bit(regs.ecx, 21);
1899 		// edx
1900 		info.extensions.mmxExtended	= bit(regs.edx, 22);
1901 		info.extensions._3DNowExtended	= bit(regs.edx, 30);
1902 		info.extensions._3DNow	= regs.edx >= BIT!(31);
1903 		break;
1904 	default:
1905 	}
1906 	
1907 	// ecx
1908 	info.extensions.lahf64	= bit(regs.ecx, 0);
1909 	info.extras.lzcnt	= bit(regs.ecx, 5);
1910 	info.cache.prefetchw	= bit(regs.ecx, 8);
1911 	info.extras.monitorx	= bit(regs.ecx, 29);
1912 	// edx
1913 	info.extras.syscall	= bit(regs.edx, 11);
1914 	info.memory.nx	= bit(regs.edx, 20);
1915 	info.memory.page1gb	= bit(regs.edx, 26);
1916 	info.extras.rdtscp	= bit(regs.edx, 27);
1917 	info.extensions.x86_64	= bit(regs.edx, 29);
1918 }
1919 
1920 pragma(inline, false)
1921 private
1922 void ddcpuid_leaf8000_0007(ref CPUINFO info, ref REGISTERS regs) {
1923 	switch (info.vendor.id) with (Vendor) {
1924 	case Intel:
1925 		info.extras.rdseed	= bit(regs.ebx, 28);
1926 		break;
1927 	case AMD:
1928 		info.sys.tm	= bit(regs.edx, 4);
1929 		info.tech.turboboost	= bit(regs.edx, 9);
1930 		break;
1931 	default:
1932 	}
1933 	
1934 	info.extras.rdtscInvariant	= bit(regs.edx, 8);
1935 }
1936 
1937 pragma(inline, false)
1938 private
1939 void ddcpuid_leaf8000_0008(ref CPUINFO info, ref REGISTERS regs) {
1940 	switch (info.vendor.id) with (Vendor) {
1941 	case Intel:
1942 		info.cache.wbnoinvd	= bit(regs.ebx, 9);
1943 		break;
1944 	case AMD:
1945 		info.security.ibpb	= bit(regs.ebx, 12);
1946 		info.security.ibrs	= bit(regs.ebx, 14);
1947 		info.security.stibp	= bit(regs.ebx, 15);
1948 		info.security.ibrsAlwaysOn	= bit(regs.ebx, 16);
1949 		info.security.stibpAlwaysOn	= bit(regs.ebx, 17);
1950 		info.security.ibrsPreferred	= bit(regs.ebx, 18);
1951 		info.security.ssbd	= bit(regs.ebx, 24);
1952 		break;
1953 	default:
1954 	}
1955 	
1956 	info.memory.physBits = regs.al;
1957 	info.memory.lineBits = regs.ah;
1958 }
1959 
1960 pragma(inline, false)
1961 private
1962 void ddcpuid_leaf8000_000A(ref CPUINFO info, ref REGISTERS regs) {
1963 	switch (info.vendor.id) {
1964 	case Vendor.AMD:
1965 		info.virt.version_	= regs.al; // EAX[7:0]
1966 		info.virt.apicv	= bit(regs.edx, 13);
1967 		break;
1968 	default:
1969 	}
1970 }
1971 
1972 pragma(inline, false)
1973 private
1974 void ddcpuid_topology(ref CPUINFO info) {
1975 	ushort sc = void;	/// raw cores shared across cache level
1976 	ushort crshrd = void;	/// actual count of shared cores
1977 	ubyte type = void;	/// cache type
1978 	ubyte mids = void;	/// maximum IDs to this cache
1979 	REGISTERS regs = void;	/// registers
1980 	
1981 	info.cache.levels = 0;
1982 	CACHEINFO *ca = cast(CACHEINFO*)info.cache.level;
1983 	
1984 	//TODO: Make 1FH/BH/4H/etc. functions.
1985 	switch (info.vendor.id) with (Vendor) {
1986 	case Intel:
1987 		if (info.maxLeaf >= 0x1f) goto L_CACHE_INTEL_1FH;
1988 		if (info.maxLeaf >= 0xb)  goto L_CACHE_INTEL_BH;
1989 		if (info.maxLeaf >= 4)    goto L_CACHE_INTEL_4H;
1990 		// Celeron 0xf34 has maxLeaf=03h and ext=8000_0008h
1991 		if (info.maxLeaf >= 2)    goto L_CACHE_INTEL_2H;
1992 		if (info.maxLeafExtended >= 0x8000_0005) goto L_CACHE_AMD_EXT_5H;
1993 		break;
1994 		
1995 L_CACHE_INTEL_1FH:
1996 		//TODO: Support levels 3,4,5 in CPUID.1FH
1997 		//      (Module, Tile, and Die)
1998 		ddcpuid_id(regs, 0x1f, 1); // Cores (logical)
1999 		info.cores.logical = regs.bx;
2000 		
2001 		ddcpuid_id(regs, 0x1f, 0); // SMT (architectural states per core)
2002 		info.cores.physical = cast(ushort)(info.cores.logical / regs.bx);
2003 		
2004 		goto L_CACHE_INTEL_4H;
2005 		
2006 L_CACHE_INTEL_BH:
2007 		ddcpuid_id(regs, 0xb, 1); // Cores (logical)
2008 		info.cores.logical = regs.bx;
2009 		
2010 		ddcpuid_id(regs, 0xb, 0); // SMT (architectural states per core)
2011 		info.cores.physical = cast(ushort)(info.cores.logical / regs.bx);
2012 		
2013 L_CACHE_INTEL_4H:
2014 		ddcpuid_id(regs, 4, info.cache.levels);
2015 		
2016 		type = regs.eax & CACHE_MASK; // EAX[4:0]
2017 		if (type == 0 || info.cache.levels >= CACHE_MAX_LEVEL) return;
2018 		
2019 		ca.type = CACHE_TYPE[type];
2020 		ca.level = regs.al >> 5;
2021 		ca.lineSize = (regs.bx & 0xfff) + 1; // bits 11-0
2022 		ca.partitions = ((regs.ebx >> 12) & 0x3ff) + 1; // bits 21-12
2023 		ca.ways = ((regs.ebx >> 22) + 1); // bits 31-22
2024 		ca.sets = regs.ecx + 1;
2025 		if (regs.eax & BIT!(8)) ca.features = 1;
2026 		if (regs.eax & BIT!(9)) ca.features |= BIT!(1);
2027 		if (regs.edx & BIT!(0)) ca.features |= BIT!(2);
2028 		if (regs.edx & BIT!(1)) ca.features |= BIT!(3);
2029 		if (regs.edx & BIT!(2)) ca.features |= BIT!(4);
2030 		ca.size = (ca.sets * ca.lineSize * ca.partitions * ca.ways) >> 10;
2031 		
2032 		mids = (regs.eax >> 26) + 1;	// EAX[31:26]
2033 		
2034 		if (info.cores.logical == 0) { // skip if already populated
2035 			info.cores.logical = mids;
2036 			info.cores.physical = info.tech.htt ? mids >> 1 : mids;
2037 		}
2038 		
2039 		crshrd = (((regs.eax >> 14) & 2047) + 1);	// EAX[25:14]
2040 		sc = cast(ushort)(info.cores.logical / crshrd); // cast for ldc 0.17.1
2041 		ca.sharedCores = sc ? sc : 1;
2042 		version (Trace) trace("intel.4h mids=%u shared=%u crshrd=%u sc=%u",
2043 			mids, ca.sharedCores, crshrd, sc);
2044 		
2045 		++info.cache.levels; ++ca;
2046 		goto L_CACHE_INTEL_4H;
2047 
2048 L_CACHE_INTEL_2H:
2049 		ddcpuid_id(regs, 2);
2050 		ddcpuid_leaf2(info, regs);
2051 		break;
2052 	case AMD:
2053 		if (info.maxLeafExtended >= 0x8000_001D) goto L_CACHE_AMD_EXT_1DH;
2054 		if (info.maxLeafExtended >= 0x8000_0005) goto L_CACHE_AMD_EXT_5H;
2055 		break;
2056 		
2057 		/*if (info.maxLeafExtended < 0x8000_001e) goto L_AMD_TOPOLOGY_EXT_8H;
2058 		
2059 		ddcpuid_id(regs, 0x8000_0001);
2060 		
2061 		if (regs.ecx & BIT!(22)) { // Topology extensions support
2062 			ddcpuid_id(regs, 0x8000_001e);
2063 			
2064 			info.cores.logical = regs.ch + 1;
2065 			info.cores.physical = regs.dh & 7;
2066 			goto L_AMD_CACHE;
2067 		}*/
2068 		
2069 /*L_AMD_TOPOLOGY_EXT_8H:
2070 		// See APM Volume 3 Appendix E.5
2071 		// For some reason, CPUID Fn8000_001E_EBX is not mentioned there
2072 		ddcpuid_id(regs, 0x8000_0008);
2073 		
2074 		type = regs.cx >> 12; // ApicIdSize
2075 		
2076 		if (type) { // Extended
2077 			info.cores.physical = regs.cl + 1;
2078 			info.cores.logical = cast(ushort)(1 << type);
2079 		} else { // Legacy
2080 			info.cores.logical = info.cores.physical = regs.cl + 1;
2081 		}*/
2082 		
2083 		//
2084 		// AMD newer cache method
2085 		//
2086 		
2087 L_CACHE_AMD_EXT_1DH: // Almost the same as Intel's
2088 		ddcpuid_id(regs, 0x8000_001d, info.cache.levels);
2089 		
2090 		type = regs.eax & CACHE_MASK; // EAX[4:0]
2091 		if (type == 0 || info.cache.levels >= CACHE_MAX_LEVEL) return;
2092 		
2093 		ca.type = CACHE_TYPE[type];
2094 		ca.level = (regs.eax >> 5) & 7;
2095 		ca.lineSize = (regs.ebx & 0xfff) + 1;
2096 		ca.partitions = ((regs.ebx >> 12) & 0x3ff) + 1;
2097 		ca.ways = (regs.ebx >> 22) + 1;
2098 		ca.sets = regs.ecx + 1;
2099 		if (regs.eax & BIT!(8)) ca.features = 1;
2100 		if (regs.eax & BIT!(9)) ca.features |= BIT!(1);
2101 		if (regs.edx & BIT!(0)) ca.features |= BIT!(2);
2102 		if (regs.edx & BIT!(1)) ca.features |= BIT!(3);
2103 		ca.size = (ca.sets * ca.lineSize * ca.partitions * ca.ways) >> 10;
2104 		
2105 		crshrd = (((regs.eax >> 14) & 0xfff) + 1); // bits 25-14
2106 		sc = cast(ushort)(info.sys.maxApicId / crshrd); // cast for ldc 0.17.1
2107 		ca.sharedCores = sc ? sc : 1;
2108 		
2109 		if (info.cores.logical == 0) with (info.cores) { // skip if already populated
2110 			logical = info.sys.maxApicId;
2111 			physical = info.tech.htt ? logical >> 1 : info.sys.maxApicId;
2112 		}
2113 		
2114 		version (Trace) trace("amd.8000_001Dh mids=%u shared=%u crshrd=%u sc=%u",
2115 			mids, ca.sharedCores, crshrd, sc);
2116 		
2117 		++info.cache.levels; ++ca;
2118 		goto L_CACHE_AMD_EXT_1DH;
2119 		
2120 		//
2121 		// AMD legacy cache
2122 		//
2123 		
2124 L_CACHE_AMD_EXT_5H:
2125 		ddcpuid_id(regs, 0x8000_0005);
2126 		
2127 		info.cache.level[0].level = 1; // L1-D
2128 		info.cache.level[0].type = 'D'; // data
2129 		info.cache.level[0].size = regs.ecx >> 24;
2130 		info.cache.level[0].ways = cast(ubyte)(regs.ecx >> 16);
2131 		info.cache.level[0].lines = regs.ch;
2132 		info.cache.level[0].lineSize = regs.cl;
2133 		info.cache.level[0].sets = 1;
2134 		
2135 		info.cache.level[1].level = 1; // L1-I
2136 		info.cache.level[1].type = 'I'; // instructions
2137 		info.cache.level[1].size = regs.edx >> 24;
2138 		info.cache.level[1].ways = cast(ubyte)(regs.edx >> 16);
2139 		info.cache.level[1].lines = regs.dh;
2140 		info.cache.level[1].lineSize = regs.dl;
2141 		info.cache.level[1].sets = 1;
2142 		
2143 		info.cache.levels = 2;
2144 		
2145 		if (info.maxLeafExtended < 0x8000_0006)
2146 			return; // No L2/L3
2147 		
2148 		// See Table E-4. L2/L3 Cache and TLB Associativity Field Encoding
2149 		static immutable ubyte[16] _amd_cache_ways = [
2150 			// 7h is reserved
2151 			// 9h mentions 8000_001D but that's already supported
2152 			0, 1, 2, 3, 4, 6, 8, 0, 16, 0, 32, 48, 64, 96, 128, 255
2153 		];
2154 		
2155 		ddcpuid_id(regs, 0x8000_0006);
2156 		
2157 		type = regs.cx >> 12; // amd_ways_l2
2158 		if (type) {
2159 			info.cache.level[2].level = 2;  // L2
2160 			info.cache.level[2].type = 'U'; // unified
2161 			info.cache.level[2].size = regs.ecx >> 16;
2162 			info.cache.level[2].ways = _amd_cache_ways[type];
2163 			info.cache.level[2].lines = regs.ch & 0xf;
2164 			info.cache.level[2].lineSize = regs.cl;
2165 			info.cache.level[2].sets = 1;
2166 			info.cache.levels = 3;
2167 			
2168 			type = regs.dx >> 12; // amd_ways_l3
2169 			if (type) {
2170 				info.cache.level[3].level = 3;  // L3
2171 				info.cache.level[3].type = 'U'; // unified
2172 				info.cache.level[3].size = ((regs.edx >> 18) + 1) << 9;
2173 				info.cache.level[3].ways = _amd_cache_ways[type];
2174 				info.cache.level[3].lines = regs.dh & 0xf;
2175 				info.cache.level[3].lineSize = regs.dl & 0x7F;
2176 				info.cache.level[3].sets = 1;
2177 				info.cache.levels = 4;
2178 			}
2179 		}
2180 		return;
2181 	default:
2182 	}
2183 	
2184 	with (info) cores.physical = cores.logical = 1;
2185 }
2186 
2187 private struct LeafInfo {
2188 	uint leaf;
2189 	uint sub;
2190 	void function(ref CPUINFO, ref REGISTERS) func;
2191 }
2192 private struct LeafExtInfo {
2193 	uint leaf;
2194 	void function(ref CPUINFO, ref REGISTERS) func;
2195 }
2196 
2197 /// Fetch CPU information.
2198 /// Params: info = CPUINFO structure
2199 pragma(inline, false)
2200 void ddcpuid_cpuinfo(ref CPUINFO info) {
2201 	static immutable LeafInfo[] regulars = [
2202 		{ 0x1,	0,	&ddcpuid_leaf1 },	// Sets brand index
2203 		{ 0x5,	0,	&ddcpuid_leaf5 },
2204 		{ 0x6,	0,	&ddcpuid_leaf6 },
2205 		{ 0x7,	0,	&ddcpuid_leaf7 },
2206 		{ 0x7,	1,	&ddcpuid_leaf7sub1 },
2207 		{ 0xd,	0,	&ddcpuid_leafD },
2208 		{ 0xd,	1,	&ddcpuid_leafDsub1 },
2209 		{ 0x12,	0,	&ddcpuid_leaf12 },
2210 	];
2211 	static immutable LeafExtInfo[] extended = [
2212 		{ 0x8000_0001,	&ddcpuid_leaf8000_0001 },
2213 		{ 0x8000_0007,	&ddcpuid_leaf8000_0007 },
2214 		{ 0x8000_0008,	&ddcpuid_leaf8000_0008 },
2215 		{ 0x8000_000a,	&ddcpuid_leaf8000_000A },
2216 	];
2217 	REGISTERS regs = void;	/// registers
2218 	
2219 	ddcpuid_vendor(info.vendor.string_);
2220 	info.vendor.id = ddcpuid_vendor_id(info.vendor);
2221 	
2222 	foreach (ref immutable(LeafInfo) l; regulars) {
2223 		if (l.leaf > info.maxLeaf) break;
2224 		
2225 		ddcpuid_id(regs, l.leaf, l.sub);
2226 		l.func(info, regs);
2227 	}
2228 	
2229 	// Paravirtualization leaves
2230 	if (info.maxLeafVirt >= 0x4000_0000) {
2231 		ddcpuid_virt_vendor(info.virt.vendor.string_);
2232 		info.virt.vendor.id = ddcpuid_virt_vendor_id(info.virt.vendor);
2233 		
2234 		switch (info.virt.vendor.id) with (VirtVendor) {
2235 		case KVM:
2236 			ddcpuid_id(regs, 0x4000_0001);
2237 			ddcpuid_leaf4000_0001(info, regs);
2238 			break;
2239 		case HyperV:
2240 			ddcpuid_id(regs, 0x4000_0002);
2241 			ddcpuid_leaf4000_0002(info, regs);
2242 			ddcpuid_id(regs, 0x4000_0003);
2243 			ddcpuid_leaf4000_0003(info, regs);
2244 			ddcpuid_id(regs, 0x4000_0004);
2245 			ddcpuid_leaf4000_0004(info, regs);
2246 			ddcpuid_id(regs, 0x4000_0006);
2247 			ddcpuid_leaf4000_0006(info, regs);
2248 			break;
2249 		case VBoxMin:
2250 			ddcpuid_id(regs, 0x4000_0010);
2251 			ddcpuid_leaf4000_0010(info, regs);
2252 			break;
2253 		default:
2254 		}
2255 	}
2256 	
2257 	// Extended leaves
2258 	if (info.maxLeafExtended >= 0x8000_0000) {
2259 		foreach (ref immutable(LeafExtInfo) l; extended) {
2260 			if (l.leaf > info.maxLeafExtended) break;
2261 			
2262 			ddcpuid_id(regs, l.leaf);
2263 			l.func(info, regs);
2264 		}
2265 	}
2266 	
2267 	ddcpuid_model_string(info); // Sets brand string
2268 	ddcpuid_topology(info);	 // Sets core/thread/cache topology
2269 }
2270 
2271 const(char) *ddcpuid_baseline(ref CPUINFO info) {
2272 	if (info.extensions.x86_64 == false) {
2273 		if (info.family >= 6) // Pentium Pro / II
2274 			return "i686";
2275 		// NOTE: K7 is still family 5 and didn't have SSE2.
2276 		return info.family == 5 ? "i586" : "i486"; // Pentium / MMX
2277 	}
2278 	
2279 	// v4
2280 	if (info.avx.avx512f && info.avx.avx512bw &&
2281 		info.avx.avx512cd && info.avx.avx512dq &&
2282 		info.avx.avx512vl) {
2283 		return "x86-64-v4";
2284 	}
2285 	
2286 	// v3
2287 	if (info.avx.avx2 && info.avx.avx &&
2288 		info.extensions.bmi2 && info.extensions.bmi1 &&
2289 		info.extensions.f16c && info.extensions.fma3 &&
2290 		info.extras.lzcnt && info.extras.movbe &&
2291 		info.extras.osxsave) {
2292 		return "x86-64-v3";
2293 	}
2294 	
2295 	// v2
2296 	if (info.sse.sse42 && info.sse.sse41 &&
2297 		info.sse.ssse3 && info.sse.sse3 &&
2298 		info.extensions.lahf64 && info.extras.popcnt &&
2299 		info.extras.cmpxchg16b) {
2300 		return "x86-64-v2";
2301 	}
2302 	
2303 	// baseline (v1)
2304 	/*if (info.sse.sse2 && info.sse.sse &&
2305 		info.extensions.mmx && info.extras.fxsr &&
2306 		info.extras.cmpxchg8b && info.extras.cmov &&
2307 		info.extensions.fpu && info.extras.syscall) {
2308 		return "x86-64";
2309 	}*/
2310 	
2311 	return "x86-64"; // v1 anyway
2312 }