static int ptrace_hbp_fill_attr_ctrl(unsigned int note_type, struct arch_hw_breakpoint_ctrl ctrl, struct perf_event_attr *attr) { int err, len, type, disabled = !ctrl.enabled; attr->disabled = disabled; if (disabled) return 0; err = arch_bp_generic_fields(ctrl, &len, &type); if (err) return err; switch (note_type) { case NT_ARM_HW_BREAK: if ((type & HW_BREAKPOINT_X) != type) return -EINVAL; break; case NT_ARM_HW_WATCH: if ((type & HW_BREAKPOINT_RW) != type) return -EINVAL; break; default: return -EINVAL; } attr->bp_len = len; attr->bp_type = type; return 0; }
static int ptrace_sethbpregs(struct task_struct *tsk, long num, unsigned long __user *data) { int idx, gen_len, gen_type, implied_type, ret = 0; u32 user_val; struct perf_event *bp; struct arch_hw_breakpoint_ctrl ctrl; struct perf_event_attr attr; if (num == 0) goto out; else if (num < 0) implied_type = HW_BREAKPOINT_RW; else implied_type = HW_BREAKPOINT_X; idx = ptrace_hbp_num_to_idx(num); if (idx < 0 || idx >= ARM_MAX_HBP_SLOTS) { ret = -EINVAL; goto out; } if (get_user(user_val, data)) { ret = -EFAULT; goto out; } bp = tsk->thread.debug.hbp[idx]; if (!bp) { bp = ptrace_hbp_create(tsk, implied_type); if (IS_ERR(bp)) { ret = PTR_ERR(bp); goto out; } tsk->thread.debug.hbp[idx] = bp; } attr = bp->attr; if (num & 0x1) { /* Address */ attr.bp_addr = user_val; } else { /* Control */ decode_ctrl_reg(user_val, &ctrl); ret = arch_bp_generic_fields(ctrl, &gen_len, &gen_type); if (ret) goto out; if ((gen_type & implied_type) != gen_type) { ret = -EINVAL; goto out; } attr.bp_len = gen_len; attr.bp_type = gen_type; attr.disabled = !ctrl.enabled; } ret = modify_user_hw_breakpoint(bp, &attr); out: return ret; }