1
2
3
4
5
6
7
8
9
10
11 package cpuid
12
13 import (
14 "flag"
15 "fmt"
16 "math"
17 "math/bits"
18 "os"
19 "runtime"
20 "strings"
21 )
22
23
24
25
26
27 type Vendor int
28
29 const (
30 VendorUnknown Vendor = iota
31 Intel
32 AMD
33 VIA
34 Transmeta
35 NSC
36 KVM
37 MSVM
38 VMware
39 XenHVM
40 Bhyve
41 Hygon
42 SiS
43 RDC
44
45 Ampere
46 ARM
47 Broadcom
48 Cavium
49 DEC
50 Fujitsu
51 Infineon
52 Motorola
53 NVIDIA
54 AMCC
55 Qualcomm
56 Marvell
57
58 lastVendor
59 )
60
61
62
63
64 type FeatureID int
65
66 const (
67
68 UNKNOWN = -1
69
70
71 ADX FeatureID = iota
72 AESNI
73 AMD3DNOW
74 AMD3DNOWEXT
75 AMXBF16
76 AMXFP16
77 AMXINT8
78 AMXTILE
79 APX_F
80 AVX
81 AVX10
82 AVX10_128
83 AVX10_256
84 AVX10_512
85 AVX2
86 AVX512BF16
87 AVX512BITALG
88 AVX512BW
89 AVX512CD
90 AVX512DQ
91 AVX512ER
92 AVX512F
93 AVX512FP16
94 AVX512IFMA
95 AVX512PF
96 AVX512VBMI
97 AVX512VBMI2
98 AVX512VL
99 AVX512VNNI
100 AVX512VP2INTERSECT
101 AVX512VPOPCNTDQ
102 AVXIFMA
103 AVXNECONVERT
104 AVXSLOW
105 AVXVNNI
106 AVXVNNIINT8
107 BHI_CTRL
108 BMI1
109 BMI2
110 CETIBT
111 CETSS
112 CLDEMOTE
113 CLMUL
114 CLZERO
115 CMOV
116 CMPCCXADD
117 CMPSB_SCADBS_SHORT
118 CMPXCHG8
119 CPBOOST
120 CPPC
121 CX16
122 EFER_LMSLE_UNS
123 ENQCMD
124 ERMS
125 F16C
126 FLUSH_L1D
127 FMA3
128 FMA4
129 FP128
130 FP256
131 FSRM
132 FXSR
133 FXSROPT
134 GFNI
135 HLE
136 HRESET
137 HTT
138 HWA
139 HYBRID_CPU
140 HYPERVISOR
141 IA32_ARCH_CAP
142 IA32_CORE_CAP
143 IBPB
144 IBPB_BRTYPE
145 IBRS
146 IBRS_PREFERRED
147 IBRS_PROVIDES_SMP
148 IBS
149 IBSBRNTRGT
150 IBSFETCHSAM
151 IBSFFV
152 IBSOPCNT
153 IBSOPCNTEXT
154 IBSOPSAM
155 IBSRDWROPCNT
156 IBSRIPINVALIDCHK
157 IBS_FETCH_CTLX
158 IBS_OPDATA4
159 IBS_OPFUSE
160 IBS_PREVENTHOST
161 IBS_ZEN4
162 IDPRED_CTRL
163 INT_WBINVD
164 INVLPGB
165 KEYLOCKER
166 KEYLOCKERW
167 LAHF
168 LAM
169 LBRVIRT
170 LZCNT
171 MCAOVERFLOW
172 MCDT_NO
173 MCOMMIT
174 MD_CLEAR
175 MMX
176 MMXEXT
177 MOVBE
178 MOVDIR64B
179 MOVDIRI
180 MOVSB_ZL
181 MOVU
182 MPX
183 MSRIRC
184 MSRLIST
185 MSR_PAGEFLUSH
186 NRIPS
187 NX
188 OSXSAVE
189 PCONFIG
190 POPCNT
191 PPIN
192 PREFETCHI
193 PSFD
194 RDPRU
195 RDRAND
196 RDSEED
197 RDTSCP
198 RRSBA_CTRL
199 RTM
200 RTM_ALWAYS_ABORT
201 SBPB
202 SERIALIZE
203 SEV
204 SEV_64BIT
205 SEV_ALTERNATIVE
206 SEV_DEBUGSWAP
207 SEV_ES
208 SEV_RESTRICTED
209 SEV_SNP
210 SGX
211 SGXLC
212 SHA
213 SME
214 SME_COHERENT
215 SPEC_CTRL_SSBD
216 SRBDS_CTRL
217 SRSO_MSR_FIX
218 SRSO_NO
219 SRSO_USER_KERNEL_NO
220 SSE
221 SSE2
222 SSE3
223 SSE4
224 SSE42
225 SSE4A
226 SSSE3
227 STIBP
228 STIBP_ALWAYSON
229 STOSB_SHORT
230 SUCCOR
231 SVM
232 SVMDA
233 SVMFBASID
234 SVML
235 SVMNP
236 SVMPF
237 SVMPFT
238 SYSCALL
239 SYSEE
240 TBM
241 TDX_GUEST
242 TLB_FLUSH_NESTED
243 TME
244 TOPEXT
245 TSCRATEMSR
246 TSXLDTRK
247 VAES
248 VMCBCLEAN
249 VMPL
250 VMSA_REGPROT
251 VMX
252 VPCLMULQDQ
253 VTE
254 WAITPKG
255 WBNOINVD
256 WRMSRNS
257 X87
258 XGETBV1
259 XOP
260 XSAVE
261 XSAVEC
262 XSAVEOPT
263 XSAVES
264
265
266 AESARM
267 ARMCPUID
268 ASIMD
269 ASIMDDP
270 ASIMDHP
271 ASIMDRDM
272 ATOMICS
273 CRC32
274 DCPOP
275 EVTSTRM
276 FCMA
277 FP
278 FPHP
279 GPA
280 JSCVT
281 LRCPC
282 PMULL
283 SHA1
284 SHA2
285 SHA3
286 SHA512
287 SM3
288 SM4
289 SVE
290
291 lastID
292
293 firstID FeatureID = UNKNOWN + 1
294 )
295
296
297 type CPUInfo struct {
298 BrandName string
299 VendorID Vendor
300 VendorString string
301 featureSet flagSet
302 PhysicalCores int
303 ThreadsPerCore int
304 LogicalCores int
305 Family int
306 Model int
307 Stepping int
308 CacheLine int
309 Hz int64
310 BoostFreq int64
311 Cache struct {
312 L1I int
313 L1D int
314 L2 int
315 L3 int
316 }
317 SGX SGXSupport
318 AMDMemEncryption AMDMemEncryptionSupport
319 AVX10Level uint8
320 maxFunc uint32
321 maxExFunc uint32
322 }
323
324 var cpuid func(op uint32) (eax, ebx, ecx, edx uint32)
325 var cpuidex func(op, op2 uint32) (eax, ebx, ecx, edx uint32)
326 var xgetbv func(index uint32) (eax, edx uint32)
327 var rdtscpAsm func() (eax, ebx, ecx, edx uint32)
328 var darwinHasAVX512 = func() bool { return false }
329
330
331
332
333
334 var CPU CPUInfo
335
336 func init() {
337 initCPU()
338 Detect()
339 }
340
341
342
343
344
345
346
347
348 func Detect() {
349
350 CPU.ThreadsPerCore = 1
351 CPU.Cache.L1I = -1
352 CPU.Cache.L1D = -1
353 CPU.Cache.L2 = -1
354 CPU.Cache.L3 = -1
355 safe := true
356 if detectArmFlag != nil {
357 safe = !*detectArmFlag
358 }
359 addInfo(&CPU, safe)
360 if displayFeats != nil && *displayFeats {
361 fmt.Println("cpu features:", strings.Join(CPU.FeatureSet(), ","))
362
363 os.Exit(1)
364 }
365 if disableFlag != nil {
366 s := strings.Split(*disableFlag, ",")
367 for _, feat := range s {
368 feat := ParseFeature(strings.TrimSpace(feat))
369 if feat != UNKNOWN {
370 CPU.featureSet.unset(feat)
371 }
372 }
373 }
374 }
375
376
377
378
379
380
381 func DetectARM() {
382 addInfo(&CPU, false)
383 }
384
385 var detectArmFlag *bool
386 var displayFeats *bool
387 var disableFlag *string
388
389
390
391
392
393
394 func Flags() {
395 disableFlag = flag.String("cpu.disable", "", "disable cpu features; comma separated list")
396 displayFeats = flag.Bool("cpu.features", false, "lists cpu features and exits")
397 detectArmFlag = flag.Bool("cpu.arm", false, "allow ARM features to be detected; can potentially crash")
398 }
399
400
401 func (c CPUInfo) Supports(ids ...FeatureID) bool {
402 for _, id := range ids {
403 if !c.featureSet.inSet(id) {
404 return false
405 }
406 }
407 return true
408 }
409
410
411
412 func (c *CPUInfo) Has(id FeatureID) bool {
413 return c.featureSet.inSet(id)
414 }
415
416
417 func (c CPUInfo) AnyOf(ids ...FeatureID) bool {
418 for _, id := range ids {
419 if c.featureSet.inSet(id) {
420 return true
421 }
422 }
423 return false
424 }
425
426
427
428 type Features *flagSet
429
430
431 func CombineFeatures(ids ...FeatureID) Features {
432 var v flagSet
433 for _, id := range ids {
434 v.set(id)
435 }
436 return &v
437 }
438
439 func (c *CPUInfo) HasAll(f Features) bool {
440 return c.featureSet.hasSetP(f)
441 }
442
443
444 var oneOfLevel = CombineFeatures(SYSEE, SYSCALL)
445 var level1Features = CombineFeatures(CMOV, CMPXCHG8, X87, FXSR, MMX, SSE, SSE2)
446 var level2Features = CombineFeatures(CMOV, CMPXCHG8, X87, FXSR, MMX, SSE, SSE2, CX16, LAHF, POPCNT, SSE3, SSE4, SSE42, SSSE3)
447 var level3Features = CombineFeatures(CMOV, CMPXCHG8, X87, FXSR, MMX, SSE, SSE2, CX16, LAHF, POPCNT, SSE3, SSE4, SSE42, SSSE3, AVX, AVX2, BMI1, BMI2, F16C, FMA3, LZCNT, MOVBE, OSXSAVE)
448 var level4Features = CombineFeatures(CMOV, CMPXCHG8, X87, FXSR, MMX, SSE, SSE2, CX16, LAHF, POPCNT, SSE3, SSE4, SSE42, SSSE3, AVX, AVX2, BMI1, BMI2, F16C, FMA3, LZCNT, MOVBE, OSXSAVE, AVX512F, AVX512BW, AVX512CD, AVX512DQ, AVX512VL)
449
450
451
452
453 func (c CPUInfo) X64Level() int {
454 if !c.featureSet.hasOneOf(oneOfLevel) {
455 return 0
456 }
457 if c.featureSet.hasSetP(level4Features) {
458 return 4
459 }
460 if c.featureSet.hasSetP(level3Features) {
461 return 3
462 }
463 if c.featureSet.hasSetP(level2Features) {
464 return 2
465 }
466 if c.featureSet.hasSetP(level1Features) {
467 return 1
468 }
469 return 0
470 }
471
472
473 func (c *CPUInfo) Disable(ids ...FeatureID) bool {
474 for _, id := range ids {
475 c.featureSet.unset(id)
476 }
477 return true
478 }
479
480
481
482 func (c *CPUInfo) Enable(ids ...FeatureID) bool {
483 for _, id := range ids {
484 c.featureSet.set(id)
485 }
486 return true
487 }
488
489
490 func (c CPUInfo) IsVendor(v Vendor) bool {
491 return c.VendorID == v
492 }
493
494
495 func (c CPUInfo) FeatureSet() []string {
496 s := make([]string, 0, c.featureSet.nEnabled())
497 s = append(s, c.featureSet.Strings()...)
498 return s
499 }
500
501
502
503
504 func (c CPUInfo) RTCounter() uint64 {
505 if !c.Supports(RDTSCP) {
506 return 0
507 }
508 a, _, _, d := rdtscpAsm()
509 return uint64(a) | (uint64(d) << 32)
510 }
511
512
513
514
515
516 func (c CPUInfo) Ia32TscAux() uint32 {
517 if !c.Supports(RDTSCP) {
518 return 0
519 }
520 _, _, ecx, _ := rdtscpAsm()
521 return ecx
522 }
523
524
525
526
527
528 func (c CPUInfo) LogicalCPU() int {
529 if c.maxFunc < 1 {
530 return -1
531 }
532 _, ebx, _, _ := cpuid(1)
533 return int(ebx >> 24)
534 }
535
536
537
538 func (c *CPUInfo) frequencies() {
539 c.Hz, c.BoostFreq = 0, 0
540 mfi := maxFunctionID()
541 if mfi >= 0x15 {
542 eax, ebx, ecx, _ := cpuid(0x15)
543 if eax != 0 && ebx != 0 && ecx != 0 {
544 c.Hz = (int64(ecx) * int64(ebx)) / int64(eax)
545 }
546 }
547 if mfi >= 0x16 {
548 a, b, _, _ := cpuid(0x16)
549
550 if a&0xffff > 0 {
551 c.Hz = int64(a&0xffff) * 1_000_000
552 }
553
554 if b&0xffff > 0 {
555 c.BoostFreq = int64(b&0xffff) * 1_000_000
556 }
557 }
558 if c.Hz > 0 {
559 return
560 }
561
562
563
564
565
566
567
568 model := c.BrandName
569 hz := strings.LastIndex(model, "Hz")
570 if hz < 3 {
571 return
572 }
573 var multiplier int64
574 switch model[hz-1] {
575 case 'M':
576 multiplier = 1000 * 1000
577 case 'G':
578 multiplier = 1000 * 1000 * 1000
579 case 'T':
580 multiplier = 1000 * 1000 * 1000 * 1000
581 }
582 if multiplier == 0 {
583 return
584 }
585 freq := int64(0)
586 divisor := int64(0)
587 decimalShift := int64(1)
588 var i int
589 for i = hz - 2; i >= 0 && model[i] != ' '; i-- {
590 if model[i] >= '0' && model[i] <= '9' {
591 freq += int64(model[i]-'0') * decimalShift
592 decimalShift *= 10
593 } else if model[i] == '.' {
594 if divisor != 0 {
595 return
596 }
597 divisor = decimalShift
598 } else {
599 return
600 }
601 }
602
603 if i < 0 {
604 return
605 }
606 if divisor != 0 {
607 c.Hz = (freq * multiplier) / divisor
608 return
609 }
610 c.Hz = freq * multiplier
611 }
612
613
614
615 func (c CPUInfo) VM() bool {
616 return CPU.featureSet.inSet(HYPERVISOR)
617 }
618
619
620 type flags uint64
621
622
623 const flagBitsLog2 = 6
624 const flagBits = 1 << flagBitsLog2
625 const flagMask = flagBits - 1
626
627
628 type flagSet [(lastID + flagMask) / flagBits]flags
629
630 func (s *flagSet) inSet(feat FeatureID) bool {
631 return s[feat>>flagBitsLog2]&(1<<(feat&flagMask)) != 0
632 }
633
634 func (s *flagSet) set(feat FeatureID) {
635 s[feat>>flagBitsLog2] |= 1 << (feat & flagMask)
636 }
637
638
639 func (s *flagSet) setIf(cond bool, features ...FeatureID) {
640 if cond {
641 for _, offset := range features {
642 s[offset>>flagBitsLog2] |= 1 << (offset & flagMask)
643 }
644 }
645 }
646
647 func (s *flagSet) unset(offset FeatureID) {
648 bit := flags(1 << (offset & flagMask))
649 s[offset>>flagBitsLog2] = s[offset>>flagBitsLog2] & ^bit
650 }
651
652
653 func (s *flagSet) or(other flagSet) {
654 for i, v := range other[:] {
655 s[i] |= v
656 }
657 }
658
659
660 func (s *flagSet) hasSet(other flagSet) bool {
661 for i, v := range other[:] {
662 if s[i]&v != v {
663 return false
664 }
665 }
666 return true
667 }
668
669
670 func (s *flagSet) hasSetP(other *flagSet) bool {
671 for i, v := range other[:] {
672 if s[i]&v != v {
673 return false
674 }
675 }
676 return true
677 }
678
679
680 func (s *flagSet) hasOneOf(other *flagSet) bool {
681 for i, v := range other[:] {
682 if s[i]&v != 0 {
683 return true
684 }
685 }
686 return false
687 }
688
689
690 func (s *flagSet) nEnabled() (n int) {
691 for _, v := range s[:] {
692 n += bits.OnesCount64(uint64(v))
693 }
694 return n
695 }
696
697 func flagSetWith(feat ...FeatureID) flagSet {
698 var res flagSet
699 for _, f := range feat {
700 res.set(f)
701 }
702 return res
703 }
704
705
706
707 func ParseFeature(s string) FeatureID {
708 s = strings.ToUpper(s)
709 for i := firstID; i < lastID; i++ {
710 if i.String() == s {
711 return i
712 }
713 }
714 return UNKNOWN
715 }
716
717
718 func (s flagSet) Strings() []string {
719 if len(s) == 0 {
720 return []string{""}
721 }
722 r := make([]string, 0)
723 for i := firstID; i < lastID; i++ {
724 if s.inSet(i) {
725 r = append(r, i.String())
726 }
727 }
728 return r
729 }
730
731 func maxExtendedFunction() uint32 {
732 eax, _, _, _ := cpuid(0x80000000)
733 return eax
734 }
735
736 func maxFunctionID() uint32 {
737 a, _, _, _ := cpuid(0)
738 return a
739 }
740
741 func brandName() string {
742 if maxExtendedFunction() >= 0x80000004 {
743 v := make([]uint32, 0, 48)
744 for i := uint32(0); i < 3; i++ {
745 a, b, c, d := cpuid(0x80000002 + i)
746 v = append(v, a, b, c, d)
747 }
748 return strings.Trim(string(valAsString(v...)), " ")
749 }
750 return "unknown"
751 }
752
753 func threadsPerCore() int {
754 mfi := maxFunctionID()
755 vend, _ := vendorID()
756
757 if mfi < 0x4 || (vend != Intel && vend != AMD) {
758 return 1
759 }
760
761 if mfi < 0xb {
762 if vend != Intel {
763 return 1
764 }
765 _, b, _, d := cpuid(1)
766 if (d & (1 << 28)) != 0 {
767
768 v := (b >> 16) & 255
769 if v > 1 {
770 a4, _, _, _ := cpuid(4)
771
772 v2 := (a4 >> 26) + 1
773 if v2 > 0 {
774 return int(v) / int(v2)
775 }
776 }
777 }
778 return 1
779 }
780 _, b, _, _ := cpuidex(0xb, 0)
781 if b&0xffff == 0 {
782 if vend == AMD {
783
784
785 fam, _, _ := familyModel()
786 _, _, _, d := cpuid(1)
787 if (d&(1<<28)) != 0 && fam >= 23 {
788 return 2
789 }
790 }
791 return 1
792 }
793 return int(b & 0xffff)
794 }
795
796 func logicalCores() int {
797 mfi := maxFunctionID()
798 v, _ := vendorID()
799 switch v {
800 case Intel:
801
802 if mfi < 0xb {
803 if mfi < 1 {
804 return 0
805 }
806
807
808
809 _, ebx, _, _ := cpuid(1)
810 logical := (ebx >> 16) & 0xff
811 return int(logical)
812 }
813 _, b, _, _ := cpuidex(0xb, 1)
814 return int(b & 0xffff)
815 case AMD, Hygon:
816 _, b, _, _ := cpuid(1)
817 return int((b >> 16) & 0xff)
818 default:
819 return 0
820 }
821 }
822
823 func familyModel() (family, model, stepping int) {
824 if maxFunctionID() < 0x1 {
825 return 0, 0, 0
826 }
827 eax, _, _, _ := cpuid(1)
828
829 family = int((eax >> 8) & 0xf)
830 extFam := family == 0x6
831 if family == 0xf {
832
833 family += int((eax >> 20) & 0xff)
834 extFam = true
835 }
836
837 model = int((eax >> 4) & 0xf)
838 if extFam {
839
840 model += int((eax >> 12) & 0xf0)
841 }
842 stepping = int(eax & 0xf)
843 return family, model, stepping
844 }
845
846 func physicalCores() int {
847 v, _ := vendorID()
848 switch v {
849 case Intel:
850 return logicalCores() / threadsPerCore()
851 case AMD, Hygon:
852 lc := logicalCores()
853 tpc := threadsPerCore()
854 if lc > 0 && tpc > 0 {
855 return lc / tpc
856 }
857
858
859 if maxExtendedFunction() >= 0x80000008 {
860 _, _, c, _ := cpuid(0x80000008)
861 if c&0xff > 0 {
862 return int(c&0xff) + 1
863 }
864 }
865 }
866 return 0
867 }
868
869
870 var vendorMapping = map[string]Vendor{
871 "AMDisbetter!": AMD,
872 "AuthenticAMD": AMD,
873 "CentaurHauls": VIA,
874 "GenuineIntel": Intel,
875 "TransmetaCPU": Transmeta,
876 "GenuineTMx86": Transmeta,
877 "Geode by NSC": NSC,
878 "VIA VIA VIA ": VIA,
879 "KVMKVMKVMKVM": KVM,
880 "Microsoft Hv": MSVM,
881 "VMwareVMware": VMware,
882 "XenVMMXenVMM": XenHVM,
883 "bhyve bhyve ": Bhyve,
884 "HygonGenuine": Hygon,
885 "Vortex86 SoC": SiS,
886 "SiS SiS SiS ": SiS,
887 "RiseRiseRise": SiS,
888 "Genuine RDC": RDC,
889 }
890
891 func vendorID() (Vendor, string) {
892 _, b, c, d := cpuid(0)
893 v := string(valAsString(b, d, c))
894 vend, ok := vendorMapping[v]
895 if !ok {
896 return VendorUnknown, v
897 }
898 return vend, v
899 }
900
901 func cacheLine() int {
902 if maxFunctionID() < 0x1 {
903 return 0
904 }
905
906 _, ebx, _, _ := cpuid(1)
907 cache := (ebx & 0xff00) >> 5
908 if cache == 0 && maxExtendedFunction() >= 0x80000006 {
909 _, _, ecx, _ := cpuid(0x80000006)
910 cache = ecx & 0xff
911 }
912
913 return int(cache)
914 }
915
916 func (c *CPUInfo) cacheSize() {
917 c.Cache.L1D = -1
918 c.Cache.L1I = -1
919 c.Cache.L2 = -1
920 c.Cache.L3 = -1
921 vendor, _ := vendorID()
922 switch vendor {
923 case Intel:
924 if maxFunctionID() < 4 {
925 return
926 }
927 c.Cache.L1I, c.Cache.L1D, c.Cache.L2, c.Cache.L3 = 0, 0, 0, 0
928 for i := uint32(0); ; i++ {
929 eax, ebx, ecx, _ := cpuidex(4, i)
930 cacheType := eax & 15
931 if cacheType == 0 {
932 break
933 }
934 cacheLevel := (eax >> 5) & 7
935 coherency := int(ebx&0xfff) + 1
936 partitions := int((ebx>>12)&0x3ff) + 1
937 associativity := int((ebx>>22)&0x3ff) + 1
938 sets := int(ecx) + 1
939 size := associativity * partitions * coherency * sets
940 switch cacheLevel {
941 case 1:
942 if cacheType == 1 {
943
944 c.Cache.L1D = size
945 } else if cacheType == 2 {
946
947 c.Cache.L1I = size
948 } else {
949 if c.Cache.L1D < 0 {
950 c.Cache.L1I = size
951 }
952 if c.Cache.L1I < 0 {
953 c.Cache.L1I = size
954 }
955 }
956 case 2:
957 c.Cache.L2 = size
958 case 3:
959 c.Cache.L3 = size
960 }
961 }
962 case AMD, Hygon:
963
964 if maxExtendedFunction() < 0x80000005 {
965 return
966 }
967 _, _, ecx, edx := cpuid(0x80000005)
968 c.Cache.L1D = int(((ecx >> 24) & 0xFF) * 1024)
969 c.Cache.L1I = int(((edx >> 24) & 0xFF) * 1024)
970
971 if maxExtendedFunction() < 0x80000006 {
972 return
973 }
974 _, _, ecx, _ = cpuid(0x80000006)
975 c.Cache.L2 = int(((ecx >> 16) & 0xFFFF) * 1024)
976
977
978 if maxExtendedFunction() < 0x8000001D || !c.Has(TOPEXT) {
979 return
980 }
981
982
983
984 nSame := 0
985 var last uint32
986 for i := uint32(0); i < math.MaxUint32; i++ {
987 eax, ebx, ecx, _ := cpuidex(0x8000001D, i)
988
989 level := (eax >> 5) & 7
990 cacheNumSets := ecx + 1
991 cacheLineSize := 1 + (ebx & 2047)
992 cachePhysPartitions := 1 + ((ebx >> 12) & 511)
993 cacheNumWays := 1 + ((ebx >> 22) & 511)
994
995 typ := eax & 15
996 size := int(cacheNumSets * cacheLineSize * cachePhysPartitions * cacheNumWays)
997 if typ == 0 {
998 return
999 }
1000
1001
1002 comb := eax ^ ebx ^ ecx
1003 if comb == last {
1004 nSame++
1005 if nSame == 100 {
1006 return
1007 }
1008 }
1009 last = comb
1010
1011 switch level {
1012 case 1:
1013 switch typ {
1014 case 1:
1015
1016 c.Cache.L1D = size
1017 case 2:
1018
1019 c.Cache.L1I = size
1020 default:
1021 if c.Cache.L1D < 0 {
1022 c.Cache.L1I = size
1023 }
1024 if c.Cache.L1I < 0 {
1025 c.Cache.L1I = size
1026 }
1027 }
1028 case 2:
1029 c.Cache.L2 = size
1030 case 3:
1031 c.Cache.L3 = size
1032 }
1033 }
1034 }
1035 }
1036
1037 type SGXEPCSection struct {
1038 BaseAddress uint64
1039 EPCSize uint64
1040 }
1041
1042 type SGXSupport struct {
1043 Available bool
1044 LaunchControl bool
1045 SGX1Supported bool
1046 SGX2Supported bool
1047 MaxEnclaveSizeNot64 int64
1048 MaxEnclaveSize64 int64
1049 EPCSections []SGXEPCSection
1050 }
1051
1052 func hasSGX(available, lc bool) (rval SGXSupport) {
1053 rval.Available = available
1054
1055 if !available {
1056 return
1057 }
1058
1059 rval.LaunchControl = lc
1060
1061 a, _, _, d := cpuidex(0x12, 0)
1062 rval.SGX1Supported = a&0x01 != 0
1063 rval.SGX2Supported = a&0x02 != 0
1064 rval.MaxEnclaveSizeNot64 = 1 << (d & 0xFF)
1065 rval.MaxEnclaveSize64 = 1 << ((d >> 8) & 0xFF)
1066 rval.EPCSections = make([]SGXEPCSection, 0)
1067
1068 for subleaf := uint32(2); subleaf < 2+8; subleaf++ {
1069 eax, ebx, ecx, edx := cpuidex(0x12, subleaf)
1070 leafType := eax & 0xf
1071
1072 if leafType == 0 {
1073
1074 break
1075 } else if leafType == 1 {
1076
1077 baseAddress := uint64(eax&0xfffff000) + (uint64(ebx&0x000fffff) << 32)
1078 size := uint64(ecx&0xfffff000) + (uint64(edx&0x000fffff) << 32)
1079
1080 section := SGXEPCSection{BaseAddress: baseAddress, EPCSize: size}
1081 rval.EPCSections = append(rval.EPCSections, section)
1082 }
1083 }
1084
1085 return
1086 }
1087
1088 type AMDMemEncryptionSupport struct {
1089 Available bool
1090 CBitPossition uint32
1091 NumVMPL uint32
1092 PhysAddrReduction uint32
1093 NumEntryptedGuests uint32
1094 MinSevNoEsAsid uint32
1095 }
1096
1097 func hasAMDMemEncryption(available bool) (rval AMDMemEncryptionSupport) {
1098 rval.Available = available
1099 if !available {
1100 return
1101 }
1102
1103 _, b, c, d := cpuidex(0x8000001f, 0)
1104
1105 rval.CBitPossition = b & 0x3f
1106 rval.PhysAddrReduction = (b >> 6) & 0x3F
1107 rval.NumVMPL = (b >> 12) & 0xf
1108 rval.NumEntryptedGuests = c
1109 rval.MinSevNoEsAsid = d
1110
1111 return
1112 }
1113
1114 func support() flagSet {
1115 var fs flagSet
1116 mfi := maxFunctionID()
1117 vend, _ := vendorID()
1118 if mfi < 0x1 {
1119 return fs
1120 }
1121 family, model, _ := familyModel()
1122
1123 _, _, c, d := cpuid(1)
1124 fs.setIf((d&(1<<0)) != 0, X87)
1125 fs.setIf((d&(1<<8)) != 0, CMPXCHG8)
1126 fs.setIf((d&(1<<11)) != 0, SYSEE)
1127 fs.setIf((d&(1<<15)) != 0, CMOV)
1128 fs.setIf((d&(1<<23)) != 0, MMX)
1129 fs.setIf((d&(1<<24)) != 0, FXSR)
1130 fs.setIf((d&(1<<25)) != 0, FXSROPT)
1131 fs.setIf((d&(1<<25)) != 0, SSE)
1132 fs.setIf((d&(1<<26)) != 0, SSE2)
1133 fs.setIf((c&1) != 0, SSE3)
1134 fs.setIf((c&(1<<5)) != 0, VMX)
1135 fs.setIf((c&(1<<9)) != 0, SSSE3)
1136 fs.setIf((c&(1<<19)) != 0, SSE4)
1137 fs.setIf((c&(1<<20)) != 0, SSE42)
1138 fs.setIf((c&(1<<25)) != 0, AESNI)
1139 fs.setIf((c&(1<<1)) != 0, CLMUL)
1140 fs.setIf(c&(1<<22) != 0, MOVBE)
1141 fs.setIf(c&(1<<23) != 0, POPCNT)
1142 fs.setIf(c&(1<<30) != 0, RDRAND)
1143
1144
1145
1146 fs.setIf(c&(1<<31) != 0, HYPERVISOR)
1147 fs.setIf(c&(1<<29) != 0, F16C)
1148 fs.setIf(c&(1<<13) != 0, CX16)
1149
1150 if vend == Intel && (d&(1<<28)) != 0 && mfi >= 4 {
1151 fs.setIf(threadsPerCore() > 1, HTT)
1152 }
1153 if vend == AMD && (d&(1<<28)) != 0 && mfi >= 4 {
1154 fs.setIf(threadsPerCore() > 1, HTT)
1155 }
1156 fs.setIf(c&1<<26 != 0, XSAVE)
1157 fs.setIf(c&1<<27 != 0, OSXSAVE)
1158
1159 const avxCheck = 1<<26 | 1<<27 | 1<<28
1160 if c&avxCheck == avxCheck {
1161
1162 eax, _ := xgetbv(0)
1163 if (eax & 0x6) == 0x6 {
1164 fs.set(AVX)
1165 switch vend {
1166 case Intel:
1167
1168 fs.setIf(family == 6 && model < 60, AVXSLOW)
1169 case AMD:
1170
1171 fs.setIf(family < 23 || (family == 23 && model < 49), AVXSLOW)
1172 }
1173 }
1174 }
1175
1176
1177 const fma3Check = 1<<12 | 1<<27
1178 fs.setIf(c&fma3Check == fma3Check, FMA3)
1179
1180
1181 if mfi >= 7 {
1182 _, ebx, ecx, edx := cpuidex(7, 0)
1183 if fs.inSet(AVX) && (ebx&0x00000020) != 0 {
1184 fs.set(AVX2)
1185 }
1186
1187 if (ebx & 0x00000008) != 0 {
1188 fs.set(BMI1)
1189 fs.setIf((ebx&0x00000100) != 0, BMI2)
1190 }
1191 fs.setIf(ebx&(1<<2) != 0, SGX)
1192 fs.setIf(ebx&(1<<4) != 0, HLE)
1193 fs.setIf(ebx&(1<<9) != 0, ERMS)
1194 fs.setIf(ebx&(1<<11) != 0, RTM)
1195 fs.setIf(ebx&(1<<14) != 0, MPX)
1196 fs.setIf(ebx&(1<<18) != 0, RDSEED)
1197 fs.setIf(ebx&(1<<19) != 0, ADX)
1198 fs.setIf(ebx&(1<<29) != 0, SHA)
1199
1200
1201 fs.setIf(ecx&(1<<5) != 0, WAITPKG)
1202 fs.setIf(ecx&(1<<7) != 0, CETSS)
1203 fs.setIf(ecx&(1<<8) != 0, GFNI)
1204 fs.setIf(ecx&(1<<9) != 0, VAES)
1205 fs.setIf(ecx&(1<<10) != 0, VPCLMULQDQ)
1206 fs.setIf(ecx&(1<<13) != 0, TME)
1207 fs.setIf(ecx&(1<<25) != 0, CLDEMOTE)
1208 fs.setIf(ecx&(1<<23) != 0, KEYLOCKER)
1209 fs.setIf(ecx&(1<<27) != 0, MOVDIRI)
1210 fs.setIf(ecx&(1<<28) != 0, MOVDIR64B)
1211 fs.setIf(ecx&(1<<29) != 0, ENQCMD)
1212 fs.setIf(ecx&(1<<30) != 0, SGXLC)
1213
1214
1215 fs.setIf(edx&(1<<4) != 0, FSRM)
1216 fs.setIf(edx&(1<<9) != 0, SRBDS_CTRL)
1217 fs.setIf(edx&(1<<10) != 0, MD_CLEAR)
1218 fs.setIf(edx&(1<<11) != 0, RTM_ALWAYS_ABORT)
1219 fs.setIf(edx&(1<<14) != 0, SERIALIZE)
1220 fs.setIf(edx&(1<<15) != 0, HYBRID_CPU)
1221 fs.setIf(edx&(1<<16) != 0, TSXLDTRK)
1222 fs.setIf(edx&(1<<18) != 0, PCONFIG)
1223 fs.setIf(edx&(1<<20) != 0, CETIBT)
1224 fs.setIf(edx&(1<<26) != 0, IBPB)
1225 fs.setIf(edx&(1<<27) != 0, STIBP)
1226 fs.setIf(edx&(1<<28) != 0, FLUSH_L1D)
1227 fs.setIf(edx&(1<<29) != 0, IA32_ARCH_CAP)
1228 fs.setIf(edx&(1<<30) != 0, IA32_CORE_CAP)
1229 fs.setIf(edx&(1<<31) != 0, SPEC_CTRL_SSBD)
1230
1231
1232 eax1, _, _, edx1 := cpuidex(7, 1)
1233 fs.setIf(fs.inSet(AVX) && eax1&(1<<4) != 0, AVXVNNI)
1234 fs.setIf(eax1&(1<<7) != 0, CMPCCXADD)
1235 fs.setIf(eax1&(1<<10) != 0, MOVSB_ZL)
1236 fs.setIf(eax1&(1<<11) != 0, STOSB_SHORT)
1237 fs.setIf(eax1&(1<<12) != 0, CMPSB_SCADBS_SHORT)
1238 fs.setIf(eax1&(1<<22) != 0, HRESET)
1239 fs.setIf(eax1&(1<<23) != 0, AVXIFMA)
1240 fs.setIf(eax1&(1<<26) != 0, LAM)
1241
1242
1243 fs.setIf(edx1&(1<<4) != 0, AVXVNNIINT8)
1244 fs.setIf(edx1&(1<<5) != 0, AVXNECONVERT)
1245 fs.setIf(edx1&(1<<14) != 0, PREFETCHI)
1246 fs.setIf(edx1&(1<<19) != 0, AVX10)
1247 fs.setIf(edx1&(1<<21) != 0, APX_F)
1248
1249
1250 if c&((1<<26)|(1<<27)) == (1<<26)|(1<<27) {
1251
1252 eax, _ := xgetbv(0)
1253
1254
1255
1256
1257 hasAVX512 := (eax>>5)&7 == 7 && (eax>>1)&3 == 3
1258 if runtime.GOOS == "darwin" {
1259 hasAVX512 = fs.inSet(AVX) && darwinHasAVX512()
1260 }
1261 if hasAVX512 {
1262 fs.setIf(ebx&(1<<16) != 0, AVX512F)
1263 fs.setIf(ebx&(1<<17) != 0, AVX512DQ)
1264 fs.setIf(ebx&(1<<21) != 0, AVX512IFMA)
1265 fs.setIf(ebx&(1<<26) != 0, AVX512PF)
1266 fs.setIf(ebx&(1<<27) != 0, AVX512ER)
1267 fs.setIf(ebx&(1<<28) != 0, AVX512CD)
1268 fs.setIf(ebx&(1<<30) != 0, AVX512BW)
1269 fs.setIf(ebx&(1<<31) != 0, AVX512VL)
1270
1271 fs.setIf(ecx&(1<<1) != 0, AVX512VBMI)
1272 fs.setIf(ecx&(1<<6) != 0, AVX512VBMI2)
1273 fs.setIf(ecx&(1<<11) != 0, AVX512VNNI)
1274 fs.setIf(ecx&(1<<12) != 0, AVX512BITALG)
1275 fs.setIf(ecx&(1<<14) != 0, AVX512VPOPCNTDQ)
1276
1277 fs.setIf(edx&(1<<8) != 0, AVX512VP2INTERSECT)
1278 fs.setIf(edx&(1<<22) != 0, AMXBF16)
1279 fs.setIf(edx&(1<<23) != 0, AVX512FP16)
1280 fs.setIf(edx&(1<<24) != 0, AMXTILE)
1281 fs.setIf(edx&(1<<25) != 0, AMXINT8)
1282
1283 fs.setIf(eax1&(1<<5) != 0, AVX512BF16)
1284 fs.setIf(eax1&(1<<19) != 0, WRMSRNS)
1285 fs.setIf(eax1&(1<<21) != 0, AMXFP16)
1286 fs.setIf(eax1&(1<<27) != 0, MSRLIST)
1287 }
1288 }
1289
1290
1291 _, _, _, edx = cpuidex(7, 2)
1292 fs.setIf(edx&(1<<0) != 0, PSFD)
1293 fs.setIf(edx&(1<<1) != 0, IDPRED_CTRL)
1294 fs.setIf(edx&(1<<2) != 0, RRSBA_CTRL)
1295 fs.setIf(edx&(1<<4) != 0, BHI_CTRL)
1296 fs.setIf(edx&(1<<5) != 0, MCDT_NO)
1297
1298
1299 if fs.inSet(KEYLOCKER) && mfi >= 0x19 {
1300 _, ebx, _, _ := cpuidex(0x19, 0)
1301 fs.setIf(ebx&5 == 5, KEYLOCKERW)
1302 }
1303
1304
1305 if fs.inSet(AVX10) && mfi >= 0x24 {
1306 _, ebx, _, _ := cpuidex(0x24, 0)
1307 fs.setIf(ebx&(1<<16) != 0, AVX10_128)
1308 fs.setIf(ebx&(1<<17) != 0, AVX10_256)
1309 fs.setIf(ebx&(1<<18) != 0, AVX10_512)
1310 }
1311 }
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326 if mfi >= 0xd {
1327 if fs.inSet(XSAVE) {
1328 eax, _, _, _ := cpuidex(0xd, 1)
1329 fs.setIf(eax&(1<<0) != 0, XSAVEOPT)
1330 fs.setIf(eax&(1<<1) != 0, XSAVEC)
1331 fs.setIf(eax&(1<<2) != 0, XGETBV1)
1332 fs.setIf(eax&(1<<3) != 0, XSAVES)
1333 }
1334 }
1335 if maxExtendedFunction() >= 0x80000001 {
1336 _, _, c, d := cpuid(0x80000001)
1337 if (c & (1 << 5)) != 0 {
1338 fs.set(LZCNT)
1339 fs.set(POPCNT)
1340 }
1341
1342 fs.setIf((c&(1<<0)) != 0, LAHF)
1343 fs.setIf((c&(1<<2)) != 0, SVM)
1344 fs.setIf((c&(1<<6)) != 0, SSE4A)
1345 fs.setIf((c&(1<<10)) != 0, IBS)
1346 fs.setIf((c&(1<<22)) != 0, TOPEXT)
1347
1348
1349 fs.setIf(d&(1<<11) != 0, SYSCALL)
1350 fs.setIf(d&(1<<20) != 0, NX)
1351 fs.setIf(d&(1<<22) != 0, MMXEXT)
1352 fs.setIf(d&(1<<23) != 0, MMX)
1353 fs.setIf(d&(1<<24) != 0, FXSR)
1354 fs.setIf(d&(1<<25) != 0, FXSROPT)
1355 fs.setIf(d&(1<<27) != 0, RDTSCP)
1356 fs.setIf(d&(1<<30) != 0, AMD3DNOWEXT)
1357 fs.setIf(d&(1<<31) != 0, AMD3DNOW)
1358
1359
1361 if fs.inSet(AVX) {
1362 fs.setIf((c&(1<<11)) != 0, XOP)
1363 fs.setIf((c&(1<<16)) != 0, FMA4)
1364 }
1365
1366 }
1367 if maxExtendedFunction() >= 0x80000007 {
1368 _, b, _, d := cpuid(0x80000007)
1369 fs.setIf((b&(1<<0)) != 0, MCAOVERFLOW)
1370 fs.setIf((b&(1<<1)) != 0, SUCCOR)
1371 fs.setIf((b&(1<<2)) != 0, HWA)
1372 fs.setIf((d&(1<<9)) != 0, CPBOOST)
1373 }
1374
1375 if maxExtendedFunction() >= 0x80000008 {
1376 _, b, _, _ := cpuid(0x80000008)
1377 fs.setIf(b&(1<<28) != 0, PSFD)
1378 fs.setIf(b&(1<<27) != 0, CPPC)
1379 fs.setIf(b&(1<<24) != 0, SPEC_CTRL_SSBD)
1380 fs.setIf(b&(1<<23) != 0, PPIN)
1381 fs.setIf(b&(1<<21) != 0, TLB_FLUSH_NESTED)
1382 fs.setIf(b&(1<<20) != 0, EFER_LMSLE_UNS)
1383 fs.setIf(b&(1<<19) != 0, IBRS_PROVIDES_SMP)
1384 fs.setIf(b&(1<<18) != 0, IBRS_PREFERRED)
1385 fs.setIf(b&(1<<17) != 0, STIBP_ALWAYSON)
1386 fs.setIf(b&(1<<15) != 0, STIBP)
1387 fs.setIf(b&(1<<14) != 0, IBRS)
1388 fs.setIf((b&(1<<13)) != 0, INT_WBINVD)
1389 fs.setIf(b&(1<<12) != 0, IBPB)
1390 fs.setIf((b&(1<<9)) != 0, WBNOINVD)
1391 fs.setIf((b&(1<<8)) != 0, MCOMMIT)
1392 fs.setIf((b&(1<<4)) != 0, RDPRU)
1393 fs.setIf((b&(1<<3)) != 0, INVLPGB)
1394 fs.setIf((b&(1<<1)) != 0, MSRIRC)
1395 fs.setIf((b&(1<<0)) != 0, CLZERO)
1396 }
1397
1398 if fs.inSet(SVM) && maxExtendedFunction() >= 0x8000000A {
1399 _, _, _, edx := cpuid(0x8000000A)
1400 fs.setIf((edx>>0)&1 == 1, SVMNP)
1401 fs.setIf((edx>>1)&1 == 1, LBRVIRT)
1402 fs.setIf((edx>>2)&1 == 1, SVML)
1403 fs.setIf((edx>>3)&1 == 1, NRIPS)
1404 fs.setIf((edx>>4)&1 == 1, TSCRATEMSR)
1405 fs.setIf((edx>>5)&1 == 1, VMCBCLEAN)
1406 fs.setIf((edx>>6)&1 == 1, SVMFBASID)
1407 fs.setIf((edx>>7)&1 == 1, SVMDA)
1408 fs.setIf((edx>>10)&1 == 1, SVMPF)
1409 fs.setIf((edx>>12)&1 == 1, SVMPFT)
1410 }
1411
1412 if maxExtendedFunction() >= 0x8000001a {
1413 eax, _, _, _ := cpuid(0x8000001a)
1414 fs.setIf((eax>>0)&1 == 1, FP128)
1415 fs.setIf((eax>>1)&1 == 1, MOVU)
1416 fs.setIf((eax>>2)&1 == 1, FP256)
1417 }
1418
1419 if maxExtendedFunction() >= 0x8000001b && fs.inSet(IBS) {
1420 eax, _, _, _ := cpuid(0x8000001b)
1421 fs.setIf((eax>>0)&1 == 1, IBSFFV)
1422 fs.setIf((eax>>1)&1 == 1, IBSFETCHSAM)
1423 fs.setIf((eax>>2)&1 == 1, IBSOPSAM)
1424 fs.setIf((eax>>3)&1 == 1, IBSRDWROPCNT)
1425 fs.setIf((eax>>4)&1 == 1, IBSOPCNT)
1426 fs.setIf((eax>>5)&1 == 1, IBSBRNTRGT)
1427 fs.setIf((eax>>6)&1 == 1, IBSOPCNTEXT)
1428 fs.setIf((eax>>7)&1 == 1, IBSRIPINVALIDCHK)
1429 fs.setIf((eax>>8)&1 == 1, IBS_OPFUSE)
1430 fs.setIf((eax>>9)&1 == 1, IBS_FETCH_CTLX)
1431 fs.setIf((eax>>10)&1 == 1, IBS_OPDATA4)
1432 fs.setIf((eax>>11)&1 == 1, IBS_ZEN4)
1433 }
1434
1435 if maxExtendedFunction() >= 0x8000001f && vend == AMD {
1436 a, _, _, _ := cpuid(0x8000001f)
1437 fs.setIf((a>>0)&1 == 1, SME)
1438 fs.setIf((a>>1)&1 == 1, SEV)
1439 fs.setIf((a>>2)&1 == 1, MSR_PAGEFLUSH)
1440 fs.setIf((a>>3)&1 == 1, SEV_ES)
1441 fs.setIf((a>>4)&1 == 1, SEV_SNP)
1442 fs.setIf((a>>5)&1 == 1, VMPL)
1443 fs.setIf((a>>10)&1 == 1, SME_COHERENT)
1444 fs.setIf((a>>11)&1 == 1, SEV_64BIT)
1445 fs.setIf((a>>12)&1 == 1, SEV_RESTRICTED)
1446 fs.setIf((a>>13)&1 == 1, SEV_ALTERNATIVE)
1447 fs.setIf((a>>14)&1 == 1, SEV_DEBUGSWAP)
1448 fs.setIf((a>>15)&1 == 1, IBS_PREVENTHOST)
1449 fs.setIf((a>>16)&1 == 1, VTE)
1450 fs.setIf((a>>24)&1 == 1, VMSA_REGPROT)
1451 }
1452
1453 if maxExtendedFunction() >= 0x80000021 && vend == AMD {
1454 a, _, _, _ := cpuid(0x80000021)
1455 fs.setIf((a>>31)&1 == 1, SRSO_MSR_FIX)
1456 fs.setIf((a>>30)&1 == 1, SRSO_USER_KERNEL_NO)
1457 fs.setIf((a>>29)&1 == 1, SRSO_NO)
1458 fs.setIf((a>>28)&1 == 1, IBPB_BRTYPE)
1459 fs.setIf((a>>27)&1 == 1, SBPB)
1460 }
1461
1462 if mfi >= 0x20 {
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472 _, ebx, _, _ := cpuid(0x4000000C)
1473 fs.setIf(ebx == 0xbe3, TDX_GUEST)
1474 }
1475
1476 if mfi >= 0x21 {
1477
1478 _, ebx, ecx, edx := cpuid(0x21)
1479 identity := string(valAsString(ebx, edx, ecx))
1480 fs.setIf(identity == "IntelTDX ", TDX_GUEST)
1481 }
1482
1483 return fs
1484 }
1485
1486 func (c *CPUInfo) supportAVX10() uint8 {
1487 if c.maxFunc >= 0x24 && c.featureSet.inSet(AVX10) {
1488 _, ebx, _, _ := cpuidex(0x24, 0)
1489 return uint8(ebx)
1490 }
1491 return 0
1492 }
1493
1494 func valAsString(values ...uint32) []byte {
1495 r := make([]byte, 4*len(values))
1496 for i, v := range values {
1497 dst := r[i*4:]
1498 dst[0] = byte(v & 0xff)
1499 dst[1] = byte((v >> 8) & 0xff)
1500 dst[2] = byte((v >> 16) & 0xff)
1501 dst[3] = byte((v >> 24) & 0xff)
1502 switch {
1503 case dst[0] == 0:
1504 return r[:i*4]
1505 case dst[1] == 0:
1506 return r[:i*4+1]
1507 case dst[2] == 0:
1508 return r[:i*4+2]
1509 case dst[3] == 0:
1510 return r[:i*4+3]
1511 }
1512 }
1513 return r
1514 }
1515
View as plain text