diff options
author | Will Deacon <will.deacon@arm.com> | 2011-08-08 09:26:53 -0400 |
---|---|---|
committer | Will Deacon <will.deacon@arm.com> | 2011-08-31 05:42:48 -0400 |
commit | 0d352e3d006c9589f22580212c3822cf62b6d775 (patch) | |
tree | 3bfaf9313cab84333380789471b7a9329f3f60fd /arch/arm/kernel/hw_breakpoint.c | |
parent | 6f26aa05c9edffff6a4c2cd71774bc659a5cceec (diff) |
ARM: hw_breakpoint: trap undef instruction exceptions in reset_ctrl_regs
The ARM debug registers can only be accessed if the DBGSWENABLE signal
to the core is driven HIGH by the DAP. The architecture does not provide
a way to detect the value of this signal, so the best we can do is
register an undef_hook to trap debug register co-processor accesses and
then fail if the trap is taken.
Signed-off-by: Will Deacon <will.deacon@arm.com>
Diffstat (limited to 'arch/arm/kernel/hw_breakpoint.c')
-rw-r--r-- | arch/arm/kernel/hw_breakpoint.c | 46 |
1 files changed, 37 insertions, 9 deletions
diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c index 448143e44b65..64ac5c672396 100644 --- a/arch/arm/kernel/hw_breakpoint.c +++ b/arch/arm/kernel/hw_breakpoint.c | |||
@@ -857,11 +857,31 @@ static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr, | |||
857 | /* | 857 | /* |
858 | * One-time initialisation. | 858 | * One-time initialisation. |
859 | */ | 859 | */ |
860 | static void reset_ctrl_regs(void *info) | 860 | static cpumask_t debug_err_mask; |
861 | |||
862 | static int debug_reg_trap(struct pt_regs *regs, unsigned int instr) | ||
863 | { | ||
864 | int cpu = smp_processor_id(); | ||
865 | |||
866 | pr_warning("Debug register access (0x%x) caused undefined instruction on CPU %d\n", | ||
867 | instr, cpu); | ||
868 | |||
869 | /* Set the error flag for this CPU and skip the faulting instruction. */ | ||
870 | cpumask_set_cpu(cpu, &debug_err_mask); | ||
871 | instruction_pointer(regs) += 4; | ||
872 | return 0; | ||
873 | } | ||
874 | |||
875 | static struct undef_hook debug_reg_hook = { | ||
876 | .instr_mask = 0x0fe80f10, | ||
877 | .instr_val = 0x0e000e10, | ||
878 | .fn = debug_reg_trap, | ||
879 | }; | ||
880 | |||
881 | static void reset_ctrl_regs(void *unused) | ||
861 | { | 882 | { |
862 | int i, raw_num_brps, err = 0, cpu = smp_processor_id(); | 883 | int i, raw_num_brps, err = 0, cpu = smp_processor_id(); |
863 | u32 dbg_power; | 884 | u32 dbg_power; |
864 | cpumask_t *cpumask = info; | ||
865 | 885 | ||
866 | /* | 886 | /* |
867 | * v7 debug contains save and restore registers so that debug state | 887 | * v7 debug contains save and restore registers so that debug state |
@@ -893,7 +913,7 @@ static void reset_ctrl_regs(void *info) | |||
893 | 913 | ||
894 | if (err) { | 914 | if (err) { |
895 | pr_warning("CPU %d debug is powered down!\n", cpu); | 915 | pr_warning("CPU %d debug is powered down!\n", cpu); |
896 | cpumask_or(cpumask, cpumask, cpumask_of(cpu)); | 916 | cpumask_or(&debug_err_mask, &debug_err_mask, cpumask_of(cpu)); |
897 | return; | 917 | return; |
898 | } | 918 | } |
899 | 919 | ||
@@ -932,6 +952,7 @@ static int __cpuinit dbg_reset_notify(struct notifier_block *self, | |||
932 | { | 952 | { |
933 | if (action == CPU_ONLINE) | 953 | if (action == CPU_ONLINE) |
934 | smp_call_function_single((int)cpu, reset_ctrl_regs, NULL, 1); | 954 | smp_call_function_single((int)cpu, reset_ctrl_regs, NULL, 1); |
955 | |||
935 | return NOTIFY_OK; | 956 | return NOTIFY_OK; |
936 | } | 957 | } |
937 | 958 | ||
@@ -942,7 +963,6 @@ static struct notifier_block __cpuinitdata dbg_reset_nb = { | |||
942 | static int __init arch_hw_breakpoint_init(void) | 963 | static int __init arch_hw_breakpoint_init(void) |
943 | { | 964 | { |
944 | u32 dscr; | 965 | u32 dscr; |
945 | cpumask_t cpumask = { CPU_BITS_NONE }; | ||
946 | 966 | ||
947 | debug_arch = get_debug_arch(); | 967 | debug_arch = get_debug_arch(); |
948 | 968 | ||
@@ -955,21 +975,29 @@ static int __init arch_hw_breakpoint_init(void) | |||
955 | core_num_brps = get_num_brps(); | 975 | core_num_brps = get_num_brps(); |
956 | core_num_wrps = get_num_wrps(); | 976 | core_num_wrps = get_num_wrps(); |
957 | 977 | ||
958 | pr_info("found %d " "%s" "breakpoint and %d watchpoint registers.\n", | 978 | /* |
959 | core_num_brps, core_has_mismatch_brps() ? "(+1 reserved) " : | 979 | * We need to tread carefully here because DBGSWENABLE may be |
960 | "", core_num_wrps); | 980 | * driven low on this core and there isn't an architected way to |
981 | * determine that. | ||
982 | */ | ||
983 | register_undef_hook(&debug_reg_hook); | ||
961 | 984 | ||
962 | /* | 985 | /* |
963 | * Reset the breakpoint resources. We assume that a halting | 986 | * Reset the breakpoint resources. We assume that a halting |
964 | * debugger will leave the world in a nice state for us. | 987 | * debugger will leave the world in a nice state for us. |
965 | */ | 988 | */ |
966 | on_each_cpu(reset_ctrl_regs, &cpumask, 1); | 989 | on_each_cpu(reset_ctrl_regs, NULL, 1); |
967 | if (!cpumask_empty(&cpumask)) { | 990 | unregister_undef_hook(&debug_reg_hook); |
991 | if (!cpumask_empty(&debug_err_mask)) { | ||
968 | core_num_brps = 0; | 992 | core_num_brps = 0; |
969 | core_num_wrps = 0; | 993 | core_num_wrps = 0; |
970 | return 0; | 994 | return 0; |
971 | } | 995 | } |
972 | 996 | ||
997 | pr_info("found %d " "%s" "breakpoint and %d watchpoint registers.\n", | ||
998 | core_num_brps, core_has_mismatch_brps() ? "(+1 reserved) " : | ||
999 | "", core_num_wrps); | ||
1000 | |||
973 | ARM_DBG_READ(c1, 0, dscr); | 1001 | ARM_DBG_READ(c1, 0, dscr); |
974 | if (dscr & ARM_DSCR_HDBGEN) { | 1002 | if (dscr & ARM_DSCR_HDBGEN) { |
975 | max_watchpoint_len = 4; | 1003 | max_watchpoint_len = 4; |