diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-03-20 15:22:07 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-03-20 15:22:07 -0400 |
| commit | fffad3e1b34aaced7724ef513dff0d8232ad8d80 (patch) | |
| tree | fc59facad055fef25108eec0ab93c2a7d8bc3070 /arch/xtensa/kernel | |
| parent | 1e75a9f34a5ed5902707fb74b468356c55142b71 (diff) | |
| parent | 9da8320bb97768e35f2e64fa7642015271d672eb (diff) | |
Merge tag 'xtensa-next-20160320' of git://github.com/czankel/xtensa-linux
Pull Xtensa updates from Chris Zankel:
"Xtensa improvements for 4.6:
- control whether perf IRQ is treated as NMI from Kconfig
- implement ioremap for regions outside KIO segment
- fix ISS serial port behaviour when EOF is reached
- fix preemption in {clear,copy}_user_highpage
- fix endianness issues for XTFPGA devices, big-endian cores are now
fully functional
- clean up debug infrastructure and add support for hardware
breakpoints and watchpoints
- add processor configurations for Three Core HiFi-2 MX and HiFi3
cpus"
* tag 'xtensa-next-20160320' of git://github.com/czankel/xtensa-linux:
xtensa: add test_kc705_hifi variant
xtensa: add Three Core HiFi-2 MX Variant.
xtensa: support hardware breakpoints/watchpoints
xtensa: use context structure for debug exceptions
xtensa: remove remaining non-functional KGDB bits
xtensa: clear all DBREAKC registers on start
xtensa: xtfpga: fix earlycon endianness
xtensa: xtfpga: fix i2c controller register width and endianness
xtensa: xtfpga: fix ethernet controller endianness
xtensa: xtfpga: fix serial port register width and endianness
xtensa: define CONFIG_CPU_{BIG,LITTLE}_ENDIAN
xtensa: fix preemption in {clear,copy}_user_highpage
xtensa: ISS: don't hang if stdin EOF is reached
xtensa: support ioremap for memory outside KIO region
xtensa: use XTENSA_INT_LEVEL macro in asm/timex.h
xtensa: make fake NMI configurable
Diffstat (limited to 'arch/xtensa/kernel')
| -rw-r--r-- | arch/xtensa/kernel/Makefile | 2 | ||||
| -rw-r--r-- | arch/xtensa/kernel/asm-offsets.c | 12 | ||||
| -rw-r--r-- | arch/xtensa/kernel/entry.S | 90 | ||||
| -rw-r--r-- | arch/xtensa/kernel/head.S | 7 | ||||
| -rw-r--r-- | arch/xtensa/kernel/hw_breakpoint.c | 317 | ||||
| -rw-r--r-- | arch/xtensa/kernel/process.c | 5 | ||||
| -rw-r--r-- | arch/xtensa/kernel/ptrace.c | 164 | ||||
| -rw-r--r-- | arch/xtensa/kernel/traps.c | 69 | ||||
| -rw-r--r-- | arch/xtensa/kernel/vectors.S | 4 |
9 files changed, 626 insertions, 44 deletions
diff --git a/arch/xtensa/kernel/Makefile b/arch/xtensa/kernel/Makefile index 4db730290d2d..c31f5d5afc7d 100644 --- a/arch/xtensa/kernel/Makefile +++ b/arch/xtensa/kernel/Makefile | |||
| @@ -8,12 +8,12 @@ obj-y := align.o coprocessor.o entry.o irq.o pci-dma.o platform.o process.o \ | |||
| 8 | ptrace.o setup.o signal.o stacktrace.o syscall.o time.o traps.o \ | 8 | ptrace.o setup.o signal.o stacktrace.o syscall.o time.o traps.o \ |
| 9 | vectors.o | 9 | vectors.o |
| 10 | 10 | ||
| 11 | obj-$(CONFIG_KGDB) += xtensa-stub.o | ||
| 12 | obj-$(CONFIG_PCI) += pci.o | 11 | obj-$(CONFIG_PCI) += pci.o |
| 13 | obj-$(CONFIG_MODULES) += xtensa_ksyms.o module.o | 12 | obj-$(CONFIG_MODULES) += xtensa_ksyms.o module.o |
| 14 | obj-$(CONFIG_FUNCTION_TRACER) += mcount.o | 13 | obj-$(CONFIG_FUNCTION_TRACER) += mcount.o |
| 15 | obj-$(CONFIG_SMP) += smp.o mxhead.o | 14 | obj-$(CONFIG_SMP) += smp.o mxhead.o |
| 16 | obj-$(CONFIG_XTENSA_VARIANT_HAVE_PERF_EVENTS) += perf_event.o | 15 | obj-$(CONFIG_XTENSA_VARIANT_HAVE_PERF_EVENTS) += perf_event.o |
| 16 | obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o | ||
| 17 | 17 | ||
| 18 | AFLAGS_head.o += -mtext-section-literals | 18 | AFLAGS_head.o += -mtext-section-literals |
| 19 | AFLAGS_mxhead.o += -mtext-section-literals | 19 | AFLAGS_mxhead.o += -mtext-section-literals |
diff --git a/arch/xtensa/kernel/asm-offsets.c b/arch/xtensa/kernel/asm-offsets.c index b123ace3b67c..8e10e357ee32 100644 --- a/arch/xtensa/kernel/asm-offsets.c +++ b/arch/xtensa/kernel/asm-offsets.c | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | #include <linux/kbuild.h> | 23 | #include <linux/kbuild.h> |
| 24 | 24 | ||
| 25 | #include <asm/ptrace.h> | 25 | #include <asm/ptrace.h> |
| 26 | #include <asm/traps.h> | ||
| 26 | #include <asm/uaccess.h> | 27 | #include <asm/uaccess.h> |
| 27 | 28 | ||
| 28 | int main(void) | 29 | int main(void) |
| @@ -117,5 +118,16 @@ int main(void) | |||
| 117 | DEFINE(_CLONE_UNTRACED, CLONE_UNTRACED); | 118 | DEFINE(_CLONE_UNTRACED, CLONE_UNTRACED); |
| 118 | DEFINE(PG_ARCH_1, PG_arch_1); | 119 | DEFINE(PG_ARCH_1, PG_arch_1); |
| 119 | 120 | ||
| 121 | /* struct debug_table */ | ||
| 122 | DEFINE(DT_DEBUG_EXCEPTION, | ||
| 123 | offsetof(struct debug_table, debug_exception)); | ||
| 124 | DEFINE(DT_DEBUG_SAVE, offsetof(struct debug_table, debug_save)); | ||
| 125 | #ifdef CONFIG_HAVE_HW_BREAKPOINT | ||
| 126 | DEFINE(DT_DBREAKC_SAVE, offsetof(struct debug_table, dbreakc_save)); | ||
| 127 | DEFINE(DT_ICOUNT_SAVE, offsetof(struct debug_table, icount_save)); | ||
| 128 | DEFINE(DT_ICOUNT_LEVEL_SAVE, | ||
| 129 | offsetof(struct debug_table, icount_level_save)); | ||
| 130 | #endif | ||
| 131 | |||
| 120 | return 0; | 132 | return 0; |
| 121 | } | 133 | } |
diff --git a/arch/xtensa/kernel/entry.S b/arch/xtensa/kernel/entry.S index db5c1765b413..fe8f7e7efb9d 100644 --- a/arch/xtensa/kernel/entry.S +++ b/arch/xtensa/kernel/entry.S | |||
| @@ -543,6 +543,12 @@ common_exception_return: | |||
| 543 | #endif | 543 | #endif |
| 544 | 544 | ||
| 545 | 5: | 545 | 5: |
| 546 | #ifdef CONFIG_HAVE_HW_BREAKPOINT | ||
| 547 | _bbci.l a4, TIF_DB_DISABLED, 7f | ||
| 548 | movi a4, restore_dbreak | ||
| 549 | callx4 a4 | ||
| 550 | 7: | ||
| 551 | #endif | ||
| 546 | #ifdef CONFIG_DEBUG_TLB_SANITY | 552 | #ifdef CONFIG_DEBUG_TLB_SANITY |
| 547 | l32i a4, a1, PT_DEPC | 553 | l32i a4, a1, PT_DEPC |
| 548 | bgeui a4, VALID_DOUBLE_EXCEPTION_ADDRESS, 4f | 554 | bgeui a4, VALID_DOUBLE_EXCEPTION_ADDRESS, 4f |
| @@ -789,39 +795,99 @@ ENTRY(debug_exception) | |||
| 789 | 795 | ||
| 790 | movi a2, 1 << PS_EXCM_BIT | 796 | movi a2, 1 << PS_EXCM_BIT |
| 791 | or a2, a0, a2 | 797 | or a2, a0, a2 |
| 792 | movi a0, debug_exception # restore a3, debug jump vector | ||
| 793 | wsr a2, ps | 798 | wsr a2, ps |
| 794 | xsr a0, SREG_EXCSAVE + XCHAL_DEBUGLEVEL | ||
| 795 | 799 | ||
| 796 | /* Switch to kernel/user stack, restore jump vector, and save a0 */ | 800 | /* Switch to kernel/user stack, restore jump vector, and save a0 */ |
| 797 | 801 | ||
| 798 | bbsi.l a2, PS_UM_BIT, 2f # jump if user mode | 802 | bbsi.l a2, PS_UM_BIT, 2f # jump if user mode |
| 799 | 803 | ||
| 800 | addi a2, a1, -16-PT_SIZE # assume kernel stack | 804 | addi a2, a1, -16-PT_SIZE # assume kernel stack |
| 805 | 3: | ||
| 806 | l32i a0, a3, DT_DEBUG_SAVE | ||
| 807 | s32i a1, a2, PT_AREG1 | ||
| 801 | s32i a0, a2, PT_AREG0 | 808 | s32i a0, a2, PT_AREG0 |
| 802 | movi a0, 0 | 809 | movi a0, 0 |
| 803 | s32i a1, a2, PT_AREG1 | ||
| 804 | s32i a0, a2, PT_DEPC # mark it as a regular exception | 810 | s32i a0, a2, PT_DEPC # mark it as a regular exception |
| 811 | xsr a3, SREG_EXCSAVE + XCHAL_DEBUGLEVEL | ||
| 805 | xsr a0, depc | 812 | xsr a0, depc |
| 806 | s32i a3, a2, PT_AREG3 | 813 | s32i a3, a2, PT_AREG3 |
| 807 | s32i a0, a2, PT_AREG2 | 814 | s32i a0, a2, PT_AREG2 |
| 808 | mov a1, a2 | 815 | mov a1, a2 |
| 816 | |||
| 817 | /* Debug exception is handled as an exception, so interrupts will | ||
| 818 | * likely be enabled in the common exception handler. Disable | ||
| 819 | * preemption if we have HW breakpoints to preserve DEBUGCAUSE.DBNUM | ||
| 820 | * meaning. | ||
| 821 | */ | ||
| 822 | #if defined(CONFIG_PREEMPT_COUNT) && defined(CONFIG_HAVE_HW_BREAKPOINT) | ||
| 823 | GET_THREAD_INFO(a2, a1) | ||
| 824 | l32i a3, a2, TI_PRE_COUNT | ||
| 825 | addi a3, a3, 1 | ||
| 826 | s32i a3, a2, TI_PRE_COUNT | ||
| 827 | #endif | ||
| 828 | |||
| 829 | rsr a2, ps | ||
| 830 | bbsi.l a2, PS_UM_BIT, _user_exception | ||
| 809 | j _kernel_exception | 831 | j _kernel_exception |
| 810 | 832 | ||
| 811 | 2: rsr a2, excsave1 | 833 | 2: rsr a2, excsave1 |
| 812 | l32i a2, a2, EXC_TABLE_KSTK # load kernel stack pointer | 834 | l32i a2, a2, EXC_TABLE_KSTK # load kernel stack pointer |
| 813 | s32i a0, a2, PT_AREG0 | 835 | j 3b |
| 836 | |||
| 837 | #ifdef CONFIG_HAVE_HW_BREAKPOINT | ||
| 838 | /* Debug exception while in exception mode. This may happen when | ||
| 839 | * window overflow/underflow handler or fast exception handler hits | ||
| 840 | * data breakpoint, in which case save and disable all data | ||
| 841 | * breakpoints, single-step faulting instruction and restore data | ||
| 842 | * breakpoints. | ||
| 843 | */ | ||
| 844 | 1: | ||
| 845 | bbci.l a0, PS_UM_BIT, 1b # jump if kernel mode | ||
| 846 | |||
| 847 | rsr a0, debugcause | ||
| 848 | bbsi.l a0, DEBUGCAUSE_DBREAK_BIT, .Ldebug_save_dbreak | ||
| 849 | |||
| 850 | .set _index, 0 | ||
| 851 | .rept XCHAL_NUM_DBREAK | ||
| 852 | l32i a0, a3, DT_DBREAKC_SAVE + _index * 4 | ||
| 853 | wsr a0, SREG_DBREAKC + _index | ||
| 854 | .set _index, _index + 1 | ||
| 855 | .endr | ||
| 856 | |||
| 857 | l32i a0, a3, DT_ICOUNT_LEVEL_SAVE | ||
| 858 | wsr a0, icountlevel | ||
| 859 | |||
| 860 | l32i a0, a3, DT_ICOUNT_SAVE | ||
| 861 | xsr a0, icount | ||
| 862 | |||
| 863 | l32i a0, a3, DT_DEBUG_SAVE | ||
| 864 | xsr a3, SREG_EXCSAVE + XCHAL_DEBUGLEVEL | ||
| 865 | rfi XCHAL_DEBUGLEVEL | ||
| 866 | |||
| 867 | .Ldebug_save_dbreak: | ||
| 868 | .set _index, 0 | ||
| 869 | .rept XCHAL_NUM_DBREAK | ||
| 814 | movi a0, 0 | 870 | movi a0, 0 |
| 815 | s32i a1, a2, PT_AREG1 | 871 | xsr a0, SREG_DBREAKC + _index |
| 816 | s32i a0, a2, PT_DEPC | 872 | s32i a0, a3, DT_DBREAKC_SAVE + _index * 4 |
| 817 | xsr a0, depc | 873 | .set _index, _index + 1 |
| 818 | s32i a3, a2, PT_AREG3 | 874 | .endr |
| 819 | s32i a0, a2, PT_AREG2 | ||
| 820 | mov a1, a2 | ||
| 821 | j _user_exception | ||
| 822 | 875 | ||
| 823 | /* Debug exception while in exception mode. */ | 876 | movi a0, XCHAL_EXCM_LEVEL + 1 |
| 877 | xsr a0, icountlevel | ||
| 878 | s32i a0, a3, DT_ICOUNT_LEVEL_SAVE | ||
| 879 | |||
| 880 | movi a0, 0xfffffffe | ||
| 881 | xsr a0, icount | ||
| 882 | s32i a0, a3, DT_ICOUNT_SAVE | ||
| 883 | |||
| 884 | l32i a0, a3, DT_DEBUG_SAVE | ||
| 885 | xsr a3, SREG_EXCSAVE + XCHAL_DEBUGLEVEL | ||
| 886 | rfi XCHAL_DEBUGLEVEL | ||
| 887 | #else | ||
| 888 | /* Debug exception while in exception mode. Should not happen. */ | ||
| 824 | 1: j 1b // FIXME!! | 889 | 1: j 1b // FIXME!! |
| 890 | #endif | ||
| 825 | 891 | ||
| 826 | ENDPROC(debug_exception) | 892 | ENDPROC(debug_exception) |
| 827 | 893 | ||
diff --git a/arch/xtensa/kernel/head.S b/arch/xtensa/kernel/head.S index 9ed55649ac8e..bc4f4bf05099 100644 --- a/arch/xtensa/kernel/head.S +++ b/arch/xtensa/kernel/head.S | |||
| @@ -128,7 +128,7 @@ ENTRY(_startup) | |||
| 128 | wsr a0, icountlevel | 128 | wsr a0, icountlevel |
| 129 | 129 | ||
| 130 | .set _index, 0 | 130 | .set _index, 0 |
| 131 | .rept XCHAL_NUM_DBREAK - 1 | 131 | .rept XCHAL_NUM_DBREAK |
| 132 | wsr a0, SREG_DBREAKC + _index | 132 | wsr a0, SREG_DBREAKC + _index |
| 133 | .set _index, _index + 1 | 133 | .set _index, _index + 1 |
| 134 | .endr | 134 | .endr |
| @@ -197,11 +197,6 @@ ENTRY(_startup) | |||
| 197 | wsr a2, ps # (enable reg-windows; progmode stack) | 197 | wsr a2, ps # (enable reg-windows; progmode stack) |
| 198 | rsync | 198 | rsync |
| 199 | 199 | ||
| 200 | /* Set up EXCSAVE[DEBUGLEVEL] to point to the Debug Exception Handler.*/ | ||
| 201 | |||
| 202 | movi a2, debug_exception | ||
| 203 | wsr a2, SREG_EXCSAVE + XCHAL_DEBUGLEVEL | ||
| 204 | |||
| 205 | #ifdef CONFIG_SMP | 200 | #ifdef CONFIG_SMP |
| 206 | /* | 201 | /* |
| 207 | * Notice that we assume with SMP that cores have PRID | 202 | * Notice that we assume with SMP that cores have PRID |
diff --git a/arch/xtensa/kernel/hw_breakpoint.c b/arch/xtensa/kernel/hw_breakpoint.c new file mode 100644 index 000000000000..b35656ab7dbd --- /dev/null +++ b/arch/xtensa/kernel/hw_breakpoint.c | |||
| @@ -0,0 +1,317 @@ | |||
| 1 | /* | ||
| 2 | * Xtensa hardware breakpoints/watchpoints handling functions | ||
| 3 | * | ||
| 4 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 5 | * License. See the file "COPYING" in the main directory of this archive | ||
| 6 | * for more details. | ||
| 7 | * | ||
| 8 | * Copyright (C) 2016 Cadence Design Systems Inc. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/hw_breakpoint.h> | ||
| 12 | #include <linux/log2.h> | ||
| 13 | #include <linux/percpu.h> | ||
| 14 | #include <linux/perf_event.h> | ||
| 15 | #include <variant/core.h> | ||
| 16 | |||
| 17 | /* Breakpoint currently in use for each IBREAKA. */ | ||
| 18 | static DEFINE_PER_CPU(struct perf_event *, bp_on_reg[XCHAL_NUM_IBREAK]); | ||
| 19 | |||
| 20 | /* Watchpoint currently in use for each DBREAKA. */ | ||
| 21 | static DEFINE_PER_CPU(struct perf_event *, wp_on_reg[XCHAL_NUM_DBREAK]); | ||
| 22 | |||
| 23 | int hw_breakpoint_slots(int type) | ||
| 24 | { | ||
| 25 | switch (type) { | ||
| 26 | case TYPE_INST: | ||
| 27 | return XCHAL_NUM_IBREAK; | ||
| 28 | case TYPE_DATA: | ||
| 29 | return XCHAL_NUM_DBREAK; | ||
| 30 | default: | ||
| 31 | pr_warn("unknown slot type: %d\n", type); | ||
| 32 | return 0; | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | int arch_check_bp_in_kernelspace(struct perf_event *bp) | ||
| 37 | { | ||
| 38 | unsigned int len; | ||
| 39 | unsigned long va; | ||
| 40 | struct arch_hw_breakpoint *info = counter_arch_bp(bp); | ||
| 41 | |||
| 42 | va = info->address; | ||
| 43 | len = bp->attr.bp_len; | ||
| 44 | |||
| 45 | return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE); | ||
| 46 | } | ||
| 47 | |||
| 48 | /* | ||
| 49 | * Construct an arch_hw_breakpoint from a perf_event. | ||
| 50 | */ | ||
| 51 | static int arch_build_bp_info(struct perf_event *bp) | ||
| 52 | { | ||
| 53 | struct arch_hw_breakpoint *info = counter_arch_bp(bp); | ||
| 54 | |||
| 55 | /* Type */ | ||
| 56 | switch (bp->attr.bp_type) { | ||
| 57 | case HW_BREAKPOINT_X: | ||
| 58 | info->type = XTENSA_BREAKPOINT_EXECUTE; | ||
| 59 | break; | ||
| 60 | case HW_BREAKPOINT_R: | ||
| 61 | info->type = XTENSA_BREAKPOINT_LOAD; | ||
| 62 | break; | ||
| 63 | case HW_BREAKPOINT_W: | ||
| 64 | info->type = XTENSA_BREAKPOINT_STORE; | ||
| 65 | break; | ||
| 66 | case HW_BREAKPOINT_RW: | ||
| 67 | info->type = XTENSA_BREAKPOINT_LOAD | XTENSA_BREAKPOINT_STORE; | ||
| 68 | break; | ||
| 69 | default: | ||
| 70 | return -EINVAL; | ||
| 71 | } | ||
| 72 | |||
| 73 | /* Len */ | ||
| 74 | info->len = bp->attr.bp_len; | ||
| 75 | if (info->len < 1 || info->len > 64 || !is_power_of_2(info->len)) | ||
| 76 | return -EINVAL; | ||
| 77 | |||
| 78 | /* Address */ | ||
| 79 | info->address = bp->attr.bp_addr; | ||
| 80 | if (info->address & (info->len - 1)) | ||
| 81 | return -EINVAL; | ||
| 82 | |||
| 83 | return 0; | ||
| 84 | } | ||
| 85 | |||
| 86 | int arch_validate_hwbkpt_settings(struct perf_event *bp) | ||
| 87 | { | ||
| 88 | int ret; | ||
| 89 | |||
| 90 | /* Build the arch_hw_breakpoint. */ | ||
| 91 | ret = arch_build_bp_info(bp); | ||
| 92 | return ret; | ||
| 93 | } | ||
| 94 | |||
| 95 | int hw_breakpoint_exceptions_notify(struct notifier_block *unused, | ||
| 96 | unsigned long val, void *data) | ||
| 97 | { | ||
| 98 | return NOTIFY_DONE; | ||
| 99 | } | ||
| 100 | |||
| 101 | static void xtensa_wsr(unsigned long v, u8 sr) | ||
| 102 | { | ||
| 103 | /* We don't have indexed wsr and creating instruction dynamically | ||
| 104 | * doesn't seem worth it given how small XCHAL_NUM_IBREAK and | ||
| 105 | * XCHAL_NUM_DBREAK are. Thus the switch. In case build breaks here | ||
| 106 | * the switch below needs to be extended. | ||
| 107 | */ | ||
| 108 | BUILD_BUG_ON(XCHAL_NUM_IBREAK > 2); | ||
| 109 | BUILD_BUG_ON(XCHAL_NUM_DBREAK > 2); | ||
| 110 | |||
| 111 | switch (sr) { | ||
| 112 | #if XCHAL_NUM_IBREAK > 0 | ||
| 113 | case SREG_IBREAKA + 0: | ||
| 114 | WSR(v, SREG_IBREAKA + 0); | ||
| 115 | break; | ||
| 116 | #endif | ||
| 117 | #if XCHAL_NUM_IBREAK > 1 | ||
| 118 | case SREG_IBREAKA + 1: | ||
| 119 | WSR(v, SREG_IBREAKA + 1); | ||
| 120 | break; | ||
| 121 | #endif | ||
| 122 | |||
| 123 | #if XCHAL_NUM_DBREAK > 0 | ||
| 124 | case SREG_DBREAKA + 0: | ||
| 125 | WSR(v, SREG_DBREAKA + 0); | ||
| 126 | break; | ||
| 127 | case SREG_DBREAKC + 0: | ||
| 128 | WSR(v, SREG_DBREAKC + 0); | ||
| 129 | break; | ||
| 130 | #endif | ||
| 131 | #if XCHAL_NUM_DBREAK > 1 | ||
| 132 | case SREG_DBREAKA + 1: | ||
| 133 | WSR(v, SREG_DBREAKA + 1); | ||
| 134 | break; | ||
| 135 | |||
| 136 | case SREG_DBREAKC + 1: | ||
| 137 | WSR(v, SREG_DBREAKC + 1); | ||
| 138 | break; | ||
| 139 | #endif | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | static int alloc_slot(struct perf_event **slot, size_t n, | ||
| 144 | struct perf_event *bp) | ||
| 145 | { | ||
| 146 | size_t i; | ||
| 147 | |||
| 148 | for (i = 0; i < n; ++i) { | ||
| 149 | if (!slot[i]) { | ||
| 150 | slot[i] = bp; | ||
| 151 | return i; | ||
| 152 | } | ||
| 153 | } | ||
| 154 | return -EBUSY; | ||
| 155 | } | ||
| 156 | |||
| 157 | static void set_ibreak_regs(int reg, struct perf_event *bp) | ||
| 158 | { | ||
| 159 | struct arch_hw_breakpoint *info = counter_arch_bp(bp); | ||
| 160 | unsigned long ibreakenable; | ||
| 161 | |||
| 162 | xtensa_wsr(info->address, SREG_IBREAKA + reg); | ||
| 163 | RSR(ibreakenable, SREG_IBREAKENABLE); | ||
| 164 | WSR(ibreakenable | (1 << reg), SREG_IBREAKENABLE); | ||
| 165 | } | ||
| 166 | |||
| 167 | static void set_dbreak_regs(int reg, struct perf_event *bp) | ||
| 168 | { | ||
| 169 | struct arch_hw_breakpoint *info = counter_arch_bp(bp); | ||
| 170 | unsigned long dbreakc = DBREAKC_MASK_MASK & -info->len; | ||
| 171 | |||
| 172 | if (info->type & XTENSA_BREAKPOINT_LOAD) | ||
| 173 | dbreakc |= DBREAKC_LOAD_MASK; | ||
| 174 | if (info->type & XTENSA_BREAKPOINT_STORE) | ||
| 175 | dbreakc |= DBREAKC_STOR_MASK; | ||
| 176 | |||
| 177 | xtensa_wsr(info->address, SREG_DBREAKA + reg); | ||
| 178 | xtensa_wsr(dbreakc, SREG_DBREAKC + reg); | ||
| 179 | } | ||
| 180 | |||
| 181 | int arch_install_hw_breakpoint(struct perf_event *bp) | ||
| 182 | { | ||
| 183 | int i; | ||
| 184 | |||
| 185 | if (counter_arch_bp(bp)->type == XTENSA_BREAKPOINT_EXECUTE) { | ||
| 186 | /* Breakpoint */ | ||
| 187 | i = alloc_slot(this_cpu_ptr(bp_on_reg), XCHAL_NUM_IBREAK, bp); | ||
| 188 | if (i < 0) | ||
| 189 | return i; | ||
| 190 | set_ibreak_regs(i, bp); | ||
| 191 | |||
| 192 | } else { | ||
| 193 | /* Watchpoint */ | ||
| 194 | i = alloc_slot(this_cpu_ptr(wp_on_reg), XCHAL_NUM_DBREAK, bp); | ||
| 195 | if (i < 0) | ||
| 196 | return i; | ||
| 197 | set_dbreak_regs(i, bp); | ||
| 198 | } | ||
| 199 | return 0; | ||
| 200 | } | ||
| 201 | |||
| 202 | static int free_slot(struct perf_event **slot, size_t n, | ||
| 203 | struct perf_event *bp) | ||
| 204 | { | ||
| 205 | size_t i; | ||
| 206 | |||
| 207 | for (i = 0; i < n; ++i) { | ||
| 208 | if (slot[i] == bp) { | ||
| 209 | slot[i] = NULL; | ||
| 210 | return i; | ||
| 211 | } | ||
| 212 | } | ||
| 213 | return -EBUSY; | ||
| 214 | } | ||
| 215 | |||
| 216 | void arch_uninstall_hw_breakpoint(struct perf_event *bp) | ||
| 217 | { | ||
| 218 | struct arch_hw_breakpoint *info = counter_arch_bp(bp); | ||
| 219 | int i; | ||
| 220 | |||
| 221 | if (info->type == XTENSA_BREAKPOINT_EXECUTE) { | ||
| 222 | unsigned long ibreakenable; | ||
| 223 | |||
| 224 | /* Breakpoint */ | ||
| 225 | i = free_slot(this_cpu_ptr(bp_on_reg), XCHAL_NUM_IBREAK, bp); | ||
| 226 | if (i >= 0) { | ||
| 227 | RSR(ibreakenable, SREG_IBREAKENABLE); | ||
| 228 | WSR(ibreakenable & ~(1 << i), SREG_IBREAKENABLE); | ||
| 229 | } | ||
| 230 | } else { | ||
| 231 | /* Watchpoint */ | ||
| 232 | i = free_slot(this_cpu_ptr(wp_on_reg), XCHAL_NUM_DBREAK, bp); | ||
| 233 | if (i >= 0) | ||
| 234 | xtensa_wsr(0, SREG_DBREAKC + i); | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 238 | void hw_breakpoint_pmu_read(struct perf_event *bp) | ||
| 239 | { | ||
| 240 | } | ||
| 241 | |||
| 242 | void flush_ptrace_hw_breakpoint(struct task_struct *tsk) | ||
| 243 | { | ||
| 244 | int i; | ||
| 245 | struct thread_struct *t = &tsk->thread; | ||
| 246 | |||
| 247 | for (i = 0; i < XCHAL_NUM_IBREAK; ++i) { | ||
| 248 | if (t->ptrace_bp[i]) { | ||
| 249 | unregister_hw_breakpoint(t->ptrace_bp[i]); | ||
| 250 | t->ptrace_bp[i] = NULL; | ||
| 251 | } | ||
| 252 | } | ||
| 253 | for (i = 0; i < XCHAL_NUM_DBREAK; ++i) { | ||
| 254 | if (t->ptrace_wp[i]) { | ||
| 255 | unregister_hw_breakpoint(t->ptrace_wp[i]); | ||
| 256 | t->ptrace_wp[i] = NULL; | ||
| 257 | } | ||
| 258 | } | ||
| 259 | } | ||
| 260 | |||
| 261 | /* | ||
| 262 | * Set ptrace breakpoint pointers to zero for this task. | ||
| 263 | * This is required in order to prevent child processes from unregistering | ||
| 264 | * breakpoints held by their parent. | ||
| 265 | */ | ||
| 266 | void clear_ptrace_hw_breakpoint(struct task_struct *tsk) | ||
| 267 | { | ||
| 268 | memset(tsk->thread.ptrace_bp, 0, sizeof(tsk->thread.ptrace_bp)); | ||
| 269 | memset(tsk->thread.ptrace_wp, 0, sizeof(tsk->thread.ptrace_wp)); | ||
| 270 | } | ||
| 271 | |||
| 272 | void restore_dbreak(void) | ||
| 273 | { | ||
| 274 | int i; | ||
| 275 | |||
| 276 | for (i = 0; i < XCHAL_NUM_DBREAK; ++i) { | ||
| 277 | struct perf_event *bp = this_cpu_ptr(wp_on_reg)[i]; | ||
| 278 | |||
| 279 | if (bp) | ||
| 280 | set_dbreak_regs(i, bp); | ||
| 281 | } | ||
| 282 | clear_thread_flag(TIF_DB_DISABLED); | ||
| 283 | } | ||
| 284 | |||
| 285 | int check_hw_breakpoint(struct pt_regs *regs) | ||
| 286 | { | ||
| 287 | if (regs->debugcause & BIT(DEBUGCAUSE_IBREAK_BIT)) { | ||
| 288 | int i; | ||
| 289 | struct perf_event **bp = this_cpu_ptr(bp_on_reg); | ||
| 290 | |||
| 291 | for (i = 0; i < XCHAL_NUM_IBREAK; ++i) { | ||
| 292 | if (bp[i] && !bp[i]->attr.disabled && | ||
| 293 | regs->pc == bp[i]->attr.bp_addr) | ||
| 294 | perf_bp_event(bp[i], regs); | ||
| 295 | } | ||
| 296 | return 0; | ||
| 297 | } else if (regs->debugcause & BIT(DEBUGCAUSE_DBREAK_BIT)) { | ||
| 298 | struct perf_event **bp = this_cpu_ptr(wp_on_reg); | ||
| 299 | int dbnum = (regs->debugcause & DEBUGCAUSE_DBNUM_MASK) >> | ||
| 300 | DEBUGCAUSE_DBNUM_SHIFT; | ||
| 301 | |||
| 302 | if (dbnum < XCHAL_NUM_DBREAK && bp[dbnum]) { | ||
| 303 | if (user_mode(regs)) { | ||
| 304 | perf_bp_event(bp[dbnum], regs); | ||
| 305 | } else { | ||
| 306 | set_thread_flag(TIF_DB_DISABLED); | ||
| 307 | xtensa_wsr(0, SREG_DBREAKC + dbnum); | ||
| 308 | } | ||
| 309 | } else { | ||
| 310 | WARN_ONCE(1, | ||
| 311 | "Wrong/unconfigured DBNUM reported in DEBUGCAUSE: %d\n", | ||
| 312 | dbnum); | ||
| 313 | } | ||
| 314 | return 0; | ||
| 315 | } | ||
| 316 | return -ENOENT; | ||
| 317 | } | ||
diff --git a/arch/xtensa/kernel/process.c b/arch/xtensa/kernel/process.c index 1c85323f01d7..5bbfed81c97b 100644 --- a/arch/xtensa/kernel/process.c +++ b/arch/xtensa/kernel/process.c | |||
| @@ -24,6 +24,7 @@ | |||
| 24 | #include <linux/unistd.h> | 24 | #include <linux/unistd.h> |
| 25 | #include <linux/ptrace.h> | 25 | #include <linux/ptrace.h> |
| 26 | #include <linux/elf.h> | 26 | #include <linux/elf.h> |
| 27 | #include <linux/hw_breakpoint.h> | ||
| 27 | #include <linux/init.h> | 28 | #include <linux/init.h> |
| 28 | #include <linux/prctl.h> | 29 | #include <linux/prctl.h> |
| 29 | #include <linux/init_task.h> | 30 | #include <linux/init_task.h> |
| @@ -43,6 +44,7 @@ | |||
| 43 | #include <linux/atomic.h> | 44 | #include <linux/atomic.h> |
| 44 | #include <asm/asm-offsets.h> | 45 | #include <asm/asm-offsets.h> |
| 45 | #include <asm/regs.h> | 46 | #include <asm/regs.h> |
| 47 | #include <asm/hw_breakpoint.h> | ||
| 46 | 48 | ||
| 47 | extern void ret_from_fork(void); | 49 | extern void ret_from_fork(void); |
| 48 | extern void ret_from_kernel_thread(void); | 50 | extern void ret_from_kernel_thread(void); |
| @@ -131,6 +133,7 @@ void flush_thread(void) | |||
| 131 | coprocessor_flush_all(ti); | 133 | coprocessor_flush_all(ti); |
| 132 | coprocessor_release_all(ti); | 134 | coprocessor_release_all(ti); |
| 133 | #endif | 135 | #endif |
| 136 | flush_ptrace_hw_breakpoint(current); | ||
| 134 | } | 137 | } |
| 135 | 138 | ||
| 136 | /* | 139 | /* |
| @@ -273,6 +276,8 @@ int copy_thread(unsigned long clone_flags, unsigned long usp_thread_fn, | |||
| 273 | ti->cpenable = 0; | 276 | ti->cpenable = 0; |
| 274 | #endif | 277 | #endif |
| 275 | 278 | ||
| 279 | clear_ptrace_hw_breakpoint(p); | ||
| 280 | |||
| 276 | return 0; | 281 | return 0; |
| 277 | } | 282 | } |
| 278 | 283 | ||
diff --git a/arch/xtensa/kernel/ptrace.c b/arch/xtensa/kernel/ptrace.c index 4d54b481123b..a651f3a628ee 100644 --- a/arch/xtensa/kernel/ptrace.c +++ b/arch/xtensa/kernel/ptrace.c | |||
| @@ -13,21 +13,23 @@ | |||
| 13 | * Marc Gauthier<marc@tensilica.com> <marc@alumni.uwaterloo.ca> | 13 | * Marc Gauthier<marc@tensilica.com> <marc@alumni.uwaterloo.ca> |
| 14 | */ | 14 | */ |
| 15 | 15 | ||
| 16 | #include <linux/errno.h> | ||
| 17 | #include <linux/hw_breakpoint.h> | ||
| 16 | #include <linux/kernel.h> | 18 | #include <linux/kernel.h> |
| 17 | #include <linux/sched.h> | ||
| 18 | #include <linux/mm.h> | 19 | #include <linux/mm.h> |
| 19 | #include <linux/errno.h> | 20 | #include <linux/perf_event.h> |
| 20 | #include <linux/ptrace.h> | 21 | #include <linux/ptrace.h> |
| 21 | #include <linux/smp.h> | 22 | #include <linux/sched.h> |
| 22 | #include <linux/security.h> | 23 | #include <linux/security.h> |
| 23 | #include <linux/signal.h> | 24 | #include <linux/signal.h> |
| 25 | #include <linux/smp.h> | ||
| 24 | 26 | ||
| 25 | #include <asm/pgtable.h> | 27 | #include <asm/coprocessor.h> |
| 28 | #include <asm/elf.h> | ||
| 26 | #include <asm/page.h> | 29 | #include <asm/page.h> |
| 27 | #include <asm/uaccess.h> | 30 | #include <asm/pgtable.h> |
| 28 | #include <asm/ptrace.h> | 31 | #include <asm/ptrace.h> |
| 29 | #include <asm/elf.h> | 32 | #include <asm/uaccess.h> |
| 30 | #include <asm/coprocessor.h> | ||
| 31 | 33 | ||
| 32 | 34 | ||
| 33 | void user_enable_single_step(struct task_struct *child) | 35 | void user_enable_single_step(struct task_struct *child) |
| @@ -267,6 +269,146 @@ int ptrace_pokeusr(struct task_struct *child, long regno, long val) | |||
| 267 | return 0; | 269 | return 0; |
| 268 | } | 270 | } |
| 269 | 271 | ||
| 272 | #ifdef CONFIG_HAVE_HW_BREAKPOINT | ||
| 273 | static void ptrace_hbptriggered(struct perf_event *bp, | ||
| 274 | struct perf_sample_data *data, | ||
| 275 | struct pt_regs *regs) | ||
| 276 | { | ||
| 277 | int i; | ||
| 278 | siginfo_t info; | ||
| 279 | struct arch_hw_breakpoint *bkpt = counter_arch_bp(bp); | ||
| 280 | |||
| 281 | if (bp->attr.bp_type & HW_BREAKPOINT_X) { | ||
| 282 | for (i = 0; i < XCHAL_NUM_IBREAK; ++i) | ||
| 283 | if (current->thread.ptrace_bp[i] == bp) | ||
| 284 | break; | ||
| 285 | i <<= 1; | ||
| 286 | } else { | ||
| 287 | for (i = 0; i < XCHAL_NUM_DBREAK; ++i) | ||
| 288 | if (current->thread.ptrace_wp[i] == bp) | ||
| 289 | break; | ||
| 290 | i = (i << 1) | 1; | ||
| 291 | } | ||
| 292 | |||
| 293 | info.si_signo = SIGTRAP; | ||
| 294 | info.si_errno = i; | ||
| 295 | info.si_code = TRAP_HWBKPT; | ||
| 296 | info.si_addr = (void __user *)bkpt->address; | ||
| 297 | |||
| 298 | force_sig_info(SIGTRAP, &info, current); | ||
| 299 | } | ||
| 300 | |||
| 301 | static struct perf_event *ptrace_hbp_create(struct task_struct *tsk, int type) | ||
| 302 | { | ||
| 303 | struct perf_event_attr attr; | ||
| 304 | |||
| 305 | ptrace_breakpoint_init(&attr); | ||
| 306 | |||
| 307 | /* Initialise fields to sane defaults. */ | ||
| 308 | attr.bp_addr = 0; | ||
| 309 | attr.bp_len = 1; | ||
| 310 | attr.bp_type = type; | ||
| 311 | attr.disabled = 1; | ||
| 312 | |||
| 313 | return register_user_hw_breakpoint(&attr, ptrace_hbptriggered, NULL, | ||
| 314 | tsk); | ||
| 315 | } | ||
| 316 | |||
| 317 | /* | ||
| 318 | * Address bit 0 choose instruction (0) or data (1) break register, bits | ||
| 319 | * 31..1 are the register number. | ||
| 320 | * Both PTRACE_GETHBPREGS and PTRACE_SETHBPREGS transfer two 32-bit words: | ||
| 321 | * address (0) and control (1). | ||
| 322 | * Instruction breakpoint contorl word is 0 to clear breakpoint, 1 to set. | ||
| 323 | * Data breakpoint control word bit 31 is 'trigger on store', bit 30 is | ||
| 324 | * 'trigger on load, bits 29..0 are length. Length 0 is used to clear a | ||
| 325 | * breakpoint. To set a breakpoint length must be a power of 2 in the range | ||
| 326 | * 1..64 and the address must be length-aligned. | ||
| 327 | */ | ||
| 328 | |||
| 329 | static long ptrace_gethbpregs(struct task_struct *child, long addr, | ||
| 330 | long __user *datap) | ||
| 331 | { | ||
| 332 | struct perf_event *bp; | ||
| 333 | u32 user_data[2] = {0}; | ||
| 334 | bool dbreak = addr & 1; | ||
| 335 | unsigned idx = addr >> 1; | ||
| 336 | |||
| 337 | if ((!dbreak && idx >= XCHAL_NUM_IBREAK) || | ||
| 338 | (dbreak && idx >= XCHAL_NUM_DBREAK)) | ||
| 339 | return -EINVAL; | ||
| 340 | |||
| 341 | if (dbreak) | ||
| 342 | bp = child->thread.ptrace_wp[idx]; | ||
| 343 | else | ||
| 344 | bp = child->thread.ptrace_bp[idx]; | ||
| 345 | |||
| 346 | if (bp) { | ||
| 347 | user_data[0] = bp->attr.bp_addr; | ||
| 348 | user_data[1] = bp->attr.disabled ? 0 : bp->attr.bp_len; | ||
| 349 | if (dbreak) { | ||
| 350 | if (bp->attr.bp_type & HW_BREAKPOINT_R) | ||
| 351 | user_data[1] |= DBREAKC_LOAD_MASK; | ||
| 352 | if (bp->attr.bp_type & HW_BREAKPOINT_W) | ||
| 353 | user_data[1] |= DBREAKC_STOR_MASK; | ||
| 354 | } | ||
| 355 | } | ||
| 356 | |||
| 357 | if (copy_to_user(datap, user_data, sizeof(user_data))) | ||
| 358 | return -EFAULT; | ||
| 359 | |||
| 360 | return 0; | ||
| 361 | } | ||
| 362 | |||
| 363 | static long ptrace_sethbpregs(struct task_struct *child, long addr, | ||
| 364 | long __user *datap) | ||
| 365 | { | ||
| 366 | struct perf_event *bp; | ||
| 367 | struct perf_event_attr attr; | ||
| 368 | u32 user_data[2]; | ||
| 369 | bool dbreak = addr & 1; | ||
| 370 | unsigned idx = addr >> 1; | ||
| 371 | int bp_type = 0; | ||
| 372 | |||
| 373 | if ((!dbreak && idx >= XCHAL_NUM_IBREAK) || | ||
| 374 | (dbreak && idx >= XCHAL_NUM_DBREAK)) | ||
| 375 | return -EINVAL; | ||
| 376 | |||
| 377 | if (copy_from_user(user_data, datap, sizeof(user_data))) | ||
| 378 | return -EFAULT; | ||
| 379 | |||
| 380 | if (dbreak) { | ||
| 381 | bp = child->thread.ptrace_wp[idx]; | ||
| 382 | if (user_data[1] & DBREAKC_LOAD_MASK) | ||
| 383 | bp_type |= HW_BREAKPOINT_R; | ||
| 384 | if (user_data[1] & DBREAKC_STOR_MASK) | ||
| 385 | bp_type |= HW_BREAKPOINT_W; | ||
| 386 | } else { | ||
| 387 | bp = child->thread.ptrace_bp[idx]; | ||
| 388 | bp_type = HW_BREAKPOINT_X; | ||
| 389 | } | ||
| 390 | |||
| 391 | if (!bp) { | ||
| 392 | bp = ptrace_hbp_create(child, | ||
| 393 | bp_type ? bp_type : HW_BREAKPOINT_RW); | ||
| 394 | if (IS_ERR(bp)) | ||
| 395 | return PTR_ERR(bp); | ||
| 396 | if (dbreak) | ||
| 397 | child->thread.ptrace_wp[idx] = bp; | ||
| 398 | else | ||
| 399 | child->thread.ptrace_bp[idx] = bp; | ||
| 400 | } | ||
| 401 | |||
| 402 | attr = bp->attr; | ||
| 403 | attr.bp_addr = user_data[0]; | ||
| 404 | attr.bp_len = user_data[1] & ~(DBREAKC_LOAD_MASK | DBREAKC_STOR_MASK); | ||
| 405 | attr.bp_type = bp_type; | ||
| 406 | attr.disabled = !attr.bp_len; | ||
| 407 | |||
| 408 | return modify_user_hw_breakpoint(bp, &attr); | ||
| 409 | } | ||
| 410 | #endif | ||
| 411 | |||
| 270 | long arch_ptrace(struct task_struct *child, long request, | 412 | long arch_ptrace(struct task_struct *child, long request, |
| 271 | unsigned long addr, unsigned long data) | 413 | unsigned long addr, unsigned long data) |
| 272 | { | 414 | { |
| @@ -307,7 +449,15 @@ long arch_ptrace(struct task_struct *child, long request, | |||
| 307 | case PTRACE_SETXTREGS: | 449 | case PTRACE_SETXTREGS: |
| 308 | ret = ptrace_setxregs(child, datap); | 450 | ret = ptrace_setxregs(child, datap); |
| 309 | break; | 451 | break; |
| 452 | #ifdef CONFIG_HAVE_HW_BREAKPOINT | ||
| 453 | case PTRACE_GETHBPREGS: | ||
| 454 | ret = ptrace_gethbpregs(child, addr, datap); | ||
| 455 | break; | ||
| 310 | 456 | ||
| 457 | case PTRACE_SETHBPREGS: | ||
| 458 | ret = ptrace_sethbpregs(child, addr, datap); | ||
| 459 | break; | ||
| 460 | #endif | ||
| 311 | default: | 461 | default: |
| 312 | ret = ptrace_request(child, request, addr, data); | 462 | ret = ptrace_request(child, request, addr, data); |
| 313 | break; | 463 | break; |
diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c index 42d441f7898b..d02fc304b31c 100644 --- a/arch/xtensa/kernel/traps.c +++ b/arch/xtensa/kernel/traps.c | |||
| @@ -39,11 +39,7 @@ | |||
| 39 | #include <asm/pgtable.h> | 39 | #include <asm/pgtable.h> |
| 40 | #include <asm/processor.h> | 40 | #include <asm/processor.h> |
| 41 | #include <asm/traps.h> | 41 | #include <asm/traps.h> |
| 42 | 42 | #include <asm/hw_breakpoint.h> | |
| 43 | #ifdef CONFIG_KGDB | ||
| 44 | extern int gdb_enter; | ||
| 45 | extern int return_from_debug_flag; | ||
| 46 | #endif | ||
| 47 | 43 | ||
| 48 | /* | 44 | /* |
| 49 | * Machine specific interrupt handlers | 45 | * Machine specific interrupt handlers |
| @@ -162,6 +158,8 @@ COPROCESSOR(7), | |||
| 162 | 158 | ||
| 163 | DEFINE_PER_CPU(unsigned long, exc_table[EXC_TABLE_SIZE/4]); | 159 | DEFINE_PER_CPU(unsigned long, exc_table[EXC_TABLE_SIZE/4]); |
| 164 | 160 | ||
| 161 | DEFINE_PER_CPU(struct debug_table, debug_table); | ||
| 162 | |||
| 165 | void die(const char*, struct pt_regs*, long); | 163 | void die(const char*, struct pt_regs*, long); |
| 166 | 164 | ||
| 167 | static inline void | 165 | static inline void |
| @@ -205,6 +203,32 @@ extern void do_IRQ(int, struct pt_regs *); | |||
| 205 | 203 | ||
| 206 | #if XTENSA_FAKE_NMI | 204 | #if XTENSA_FAKE_NMI |
| 207 | 205 | ||
| 206 | #define IS_POW2(v) (((v) & ((v) - 1)) == 0) | ||
| 207 | |||
| 208 | #if !(PROFILING_INTLEVEL == XCHAL_EXCM_LEVEL && \ | ||
| 209 | IS_POW2(XTENSA_INTLEVEL_MASK(PROFILING_INTLEVEL))) | ||
| 210 | #warning "Fake NMI is requested for PMM, but there are other IRQs at or above its level." | ||
| 211 | #warning "Fake NMI will be used, but there will be a bugcheck if one of those IRQs fire." | ||
| 212 | |||
| 213 | static inline void check_valid_nmi(void) | ||
| 214 | { | ||
| 215 | unsigned intread = get_sr(interrupt); | ||
| 216 | unsigned intenable = get_sr(intenable); | ||
| 217 | |||
| 218 | BUG_ON(intread & intenable & | ||
| 219 | ~(XTENSA_INTLEVEL_ANDBELOW_MASK(PROFILING_INTLEVEL) ^ | ||
| 220 | XTENSA_INTLEVEL_MASK(PROFILING_INTLEVEL) ^ | ||
| 221 | BIT(XCHAL_PROFILING_INTERRUPT))); | ||
| 222 | } | ||
| 223 | |||
| 224 | #else | ||
| 225 | |||
| 226 | static inline void check_valid_nmi(void) | ||
| 227 | { | ||
| 228 | } | ||
| 229 | |||
| 230 | #endif | ||
| 231 | |||
| 208 | irqreturn_t xtensa_pmu_irq_handler(int irq, void *dev_id); | 232 | irqreturn_t xtensa_pmu_irq_handler(int irq, void *dev_id); |
| 209 | 233 | ||
| 210 | DEFINE_PER_CPU(unsigned long, nmi_count); | 234 | DEFINE_PER_CPU(unsigned long, nmi_count); |
| @@ -219,6 +243,7 @@ void do_nmi(struct pt_regs *regs) | |||
| 219 | old_regs = set_irq_regs(regs); | 243 | old_regs = set_irq_regs(regs); |
| 220 | nmi_enter(); | 244 | nmi_enter(); |
| 221 | ++*this_cpu_ptr(&nmi_count); | 245 | ++*this_cpu_ptr(&nmi_count); |
| 246 | check_valid_nmi(); | ||
| 222 | xtensa_pmu_irq_handler(0, NULL); | 247 | xtensa_pmu_irq_handler(0, NULL); |
| 223 | nmi_exit(); | 248 | nmi_exit(); |
| 224 | set_irq_regs(old_regs); | 249 | set_irq_regs(old_regs); |
| @@ -314,23 +339,22 @@ do_unaligned_user (struct pt_regs *regs) | |||
| 314 | } | 339 | } |
| 315 | #endif | 340 | #endif |
| 316 | 341 | ||
| 342 | /* Handle debug events. | ||
| 343 | * When CONFIG_HAVE_HW_BREAKPOINT is on this handler is called with | ||
| 344 | * preemption disabled to avoid rescheduling and keep mapping of hardware | ||
| 345 | * breakpoint structures to debug registers intact, so that | ||
| 346 | * DEBUGCAUSE.DBNUM could be used in case of data breakpoint hit. | ||
| 347 | */ | ||
| 317 | void | 348 | void |
| 318 | do_debug(struct pt_regs *regs) | 349 | do_debug(struct pt_regs *regs) |
| 319 | { | 350 | { |
| 320 | #ifdef CONFIG_KGDB | 351 | #ifdef CONFIG_HAVE_HW_BREAKPOINT |
| 321 | /* If remote debugging is configured AND enabled, we give control to | 352 | int ret = check_hw_breakpoint(regs); |
| 322 | * kgdb. Otherwise, we fall through, perhaps giving control to the | 353 | |
| 323 | * native debugger. | 354 | preempt_enable(); |
| 324 | */ | 355 | if (ret == 0) |
| 325 | |||
| 326 | if (gdb_enter) { | ||
| 327 | extern void gdb_handle_exception(struct pt_regs *); | ||
| 328 | gdb_handle_exception(regs); | ||
| 329 | return_from_debug_flag = 1; | ||
| 330 | return; | 356 | return; |
| 331 | } | ||
| 332 | #endif | 357 | #endif |
| 333 | |||
| 334 | __die_if_kernel("Breakpoint in kernel", regs, SIGKILL); | 358 | __die_if_kernel("Breakpoint in kernel", regs, SIGKILL); |
| 335 | 359 | ||
| 336 | /* If in user mode, send SIGTRAP signal to current process */ | 360 | /* If in user mode, send SIGTRAP signal to current process */ |
| @@ -364,6 +388,15 @@ static void trap_init_excsave(void) | |||
| 364 | __asm__ __volatile__("wsr %0, excsave1\n" : : "a" (excsave1)); | 388 | __asm__ __volatile__("wsr %0, excsave1\n" : : "a" (excsave1)); |
| 365 | } | 389 | } |
| 366 | 390 | ||
| 391 | static void trap_init_debug(void) | ||
| 392 | { | ||
| 393 | unsigned long debugsave = (unsigned long)this_cpu_ptr(&debug_table); | ||
| 394 | |||
| 395 | this_cpu_ptr(&debug_table)->debug_exception = debug_exception; | ||
| 396 | __asm__ __volatile__("wsr %0, excsave" __stringify(XCHAL_DEBUGLEVEL) | ||
| 397 | :: "a"(debugsave)); | ||
| 398 | } | ||
| 399 | |||
| 367 | /* | 400 | /* |
| 368 | * Initialize dispatch tables. | 401 | * Initialize dispatch tables. |
| 369 | * | 402 | * |
| @@ -407,12 +440,14 @@ void __init trap_init(void) | |||
| 407 | 440 | ||
| 408 | /* Initialize EXCSAVE_1 to hold the address of the exception table. */ | 441 | /* Initialize EXCSAVE_1 to hold the address of the exception table. */ |
| 409 | trap_init_excsave(); | 442 | trap_init_excsave(); |
| 443 | trap_init_debug(); | ||
| 410 | } | 444 | } |
| 411 | 445 | ||
| 412 | #ifdef CONFIG_SMP | 446 | #ifdef CONFIG_SMP |
| 413 | void secondary_trap_init(void) | 447 | void secondary_trap_init(void) |
| 414 | { | 448 | { |
| 415 | trap_init_excsave(); | 449 | trap_init_excsave(); |
| 450 | trap_init_debug(); | ||
| 416 | } | 451 | } |
| 417 | #endif | 452 | #endif |
| 418 | 453 | ||
diff --git a/arch/xtensa/kernel/vectors.S b/arch/xtensa/kernel/vectors.S index fc25318e75ad..332e9d635fb6 100644 --- a/arch/xtensa/kernel/vectors.S +++ b/arch/xtensa/kernel/vectors.S | |||
| @@ -601,7 +601,9 @@ ENDPROC(window_overflow_restore_a0_fixup) | |||
| 601 | 601 | ||
| 602 | ENTRY(_DebugInterruptVector) | 602 | ENTRY(_DebugInterruptVector) |
| 603 | 603 | ||
| 604 | xsr a0, SREG_EXCSAVE + XCHAL_DEBUGLEVEL | 604 | xsr a3, SREG_EXCSAVE + XCHAL_DEBUGLEVEL |
| 605 | s32i a0, a3, DT_DEBUG_SAVE | ||
| 606 | l32i a0, a3, DT_DEBUG_EXCEPTION | ||
| 605 | jx a0 | 607 | jx a0 |
| 606 | 608 | ||
| 607 | ENDPROC(_DebugInterruptVector) | 609 | ENDPROC(_DebugInterruptVector) |
