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-2021 dd86k
28  * License: MIT
29  */
30 module ddcpuid;
31 
32 // NOTE: Please no naked assembler.
33 //       I'd rather let the compiler deal with a little bit of prolog and
34 //       epilog than slamming my head into my desk violently trying to match
35 //       every operating system ABI, compiler versions, and major compilers.
36 //       Besides, final compiled binary is plenty fine on every compiler.
37 // NOTE: GAS syntax reminder
38 //       asm { "asm;\n\t" : "constraint" output : "constraint" input : clobbers }
39 // NOTE: bhyve doesn't not emit cpuid bits within 0x40000000, so not supported
40 
41 @system:
42 extern (C):
43 
44 version (X86)
45 	enum DDCPUID_PLATFORM = "x86"; /// Target platform
46 else version (X86_64)
47 	enum DDCPUID_PLATFORM = "amd64"; /// Target platform
48 else static assert(0, "Unsupported platform");
49 
50 version (DigitalMars) {
51 	version = DMD;
52 } else version (GNU) {
53 	version = GDC;
54 } else version (LDC) {
55 	
56 } else static assert(0, "Unsupported compiler");
57 
58 enum DDCPUID_VERSION   = "0.19.1";	/// Library version
59 private enum CACHE_LEVELS = 6;	/// For buffer
60 private enum CACHE_MAX_LEVEL = CACHE_LEVELS - 1;
61 private enum VENDOR_OFFSET     = CPUINFO.vendorString.offsetof;
62 private enum BRAND_OFFSET      = CPUINFO.brandString.offsetof;
63 private enum VIRTVENDOR_OFFSET = CPUINFO.virt.offsetof + CPUINFO.virt.vendorString.offsetof;
64 
65 version (PrintInfo) {
66 	pragma(msg, "VENDOR_OFFSET\t", VENDOR_OFFSET);
67 	pragma(msg, "BRAND_OFFSET\t", BRAND_OFFSET);
68 	pragma(msg, "VIRTVENDOR_OFFSET\t", VIRTVENDOR_OFFSET);
69 	pragma(msg, "CPUINFO.sizeof\t", CPUINFO.sizeof);
70 	pragma(msg, "CACHE.sizeof\t", CACHEINFO.sizeof);
71 }
72 
73 /// Make a bit mask of one bit at n position
74 private
75 template BIT(int n) if (n <= 31) { enum uint BIT = 1 << n; }
76 
77 /// Vendor ID template
78 // Little-endian only, unless x86 gets any crazier
79 private
80 template ID(char[4] c) {
81 	enum uint ID = c[0] | c[1] << 8 | c[2] << 16 | c[3] << 24;
82 }
83 
84 /// Vendor ID.
85 ///
86 /// The CPUINFO.vendor_id field is set according to the Vendor String.
87 /// They are validated in the getVendor function, so they are safe to use.
88 enum Vendor {
89 	Other = 0,
90 	Intel = ID!("Genu"),	/// `"GenuineIntel"`: Intel
91 	AMD   = ID!("Auth"),	/// `"AuthenticAMD"`: AMD
92 	VIA   = ID!("VIA "),	/// `"VIA VIA VIA "`: VIA
93 }
94 
95 /// Virtual Vendor ID, used as the interface type.
96 ///
97 /// The CPUINFO.virt.vendor_id field is set according to the Vendor String.
98 /// They are validated in the getVendor function, so they are safe to use.
99 /// The VBoxHyperV ID will be adjusted for HyperV since it's the same interface,
100 /// but simply a different implementation.
101 enum VirtVendor {
102 	Other = 0,
103 	KVM        = ID!("KVMK"), /// `"KVMKVMKVM\0\0\0"`: KVM
104 	HyperV     = ID!("Micr"), /// `"Microsoft Hv"`: Hyper-V interface
105 	VBoxHyperV = ID!("VBox"), /// `"VBoxVBoxVBox"`: VirtualBox's Hyper-V interface
106 	VBoxMin    = 0, /// Unset: VirtualBox minimal interface
107 }
108 
109 /// Registers structure used with the asmcpuid function.
110 struct REGISTERS {
111 	union {
112 		uint eax;
113 		ushort ax;
114 		struct { ubyte al, ah; }
115 	}
116 	union {
117 		uint ebx;
118 		ushort bx;
119 		struct { ubyte bl, bh; }
120 	}
121 	union {
122 		uint ecx;
123 		ushort cx;
124 		struct { ubyte cl, ch; }
125 	}
126 	union {
127 		uint edx;
128 		ushort dx;
129 		struct { ubyte dl, dh; }
130 	}
131 }
132 ///
133 @system unittest {
134 	REGISTERS regs = void;
135 	regs.eax = 0xaabbccdd;
136 	assert(regs.eax == 0xaabbccdd);
137 	assert(regs.ax  == 0xccdd);
138 	assert(regs.al  == 0xdd);
139 	assert(regs.ah  == 0xcc);
140 }
141 
142 /// CPU cache entry
143 struct CACHEINFO { align(1):
144 	deprecated union {
145 		package uint __bundle1;
146 		struct {
147 			ubyte linesize; /// Size of the line in bytes
148 			ubyte partitions_;	/// Number of partitions
149 			ubyte ways_;	/// Number of ways per line
150 			package ubyte _amdsize;	/// (AMD, legacy) Size in KiB
151 		}
152 	}
153 	ushort lineSize;	/// Size of the line in bytes.
154 	union {
155 		ushort partitions;	/// Number of partitions.
156 		ushort lines;	/// AMD legacy way of saying sets.
157 	}
158 	ushort ways;	/// Number of ways per line.
159 	uint sets; /// Number of cache sets.
160 	/// Cache size in kilobytes.
161 	// (Ways + 1) * (Partitions + 1) * (LineSize + 1) * (Sets + 1)
162 	// (EBX[31:22] + 1) * (EBX[21:12] + 1) * (EBX[11:0] + 1) * (ECX + 1)
163 	uint size;
164 	/// Number of CPU cores sharing this cache.
165 	ushort sharedCores;
166 	/// Cache feature, bit flags.
167 	/// - Bit 0: Self Initializing cache
168 	/// - Bit 1: Fully Associative cache
169 	/// - Bit 2: No Write-Back Invalidation (toggle)
170 	/// - Bit 3:  Cache Inclusiveness (toggle)
171 	/// - Bit 4: Complex Cache Indexing (toggle)
172 	ushort features;
173 	ubyte level;	/// Cache level: L1, L2, etc.
174 	char type = 0;	/// Type entry character: 'D'=Data, 'I'=Instructions, 'U'=Unified
175 }
176 
177 /// CPU information structure
178 struct CPUINFO { align(1):
179 	uint maxLeaf;	/// Highest cpuid leaf
180 	uint maxLeafVirt;	/// Highest cpuid virtualization leaf
181 	uint maxLeafExtended;	/// Highest cpuid extended leaf
182 	
183 	// Vendor strings
184 	
185 	union {
186 		package uint[3] vendor32;	/// Vendor 32-bit parts
187 		char[12] vendorString;	/// Vendor String
188 	}
189 	union {
190 		package uint[12] brand32;	// For init
191 		char[48] brandString;	/// Processor Brand String
192 	}
193 	union {
194 		package uint vendorId32;
195 		Vendor vendorId;	/// Validated vendor ID
196 	}
197 	ubyte brandIndex;	/// Brand string index (not used)
198 	
199 	// Core
200 	
201 	/// Contains the information on the number of cores.
202 	struct Cores {
203 		ushort logical;	/// Logical cores in this processor
204 		ushort physical;	/// Physical cores in this processor
205 	}
206 	align(2) Cores cores;	/// Processor package cores
207 	
208 	// Identifier
209 	
210 	uint identifier;	/// Raw identifier (EAX)
211 	ubyte family;	/// Effective family identifier
212 	ubyte familyBase;	/// Base family identifier
213 	ubyte familyExtended;	/// Extended family identifier
214 	ubyte model;	/// Effective model identifier
215 	ubyte modelBase;	/// Base model identifier
216 	ubyte modelExtended;	/// Extended model identifier
217 	ubyte stepping;	/// Stepping revision
218 	ubyte type;	/// Processor type number
219 	const(char) *typeString;	/// Processor type string.
220 	
221 	/// Contains processor extensions.
222 	/// Extensions contain a variety of instructions to aid particular
223 	/// tasks.
224 	struct Extensions {
225 		bool fpu;	/// On-Chip x87 FPU
226 		bool f16c;	/// Float16 Conversions
227 		bool mmx;	/// MMX
228 		bool mmxExtended;	/// MMX Extended
229 		bool _3DNow;	/// 3DNow!
230 		bool _3DNowExtended;	/// 3DNow! Extended
231 		bool aes_ni;	/// Advanced Encryption Standard New Instructions
232 		bool sha;	/// SHA-1
233 		bool fma3;	/// Fused Multiply-Add
234 		bool fma4;	/// FMA4
235 		bool bmi1;	/// BMI1
236 		bool bmi2;	/// BMI2
237 		bool x86_64;	/// 64-bit mode (Long mode)
238 		bool lahf64;	/// LAHF+SAHF in 64-bit mode
239 		bool waitpkg;	/// User Level Monitor Wait (UMWAIT)
240 		bool xop;	/// AMD eXtended OPerations
241 		bool tbm;	/// Trailing Bit Manipulation
242 		bool adx;	/// Multi-precision Add-Carry (ADCX+ADOX)
243 	}
244 	align(2) Extensions extensions;	/// Extensions
245 		
246 	struct SSE {
247 		bool sse;	/// Streaming SIMD Extensions
248 		bool sse2;	/// SSE2
249 		bool sse3;	/// SSE3
250 		bool ssse3;	/// SSSE3
251 		bool sse41;	/// SSE4.1
252 		bool sse42;	/// SSE4.2
253 		bool sse4a;	/// SSE4a
254 	}
255 	align(2) SSE sse;	/// Streaming SIMD Extensions
256 	
257 	struct AVX {
258 		bool avx;	/// Advanced Vector eXtension
259 		bool avx2;	/// AVX2
260 		bool avx512f;	/// AVX512
261 		bool avx512er;	/// AVX512_ER
262 		bool avx512pf;	/// AVX512_PF
263 		bool avx512cd;	/// AVX512_CD
264 		bool avx512dq;	/// AVX512_DQ
265 		bool avx512bw;	/// AVX512_BW
266 		bool avx512vl;	/// AVX512_VL
267 		bool avx512_ifma;	/// AVX512_IFMA
268 		bool avx512_vbmi;	/// AVX512_VBMI
269 		bool avx512_vbmi2;	/// AVX512_VBMI2
270 		bool avx512_gfni;	/// AVX512_GFNI
271 		bool avx512_vaes;	/// AVX512_VAES
272 		bool avx512_vnni;	/// AVX512_VNNI
273 		bool avx512_bitalg;	/// AVX512_BITALG
274 		bool avx512_vpopcntdq;	/// AVX512_VPOPCNTDQ
275 		bool avx512_4vnniw;	/// AVX512_4VNNIW
276 		bool avx512_4fmaps;	/// AVX512_4FMAPS
277 		bool avx512_bf16;	/// AVX512_BF16
278 		bool avx512_vp2intersect;	/// AVX512_VP2INTERSECT
279 	}
280 	align(2) AVX avx;	/// Advanced Vector eXtension
281 	
282 	struct AMX {
283 		bool enabled;	/// Advanced Matrix eXtension
284 		bool bf16;	/// AMX_BF16
285 		bool int8;	/// AMX_INT8
286 		bool xtilecfg;	/// AMX_XTILECFG
287 		bool xtiledata;	/// AMX_XTILEDATA
288 		bool xfd;	/// AMX_XFD
289 	}
290 	align(2) AMX amx;	/// Intel AMX
291 	
292 	struct SGX {
293 		bool supported;	/// If SGX is supported (and enabled)
294 		bool sgx1;	/// SGX1
295 		bool sgx2;	/// SGX2
296 		ubyte maxSize;	/// 2^n maximum enclave size in non-64-bit
297 		ubyte maxSize64;	/// 2^n maximum enclave size in 64-bit
298 	}
299 	align(2) SGX sgx;	/// Intel SGX
300 	
301 	/// Additional instructions. Often not part of extensions.
302 	struct Extras {
303 		bool pclmulqdq;	/// PCLMULQDQ instruction
304 		bool monitor;	/// MONITOR and MWAIT instructions
305 		ushort mwaitMin;	/// (With MONITOR+MWAIT) MWAIT minimum size in bytes
306 		ushort mwaitMax;	/// (With MONITOR+MWAIT) MWAIT maximum size in bytes
307 		bool cmpxchg8b;	/// CMPXCHG8B
308 		bool cmpxchg16b;	/// CMPXCHG16B instruction
309 		bool movbe;	/// MOVBE instruction
310 		bool rdrand;	/// RDRAND instruction
311 		bool rdseed;	/// RDSEED instruction
312 		bool rdmsr;	/// RDMSR instruction
313 		bool sysenter;	/// SYSENTER and SYSEXIT instructions
314 		bool rdtsc;	/// RDTSC instruction
315 		bool rdtscDeadline;	/// (With RDTSC) IA32_TSC_DEADLINE MSR
316 		bool rdtscInvariant;	/// (With RDTSC) Timestamp counter invariant of C/P/T-state
317 		bool rdtscp;	/// RDTSCP instruction
318 		bool rdpid;	/// RDPID instruction
319 		bool cmov;	/// CMOVcc instruction
320 		bool lzcnt;	/// LZCNT instruction
321 		bool popcnt;	/// POPCNT instruction
322 		bool xsave;	/// XSAVE and XRSTOR instructions
323 		bool osxsave;	/// OSXSAVE and XGETBV instructions
324 		bool fxsr;	/// FXSAVE and FXRSTOR instructions
325 		bool pconfig;	/// PCONFIG instruction
326 		bool cldemote;	/// CLDEMOTE instruction
327 		bool movdiri;	/// MOVDIRI instruction
328 		bool movdir64b;	/// MOVDIR64B instruction
329 		bool enqcmd;	/// ENQCMD instruction
330 		bool syscall;	/// SYSCALL and SYSRET instructions
331 		bool monitorx;	/// MONITORX and MWAITX instructions
332 		bool skinit;	/// SKINIT instruction
333 		bool serialize;	/// SERIALIZE instruction
334 	}
335 	align(2) Extras extras;	/// Additional instructions
336 	
337 	/// Processor technologies.
338 	struct Technologies {
339 		bool eist;	/// Intel SpeedStep/AMD PowerNow/AMD Cool'n'Quiet
340 		bool turboboost;	/// Intel TurboBoost/AMD CorePerformanceBoost
341 		bool turboboost30;	/// Intel TurboBoost 3.0
342 		bool smx;	/// Intel TXT
343 		bool htt;	/// (HTT) HyperThreading Technology
344 	}
345 	align(2) Technologies tech;	/// Processor technologies
346 	
347 	/// Cache information.
348 	struct CacheInfo {
349 		uint levels;
350 		CACHEINFO[CACHE_LEVELS] level;
351 		bool clflush;	/// CLFLUSH instruction
352 		ubyte clflushLinesize;	/// Linesize of CLFLUSH in bytes
353 		bool clflushopt;	/// CLFLUSH instruction
354 		bool cnxtId;	/// L1 Context ID
355 		bool ss;	/// SelfSnoop
356 		bool prefetchw;	/// PREFETCHW instruction
357 		bool invpcid;	/// INVPCID instruction
358 		bool wbnoinvd;	/// WBNOINVD instruction
359 	}
360 	align(2) CacheInfo cache;	/// Cache information
361 	
362 	/// ACPI information.
363 	struct SysInfo {
364 		bool available;	/// ACPI
365 		bool apic;	/// APIC
366 		bool x2apic;	/// x2APIC
367 		bool arat;	/// Always-Running-APIC-Timer
368 		bool tm;	/// Thermal Monitor
369 		bool tm2;	/// Thermal Monitor 2
370 		ubyte maxApicId;	/// Maximum APIC ID
371 		ubyte apicId;	/// Initial APIC ID (running core where CPUID was called)
372 	}
373 	align(2) SysInfo sys;	/// System features
374 	
375 	/// Virtualization features. If a paravirtual interface is available,
376 	/// its information will be found here.
377 	struct Virtualization {
378 		bool available;	/// Intel VT-x/AMD-V
379 		ubyte version_;	/// (AMD) Virtualization platform version
380 		bool vme;	/// Enhanced vm8086
381 		bool apicv;	/// (AMD) APICv. Intel's is available via a MSR.
382 		union {
383 			package uint[3] vendor32;
384 			char[12] vendorString;	/// Paravirtualization interface vendor string
385 		}
386 		union {
387 			package uint vendorId32;
388 			VirtVendor vendorId;	/// Effective paravirtualization vendor id
389 		}
390 		
391 		struct VBox {
392 			uint tsc_freq_khz;	/// (VBox) Timestamp counter frequency in KHz
393 			uint apic_freq_khz;	/// (VBox) Paravirtualization API KHz frequency
394 		}
395 		VBox vbox;
396 		
397 		struct KVM {
398 			bool feature_clocksource;	/// (KVM) kvmclock interface
399 			bool feature_nop_io_delay;	/// (KVM) No delays required on I/O operations
400 			bool feature_mmu_op;	/// (KVM) Deprecated
401 			bool feature_clocksource2;	/// (KVM) Remapped kvmclock interface
402 			bool feature_async_pf;	/// (KVM) Asynchronous Page Fault
403 			bool feature_steal_time;	/// (KVM) Steal time
404 			bool feature_pv_eoi;	/// (KVM) Paravirtualized End Of the Interrupt handler
405 			bool feature_pv_unhault;	/// (KVM) Paravirtualized spinlock
406 			bool feature_pv_tlb_flush;	/// (KVM) Paravirtualized TLB flush
407 			bool feature_async_pf_vmexit;	/// (KVM) Asynchronous Page Fault at VM exit
408 			bool feature_pv_send_ipi;	/// (KVM) Paravirtualized SEBD inter-processor-interrupt
409 			bool feature_pv_poll_control;	/// (KVM) Host-side polling on HLT
410 			bool feature_pv_sched_yield;	/// (KVM) paravirtualized scheduler yield
411 			bool feature_clocsource_stable_bit;	/// (KVM) kvmclock warning
412 			bool hint_realtime;	/// (KVM) vCPUs are never preempted for an unlimited amount of time
413 		}
414 		KVM kvm;
415 		
416 		struct HyperV {
417 			ushort guest_vendor_id;	/// (Hyper-V) Paravirtualization Guest Vendor ID
418 			ushort guest_build;	/// (Hyper-V) Paravirtualization Guest Build number
419 			ubyte guest_os;	/// (Hyper-V) Paravirtualization Guest OS ID
420 			ubyte guest_major;	/// (Hyper-V) Paravirtualization Guest OS Major version
421 			ubyte guest_minor;	/// (Hyper-V) Paravirtualization Guest OS Minor version
422 			ubyte guest_service;	/// (Hyper-V) Paravirtualization Guest Service ID
423 			bool guest_opensource;	/// (Hyper-V) Paravirtualization Guest additions open-source
424 			bool base_feat_vp_runtime_msr;	/// (Hyper-V) Virtual processor runtime MSR
425 			bool base_feat_part_time_ref_count_msr;	/// (Hyper-V) Partition reference counter MSR
426 			bool base_feat_basic_synic_msrs;	/// (Hyper-V) Basic Synthetic Interrupt Controller MSRs
427 			bool base_feat_stimer_msrs;	/// (Hyper-V) Synthetic Timer MSRs
428 			bool base_feat_apic_access_msrs;	/// (Hyper-V) APIC access MSRs (EOI, ICR, TPR)
429 			bool base_feat_hypercall_msrs;	/// (Hyper-V) Hypercalls API MSRs
430 			bool base_feat_vp_id_msr;	/// (Hyper-V) vCPU index MSR
431 			bool base_feat_virt_sys_reset_msr;	/// (Hyper-V) Virtual system reset MSR
432 			bool base_feat_stat_pages_msr;	/// (Hyper-V) Statistic pages MSRs
433 			bool base_feat_part_ref_tsc_msr;	/// (Hyper-V) Partition reference timestamp counter MSR
434 			bool base_feat_guest_idle_state_msr;	/// (Hyper-V) Virtual guest idle state MSR
435 			bool base_feat_timer_freq_msrs;	/// (Hyper-V) Timer frequency MSRs (TSC and APIC)
436 			bool base_feat_debug_msrs;	/// (Hyper-V) Debug MSRs
437 			bool part_flags_create_part;	/// (Hyper-V) Partitions can be created
438 			bool part_flags_access_part_id;	/// (Hyper-V) Partitions IDs can be accessed
439 			bool part_flags_access_memory_pool;	/// (Hyper-V) Memory pool can be accessed
440 			bool part_flags_adjust_msg_buffers;	/// (Hyper-V) Possible to adjust message buffers
441 			bool part_flags_post_msgs;	/// (Hyper-V) Possible to send messages
442 			bool part_flags_signal_events;	/// (Hyper-V) Possible to signal events
443 			bool part_flags_create_port;	/// (Hyper-V) Possible to create ports
444 			bool part_flags_connect_port;	/// (Hyper-V) Possible to connect to ports
445 			bool part_flags_access_stats;	/// (Hyper-V) Can access statistics
446 			bool part_flags_debugging;	/// (Hyper-V) Debugging features available
447 			bool part_flags_cpu_mgmt;	/// (Hyper-V) Processor management available
448 			bool part_flags_cpu_profiler;	/// (Hyper-V) Processor profiler available
449 			bool part_flags_expanded_stack_walk;	/// (Hyper-V) Extended stack walking available
450 			bool part_flags_access_vsm;	/// (Hyper-V) Virtual system monitor available
451 			bool part_flags_access_vp_regs;	/// (Hyper-V) Virtual private registers available
452 			bool part_flags_extended_hypercalls;	/// (Hyper-V) Extended hypercalls API available
453 			bool part_flags_start_vp;	/// (Hyper-V) Virtual processor has started
454 			bool pm_max_cpu_power_state_c0;	/// (Hyper-V) Processor C0 is maximum state
455 			bool pm_max_cpu_power_state_c1;	/// (Hyper-V) Processor C1 is maximum state
456 			bool pm_max_cpu_power_state_c2;	/// (Hyper-V) Processor C2 is maximum state
457 			bool pm_max_cpu_power_state_c3;	/// (Hyper-V) Processor C3 is maximum state
458 			bool pm_hpet_reqd_for_c3;	/// (Hyper-V) High-precision event timer required for C3 state
459 			bool misc_feat_mwait;	/// (Hyper-V) MWAIT instruction available for guest
460 			bool misc_feat_guest_debugging;	/// (Hyper-V) Guest supports debugging
461 			bool misc_feat_perf_mon;	/// (Hyper-V) Performance monitor support available
462 			bool misc_feat_pcpu_dyn_part_event;	/// (Hyper-V) Physicap CPU dynamic partitioning event available
463 			bool misc_feat_xmm_hypercall_input;	/// (Hyper-V) Hypercalls via XMM registers available
464 			bool misc_feat_guest_idle_state;	/// (Hyper-V) Virtual guest supports idle state
465 			bool misc_feat_hypervisor_sleep_state;	/// (Hyper-V) Hypervisor supports sleep
466 			bool misc_feat_query_numa_distance;	/// (Hyper-V) NUMA distance query available
467 			bool misc_feat_timer_freq;	/// (Hyper-V) Determining timer frequencies available
468 			bool misc_feat_inject_synmc_xcpt;	/// (Hyper-V) Support for injecting synthetic machine checks
469 			bool misc_feat_guest_crash_msrs;	/// (Hyper-V) Guest crash MSR available
470 			bool misc_feat_debug_msrs;	/// (Hyper-V) Debug MSR available
471 			bool misc_feat_npiep1;	/// (Hyper-V) Documentation unavailable
472 			bool misc_feat_disable_hypervisor;	/// (Hyper-V) Hypervisor can be disabled
473 			bool misc_feat_ext_gva_range_for_flush_va_list;	/// (Hyper-V) Extended guest virtual address (GVA) ranges for FlushVirtualAddressList available
474 			bool misc_feat_hypercall_output_xmm;	/// (Hyper-V) Returning hypercall output via XMM registers available
475 			bool misc_feat_sint_polling_mode;	/// (Hyper-V) Synthetic interrupt source polling mode available
476 			bool misc_feat_hypercall_msr_lock;	/// (Hyper-V) Hypercall MISR lock feature available
477 			bool misc_feat_use_direct_synth_msrs;	/// (Hyper-V) Possible to directly use synthetic MSRs
478 			bool hint_hypercall_for_process_switch;	/// (Hyper-V) Guest should use the Hypercall API for address space switches rather than MOV CR3
479 			bool hint_hypercall_for_tlb_flush;	/// (Hyper-V) Guest should use the Hypercall API for local TLB flushes rather than INVLPG/MOV CR3
480 			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)
481 			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)
482 			bool hint_msr_for_sys_reset;	/// (Hyper-V) Guest should use the hypervisor-provided MSR for a system reset instead of traditional methods
483 			bool hint_relax_time_checks;	/// (Hyper-V) Guest should relax timer-related checks (watchdogs/deadman timeouts) that rely on timely deliver of external interrupts
484 			bool hint_dma_remapping;	/// (Hyper-V) Guest should use the direct memory access (DMA) remapping
485 			bool hint_interrupt_remapping;	/// (Hyper-V) Guest should use the interrupt remapping
486 			bool hint_x2apic_msrs;	/// (Hyper-V) Guest should use the X2APIC MSRs rather than memory mapped input/output (MMIO)
487 			bool hint_deprecate_auto_eoi;	/// (Hyper-V) Guest should deprecate Auto EOI (End Of Interrupt) features
488 			bool hint_synth_cluster_ipi_hypercall;	/// (Hyper-V) Guest should use the SyntheticClusterIpi Hypercall
489 			bool hint_ex_proc_masks_interface;	/// (Hyper-V) Guest should use the newer ExProcessMasks interface over ProcessMasks
490 			bool hint_nested_hyperv;	/// (Hyper-V) Hyper-V instance is nested within a Hyper-V partition
491 			bool hint_int_for_mbec_syscalls;	/// (Hyper-V) Guest should use the INT instruction for Mode Based Execution Control (MBEC) system calls
492 			bool hint_nested_enlightened_vmcs_interface;	/// (Hyper-V) Guest should use enlightened Virtual Machine Control Structure (VMCS) interfaces and nested enlightenment
493 			bool host_feat_avic;	/// (Hyper-V) Hypervisor is using the Advanced Virtual Interrupt Controller (AVIC) overlay
494 			bool host_feat_msr_bitmap;	/// (Hyper-V) Hypervisor is using MSR bitmaps
495 			bool host_feat_perf_counter;	/// (Hyper-V) Hypervisor supports the architectural performance counter
496 			bool host_feat_nested_paging;	/// (Hyper-V) Hypervisor is using nested paging
497 			bool host_feat_dma_remapping;	/// (Hyper-V) Hypervisor is using direct memory access (DMA) remapping
498 			bool host_feat_interrupt_remapping;	/// (Hyper-V) Hypervisor is using interrupt remapping
499 			bool host_feat_mem_patrol_scrubber;	/// (Hyper-V) Hypervisor's memory patrol scrubber is present
500 			bool host_feat_dma_prot_in_use;	/// (Hyper-V) Hypervisor is using direct memory access (DMA) protection
501 			bool host_feat_hpet_requested;	/// (Hyper-V) Hypervisor requires a High Precision Event Timer (HPET)
502 			bool host_feat_stimer_volatile;	/// (Hyper-V) Hypervisor's synthetic timers are volatile
503 		}
504 		HyperV hv;
505 	}
506 	align(2) Virtualization virt;	/// Virtualization features
507 	
508 	/// Memory features.
509 	struct Memory {
510 		bool pae;	/// Physical Address Extension 
511 		bool pse;	/// Page Size Extension
512 		bool pse36;	/// 36-bit PSE
513 		bool page1gb;	/// 1GiB pages in 4-level paging and higher
514 		bool mtrr;	/// Memory Type Range Registers
515 		bool pat;	/// Page Attribute Table
516 		bool pge;	/// Page Global Bit
517 		bool dca;	/// Direct Cache Access
518 		bool nx;	/// Intel XD (No eXecute bit)
519 		union {
520 			uint tsx;	/// Intel TSX. If set, has one of HLE, RTM, or TSXLDTRK.
521 			struct {
522 				bool hle;	/// (TSX) Hardware Lock Elision
523 				bool rtm;	/// (TSX) Restricted Transactional Memory
524 				bool tsxldtrk;	/// (TSX) Suspend Load Address Tracking
525 			}
526 		}
527 		bool smep;	/// Supervisor Mode Execution Protection
528 		bool smap;	/// Supervisor Mode Access Protection
529 		bool pku;	/// Protection Key Units
530 		bool _5pl;	/// 5-level paging
531 		bool fsrepmov;	/// Fast Short REP MOVSB optimization
532 		bool lam;	/// Linear Address Masking
533 		union {
534 			package ushort b_8000_0008_ax;
535 			struct {
536 				ubyte physBits;	/// Memory physical bits
537 				ubyte lineBits;	/// Memory linear bits
538 			}
539 		}
540 	}
541 	align (2) Memory memory;	/// Memory features
542 	
543 	/// Debugging features.
544 	struct Debugging {
545 		bool mca;	/// Machine Check Architecture
546 		bool mce;	/// Machine Check Exception
547 		bool de;	/// Degging Extensions
548 		bool ds;	/// Debug Store
549 		bool dsCpl;	/// Debug Store - Curernt Privilege Level
550 		bool dtes64;	/// 64-bit Debug Store area
551 		bool pdcm;	/// Perfmon And Debug Capability
552 		bool sdbg;	/// Silicon Debug
553 		bool pbe;	/// Pending Break Enable
554 	}
555 	align(2) Debugging debugging;	/// Debugging feature
556 	
557 	/// Security features and mitigations.
558 	struct Security {
559 		bool ia32_arch_capabilities;	/// IA32_ARCH_CAPABILITIES MSR
560 		// NOTE: IA32_CORE_CAPABILITIES is currently empty
561 		bool ibpb;	/// Indirect Branch Predictor Barrier
562 		bool ibrs;	/// Indirect Branch Restricted Speculation
563 		bool ibrsAlwaysOn;	/// IBRS always enabled
564 		bool ibrsPreferred;	/// IBRS preferred over software solution
565 		bool stibp;	/// Single Thread Indirect Branch Predictors
566 		bool stibpAlwaysOn;	/// STIBP always enabled
567 		bool ssbd;	/// Speculative Store Bypass Disable
568 		bool l1dFlush;	/// L1D Cache Flush
569 		bool md_clear;	/// MDS mitigation
570 		bool cetIbt;	/// (Control-flow Enforcement Technology) Indirect Branch Tracking 
571 		bool cetSs;	/// (Control-flow Enforcement Technology) Shadow Stack
572 	}
573 	align(2) Security security;	/// Security features
574 	
575 	/// Miscellaneous features.
576 	struct Miscellaneous {
577 		bool psn;	/// Processor Serial Number (Pentium III only)
578 		bool pcid;	/// PCID
579 		bool xtpr;	/// xTPR
580 		bool fsgsbase;	/// FS and GS register base
581 		bool uintr;	/// User Interrupts
582 	}
583 	align(2) Miscellaneous misc;	/// Miscellaneous features
584 }
585 
586 // EAX[4:0], 0-31, but there aren't that many
587 // So we limit it to 0-7
588 private enum CACHE_MASK = 7; // Max 31
589 private immutable const(char)* CACHE_TYPE = "?DIU????";
590 
591 private
592 immutable const(char)*[4] PROCESSOR_TYPE = [ "Original", "OverDrive", "Dual", "Reserved" ];
593 
594 version (Trace) {
595 	import core.stdc.stdio;
596 	import core.stdc.stdarg;
597 	
598 	private extern (C) int putchar(int);
599 	
600 	/// Trace application
601 	void trace(string func = __FUNCTION__)(const(char) *fmt, ...) {
602 		va_list va;
603 		va_start(va, fmt);
604 		printf("TRACE:%s: ", func.ptr);
605 		vprintf(fmt, va);
606 		putchar('\n');
607 	}
608 }
609 
610 /// Query processor with CPUID.
611 /// Params:
612 ///   regs = REGISTERS structure
613 ///   level = Leaf (EAX)
614 ///   sublevel = Sub-leaf (ECX)
615 pragma(inline, false)
616 void asmcpuid(ref REGISTERS regs, uint level, uint sublevel = 0) {
617 	version (DMD) {
618 		version (X86) asm {
619 			mov EDI, regs;
620 			mov EAX, level;
621 			mov ECX, sublevel;
622 			cpuid;
623 			mov [EDI + regs.eax.offsetof], EAX;
624 			mov [EDI + regs.ebx.offsetof], EBX;
625 			mov [EDI + regs.ecx.offsetof], ECX;
626 			mov [EDI + regs.edx.offsetof], EDX;
627 		} else version (X86_64) asm {
628 			mov RDI, regs;
629 			mov EAX, level;
630 			mov ECX, sublevel;
631 			cpuid;
632 			mov [RDI + regs.eax.offsetof], EAX;
633 			mov [RDI + regs.ebx.offsetof], EBX;
634 			mov [RDI + regs.ecx.offsetof], ECX;
635 			mov [RDI + regs.edx.offsetof], EDX;
636 		}
637 	} else version (GDC) {
638 		asm {
639 			"cpuid"
640 			: "=a" (regs.eax), "=b" (regs.ebx), "=c" (regs.ecx), "=d" (regs.edx)
641 			: "a" (level), "c" (sublevel);
642 		}
643 	} else version (LDC) {
644 		version (X86) asm {
645 			lea EDI, regs;
646 			mov EAX, level;
647 			mov ECX, sublevel;
648 			cpuid;
649 			mov [EDI + regs.eax.offsetof], EAX;
650 			mov [EDI + regs.ebx.offsetof], EBX;
651 			mov [EDI + regs.ecx.offsetof], ECX;
652 			mov [EDI + regs.edx.offsetof], EDX;
653 		} else version (X86_64) asm {
654 			lea RDI, regs;
655 			mov EAX, level;
656 			mov ECX, sublevel;
657 			cpuid;
658 			mov [RDI + regs.eax.offsetof], EAX;
659 			mov [RDI + regs.ebx.offsetof], EBX;
660 			mov [RDI + regs.ecx.offsetof], ECX;
661 			mov [RDI + regs.edx.offsetof], EDX;
662 		}
663 	}
664 	version (Trace) with (regs) trace(
665 		"level=%x sub=%x -> eax=%x ebx=%x ecx=%x edx=%x",
666 		level, sublevel, eax, ebx, ecx, edx);
667 }
668 /// 
669 @system unittest {
670 	REGISTERS regs;
671 	asmcpuid(regs, 0);
672 	assert(regs.eax > 0 && regs.eax < 0x4000_0000);
673 	asmcpuid(regs, 0x8000_0000);
674 	assert(regs.eax > 0x8000_0000);
675 }
676 
677 /// Get CPU leaf levels.
678 /// Params: info = CPUINFO structure
679 pragma(inline, false)
680 void getLeaves(ref CPUINFO info) {
681 	version (DMD) {
682 		version (X86) asm {
683 			mov EDI, info;
684 			mov EAX, 0;
685 			cpuid;
686 			mov [EDI + info.maxLeaf.offsetof], EAX;
687 			mov EAX, 0x4000_0000;
688 			cpuid;
689 			mov [EDI + info.maxLeafVirt.offsetof], EAX;
690 			mov EAX, 0x8000_0000;
691 			cpuid;
692 			mov [EDI + info.maxLeafExtended.offsetof], EAX;
693 		} else version (X86_64) asm {
694 			mov RDI, info;
695 			mov EAX, 0;
696 			cpuid;
697 			mov [RDI + info.maxLeaf.offsetof], EAX;
698 			mov EAX, 0x4000_0000;
699 			cpuid;
700 			mov [RDI + info.maxLeafVirt.offsetof], EAX;
701 			mov EAX, 0x8000_0000;
702 			cpuid;
703 			mov [RDI + info.maxLeafExtended.offsetof], EAX;
704 		}
705 	} else version (GDC) {
706 		asm {
707 			"cpuid"
708 			: "=a" (info.maxLeaf)
709 			: "a" (0);
710 		}
711 		asm {
712 			"cpuid"
713 			: "=a" (info.maxLeafVirt)
714 			: "a" (0x40000000);
715 		}
716 		asm {
717 			"cpuid"
718 			: "=a" (info.maxLeafExtended)
719 			: "a" (0x80000000);
720 		}
721 	} else version (LDC) {
722 		version (X86) asm {
723 			lea EDI, info;
724 			mov EAX, 0;
725 			cpuid;
726 			mov [EDI + info.maxLeaf.offsetof], EAX;
727 			mov EAX, 0x4000_0000;
728 			cpuid;
729 			mov [EDI + info.maxLeafVirt.offsetof], EAX;
730 			mov EAX, 0x8000_0000;
731 			cpuid;
732 			mov [EDI + info.maxLeafExtended.offsetof], EAX;
733 		} else version (X86_64) asm {
734 			lea RDI, info;
735 			mov EAX, 0;
736 			cpuid;
737 			mov [RDI + info.maxLeaf.offsetof], EAX;
738 			mov EAX, 0x4000_0000;
739 			cpuid;
740 			mov [RDI + info.maxLeafVirt.offsetof], EAX;
741 			mov EAX, 0x8000_0000;
742 			cpuid;
743 			mov [RDI + info.maxLeafExtended.offsetof], EAX;
744 		}
745 	}
746 	version (Trace) with(info) trace(
747 		"leaf=%x vleaf=%x eleaf=%x",
748 		maxLeaf, maxLeafVirt, maxLeafExtended);
749 }
750 
751 /// Fetch CPU vendor
752 pragma(inline, false)
753 void getVendor(ref CPUINFO info) {
754 	version (DMD) {
755 		version (X86) asm {
756 			mov EDI, info;
757 			mov EAX, 0;
758 			cpuid;
759 			mov [EDI + VENDOR_OFFSET], EBX;
760 			mov [EDI + VENDOR_OFFSET + 4], EDX;
761 			mov [EDI + VENDOR_OFFSET + 8], ECX;
762 		} else asm { // x86-64
763 			mov RDI, info;
764 			mov EAX, 0;
765 			cpuid;
766 			mov [RDI + VENDOR_OFFSET], EBX;
767 			mov [RDI + VENDOR_OFFSET + 4], EDX;
768 			mov [RDI + VENDOR_OFFSET + 8], ECX;
769 		}
770 	} else version (GDC) {
771 		version (X86) asm {
772 			"lea %0, %%edi\n\t"~
773 			"mov $0, %%eax\n\t"~
774 			"cpuid\n"~
775 			"mov %%ebx, (%%edi)\n\t"~
776 			"mov %%edx, 4(%%edi)\n\t"~
777 			"mov %%ecx, 8(%%edi)"
778 			:
779 			: "m" (info.vendor)
780 			: "edi", "eax", "ebx", "ecx", "edx";
781 		} else asm { // x86-64
782 			"lea %0, %%rdi\n\t"~
783 			"mov $0, %%eax\n\t"~
784 			"cpuid\n"~
785 			"mov %%ebx, (%%rdi)\n\t"~
786 			"mov %%edx, 4(%%rdi)\n\t"~
787 			"mov %%ecx, 8(%%rdi)"
788 			:
789 			: "m" (info.vendor)
790 			: "rdi", "rax", "rbx", "rcx", "rdx";
791 		}
792 	} else version (LDC) {
793 		version (X86) asm {
794 			lea EDI, info;
795 			mov EAX, 0;
796 			cpuid;
797 			mov [EDI + VENDOR_OFFSET], EBX;
798 			mov [EDI + VENDOR_OFFSET + 4], EDX;
799 			mov [EDI + VENDOR_OFFSET + 8], ECX;
800 		} else asm { // x86-64
801 			lea RDI, info;
802 			mov EAX, 0;
803 			cpuid;
804 			mov [RDI + VENDOR_OFFSET], EBX;
805 			mov [RDI + VENDOR_OFFSET + 4], EDX;
806 			mov [RDI + VENDOR_OFFSET + 8], ECX;
807 		}
808 	}
809 	
810 	// Vendor string verification
811 	// If the rest of the string doesn't correspond, the id is unset
812 	switch (info.vendor32[0]) {
813 	case Vendor.Intel:	// "GenuineIntel"
814 		if (info.vendor32[1] != ID!("ineI")) goto default;
815 		if (info.vendor32[2] != ID!("ntel")) goto default;
816 		break;
817 	case Vendor.AMD:	// "AuthenticAMD"
818 		if (info.vendor32[1] != ID!("enti")) goto default;
819 		if (info.vendor32[2] != ID!("cAMD")) goto default;
820 		break;
821 	case Vendor.VIA:	// "VIA VIA VIA "
822 		if (info.vendor32[1] != ID!("VIA ")) goto default;
823 		if (info.vendor32[2] != ID!("VIA ")) goto default;
824 		break;
825 	default: // Unknown
826 		info.vendorId32 = 0;
827 		return;
828 	}
829 	
830 	info.vendorId32 = info.vendor32[0];
831 }
832 
833 pragma(inline, false)
834 private
835 void getBrand(ref CPUINFO info) {
836 	version (DMD) {
837 		version (X86) asm {
838 			mov EDI, info;
839 			mov EAX, 0x8000_0002;
840 			cpuid;
841 			mov [EDI + BRAND_OFFSET], EAX;
842 			mov [EDI + BRAND_OFFSET +  4], EBX;
843 			mov [EDI + BRAND_OFFSET +  8], ECX;
844 			mov [EDI + BRAND_OFFSET + 12], EDX;
845 			mov EAX, 0x8000_0003;
846 			cpuid;
847 			mov [EDI + BRAND_OFFSET + 16], EAX;
848 			mov [EDI + BRAND_OFFSET + 20], EBX;
849 			mov [EDI + BRAND_OFFSET + 24], ECX;
850 			mov [EDI + BRAND_OFFSET + 28], EDX;
851 			mov EAX, 0x8000_0004;
852 			cpuid;
853 			mov [EDI + BRAND_OFFSET + 32], EAX;
854 			mov [EDI + BRAND_OFFSET + 36], EBX;
855 			mov [EDI + BRAND_OFFSET + 40], ECX;
856 			mov [EDI + BRAND_OFFSET + 44], EDX;
857 		} else version (X86_64) asm {
858 			mov RDI, info;
859 			mov EAX, 0x8000_0002;
860 			cpuid;
861 			mov [RDI + BRAND_OFFSET], EAX;
862 			mov [RDI + BRAND_OFFSET +  4], EBX;
863 			mov [RDI + BRAND_OFFSET +  8], ECX;
864 			mov [RDI + BRAND_OFFSET + 12], EDX;
865 			mov EAX, 0x8000_0003;
866 			cpuid;
867 			mov [RDI + BRAND_OFFSET + 16], EAX;
868 			mov [RDI + BRAND_OFFSET + 20], EBX;
869 			mov [RDI + BRAND_OFFSET + 24], ECX;
870 			mov [RDI + BRAND_OFFSET + 28], EDX;
871 			mov EAX, 0x8000_0004;
872 			cpuid;
873 			mov [RDI + BRAND_OFFSET + 32], EAX;
874 			mov [RDI + BRAND_OFFSET + 36], EBX;
875 			mov [RDI + BRAND_OFFSET + 40], ECX;
876 			mov [RDI + BRAND_OFFSET + 44], EDX;
877 		}
878 	} else version (GDC) {
879 		version (X86) asm {
880 			"lea %0, %%edi\n\t"~
881 			"mov $0x80000002, %%eax\n\t"~
882 			"cpuid\n\t"~
883 			"mov %%eax, (%%rdi)\n\t"~
884 			"mov %%ebx, 4(%%rdi)\n\t"~
885 			"mov %%ecx, 8(%%rdi)\n\t"~
886 			"mov %%edx, 12(%%rdi)\n\t"~
887 			"mov $0x80000003, %%eax\n\t"~
888 			"cpuid\n\t"~
889 			"mov %%eax, 16(%%rdi)\n\t"~
890 			"mov %%ebx, 20(%%rdi)\n\t"~
891 			"mov %%ecx, 24(%%rdi)\n\t"~
892 			"mov %%edx, 28(%%rdi)\n\t"~
893 			"mov $0x80000004, %%eax\n\t"~
894 			"cpuid\n\t"~
895 			"mov %%eax, 32(%%rdi)\n\t"~
896 			"mov %%ebx, 36(%%rdi)\n\t"~
897 			"mov %%ecx, 40(%%rdi)\n\t"~
898 			"mov %%edx, 44(%%rdi)"
899 			:
900 			: "m" (info.brand)
901 			: "edi", "eax", "ebx", "ecx", "edx";
902 		} else version (X86_64) asm {
903 			"lea %0, %%rdi\n\t"~
904 			"mov $0x80000002, %%eax\n\t"~
905 			"cpuid\n\t"~
906 			"mov %%eax, (%%rdi)\n\t"~
907 			"mov %%ebx, 4(%%rdi)\n\t"~
908 			"mov %%ecx, 8(%%rdi)\n\t"~
909 			"mov %%edx, 12(%%rdi)\n\t"~
910 			"mov $0x80000003, %%eax\n\t"~
911 			"cpuid\n\t"~
912 			"mov %%eax, 16(%%rdi)\n\t"~
913 			"mov %%ebx, 20(%%rdi)\n\t"~
914 			"mov %%ecx, 24(%%rdi)\n\t"~
915 			"mov %%edx, 28(%%rdi)\n\t"~
916 			"mov $0x80000004, %%eax\n\t"~
917 			"cpuid\n\t"~
918 			"mov %%eax, 32(%%rdi)\n\t"~
919 			"mov %%ebx, 36(%%rdi)\n\t"~
920 			"mov %%ecx, 40(%%rdi)\n\t"~
921 			"mov %%edx, 44(%%rdi)"
922 			:
923 			: "m" (info.brand)
924 			: "rdi", "rax", "rbx", "rcx", "rdx";
925 		}
926 	} else version (LDC) {
927 		version (X86) asm {
928 			lea EDI, info;
929 			mov EAX, 0x8000_0002;
930 			cpuid;
931 			mov [EDI + BRAND_OFFSET], EAX;
932 			mov [EDI + BRAND_OFFSET +  4], EBX;
933 			mov [EDI + BRAND_OFFSET +  8], ECX;
934 			mov [EDI + BRAND_OFFSET + 12], EDX;
935 			mov EAX, 0x8000_0003;
936 			cpuid;
937 			mov [EDI + BRAND_OFFSET + 16], EAX;
938 			mov [EDI + BRAND_OFFSET + 20], EBX;
939 			mov [EDI + BRAND_OFFSET + 24], ECX;
940 			mov [EDI + BRAND_OFFSET + 28], EDX;
941 			mov EAX, 0x8000_0004;
942 			cpuid;
943 			mov [EDI + BRAND_OFFSET + 32], EAX;
944 			mov [EDI + BRAND_OFFSET + 36], EBX;
945 			mov [EDI + BRAND_OFFSET + 40], ECX;
946 			mov [EDI + BRAND_OFFSET + 44], EDX;
947 		} else version (X86_64) asm {
948 			lea RDI, info;
949 			mov EAX, 0x8000_0002;
950 			cpuid;
951 			mov [RDI + BRAND_OFFSET], EAX;
952 			mov [RDI + BRAND_OFFSET +  4], EBX;
953 			mov [RDI + BRAND_OFFSET +  8], ECX;
954 			mov [RDI + BRAND_OFFSET + 12], EDX;
955 			mov EAX, 0x8000_0003;
956 			cpuid;
957 			mov [RDI + BRAND_OFFSET + 16], EAX;
958 			mov [RDI + BRAND_OFFSET + 20], EBX;
959 			mov [RDI + BRAND_OFFSET + 24], ECX;
960 			mov [RDI + BRAND_OFFSET + 28], EDX;
961 			mov EAX, 0x8000_0004;
962 			cpuid;
963 			mov [RDI + BRAND_OFFSET + 32], EAX;
964 			mov [RDI + BRAND_OFFSET + 36], EBX;
965 			mov [RDI + BRAND_OFFSET + 40], ECX;
966 			mov [RDI + BRAND_OFFSET + 44], EDX;
967 		}
968 	}
969 }
970 
971 pragma(inline, false)
972 private
973 void getVirtVendor(ref CPUINFO info) {
974 	version (DMD) {
975 		version (X86) asm {
976 			mov EDI, info;
977 			mov EAX, 0x40000000;
978 			cpuid;
979 			mov [EDI + VIRTVENDOR_OFFSET], EBX;
980 			mov [EDI + VIRTVENDOR_OFFSET + 4], ECX;
981 			mov [EDI + VIRTVENDOR_OFFSET + 8], EDX;
982 		} else asm { // x86-64
983 			mov RDI, info;
984 			mov EAX, 0x40000000;
985 			cpuid;
986 			mov [RDI + VIRTVENDOR_OFFSET], EBX;
987 			mov [RDI + VIRTVENDOR_OFFSET + 4], ECX;
988 			mov [RDI + VIRTVENDOR_OFFSET + 8], EDX;
989 		}
990 	} else version (GDC) {
991 		version (X86) asm {
992 			"lea %0, %%edi\n\t"~
993 			"mov $0x40000000, %%eax\n\t"~
994 			"cpuid\n"~
995 			"mov %%ebx, (%%edi)\n\t"~
996 			"mov %%ecx, 4(%%edi)\n\t"~
997 			"mov %%edx, 8(%%edi)"
998 			:
999 			: "m" (info.virt.vendor)
1000 			: "edi", "eax", "ebx", "ecx", "edx";
1001 		} else asm { // x86-64
1002 			"lea %0, %%rdi\n\t"~
1003 			"mov $0x40000000, %%eax\n\t"~
1004 			"cpuid\n"~
1005 			"mov %%ebx, (%%rdi)\n\t"~
1006 			"mov %%ecx, 4(%%rdi)\n\t"~
1007 			"mov %%edx, 8(%%rdi)"
1008 			:
1009 			: "m" (info.virt.vendor)
1010 			: "rdi", "rax", "rbx", "rcx", "rdx";
1011 		}
1012 	} else version (LDC) {
1013 		version (X86) asm {
1014 			lea EDI, info;
1015 			mov EAX, 0x40000000;
1016 			cpuid;
1017 			mov [EDI + VIRTVENDOR_OFFSET], EBX;
1018 			mov [EDI + VIRTVENDOR_OFFSET + 4], ECX;
1019 			mov [EDI + VIRTVENDOR_OFFSET + 8], EDX;
1020 		} else asm { // x86-64
1021 			lea RDI, info;
1022 			mov EAX, 0x40000000;
1023 			cpuid;
1024 			mov [RDI + VIRTVENDOR_OFFSET], EBX;
1025 			mov [RDI + VIRTVENDOR_OFFSET + 4], ECX;
1026 			mov [RDI + VIRTVENDOR_OFFSET + 8], EDX;
1027 		}
1028 	}
1029 	
1030 	// Paravirtual vendor string verification
1031 	// If the rest of the string doesn't correspond, the id is unset
1032 	switch (info.virt.vendor32[0]) {
1033 	case VirtVendor.KVM:	// "KVMKVMKVM\0\0\0"
1034 		if (info.virt.vendor32[1] != ID!("VMKV")) goto default;
1035 		if (info.virt.vendor32[2] != ID!("M\0\0\0")) goto default;
1036 		break;
1037 	case VirtVendor.HyperV:	// "Microsoft Hv"
1038 		if (info.virt.vendor32[1] != ID!("osof")) goto default;
1039 		if (info.virt.vendor32[2] != ID!("t Hv")) goto default;
1040 		break;
1041 	case VirtVendor.VBoxHyperV:	// "VBoxVBoxVBox"
1042 		if (info.virt.vendor32[1] != ID!("VBox")) goto default;
1043 		if (info.virt.vendor32[2] != ID!("VBox")) goto default;
1044 		info.virt.vendorId = VirtVendor.HyperV;
1045 		return;
1046 	default:
1047 		info.virt.vendorId32 = 0;
1048 		return;
1049 	}
1050 	
1051 	info.virt.vendorId32 = info.virt.vendor32[0];
1052 	version (Trace) trace("id=%u", info.virt.vendorId32);
1053 }
1054 
1055 /// Fetch CPU information.
1056 /// 
1057 /// Here is a list of phases this function goes through:
1058 /// 1. Get vendor string and ID
1059 /// 2. Get brand string
1060 /// 3. Get family IDs and basic information
1061 /// 4. Get virtualization features
1062 /// 5. Get extended features
1063 /// 6. Get cache and core information
1064 ///
1065 /// When a section is no longer capable of proceeding, it skips to the next
1066 /// phase.
1067 /// 
1068 /// Params: info = CPUINFO structure
1069 pragma(inline, false)
1070 void getInfo(ref CPUINFO info) {
1071 	ushort sc = void;	/// raw cores shared across cache level
1072 	ushort crshrd = void;	/// actual count of shared cores
1073 	ubyte type = void;	/// cache type
1074 	ubyte mids = void;	/// maximum IDs to this cache
1075 	
1076 	getVendor(info);
1077 	getBrand(info);
1078 	
1079 	REGISTERS regs = void;
1080 	
1081 	//
1082 	// Leaf 1H
1083 	//
1084 	
1085 	asmcpuid(regs, 1);
1086 	
1087 	// EAX
1088 	info.identifier = regs.eax;
1089 	info.stepping   = regs.eax & 0xF;        // EAX[3:0]
1090 	info.modelBase  = regs.eax >>  4 &  0xF; // EAX[7:4]
1091 	info.familyBase = regs.eax >>  8 &  0xF; // EAX[11:8]
1092 	info.type       = regs.eax >> 12 & 0b11; // EAX[13:12]
1093 	info.typeString = PROCESSOR_TYPE[info.type];
1094 	info.modelExtended   = regs.eax >> 16 &  0xF; // EAX[19:16]
1095 	info.familyExtended  = cast(ubyte)(regs.eax >> 20); // EAX[27:20]
1096 	
1097 	switch (info.vendorId) {
1098 	case Vendor.Intel:
1099 		info.family = info.familyBase != 0 ?
1100 			info.familyBase :
1101 			cast(ubyte)(info.familyExtended + info.familyBase);
1102 		
1103 		info.model = info.familyBase == 6 || info.familyBase == 0 ?
1104 			cast(ubyte)((info.modelExtended << 4) + info.modelBase) :
1105 			info.modelBase; // DisplayModel = Model_ID;
1106 		
1107 		// ECX
1108 		info.debugging.dtes64	= (regs.ecx & BIT!(2)) != 0;
1109 		info.debugging.dsCpl	= (regs.ecx & BIT!(4)) != 0;
1110 		info.virt.available	= (regs.ecx & BIT!(5)) != 0;
1111 		info.tech.smx	= (regs.ecx & BIT!(6)) != 0;
1112 		info.tech.eist	= (regs.ecx & BIT!(7)) != 0;
1113 		info.sys.tm2	= (regs.ecx & BIT!(8)) != 0;
1114 		info.cache.cnxtId	= (regs.ecx & BIT!(10)) != 0;
1115 		info.debugging.sdbg	= (regs.ecx & BIT!(11)) != 0;
1116 		info.misc.xtpr	= (regs.ecx & BIT!(14)) != 0;
1117 		info.debugging.pdcm	= (regs.ecx & BIT!(15)) != 0;
1118 		info.misc.pcid	= (regs.ecx & BIT!(17)) != 0;
1119 		info.debugging.mca	= (regs.ecx & BIT!(18)) != 0;
1120 		info.sys.x2apic	= (regs.ecx & BIT!(21)) != 0;
1121 		info.extras.rdtscDeadline	= (regs.ecx & BIT!(24)) != 0;
1122 		
1123 		// EDX
1124 		info.misc.psn	= (regs.edx & BIT!(18)) != 0;
1125 		info.debugging.ds	= (regs.edx & BIT!(21)) != 0;
1126 		info.sys.available	= (regs.edx & BIT!(22)) != 0;
1127 		info.cache.ss	= (regs.edx & BIT!(27)) != 0;
1128 		info.sys.tm	= (regs.edx & BIT!(29)) != 0;
1129 		info.debugging.pbe	= regs.edx >= BIT!(31);
1130 		break;
1131 	case Vendor.AMD:
1132 		if (info.familyBase < 0xF) {
1133 			info.family = info.familyBase;
1134 			info.model = info.modelBase;
1135 		} else {
1136 			info.family = cast(ubyte)(info.familyExtended + info.familyBase);
1137 			info.model = cast(ubyte)((info.modelExtended << 4) + info.modelBase);
1138 		}
1139 		break;
1140 	default:
1141 	}
1142 	
1143 	// EBX
1144 	info.sys.apicId = regs.ebx >> 24;
1145 	info.sys.maxApicId = cast(ubyte)(regs.ebx >> 16);
1146 	info.cache.clflushLinesize = regs.bh;
1147 	info.brandIndex = regs.bl;
1148 	
1149 	// ECX
1150 	info.sse.sse3	= (regs.ecx & BIT!(0)) != 0;
1151 	info.extras.pclmulqdq	= (regs.ecx & BIT!(1)) != 0;
1152 	info.extras.monitor	= (regs.ecx & BIT!(3)) != 0;
1153 	info.sse.ssse3	= (regs.ecx & BIT!(9)) != 0;
1154 	info.extensions.fma3	= (regs.ecx & BIT!(12)) != 0;
1155 	info.extras.cmpxchg16b	= (regs.ecx & BIT!(13)) != 0;
1156 	info.sse.sse41	= (regs.ecx & BIT!(15)) != 0;
1157 	info.sse.sse42	= (regs.ecx & BIT!(20)) != 0;
1158 	info.extras.movbe	= (regs.ecx & BIT!(22)) != 0;
1159 	info.extras.popcnt	= (regs.ecx & BIT!(23)) != 0;
1160 	info.extensions.aes_ni	= (regs.ecx & BIT!(25)) != 0;
1161 	info.extras.xsave	= (regs.ecx & BIT!(26)) != 0;
1162 	info.extras.osxsave	= (regs.ecx & BIT!(27)) != 0;
1163 	info.avx.avx	= (regs.ecx & BIT!(28)) != 0;
1164 	info.extensions.f16c	= (regs.ecx & BIT!(29)) != 0;
1165 	info.extras.rdrand	= (regs.ecx & BIT!(30)) != 0;
1166 	
1167 	// EDX
1168 	info.extensions.fpu	= (regs.edx & BIT!(0)) != 0;
1169 	info.virt.vme	= (regs.edx & BIT!(1)) != 0;
1170 	info.debugging.de	= (regs.edx & BIT!(2)) != 0;
1171 	info.memory.pse	= (regs.edx & BIT!(3)) != 0;
1172 	info.extras.rdtsc	= (regs.edx & BIT!(4)) != 0;
1173 	info.extras.rdmsr	= (regs.edx & BIT!(5)) != 0;
1174 	info.memory.pae	= (regs.edx & BIT!(6)) != 0;
1175 	info.debugging.mce	= (regs.edx & BIT!(7)) != 0;
1176 	info.extras.cmpxchg8b	= (regs.edx & BIT!(8)) != 0;
1177 	info.sys.apic	= (regs.edx & BIT!(9)) != 0;
1178 	info.extras.sysenter	= (regs.edx & BIT!(11)) != 0;
1179 	info.memory.mtrr	= (regs.edx & BIT!(12)) != 0;
1180 	info.memory.pge	= (regs.edx & BIT!(13)) != 0;
1181 	info.debugging.mca	= (regs.edx & BIT!(14)) != 0;
1182 	info.extras.cmov	= (regs.edx & BIT!(15)) != 0;
1183 	info.memory.pat	= (regs.edx & BIT!(16)) != 0;
1184 	info.memory.pse36	= (regs.edx & BIT!(17)) != 0;
1185 	info.cache.clflush	= (regs.edx & BIT!(19)) != 0;
1186 	info.extensions.mmx	= (regs.edx & BIT!(23)) != 0;
1187 	info.extras.fxsr	= (regs.edx & BIT!(24)) != 0;
1188 	info.sse.sse	= (regs.edx & BIT!(25)) != 0;
1189 	info.sse.sse2	= (regs.edx & BIT!(26)) != 0;
1190 	info.tech.htt	= (regs.edx & BIT!(28)) != 0;
1191 	
1192 	//
1193 	// Leaf 5H
1194 	//
1195 	
1196 	if (info.maxLeaf < 5) goto L_VIRT;
1197 	
1198 	asmcpuid(regs, 5);
1199 	
1200 	info.extras.mwaitMin = regs.ax;
1201 	info.extras.mwaitMax = regs.bx;
1202 	
1203 	//
1204 	// Leaf 6H
1205 	//
1206 	
1207 	if (info.maxLeaf < 6) goto L_VIRT;
1208 	
1209 	asmcpuid(regs, 6);
1210 	
1211 	switch (info.vendorId) {
1212 	case Vendor.Intel:
1213 		info.tech.turboboost	= (regs.eax & BIT!(1)) != 0;
1214 		info.tech.turboboost30	= (regs.eax & BIT!(14)) != 0;
1215 		break;
1216 	default:
1217 	}
1218 	
1219 	info.sys.arat = (regs.eax & BIT!(2)) != 0;
1220 	
1221 	//
1222 	// Leaf 7H
1223 	//
1224 	
1225 	if (info.maxLeaf < 7) goto L_VIRT;
1226 	
1227 	asmcpuid(regs, 7);
1228 	
1229 	switch (info.vendorId) {
1230 	case Vendor.Intel:
1231 		// EBX
1232 		info.sgx.supported	= (regs.ebx & BIT!(2)) != 0;
1233 		info.memory.hle	= (regs.ebx & BIT!(4)) != 0;
1234 		info.cache.invpcid	= (regs.ebx & BIT!(10)) != 0;
1235 		info.memory.rtm	= (regs.ebx & BIT!(11)) != 0;
1236 		info.avx.avx512f	= (regs.ebx & BIT!(16)) != 0;
1237 		info.memory.smap	= (regs.ebx & BIT!(20)) != 0;
1238 		info.avx.avx512er	= (regs.ebx & BIT!(27)) != 0;
1239 		info.avx.avx512pf	= (regs.ebx & BIT!(26)) != 0;
1240 		info.avx.avx512cd	= (regs.ebx & BIT!(28)) != 0;
1241 		info.avx.avx512dq	= (regs.ebx & BIT!(17)) != 0;
1242 		info.avx.avx512bw	= (regs.ebx & BIT!(30)) != 0;
1243 		info.avx.avx512_ifma	= (regs.ebx & BIT!(21)) != 0;
1244 		info.avx.avx512_vbmi	= regs.ebx >= BIT!(31);
1245 		// ECX
1246 		info.avx.avx512vl	= (regs.ecx & BIT!(1)) != 0;
1247 		info.memory.pku	= (regs.ecx & BIT!(3)) != 0;
1248 		info.memory.fsrepmov	= (regs.ecx & BIT!(4)) != 0;
1249 		info.extensions.waitpkg	= (regs.ecx & BIT!(5)) != 0;
1250 		info.avx.avx512_vbmi2	= (regs.ecx & BIT!(6)) != 0;
1251 		info.security.cetSs	= (regs.ecx & BIT!(7)) != 0;
1252 		info.avx.avx512_gfni	= (regs.ecx & BIT!(8)) != 0;
1253 		info.avx.avx512_vaes	= (regs.ecx & BIT!(9)) != 0;
1254 		info.avx.avx512_vnni	= (regs.ecx & BIT!(11)) != 0;
1255 		info.avx.avx512_bitalg	= (regs.ecx & BIT!(12)) != 0;
1256 		info.avx.avx512_vpopcntdq	= (regs.ecx & BIT!(14)) != 0;
1257 		info.memory._5pl	= (regs.ecx & BIT!(16)) != 0;
1258 		info.extras.cldemote	= (regs.ecx & BIT!(25)) != 0;
1259 		info.extras.movdiri	= (regs.ecx & BIT!(27)) != 0;
1260 		info.extras.movdir64b	= (regs.ecx & BIT!(28)) != 0;
1261 		info.extras.enqcmd	= (regs.ecx & BIT!(29)) != 0;
1262 		// EDX
1263 		info.avx.avx512_4vnniw	= (regs.edx & BIT!(2)) != 0;
1264 		info.avx.avx512_4fmaps	= (regs.edx & BIT!(3)) != 0;
1265 		info.misc.uintr	= (regs.edx & BIT!(5)) != 0;
1266 		info.avx.avx512_vp2intersect	= (regs.edx & BIT!(8)) != 0;
1267 		info.security.md_clear	= (regs.edx & BIT!(10)) != 0;
1268 		info.extras.serialize	= (regs.edx & BIT!(14)) != 0;
1269 		info.memory.tsxldtrk	= (regs.edx & BIT!(16)) != 0;
1270 		info.extras.pconfig	= (regs.edx & BIT!(18)) != 0;
1271 		info.security.cetIbt	= (regs.edx & BIT!(20)) != 0;
1272 		info.amx.bf16	= (regs.edx & BIT!(22)) != 0;
1273 		info.amx.enabled	= (regs.edx & BIT!(24)) != 0;
1274 		info.amx.int8	= (regs.edx & BIT!(25)) != 0;
1275 		info.security.ibrs = (regs.edx & BIT!(26)) != 0;
1276 		info.security.stibp	= (regs.edx & BIT!(27)) != 0;
1277 		info.security.l1dFlush	= (regs.edx & BIT!(28)) != 0;
1278 		info.security.ia32_arch_capabilities	= (regs.edx & BIT!(29)) != 0;
1279 		info.security.ssbd	= regs.edx >= BIT!(31);
1280 		break;
1281 	default:
1282 	}
1283 
1284 	// ebx
1285 	info.misc.fsgsbase	= (regs.ebx & BIT!(0)) != 0;
1286 	info.extensions.bmi1	= (regs.ebx & BIT!(3)) != 0;
1287 	info.avx.avx2	= (regs.ebx & BIT!(5)) != 0;
1288 	info.memory.smep	= (regs.ebx & BIT!(7)) != 0;
1289 	info.extensions.bmi2	= (regs.ebx & BIT!(8)) != 0;
1290 	info.extras.rdseed	= (regs.ebx & BIT!(18)) != 0;
1291 	info.extensions.adx	= (regs.ebx & BIT!(19)) != 0;
1292 	info.cache.clflushopt	= (regs.ebx & BIT!(23)) != 0;
1293 	info.extensions.sha	= (regs.ebx & BIT!(29)) != 0;
1294 	// ecx
1295 	info.extras.rdpid	= (regs.ecx & BIT!(22)) != 0;
1296 	
1297 	//
1298 	// Leaf 7H(ECX=01h)
1299 	//
1300 	
1301 	switch (info.vendorId) {
1302 	case Vendor.Intel:
1303 		asmcpuid(regs, 7, 1);
1304 		// a
1305 		info.avx.avx512_bf16	= (regs.eax & BIT!(5)) != 0;
1306 		info.memory.lam	= (regs.eax & BIT!(26)) != 0;
1307 		break;
1308 	default:
1309 	}
1310 	
1311 	//
1312 	// Leaf DH
1313 	//
1314 	
1315 	if (info.maxLeaf < 0xd) goto L_VIRT;
1316 	
1317 	switch (info.vendorId) {
1318 	case Vendor.Intel:
1319 		asmcpuid(regs, 0xd);
1320 		info.amx.xtilecfg	= (regs.eax & BIT!(17)) != 0;
1321 		info.amx.xtiledata	= (regs.eax & BIT!(18)) != 0;
1322 		break;
1323 	default:
1324 	}
1325 	
1326 	//
1327 	// Leaf DH(ECX=01h)
1328 	//
1329 	
1330 	switch (info.vendorId) {
1331 	case Vendor.Intel:
1332 		asmcpuid(regs, 0xd, 1);
1333 		info.amx.xfd	= (regs.eax & BIT!(18)) != 0;
1334 		break;
1335 	default:
1336 	}
1337 	
1338 	//
1339 	// Leaf 12H
1340 	//
1341 	
1342 	if (info.maxLeaf < 0x12) goto L_VIRT;
1343 	
1344 	switch (info.vendorId) {
1345 	case Vendor.Intel:
1346 		asmcpuid(regs, 0x12);
1347 		info.sgx.sgx1 = (regs.al & BIT!(0)) != 0;
1348 		info.sgx.sgx2 = (regs.al & BIT!(1)) != 0;
1349 		info.sgx.maxSize   = regs.dl;
1350 		info.sgx.maxSize64 = regs.dh;
1351 		break;
1352 	default:
1353 	}
1354 	
1355 	//
1356 	// Leaf 4000_000H
1357 	//
1358 	
1359 L_VIRT:
1360 	if (info.maxLeafVirt < 0x4000_0000) goto L_EXTENDED;
1361 	
1362 	getVirtVendor(info);
1363 	
1364 	//
1365 	// Leaf 4000_0001H
1366 	//
1367 
1368 	if (info.maxLeafVirt < 0x4000_0001) goto L_EXTENDED;
1369 	
1370 	switch (info.virt.vendorId) {
1371 	case VirtVendor.KVM:
1372 		asmcpuid(regs, 0x4000_0001);
1373 		info.virt.kvm.feature_clocksource	= (regs.eax & BIT!(0)) != 0;
1374 		info.virt.kvm.feature_nop_io_delay	= (regs.eax & BIT!(1)) != 0;
1375 		info.virt.kvm.feature_mmu_op	= (regs.eax & BIT!(2)) != 0;
1376 		info.virt.kvm.feature_clocksource2	= (regs.eax & BIT!(3)) != 0;
1377 		info.virt.kvm.feature_async_pf	= (regs.eax & BIT!(4)) != 0;
1378 		info.virt.kvm.feature_steal_time	= (regs.eax & BIT!(5)) != 0;
1379 		info.virt.kvm.feature_pv_eoi	= (regs.eax & BIT!(6)) != 0;
1380 		info.virt.kvm.feature_pv_unhault	= (regs.eax & BIT!(7)) != 0;
1381 		info.virt.kvm.feature_pv_tlb_flush	= (regs.eax & BIT!(9)) != 0;
1382 		info.virt.kvm.feature_async_pf_vmexit	= (regs.eax & BIT!(10)) != 0;
1383 		info.virt.kvm.feature_pv_send_ipi	= (regs.eax & BIT!(11)) != 0;
1384 		info.virt.kvm.feature_pv_poll_control	= (regs.eax & BIT!(12)) != 0;
1385 		info.virt.kvm.feature_pv_sched_yield	= (regs.eax & BIT!(13)) != 0;
1386 		info.virt.kvm.feature_clocsource_stable_bit	= (regs.eax & BIT!(24)) != 0;
1387 		info.virt.kvm.hint_realtime	= (regs.edx & BIT!(0)) != 0;
1388 		break;
1389 	default:
1390 	}
1391 	
1392 	//
1393 	// Leaf 4000_002H
1394 	//
1395 
1396 	if (info.maxLeafVirt < 0x4000_0002) goto L_EXTENDED;
1397 	
1398 	switch (info.virt.vendorId) {
1399 	case VirtVendor.HyperV:
1400 		asmcpuid(regs, 0x4000_0002);
1401 		info.virt.hv.guest_minor	= cast(ubyte)(regs.eax >> 24);
1402 		info.virt.hv.guest_service	= cast(ubyte)(regs.eax >> 16);
1403 		info.virt.hv.guest_build	= regs.ax;
1404 		info.virt.hv.guest_opensource	= regs.edx >= BIT!(31);
1405 		info.virt.hv.guest_vendor_id	= (regs.edx >> 16) & 0xFFF;
1406 		info.virt.hv.guest_os	= regs.dh;
1407 		info.virt.hv.guest_major	= regs.dl;
1408 		break;
1409 	default:
1410 	}
1411 	
1412 	//
1413 	// Leaf 4000_0003H
1414 	//
1415 
1416 	if (info.maxLeafVirt < 0x4000_0003) goto L_EXTENDED;
1417 	
1418 	switch (info.virt.vendorId) {
1419 	case VirtVendor.HyperV:
1420 		asmcpuid(regs, 0x4000_0003);
1421 		info.virt.hv.base_feat_vp_runtime_msr	= (regs.eax & BIT!(0)) != 0;
1422 		info.virt.hv.base_feat_part_time_ref_count_msr	= (regs.eax & BIT!(1)) != 0;
1423 		info.virt.hv.base_feat_basic_synic_msrs	= (regs.eax & BIT!(2)) != 0;
1424 		info.virt.hv.base_feat_stimer_msrs	= (regs.eax & BIT!(3)) != 0;
1425 		info.virt.hv.base_feat_apic_access_msrs	= (regs.eax & BIT!(4)) != 0;
1426 		info.virt.hv.base_feat_hypercall_msrs	= (regs.eax & BIT!(5)) != 0;
1427 		info.virt.hv.base_feat_vp_id_msr	= (regs.eax & BIT!(6)) != 0;
1428 		info.virt.hv.base_feat_virt_sys_reset_msr	= (regs.eax & BIT!(7)) != 0;
1429 		info.virt.hv.base_feat_stat_pages_msr	= (regs.eax & BIT!(8)) != 0;
1430 		info.virt.hv.base_feat_part_ref_tsc_msr	= (regs.eax & BIT!(9)) != 0;
1431 		info.virt.hv.base_feat_guest_idle_state_msr	= (regs.eax & BIT!(10)) != 0;
1432 		info.virt.hv.base_feat_timer_freq_msrs	= (regs.eax & BIT!(11)) != 0;
1433 		info.virt.hv.base_feat_debug_msrs	= (regs.eax & BIT!(12)) != 0;
1434 		info.virt.hv.part_flags_create_part	= (regs.ebx & BIT!(0)) != 0;
1435 		info.virt.hv.part_flags_access_part_id	= (regs.ebx & BIT!(1)) != 0;
1436 		info.virt.hv.part_flags_access_memory_pool	= (regs.ebx & BIT!(2)) != 0;
1437 		info.virt.hv.part_flags_adjust_msg_buffers	= (regs.ebx & BIT!(3)) != 0;
1438 		info.virt.hv.part_flags_post_msgs	= (regs.ebx & BIT!(4)) != 0;
1439 		info.virt.hv.part_flags_signal_events	= (regs.ebx & BIT!(5)) != 0;
1440 		info.virt.hv.part_flags_create_port	= (regs.ebx & BIT!(6)) != 0;
1441 		info.virt.hv.part_flags_connect_port	= (regs.ebx & BIT!(7)) != 0;
1442 		info.virt.hv.part_flags_access_stats	= (regs.ebx & BIT!(8)) != 0;
1443 		info.virt.hv.part_flags_debugging	= (regs.ebx & BIT!(11)) != 0;
1444 		info.virt.hv.part_flags_cpu_mgmt	= (regs.ebx & BIT!(12)) != 0;
1445 		info.virt.hv.part_flags_cpu_profiler	= (regs.ebx & BIT!(13)) != 0;
1446 		info.virt.hv.part_flags_expanded_stack_walk	= (regs.ebx & BIT!(14)) != 0;
1447 		info.virt.hv.part_flags_access_vsm	= (regs.ebx & BIT!(16)) != 0;
1448 		info.virt.hv.part_flags_access_vp_regs	= (regs.ebx & BIT!(17)) != 0;
1449 		info.virt.hv.part_flags_extended_hypercalls	= (regs.ebx & BIT!(20)) != 0;
1450 		info.virt.hv.part_flags_start_vp	= (regs.ebx & BIT!(21)) != 0;
1451 		info.virt.hv.pm_max_cpu_power_state_c0	= (regs.ecx & BIT!(0)) != 0;
1452 		info.virt.hv.pm_max_cpu_power_state_c1	= (regs.ecx & BIT!(1)) != 0;
1453 		info.virt.hv.pm_max_cpu_power_state_c2	= (regs.ecx & BIT!(2)) != 0;
1454 		info.virt.hv.pm_max_cpu_power_state_c3	= (regs.ecx & BIT!(3)) != 0;
1455 		info.virt.hv.pm_hpet_reqd_for_c3	= (regs.ecx & BIT!(4)) != 0;
1456 		info.virt.hv.misc_feat_mwait	= (regs.eax & BIT!(0)) != 0;
1457 		info.virt.hv.misc_feat_guest_debugging	= (regs.eax & BIT!(1)) != 0;
1458 		info.virt.hv.misc_feat_perf_mon	= (regs.eax & BIT!(2)) != 0;
1459 		info.virt.hv.misc_feat_pcpu_dyn_part_event	= (regs.eax & BIT!(3)) != 0;
1460 		info.virt.hv.misc_feat_xmm_hypercall_input	= (regs.eax & BIT!(4)) != 0;
1461 		info.virt.hv.misc_feat_guest_idle_state	= (regs.eax & BIT!(5)) != 0;
1462 		info.virt.hv.misc_feat_hypervisor_sleep_state	= (regs.eax & BIT!(6)) != 0;
1463 		info.virt.hv.misc_feat_query_numa_distance	= (regs.eax & BIT!(7)) != 0;
1464 		info.virt.hv.misc_feat_timer_freq	= (regs.eax & BIT!(8)) != 0;
1465 		info.virt.hv.misc_feat_inject_synmc_xcpt	= (regs.eax & BIT!(9)) != 0;
1466 		info.virt.hv.misc_feat_guest_crash_msrs	= (regs.eax & BIT!(10)) != 0;
1467 		info.virt.hv.misc_feat_debug_msrs	= (regs.eax & BIT!(11)) != 0;
1468 		info.virt.hv.misc_feat_npiep1	= (regs.eax & BIT!(12)) != 0;
1469 		info.virt.hv.misc_feat_disable_hypervisor	= (regs.eax & BIT!(13)) != 0;
1470 		info.virt.hv.misc_feat_ext_gva_range_for_flush_va_list	= (regs.eax & BIT!(14)) != 0;
1471 		info.virt.hv.misc_feat_hypercall_output_xmm	= (regs.eax & BIT!(15)) != 0;
1472 		info.virt.hv.misc_feat_sint_polling_mode	= (regs.eax & BIT!(17)) != 0;
1473 		info.virt.hv.misc_feat_hypercall_msr_lock	= (regs.eax & BIT!(18)) != 0;
1474 		info.virt.hv.misc_feat_use_direct_synth_msrs	= (regs.eax & BIT!(19)) != 0;
1475 		break;
1476 	default:
1477 	}
1478 	
1479 	//
1480 	// Leaf 4000_0004H
1481 	//
1482 
1483 	if (info.maxLeafVirt < 0x4000_0004) goto L_EXTENDED;
1484 	
1485 	switch (info.virt.vendorId) {
1486 	case VirtVendor.HyperV:
1487 		asmcpuid(regs, 0x4000_0004);
1488 		info.virt.hv.hint_hypercall_for_process_switch	= (regs.eax & BIT!(0)) != 0;
1489 		info.virt.hv.hint_hypercall_for_tlb_flush	= (regs.eax & BIT!(1)) != 0;
1490 		info.virt.hv.hint_hypercall_for_tlb_shootdown	= (regs.eax & BIT!(2)) != 0;
1491 		info.virt.hv.hint_msr_for_apic_access	= (regs.eax & BIT!(3)) != 0;
1492 		info.virt.hv.hint_msr_for_sys_reset	= (regs.eax & BIT!(4)) != 0;
1493 		info.virt.hv.hint_relax_time_checks	= (regs.eax & BIT!(5)) != 0;
1494 		info.virt.hv.hint_dma_remapping	= (regs.eax & BIT!(6)) != 0;
1495 		info.virt.hv.hint_interrupt_remapping	= (regs.eax & BIT!(7)) != 0;
1496 		info.virt.hv.hint_x2apic_msrs	= (regs.eax & BIT!(8)) != 0;
1497 		info.virt.hv.hint_deprecate_auto_eoi	= (regs.eax & BIT!(9)) != 0;
1498 		info.virt.hv.hint_synth_cluster_ipi_hypercall	= (regs.eax & BIT!(10)) != 0;
1499 		info.virt.hv.hint_ex_proc_masks_interface	= (regs.eax & BIT!(11)) != 0;
1500 		info.virt.hv.hint_nested_hyperv	= (regs.eax & BIT!(12)) != 0;
1501 		info.virt.hv.hint_int_for_mbec_syscalls	= (regs.eax & BIT!(13)) != 0;
1502 		info.virt.hv.hint_nested_enlightened_vmcs_interface	= (regs.eax & BIT!(14)) != 0;
1503 		break;
1504 	default:
1505 	}
1506 	
1507 	//
1508 	// Leaf 4000_0006H
1509 	//
1510 
1511 	if (info.maxLeafVirt < 0x4000_0006) goto L_EXTENDED;
1512 	
1513 	switch (info.virt.vendorId) {
1514 	case VirtVendor.HyperV:
1515 		asmcpuid(regs, 0x4000_0006);
1516 		info.virt.hv.host_feat_avic	= (regs.eax & BIT!(0)) != 0;
1517 		info.virt.hv.host_feat_msr_bitmap	= (regs.eax & BIT!(1)) != 0;
1518 		info.virt.hv.host_feat_perf_counter	= (regs.eax & BIT!(2)) != 0;
1519 		info.virt.hv.host_feat_nested_paging	= (regs.eax & BIT!(3)) != 0;
1520 		info.virt.hv.host_feat_dma_remapping	= (regs.eax & BIT!(4)) != 0;
1521 		info.virt.hv.host_feat_interrupt_remapping	= (regs.eax & BIT!(5)) != 0;
1522 		info.virt.hv.host_feat_mem_patrol_scrubber	= (regs.eax & BIT!(6)) != 0;
1523 		info.virt.hv.host_feat_dma_prot_in_use	= (regs.eax & BIT!(7)) != 0;
1524 		info.virt.hv.host_feat_hpet_requested	= (regs.eax & BIT!(8)) != 0;
1525 		info.virt.hv.host_feat_stimer_volatile	= (regs.eax & BIT!(9)) != 0;
1526 		break;
1527 	default:
1528 	}
1529 	
1530 	//
1531 	// Leaf 4000_0010H
1532 	//
1533 
1534 	if (info.maxLeafVirt < 0x4000_0010) goto L_EXTENDED;
1535 	
1536 	switch (info.virt.vendorId) {
1537 	case VirtVendor.VBoxMin: // VBox Minimal
1538 		asmcpuid(regs, 0x4000_0010);
1539 		info.virt.vbox.tsc_freq_khz = regs.eax;
1540 		info.virt.vbox.apic_freq_khz = regs.ebx;
1541 		break;
1542 	default:
1543 	}
1544 
1545 	//
1546 	// Leaf 8000_0001H
1547 	//
1548 	
1549 L_EXTENDED:
1550 	asmcpuid(regs, 0x8000_0001);
1551 	
1552 	switch (info.vendorId) {
1553 	case Vendor.AMD:
1554 		// ecx
1555 		info.virt.available	= (regs.ecx & BIT!(2)) != 0;
1556 		info.sys.x2apic	= (regs.ecx & BIT!(3)) != 0;
1557 		info.sse.sse4a	= (regs.ecx & BIT!(6)) != 0;
1558 		info.extensions.xop	= (regs.ecx & BIT!(11)) != 0;
1559 		info.extras.skinit	= (regs.ecx & BIT!(12)) != 0;
1560 		info.extensions.fma4	= (regs.ecx & BIT!(16)) != 0;
1561 		info.extensions.tbm	= (regs.ecx & BIT!(21)) != 0;
1562 		// edx
1563 		info.extensions.mmxExtended	= (regs.edx & BIT!(22)) != 0;
1564 		info.extensions._3DNowExtended	= (regs.edx & BIT!(30)) != 0;
1565 		info.extensions._3DNow	= regs.edx >= BIT!(31);
1566 		break;
1567 	default:
1568 	}
1569 	
1570 	// ecx
1571 	info.extensions.lahf64	= (regs.ecx & BIT!(0)) != 0;
1572 	info.extras.lzcnt	= (regs.ecx & BIT!(5)) != 0;
1573 	info.cache.prefetchw	= (regs.ecx & BIT!(8)) != 0;
1574 	info.extras.monitorx	= (regs.ecx & BIT!(29)) != 0;
1575 	// edx
1576 	info.extras.syscall	= (regs.edx & BIT!(11)) != 0;
1577 	info.memory.nx	= (regs.edx & BIT!(20)) != 0;
1578 	info.memory.page1gb	= (regs.edx & BIT!(26)) != 0;
1579 	info.extras.rdtscp	= (regs.edx & BIT!(27)) != 0;
1580 	info.extensions.x86_64	= (regs.edx & BIT!(29)) != 0;
1581 	
1582 	//
1583 	// Leaf 8000_0007H
1584 	//
1585 	
1586 	if (info.maxLeafExtended < 0x8000_0007) goto L_CACHE_INFO;
1587 	
1588 	asmcpuid(regs, 0x8000_0007);
1589 	
1590 	switch (info.vendorId) {
1591 	case Vendor.Intel:
1592 		info.extras.rdseed	= (regs.ebx & BIT!(28)) != 0;
1593 		break;
1594 	case Vendor.AMD:
1595 		info.sys.tm	= (regs.edx & BIT!(4)) != 0;
1596 		info.tech.turboboost	= (regs.edx & BIT!(9)) != 0;
1597 		break;
1598 	default:
1599 	}
1600 	
1601 	info.extras.rdtscInvariant	= (regs.edx & BIT!(8)) != 0;
1602 	
1603 	//
1604 	// Leaf 8000_0008H
1605 	//
1606 	
1607 	if (info.maxLeafExtended < 0x8000_0008) goto L_CACHE_INFO;
1608 	
1609 	asmcpuid(regs, 0x8000_0008);
1610 	
1611 	switch (info.vendorId) {
1612 	case Vendor.Intel:
1613 		info.cache.wbnoinvd	= (regs.ebx & BIT!(9)) != 0;
1614 		break;
1615 	case Vendor.AMD:
1616 		info.security.ibpb	= (regs.ebx & BIT!(12)) != 0;
1617 		info.security.ibrs	= (regs.ebx & BIT!(14)) != 0;
1618 		info.security.stibp	= (regs.ebx & BIT!(15)) != 0;
1619 		info.security.ibrsAlwaysOn	= (regs.ebx & BIT!(16)) != 0;
1620 		info.security.stibpAlwaysOn	= (regs.ebx & BIT!(17)) != 0;
1621 		info.security.ibrsPreferred	= (regs.ebx & BIT!(18)) != 0;
1622 		info.security.ssbd	= (regs.ebx & BIT!(24)) != 0;
1623 		break;
1624 	default:
1625 	}
1626 
1627 	info.memory.b_8000_0008_ax = regs.ax; // info.addr_phys_bits, info.addr_line_bits
1628 	
1629 	//
1630 	// Leaf 8000_000AH
1631 	//
1632 
1633 	if (info.maxLeafExtended < 0x8000_000A) goto L_CACHE_INFO;
1634 	
1635 	asmcpuid(regs, 0x8000_000A);
1636 	
1637 	switch (info.vendorId) {
1638 	case Vendor.AMD:
1639 		info.virt.version_	= regs.al; // EAX[7:0]
1640 		info.virt.apicv	= (regs.edx & BIT!(13)) != 0;
1641 		break;
1642 	default:
1643 	}
1644 
1645 	//if (info.maxLeafExtended < ...) goto L_CACHE_INFO;
1646 	
1647 L_CACHE_INFO:
1648 	info.cache.levels = 0;
1649 	CACHEINFO *ca = cast(CACHEINFO*)info.cache.level;
1650 	
1651 	switch (info.vendorId) {
1652 	case Vendor.Intel:
1653 		if (info.maxLeaf < 0x1f) goto L_CACHE_INTEL_BH;
1654 		if (info.maxLeaf < 0xb)  goto L_CACHE_INTEL_4H;
1655 		if (info.maxLeaf < 4) {
1656 			with (info.cores) logical = physical = 1;
1657 			break;
1658 		}
1659 		
1660 //L_CACHE_INTEL_1FH:
1661 		//TODO: Support levels 3,4,5 in CPUID.1FH
1662 		//      (Module, Tile, and Die)
1663 		asmcpuid(regs, 0x1f, 1); // Cores (logical)
1664 		
1665 		info.cores.logical = regs.bx;
1666 		
1667 		asmcpuid(regs, 0x1f, 0); // SMT (architectural states per core)
1668 		
1669 		info.cores.physical = cast(ushort)(info.cores.logical / regs.bx);
1670 		
1671 		goto L_CACHE_INTEL_4H;
1672 		
1673 L_CACHE_INTEL_BH:
1674 		asmcpuid(regs, 0xb, 1); // Cores (logical)
1675 		
1676 		info.cores.logical = regs.bx;
1677 		
1678 		asmcpuid(regs, 0xb, 0); // SMT (architectural states per core)
1679 		
1680 		info.cores.physical = cast(ushort)(info.cores.logical / regs.bx);
1681 		
1682 L_CACHE_INTEL_4H:
1683 		asmcpuid(regs, 4, info.cache.levels);
1684 		
1685 		type = regs.eax & CACHE_MASK; // EAX[4:0]
1686 		if (type == 0 || info.cache.levels >= CACHE_MAX_LEVEL) break;
1687 		
1688 		ca.type = CACHE_TYPE[type];
1689 		ca.level = regs.al >> 5;
1690 		ca.lineSize = (regs.bx & 0xfff) + 1; // bits 11-0
1691 		ca.partitions = ((regs.ebx >> 12) & 0x3ff) + 1; // bits 21-12
1692 		ca.ways = ((regs.ebx >> 22) + 1); // bits 31-22
1693 		ca.sets = regs.ecx + 1;
1694 		if (regs.eax & BIT!(8)) ca.features = 1;
1695 		if (regs.eax & BIT!(9)) ca.features |= BIT!(1);
1696 		if (regs.edx & BIT!(0)) ca.features |= BIT!(2);
1697 		if (regs.edx & BIT!(1)) ca.features |= BIT!(3);
1698 		if (regs.edx & BIT!(2)) ca.features |= BIT!(4);
1699 		ca.size = (ca.sets * ca.lineSize * ca.partitions * ca.ways) >> 10;
1700 		
1701 		mids = (regs.eax >> 26) + 1;	// EAX[31:26]
1702 		
1703 		if (info.cores.logical == 0) { // skip if already populated
1704 			info.cores.logical = mids;
1705 			info.cores.physical = info.tech.htt ? mids >> 1 : mids;
1706 		}
1707 		
1708 		crshrd = (((regs.eax >> 14) & 2047) + 1);	// EAX[25:14]
1709 		sc = cast(ushort)(info.cores.logical / crshrd); // cast for ldc 0.17.1
1710 		ca.sharedCores = sc ? sc : 1;
1711 		version (Trace) trace("intel.4h mids=%u shared=%u crshrd=%u sc=%u",
1712 			mids, ca.sharedCores, crshrd, sc);
1713 		
1714 		++info.cache.levels; ++ca;
1715 		goto L_CACHE_INTEL_4H;
1716 	case Vendor.AMD:
1717 		/*if (info.maxLeafExtended < 0x8000_001e) goto L_AMD_TOPOLOGY_EXT_8H;
1718 		
1719 		asmcpuid(regs, 0x8000_0001);
1720 		
1721 		if (regs.ecx & BIT!(22)) { // Topology extensions support
1722 			asmcpuid(regs, 0x8000_001e);
1723 			
1724 			info.cores.logical = regs.ch + 1;
1725 			info.cores.physical = regs.dh & 7;
1726 			goto L_AMD_CACHE;
1727 		}*/
1728 		
1729 /*L_AMD_TOPOLOGY_EXT_8H:
1730 		// See APM Volume 3 Appendix E.5
1731 		// For some reason, CPUID Fn8000_001E_EBX is not mentioned there
1732 		asmcpuid(regs, 0x8000_0008);
1733 		
1734 		type = regs.cx >> 12; // ApicIdSize
1735 		
1736 		if (type) { // Extended
1737 			info.cores.physical = regs.cl + 1;
1738 			info.cores.logical = cast(ushort)(1 << type);
1739 		} else { // Legacy
1740 			info.cores.logical = info.cores.physical = regs.cl + 1;
1741 		}*/
1742 
1743 //L_AMD_CACHE:
1744 		if (info.maxLeafExtended < 0x8000_001D)
1745 			goto L_CACHE_AMD_EXT_5H;
1746 		if (info.maxLeafExtended < 0x8000_0005) {
1747 			with (info.cores) logical = physical = 1;
1748 			break;
1749 		}
1750 		
1751 		//
1752 		// AMD newer cache method
1753 		//
1754 		
1755 L_CACHE_AMD_EXT_1DH: // Almost the same as Intel's
1756 		asmcpuid(regs, 0x8000_001d, info.cache.levels);
1757 		
1758 		type = regs.eax & CACHE_MASK; // EAX[4:0]
1759 		if (type == 0 || info.cache.levels >= CACHE_MAX_LEVEL) break;
1760 		
1761 		ca.type = CACHE_TYPE[type];
1762 		ca.level = (regs.eax >> 5) & 7;
1763 		ca.lineSize = (regs.ebx & 0xfff) + 1;
1764 		ca.partitions = ((regs.ebx >> 12) & 0x3ff) + 1;
1765 		ca.ways = (regs.ebx >> 22) + 1;
1766 		ca.sets = regs.ecx + 1;
1767 		if (regs.eax & BIT!(8)) ca.features = 1;
1768 		if (regs.eax & BIT!(9)) ca.features |= BIT!(1);
1769 		if (regs.edx & BIT!(0)) ca.features |= BIT!(2);
1770 		if (regs.edx & BIT!(1)) ca.features |= BIT!(3);
1771 		ca.size = (ca.sets * ca.lineSize * ca.partitions * ca.ways) >> 10;
1772 		
1773 		crshrd = (((regs.eax >> 14) & 0xfff) + 1); // bits 25-14
1774 		sc = cast(ushort)(info.sys.maxApicId / crshrd); // cast for ldc 0.17.1
1775 		ca.sharedCores = sc ? sc : 1;
1776 		
1777 		if (info.cores.logical == 0) with (info.cores) { // skip if already populated
1778 			logical = info.sys.maxApicId;
1779 			physical = info.tech.htt ? logical >> 1 : info.sys.maxApicId;
1780 		}
1781 		
1782 		version (Trace) trace("amd.8000_001Dh mids=%u shared=%u crshrd=%u sc=%u",
1783 			mids, ca.sharedCores, crshrd, sc);
1784 		
1785 		++info.cache.levels; ++ca;
1786 		goto L_CACHE_AMD_EXT_1DH;
1787 		
1788 		//
1789 		// AMD legacy cache
1790 		//
1791 		
1792 L_CACHE_AMD_EXT_5H:
1793 		asmcpuid(regs, 0x8000_0005);
1794 		
1795 		info.cache.level[0].level = 1; // L1-D
1796 		info.cache.level[0].type = 'D'; // data
1797 		info.cache.level[0].size = regs.ecx >> 24;
1798 		info.cache.level[0].ways = cast(ubyte)(regs.ecx >> 16);
1799 		info.cache.level[0].lines = regs.ch;
1800 		info.cache.level[0].lineSize = regs.cl;
1801 		info.cache.level[0].sets = 1;
1802 		
1803 		info.cache.level[1].level = 1; // L1-I
1804 		info.cache.level[1].type = 'I'; // instructions
1805 		info.cache.level[1].size = regs.edx >> 24;
1806 		info.cache.level[1].ways = cast(ubyte)(regs.edx >> 16);
1807 		info.cache.level[1].lines = regs.dh;
1808 		info.cache.level[1].lineSize = regs.dl;
1809 		info.cache.level[1].sets = 1;
1810 		
1811 		info.cache.levels = 2;
1812 		
1813 		if (info.maxLeafExtended < 0x8000_0006)
1814 			break; // No L2/L3
1815 		
1816 		// See Table E-4. L2/L3 Cache and TLB Associativity Field Encoding
1817 		static immutable ubyte[16] _amd_cache_ways = [
1818 			// 7h is reserved
1819 			// 9h mentions 8000_001D but that's already supported
1820 			0, 1, 2, 3, 4, 6, 8, 0, 16, 0, 32, 48, 64, 96, 128, 255
1821 		];
1822 		
1823 		asmcpuid(regs, 0x8000_0006);
1824 		
1825 		type = regs.cx >> 12; // amd_ways_l2
1826 		if (type) {
1827 			info.cache.level[2].level = 2;  // L2
1828 			info.cache.level[2].type = 'U'; // unified
1829 			info.cache.level[2].size = regs.ecx >> 16;
1830 			info.cache.level[2].ways = _amd_cache_ways[type];
1831 			info.cache.level[2].lines = regs.ch & 0xf;
1832 			info.cache.level[2].lineSize = regs.cl;
1833 			info.cache.level[2].sets = 1;
1834 			info.cache.levels = 3;
1835 			
1836 			type = regs.dx >> 12; // amd_ways_l3
1837 			if (type) {
1838 				info.cache.level[3].level = 3;  // L3
1839 				info.cache.level[3].type = 'U'; // unified
1840 				info.cache.level[3].size = ((regs.edx >> 18) + 1) << 9;
1841 				info.cache.level[3].ways = _amd_cache_ways[type];
1842 				info.cache.level[3].lines = regs.dh & 0xf;
1843 				info.cache.level[3].lineSize = regs.dl & 0x7F;
1844 				info.cache.level[3].sets = 1;
1845 				info.cache.levels = 4;
1846 			}
1847 		}
1848 		break;
1849 	default:
1850 	}
1851 }