diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c index bcaaac9e14d6..f17f581116fc 100644 --- a/arch/arm64/kernel/hw_breakpoint.c +++ b/arch/arm64/kernel/hw_breakpoint.c @@ -894,29 +894,10 @@ static struct notifier_block hw_breakpoint_reset_nb = { .notifier_call = hw_breakpoint_reset_notify, }; -#ifdef CONFIG_CPU_PM -static int hw_breakpoint_cpu_pm_notify(struct notifier_block *self, - unsigned long action, - void *v) -{ - if (action == CPU_PM_EXIT) { - hw_breakpoint_reset(NULL); - return NOTIFY_OK; - } - - return NOTIFY_DONE; -} - -static struct notifier_block hw_breakpoint_cpu_pm_nb = { - .notifier_call = hw_breakpoint_cpu_pm_notify, -}; - -static void __init hw_breakpoint_pm_init(void) -{ - cpu_pm_register_notifier(&hw_breakpoint_cpu_pm_nb); -} +#ifdef CONFIG_ARM64_CPU_SUSPEND +extern void cpu_suspend_set_dbg_restorer(void (*hw_bp_restore)(void *)); #else -static inline void hw_breakpoint_pm_init(void) +static inline void cpu_suspend_set_dbg_restorer(void (*hw_bp_restore)(void *)) { } #endif @@ -947,7 +928,8 @@ static int __init arch_hw_breakpoint_init(void) /* Register hotplug notifier. */ register_cpu_notifier(&hw_breakpoint_reset_nb); - hw_breakpoint_pm_init(); + /* Register cpu_suspend hw breakpoint restore hook */ + cpu_suspend_set_dbg_restorer(hw_breakpoint_reset); return 0; } diff --git a/arch/arm64/kernel/suspend.c b/arch/arm64/kernel/suspend.c index e074b1c32723..430344e2c989 100644 --- a/arch/arm64/kernel/suspend.c +++ b/arch/arm64/kernel/suspend.c @@ -38,6 +38,22 @@ int __cpu_suspend_finisher(unsigned long arg, struct cpu_suspend_ctx *ptr, return cpu_ops[cpu]->cpu_suspend(arg); } +/* + * This hook is provided so that cpu_suspend code can restore HW + * breakpoints as early as possible in the resume path, before reenabling + * debug exceptions. Code cannot be run from a CPU PM notifier since by the + * time the notifier runs debug exceptions might have been enabled already, + * with HW breakpoints registers content still in an unknown state. + */ +void (*hw_breakpoint_restore)(void *); +void __init cpu_suspend_set_dbg_restorer(void (*hw_bp_restore)(void *)) +{ + /* Prevent multiple restore hook initializations */ + if (WARN_ON(hw_breakpoint_restore)) + return; + hw_breakpoint_restore = hw_bp_restore; +} + /** * cpu_suspend * @@ -73,6 +89,13 @@ int cpu_suspend(unsigned long arg) if (ret == 0) { cpu_switch_mm(mm->pgd, mm); flush_tlb_all(); + /* + * Restore HW breakpoint registers to sane values + * before debug exceptions are possibly reenabled + * through local_dbg_restore. + */ + if (hw_breakpoint_restore) + hw_breakpoint_restore(NULL); } /*