Add support for the s390's TROO insn. These are the valgrind bits.
Detect ETF2 enhancement facility using STFLE. Add testcases.
Patch by Divya Vyas (divyvyas@linux.vnet.ibm.com) with
modifications. Partial fix of #273114


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@12335 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/m_machine.c b/coregrind/m_machine.c
index 5df9e04..bbfaa1f 100644
--- a/coregrind/m_machine.c
+++ b/coregrind/m_machine.c
@@ -999,6 +999,7 @@
      vki_sigaction_toK_t     tmp_sigill_act;
 
      volatile Bool have_LDISP, have_EIMM, have_GIE, have_DFP, have_FGX;
+     volatile Bool have_STFLE, have_ETF2;
      Int r, model;
 
      /* Unblock SIGILL and stash away the old action for that signal */
@@ -1066,6 +1067,24 @@
         __asm__ __volatile__(".long 0xb3cd0000" : : : "r0");  /* lgdr r0,f0 */
      }
 
+     /* Detect presence of the ETF2-enhancement facility using the
+        STFLE insn. Note, that STFLE and ETF2 were introduced at the same
+        time, so the absence of STLFE implies the absence of ETF2. */
+     have_STFLE = True;
+     have_ETF2 = False;
+     if (VG_MINIMAL_SETJMP(env_unsup_insn)) {
+        have_STFLE = False;
+     } else {
+         ULong hoststfle[1];
+         register ULong reg0 asm("0") = 0; /* one double word available */
+
+         __asm__ __volatile__(" .insn s,0xb2b00000,%0\n"   /* stfle */
+                              : "=m" (hoststfle), "+d"(reg0)
+                              : : "cc", "memory");
+         if (hoststfle[0] & (1ULL << (63 - 24)))
+             have_ETF2 = True;
+     }
+
      /* Restore signals */
      r = VG_(sigaction)(VKI_SIGILL, &saved_sigill_act, NULL);
      vg_assert(r == 0);
@@ -1076,8 +1095,8 @@
      model = VG_(get_machine_model)();
 
      VG_(debugLog)(1, "machine", "machine %d  LDISP %d EIMM %d GIE %d DFP %d "
-                   "FGX %d\n", model, have_LDISP, have_EIMM, have_GIE,
-                   have_DFP, have_FGX);
+                   "FGX %d STFLE %d ETF2 %d\n", model, have_LDISP, have_EIMM,
+                   have_GIE, have_DFP, have_FGX, have_STFLE, have_ETF2);
 
      if (model == VEX_S390X_MODEL_INVALID) return False;
 
@@ -1092,6 +1111,7 @@
      if (have_GIE)   vai.hwcaps |= VEX_HWCAPS_S390X_GIE;
      if (have_DFP)   vai.hwcaps |= VEX_HWCAPS_S390X_DFP;
      if (have_FGX)   vai.hwcaps |= VEX_HWCAPS_S390X_FGX;
+     if (have_ETF2)  vai.hwcaps |= VEX_HWCAPS_S390X_ETF2;
 
      VG_(debugLog)(1, "machine", "hwcaps = 0x%x\n", vai.hwcaps);
 
diff --git a/none/tests/s390x/Makefile.am b/none/tests/s390x/Makefile.am
index bb0a281..2ede439 100644
--- a/none/tests/s390x/Makefile.am
+++ b/none/tests/s390x/Makefile.am
@@ -5,7 +5,7 @@
 INSN_TESTS = clc clcle cvb cvd icm lpr tcxb lam_stam xc mvst add sub mul \
              and or xor insert div srst fold_And16 flogr sub_EI add_EI \
              and_EI or_EI xor_EI insert_EI mul_GE add_GE condloadstore \
-             op_exception fgx stck stckf stcke stfle cksm mvcl clcl
+             op_exception fgx stck stckf stcke stfle cksm mvcl clcl troo
 
 check_PROGRAMS = $(INSN_TESTS) \
 		 allexec \
