diff options
Diffstat (limited to 'arch/arm64/kernel/hw_breakpoint.c')
-rw-r--r-- | arch/arm64/kernel/hw_breakpoint.c | 79 |
1 files changed, 66 insertions, 13 deletions
diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c index e89459123fa3..bcaaac9e14d6 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> |
@@ -171,7 +172,8 @@ static enum debug_el debug_exception_level(int privilege) | |||
171 | 172 | ||
172 | enum hw_breakpoint_ops { | 173 | enum hw_breakpoint_ops { |
173 | HW_BREAKPOINT_INSTALL, | 174 | HW_BREAKPOINT_INSTALL, |
174 | HW_BREAKPOINT_UNINSTALL | 175 | HW_BREAKPOINT_UNINSTALL, |
176 | HW_BREAKPOINT_RESTORE | ||
175 | }; | 177 | }; |
176 | 178 | ||
177 | /** | 179 | /** |
@@ -210,6 +212,10 @@ static int hw_breakpoint_slot_setup(struct perf_event **slots, int max_slots, | |||
210 | return i; | 212 | return i; |
211 | } | 213 | } |
212 | break; | 214 | break; |
215 | case HW_BREAKPOINT_RESTORE: | ||
216 | if (*slot == bp) | ||
217 | return i; | ||
218 | break; | ||
213 | default: | 219 | default: |
214 | pr_warn_once("Unhandled hw breakpoint ops %d\n", ops); | 220 | pr_warn_once("Unhandled hw breakpoint ops %d\n", ops); |
215 | return -EINVAL; | 221 | return -EINVAL; |
@@ -256,7 +262,8 @@ static int hw_breakpoint_control(struct perf_event *bp, | |||
256 | * level. | 262 | * level. |
257 | */ | 263 | */ |
258 | enable_debug_monitors(dbg_el); | 264 | enable_debug_monitors(dbg_el); |
259 | 265 | /* Fall through */ | |
266 | case HW_BREAKPOINT_RESTORE: | ||
260 | /* Setup the address register. */ | 267 | /* Setup the address register. */ |
261 | write_wb_reg(val_reg, i, info->address); | 268 | write_wb_reg(val_reg, i, info->address); |
262 | 269 | ||
@@ -840,18 +847,36 @@ void hw_breakpoint_thread_switch(struct task_struct *next) | |||
840 | /* | 847 | /* |
841 | * CPU initialisation. | 848 | * CPU initialisation. |
842 | */ | 849 | */ |
843 | static void reset_ctrl_regs(void *unused) | 850 | static void hw_breakpoint_reset(void *unused) |
844 | { | 851 | { |
845 | int i; | 852 | int i; |
846 | 853 | struct perf_event **slots; | |
847 | for (i = 0; i < core_num_brps; ++i) { | 854 | /* |
848 | write_wb_reg(AARCH64_DBG_REG_BCR, i, 0UL); | 855 | * When a CPU goes through cold-boot, it does not have any installed |
849 | 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 | } | ||
850 | } | 871 | } |
851 | 872 | ||
852 | for (i = 0; i < core_num_wrps; ++i) { | 873 | for (slots = this_cpu_ptr(wp_on_reg), i = 0; i < core_num_wrps; ++i) { |
853 | write_wb_reg(AARCH64_DBG_REG_WCR, i, 0UL); | 874 | if (slots[i]) { |
854 | 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 | } | ||
855 | } | 880 | } |
856 | } | 881 | } |
857 | 882 | ||
@@ -861,7 +886,7 @@ static int hw_breakpoint_reset_notify(struct notifier_block *self, | |||
861 | { | 886 | { |
862 | int cpu = (long)hcpu; | 887 | int cpu = (long)hcpu; |
863 | if (action == CPU_ONLINE) | 888 | if (action == CPU_ONLINE) |
864 | smp_call_function_single(cpu, reset_ctrl_regs, NULL, 1); | 889 | smp_call_function_single(cpu, hw_breakpoint_reset, NULL, 1); |
865 | return NOTIFY_OK; | 890 | return NOTIFY_OK; |
866 | } | 891 | } |
867 | 892 | ||
@@ -869,6 +894,33 @@ static struct notifier_block hw_breakpoint_reset_nb = { | |||
869 | .notifier_call = hw_breakpoint_reset_notify, | 894 | .notifier_call = hw_breakpoint_reset_notify, |
870 | }; | 895 | }; |
871 | 896 | ||
897 | #ifdef CONFIG_CPU_PM | ||
898 | static int hw_breakpoint_cpu_pm_notify(struct notifier_block *self, | ||
899 | unsigned long action, | ||
900 | void *v) | ||
901 | { | ||
902 | if (action == CPU_PM_EXIT) { | ||
903 | hw_breakpoint_reset(NULL); | ||
904 | return NOTIFY_OK; | ||
905 | } | ||
906 | |||
907 | return NOTIFY_DONE; | ||
908 | } | ||
909 | |||
910 | static struct notifier_block hw_breakpoint_cpu_pm_nb = { | ||
911 | .notifier_call = hw_breakpoint_cpu_pm_notify, | ||
912 | }; | ||
913 | |||
914 | static void __init hw_breakpoint_pm_init(void) | ||
915 | { | ||
916 | cpu_pm_register_notifier(&hw_breakpoint_cpu_pm_nb); | ||
917 | } | ||
918 | #else | ||
919 | static inline void hw_breakpoint_pm_init(void) | ||
920 | { | ||
921 | } | ||
922 | #endif | ||
923 | |||
872 | /* | 924 | /* |
873 | * One-time initialisation. | 925 | * One-time initialisation. |
874 | */ | 926 | */ |
@@ -884,8 +936,8 @@ static int __init arch_hw_breakpoint_init(void) | |||
884 | * Reset the breakpoint resources. We assume that a halting | 936 | * Reset the breakpoint resources. We assume that a halting |
885 | * debugger will leave the world in a nice state for us. | 937 | * debugger will leave the world in a nice state for us. |
886 | */ | 938 | */ |
887 | smp_call_function(reset_ctrl_regs, NULL, 1); | 939 | smp_call_function(hw_breakpoint_reset, NULL, 1); |
888 | reset_ctrl_regs(NULL); | 940 | hw_breakpoint_reset(NULL); |
889 | 941 | ||
890 | /* Register debug fault handlers. */ | 942 | /* Register debug fault handlers. */ |
891 | hook_debug_fault_code(DBG_ESR_EVT_HWBP, breakpoint_handler, SIGTRAP, | 943 | hook_debug_fault_code(DBG_ESR_EVT_HWBP, breakpoint_handler, SIGTRAP, |
@@ -895,6 +947,7 @@ static int __init arch_hw_breakpoint_init(void) | |||
895 | 947 | ||
896 | /* Register hotplug notifier. */ | 948 | /* Register hotplug notifier. */ |
897 | register_cpu_notifier(&hw_breakpoint_reset_nb); | 949 | register_cpu_notifier(&hw_breakpoint_reset_nb); |
950 | hw_breakpoint_pm_init(); | ||
898 | 951 | ||
899 | return 0; | 952 | return 0; |
900 | } | 953 | } |