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  * getVendor(info);  // Get vendor string (mandatory step before info)
11  * getInfo(info);    // Fill CPUINFO structure (optional)
12  * ---
13  *
14  * Then checking the corresponding field:
15  * ---
16  * if (info.ext.amx_xfd) {
17  *   // Intel AMX is available
18  * } else {
19  *   // Feature unavailable
20  * }
21  * ---
22  *
23  * See the CPUINFO structure for available fields.
24  *
25  * For more information, it's encouraged to consult the technical manual.
26  *
27  * NOTICE: This is best used with DMD and LDC. Will crash on GDC -O1.
28  *
29  * Authors: dd86k (dd@dax.moe)
30  * Copyright: © 2016-2021 dd86k
31  * License: MIT
32  */
33 module ddcpuid;
34 
35 //TODO: Consider moving all lone instructions into extras (again?)
36 //      And probs have an argument to show them (to ouput)
37 
38 // NOTE: Please no naked assembler.
39 //       I'd rather deal with a bit of prolog and epilog than slamming
40 //       my head into my desk violently trying to match every operating
41 //       system ABI, compiler versions, and major compilers.
42 //       Besides, final compiled binary is plenty fine.
43 // NOTE: GAS syntax reminder
44 //       asm { "asm;\n\t" : "constraint" output : "constraint" input : clobbers }
45 
46 @system:
47 extern (C):
48 
49 version (X86) enum DDCPUID_PLATFORM = "x86"; /// Target platform
50 else version (X86_64) enum DDCPUID_PLATFORM = "amd64"; /// Target platform
51 else static assert(0, "Unsupported platform");
52 
53 version (DigitalMars) {
54 	version = DMD;
55 } else version (GNU) {
56 	version = GDC;
57 } else version (LDC) {
58 	
59 } else static assert(0, "Unsupported compiler");
60 
61 enum DDCPUID_VERSION   = "0.18.0";	/// Library version
62 enum DDCPUID_CACHE_MAX = 6;	/// Cache levels buffer size (and maximum size)
63 private enum VENDOR_OFFSET     = CPUINFO.vendor.offsetof;
64 private enum BRAND_OFFSET      = CPUINFO.brand.offsetof;
65 private enum VIRTVENDOR_OFFSET = CPUINFO.virt.offsetof + CPUINFO.virt.vendor.offsetof;
66 
67 version (PrintInfo) {
68 	pragma(msg, "VENDOR_OFFSET\t", VENDOR_OFFSET);
69 	pragma(msg, "BRAND_OFFSET\t", BRAND_OFFSET);
70 	pragma(msg, "VIRTVENDOR_OFFSET\t", VIRTVENDOR_OFFSET);
71 	pragma(msg, "CPUINFO.sizeof\t", CPUINFO.sizeof);
72 	pragma(msg, "CACHE.sizeof\t", CACHEINFO.sizeof);
73 }
74 
75 /// Make a bit mask of one bit at n position
76 private
77 template BIT(int n) if (n <= 31) { enum uint BIT = 1 << n; }
78 
79 /// Vendor ID template
80 // Little-endian only, unless x86 gets any crazier
81 private
82 template ID(char[4] c) {
83 	enum uint ID = c[0] | c[1] << 8 | c[2] << 16 | c[3] << 24;
84 }
85 
86 // Self-made vendor "IDs" for faster look-ups, LSB-based.
87 enum : uint {
88 	VENDOR_OTHER = 0,	/// Other or unknown (zero)
89 	VENDOR_INTEL = ID!("Genu"),	/// "GenuineIntel": Intel
90 	VENDOR_AMD   = ID!("Auth"),	/// "AuthenticAMD": AMD
91 	VENDOR_VIA   = ID!("VIA "),	/// "VIA VIA VIA ": VIA
92 	VIRT_VENDOR_KVM      = ID!("KVMK"), /// "KVMKVMKVM\0\0\0": KVM
93 	VIRT_VENDOR_VBOX_HV  = ID!("VBox"), /// "VBoxVBoxVBox": VirtualBox/Hyper-V interface
94 	VIRT_VENDOR_VBOX_MIN = 0, /// VirtualBox minimal interface (zero)
95 }
96 
97 private
98 union __01ebx_t { // 01h.EBX internal
99 	uint all;
100 	struct {
101 		ubyte brand_index;	/// Processor brand index. No longer used.
102 		ubyte clflush_linesize;	/// Linesize of CLFLUSH in bytes
103 		ubyte max_apic_id;	/// Maximum APIC ID
104 		ubyte apic_id;	/// Initial APIC ID (running core where CPUID was called)
105 	}
106 }
107 
108 struct REGISTERS { uint eax, ebx, ecx, edx; }
109 
110 /// CPU cache entry
111 struct CACHEINFO { align(1):
112 	union {
113 		package uint __bundle1;
114 		struct {
115 			ubyte linesize; /// Size of the line in bytes
116 			ubyte partitions;	/// Number of partitions
117 			ubyte ways;	/// Number of ways per line
118 			package ubyte _amdsize;	/// (AMD, legacy) Size in KiB
119 		}
120 	}
121 	/// Cache Size in kilobytes.
122 	// (Ways + 1) * (Partitions + 1) * (Line_Size + 1) * (Sets + 1)
123 	// (EBX[31:22] + 1) * (EBX[21:12] + 1) * (EBX[11:0] + 1) * (ECX + 1)
124 	uint size;
125 	/// Number of cache sets.
126 	ushort sets;
127 	/// Number of CPU cores sharing this cache.
128 	ushort sharedCores;
129 	/// Cache feature, bit flags.
130 	/// - Bit 0: Self Initializing cache
131 	/// - Bit 1: Fully Associative cache
132 	/// - Bit 2: No Write-Back Invalidation (toggle)
133 	/// - Bit 3:  Cache Inclusiveness (toggle)
134 	/// - Bit 4: Complex Cache Indexing (toggle)
135 	ushort feat;
136 	ubyte level;	/// Cache level: L1, L2, etc.
137 	char type = 0;	/// Type entry character: 'D'=Data, 'I'=Instructions, 'U'=Unified
138 }
139 
140 /// CPU information structure
141 struct CPUINFO { align(1):
142 	uint max_leaf;	/// Highest cpuid leaf
143 	uint max_virt_leaf;	/// Highest cpuid virtualization leaf
144 	uint max_ext_leaf;	/// Highest cpuid extended leaf
145 	
146 	// Vendor strings
147 	
148 	union {
149 		package uint[3] vendor32;	/// Vendor 32-bit parts
150 		char[12] vendor;	/// Vendor String
151 	}
152 	union {
153 		package uint[12] brand32;	// For init
154 		char[48] brand;	/// Processor Brand String
155 	}
156 	uint vendor_id;	/// Validated vendor ID
157 	ubyte brand_index;	/// Brand string index (not used)
158 	
159 	// Core
160 	
161 	/// Contains the information on the number of cores.
162 	struct Cores {
163 		//TODO: Physical cores
164 //		ushort physical;	/// Physical cores in this processor
165 		ushort logical;	/// Logical cores in this processor
166 	}
167 	align(2) Cores cores;	/// Processor package cores
168 	
169 	// Identifier
170 
171 	ubyte family;	/// Effective family identifier
172 	ubyte family_base;	/// Base family identifier
173 	ubyte family_ext;	/// Extended family identifier
174 	ubyte model;	/// Effective model identifier
175 	ubyte model_base;	/// Base model identifier
176 	ubyte model_ext;	/// Extended model identifier
177 	ubyte stepping;	/// Stepping revision
178 	ubyte type;	/// Processor type number
179 	const(char) *type_string;	/// Processor type string.
180 	
181 	//TODO: Consider bit flags for some families
182 	//      Like MMX, SSE, AVX, AMX, you get the gist
183 	/// Contains processor extensions.
184 	/// Extensions contain a variety of instructions to aid particular
185 	/// tasks.
186 	struct Extensions {
187 		bool fpu;	/// On-Chip x87 FPU
188 		bool f16c;	/// Float16 Conversions
189 		bool mmx;	/// MMX
190 		bool mmxext;	/// MMX Extended
191 		bool _3dnow;	/// 3DNow!
192 		bool _3dnowext;	/// 3DNow! Extended
193 		bool aes_ni;	/// Advanced Encryption Standard New Instructions
194 		bool sha;	/// SHA-1
195 		bool fma3;	/// Fused Multiply-Add
196 		bool fma4;	/// FMA4
197 		bool bmi1;	/// BMI1
198 		bool bmi2;	/// BMI2
199 		bool x86_64;	/// 64-bit mode (Long mode)
200 		bool lahf64;	/// LAHF+SAHF in 64-bit mode
201 		bool waitpkg;	/// User Level Monitor Wait (UMWAIT)
202 		bool xop;	/// AMD eXtended OPerations
203 		bool tbm;	/// Trailing Bit Manipulation
204 		bool adx;	/// Multi-precision Add-Carry (ADCX+ADOX)
205 		
206 		// SSE
207 		bool sse;	/// Streaming SIMD Extensions
208 		bool sse2;	/// SSE2
209 		bool sse3;	/// SSE3
210 		bool ssse3;	/// SSSE3
211 		bool sse41;	/// SSE4.1
212 		bool sse42;	/// SSE4.2
213 		bool sse4a;	/// SSE4a
214 		
215 		// AVX
216 		bool avx;	/// Advanced Vector eXtension
217 		bool avx2;	/// AVX-2
218 		bool avx512f;	/// AVX-512
219 		bool avx512er;	/// AVX-512-ER
220 		bool avx512pf;	/// AVX-512-PF
221 		bool avx512cd;	/// AVX-512-CD
222 		bool avx512dq;	/// AVX-512-DQ
223 		bool avx512bw;	/// AVX-512-BW
224 		bool avx512vl;	/// AVX-512-VL
225 		bool avx512_ifma;	/// AVX-512-IFMA
226 		bool avx512_vbmi;	/// AVX-512-VBMI
227 		bool avx512_vbmi2;	/// AVX-512-VBMI2
228 		bool avx512_gfni;	/// AVX-512-GFNI
229 		bool avx512_vaes;	/// AVX-512-VAES
230 		bool avx512_vnni;	/// AVX-512-VNNI
231 		bool avx512_bitalg;	/// AVX-512-BITALG
232 		bool avx512_vpopcntdq;	/// AVX-512-VPOPCNTDQ
233 		bool avx512_4vnniw;	/// AVX-512-4VNNIW
234 		bool avx512_4fmaps;	/// AVX-512-4FMAPS
235 		bool avx512_bf16;	/// AVX-512-BF16
236 		bool avx512_vp2intersect;	/// AVX-512-VP2INTERSECT
237 		
238 		// AMX
239 		bool amx;	/// Advanced Matrix eXtension
240 		bool amx_bf16;	/// AMX-BF16
241 		bool amx_int8;	/// AMX-INT8
242 		bool amx_xtilecfg;	/// AMX-XTILECFG
243 		bool amx_xtiledata;	/// AMX-XTILEDATA
244 		bool amx_xfd;	/// AMX-XFD
245 	}
246 	align(2) Extensions ext;	/// Extensions
247 	
248 	/// Additional instructions. Often not part of extensions.
249 	struct Extras {
250 		bool pclmulqdq;	/// PCLMULQDQ instruction
251 		bool monitor;	/// MONITOR and MWAIT instructions
252 		ushort mwait_min;	/// (With MONITOR+MWAIT) MWAIT minimum size in bytes
253 		ushort mwait_max;	/// (With MONITOR+MWAIT) MWAIT maximum size in bytes
254 		bool cmpxchg8b;	/// CMPXCHG8B
255 		bool cmpxchg16b;	/// CMPXCHG16B instruction
256 		bool movbe;	/// MOVBE instruction
257 		bool rdrand;	/// RDRAND instruction
258 		bool rdseed;	/// RDSEED instruction
259 		bool rdmsr;	/// RDMSR instruction
260 		bool sysenter;	/// SYSENTER and SYSEXIT instructions
261 		bool rdtsc;	/// RDTSC instruction
262 		bool rdtsc_deadline;	/// (With RDTSC) IA32_TSC_DEADLINE MSR
263 		bool rdtsc_invariant;	/// (With RDTSC) Timestamp counter invariant of C/P/T-state
264 		bool rdtscp;	/// RDTSCP instruction
265 		bool rdpid;	/// RDPID instruction
266 		bool cmov;	/// CMOVcc instruction
267 		bool lzcnt;	/// LZCNT instruction
268 		bool popcnt;	/// POPCNT instruction
269 		bool xsave;	/// XSAVE and XRSTOR instructions
270 		bool osxsave;	/// OSXSAVE and XGETBV instructions
271 		bool fxsr;	/// FXSAVE and FXRSTOR instructions
272 		bool pconfig;	/// PCONFIG instruction
273 		bool cldemote;	/// CLDEMOTE instruction
274 		bool movdiri;	/// MOVDIRI instruction
275 		bool movdir64b;	/// MOVDIR64B instruction
276 		bool enqcmd;	/// ENQCMD instruction
277 		bool syscall;	/// SYSCALL and SYSRET instructions
278 		bool monitorx;	/// MONITORX and MWAITX instructions
279 		bool skinit;	/// SKINIT instruction
280 		bool serialize;	/// SERIALIZE instruction
281 	}
282 	align(2) Extras extras;	/// Additional instructions
283 	
284 	/// Processor technologies.
285 	struct Technologies {
286 		bool eist;	/// Intel SpeedStep/AMD PowerNow/AMD Cool'n'Quiet
287 		bool turboboost;	/// Intel TurboBoost/AMD CorePerformanceBoost
288 		bool turboboost30;	/// Intel TurboBoost 3.0
289 		bool smx;	/// Intel TXT
290 		bool sgx;	/// Intel SGX
291 		bool htt;	/// (HTT) HyperThreading Technology
292 	}
293 	align(2) Technologies tech;	/// Processor technologies
294 	
295 	/// Cache information.
296 	struct CacheInfo {
297 		uint levels;
298 		CACHEINFO[DDCPUID_CACHE_MAX] level;
299 		bool clflush;	/// CLFLUSH instruction
300 		ubyte clflush_linesize;	/// Linesize of CLFLUSH in bytes
301 		bool clflushopt;	/// CLFLUSH instruction
302 		bool cnxt_id;	/// L1 Context ID
303 		bool ss;	/// SelfSnoop
304 		bool prefetchw;	/// PREFETCHW instruction
305 		bool invpcid;	/// INVPCID instruction
306 		bool wbnoinvd;	/// WBNOINVD instruction
307 	}
308 	align(2) CacheInfo cache;	/// Cache information
309 	
310 	/// ACPI information.
311 	struct AcpiInfo {
312 		bool available;	/// ACPI
313 		bool apic;	/// APIC
314 		bool x2apic;	/// x2APIC
315 		bool arat;	/// Always-Running-APIC-Timer
316 		bool tm;	/// Thermal Monitor
317 		bool tm2;	/// Thermal Monitor 2
318 		ubyte max_apic_id;	/// Maximum APIC ID
319 		ubyte apic_id;	/// Initial APIC ID (running core where CPUID was called)
320 	}
321 	align(2) AcpiInfo acpi;	/// ACPI features
322 	
323 	/// Virtualization features. If a paravirtual interface is available,
324 	/// its information will be found here.
325 	struct Virtualization {
326 		bool available;	/// Intel VT-x/AMD-V
327 		ubyte version_;	/// (AMD) Virtualization platform version
328 		bool vme;	/// Enhanced vm8086
329 		bool apivc;	/// (AMD) APICv. Intel's is available via a MSR.
330 		union {
331 			package uint[3] vendor32;
332 			char[12] vendor;	/// Paravirtualization interface vendor string
333 		}
334 		uint vendor_id;	/// Effective paravirtualization vendor id
335 		
336 		//TODO: Consider bit flags for paravirtualization flags
337 		
338 		// VBox
339 		
340 		uint vbox_tsc_freq_khz;	/// (VBox) Timestamp counter frequency in KHz
341 		uint vbox_apic_freq_khz;	/// (VBox) Paravirtualization API KHz frequency
342 		
343 		// KVM
344 		
345 		bool kvm_feature_clocksource;	/// (KVM) kvmclock interface
346 		bool kvm_feature_nop_io_delay;	/// (KVM) No delays required on I/O operations
347 		bool kvm_feature_mmu_op;	/// (KVM) Deprecated
348 		bool kvm_feature_clocksource2;	/// (KVM) Remapped kvmclock interface
349 		bool kvm_feature_async_pf;	/// (KVM) Asynchronous Page Fault
350 		bool kvm_feature_steal_time;	/// (KVM) Steal time
351 		bool kvm_feature_pv_eoi;	/// (KVM) Paravirtualized End Of the Interrupt handler
352 		bool kvm_feature_pv_unhault;	/// (KVM) Paravirtualized spinlock
353 		bool kvm_feature_pv_tlb_flush;	/// (KVM) Paravirtualized TLB flush
354 		bool kvm_feature_async_pf_vmexit;	/// (KVM) Asynchronous Page Fault at VM exit
355 		bool kvm_feature_pv_send_ipi;	/// (KVM) Paravirtualized SEBD inter-processor-interrupt
356 		bool kvm_feature_pv_poll_control;	/// (KVM) Host-side polling on HLT
357 		bool kvm_feature_pv_sched_yield;	/// (KVM) paravirtualized scheduler yield
358 		bool kvm_feature_clocsource_stable_bit;	/// (KVM) kvmclock warning
359 		bool kvm_hint_realtime;	/// (KVM) vCPUs are never preempted for an unlimited amount of time
360 		
361 		// Hyper-V
362 		
363 		ushort hv_guest_vendor_id;	/// (Hyper-V) Paravirtualization Guest Vendor ID
364 		ushort hv_guest_build;	/// (Hyper-V) Paravirtualization Guest Build number
365 		ubyte hv_guest_os;	/// (Hyper-V) Paravirtualization Guest OS ID
366 		ubyte hv_guest_major;	/// (Hyper-V) Paravirtualization Guest OS Major version
367 		ubyte hv_guest_minor;	/// (Hyper-V) Paravirtualization Guest OS Minor version
368 		ubyte hv_guest_service;	/// (Hyper-V) Paravirtualization Guest Service ID
369 		bool hv_guest_opensource;	/// (Hyper-V) Paravirtualization Guest additions open-source
370 		bool hv_base_feat_vp_runtime_msr;	/// (Hyper-V) Virtual processor runtime MSR
371 		bool hv_base_feat_part_time_ref_count_msr;	/// (Hyper-V) Partition reference counter MSR
372 		bool hv_base_feat_basic_synic_msrs;	/// (Hyper-V) Basic Synthetic Interrupt Controller MSRs
373 		bool hv_base_feat_stimer_msrs;	/// (Hyper-V) Synthetic Timer MSRs
374 		bool hv_base_feat_apic_access_msrs;	/// (Hyper-V) APIC access MSRs (EOI, ICR, TPR)
375 		bool hv_base_feat_hypercall_msrs;	/// (Hyper-V) Hypercalls API MSRs
376 		bool hv_base_feat_vp_id_msr;	/// (Hyper-V) vCPU index MSR
377 		bool hv_base_feat_virt_sys_reset_msr;	/// (Hyper-V) Virtual system reset MSR
378 		bool hv_base_feat_stat_pages_msr;	/// (Hyper-V) Statistic pages MSRs
379 		bool hv_base_feat_part_ref_tsc_msr;	/// (Hyper-V) Partition reference timestamp counter MSR
380 		bool hv_base_feat_guest_idle_state_msr;	/// (Hyper-V) Virtual guest idle state MSR
381 		bool hv_base_feat_timer_freq_msrs;	/// (Hyper-V) Timer frequency MSRs (TSC and APIC)
382 		bool hv_base_feat_debug_msrs;	/// (Hyper-V) Debug MSRs
383 		bool hv_part_flags_create_part;	/// (Hyper-V) Partitions can be created
384 		bool hv_part_flags_access_part_id;	/// (Hyper-V) Partitions IDs can be accessed
385 		bool hv_part_flags_access_memory_pool;	/// (Hyper-V) Memory pool can be accessed
386 		bool hv_part_flags_adjust_msg_buffers;	/// (Hyper-V) Possible to adjust message buffers
387 		bool hv_part_flags_post_msgs;	/// (Hyper-V) Possible to send messages
388 		bool hv_part_flags_signal_events;	/// (Hyper-V) Possible to signal events
389 		bool hv_part_flags_create_port;	/// (Hyper-V) Possible to create ports
390 		bool hv_part_flags_connect_port;	/// (Hyper-V) Possible to connect to ports
391 		bool hv_part_flags_access_stats;	/// (Hyper-V) Can access statistics
392 		bool hv_part_flags_debugging;	/// (Hyper-V) Debugging features available
393 		bool hv_part_flags_cpu_mgmt;	/// (Hyper-V) Processor management available
394 		bool hv_part_flags_cpu_profiler;	/// (Hyper-V) Processor profiler available
395 		bool hv_part_flags_expanded_stack_walk;	/// (Hyper-V) Extended stack walking available
396 		bool hv_part_flags_access_vsm;	/// (Hyper-V) Virtual system monitor available
397 		bool hv_part_flags_access_vp_regs;	/// (Hyper-V) Virtual private registers available
398 		bool hv_part_flags_extended_hypercalls;	/// (Hyper-V) Extended hypercalls API available
399 		bool hv_part_flags_start_vp;	/// (Hyper-V) Virtual processor has started
400 		bool hv_pm_max_cpu_power_state_c0;	/// (Hyper-V) Processor C0 is maximum state
401 		bool hv_pm_max_cpu_power_state_c1;	/// (Hyper-V) Processor C1 is maximum state
402 		bool hv_pm_max_cpu_power_state_c2;	/// (Hyper-V) Processor C2 is maximum state
403 		bool hv_pm_max_cpu_power_state_c3;	/// (Hyper-V) Processor C3 is maximum state
404 		bool hv_pm_hpet_reqd_for_c3;	/// (Hyper-V) High-precision event timer required for C3 state
405 		bool hv_misc_feat_mwait;	/// (Hyper-V) MWAIT instruction available for guest
406 		bool hv_misc_feat_guest_debugging;	/// (Hyper-V) Guest supports debugging
407 		bool hv_misc_feat_perf_mon;	/// (Hyper-V) Performance monitor support available
408 		bool hv_misc_feat_pcpu_dyn_part_event;	/// (Hyper-V) Physicap CPU dynamic partitioning event available
409 		bool hv_misc_feat_xmm_hypercall_input;	/// (Hyper-V) Hypercalls via XMM registers available
410 		bool hv_misc_feat_guest_idle_state;	/// (Hyper-V) Virtual guest supports idle state
411 		bool hv_misc_feat_hypervisor_sleep_state;	/// (Hyper-V) Hypervisor supports sleep
412 		bool hv_misc_feat_query_numa_distance;	/// (Hyper-V) NUMA distance query available
413 		bool hv_misc_feat_timer_freq;	/// (Hyper-V) Determining timer frequencies available
414 		bool hv_misc_feat_inject_synmc_xcpt;	/// (Hyper-V) Support for injecting synthetic machine checks
415 		bool hv_misc_feat_guest_crash_msrs;	/// (Hyper-V) Guest crash MSR available
416 		bool hv_misc_feat_debug_msrs;	/// (Hyper-V) Debug MSR available
417 		bool hv_misc_feat_npiep1;	/// (Hyper-V) Documentation unavailable
418 		bool hv_misc_feat_disable_hypervisor;	/// (Hyper-V) Hypervisor can be disabled
419 		bool hv_misc_feat_ext_gva_range_for_flush_va_list;	/// (Hyper-V) Extended guest virtual address (GVA) ranges for FlushVirtualAddressList available
420 		bool hv_misc_feat_hypercall_output_xmm;	/// (Hyper-V) Returning hypercall output via XMM registers available
421 		bool hv_misc_feat_sint_polling_mode;	/// (Hyper-V) Synthetic interrupt source polling mode available
422 		bool hv_misc_feat_hypercall_msr_lock;	/// (Hyper-V) Hypercall MISR lock feature available
423 		bool hv_misc_feat_use_direct_synth_msrs;	/// (Hyper-V) Possible to directly use synthetic MSRs
424 		bool hv_hint_hypercall_for_process_switch;	/// (Hyper-V) Guest should use the Hypercall API for address space switches rather than MOV CR3
425 		bool hv_hint_hypercall_for_tlb_flush;	/// (Hyper-V) Guest should use the Hypercall API for local TLB flushes rather than INVLPG/MOV CR3
426 		bool hv_hint_hypercall_for_tlb_shootdown;	/// (Hyper-V) Guest should use the Hypercall API for inter-CPU TLB flushes rather than inter-processor-interrupts (IPI)
427 		bool hv_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)
428 		bool hv_hint_msr_for_sys_reset;	/// (Hyper-V) Guest should use the hypervisor-provided MSR for a system reset instead of traditional methods
429 		bool hv_hint_relax_time_checks;	/// (Hyper-V) Guest should relax timer-related checks (watchdogs/deadman timeouts) that rely on timely deliver of external interrupts
430 		bool hv_hint_dma_remapping;	/// (Hyper-V) Guest should use the direct memory access (DMA) remapping
431 		bool hv_hint_interrupt_remapping;	/// (Hyper-V) Guest should use the interrupt remapping
432 		bool hv_hint_x2apic_msrs;	/// (Hyper-V) Guest should use the X2APIC MSRs rather than memory mapped input/output (MMIO)
433 		bool hv_hint_deprecate_auto_eoi;	/// (Hyper-V) Guest should deprecate Auto EOI (End Of Interrupt) features
434 		bool hv_hint_synth_cluster_ipi_hypercall;	/// (Hyper-V) Guest should use the SyntheticClusterIpi Hypercall
435 		bool hv_hint_ex_proc_masks_interface;	/// (Hyper-V) Guest should use the newer ExProcessMasks interface over ProcessMasks
436 		bool hv_hint_nested_hyperv;	/// (Hyper-V) Hyper-V instance is nested within a Hyper-V partition
437 		bool hv_hint_int_for_mbec_syscalls;	/// (Hyper-V) Guest should use the INT instruction for Mode Based Execution Control (MBEC) system calls
438 		bool hv_hint_nested_enlightened_vmcs_interface;	/// (Hyper-V) Guest should use enlightened Virtual Machine Control Structure (VMCS) interfaces and nested enlightenment
439 		bool hv_host_feat_avic;	/// (Hyper-V) Hypervisor is using the Advanced Virtual Interrupt Controller (AVIC) overlay
440 		bool hv_host_feat_msr_bitmap;	/// (Hyper-V) Hypervisor is using MSR bitmaps
441 		bool hv_host_feat_perf_counter;	/// (Hyper-V) Hypervisor supports the architectural performance counter
442 		bool hv_host_feat_nested_paging;	/// (Hyper-V) Hypervisor is using nested paging
443 		bool hv_host_feat_dma_remapping;	/// (Hyper-V) Hypervisor is using direct memory access (DMA) remapping
444 		bool hv_host_feat_interrupt_remapping;	/// (Hyper-V) Hypervisor is using interrupt remapping
445 		bool hv_host_feat_mem_patrol_scrubber;	/// (Hyper-V) Hypervisor's memory patrol scrubber is present
446 		bool hv_host_feat_dma_prot_in_use;	/// (Hyper-V) Hypervisor is using direct memory access (DMA) protection
447 		bool hv_host_feat_hpet_requested;	/// (Hyper-V) Hypervisor requires a High Precision Event Timer (HPET)
448 		bool hv_host_feat_stimer_volatile;	/// (Hyper-V) Hypervisor's synthetic timers are volatile
449 	}
450 	align(2) Virtualization virt;	/// Virtualization features
451 	
452 	/// Memory features.
453 	struct Memory {
454 		bool pae;	/// Physical Address Extension 
455 		bool pse;	/// Page Size Extension
456 		bool pse_36;	/// 36-bit PSE
457 		bool page1gb;	/// 1GiB pages in 4-level paging and higher
458 		bool mtrr;	/// Memory Type Range Registers
459 		bool pat;	/// Page Attribute Table
460 		bool pge;	/// Page Global Bit
461 		bool dca;	/// Direct Cache Access
462 		bool nx;	/// Intel XD (No eXecute bit)
463 		union {
464 			uint tsx;	/// Intel TSX. If set, has one of HLE, RTM, or TSXLDTRK.
465 			struct {
466 				bool hle;	/// (TSX) Hardware Lock Elision
467 				bool rtm;	/// (TSX) Restricted Transactional Memory
468 				bool tsxldtrk;	/// (TSX) Suspend Load Address Tracking
469 			}
470 		}
471 		bool smep;	/// Supervisor Mode Execution Protection
472 		bool smap;	/// Supervisor Mode Access Protection
473 		bool pku;	/// Protection Key Units
474 		bool _5pl;	/// 5-level paging
475 		bool fsrepmov;	/// Fast Short REP MOVSB optimization
476 		bool lam;	/// Linear Address Masking
477 		union {
478 			package ushort b_8000_0008_ax;
479 			struct {
480 				ubyte phys_bits;	/// Memory physical bits
481 				ubyte line_bits;	/// Memory linear bits
482 			}
483 		}
484 	}
485 	align (2) Memory mem;	/// Memory features
486 	
487 	/// Debugging features.
488 	struct Debugging {
489 		bool mca;	/// Machine Check Architecture
490 		bool mce;	/// Machine Check Exception
491 		bool de;	/// Degging Extensions
492 		bool ds;	/// Debug Store
493 		bool ds_cpl;	/// Debug Store - Curernt Privilege Level
494 		bool dtes64;	/// 64-bit Debug Store area
495 		bool pdcm;	/// Perfmon And Debug Capability
496 		bool sdbg;	/// Silicon Debug
497 		bool pbe;	/// Pending Break Enable
498 	}
499 	align(2) Debugging dbg;	/// Debugging feature
500 	
501 	/// Security features and mitigations.
502 	struct Security {
503 		bool ia32_arch_capabilities;	/// IA32_ARCH_CAPABILITIES MSR
504 		// NOTE: IA32_CORE_CAPABILITIES is currently empty
505 		bool ibpb;	/// Indirect Branch Predictor Barrier
506 		bool ibrs;	/// Indirect Branch Restricted Speculation
507 		bool ibrs_on;	/// IBRS always enabled
508 		bool ibrs_pref;	/// IBRS preferred
509 		bool stibp;	/// Single Thread Indirect Branch Predictors
510 		bool stibp_on;	/// STIBP always enabled
511 		bool ssbd;	/// Speculative Store Bypass Disable
512 		bool l1d_flush;	/// L1D Cache Flush
513 		bool md_clear;	/// MDS mitigation
514 		bool cet_ibt;	/// (Control-flow Enforcement Technology) Indirect Branch Tracking 
515 		bool cet_ss;	/// (Control-flow Enforcement Technology) Shadow Stack
516 	}
517 	align(2) Security sec;	/// Security features
518 	
519 	/// Miscellaneous features.
520 	struct Miscellaneous {
521 		bool psn;	/// Processor Serial Number (Pentium III only)
522 		bool pcid;	/// PCID
523 		bool xtpr;	/// xTPR
524 		bool fsgsbase;	/// FS and GS register base
525 		bool uintr;	/// User Interrupts
526 	}
527 	align(2) Miscellaneous misc;	/// Miscellaneous features
528 }
529 
530 // EAX[4:0], 0-31, but there aren't that many
531 // So we limit it to 0-7
532 private
533 enum CACHE_MASK = 7; // Max 31
534 private
535 immutable const(char)* CACHE_TYPE = "?DIU????";
536 
537 private
538 immutable const(char)*[] PROCESSOR_TYPE = [ "Original", "OverDrive", "Dual", "Reserved" ];
539 
540 pragma(inline, false)
541 void asmcpuid(ref REGISTERS regs, uint level, uint sublevel = 0) {
542 	version (DMD) {
543 		version (X86) asm {
544 			mov EDI, regs;
545 			mov EAX, level;
546 			mov ECX, sublevel;
547 			cpuid;
548 			mov [EDI + regs.eax.offsetof], EAX;
549 			mov [EDI + regs.ebx.offsetof], EBX;
550 			mov [EDI + regs.ecx.offsetof], ECX;
551 			mov [EDI + regs.edx.offsetof], EDX;
552 		} else version (X86_64) asm {
553 			mov RDI, regs;
554 			mov EAX, level;
555 			mov ECX, sublevel;
556 			cpuid;
557 			mov [RDI + regs.eax.offsetof], EAX;
558 			mov [RDI + regs.ebx.offsetof], EBX;
559 			mov [RDI + regs.ecx.offsetof], ECX;
560 			mov [RDI + regs.edx.offsetof], EDX;
561 		}
562 	} else version (GDC) {
563 		asm {
564 			"cpuid"
565 			: "=a" (regs.eax), "=b" (regs.ebx), "=c" (regs.ecx), "=d" (regs.edx)
566 			: "a" (level), "c" (sublevel);
567 		}
568 	} else version (LDC) {
569 		version (X86) asm {
570 			lea EDI, regs;
571 			mov EAX, level;
572 			mov ECX, sublevel;
573 			cpuid;
574 			mov [EDI + regs.eax.offsetof], EAX;
575 			mov [EDI + regs.ebx.offsetof], EBX;
576 			mov [EDI + regs.ecx.offsetof], ECX;
577 			mov [EDI + regs.edx.offsetof], EDX;
578 		} else version (X86_64) asm {
579 			lea RDI, regs;
580 			mov EAX, level;
581 			mov ECX, sublevel;
582 			cpuid;
583 			mov [RDI + regs.eax.offsetof], EAX;
584 			mov [RDI + regs.ebx.offsetof], EBX;
585 			mov [RDI + regs.ecx.offsetof], ECX;
586 			mov [RDI + regs.edx.offsetof], EDX;
587 		}
588 	}
589 }
590 
591 /// Get CPU leaf levels.
592 /// Params: info = CPUINFO structure
593 pragma(inline, false)
594 void getLeaves(ref CPUINFO info) {
595 	version (DMD) {
596 		version (X86) asm {
597 			mov EDI, info;
598 			mov EAX, 0;
599 			cpuid;
600 			mov [EDI + info.max_leaf.offsetof], EAX;
601 			mov EAX, 0x4000_0000;
602 			cpuid;
603 			mov [EDI + info.max_virt_leaf.offsetof], EAX;
604 			mov EAX, 0x8000_0000;
605 			cpuid;
606 			mov [EDI + info.max_ext_leaf.offsetof], EAX;
607 		} else version (X86_64) asm {
608 			mov RDI, info;
609 			mov EAX, 0;
610 			cpuid;
611 			mov [RDI + info.max_leaf.offsetof], EAX;
612 			mov EAX, 0x4000_0000;
613 			cpuid;
614 			mov [RDI + info.max_virt_leaf.offsetof], EAX;
615 			mov EAX, 0x8000_0000;
616 			cpuid;
617 			mov [RDI + info.max_ext_leaf.offsetof], EAX;
618 		}
619 	} else version (GDC) {
620 		asm {
621 			"cpuid"
622 			: "=a" (info.max_leaf)
623 			: "a" (0);
624 		}
625 		asm {
626 			"cpuid"
627 			: "=a" (info.max_virt_leaf)
628 			: "a" (0x40000000);
629 		}
630 		asm {
631 			"cpuid"
632 			: "=a" (info.max_ext_leaf)
633 			: "a" (0x80000000);
634 		}
635 	} else version (LDC) {
636 		version (X86) asm {
637 			lea EDI, info;
638 			mov EAX, 0;
639 			cpuid;
640 			mov [EDI + info.max_leaf.offsetof], EAX;
641 			mov EAX, 0x4000_0000;
642 			cpuid;
643 			mov [EDI + info.max_virt_leaf.offsetof], EAX;
644 			mov EAX, 0x8000_0000;
645 			cpuid;
646 			mov [EDI + info.max_ext_leaf.offsetof], EAX;
647 		} else version (X86_64) asm {
648 			lea RDI, info;
649 			mov EAX, 0;
650 			cpuid;
651 			mov [RDI + info.max_leaf.offsetof], EAX;
652 			mov EAX, 0x4000_0000;
653 			cpuid;
654 			mov [RDI + info.max_virt_leaf.offsetof], EAX;
655 			mov EAX, 0x8000_0000;
656 			cpuid;
657 			mov [RDI + info.max_ext_leaf.offsetof], EAX;
658 		}
659 	}
660 }
661 
662 /// Fetch CPU vendor
663 pragma(inline, false)
664 void getVendor(ref CPUINFO info) {
665 	version (DMD) {
666 		version (X86) asm {
667 			mov EDI, info;
668 			mov EAX, 0;
669 			cpuid;
670 			mov [EDI + VENDOR_OFFSET], EBX;
671 			mov [EDI + VENDOR_OFFSET + 4], EDX;
672 			mov [EDI + VENDOR_OFFSET + 8], ECX;
673 		} else asm { // x86-64
674 			mov RDI, info;
675 			mov EAX, 0;
676 			cpuid;
677 			mov [RDI + VENDOR_OFFSET], EBX;
678 			mov [RDI + VENDOR_OFFSET + 4], EDX;
679 			mov [RDI + VENDOR_OFFSET + 8], ECX;
680 		}
681 	} else version (GDC) {
682 		version (X86) asm {
683 			"lea %0, %%edi\n\t"~
684 			"mov $0, %%eax\n\t"~
685 			"cpuid\n"~
686 			"mov %%ebx, (%%edi)\n\t"~
687 			"mov %%edx, 4(%%edi)\n\t"~
688 			"mov %%ecx, 8(%%edi)"
689 			:
690 			: "m" (info.vendor)
691 			: "edi", "eax", "ebx", "ecx", "edx";
692 		} else asm { // x86-64
693 			"lea %0, %%rdi\n\t"~
694 			"mov $0, %%eax\n\t"~
695 			"cpuid\n"~
696 			"mov %%ebx, (%%rdi)\n\t"~
697 			"mov %%edx, 4(%%rdi)\n\t"~
698 			"mov %%ecx, 8(%%rdi)"
699 			:
700 			: "m" (info.vendor)
701 			: "rdi", "rax", "rbx", "rcx", "rdx";
702 		}
703 	} else version (LDC) {
704 		version (X86) asm {
705 			lea EDI, info;
706 			mov EAX, 0;
707 			cpuid;
708 			mov [EDI + VENDOR_OFFSET], EBX;
709 			mov [EDI + VENDOR_OFFSET + 4], EDX;
710 			mov [EDI + VENDOR_OFFSET + 8], ECX;
711 		} else asm { // x86-64
712 			lea RDI, info;
713 			mov EAX, 0;
714 			cpuid;
715 			mov [RDI + VENDOR_OFFSET], EBX;
716 			mov [RDI + VENDOR_OFFSET + 4], EDX;
717 			mov [RDI + VENDOR_OFFSET + 8], ECX;
718 		}
719 	}
720 	
721 	// Vendor string verification
722 	// If the rest of the string doesn't correspond, the id is unset
723 	switch (info.vendor32[0]) {
724 	case VENDOR_INTEL:	// "GenuineIntel"
725 		if (info.vendor32[1] != ID!("ineI")) goto default;
726 		if (info.vendor32[2] != ID!("ntel")) goto default;
727 		break;
728 	case VENDOR_AMD:	// "AuthenticAMD"
729 		if (info.vendor32[1] != ID!("enti")) goto default;
730 		if (info.vendor32[2] != ID!("cAMD")) goto default;
731 		break;
732 	case VENDOR_VIA:	// "VIA VIA VIA "
733 		if (info.vendor32[1] != ID!("VIA ")) goto default;
734 		if (info.vendor32[2] != ID!("VIA ")) goto default;
735 		break;
736 	default: // Unknown
737 		info.vendor_id = 0;
738 		return;
739 	}
740 	
741 	info.vendor_id = info.vendor32[0];
742 }
743 
744 pragma(inline, false)
745 private
746 void getBrand(ref CPUINFO info) {
747 	version (DMD) {
748 		version (X86) asm {
749 			mov EDI, info;
750 			mov EAX, 0x8000_0002;
751 			cpuid;
752 			mov [EDI + BRAND_OFFSET], EAX;
753 			mov [EDI + BRAND_OFFSET +  4], EBX;
754 			mov [EDI + BRAND_OFFSET +  8], ECX;
755 			mov [EDI + BRAND_OFFSET + 12], EDX;
756 			mov EAX, 0x8000_0003;
757 			cpuid;
758 			mov [EDI + BRAND_OFFSET + 16], EAX;
759 			mov [EDI + BRAND_OFFSET + 20], EBX;
760 			mov [EDI + BRAND_OFFSET + 24], ECX;
761 			mov [EDI + BRAND_OFFSET + 28], EDX;
762 			mov EAX, 0x8000_0004;
763 			cpuid;
764 			mov [EDI + BRAND_OFFSET + 32], EAX;
765 			mov [EDI + BRAND_OFFSET + 36], EBX;
766 			mov [EDI + BRAND_OFFSET + 40], ECX;
767 			mov [EDI + BRAND_OFFSET + 44], EDX;
768 		} else version (X86_64) asm {
769 			mov RDI, info;
770 			mov EAX, 0x8000_0002;
771 			cpuid;
772 			mov [RDI + BRAND_OFFSET], EAX;
773 			mov [RDI + BRAND_OFFSET +  4], EBX;
774 			mov [RDI + BRAND_OFFSET +  8], ECX;
775 			mov [RDI + BRAND_OFFSET + 12], EDX;
776 			mov EAX, 0x8000_0003;
777 			cpuid;
778 			mov [RDI + BRAND_OFFSET + 16], EAX;
779 			mov [RDI + BRAND_OFFSET + 20], EBX;
780 			mov [RDI + BRAND_OFFSET + 24], ECX;
781 			mov [RDI + BRAND_OFFSET + 28], EDX;
782 			mov EAX, 0x8000_0004;
783 			cpuid;
784 			mov [RDI + BRAND_OFFSET + 32], EAX;
785 			mov [RDI + BRAND_OFFSET + 36], EBX;
786 			mov [RDI + BRAND_OFFSET + 40], ECX;
787 			mov [RDI + BRAND_OFFSET + 44], EDX;
788 		}
789 	} else version (GDC) {
790 		version (X86) asm {
791 			"lea %0, %%edi\n\t"~
792 			"mov $0x80000002, %%eax\n\t"~
793 			"cpuid\n\t"~
794 			"mov %%eax, (%%rdi)\n\t"~
795 			"mov %%ebx, 4(%%rdi)\n\t"~
796 			"mov %%ecx, 8(%%rdi)\n\t"~
797 			"mov %%edx, 12(%%rdi)\n\t"~
798 			"mov $0x80000003, %%eax\n\t"~
799 			"cpuid\n\t"~
800 			"mov %%eax, 16(%%rdi)\n\t"~
801 			"mov %%ebx, 20(%%rdi)\n\t"~
802 			"mov %%ecx, 24(%%rdi)\n\t"~
803 			"mov %%edx, 28(%%rdi)\n\t"~
804 			"mov $0x80000004, %%eax\n\t"~
805 			"cpuid\n\t"~
806 			"mov %%eax, 32(%%rdi)\n\t"~
807 			"mov %%ebx, 36(%%rdi)\n\t"~
808 			"mov %%ecx, 40(%%rdi)\n\t"~
809 			"mov %%edx, 44(%%rdi)"
810 			:
811 			: "m" (info.brand)
812 			: "edi", "eax", "ebx", "ecx", "edx";
813 		} else version (X86_64) asm {
814 			"lea %0, %%rdi\n\t"~
815 			"mov $0x80000002, %%eax\n\t"~
816 			"cpuid\n\t"~
817 			"mov %%eax, (%%rdi)\n\t"~
818 			"mov %%ebx, 4(%%rdi)\n\t"~
819 			"mov %%ecx, 8(%%rdi)\n\t"~
820 			"mov %%edx, 12(%%rdi)\n\t"~
821 			"mov $0x80000003, %%eax\n\t"~
822 			"cpuid\n\t"~
823 			"mov %%eax, 16(%%rdi)\n\t"~
824 			"mov %%ebx, 20(%%rdi)\n\t"~
825 			"mov %%ecx, 24(%%rdi)\n\t"~
826 			"mov %%edx, 28(%%rdi)\n\t"~
827 			"mov $0x80000004, %%eax\n\t"~
828 			"cpuid\n\t"~
829 			"mov %%eax, 32(%%rdi)\n\t"~
830 			"mov %%ebx, 36(%%rdi)\n\t"~
831 			"mov %%ecx, 40(%%rdi)\n\t"~
832 			"mov %%edx, 44(%%rdi)"
833 			:
834 			: "m" (info.brand)
835 			: "rdi", "rax", "rbx", "rcx", "rdx";
836 		}
837 	} else version (LDC) {
838 		version (X86) asm {
839 			lea EDI, info;
840 			mov EAX, 0x8000_0002;
841 			cpuid;
842 			mov [EDI + BRAND_OFFSET], EAX;
843 			mov [EDI + BRAND_OFFSET +  4], EBX;
844 			mov [EDI + BRAND_OFFSET +  8], ECX;
845 			mov [EDI + BRAND_OFFSET + 12], EDX;
846 			mov EAX, 0x8000_0003;
847 			cpuid;
848 			mov [EDI + BRAND_OFFSET + 16], EAX;
849 			mov [EDI + BRAND_OFFSET + 20], EBX;
850 			mov [EDI + BRAND_OFFSET + 24], ECX;
851 			mov [EDI + BRAND_OFFSET + 28], EDX;
852 			mov EAX, 0x8000_0004;
853 			cpuid;
854 			mov [EDI + BRAND_OFFSET + 32], EAX;
855 			mov [EDI + BRAND_OFFSET + 36], EBX;
856 			mov [EDI + BRAND_OFFSET + 40], ECX;
857 			mov [EDI + BRAND_OFFSET + 44], EDX;
858 		} else version (X86_64) asm {
859 			lea RDI, info;
860 			mov EAX, 0x8000_0002;
861 			cpuid;
862 			mov [RDI + BRAND_OFFSET], EAX;
863 			mov [RDI + BRAND_OFFSET +  4], EBX;
864 			mov [RDI + BRAND_OFFSET +  8], ECX;
865 			mov [RDI + BRAND_OFFSET + 12], EDX;
866 			mov EAX, 0x8000_0003;
867 			cpuid;
868 			mov [RDI + BRAND_OFFSET + 16], EAX;
869 			mov [RDI + BRAND_OFFSET + 20], EBX;
870 			mov [RDI + BRAND_OFFSET + 24], ECX;
871 			mov [RDI + BRAND_OFFSET + 28], EDX;
872 			mov EAX, 0x8000_0004;
873 			cpuid;
874 			mov [RDI + BRAND_OFFSET + 32], EAX;
875 			mov [RDI + BRAND_OFFSET + 36], EBX;
876 			mov [RDI + BRAND_OFFSET + 40], ECX;
877 			mov [RDI + BRAND_OFFSET + 44], EDX;
878 		}
879 	}
880 }
881 
882 pragma(inline, false)
883 private
884 void getVirtVendor(ref CPUINFO info) {
885 	version (DMD) {
886 		version (X86) asm {
887 			mov EDI, info;
888 			mov EAX, 0x40000000;
889 			cpuid;
890 			mov [EDI + VIRTVENDOR_OFFSET], EBX;
891 			mov [EDI + VIRTVENDOR_OFFSET + 4], ECX;
892 			mov [EDI + VIRTVENDOR_OFFSET + 8], EDX;
893 		} else asm { // x86-64
894 			mov RDI, info;
895 			mov EAX, 0x40000000;
896 			cpuid;
897 			mov [RDI + VIRTVENDOR_OFFSET], EBX;
898 			mov [RDI + VIRTVENDOR_OFFSET + 4], ECX;
899 			mov [RDI + VIRTVENDOR_OFFSET + 8], EDX;
900 		}
901 	} else version (GDC) {
902 		version (X86) asm {
903 			"lea %0, %%edi\n\t"~
904 			"mov $0x40000000, %%eax\n\t"~
905 			"cpuid\n"~
906 			"mov %%ebx, (%%edi)\n\t"~
907 			"mov %%ecx, 4(%%edi)\n\t"~
908 			"mov %%edx, 8(%%edi)"
909 			:
910 			: "m" (info.virt.vendor)
911 			: "edi", "eax", "ebx", "ecx", "edx";
912 		} else asm { // x86-64
913 			"lea %0, %%rdi\n\t"~
914 			"mov $0x40000000, %%eax\n\t"~
915 			"cpuid\n"~
916 			"mov %%ebx, (%%rdi)\n\t"~
917 			"mov %%ecx, 4(%%rdi)\n\t"~
918 			"mov %%edx, 8(%%rdi)"
919 			:
920 			: "m" (info.virt.vendor)
921 			: "rdi", "rax", "rbx", "rcx", "rdx";
922 		}
923 	} else version (LDC) {
924 		version (X86) asm {
925 			lea EDI, info;
926 			mov EAX, 0x40000000;
927 			cpuid;
928 			mov [EDI + VIRTVENDOR_OFFSET], EBX;
929 			mov [EDI + VIRTVENDOR_OFFSET + 4], ECX;
930 			mov [EDI + VIRTVENDOR_OFFSET + 8], EDX;
931 		} else asm { // x86-64
932 			lea RDI, info;
933 			mov EAX, 0x40000000;
934 			cpuid;
935 			mov [RDI + VIRTVENDOR_OFFSET], EBX;
936 			mov [RDI + VIRTVENDOR_OFFSET + 4], ECX;
937 			mov [RDI + VIRTVENDOR_OFFSET + 8], EDX;
938 		}
939 	}
940 	
941 	// Paravirtual vendor string verification
942 	// If the rest of the string doesn't correspond, the id is unset
943 	switch (info.virt.vendor32[0]) {
944 	case VIRT_VENDOR_KVM:	// "KVMKVMKVM\0\0\0"
945 		if (info.virt.vendor32[1] != ID!("VMKV")) goto default;
946 		if (info.virt.vendor32[2] != ID!("M\0\0\0")) goto default;
947 		info.virt.vendor_id = VIRT_VENDOR_KVM;
948 		break;
949 	case VIRT_VENDOR_VBOX_HV:	// "VBoxVBoxVBox"
950 		if (info.virt.vendor32[1] != ID!("VBox")) goto default;
951 		if (info.virt.vendor32[2] != ID!("VBox")) goto default;
952 		info.virt.vendor_id = VIRT_VENDOR_VBOX_HV;
953 		break;
954 	default:
955 		info.virt.vendor_id = 0;
956 	}
957 }
958 
959 /// Fetch CPU information.
960 /// Params: info = CPUINFO structure
961 // There are essentially 5 sections to this function:
962 // - Brand String
963 // - Normal leaf information
964 // - Paravirtualization leaf information
965 // - Extended leaf information
966 // - Cache information
967 pragma(inline, false)
968 void getInfo(ref CPUINFO info) {
969 	getVendor(info);
970 	getBrand(info);
971 	
972 	REGISTERS regs = void;
973 	
974 	//
975 	// Leaf 1H
976 	//
977 	
978 	asmcpuid(regs, 1);
979 	
980 	// EAX
981 	info.stepping    = regs.eax & 0xF;        // EAX[3:0]
982 	info.model_base  = regs.eax >>  4 &  0xF; // EAX[7:4]
983 	info.family_base = regs.eax >>  8 &  0xF; // EAX[11:8]
984 	info.type        = regs.eax >> 12 & 0b11; // EAX[13:12]
985 	info.type_string = PROCESSOR_TYPE[info.type];
986 	info.model_ext   = regs.eax >> 16 &  0xF; // EAX[19:16]
987 	info.family_ext  = cast(ubyte)(regs.eax >> 20); // EAX[27:20]
988 	
989 	switch (info.vendor_id) {
990 	case VENDOR_INTEL:
991 		info.family = info.family_base != 0 ?
992 			info.family_base :
993 			cast(ubyte)(info.family_ext + info.family_base);
994 		
995 		info.model = info.family_base == 6 || info.family_base == 0 ?
996 			cast(ubyte)((info.model_ext << 4) + info.model_base) :
997 			info.model_base; // DisplayModel = Model_ID;
998 		
999 		// ECX
1000 		info.dbg.dtes64	= (regs.ecx & BIT!(2)) != 0;
1001 		info.dbg.ds_cpl	= (regs.ecx & BIT!(4)) != 0;
1002 		info.virt.available	= (regs.ecx & BIT!(5)) != 0;
1003 		info.tech.smx	= (regs.ecx & BIT!(6)) != 0;
1004 		info.tech.eist	= (regs.ecx & BIT!(7)) != 0;
1005 		info.acpi.tm2	= (regs.ecx & BIT!(8)) != 0;
1006 		info.cache.cnxt_id	= (regs.ecx & BIT!(10)) != 0;
1007 		info.dbg.sdbg	= (regs.ecx & BIT!(11)) != 0;
1008 		info.misc.xtpr	= (regs.ecx & BIT!(14)) != 0;
1009 		info.dbg.pdcm	= (regs.ecx & BIT!(15)) != 0;
1010 		info.misc.pcid	= (regs.ecx & BIT!(17)) != 0;
1011 		info.dbg.mca	= (regs.ecx & BIT!(18)) != 0;
1012 		info.acpi.x2apic	= (regs.ecx & BIT!(21)) != 0;
1013 		info.extras.rdtsc_deadline	= (regs.ecx & BIT!(24)) != 0;
1014 		
1015 		// EDX
1016 		info.misc.psn	= (regs.edx & BIT!(18)) != 0;
1017 		info.dbg.ds	= (regs.edx & BIT!(21)) != 0;
1018 		info.acpi.available	= (regs.edx & BIT!(22)) != 0;
1019 		info.cache.ss	= (regs.edx & BIT!(27)) != 0;
1020 		info.acpi.tm	= (regs.edx & BIT!(29)) != 0;
1021 		info.dbg.pbe	= regs.edx >= BIT!(31);
1022 		break;
1023 	case VENDOR_AMD:
1024 		if (info.family_base < 0xF) {
1025 			info.family = info.family_base;
1026 			info.model = info.model_base;
1027 		} else {
1028 			info.family = cast(ubyte)(info.family_ext + info.family_base);
1029 			info.model = cast(ubyte)((info.model_ext << 4) + info.model_base);
1030 		}
1031 		break;
1032 	default:
1033 	}
1034 	
1035 	// EBX
1036 	__01ebx_t t = void; // BrandIndex, CLFLUSHLineSize, MaxIDs, InitialAPICID
1037 	t.all = regs.ebx;
1038 	info.brand_index = t.brand_index;
1039 	info.cache.clflush_linesize = t.clflush_linesize;
1040 	info.acpi.max_apic_id = t.max_apic_id;
1041 	info.acpi.apic_id = t.apic_id;
1042 	
1043 	// ECX
1044 	info.ext.sse3	= (regs.ecx & BIT!(0)) != 0;
1045 	info.extras.pclmulqdq	= (regs.ecx & BIT!(1)) != 0;
1046 	info.extras.monitor	= (regs.ecx & BIT!(3)) != 0;
1047 	info.ext.ssse3	= (regs.ecx & BIT!(9)) != 0;
1048 	info.ext.fma3	= (regs.ecx & BIT!(12)) != 0;
1049 	info.extras.cmpxchg16b	= (regs.ecx & BIT!(13)) != 0;
1050 	info.ext.sse41	= (regs.ecx & BIT!(15)) != 0;
1051 	info.ext.sse42	= (regs.ecx & BIT!(20)) != 0;
1052 	info.extras.movbe	= (regs.ecx & BIT!(22)) != 0;
1053 	info.extras.popcnt	= (regs.ecx & BIT!(23)) != 0;
1054 	info.ext.aes_ni	= (regs.ecx & BIT!(25)) != 0;
1055 	info.extras.xsave	= (regs.ecx & BIT!(26)) != 0;
1056 	info.extras.osxsave	= (regs.ecx & BIT!(27)) != 0;
1057 	info.ext.avx	= (regs.ecx & BIT!(28)) != 0;
1058 	info.ext.f16c	= (regs.ecx & BIT!(29)) != 0;
1059 	info.extras.rdrand	= (regs.ecx & BIT!(30)) != 0;
1060 	
1061 	// EDX
1062 	info.ext.fpu	= (regs.edx & BIT!(0)) != 0;
1063 	info.virt.vme	= (regs.edx & BIT!(1)) != 0;
1064 	info.dbg.de	= (regs.edx & BIT!(2)) != 0;
1065 	info.mem.pse	= (regs.edx & BIT!(3)) != 0;
1066 	info.extras.rdtsc	= (regs.edx & BIT!(4)) != 0;
1067 	info.extras.rdmsr	= (regs.edx & BIT!(5)) != 0;
1068 	info.mem.pae	= (regs.edx & BIT!(6)) != 0;
1069 	info.dbg.mce	= (regs.edx & BIT!(7)) != 0;
1070 	info.extras.cmpxchg8b	= (regs.edx & BIT!(8)) != 0;
1071 	info.acpi.apic	= (regs.edx & BIT!(9)) != 0;
1072 	info.extras.sysenter	= (regs.edx & BIT!(11)) != 0;
1073 	info.mem.mtrr	= (regs.edx & BIT!(12)) != 0;
1074 	info.mem.pge	= (regs.edx & BIT!(13)) != 0;
1075 	info.dbg.mca	= (regs.edx & BIT!(14)) != 0;
1076 	info.extras.cmov	= (regs.edx & BIT!(15)) != 0;
1077 	info.mem.pat	= (regs.edx & BIT!(16)) != 0;
1078 	info.mem.pse_36	= (regs.edx & BIT!(17)) != 0;
1079 	info.cache.clflush	= (regs.edx & BIT!(19)) != 0;
1080 	info.ext.mmx	= (regs.edx & BIT!(23)) != 0;
1081 	info.extras.fxsr	= (regs.edx & BIT!(24)) != 0;
1082 	info.ext.sse	= (regs.edx & BIT!(25)) != 0;
1083 	info.ext.sse2	= (regs.edx & BIT!(26)) != 0;
1084 	info.tech.htt	= (regs.edx & BIT!(28)) != 0;
1085 	
1086 	switch (info.vendor_id) {
1087 	case VENDOR_AMD:
1088 		if (info.tech.htt)
1089 			info.cores.logical = info.acpi.max_apic_id;
1090 		break;
1091 	default:
1092 	}
1093 	
1094 	if (info.max_leaf < 5) goto L_VIRT;
1095 	
1096 	//
1097 	// Leaf 5H
1098 	//
1099 	
1100 	asmcpuid(regs, 5);
1101 	
1102 	info.extras.mwait_min = cast(ushort)regs.eax;
1103 	info.extras.mwait_max = cast(ushort)regs.ebx;
1104 	
1105 	if (info.max_leaf < 6) goto L_VIRT;
1106 	
1107 	//
1108 	// Leaf 6H
1109 	//
1110 	
1111 	asmcpuid(regs, 6);
1112 	
1113 	switch (info.vendor_id) {
1114 	case VENDOR_INTEL:
1115 		info.tech.turboboost	= (regs.eax & BIT!(1)) != 0;
1116 		info.tech.turboboost30	= (regs.eax & BIT!(14)) != 0;
1117 		break;
1118 	default:
1119 	}
1120 	
1121 	info.acpi.arat = (regs.eax & BIT!(2)) != 0;
1122 	
1123 	if (info.max_leaf < 7) goto L_VIRT;
1124 	
1125 	//
1126 	// Leaf 7H
1127 	//
1128 	
1129 	asmcpuid(regs, 7);
1130 	
1131 	switch (info.vendor_id) {
1132 	case VENDOR_INTEL:
1133 		// EBX
1134 		info.tech.sgx	= (regs.ebx & BIT!(2)) != 0;
1135 		info.mem.hle	= (regs.ebx & BIT!(4)) != 0;
1136 		info.cache.invpcid	= (regs.ebx & BIT!(10)) != 0;
1137 		info.mem.rtm	= (regs.ebx & BIT!(11)) != 0;
1138 		info.ext.avx512f	= (regs.ebx & BIT!(16)) != 0;
1139 		info.mem.smap	= (regs.ebx & BIT!(20)) != 0;
1140 		info.ext.avx512er	= (regs.ebx & BIT!(27)) != 0;
1141 		info.ext.avx512pf	= (regs.ebx & BIT!(26)) != 0;
1142 		info.ext.avx512cd	= (regs.ebx & BIT!(28)) != 0;
1143 		info.ext.avx512dq	= (regs.ebx & BIT!(17)) != 0;
1144 		info.ext.avx512bw	= (regs.ebx & BIT!(30)) != 0;
1145 		info.ext.avx512_ifma	= (regs.ebx & BIT!(21)) != 0;
1146 		info.ext.avx512_vbmi	= regs.ebx >= BIT!(31);
1147 		// ECX
1148 		info.ext.avx512vl	= (regs.ecx & BIT!(1)) != 0;
1149 		info.mem.pku	= (regs.ecx & BIT!(3)) != 0;
1150 		info.mem.fsrepmov	= (regs.ecx & BIT!(4)) != 0;
1151 		info.ext.waitpkg	= (regs.ecx & BIT!(5)) != 0;
1152 		info.ext.avx512_vbmi2	= (regs.ecx & BIT!(6)) != 0;
1153 		info.sec.cet_ss	= (regs.ecx & BIT!(7)) != 0;
1154 		info.ext.avx512_gfni	= (regs.ecx & BIT!(8)) != 0;
1155 		info.ext.avx512_vaes	= (regs.ecx & BIT!(9)) != 0;
1156 		info.ext.avx512_vnni	= (regs.ecx & BIT!(11)) != 0;
1157 		info.ext.avx512_bitalg	= (regs.ecx & BIT!(12)) != 0;
1158 		info.ext.avx512_vpopcntdq	= (regs.ecx & BIT!(14)) != 0;
1159 		info.mem._5pl	= (regs.ecx & BIT!(16)) != 0;
1160 		info.extras.cldemote	= (regs.ecx & BIT!(25)) != 0;
1161 		info.extras.movdiri	= (regs.ecx & BIT!(27)) != 0;
1162 		info.extras.movdir64b	= (regs.ecx & BIT!(28)) != 0;
1163 		info.extras.enqcmd	= (regs.ecx & BIT!(29)) != 0;
1164 		// EDX
1165 		info.ext.avx512_4vnniw	= (regs.edx & BIT!(2)) != 0;
1166 		info.ext.avx512_4fmaps	= (regs.edx & BIT!(3)) != 0;
1167 		info.misc.uintr	= (regs.edx & BIT!(5)) != 0;
1168 		info.ext.avx512_vp2intersect	= (regs.edx & BIT!(8)) != 0;
1169 		info.sec.md_clear	= (regs.edx & BIT!(10)) != 0;
1170 		info.extras.serialize	= (regs.edx & BIT!(14)) != 0;
1171 		info.mem.tsxldtrk	= (regs.edx & BIT!(16)) != 0;
1172 		info.extras.pconfig	= (regs.edx & BIT!(18)) != 0;
1173 		info.sec.cet_ibt	= (regs.edx & BIT!(20)) != 0;
1174 		info.ext.amx_bf16	= (regs.edx & BIT!(22)) != 0;
1175 		info.ext.amx	= (regs.edx & BIT!(24)) != 0;
1176 		info.ext.amx_int8	= (regs.edx & BIT!(25)) != 0;
1177 		info.sec.ibrs = (regs.edx & BIT!(26)) != 0;
1178 		info.sec.stibp	= (regs.edx & BIT!(27)) != 0;
1179 		info.sec.l1d_flush	= (regs.edx & BIT!(28)) != 0;
1180 		info.sec.ia32_arch_capabilities	= (regs.edx & BIT!(29)) != 0;
1181 		info.sec.ssbd	= regs.edx >= BIT!(31);
1182 		break;
1183 	default:
1184 	}
1185 
1186 	// ebx
1187 	info.misc.fsgsbase	= (regs.ebx & BIT!(0)) != 0;
1188 	info.ext.bmi1	= (regs.ebx & BIT!(3)) != 0;
1189 	info.ext.avx2	= (regs.ebx & BIT!(5)) != 0;
1190 	info.mem.smep	= (regs.ebx & BIT!(7)) != 0;
1191 	info.ext.bmi2	= (regs.ebx & BIT!(8)) != 0;
1192 	info.extras.rdseed	= (regs.ebx & BIT!(18)) != 0;
1193 	info.ext.adx	= (regs.ebx & BIT!(19)) != 0;
1194 	info.cache.clflushopt	= (regs.ebx & BIT!(23)) != 0;
1195 	info.ext.sha	= (regs.ebx & BIT!(29)) != 0;
1196 	// ecx
1197 	info.extras.rdpid	= (regs.ecx & BIT!(22)) != 0;
1198 	
1199 	//
1200 	// Leaf 7H(ECX=01h)
1201 	//
1202 	
1203 	switch (info.vendor_id) {
1204 	case VENDOR_INTEL:
1205 		asmcpuid(regs, 7, 1);
1206 		// a
1207 		info.ext.avx512_bf16	= (regs.eax & BIT!(5)) != 0;
1208 		info.mem.lam	= (regs.eax & BIT!(26)) != 0;
1209 		break;
1210 	default:
1211 	}
1212 	
1213 	if (info.max_leaf < 0xD) goto L_VIRT;
1214 	
1215 	//
1216 	// Leaf DH
1217 	//
1218 	
1219 	switch (info.vendor_id) {
1220 	case VENDOR_INTEL:
1221 		asmcpuid(regs, 0xd);
1222 		info.ext.amx_xtilecfg	= (regs.eax & BIT!(17)) != 0;
1223 		info.ext.amx_xtiledata	= (regs.eax & BIT!(18)) != 0;
1224 		break;
1225 	default:
1226 	}
1227 	
1228 	//
1229 	// Leaf DH(ECX=01h)
1230 	//
1231 
1232 	switch (info.vendor_id) {
1233 	case VENDOR_INTEL:
1234 		asmcpuid(regs, 0xd, 1);
1235 		info.ext.amx_xfd	= (regs.eax & BIT!(18)) != 0;
1236 		break;
1237 	default:
1238 	}
1239 	
1240 	if (info.max_virt_leaf < 0x4000_0000) goto L_VIRT;
1241 	
1242 	//
1243 	// Leaf 4000_000H
1244 	//
1245 	
1246 L_VIRT:
1247 	getVirtVendor(info);
1248 
1249 	if (info.max_virt_leaf < 0x4000_0001) goto L_EXTENDED;
1250 	
1251 	//
1252 	// Leaf 4000_0001H
1253 	//
1254 	
1255 	switch (info.virt.vendor_id) {
1256 	case VIRT_VENDOR_KVM:
1257 		asmcpuid(regs, 0x4000_0001);
1258 		info.virt.kvm_feature_clocksource	= (regs.eax & BIT!(0)) != 0;
1259 		info.virt.kvm_feature_nop_io_delay	= (regs.eax & BIT!(1)) != 0;
1260 		info.virt.kvm_feature_mmu_op	= (regs.eax & BIT!(2)) != 0;
1261 		info.virt.kvm_feature_clocksource2	= (regs.eax & BIT!(3)) != 0;
1262 		info.virt.kvm_feature_async_pf	= (regs.eax & BIT!(4)) != 0;
1263 		info.virt.kvm_feature_steal_time	= (regs.eax & BIT!(5)) != 0;
1264 		info.virt.kvm_feature_pv_eoi	= (regs.eax & BIT!(6)) != 0;
1265 		info.virt.kvm_feature_pv_unhault	= (regs.eax & BIT!(7)) != 0;
1266 		info.virt.kvm_feature_pv_tlb_flush	= (regs.eax & BIT!(9)) != 0;
1267 		info.virt.kvm_feature_async_pf_vmexit	= (regs.eax & BIT!(10)) != 0;
1268 		info.virt.kvm_feature_pv_send_ipi	= (regs.eax & BIT!(11)) != 0;
1269 		info.virt.kvm_feature_pv_poll_control	= (regs.eax & BIT!(12)) != 0;
1270 		info.virt.kvm_feature_pv_sched_yield	= (regs.eax & BIT!(13)) != 0;
1271 		info.virt.kvm_feature_clocsource_stable_bit	= (regs.eax & BIT!(24)) != 0;
1272 		info.virt.kvm_hint_realtime	= (regs.edx & BIT!(0)) != 0;
1273 		break;
1274 	default:
1275 	}
1276 
1277 	if (info.max_virt_leaf < 0x4000_0002) goto L_EXTENDED;
1278 	
1279 	//
1280 	// Leaf 4000_002H
1281 	//
1282 
1283 	switch (info.virt.vendor_id) {
1284 	case VIRT_VENDOR_VBOX_HV:
1285 		asmcpuid(regs, 0x4000_0002);
1286 		info.virt.hv_guest_minor	= cast(ubyte)(regs.eax >> 24);
1287 		info.virt.hv_guest_service	= cast(ubyte)(regs.eax >> 16);
1288 		info.virt.hv_guest_build	= cast(ushort)regs.eax;
1289 		info.virt.hv_guest_opensource	= regs.edx >= BIT!(31);
1290 		info.virt.hv_guest_vendor_id	= (regs.edx >> 16) & 0xFFF;
1291 		info.virt.hv_guest_os	= cast(ubyte)(regs.edx >> 8);
1292 		info.virt.hv_guest_major	= cast(ubyte)regs.edx;
1293 		break;
1294 	default:
1295 	}
1296 
1297 	if (info.max_virt_leaf < 0x4000_0003) goto L_EXTENDED;
1298 	
1299 	//
1300 	// Leaf 4000_0003H
1301 	//
1302 	
1303 	switch (info.virt.vendor_id) {
1304 	case VIRT_VENDOR_VBOX_HV:
1305 		asmcpuid(regs, 0x4000_0003);
1306 		info.virt.hv_base_feat_vp_runtime_msr	= (regs.eax & BIT!(0)) != 0;
1307 		info.virt.hv_base_feat_part_time_ref_count_msr	= (regs.eax & BIT!(1)) != 0;
1308 		info.virt.hv_base_feat_basic_synic_msrs	= (regs.eax & BIT!(2)) != 0;
1309 		info.virt.hv_base_feat_stimer_msrs	= (regs.eax & BIT!(3)) != 0;
1310 		info.virt.hv_base_feat_apic_access_msrs	= (regs.eax & BIT!(4)) != 0;
1311 		info.virt.hv_base_feat_hypercall_msrs	= (regs.eax & BIT!(5)) != 0;
1312 		info.virt.hv_base_feat_vp_id_msr	= (regs.eax & BIT!(6)) != 0;
1313 		info.virt.hv_base_feat_virt_sys_reset_msr	= (regs.eax & BIT!(7)) != 0;
1314 		info.virt.hv_base_feat_stat_pages_msr	= (regs.eax & BIT!(8)) != 0;
1315 		info.virt.hv_base_feat_part_ref_tsc_msr	= (regs.eax & BIT!(9)) != 0;
1316 		info.virt.hv_base_feat_guest_idle_state_msr	= (regs.eax & BIT!(10)) != 0;
1317 		info.virt.hv_base_feat_timer_freq_msrs	= (regs.eax & BIT!(11)) != 0;
1318 		info.virt.hv_base_feat_debug_msrs	= (regs.eax & BIT!(12)) != 0;
1319 		info.virt.hv_part_flags_create_part	= (regs.ebx & BIT!(0)) != 0;
1320 		info.virt.hv_part_flags_access_part_id	= (regs.ebx & BIT!(1)) != 0;
1321 		info.virt.hv_part_flags_access_memory_pool	= (regs.ebx & BIT!(2)) != 0;
1322 		info.virt.hv_part_flags_adjust_msg_buffers	= (regs.ebx & BIT!(3)) != 0;
1323 		info.virt.hv_part_flags_post_msgs	= (regs.ebx & BIT!(4)) != 0;
1324 		info.virt.hv_part_flags_signal_events	= (regs.ebx & BIT!(5)) != 0;
1325 		info.virt.hv_part_flags_create_port	= (regs.ebx & BIT!(6)) != 0;
1326 		info.virt.hv_part_flags_connect_port	= (regs.ebx & BIT!(7)) != 0;
1327 		info.virt.hv_part_flags_access_stats	= (regs.ebx & BIT!(8)) != 0;
1328 		info.virt.hv_part_flags_debugging	= (regs.ebx & BIT!(11)) != 0;
1329 		info.virt.hv_part_flags_cpu_mgmt	= (regs.ebx & BIT!(12)) != 0;
1330 		info.virt.hv_part_flags_cpu_profiler	= (regs.ebx & BIT!(13)) != 0;
1331 		info.virt.hv_part_flags_expanded_stack_walk	= (regs.ebx & BIT!(14)) != 0;
1332 		info.virt.hv_part_flags_access_vsm	= (regs.ebx & BIT!(16)) != 0;
1333 		info.virt.hv_part_flags_access_vp_regs	= (regs.ebx & BIT!(17)) != 0;
1334 		info.virt.hv_part_flags_extended_hypercalls	= (regs.ebx & BIT!(20)) != 0;
1335 		info.virt.hv_part_flags_start_vp	= (regs.ebx & BIT!(21)) != 0;
1336 		info.virt.hv_pm_max_cpu_power_state_c0	= (regs.ecx & BIT!(0)) != 0;
1337 		info.virt.hv_pm_max_cpu_power_state_c1	= (regs.ecx & BIT!(1)) != 0;
1338 		info.virt.hv_pm_max_cpu_power_state_c2	= (regs.ecx & BIT!(2)) != 0;
1339 		info.virt.hv_pm_max_cpu_power_state_c3	= (regs.ecx & BIT!(3)) != 0;
1340 		info.virt.hv_pm_hpet_reqd_for_c3	= (regs.ecx & BIT!(4)) != 0;
1341 		info.virt.hv_misc_feat_mwait	= (regs.eax & BIT!(0)) != 0;
1342 		info.virt.hv_misc_feat_guest_debugging	= (regs.eax & BIT!(1)) != 0;
1343 		info.virt.hv_misc_feat_perf_mon	= (regs.eax & BIT!(2)) != 0;
1344 		info.virt.hv_misc_feat_pcpu_dyn_part_event	= (regs.eax & BIT!(3)) != 0;
1345 		info.virt.hv_misc_feat_xmm_hypercall_input	= (regs.eax & BIT!(4)) != 0;
1346 		info.virt.hv_misc_feat_guest_idle_state	= (regs.eax & BIT!(5)) != 0;
1347 		info.virt.hv_misc_feat_hypervisor_sleep_state	= (regs.eax & BIT!(6)) != 0;
1348 		info.virt.hv_misc_feat_query_numa_distance	= (regs.eax & BIT!(7)) != 0;
1349 		info.virt.hv_misc_feat_timer_freq	= (regs.eax & BIT!(8)) != 0;
1350 		info.virt.hv_misc_feat_inject_synmc_xcpt	= (regs.eax & BIT!(9)) != 0;
1351 		info.virt.hv_misc_feat_guest_crash_msrs	= (regs.eax & BIT!(10)) != 0;
1352 		info.virt.hv_misc_feat_debug_msrs	= (regs.eax & BIT!(11)) != 0;
1353 		info.virt.hv_misc_feat_npiep1	= (regs.eax & BIT!(12)) != 0;
1354 		info.virt.hv_misc_feat_disable_hypervisor	= (regs.eax & BIT!(13)) != 0;
1355 		info.virt.hv_misc_feat_ext_gva_range_for_flush_va_list	= (regs.eax & BIT!(14)) != 0;
1356 		info.virt.hv_misc_feat_hypercall_output_xmm	= (regs.eax & BIT!(15)) != 0;
1357 		info.virt.hv_misc_feat_sint_polling_mode	= (regs.eax & BIT!(17)) != 0;
1358 		info.virt.hv_misc_feat_hypercall_msr_lock	= (regs.eax & BIT!(18)) != 0;
1359 		info.virt.hv_misc_feat_use_direct_synth_msrs	= (regs.eax & BIT!(19)) != 0;
1360 		break;
1361 	default:
1362 	}
1363 
1364 	if (info.max_virt_leaf < 0x4000_0004) goto L_EXTENDED;
1365 	
1366 	//
1367 	// Leaf 4000_0004H
1368 	//
1369 	
1370 	switch (info.virt.vendor_id) {
1371 	case VIRT_VENDOR_VBOX_HV:
1372 		asmcpuid(regs, 0x4000_0004);
1373 		info.virt.hv_hint_hypercall_for_process_switch	= (regs.eax & BIT!(0)) != 0;
1374 		info.virt.hv_hint_hypercall_for_tlb_flush	= (regs.eax & BIT!(1)) != 0;
1375 		info.virt.hv_hint_hypercall_for_tlb_shootdown	= (regs.eax & BIT!(2)) != 0;
1376 		info.virt.hv_hint_msr_for_apic_access	= (regs.eax & BIT!(3)) != 0;
1377 		info.virt.hv_hint_msr_for_sys_reset	= (regs.eax & BIT!(4)) != 0;
1378 		info.virt.hv_hint_relax_time_checks	= (regs.eax & BIT!(5)) != 0;
1379 		info.virt.hv_hint_dma_remapping	= (regs.eax & BIT!(6)) != 0;
1380 		info.virt.hv_hint_interrupt_remapping	= (regs.eax & BIT!(7)) != 0;
1381 		info.virt.hv_hint_x2apic_msrs	= (regs.eax & BIT!(8)) != 0;
1382 		info.virt.hv_hint_deprecate_auto_eoi	= (regs.eax & BIT!(9)) != 0;
1383 		info.virt.hv_hint_synth_cluster_ipi_hypercall	= (regs.eax & BIT!(10)) != 0;
1384 		info.virt.hv_hint_ex_proc_masks_interface	= (regs.eax & BIT!(11)) != 0;
1385 		info.virt.hv_hint_nested_hyperv	= (regs.eax & BIT!(12)) != 0;
1386 		info.virt.hv_hint_int_for_mbec_syscalls	= (regs.eax & BIT!(13)) != 0;
1387 		info.virt.hv_hint_nested_enlightened_vmcs_interface	= (regs.eax & BIT!(14)) != 0;
1388 		break;
1389 	default:
1390 	}
1391 
1392 	if (info.max_virt_leaf < 0x4000_0006) goto L_EXTENDED;
1393 	
1394 	//
1395 	// Leaf 4000_0006H
1396 	//
1397 	
1398 	switch (info.virt.vendor_id) {
1399 	case VIRT_VENDOR_VBOX_HV:
1400 		asmcpuid(regs, 0x4000_0006);
1401 		info.virt.hv_host_feat_avic	= (regs.eax & BIT!(0)) != 0;
1402 		info.virt.hv_host_feat_msr_bitmap	= (regs.eax & BIT!(1)) != 0;
1403 		info.virt.hv_host_feat_perf_counter	= (regs.eax & BIT!(2)) != 0;
1404 		info.virt.hv_host_feat_nested_paging	= (regs.eax & BIT!(3)) != 0;
1405 		info.virt.hv_host_feat_dma_remapping	= (regs.eax & BIT!(4)) != 0;
1406 		info.virt.hv_host_feat_interrupt_remapping	= (regs.eax & BIT!(5)) != 0;
1407 		info.virt.hv_host_feat_mem_patrol_scrubber	= (regs.eax & BIT!(6)) != 0;
1408 		info.virt.hv_host_feat_dma_prot_in_use	= (regs.eax & BIT!(7)) != 0;
1409 		info.virt.hv_host_feat_hpet_requested	= (regs.eax & BIT!(8)) != 0;
1410 		info.virt.hv_host_feat_stimer_volatile	= (regs.eax & BIT!(9)) != 0;
1411 		break;
1412 	default:
1413 	}
1414 
1415 	if (info.max_virt_leaf < 0x4000_0010) goto L_EXTENDED;
1416 	
1417 	//
1418 	// Leaf 4000_0010H
1419 	//
1420 	
1421 	switch (info.virt.vendor_id) {
1422 	case VIRT_VENDOR_VBOX_MIN: // VBox Minimal
1423 		asmcpuid(regs, 0x4000_0010);
1424 		info.virt.vbox_tsc_freq_khz = regs.eax;
1425 		info.virt.vbox_apic_freq_khz = regs.ebx;
1426 		break;
1427 	default:
1428 	}
1429 
1430 	//
1431 	// Leaf 8000_0001H
1432 	//
1433 	
1434 L_EXTENDED:
1435 	asmcpuid(regs, 0x8000_0001);
1436 	
1437 	switch (info.vendor_id) {
1438 	case VENDOR_AMD:
1439 		// ecx
1440 		info.virt.available	= (regs.ecx & BIT!(2)) != 0;
1441 		info.acpi.x2apic	= (regs.ecx & BIT!(3)) != 0;
1442 		info.ext.sse4a	= (regs.ecx & BIT!(6)) != 0;
1443 		info.ext.xop	= (regs.ecx & BIT!(11)) != 0;
1444 		info.extras.skinit	= (regs.ecx & BIT!(12)) != 0;
1445 		info.ext.fma4	= (regs.ecx & BIT!(16)) != 0;
1446 		info.ext.tbm	= (regs.ecx & BIT!(21)) != 0;
1447 		// edx
1448 		info.ext.mmxext	= (regs.edx & BIT!(22)) != 0;
1449 		info.ext._3dnowext	= (regs.edx & BIT!(30)) != 0;
1450 		info.ext._3dnow	= regs.edx >= BIT!(31);
1451 		break;
1452 	default:
1453 	}
1454 	
1455 	// ecx
1456 	info.ext.lahf64	= (regs.ecx & BIT!(0)) != 0;
1457 	info.extras.lzcnt	= (regs.ecx & BIT!(5)) != 0;
1458 	info.cache.prefetchw	= (regs.ecx & BIT!(8)) != 0;
1459 	info.extras.monitorx	= (regs.ecx & BIT!(29)) != 0;
1460 	// edx
1461 	info.extras.syscall	= (regs.edx & BIT!(11)) != 0;
1462 	info.mem.nx	= (regs.edx & BIT!(20)) != 0;
1463 	info.mem.page1gb	= (regs.edx & BIT!(26)) != 0;
1464 	info.extras.rdtscp	= (regs.edx & BIT!(27)) != 0;
1465 	info.ext.x86_64	= (regs.edx & BIT!(29)) != 0;
1466 	
1467 	if (info.max_ext_leaf < 0x8000_0007) goto L_CACHE_INFO;
1468 	
1469 	//
1470 	// Leaf 8000_0007H
1471 	//
1472 	
1473 	asmcpuid(regs, 0x8000_0007);
1474 	
1475 	switch (info.vendor_id) {
1476 	case VENDOR_INTEL:
1477 		info.extras.rdseed	= (regs.ebx & BIT!(28)) != 0;
1478 		break;
1479 	case VENDOR_AMD:
1480 		info.acpi.tm	= (regs.edx & BIT!(4)) != 0;
1481 		info.tech.turboboost	= (regs.edx & BIT!(9)) != 0;
1482 		break;
1483 	default:
1484 	}
1485 	
1486 	info.extras.rdtsc_invariant	= (regs.edx & BIT!(8)) != 0;
1487 	
1488 	if (info.max_ext_leaf < 0x8000_0008) goto L_CACHE_INFO;
1489 	
1490 	//
1491 	// Leaf 8000_0008H
1492 	//
1493 	
1494 	asmcpuid(regs, 0x8000_0008);
1495 	
1496 	switch (info.vendor_id) {
1497 	case VENDOR_INTEL:
1498 		info.cache.wbnoinvd	= (regs.ebx & BIT!(9)) != 0;
1499 		break;
1500 	case VENDOR_AMD:
1501 		info.sec.ibpb	= (regs.ebx & BIT!(12)) != 0;
1502 		info.sec.ibrs	= (regs.ebx & BIT!(14)) != 0;
1503 		info.sec.stibp	= (regs.ebx & BIT!(15)) != 0;
1504 		info.sec.ibrs_on	= (regs.ebx & BIT!(16)) != 0;
1505 		info.sec.stibp_on	= (regs.ebx & BIT!(17)) != 0;
1506 		info.sec.ibrs_pref	= (regs.ebx & BIT!(18)) != 0;
1507 		info.sec.ssbd	= (regs.ebx & BIT!(24)) != 0;
1508 		info.cores.logical	= (cast(ubyte)regs.ecx) + 1;
1509 		break;
1510 	default:
1511 	}
1512 
1513 	info.mem.b_8000_0008_ax = cast(ushort)regs.eax; // info.addr_phys_bits, info.addr_line_bits
1514 
1515 	if (info.max_ext_leaf < 0x8000_000A) goto L_CACHE_INFO;
1516 	
1517 	//
1518 	// Leaf 8000_000AH
1519 	//
1520 	
1521 	asmcpuid(regs, 0x8000_000A);
1522 	
1523 	switch (info.vendor_id) {
1524 	case VENDOR_AMD:
1525 		info.virt.version_	= cast(ubyte)regs.eax; // EAX[7:0]
1526 		info.virt.apivc	= (regs.edx & BIT!(13)) != 0;
1527 		break;
1528 	default:
1529 	}
1530 
1531 	//if (info.max_ext_leaf < ...) goto L_CACHE_INFO;
1532 	
1533 L_CACHE_INFO:
1534 	// Cache information
1535 	// - done at the very end since we may need prior information
1536 	//   - e.g. amd cpuid.8000_0008h
1537 	// - maxleaf < 4 is too old/rare these days (es. for D programs)
1538 	
1539 	//TODO: Maybe do physical cores
1540 	
1541 	info.cache.levels = 0;
1542 	CACHEINFO *ca = cast(CACHEINFO*)info.cache.level;
1543 	
1544 	ushort sc = void;	/// raw cores shared across cache level
1545 	ushort crshrd = void;	/// actual count of shared cores
1546 	ubyte type = void;
1547 	switch (info.vendor_id) {
1548 	case VENDOR_INTEL:
1549 		asmcpuid(regs, 4, info.cache.levels);
1550 		
1551 		type = regs.eax & CACHE_MASK; // EAX[4:0]
1552 		if (type == 0) break;
1553 		if (info.cache.levels >= DDCPUID_CACHE_MAX) break;
1554 		
1555 		ca.type = CACHE_TYPE[type];
1556 		ca.level = cast(ubyte)((regs.eax >> 5) & 7);
1557 		ca.linesize = cast(ubyte)((regs.ebx & 0x7FF) + 1);
1558 		ca.partitions = cast(ubyte)(((regs.ebx >> 12) & 0x7FF) + 1);
1559 		ca.ways = cast(ubyte)((regs.ebx >> 22) + 1);
1560 		ca.sets = cast(ushort)(regs.ecx + 1);
1561 		if (regs.eax & BIT!(8)) ca.feat = 1;
1562 		if (regs.eax & BIT!(9)) ca.feat |= BIT!(1);
1563 		if (regs.edx & BIT!(0)) ca.feat |= BIT!(2);
1564 		if (regs.edx & BIT!(1)) ca.feat |= BIT!(3);
1565 		if (regs.edx & BIT!(2)) ca.feat |= BIT!(4);
1566 		ca.size = (ca.sets * ca.linesize * ca.partitions * ca.ways) >> 10;
1567 		
1568 		info.cores.logical = (regs.eax >> 26) + 1;	// EAX[31:26]
1569 		crshrd = (((regs.eax >> 14) & 2047) + 1);	// EAX[25:14]
1570 		sc = cast(ushort)(info.cores.logical / crshrd); // cast for ldc 0.17.1
1571 		ca.sharedCores = sc ? sc : 1;
1572 		
1573 		++info.cache.levels; ++ca;
1574 		goto case VENDOR_INTEL;
1575 	case VENDOR_AMD:
1576 		if (info.max_ext_leaf < 0x8000_001D)
1577 			goto L_CACHE_AMD_LEGACY;
1578 		
1579 		//
1580 		// AMD newer cache method
1581 		//
1582 		
1583 L_CACHE_AMD_EXT_1DH: // Almost the same as Intel's
1584 		asmcpuid(regs, 0x8000_001D, info.cache.levels);
1585 		
1586 		type = regs.eax & CACHE_MASK; // EAX[4:0]
1587 		if (type == 0) break;
1588 		if (info.cache.levels >= DDCPUID_CACHE_MAX) break;
1589 		
1590 		ca.type = CACHE_TYPE[type];
1591 		ca.level = cast(ubyte)((regs.eax >> 5) & 7);
1592 		ca.linesize = cast(ubyte)((regs.ebx & 0x7FF) + 1);
1593 		ca.partitions = cast(ubyte)(((regs.ebx >> 12) & 0x7FF) + 1);
1594 		ca.ways = cast(ubyte)((regs.ebx >> 22) + 1);
1595 		ca.sets = cast(ushort)(regs.ecx + 1);
1596 		if (regs.eax & BIT!(8)) ca.feat = 1;
1597 		if (regs.eax & BIT!(9)) ca.feat |= BIT!(1);
1598 		if (regs.edx & BIT!(0)) ca.feat |= BIT!(2);
1599 		if (regs.edx & BIT!(1)) ca.feat |= BIT!(3);
1600 		ca.size = (ca.sets * ca.linesize * ca.partitions * ca.ways) >> 10;
1601 		
1602 		crshrd = (((regs.eax >> 14) & 2047) + 1);	// EAX[25:14]
1603 		sc = cast(ushort)(info.cores.logical / crshrd); // cast for ldc 0.17.1
1604 		ca.sharedCores = sc ? sc : 1;
1605 		
1606 		++info.cache.levels; ++ca;
1607 		goto L_CACHE_AMD_EXT_1DH;
1608 		
1609 		//
1610 		// AMD legacy cache
1611 		//
1612 		
1613 L_CACHE_AMD_LEGACY:
1614 		asmcpuid(regs, 0x8000_0005);
1615 		
1616 		info.cache.level[0].level = 1; // L1
1617 		info.cache.level[0].type = 'D'; // data
1618 		info.cache.level[0].__bundle1 = regs.ecx;
1619 		info.cache.level[0].size = info.cache.level[0]._amdsize;
1620 		info.cache.level[1].level = 1; // L1
1621 		info.cache.level[1].type = 'I'; // instructions
1622 		info.cache.level[1].__bundle1 = regs.edx;
1623 		info.cache.level[1].size = info.cache.level[1]._amdsize;
1624 		
1625 		info.cache.levels = 2;
1626 		
1627 		if (info.max_ext_leaf < 0x8000_0006)
1628 			break; // No L2/L3
1629 		
1630 		// See Table E-4. L2/L3 Cache and TLB Associativity Field Encoding
1631 		static immutable ubyte[16] _amd_cache_ways = [
1632 			// 7h is reserved
1633 			// 9h mentions 8000_001D but that's already supported
1634 			0, 1, 2, 3, 4, 6, 8, 0, 16, 0, 32, 48, 64, 96, 128, 255
1635 		];
1636 		
1637 		asmcpuid(regs, 0x8000_0006);
1638 		
1639 		ubyte _amd_ways_l2 = (regs.ecx >> 12) & 15;
1640 		if (_amd_ways_l2) {
1641 			info.cache.level[2].level = 2; // L2
1642 			info.cache.level[2].type = 'U'; // unified
1643 			info.cache.level[2].ways = _amd_cache_ways[_amd_ways_l2];
1644 			info.cache.level[2].size = regs.ecx >> 16;
1645 			info.cache.level[2].sets = (regs.ecx >> 8) & 7;
1646 			info.cache.level[2].linesize = cast(ubyte)regs.ecx;
1647 			
1648 			info.cache.levels = 3;
1649 			
1650 			ubyte _amd_ways_l3 = (regs.edx >> 12) & 15;
1651 			if (_amd_ways_l3) {
1652 				info.cache.level[3].level = 3;  // L3
1653 				info.cache.level[3].type = 'U'; // unified
1654 				info.cache.level[3].ways = _amd_cache_ways[_amd_ways_l3];
1655 				info.cache.level[3].size = ((regs.edx >> 18) + 1) * 512;
1656 				info.cache.level[3].sets = (regs.edx >> 8) & 7;
1657 				info.cache.level[3].linesize = cast(ubyte)(regs.edx & 0x7F);
1658 				
1659 				info.cache.levels = 4;
1660 			}
1661 		}
1662 		break;
1663 	default:
1664 	}
1665 }