1/* 2 * Exception handling for Microblaze 3 * 4 * Rewriten interrupt handling 5 * 6 * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu> 7 * Copyright (C) 2008-2009 PetaLogix 8 * 9 * uClinux customisation (C) 2005 John Williams 10 * 11 * MMU code derived from arch/ppc/kernel/head_4xx.S: 12 * Copyright (C) 1995-1996 Gary Thomas <gdt@linuxppc.org> 13 * Initial PowerPC version. 14 * Copyright (C) 1996 Cort Dougan <cort@cs.nmt.edu> 15 * Rewritten for PReP 16 * Copyright (C) 1996 Paul Mackerras <paulus@cs.anu.edu.au> 17 * Low-level exception handers, MMU support, and rewrite. 18 * Copyright (C) 1997 Dan Malek <dmalek@jlc.net> 19 * PowerPC 8xx modifications. 20 * Copyright (C) 1998-1999 TiVo, Inc. 21 * PowerPC 403GCX modifications. 22 * Copyright (C) 1999 Grant Erickson <grant@lcse.umn.edu> 23 * PowerPC 403GCX/405GP modifications. 24 * Copyright 2000 MontaVista Software Inc. 25 * PPC405 modifications 26 * PowerPC 403GCX/405GP modifications. 27 * Author: MontaVista Software, Inc. 28 * frank_rowand@mvista.com or source@mvista.com 29 * debbie_chu@mvista.com 30 * 31 * Original code 32 * Copyright (C) 2004 Xilinx, Inc. 33 * 34 * This program is free software; you can redistribute it and/or modify it 35 * under the terms of the GNU General Public License version 2 as published 36 * by the Free Software Foundation. 37 */ 38 39/* 40 * Here are the handlers which don't require enabling translation 41 * and calling other kernel code thus we can keep their design very simple 42 * and do all processing in real mode. All what they need is a valid current 43 * (that is an issue for the CONFIG_REGISTER_TASK_PTR case) 44 * This handlers use r3,r4,r5,r6 and optionally r[current] to work therefore 45 * these registers are saved/restored 46 * The handlers which require translation are in entry.S --KAA 47 * 48 * Microblaze HW Exception Handler 49 * - Non self-modifying exception handler for the following exception conditions 50 * - Unalignment 51 * - Instruction bus error 52 * - Data bus error 53 * - Illegal instruction opcode 54 * - Divide-by-zero 55 * 56 * Note we disable interrupts during exception handling, otherwise we will 57 * possibly get multiple re-entrancy if interrupt handles themselves cause 58 * exceptions. JW 59 */ 60 61#include <asm/exceptions.h> 62#include <asm/unistd.h> 63#include <asm/page.h> 64 65#include <asm/entry.h> 66#include <asm/current.h> 67#include <linux/linkage.h> 68 69#include <asm/mmu.h> 70#include <asm/pgtable.h> 71#include <asm/asm-offsets.h> 72 73/* Helpful Macros */ 74#define EX_HANDLER_STACK_SIZ (4*19) 75#define NUM_TO_REG(num) r ## num 76 77#define LWREG_NOP \ 78 bri ex_handler_unhandled; \ 79 nop; 80 81#define SWREG_NOP \ 82 bri ex_handler_unhandled; \ 83 nop; 84 85/* FIXME this is weird - for noMMU kernel is not possible to use brid 86 * instruction which can shorten executed time 87 */ 88 89/* r3 is the source */ 90#define R3_TO_LWREG_V(regnum) \ 91 swi r3, r1, 4 * regnum; \ 92 bri ex_handler_done; 93 94/* r3 is the source */ 95#define R3_TO_LWREG(regnum) \ 96 or NUM_TO_REG (regnum), r0, r3; \ 97 bri ex_handler_done; 98 99/* r3 is the target */ 100#define SWREG_TO_R3_V(regnum) \ 101 lwi r3, r1, 4 * regnum; \ 102 bri ex_sw_tail; 103 104/* r3 is the target */ 105#define SWREG_TO_R3(regnum) \ 106 or r3, r0, NUM_TO_REG (regnum); \ 107 bri ex_sw_tail; 108 109.extern other_exception_handler /* Defined in exception.c */ 110 111/* 112 * hw_exception_handler - Handler for exceptions 113 * 114 * Exception handler notes: 115 * - Handles all exceptions 116 * - Does not handle unaligned exceptions during load into r17, r1, r0. 117 * - Does not handle unaligned exceptions during store from r17 (cannot be 118 * done) and r1 (slows down common case) 119 * 120 * Relevant register structures 121 * 122 * EAR - |----|----|----|----|----|----|----|----| 123 * - < ## 32 bit faulting address ## > 124 * 125 * ESR - |----|----|----|----|----| - | - |-----|-----| 126 * - W S REG EXC 127 * 128 * 129 * STACK FRAME STRUCTURE (for NO_MMU) 130 * --------------------------------- 131 * 132 * +-------------+ + 0 133 * | MSR | 134 * +-------------+ + 4 135 * | r1 | 136 * | . | 137 * | . | 138 * | . | 139 * | . | 140 * | r18 | 141 * +-------------+ + 76 142 * | . | 143 * | . | 144 * 145 * NO_MMU kernel use the same r0_ram pointed space - look to vmlinux.lds.S 146 * which is used for storing register values - old style was, that value were 147 * stored in stack but in case of failure you lost information about register. 148 * Currently you can see register value in memory in specific place. 149 * In compare to with previous solution the speed should be the same. 150 * 151 * MMU exception handler has different handling compare to no MMU kernel. 152 * Exception handler use jump table for directing of what happen. For MMU kernel 153 * is this approach better because MMU relate exception are handled by asm code 154 * in this file. In compare to with MMU expect of unaligned exception 155 * is everything handled by C code. 156 */ 157 158/* 159 * every of these handlers is entered having R3/4/5/6/11/current saved on stack 160 * and clobbered so care should be taken to restore them if someone is going to 161 * return from exception 162 */ 163 164/* wrappers to restore state before coming to entry.S */ 165 166.global _hw_exception_handler 167.section .text 168.align 4 169.ent _hw_exception_handler 170_hw_exception_handler: 171 addik r1, r1, -(EX_HANDLER_STACK_SIZ); /* Create stack frame */ 172 swi r3, r1, PT_R3 173 swi r4, r1, PT_R4 174 swi r5, r1, PT_R5 175 swi r6, r1, PT_R6 176 177 mfs r5, rmsr; 178 nop 179 swi r5, r1, 0; 180 mfs r4, rbtr /* Save BTR before jumping to handler */ 181 nop 182 mfs r3, resr 183 nop 184 185 andi r5, r3, 0x1000; /* Check ESR[DS] */ 186 beqi r5, not_in_delay_slot; /* Branch if ESR[DS] not set */ 187 mfs r17, rbtr; /* ESR[DS] set - return address in BTR */ 188 nop 189not_in_delay_slot: 190 swi r17, r1, PT_R17 191 192 andi r5, r3, 0x1F; /* Extract ESR[EXC] */ 193 194 /* Exceptions enabled here. This will allow nested exceptions */ 195 mfs r6, rmsr; 196 nop 197 swi r6, r1, 0; /* RMSR_OFFSET */ 198 ori r6, r6, 0x100; /* Turn ON the EE bit */ 199 andi r6, r6, ~2; /* Disable interrupts */ 200 mts rmsr, r6; 201 nop 202 203 xori r6, r5, 1; /* 00001 = Unaligned Exception */ 204 /* Jump to unalignment exception handler */ 205 beqi r6, handle_unaligned_ex; 206 207handle_other_ex: /* Handle Other exceptions here */ 208 /* Save other volatiles before we make procedure calls below */ 209 swi r7, r1, PT_R7 210 swi r8, r1, PT_R8 211 swi r9, r1, PT_R9 212 swi r10, r1, PT_R10 213 swi r11, r1, PT_R11 214 swi r12, r1, PT_R12 215 swi r14, r1, PT_R14 216 swi r15, r1, PT_R15 217 swi r18, r1, PT_R18 218 219 or r5, r1, r0 220 andi r6, r3, 0x1F; /* Load ESR[EC] */ 221 lwi r7, r0, PER_CPU(KM) /* MS: saving current kernel mode to regs */ 222 swi r7, r1, PT_MODE 223 mfs r7, rfsr 224 nop 225 addk r8, r17, r0; /* Load exception address */ 226 bralid r15, full_exception; /* Branch to the handler */ 227 nop; 228 229 /* 230 * Trigger execution of the signal handler by enabling 231 * interrupts and calling an invalid syscall. 232 */ 233 mfs r5, rmsr; 234 nop 235 ori r5, r5, 2; 236 mts rmsr, r5; /* enable interrupt */ 237 nop 238 addi r12, r0, __NR_syscalls; 239 brki r14, 0x08; 240 mfs r5, rmsr; /* disable interrupt */ 241 nop 242 andi r5, r5, ~2; 243 mts rmsr, r5; 244 nop 245 246 lwi r7, r1, PT_R7 247 lwi r8, r1, PT_R8 248 lwi r9, r1, PT_R9 249 lwi r10, r1, PT_R10 250 lwi r11, r1, PT_R11 251 lwi r12, r1, PT_R12 252 lwi r14, r1, PT_R14 253 lwi r15, r1, PT_R15 254 lwi r18, r1, PT_R18 255 256 bri ex_handler_done; /* Complete exception handling */ 257 258/* 0x01 - Unaligned data access exception 259 * This occurs when a word access is not aligned on a word boundary, 260 * or when a 16-bit access is not aligned on a 16-bit boundary. 261 * This handler perform the access, and returns, except for MMU when 262 * the unaligned address is last on a 4k page or the physical address is 263 * not found in the page table, in which case unaligned_data_trap is called. 264 */ 265handle_unaligned_ex: 266 /* Working registers already saved: R3, R4, R5, R6 267 * R3 = ESR 268 * R4 = BTR 269 */ 270 mfs r4, rear; 271 nop 272 273 andi r6, r3, 0x3E0; /* Mask and extract the register operand */ 274 srl r6, r6; /* r6 >> 5 */ 275 srl r6, r6; 276 srl r6, r6; 277 srl r6, r6; 278 srl r6, r6; 279 /* Store the register operand in a temporary location */ 280 sbi r6, r0, TOPHYS(ex_reg_op); 281 282 andi r6, r3, 0x400; /* Extract ESR[S] */ 283 bnei r6, ex_sw; 284ex_lw: 285 andi r6, r3, 0x800; /* Extract ESR[W] */ 286 beqi r6, ex_lhw; 287 lbui r5, r4, 0; /* Exception address in r4 */ 288 /* Load a word, byte-by-byte from destination address 289 and save it in tmp space */ 290 sbi r5, r0, TOPHYS(ex_tmp_data_loc_0); 291 lbui r5, r4, 1; 292 sbi r5, r0, TOPHYS(ex_tmp_data_loc_1); 293 lbui r5, r4, 2; 294 sbi r5, r0, TOPHYS(ex_tmp_data_loc_2); 295 lbui r5, r4, 3; 296 sbi r5, r0, TOPHYS(ex_tmp_data_loc_3); 297 /* Get the destination register value into r3 */ 298 lwi r3, r0, TOPHYS(ex_tmp_data_loc_0); 299 bri ex_lw_tail; 300ex_lhw: 301 lbui r5, r4, 0; /* Exception address in r4 */ 302 /* Load a half-word, byte-by-byte from destination 303 address and save it in tmp space */ 304 sbi r5, r0, TOPHYS(ex_tmp_data_loc_0); 305 lbui r5, r4, 1; 306 sbi r5, r0, TOPHYS(ex_tmp_data_loc_1); 307 /* Get the destination register value into r3 */ 308 lhui r3, r0, TOPHYS(ex_tmp_data_loc_0); 309ex_lw_tail: 310 /* Get the destination register number into r5 */ 311 lbui r5, r0, TOPHYS(ex_reg_op); 312 /* Form load_word jump table offset (lw_table + (8 * regnum)) */ 313 la r6, r0, TOPHYS(lw_table); 314 addk r5, r5, r5; 315 addk r5, r5, r5; 316 addk r5, r5, r5; 317 addk r5, r5, r6; 318 bra r5; 319ex_lw_end: /* Exception handling of load word, ends */ 320ex_sw: 321 /* Get the destination register number into r5 */ 322 lbui r5, r0, TOPHYS(ex_reg_op); 323 /* Form store_word jump table offset (sw_table + (8 * regnum)) */ 324 la r6, r0, TOPHYS(sw_table); 325 add r5, r5, r5; 326 add r5, r5, r5; 327 add r5, r5, r5; 328 add r5, r5, r6; 329 bra r5; 330ex_sw_tail: 331 mfs r6, resr; 332 nop 333 andi r6, r6, 0x800; /* Extract ESR[W] */ 334 beqi r6, ex_shw; 335 /* Get the word - delay slot */ 336 swi r3, r0, TOPHYS(ex_tmp_data_loc_0); 337 /* Store the word, byte-by-byte into destination address */ 338 lbui r3, r0, TOPHYS(ex_tmp_data_loc_0); 339 sbi r3, r4, 0; 340 lbui r3, r0, TOPHYS(ex_tmp_data_loc_1); 341 sbi r3, r4, 1; 342 lbui r3, r0, TOPHYS(ex_tmp_data_loc_2); 343 sbi r3, r4, 2; 344 lbui r3, r0, TOPHYS(ex_tmp_data_loc_3); 345 sbi r3, r4, 3; 346 bri ex_handler_done; 347 348ex_shw: 349 /* Store the lower half-word, byte-by-byte into destination address */ 350 swi r3, r0, TOPHYS(ex_tmp_data_loc_0); 351 lbui r3, r0, TOPHYS(ex_tmp_data_loc_2); 352 sbi r3, r4, 0; 353 lbui r3, r0, TOPHYS(ex_tmp_data_loc_3); 354 sbi r3, r4, 1; 355ex_sw_end: /* Exception handling of store word, ends. */ 356 357ex_handler_done: 358 lwi r5, r1, 0 /* RMSR */ 359 mts rmsr, r5 360 nop 361 lwi r3, r1, PT_R3 362 lwi r4, r1, PT_R4 363 lwi r5, r1, PT_R5 364 lwi r6, r1, PT_R6 365 lwi r17, r1, PT_R17 366 367 rted r17, 0 368 addik r1, r1, (EX_HANDLER_STACK_SIZ); /* Restore stack frame */ 369 370.end _hw_exception_handler 371 372ex_handler_unhandled: 373/* FIXME add handle function for unhandled exception - dump register */ 374 bri 0 375 376.section .text 377.align 4 378lw_table: 379lw_r0: R3_TO_LWREG (0); 380lw_r1: LWREG_NOP; 381lw_r2: R3_TO_LWREG (2); 382lw_r3: R3_TO_LWREG_V (3); 383lw_r4: R3_TO_LWREG_V (4); 384lw_r5: R3_TO_LWREG_V (5); 385lw_r6: R3_TO_LWREG_V (6); 386lw_r7: R3_TO_LWREG (7); 387lw_r8: R3_TO_LWREG (8); 388lw_r9: R3_TO_LWREG (9); 389lw_r10: R3_TO_LWREG (10); 390lw_r11: R3_TO_LWREG (11); 391lw_r12: R3_TO_LWREG (12); 392lw_r13: R3_TO_LWREG (13); 393lw_r14: R3_TO_LWREG (14); 394lw_r15: R3_TO_LWREG (15); 395lw_r16: R3_TO_LWREG (16); 396lw_r17: LWREG_NOP; 397lw_r18: R3_TO_LWREG (18); 398lw_r19: R3_TO_LWREG (19); 399lw_r20: R3_TO_LWREG (20); 400lw_r21: R3_TO_LWREG (21); 401lw_r22: R3_TO_LWREG (22); 402lw_r23: R3_TO_LWREG (23); 403lw_r24: R3_TO_LWREG (24); 404lw_r25: R3_TO_LWREG (25); 405lw_r26: R3_TO_LWREG (26); 406lw_r27: R3_TO_LWREG (27); 407lw_r28: R3_TO_LWREG (28); 408lw_r29: R3_TO_LWREG (29); 409lw_r30: R3_TO_LWREG (30); 410lw_r31: R3_TO_LWREG (31); 411 412sw_table: 413sw_r0: SWREG_TO_R3 (0); 414sw_r1: SWREG_NOP; 415sw_r2: SWREG_TO_R3 (2); 416sw_r3: SWREG_TO_R3_V (3); 417sw_r4: SWREG_TO_R3_V (4); 418sw_r5: SWREG_TO_R3_V (5); 419sw_r6: SWREG_TO_R3_V (6); 420sw_r7: SWREG_TO_R3 (7); 421sw_r8: SWREG_TO_R3 (8); 422sw_r9: SWREG_TO_R3 (9); 423sw_r10: SWREG_TO_R3 (10); 424sw_r11: SWREG_TO_R3 (11); 425sw_r12: SWREG_TO_R3 (12); 426sw_r13: SWREG_TO_R3 (13); 427sw_r14: SWREG_TO_R3 (14); 428sw_r15: SWREG_TO_R3 (15); 429sw_r16: SWREG_TO_R3 (16); 430sw_r17: SWREG_NOP; 431sw_r18: SWREG_TO_R3 (18); 432sw_r19: SWREG_TO_R3 (19); 433sw_r20: SWREG_TO_R3 (20); 434sw_r21: SWREG_TO_R3 (21); 435sw_r22: SWREG_TO_R3 (22); 436sw_r23: SWREG_TO_R3 (23); 437sw_r24: SWREG_TO_R3 (24); 438sw_r25: SWREG_TO_R3 (25); 439sw_r26: SWREG_TO_R3 (26); 440sw_r27: SWREG_TO_R3 (27); 441sw_r28: SWREG_TO_R3 (28); 442sw_r29: SWREG_TO_R3 (29); 443sw_r30: SWREG_TO_R3 (30); 444sw_r31: SWREG_TO_R3 (31); 445 446/* Temporary data structures used in the handler */ 447.section .data 448.align 4 449ex_tmp_data_loc_0: 450 .byte 0 451ex_tmp_data_loc_1: 452 .byte 0 453ex_tmp_data_loc_2: 454 .byte 0 455ex_tmp_data_loc_3: 456 .byte 0 457ex_reg_op: 458 .byte 0 459