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