diff --git a/none/tests/s390x/troo.c b/none/tests/s390x/troo.c
new file mode 100644
index 0000000..925c7a7
--- /dev/null
+++ b/none/tests/s390x/troo.c
@@ -0,0 +1,126 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+
+/* Register contents after executing an TROO insn */
+typedef struct {
+  uint64_t srcaddr;
+  uint64_t len;
+  uint64_t desaddr;
+  uint64_t tabaddr;
+  uint8_t testbyte;
+  uint64_t cc;
+} troo_regs;
+
+uint8_t tran_table[20] = {
+   0xaa,0xbb,0xcc,0xdd,0xff,0xda,0xbc,0xab,0xca,0xea,0xcc,0xee
+};
+
+uint8_t src[20] = {
+   0x04,0x01,0x03,0x07,0x08,0x06,0x02,0x05,0x09
+};
+
+uint8_t des[20];
+
+troo_regs tr(uint8_t *addr, uint8_t *codepage, uint8_t *dest, uint64_t len,
+             uint8_t test)
+{
+   troo_regs regs;
+   register uint64_t test_byte asm("0") = test;
+   register uint64_t length asm("3") = len;
+   register uint64_t srcaddr asm("4") = (uint64_t)addr;
+   register uint64_t codepage2 asm("1") = (uint64_t)codepage;
+   register uint64_t desaddr asm("2") = (uint64_t)dest;
+   register uint64_t cc asm("5");
+
+   cc = 2;  /* cc result will never be 2 */
+   asm volatile(
+                " troo %1,%2\n"
+                " ipm   %0\n"
+                " srl   %0,28\n"
+                : "=d"(cc),"+&d"(desaddr)
+                : "d" (srcaddr),"d"(test_byte),"d" (codepage2),"d"(length)
+                : "memory" );
+
+   regs.srcaddr = srcaddr;
+   regs.len = length;
+   regs.desaddr = desaddr;
+   regs.tabaddr = codepage2;
+   regs.testbyte = test_byte;
+   regs.cc = cc;
+   return regs;
+}
+
+int run_test(void *srcaddr, void *tableaddr, void *desaddr, uint64_t len,
+             uint8_t testbyte)
+{
+   troo_regs regs;
+   int i;
+
+   assert(len <= sizeof src);
+
+   if ((testbyte & 0xff) != testbyte)
+      printf("testbyte should be 1 byte only\n");
+
+   regs = tr(srcaddr, tableaddr, desaddr, len, testbyte);
+
+   if ((uint64_t)tableaddr != regs.tabaddr)
+      printf("translation table address changed\n");
+   if ((uint64_t)srcaddr + (len - regs.len) != regs.srcaddr)
+      printf("source address/length not updated properly\n");
+   if ((uint64_t)desaddr + (len - regs.len) != regs.desaddr)
+      printf("destination address/length not updated properly\n");
+   if (regs.cc == 0  && regs.len != 0)
+      printf("length is not zero but cc is zero\n");
+   printf("%u bytes translated\n", (unsigned)(len - regs.len));
+   printf("the translated values are");
+   for (i = 0; i < len - regs.len; i++) {
+      printf(" %x", des[i]);
+   }
+   printf("\n");
+
+   return regs.cc;
+}
+
+int main()
+{
+   int cc;
+ 
+   assert(sizeof des >= sizeof src);
+
+   /* Test 1 : len == 0 */
+   cc = run_test(NULL, NULL, NULL, 0, 0x0);
+   if (cc != 0)
+      printf("cc not updated properly:%d", cc);
+
+   cc = run_test(&src, &tran_table, &des, 0, 0xca);
+   if (cc != 0)
+      printf("cc not updated properly:%d",cc);
+
+   /* Test 2 : len > 0, testbyte not matching */
+   cc = run_test(&src, &tran_table, &des, 5, 0xee);
+   if (cc != 0)
+      printf("cc not updated properly:%d",cc);
+
+   cc = run_test(&src, &tran_table, &des, 10, 0x00);
+   if (cc != 0)
+      printf("cc not updated properly:%d",cc);
+
+   memset((char *)&des, 0, 10);
+
+   /* Test 3 : len > 0, testbyte matching */
+   cc = run_test(&src, &tran_table, &des, 5, 0xff);  /* 1st byte matches */
+   if (cc != 1)
+      printf("cc not updated properly:%d",cc);
+
+   cc = run_test(&src, &tran_table, &des, 5, 0xbb);  /* 2nd byte matches */
+   if (cc != 1)
+      printf("cc not updated properly:%d",cc);
+
+   cc = run_test(&src, &tran_table, &des, 10, 0xea);
+   if (cc != 1)
+      printf("cc not updated properly:%d",cc);
+
+   return 0;
+}
diff --git a/none/tests/s390x/troo.stderr.exp b/none/tests/s390x/troo.stderr.exp
new file mode 100644
index 0000000..139597f
--- /dev/null
+++ b/none/tests/s390x/troo.stderr.exp
@@ -0,0 +1,2 @@
+
+
diff --git a/none/tests/s390x/troo.stdout.exp b/none/tests/s390x/troo.stdout.exp
new file mode 100644
index 0000000..bebf998
--- /dev/null
+++ b/none/tests/s390x/troo.stdout.exp
@@ -0,0 +1,14 @@
+0 bytes translated
+the translated values are
+0 bytes translated
+the translated values are
+5 bytes translated
+the translated values are ff bb dd ab ca
+10 bytes translated
+the translated values are ff bb dd ab ca bc cc da ea aa
+0 bytes translated
+the translated values are
+1 bytes translated
+the translated values are ff
+8 bytes translated
+the translated values are ff bb dd ab ca bc cc da
diff --git a/none/tests/s390x/troo.vgtest b/none/tests/s390x/troo.vgtest
new file mode 100644
index 0000000..500076e
--- /dev/null
+++ b/none/tests/s390x/troo.vgtest
@@ -0,0 +1 @@
+prog: troo