cpu: add support for zos/s390x

This adds feature detection that works on the s390x running zos.
There are some differences with Linux (no hwcap, facilities vector available in-memory).
These changes should not affect other platforms.

Fixes golang/go#41984.

Change-Id: Ieabbfafd07367a346a4d279dde0f00aa3fc4321b
Reviewed-on: https://go-review.googlesource.com/c/sys/+/262477
Run-TryBot: Emmanuel Odeke <emmanuel@orijtech.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Tobias Klauser <tobias.klauser@gmail.com>
Reviewed-by: Martin Möhrmann <moehrmann@google.com>
Trust: Martin Möhrmann <moehrmann@google.com>
Trust: Emmanuel Odeke <emmanuel@orijtech.com>
diff --git a/cpu/cpu_linux_s390x.go b/cpu/cpu_linux_s390x.go
index b88d6b8..1517ac6 100644
--- a/cpu/cpu_linux_s390x.go
+++ b/cpu/cpu_linux_s390x.go
@@ -17,86 +17,7 @@
 	hwcap_VXE    = 8192
 )
 
-// bitIsSet reports whether the bit at index is set. The bit index
-// is in big endian order, so bit index 0 is the leftmost bit.
-func bitIsSet(bits []uint64, index uint) bool {
-	return bits[index/64]&((1<<63)>>(index%64)) != 0
-}
-
-// function is the code for the named cryptographic function.
-type function uint8
-
-const (
-	// KM{,A,C,CTR} function codes
-	aes128 function = 18 // AES-128
-	aes192 function = 19 // AES-192
-	aes256 function = 20 // AES-256
-
-	// K{I,L}MD function codes
-	sha1     function = 1  // SHA-1
-	sha256   function = 2  // SHA-256
-	sha512   function = 3  // SHA-512
-	sha3_224 function = 32 // SHA3-224
-	sha3_256 function = 33 // SHA3-256
-	sha3_384 function = 34 // SHA3-384
-	sha3_512 function = 35 // SHA3-512
-	shake128 function = 36 // SHAKE-128
-	shake256 function = 37 // SHAKE-256
-
-	// KLMD function codes
-	ghash function = 65 // GHASH
-)
-
-// queryResult contains the result of a Query function
-// call. Bits are numbered in big endian order so the
-// leftmost bit (the MSB) is at index 0.
-type queryResult struct {
-	bits [2]uint64
-}
-
-// Has reports whether the given functions are present.
-func (q *queryResult) Has(fns ...function) bool {
-	if len(fns) == 0 {
-		panic("no function codes provided")
-	}
-	for _, f := range fns {
-		if !bitIsSet(q.bits[:], uint(f)) {
-			return false
-		}
-	}
-	return true
-}
-
-// facility is a bit index for the named facility.
-type facility uint8
-
-const (
-	// cryptography facilities
-	msa4 facility = 77  // message-security-assist extension 4
-	msa8 facility = 146 // message-security-assist extension 8
-)
-
-// facilityList contains the result of an STFLE call.
-// Bits are numbered in big endian order so the
-// leftmost bit (the MSB) is at index 0.
-type facilityList struct {
-	bits [4]uint64
-}
-
-// Has reports whether the given facilities are present.
-func (s *facilityList) Has(fs ...facility) bool {
-	if len(fs) == 0 {
-		panic("no facility bits provided")
-	}
-	for _, f := range fs {
-		if !bitIsSet(s.bits[:], uint(f)) {
-			return false
-		}
-	}
-	return true
-}
-
-func doinit() {
+func initS390Xbase() {
 	// test HWCAP bit vector
 	has := func(featureMask uint) bool {
 		return hwCap&featureMask == featureMask
@@ -116,44 +37,4 @@
 	if S390X.HasVX {
 		S390X.HasVXE = has(hwcap_VXE)
 	}
-
-	// We need implementations of stfle, km and so on
-	// to detect cryptographic features.
-	if !haveAsmFunctions() {
-		return
-	}
-
-	// optional cryptographic functions
-	if S390X.HasMSA {
-		aes := []function{aes128, aes192, aes256}
-
-		// cipher message
-		km, kmc := kmQuery(), kmcQuery()
-		S390X.HasAES = km.Has(aes...)
-		S390X.HasAESCBC = kmc.Has(aes...)
-		if S390X.HasSTFLE {
-			facilities := stfle()
-			if facilities.Has(msa4) {
-				kmctr := kmctrQuery()
-				S390X.HasAESCTR = kmctr.Has(aes...)
-			}
-			if facilities.Has(msa8) {
-				kma := kmaQuery()
-				S390X.HasAESGCM = kma.Has(aes...)
-			}
-		}
-
-		// compute message digest
-		kimd := kimdQuery() // intermediate (no padding)
-		klmd := klmdQuery() // last (padding)
-		S390X.HasSHA1 = kimd.Has(sha1) && klmd.Has(sha1)
-		S390X.HasSHA256 = kimd.Has(sha256) && klmd.Has(sha256)
-		S390X.HasSHA512 = kimd.Has(sha512) && klmd.Has(sha512)
-		S390X.HasGHASH = kimd.Has(ghash) // KLMD-GHASH does not exist
-		sha3 := []function{
-			sha3_224, sha3_256, sha3_384, sha3_512,
-			shake128, shake256,
-		}
-		S390X.HasSHA3 = kimd.Has(sha3...) && klmd.Has(sha3...)
-	}
 }
diff --git a/cpu/cpu_s390x.go b/cpu/cpu_s390x.go
index 544cd62..5881b88 100644
--- a/cpu/cpu_s390x.go
+++ b/cpu/cpu_s390x.go
@@ -8,10 +8,10 @@
 
 func initOptions() {
 	options = []option{
-		{Name: "zarch", Feature: &S390X.HasZARCH},
-		{Name: "stfle", Feature: &S390X.HasSTFLE},
-		{Name: "ldisp", Feature: &S390X.HasLDISP},
-		{Name: "eimm", Feature: &S390X.HasEIMM},
+		{Name: "zarch", Feature: &S390X.HasZARCH, Required: true},
+		{Name: "stfle", Feature: &S390X.HasSTFLE, Required: true},
+		{Name: "ldisp", Feature: &S390X.HasLDISP, Required: true},
+		{Name: "eimm", Feature: &S390X.HasEIMM, Required: true},
 		{Name: "dfp", Feature: &S390X.HasDFP},
 		{Name: "etf3eh", Feature: &S390X.HasETF3EH},
 		{Name: "msa", Feature: &S390X.HasMSA},
@@ -28,3 +28,145 @@
 		{Name: "vxe", Feature: &S390X.HasVXE},
 	}
 }
+
+// bitIsSet reports whether the bit at index is set. The bit index
+// is in big endian order, so bit index 0 is the leftmost bit.
+func bitIsSet(bits []uint64, index uint) bool {
+	return bits[index/64]&((1<<63)>>(index%64)) != 0
+}
+
+// facility is a bit index for the named facility.
+type facility uint8
+
+const (
+	// mandatory facilities
+	zarch  facility = 1  // z architecture mode is active
+	stflef facility = 7  // store-facility-list-extended
+	ldisp  facility = 18 // long-displacement
+	eimm   facility = 21 // extended-immediate
+
+	// miscellaneous facilities
+	dfp    facility = 42 // decimal-floating-point
+	etf3eh facility = 30 // extended-translation 3 enhancement
+
+	// cryptography facilities
+	msa  facility = 17  // message-security-assist
+	msa3 facility = 76  // message-security-assist extension 3
+	msa4 facility = 77  // message-security-assist extension 4
+	msa5 facility = 57  // message-security-assist extension 5
+	msa8 facility = 146 // message-security-assist extension 8
+	msa9 facility = 155 // message-security-assist extension 9
+
+	// vector facilities
+	vx   facility = 129 // vector facility
+	vxe  facility = 135 // vector-enhancements 1
+	vxe2 facility = 148 // vector-enhancements 2
+)
+
+// facilityList contains the result of an STFLE call.
+// Bits are numbered in big endian order so the
+// leftmost bit (the MSB) is at index 0.
+type facilityList struct {
+	bits [4]uint64
+}
+
+// Has reports whether the given facilities are present.
+func (s *facilityList) Has(fs ...facility) bool {
+	if len(fs) == 0 {
+		panic("no facility bits provided")
+	}
+	for _, f := range fs {
+		if !bitIsSet(s.bits[:], uint(f)) {
+			return false
+		}
+	}
+	return true
+}
+
+// function is the code for the named cryptographic function.
+type function uint8
+
+const (
+	// KM{,A,C,CTR} function codes
+	aes128 function = 18 // AES-128
+	aes192 function = 19 // AES-192
+	aes256 function = 20 // AES-256
+
+	// K{I,L}MD function codes
+	sha1     function = 1  // SHA-1
+	sha256   function = 2  // SHA-256
+	sha512   function = 3  // SHA-512
+	sha3_224 function = 32 // SHA3-224
+	sha3_256 function = 33 // SHA3-256
+	sha3_384 function = 34 // SHA3-384
+	sha3_512 function = 35 // SHA3-512
+	shake128 function = 36 // SHAKE-128
+	shake256 function = 37 // SHAKE-256
+
+	// KLMD function codes
+	ghash function = 65 // GHASH
+)
+
+// queryResult contains the result of a Query function
+// call. Bits are numbered in big endian order so the
+// leftmost bit (the MSB) is at index 0.
+type queryResult struct {
+	bits [2]uint64
+}
+
+// Has reports whether the given functions are present.
+func (q *queryResult) Has(fns ...function) bool {
+	if len(fns) == 0 {
+		panic("no function codes provided")
+	}
+	for _, f := range fns {
+		if !bitIsSet(q.bits[:], uint(f)) {
+			return false
+		}
+	}
+	return true
+}
+
+func doinit() {
+	initS390Xbase()
+
+	// We need implementations of stfle, km and so on
+	// to detect cryptographic features.
+	if !haveAsmFunctions() {
+		return
+	}
+
+	// optional cryptographic functions
+	if S390X.HasMSA {
+		aes := []function{aes128, aes192, aes256}
+
+		// cipher message
+		km, kmc := kmQuery(), kmcQuery()
+		S390X.HasAES = km.Has(aes...)
+		S390X.HasAESCBC = kmc.Has(aes...)
+		if S390X.HasSTFLE {
+			facilities := stfle()
+			if facilities.Has(msa4) {
+				kmctr := kmctrQuery()
+				S390X.HasAESCTR = kmctr.Has(aes...)
+			}
+			if facilities.Has(msa8) {
+				kma := kmaQuery()
+				S390X.HasAESGCM = kma.Has(aes...)
+			}
+		}
+
+		// compute message digest
+		kimd := kimdQuery() // intermediate (no padding)
+		klmd := klmdQuery() // last (padding)
+		S390X.HasSHA1 = kimd.Has(sha1) && klmd.Has(sha1)
+		S390X.HasSHA256 = kimd.Has(sha256) && klmd.Has(sha256)
+		S390X.HasSHA512 = kimd.Has(sha512) && klmd.Has(sha512)
+		S390X.HasGHASH = kimd.Has(ghash) // KLMD-GHASH does not exist
+		sha3 := []function{
+			sha3_224, sha3_256, sha3_384, sha3_512,
+			shake128, shake256,
+		}
+		S390X.HasSHA3 = kimd.Has(sha3...) && klmd.Has(sha3...)
+	}
+}
diff --git a/cpu/cpu_s390x_test.go b/cpu/cpu_s390x_test.go
new file mode 100644
index 0000000..9bee163
--- /dev/null
+++ b/cpu/cpu_s390x_test.go
@@ -0,0 +1,75 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cpu_test
+
+import (
+	"runtime"
+	"testing"
+	"unsafe"
+
+	"golang.org/x/sys/cpu"
+)
+
+var s390xTests = []struct {
+	name      string
+	feature   bool
+	facility  uint
+	mandatory bool
+}{
+	{"ZARCH", cpu.S390X.HasZARCH, 1, true},
+	{"STFLE", cpu.S390X.HasSTFLE, 7, true},
+	{"LDISP", cpu.S390X.HasLDISP, 18, true},
+	{"EIMM", cpu.S390X.HasEIMM, 21, true},
+	{"DFP", cpu.S390X.HasDFP, 42, false},
+	{"MSA", cpu.S390X.HasMSA, 17, false},
+	{"VX", cpu.S390X.HasVX, 129, false},
+	{"VXE", cpu.S390X.HasVXE, 135, false},
+}
+
+// bitIsSet reports whether the bit at index is set. The bit index
+// is in big endian order, so bit index 0 is the leftmost bit.
+func bitIsSet(bits [4]uint64, i uint) bool {
+	return bits[i/64]&((1<<63)>>(i%64)) != 0
+}
+
+// facilityList contains the contents of location 200 on zos.
+// Bits are numbered in big endian order so the
+// leftmost bit (the MSB) is at index 0.
+type facilityList struct {
+	bits [4]uint64
+}
+
+func TestS390XVectorFacilityFeatures(t *testing.T) {
+	// vector-enhancements require vector facility to be enabled
+	if cpu.S390X.HasVXE && !cpu.S390X.HasVX {
+		t.Error("HasVX expected true, got false (VXE is true)")
+	}
+}
+
+func TestS390XMandatoryFeatures(t *testing.T) {
+	for _, tc := range s390xTests {
+		if tc.mandatory && !tc.feature {
+			t.Errorf("Feature %s is mandatory but is not present", tc.name)
+		}
+	}
+}
+
+func TestS390XFeatures(t *testing.T) {
+	if runtime.GOOS != "zos" {
+		return
+	}
+	// Read available facilities from address 200.
+	facilitiesAddress := uintptr(200)
+	var facilities facilityList
+	for i := 0; i < 4; i++ {
+		facilities.bits[i] = *(*uint64)(unsafe.Pointer(facilitiesAddress + uintptr(8*i)))
+	}
+
+	for _, tc := range s390xTests {
+		if want := bitIsSet(facilities.bits, tc.facility); want != tc.feature {
+			t.Errorf("Feature %s expected %v, got %v", tc.name, want, tc.feature)
+		}
+	}
+}
diff --git a/cpu/cpu_test.go b/cpu/cpu_test.go
index 4f6c979..5f7f843 100644
--- a/cpu/cpu_test.go
+++ b/cpu/cpu_test.go
@@ -74,20 +74,3 @@
 		}
 	}
 }
