From 708e7fb298acb681b049dc66adab734a0a78a528 Mon Sep 17 00:00:00 2001 From: Bill O'Farrell Date: Wed, 14 Oct 2020 19:41:11 -0400 Subject: [PATCH] cpu: add support for zos/s390x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 TryBot-Result: Go Bot Reviewed-by: Tobias Klauser Reviewed-by: Martin Möhrmann Trust: Martin Möhrmann Trust: Emmanuel Odeke --- cpu/cpu_linux_s390x.go | 121 +-------------------------------- cpu/cpu_s390x.go | 150 +++++++++++++++++++++++++++++++++++++++-- cpu/cpu_s390x_test.go | 75 +++++++++++++++++++++ cpu/cpu_test.go | 17 ----- cpu/cpu_zos.go | 10 +++ cpu/cpu_zos_s390x.go | 25 +++++++ 6 files changed, 257 insertions(+), 141 deletions(-) create mode 100644 cpu/cpu_s390x_test.go create mode 100644 cpu/cpu_zos.go create mode 100644 cpu/cpu_zos_s390x.go diff --git a/cpu/cpu_linux_s390x.go b/cpu/cpu_linux_s390x.go index b88d6b8f..1517ac61 100644 --- a/cpu/cpu_linux_s390x.go +++ b/cpu/cpu_linux_s390x.go @@ -17,86 +17,7 @@ const ( 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 @@ func doinit() { 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 544cd621..5881b883 100644 --- a/cpu/cpu_s390x.go +++ b/cpu/cpu_s390x.go @@ -8,10 +8,10 @@ const cacheLineSize = 256 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 @@ func initOptions() { {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 00000000..9bee1633 --- /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 4f6c9797..5f7f843f 100644 --- a/cpu/cpu_test.go +++ b/cpu/cpu_test.go @@ -74,20 +74,3 @@ func TestPPC64minimalFeatures(t *testing.T) { } } } - -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 00000000..5f54683a --- /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 00000000..ccb1b708 --- /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) + } +}