1/* 2 * Copyright 2006, Ingo Weinhold <bonefish@cs.tu-berlin.de>. 3 * All rights reserved. Distributed under the terms of the MIT License. 4 * 5 * Copyright 2003, Travis Geiselbrecht. All rights reserved. 6 * Distributed under the terms of the NewOS License. 7 */ 8#define FUNCTION(x) .global x; .type x,@function; x 9#define LOCAL_FUNCTION(x) .type x,@function; x 10 11/* General exception handling concept: 12 13 The PPC architecture specifies entry point offsets for the various 14 exceptions in the first two physical pages. We put a short piece of code 15 (VEC_ENTRY()) into each exception vector. It calls exception_vector_common, 16 which is defined in the unused space at the beginning of the first physical 17 page. It re-enables address translation and calls ppc_exception_tail which 18 lies in the kernel. It dumps an iframe and invokes ppc_exception_entry() 19 (arch_int.cpp), which handles the exception and returns eventually. 20 The registers are restored from the iframe and we return from the 21 interrupt. 22 23 algorithm overview: 24 25 * VEC_ENTRY 26 * exception_vector_common 27 * ppc_exception_tail 28 - dump iframe 29 - ppc_exception_entry() 30 - restore registers and return from interrupt 31 32 Here we use the following SPRG registers, which are at the disposal of the 33 operating system: 34 * SPRG0: Physical address pointer to a struct cpu_exception_context 35 for the current CPU. The structure contains helpful pointers 36 as well as some scratch memory for temporarily saving registers. 37 * SPRG1: Scratch. 38 39 struct cpu_exception_context (defined in arch_int.h): 40 offset 0: virtual address of the exception handler routine in the kernel 41 offset 4: virtual address of the exception context 42 offset 8: kernel stack for the current thread 43 offset 12: start of scratch memory for saving registers etc. 44 45 algorithm in detail: 46 47 * VEC_ENTRY 48 - save r1 in SPRG1 and load cpu_exception_context into r1 49 - save r0, save LR in r0 50 * exception_vector_common 51 - params: 52 . r0: old LR 53 . r1: exception context (physical address) 54 . SPRG1: original r1 55 - save r0-3 56 - load virtual exception context address to r1 57 - turn on BAT for exception vector code 58 - turn on address translation 59 - get exception vector offset from LR 60 * ppc_exception_tail 61 - params: 62 . r1: exception context (virtual address) 63 . r3: exception vector offset 64 . SPRG1: original r1 65 - turn off BAT 66 - get kernel stack pointer 67 - dump iframe 68 - ppc_exception_entry() 69 - restore registers and return from interrupt 70 */ 71 72 73/* exception vector definitions */ 74 75/* code in each exception vector */ 76#define VEC_ENTRY() \ 77 mtsprg1 %r1 ; /* temporarily save r1 in SPRG1 */ \ 78 mfsprg0 %r1 ; /* ppc_cpu_exception_context* -> r1 */ \ 79 stw %r0, 16(%r1) ; /* save r0 */ \ 80 mflr %r0 ; /* save LR in r0 */ \ 81 bl exception_vector_common ; /* continue with the common part */ 82 83/* defines an exception vector */ 84#define DEFINE_VECTOR(offset, name) \ 85.skip offset - (. - __irqvec_start); \ 86FUNCTION(name): \ 87 VEC_ENTRY() 88 89 90.global __irqvec_start 91__irqvec_start: 92 .long 0 93 94/* Called by the exception vector code. 95 * LR: Points to the end of the exception vector code we're coming from. 96 * r0: original LR 97 * r1: ppc_cpu_exception_context* (physical address) 98 * SPRG1: original r1 99 */ 100exception_vector_common: 101 stw %r0, 20(%r1) /* save original LR */ 102 stw %r2, 24(%r1) /* save r2 */ 103 stw %r3, 28(%r1) /* save r3 */ 104 105 /* load the virtual address of the ppc_cpu_exception_context for this CPU */ 106 lwz %r1, 4(%r1) 107 108 /* Address translation is turned off. We map this code via BAT, turn on 109 address translation, and continue in the kernel proper. */ 110 li %r0, 0x10|0x2 /* BATL_MC | BATL_PP_RW */ 111 mtibatl 0, %r0 /* load lower word of the instruction BAT */ 112 li %r0, 0x2 /* BEPI = 0, BL = 0 (128 KB), BATU_VS */ 113 mtibatu 0, %r0 /* load upper word of the instruction BAT */ 114 isync 115 sync 116 117 /* turn on address translation */ 118 mfsrr1 %r0 /* load saved msr */ 119 rlwinm %r0, %r0, 28, 30, 31 /* extract mmu bits */ 120 mfmsr %r3 /* load the current msr */ 121 rlwimi %r3, %r0, 4, 26, 27 /* merge the mmu bits with the current msr */ 122 li %r0, 1 123 rlwimi %r3, %r0, 13, 18, 18 /* turn on FPU, too */ 124 mtmsr %r3 /* load new msr (turning the mmu back on) */ 125 isync 126 127 /* Get LR -- it points to the end of the exception vector code. We adjust it 128 to point to the beginning and can use it to identify the vector later. */ 129 mflr %r3 130 subi %r3, %r3, 20 /* 5 instructions */ 131 132 /* jump to kernel code (ppc_exception_tail) */ 133 lwz %r2, 0(%r1) 134 mtlr %r2 135 blr 136 137 138DEFINE_VECTOR(0x100, system_reset_exception) 139DEFINE_VECTOR(0x200, machine_check_exception) 140DEFINE_VECTOR(0x300, DSI_exception) 141DEFINE_VECTOR(0x400, ISI_exception) 142DEFINE_VECTOR(0x500, external_interrupt_exception) 143DEFINE_VECTOR(0x600, alignment_exception) 144DEFINE_VECTOR(0x700, program_exception) 145DEFINE_VECTOR(0x800, FP_unavailable_exception) 146DEFINE_VECTOR(0x900, decrementer_exception) 147DEFINE_VECTOR(0xc00, system_call_exception) 148DEFINE_VECTOR(0xd00, trace_exception) 149DEFINE_VECTOR(0xe00, FP_assist_exception) 150DEFINE_VECTOR(0xf00, perf_monitor_exception) 151DEFINE_VECTOR(0xf20, altivec_unavailable_exception) 152DEFINE_VECTOR(0x1000, ITLB_miss_exception) 153DEFINE_VECTOR(0x1100, DTLB_miss_on_load_exception) 154DEFINE_VECTOR(0x1200, DTLB_miss_on_store_exception) 155DEFINE_VECTOR(0x1300, instruction_address_breakpoint_exception) 156DEFINE_VECTOR(0x1400, system_management_exception) 157DEFINE_VECTOR(0x1600, altivec_assist_exception) 158DEFINE_VECTOR(0x1700, thermal_management_exception) 159 160.global __irqvec_end 161__irqvec_end: 162 163 164/* This is where exception_vector_common continues. We're in the kernel here. 165 r1: ppc_cpu_exception_context* (virtual address) 166 r3: exception vector offset 167 SPRG1: original r1 168 */ 169FUNCTION(ppc_exception_tail): 170 /* turn off BAT */ 171 li %r2, 0 172 mtibatu 0, %r2 173 mtibatl 0, %r2 174 isync 175 sync 176 177 /* save CR */ 178 mfcr %r0 179 180 mfsrr1 %r2 /* load saved msr */ 181 andi. %r2, %r2, (1 << 14) /* see if it was in kernel mode */ 182 beq .kernel /* yep */ 183 184 /* We come from userland. Load the kernel stack top address for the current 185 userland thread. */ 186 mr %r2, %r1 187 lwz %r1, 8(%r1) 188 b .restore_stack_end 189 190.kernel: 191 mr %r2, %r1 192 mfsprg1 %r1 193 194.restore_stack_end: 195 /* now r2 points to the ppc_cpu_exception_context, r1 to the kernel stack */ 196 /* restore the CR, it was messed up in the previous compare */ 197 mtcrf 0xff, %r0 198 199 /* align r1 to 8 bytes, so the iframe will be aligned too */ 200 rlwinm %r1, %r1, 0, 0, 28 201 202 /* save the registers */ 203 bl __save_regs 204 205 /* iframe pointer to r4 and a backup to r20 */ 206 mr %r4, %r1 207 mr %r20, %r1 208 209 /* adjust the stack pointer for ABI compatibility */ 210 subi %r1, %r1, 8 /* make sure there's space for the previous 211 frame pointer and the return address */ 212 rlwinm %r1, %r1, 0, 0, 27 /* 16 byte align the stack pointer */ 213 li %r0, 0 214 stw %r0, 0(%r1) /* previous frame pointer: NULL */ 215 /* 4(%r1) is room for the return address to be filled in by the 216 called function. */ 217 218 /* r3: exception vector offset 219 r4: iframe pointer */ 220 bl ppc_exception_entry 221 222 /* move the iframe to r1 */ 223 mr %r1, %r20 224 225 b __restore_regs_and_rfi 226 227 228/* called by ppc_exception_tail 229 * register expectations: 230 * r1: stack 231 * r2: ppc_cpu_exception_context* 232 * SPRG1: original r1 233 * r0,r3, LR: scrambled, but saved in scratch memory 234 * all other regs should have been unmodified by the exception handler, 235 * and ready to be saved 236 */ 237__save_regs: 238 /* Note: The iframe must be 8 byte aligned. The stack pointer we are passed 239 in r1 is aligned. So we store the floating point registers first and 240 need to take care that an even number of 4 byte registers is stored, 241 or insert padding respectively. */ 242 243 /* push f0-f31 */ 244 stfdu %f0, -8(%r1) 245 stfdu %f1, -8(%r1) 246 stfdu %f2, -8(%r1) 247 stfdu %f3, -8(%r1) 248 stfdu %f4, -8(%r1) 249 stfdu %f5, -8(%r1) 250 stfdu %f6, -8(%r1) 251 stfdu %f7, -8(%r1) 252 stfdu %f8, -8(%r1) 253 stfdu %f9, -8(%r1) 254 stfdu %f10, -8(%r1) 255 stfdu %f11, -8(%r1) 256 stfdu %f12, -8(%r1) 257 stfdu %f13, -8(%r1) 258 stfdu %f14, -8(%r1) 259 stfdu %f15, -8(%r1) 260 stfdu %f16, -8(%r1) 261 stfdu %f17, -8(%r1) 262 stfdu %f18, -8(%r1) 263 stfdu %f19, -8(%r1) 264 stfdu %f20, -8(%r1) 265 stfdu %f21, -8(%r1) 266 stfdu %f22, -8(%r1) 267 stfdu %f23, -8(%r1) 268 stfdu %f24, -8(%r1) 269 stfdu %f25, -8(%r1) 270 stfdu %f26, -8(%r1) 271 stfdu %f27, -8(%r1) 272 stfdu %f28, -8(%r1) 273 stfdu %f29, -8(%r1) 274 stfdu %f30, -8(%r1) 275 stfdu %f31, -8(%r1) 276 277 /* push r0-r3 */ 278 lwz %r0, 16(%r2) /* original r0 */ 279 stwu %r0, -4(%r1) /* push r0 */ 280 mfsprg1 %r0 /* original r1 */ 281 stwu %r0, -4(%r1) /* push r1 */ 282 lwz %r0, 24(%r2) /* original r2 */ 283 stwu %r0, -4(%r1) /* push r2 */ 284 lwz %r0, 28(%r2) /* original r3 */ 285 stwu %r0, -4(%r1) /* push r3 */ 286 287 /* push r4-r31 */ 288 stwu %r4, -4(%r1) 289 stwu %r5, -4(%r1) 290 stwu %r6, -4(%r1) 291 stwu %r7, -4(%r1) 292 stwu %r8, -4(%r1) 293 stwu %r9, -4(%r1) 294 stwu %r10, -4(%r1) 295 stwu %r11, -4(%r1) 296 stwu %r12, -4(%r1) 297 stwu %r13, -4(%r1) 298 stwu %r14, -4(%r1) 299 stwu %r15, -4(%r1) 300 stwu %r16, -4(%r1) 301 stwu %r17, -4(%r1) 302 stwu %r18, -4(%r1) 303 stwu %r19, -4(%r1) 304 stwu %r20, -4(%r1) 305 stwu %r21, -4(%r1) 306 stwu %r22, -4(%r1) 307 stwu %r23, -4(%r1) 308 stwu %r24, -4(%r1) 309 stwu %r25, -4(%r1) 310 stwu %r26, -4(%r1) 311 stwu %r27, -4(%r1) 312 stwu %r28, -4(%r1) 313 stwu %r29, -4(%r1) 314 stwu %r30, -4(%r1) 315 stwu %r31, -4(%r1) 316 317 /* save some of the other regs */ 318 mffs %f0 319 stfsu %f0, -4(%r1) /* push FPSCR */ 320 mfctr %r0 321 stwu %r0, -4(%r1) /* push CTR */ 322 mfxer %r0 323 stwu %r0, -4(%r1) /* push XER */ 324 mfcr %r0 325 stwu %r0, -4(%r1) /* push CR */ 326 lwz %r0, 20(%r2) /* original LR */ 327 stwu %r0, -4(%r1) /* push LR */ 328 mfspr %r0, %dsisr 329 stwu %r0, -4(%r1) /* push DSISR */ 330 mfspr %r0, %dar 331 stwu %r0, -4(%r1) /* push DAR */ 332 mfspr %r0, %srr1 333 stwu %r0, -4(%r1) /* push SRR1 */ 334 mfspr %r0, %srr0 335 stwu %r0, -4(%r1) /* push SRR0 */ 336 337 stwu %r3, -4(%r1) /* exception vector offset */ 338 339 blr 340 341 342/* called at the tail end of each of the exceptions 343 * r1: iframe pointer 344 */ 345__restore_regs_and_rfi: 346 lwzu %r0, 4(%r1) /* SRR0 (skip vector offset) */ 347 mtspr %srr0, %r0 348 lwzu %r0, 4(%r1) /* SRR1 */ 349 mtspr %srr1, %r0 350 lwzu %r0, 4(%r1) /* DAR */ 351 mtspr %dar, %r0 352 lwzu %r0, 4(%r1) /* DSISR */ 353 mtspr %dsisr, %r0 354 lwzu %r0, 4(%r1) /* LR */ 355 mtlr %r0 356 lwzu %r0, 4(%r1) /* CR */ 357 mtcr %r0 358 lwzu %r0, 4(%r1) /* XER */ 359 mtxer %r0 360 lwzu %r0, 4(%r1) /* CTR */ 361 mtctr %r0 362 lfsu %f0, 4(%r1) /* FPSCR */ 363 mtfsf 0xff, %f0 364 365 lwzu %r31, 4(%r1) 366 lwzu %r30, 4(%r1) 367 lwzu %r29, 4(%r1) 368 lwzu %r28, 4(%r1) 369 lwzu %r27, 4(%r1) 370 lwzu %r26, 4(%r1) 371 lwzu %r25, 4(%r1) 372 lwzu %r24, 4(%r1) 373 lwzu %r23, 4(%r1) 374 lwzu %r22, 4(%r1) 375 lwzu %r21, 4(%r1) 376 lwzu %r20, 4(%r1) 377 lwzu %r19, 4(%r1) 378 lwzu %r18, 4(%r1) 379 lwzu %r17, 4(%r1) 380 lwzu %r16, 4(%r1) 381 lwzu %r15, 4(%r1) 382 lwzu %r14, 4(%r1) 383 lwzu %r13, 4(%r1) 384 lwzu %r12, 4(%r1) 385 lwzu %r11, 4(%r1) 386 lwzu %r10, 4(%r1) 387 lwzu %r9, 4(%r1) 388 lwzu %r8, 4(%r1) 389 lwzu %r7, 4(%r1) 390 lwzu %r6, 4(%r1) 391 lwzu %r5, 4(%r1) 392 lwzu %r4, 4(%r1) 393 lwzu %r3, 4(%r1) 394 395 /* Stop here, before we overwrite r1, and continue with the floating point 396 registers first. */ 397 addi %r2, %r1, 16 /* skip r3-r0 */ 398 399 /* f31-f0 */ 400 lfd %f31, 0(%r2) 401 lfdu %f30, 8(%r2) 402 lfdu %f29, 8(%r2) 403 lfdu %f28, 8(%r2) 404 lfdu %f27, 8(%r2) 405 lfdu %f26, 8(%r2) 406 lfdu %f25, 8(%r2) 407 lfdu %f24, 8(%r2) 408 lfdu %f23, 8(%r2) 409 lfdu %f22, 8(%r2) 410 lfdu %f21, 8(%r2) 411 lfdu %f20, 8(%r2) 412 lfdu %f19, 8(%r2) 413 lfdu %f18, 8(%r2) 414 lfdu %f17, 8(%r2) 415 lfdu %f16, 8(%r2) 416 lfdu %f15, 8(%r2) 417 lfdu %f14, 8(%r2) 418 lfdu %f13, 8(%r2) 419 lfdu %f12, 8(%r2) 420 lfdu %f11, 8(%r2) 421 lfdu %f10, 8(%r2) 422 lfdu %f9, 8(%r2) 423 lfdu %f8, 8(%r2) 424 lfdu %f7, 8(%r2) 425 lfdu %f6, 8(%r2) 426 lfdu %f5, 8(%r2) 427 lfdu %f4, 8(%r2) 428 lfdu %f3, 8(%r2) 429 lfdu %f2, 8(%r2) 430 lfdu %f1, 8(%r2) 431 lfd %f0, 8(%r2) 432 433 /* r2-r0 */ 434 lwzu %r2, 4(%r1) 435 lwz %r0, 8(%r1) 436 lwz %r1, 4(%r1) 437 438 /* return from interrupt */ 439 rfi 440