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