├── .gitignore ├── .ocamlinit ├── .ocp-indent ├── .travis.yml ├── CHANGES.md ├── LICENSE.md ├── README.md ├── TODO.md ├── cpuid.opam ├── dune-project ├── src ├── cpuid.ml ├── cpuid.mli ├── dune └── native │ └── cpuid.c └── test ├── dune └── test.ml /.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | tmp 3 | *~ 4 | \.\#* 5 | \#*# 6 | *.install 7 | *.native 8 | *.byte 9 | .merlin 10 | -------------------------------------------------------------------------------- /.ocamlinit: -------------------------------------------------------------------------------- 1 | #directory "_build/src" 2 | #load "cpuid.cma" 3 | -------------------------------------------------------------------------------- /.ocp-indent: -------------------------------------------------------------------------------- 1 | strict_with=always,match_clause=4,strict_else=never 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | install: wget https://raw.githubusercontent.com/ocaml/ocaml-ci-scripts/master/.travis-opam.sh 3 | script: bash -ex .travis-opam.sh 4 | sudo: required 5 | env: 6 | global: 7 | - PACKAGE="cpuid" 8 | matrix: 9 | - OCAML_VERSION=4.03 10 | - OCAML_VERSION=4.04 11 | - OCAML_VERSION=4.05 12 | - OCAML_VERSION=4.06 13 | - OCAML_VERSION=4.07 14 | notifications: 15 | email: false 16 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | ## v0.1.2 2019-04-02 2 | 3 | * Switch the build to dune. 4 | 5 | ## v0.1.1 2017-04-01 6 | 7 | * Don't include x86 intrinsics header as the probing actually uses `cpuid.h`. 8 | 9 | ## v0.1.0 2016-11-04 10 | 11 | * `model` (with family, model, and stepping) 12 | * `cores` 13 | 14 | ## v0.0.1 2016-10-12 15 | 16 | First release. 17 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 David Kaloper Meršinjak 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cpuid — Detect CPU features 2 | 3 | %%VERSION%% 4 | 5 | Cpuid allows detection of CPU features from OCaml. 6 | 7 | Cpuid is distributed under the ISC license. 8 | 9 | ## Installation 10 | 11 | cpuid can be installed with `opam`: 12 | 13 | opam install cpuid 14 | 15 | If you don't use `opam` consult the [`opam`](cpuid.opam) file for build 16 | instructions. 17 | 18 | ## Documentation 19 | 20 | The documentation and API reference is automatically generated by 21 | `odoc` from the interfaces. It can be consulted [online][doc]. 22 | 23 | [doc]: https://pqwy.github.io/cpuid/doc/cpuid/ 24 | 25 | [![Build Status](https://travis-ci.org/pqwy/cpuid.svg?branch=master)](https://travis-ci.org/pqwy/cpuid) 26 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | * Provide per-CPU discovery? 2 | * Widen discovery to other CPUID-leaves (power management, &c)? 3 | * Generalize beyond CPUID (ARM)? 4 | -------------------------------------------------------------------------------- /cpuid.opam: -------------------------------------------------------------------------------- 1 | opam-version: "2.0" 2 | maintainer: "David Kaloper Meršinjak " 3 | authors: ["David Kaloper Meršinjak "] 4 | homepage: "https://github.com/pqwy/cpuid" 5 | doc: "https://pqwy.github.io/cpuid/doc" 6 | license: "ISC" 7 | dev-repo: "git+https://github.com/pqwy/cpuid.git" 8 | bug-reports: "https://github.com/pqwy/cpuid/issues" 9 | build: [ ["dune" "subst"] {pinned} 10 | ["dune" "build" "-p" name "-j" jobs ] 11 | ["dune" "runtest"] {with-test} ] 12 | depends: [ 13 | "ocaml" {>="4.03.0"} 14 | "dune" {build} 15 | ] 16 | synopsis: "Detect CPU features" 17 | description: "CPUID" 18 | -------------------------------------------------------------------------------- /dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.7) 2 | (name cpuid) 3 | (version %%VERSION_NUM%%) 4 | -------------------------------------------------------------------------------- /src/cpuid.ml: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2016 David Kaloper Meršinjak. All rights reserved. 2 | See LICENSE.md. *) 3 | 4 | type raw = { 5 | sign : int32; 6 | l1_eax : int32; 7 | l1_ecx : int32; 8 | l1_edx : int32; 9 | l7_ebx : int32; 10 | l7_ecx : int32; 11 | l12_ebx : int32; 12 | l1e_ecx : int32; 13 | l1e_edx : int32; 14 | } 15 | 16 | external cpudetect_raw : unit -> raw option = "caml_cpuid_cpudetect" 17 | 18 | type error = [ `Unsupported ] 19 | 20 | type nonrec 'a result = ('a, error) result 21 | 22 | type vendor = [ 23 | (* CPU manufacturer *) 24 | `AMD 25 | | `Centaur 26 | | `Cyrix 27 | | `Intel 28 | | `Transmeta 29 | | `NSC 30 | | `NexGen 31 | | `Rise 32 | | `SiS 33 | | `UMC 34 | | `VIA 35 | | `Vortex 36 | (* Virtual environment *) 37 | | `KVM 38 | | `Hyper_V 39 | | `Parallels 40 | | `VMware 41 | | `Xen 42 | (* WAT *) 43 | | `UNKNOWN 44 | ] 45 | 46 | type flag = [ 47 | (* Level 0x00000001 (EDX) *) 48 | | `FPU (** Onboard FPU *) 49 | | `VME (** Virtual Mode Extensions *) 50 | | `DE (** Debugging Extensions *) 51 | | `PSE (** Page Size Extensions *) 52 | | `TSC (** Time Stamp Counter *) 53 | | `MSR (** Model-Specific Registers *) 54 | | `PAE (** Physical Address Extensions *) 55 | | `MCE (** Machine Check Exception *) 56 | | `CX8 (** CMPXCHG8 instruction *) 57 | | `APIC (** Onboard APIC *) 58 | | `SEP (** SYSENTER/SYSEXIT *) 59 | | `MTRR (** Memory Type Range Registers *) 60 | | `PGE (** Page Global Enable *) 61 | | `MCA (** Machine Check Architecture *) 62 | | `CMOV (** CMOV instructions *) 63 | | `PAT (** Page Attribute Table *) 64 | | `PSE36 (** 36-bit PSEs *) 65 | | `PN (** Processor serial number *) 66 | | `CLFLUSH (** CLFLUSH instruction *) 67 | | `DTS (** Debug Store *) 68 | | `ACPI (** ACPI via MSR *) 69 | | `MMX (** Multimedia Extensions *) 70 | | `FXSR (** FXSAVE/FXRSTOR, CR4.OSFXSR *) 71 | | `SSE (** SSE *) 72 | | `SSE2 (** SSE2 *) 73 | | `SS (** CPU self snoop *) 74 | | `HT (** Hyper-Threading *) 75 | | `TM (** Automatic clock control *) 76 | | `IA64 (** IA-64 processor *) 77 | | `PBE (** Pending Break Enable *) 78 | (* Level 0x80000001 (EDX) *) 79 | | `SYSCALL (** SYSCALL/SYSRET *) 80 | | `MP (** MP Capable. *) 81 | | `NX (** Execute Disable *) 82 | | `MMXEXT (** AMD MMX extensions *) 83 | | `FXSR_OPT (** FXSAVE/FXRSTOR optimizations *) 84 | | `PDPE1GB (** GB pages *) 85 | | `RDTSCP (** RDTSCP *) 86 | | `LM (** Long Mode (x86-64) *) 87 | | `F_3DNOWEXT (** AMD 3DNow! extensions *) 88 | | `F_3DNOW (** 3DNow! *) 89 | (* Level 0x00000001 (EDX) *) 90 | | `PNI (** SSE-3 *) 91 | | `PCLMULQDQ (** PCLMULQDQ instruction *) 92 | | `DTES64 (** 64-bit Debug Store *) 93 | | `MONITOR (** Monitor/Mwait support *) 94 | | `DS_CPL (** CPL Qual. Debug Store *) 95 | | `VMX (** Hardware virtualization *) 96 | | `SMX (** Safer mode *) 97 | | `EST (** Enhanced SpeedStep *) 98 | | `TM2 (** Thermal Monitor 2 *) 99 | | `SSSE3 (** Supplemental SSE-3 *) 100 | | `CID (** Context ID *) 101 | | `SDBG (** Silicon Debug *) 102 | | `FMA (** Fused multiply-add *) 103 | | `CX16 (** CMPXCHG16B *) 104 | | `XTPR (** Send Task Priority Messages *) 105 | | `PDCM (** Performance Capabilities *) 106 | | `PCID (** Process Context Identifiers *) 107 | | `DCA (** Direct Cache Access *) 108 | | `SSE4_1 (** SSE-4.1 *) 109 | | `SSE4_2 (** SSE-4.2 *) 110 | | `X2APIC (** x2APIC *) 111 | | `MOVBE (** MOVBE instruction *) 112 | | `POPCNT (** POPCNT instruction *) 113 | | `TSC_DEADLINE_TIMER (** Tsc deadline timer *) 114 | | `AES (** AES instructions *) 115 | | `XSAVE (** XSAVE/XRSTOR/XSETBV/XGETBV *) 116 | | `OSXSAVE (** XSAVE enabled in the OS *) 117 | | `AVX (** Advanced Vector Extensions *) 118 | | `F16C (** 16-bit fp conversions *) 119 | | `RDRAND (** The RDRAND instruction *) 120 | | `HYPERVISOR (** Running on a hypervisor *) 121 | (* Level 0x80000001 (ECX) *) 122 | | `LAHF_LM (** LAHF/SAHF in long mode *) 123 | | `CMP_LEGACY (** If yes HyperThreading not valid *) 124 | | `SVM (** Secure virtual machine *) 125 | | `EXTAPIC (** Extended APIC space *) 126 | | `CR8_LEGACY (** CR8 in 32-bit mode *) 127 | | `ABM (** Advanced bit manipulation *) 128 | | `SSE4A (** SSE-4A *) 129 | | `MISALIGNSSE (** Misaligned SSE mode *) 130 | | `F_3DNOWPREFETCH (** 3DNow prefetch instructions *) 131 | | `OSVW (** OS Visible Workaround *) 132 | | `IBS (** Instruction Based Sampling *) 133 | | `XOP (** extended AVX instructions *) 134 | | `SKINIT (** SKINIT/STGI instructions *) 135 | | `WDT (** Watchdog timer *) 136 | | `LWP (** Light Weight Profiling *) 137 | | `FMA4 (** 4 operands MAC instructions *) 138 | | `TCE (** translation cache extension *) 139 | | `NODEID_MSR (** NodeId MSR *) 140 | | `TBM (** trailing bit manipulations *) 141 | | `TOPOEXT (** topology extensions CPUID leafs *) 142 | | `PERFCTR_CORE (** core performance counter extensions *) 143 | | `PERFCTR_NB (** NB performance counter extensions *) 144 | | `BPEXT (** data breakpoint extension *) 145 | | `PTSC (** performance time-stamp counter *) 146 | | `PERFCTR_L2 (** L2 performance counter extensions *) 147 | | `MWAITX (** MWAIT extension ( MONITORX/MWAITX) *) 148 | (* Level 0x00000007 (EBX) *) 149 | | `FSGSBASE (** {RD/WR}{FS/GS}BASE instructions*) 150 | | `TSC_ADJUST (** TSC adjustment MSR 0x3b *) 151 | | `BMI1 (** 1st group bit manipulation extensions *) 152 | | `HLE (** Hardware Lock Elision *) 153 | | `AVX2 (** AVX2 instructions *) 154 | | `SMEP (** Supervisor Mode Execution Protection *) 155 | | `BMI2 (** 2nd group bit manipulation extensions *) 156 | | `ERMS (** Enhanced REP MOVSB/STOSB *) 157 | | `INVPCID (** Invalidate Processor Context ID *) 158 | | `RTM (** Restricted Transactional Memory *) 159 | | `CQM (** Cache QoS Monitoring *) 160 | | `MPX (** Memory Protection Extension *) 161 | | `AVX512F (** AVX-512 Foundation *) 162 | | `AVX512DQ (** AVX-512 DQ (Double/Quad granular) Instructions *) 163 | | `RDSEED (** The RDSEED instruction *) 164 | | `ADX (** The ADCX and ADOX instructions *) 165 | | `SMAP (** Supervisor Mode Access Prevention *) 166 | | `CLFLUSHOPT (** CLFLUSHOPT instruction *) 167 | | `CLWB (** CLWB instruction *) 168 | | `AVX512PF (** AVX-512 Prefetch *) 169 | | `AVX512ER (** AVX-512 Exponential and Reciprocal *) 170 | | `AVX512CD (** AVX-512 Conflict Detection *) 171 | | `SHA_NI (** SHA1/SHA256 Instruction Extensions *) 172 | | `AVX512BW (** AVX-512 BW (Byte/Word granular) Instructions *) 173 | | `AVX512VL (** AVX-512 VL (128/256 Vector Length) Extensions *) 174 | (* Level 0x00000007 (ECX) *) 175 | | `PKU (** Protection Keys for Userspace *) 176 | | `OSPKE (** OS Protection Keys Enable *) 177 | ] 178 | 179 | module FlagS = Set.Make (struct 180 | type t = flag 181 | let compare (a : flag) b = compare a b 182 | end) 183 | 184 | type id = { 185 | vendor : vendor; 186 | model : int * int * int; 187 | flags : FlagS.t; 188 | cores : int; 189 | } 190 | 191 | let pp_error fmt e = 192 | Format.pp_print_string fmt @@ 193 | match e with `Unsupported -> "Unsupported" 194 | 195 | let pp_vendor fmt v = 196 | Format.pp_print_string fmt @@ 197 | match v with 198 | | `AMD -> "AMD" 199 | | `Centaur -> "Centaur" 200 | | `Cyrix -> "Cyrix" 201 | | `Intel -> "Intel" 202 | | `Transmeta -> "Transmeta" 203 | | `NSC -> "NSC" 204 | | `NexGen -> "NexGen" 205 | | `Rise -> "Rise" 206 | | `SiS -> "SiS" 207 | | `UMC -> "UMC" 208 | | `VIA -> "VIA" 209 | | `Vortex -> "Vortex" 210 | | `KVM -> "KVM" 211 | | `Hyper_V -> "Hyper_V" 212 | | `Parallels -> "Parallels" 213 | | `VMware -> "VMware" 214 | | `Xen -> "Xen" 215 | | `UNKNOWN -> "UNKNOWN" 216 | 217 | let pp_flag fmt f = 218 | Format.pp_print_string fmt @@ 219 | match f with 220 | | `FPU -> "FPU" 221 | | `VME -> "VME" 222 | | `DE -> "DE" 223 | | `PSE -> "PSE" 224 | | `TSC -> "TSC" 225 | | `MSR -> "MSR" 226 | | `PAE -> "PAE" 227 | | `MCE -> "MCE" 228 | | `CX8 -> "CX8" 229 | | `APIC -> "APIC" 230 | | `SEP -> "SEP" 231 | | `MTRR -> "MTRR" 232 | | `PGE -> "PGE" 233 | | `MCA -> "MCA" 234 | | `CMOV -> "CMOV" 235 | | `PAT -> "PAT" 236 | | `PSE36 -> "PSE36" 237 | | `PN -> "PN" 238 | | `CLFLUSH -> "CLFLUSH" 239 | | `DTS -> "DTS" 240 | | `ACPI -> "ACPI" 241 | | `MMX -> "MMX" 242 | | `FXSR -> "FXSR" 243 | | `SSE -> "SSE" 244 | | `SSE2 -> "SSE2" 245 | | `SS -> "SS" 246 | | `HT -> "HT" 247 | | `TM -> "TM" 248 | | `IA64 -> "IA64" 249 | | `PBE -> "PBE" 250 | | `SYSCALL -> "SYSCALL" 251 | | `MP -> "MP" 252 | | `NX -> "NX" 253 | | `MMXEXT -> "MMXEXT" 254 | | `FXSR_OPT -> "FXSR_OPT" 255 | | `PDPE1GB -> "PDPE1GB" 256 | | `RDTSCP -> "RDTSCP" 257 | | `LM -> "LM" 258 | | `F_3DNOWEXT -> "3DNOWEXT" 259 | | `F_3DNOW -> "3DNOW" 260 | | `PNI -> "PNI" 261 | | `PCLMULQDQ -> "PCLMULQDQ" 262 | | `DTES64 -> "DTES64" 263 | | `MONITOR -> "MONITOR" 264 | | `DS_CPL -> "DS_CPL" 265 | | `VMX -> "VMX" 266 | | `SMX -> "SMX" 267 | | `EST -> "EST" 268 | | `TM2 -> "TM2" 269 | | `SSSE3 -> "SSSE3" 270 | | `CID -> "CID" 271 | | `SDBG -> "SDBG" 272 | | `FMA -> "FMA" 273 | | `CX16 -> "CX16" 274 | | `XTPR -> "XTPR" 275 | | `PDCM -> "PDCM" 276 | | `PCID -> "PCID" 277 | | `DCA -> "DCA" 278 | | `SSE4_1 -> "SSE4_1" 279 | | `SSE4_2 -> "SSE4_2" 280 | | `X2APIC -> "X2APIC" 281 | | `MOVBE -> "MOVBE" 282 | | `POPCNT -> "POPCNT" 283 | | `TSC_DEADLINE_TIMER -> "TSC_DEADLINE_TIMER" 284 | | `AES -> "AES" 285 | | `XSAVE -> "XSAVE" 286 | | `OSXSAVE -> "OSXSAVE" 287 | | `AVX -> "AVX" 288 | | `F16C -> "F16C" 289 | | `RDRAND -> "RDRAND" 290 | | `HYPERVISOR -> "HYPERVISOR" 291 | | `LAHF_LM -> "LAHF_LM" 292 | | `CMP_LEGACY -> "CMP_LEGACY" 293 | | `SVM -> "SVM" 294 | | `EXTAPIC -> "EXTAPIC" 295 | | `CR8_LEGACY -> "CR8_LEGACY" 296 | | `ABM -> "ABM" 297 | | `SSE4A -> "SSE4A" 298 | | `MISALIGNSSE -> "MISALIGNSSE" 299 | | `F_3DNOWPREFETCH -> "3DNOWPREFETCH" 300 | | `OSVW -> "OSVW" 301 | | `IBS -> "IBS" 302 | | `XOP -> "XOP" 303 | | `SKINIT -> "SKINIT" 304 | | `WDT -> "WDT" 305 | | `LWP -> "LWP" 306 | | `FMA4 -> "FMA4" 307 | | `TCE -> "TCE" 308 | | `NODEID_MSR -> "NODEID_MSR" 309 | | `TBM -> "TBM" 310 | | `TOPOEXT -> "TOPOEXT" 311 | | `PERFCTR_CORE -> "PERFCTR_CORE" 312 | | `PERFCTR_NB -> "PERFCTR_NB" 313 | | `BPEXT -> "BPEXT" 314 | | `PTSC -> "PTSC" 315 | | `PERFCTR_L2 -> "PERFCTR_L2" 316 | | `MWAITX -> "MWAITX" 317 | | `FSGSBASE -> "FSGSBASE" 318 | | `TSC_ADJUST -> "TSC_ADJUST" 319 | | `BMI1 -> "BMI1" 320 | | `HLE -> "HLE" 321 | | `AVX2 -> "AVX2" 322 | | `SMEP -> "SMEP" 323 | | `BMI2 -> "BMI2" 324 | | `ERMS -> "ERMS" 325 | | `INVPCID -> "INVPCID" 326 | | `RTM -> "RTM" 327 | | `CQM -> "CQM" 328 | | `MPX -> "MPX" 329 | | `AVX512F -> "AVX512F" 330 | | `AVX512DQ -> "AVX512DQ" 331 | | `RDSEED -> "RDSEED" 332 | | `ADX -> "ADX" 333 | | `SMAP -> "SMAP" 334 | | `CLFLUSHOPT -> "CLFLUSHOPT" 335 | | `CLWB -> "CLWB" 336 | | `AVX512PF -> "AVX512PF" 337 | | `AVX512ER -> "AVX512ER" 338 | | `AVX512CD -> "AVX512CD" 339 | | `SHA_NI -> "SHA_NI" 340 | | `AVX512BW -> "AVX512BW" 341 | | `AVX512VL -> "AVX512VL" 342 | | `PKU -> "PKU" 343 | | `OSPKE -> "OSPKE" 344 | 345 | let vendor_of_sig_ebx = function 346 | | 0x68747541l -> `AMD 347 | | 0x746e6543l -> `Centaur 348 | | 0x69727943l -> `Cyrix 349 | | 0x756e6547l -> `Intel 350 | | 0x6e617254l -> `Transmeta 351 | (* | 0x756e6547 -> `Transmeta2 *) 352 | | 0x646f6547l -> `NSC 353 | | 0x4778654el -> `NexGen 354 | | 0x65736952l -> `Rise 355 | | 0x20536953l -> `SiS 356 | | 0x20434d55l -> `UMC 357 | | 0x20414956l -> `VIA 358 | | 0x74726f56l -> `Vortex 359 | | 0x4b4d564bl -> `KVM 360 | | 0x7263694dl -> `Hyper_V 361 | | 0x70726c20l -> `Parallels 362 | | 0x61774d56l -> `VMware 363 | | 0x566e6558l -> `Xen 364 | | _ -> `UNKNOWN 365 | 366 | let l1_edx_f = function 367 | | 0 -> `FPU 368 | | 1 -> `VME 369 | | 2 -> `DE 370 | | 3 -> `PSE 371 | | 4 -> `TSC 372 | | 5 -> `MSR 373 | | 6 -> `PAE 374 | | 7 -> `MCE 375 | | 8 -> `CX8 376 | | 9 -> `APIC 377 | | 11 -> `SEP 378 | | 12 -> `MTRR 379 | | 13 -> `PGE 380 | | 14 -> `MCA 381 | | 15 -> `CMOV 382 | | 16 -> `PAT 383 | | 17 -> `PSE36 384 | | 18 -> `PN 385 | | 19 -> `CLFLUSH 386 | | 21 -> `DTS 387 | | 22 -> `ACPI 388 | | 23 -> `MMX 389 | | 24 -> `FXSR 390 | | 25 -> `SSE 391 | | 26 -> `SSE2 392 | | 27 -> `SS 393 | | 28 -> `HT 394 | | 29 -> `TM 395 | | 30 -> `IA64 396 | | 31 -> `PBE 397 | | _ -> `Nothing 398 | 399 | let l1e_edx_f = function 400 | | 11 -> `SYSCALL 401 | | 19 -> `MP 402 | | 20 -> `NX 403 | | 22 -> `MMXEXT 404 | | 25 -> `FXSR_OPT 405 | | 26 -> `PDPE1GB 406 | | 27 -> `RDTSCP 407 | | 29 -> `LM 408 | | 30 -> `F_3DNOWEXT 409 | | 31 -> `F_3DNOW 410 | | _ -> `Nothing 411 | 412 | let l1_ecx_f = function 413 | | 0 -> `PNI 414 | | 1 -> `PCLMULQDQ 415 | | 2 -> `DTES64 416 | | 3 -> `MONITOR 417 | | 4 -> `DS_CPL 418 | | 5 -> `VMX 419 | | 6 -> `SMX 420 | | 7 -> `EST 421 | | 8 -> `TM2 422 | | 9 -> `SSSE3 423 | | 10 -> `CID 424 | | 11 -> `SDBG 425 | | 12 -> `FMA 426 | | 13 -> `CX16 427 | | 14 -> `XTPR 428 | | 15 -> `PDCM 429 | | 17 -> `PCID 430 | | 18 -> `DCA 431 | | 19 -> `SSE4_1 432 | | 20 -> `SSE4_2 433 | | 21 -> `X2APIC 434 | | 22 -> `MOVBE 435 | | 23 -> `POPCNT 436 | | 24 -> `TSC_DEADLINE_TIMER 437 | | 25 -> `AES 438 | | 26 -> `XSAVE 439 | | 27 -> `OSXSAVE 440 | | 28 -> `AVX 441 | | 29 -> `F16C 442 | | 30 -> `RDRAND 443 | | 31 -> `HYPERVISOR 444 | | _ -> `Nothing 445 | 446 | let l1e_ecx_f = function 447 | | 0 -> `LAHF_LM 448 | | 1 -> `CMP_LEGACY 449 | | 2 -> `SVM 450 | | 3 -> `EXTAPIC 451 | | 4 -> `CR8_LEGACY 452 | | 5 -> `ABM 453 | | 6 -> `SSE4A 454 | | 7 -> `MISALIGNSSE 455 | | 8 -> `F_3DNOWPREFETCH 456 | | 9 -> `OSVW 457 | | 10 -> `IBS 458 | | 11 -> `XOP 459 | | 12 -> `SKINIT 460 | | 13 -> `WDT 461 | | 15 -> `LWP 462 | | 16 -> `FMA4 463 | | 17 -> `TCE 464 | | 19 -> `NODEID_MSR 465 | | 21 -> `TBM 466 | | 22 -> `TOPOEXT 467 | | 23 -> `PERFCTR_CORE 468 | | 24 -> `PERFCTR_NB 469 | | 26 -> `BPEXT 470 | | 27 -> `PTSC 471 | | 28 -> `PERFCTR_L2 472 | | 29 -> `MWAITX 473 | | _ -> `Nothing 474 | 475 | let l7_ebx_f = function 476 | | 0 -> `FSGSBASE 477 | | 1 -> `TSC_ADJUST 478 | | 3 -> `BMI1 479 | | 4 -> `HLE 480 | | 5 -> `AVX2 481 | | 7 -> `SMEP 482 | | 8 -> `BMI2 483 | | 9 -> `ERMS 484 | | 10 -> `INVPCID 485 | | 11 -> `RTM 486 | | 12 -> `CQM 487 | | 14 -> `MPX 488 | | 16 -> `AVX512F 489 | | 17 -> `AVX512DQ 490 | | 18 -> `RDSEED 491 | | 19 -> `ADX 492 | | 20 -> `SMAP 493 | | 23 -> `CLFLUSHOPT 494 | | 24 -> `CLWB 495 | | 26 -> `AVX512PF 496 | | 27 -> `AVX512ER 497 | | 28 -> `AVX512CD 498 | | 29 -> `SHA_NI 499 | | 30 -> `AVX512BW 500 | | 31 -> `AVX512VL 501 | | _ -> `Nothing 502 | 503 | let l7_ecx_f = function 504 | | 3 -> `PKU 505 | | 4 -> `OSPKE 506 | | _ -> `Nothing 507 | 508 | let bit x i = Int32.(shift_right_logical x i |> to_int) land 1 = 1 509 | let fl f reg s = 510 | let rec go s = function 511 | | 32 -> s 512 | | i when bit reg i -> 513 | go (match f i with #flag as f -> FlagS.add f s | `Nothing -> s) (succ i) 514 | | i -> go s (succ i) in 515 | go s 0 516 | 517 | let fms eax = 518 | let r = Int32.to_int eax in 519 | let family = 520 | match (r lsr 8) land 0xf with 521 | | 0xf -> 0xf + ((r lsr 20) land 0xff) 522 | | x -> x in 523 | let model = 524 | (r lsr 4) land 0xf + 525 | if family >= 6 then ((r lsr 16) land 0xf) lsl 4 else 0 526 | and stepping = r land 0xf in 527 | (family, model, stepping) 528 | 529 | let cores _ reg = Int32.to_int reg 530 | (* This works with Intel after 2010, status unknown otherwise. 531 | God's own official dox at 532 | https://stackoverflow.com/questions/24088837/logical-cpu-count-return-16-instead-of-4/24704156#24704156 *) 533 | (* match vendor with | `Intel -> Int32.to_int reg | _ -> -1 *) 534 | 535 | let id () = 536 | match cpudetect_raw () with 537 | | None -> None 538 | | Some raw -> 539 | let vendor = vendor_of_sig_ebx raw.sign 540 | and flags = 541 | fl l1_edx_f raw.l1_edx @@ fl l1e_edx_f raw.l1e_edx @@ 542 | fl l1_ecx_f raw.l1_ecx @@ fl l1e_ecx_f raw.l1e_ecx @@ 543 | fl l7_ebx_f raw.l7_ebx @@ fl l7_ecx_f raw.l7_ecx FlagS.empty 544 | and model = fms raw.l1_eax in 545 | let cores = cores vendor raw.l12_ebx in 546 | Some { vendor; model; flags; cores } 547 | 548 | let _id = lazy (id ()) 549 | let q f = match Lazy.force _id with 550 | Some id -> Ok (f id) | _ -> Error `Unsupported 551 | 552 | let vendor () = q @@ fun id -> id.vendor 553 | let model () = q @@ fun id -> id.model 554 | let flags () = q @@ fun id -> FlagS.elements id.flags 555 | let supports qfs = q @@ fun id -> 556 | List.for_all (fun qf -> FlagS.mem qf id.flags) qfs 557 | let cores () = q @@ fun id -> id.cores 558 | -------------------------------------------------------------------------------- /src/cpuid.mli: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2016 David Kaloper Meršinjak. All rights reserved. 2 | See LICENSE.md. *) 3 | 4 | (** Detect CPU features. 5 | 6 | {e %%VERSION%% — {{: %%PKG_HOMEPAGE%%}homepage}} *) 7 | 8 | (** {1 Overview} 9 | 10 | Cpuid provides runtime detection of CPU features through the x86 {b CPUID} 11 | instruction. Detection discovers the CPU {{!vendor}[vendor]} and a set of 12 | feature {{!flag}[flag]}s. 13 | 14 | The flag names reflect the Linux 15 | {{:https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/arch/x86/include/asm/cpufeatures.h?id=c8d2bc9bc39ebea8437fd974fdbc21847bb897a3}convention}, 16 | except in uppercase. Only the features reported by the CPU are included 17 | (i.e. there are no synthetic flags). 18 | 19 | {2:lim Limitations} 20 | 21 | Cpuid currently relies on an x86-specific feature. The library runs on ARM 22 | processors, but calls return an {{!error}[error]}. 23 | 24 | The only CPUID-leaves consulted for {{!flags}feature flags} are [0x1], 25 | [0x7:0] and [0x80000001]. Hence, the reported features are a subset 26 | of what Linux would report. 27 | 28 | Number of {{!cores}cores} is CPUID [0xb:1] [EBX]. This only works on Intel 29 | CPUs after around 2010, and fails under virtualization. *) 30 | 31 | (** {1 Cpuid} *) 32 | 33 | type error = [ `Unsupported ] 34 | 35 | type nonrec 'a result = ('a, error) result 36 | 37 | type vendor = [ 38 | (* CPU manufacturer *) 39 | `AMD 40 | | `Centaur 41 | | `Cyrix 42 | | `Intel 43 | | `Transmeta 44 | | `NSC 45 | | `NexGen 46 | | `Rise 47 | | `SiS 48 | | `UMC 49 | | `VIA 50 | | `Vortex 51 | (* Virtual environment *) 52 | | `KVM 53 | | `Hyper_V 54 | | `Parallels 55 | | `VMware 56 | | `Xen 57 | (* WAT *) 58 | | `UNKNOWN 59 | ] 60 | (** The CPU manufacturer, the type of virtual environment, or [`UNKNOWN]. *) 61 | 62 | type flag = [ 63 | (* Level 0x00000001 (EDX) *) 64 | | `FPU (** Onboard FPU *) 65 | | `VME (** Virtual Mode Extensions *) 66 | | `DE (** Debugging Extensions *) 67 | | `PSE (** Page Size Extensions *) 68 | | `TSC (** Time Stamp Counter *) 69 | | `MSR (** Model-Specific Registers *) 70 | | `PAE (** Physical Address Extensions *) 71 | | `MCE (** Machine Check Exception *) 72 | | `CX8 (** CMPXCHG8 instruction *) 73 | | `APIC (** Onboard APIC *) 74 | | `SEP (** SYSENTER/SYSEXIT *) 75 | | `MTRR (** Memory Type Range Registers *) 76 | | `PGE (** Page Global Enable *) 77 | | `MCA (** Machine Check Architecture *) 78 | | `CMOV (** CMOV instructions *) 79 | | `PAT (** Page Attribute Table *) 80 | | `PSE36 (** 36-bit PSEs *) 81 | | `PN (** Processor serial number *) 82 | | `CLFLUSH (** CLFLUSH instruction *) 83 | | `DTS (** Debug Store *) 84 | | `ACPI (** ACPI via MSR *) 85 | | `MMX (** Multimedia Extensions *) 86 | | `FXSR (** FXSAVE/FXRSTOR, CR4.OSFXSR *) 87 | | `SSE (** SSE *) 88 | | `SSE2 (** SSE2 *) 89 | | `SS (** CPU self snoop *) 90 | | `HT (** Hyper-Threading *) 91 | | `TM (** Automatic clock control *) 92 | | `IA64 (** IA-64 processor *) 93 | | `PBE (** Pending Break Enable *) 94 | (* Level 0x80000001 (EDX) *) 95 | | `SYSCALL (** SYSCALL/SYSRET *) 96 | | `MP (** MP Capable. *) 97 | | `NX (** Execute Disable *) 98 | | `MMXEXT (** AMD MMX extensions *) 99 | | `FXSR_OPT (** FXSAVE/FXRSTOR optimizations *) 100 | | `PDPE1GB (** GB pages *) 101 | | `RDTSCP (** RDTSCP *) 102 | | `LM (** Long Mode (x86-64) *) 103 | | `F_3DNOWEXT (** AMD 3DNow! extensions *) 104 | | `F_3DNOW (** 3DNow! *) 105 | (* Level 0x00000001 (EDX) *) 106 | | `PNI (** SSE-3 *) 107 | | `PCLMULQDQ (** PCLMULQDQ instruction *) 108 | | `DTES64 (** 64-bit Debug Store *) 109 | | `MONITOR (** Monitor/Mwait support *) 110 | | `DS_CPL (** CPL Qual. Debug Store *) 111 | | `VMX (** Hardware virtualization *) 112 | | `SMX (** Safer mode *) 113 | | `EST (** Enhanced SpeedStep *) 114 | | `TM2 (** Thermal Monitor 2 *) 115 | | `SSSE3 (** Supplemental SSE-3 *) 116 | | `CID (** Context ID *) 117 | | `SDBG (** Silicon Debug *) 118 | | `FMA (** Fused multiply-add *) 119 | | `CX16 (** CMPXCHG16B *) 120 | | `XTPR (** Send Task Priority Messages *) 121 | | `PDCM (** Performance Capabilities *) 122 | | `PCID (** Process Context Identifiers *) 123 | | `DCA (** Direct Cache Access *) 124 | | `SSE4_1 (** SSE-4.1 *) 125 | | `SSE4_2 (** SSE-4.2 *) 126 | | `X2APIC (** x2APIC *) 127 | | `MOVBE (** MOVBE instruction *) 128 | | `POPCNT (** POPCNT instruction *) 129 | | `TSC_DEADLINE_TIMER (** Tsc deadline timer *) 130 | | `AES (** AES instructions *) 131 | | `XSAVE (** XSAVE/XRSTOR/XSETBV/XGETBV *) 132 | | `OSXSAVE (** XSAVE enabled in the OS *) 133 | | `AVX (** Advanced Vector Extensions *) 134 | | `F16C (** 16-bit fp conversions *) 135 | | `RDRAND (** The RDRAND instruction *) 136 | | `HYPERVISOR (** Running on a hypervisor *) 137 | (* Level 0x80000001 (ECX) *) 138 | | `LAHF_LM (** LAHF/SAHF in long mode *) 139 | | `CMP_LEGACY (** If yes HyperThreading not valid *) 140 | | `SVM (** Secure virtual machine *) 141 | | `EXTAPIC (** Extended APIC space *) 142 | | `CR8_LEGACY (** CR8 in 32-bit mode *) 143 | | `ABM (** Advanced bit manipulation *) 144 | | `SSE4A (** SSE-4A *) 145 | | `MISALIGNSSE (** Misaligned SSE mode *) 146 | | `F_3DNOWPREFETCH (** 3DNow prefetch instructions *) 147 | | `OSVW (** OS Visible Workaround *) 148 | | `IBS (** Instruction Based Sampling *) 149 | | `XOP (** extended AVX instructions *) 150 | | `SKINIT (** SKINIT/STGI instructions *) 151 | | `WDT (** Watchdog timer *) 152 | | `LWP (** Light Weight Profiling *) 153 | | `FMA4 (** 4 operands MAC instructions *) 154 | | `TCE (** translation cache extension *) 155 | | `NODEID_MSR (** NodeId MSR *) 156 | | `TBM (** trailing bit manipulations *) 157 | | `TOPOEXT (** topology extensions CPUID leafs *) 158 | | `PERFCTR_CORE (** core performance counter extensions *) 159 | | `PERFCTR_NB (** NB performance counter extensions *) 160 | | `BPEXT (** data breakpoint extension *) 161 | | `PTSC (** performance time-stamp counter *) 162 | | `PERFCTR_L2 (** L2 performance counter extensions *) 163 | | `MWAITX (** MWAIT extension (MONITORX/MWAITX) *) 164 | (* Level 0x00000007 (EBX) *) 165 | | `FSGSBASE (** \{RD/WR\}\{FS/GS\}BASE instructions*) 166 | | `TSC_ADJUST (** TSC adjustment MSR 0x3b *) 167 | | `BMI1 (** 1st group bit manipulation extensions *) 168 | | `HLE (** Hardware Lock Elision *) 169 | | `AVX2 (** AVX2 instructions *) 170 | | `SMEP (** Supervisor Mode Execution Protection *) 171 | | `BMI2 (** 2nd group bit manipulation extensions *) 172 | | `ERMS (** Enhanced REP MOVSB/STOSB *) 173 | | `INVPCID (** Invalidate Processor Context ID *) 174 | | `RTM (** Restricted Transactional Memory *) 175 | | `CQM (** Cache QoS Monitoring *) 176 | | `MPX (** Memory Protection Extension *) 177 | | `AVX512F (** AVX-512 Foundation *) 178 | | `AVX512DQ (** AVX-512 DQ (Double/Quad granular) Instructions *) 179 | | `RDSEED (** The RDSEED instruction *) 180 | | `ADX (** The ADCX and ADOX instructions *) 181 | | `SMAP (** Supervisor Mode Access Prevention *) 182 | | `CLFLUSHOPT (** CLFLUSHOPT instruction *) 183 | | `CLWB (** CLWB instruction *) 184 | | `AVX512PF (** AVX-512 Prefetch *) 185 | | `AVX512ER (** AVX-512 Exponential and Reciprocal *) 186 | | `AVX512CD (** AVX-512 Conflict Detection *) 187 | | `SHA_NI (** SHA1/SHA256 Instruction Extensions *) 188 | | `AVX512BW (** AVX-512 BW (Byte/Word granular) Instructions *) 189 | | `AVX512VL (** AVX-512 VL (128/256 Vector Length) Extensions *) 190 | (* Level 0x00000007 (ECX) *) 191 | | `PKU (** Protection Keys for Userspace *) 192 | | `OSPKE (** OS Protection Keys Enable *) 193 | ] 194 | (** CPU flags signify presence of individual features. 195 | 196 | Consult the interface file for the meaning of individual flags. *) 197 | 198 | val pp_error : Format.formatter -> error -> unit 199 | (** [pp_error ppf e] formats the {{!error}[error]} [e] to the formatter [ppf]. *) 200 | 201 | val pp_vendor : Format.formatter -> vendor -> unit 202 | (** [pp_vendor ppf v] formats the {{!vendor}[vendor]} [v] to the formatter [ppf]. *) 203 | 204 | val pp_flag : Format.formatter -> flag -> unit 205 | (** [pp_flag ppf f] formats the {{!flag}[flag]} [f] to the formatter [ppf]. *) 206 | 207 | (** {2 Queries} *) 208 | 209 | val vendor : unit -> vendor result 210 | (** [vendor ()] is the CPU {!vendor}, or an [error]. *) 211 | 212 | val model : unit -> (int * int * int) result 213 | (** [model ()] is the CPU's {e family}, {e model} and {e stepping}, or an 214 | [error]. *) 215 | 216 | val flags : unit -> flag list result 217 | (** [flags ()] is the list of CPU {!flag}s, or an [error]. *) 218 | 219 | val supports : flag list -> bool result 220 | (** [supports fs] is [true] iff all [fs] are in [flags ()].*) 221 | 222 | val cores : unit -> int result 223 | (** [cores ()] is the number of available logical cores. 224 | 225 | {b Note} Do not take [cores ()] too seriously. See {{!lim}limitations}. *) 226 | -------------------------------------------------------------------------------- /src/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (public_name cpuid) 3 | (synopsis "Detect CPU features") 4 | (wrapped false) 5 | (c_names cpuid) 6 | (c_flags --std=c99 -Wall -Wextra -O3)) 7 | 8 | (include_subdirs unqualified) 9 | 10 | -------------------------------------------------------------------------------- /src/native/cpuid.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2016 David Kaloper Meršinjak. All rights reserved. 2 | See LICENSE.md. */ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #if defined (__i386__) || defined (__x86_64__) 9 | #define __x86__ 10 | #include 11 | #endif /* __i386__ || __x86_64__ */ 12 | 13 | static inline int cpudetect (unsigned int res[9]) { 14 | #if defined (__x86__) 15 | unsigned int __reg1, __reg2, __reg3, level = __get_cpuid_max (0x0, res); 16 | if (level < 1) return 0; 17 | __cpuid (1, res[1], __reg2, res[2], res[3]); 18 | if (level >= 7) 19 | __cpuid_count (7, 0, __reg1, res[4], res[5], __reg2); 20 | if (level >= 0xb) 21 | __cpuid_count (0xb, 1, __reg1, res[6], __reg2, __reg3); 22 | level = __get_cpuid_max (0x80000000, NULL); 23 | if (level >= 0x80000001) 24 | __cpuid (0x80000001, __reg1, __reg2, res[7], res[8]); 25 | return 1; 26 | #else 27 | return 0; 28 | #endif /* __x86__ */ 29 | } 30 | 31 | CAMLprim value caml_cpuid_cpudetect (value unit) { 32 | CAMLparam1 (unit); 33 | CAMLlocal2 (opt, tup); 34 | unsigned int arr[9] = { 0 }; 35 | if (cpudetect (arr)) { 36 | opt = caml_alloc (1, 0); 37 | Field (opt, 0) = tup = caml_alloc_tuple (9); 38 | for (int i = 0; i < 9; i++) 39 | Field (tup, i) = caml_copy_int32 (arr[i]); 40 | } 41 | CAMLreturn (opt); 42 | } 43 | -------------------------------------------------------------------------------- /test/dune: -------------------------------------------------------------------------------- 1 | (test 2 | (name test) 3 | (libraries cpuid)) 4 | -------------------------------------------------------------------------------- /test/test.ml: -------------------------------------------------------------------------------- 1 | (* Copyright (c) 2016 David Kaloper Meršinjak. All rights reserved. 2 | See LICENSE.md. *) 3 | 4 | open Cpuid 5 | 6 | let pp_list pp ppf = Format.(function 7 | | [] -> pp_print_string ppf "[]" 8 | | [x] -> fprintf ppf "[%a]" pp x 9 | | x::xs -> fprintf ppf "@[%a%a@]" pp x 10 | (fun _ -> List.iter (fprintf ppf "@ %a" pp)) xs) 11 | 12 | let (>>=) r k = match r with Ok x -> k x | Error e -> Error e 13 | let (>>|) r k = r >>= fun x -> Ok (k x) 14 | 15 | let () = 16 | let pr = 17 | (vendor () >>| Format.printf "vendor: %a\n%!" pp_vendor) 18 | >>= fun () -> 19 | (model () >>| fun (f, m, s) -> 20 | Format.printf "family: %d\nmodel: %d\nstepping: %d\n%!" f m s) 21 | >>= fun () -> 22 | (cores () >>| Format.printf "cores: %d\n%!") 23 | >>= fun () -> 24 | (flags () >>| Format.printf "flags: %a\n%!" (pp_list pp_flag)) 25 | in match pr with Ok () -> () 26 | | Error e -> Format.printf "Error: %a\n%!" pp_error e 27 | --------------------------------------------------------------------------------