diff --git a/sys/cddl/dev/kinst/amd64/kinst_isa.c b/sys/cddl/dev/kinst/amd64/kinst_isa.c --- a/sys/cddl/dev/kinst/amd64/kinst_isa.c +++ b/sys/cddl/dev/kinst/amd64/kinst_isa.c @@ -6,6 +6,7 @@ */ #include +#include #include #include @@ -39,6 +40,14 @@ #define KINST_F_JMP 0x0008 /* instruction is a %rip-relative jmp */ #define KINST_F_MOD_DIRECT 0x0010 /* operand is not a memory address */ +/* + * Per-CPU trampolines used when the interrupted thread is executing with + * interrupts disabled. If an interrupt is raised while executing a trampoline, + * the interrupt thread cannot safely overwrite its trampoline if it hits a + * kinst probe while executing the interrupt handler. + */ +DPCPU_DEFINE_STATIC(uint8_t *, intr_tramp); + /* * Map ModR/M register bits to a trapframe offset. */ @@ -185,7 +194,10 @@ } return (DTRACE_INVOP_CALL); } else { - tramp = curthread->t_kinst; + if ((frame->tf_rflags & PSL_I) == 0) + tramp = DPCPU_GET(intr_tramp); + else + tramp = curthread->t_kinst; if (tramp == NULL) { /* * A trampoline allocation failed, so this probe is @@ -495,7 +507,7 @@ struct kinst_probe *kp; dtrace_kinst_probedesc_t *pd; const char *func; - int error, n, off; + int error, instrsize, n, off; uint8_t *instr, *limit; pd = opaque; @@ -510,17 +522,37 @@ /* * Ignore functions not beginning with the usual function prologue. - * These might correspond to assembly routines with which we should not - * meddle. + * These might correspond to exception handlers with which we should not + * meddle. This does however exclude functions which can be safely + * traced, such as cpu_switch(). */ if (*instr != KINST_PUSHL_RBP) return (0); n = 0; while (instr < limit) { + instrsize = dtrace_instr_size(instr); off = (int)(instr - (uint8_t *)symval->value); if (pd->kpd_off != -1 && off != pd->kpd_off) { - instr += dtrace_instr_size(instr); + instr += instrsize; + continue; + } + + /* + * Check for instructions which may enable interrupts. Such + * instructions are tricky to trace since it is unclear whether + * to use the per-thread or per-CPU trampolines. Since they are + * rare, we don't bother to implement special handling for them. + * + * If the caller specified an offset, return an error, otherwise + * silently ignore the instruction so that it remains possible + * to enable all instructions in a function. + */ + if (instrsize == 1 && + (instr[0] == KINST_POPF || instr[0] == KINST_STI)) { + if (pd->kpd_off != -1) + return (EINVAL); + instr += instrsize; continue; } @@ -554,3 +586,34 @@ return (0); } + +int +kinst_md_init(void) +{ + uint8_t *tramp; + int cpu; + + CPU_FOREACH(cpu) { + tramp = kinst_trampoline_alloc(M_WAITOK); + if (tramp == NULL) + return (ENOMEM); + DPCPU_ID_SET(cpu, intr_tramp, tramp); + } + + return (0); +} + +void +kinst_md_deinit(void) +{ + uint8_t *tramp; + int cpu; + + CPU_FOREACH(cpu) { + tramp = DPCPU_ID_GET(cpu, intr_tramp); + if (tramp != NULL) { + kinst_trampoline_dealloc(DPCPU_ID_GET(cpu, intr_tramp)); + DPCPU_ID_SET(cpu, intr_tramp, NULL); + } + } +} diff --git a/sys/cddl/dev/kinst/kinst.h b/sys/cddl/dev/kinst/kinst.h --- a/sys/cddl/dev/kinst/kinst.h +++ b/sys/cddl/dev/kinst/kinst.h @@ -57,6 +57,9 @@ uint8_t *kinst_trampoline_alloc(int); void kinst_trampoline_dealloc(uint8_t *); +int kinst_md_init(void); +void kinst_md_deinit(void); + #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_KINST); #endif /* MALLOC_DECLARE */ diff --git a/sys/cddl/dev/kinst/kinst.c b/sys/cddl/dev/kinst/kinst.c --- a/sys/cddl/dev/kinst/kinst.c +++ b/sys/cddl/dev/kinst/kinst.c @@ -180,10 +180,16 @@ error = kinst_trampoline_init(); if (error != 0) return (error); + error = kinst_md_init(); + if (error != 0) { + kinst_trampoline_deinit(); + return (error); + } error = dtrace_register("kinst", &kinst_attr, DTRACE_PRIV_USER, NULL, &kinst_pops, NULL, &kinst_id); if (error != 0) { + kinst_md_deinit(); kinst_trampoline_deinit(); return (error); } @@ -201,6 +207,7 @@ kinst_unload(void *dummy) { free(kinst_probetab, M_KINST); + kinst_md_deinit(); kinst_trampoline_deinit(); dtrace_invop_remove(kinst_invop); destroy_dev(kinst_cdev);