aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel/traps.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/kernel/traps.c')
-rw-r--r--arch/mips/kernel/traps.c227
1 files changed, 214 insertions, 13 deletions
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index b2fa607eeeb8..0a3969aa8dc6 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -20,6 +20,7 @@
20#include <linux/smp_lock.h> 20#include <linux/smp_lock.h>
21#include <linux/spinlock.h> 21#include <linux/spinlock.h>
22#include <linux/kallsyms.h> 22#include <linux/kallsyms.h>
23#include <linux/bootmem.h>
23 24
24#include <asm/bootinfo.h> 25#include <asm/bootinfo.h>
25#include <asm/branch.h> 26#include <asm/branch.h>
@@ -64,6 +65,9 @@ extern int fpu_emulator_cop1Handler(int xcptno, struct pt_regs *xcp,
64 65
65void (*board_be_init)(void); 66void (*board_be_init)(void);
66int (*board_be_handler)(struct pt_regs *regs, int is_fixup); 67int (*board_be_handler)(struct pt_regs *regs, int is_fixup);
68void (*board_nmi_handler_setup)(void);
69void (*board_ejtag_handler_setup)(void);
70void (*board_bind_eic_interrupt)(int irq, int regset);
67 71
68/* 72/*
69 * These constant is for searching for possible module text segments. 73 * These constant is for searching for possible module text segments.
@@ -813,6 +817,12 @@ asmlinkage void do_reserved(struct pt_regs *regs)
813 (regs->cp0_cause & 0x7f) >> 2); 817 (regs->cp0_cause & 0x7f) >> 2);
814} 818}
815 819
820asmlinkage void do_default_vi(struct pt_regs *regs)
821{
822 show_regs(regs);
823 panic("Caught unexpected vectored interrupt.");
824}
825
816/* 826/*
817 * Some MIPS CPUs can enable/disable for cache parity detection, but do 827 * Some MIPS CPUs can enable/disable for cache parity detection, but do
818 * it different ways. 828 * it different ways.
@@ -921,7 +931,11 @@ void nmi_exception_handler(struct pt_regs *regs)
921 while(1) ; 931 while(1) ;
922} 932}
923 933
934#define VECTORSPACING 0x100 /* for EI/VI mode */
935
936unsigned long ebase;
924unsigned long exception_handlers[32]; 937unsigned long exception_handlers[32];
938unsigned long vi_handlers[64];
925 939
926/* 940/*
927 * As a side effect of the way this is implemented we're limited 941 * As a side effect of the way this is implemented we're limited
@@ -935,13 +949,156 @@ void *set_except_vector(int n, void *addr)
935 949
936 exception_handlers[n] = handler; 950 exception_handlers[n] = handler;
937 if (n == 0 && cpu_has_divec) { 951 if (n == 0 && cpu_has_divec) {
938 *(volatile u32 *)(CAC_BASE + 0x200) = 0x08000000 | 952 *(volatile u32 *)(ebase + 0x200) = 0x08000000 |
939 (0x03ffffff & (handler >> 2)); 953 (0x03ffffff & (handler >> 2));
940 flush_icache_range(CAC_BASE + 0x200, CAC_BASE + 0x204); 954 flush_icache_range(ebase + 0x200, ebase + 0x204);
955 }
956 return (void *)old_handler;
957}
958
959#ifdef CONFIG_CPU_MIPSR2
960/*
961 * Shadow register allocation
962 * FIXME: SMP...
963 */
964
965/* MIPSR2 shadow register sets */
966struct shadow_registers {
967 spinlock_t sr_lock; /* */
968 int sr_supported; /* Number of shadow register sets supported */
969 int sr_allocated; /* Bitmap of allocated shadow registers */
970} shadow_registers;
971
972void mips_srs_init(void)
973{
974#ifdef CONFIG_CPU_MIPSR2_SRS
975 shadow_registers.sr_supported = ((read_c0_srsctl() >> 26) & 0x0f) + 1;
976 printk ("%d MIPSR2 register sets available\n", shadow_registers.sr_supported);
977#else
978 shadow_registers.sr_supported = 1;
979#endif
980 shadow_registers.sr_allocated = 1; /* Set 0 used by kernel */
981 spin_lock_init(&shadow_registers.sr_lock);
982}
983
984int mips_srs_max(void)
985{
986 return shadow_registers.sr_supported;
987}
988
989int mips_srs_alloc (void)
990{
991 struct shadow_registers *sr = &shadow_registers;
992 unsigned long flags;
993 int set;
994
995 spin_lock_irqsave(&sr->sr_lock, flags);
996
997 for (set = 0; set < sr->sr_supported; set++) {
998 if ((sr->sr_allocated & (1 << set)) == 0) {
999 sr->sr_allocated |= 1 << set;
1000 spin_unlock_irqrestore(&sr->sr_lock, flags);
1001 return set;
1002 }
1003 }
1004
1005 /* None available */
1006 spin_unlock_irqrestore(&sr->sr_lock, flags);
1007 return -1;
1008}
1009
1010void mips_srs_free (int set)
1011{
1012 struct shadow_registers *sr = &shadow_registers;
1013 unsigned long flags;
1014
1015 spin_lock_irqsave(&sr->sr_lock, flags);
1016 sr->sr_allocated &= ~(1 << set);
1017 spin_unlock_irqrestore(&sr->sr_lock, flags);
1018}
1019
1020void *set_vi_srs_handler (int n, void *addr, int srs)
1021{
1022 unsigned long handler;
1023 unsigned long old_handler = vi_handlers[n];
1024 u32 *w;
1025 unsigned char *b;
1026
1027 if (!cpu_has_veic && !cpu_has_vint)
1028 BUG();
1029
1030 if (addr == NULL) {
1031 handler = (unsigned long) do_default_vi;
1032 srs = 0;
1033 }
1034 else
1035 handler = (unsigned long) addr;
1036 vi_handlers[n] = (unsigned long) addr;
1037
1038 b = (unsigned char *)(ebase + 0x200 + n*VECTORSPACING);
1039
1040 if (srs >= mips_srs_max())
1041 panic("Shadow register set %d not supported", srs);
1042
1043 if (cpu_has_veic) {
1044 if (board_bind_eic_interrupt)
1045 board_bind_eic_interrupt (n, srs);
1046 }
1047 else if (cpu_has_vint) {
1048 /* SRSMap is only defined if shadow sets are implemented */
1049 if (mips_srs_max() > 1)
1050 change_c0_srsmap (0xf << n*4, srs << n*4);
1051 }
1052
1053 if (srs == 0) {
1054 /*
1055 * If no shadow set is selected then use the default handler
1056 * that does normal register saving and a standard interrupt exit
1057 */
1058
1059 extern char except_vec_vi, except_vec_vi_lui;
1060 extern char except_vec_vi_ori, except_vec_vi_end;
1061 const int handler_len = &except_vec_vi_end - &except_vec_vi;
1062 const int lui_offset = &except_vec_vi_lui - &except_vec_vi;
1063 const int ori_offset = &except_vec_vi_ori - &except_vec_vi;
1064
1065 if (handler_len > VECTORSPACING) {
1066 /*
1067 * Sigh... panicing won't help as the console
1068 * is probably not configured :(
1069 */
1070 panic ("VECTORSPACING too small");
1071 }
1072
1073 memcpy (b, &except_vec_vi, handler_len);
1074 w = (u32 *)(b + lui_offset);
1075 *w = (*w & 0xffff0000) | (((u32)handler >> 16) & 0xffff);
1076 w = (u32 *)(b + ori_offset);
1077 *w = (*w & 0xffff0000) | ((u32)handler & 0xffff);
1078 flush_icache_range((unsigned long)b, (unsigned long)(b+handler_len));
1079 }
1080 else {
1081 /*
1082 * In other cases jump directly to the interrupt handler
1083 *
1084 * It is the handlers responsibility to save registers if required
1085 * (eg hi/lo) and return from the exception using "eret"
1086 */
1087 w = (u32 *)b;
1088 *w++ = 0x08000000 | (((u32)handler >> 2) & 0x03fffff); /* j handler */
1089 *w = 0;
1090 flush_icache_range((unsigned long)b, (unsigned long)(b+8));
941 } 1091 }
1092
942 return (void *)old_handler; 1093 return (void *)old_handler;
943} 1094}
944 1095
1096void *set_vi_handler (int n, void *addr)
1097{
1098 return set_vi_srs_handler (n, addr, 0);
1099}
1100#endif
1101
945/* 1102/*
946 * This is used by native signal handling 1103 * This is used by native signal handling
947 */ 1104 */
@@ -1016,10 +1173,18 @@ void __init per_cpu_trap_init(void)
1016 if (cpu_has_dsp) 1173 if (cpu_has_dsp)
1017 set_c0_status(ST0_MX); 1174 set_c0_status(ST0_MX);
1018 1175
1176#ifdef CONFIG_CPU_MIPSR2
1177 write_c0_hwrena (0x0000000f); /* Allow rdhwr to all registers */
1178#endif
1179
1019 /* 1180 /*
1020 * Some MIPS CPUs have a dedicated interrupt vector which reduces the 1181 * Interrupt handling.
1021 * interrupt processing overhead. Use it where available.
1022 */ 1182 */
1183 if (cpu_has_veic || cpu_has_vint) {
1184 write_c0_ebase (ebase);
1185 /* Setting vector spacing enables EI/VI mode */
1186 change_c0_intctl (0x3e0, VECTORSPACING);
1187 }
1023 if (cpu_has_divec) 1188 if (cpu_has_divec)
1024 set_c0_cause(CAUSEF_IV); 1189 set_c0_cause(CAUSEF_IV);
1025 1190
@@ -1035,13 +1200,41 @@ void __init per_cpu_trap_init(void)
1035 tlb_init(); 1200 tlb_init();
1036} 1201}
1037 1202
1203/* Install CPU exception handler */
1204void __init set_handler (unsigned long offset, void *addr, unsigned long size)
1205{
1206 memcpy((void *)(ebase + offset), addr, size);
1207 flush_icache_range(ebase + offset, ebase + offset + size);
1208}
1209
1210/* Install uncached CPU exception handler */
1211void __init set_uncached_handler (unsigned long offset, void *addr, unsigned long size)
1212{
1213#ifdef CONFIG_32BIT
1214 unsigned long uncached_ebase = KSEG1ADDR(ebase);
1215#endif
1216#ifdef CONFIG_64BIT
1217 unsigned long uncached_ebase = TO_UNCAC(ebase);
1218#endif
1219
1220 memcpy((void *)(uncached_ebase + offset), addr, size);
1221}
1222
1038void __init trap_init(void) 1223void __init trap_init(void)
1039{ 1224{
1040 extern char except_vec3_generic, except_vec3_r4000; 1225 extern char except_vec3_generic, except_vec3_r4000;
1041 extern char except_vec_ejtag_debug;
1042 extern char except_vec4; 1226 extern char except_vec4;
1043 unsigned long i; 1227 unsigned long i;
1044 1228
1229 if (cpu_has_veic || cpu_has_vint)
1230 ebase = (unsigned long) alloc_bootmem_low_pages (0x200 + VECTORSPACING*64);
1231 else
1232 ebase = CAC_BASE;
1233
1234#ifdef CONFIG_CPU_MIPSR2
1235 mips_srs_init();
1236#endif
1237
1045 per_cpu_trap_init(); 1238 per_cpu_trap_init();
1046 1239
1047 /* 1240 /*
@@ -1049,7 +1242,7 @@ void __init trap_init(void)
1049 * This will be overriden later as suitable for a particular 1242 * This will be overriden later as suitable for a particular
1050 * configuration. 1243 * configuration.
1051 */ 1244 */
1052 memcpy((void *)(CAC_BASE + 0x180), &except_vec3_generic, 0x80); 1245 set_handler(0x180, &except_vec3_generic, 0x80);
1053 1246
1054 /* 1247 /*
1055 * Setup default vectors 1248 * Setup default vectors
@@ -1061,8 +1254,8 @@ void __init trap_init(void)
1061 * Copy the EJTAG debug exception vector handler code to it's final 1254 * Copy the EJTAG debug exception vector handler code to it's final
1062 * destination. 1255 * destination.
1063 */ 1256 */
1064 if (cpu_has_ejtag) 1257 if (cpu_has_ejtag && board_ejtag_handler_setup)
1065 memcpy((void *)(CAC_BASE + 0x300), &except_vec_ejtag_debug, 0x80); 1258 board_ejtag_handler_setup ();
1066 1259
1067 /* 1260 /*
1068 * Only some CPUs have the watch exceptions. 1261 * Only some CPUs have the watch exceptions.
@@ -1071,11 +1264,15 @@ void __init trap_init(void)
1071 set_except_vector(23, handle_watch); 1264 set_except_vector(23, handle_watch);
1072 1265
1073 /* 1266 /*
1074 * Some MIPS CPUs have a dedicated interrupt vector which reduces the 1267 * Initialise interrupt handlers
1075 * interrupt processing overhead. Use it where available.
1076 */ 1268 */
1077 if (cpu_has_divec) 1269 if (cpu_has_veic || cpu_has_vint) {
1078 memcpy((void *)(CAC_BASE + 0x200), &except_vec4, 0x8); 1270 int nvec = cpu_has_veic ? 64 : 8;
1271 for (i = 0; i < nvec; i++)
1272 set_vi_handler (i, NULL);
1273 }
1274 else if (cpu_has_divec)
1275 set_handler(0x200, &except_vec4, 0x8);
1079 1276
1080 /* 1277 /*
1081 * Some CPUs can enable/disable for cache parity detection, but does 1278 * Some CPUs can enable/disable for cache parity detection, but does
@@ -1122,6 +1319,10 @@ void __init trap_init(void)
1122 //set_except_vector(15, handle_ndc); 1319 //set_except_vector(15, handle_ndc);
1123 } 1320 }
1124 1321
1322
1323 if (board_nmi_handler_setup)
1324 board_nmi_handler_setup();
1325
1125 if (cpu_has_fpu && !cpu_has_nofpuex) 1326 if (cpu_has_fpu && !cpu_has_nofpuex)
1126 set_except_vector(15, handle_fpe); 1327 set_except_vector(15, handle_fpe);
1127 1328
@@ -1146,5 +1347,5 @@ void __init trap_init(void)
1146 signal32_init(); 1347 signal32_init();
1147#endif 1348#endif
1148 1349
1149 flush_icache_range(CAC_BASE, CAC_BASE + 0x400); 1350 flush_icache_range(ebase, ebase + 0x400);
1150} 1351}