// SPDX-License-Identifier: GPL-2.0 /* ptrace.c */ /* By Ross Biro 1/23/92 */ /* edited by Linus Torvalds */ /* mangled further by Bob Manson (manson@santafe.edu) */ /* more mutilation by David Mosberger (davidm@azstarnet.com) */ #include #include #include #include "proto.h" #define CREATE_TRACE_POINTS #include #define BREAKINST 0x00000080 /* sys_call bpt */ /* * does not yet catch signals sent when the child dies. * in exit.c or in signal.c. */ /* * Processes always block with the following stack-layout: * * +================================+ <---- task + 2*PAGE_SIZE * | HMcode saved frame (ps, pc, | ^ * | gp, a0, a1, a2) | | * +================================+ | struct pt_regs * | | | * | frame generated by SAVE_ALL | | * | | v * +================================+ * | | ^ * | frame saved by do_switch_stack | | struct switch_stack * | | v * +================================+ */ /* * The following table maps a register index into the stack offset at * which the register is saved. Register indices are 0-31 for integer * regs, 32-63 for fp regs, and 64 for the pc. Notice that sp and * zero have no stack-slot and need to be treated specially (see * get_reg/put_reg below). */ enum { REG_R0 = 0, REG_F0 = 32, REG_FPCR = 63, REG_PC = 64, REG_SP = 30, REG_PS = 31, REG_GP = 29 }; #define PT_REG(reg) \ (PAGE_SIZE * 2 - sizeof(struct pt_regs) + offsetof(struct pt_regs, reg)) #define SW_REG(reg) \ (PAGE_SIZE * 2 - sizeof(struct pt_regs) - sizeof(struct switch_stack) \ + offsetof(struct switch_stack, reg)) #define FP_REG(fp_regno, vector_regno) \ (fp_regno * 32 + vector_regno * 8) static int regoff[] = { PT_REG(r0), PT_REG(r1), PT_REG(r2), PT_REG(r3), PT_REG(r4), PT_REG(r5), PT_REG(r6), PT_REG(r7), PT_REG(r8), SW_REG(r9), SW_REG(r10), SW_REG(r11), SW_REG(r12), SW_REG(r13), SW_REG(r14), SW_REG(r15), PT_REG(r16), PT_REG(r17), PT_REG(r18), PT_REG(r19), PT_REG(r20), PT_REG(r21), PT_REG(r22), PT_REG(r23), PT_REG(r24), PT_REG(r25), PT_REG(r26), PT_REG(r27), PT_REG(r28), PT_REG(gp), -1, -1 }; #define PCB_OFF(var) offsetof(struct pcb_struct, var) static int pcboff[] = { [USP] = PCB_OFF(usp), [UNIQUE] = PCB_OFF(unique), [DA_MATCH] = PCB_OFF(da_match), [DA_MASK] = PCB_OFF(da_mask), [DV_MATCH] = PCB_OFF(dv_match), [DV_MASK] = PCB_OFF(dv_mask), [DC_CTL] = PCB_OFF(dc_ctl) }; static unsigned long zero; /* * Get address of register REGNO in task TASK. */ static unsigned long * get_reg_addr(struct task_struct *task, unsigned long regno) { unsigned long *addr; int fp_regno, vector_regno; switch (regno) { case USP: case UNIQUE: case DA_MATCH: case DA_MASK: case DV_MATCH: case DV_MASK: case DC_CTL: addr = (void *)task_thread_info(task) + pcboff[regno]; break; case REG_BASE ... REG_END: addr = (void *)task_thread_info(task) + regoff[regno]; break; case FPREG_BASE ... FPREG_END: fp_regno = regno - FPREG_BASE; vector_regno = 0; addr = (void *)((unsigned long)&task->thread.ctx_fp + FP_REG(fp_regno, vector_regno)); break; case VECREG_BASE ... VECREG_END: /* * return addr for zero value if we catch vectors of f31 * v0 and v3 of f31 are not in this range so ignore them */ if (regno == F31_V1 || regno == F31_V2) { addr = &zero; break; } fp_regno = (regno - VECREG_BASE) & 0x1f; vector_regno = 1 + ((regno - VECREG_BASE) >> 5); addr = (void *)((unsigned long)&task->thread.ctx_fp + FP_REG(fp_regno, vector_regno)); break; case FPCR: addr = (void *)&task->thread.fpcr; break; case PC: addr = (void *)task_thread_info(task) + PT_REG(pc); break; default: addr = &zero; } return addr; } /* * Get contents of register REGNO in task TASK. */ unsigned long get_reg(struct task_struct *task, unsigned long regno) { return *get_reg_addr(task, regno); } /* * Write contents of register REGNO in task TASK. */ static int put_reg(struct task_struct *task, unsigned long regno, unsigned long data) { *get_reg_addr(task, regno) = data; return 0; } static inline int read_int(struct task_struct *task, unsigned long addr, int *data) { int copied = access_process_vm(task, addr, data, sizeof(int), FOLL_FORCE); return (copied == sizeof(int)) ? 0 : -EIO; } static inline int write_int(struct task_struct *task, unsigned long addr, int data) { int copied = access_process_vm(task, addr, &data, sizeof(int), FOLL_FORCE | FOLL_WRITE); return (copied == sizeof(int)) ? 0 : -EIO; } /* * Set breakpoint. */ int ptrace_set_bpt(struct task_struct *child) { int displ, i, res, reg_b, nsaved = 0; unsigned int insn, op_code; unsigned long pc; pc = get_reg(child, REG_PC); res = read_int(child, pc, (int *)&insn); if (res < 0) return res; op_code = insn >> 26; /* br bsr beq bne blt ble bgt bge blbc blbs fbeq fbne fblt fble fbgt fbge */ if ((1UL << op_code) & 0x3fff000000000030UL) { /* * It's a branch: instead of trying to figure out * whether the branch will be taken or not, we'll put * a breakpoint at either location. This is simpler, * more reliable, and probably not a whole lot slower * than the alternative approach of emulating the * branch (emulation can be tricky for fp branches). */ displ = ((s32)(insn << 11)) >> 9; task_thread_info(child)->bpt_addr[nsaved++] = pc + 4; if (displ) /* guard against unoptimized code */ task_thread_info(child)->bpt_addr[nsaved++] = pc + 4 + displ; /*call ret jmp*/ } else if (op_code >= 0x1 && op_code <= 0x3) { reg_b = (insn >> 16) & 0x1f; task_thread_info(child)->bpt_addr[nsaved++] = get_reg(child, reg_b); } else { task_thread_info(child)->bpt_addr[nsaved++] = pc + 4; } /* install breakpoints: */ for (i = 0; i < nsaved; ++i) { res = read_int(child, task_thread_info(child)->bpt_addr[i], (int *)&insn); if (res < 0) return res; task_thread_info(child)->bpt_insn[i] = insn; res = write_int(child, task_thread_info(child)->bpt_addr[i], BREAKINST); if (res < 0) return res; } task_thread_info(child)->bpt_nsaved = nsaved; return 0; } /* * Ensure no single-step breakpoint is pending. Returns non-zero * value if child was being single-stepped. */ int ptrace_cancel_bpt(struct task_struct *child) { int i, nsaved = task_thread_info(child)->bpt_nsaved; task_thread_info(child)->bpt_nsaved = 0; if (nsaved > 2) { printk("%s: bogus nsaved: %d!\n", __func__, nsaved); nsaved = 2; } for (i = 0; i < nsaved; ++i) { write_int(child, task_thread_info(child)->bpt_addr[i], task_thread_info(child)->bpt_insn[i]); } return (nsaved != 0); } void user_enable_single_step(struct task_struct *child) { /* Mark single stepping. */ task_thread_info(child)->bpt_nsaved = -1; } void user_disable_single_step(struct task_struct *child) { ptrace_cancel_bpt(child); } /* * Called by kernel/ptrace.c when detaching.. * * Make sure the single step bit is not set. */ void ptrace_disable(struct task_struct *child) { user_disable_single_step(child); } int ptrace_getregs(struct task_struct *child, __s64 __user *data) { int ret, retval = 0; int i; unsigned long regval; if (!access_ok(data, sizeof(long) * 33)) return -EIO; /* r0-r15 */ for (i = 0; i < 16; i++) { regval = get_reg(child, i); retval |= __put_user((long)regval, data + i); } /* r19-r28 */ for (i = 19; i < 29; i++) { regval = get_reg(child, i); retval |= __put_user((long)regval, data + i - 3); } /*SP, PS ,PC,GP*/ retval |= __put_user((long)(get_reg(child, REG_SP)), data + EF_SP); retval |= __put_user((long)(get_reg(child, REG_PS)), data + EF_PS); retval |= __put_user((long)(get_reg(child, REG_PC)), data + EF_PC); retval |= __put_user((long)(get_reg(child, REG_GP)), data + EF_GP); /* r16-r18 */ retval |= __put_user((long)(get_reg(child, 16)), data + EF_A0); retval |= __put_user((long)(get_reg(child, 17)), data + EF_A1); retval |= __put_user((long)(get_reg(child, 18)), data + EF_A2); ret = retval ? -EIO : 0; return ret; } int ptrace_setregs(struct task_struct *child, __s64 __user *data) { int ret, retval = 0; int i; unsigned long regval; if (!access_ok(data, sizeof(long) * 33)) return -EIO; /* r0-r15 */ for (i = 0; i < 16; i++) { retval |= __get_user(regval, data + i); ret = put_reg(child, i, regval); } /* r19-r28 */ for (i = 19; i < 29; i++) { retval |= __get_user(regval, data + i - 3); ret = put_reg(child, i, regval); } /*SP, PS ,PC,GP*/ retval |= __get_user(regval, data + EF_SP); ret = put_reg(child, REG_SP, regval); retval |= __get_user(regval, data + EF_PS); ret = put_reg(child, REG_PS, regval); retval |= __get_user(regval, data + EF_PC); ret = put_reg(child, REG_PC, regval); retval |= __get_user(regval, data + EF_GP); ret = put_reg(child, REG_GP, regval); /* r16-r18 */ retval |= __get_user(regval, data + EF_A0); ret = put_reg(child, 16, regval); retval |= __get_user(regval, data + EF_A1); ret = put_reg(child, 17, regval); retval |= __get_user(regval, data + EF_A2); ret = put_reg(child, 18, regval); ret = retval ? -EIO : 0; return 0; } int ptrace_getfpregs(struct task_struct *child, __s64 __user *data) { int ret, retval = 0; int i; unsigned long regval; if (!access_ok(data, sizeof(long) * 32)) return -EIO; /* fp0-fp31 */ for (i = 0; i < 32; i++) { regval = get_reg(child, REG_F0 + i); retval |= __put_user((long)regval, data + i); } ret = retval ? -EIO : 0; return 0; } int ptrace_setfpregs(struct task_struct *child, __s64 __user *data) { int ret, retval = 0; int i; unsigned long regval; if (!access_ok(data, sizeof(long) * 32)) return -EIO; /* fp0-fp31 */ for (i = 0; i < 32; i++) { retval |= __get_user(regval, data + i); ret = put_reg(child, REG_F0 + i, regval); } return ret; } long arch_ptrace(struct task_struct *child, long request, unsigned long addr, unsigned long data) { unsigned long tmp; size_t copied; long ret; void __user *datavp = (void __user *) data; switch (request) { /* When I and D space are separate, these will need to be fixed. */ case PTRACE_PEEKTEXT: /* read word at location addr. */ case PTRACE_PEEKDATA: copied = access_process_vm(child, addr, &tmp, sizeof(tmp), FOLL_FORCE); ret = -EIO; if (copied != sizeof(tmp)) break; force_successful_syscall_return(); ret = tmp; break; /* Read register number ADDR. */ case PTRACE_PEEKUSR: force_successful_syscall_return(); ret = get_reg(child, addr); break; /* When I and D space are separate, this will have to be fixed. */ case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKEDATA: ret = generic_ptrace_pokedata(child, addr, data); break; case PTRACE_POKEUSR: /* write the specified register */ ret = put_reg(child, addr, data); break; case PTRACE_GETREGS: ret = ptrace_getregs(child, datavp); break; case PTRACE_SETREGS: ret = ptrace_setregs(child, datavp); break; case PTRACE_GETFPREGS: ret = ptrace_getfpregs(child, datavp); break; case PTRACE_SETFPREGS: ret = ptrace_setfpregs(child, datavp); break; default: ret = ptrace_request(child, request, addr, data); break; } return ret; } asmlinkage unsigned long syscall_trace_enter(void) { unsigned long ret = 0; struct pt_regs *regs = current_pt_regs(); if (test_thread_flag(TIF_SYSCALL_TRACE) && tracehook_report_syscall_entry(current_pt_regs())) ret = -1UL; #ifdef CONFIG_SECCOMP /* Do seccomp after ptrace, to catch any tracer changes. */ if (secure_computing() == -1) return -1; #endif if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) trace_sys_enter(regs, regs->r0); audit_syscall_entry(regs->r0, regs->r16, regs->r17, regs->r18, regs->r19); return ret ?: current_pt_regs()->r0; } asmlinkage void syscall_trace_leave(void) { struct pt_regs *regs = current_pt_regs(); audit_syscall_exit(current_pt_regs()); if (test_thread_flag(TIF_SYSCALL_TRACE)) tracehook_report_syscall_exit(current_pt_regs(), 0); if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) trace_sys_exit(regs, regs_return_value(regs)); } static long rwcsr(int rw, unsigned long csr, unsigned long value) { register unsigned long __r0 __asm__("$0"); register unsigned long __r16 __asm__("$16") = rw; register unsigned long __r17 __asm__("$17") = csr; register unsigned long __r18 __asm__("$18") = value; __asm__ __volatile__( "sys_call %4" : "=r"(__r0), "=r"(__r16), "=r"(__r17), "=r"(__r18) : "i"(HMC_rwreg), "1"(__r16), "2"(__r17), "3"(__r18) : "$1", "$22", "$23", "$24", "$25"); return __r0; } #define RCSR 0 #define WCSR 1 #define CSR_DA_MATCH 0 #define CSR_DA_MASK 1 #define CSR_IA_MATCH 2 #define CSR_IA_MASK 3 #define CSR_IDA_MATCH 6 #define CSR_IDA_MASK 7 #define CSR_DC_CTL 11 #define CSR_DV_MATCH 15 #define CSR_DV_MASK 16 #define DV_MATCH_EN_S 19 #define DAV_MATCH_EN_S 20 int do_match(unsigned long address, unsigned long mmcsr, long cause, struct pt_regs *regs) { unsigned long dc_ctl; unsigned long value; printk("%s: pid %d, name = %s,cause = %#lx, mmcsr = %#lx, address = %#lx, pc %#lx\n", __func__, current->pid, current->comm, cause, mmcsr, address, regs->pc); switch (mmcsr) { case MMCSR__DA_MATCH: case MMCSR__DV_MATCH: case MMCSR__DAV_MATCH: dik_show_regs(regs, (unsigned long *)regs-15); if (!(current->ptrace & PT_PTRACED)) { printk(" pid %d %s not be ptraced, return\n", current->pid, current->comm); if (mmcsr == MMCSR__DA_MATCH) rwcsr(WCSR, CSR_DA_MATCH, 0); //clear da_match if (mmcsr == MMCSR__DV_MATCH) { value = rwcsr(RCSR, CSR_DV_MATCH, 0); printk("value is %#lx\n", value); value = rwcsr(RCSR, CSR_DV_MASK, 0); printk("value is %#lx\n", value); dc_ctl = rwcsr(RCSR, CSR_DC_CTL, 0); dc_ctl &= ~(0x1UL << DV_MATCH_EN_S); rwcsr(WCSR, CSR_DC_CTL, dc_ctl); } if (mmcsr == MMCSR__DAV_MATCH) { dc_ctl = rwcsr(RCSR, CSR_DC_CTL, 0); dc_ctl &= ~((0x1UL << DV_MATCH_EN_S) | (0x1UL << DAV_MATCH_EN_S)); rwcsr(WCSR, CSR_DC_CTL, dc_ctl); rwcsr(WCSR, CSR_DA_MATCH, 0); //clear da_match } task_thread_info(current)->pcb.da_match = 0; task_thread_info(current)->pcb.dv_match = 0; task_thread_info(current)->pcb.dc_ctl = 0; return 1; } if (mmcsr == MMCSR__DA_MATCH) { rwcsr(WCSR, CSR_DA_MATCH, 0); //clear da_match task_thread_info(current)->pcb.da_match = 0; } if (mmcsr == MMCSR__DV_MATCH) { dc_ctl = rwcsr(RCSR, CSR_DC_CTL, 0); dc_ctl &= ~(0x1UL << DV_MATCH_EN_S); rwcsr(WCSR, CSR_DC_CTL, dc_ctl); } if (mmcsr == MMCSR__DAV_MATCH) { dc_ctl = rwcsr(RCSR, CSR_DC_CTL, 0); dc_ctl &= ~((0x1UL << DV_MATCH_EN_S) | (0x1UL << DAV_MATCH_EN_S)); rwcsr(WCSR, CSR_DC_CTL, dc_ctl); rwcsr(WCSR, CSR_DA_MATCH, 0); //clear da_match } task_thread_info(current)->pcb.dv_match = 0; task_thread_info(current)->pcb.dc_ctl = 0; printk("do_page_fault: want to send SIGTRAP, pid = %d\n", current->pid); force_sig_fault(SIGTRAP, TRAP_HWBKPT, (void *) address, 0); return 1; case MMCSR__IA_MATCH: rwcsr(WCSR, CSR_IA_MATCH, 0); //clear ia_match return 1; case MMCSR__IDA_MATCH: rwcsr(WCSR, CSR_IDA_MATCH, 0); //clear ida_match return 1; } return 0; } void restore_da_match_after_sched(void) { unsigned long dc_ctl_mode; unsigned long dc_ctl; struct pcb_struct *pcb = &task_thread_info(current)->pcb; if (!(pcb->da_match || pcb->da_mask || pcb->dv_match || pcb->dv_mask || pcb->dc_ctl)) return; printk("Restroe MATCH status, pid: %d\n", current->pid); rwcsr(WCSR, CSR_DA_MATCH, 0); rwcsr(WCSR, CSR_DA_MASK, pcb->da_mask); rwcsr(WCSR, CSR_DA_MATCH, pcb->da_match); dc_ctl_mode = pcb->dc_ctl; dc_ctl = rwcsr(RCSR, CSR_DC_CTL, 0); dc_ctl &= ~((0x1UL << DV_MATCH_EN_S) | (0x1UL << DAV_MATCH_EN_S)); dc_ctl |= ((dc_ctl_mode << DV_MATCH_EN_S) & ((0x1UL << DV_MATCH_EN_S) | (0x1UL << DAV_MATCH_EN_S))); if (dc_ctl_mode & 0x1) { rwcsr(WCSR, CSR_DV_MATCH, pcb->dv_match); rwcsr(WCSR, CSR_DV_MASK, pcb->dv_mask); rwcsr(WCSR, CSR_DC_CTL, dc_ctl); } } struct pt_regs_offset { const char *name; int offset; }; #define REG_OFFSET_NAME(r) { \ .name = #r, \ .offset = offsetof(struct pt_regs, r) \ } #define REG_OFFSET_END { \ .name = NULL, \ .offset = 0 \ } static const struct pt_regs_offset regoffset_table[] = { REG_OFFSET_NAME(r0), REG_OFFSET_NAME(r1), REG_OFFSET_NAME(r2), REG_OFFSET_NAME(r3), REG_OFFSET_NAME(r4), REG_OFFSET_NAME(r5), REG_OFFSET_NAME(r6), REG_OFFSET_NAME(r7), REG_OFFSET_NAME(r8), REG_OFFSET_NAME(r19), REG_OFFSET_NAME(r20), REG_OFFSET_NAME(r21), REG_OFFSET_NAME(r22), REG_OFFSET_NAME(r23), REG_OFFSET_NAME(r24), REG_OFFSET_NAME(r25), REG_OFFSET_NAME(r26), REG_OFFSET_NAME(r27), REG_OFFSET_NAME(r28), REG_OFFSET_NAME(hae), REG_OFFSET_NAME(trap_a0), REG_OFFSET_NAME(trap_a1), REG_OFFSET_NAME(trap_a2), REG_OFFSET_NAME(ps), REG_OFFSET_NAME(pc), REG_OFFSET_NAME(gp), REG_OFFSET_NAME(r16), REG_OFFSET_NAME(r17), REG_OFFSET_NAME(r18), REG_OFFSET_END, }; /** * regs_query_register_offset() - query register offset from its name * @name: the name of a register * * regs_query_register_offset() returns the offset of a register in struct * pt_regs from its name. If the name is invalid, this returns -EINVAL; */ int regs_query_register_offset(const char *name) { const struct pt_regs_offset *roff; for (roff = regoffset_table; roff->name != NULL; roff++) if (!strcmp(roff->name, name)) return roff->offset; return -EINVAL; } static int regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr) { unsigned long ksp = kernel_stack_pointer(regs); return (addr & ~(THREAD_SIZE - 1)) == (ksp & ~(THREAD_SIZE - 1)); } /** * regs_get_kernel_stack_nth() - get Nth entry of the stack * @regs:pt_regs which contains kernel stack pointer. * @n:stack entry number. * * regs_get_kernel_stack_nth() returns @n th entry of the kernel stack which * is specifined by @regs. If the @n th entry is NOT in the kernel stack, * this returns 0. */ unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, unsigned int n) { unsigned long addr; addr = kernel_stack_pointer(regs) + n * sizeof(long); if (!regs_within_kernel_stack(regs, addr)) return 0; return *(unsigned long *)addr; }