diff options
Diffstat (limited to 'arch/arm64/kernel/hw_breakpoint.c')
-rw-r--r-- | arch/arm64/kernel/hw_breakpoint.c | 203 |
1 files changed, 136 insertions, 67 deletions
diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c index ff516f6691e4..f17f581116fc 100644 --- a/arch/arm64/kernel/hw_breakpoint.c +++ b/arch/arm64/kernel/hw_breakpoint.c | |||
@@ -20,6 +20,7 @@ | |||
20 | 20 | ||
21 | #define pr_fmt(fmt) "hw-breakpoint: " fmt | 21 | #define pr_fmt(fmt) "hw-breakpoint: " fmt |
22 | 22 | ||
23 | #include <linux/cpu_pm.h> | ||
23 | #include <linux/errno.h> | 24 | #include <linux/errno.h> |
24 | #include <linux/hw_breakpoint.h> | 25 | #include <linux/hw_breakpoint.h> |
25 | #include <linux/perf_event.h> | 26 | #include <linux/perf_event.h> |
@@ -169,15 +170,68 @@ static enum debug_el debug_exception_level(int privilege) | |||
169 | } | 170 | } |
170 | } | 171 | } |
171 | 172 | ||
172 | /* | 173 | enum hw_breakpoint_ops { |
173 | * Install a perf counter breakpoint. | 174 | HW_BREAKPOINT_INSTALL, |
175 | HW_BREAKPOINT_UNINSTALL, | ||
176 | HW_BREAKPOINT_RESTORE | ||
177 | }; | ||
178 | |||
179 | /** | ||
180 | * hw_breakpoint_slot_setup - Find and setup a perf slot according to | ||
181 | * operations | ||
182 | * | ||
183 | * @slots: pointer to array of slots | ||
184 | * @max_slots: max number of slots | ||
185 | * @bp: perf_event to setup | ||
186 | * @ops: operation to be carried out on the slot | ||
187 | * | ||
188 | * Return: | ||
189 | * slot index on success | ||
190 | * -ENOSPC if no slot is available/matches | ||
191 | * -EINVAL on wrong operations parameter | ||
174 | */ | 192 | */ |
175 | int arch_install_hw_breakpoint(struct perf_event *bp) | 193 | static int hw_breakpoint_slot_setup(struct perf_event **slots, int max_slots, |
194 | struct perf_event *bp, | ||
195 | enum hw_breakpoint_ops ops) | ||
196 | { | ||
197 | int i; | ||
198 | struct perf_event **slot; | ||
199 | |||
200 | for (i = 0; i < max_slots; ++i) { | ||
201 | slot = &slots[i]; | ||
202 | switch (ops) { | ||
203 | case HW_BREAKPOINT_INSTALL: | ||
204 | if (!*slot) { | ||
205 | *slot = bp; | ||
206 | return i; | ||
207 | } | ||
208 | break; | ||
209 | case HW_BREAKPOINT_UNINSTALL: | ||
210 | if (*slot == bp) { | ||
211 | *slot = NULL; | ||
212 | return i; | ||
213 | } | ||
214 | break; | ||
215 | case HW_BREAKPOINT_RESTORE: | ||
216 | if (*slot == bp) | ||
217 | return i; | ||
218 | break; | ||
219 | default: | ||
220 | pr_warn_once("Unhandled hw breakpoint ops %d\n", ops); | ||
221 | return -EINVAL; | ||
222 | } | ||
223 | } | ||
224 | return -ENOSPC; | ||
225 | } | ||
226 | |||
227 | static int hw_breakpoint_control(struct perf_event *bp, | ||
228 | enum hw_breakpoint_ops ops) | ||
176 | { | 229 | { |
177 | struct arch_hw_breakpoint *info = counter_arch_bp(bp); | 230 | struct arch_hw_breakpoint *info = counter_arch_bp(bp); |
178 | struct perf_event **slot, **slots; | 231 | struct perf_event **slots; |
179 | struct debug_info *debug_info = ¤t->thread.debug; | 232 | struct debug_info *debug_info = ¤t->thread.debug; |
180 | int i, max_slots, ctrl_reg, val_reg, reg_enable; | 233 | int i, max_slots, ctrl_reg, val_reg, reg_enable; |
234 | enum debug_el dbg_el = debug_exception_level(info->ctrl.privilege); | ||
181 | u32 ctrl; | 235 | u32 ctrl; |
182 | 236 | ||
183 | if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) { | 237 | if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) { |
@@ -196,67 +250,54 @@ int arch_install_hw_breakpoint(struct perf_event *bp) | |||
196 | reg_enable = !debug_info->wps_disabled; | 250 | reg_enable = !debug_info->wps_disabled; |
197 | } | 251 | } |
198 | 252 | ||
199 | for (i = 0; i < max_slots; ++i) { | 253 | i = hw_breakpoint_slot_setup(slots, max_slots, bp, ops); |
200 | slot = &slots[i]; | ||
201 | |||
202 | if (!*slot) { | ||
203 | *slot = bp; | ||
204 | break; | ||
205 | } | ||
206 | } | ||
207 | |||
208 | if (WARN_ONCE(i == max_slots, "Can't find any breakpoint slot")) | ||
209 | return -ENOSPC; | ||
210 | 254 | ||
211 | /* Ensure debug monitors are enabled at the correct exception level. */ | 255 | if (WARN_ONCE(i < 0, "Can't find any breakpoint slot")) |
212 | enable_debug_monitors(debug_exception_level(info->ctrl.privilege)); | 256 | return i; |
213 | 257 | ||
214 | /* Setup the address register. */ | 258 | switch (ops) { |
215 | write_wb_reg(val_reg, i, info->address); | 259 | case HW_BREAKPOINT_INSTALL: |
260 | /* | ||
261 | * Ensure debug monitors are enabled at the correct exception | ||
262 | * level. | ||
263 | */ | ||
264 | enable_debug_monitors(dbg_el); | ||
265 | /* Fall through */ | ||
266 | case HW_BREAKPOINT_RESTORE: | ||
267 | /* Setup the address register. */ | ||
268 | write_wb_reg(val_reg, i, info->address); | ||
269 | |||
270 | /* Setup the control register. */ | ||
271 | ctrl = encode_ctrl_reg(info->ctrl); | ||
272 | write_wb_reg(ctrl_reg, i, | ||
273 | reg_enable ? ctrl | 0x1 : ctrl & ~0x1); | ||
274 | break; | ||
275 | case HW_BREAKPOINT_UNINSTALL: | ||
276 | /* Reset the control register. */ | ||
277 | write_wb_reg(ctrl_reg, i, 0); | ||
216 | 278 | ||
217 | /* Setup the control register. */ | 279 | /* |
218 | ctrl = encode_ctrl_reg(info->ctrl); | 280 | * Release the debug monitors for the correct exception |
219 | write_wb_reg(ctrl_reg, i, reg_enable ? ctrl | 0x1 : ctrl & ~0x1); | 281 | * level. |
282 | */ | ||
283 | disable_debug_monitors(dbg_el); | ||
284 | break; | ||
285 | } | ||
220 | 286 | ||
221 | return 0; | 287 | return 0; |
222 | } | 288 | } |
223 | 289 | ||
224 | void arch_uninstall_hw_breakpoint(struct perf_event *bp) | 290 | /* |
291 | * Install a perf counter breakpoint. | ||
292 | */ | ||
293 | int arch_install_hw_breakpoint(struct perf_event *bp) | ||
225 | { | 294 | { |
226 | struct arch_hw_breakpoint *info = counter_arch_bp(bp); | 295 | return hw_breakpoint_control(bp, HW_BREAKPOINT_INSTALL); |
227 | struct perf_event **slot, **slots; | 296 | } |
228 | int i, max_slots, base; | ||
229 | |||
230 | if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) { | ||
231 | /* Breakpoint */ | ||
232 | base = AARCH64_DBG_REG_BCR; | ||
233 | slots = this_cpu_ptr(bp_on_reg); | ||
234 | max_slots = core_num_brps; | ||
235 | } else { | ||
236 | /* Watchpoint */ | ||
237 | base = AARCH64_DBG_REG_WCR; | ||
238 | slots = this_cpu_ptr(wp_on_reg); | ||
239 | max_slots = core_num_wrps; | ||
240 | } | ||
241 | |||
242 | /* Remove the breakpoint. */ | ||
243 | for (i = 0; i < max_slots; ++i) { | ||
244 | slot = &slots[i]; | ||
245 | |||
246 | if (*slot == bp) { | ||
247 | *slot = NULL; | ||
248 | break; | ||
249 | } | ||
250 | } | ||
251 | |||
252 | if (WARN_ONCE(i == max_slots, "Can't find any breakpoint slot")) | ||
253 | return; | ||
254 | |||
255 | /* Reset the control register. */ | ||
256 | write_wb_reg(base, i, 0); | ||
257 | 297 | ||
258 | /* Release the debug monitors for the correct exception level. */ | 298 | void arch_uninstall_hw_breakpoint(struct perf_event *bp) |
259 | disable_debug_monitors(debug_exception_level(info->ctrl.privilege)); | 299 | { |
300 | hw_breakpoint_control(bp, HW_BREAKPOINT_UNINSTALL); | ||
260 | } | 301 | } |
261 | 302 | ||
262 | static int get_hbp_len(u8 hbp_len) | 303 | static int get_hbp_len(u8 hbp_len) |
@@ -806,18 +847,36 @@ void hw_breakpoint_thread_switch(struct task_struct *next) | |||
806 | /* | 847 | /* |
807 | * CPU initialisation. | 848 | * CPU initialisation. |
808 | */ | 849 | */ |
809 | static void reset_ctrl_regs(void *unused) | 850 | static void hw_breakpoint_reset(void *unused) |
810 | { | 851 | { |
811 | int i; | 852 | int i; |
812 | 853 | struct perf_event **slots; | |
813 | for (i = 0; i < core_num_brps; ++i) { | 854 | /* |
814 | write_wb_reg(AARCH64_DBG_REG_BCR, i, 0UL); | 855 | * When a CPU goes through cold-boot, it does not have any installed |
815 | write_wb_reg(AARCH64_DBG_REG_BVR, i, 0UL); | 856 | * slot, so it is safe to share the same function for restoring and |
857 | * resetting breakpoints; when a CPU is hotplugged in, it goes | ||
858 | * through the slots, which are all empty, hence it just resets control | ||
859 | * and value for debug registers. | ||
860 | * When this function is triggered on warm-boot through a CPU PM | ||
861 | * notifier some slots might be initialized; if so they are | ||
862 | * reprogrammed according to the debug slots content. | ||
863 | */ | ||
864 | for (slots = this_cpu_ptr(bp_on_reg), i = 0; i < core_num_brps; ++i) { | ||
865 | if (slots[i]) { | ||
866 | hw_breakpoint_control(slots[i], HW_BREAKPOINT_RESTORE); | ||
867 | } else { | ||
868 | write_wb_reg(AARCH64_DBG_REG_BCR, i, 0UL); | ||
869 | write_wb_reg(AARCH64_DBG_REG_BVR, i, 0UL); | ||
870 | } | ||
816 | } | 871 | } |
817 | 872 | ||
818 | for (i = 0; i < core_num_wrps; ++i) { | 873 | for (slots = this_cpu_ptr(wp_on_reg), i = 0; i < core_num_wrps; ++i) { |
819 | write_wb_reg(AARCH64_DBG_REG_WCR, i, 0UL); | 874 | if (slots[i]) { |
820 | write_wb_reg(AARCH64_DBG_REG_WVR, i, 0UL); | 875 | hw_breakpoint_control(slots[i], HW_BREAKPOINT_RESTORE); |
876 | } else { | ||
877 | write_wb_reg(AARCH64_DBG_REG_WCR, i, 0UL); | ||
878 | write_wb_reg(AARCH64_DBG_REG_WVR, i, 0UL); | ||
879 | } | ||
821 | } | 880 | } |
822 | } | 881 | } |
823 | 882 | ||
@@ -827,7 +886,7 @@ static int hw_breakpoint_reset_notify(struct notifier_block *self, | |||
827 | { | 886 | { |
828 | int cpu = (long)hcpu; | 887 | int cpu = (long)hcpu; |
829 | if (action == CPU_ONLINE) | 888 | if (action == CPU_ONLINE) |
830 | smp_call_function_single(cpu, reset_ctrl_regs, NULL, 1); | 889 | smp_call_function_single(cpu, hw_breakpoint_reset, NULL, 1); |
831 | return NOTIFY_OK; | 890 | return NOTIFY_OK; |
832 | } | 891 | } |
833 | 892 | ||
@@ -835,6 +894,14 @@ static struct notifier_block hw_breakpoint_reset_nb = { | |||
835 | .notifier_call = hw_breakpoint_reset_notify, | 894 | .notifier_call = hw_breakpoint_reset_notify, |
836 | }; | 895 | }; |
837 | 896 | ||
897 | #ifdef CONFIG_ARM64_CPU_SUSPEND | ||
898 | extern void cpu_suspend_set_dbg_restorer(void (*hw_bp_restore)(void *)); | ||
899 | #else | ||
900 | static inline void cpu_suspend_set_dbg_restorer(void (*hw_bp_restore)(void *)) | ||
901 | { | ||
902 | } | ||
903 | #endif | ||
904 | |||
838 | /* | 905 | /* |
839 | * One-time initialisation. | 906 | * One-time initialisation. |
840 | */ | 907 | */ |
@@ -850,8 +917,8 @@ static int __init arch_hw_breakpoint_init(void) | |||
850 | * Reset the breakpoint resources. We assume that a halting | 917 | * Reset the breakpoint resources. We assume that a halting |
851 | * debugger will leave the world in a nice state for us. | 918 | * debugger will leave the world in a nice state for us. |
852 | */ | 919 | */ |
853 | smp_call_function(reset_ctrl_regs, NULL, 1); | 920 | smp_call_function(hw_breakpoint_reset, NULL, 1); |
854 | reset_ctrl_regs(NULL); | 921 | hw_breakpoint_reset(NULL); |
855 | 922 | ||
856 | /* Register debug fault handlers. */ | 923 | /* Register debug fault handlers. */ |
857 | hook_debug_fault_code(DBG_ESR_EVT_HWBP, breakpoint_handler, SIGTRAP, | 924 | hook_debug_fault_code(DBG_ESR_EVT_HWBP, breakpoint_handler, SIGTRAP, |
@@ -861,6 +928,8 @@ static int __init arch_hw_breakpoint_init(void) | |||
861 | 928 | ||
862 | /* Register hotplug notifier. */ | 929 | /* Register hotplug notifier. */ |
863 | register_cpu_notifier(&hw_breakpoint_reset_nb); | 930 | register_cpu_notifier(&hw_breakpoint_reset_nb); |
931 | /* Register cpu_suspend hw breakpoint restore hook */ | ||
932 | cpu_suspend_set_dbg_restorer(hw_breakpoint_reset); | ||
864 | 933 | ||
865 | return 0; | 934 | return 0; |
866 | } | 935 | } |