diff options
Diffstat (limited to 'arch/mips/kernel/traps.c')
-rw-r--r-- | arch/mips/kernel/traps.c | 227 |
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 | ||
65 | void (*board_be_init)(void); | 66 | void (*board_be_init)(void); |
66 | int (*board_be_handler)(struct pt_regs *regs, int is_fixup); | 67 | int (*board_be_handler)(struct pt_regs *regs, int is_fixup); |
68 | void (*board_nmi_handler_setup)(void); | ||
69 | void (*board_ejtag_handler_setup)(void); | ||
70 | void (*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 | ||
820 | asmlinkage 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 | |||
936 | unsigned long ebase; | ||
924 | unsigned long exception_handlers[32]; | 937 | unsigned long exception_handlers[32]; |
938 | unsigned 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 */ | ||
966 | struct 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 | |||
972 | void 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 | |||
984 | int mips_srs_max(void) | ||
985 | { | ||
986 | return shadow_registers.sr_supported; | ||
987 | } | ||
988 | |||
989 | int 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 | |||
1010 | void 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 | |||
1020 | void *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 | ||
1096 | void *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 */ | ||
1204 | void __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 */ | ||
1211 | void __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 | |||
1038 | void __init trap_init(void) | 1223 | void __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 | } |