internal/cpu, runtime: add S/390 CPU capability support

Patch by Robin Dapp.

Updates https://gcc.gnu.org/PR89123

Change-Id: I4d1583747111b7446eb63aade3612df669ac1ed1
Reviewed-on: https://go-review.googlesource.com/c/162887
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/libgo/go/internal/cpu/cpu_gccgo.c b/libgo/go/internal/cpu/cpu_gccgo.c
index f9ebd8b..6908d34 100644
--- a/libgo/go/internal/cpu/cpu_gccgo.c
+++ b/libgo/go/internal/cpu/cpu_gccgo.c
@@ -70,3 +70,118 @@
 #pragma GCC pop_options
 
 #endif /* defined(__i386__) || defined(__x86_64__)  */
+
+#ifdef __s390__
+
+struct facilityList {
+	uint64_t bits[4];
+};
+
+struct queryResult {
+	uint64_t bits[2];
+};
+
+struct facilityList stfle(void)
+  __asm__(GOSYM_PREFIX "internal..z2fcpu.stfle")
+  __attribute__((no_split_stack));
+
+struct facilityList stfle(void) {
+    struct facilityList ret;
+    __asm__ ("la    %%r1, %[ret]\t\n"
+	     "lghi  %%r0, 3\t\n" // last doubleword index to store
+	     "xc    0(32,%%r1), 0(%%r1)\t\n" // clear 4 doublewords (32 bytes)
+	     ".long 0xb2b01000\t\n"  // store facility list extended (STFLE)
+	     :[ret] "=Q" (ret) : : "r0", "r1", "cc");
+    return ret;
+}
+
+struct queryResult kmQuery(void)
+  __asm__(GOSYM_PREFIX "internal..z2fcpu.kmQuery")
+  __attribute__((no_split_stack));
+
+struct queryResult kmQuery() {
+    struct queryResult ret;
+
+    __asm__ ("lghi   %%r0, 0\t\n" // set function code to 0 (KM-Query)
+	     "la     %%r1, %[ret]\t\n"
+	     ".long  0xb92e0024\t\n" // cipher message (KM)
+	     :[ret] "=Q" (ret) : : "r0", "r1", "cc");
+    return ret;
+}
+
+struct queryResult kmcQuery(void)
+  __asm__(GOSYM_PREFIX "internal..z2fcpu.kmcQuery")
+  __attribute__((no_split_stack));
+
+struct queryResult kmcQuery() {
+    struct queryResult ret;
+
+    __asm__ ("lghi   %%r0, 0\t\n" // set function code to 0 (KMC-Query)
+	     "la     %%r1, %[ret]\t\n"
+	     ".long  0xb92f0024\t\n"  // cipher message with chaining (KMC)
+	     :[ret] "=Q" (ret) : : "r0", "r1", "cc");
+
+    return ret;
+}
+
+struct queryResult kmctrQuery(void)
+  __asm__(GOSYM_PREFIX "internal..z2fcpu.kmctrQuery")
+  __attribute__((no_split_stack));
+
+struct queryResult kmctrQuery() {
+    struct queryResult ret;
+
+    __asm__ ("lghi   %%r0, 0\t\n" // set function code to 0 (KMCTR-Query)
+	     "la     %%r1, %[ret]\t\n"
+	     ".long  0xb92d4024\t\n" // cipher message with counter (KMCTR)
+	     :[ret] "=Q" (ret) : : "r0", "r1", "cc");
+
+    return ret;
+}
+
+struct queryResult kmaQuery(void)
+  __asm__(GOSYM_PREFIX "internal..z2fcpu.kmaQuery")
+  __attribute__((no_split_stack));
+
+struct queryResult kmaQuery() {
+    struct queryResult ret;
+
+    __asm__ ("lghi   %%r0, 0\t\n" // set function code to 0 (KMA-Query)
+	     "la     %%r1, %[ret]\t\n"
+	     ".long  0xb9296024\t\n" // cipher message with authentication (KMA)
+	     :[ret] "=Q" (ret) : : "r0", "r1", "cc");
+
+    return ret;
+}
+
+struct queryResult kimdQuery(void)
+  __asm__(GOSYM_PREFIX "internal..z2fcpu.kimdQuery")
+  __attribute__((no_split_stack));
+
+struct queryResult kimdQuery() {
+    struct queryResult ret;
+
+    __asm__ ("lghi   %%r0, 0\t\n"  // set function code to 0 (KIMD-Query)
+	     "la     %%r1, %[ret]\t\n"
+	     ".long  0xb93e0024\t\n"  // compute intermediate message digest (KIMD)
+	     :[ret] "=Q" (ret) : : "r0", "r1", "cc");
+
+    return ret;
+}
+
+struct queryResult klmdQuery(void)
+  __asm__(GOSYM_PREFIX "internal..z2fcpu.klmdQuery")
+  __attribute__((no_split_stack));
+
+struct queryResult klmdQuery() {
+    struct queryResult ret;
+
+    __asm__ ("lghi   %%r0, 0\t\n"  // set function code to 0 (KLMD-Query)
+	     "la     %%r1, %[ret]\t\n"
+	     ".long  0xb93f0024\t\n"  // compute last message digest (KLMD)
+	     :[ret] "=Q" (ret) : : "r0", "r1", "cc");
+
+    return ret;
+}
+
+#endif /* defined(__s390__)  */
diff --git a/libgo/go/internal/cpu/cpu_s390x.go b/libgo/go/internal/cpu/cpu_s390x.go
index 81d1afa..22d9aef 100644
--- a/libgo/go/internal/cpu/cpu_s390x.go
+++ b/libgo/go/internal/cpu/cpu_s390x.go
@@ -98,13 +98,13 @@
 
 // The following feature detection functions are defined in cpu_s390x.s.
 // They are likely to be expensive to call so the results should be cached.