-
-func TestS390X(t *testing.T) {
-	if runtime.GOARCH != "s390x" {
-		return
-	}
-	if testing.Verbose() {
-		t.Logf("%+v\n", cpu.S390X)
-	}
-	// z/Architecture is mandatory
-	if !cpu.S390X.HasZARCH {
-		t.Error("HasZARCH expected true, got false")
-	}
-	// vector-enhancements require vector facility to be enabled
-	if cpu.S390X.HasVXE && !cpu.S390X.HasVX {
-		t.Error("HasVX expected true, got false (VXE is true)")
-	}
-}
diff --git a/cpu/cpu_zos.go b/cpu/cpu_zos.go
new file mode 100644
index 0000000..5f54683
--- /dev/null
+++ b/cpu/cpu_zos.go
@@ -0,0 +1,10 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cpu
+
+func archInit() {
+	doinit()
+	Initialized = true
+}
diff --git a/cpu/cpu_zos_s390x.go b/cpu/cpu_zos_s390x.go
new file mode 100644
index 0000000..ccb1b70
--- /dev/null
+++ b/cpu/cpu_zos_s390x.go
@@ -0,0 +1,25 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cpu
+
+func initS390Xbase() {
+	// get the facilities list
+	facilities := stfle()
+
+	// mandatory
+	S390X.HasZARCH = facilities.Has(zarch)
+	S390X.HasSTFLE = facilities.Has(stflef)
+	S390X.HasLDISP = facilities.Has(ldisp)
+	S390X.HasEIMM = facilities.Has(eimm)
+
+	// optional
+	S390X.HasETF3EH = facilities.Has(etf3eh)
+	S390X.HasDFP = facilities.Has(dfp)
+	S390X.HasMSA = facilities.Has(msa)
+	S390X.HasVX = facilities.Has(vx)
+	if S390X.HasVX {
+		S390X.HasVXE = facilities.Has(vxe)
+	}
+}