aboutsummaryrefslogtreecommitdiffstats
path: root/arch/tile/kernel/traps.c
diff options
context:
space:
mode:
authorChris Metcalf <cmetcalf@tilera.com>2010-05-28 23:09:12 -0400
committerChris Metcalf <cmetcalf@tilera.com>2010-06-04 17:11:18 -0400
commit867e359b97c970a60626d5d76bbe2a8fadbf38fb (patch)
treec5ccbb7f5172e8555977119608ecb1eee3cc37e3 /arch/tile/kernel/traps.c
parent5360bd776f73d0a7da571d72a09a03f237e99900 (diff)
arch/tile: core support for Tilera 32-bit chips.
This change is the core kernel support for TILEPro and TILE64 chips. No driver support (except the console driver) is included yet. This includes the relevant Linux headers in asm/; the low-level low-level "Tile architecture" headers in arch/, which are shared with the hypervisor, etc., and are build-system agnostic; and the relevant hypervisor headers in hv/. Signed-off-by: Chris Metcalf <cmetcalf@tilera.com> Acked-by: Arnd Bergmann <arnd@arndb.de> Acked-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> Reviewed-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/tile/kernel/traps.c')
-rw-r--r--arch/tile/kernel/traps.c237
1 files changed, 237 insertions, 0 deletions
diff --git a/arch/tile/kernel/traps.c b/arch/tile/kernel/traps.c
new file mode 100644
index 000000000000..12cb10f38527
--- /dev/null
+++ b/arch/tile/kernel/traps.c
@@ -0,0 +1,237 @@
1/*
2 * Copyright 2010 Tilera Corporation. All Rights Reserved.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation, version 2.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
11 * NON INFRINGEMENT. See the GNU General Public License for
12 * more details.
13 */
14
15#include <linux/sched.h>
16#include <linux/kernel.h>
17#include <linux/kprobes.h>
18#include <linux/module.h>
19#include <linux/reboot.h>
20#include <linux/uaccess.h>
21#include <linux/ptrace.h>
22#include <asm/opcode-tile.h>
23
24#include <arch/interrupts.h>
25#include <arch/spr_def.h>
26
27void __init trap_init(void)
28{
29 /* Nothing needed here since we link code at .intrpt1 */
30}
31
32int unaligned_fixup = 1;
33
34static int __init setup_unaligned_fixup(char *str)
35{
36 /*
37 * Say "=-1" to completely disable it. If you just do "=0", we
38 * will still parse the instruction, then fire a SIGBUS with
39 * the correct address from inside the single_step code.
40 */
41 long val;
42 if (strict_strtol(str, 0, &val) != 0)
43 return 0;
44 unaligned_fixup = val;
45 printk("Fixups for unaligned data accesses are %s\n",
46 unaligned_fixup >= 0 ?
47 (unaligned_fixup ? "enabled" : "disabled") :
48 "completely disabled");
49 return 1;
50}
51__setup("unaligned_fixup=", setup_unaligned_fixup);
52
53#if CHIP_HAS_TILE_DMA()
54
55static int dma_disabled;
56
57static int __init nodma(char *str)
58{
59 printk("User-space DMA is disabled\n");
60 dma_disabled = 1;
61 return 1;
62}
63__setup("nodma", nodma);
64
65/* How to decode SPR_GPV_REASON */
66#define IRET_ERROR (1U << 31)
67#define MT_ERROR (1U << 30)
68#define MF_ERROR (1U << 29)
69#define SPR_INDEX ((1U << 15) - 1)
70#define SPR_MPL_SHIFT 9 /* starting bit position for MPL encoded in SPR */
71
72/*
73 * See if this GPV is just to notify the kernel of SPR use and we can
74 * retry the user instruction after adjusting some MPLs suitably.
75 */
76static int retry_gpv(unsigned int gpv_reason)
77{
78 int mpl;
79
80 if (gpv_reason & IRET_ERROR)
81 return 0;
82
83 BUG_ON((gpv_reason & (MT_ERROR|MF_ERROR)) == 0);
84 mpl = (gpv_reason & SPR_INDEX) >> SPR_MPL_SHIFT;
85 if (mpl == INT_DMA_NOTIFY && !dma_disabled) {
86 /* User is turning on DMA. Allow it and retry. */
87 printk(KERN_DEBUG "Process %d/%s is now enabled for DMA\n",
88 current->pid, current->comm);
89 BUG_ON(current->thread.tile_dma_state.enabled);
90 current->thread.tile_dma_state.enabled = 1;
91 grant_dma_mpls();
92 return 1;
93 }
94
95 return 0;
96}
97
98#endif /* CHIP_HAS_TILE_DMA() */
99
100/* Defined inside do_trap(), below. */
101#ifdef __tilegx__
102extern tilegx_bundle_bits bpt_code;
103#else
104extern tile_bundle_bits bpt_code;
105#endif
106
107void __kprobes do_trap(struct pt_regs *regs, int fault_num,
108 unsigned long reason)
109{
110 siginfo_t info = { 0 };
111 int signo, code;
112 unsigned long address;
113 __typeof__(bpt_code) instr;
114
115 /* Re-enable interrupts. */
116 local_irq_enable();
117
118 /*
119 * If it hits in kernel mode and we can't fix it up, just exit the
120 * current process and hope for the best.
121 */
122 if (!user_mode(regs)) {
123 if (fixup_exception(regs)) /* only UNALIGN_DATA in practice */
124 return;
125 printk(KERN_ALERT "Kernel took bad trap %d at PC %#lx\n",
126 fault_num, regs->pc);
127 if (fault_num == INT_GPV)
128 printk(KERN_ALERT "GPV_REASON is %#lx\n", reason);
129 show_regs(regs);
130 do_exit(SIGKILL); /* FIXME: implement i386 die() */
131 return;
132 }
133
134 switch (fault_num) {
135 case INT_ILL:
136 asm(".pushsection .rodata.bpt_code,\"a\";"
137 ".align 8;"
138 "bpt_code: bpt;"
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);
145 do_exit(SIGKILL);
146 return;
147 }
148 if (instr == bpt_code) {
149 signo = SIGTRAP;
150 code = TRAP_BRKPT;
151 } else {
152 signo = SIGILL;
153 code = ILL_ILLOPC;
154 }
155 address = regs->pc;
156 break;
157 case INT_GPV:
158#if CHIP_HAS_TILE_DMA()
159 if (retry_gpv(reason))
160 return;
161#endif
162 /*FALLTHROUGH*/
163 case INT_UDN_ACCESS:
164 case INT_IDN_ACCESS:
165#if CHIP_HAS_SN()
166 case INT_SN_ACCESS:
167#endif
168 signo = SIGILL;
169 code = ILL_PRVREG;
170 address = regs->pc;
171 break;
172 case INT_SWINT_3:
173 case INT_SWINT_2:
174 case INT_SWINT_0:
175 signo = SIGILL;
176 code = ILL_ILLTRP;
177 address = regs->pc;
178 break;
179 case INT_UNALIGN_DATA:
180#ifndef __tilegx__ /* FIXME: GX: no single-step yet */
181 if (unaligned_fixup >= 0) {
182 struct single_step_state *state =
183 current_thread_info()->step_state;
184 if (!state || (void *)(regs->pc) != state->buffer) {
185 single_step_once(regs);
186 return;
187 }
188 }
189#endif
190 signo = SIGBUS;
191 code = BUS_ADRALN;
192 address = 0;
193 break;
194 case INT_DOUBLE_FAULT:
195 /*
196 * For double fault, "reason" is actually passed as
197 * SYSTEM_SAVE_1_2, the hypervisor's double-fault info, so
198 * we can provide the original fault number rather than
199 * the uninteresting "INT_DOUBLE_FAULT" so the user can
200 * learn what actually struck while PL0 ICS was set.
201 */
202 fault_num = reason;
203 signo = SIGILL;
204 code = ILL_DBLFLT;
205 address = regs->pc;
206 break;
207#ifdef __tilegx__
208 case INT_ILL_TRANS:
209 signo = SIGSEGV;
210 code = SEGV_MAPERR;
211 if (reason & SPR_ILL_TRANS_REASON__I_STREAM_VA_RMASK)
212 address = regs->pc;
213 else
214 address = 0; /* FIXME: GX: single-step for address */
215 break;
216#endif
217 default:
218 panic("Unexpected do_trap interrupt number %d", fault_num);
219 return;
220 }
221
222 info.si_signo = signo;
223 info.si_code = code;
224 info.si_addr = (void *)address;
225 if (signo == SIGILL)
226 info.si_trapno = fault_num;
227 force_sig_info(signo, &info, current);
228}
229
230extern void _dump_stack(int dummy, ulong pc, ulong lr, ulong sp, ulong r52);
231
232void kernel_double_fault(int dummy, ulong pc, ulong lr, ulong sp, ulong r52)
233{
234 _dump_stack(dummy, pc, lr, sp, r52);
235 printk("Double fault: exiting\n");
236 machine_halt();
237}