]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Add extension concept and exploit it for s390x `PRNO'
authorAndreas Arnez <arnez@linux.ibm.com>
Thu, 18 Apr 2024 11:49:52 +0000 (13:49 +0200)
committerAndreas Arnez <arnez@linux.ibm.com>
Thu, 18 Apr 2024 11:49:52 +0000 (13:49 +0200)
z/Architecture specifies various "complex" machine instructions whose
register and memory effects cannot be expressed with Valgrind's current
mechanisms.

One example is the PPNO instruction -- "perform pseudorandom number
operation", whose main purpose is to generate a chunk of pseudorandom
data.  When doing so, it reads and writes a so-called "parameter block"
while also writing the variable-sized pseudorandom data itself.  These
memory effects cannot be adequately represented by the IRDirty concept.

Instead of using dirty helpers, a different approach is to treat such
instructions similar to syscalls, adding a new IRJumpKind for this
purpose.

This patch provides an implementation of this approach and an illustration
of its use at the example of PPNO (or its newer instantiation "PRNO").

15 files changed:
.gitignore
VEX/priv/guest_s390_toIR.c
VEX/priv/host_s390_defs.c
VEX/priv/host_s390_isel.c
VEX/priv/ir_defs.c
VEX/pub/libvex_ir.h
VEX/pub/libvex_s390x_common.h
VEX/pub/libvex_trc_values.h
coregrind/Makefile.am
coregrind/m_extension/extension-main.c [new file with mode: 0644]
coregrind/m_extension/extension-s390x.c [new file with mode: 0644]
coregrind/m_extension/priv_extension.h [new file with mode: 0644]
coregrind/m_extension/priv_types_n_macros.h [new file with mode: 0644]
coregrind/m_scheduler/scheduler.c
coregrind/pub_core_extension.h [new file with mode: 0644]

index b242eb6e2922cc97d135c9775dc7fbd247560f39..8eadfd76ca59e9ab96c66de11d2d0d48458fab71 100644 (file)
 /coregrind/m_dispatch/Makefile
 /coregrind/m_dispatch/Makefile.in
 
+# /coregrind/m_extension/
+/coregrind/m_extension/*.a
+/coregrind/m_extension/.deps
+/coregrind/m_extension/.dirstamp
+/coregrind/m_extension/Makefile
+/coregrind/m_extension/Makefile.in
+
 # /coregrind/m_gdbserver/
 /coregrind/m_gdbserver/.deps
 /coregrind/m_gdbserver/.dirstamp
index d65ba998c2895a34ea7be1218a34f2074db0bcf5..44a0293e2f07a04596746b67ff2db75f8e5a6a66 100644 (file)
@@ -8,7 +8,7 @@
    This file is part of Valgrind, a dynamic binary instrumentation
    framework.
 
-   Copyright IBM Corp. 2010-2021
+   Copyright IBM Corp. 2010-2024
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
@@ -552,6 +552,27 @@ system_call(IRExpr *sysno)
    dis_res->jk_StopHere = Ijk_Sys_syscall;
 }
 
+/* An extension */
+static void
+extension(ULong id, ULong variant)
+{
+   vassert(id < (1 << S390_EXT_ID_NBITS));
+   vassert(variant <= ~((ULong) 0) >> S390_EXT_ID_NBITS);
+
+   /* Store the extension ID in the pseudo register. */
+   ULong ext_id = id | (variant << S390_EXT_ID_NBITS);
+   stmt(IRStmt_Put(S390X_GUEST_OFFSET(guest_SYSNO), mkU64(ext_id)));
+
+   /* Store the current IA into guest_IP_AT_SYSCALL. */
+   stmt(IRStmt_Put(S390X_GUEST_OFFSET(guest_IP_AT_SYSCALL),
+                   mkU64(guest_IA_curr_instr)));
+
+   put_IA(mkaddr_expr(guest_IA_next_instr));
+
+   dis_res->whatNext    = Dis_StopHere;
+   dis_res->jk_StopHere = Ijk_Extension;
+}
+
 /* A side exit that branches back to the current insn if CONDITION is
    true. Does not set DisResult. */
 static void
@@ -17068,59 +17089,7 @@ s390_irgen_LCBB(UChar r1, IRTemp op2addr, UChar m3)
    return "lcbb";
 }
 
-/* Regarding the use of 
-   // Dummy helper which is used to signal VEX library that memory was loaded
-   sha512_loadparam 
-     = unsafeIRDirty_0_N(0, "s390x_dirtyhelper_PPNO_sha512_load_param_block",
-                             &s390x_dirtyhelper_PPNO_sha512_load_param_block,
-                             mkIRExprVec_0());
-
-   in the following function (s390_irgen_PPNO).  This is a workaround to get
-   around the fact that IRDirty annotations cannot indicate two memory side
-   effects, which are unfortunately necessary here.  It will possibly lead to
-   losing undefinedness (undefinedness in some inputs might not be propagated
-   to the outputs as it shouod, in Memcheck).  The correct fix would be to
-   extend IRDirty to represent two memory side effects, but that's quite a bit
-   of work.
-
-   Here's a summary of what this insn does.
-
-   // getReg(RegisterNumber n) returns the value of GPR number 'n'
-
-   // reg1 and reg2 are even
-   void ppno(RegisterNumber reg1, RegisterNumber reg2) {
-
-       switch(getReg(0)) {
-       case 0x0:
-           // Query mode, ignore reg1 and reg2
-           // Write 16 bytes                    at  getReg(1)
-           break;
-
-       case 0x3:
-           // SHA-512 generate mode, ignore reg2
-
-           // Read 240 bytes                    at  getReg(1)
-           // Write getReg(reg1 + 1) bytes      at  getReg(reg1)
-           // Write some of 240 bytes starting  at  getReg(1)
-           break;
-
-       case 0x83:
-           // SHA-512 seed mode, ignore reg1
-
-           // Read some of 240 bytes starting  at  getReg(1)
-           // Read getReg(reg2 + 1) bytes      at  getReg(reg2)
-           // Write 240 bytes                  at  getReg(1)
-           break;
-
-       default:
-           // Specification exception, abort execution.
-       }
-   }
-*/
-/* Also known as "prno"
-   If you implement new functions please don't forget to update
-   "s390x_dirtyhelper_PPNO_query" function.
- */
+/* Also known as "PRNO" */
 static const HChar *
 s390_irgen_PPNO(UChar r1, UChar r2)
 {
@@ -17129,131 +17098,10 @@ s390_irgen_PPNO(UChar r1, UChar r2)
       return "ppno";
    }
 
-   /* Theese conditions lead to specification exception */
-   vassert(r1 % 2 == 0);
-   vassert(r2 % 2 == 0);
-   vassert((r1 != 0) && (r2 != 0));
-
-   IRDirty *query, *sha512_gen, *sha512_seed, *sha512_loadparam;
-   IRTemp gpr1num = newTemp(Ity_I64);
-   IRTemp gpr2num = newTemp(Ity_I64);
-
-   IRTemp funcCode = newTemp(Ity_I8);
-   IRTemp is_query = newTemp(Ity_I1);
-   IRTemp is_sha512_gen = newTemp(Ity_I1);
-   IRTemp is_sha512_seed = newTemp(Ity_I1);
-   IRTemp is_sha512 = newTemp(Ity_I1);
-
-   assign(funcCode, unop(Iop_64to8, binop(Iop_And64, get_gpr_dw0(0),
-                                          mkU64(0xffULL))));
-   assign(gpr1num, mkU64(r1));
-   assign(gpr2num, mkU64(r2));
-
-   assign(is_query, binop(Iop_CmpEQ8, mkexpr(funcCode), mkU8(S390_PPNO_QUERY)));
-   assign(is_sha512_gen, binop(Iop_CmpEQ8, mkexpr(funcCode),
-                               mkU8(S390_PPNO_SHA512_GEN)));
-   assign(is_sha512_seed, binop(Iop_CmpEQ8, mkexpr(funcCode),
-                                mkU8(S390_PPNO_SHA512_SEED)));
-   assign(is_sha512, binop(Iop_CmpEQ8,
-                           mkU8(S390_PPNO_SHA512_GEN),
-                           binop(Iop_And8,
-                                 mkexpr(funcCode),
-                                 mkU8(S390_PPNO_SHA512_GEN)
-                                 )
-                           ));
-
-   query = unsafeIRDirty_0_N(0, "s390x_dirtyhelper_PPNO_query",
-                             &s390x_dirtyhelper_PPNO_query,
-                             mkIRExprVec_3(IRExpr_GSPTR(), mkexpr(gpr1num),
-                                           mkexpr(gpr2num)));
-   query->guard = mkexpr(is_query);
-   query->nFxState = 1;
-   vex_bzero(&query->fxState, sizeof(query->fxState));
-   query->fxState[0].fx     = Ifx_Read;
-   query->fxState[0].offset = S390X_GUEST_OFFSET(guest_r0);
-   query->fxState[0].size   = 2 * sizeof(ULong); /* gpr0 and gpr1 are read */
-   query->mAddr = get_gpr_dw0(1);
-   query->mSize = S390_PPNO_PARAM_BLOCK_SIZE_QUERY;
-   query->mFx   = Ifx_Write;
-
-   IRTemp gen_cc = newTemp(Ity_I64);
-   sha512_gen = unsafeIRDirty_1_N(gen_cc, 0, "s390x_dirtyhelper_PPNO_sha512",
-                             &s390x_dirtyhelper_PPNO_sha512,
-                             mkIRExprVec_3(IRExpr_GSPTR(), mkexpr(gpr1num),
-                                           mkexpr(gpr2num)));
-   sha512_gen->guard = mkexpr(is_sha512_gen);
-   sha512_gen->nFxState = 3;
-   vex_bzero(&sha512_gen->fxState, sizeof(sha512_gen->fxState));
-   sha512_gen->fxState[0].fx     = Ifx_Read;
-   sha512_gen->fxState[0].offset = S390X_GUEST_OFFSET(guest_r0);
-   sha512_gen->fxState[0].size   = 2 * sizeof(ULong); /* gpr0 and gpr1 are read */
-   sha512_gen->fxState[1].fx     = Ifx_Read;
-   sha512_gen->fxState[1].offset = S390X_GUEST_OFFSET(guest_r0) + r1 * sizeof(ULong);
-   sha512_gen->fxState[1].size   = sizeof(ULong);
-   sha512_gen->fxState[2].fx     = Ifx_Modify;
-   sha512_gen->fxState[2].offset = S390X_GUEST_OFFSET(guest_r0) + (r1 + 1) * sizeof(ULong);
-   sha512_gen->fxState[2].size   = sizeof(ULong);
-   sha512_gen->mAddr = get_gpr_dw0(r1);
-   sha512_gen->mSize = S390_PPNO_MAX_SIZE_SHA512_GEN;
-   sha512_gen->mFx   = Ifx_Write;
-
-   IRTemp unused = newTemp(Ity_I64);
-   sha512_seed = unsafeIRDirty_1_N(unused, 0, "s390x_dirtyhelper_PPNO_sha512",
-                             &s390x_dirtyhelper_PPNO_sha512,
-                             mkIRExprVec_3(IRExpr_GSPTR(), mkexpr(gpr1num),
-                                           mkexpr(gpr2num)));
-   sha512_seed->guard = mkexpr(is_sha512_seed);
-   sha512_seed->nFxState = 2;
-   vex_bzero(&sha512_seed->fxState, sizeof(sha512_seed->fxState));
-   sha512_seed->fxState[0].fx     = Ifx_Read;
-   sha512_seed->fxState[0].offset = S390X_GUEST_OFFSET(guest_r0);
-   sha512_seed->fxState[0].size   = 2 * sizeof(ULong); /* gpr0 and gpr1 are read */
-   sha512_seed->fxState[1].fx     = Ifx_Read;
-   sha512_seed->fxState[1].offset = S390X_GUEST_OFFSET(guest_r0) + r2 * sizeof(ULong);
-   sha512_seed->fxState[1].size   = 2 * sizeof(ULong); /* r2 and r2 + 1 are read */
-   sha512_seed->mAddr = get_gpr_dw0(r2);
-   sha512_seed->mSize = S390_PPNO_MAX_SIZE_SHA512_SEED;
-   sha512_seed->mFx   = Ifx_Write;
-
-   /* Dummy helper which is used to signal VEX library that memory was loaded */
-   sha512_loadparam =
-      unsafeIRDirty_0_N(0, "s390x_dirtyhelper_PPNO_sha512_load_param_block",
-                        &s390x_dirtyhelper_PPNO_sha512_load_param_block,
-                        mkIRExprVec_0());
-   sha512_loadparam->guard = mkexpr(is_sha512);
-   sha512_loadparam->nFxState = 0;
-   vex_bzero(&sha512_loadparam->fxState, sizeof(sha512_loadparam->fxState));
-   sha512_loadparam->mAddr = get_gpr_dw0(1);
-   sha512_loadparam->mSize = S390_PPNO_PARAM_BLOCK_SIZE_SHA512;
-   sha512_loadparam->mFx   = Ifx_Read;
-
-   IRDirty* sha512_saveparam =
-      unsafeIRDirty_0_N(0, "s390x_dirtyhelper_PPNO_sha512_save_param_block",
-                        &s390x_dirtyhelper_PPNO_sha512_load_param_block,
-                        mkIRExprVec_0());
-   sha512_saveparam->guard = mkexpr(is_sha512);
-   sha512_saveparam->nFxState = 0;
-   vex_bzero(&sha512_saveparam->fxState, sizeof(sha512_saveparam->fxState));
-   sha512_saveparam->mAddr = get_gpr_dw0(1);
-   sha512_saveparam->mSize = S390_PPNO_PARAM_BLOCK_SIZE_SHA512;
-   sha512_saveparam->mFx   = Ifx_Write;
-
-   stmt(IRStmt_Dirty(query));
-   stmt(IRStmt_Dirty(sha512_loadparam));
-   stmt(IRStmt_Dirty(sha512_gen));
-   stmt(IRStmt_Dirty(sha512_seed));
-   stmt(IRStmt_Dirty(sha512_saveparam));
-
-   IRTemp cc = newTemp(Ity_I64);
-   assign(cc,
-          mkite(mkexpr(is_sha512_gen),
-                mkexpr(gen_cc),
-                mkU64(0)
-               )
-         );
-
-   s390_cc_thunk_fill(mkU64(S390_CC_OP_SET), mkexpr(cc), mkU64(0), mkU64(0));
+   /* Check for obvious specification exceptions */
+   s390_insn_assert("ppno", r1 % 2 == 0 && r2 % 2 == 0 && r1 != 0 && r2 != 0);
 
+   extension(S390_EXT_PRNO, r1 | (r2 << 4));
    return "ppno";
 }
 
index b52825d743ddb7e763893929b5d5accf8f38f8d8..ee240347d63b9441c314f2cd5f7e9800a8c00b70 100644 (file)
@@ -7706,6 +7706,7 @@ s390_jump_kind_as_string(IRJumpKind kind)
    case Ijk_SigSEGV:     return "SigSEGV";
    case Ijk_SigBUS:      return "SigBUS";
    case Ijk_Sys_syscall: return "Sys_syscall";
+   case Ijk_Extension:   return "Extension";
    default:
       vpanic("s390_jump_kind_as_string");
    }
@@ -11428,6 +11429,7 @@ s390_insn_xassisted_emit(UChar *buf, const s390_insn *insn,
    switch (insn->variant.xassisted.kind) {
    case Ijk_ClientReq:   trcval = VEX_TRC_JMP_CLIENTREQ;   break;
    case Ijk_Sys_syscall: trcval = VEX_TRC_JMP_SYS_SYSCALL; break;
+   case Ijk_Extension:   trcval = VEX_TRC_JMP_EXTENSION;   break;
    case Ijk_Yield:       trcval = VEX_TRC_JMP_YIELD;       break;
    case Ijk_EmWarn:      trcval = VEX_TRC_JMP_EMWARN;      break;
    case Ijk_EmFail:      trcval = VEX_TRC_JMP_EMFAIL;      break;
index 3ae7c07c0787b1bb7da6bd5fc3984d3e85c6981c..10aebf73f0aa5a027841f01a48337861e8a77c57 100644 (file)
@@ -5361,6 +5361,7 @@ no_memcpy_put:
       case Ijk_NoDecode:
       case Ijk_InvalICache:
       case Ijk_Sys_syscall:
+      case Ijk_Extension:
       case Ijk_ClientReq:
       case Ijk_NoRedir:
       case Ijk_Yield:
@@ -5477,6 +5478,7 @@ iselNext(ISelEnv *env, IRExpr *next, IRJumpKind jk, Int offsIP)
    case Ijk_NoDecode:
    case Ijk_InvalICache:
    case Ijk_Sys_syscall:
+   case Ijk_Extension:
    case Ijk_ClientReq:
    case Ijk_NoRedir:
    case Ijk_Yield:
index 93e9a98d50fb9bb52acee8dad30fb62eaccf9675..9e7fbf920ef4c1bcdd6f1922fba2a140964c295c 100644 (file)
@@ -2089,6 +2089,7 @@ void ppIRJumpKind ( IRJumpKind kind )
       case Ijk_Sys_int145:    vex_printf("Sys_int145"); break;
       case Ijk_Sys_int210:    vex_printf("Sys_int210"); break;
       case Ijk_Sys_sysenter:  vex_printf("Sys_sysenter"); break;
+      case Ijk_Extension:     vex_printf("Extension"); break;
       default:                vpanic("ppIRJumpKind");
    }
 }
index 1b4efdc90e1e617f7e14cba2d0613d2ed3313fb3..f6f347a052ea223ea6388550fe0134f6f2dfb6c3 100644 (file)
@@ -2485,6 +2485,11 @@ extern Bool eqIRAtom ( const IRExpr*, const IRExpr* );
    executed kernel-entering (system call) instruction.  This makes it
    very much easier (viz, actually possible at all) to back up the
    guest to restart a syscall that has been interrupted by a signal.
+
+   Re Ijk_Extension: the guest state must have the pseudo-register
+   guest_IP_AT_SYSCALL, which is also used for Ijk_Sys_*.  Front ends
+   must set this to the current instruction address before jumping to
+   an extension handler.
 */
 typedef
    enum {
@@ -2517,8 +2522,9 @@ typedef
       Ijk_Sys_int130,     /* amd64/x86 'int $0x82' */
       Ijk_Sys_int145,     /* amd64/x86 'int $0x91' */
       Ijk_Sys_int210,     /* amd64/x86 'int $0xD2' */
-      Ijk_Sys_sysenter    /* x86 'sysenter'.  guest_EIP becomes 
+      Ijk_Sys_sysenter,   /* x86 'sysenter'.  guest_EIP becomes
                              invalid at the point this happens. */
+      Ijk_Extension,      /* invoke guest-specific extension */
    }
    IRJumpKind;
 
index 256541b568737c9c90cbe42a63d9aab8a714fd73..7217fdec3a6c77572883e4d5e4300a4c658f1ab2 100644 (file)
 #define S390_FAC_DFLT    151 // deflate-conversion facility
 #define S390_FAC_NNPA    165 // NNPA facility
 
+/*--------------------------------------------------------------*/
+/*--- Extensions                                             ---*/
+/*--------------------------------------------------------------*/
+
+/* The extension ID is stored in the low 16 bits of the guest_SYSNO pseudo
+   register. */
+#define S390_EXT_ID_NBITS 16
+
+#define S390_EXT_PRNO    1
 
 /*--------------------------------------------------------------*/
 /*--- Miscellaneous                                          ---*/
index cfd54ded373f9a7bd5d88f831c01bd41a938b1a9..c9adcb70d776a138e14f96b71d1b1f2cef6274a9 100644 (file)
@@ -77,6 +77,8 @@
 #define VEX_TRC_JMP_NODECODE   69  /* next instruction is not decodable */
 #define VEX_TRC_JMP_MAPFAIL    71  /* address translation failed */
 
+#define VEX_TRC_JMP_EXTENSION 114  /* invoke extension before continuing */
+
 #define VEX_TRC_JMP_SYS_SYSCALL  73 /* do syscall before continuing */
 #define VEX_TRC_JMP_SYS_INT32    75 /* do syscall before continuing */
 #define VEX_TRC_JMP_SYS_INT128   77 /* do syscall before continuing */
index 024dfbbff16dfdb222d1fc2a07a6ca9db7f36d0f..c1182b61618f4277b39b164a26111a54212439e0 100644 (file)
@@ -270,6 +270,8 @@ noinst_HEADERS = \
        m_demangle/demangle.h   \
        m_demangle/safe-ctype.h \
        m_demangle/vg_libciface.h \
+       m_extension/priv_extension.h \
+       m_extension/priv_types_n_macros.h \
        m_gdbserver/regcache.h \
        m_gdbserver/regdef.h \
        m_gdbserver/server.h \
@@ -394,6 +396,8 @@ COREGRIND_SOURCES_COMMON = \
        m_dispatch/dispatch-amd64-darwin.S \
        m_dispatch/dispatch-x86-solaris.S \
        m_dispatch/dispatch-amd64-solaris.S \
+       m_extension/extension-main.c \
+       m_extension/extension-s390x.c \
        m_gdbserver/inferiors.c \
        m_gdbserver/regcache.c \
        m_gdbserver/remote-utils.c \
diff --git a/coregrind/m_extension/extension-main.c b/coregrind/m_extension/extension-main.c
new file mode 100644 (file)
index 0000000..5590c9d
--- /dev/null
@@ -0,0 +1,65 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Handle extensions.                          extension-main.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) IBM Corp. 2024
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+   The GNU General Public License is contained in the file COPYING.
+*/
+
+/* Contributed by Andreas Arnez */
+
+#include "libvex_guest_offsets.h"
+#include "pub_core_extension.h"
+#include "pub_core_libcassert.h"
+#include "pub_core_threadstate.h"
+#include "priv_extension.h"
+
+/* This is the top-level of the extension handler module.  Extensions provide a
+   means of executing instructions whose register and memory effects are too
+   complex to be expressed with dirty helpers.
+ */
+
+#if defined(VGP_s390x_linux)
+
+/* --- This is the main function of this file. --- */
+
+enum ExtensionError VG_(client_extension)(ThreadId tid)
+{
+   ThreadState*             tst;
+
+   vg_assert(VG_(is_valid_tid)(tid));
+   vg_assert(tid >= 1 && tid < VG_N_THREADS);
+   vg_assert(VG_(is_running_thread)(tid));
+
+   tst = VG_(get_ThreadState)(tid);
+
+   return ML_(do_client_extension)(tst);
+}
+
+#else
+
+enum ExtensionError VG_(client_extension)(ThreadId tid)
+{
+   VG_(core_panic)("Extension handler not implemented for this architecture");
+}
+
+#endif
diff --git a/coregrind/m_extension/extension-s390x.c b/coregrind/m_extension/extension-s390x.c
new file mode 100644 (file)
index 0000000..3e9e634
--- /dev/null
@@ -0,0 +1,227 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Handle s390x-specific extensions.          extension-s390x.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) IBM Corp. 2024
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+   The GNU General Public License is contained in the file COPYING.
+*/
+
+/* Contributed by Andreas Arnez */
+
+#if defined(VGP_s390x_linux)
+
+#include "libvex_s390x_common.h"
+#include "priv_extension.h"
+#include "priv_types_n_macros.h"
+#include "pub_core_libcassert.h"
+#include "pub_core_threadstate.h"
+
+#undef SYSNO
+
+#define READ_FUNCTION_CODE(tst)                                                \
+   ({                                                                          \
+      PRE_REG_READ(tst, "func_code", r0, 7, sizeof(UChar));                    \
+      tst->arch.vex.guest_r0 & 0xff;                                           \
+   })
+
+#define READ_GPR(tst, name, regno)                                             \
+   ({                                                                          \
+      PRE_REG_READ(tst, name, r0, sizeof(ULong) * (regno), sizeof(ULong));     \
+      *((&tst->arch.vex.guest_r0) + (regno));                                  \
+   })
+
+#define WRITE_GPR(tst, regno, value)                                           \
+   ({                                                                          \
+      *((&tst->arch.vex.guest_r0) + (regno)) = value;                          \
+      POST_REG_WRITE(tst, r0, sizeof(ULong) * (regno), sizeof(ULong));         \
+   })
+
+#define S390_CC_OP_SET 35
+
+#define WRITE_CC(tst, value)                                                   \
+   ({                                                                          \
+      tst->arch.vex.guest_CC_OP   = S390_CC_OP_SET;                            \
+      tst->arch.vex.guest_CC_DEP1 = value;                                     \
+      tst->arch.vex.guest_CC_DEP2 = 0;                                         \
+      tst->arch.vex.guest_CC_NDEP = 0;                                         \
+      POST_REG_WRITE(tst, CC_OP, 0, sizeof(ULong) * 4);                        \
+   })
+
+#define INSN_ERR(msg)                                                          \
+   ({                                                                          \
+      VG_(umsg)("Illegal operation: ");                                        \
+      VG_(umsg)(msg);                                                          \
+      ExtErr_Illop;                                                            \
+   })
+
+union reg_pair {
+   struct {
+      ULong a, b;
+   };
+   unsigned __int128 pair;
+};
+
+#define S390_SETBIT(x) (1UL << (63 - (x % 64)))
+
+/* Helper routine for query functions: Filter the bit vector `fc' using a given
+   `filter' vector */
+static void s390_filter_functions(ULong*       fc,
+                                  ULong        fc_len,
+                                  const ULong* filter,
+                                  ULong        filter_len)
+{
+   ULong n_fc     = fc_len / sizeof(ULong);
+   ULong n_filter = filter_len / sizeof(ULong);
+
+   for (ULong i = 0; i < n_fc; i++) {
+      if (i < n_filter)
+         fc[i] &= filter[i];
+      else
+         fc[i] = 0;
+   }
+}
+
+/*---------------------------------------------------------------*/
+/*--- PRNO (perform random number operation)                  ---*/
+/*---------------------------------------------------------------*/
+
+static Int do_PRNO_insn(UChar  func,
+                        ULong  parms,
+                        ULong* addr1,
+                        ULong* len1,
+                        ULong* addr2,
+                        ULong* len2)
+{
+   register UChar reg0 asm("0") = func;
+   register void* reg1 asm("1") = (void*)parms;
+   union reg_pair op1           = {{*addr1, *len1}};
+   union reg_pair op2           = {{*addr2, *len2}};
+   Int            cc;
+
+   asm volatile(".insn rre, 0xb93c0000, %[op1], %[op2]\n"
+                "ipm %[cc]\n"
+                "srl %[cc], 28\n"
+                : [cc] "=d"(cc), [op1] "+a"(op1.pair), [op2] "+a"(op2.pair)
+                : "d"(reg0), "d"(reg1)
+                : "cc", "memory");
+   *addr1 = op1.a;
+   *len1  = op1.b;
+   *addr2 = op2.a;
+   *len2  = op2.b;
+   return cc;
+}
+
+/* PRNO functions that we support if the hardware does. */
+static const ULong PRNO_functions[] = {
+   (S390_SETBIT(0)       // Query
+    | S390_SETBIT(3)),   // SHA-512-DRNG
+   (S390_SETBIT(112)     // TRNG-Query-Raw-to-Conditioned-Ratio
+    | S390_SETBIT(114)), // TRNG
+};
+
+static UWord do_extension_PRNO(ThreadState* tst, ULong variant)
+{
+   UChar r1    = variant & 0xf;
+   UChar r2    = (variant >> 4) & 0xf;
+   UChar func  = READ_FUNCTION_CODE(tst);
+   UChar fc    = func & 0x7f;
+   UChar mflag = func & 128;
+   ULong parms = READ_GPR(tst, "r1", 1);
+   ULong parms_len;
+   Int   cc         = 0;
+   ULong orig_addr1 = 0, orig_len1 = 0, orig_addr2 = 0, orig_len2 = 0;
+   ULong addr1 = 0, len1 = 0, addr2 = 0, len2 = 0;
+
+   switch (fc) {
+   case 0: // Query
+      parms_len = 16;
+      PRE_MEM_WRITE(tst, "parms", parms, parms_len);
+      cc = do_PRNO_insn(func, parms, &addr1, &len1, &addr2, &len2);
+      s390_filter_functions((ULong*)parms, parms_len, PRNO_functions,
+                            sizeof(PRNO_functions));
+      POST_MEM_WRITE(tst, parms, parms_len);
+      break;
+   case 112: // TRNG-Query-Raw-to-Conditioned-Ratio
+      parms_len = 8;
+      PRE_MEM_WRITE(tst, "parms", parms, parms_len);
+      cc = do_PRNO_insn(func, parms, &addr1, &len1, &addr2, &len2);
+      POST_MEM_WRITE(tst, parms, parms_len);
+      break;
+   case 3: // SHA-512-DRNG
+      parms_len = 240;
+      PRE_MEM_READ(tst, "parms", parms, parms_len);
+      if (mflag == 0) {
+         // Generate operation
+         addr1 = orig_addr1 = READ_GPR(tst, "op1_addr", r1);
+         len1 = orig_len1 = READ_GPR(tst, "op1_len", r1 + 1);
+         PRE_MEM_WRITE(tst, "operand1", addr1, len1);
+      } else {
+         // Seed operation
+         addr2 = orig_addr2 = READ_GPR(tst, "op2_addr", r2);
+         len2 = orig_len2 = READ_GPR(tst, "op2_len", r2 + 1);
+         PRE_MEM_READ(tst, "operand2", addr2, len2);
+      }
+      PRE_MEM_WRITE(tst, "parms", parms, parms_len);
+      cc = do_PRNO_insn(func, parms, &addr1, &len1, &addr2, &len2);
+      POST_MEM_WRITE(tst, parms, parms_len);
+      if (mflag == 0) {
+         WRITE_GPR(tst, r2 + 1, len1);
+         POST_MEM_WRITE(tst, orig_addr1, orig_len1 - len1);
+      }
+      break;
+   case 114: // TRNG
+      addr1 = orig_addr1 = READ_GPR(tst, "op1_addr", r1);
+      len1  = orig_len1 = READ_GPR(tst, "op1_len", r1 + 1);
+      PRE_MEM_WRITE(tst, "operand1", addr1, len1);
+      addr2 = orig_addr2 = READ_GPR(tst, "op2_addr", r2);
+      len2  = orig_len2 = READ_GPR(tst, "op2_len", r2 + 1);
+      PRE_MEM_WRITE(tst, "operand2", addr2, len2);
+      cc = do_PRNO_insn(func, parms, &addr1, &len1, &addr2, &len2);
+      WRITE_GPR(tst, r1, addr1);
+      WRITE_GPR(tst, r1 + 1, len1);
+      WRITE_GPR(tst, r2, addr2);
+      WRITE_GPR(tst, r2 + 1, len2);
+      POST_MEM_WRITE(tst, orig_addr1, orig_len1 - len1);
+      POST_MEM_WRITE(tst, orig_addr2, orig_len2 - len2);
+      break;
+   default:
+      return INSN_ERR("PRNO: unknown function code\n");
+   }
+   WRITE_CC(tst, cc);
+   return ExtErr_OK;
+}
+
+enum ExtensionError ML_(do_client_extension)(ThreadState* tst)
+{
+   ULong code    = REG_READ(tst, SYSNO);
+   ULong id      = code & ((1ULL << S390_EXT_ID_NBITS) - 1);
+   ULong variant = code >> S390_EXT_ID_NBITS;
+
+   switch (id) {
+   case S390_EXT_PRNO:
+      return do_extension_PRNO(tst, variant);
+   default:
+      VG_(core_panic)("unknown extension ID");
+   }
+}
+
+#endif
diff --git a/coregrind/m_extension/priv_extension.h b/coregrind/m_extension/priv_extension.h
new file mode 100644 (file)
index 0000000..6f6dbac
--- /dev/null
@@ -0,0 +1,38 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Private extensions header.                  priv_extension.h ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) IBM Corp. 2024
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+   The GNU General Public License is contained in the file COPYING.
+*/
+
+/* Contributed by Andreas Arnez */
+
+#ifndef __PRIV_EXTENSION_H
+#define __PRIV_EXTENSION_H
+
+#include "pub_core_extension.h"
+#include "pub_core_threadstate.h"
+
+enum ExtensionError ML_(do_client_extension)(ThreadState* thread_state);
+
+#endif
diff --git a/coregrind/m_extension/priv_types_n_macros.h b/coregrind/m_extension/priv_types_n_macros.h
new file mode 100644 (file)
index 0000000..347fa4d
--- /dev/null
@@ -0,0 +1,80 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Types and macros for writing extensions.                     ---*/
+/*---                                        priv_types_n_macros.h ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) IBM Corp. 2024
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+   The GNU General Public License is contained in the file COPYING.
+*/
+
+/* Contributed by Andreas Arnez */
+
+#ifndef __PRIV_TYPES_N_MACROS_H
+#define __PRIV_TYPES_N_MACROS_H
+
+#include "pub_core_basics.h" // Addr
+#include "pub_core_libcprint.h"
+#include "pub_core_signals.h"
+#include "pub_core_tooliface.h"
+#include "pub_tool_tooliface.h"
+
+#define PRE_REG_READ(tst, name, reg, offset, len)                              \
+   if (VG_(tdict).track_pre_reg_read) {                                        \
+      VexGuestArchState* gst = &tst->arch.vex;                                 \
+      VG_(tdict).track_pre_reg_read(                                           \
+         Vg_CoreSysCall, tst->tid, name,                                       \
+         (UChar*)&gst->guest_##reg - (UChar*)gst + (offset), len);             \
+   }
+
+#define POST_REG_WRITE(tst, reg, offset, len)                                  \
+   if (VG_(tdict).track_post_reg_write) {                                      \
+      VexGuestArchState* gst = &tst->arch.vex;                                 \
+      VG_(tdict).track_post_reg_write(                                         \
+         Vg_CoreSysCall, tst->tid,                                             \
+         (UChar*)&gst->guest_##reg - (UChar*)gst + offset, len);               \
+   }
+
+#define PRE_MEM_READ(tst, name, addr, len)                                     \
+   if (VG_(tdict).track_pre_mem_read) {                                        \
+      VG_(tdict).track_pre_mem_read(Vg_CoreSysCall, tst->tid, name, addr,      \
+                                    len);                                      \
+   }
+
+#define PRE_MEM_WRITE(tst, name, addr, len)                                    \
+   if (VG_(tdict).track_pre_mem_write) {                                       \
+      VG_(tdict).track_pre_mem_write(Vg_CoreSysCall, tst->tid, name, addr,     \
+                                     len);                                     \
+   }
+
+#define POST_MEM_WRITE(tst, addr, len)                                         \
+   if (VG_(tdict).track_post_mem_write) {                                      \
+      VG_(tdict).track_post_mem_write(Vg_CoreSysCall, tst->tid, addr, len);    \
+   }
+
+#define REG_READ(tst, reg)                                                     \
+   ({                                                                          \
+      PRE_REG_READ(tst, #reg, reg, 0, sizeof(tst->arch.vex.guest_##reg));      \
+      tst->arch.vex.guest_##reg;                                               \
+   })
+
+
+#endif
index 56194516017ae005d73dd476ef0163dc354d1699..fc8cf7c9cb1d421e3faaf568b37012e1154354d9 100644 (file)
@@ -66,6 +66,7 @@
 #include "pub_core_clreq.h"      // for VG_USERREQ__*
 #include "pub_core_dispatch.h"
 #include "pub_core_errormgr.h"   // For VG_(get_n_errs_found)()
+#include "pub_core_extension.h"
 #include "pub_core_gdbserver.h"  // for VG_(gdbserver)/VG_(gdbserver_activity)
 #include "pub_core_libcbase.h"
 #include "pub_core_libcassert.h"
@@ -277,6 +278,7 @@ const HChar* name_of_sched_event ( UInt event )
       case VEX_TRC_JMP_YIELD:          return "YIELD";
       case VEX_TRC_JMP_NODECODE:       return "NODECODE";
       case VEX_TRC_JMP_MAPFAIL:        return "MAPFAIL";
+      case VEX_TRC_JMP_EXTENSION:      return "EXTENSION";
       case VEX_TRC_JMP_SYS_SYSCALL:    return "SYSCALL";
       case VEX_TRC_JMP_SYS_INT32:      return "INT32";
       case VEX_TRC_JMP_SYS_INT128:     return "INT128";
@@ -1223,6 +1225,32 @@ static void handle_syscall(ThreadId tid, UInt trc)
    }
 }
 
+static void handle_extension(ThreadId tid)
+{
+   volatile UWord jumped;
+   enum ExtensionError err;
+
+   SCHEDSETJMP(tid, jumped, err = VG_(client_extension)(tid));
+   vg_assert(VG_(is_running_thread)(tid));
+
+   if (err != ExtErr_OK) {
+      ThreadState* tst = VG_(get_ThreadState)(tid);
+      Addr addr = tst->arch.vex.guest_IP_AT_SYSCALL;
+      switch (err) {
+      case ExtErr_Illop:
+         VG_(synth_sigill)(tid, addr);
+         break;
+      default:
+         VG_(core_panic)("scheduler: bad return code from extension");
+      }
+   }
+
+   if (jumped != (UWord)0) {
+      block_signals();
+      VG_(poll_signals)(tid);
+   }
+}
+
 /* tid just requested a jump to the noredir version of its current
    program counter.  So make up that translation if needed, run it,
    and return the resulting thread return code in two_words[]. */
@@ -1542,6 +1570,11 @@ VgSchedReturnCode VG_(scheduler) ( ThreadId tid )
         do_client_request(tid);
         break;
 
+      case VEX_TRC_JMP_EXTENSION: {
+         handle_extension(tid);
+         break;
+      }
+
       case VEX_TRC_JMP_SYS_INT128:  /* x86-linux */
       case VEX_TRC_JMP_SYS_INT129:  /* x86-darwin */
       case VEX_TRC_JMP_SYS_INT130:  /* x86-darwin */
diff --git a/coregrind/pub_core_extension.h b/coregrind/pub_core_extension.h
new file mode 100644 (file)
index 0000000..39fa4dd
--- /dev/null
@@ -0,0 +1,45 @@
+/*--------------------------------------------------------------------*/
+/*--- Extensions                              pub_core_extension.h ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) IBM Corp. 2024
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+   The GNU General Public License is contained in the file COPYING.
+*/
+
+/* Contributed by Andreas Arnez */
+
+#ifndef __PUB_CORE_EXTENSION_H
+#define __PUB_CORE_EXTENSION_H
+
+#include "pub_core_basics.h"        // VG_ macro
+
+//--------------------------------------------------------------------
+// PURPOSE: This module contains the extension handling stuff
+//--------------------------------------------------------------------
+
+enum ExtensionError {
+   ExtErr_OK,
+   ExtErr_Illop,
+};
+
+extern enum ExtensionError VG_(client_extension) ( ThreadId tid );
+
+#endif