/* * CPU initialisation. */ static void hw_breakpoint_reset(void *unused) { int i; struct perf_event **slots; /* * When a CPU goes through cold-boot, it does not have any installed * slot, so it is safe to share the same function for restoring and * resetting breakpoints; when a CPU is hotplugged in, it goes * through the slots, which are all empty, hence it just resets control * and value for debug registers. * When this function is triggered on warm-boot through a CPU PM * notifier some slots might be initialized; if so they are * reprogrammed according to the debug slots content. */ for (slots = this_cpu_ptr(bp_on_reg), i = 0; i < core_num_brps; ++i) { if (slots[i]) { hw_breakpoint_control(slots[i], HW_BREAKPOINT_RESTORE); } else { write_wb_reg(AARCH64_DBG_REG_BCR, i, 0UL); write_wb_reg(AARCH64_DBG_REG_BVR, i, 0UL); } } for (slots = this_cpu_ptr(wp_on_reg), i = 0; i < core_num_wrps; ++i) { if (slots[i]) { hw_breakpoint_control(slots[i], HW_BREAKPOINT_RESTORE); } else { write_wb_reg(AARCH64_DBG_REG_WCR, i, 0UL); write_wb_reg(AARCH64_DBG_REG_WVR, i, 0UL); } } }
/* * CPU initialisation. */ static void reset_ctrl_regs(void *unused) { int i; for (i = 0; i < core_num_brps; ++i) { write_wb_reg(AARCH64_DBG_REG_BCR, i, 0UL); write_wb_reg(AARCH64_DBG_REG_BVR, i, 0UL); } for (i = 0; i < core_num_wrps; ++i) { write_wb_reg(AARCH64_DBG_REG_WCR, i, 0UL); write_wb_reg(AARCH64_DBG_REG_WVR, i, 0UL); } }
/* * Install a perf counter breakpoint. */ int arch_install_hw_breakpoint(struct perf_event *bp) { struct arch_hw_breakpoint *info = counter_arch_bp(bp); struct perf_event **slot, **slots; struct debug_info *debug_info = ¤t->thread.debug; int i, max_slots, ctrl_reg, val_reg, reg_enable; u32 ctrl; if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) { /* Breakpoint */ ctrl_reg = AARCH64_DBG_REG_BCR; val_reg = AARCH64_DBG_REG_BVR; slots = __get_cpu_var(bp_on_reg); max_slots = core_num_brps; reg_enable = !debug_info->bps_disabled; } else { /* Watchpoint */ ctrl_reg = AARCH64_DBG_REG_WCR; val_reg = AARCH64_DBG_REG_WVR; slots = __get_cpu_var(wp_on_reg); max_slots = core_num_wrps; reg_enable = !debug_info->wps_disabled; } for (i = 0; i < max_slots; ++i) { slot = &slots[i]; if (!*slot) { *slot = bp; break; } } if (WARN_ONCE(i == max_slots, "Can't find any breakpoint slot")) return -ENOSPC; /* Ensure debug monitors are enabled at the correct exception level. */ enable_debug_monitors(debug_exception_level(info->ctrl.privilege)); /* Setup the address register. */ write_wb_reg(val_reg, i, info->address); /* Setup the control register. */ ctrl = encode_ctrl_reg(info->ctrl); write_wb_reg(ctrl_reg, i, reg_enable ? ctrl | 0x1 : ctrl & ~0x1); return 0; }
/* * Enable/disable all of the breakpoints active at the specified * exception level at the register level. * This is used when single-stepping after a breakpoint exception. */ static void toggle_bp_registers(int reg, enum debug_el el, int enable) { int i, max_slots, privilege; u32 ctrl; struct perf_event **slots; switch (reg) { case AARCH64_DBG_REG_BCR: slots = this_cpu_ptr(bp_on_reg); max_slots = core_num_brps; break; case AARCH64_DBG_REG_WCR: slots = this_cpu_ptr(wp_on_reg); max_slots = core_num_wrps; break; default: return; } for (i = 0; i < max_slots; ++i) { if (!slots[i]) continue; privilege = counter_arch_bp(slots[i])->ctrl.privilege; if (debug_exception_level(privilege) != el) continue; ctrl = read_wb_reg(reg, i); if (enable) ctrl |= 0x1; else ctrl &= ~0x1; write_wb_reg(reg, i, ctrl); } }
/* * CPU initialisation. */ static void reset_ctrl_regs(void *unused) { #ifdef CONFIG_MEDIATEK_SOLUTION /* mediatek will use our own operations for hw breakpoint/watchpoint */ #else int i; for (i = 0; i < core_num_brps; ++i) { write_wb_reg(AARCH64_DBG_REG_BCR, i, 0UL); write_wb_reg(AARCH64_DBG_REG_BVR, i, 0UL); } for (i = 0; i < core_num_wrps; ++i) { write_wb_reg(AARCH64_DBG_REG_WCR, i, 0UL); write_wb_reg(AARCH64_DBG_REG_WVR, i, 0UL); } #endif }
static void hw_breakpoint_restore(void) { int i; struct perf_event **slots; for (slots = __get_cpu_var(bp_on_reg), i = 0; i < core_num_brps; ++i) { if (slots[i]) { hw_breakpoint_control(slots[i], HW_BREAKPOINT_RESTORE); } else { write_wb_reg(AARCH64_DBG_REG_BCR, i, 0UL); write_wb_reg(AARCH64_DBG_REG_BVR, i, 0UL); } } for (slots = __get_cpu_var(wp_on_reg), i = 0; i < core_num_wrps; ++i) { if (slots[i]) { hw_breakpoint_control(slots[i], HW_BREAKPOINT_RESTORE); } else { write_wb_reg(AARCH64_DBG_REG_WCR, i, 0UL); write_wb_reg(AARCH64_DBG_REG_WVR, i, 0UL); } } }
void arch_uninstall_hw_breakpoint(struct perf_event *bp) { struct arch_hw_breakpoint *info = counter_arch_bp(bp); struct perf_event **slot, **slots; int i, max_slots, base; if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) { /* Breakpoint */ base = AARCH64_DBG_REG_BCR; slots = __get_cpu_var(bp_on_reg); max_slots = core_num_brps; } else { /* Watchpoint */ base = AARCH64_DBG_REG_WCR; slots = __get_cpu_var(wp_on_reg); max_slots = core_num_wrps; } /* Remove the breakpoint. */ for (i = 0; i < max_slots; ++i) { slot = &slots[i]; if (*slot == bp) { *slot = NULL; break; } } if (WARN_ONCE(i == max_slots, "Can't find any breakpoint slot")) return; /* Reset the control register. */ write_wb_reg(base, i, 0); /* Release the debug monitors for the correct exception level. */ disable_debug_monitors(debug_exception_level(info->ctrl.privilege)); }
static int hw_breakpoint_control(struct perf_event *bp, enum hw_breakpoint_ops ops) { struct arch_hw_breakpoint *info = counter_arch_bp(bp); struct perf_event **slots; struct debug_info *debug_info = ¤t->thread.debug; int i, max_slots, ctrl_reg, val_reg, reg_enable; enum debug_el dbg_el = debug_exception_level(info->ctrl.privilege); u32 ctrl; if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) { /* Breakpoint */ ctrl_reg = AARCH64_DBG_REG_BCR; val_reg = AARCH64_DBG_REG_BVR; slots = this_cpu_ptr(bp_on_reg); max_slots = core_num_brps; reg_enable = !debug_info->bps_disabled; } else { /* Watchpoint */ ctrl_reg = AARCH64_DBG_REG_WCR; val_reg = AARCH64_DBG_REG_WVR; slots = this_cpu_ptr(wp_on_reg); max_slots = core_num_wrps; reg_enable = !debug_info->wps_disabled; } i = hw_breakpoint_slot_setup(slots, max_slots, bp, ops); if (WARN_ONCE(i < 0, "Can't find any breakpoint slot")) return i; switch (ops) { case HW_BREAKPOINT_INSTALL: /* * Ensure debug monitors are enabled at the correct exception * level. */ enable_debug_monitors(dbg_el); /* Fall through */ case HW_BREAKPOINT_RESTORE: /* Setup the address register. */ write_wb_reg(val_reg, i, info->address); /* Setup the control register. */ ctrl = encode_ctrl_reg(info->ctrl); write_wb_reg(ctrl_reg, i, reg_enable ? ctrl | 0x1 : ctrl & ~0x1); break; case HW_BREAKPOINT_UNINSTALL: /* Reset the control register. */ write_wb_reg(ctrl_reg, i, 0); /* * Release the debug monitors for the correct exception * level. */ disable_debug_monitors(dbg_el); break; } return 0; }