diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h b/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h index c15cc39189b..5cad5a3b18c 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h +++ b/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h @@ -2440,6 +2440,10 @@ extern void dtrace_helpers_destroy(proc_t *); #define DTRACE_INVOP_NOP 4 #define DTRACE_INVOP_RET 5 +#if defined(__amd64) +#define DTRACE_INVOP_CALL 6 +#endif + #elif defined(__powerpc__) #define DTRACE_INVOP_BCTR 1 diff --git a/sys/cddl/dev/dtrace/amd64/dtrace_asm.S b/sys/cddl/dev/dtrace/amd64/dtrace_asm.S index 9a83c87dda0..ffeaa787bd5 100644 --- a/sys/cddl/dev/dtrace/amd64/dtrace_asm.S +++ b/sys/cddl/dev/dtrace/amd64/dtrace_asm.S @@ -58,6 +58,8 @@ swapgs; \ 1: addq $TF_RIP,%rsp; +.globl dtrace_invop_callsite +.type dtrace_invop_callsite,@function ENTRY(dtrace_invop_start) @@ -69,11 +71,22 @@ movq TF_RIP(%rsp), %rdi decq %rdi movq %rsp, %rsi - movq TF_RAX(%rsp), %rdx + + /* + * Allocate some scratch space to let the invop handler return a value. + * This is needed when emulating "call" instructions. + */ + subq $16, %rsp + movq %rsp, %rdx + call dtrace_invop - ENTRY(dtrace_invop_callsite) +dtrace_invop_callsite: + addq $16, %rsp + cmpl $DTRACE_INVOP_PUSHL_EBP, %eax je bp_push + cmpl $DTRACE_INVOP_CALL, %eax + je bp_call cmpl $DTRACE_INVOP_LEAVE, %eax je bp_leave cmpl $DTRACE_INVOP_NOP, %eax @@ -109,6 +122,40 @@ bp_push: iretq /* return from interrupt */ /*NOTREACHED*/ +bp_call: + /* + * Emulate a "call" instruction. The invop handler must have already + * updated the saved copy of %rip in the register set. It's our job to + * pull the hardware-saved registers down to make space for the return + * address, which is provided by the invop handler in our scratch + * space. + */ + INTR_POP + subq $16, %rsp /* make room for %rbp */ + pushq %rax /* push temp */ + pushq %rbx /* push temp */ + + movq 32(%rsp), %rax /* load calling RIP */ + movq %rax, 16(%rsp) /* store calling RIP */ + movq 40(%rsp), %rax /* load calling CS */ + movq %rax, 24(%rsp) /* store calling CS */ + movq 48(%rsp), %rax /* load calling RFLAGS */ + movq %rax, 32(%rsp) /* store calling RFLAGS */ + movq 56(%rsp), %rax /* load calling RSP */ + subq $8, %rax /* make room for return address */ + movq %rax, 40(%rsp) /* store calling RSP */ + movq 64(%rsp), %rax /* load calling SS */ + movq %rax, 48(%rsp) /* store calling SS */ + + movq -(TF_RIP - 16)(%rsp), %rax /* load return address */ + movq 40(%rsp), %rbx /* reload calling RSP */ + movq %rax, (%rbx) /* store return address */ + + popq %rbx /* pop temp */ + popq %rax /* pop temp */ + iretq /* return from interrupt */ + /*NOTREACHED*/ + bp_leave: /* * We must emulate a "leave", which is the same as a "movq %rbp, %rsp" diff --git a/sys/cddl/dev/dtrace/amd64/dtrace_subr.c b/sys/cddl/dev/dtrace/amd64/dtrace_subr.c index 4f9d9995cba..f4fb70f80a6 100644 --- a/sys/cddl/dev/dtrace/amd64/dtrace_subr.c +++ b/sys/cddl/dev/dtrace/amd64/dtrace_subr.c @@ -50,7 +50,7 @@ extern void dtrace_getnanotime(struct timespec *tsp); extern int (*dtrace_invop_jump_addr)(struct trapframe *); -int dtrace_invop(uintptr_t, struct trapframe *, uintptr_t); +int dtrace_invop(uintptr_t, struct trapframe *, void **); int dtrace_invop_start(struct trapframe *frame); void dtrace_invop_init(void); void dtrace_invop_uninit(void); @@ -63,15 +63,16 @@ typedef struct dtrace_invop_hdlr { dtrace_invop_hdlr_t *dtrace_invop_hdlr; int -dtrace_invop(uintptr_t addr, struct trapframe *frame, uintptr_t eax) +dtrace_invop(uintptr_t addr, struct trapframe *frame, void **scratch) { dtrace_invop_hdlr_t *hdlr; int rval; - for (hdlr = dtrace_invop_hdlr; hdlr != NULL; hdlr = hdlr->dtih_next) - if ((rval = hdlr->dtih_func(addr, frame, eax)) != 0) + for (hdlr = dtrace_invop_hdlr; hdlr != NULL; hdlr = hdlr->dtih_next) { + rval = hdlr->dtih_func(addr, frame, (uintptr_t)scratch); + if (rval != 0) return (rval); - + } return (0); }