diff options
Diffstat (limited to 'arch/tile/kernel/traps.c')
-rw-r--r-- | arch/tile/kernel/traps.c | 130 |
1 files changed, 105 insertions, 25 deletions
diff --git a/arch/tile/kernel/traps.c b/arch/tile/kernel/traps.c index 12cb10f38527..3870abbeeaa2 100644 --- a/arch/tile/kernel/traps.c +++ b/arch/tile/kernel/traps.c | |||
@@ -20,6 +20,9 @@ | |||
20 | #include <linux/uaccess.h> | 20 | #include <linux/uaccess.h> |
21 | #include <linux/ptrace.h> | 21 | #include <linux/ptrace.h> |
22 | #include <asm/opcode-tile.h> | 22 | #include <asm/opcode-tile.h> |
23 | #include <asm/opcode_constants.h> | ||
24 | #include <asm/stack.h> | ||
25 | #include <asm/traps.h> | ||
23 | 26 | ||
24 | #include <arch/interrupts.h> | 27 | #include <arch/interrupts.h> |
25 | #include <arch/spr_def.h> | 28 | #include <arch/spr_def.h> |
@@ -42,7 +45,7 @@ static int __init setup_unaligned_fixup(char *str) | |||
42 | if (strict_strtol(str, 0, &val) != 0) | 45 | if (strict_strtol(str, 0, &val) != 0) |
43 | return 0; | 46 | return 0; |
44 | unaligned_fixup = val; | 47 | unaligned_fixup = val; |
45 | printk("Fixups for unaligned data accesses are %s\n", | 48 | pr_info("Fixups for unaligned data accesses are %s\n", |
46 | unaligned_fixup >= 0 ? | 49 | unaligned_fixup >= 0 ? |
47 | (unaligned_fixup ? "enabled" : "disabled") : | 50 | (unaligned_fixup ? "enabled" : "disabled") : |
48 | "completely disabled"); | 51 | "completely disabled"); |
@@ -56,7 +59,7 @@ static int dma_disabled; | |||
56 | 59 | ||
57 | static int __init nodma(char *str) | 60 | static int __init nodma(char *str) |
58 | { | 61 | { |
59 | printk("User-space DMA is disabled\n"); | 62 | pr_info("User-space DMA is disabled\n"); |
60 | dma_disabled = 1; | 63 | dma_disabled = 1; |
61 | return 1; | 64 | return 1; |
62 | } | 65 | } |
@@ -97,20 +100,106 @@ static int retry_gpv(unsigned int gpv_reason) | |||
97 | 100 | ||
98 | #endif /* CHIP_HAS_TILE_DMA() */ | 101 | #endif /* CHIP_HAS_TILE_DMA() */ |
99 | 102 | ||
100 | /* Defined inside do_trap(), below. */ | ||
101 | #ifdef __tilegx__ | 103 | #ifdef __tilegx__ |
102 | extern tilegx_bundle_bits bpt_code; | 104 | #define bundle_bits tilegx_bundle_bits |
103 | #else | 105 | #else |
104 | extern tile_bundle_bits bpt_code; | 106 | #define bundle_bits tile_bundle_bits |
105 | #endif | 107 | #endif |
106 | 108 | ||
109 | extern bundle_bits bpt_code; | ||
110 | |||
111 | asm(".pushsection .rodata.bpt_code,\"a\";" | ||
112 | ".align 8;" | ||
113 | "bpt_code: bpt;" | ||
114 | ".size bpt_code,.-bpt_code;" | ||
115 | ".popsection"); | ||
116 | |||
117 | static int special_ill(bundle_bits bundle, int *sigp, int *codep) | ||
118 | { | ||
119 | int sig, code, maxcode; | ||
120 | |||
121 | if (bundle == bpt_code) { | ||
122 | *sigp = SIGTRAP; | ||
123 | *codep = TRAP_BRKPT; | ||
124 | return 1; | ||
125 | } | ||
126 | |||
127 | /* If it's a "raise" bundle, then "ill" must be in pipe X1. */ | ||
128 | #ifdef __tilegx__ | ||
129 | if ((bundle & TILEGX_BUNDLE_MODE_MASK) != 0) | ||
130 | return 0; | ||
131 | if (get_Opcode_X1(bundle) != UNARY_OPCODE_X1) | ||
132 | return 0; | ||
133 | if (get_UnaryOpcodeExtension_X1(bundle) != ILL_UNARY_OPCODE_X1) | ||
134 | return 0; | ||
135 | #else | ||
136 | if (bundle & TILE_BUNDLE_Y_ENCODING_MASK) | ||
137 | return 0; | ||
138 | if (get_Opcode_X1(bundle) != SHUN_0_OPCODE_X1) | ||
139 | return 0; | ||
140 | if (get_UnShOpcodeExtension_X1(bundle) != UN_0_SHUN_0_OPCODE_X1) | ||
141 | return 0; | ||
142 | if (get_UnOpcodeExtension_X1(bundle) != ILL_UN_0_SHUN_0_OPCODE_X1) | ||
143 | return 0; | ||
144 | #endif | ||
145 | |||
146 | /* Check that the magic distinguishers are set to mean "raise". */ | ||
147 | if (get_Dest_X1(bundle) != 29 || get_SrcA_X1(bundle) != 37) | ||
148 | return 0; | ||
149 | |||
150 | /* There must be an "addli zero, zero, VAL" in X0. */ | ||
151 | if (get_Opcode_X0(bundle) != ADDLI_OPCODE_X0) | ||
152 | return 0; | ||
153 | if (get_Dest_X0(bundle) != TREG_ZERO) | ||
154 | return 0; | ||
155 | if (get_SrcA_X0(bundle) != TREG_ZERO) | ||
156 | return 0; | ||
157 | |||
158 | /* | ||
159 | * Validate the proposed signal number and si_code value. | ||
160 | * Note that we embed these in the static instruction itself | ||
161 | * so that we perturb the register state as little as possible | ||
162 | * at the time of the actual fault; it's unlikely you'd ever | ||
163 | * need to dynamically choose which kind of fault to raise | ||
164 | * from user space. | ||
165 | */ | ||
166 | sig = get_Imm16_X0(bundle) & 0x3f; | ||
167 | switch (sig) { | ||
168 | case SIGILL: | ||
169 | maxcode = NSIGILL; | ||
170 | break; | ||
171 | case SIGFPE: | ||
172 | maxcode = NSIGFPE; | ||
173 | break; | ||
174 | case SIGSEGV: | ||
175 | maxcode = NSIGSEGV; | ||
176 | break; | ||
177 | case SIGBUS: | ||
178 | maxcode = NSIGBUS; | ||
179 | break; | ||
180 | case SIGTRAP: | ||
181 | maxcode = NSIGTRAP; | ||
182 | break; | ||
183 | default: | ||
184 | return 0; | ||
185 | } | ||
186 | code = (get_Imm16_X0(bundle) >> 6) & 0xf; | ||
187 | if (code <= 0 || code > maxcode) | ||
188 | return 0; | ||
189 | |||
190 | /* Make it the requested signal. */ | ||
191 | *sigp = sig; | ||
192 | *codep = code | __SI_FAULT; | ||
193 | return 1; | ||
194 | } | ||
195 | |||
107 | void __kprobes do_trap(struct pt_regs *regs, int fault_num, | 196 | void __kprobes do_trap(struct pt_regs *regs, int fault_num, |
108 | unsigned long reason) | 197 | unsigned long reason) |
109 | { | 198 | { |
110 | siginfo_t info = { 0 }; | 199 | siginfo_t info = { 0 }; |
111 | int signo, code; | 200 | int signo, code; |
112 | unsigned long address; | 201 | unsigned long address; |
113 | __typeof__(bpt_code) instr; | 202 | bundle_bits instr; |
114 | 203 | ||
115 | /* Re-enable interrupts. */ | 204 | /* Re-enable interrupts. */ |
116 | local_irq_enable(); | 205 | local_irq_enable(); |
@@ -122,10 +211,10 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, | |||
122 | if (!user_mode(regs)) { | 211 | if (!user_mode(regs)) { |
123 | if (fixup_exception(regs)) /* only UNALIGN_DATA in practice */ | 212 | if (fixup_exception(regs)) /* only UNALIGN_DATA in practice */ |
124 | return; | 213 | return; |
125 | printk(KERN_ALERT "Kernel took bad trap %d at PC %#lx\n", | 214 | pr_alert("Kernel took bad trap %d at PC %#lx\n", |
126 | fault_num, regs->pc); | 215 | fault_num, regs->pc); |
127 | if (fault_num == INT_GPV) | 216 | if (fault_num == INT_GPV) |
128 | printk(KERN_ALERT "GPV_REASON is %#lx\n", reason); | 217 | pr_alert("GPV_REASON is %#lx\n", reason); |
129 | show_regs(regs); | 218 | show_regs(regs); |
130 | do_exit(SIGKILL); /* FIXME: implement i386 die() */ | 219 | do_exit(SIGKILL); /* FIXME: implement i386 die() */ |
131 | return; | 220 | return; |
@@ -133,22 +222,14 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, | |||
133 | 222 | ||
134 | switch (fault_num) { | 223 | switch (fault_num) { |
135 | case INT_ILL: | 224 | case INT_ILL: |
136 | asm(".pushsection .rodata.bpt_code,\"a\";" | 225 | if (copy_from_user(&instr, (void __user *)regs->pc, |
137 | ".align 8;" | 226 | sizeof(instr))) { |
138 | "bpt_code: bpt;" | 227 | pr_err("Unreadable instruction for INT_ILL:" |
139 | ".size bpt_code,.-bpt_code;" | ||
140 | ".popsection"); | ||
141 | |||
142 | if (copy_from_user(&instr, (void *)regs->pc, sizeof(instr))) { | ||
143 | printk(KERN_ERR "Unreadable instruction for INT_ILL:" | ||
144 | " %#lx\n", regs->pc); | 228 | " %#lx\n", regs->pc); |
145 | do_exit(SIGKILL); | 229 | do_exit(SIGKILL); |
146 | return; | 230 | return; |
147 | } | 231 | } |
148 | if (instr == bpt_code) { | 232 | if (!special_ill(instr, &signo, &code)) { |
149 | signo = SIGTRAP; | ||
150 | code = TRAP_BRKPT; | ||
151 | } else { | ||
152 | signo = SIGILL; | 233 | signo = SIGILL; |
153 | code = ILL_ILLOPC; | 234 | code = ILL_ILLOPC; |
154 | } | 235 | } |
@@ -181,7 +262,8 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, | |||
181 | if (unaligned_fixup >= 0) { | 262 | if (unaligned_fixup >= 0) { |
182 | struct single_step_state *state = | 263 | struct single_step_state *state = |
183 | current_thread_info()->step_state; | 264 | current_thread_info()->step_state; |
184 | if (!state || (void *)(regs->pc) != state->buffer) { | 265 | if (!state || |
266 | (void __user *)(regs->pc) != state->buffer) { | ||
185 | single_step_once(regs); | 267 | single_step_once(regs); |
186 | return; | 268 | return; |
187 | } | 269 | } |
@@ -221,17 +303,15 @@ void __kprobes do_trap(struct pt_regs *regs, int fault_num, | |||
221 | 303 | ||
222 | info.si_signo = signo; | 304 | info.si_signo = signo; |
223 | info.si_code = code; | 305 | info.si_code = code; |
224 | info.si_addr = (void *)address; | 306 | info.si_addr = (void __user *)address; |
225 | if (signo == SIGILL) | 307 | if (signo == SIGILL) |
226 | info.si_trapno = fault_num; | 308 | info.si_trapno = fault_num; |
227 | force_sig_info(signo, &info, current); | 309 | force_sig_info(signo, &info, current); |
228 | } | 310 | } |
229 | 311 | ||
230 | extern void _dump_stack(int dummy, ulong pc, ulong lr, ulong sp, ulong r52); | ||
231 | |||
232 | void kernel_double_fault(int dummy, ulong pc, ulong lr, ulong sp, ulong r52) | 312 | void kernel_double_fault(int dummy, ulong pc, ulong lr, ulong sp, ulong r52) |
233 | { | 313 | { |
234 | _dump_stack(dummy, pc, lr, sp, r52); | 314 | _dump_stack(dummy, pc, lr, sp, r52); |
235 | printk("Double fault: exiting\n"); | 315 | pr_emerg("Double fault: exiting\n"); |
236 | machine_halt(); | 316 | machine_halt(); |
237 | } | 317 | } |