/* We only support one DABR and no IABRS at the moment */ if (addr > 0) return -EINVAL;
dabr_fake = ((child->thread.hw_brk[0].address & (~HW_BRK_TYPE_DABR)) |
(child->thread.hw_brk[0].type & HW_BRK_TYPE_DABR)); return put_user(dabr_fake, datalp);
}
/* * ptrace_set_debugreg() fakes DABR and DABR is only one. So even if * internal hw supports more than one watchpoint, we support only one * watchpoint with this interface.
*/ int ptrace_set_debugreg(struct task_struct *task, unsignedlong addr, unsignedlong data)
{ #ifdef CONFIG_HAVE_HW_BREAKPOINT int ret; struct thread_struct *thread = &task->thread; struct perf_event *bp; struct perf_event_attr attr; #endif/* CONFIG_HAVE_HW_BREAKPOINT */ bool set_bp = true; struct arch_hw_breakpoint hw_brk;
/* For ppc64 we support one DABR and no IABR's at the moment (ppc64). * For embedded processors we support one DAC and no IAC's at the * moment.
*/ if (addr > 0) return -EINVAL;
/* The bottom 3 bits in dabr are flags */ if ((data & ~0x7UL) >= TASK_SIZE) return -EIO;
/* For processors using DABR (i.e. 970), the bottom 3 bits are flags. * It was assumed, on previous implementations, that 3 bits were * passed together with the data address, fitting the design of the * DABR register, as follows: * * bit 0: Read flag * bit 1: Write flag * bit 2: Breakpoint translation * * Thus, we use them here as so.
*/
/* Ensure breakpoint translation bit is set */ if (data && !(data & HW_BRK_TYPE_TRANSLATE)) return -EIO;
hw_brk.address = data & (~HW_BRK_TYPE_DABR);
hw_brk.type = (data & HW_BRK_TYPE_DABR) | HW_BRK_TYPE_PRIV_ALL;
hw_brk.len = DABR_MAX_LEN;
hw_brk.hw_len = DABR_MAX_LEN;
set_bp = (data) && (hw_brk.type & HW_BRK_TYPE_RDWR); #ifdef CONFIG_HAVE_HW_BREAKPOINT
bp = thread->ptrace_bps[0]; if (!set_bp) { if (bp) {
unregister_hw_breakpoint(bp);
thread->ptrace_bps[0] = NULL;
} return 0;
} if (bp) {
attr = bp->attr;
attr.bp_addr = hw_brk.address;
attr.bp_len = DABR_MAX_LEN;
arch_bp_generic_fields(hw_brk.type, &attr.bp_type);
/* Enable breakpoint */
attr.disabled = false;
ret = modify_user_hw_breakpoint(bp, &attr); if (ret) return ret;
#ifdef CONFIG_HAVE_HW_BREAKPOINT staticint find_empty_ptrace_bp(struct thread_struct *thread)
{ int i;
for (i = 0; i < nr_wp_slots(); i++) { if (!thread->ptrace_bps[i]) return i;
} return -1;
} #endif
staticint find_empty_hw_brk(struct thread_struct *thread)
{ int i;
for (i = 0; i < nr_wp_slots(); i++) { if (!thread->hw_brk[i].address) return i;
} return -1;
}
long ppc_set_hwdebug(struct task_struct *child, struct ppc_hw_breakpoint *bp_info)
{ int i; #ifdef CONFIG_HAVE_HW_BREAKPOINT int len = 0; struct thread_struct *thread = &child->thread; struct perf_event *bp; struct perf_event_attr attr; #endif/* CONFIG_HAVE_HW_BREAKPOINT */ struct arch_hw_breakpoint brk;
if (bp_info->version != 1) return -ENOTSUPP; /* * We only support one data breakpoint
*/ if ((bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_RW) == 0 ||
(bp_info->trigger_type & ~PPC_BREAKPOINT_TRIGGER_RW) != 0 ||
bp_info->condition_mode != PPC_BREAKPOINT_CONDITION_NONE) return -EINVAL;
if ((unsignedlong)bp_info->addr >= TASK_SIZE) return -EIO;
brk.address = ALIGN_DOWN(bp_info->addr, HW_BREAKPOINT_SIZE);
brk.type = HW_BRK_TYPE_TRANSLATE | HW_BRK_TYPE_PRIV_ALL;
brk.len = DABR_MAX_LEN;
brk.hw_len = DABR_MAX_LEN; if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_READ)
brk.type |= HW_BRK_TYPE_READ; if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_WRITE)
brk.type |= HW_BRK_TYPE_WRITE; #ifdef CONFIG_HAVE_HW_BREAKPOINT if (bp_info->addr_mode == PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE)
len = bp_info->addr2 - bp_info->addr; elseif (bp_info->addr_mode == PPC_BREAKPOINT_MODE_EXACT)
len = 1; else return -EINVAL;
i = find_empty_ptrace_bp(thread); if (i < 0) return -ENOSPC;
/* Create a new breakpoint request if one doesn't exist already */
hw_breakpoint_init(&attr);
attr.bp_addr = (unsignedlong)bp_info->addr;
attr.bp_len = len;
arch_bp_generic_fields(brk.type, &attr.bp_type);
bp = register_user_hw_breakpoint(&attr, ptrace_triggered, NULL, child);
thread->ptrace_bps[i] = bp; if (IS_ERR(bp)) {
thread->ptrace_bps[i] = NULL; return PTR_ERR(bp);
}
return i + 1; #endif/* CONFIG_HAVE_HW_BREAKPOINT */
if (bp_info->addr_mode != PPC_BREAKPOINT_MODE_EXACT) return -EINVAL;
i = find_empty_hw_brk(&child->thread); if (i < 0) return -ENOSPC;
if (!ppc_breakpoint_available()) return -ENODEV;
child->thread.hw_brk[i] = brk;
return i + 1;
}
long ppc_del_hwdebug(struct task_struct *child, long data)
{ #ifdef CONFIG_HAVE_HW_BREAKPOINT int ret = 0; struct thread_struct *thread = &child->thread; struct perf_event *bp; #endif/* CONFIG_HAVE_HW_BREAKPOINT */ if (data < 1 || data > nr_wp_slots()) return -EINVAL;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.