aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm64
diff options
context:
space:
mode:
authorLorenzo Pieralisi <lorenzo.pieralisi@arm.com>2013-08-05 10:20:35 -0400
committerLorenzo Pieralisi <lorenzo.pieralisi@arm.com>2013-12-16 12:17:34 -0500
commit60fc6942f6ac124503ed7f8506736a8f56b4ca7e (patch)
tree62187c5311329a3bff8f62eb91c50d805e3d7a60 /arch/arm64
parent2f04304587544dd14277413ebff12fa0f4fc932c (diff)
arm64: kernel: implement HW breakpoints CPU PM notifier
When a CPU is shutdown either through CPU idle or suspend to RAM, the content of HW breakpoint registers must be reset or restored to proper values when CPU resume from low power states. This patch adds debug register restore operations to the HW breakpoint control function and implements a CPU PM notifier that allows to restore the content of HW breakpoint registers to allow proper suspend/resume operations. Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Diffstat (limited to 'arch/arm64')
-rw-r--r--arch/arm64/kernel/hw_breakpoint.c79
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
172enum hw_breakpoint_ops { 173enum 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 */
843static void reset_ctrl_regs(void *unused) 850static 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
898static 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
910static struct notifier_block hw_breakpoint_cpu_pm_nb = {
911 .notifier_call = hw_breakpoint_cpu_pm_notify,
912};
913
914static void __init hw_breakpoint_pm_init(void)
915{
916 cpu_pm_register_notifier(&hw_breakpoint_cpu_pm_nb);
917}
918#else
919static 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}