Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/xtensa/kernel/traps.c
10817 views
1
/*
2
* arch/xtensa/kernel/traps.c
3
*
4
* Exception handling.
5
*
6
* Derived from code with the following copyrights:
7
* Copyright (C) 1994 - 1999 by Ralf Baechle
8
* Modified for R3000 by Paul M. Antoine, 1995, 1996
9
* Complete output from die() by Ulf Carlsson, 1998
10
* Copyright (C) 1999 Silicon Graphics, Inc.
11
*
12
* Essentially rewritten for the Xtensa architecture port.
13
*
14
* Copyright (C) 2001 - 2005 Tensilica Inc.
15
*
16
* Joe Taylor <[email protected], [email protected]>
17
* Chris Zankel <[email protected]>
18
* Marc Gauthier<[email protected], [email protected]>
19
* Kevin Chea
20
*
21
* This file is subject to the terms and conditions of the GNU General Public
22
* License. See the file "COPYING" in the main directory of this archive
23
* for more details.
24
*/
25
26
#include <linux/kernel.h>
27
#include <linux/sched.h>
28
#include <linux/init.h>
29
#include <linux/module.h>
30
#include <linux/stringify.h>
31
#include <linux/kallsyms.h>
32
#include <linux/delay.h>
33
#include <linux/hardirq.h>
34
35
#include <asm/ptrace.h>
36
#include <asm/timex.h>
37
#include <asm/uaccess.h>
38
#include <asm/pgtable.h>
39
#include <asm/processor.h>
40
41
#ifdef CONFIG_KGDB
42
extern int gdb_enter;
43
extern int return_from_debug_flag;
44
#endif
45
46
/*
47
* Machine specific interrupt handlers
48
*/
49
50
extern void kernel_exception(void);
51
extern void user_exception(void);
52
53
extern void fast_syscall_kernel(void);
54
extern void fast_syscall_user(void);
55
extern void fast_alloca(void);
56
extern void fast_unaligned(void);
57
extern void fast_second_level_miss(void);
58
extern void fast_store_prohibited(void);
59
extern void fast_coprocessor(void);
60
61
extern void do_illegal_instruction (struct pt_regs*);
62
extern void do_interrupt (struct pt_regs*);
63
extern void do_unaligned_user (struct pt_regs*);
64
extern void do_multihit (struct pt_regs*, unsigned long);
65
extern void do_page_fault (struct pt_regs*, unsigned long);
66
extern void do_debug (struct pt_regs*);
67
extern void system_call (struct pt_regs*);
68
69
/*
70
* The vector table must be preceded by a save area (which
71
* implies it must be in RAM, unless one places RAM immediately
72
* before a ROM and puts the vector at the start of the ROM (!))
73
*/
74
75
#define KRNL 0x01
76
#define USER 0x02
77
78
#define COPROCESSOR(x) \
79
{ EXCCAUSE_COPROCESSOR ## x ## _DISABLED, USER, fast_coprocessor }
80
81
typedef struct {
82
int cause;
83
int fast;
84
void* handler;
85
} dispatch_init_table_t;
86
87
static dispatch_init_table_t __initdata dispatch_init_table[] = {
88
89
{ EXCCAUSE_ILLEGAL_INSTRUCTION, 0, do_illegal_instruction},
90
{ EXCCAUSE_SYSTEM_CALL, KRNL, fast_syscall_kernel },
91
{ EXCCAUSE_SYSTEM_CALL, USER, fast_syscall_user },
92
{ EXCCAUSE_SYSTEM_CALL, 0, system_call },
93
/* EXCCAUSE_INSTRUCTION_FETCH unhandled */
94
/* EXCCAUSE_LOAD_STORE_ERROR unhandled*/
95
{ EXCCAUSE_LEVEL1_INTERRUPT, 0, do_interrupt },
96
{ EXCCAUSE_ALLOCA, USER|KRNL, fast_alloca },
97
/* EXCCAUSE_INTEGER_DIVIDE_BY_ZERO unhandled */
98
/* EXCCAUSE_PRIVILEGED unhandled */
99
#if XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION
100
#ifdef CONFIG_UNALIGNED_USER
101
{ EXCCAUSE_UNALIGNED, USER, fast_unaligned },
102
#else
103
{ EXCCAUSE_UNALIGNED, 0, do_unaligned_user },
104
#endif
105
{ EXCCAUSE_UNALIGNED, KRNL, fast_unaligned },
106
#endif
107
#ifdef CONFIG_MMU
108
{ EXCCAUSE_ITLB_MISS, 0, do_page_fault },
109
{ EXCCAUSE_ITLB_MISS, USER|KRNL, fast_second_level_miss},
110
{ EXCCAUSE_ITLB_MULTIHIT, 0, do_multihit },
111
{ EXCCAUSE_ITLB_PRIVILEGE, 0, do_page_fault },
112
/* EXCCAUSE_SIZE_RESTRICTION unhandled */
113
{ EXCCAUSE_FETCH_CACHE_ATTRIBUTE, 0, do_page_fault },
114
{ EXCCAUSE_DTLB_MISS, USER|KRNL, fast_second_level_miss},
115
{ EXCCAUSE_DTLB_MISS, 0, do_page_fault },
116
{ EXCCAUSE_DTLB_MULTIHIT, 0, do_multihit },
117
{ EXCCAUSE_DTLB_PRIVILEGE, 0, do_page_fault },
118
/* EXCCAUSE_DTLB_SIZE_RESTRICTION unhandled */
119
{ EXCCAUSE_STORE_CACHE_ATTRIBUTE, USER|KRNL, fast_store_prohibited },
120
{ EXCCAUSE_STORE_CACHE_ATTRIBUTE, 0, do_page_fault },
121
{ EXCCAUSE_LOAD_CACHE_ATTRIBUTE, 0, do_page_fault },
122
#endif /* CONFIG_MMU */
123
/* XCCHAL_EXCCAUSE_FLOATING_POINT unhandled */
124
#if XTENSA_HAVE_COPROCESSOR(0)
125
COPROCESSOR(0),
126
#endif
127
#if XTENSA_HAVE_COPROCESSOR(1)
128
COPROCESSOR(1),
129
#endif
130
#if XTENSA_HAVE_COPROCESSOR(2)
131
COPROCESSOR(2),
132
#endif
133
#if XTENSA_HAVE_COPROCESSOR(3)
134
COPROCESSOR(3),
135
#endif
136
#if XTENSA_HAVE_COPROCESSOR(4)
137
COPROCESSOR(4),
138
#endif
139
#if XTENSA_HAVE_COPROCESSOR(5)
140
COPROCESSOR(5),
141
#endif
142
#if XTENSA_HAVE_COPROCESSOR(6)
143
COPROCESSOR(6),
144
#endif
145
#if XTENSA_HAVE_COPROCESSOR(7)
146
COPROCESSOR(7),
147
#endif
148
{ EXCCAUSE_MAPPED_DEBUG, 0, do_debug },
149
{ -1, -1, 0 }
150
151
};
152
153
/* The exception table <exc_table> serves two functions:
154
* 1. it contains three dispatch tables (fast_user, fast_kernel, default-c)
155
* 2. it is a temporary memory buffer for the exception handlers.
156
*/
157
158
unsigned long exc_table[EXC_TABLE_SIZE/4];
159
160
void die(const char*, struct pt_regs*, long);
161
162
static inline void
163
__die_if_kernel(const char *str, struct pt_regs *regs, long err)
164
{
165
if (!user_mode(regs))
166
die(str, regs, err);
167
}
168
169
/*
170
* Unhandled Exceptions. Kill user task or panic if in kernel space.
171
*/
172
173
void do_unhandled(struct pt_regs *regs, unsigned long exccause)
174
{
175
__die_if_kernel("Caught unhandled exception - should not happen",
176
regs, SIGKILL);
177
178
/* If in user mode, send SIGILL signal to current process */
179
printk("Caught unhandled exception in '%s' "
180
"(pid = %d, pc = %#010lx) - should not happen\n"
181
"\tEXCCAUSE is %ld\n",
182
current->comm, task_pid_nr(current), regs->pc, exccause);
183
force_sig(SIGILL, current);
184
}
185
186
/*
187
* Multi-hit exception. This if fatal!
188
*/
189
190
void do_multihit(struct pt_regs *regs, unsigned long exccause)
191
{
192
die("Caught multihit exception", regs, SIGKILL);
193
}
194
195
/*
196
* Level-1 interrupt.
197
* We currently have no priority encoding.
198
*/
199
200
unsigned long ignored_level1_interrupts;
201
extern void do_IRQ(int, struct pt_regs *);
202
203
void do_interrupt (struct pt_regs *regs)
204
{
205
unsigned long intread = get_sr (INTREAD);
206
unsigned long intenable = get_sr (INTENABLE);
207
int i, mask;
208
209
/* Handle all interrupts (no priorities).
210
* (Clear the interrupt before processing, in case it's
211
* edge-triggered or software-generated)
212
*/
213
214
for (i=0, mask = 1; i < XCHAL_NUM_INTERRUPTS; i++, mask <<= 1) {
215
if (mask & (intread & intenable)) {
216
set_sr (mask, INTCLEAR);
217
do_IRQ (i,regs);
218
}
219
}
220
}
221
222
/*
223
* Illegal instruction. Fatal if in kernel space.
224
*/
225
226
void
227
do_illegal_instruction(struct pt_regs *regs)
228
{
229
__die_if_kernel("Illegal instruction in kernel", regs, SIGKILL);
230
231
/* If in user mode, send SIGILL signal to current process. */
232
233
printk("Illegal Instruction in '%s' (pid = %d, pc = %#010lx)\n",
234
current->comm, task_pid_nr(current), regs->pc);
235
force_sig(SIGILL, current);
236
}
237
238
239
/*
240
* Handle unaligned memory accesses from user space. Kill task.
241
*
242
* If CONFIG_UNALIGNED_USER is not set, we don't allow unaligned memory
243
* accesses causes from user space.
244
*/
245
246
#if XCHAL_UNALIGNED_LOAD_EXCEPTION || XCHAL_UNALIGNED_STORE_EXCEPTION
247
#ifndef CONFIG_UNALIGNED_USER
248
void
249
do_unaligned_user (struct pt_regs *regs)
250
{
251
siginfo_t info;
252
253
__die_if_kernel("Unhandled unaligned exception in kernel",
254
regs, SIGKILL);
255
256
current->thread.bad_vaddr = regs->excvaddr;
257
current->thread.error_code = -3;
258
printk("Unaligned memory access to %08lx in '%s' "
259
"(pid = %d, pc = %#010lx)\n",
260
regs->excvaddr, current->comm, task_pid_nr(current), regs->pc);
261
info.si_signo = SIGBUS;
262
info.si_errno = 0;
263
info.si_code = BUS_ADRALN;
264
info.si_addr = (void *) regs->excvaddr;
265
force_sig_info(SIGSEGV, &info, current);
266
267
}
268
#endif
269
#endif
270
271
void
272
do_debug(struct pt_regs *regs)
273
{
274
#ifdef CONFIG_KGDB
275
/* If remote debugging is configured AND enabled, we give control to
276
* kgdb. Otherwise, we fall through, perhaps giving control to the
277
* native debugger.
278
*/
279
280
if (gdb_enter) {
281
extern void gdb_handle_exception(struct pt_regs *);
282
gdb_handle_exception(regs);
283
return_from_debug_flag = 1;
284
return;
285
}
286
#endif
287
288
__die_if_kernel("Breakpoint in kernel", regs, SIGKILL);
289
290
/* If in user mode, send SIGTRAP signal to current process */
291
292
force_sig(SIGTRAP, current);
293
}
294
295
296
/*
297
* Initialize dispatch tables.
298
*
299
* The exception vectors are stored compressed the __init section in the
300
* dispatch_init_table. This function initializes the following three tables
301
* from that compressed table:
302
* - fast user first dispatch table for user exceptions
303
* - fast kernel first dispatch table for kernel exceptions
304
* - default C-handler C-handler called by the default fast handler.
305
*
306
* See vectors.S for more details.
307
*/
308
309
#define set_handler(idx,handler) (exc_table[idx] = (unsigned long) (handler))
310
311
void __init trap_init(void)
312
{
313
int i;
314
315
/* Setup default vectors. */
316
317
for(i = 0; i < 64; i++) {
318
set_handler(EXC_TABLE_FAST_USER/4 + i, user_exception);
319
set_handler(EXC_TABLE_FAST_KERNEL/4 + i, kernel_exception);
320
set_handler(EXC_TABLE_DEFAULT/4 + i, do_unhandled);
321
}
322
323
/* Setup specific handlers. */
324
325
for(i = 0; dispatch_init_table[i].cause >= 0; i++) {
326
327
int fast = dispatch_init_table[i].fast;
328
int cause = dispatch_init_table[i].cause;
329
void *handler = dispatch_init_table[i].handler;
330
331
if (fast == 0)
332
set_handler (EXC_TABLE_DEFAULT/4 + cause, handler);
333
if (fast && fast & USER)
334
set_handler (EXC_TABLE_FAST_USER/4 + cause, handler);
335
if (fast && fast & KRNL)
336
set_handler (EXC_TABLE_FAST_KERNEL/4 + cause, handler);
337
}
338
339
/* Initialize EXCSAVE_1 to hold the address of the exception table. */
340
341
i = (unsigned long)exc_table;
342
__asm__ __volatile__("wsr %0, "__stringify(EXCSAVE_1)"\n" : : "a" (i));
343
}
344
345
/*
346
* This function dumps the current valid window frame and other base registers.
347
*/
348
349
void show_regs(struct pt_regs * regs)
350
{
351
int i, wmask;
352
353
wmask = regs->wmask & ~1;
354
355
for (i = 0; i < 16; i++) {
356
if ((i % 8) == 0)
357
printk(KERN_INFO "a%02d:", i);
358
printk(KERN_CONT " %08lx", regs->areg[i]);
359
}
360
printk(KERN_CONT "\n");
361
362
printk("pc: %08lx, ps: %08lx, depc: %08lx, excvaddr: %08lx\n",
363
regs->pc, regs->ps, regs->depc, regs->excvaddr);
364
printk("lbeg: %08lx, lend: %08lx lcount: %08lx, sar: %08lx\n",
365
regs->lbeg, regs->lend, regs->lcount, regs->sar);
366
if (user_mode(regs))
367
printk("wb: %08lx, ws: %08lx, wmask: %08lx, syscall: %ld\n",
368
regs->windowbase, regs->windowstart, regs->wmask,
369
regs->syscall);
370
}
371
372
static __always_inline unsigned long *stack_pointer(struct task_struct *task)
373
{
374
unsigned long *sp;
375
376
if (!task || task == current)
377
__asm__ __volatile__ ("mov %0, a1\n" : "=a"(sp));
378
else
379
sp = (unsigned long *)task->thread.sp;
380
381
return sp;
382
}
383
384
void show_trace(struct task_struct *task, unsigned long *sp)
385
{
386
unsigned long a0, a1, pc;
387
unsigned long sp_start, sp_end;
388
389
if (sp)
390
a1 = (unsigned long)sp;
391
else
392
a1 = (unsigned long)stack_pointer(task);
393
394
sp_start = a1 & ~(THREAD_SIZE-1);
395
sp_end = sp_start + THREAD_SIZE;
396
397
printk("Call Trace:");
398
#ifdef CONFIG_KALLSYMS
399
printk("\n");
400
#endif
401
spill_registers();
402
403
while (a1 > sp_start && a1 < sp_end) {
404
sp = (unsigned long*)a1;
405
406
a0 = *(sp - 4);
407
a1 = *(sp - 3);
408
409
if (a1 <= (unsigned long) sp)
410
break;
411
412
pc = MAKE_PC_FROM_RA(a0, a1);
413
414
if (kernel_text_address(pc)) {
415
printk(" [<%08lx>] ", pc);
416
print_symbol("%s\n", pc);
417
}
418
}
419
printk("\n");
420
}
421
422
/*
423
* This routine abuses get_user()/put_user() to reference pointers
424
* with at least a bit of error checking ...
425
*/
426
427
static int kstack_depth_to_print = 24;
428
429
void show_stack(struct task_struct *task, unsigned long *sp)
430
{
431
int i = 0;
432
unsigned long *stack;
433
434
if (!sp)
435
sp = stack_pointer(task);
436
stack = sp;
437
438
printk("\nStack: ");
439
440
for (i = 0; i < kstack_depth_to_print; i++) {
441
if (kstack_end(sp))
442
break;
443
if (i && ((i % 8) == 0))
444
printk("\n ");
445
printk("%08lx ", *sp++);
446
}
447
printk("\n");
448
show_trace(task, stack);
449
}
450
451
void dump_stack(void)
452
{
453
show_stack(current, NULL);
454
}
455
456
EXPORT_SYMBOL(dump_stack);
457
458
459
void show_code(unsigned int *pc)
460
{
461
long i;
462
463
printk("\nCode:");
464
465
for(i = -3 ; i < 6 ; i++) {
466
unsigned long insn;
467
if (__get_user(insn, pc + i)) {
468
printk(" (Bad address in pc)\n");
469
break;
470
}
471
printk("%c%08lx%c",(i?' ':'<'),insn,(i?' ':'>'));
472
}
473
}
474
475
DEFINE_SPINLOCK(die_lock);
476
477
void die(const char * str, struct pt_regs * regs, long err)
478
{
479
static int die_counter;
480
int nl = 0;
481
482
console_verbose();
483
spin_lock_irq(&die_lock);
484
485
printk("%s: sig: %ld [#%d]\n", str, err, ++die_counter);
486
#ifdef CONFIG_PREEMPT
487
printk("PREEMPT ");
488
nl = 1;
489
#endif
490
if (nl)
491
printk("\n");
492
show_regs(regs);
493
if (!user_mode(regs))
494
show_stack(NULL, (unsigned long*)regs->areg[1]);
495
496
add_taint(TAINT_DIE);
497
spin_unlock_irq(&die_lock);
498
499
if (in_interrupt())
500
panic("Fatal exception in interrupt");
501
502
if (panic_on_oops)
503
panic("Fatal exception");
504
505
do_exit(err);
506
}
507
508
509
510