diff --git a/cpu/cpu.go b/cpu/cpu.go index 11e8fb9c..84962cf0 100644 --- a/cpu/cpu.go +++ b/cpu/cpu.go @@ -68,3 +68,20 @@ var ARM64 struct { HasASIMDFHM bool // Advanced SIMD multiplication FP16 to FP32 _ CacheLinePad } + +// PPC64 contains the supported CPU features of the current ppc64/ppc64le platforms. +// If the current platform is not ppc64/ppc64le then all feature flags are false. +// +// For ppc64/ppc64le, it is safe to check only for ISA level starting on ISA v3.00, +// since there are no optional categories. There are some exceptions that also +// require kernel support to work (DARN, SCV), so there are feature bits for +// those as well. The minimum processor requirement is POWER8 (ISA 2.07). +// The struct is padded to avoid false sharing. +var PPC64 struct { + _ CacheLinePad + HasDARN bool // Hardware random number generator (requires kernel enablement) + HasSCV bool // Syscall vectored (requires kernel enablement) + IsPOWER8 bool // ISA v2.07 (POWER8) + IsPOWER9 bool // ISA v3.00 (POWER9) + _ CacheLinePad +} diff --git a/cpu/cpu_linux.go b/cpu/cpu_linux.go index 126bb684..a8452e09 100644 --- a/cpu/cpu_linux.go +++ b/cpu/cpu_linux.go @@ -9,6 +9,7 @@ package cpu import ( "encoding/binary" "io/ioutil" + "runtime" ) const ( @@ -41,8 +42,13 @@ func init() { tag = uint(binary.LittleEndian.Uint32(buf[i:])) val = uint(binary.LittleEndian.Uint32(buf[i+pb:])) case 64: - tag = uint(binary.LittleEndian.Uint64(buf[i:])) - val = uint(binary.LittleEndian.Uint64(buf[i+pb:])) + if runtime.GOARCH == "ppc64" { + tag = uint(binary.BigEndian.Uint64(buf[i:])) + val = uint(binary.BigEndian.Uint64(buf[i+pb:])) + } else { + tag = uint(binary.LittleEndian.Uint64(buf[i:])) + val = uint(binary.LittleEndian.Uint64(buf[i+pb:])) + } } switch tag { case _AT_HWCAP: diff --git a/cpu/cpu_ppc64x.go b/cpu/cpu_ppc64x.go index 70c959bd..ed975de6 100644 --- a/cpu/cpu_ppc64x.go +++ b/cpu/cpu_ppc64x.go @@ -8,4 +8,25 @@ package cpu const cacheLineSize = 128 -func doinit() {} +// HWCAP/HWCAP2 bits. These are exposed by the kernel. +const ( + // ISA Level + _PPC_FEATURE2_ARCH_2_07 = 0x80000000 + _PPC_FEATURE2_ARCH_3_00 = 0x00800000 + + // CPU features + _PPC_FEATURE2_DARN = 0x00200000 + _PPC_FEATURE2_SCV = 0x00100000 +) + +func doinit() { + // HWCAP2 feature bits + PPC64.IsPOWER8 = isSet(HWCap2, _PPC_FEATURE2_ARCH_2_07) + PPC64.IsPOWER9 = isSet(HWCap2, _PPC_FEATURE2_ARCH_3_00) + PPC64.HasDARN = isSet(HWCap2, _PPC_FEATURE2_DARN) + PPC64.HasSCV = isSet(HWCap2, _PPC_FEATURE2_SCV) +} + +func isSet(hwc uint, value uint) bool { + return hwc&value != 0 +} diff --git a/cpu/cpu_test.go b/cpu/cpu_test.go index 0c8cc83b..e4b0f0d1 100644 --- a/cpu/cpu_test.go +++ b/cpu/cpu_test.go @@ -38,3 +38,17 @@ func TestARM64minimalFeatures(t *testing.T) { t.Fatal("HasFP expected true, got false") } } + +// On ppc64x, the ISA bit for POWER8 should always be set on POWER8 and beyond. +func TestPPC64minimalFeatures(t *testing.T) { + // Do not run this with gccgo on ppc64, as it doesn't have POWER8 as a minimum + // requirement. + if runtime.Compiler == "gccgo" && runtime.GOARCH == "ppc64" { + t.Skip("gccgo does not require POWER8 on ppc64; skipping") + } + if runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" { + if !cpu.PPC64.IsPOWER8 { + t.Fatal("IsPOWER8 expected true, got false") + } + } +}