Messages in this thread Patch in this message | | | From | Qing Zhang <> | Subject | [PATCH 2/2] LoongArch: Add ptrace single step support | Date | Wed, 8 Feb 2023 18:08:37 +0800 |
| |
Use the generic ptrace_resume code for PTRACE_SYSCALL, PTRACE_CONT, PTRACE_KILL and PTRACE_SINGLESTEP. This implies defining arch_has_single_step in and implementing the user_enable_single_step and user_disable_single_step functions.
LongArch has no hardware single-step register. the hardware single-step function multiplex fetch instruction watchpoint(FWPS) and specifies that the next instruction must trigger the watch exception by setting the mask bit.
Signed-off-by: Qing Zhang <zhangqing@loongson.cn> --- arch/loongarch/include/asm/ptrace.h | 2 + arch/loongarch/kernel/hw_breakpoint.c | 13 ++++- arch/loongarch/kernel/ptrace.c | 68 +++++++++++++++++++++++++++ arch/loongarch/kernel/traps.c | 27 +++++++++-- 4 files changed, 103 insertions(+), 7 deletions(-)
diff --git a/arch/loongarch/include/asm/ptrace.h b/arch/loongarch/include/asm/ptrace.h index 58596c4f8a0f..66a0e6c480a3 100644 --- a/arch/loongarch/include/asm/ptrace.h +++ b/arch/loongarch/include/asm/ptrace.h @@ -150,4 +150,6 @@ static inline void user_stack_pointer_set(struct pt_regs *regs, regs->regs[3] = val; } +#define arch_has_single_step() (1) + #endif /* _ASM_PTRACE_H */ diff --git a/arch/loongarch/kernel/hw_breakpoint.c b/arch/loongarch/kernel/hw_breakpoint.c index 4ba1e730f3e9..bcecc076b420 100644 --- a/arch/loongarch/kernel/hw_breakpoint.c +++ b/arch/loongarch/kernel/hw_breakpoint.c @@ -497,9 +497,18 @@ arch_initcall(arch_hw_breakpoint_init); void hw_breakpoint_thread_switch(struct task_struct *next) { struct pt_regs *regs = task_pt_regs(next); + u64 addr, mask; - /* Update breakpoints/watchpoints. */ - update_bp_registers(regs, 1); + if (test_bit(TIF_SINGLESTEP, &task_thread_info(next)->flags)) { + addr = read_wb_reg(CSR_CFG_ADDR, 0, 0); + mask = read_wb_reg(CSR_CFG_MASK, 0, 0); + if ((task_pt_regs(next)->csr_era & ~mask) == (addr & ~mask)) + csr_write32(0x10000, LOONGARCH_CSR_FWPS); + regs->csr_prmd |= CSR_PRMD_PWE; + } else { + /* Update breakpoints/watchpoints. */ + update_bp_registers(regs, 1); + } } void hw_breakpoint_pmu_read(struct perf_event *bp) diff --git a/arch/loongarch/kernel/ptrace.c b/arch/loongarch/kernel/ptrace.c index bee4194177fd..52a3ee4366f4 100644 --- a/arch/loongarch/kernel/ptrace.c +++ b/arch/loongarch/kernel/ptrace.c @@ -20,6 +20,7 @@ #include <linux/context_tracking.h> #include <linux/elf.h> #include <linux/errno.h> +#include <linux/hw_breakpoint.h> #include <linux/mm.h> #include <linux/ptrace.h> #include <linux/regset.h> @@ -30,6 +31,7 @@ #include <linux/stddef.h> #include <linux/seccomp.h> #include <linux/uaccess.h> +#include <linux/thread_info.h> #include <asm/byteorder.h> #include <asm/cpu.h> @@ -39,6 +41,7 @@ #include <asm/page.h> #include <asm/pgtable.h> #include <asm/processor.h> +#include <asm/ptrace.h> #include <asm/reg.h> #include <asm/syscall.h> @@ -541,3 +544,68 @@ long arch_ptrace(struct task_struct *child, long request, return ret; } + +void ptrace_triggered(struct perf_event *bp, + struct perf_sample_data *data, struct pt_regs *regs) +{ + struct perf_event_attr attr; + + attr = bp->attr; + attr.disabled = true; + modify_user_hw_breakpoint(bp, &attr); +} + +static int set_single_step(struct task_struct *tsk, unsigned long addr) +{ + struct thread_struct *thread = &tsk->thread; + struct perf_event *bp; + struct perf_event_attr attr; + struct arch_hw_breakpoint *info; + + bp = thread->hbp_break[0]; + if (!bp) { + ptrace_breakpoint_init(&attr); + + attr.bp_addr = addr; + attr.bp_len = HW_BREAKPOINT_LEN_8; + attr.bp_type = HW_BREAKPOINT_X; + + bp = register_user_hw_breakpoint(&attr, ptrace_triggered, + NULL, tsk); + if (IS_ERR(bp)) + return PTR_ERR(bp); + + thread->hbp_break[0] = bp; + } else { + int err; + + attr = bp->attr; + attr.bp_addr = addr; + /* reenable breakpoint */ + attr.disabled = false; + err = modify_user_hw_breakpoint(bp, &attr); + if (unlikely(err)) + return err; + + csr_write64(attr.bp_addr, LOONGARCH_CSR_IB0ADDR); + } + info = counter_arch_bp(bp); + info->mask = 0xffffffffffff; + + return 0; +} + +/* ptrace API */ +void user_enable_single_step(struct task_struct *task) +{ + struct thread_info *ti = task_thread_info(task); + + set_single_step(task, task_pt_regs(task)->csr_era); + task->thread.single_step = task_pt_regs(task)->csr_era; + set_ti_thread_flag(ti, TIF_SINGLESTEP); +} + +void user_disable_single_step(struct task_struct *task) +{ + clear_tsk_thread_flag(task, TIF_SINGLESTEP); +} diff --git a/arch/loongarch/kernel/traps.c b/arch/loongarch/kernel/traps.c index 70085d83476d..0c4c6dcf9a4e 100644 --- a/arch/loongarch/kernel/traps.c +++ b/arch/loongarch/kernel/traps.c @@ -512,13 +512,30 @@ asmlinkage void noinstr do_watch(struct pt_regs *regs) prev_state = exception_enter(); + if (test_tsk_thread_flag(current, TIF_SINGLESTEP)) { + int llbit = (csr_read32(LOONGARCH_CSR_LLBCTL) & 0x1); + unsigned long pc = regs->csr_era; + + if (llbit) { + csr_write32(0x10000, LOONGARCH_CSR_FWPS); + csr_write32(0x4, LOONGARCH_CSR_LLBCTL); + } else if (pc == current->thread.single_step) { + csr_write32(0x10000, LOONGARCH_CSR_FWPS); + } else { + csr_write32(0x10000, LOONGARCH_CSR_FWPS); + pr_info("Single step in progress\n"); + force_sig(SIGTRAP); + } + } else { #ifdef CONFIG_HAVE_HW_BREAKPOINT - if (breakpoint_handler(regs)) - return; - if (watchpoint_handler(regs)) - return; + if (breakpoint_handler(regs)) + return; + if (watchpoint_handler(regs)) + return; + force_sig(SIGTRAP); #endif - force_sig(SIGTRAP); + } + exception_exit(prev_state); return; } -- 2.36.0
| |