diff --git a/src/system/kernel/arch/ppc/Jamfile b/src/system/kernel/arch/ppc/Jamfile index 075c70ee73..7cc64b8060 100644 --- a/src/system/kernel/arch/ppc/Jamfile +++ b/src/system/kernel/arch/ppc/Jamfile @@ -14,6 +14,7 @@ KernelMergeObject kernel_arch_ppc.o : arch_debug.cpp arch_elf.cpp arch_exceptions.S + arch_exceptions_44x.S arch_int.cpp arch_mmu.cpp arch_platform.cpp diff --git a/src/system/kernel/arch/ppc/arch_exceptions_44x.S b/src/system/kernel/arch/ppc/arch_exceptions_44x.S new file mode 100644 index 0000000000..c4a1a60afa --- /dev/null +++ b/src/system/kernel/arch/ppc/arch_exceptions_44x.S @@ -0,0 +1,441 @@ +/* + * Copyright 2013, François Revol . + * All rights reserved. Distributed under the terms of the MIT License. + * + * Copyright 2006, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + * + * Copyright 2003, Travis Geiselbrecht. All rights reserved. + * Distributed under the terms of the NewOS License. + */ +#include + +/* General exception handling concept: + + The PPC architecture specifies entry point offsets for the various + exceptions in the first two physical pages. We put a short piece of code + (VEC_ENTRY()) into each exception vector. It calls exception_vector_common, + which is defined in the unused space at the beginning of the first physical + page. It re-enables address translation and calls ppc_exception_tail which + lies in the kernel. It dumps an iframe and invokes ppc_exception_entry() + (arch_int.cpp), which handles the exception and returns eventually. + The registers are restored from the iframe and we return from the + interrupt. + + algorithm overview: + + * VEC_ENTRY + * ppc_44x_exception_vector_common + * ppc_44x_exception_tail + - dump iframe + - ppc_exception_entry() + - restore registers and return from interrupt + + Here we use the following SPRG registers, which are at the disposal of the + operating system: + * SPRG0: Physical address pointer to a struct cpu_exception_context + for the current CPU. The structure contains helpful pointers + as well as some scratch memory for temporarily saving registers. + * SPRG1: Scratch. + + struct cpu_exception_context (defined in arch_int.h): + offset 0: virtual address of the exception handler routine in the kernel + offset 4: virtual address of the exception context + offset 8: kernel stack for the current thread + offset 12: start of scratch memory for saving registers etc. + + algorithm in detail: + + * VEC_ENTRY + - save r1 in SPRG1 and load cpu_exception_context into r1 + - save r0, save LR in r0 + * ppc_44x_exception_vector_common + - params: + . r0: old LR + . r1: exception context (physical address) + . SPRG1: original r1 + - save r0-3 + - load virtual exception context address to r1 + - turn on BAT for exception vector code + - turn on address translation + - get exception vector offset from LR + * ppc_44x_exception_tail + - params: + . r1: exception context (virtual address) + . r3: exception vector offset + . SPRG1: original r1 + - turn off BAT + - get kernel stack pointer + - dump iframe + - ppc_exception_entry() + - restore registers and return from interrupt + */ + + +/* exception vector definitions */ + +/* code in each exception vector */ +#define VEC_ENTRY() \ + mtsprg1 %r1 ; /* temporarily save r1 in SPRG1 */ \ + mfsprg0 %r1 ; /* ppc_cpu_exception_context* -> r1 */ \ + stw %r0, 16(%r1) ; /* save r0 */ \ + mflr %r0 ; /* save LR in r0 */ \ + bl ppc_44x_exception_vector_common ; /* continue with the common part */ + +/* defines an exception vector */ +#define DEFINE_VECTOR(offset, name) \ +.skip offset - (. - __44x_irqvec_start); \ +FUNCTION(ppc_44x_##name): \ + VEC_ENTRY() + + +.global __44x_irqvec_start +__44x_irqvec_start: + .long 0 + +/* Called by the exception vector code. + * LR: Points to the end of the exception vector code we're coming from. + * r0: original LR + * r1: ppc_cpu_exception_context* (physical address) + * SPRG1: original r1 + */ +ppc_44x_exception_vector_common: + stw %r0, 20(%r1) /* save original LR */ + stw %r2, 24(%r1) /* save r2 */ + stw %r3, 28(%r1) /* save r3 */ + + /* load the virtual address of the ppc_cpu_exception_context for this CPU */ + lwz %r1, 4(%r1) + + /* Address translation is turned off. We map this code via BAT, turn on + address translation, and continue in the kernel proper. */ + li %r0, 0x10|0x2 /* BATL_MC | BATL_PP_RW */ + mtibatl 0, %r0 /* load lower word of the instruction BAT */ + li %r0, 0x2 /* BEPI = 0, BL = 0 (128 KB), BATU_VS */ + mtibatu 0, %r0 /* load upper word of the instruction BAT */ + isync + sync + + /* turn on address translation */ + mfsrr1 %r0 /* load saved msr */ + rlwinm %r0, %r0, 28, 30, 31 /* extract mmu bits */ + mfmsr %r3 /* load the current msr */ + rlwimi %r3, %r0, 4, 26, 27 /* merge the mmu bits with the current msr */ + li %r0, 1 + rlwimi %r3, %r0, 13, 18, 18 /* turn on FPU, too */ + mtmsr %r3 /* load new msr (turning the mmu back on) */ + isync + + /* Get LR -- it points to the end of the exception vector code. We adjust it + to point to the beginning and can use it to identify the vector later. */ + mflr %r3 + subi %r3, %r3, 20 /* 5 instructions */ + + /* jump to kernel code (ppc_exception_tail) */ + lwz %r2, 0(%r1) + mtlr %r2 + blr + + +DEFINE_VECTOR(0x100, system_reset_exception) +DEFINE_VECTOR(0x200, machine_check_exception) +DEFINE_VECTOR(0x300, DSI_exception) +DEFINE_VECTOR(0x400, ISI_exception) +DEFINE_VECTOR(0x500, external_interrupt_exception) +DEFINE_VECTOR(0x600, alignment_exception) +DEFINE_VECTOR(0x700, program_exception) +DEFINE_VECTOR(0x800, FP_unavailable_exception) +DEFINE_VECTOR(0x900, decrementer_exception) +DEFINE_VECTOR(0xc00, system_call_exception) +DEFINE_VECTOR(0xd00, trace_exception) +DEFINE_VECTOR(0xe00, FP_assist_exception) +DEFINE_VECTOR(0xf00, perf_monitor_exception) +DEFINE_VECTOR(0xf20, altivec_unavailable_exception) +DEFINE_VECTOR(0x1000, ITLB_miss_exception) +DEFINE_VECTOR(0x1100, DTLB_miss_on_load_exception) +DEFINE_VECTOR(0x1200, DTLB_miss_on_store_exception) +DEFINE_VECTOR(0x1300, instruction_address_breakpoint_exception) +DEFINE_VECTOR(0x1400, system_management_exception) +DEFINE_VECTOR(0x1600, altivec_assist_exception) +DEFINE_VECTOR(0x1700, thermal_management_exception) + +.global __44x_irqvec_end +__44x_irqvec_end: + + +/* This is where ppc_44x_exception_vector_common continues. We're in the kernel here. + r1: ppc_cpu_exception_context* (virtual address) + r3: exception vector offset + SPRG1: original r1 + */ +FUNCTION(ppc_44x_exception_tail): + /* turn off BAT */ + li %r2, 0 + mtibatu 0, %r2 + mtibatl 0, %r2 + isync + sync + + /* save CR */ + mfcr %r0 + + mfsrr1 %r2 /* load saved msr */ + andi. %r2, %r2, (1 << 14) /* see if it was in kernel mode */ + beq .kernel /* yep */ + + /* We come from userland. Load the kernel stack top address for the current + userland thread. */ + mr %r2, %r1 + lwz %r1, 8(%r1) + b .restore_stack_end + +.kernel: + mr %r2, %r1 + mfsprg1 %r1 + +.restore_stack_end: + /* now r2 points to the ppc_cpu_exception_context, r1 to the kernel stack */ + /* restore the CR, it was messed up in the previous compare */ + mtcrf 0xff, %r0 + + /* align r1 to 8 bytes, so the iframe will be aligned too */ + rlwinm %r1, %r1, 0, 0, 28 + + /* save the registers */ + bl __44x_save_regs + + /* iframe pointer to r4 and a backup to r20 */ + mr %r4, %r1 + mr %r20, %r1 + + /* adjust the stack pointer for ABI compatibility */ + subi %r1, %r1, 8 /* make sure there's space for the previous + frame pointer and the return address */ + rlwinm %r1, %r1, 0, 0, 27 /* 16 byte align the stack pointer */ + li %r0, 0 + stw %r0, 0(%r1) /* previous frame pointer: NULL */ + /* 4(%r1) is room for the return address to be filled in by the + called function. */ + + /* r3: exception vector offset + r4: iframe pointer */ + bl ppc_exception_entry + + /* move the iframe to r1 */ + mr %r1, %r20 + + b __44x_restore_regs_and_rfi + + +/* called by ppc_44x_exception_tail + * register expectations: + * r1: stack + * r2: ppc_cpu_exception_context* + * SPRG1: original r1 + * r0,r3, LR: scrambled, but saved in scratch memory + * all other regs should have been unmodified by the exception handler, + * and ready to be saved + */ +__44x_save_regs: + /* Note: The iframe must be 8 byte aligned. The stack pointer we are passed + in r1 is aligned. So we store the floating point registers first and + need to take care that an even number of 4 byte registers is stored, + or insert padding respectively. */ + + /* push f0-f31 */ + stfdu %f0, -8(%r1) + stfdu %f1, -8(%r1) + stfdu %f2, -8(%r1) + stfdu %f3, -8(%r1) + stfdu %f4, -8(%r1) + stfdu %f5, -8(%r1) + stfdu %f6, -8(%r1) + stfdu %f7, -8(%r1) + stfdu %f8, -8(%r1) + stfdu %f9, -8(%r1) + stfdu %f10, -8(%r1) + stfdu %f11, -8(%r1) + stfdu %f12, -8(%r1) + stfdu %f13, -8(%r1) + stfdu %f14, -8(%r1) + stfdu %f15, -8(%r1) + stfdu %f16, -8(%r1) + stfdu %f17, -8(%r1) + stfdu %f18, -8(%r1) + stfdu %f19, -8(%r1) + stfdu %f20, -8(%r1) + stfdu %f21, -8(%r1) + stfdu %f22, -8(%r1) + stfdu %f23, -8(%r1) + stfdu %f24, -8(%r1) + stfdu %f25, -8(%r1) + stfdu %f26, -8(%r1) + stfdu %f27, -8(%r1) + stfdu %f28, -8(%r1) + stfdu %f29, -8(%r1) + stfdu %f30, -8(%r1) + stfdu %f31, -8(%r1) + + /* push r0-r3 */ + lwz %r0, 16(%r2) /* original r0 */ + stwu %r0, -4(%r1) /* push r0 */ + mfsprg1 %r0 /* original r1 */ + stwu %r0, -4(%r1) /* push r1 */ + lwz %r0, 24(%r2) /* original r2 */ + stwu %r0, -4(%r1) /* push r2 */ + lwz %r0, 28(%r2) /* original r3 */ + stwu %r0, -4(%r1) /* push r3 */ + + /* push r4-r31 */ + stwu %r4, -4(%r1) + stwu %r5, -4(%r1) + stwu %r6, -4(%r1) + stwu %r7, -4(%r1) + stwu %r8, -4(%r1) + stwu %r9, -4(%r1) + stwu %r10, -4(%r1) + stwu %r11, -4(%r1) + stwu %r12, -4(%r1) + stwu %r13, -4(%r1) + stwu %r14, -4(%r1) + stwu %r15, -4(%r1) + stwu %r16, -4(%r1) + stwu %r17, -4(%r1) + stwu %r18, -4(%r1) + stwu %r19, -4(%r1) + stwu %r20, -4(%r1) + stwu %r21, -4(%r1) + stwu %r22, -4(%r1) + stwu %r23, -4(%r1) + stwu %r24, -4(%r1) + stwu %r25, -4(%r1) + stwu %r26, -4(%r1) + stwu %r27, -4(%r1) + stwu %r28, -4(%r1) + stwu %r29, -4(%r1) + stwu %r30, -4(%r1) + stwu %r31, -4(%r1) + + /* save some of the other regs */ + mffs %f0 + stfsu %f0, -4(%r1) /* push FPSCR */ + mfctr %r0 + stwu %r0, -4(%r1) /* push CTR */ + mfxer %r0 + stwu %r0, -4(%r1) /* push XER */ + mfcr %r0 + stwu %r0, -4(%r1) /* push CR */ + lwz %r0, 20(%r2) /* original LR */ + stwu %r0, -4(%r1) /* push LR */ + mfspr %r0, %dsisr + stwu %r0, -4(%r1) /* push DSISR */ + mfspr %r0, %dar + stwu %r0, -4(%r1) /* push DAR */ + mfspr %r0, %srr1 + stwu %r0, -4(%r1) /* push SRR1 */ + mfspr %r0, %srr0 + stwu %r0, -4(%r1) /* push SRR0 */ + + stwu %r3, -4(%r1) /* exception vector offset */ + + blr + + +/* called at the tail end of each of the exceptions + * r1: iframe pointer + */ +__44x_restore_regs_and_rfi: + lwzu %r0, 4(%r1) /* SRR0 (skip vector offset) */ + mtspr %srr0, %r0 + lwzu %r0, 4(%r1) /* SRR1 */ + mtspr %srr1, %r0 + lwzu %r0, 4(%r1) /* DAR */ + mtspr %dar, %r0 + lwzu %r0, 4(%r1) /* DSISR */ + mtspr %dsisr, %r0 + lwzu %r0, 4(%r1) /* LR */ + mtlr %r0 + lwzu %r0, 4(%r1) /* CR */ + mtcr %r0 + lwzu %r0, 4(%r1) /* XER */ + mtxer %r0 + lwzu %r0, 4(%r1) /* CTR */ + mtctr %r0 + lfsu %f0, 4(%r1) /* FPSCR */ + mtfsf 0xff, %f0 + + lwzu %r31, 4(%r1) + lwzu %r30, 4(%r1) + lwzu %r29, 4(%r1) + lwzu %r28, 4(%r1) + lwzu %r27, 4(%r1) + lwzu %r26, 4(%r1) + lwzu %r25, 4(%r1) + lwzu %r24, 4(%r1) + lwzu %r23, 4(%r1) + lwzu %r22, 4(%r1) + lwzu %r21, 4(%r1) + lwzu %r20, 4(%r1) + lwzu %r19, 4(%r1) + lwzu %r18, 4(%r1) + lwzu %r17, 4(%r1) + lwzu %r16, 4(%r1) + lwzu %r15, 4(%r1) + lwzu %r14, 4(%r1) + lwzu %r13, 4(%r1) + lwzu %r12, 4(%r1) + lwzu %r11, 4(%r1) + lwzu %r10, 4(%r1) + lwzu %r9, 4(%r1) + lwzu %r8, 4(%r1) + lwzu %r7, 4(%r1) + lwzu %r6, 4(%r1) + lwzu %r5, 4(%r1) + lwzu %r4, 4(%r1) + lwzu %r3, 4(%r1) + + /* Stop here, before we overwrite r1, and continue with the floating point + registers first. */ + addi %r2, %r1, 16 /* skip r3-r0 */ + + /* f31-f0 */ + lfd %f31, 0(%r2) + lfdu %f30, 8(%r2) + lfdu %f29, 8(%r2) + lfdu %f28, 8(%r2) + lfdu %f27, 8(%r2) + lfdu %f26, 8(%r2) + lfdu %f25, 8(%r2) + lfdu %f24, 8(%r2) + lfdu %f23, 8(%r2) + lfdu %f22, 8(%r2) + lfdu %f21, 8(%r2) + lfdu %f20, 8(%r2) + lfdu %f19, 8(%r2) + lfdu %f18, 8(%r2) + lfdu %f17, 8(%r2) + lfdu %f16, 8(%r2) + lfdu %f15, 8(%r2) + lfdu %f14, 8(%r2) + lfdu %f13, 8(%r2) + lfdu %f12, 8(%r2) + lfdu %f11, 8(%r2) + lfdu %f10, 8(%r2) + lfdu %f9, 8(%r2) + lfdu %f8, 8(%r2) + lfdu %f7, 8(%r2) + lfdu %f6, 8(%r2) + lfdu %f5, 8(%r2) + lfdu %f4, 8(%r2) + lfdu %f3, 8(%r2) + lfdu %f2, 8(%r2) + lfdu %f1, 8(%r2) + lfd %f0, 8(%r2) + + /* r2-r0 */ + lwzu %r2, 4(%r1) + lwz %r0, 8(%r1) + lwz %r1, 4(%r1) + + /* return from interrupt */ + rfi