/* ptrace.c COPYRIGHT FUJITSU LIMITED 2016-2017 */ #include #include #include #include #include #include #include #include #include #include #include #include //#define DEBUG_PRINT_SC #ifdef DEBUG_PRINT_SC #define dkprintf kprintf #define ekprintf(...) do { if (0) kprintf(__VA_ARGS__); } while (0) #else #define dkprintf(...) do { if (0) kprintf(__VA_ARGS__); } while (0) #define ekprintf(...) do { if (0) kprintf(__VA_ARGS__); } while (0) #endif #define NOT_IMPLEMENTED() do { kprintf("%s is not implemented\n", __func__); while(1);} while(0) #define BUG_ON(condition) do { if (condition) { kprintf("PANIC: %s: %s(line:%d)\n",\ __FILE__, __FUNCTION__, __LINE__); panic(""); } } while(0) extern void save_debugreg(unsigned long *debugreg); extern unsigned long do_kill(struct thread *thread, int pid, int tid, int sig, struct siginfo *info, int ptracecont); extern int interrupt_from_user(void *); enum aarch64_regset { REGSET_GPR, REGSET_FPR, REGSET_TLS, REGSET_HW_BREAK, REGSET_HW_WATCH, REGSET_SYSTEM_CALL, #ifdef CONFIG_ARM64_SVE REGSET_SVE, #endif /* CONFIG_ARM64_SVE */ }; struct user_regset; typedef long user_regset_get_fn(struct thread *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf); typedef long user_regset_set_fn(struct thread *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf); struct user_regset { user_regset_get_fn *get; user_regset_set_fn *set; unsigned int n; unsigned int size; unsigned int core_note_type; }; long ptrace_read_user(struct thread *thread, long addr, unsigned long *value) { return -EIO; } long ptrace_write_user(struct thread *thread, long addr, unsigned long value) { return -EIO; } long ptrace_read_fpregs(struct thread *thread, void *fpregs) { return -EIO; } long ptrace_write_fpregs(struct thread *thread, void *fpregs) { return -EIO; } /* @ref.impl arch/arm64/kernel/ptrace.c::ptrace_hbp_get_resource_info */ unsigned int ptrace_hbp_get_resource_info(unsigned int note_type) { unsigned char num; unsigned int reg = 0; switch (note_type) { case NT_ARM_HW_BREAK: num = hw_breakpoint_slots(TYPE_INST); break; case NT_ARM_HW_WATCH: num = hw_breakpoint_slots(TYPE_DATA); break; default: return -EINVAL; } reg |= debug_monitors_arch(); reg <<= 8; reg |= num; return reg; } /* @ref.impl include/linux/regset.h::user_regset_copyout */ static inline long user_regset_copyout(unsigned int *pos, unsigned int *count, void **kbuf, void __user **ubuf, const void *data, const int start_pos, const int end_pos) { if (*count == 0) { return 0; } BUG_ON(*pos < start_pos); if (end_pos < 0 || *pos < end_pos) { unsigned int copy = 0; if ((end_pos < 0) || (*count < (end_pos - *pos))) { copy = *count; } else { copy = (end_pos - *pos); } data += *pos - start_pos; if (*kbuf) { memcpy(*kbuf, data, copy); *kbuf += copy; } else if (copy_to_user(*ubuf, data, copy)) { return -EFAULT; } else { *ubuf += copy; } *pos += copy; *count -= copy; } return 0; } /* @ref.impl include/linux/regset.h::user_regset_copyin */ static inline long user_regset_copyin(unsigned int *pos, unsigned int *count, const void **kbuf, const void __user **ubuf, void *data, const int start_pos, const int end_pos) { if (*count == 0) { return 0; } BUG_ON(*pos < start_pos); if (end_pos < 0 || *pos < end_pos) { unsigned int copy = 0; if ((end_pos < 0) || (*count < (end_pos - *pos))) { copy = *count; } else { copy = (end_pos - *pos); } data += *pos - start_pos; if (*kbuf) { memcpy(data, *kbuf, copy); *kbuf += copy; } else if (copy_from_user(data, *ubuf, copy)) { return -EFAULT; } else { *ubuf += copy; } *pos += copy; *count -= copy; } return 0; } /* @ref.impl include/linux/regset.h::user_regset_copyout_zero */ static inline long user_regset_copyout_zero(unsigned int *pos, unsigned int *count, void **kbuf, void __user **ubuf, const int start_pos, const int end_pos) { if (*count == 0) { return 0; } BUG_ON(*pos < start_pos); if (end_pos < 0 || *pos < end_pos) { unsigned int copy = 0; char *tmp = NULL; if ((end_pos < 0) || (*count < (end_pos - *pos))) { copy = *count; } else { copy = (end_pos - *pos); } if (*kbuf) { memset(*kbuf, 0, copy); *kbuf += copy; } else { tmp = kmalloc(copy, IHK_MC_AP_NOWAIT); if (tmp == NULL) { return -ENOMEM; } memset(tmp, 0, copy); if (copy_to_user(*ubuf, tmp, copy)) { kfree(tmp); return -EFAULT; } else { *ubuf += copy; } kfree(tmp); } *pos += copy; *count -= copy; } return 0; } /* @ref.impl include/linux/regset.h::user_regset_copyin_ignore */ static inline int user_regset_copyin_ignore(unsigned int *pos, unsigned int *count, const void **kbuf, const void __user **ubuf, const int start_pos, const int end_pos) { if (*count == 0) { return 0; } BUG_ON(*pos < start_pos); if (end_pos < 0 || *pos < end_pos) { unsigned int copy = 0; if ((end_pos < 0) || (*count < (end_pos - *pos))) { copy = *count; } else { copy = (end_pos - *pos); } if (*kbuf) { *kbuf += copy; } else { *ubuf += copy; } *pos += copy; *count -= copy; } return 0; } /* @ref.impl include/linux/regset.h::copy_regset_to_user */ static inline long copy_regset_to_user(struct thread *target, const struct user_regset *regset, unsigned int offset, unsigned int size, void __user *data) { if (!regset->get) { return -EOPNOTSUPP; } return regset->get(target, regset, offset, size, NULL, data); } /* @ref.impl include/linux/regset.h::copy_regset_from_user */ static inline long copy_regset_from_user(struct thread *target, const struct user_regset *regset, unsigned int offset, unsigned int size, const void __user *data) { if (!regset->set) { return -EOPNOTSUPP; } return regset->set(target, regset, offset, size, NULL, data); } /* * Bits which are always architecturally RES0 per ARM DDI 0487A.h * Userspace cannot use these until they have an architectural meaning. * We also reserve IL for the kernel; SS is handled dynamically. */ #define SPSR_EL1_AARCH64_RES0_BITS 0xffffffff0fdffc20UL static int valid_native_regs(struct user_pt_regs *regs) { regs->pstate &= ~SPSR_EL1_AARCH64_RES0_BITS; if (interrupt_from_user(regs) && !(regs->pstate & PSR_MODE32_BIT) && (regs->pstate & PSR_D_BIT) == 0 && (regs->pstate & PSR_A_BIT) == 0 && (regs->pstate & PSR_I_BIT) == 0 && (regs->pstate & PSR_F_BIT) == 0) { return 1; } /* Force PSR to a valid 64-bit EL0t */ regs->pstate &= PSR_N_BIT | PSR_Z_BIT | PSR_C_BIT | PSR_V_BIT; return 0; } static int valid_user_regs(struct user_pt_regs *regs, struct thread *thread) { if (!(thread->ctx.thread->flags & TIF_SINGLESTEP)) { regs->pstate &= ~DBG_SPSR_SS; } return valid_native_regs(regs); } /* read NT_PRSTATUS */ static long gpr_get(struct thread *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { struct user_pt_regs *uregs = &target->uctx->user_regs; return user_regset_copyout(&pos, &count, &kbuf, &ubuf, uregs, 0, -1); } /* write NT_PRSTATUS */ static long gpr_set(struct thread *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf) { long ret; struct user_pt_regs newregs = target->uctx->user_regs; ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &newregs, 0, -1); if (ret) { goto out; } if (!valid_user_regs(&newregs, target)) { ret = -EINVAL; goto out; } target->uctx->user_regs = newregs; out: return ret; } /* read NT_PRFPREG */ static long fpr_get(struct thread *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { long ret = -EINVAL; if (target->fp_regs == NULL) { ret = -ENOMEM; goto out; } if (likely(elf_hwcap & (HWCAP_FP | HWCAP_ASIMD))) { struct user_fpsimd_state *uregs; if (likely(elf_hwcap & HWCAP_SVE)) { /* sync to sve --> fpsimd */ thread_sve_to_fpsimd(target, target->fp_regs); } uregs = &target->fp_regs->user_fpsimd; ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, uregs, 0, -1); } out: return ret; } /* write NT_PRFPREG */ static long __fpr_set(struct thread *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf, unsigned int start_pos) { long ret = -EINVAL; if (target->fp_regs == NULL) { ret = -ENOMEM; goto out; } if (likely(elf_hwcap & (HWCAP_FP | HWCAP_ASIMD))) { struct user_fpsimd_state newstate; if (likely(elf_hwcap & HWCAP_SVE)) { /* sync to sve --> fpsimd */ thread_sve_to_fpsimd(target, target->fp_regs); } newstate = target->fp_regs->user_fpsimd; ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &newstate, start_pos, start_pos + sizeof(newstate)); if (ret) { goto out; } target->fp_regs->user_fpsimd = newstate; if (likely(elf_hwcap & HWCAP_SVE)) { /* sync to fpsimd --> sve */ thread_fpsimd_to_sve(target, target->fp_regs); } } out: return ret; } /* write NT_PRFPREG */ static long fpr_set(struct thread *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf) { return __fpr_set(target, regset, pos, count, kbuf, ubuf, 0); } /* read NT_ARM_TLS */ static long tls_get(struct thread *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { unsigned long *tls = &target->tlsblock_base; return user_regset_copyout(&pos, &count, &kbuf, &ubuf, tls, 0, -1); } /* write NT_ARM_TLS */ static long tls_set(struct thread *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf) { long ret; unsigned long tls = target->tlsblock_base; ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &tls, 0, -1); if (ret) { goto out; } target->tlsblock_base = tls; out: return ret; } /* read NT_ARM_SYSTEM_CALL */ static long system_call_get(struct thread *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { int syscallno = target->uctx->syscallno; return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &syscallno, 0, -1); } /* write NT_ARM_SYSTEM_CALL */ static long system_call_set(struct thread *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf) { int syscallno = target->uctx->syscallno; long ret; ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &syscallno, 0, -1); if (ret) { goto out; } target->uctx->syscallno = syscallno; out: return ret; } #define PTRACE_HBP_ADDR_SZ sizeof(uint64_t) #define PTRACE_HBP_CTRL_SZ sizeof(uint32_t) #define PTRACE_HBP_PAD_SZ sizeof(uint32_t) /* read NT_ARM_HW_BREAK or NT_ARM_HW_WATCH */ static long hw_break_get(struct thread *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { unsigned int note_type = regset->core_note_type; long ret = -EINVAL; int idx = 0, offset, limit, bw; uint32_t info, ctrl; uint64_t addr; struct user_hwdebug_state *hws = NULL; if (note_type != NT_ARM_HW_BREAK && note_type != NT_ARM_HW_WATCH) { goto out; } if (target->ptrace_debugreg == NULL) { ret = -ENOMEM; goto out; } bw = (note_type == NT_ARM_HW_BREAK ? HWS_BREAK : HWS_WATCH); hws = (struct user_hwdebug_state *)target->ptrace_debugreg + bw; /* Resource info */ info = hws->dbg_info; ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &info, 0, sizeof(info)); if (ret) { goto out; } /* Pad */ offset = offsetof(struct user_hwdebug_state, pad); ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, offset, offset + PTRACE_HBP_PAD_SZ); if (ret) { goto out; } /* (address, ctrl) registers */ offset = offsetof(struct user_hwdebug_state, dbg_regs); limit = regset->n * regset->size; while (count && offset < limit) { addr = hws->dbg_regs[idx].addr; ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &addr, offset, offset + PTRACE_HBP_ADDR_SZ); if (ret) { goto out; } offset += PTRACE_HBP_ADDR_SZ; ctrl = hws->dbg_regs[idx].ctrl; ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &ctrl, offset, offset + PTRACE_HBP_CTRL_SZ); if (ret) { goto out; } offset += PTRACE_HBP_CTRL_SZ; ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, offset, offset + PTRACE_HBP_PAD_SZ); if (ret) { goto out; } offset += PTRACE_HBP_PAD_SZ; idx++; } out: return ret; } /* write NT_ARM_HW_BREAK or NT_ARM_HW_WATCH */ static long hw_break_set(struct thread *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf) { unsigned int note_type = regset->core_note_type; long ret = -EINVAL; int idx = 0, offset, limit, bw; uint32_t ctrl; uint64_t addr; struct user_hwdebug_state *hws = NULL; if (note_type != NT_ARM_HW_BREAK && note_type != NT_ARM_HW_WATCH) { goto out; } if (target->ptrace_debugreg == NULL) { ret = -ENOMEM; goto out; } bw = (note_type == NT_ARM_HW_BREAK ? HWS_BREAK : HWS_WATCH); hws = (struct user_hwdebug_state *)target->ptrace_debugreg + bw; /* Resource info and pad */ offset = offsetof(struct user_hwdebug_state, dbg_regs); ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 0, offset); if (ret) { goto out; } /* (address, ctrl) registers */ limit = regset->n * regset->size; while (count && offset < limit) { if (count < PTRACE_HBP_ADDR_SZ) { ret = -EINVAL; goto out; } ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &addr, offset, offset + PTRACE_HBP_ADDR_SZ); if (ret) { goto out; } hws->dbg_regs[idx].addr = addr; offset += PTRACE_HBP_ADDR_SZ; if (!count) { break; } ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ctrl, offset, offset + PTRACE_HBP_CTRL_SZ); if (ret) { goto out; } hws->dbg_regs[idx].ctrl = ctrl; offset += PTRACE_HBP_CTRL_SZ; ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, offset, offset + PTRACE_HBP_PAD_SZ); if (ret) { goto out; } offset += PTRACE_HBP_PAD_SZ; idx++; } out: return ret; } #ifdef CONFIG_ARM64_SVE /* read NT_ARM_SVE */ static long sve_get(struct thread *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { long ret = -EINVAL; struct user_sve_header header; unsigned int vq; unsigned long start, end; if (target->fp_regs == NULL) { ret = -ENOMEM; goto out; } /* Instead of system_supports_sve() */ if (unlikely(!(elf_hwcap & HWCAP_SVE))) { goto out; } /* Header */ memset(&header, 0, sizeof(header)); header.vl = target->ctx.thread->sve_vl; BUG_ON(!sve_vl_valid(header.vl)); vq = sve_vq_from_vl(header.vl); BUG_ON(!sve_vl_valid(sve_max_vl)); header.max_vl = sve_max_vl; /* McKernel processes always enable SVE. */ header.flags = SVE_PT_REGS_SVE; header.size = SVE_PT_SIZE(vq, header.flags); header.max_size = SVE_PT_SIZE(sve_vq_from_vl(header.max_vl), SVE_PT_REGS_SVE); ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &header, 0, sizeof(header)); if (ret) { goto out; } /* Registers: FPSIMD-only case */ /* * If McKernel, Nothing to do. * Because McKernel processes always enable SVE. */ /* Otherwise: full SVE case */ start = SVE_PT_SVE_OFFSET; end = SVE_PT_SVE_FFR_OFFSET(vq) + SVE_PT_SVE_FFR_SIZE(vq); BUG_ON(end < start); BUG_ON(end - start > sve_state_size(target)); ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, target->ctx.thread->sve_state, start, end); if (ret) { goto out; } start = end; end = SVE_PT_SVE_FPSR_OFFSET(vq); BUG_ON(end < start); ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, start, end); if (ret) { goto out; } start = end; end = SVE_PT_SVE_FPCR_OFFSET(vq) + SVE_PT_SVE_FPCR_SIZE; BUG_ON((char *)(&target->fp_regs->fpcr + 1) < (char *)&target->fp_regs->fpsr); BUG_ON(end < start); BUG_ON((char *)(&target->fp_regs->fpcr + 1) - (char *)&target->fp_regs->fpsr != end - start); ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &target->fp_regs->fpsr, start, end); if (ret) { goto out; } start = end; end = (SVE_PT_SIZE(SVE_VQ_MAX, SVE_PT_REGS_SVE) + 15) / 16 * 16; BUG_ON(end < start); ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, start, end); out: return ret; } /* write NT_ARM_SVE case */ static long sve_set(struct thread *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf) { long ret = -EINVAL; struct user_sve_header header; unsigned int vq; unsigned long start, end; if (target->fp_regs == NULL) { ret = -ENOMEM; goto out; } /* Instead of system_supports_sve() */ if (unlikely(!(elf_hwcap & HWCAP_SVE))) { goto out; } /* Header */ if (count < sizeof(header)) { goto out; } ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &header, 0, sizeof(header)); if (ret) { goto out; } /* * Apart from PT_SVE_REGS_MASK, all PT_SVE_* flags are consumed by * sve_set_vector_length(), which will also validate them for us: */ ret = sve_set_vector_length(target, header.vl, header.flags & ~SVE_PT_REGS_MASK); if (ret) { goto out; } /* Actual VL set may be less than the user asked for: */ BUG_ON(!sve_vl_valid(target->ctx.thread->sve_vl)); vq = sve_vq_from_vl(target->ctx.thread->sve_vl); /* Registers: FPSIMD-only case */ if ((header.flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_FPSIMD) { ret = __fpr_set(target, regset, pos, count, kbuf, ubuf, SVE_PT_FPSIMD_OFFSET); goto out; } /* Otherwise: full SVE case */ start = SVE_PT_SVE_OFFSET; end = SVE_PT_SVE_FFR_OFFSET(vq) + SVE_PT_SVE_FFR_SIZE(vq); BUG_ON(end < start); BUG_ON(end - start > sve_state_size(target)); ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, target->ctx.thread->sve_state, start, end); if (ret) { goto out; } start = end; end = SVE_PT_SVE_FPSR_OFFSET(vq); BUG_ON(end < start); ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, start, end); if (ret) { goto out; } start = end; end = SVE_PT_SVE_FPCR_OFFSET(vq) + SVE_PT_SVE_FPCR_SIZE; BUG_ON((char *)(&target->fp_regs->fpcr + 1) < (char *)&target->fp_regs->fpsr); BUG_ON(end < start); BUG_ON((char *)(&target->fp_regs->fpcr + 1) - (char *)&target->fp_regs->fpsr != end - start); user_regset_copyin(&pos, &count, &kbuf, &ubuf, &target->fp_regs->fpsr, start, end); out: return ret; } #endif /* CONFIG_ARM64_SVE */ static const struct user_regset aarch64_regsets[] = { [REGSET_GPR] = { .core_note_type = NT_PRSTATUS, .n = sizeof(struct user_pt_regs) / sizeof(uint64_t), .size = sizeof(uint64_t), .get = gpr_get, .set = gpr_set }, [REGSET_FPR] = { .core_note_type = NT_PRFPREG, .n = sizeof(struct user_fpsimd_state) / sizeof(uint32_t), /* * We pretend we have 32-bit registers because the fpsr and * fpcr are 32-bits wide. */ .size = sizeof(uint32_t), .get = fpr_get, .set = fpr_set }, [REGSET_TLS] = { .core_note_type = NT_ARM_TLS, .n = 1, .size = sizeof(void *), .get = tls_get, .set = tls_set }, [REGSET_HW_BREAK] = { .core_note_type = NT_ARM_HW_BREAK, .n = sizeof(struct user_hwdebug_state) / sizeof(uint32_t), .size = sizeof(uint32_t), .get = hw_break_get, .set = hw_break_set }, [REGSET_HW_WATCH] = { .core_note_type = NT_ARM_HW_WATCH, .n = sizeof(struct user_hwdebug_state) / sizeof(uint32_t), .size = sizeof(uint32_t), .get = hw_break_get, .set = hw_break_set }, [REGSET_SYSTEM_CALL] = { .core_note_type = NT_ARM_SYSTEM_CALL, .n = 1, .size = sizeof(int), .get = system_call_get, .set = system_call_set }, #ifdef CONFIG_ARM64_SVE [REGSET_SVE] = { /* Scalable Vector Extension */ .core_note_type = NT_ARM_SVE, .n = (SVE_PT_SIZE(SVE_VQ_MAX, SVE_PT_REGS_SVE) + 15) / 16, .size = 16, .get = sve_get, .set = sve_set }, #endif /* CONFIG_ARM64_SVE */ }; static const struct user_regset * find_regset(const struct user_regset *regset, unsigned int type, int n) { int i = 0; for (i = 0; i < n; i++) { if (regset[i].core_note_type == type) { return ®set[i]; } } return NULL; } static long ptrace_regset(struct thread *thread, int req, long type, struct iovec *iov) { long rc = -EINVAL; const struct user_regset *regset = find_regset(aarch64_regsets, type, sizeof(aarch64_regsets) / sizeof(aarch64_regsets[0])); if (!regset) { kprintf("%s: not supported type 0x%x\n", __FUNCTION__, type); goto out; } if ((iov->iov_len % regset->size) != 0) { goto out; } if ((size_t)(regset->n * regset->size) < iov->iov_len) { iov->iov_len = (size_t)(regset->n * regset->size); } if (req == PTRACE_GETREGSET) { rc = copy_regset_to_user(thread, regset, 0, iov->iov_len, iov->iov_base); } else { rc = copy_regset_from_user(thread, regset, 0, iov->iov_len, iov->iov_base); } out: return rc; } long ptrace_read_regset(struct thread *thread, long type, struct iovec *iov) { return ptrace_regset(thread, PTRACE_GETREGSET, type, iov); } long ptrace_write_regset(struct thread *thread, long type, struct iovec *iov) { return ptrace_regset(thread, PTRACE_SETREGSET, type, iov); } void ptrace_report_signal(struct thread *thread, int sig) { struct mcs_rwlock_node_irqsave lock; struct process *proc = thread->proc; int parent_pid; siginfo_t info; struct thread_info tinfo; dkprintf("ptrace_report_signal, tid=%d, pid=%d\n", thread->tid, thread->proc->pid); /* save thread_info, if called by ptrace_report_exec() */ if (sig == ((SIGTRAP | (PTRACE_EVENT_EXEC << 8)))) { memcpy(&tinfo, thread->ctx.thread, sizeof(struct thread_info)); } mcs_rwlock_writer_lock(&proc->update_lock, &lock); if(!(proc->ptrace & PT_TRACED)){ mcs_rwlock_writer_unlock(&proc->update_lock, &lock); return; } thread->exit_status = sig; /* Transition thread state */ #ifdef POSTK_DEBUG_TEMP_FIX_41 /* early to wait4() wakeup for ptrace, fix. */ proc->status = PS_DELAY_TRACED; #else /* POSTK_DEBUG_TEMP_FIX_41 */ proc->status = PS_TRACED; #endif /* POSTK_DEBUG_TEMP_FIX_41 */ thread->status = PS_TRACED; proc->ptrace &= ~PT_TRACE_SYSCALL; if (sig == SIGSTOP || sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU) { proc->signal_flags |= SIGNAL_STOP_STOPPED; } else { proc->signal_flags &= ~SIGNAL_STOP_STOPPED; } parent_pid = proc->parent->pid; save_debugreg(thread->ptrace_debugreg); mcs_rwlock_writer_unlock(&proc->update_lock, &lock); memset(&info, '\0', sizeof info); info.si_signo = SIGCHLD; info.si_code = CLD_TRAPPED; info._sifields._sigchld.si_pid = thread->tid; info._sifields._sigchld.si_status = thread->exit_status; do_kill(cpu_local_var(current), parent_pid, -1, SIGCHLD, &info, 0); #ifndef POSTK_DEBUG_TEMP_FIX_41 /* early to wait4() wakeup for ptrace, fix. */ /* Wake parent (if sleeping in wait4()) */ waitq_wakeup(&proc->parent->waitpid_q); #endif /* !POSTK_DEBUG_TEMP_FIX_41 */ dkprintf("ptrace_report_signal,sleeping\n"); /* Sleep */ schedule(); dkprintf("ptrace_report_signal,wake up\n"); /* restore thread_info, if called by ptrace_report_exec() */ if (sig == ((SIGTRAP | (PTRACE_EVENT_EXEC << 8)))) { memcpy(thread->ctx.thread, &tinfo, sizeof(struct thread_info)); } } long arch_ptrace(long request, int pid, long addr, long data) { return -EIO; }