Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

powerpc: Allow clearing and restoring registers independent of saved breakpoint state

For the coming temporary mm used for instruction patching, the
breakpoint registers need to be cleared to prevent them from
accidentally being triggered. As soon as the patching is done, the
breakpoints will be restored.

The breakpoint state is stored in the per-cpu variable current_brk[].
Add a suspend_breakpoints() function which will clear the breakpoint
registers without touching the state in current_brk[]. Add a pair
function restore_breakpoints() which will move the state in
current_brk[] back to the registers.

Signed-off-by: Jordan Niethe <jniethe5@gmail.com>
Signed-off-by: Benjamin Gray <bgray@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20221109045112.187069-2-bgray@linux.ibm.com

authored by

Jordan Niethe and committed by
Michael Ellerman
3671f4eb e082e99f

+37 -3
+2
arch/powerpc/include/asm/debug.h
··· 46 46 #endif 47 47 48 48 void __set_breakpoint(int nr, struct arch_hw_breakpoint *brk); 49 + void suspend_breakpoints(void); 50 + void restore_breakpoints(void); 49 51 bool ppc_breakpoint_available(void); 50 52 #ifdef CONFIG_PPC_ADV_DEBUG_REGS 51 53 extern void do_send_trap(struct pt_regs *regs, unsigned long address,
+35 -3
arch/powerpc/kernel/process.c
··· 862 862 return 0; 863 863 } 864 864 865 - void __set_breakpoint(int nr, struct arch_hw_breakpoint *brk) 865 + static void set_hw_breakpoint(int nr, struct arch_hw_breakpoint *brk) 866 866 { 867 - memcpy(this_cpu_ptr(&current_brk[nr]), brk, sizeof(*brk)); 868 - 869 867 if (dawr_enabled()) 870 868 // Power8 or later 871 869 set_dawr(nr, brk); ··· 877 879 WARN_ON_ONCE(1); 878 880 } 879 881 882 + void __set_breakpoint(int nr, struct arch_hw_breakpoint *brk) 883 + { 884 + memcpy(this_cpu_ptr(&current_brk[nr]), brk, sizeof(*brk)); 885 + set_hw_breakpoint(nr, brk); 886 + } 887 + 880 888 /* Check if we have DAWR or DABR hardware */ 881 889 bool ppc_breakpoint_available(void) 882 890 { ··· 894 890 return true; 895 891 } 896 892 EXPORT_SYMBOL_GPL(ppc_breakpoint_available); 893 + 894 + /* Disable the breakpoint in hardware without touching current_brk[] */ 895 + void suspend_breakpoints(void) 896 + { 897 + struct arch_hw_breakpoint brk = {0}; 898 + int i; 899 + 900 + if (!ppc_breakpoint_available()) 901 + return; 902 + 903 + for (i = 0; i < nr_wp_slots(); i++) 904 + set_hw_breakpoint(i, &brk); 905 + } 906 + 907 + /* 908 + * Re-enable breakpoints suspended by suspend_breakpoints() in hardware 909 + * from current_brk[] 910 + */ 911 + void restore_breakpoints(void) 912 + { 913 + int i; 914 + 915 + if (!ppc_breakpoint_available()) 916 + return; 917 + 918 + for (i = 0; i < nr_wp_slots(); i++) 919 + set_hw_breakpoint(i, this_cpu_ptr(&current_brk[i])); 920 + } 897 921 898 922 #ifdef CONFIG_PPC_TRANSACTIONAL_MEM 899 923