From: Carl Love Date: Tue, 4 May 2021 19:49:49 +0000 (-0500) Subject: PPC64: add support for the vectored system call instruction scv. X-Git-Tag: VALGRIND_3_18_0~132 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8f69756a2215e396b93573c9568cab00cc6c116d;p=thirdparty%2Fvalgrind.git PPC64: add support for the vectored system call instruction scv. --- diff --git a/NEWS b/NEWS index 4b4d33309c..a84ee6b730 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,7 @@ support for X86/macOS 10.13, AMD64/macOS 10.13 and nanoMIPS/Linux. - ISA 3.1 support is now complete - ISA 3.0 support for the darn instruction added. + - ISA 3.0 support for the vector system call instruction scv added. * ==================== TOOL CHANGES ==================== @@ -33,6 +34,7 @@ are not entered into bugzilla tend to get forgotten about or ignored. 423963 Error in child thread when CLONE_PIDFD is used 429375 PPC ISA 3.1 support is missing, part 9 +431157 PPC_FEATURE2_SCV needs to be masked in AT_HWCAP2 433801 PPC ISA 3.1 support is missing, part 10 (ISA 3.1 support complete) 433863 s390x: memcheck/tests/s390x/{cds,cs,csg} failures 434840 PPC64 darn instruction not supported diff --git a/VEX/priv/guest_ppc_defs.h b/VEX/priv/guest_ppc_defs.h index 3c22096673..7c50f85819 100644 --- a/VEX/priv/guest_ppc_defs.h +++ b/VEX/priv/guest_ppc_defs.h @@ -276,6 +276,13 @@ extern ULong darn_dirty_helper ( UInt L ); #endif /* ndef __VEX_GUEST_PPC_DEFS_H */ +/* SCV flag defines. Must be consistently defined here and in + coregrind/pub_core_syscall.h,in the do_syscall_WRK() assembly code in + coregrind/m_syscall.c and coregrind/m_syswrap/syscall-ppc64le-linux.S + code. */ +#define SC_FLAG 1 +#define SCV_FLAG 2 + /*---------------------------------------------------------------*/ /*--- end guest_ppc_defs.h ---*/ /*---------------------------------------------------------------*/ diff --git a/VEX/priv/guest_ppc_toIR.c b/VEX/priv/guest_ppc_toIR.c index 09e9c49087..1ed6f8d6f7 100644 --- a/VEX/priv/guest_ppc_toIR.c +++ b/VEX/priv/guest_ppc_toIR.c @@ -375,6 +375,7 @@ static Bool OV32_CA32_supported = False; #define OFFB_ACC_7_r1 offsetofPPCGuestState(guest_ACC_7_r1) #define OFFB_ACC_7_r2 offsetofPPCGuestState(guest_ACC_7_r2) #define OFFB_ACC_7_r3 offsetofPPCGuestState(guest_ACC_7_r3) +#define OFFB_syscall_flag offsetofPPCGuestState(guest_syscall_flag) /*------------------------------------------------------------*/ @@ -4068,6 +4069,18 @@ static IRExpr* /* ::Ity_I32 */ getFPCC ( void ) return mkexpr(val); } +static void put_syscall_flag( IRExpr* src ) +{ + /* Need to pass a flag indicating if the system call is using the sc or + scv instructions. Because Valgrind does an end-of-block after the + system call, the contents of a gpr can not be saved and restored after + the system call. A custom guest state register guest_syscall_flag is + used to pass the flag so the guest state is not disturbed. */ + + stmt( IRStmt_Put( offsetofPPCGuestState(guest_syscall_flag), src ) ); +} + + /*-----------------------------------------------------------*/ /* Helpers to access VSX Accumulator register file *-----------------------------------------------------------*/ @@ -11011,6 +11024,7 @@ static Bool dis_trap ( UInt prefix, UInt theInstr, /* System Linkage Instructions */ + static Bool dis_syslink ( UInt prefix, UInt theInstr, const VexAbiInfo* abiinfo, DisResult* dres ) { @@ -11019,14 +11033,27 @@ static Bool dis_syslink ( UInt prefix, UInt theInstr, /* There is no prefixed version of these instructions. */ PREFIX_CHECK - if (theInstr != 0x44000002) { // sc - if (theInstr != 0x44000001) // scv - vex_printf("dis_syslink(ppc)(theInstr)\n"); + if ((theInstr != 0x44000002) // sc + && (theInstr != 0x44000001)) { // scv + vex_printf("dis_syslink(ppc)(theInstr)\n"); return False; } - // sc (System Call, PPC32 p504) - DIP("sc\n"); + /* The PPC syscall uses guest_GPR9 to pass a flag to indicate which + system call instruction is to be used. Arg7 = SC_FLAG for the sc + instruction; Arg7 = SCV_FLAG for the scv instruction. */ + if (theInstr == 0x44000002) { + // sc (System Call, PPC32 p504) + DIP("sc\n"); + put_syscall_flag( mkU32(SC_FLAG) ); + } else if (theInstr == 0x44000001) { + // scv + DIP("scv\n"); + put_syscall_flag( mkU32(SCV_FLAG) ); + } else { + /* Unknown instruction */ + return False; + } /* Copy CIA into the IP_AT_SYSCALL pseudo-register, so that on Darwin Valgrind can back the guest up to this instruction if it needs @@ -36113,8 +36140,9 @@ DisResult disInstr_PPC_WRK ( goto decode_failure; /* System Linkage Instructions */ - case 0x11: // sc - if (dis_syslink( prefix, theInstr, abiinfo, &dres)) goto decode_success; + case 0x11: // sc, scv + if (dis_syslink( prefix, theInstr, abiinfo, &dres)) + goto decode_success; goto decode_failure; /* Trap Instructions */ diff --git a/VEX/pub/libvex.h b/VEX/pub/libvex.h index 46a7a5553e..dcf022159b 100644 --- a/VEX/pub/libvex.h +++ b/VEX/pub/libvex.h @@ -125,6 +125,8 @@ typedef #define VEX_HWCAPS_PPC64_ISA2_07 (1<<20) /* ISA 2.07 -- e.g., mtvsrd */ #define VEX_HWCAPS_PPC64_ISA3_0 (1<<22) /* ISA 3.0 -- e.g., cnttzw */ #define VEX_HWCAPS_PPC64_ISA3_1 (1<<23) /* ISA 3.1 -- e.g., brh */ +#define VEX_HWCAPS_PPC64_SCV (1<<24) /* ISA 3.0, Kernel supports scv + instruction. */ /* s390x: Hardware capability encoding diff --git a/VEX/pub/libvex_guest_ppc32.h b/VEX/pub/libvex_guest_ppc32.h index eb1f728b74..9457e9bc0f 100644 --- a/VEX/pub/libvex_guest_ppc32.h +++ b/VEX/pub/libvex_guest_ppc32.h @@ -290,9 +290,8 @@ typedef /* 1924 */ U128 guest_ACC_7_r3; /* Padding to make it have an 16-aligned size */ - /* 1940 */ UInt padding2; -// /* 1944 */ UInt padding3; -// /* 1948 */ UInt padding4; + /* 1940 */ UInt guest_syscall_flag; + /* 1944 * UInt padding2; */ } VexGuestPPC32State; diff --git a/VEX/pub/libvex_guest_ppc64.h b/VEX/pub/libvex_guest_ppc64.h index c8c9d072d4..92e93cb881 100644 --- a/VEX/pub/libvex_guest_ppc64.h +++ b/VEX/pub/libvex_guest_ppc64.h @@ -328,6 +328,10 @@ typedef /* 2192 */ U128 guest_ACC_7_r2; /* 2208 */ U128 guest_ACC_7_r3; + /* 2224 */ UInt guest_syscall_flag; + /* 2228 */ UInt padding1; + /* 2232 */ UInt padding2; + /* 2236 */ UInt padding3; /* Padding to make it have an 16-aligned size */ /* 2222 UInt padding0; */ } diff --git a/coregrind/m_initimg/initimg-linux.c b/coregrind/m_initimg/initimg-linux.c index 55559b4923..fc1a32ecf8 100644 --- a/coregrind/m_initimg/initimg-linux.c +++ b/coregrind/m_initimg/initimg-linux.c @@ -726,6 +726,7 @@ Addr setup_client_stack( void* init_sp, Bool auxv_2_07, hw_caps_2_07; Bool auxv_3_0, hw_caps_3_0; Bool auxv_3_1, hw_caps_3_1; + Bool auxv_scv_supported, hw_caps_scv_supported; /* The HWCAP2 field may contain an arch_2_07 entry that indicates * if the processor is compliant with the 2.07 ISA. (i.e. Power 8 @@ -796,6 +797,19 @@ Addr setup_client_stack( void* init_sp, ADD PUBLIC LINK WHEN AVAILABLE */ + + /* Check for SCV support */ + auxv_scv_supported = (auxv->u.a_val & 0x00100000ULL) + == 0x00100000ULL; + hw_caps_scv_supported = + (vex_archinfo->hwcaps & VEX_HWCAPS_PPC64_SCV) + == VEX_HWCAPS_PPC64_SCV; + + /* Verify the scv_supported setting in HWCAP2 matches the setting + in VEX HWCAPS. + */ + vg_assert(auxv_scv_supported == hw_caps_scv_supported); + /* ISA 3.1 */ auxv_3_1 = (auxv->u.a_val & 0x00040000ULL) == 0x00040000ULL; hw_caps_3_1 = (vex_archinfo->hwcaps & VEX_HWCAPS_PPC64_ISA3_1) @@ -820,6 +834,7 @@ Addr setup_client_stack( void* init_sp, | 0x04000000ULL /* TAR */ | 0x04000000ULL /* VEC_CRYPTO */ | 0x00800000ULL /* ARCH_3_00 */ + | 0x00100000ULL /* PPC_FEATURE2_SCV */ | 0x00400000ULL /* HAS_IEEE128 */ | 0x00200000ULL /* PPC_FEATURE2_DARN */ | 0x00040000ULL); /* ARCH_3_1 */ diff --git a/coregrind/m_machine.c b/coregrind/m_machine.c index 1bf50846d2..ab436aec6e 100644 --- a/coregrind/m_machine.c +++ b/coregrind/m_machine.c @@ -1291,6 +1291,7 @@ Bool VG_(machine_get_hwcaps)( void ) volatile Bool have_F, have_V, have_FX, have_GX, have_VX, have_DFP; volatile Bool have_isa_2_07, have_isa_3_0, have_isa_3_1; + volatile Bool have_scv_support; Int r; /* This is a kludge. Really we ought to back-convert saved_act @@ -1401,6 +1402,18 @@ Bool VG_(machine_get_hwcaps)( void ) __asm__ __volatile__(".long 0x7f1401b6"); /* brh RA, RS */ } + /* Check if Host supports scv instruction */ + have_scv_support = True; + if (VG_MINIMAL_SETJMP(env_unsup_insn)) { + have_scv_support = False; + } else { + /* Set r0 to 13 for the system time call. Don't want to make a random + system call. */ + __asm__ __volatile__(".long 0x7c000278"); /* clear r0 */ + __asm__ __volatile__(".long 0x6009000d"); /* set r0 to 13 */ + __asm__ __volatile__(".long 0x44000001"); /* scv */ + } + /* determine dcbz/dcbzl sizes while we still have the signal * handlers registered */ find_ppc_dcbz_sz(&vai); @@ -1436,6 +1449,7 @@ Bool VG_(machine_get_hwcaps)( void ) if (have_isa_2_07) vai.hwcaps |= VEX_HWCAPS_PPC64_ISA2_07; if (have_isa_3_0) vai.hwcaps |= VEX_HWCAPS_PPC64_ISA3_0; if (have_isa_3_1) vai.hwcaps |= VEX_HWCAPS_PPC64_ISA3_1; + if (have_scv_support) vai.hwcaps |= VEX_HWCAPS_PPC64_SCV; VG_(machine_get_cache_info)(&vai); diff --git a/coregrind/m_signals.c b/coregrind/m_signals.c index 720fb5f661..b45afe5992 100644 --- a/coregrind/m_signals.c +++ b/coregrind/m_signals.c @@ -359,8 +359,12 @@ typedef struct SigQueue { { ULong err = (uc->uc_mcontext.gp_regs[VKI_PT_CCR] >> 28) & 1; ULong r3 = uc->uc_mcontext.gp_regs[VKI_PT_R3]; + ThreadId tid = VG_(lwpid_to_vgtid)(VG_(gettid)()); + ThreadState *tst = VG_(get_ThreadState)(tid); + if (err) r3 &= 0xFF; - return VG_(mk_SysRes_ppc64_linux)( r3, err ); + return VG_(mk_SysRes_ppc64_linux)( r3, err, + tst->arch.vex.guest_syscall_flag); } # define VG_UCONTEXT_TO_UnwindStartRegs(srP, uc) \ { (srP)->r_pc = (uc)->uc_mcontext.gp_regs[VKI_PT_NIP]; \ diff --git a/coregrind/m_syscall.c b/coregrind/m_syscall.c index 4053d40247..80e2af4397 100644 --- a/coregrind/m_syscall.c +++ b/coregrind/m_syscall.c @@ -145,11 +145,29 @@ SysRes VG_(mk_SysRes_ppc32_linux) ( UInt val, UInt cr0so ) { return res; } -/* As per ppc32 version, cr0.so must be in l.s.b. of 2nd arg */ -SysRes VG_(mk_SysRes_ppc64_linux) ( ULong val, ULong cr0so ) { +/* As per ppc32 version, for the sc instruction cr0.so must be in + l.s.b. of 2nd arg. + For the scv 0 instruction, the return value indicates failure if + it is -4095..-1 (i.e., it is >= -MAX_ERRNO (-4095) as an unsigned + comparison), in which case the error value is the negated return value. */ +SysRes VG_(mk_SysRes_ppc64_linux) ( ULong val, ULong cr0so, UInt flag ) { SysRes res; - res._isError = (cr0so & 1) != 0; - res._val = val; + + if (flag == SC_FLAG) { + /* sc instruction */ + res._isError = (cr0so & 1) != 0; + res._val = val; + } else if (flag == SCV_FLAG) { + /* scv instruction */ + if ( (Long)val >= -4095 && (Long)val <= -1) { + res._isError = True; + res._val = (ULong)(-val); + } else { + res._isError = False; + res._val = (ULong)(val); + } + } else + vg_assert(0); return res; } @@ -541,6 +559,12 @@ asm( " addi 2,2,.TOC.-0b@l\n" " .localentry do_syscall_WRK, .-do_syscall_WRK\n" "#endif" "\n" +/* Check which system call instruction to issue*/ +" ld 8, 56(3)\n" /* arg 7 holds sc/scv flag */ +" cmpdi 8,1\n" /* check sc/scv flag not equal to SC_FLAG*/ +" bne issue_scv\n" + +/* setup and issue the sc instruction */ " std 3,-16(1)\n" /* stash arg */ " ld 8, 48(3)\n" /* sc arg 6 */ " ld 7, 40(3)\n" /* sc arg 5 */ @@ -556,6 +580,34 @@ asm( " srwi 3,3,28\n" " andi. 3,3,1\n" " std 3,8(5)\n" /* argblock[1] = cr0.s0 & 1 */ +" blr\n" /* return */ + +/* setup to do scv instruction */ +"issue_scv: " +/* The scv instruction requires a new stack frame */ +" stdu 1,-80(1)\n" +" std 27,40(1)\n" /* save r27 to stack frame */ +" mflr 27\n" /* Get link register */ +" std 27,16(1)\n" /* Save link register */ + +/* setup and issue the scv instruction */ +" std 3,-16(1)\n" /* stash arg */ +" ld 8, 48(3)\n" /* sc arg 6 */ +" ld 7, 40(3)\n" /* sc arg 5 */ +" ld 6, 32(3)\n" /* sc arg 4 */ +" ld 5, 24(3)\n" /* sc arg 3 */ +" ld 4, 16(3)\n" /* sc arg 2 */ +" ld 0, 0(3)\n" /* sc number */ +" ld 3, 8(3)\n" /* sc arg 1 */ +" scv 0\n" +" ld 5,-16(1)\n" /* reacquire argblock ptr (r5 is caller-save) */ +" std 3,0(5)\n" /* argblock[0] = r3 */ + +/* pop off stack frame */ +" ld 27,16(1)\n" /* Fetch LR from frame */ +" mtlr 27\n" /* restore LR */ +" ld 27,40(1)\n" /* restore r27 from stack frame */ +" addi 1,1,80\n" " blr\n" " .size do_syscall_WRK, .-do_syscall_WRK\n" ); @@ -978,7 +1030,11 @@ SysRes VG_(do_syscall) ( UWord sysno, RegWord a1, RegWord a2, RegWord a3, return VG_(mk_SysRes_ppc32_linux)( val, cr0so ); # elif defined(VGP_ppc64be_linux) || defined(VGP_ppc64le_linux) - ULong argblock[7]; + ULong argblock[8]; + /* PPC system calls have at most 6 arguments. The Valgrind infrastructure + supports 8 system call arguments. Argument 7 is used on PPC LE to pass + the flag indicating if the sc or scv instruction should be used for the + system call. */ argblock[0] = sysno; argblock[1] = a1; argblock[2] = a2; @@ -986,8 +1042,9 @@ SysRes VG_(do_syscall) ( UWord sysno, RegWord a1, RegWord a2, RegWord a3, argblock[4] = a4; argblock[5] = a5; argblock[6] = a6; + argblock[7] = a7; do_syscall_WRK( &argblock[0] ); - return VG_(mk_SysRes_ppc64_linux)( argblock[0], argblock[1] ); + return VG_(mk_SysRes_ppc64_linux)( argblock[0], argblock[1], a7 ); # elif defined(VGP_arm_linux) UWord val = do_syscall_WRK(a1,a2,a3,a4,a5,a6,sysno); diff --git a/coregrind/m_syswrap/priv_types_n_macros.h b/coregrind/m_syswrap/priv_types_n_macros.h index 014db76fb6..7a9fd3260f 100644 --- a/coregrind/m_syswrap/priv_types_n_macros.h +++ b/coregrind/m_syswrap/priv_types_n_macros.h @@ -89,7 +89,6 @@ typedef Int o_sysno; # if defined(VGP_x86_linux) || defined(VGP_amd64_linux) \ || defined(VGP_ppc32_linux) \ - || defined(VGP_ppc64be_linux) || defined(VGP_ppc64le_linux) \ || defined(VGP_arm_linux) || defined(VGP_s390x_linux) \ || defined(VGP_arm64_linux) \ || defined(VGP_nanomips_linux) @@ -101,6 +100,15 @@ typedef Int o_arg6; Int uu_arg7; Int uu_arg8; +# elif defined(VGP_ppc64be_linux) || defined(VGP_ppc64le_linux) + Int o_arg1; + Int o_arg2; + Int o_arg3; + Int o_arg4; + Int o_arg5; + Int o_arg6; + Int o_arg7; + Int uu_arg8; # elif defined(VGP_mips32_linux) Int o_arg1; Int o_arg2; diff --git a/coregrind/m_syswrap/syscall-ppc64le-linux.S b/coregrind/m_syswrap/syscall-ppc64le-linux.S index 2eee752a8d..e416688e37 100644 --- a/coregrind/m_syswrap/syscall-ppc64le-linux.S +++ b/coregrind/m_syswrap/syscall-ppc64le-linux.S @@ -70,6 +70,14 @@ Int sigsetSzB) // r7 */ /* from vki_arch.h */ + +/* SCV flag defines. Must be consistently defined here and in + coregrind/pub_core_syscall.h,in the do_syscall_WRK() assembly code in + coregrind/m_syscall.c and coregrind/m_syswrap/syscall-ppcle-linux.S code. +*/ +#define SC_FLAG 1 +#define SCV_FLAG 2 + #define VKI_SIG_SETMASK 2 .align 2 @@ -101,6 +109,10 @@ ML_(do_syscall_for_client_WRK): std 30,64(1) std 29,56(1) std 28,48(1) + std 27,40(1) /* save r27 to stack frame */ + mflr 27 /* Get link register */ + std 27,16(1) /* Save link register */ + std 9,32(1) /* save gpr 9 */ mr 31,3 /* syscall number */ mr 30,4 /* guest_state */ mr 29,6 /* postmask */ @@ -123,14 +135,27 @@ ML_(do_syscall_for_client_WRK): ld 6,OFFSET_ppc64_GPR6(30) ld 7,OFFSET_ppc64_GPR7(30) ld 8,OFFSET_ppc64_GPR8(30) + ld 9,OFFSET_ppc64_GPR9(30) /* get flag for sc or scv inst */ mr 0,31 /* syscall number */ + cmpdi 9,SC_FLAG + bne 8f /* jump to scv call if gpr9 != 0 */ + /* If you change the code between labels 2 and 3, you need to update + the corresponding ppc64le calculations for blksys_restart and + blksys_complete in function getSyscallArgsFromGuestState in file + syswrap-main.c. */ 2: sc /* do the syscall */ + b 3f /* jump over scv call */ +8: scv 0 /* put the result back in the threadstate */ 3: std 3,OFFSET_ppc64_GPR3(30) /* gst->GPR3 = sc result */ + /* copy cr0.so back to simulated state */ mfcr 5 /* r5 = CR */ rlwinm 5,5,4,31,31 /* r5 = (CR >> 28) & 1 */ + ld 9,OFFSET_ppc64_GPR9(30) + cmpdi 9,SC_FLAG + bne 4f /* scv does not change CR */ stb 5,OFFSET_ppc64_CR0_0(30) /* gst->CR0.SO = cr0.so */ /* block signals again */ @@ -150,6 +175,10 @@ ML_(do_syscall_for_client_WRK): ld 29,56(1) ld 30,64(1) ld 31,72(1) + ld 9,32(1) + ld 27,16(1) /* Fetch LR from frame */ + mtlr 27 /* restore LR */ + ld 27,40(1) /* restore r27 from stack frame */ addi 1,1,80 blr diff --git a/coregrind/m_syswrap/syswrap-linux.c b/coregrind/m_syswrap/syswrap-linux.c index c59d8ee26b..818b11ff6e 100644 --- a/coregrind/m_syswrap/syswrap-linux.c +++ b/coregrind/m_syswrap/syswrap-linux.c @@ -474,6 +474,7 @@ static SysRes clone_new_thread ( Word (*fn)(void *), #elif defined(VGP_ppc64be_linux) || defined(VGP_ppc64le_linux) ULong word64; UInt old_cr = LibVEX_GuestPPC64_get_CR( &ctst->arch.vex ); + UInt flag = ctst->arch.vex.guest_syscall_flag; /* %r3 = 0 */ ctst->arch.vex.guest_GPR3 = 0; /* %cr0.so = 0 */ @@ -486,7 +487,7 @@ static SysRes clone_new_thread ( Word (*fn)(void *), /* VG_(printf)("word64 = 0x%llx\n", word64); */ res = VG_(mk_SysRes_ppc64_linux) (/*val*/(UInt)(word64 & 0xFFFFFFFFULL), - /*errflag*/ (UInt)((word64 >> (32+28)) & 1)); + /*errflag*/ (UInt)((word64 >> (32+28)) & 1), flag); #elif defined(VGP_s390x_linux) ULong r2; ctst->arch.vex.guest_r2 = 0; diff --git a/coregrind/m_syswrap/syswrap-main.c b/coregrind/m_syswrap/syswrap-main.c index cdb2663a22..1a475422b1 100644 --- a/coregrind/m_syswrap/syswrap-main.c +++ b/coregrind/m_syswrap/syswrap-main.c @@ -504,9 +504,20 @@ void getSyscallArgsFromGuestState ( /*OUT*/SyscallArgs* canonical, canonical->arg4 = gst->guest_GPR6; canonical->arg5 = gst->guest_GPR7; canonical->arg6 = gst->guest_GPR8; - canonical->arg7 = 0; + /* ISA 3.0 adds the scv system call instruction. + The PPC syscalls have at most 6 args. Arg 7 is being used to pass a + flag to indicate which system call instruction is to be used. + Arg7 = SC_FLAG for the sc instruction; Arg7 = SCV_FLAG for the scv + instruction. The guest register guest_syscall_flag was created to pass + the flag so the actual guest state would not be changed. */ + canonical->arg7 = gst->guest_syscall_flag; canonical->arg8 = 0; +#if defined(VGP_ppc64be_linux) + /* The sc instruction is currently only supported on LE systems. */ + vg_assert(gst->guest_syscall_flag == SC_FLAG); +#endif + #elif defined(VGP_arm_linux) VexGuestARMState* gst = (VexGuestARMState*)gst_vanilla; canonical->sysno = gst->guest_R7; @@ -829,6 +840,7 @@ void putSyscallArgsIntoGuestState ( /*IN*/ SyscallArgs* canonical, gst->guest_GPR6 = canonical->arg4; gst->guest_GPR7 = canonical->arg5; gst->guest_GPR8 = canonical->arg6; + gst->guest_GPR9 = canonical->arg7; #elif defined(VGP_arm_linux) VexGuestARMState* gst = (VexGuestARMState*)gst_vanilla; @@ -1004,10 +1016,15 @@ void getSyscallStatusFromGuestState ( /*OUT*/SyscallStatus* canonical, canonical->what = SsComplete; # elif defined(VGP_ppc64be_linux) || defined(VGP_ppc64le_linux) + /* There is a Valgrind specific guest state register guest_syscall_flag + that is set to zero to indicate if the sc instruction was used or one + if the scv instruction was used for the system call. */ VexGuestPPC64State* gst = (VexGuestPPC64State*)gst_vanilla; UInt cr = LibVEX_GuestPPC64_get_CR( gst ); UInt cr0so = (cr >> 28) & 1; - canonical->sres = VG_(mk_SysRes_ppc64_linux)( gst->guest_GPR3, cr0so ); + UInt flag = gst->guest_syscall_flag; + + canonical->sres = VG_(mk_SysRes_ppc64_linux)( gst->guest_GPR3, cr0so, flag ); canonical->what = SsComplete; # elif defined(VGP_arm_linux) @@ -1188,20 +1205,34 @@ void putSyscallStatusIntoGuestState ( /*IN*/ ThreadId tid, # elif defined(VGP_ppc64be_linux) || defined(VGP_ppc64le_linux) VexGuestPPC64State* gst = (VexGuestPPC64State*)gst_vanilla; UInt old_cr = LibVEX_GuestPPC64_get_CR(gst); + UInt flag = gst->guest_syscall_flag; + vg_assert(canonical->what == SsComplete); - if (sr_isError(canonical->sres)) { - /* set CR0.SO */ - LibVEX_GuestPPC64_put_CR( old_cr | (1<<28), gst ); - gst->guest_GPR3 = sr_Err(canonical->sres); + if (flag == SC_FLAG) { + /* sc syscall */ + if (sr_isError(canonical->sres)) { + /* set CR0.SO */ + LibVEX_GuestPPC64_put_CR( old_cr | (1<<28), gst ); + gst->guest_GPR3 = sr_Err(canonical->sres); + } else { + /* clear CR0.SO */ + LibVEX_GuestPPC64_put_CR( old_cr & ~(1<<28), gst ); + gst->guest_GPR3 = sr_Res(canonical->sres); + } + VG_TRACK( post_reg_write, Vg_CoreSysCall, tid, + OFFSET_ppc64_GPR3, sizeof(UWord) ); + VG_TRACK( post_reg_write, Vg_CoreSysCall, tid, + OFFSET_ppc64_CR0_0, sizeof(UChar) ); } else { - /* clear CR0.SO */ - LibVEX_GuestPPC64_put_CR( old_cr & ~(1<<28), gst ); - gst->guest_GPR3 = sr_Res(canonical->sres); + /* scv system call instruction */ + if (sr_isError(canonical->sres)) + gst->guest_GPR3 = - (Long)canonical->sres._val; + else + gst->guest_GPR3 = canonical->sres._val; + + VG_TRACK( post_reg_write, Vg_CoreSysCall, tid, + OFFSET_ppc64_GPR3, sizeof(UWord) ); } - VG_TRACK( post_reg_write, Vg_CoreSysCall, tid, - OFFSET_ppc64_GPR3, sizeof(UWord) ); - VG_TRACK( post_reg_write, Vg_CoreSysCall, tid, - OFFSET_ppc64_CR0_0, sizeof(UChar) ); # elif defined(VGP_arm_linux) VexGuestARMState* gst = (VexGuestARMState*)gst_vanilla; @@ -1459,7 +1490,7 @@ void getSyscallArgLayout ( /*OUT*/SyscallArgLayout* layout ) layout->o_arg4 = OFFSET_ppc64_GPR6; layout->o_arg5 = OFFSET_ppc64_GPR7; layout->o_arg6 = OFFSET_ppc64_GPR8; - layout->uu_arg7 = -1; /* impossible value */ + layout->o_arg7 = OFFSET_ppc64_GPR9; layout->uu_arg8 = -1; /* impossible value */ #elif defined(VGP_arm_linux) @@ -2343,16 +2374,20 @@ void ML_(fixup_guest_state_to_restart_syscall) ( ThreadArchState* arch ) back over a syscall. sc == 44 00 00 02 + or + scv == 44 00 00 01 */ { UChar *p = (UChar *)arch->vex.guest_CIA; - if (p[3] != 0x44 || p[2] != 0x0 || p[1] != 0x0 || p[0] != 0x02) + if (!(p[3] == 0x44 && p[2] == 0x0 && p[1] == 0x0 + && (p[0] == 0x01 || p[0] == 0x02))) VG_(message)(Vg_DebugMsg, "?! restarting over syscall at %#llx %02x %02x %02x %02x\n", arch->vex.guest_CIA, p[3], p[2], p[1], p[0]); - vg_assert(p[3] == 0x44 && p[2] == 0x0 && p[1] == 0x0 && p[0] == 0x2); + vg_assert(p[3] == 0x44 && p[2] == 0x0 && p[1] == 0x0 + && (p[0] == 0x1 || p[0] == 0x2)); } #elif defined(VGP_arm_linux) @@ -2670,10 +2705,29 @@ VG_(fixup_guest_state_after_syscall_interrupted)( ThreadId tid, = ip < ML_(blksys_setup) || ip >= ML_(blksys_finished); in_setup_to_restart = ip >= ML_(blksys_setup) && ip < ML_(blksys_restart); + +#if defined(VGP_ppc64le_linux) + /* Starting with ISA 3.0, Power supports two system call instructions sc + and scv. The code in file syscall-ppc64[be|le]-linux.S uses an input + to call the requested system call. The definitions for blksys_restart + and blksys_complete must account for being at either of the two system + calls and account for the branch to lable 3 if the sc instruction was + called. at_restart is true if the ip is at either system call + instruction. in_complete_to_committed is true if the ip is between + blksys_complete and blksys_committed OR at the branch after the sc + instruction. The scv instruction is currently only supported on LE. */ + at_restart + = (ip == ML_(blksys_restart)) || ((ip-8) == ML_(blksys_restart)); + in_complete_to_committed + = (ip >= ML_(blksys_complete) && ip < ML_(blksys_committed)) || + ((ip+8) == ML_(blksys_complete)); +#else at_restart = ip == ML_(blksys_restart); in_complete_to_committed = ip >= ML_(blksys_complete) && ip < ML_(blksys_committed); +#endif + in_committed_to_finished = ip >= ML_(blksys_committed) && ip < ML_(blksys_finished); # elif defined(VGO_darwin) diff --git a/coregrind/pub_core_syscall.h b/coregrind/pub_core_syscall.h index 49b8586a80..ddcca3809f 100644 --- a/coregrind/pub_core_syscall.h +++ b/coregrind/pub_core_syscall.h @@ -31,6 +31,14 @@ #include "pub_core_basics.h" // VG_ macro +/* PPC64 supports two system call instructions. The flags are used to + identify which of the two system call instructions sc or scv is to be + used. The following flags must be consistently defined here and in + VEX/priv/guest_ppc_defs.h, in the do_syscall_WRK() assembly code + below and coregrind/m_syswrap/syscall-ppcle-linux.S code. */ +#define SC_FLAG 1 +#define SCV_FLAG 2 + //-------------------------------------------------------------------- // PURPOSE: This module contains the code for actually executing syscalls. //-------------------------------------------------------------------- @@ -51,19 +59,29 @@ extern SysRes VG_(do_syscall) ( UWord sysno, /* Macros make life easier. */ -#define vgPlain_do_syscall0(s) VG_(do_syscall)((s),0,0,0,0,0,0,0,0) +#if defined(VGP_ppc64be_linux) || defined(VGP_ppc64le_linux) +/* PPC64 uses the 7th argument to pass a flag indicating if the sc or scv + instruction is to be used for the system call. Need to set the flag to the + sc instruction by default. */ +#define A7 SC_FLAG +#else +#define A7 0 +#endif + +#define vgPlain_do_syscall0(s) VG_(do_syscall)((s),0,0,0,0,0,0,A7,0) + #define vgPlain_do_syscall1(s,a) VG_(do_syscall)((s),(a),\ - 0,0,0,0,0,0,0) + 0,0,0,0,0,A7,0) #define vgPlain_do_syscall2(s,a,b) VG_(do_syscall)((s),(a),(b),\ - 0,0,0,0,0,0) + 0,0,0,0,A7,0) #define vgPlain_do_syscall3(s,a,b,c) VG_(do_syscall)((s),(a),(b),(c),\ - 0,0,0,0,0) + 0,0,0,A7,0) #define vgPlain_do_syscall4(s,a,b,c,d) VG_(do_syscall)((s),(a),(b),(c),\ - (d),0,0,0,0) + (d),0,0,A7,0) #define vgPlain_do_syscall5(s,a,b,c,d,e) VG_(do_syscall)((s),(a),(b),(c),\ - (d),(e),0,0,0) + (d),(e),0,A7,0) #define vgPlain_do_syscall6(s,a,b,c,d,e,f) VG_(do_syscall)((s),(a),(b),(c),\ - (d),(e),(f),0,0) + (d),(e),(f),A7,0) #define vgPlain_do_syscall7(s,a,b,c,d,e,f,g) VG_(do_syscall)((s),(a),(b),(c),\ (d),(e),(f),(g),0) #define vgPlain_do_syscall8(s,a,b,c,d,e,f,g,h) VG_(do_syscall)((s),(a),(b),(c),\ @@ -72,7 +90,7 @@ extern SysRes VG_(do_syscall) ( UWord sysno, extern SysRes VG_(mk_SysRes_x86_linux) ( Int val ); extern SysRes VG_(mk_SysRes_amd64_linux) ( Long val ); extern SysRes VG_(mk_SysRes_ppc32_linux) ( UInt val, UInt cr0so ); -extern SysRes VG_(mk_SysRes_ppc64_linux) ( ULong val, ULong cr0so ); +extern SysRes VG_(mk_SysRes_ppc64_linux) ( ULong val, ULong cr0so, UInt flag ); extern SysRes VG_(mk_SysRes_arm_linux) ( Int val ); extern SysRes VG_(mk_SysRes_arm64_linux) ( Long val ); extern SysRes VG_(mk_SysRes_x86_darwin) ( UChar scclass, Bool isErr, @@ -104,7 +122,6 @@ extern SysRes VG_(mk_SysRes_SuccessEx) ( UWord val, UWord valEx ); extern const HChar* VG_(strerror) ( UWord errnum ); - #endif // __PUB_CORE_SYSCALL_H /*--------------------------------------------------------------------*/ diff --git a/memcheck/mc_machine.c b/memcheck/mc_machine.c index a95bfd9959..919c7fae88 100644 --- a/memcheck/mc_machine.c +++ b/memcheck/mc_machine.c @@ -395,6 +395,7 @@ static Int get_otrack_shadow_offset_wrk ( Int offset, Int szB ) return 0+ GOF(ACC_7_r2); if (o >= GOF(ACC_7_r3) && o+sz <= GOF(ACC_7_r3)+SZB(ACC_7_r3)) return 0+ GOF(ACC_7_r3); + if (o == GOF(syscall_flag) && sz == 4) return -1; VG_(printf)("MC_(get_otrack_shadow_offset)(ppc64)(off=%d,sz=%d)\n", offset,szB); diff --git a/none/tests/ppc64/Makefile.am b/none/tests/ppc64/Makefile.am index 00c65b5654..1636613345 100644 --- a/none/tests/ppc64/Makefile.am +++ b/none/tests/ppc64/Makefile.am @@ -58,7 +58,8 @@ EXTRA_DIST = \ test_isa_3_1_AT.vgtest test_isa_3_1_AT.stderr.exp test_isa_3_1_AT.stdout.exp \ subnormal_test.stderr.exp subnormal_test.stdout.exp \ subnormal_test.vgtest test_darn_inst.stderr.exp \ - test_darn_inst.stdout.exp test_darn_inst.vgtest + test_darn_inst.stdout.exp test_darn_inst.vgtest \ + scv_test.stderr.exp scv_test.stdout.exp scv_test.vgtest check_PROGRAMS = \ allexec \ @@ -72,7 +73,7 @@ check_PROGRAMS = \ subnormal_test test_darn_inst \ test_tm test_touch_tm data-cache-instructions \ power6_mf_gpr std_reg_imm \ - twi_tdi tw_td power6_bcmp + twi_tdi tw_td power6_bcmp scv_test # lmw, stmw, lswi, lswx, stswi, stswx compile (and run) only on big endian. if VGCONF_PLATFORMS_INCLUDE_PPC64BE_LINUX diff --git a/none/tests/ppc64/scv_test.c b/none/tests/ppc64/scv_test.c new file mode 100644 index 0000000000..d292c4be8f --- /dev/null +++ b/none/tests/ppc64/scv_test.c @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include + + +#define ASM_INPUT_0 "0" (r0) + +#define INTERNAL_SYSCALL_SCV(name, nr) \ + ({ \ + register long int r0 __asm__ ("r0"); \ + register long int r3 __asm__ ("r3"); \ + register long int r4 __asm__ ("r4"); \ + register long int r5 __asm__ ("r5"); \ + register long int r6 __asm__ ("r6"); \ + register long int r7 __asm__ ("r7"); \ + register long int r8 __asm__ ("r8"); \ + r0=name; \ + __asm__ __volatile__ \ + ("scv 0\n\t" \ + "0:" \ + : "=&r" (r0), \ + "=&r" (r3), "=&r" (r4), "=&r" (r5), \ + "=&r" (r6), "=&r" (r7), "=&r" (r8) \ + : ASM_INPUT_##nr \ + : "r9", "r10", "r11", "r12", \ + "cr0", "cr1", "cr5", "cr6", "cr7", "xer", \ + "lr", "ctr", "memory"); \ + r3; \ + }) + +#define INTERNAL_SYSCALL_SC(name, nr) \ + ({ \ + register long int r0 __asm__ ("r0"); \ + register long int r3 __asm__ ("r3"); \ + register long int r4 __asm__ ("r4"); \ + register long int r5 __asm__ ("r5"); \ + register long int r6 __asm__ ("r6"); \ + register long int r7 __asm__ ("r7"); \ + register long int r8 __asm__ ("r8"); \ + r0=name; \ + __asm__ __volatile__ \ + ("sc\n\t" \ + "mfcr %0\n\t" \ + "0:" \ + : "=&r" (r0), \ + "=&r" (r3), "=&r" (r4), "=&r" (r5), \ + "=&r" (r6), "=&r" (r7), "=&r" (r8) \ + : ASM_INPUT_##nr \ + : "r9", "r10", "r11", "r12", \ + "xer", "cr0", "ctr", "memory"); \ + r0 & (1 << 28) ? -r3 : r3; \ + }) + +#define PPC_FEATURE2_SCV 0x00100000 /* scv syscall enabled */ + +int +main(void) +{ + int result; + unsigned long hwcaps2_val; + + result = INTERNAL_SYSCALL_SC(__NR_gettid, 0); + + if (result < 0) { + printf("The sc instruction test unexpectedly failed\n"); + exit (-1); + } + + hwcaps2_val = getauxval(AT_HWCAP2); + + if ((hwcaps2_val & PPC_FEATURE2_SCV) == PPC_FEATURE2_SCV) { + /* system supports the scv instruction */ + result = INTERNAL_SYSCALL_SCV(__NR_gettid, 0); + + if (result < 0) { + printf("The scv instruction test unexpectedly failed\n"); + exit (-1); + } + } + + printf("Success\n"); + return 0; +} diff --git a/none/tests/ppc64/scv_test.stderr.exp b/none/tests/ppc64/scv_test.stderr.exp new file mode 100644 index 0000000000..139597f9cb --- /dev/null +++ b/none/tests/ppc64/scv_test.stderr.exp @@ -0,0 +1,2 @@ + + diff --git a/none/tests/ppc64/scv_test.stdout.exp b/none/tests/ppc64/scv_test.stdout.exp new file mode 100644 index 0000000000..35821117c8 --- /dev/null +++ b/none/tests/ppc64/scv_test.stdout.exp @@ -0,0 +1 @@ +Success diff --git a/none/tests/ppc64/scv_test.vgtest b/none/tests/ppc64/scv_test.vgtest new file mode 100644 index 0000000000..163d874a0b --- /dev/null +++ b/none/tests/ppc64/scv_test.vgtest @@ -0,0 +1,2 @@ +prereq: +prog: scv_test