From: Andreas Arnez Date: Thu, 18 Apr 2024 11:49:52 +0000 (+0200) Subject: Add extension concept and exploit it for s390x `PRNO' X-Git-Tag: VALGRIND_3_23_0~33 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=01d106084a8e2e3164a53b2f29cb0239a45bb81e;p=thirdparty%2Fvalgrind.git Add extension concept and exploit it for s390x `PRNO' 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"). --- diff --git a/.gitignore b/.gitignore index b242eb6e2..8eadfd76c 100644 --- a/.gitignore +++ b/.gitignore @@ -212,6 +212,13 @@ /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 diff --git a/VEX/priv/guest_s390_toIR.c b/VEX/priv/guest_s390_toIR.c index d65ba998c..44a0293e2 100644 --- a/VEX/priv/guest_s390_toIR.c +++ b/VEX/priv/guest_s390_toIR.c @@ -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"; } diff --git a/VEX/priv/host_s390_defs.c b/VEX/priv/host_s390_defs.c index b52825d74..ee240347d 100644 --- a/VEX/priv/host_s390_defs.c +++ b/VEX/priv/host_s390_defs.c @@ -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; diff --git a/VEX/priv/host_s390_isel.c b/VEX/priv/host_s390_isel.c index 3ae7c07c0..10aebf73f 100644 --- a/VEX/priv/host_s390_isel.c +++ b/VEX/priv/host_s390_isel.c @@ -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: diff --git a/VEX/priv/ir_defs.c b/VEX/priv/ir_defs.c index 93e9a98d5..9e7fbf920 100644 --- a/VEX/priv/ir_defs.c +++ b/VEX/priv/ir_defs.c @@ -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"); } } diff --git a/VEX/pub/libvex_ir.h b/VEX/pub/libvex_ir.h index 1b4efdc90..f6f347a05 100644 --- a/VEX/pub/libvex_ir.h +++ b/VEX/pub/libvex_ir.h @@ -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; diff --git a/VEX/pub/libvex_s390x_common.h b/VEX/pub/libvex_s390x_common.h index 256541b56..7217fdec3 100644 --- a/VEX/pub/libvex_s390x_common.h +++ b/VEX/pub/libvex_s390x_common.h @@ -108,6 +108,15 @@ #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 ---*/ diff --git a/VEX/pub/libvex_trc_values.h b/VEX/pub/libvex_trc_values.h index cfd54ded3..c9adcb70d 100644 --- a/VEX/pub/libvex_trc_values.h +++ b/VEX/pub/libvex_trc_values.h @@ -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 */ diff --git a/coregrind/Makefile.am b/coregrind/Makefile.am index 024dfbbff..c1182b616 100644 --- a/coregrind/Makefile.am +++ b/coregrind/Makefile.am @@ -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 index 000000000..5590c9d28 --- /dev/null +++ b/coregrind/m_extension/extension-main.c @@ -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 . + + 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 index 000000000..3e9e634d9 --- /dev/null +++ b/coregrind/m_extension/extension-s390x.c @@ -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 . + + 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 index 000000000..6f6dbaca8 --- /dev/null +++ b/coregrind/m_extension/priv_extension.h @@ -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 . + + 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 index 000000000..347fa4da6 --- /dev/null +++ b/coregrind/m_extension/priv_types_n_macros.h @@ -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 . + + 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 diff --git a/coregrind/m_scheduler/scheduler.c b/coregrind/m_scheduler/scheduler.c index 561945160..fc8cf7c9c 100644 --- a/coregrind/m_scheduler/scheduler.c +++ b/coregrind/m_scheduler/scheduler.c @@ -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 index 000000000..39fa4dd8b --- /dev/null +++ b/coregrind/pub_core_extension.h @@ -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 . + + 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