git: 2517b2085b58 - main - kinst: use per-probe trampolines in riscv
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 19 Jul 2023 14:59:53 UTC
The branch main has been updated by christos: URL: https://cgit.FreeBSD.org/src/commit/?id=2517b2085b58e0ca4837faecd94b91ad80872547 commit 2517b2085b58e0ca4837faecd94b91ad80872547 Author: Christos Margiolis <christos@FreeBSD.org> AuthorDate: 2023-07-19 14:57:59 +0000 Commit: Christos Margiolis <christos@FreeBSD.org> CommitDate: 2023-07-19 14:57:59 +0000 kinst: use per-probe trampolines in riscv Reviewed by: markj Approved by: markj (mentor) Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D40963 --- sys/cddl/dev/kinst/riscv/kinst_isa.c | 113 +++++++++++++---------------------- 1 file changed, 42 insertions(+), 71 deletions(-) diff --git a/sys/cddl/dev/kinst/riscv/kinst_isa.c b/sys/cddl/dev/kinst/riscv/kinst_isa.c index 8bd4449a027a..9c1f4a239f83 100644 --- a/sys/cddl/dev/kinst/riscv/kinst_isa.c +++ b/sys/cddl/dev/kinst/riscv/kinst_isa.c @@ -14,20 +14,7 @@ #include "kinst.h" -/* - * 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); - -/* - * The double-breakpoint mechanism needs to save the current probe for the next - * call to kinst_invop(). As with per-CPU trampolines, this also has to be done - * per-CPU when interrupts are disabled. - */ -DPCPU_DEFINE_STATIC(struct kinst_probe *, intr_probe); +DPCPU_DEFINE_STATIC(struct kinst_cpu_state, kinst_state); #define _MATCH_REG(reg) \ (offsetof(struct trapframe, tf_ ## reg) / sizeof(register_t)) @@ -78,7 +65,7 @@ kinst_c_regoff(struct trapframe *frame, int n) #undef _MATCH_REG static int -kinst_emulate(struct trapframe *frame, struct kinst_probe *kp) +kinst_emulate(struct trapframe *frame, const struct kinst_probe *kp) { kinst_patchval_t instr = kp->kp_savedval; register_t prevpc; @@ -244,23 +231,23 @@ kinst_emulate(struct trapframe *frame, struct kinst_probe *kp) } static int -kinst_jump_next_instr(struct trapframe *frame, struct kinst_probe *kp) +kinst_jump_next_instr(struct trapframe *frame, const struct kinst_probe *kp) { - frame->tf_sepc = (register_t)((uint8_t *)kp->kp_patchpoint + + frame->tf_sepc = (register_t)((const uint8_t *)kp->kp_patchpoint + kp->kp_md.instlen); return (MATCH_C_NOP); } static void -kinst_trampoline_populate(struct kinst_probe *kp, uint8_t *tramp) +kinst_trampoline_populate(struct kinst_probe *kp) { static uint16_t nop = MATCH_C_NOP; static uint32_t ebreak = MATCH_EBREAK; int ilen; ilen = kp->kp_md.instlen; - kinst_memcpy(tramp, &kp->kp_savedval, ilen); + kinst_memcpy(kp->kp_tramp, &kp->kp_savedval, ilen); /* * Since we cannot encode large displacements in a single instruction @@ -273,9 +260,9 @@ kinst_trampoline_populate(struct kinst_probe *kp, uint8_t *tramp) * Add a NOP after a compressed instruction for padding. */ if (ilen == INSN_C_SIZE) - kinst_memcpy(&tramp[ilen], &nop, INSN_C_SIZE); + kinst_memcpy(&kp->kp_tramp[ilen], &nop, INSN_C_SIZE); - kinst_memcpy(&tramp[INSN_SIZE], &ebreak, INSN_SIZE); + kinst_memcpy(&kp->kp_tramp[INSN_SIZE], &ebreak, INSN_SIZE); fence_i(); } @@ -306,27 +293,26 @@ int kinst_invop(uintptr_t addr, struct trapframe *frame, uintptr_t scratch) { solaris_cpu_t *cpu; - struct kinst_probe *kp; - uint8_t *tramp; + struct kinst_cpu_state *ks; + const struct kinst_probe *kp; - /* - * Use per-CPU trampolines and probes if the thread executing the - * instruction was executing with interrupts disabled. - */ - if ((frame->tf_sstatus & SSTATUS_SPIE) == 0) { - tramp = DPCPU_GET(intr_tramp); - kp = DPCPU_GET(intr_probe); - } else { - tramp = curthread->t_kinst_tramp; - kp = curthread->t_kinst_curprobe; - } + ks = DPCPU_PTR(kinst_state); /* * Detect if the breakpoint was triggered by the trampoline, and * manually set the PC to the next instruction. */ - if (addr == (uintptr_t)(tramp + INSN_SIZE)) - return (kinst_jump_next_instr(frame, kp)); + if (ks->state == KINST_PROBE_FIRED && + addr == (uintptr_t)(ks->kp->kp_tramp + INSN_SIZE)) { + /* + * Restore interrupts if they were enabled prior to the first + * breakpoint. + */ + if ((ks->status & SSTATUS_SPIE) != 0) + frame->tf_sstatus |= SSTATUS_SPIE; + ks->state = KINST_PROBE_ARMED; + return (kinst_jump_next_instr(frame, ks->kp)); + } LIST_FOREACH(kp, KINST_GETPROBE(addr), kp_hashnext) { if ((uintptr_t)kp->kp_patchpoint == addr) @@ -343,26 +329,16 @@ kinst_invop(uintptr_t addr, struct trapframe *frame, uintptr_t scratch) if (kp->kp_md.emulate) return (kinst_emulate(frame, kp)); - if (tramp == NULL) { - /* - * A trampoline allocation failed, so this probe is - * effectively disabled. Restore the original - * instruction. - * - * We can't safely print anything here, but the - * trampoline allocator should have left a breadcrumb in - * the dmesg. - */ - kinst_patch_tracepoint(kp, kp->kp_savedval); - frame->tf_sepc = (register_t)kp->kp_patchpoint; - } else { - kinst_trampoline_populate(kp, tramp); - frame->tf_sepc = (register_t)tramp; - if ((frame->tf_sstatus & SSTATUS_SPIE) == 0) - DPCPU_SET(intr_probe, kp); - else - curthread->t_kinst_curprobe = kp; - } + ks->state = KINST_PROBE_FIRED; + ks->kp = kp; + + /* + * Cache the current SSTATUS and clear interrupts for the + * duration of the double breakpoint. + */ + ks->status = frame->tf_sstatus; + frame->tf_sstatus &= ~SSTATUS_SPIE; + frame->tf_sepc = (register_t)kp->kp_tramp; return (MATCH_C_NOP); } @@ -427,6 +403,9 @@ kinst_instr_dissect(struct kinst_probe *kp, int instrsize) break; } } + + if (!kpmd->emulate) + kinst_trampoline_populate(kp); } static bool @@ -556,6 +535,10 @@ kinst_make_probe(linker_file_t lf, int symindx, linker_symval_t *symval, kp->kp_patchval = KINST_PATCHVAL; else kp->kp_patchval = KINST_C_PATCHVAL; + if ((kp->kp_tramp = kinst_trampoline_alloc(M_WAITOK)) == NULL) { + KINST_LOG("cannot allocate trampoline for %p", instr); + return (ENOMEM); + } kinst_instr_dissect(kp, instrsize); kinst_probe_create(kp, lf); @@ -571,14 +554,12 @@ cont: int kinst_md_init(void) { - uint8_t *tramp; + struct kinst_cpu_state *ks; int cpu; CPU_FOREACH(cpu) { - tramp = kinst_trampoline_alloc(M_WAITOK); - if (tramp == NULL) - return (ENOMEM); - DPCPU_ID_SET(cpu, intr_tramp, tramp); + ks = DPCPU_PTR(kinst_state); + ks->state = KINST_PROBE_ARMED; } return (0); @@ -587,16 +568,6 @@ kinst_md_init(void) 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(tramp); - DPCPU_ID_SET(cpu, intr_tramp, NULL); - } - } } /*