diff options
Diffstat (limited to 'arch/mips/kernel/traps.c')
-rw-r--r-- | arch/mips/kernel/traps.c | 184 |
1 files changed, 73 insertions, 111 deletions
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index 0a18b4c62afb..d612c6dcb746 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c | |||
@@ -25,10 +25,12 @@ | |||
25 | #include <linux/ptrace.h> | 25 | #include <linux/ptrace.h> |
26 | #include <linux/kgdb.h> | 26 | #include <linux/kgdb.h> |
27 | #include <linux/kdebug.h> | 27 | #include <linux/kdebug.h> |
28 | #include <linux/notifier.h> | ||
28 | 29 | ||
29 | #include <asm/bootinfo.h> | 30 | #include <asm/bootinfo.h> |
30 | #include <asm/branch.h> | 31 | #include <asm/branch.h> |
31 | #include <asm/break.h> | 32 | #include <asm/break.h> |
33 | #include <asm/cop2.h> | ||
32 | #include <asm/cpu.h> | 34 | #include <asm/cpu.h> |
33 | #include <asm/dsp.h> | 35 | #include <asm/dsp.h> |
34 | #include <asm/fpu.h> | 36 | #include <asm/fpu.h> |
@@ -48,6 +50,7 @@ | |||
48 | #include <asm/types.h> | 50 | #include <asm/types.h> |
49 | #include <asm/stacktrace.h> | 51 | #include <asm/stacktrace.h> |
50 | #include <asm/irq.h> | 52 | #include <asm/irq.h> |
53 | #include <asm/uasm.h> | ||
51 | 54 | ||
52 | extern void check_wait(void); | 55 | extern void check_wait(void); |
53 | extern asmlinkage void r4k_wait(void); | 56 | extern asmlinkage void r4k_wait(void); |
@@ -79,10 +82,6 @@ extern asmlinkage void handle_reserved(void); | |||
79 | extern int fpu_emulator_cop1Handler(struct pt_regs *xcp, | 82 | extern int fpu_emulator_cop1Handler(struct pt_regs *xcp, |
80 | struct mips_fpu_struct *ctx, int has_fpu); | 83 | struct mips_fpu_struct *ctx, int has_fpu); |
81 | 84 | ||
82 | #ifdef CONFIG_CPU_CAVIUM_OCTEON | ||
83 | extern asmlinkage void octeon_cop2_restore(struct octeon_cop2_state *task); | ||
84 | #endif | ||
85 | |||
86 | void (*board_be_init)(void); | 85 | void (*board_be_init)(void); |
87 | int (*board_be_handler)(struct pt_regs *regs, int is_fixup); | 86 | int (*board_be_handler)(struct pt_regs *regs, int is_fixup); |
88 | void (*board_nmi_handler_setup)(void); | 87 | void (*board_nmi_handler_setup)(void); |
@@ -353,9 +352,10 @@ void show_registers(const struct pt_regs *regs) | |||
353 | 352 | ||
354 | static DEFINE_SPINLOCK(die_lock); | 353 | static DEFINE_SPINLOCK(die_lock); |
355 | 354 | ||
356 | void __noreturn die(const char * str, const struct pt_regs * regs) | 355 | void __noreturn die(const char * str, struct pt_regs * regs) |
357 | { | 356 | { |
358 | static int die_counter; | 357 | static int die_counter; |
358 | int sig = SIGSEGV; | ||
359 | #ifdef CONFIG_MIPS_MT_SMTC | 359 | #ifdef CONFIG_MIPS_MT_SMTC |
360 | unsigned long dvpret = dvpe(); | 360 | unsigned long dvpret = dvpe(); |
361 | #endif /* CONFIG_MIPS_MT_SMTC */ | 361 | #endif /* CONFIG_MIPS_MT_SMTC */ |
@@ -366,6 +366,10 @@ void __noreturn die(const char * str, const struct pt_regs * regs) | |||
366 | #ifdef CONFIG_MIPS_MT_SMTC | 366 | #ifdef CONFIG_MIPS_MT_SMTC |
367 | mips_mt_regdump(dvpret); | 367 | mips_mt_regdump(dvpret); |
368 | #endif /* CONFIG_MIPS_MT_SMTC */ | 368 | #endif /* CONFIG_MIPS_MT_SMTC */ |
369 | |||
370 | if (notify_die(DIE_OOPS, str, regs, 0, current->thread.trap_no, SIGSEGV) == NOTIFY_STOP) | ||
371 | sig = 0; | ||
372 | |||
369 | printk("%s[#%d]:\n", str, ++die_counter); | 373 | printk("%s[#%d]:\n", str, ++die_counter); |
370 | show_registers(regs); | 374 | show_registers(regs); |
371 | add_taint(TAINT_DIE); | 375 | add_taint(TAINT_DIE); |
@@ -380,7 +384,7 @@ void __noreturn die(const char * str, const struct pt_regs * regs) | |||
380 | panic("Fatal exception"); | 384 | panic("Fatal exception"); |
381 | } | 385 | } |
382 | 386 | ||
383 | do_exit(SIGSEGV); | 387 | do_exit(sig); |
384 | } | 388 | } |
385 | 389 | ||
386 | extern struct exception_table_entry __start___dbe_table[]; | 390 | extern struct exception_table_entry __start___dbe_table[]; |
@@ -857,6 +861,44 @@ static void mt_ase_fp_affinity(void) | |||
857 | #endif /* CONFIG_MIPS_MT_FPAFF */ | 861 | #endif /* CONFIG_MIPS_MT_FPAFF */ |
858 | } | 862 | } |
859 | 863 | ||
864 | /* | ||
865 | * No lock; only written during early bootup by CPU 0. | ||
866 | */ | ||
867 | static RAW_NOTIFIER_HEAD(cu2_chain); | ||
868 | |||
869 | int __ref register_cu2_notifier(struct notifier_block *nb) | ||
870 | { | ||
871 | return raw_notifier_chain_register(&cu2_chain, nb); | ||
872 | } | ||
873 | |||
874 | int cu2_notifier_call_chain(unsigned long val, void *v) | ||
875 | { | ||
876 | return raw_notifier_call_chain(&cu2_chain, val, v); | ||
877 | } | ||
878 | |||
879 | static int default_cu2_call(struct notifier_block *nfb, unsigned long action, | ||
880 | void *data) | ||
881 | { | ||
882 | struct pt_regs *regs = data; | ||
883 | |||
884 | switch (action) { | ||
885 | default: | ||
886 | die_if_kernel("Unhandled kernel unaligned access or invalid " | ||
887 | "instruction", regs); | ||
888 | /* Fall through */ | ||
889 | |||
890 | case CU2_EXCEPTION: | ||
891 | force_sig(SIGILL, current); | ||
892 | } | ||
893 | |||
894 | return NOTIFY_OK; | ||
895 | } | ||
896 | |||
897 | static struct notifier_block default_cu2_notifier = { | ||
898 | .notifier_call = default_cu2_call, | ||
899 | .priority = 0x80000000, /* Run last */ | ||
900 | }; | ||
901 | |||
860 | asmlinkage void do_cpu(struct pt_regs *regs) | 902 | asmlinkage void do_cpu(struct pt_regs *regs) |
861 | { | 903 | { |
862 | unsigned int __user *epc; | 904 | unsigned int __user *epc; |
@@ -920,17 +962,9 @@ asmlinkage void do_cpu(struct pt_regs *regs) | |||
920 | return; | 962 | return; |
921 | 963 | ||
922 | case 2: | 964 | case 2: |
923 | #ifdef CONFIG_CPU_CAVIUM_OCTEON | 965 | raw_notifier_call_chain(&cu2_chain, CU2_EXCEPTION, regs); |
924 | prefetch(¤t->thread.cp2); | 966 | break; |
925 | local_irq_save(flags); | 967 | |
926 | KSTK_STATUS(current) |= ST0_CU2; | ||
927 | status = read_c0_status(); | ||
928 | write_c0_status(status | ST0_CU2); | ||
929 | octeon_cop2_restore(&(current->thread.cp2)); | ||
930 | write_c0_status(status & ~ST0_CU2); | ||
931 | local_irq_restore(flags); | ||
932 | return; | ||
933 | #endif | ||
934 | case 3: | 968 | case 3: |
935 | break; | 969 | break; |
936 | } | 970 | } |
@@ -1243,21 +1277,25 @@ unsigned long ebase; | |||
1243 | unsigned long exception_handlers[32]; | 1277 | unsigned long exception_handlers[32]; |
1244 | unsigned long vi_handlers[64]; | 1278 | unsigned long vi_handlers[64]; |
1245 | 1279 | ||
1246 | /* | 1280 | void __init *set_except_vector(int n, void *addr) |
1247 | * As a side effect of the way this is implemented we're limited | ||
1248 | * to interrupt handlers in the address range from | ||
1249 | * KSEG0 <= x < KSEG0 + 256mb on the Nevada. Oh well ... | ||
1250 | */ | ||
1251 | void *set_except_vector(int n, void *addr) | ||
1252 | { | 1281 | { |
1253 | unsigned long handler = (unsigned long) addr; | 1282 | unsigned long handler = (unsigned long) addr; |
1254 | unsigned long old_handler = exception_handlers[n]; | 1283 | unsigned long old_handler = exception_handlers[n]; |
1255 | 1284 | ||
1256 | exception_handlers[n] = handler; | 1285 | exception_handlers[n] = handler; |
1257 | if (n == 0 && cpu_has_divec) { | 1286 | if (n == 0 && cpu_has_divec) { |
1258 | *(u32 *)(ebase + 0x200) = 0x08000000 | | 1287 | unsigned long jump_mask = ~((1 << 28) - 1); |
1259 | (0x03ffffff & (handler >> 2)); | 1288 | u32 *buf = (u32 *)(ebase + 0x200); |
1260 | local_flush_icache_range(ebase + 0x200, ebase + 0x204); | 1289 | unsigned int k0 = 26; |
1290 | if ((handler & jump_mask) == ((ebase + 0x200) & jump_mask)) { | ||
1291 | uasm_i_j(&buf, handler & ~jump_mask); | ||
1292 | uasm_i_nop(&buf); | ||
1293 | } else { | ||
1294 | UASM_i_LA(&buf, k0, handler); | ||
1295 | uasm_i_jr(&buf, k0); | ||
1296 | uasm_i_nop(&buf); | ||
1297 | } | ||
1298 | local_flush_icache_range(ebase + 0x200, (unsigned long)buf); | ||
1261 | } | 1299 | } |
1262 | return (void *)old_handler; | 1300 | return (void *)old_handler; |
1263 | } | 1301 | } |
@@ -1367,77 +1405,6 @@ void *set_vi_handler(int n, vi_handler_t addr) | |||
1367 | return set_vi_srs_handler(n, addr, 0); | 1405 | return set_vi_srs_handler(n, addr, 0); |
1368 | } | 1406 | } |
1369 | 1407 | ||
1370 | /* | ||
1371 | * This is used by native signal handling | ||
1372 | */ | ||
1373 | asmlinkage int (*save_fp_context)(struct sigcontext __user *sc); | ||
1374 | asmlinkage int (*restore_fp_context)(struct sigcontext __user *sc); | ||
1375 | |||
1376 | extern asmlinkage int _save_fp_context(struct sigcontext __user *sc); | ||
1377 | extern asmlinkage int _restore_fp_context(struct sigcontext __user *sc); | ||
1378 | |||
1379 | extern asmlinkage int fpu_emulator_save_context(struct sigcontext __user *sc); | ||
1380 | extern asmlinkage int fpu_emulator_restore_context(struct sigcontext __user *sc); | ||
1381 | |||
1382 | #ifdef CONFIG_SMP | ||
1383 | static int smp_save_fp_context(struct sigcontext __user *sc) | ||
1384 | { | ||
1385 | return raw_cpu_has_fpu | ||
1386 | ? _save_fp_context(sc) | ||
1387 | : fpu_emulator_save_context(sc); | ||
1388 | } | ||
1389 | |||
1390 | static int smp_restore_fp_context(struct sigcontext __user *sc) | ||
1391 | { | ||
1392 | return raw_cpu_has_fpu | ||
1393 | ? _restore_fp_context(sc) | ||
1394 | : fpu_emulator_restore_context(sc); | ||
1395 | } | ||
1396 | #endif | ||
1397 | |||
1398 | static inline void signal_init(void) | ||
1399 | { | ||
1400 | #ifdef CONFIG_SMP | ||
1401 | /* For now just do the cpu_has_fpu check when the functions are invoked */ | ||
1402 | save_fp_context = smp_save_fp_context; | ||
1403 | restore_fp_context = smp_restore_fp_context; | ||
1404 | #else | ||
1405 | if (cpu_has_fpu) { | ||
1406 | save_fp_context = _save_fp_context; | ||
1407 | restore_fp_context = _restore_fp_context; | ||
1408 | } else { | ||
1409 | save_fp_context = fpu_emulator_save_context; | ||
1410 | restore_fp_context = fpu_emulator_restore_context; | ||
1411 | } | ||
1412 | #endif | ||
1413 | } | ||
1414 | |||
1415 | #ifdef CONFIG_MIPS32_COMPAT | ||
1416 | |||
1417 | /* | ||
1418 | * This is used by 32-bit signal stuff on the 64-bit kernel | ||
1419 | */ | ||
1420 | asmlinkage int (*save_fp_context32)(struct sigcontext32 __user *sc); | ||
1421 | asmlinkage int (*restore_fp_context32)(struct sigcontext32 __user *sc); | ||
1422 | |||
1423 | extern asmlinkage int _save_fp_context32(struct sigcontext32 __user *sc); | ||
1424 | extern asmlinkage int _restore_fp_context32(struct sigcontext32 __user *sc); | ||
1425 | |||
1426 | extern asmlinkage int fpu_emulator_save_context32(struct sigcontext32 __user *sc); | ||
1427 | extern asmlinkage int fpu_emulator_restore_context32(struct sigcontext32 __user *sc); | ||
1428 | |||
1429 | static inline void signal32_init(void) | ||
1430 | { | ||
1431 | if (cpu_has_fpu) { | ||
1432 | save_fp_context32 = _save_fp_context32; | ||
1433 | restore_fp_context32 = _restore_fp_context32; | ||
1434 | } else { | ||
1435 | save_fp_context32 = fpu_emulator_save_context32; | ||
1436 | restore_fp_context32 = fpu_emulator_restore_context32; | ||
1437 | } | ||
1438 | } | ||
1439 | #endif | ||
1440 | |||
1441 | extern void cpu_cache_init(void); | 1408 | extern void cpu_cache_init(void); |
1442 | extern void tlb_init(void); | 1409 | extern void tlb_init(void); |
1443 | extern void flush_tlb_handlers(void); | 1410 | extern void flush_tlb_handlers(void); |
@@ -1446,6 +1413,7 @@ extern void flush_tlb_handlers(void); | |||
1446 | * Timer interrupt | 1413 | * Timer interrupt |
1447 | */ | 1414 | */ |
1448 | int cp0_compare_irq; | 1415 | int cp0_compare_irq; |
1416 | int cp0_compare_irq_shift; | ||
1449 | 1417 | ||
1450 | /* | 1418 | /* |
1451 | * Performance counter IRQ or -1 if shared with timer | 1419 | * Performance counter IRQ or -1 if shared with timer |
@@ -1536,12 +1504,14 @@ void __cpuinit per_cpu_trap_init(void) | |||
1536 | * o read IntCtl.IPPCI to determine the performance counter interrupt | 1504 | * o read IntCtl.IPPCI to determine the performance counter interrupt |
1537 | */ | 1505 | */ |
1538 | if (cpu_has_mips_r2) { | 1506 | if (cpu_has_mips_r2) { |
1539 | cp0_compare_irq = (read_c0_intctl() >> 29) & 7; | 1507 | cp0_compare_irq_shift = CAUSEB_TI - CAUSEB_IP; |
1540 | cp0_perfcount_irq = (read_c0_intctl() >> 26) & 7; | 1508 | cp0_compare_irq = (read_c0_intctl() >> INTCTLB_IPTI) & 7; |
1509 | cp0_perfcount_irq = (read_c0_intctl() >> INTCTLB_IPPCI) & 7; | ||
1541 | if (cp0_perfcount_irq == cp0_compare_irq) | 1510 | if (cp0_perfcount_irq == cp0_compare_irq) |
1542 | cp0_perfcount_irq = -1; | 1511 | cp0_perfcount_irq = -1; |
1543 | } else { | 1512 | } else { |
1544 | cp0_compare_irq = CP0_LEGACY_COMPARE_IRQ; | 1513 | cp0_compare_irq = CP0_LEGACY_COMPARE_IRQ; |
1514 | cp0_compare_irq_shift = cp0_compare_irq; | ||
1545 | cp0_perfcount_irq = -1; | 1515 | cp0_perfcount_irq = -1; |
1546 | } | 1516 | } |
1547 | 1517 | ||
@@ -1592,12 +1562,7 @@ static char panic_null_cerr[] __cpuinitdata = | |||
1592 | void __cpuinit set_uncached_handler(unsigned long offset, void *addr, | 1562 | void __cpuinit set_uncached_handler(unsigned long offset, void *addr, |
1593 | unsigned long size) | 1563 | unsigned long size) |
1594 | { | 1564 | { |
1595 | #ifdef CONFIG_32BIT | 1565 | unsigned long uncached_ebase = CKSEG1ADDR(ebase); |
1596 | unsigned long uncached_ebase = KSEG1ADDR(ebase); | ||
1597 | #endif | ||
1598 | #ifdef CONFIG_64BIT | ||
1599 | unsigned long uncached_ebase = TO_UNCAC(ebase); | ||
1600 | #endif | ||
1601 | 1566 | ||
1602 | if (!addr) | 1567 | if (!addr) |
1603 | panic(panic_null_cerr); | 1568 | panic(panic_null_cerr); |
@@ -1634,7 +1599,7 @@ void __init trap_init(void) | |||
1634 | ebase = (unsigned long) | 1599 | ebase = (unsigned long) |
1635 | __alloc_bootmem(size, 1 << fls(size), 0); | 1600 | __alloc_bootmem(size, 1 << fls(size), 0); |
1636 | } else { | 1601 | } else { |
1637 | ebase = CAC_BASE; | 1602 | ebase = CKSEG0; |
1638 | if (cpu_has_mips_r2) | 1603 | if (cpu_has_mips_r2) |
1639 | ebase += (read_c0_ebase() & 0x3ffff000); | 1604 | ebase += (read_c0_ebase() & 0x3ffff000); |
1640 | } | 1605 | } |
@@ -1751,13 +1716,10 @@ void __init trap_init(void) | |||
1751 | else | 1716 | else |
1752 | memcpy((void *)(ebase + 0x080), &except_vec3_generic, 0x80); | 1717 | memcpy((void *)(ebase + 0x080), &except_vec3_generic, 0x80); |
1753 | 1718 | ||
1754 | signal_init(); | ||
1755 | #ifdef CONFIG_MIPS32_COMPAT | ||
1756 | signal32_init(); | ||
1757 | #endif | ||
1758 | |||
1759 | local_flush_icache_range(ebase, ebase + 0x400); | 1719 | local_flush_icache_range(ebase, ebase + 0x400); |
1760 | flush_tlb_handlers(); | 1720 | flush_tlb_handlers(); |
1761 | 1721 | ||
1762 | sort_extable(__start___dbe_table, __stop___dbe_table); | 1722 | sort_extable(__start___dbe_table, __stop___dbe_table); |
1723 | |||
1724 | register_cu2_notifier(&default_cu2_notifier); | ||
1763 | } | 1725 | } |