-func stfle() facilityList     { panic("not implemented for gccgo") }
-func kmQuery() queryResult    { panic("not implemented for gccgo") }
-func kmcQuery() queryResult   { panic("not implemented for gccgo") }
-func kmctrQuery() queryResult { panic("not implemented for gccgo") }
-func kmaQuery() queryResult   { panic("not implemented for gccgo") }
-func kimdQuery() queryResult  { panic("not implemented for gccgo") }
-func klmdQuery() queryResult  { panic("not implemented for gccgo") }
+func stfle() facilityList
+func kmQuery() queryResult
+func kmcQuery() queryResult
+func kmctrQuery() queryResult
+func kmaQuery() queryResult
+func kimdQuery() queryResult
+func klmdQuery() queryResult
 
 func doinit() {
 	options = []option{
@@ -122,14 +122,6 @@
 	aes := []function{aes128, aes192, aes256}
 	facilities := stfle()
 
-	S390X.HasZArch = facilities.Has(zarch)
-	S390X.HasSTFLE = facilities.Has(stflef)
-	S390X.HasLDisp = facilities.Has(ldisp)
-	S390X.HasEImm = facilities.Has(eimm)
-	S390X.HasDFP = facilities.Has(dfp)
-	S390X.HasETF3Enhanced = facilities.Has(etf3eh)
-	S390X.HasMSA = facilities.Has(msa)
-
 	if S390X.HasMSA {
 		// cipher message
 		km, kmc := kmQuery(), kmcQuery()
diff --git a/libgo/go/runtime/os_linux_s390x.go b/libgo/go/runtime/os_linux_s390x.go
index 55d35c7..7640273 100644
--- a/libgo/go/runtime/os_linux_s390x.go
+++ b/libgo/go/runtime/os_linux_s390x.go
@@ -8,12 +8,26 @@
 
 const (
 	// bit masks taken from bits/hwcap.h
-	_HWCAP_S390_VX = 2048 // vector facility
+	_HWCAP_S390_ZARCH  = 2
+	_HWCAP_S390_STFLE  = 4
+	_HWCAP_S390_MSA    = 8
+	_HWCAP_S390_LDISP  = 16
+	_HWCAP_S390_EIMM   = 32
+	_HWCAP_S390_DFP    = 64
+	_HWCAP_S390_ETF3EH = 256
+	_HWCAP_S390_VX     = 2048 // vector facility
 )
 
 func archauxv(tag, val uintptr) {
 	switch tag {
 	case _AT_HWCAP: // CPU capability bit flags
+		cpu.S390X.HasZArch = val&_HWCAP_S390_ZARCH != 0
+		cpu.S390X.HasSTFLE = val&_HWCAP_S390_STFLE != 0
+		cpu.S390X.HasMSA = val&_HWCAP_S390_MSA != 0
+		cpu.S390X.HasLDisp = val&_HWCAP_S390_LDISP != 0
+		cpu.S390X.HasEImm = val&_HWCAP_S390_EIMM != 0
+		cpu.S390X.HasDFP = val&_HWCAP_S390_DFP != 0
+		cpu.S390X.HasETF3Enhanced = val&_HWCAP_S390_ETF3EH != 0
 		cpu.S390X.HasVX = val&_HWCAP_S390_VX != 0
 	}
 }