📄 head_4xx.s
字号:
/* * Copyright (c) 1995-1996 Gary Thomas <gdt@linuxppc.org> * Initial PowerPC version. * Copyright (c) 1996 Cort Dougan <cort@cs.nmt.edu> * Rewritten for PReP * Copyright (c) 1996 Paul Mackerras <paulus@cs.anu.edu.au> * Low-level exception handers, MMU support, and rewrite. * Copyright (c) 1997 Dan Malek <dmalek@jlc.net> * PowerPC 8xx modifications. * Copyright (c) 1998-1999 TiVo, Inc. * PowerPC 403GCX modifications. * Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu> * PowerPC 403GCX/405GP modifications. * Copyright 2000 MontaVista Software Inc. * PPC405 modifications * PowerPC 403GCX/405GP modifications. * Author: MontaVista Software, Inc. * frank_rowand@mvista.com or source@mvista.com * debbie_chu@mvista.com * * * Module name: head_4xx.S * * Description: * Kernel execution entry point code. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * */#include <linux/config.h>#include <asm/processor.h>#include <asm/page.h>#include <asm/mmu.h>#include <asm/pgtable.h>#include <asm/ibm4xx.h>#include <asm/cputable.h>#include <asm/thread_info.h>#include <asm/ppc_asm.h>#include "ppc_defs.h"/* Preprocessor Defines */#define STND_EXC 0#define CRIT_EXC 1/* As with the other PowerPC ports, it is expected that when code * execution begins here, the following registers contain valid, yet * optional, information: * * r3 - Board info structure pointer (DRAM, frequency, MAC address, etc.) * r4 - Starting address of the init RAM disk * r5 - Ending address of the init RAM disk * r6 - Start of kernel command line string (e.g. "mem=96m") * r7 - End of kernel command line string * * This is all going to change RSN when we add bi_recs....... -- Dan */ .text_GLOBAL(_stext)_GLOBAL(_start) /* Save parameters we are passed. */ mr r31,r3 mr r30,r4 mr r29,r5 mr r28,r6 mr r27,r7 li r24,0 /* CPU number */ /* We have to turn on the MMU right away so we get cache modes * set correctly. */ bl initial_mmu/* We now have the lower 16 Meg mapped into TLB entries, and the caches * ready to work. */turn_on_mmu: li r0,MSR_KERNEL mtspr SRR1,r0 lis r0,start_here@h ori r0,r0,start_here@l mtspr SRR0,r0 SYNC rfi /* enables MMU *//* Exception vector entry code. This code runs with address translation * turned off (i.e. using physical addresses). We assume SPRG3 has the * physical address of the current task thread_struct. */ #define COMMON_PROLOG(n) \0: mtspr SPRN_SPRG0,r20; /* We need r20, move it to SPRG0 */\ mtspr SPRN_SPRG1,r21; /* We need r21, move it to SPRG1 */\ mfcr r20; /* We need the CR, move it to r20 */\ mfspr r21,SPRN_SPRG2; /* Exception stack to use */\ cmpwi cr0,r21,0; /* From user mode or RTAS? */\ bne 1f; /* Not RTAS, branch */\ tophys(r21, r1); /* Convert vka in r1 to pka in r21 */\ subi r21,r21,INT_FRAME_SIZE; /* Allocate an exception frame */\1: stw r20,_CCR(r21); /* Save CR on the stack */\ stw r22,GPR22(r21); /* Save r22 on the stack */\ stw r23,GPR23(r21); /* r23 Save on the stack */\ mfspr r20,SPRN_SPRG0; /* Get r20 back out of SPRG0 */\ stw r20,GPR20(r21); /* Save r20 on the stack */\ mfspr r22,SPRN_SPRG1; /* Get r21 back out of SPRG0 */\ stw r22,GPR21(r21); /* Save r21 on the stack */\ mflr r20; \ stw r20,_LINK(r21); /* Save LR on the stack */\ mfctr r22; \ stw r22,_CTR(r21); /* Save CTR on the stack */\ mfspr r20,XER; \ stw r20,_XER(r21); /* Save XER on the stack */\ mfspr r20,SPRN_DBCR0; \ stw r20,_DBCR0(r21); /* Save Debug Control on the stack */#define COMMON_EPILOG \ stw r0,GPR0(r21); /* Save r0 on the stack */\ stw r1,GPR1(r21); /* Save r1 on the stack */\ stw r2,GPR2(r21); /* Save r2 on the stack */\ stw r1,0(r21); \ tovirt(r1,r21); /* Set-up new kernel stack pointer */\ SAVE_4GPRS(3, r21); /* Save r3 through r6 on the stack */\ SAVE_GPR(7, r21); /* Save r7 on the stack */#define STND_EXCEPTION_PROLOG(n) \ COMMON_PROLOG(n); \ mfspr r22,SPRN_SRR0; /* Faulting instruction address */\ lis r20,MSR_WE@h; \ mfspr r23,SPRN_SRR1; /* MSR at the time of fault */\ andc r23,r23,r20; /* disable processor wait state */\ COMMON_EPILOG;#define CRIT_EXCEPTION_PROLOG(n) \ COMMON_PROLOG(n); \ mfspr r22,SPRN_SRR2; /* Faulting instruction address */\ lis r20,MSR_WE@h; \ mfspr r23,SPRN_SRR3; /* MSR at the time of fault */\ andc r23,r23,r20; /* disable processor wait state */\ COMMON_EPILOG;#define START_EXCEPTION(n, label) \ . = n; \label:#define FINISH_EXCEPTION(func) \ bl transfer_to_handler; \ .long func; \ .long ret_from_except#define STND_EXCEPTION(n, label, func) \ START_EXCEPTION(n, label); \ STND_EXCEPTION_PROLOG(n); \ addi r3,r1,STACK_FRAME_OVERHEAD; \ li r7,STND_EXC; \ li r20,MSR_KERNEL; \ FINISH_EXCEPTION(func)#define CRIT_EXCEPTION(n, label, func) \ START_EXCEPTION(n, label); \ CRIT_EXCEPTION_PROLOG(n); \ addi r3,r1,STACK_FRAME_OVERHEAD; \ li r7,CRIT_EXC; \ li r20,MSR_KERNEL; \ FINISH_EXCEPTION(func)/* Exception vectors.*/ /* 0x0100 - Critical Interrupt Exception*/ CRIT_EXCEPTION(0x0100, CriticalInterrupt, UnknownException)/* 0x0200 - Machine Check Exception*/#if 0 CRIT_EXCEPTION(0x0200, MachineCheck, MachineCheckException)#else START_EXCEPTION(0x0200, MachineCheck) CRIT_EXCEPTION_PROLOG(0x0200) /* lis r4,0x0400 mtdcr DCRN_POB0_BESR0,r4 */#ifdef DCRN_POB0_BEAR mfdcr r4,DCRN_POB0_BEAR mfdcr r4,DCRN_POB0_BESR0 mfdcr r4,DCRN_POB0_BESR1#endif#ifdef DCRN_PLB0_BEAR mfdcr r4,DCRN_PLB0_ACR mfdcr r4,DCRN_PLB0_BEAR mfdcr r4,DCRN_PLB0_BESR#endif addi r3,r1,STACK_FRAME_OVERHEAD li r7,CRIT_EXC li r20,MSR_KERNEL FINISH_EXCEPTION(MachineCheckException)#endif/* 0x0300 - Data Storage Exception * This happens for just a few reasons. U0 set (but we don't do that), * or zone protection fault (user violation, write to protected page). * If this is just an update of modified status, we do that quickly * and exit. Otherwise, we call heavywight functions to do the work. */ START_EXCEPTION(0x0300, DataStore) mtspr SPRG0, r20 /* Save some working registers */ mtspr SPRG1, r21#ifdef CONFIG_403GCX stw r22, 0(r0) stw r23, 4(r0) mfcr r21 mfspr r22, SPRN_PID stw r21, 8(r0) stw r22, 12(r0)#else mtspr SPRG4, r22 mtspr SPRG5, r23 mfcr r21 mfspr r22, SPRN_PID mtspr SPRG7, r21 mtspr SPRG6, r22#endif /* First, check if it was a zone fault (which means a user * tried to access a kernel or read-protected page - always * a SEGV). All other faults here must be stores, so no * need to check ESR_DST as well. */ mfspr r20, SPRN_ESR andis. r20, r20, ESR_DIZ@h bne 2f mfspr r20, SPRN_DEAR /* Get faulting address */ /* If we are faulting a kernel address, we have to use the * kernel page tables. */ andis. r21, r20, 0x8000 beq 3f lis r21, swapper_pg_dir@h ori r21, r21, swapper_pg_dir@l li r23, 0 mtspr SPRN_PID, r23 /* TLB will have 0 TID */ b 4f /* Get the PGD for the current thread. */3: mfspr r21,SPRG3 lwz r21,PGDIR(r21)4: tophys(r21, r21) rlwimi r21, r20, 12, 20, 29 /* Create L1 (pgdir/pmd) address */ lwz r21, 0(r21) /* Get L1 entry */ rlwinm. r22, r21, 0, 0, 19 /* Extract L2 (pte) base address */ beq 2f /* Bail if no table */ tophys(r22, r22) rlwimi r22, r20, 22, 20, 29 /* Compute PTE address */ lwz r21, 0(r22) /* Get Linux PTE */ andi. r23, r21, _PAGE_RW /* Is it writeable? */ beq 2f /* Bail if not */ /* Update 'changed'. */ ori r21, r21, _PAGE_DIRTY|_PAGE_ACCESSED|_PAGE_HWWRITE stw r21, 0(r22) /* Update Linux page table */ /* Most of the Linux PTE is ready to load into the TLB LO. * We set ZSEL, where only the LS-bit determines user access. * We set execute, because we don't have the granularity to * properly set this at the page level (Linux problem). * If shared is set, we cause a zero PID->TID load. * Many of these bits are software only. Bits we don't set * here we (properly should) assume have the appropriate value. */ li r22, 0x0ce2 andc r21, r21, r22 /* Make sure 20, 21 are zero */ /* find the TLB index that caused the fault. It has to be here. */ tlbsx r23, 0, r20 tlbwe r21, r23, TLB_DATA /* Load TLB LO */ /* Done...restore registers and get out of here. */#ifdef CONFIG_403GCX lwz r22, 12(r0) lwz r21, 8(r0) mtspr SPRN_PID, r22 mtcr r21 lwz r23, 4(r0) lwz r22, 0(r0)#else mfspr r22, SPRG6 mfspr r21, SPRG7 mtspr SPRN_PID, r22 mtcr r21 mfspr r23, SPRG5 mfspr r22, SPRG4#endif mfspr r21, SPRG1 mfspr r20, SPRG0 PPC405_ERR77_SYNC rfi /* Should sync shadow TLBs */2: /* The bailout. Restore registers to pre-exception conditions * and call the heavyweights to help us out. */#ifdef CONFIG_403GCX lwz r22, 12(r0) lwz r21, 8(r0) mtspr SPRN_PID, r22 mtcr r21 lwz r23, 4(r0) lwz r22, 0(r0)#else mfspr r22, SPRG6 mfspr r21, SPRG7 mtspr SPRN_PID, r22 mtcr r21 mfspr r23, SPRG5 mfspr r22, SPRG4#endif mfspr r21, SPRG1 mfspr r20, SPRG0 b DataAccess/* 0x0400 - Instruction Storage Exception * I don't know why it is called "Storage"....This is caused by a fetch * from non-execute or guarded pages. */ START_EXCEPTION(0x0400, InstructionAccess) STND_EXCEPTION_PROLOG(0x0400) mr r4,r22 /* Pass SRR0 as arg2 */ li r5,0 /* Pass zero as arg3 */ addi r3,r1,STACK_FRAME_OVERHEAD li r7,STND_EXC li r20,MSR_KERNEL rlwimi r20,r23,0,16,16 /* Copy EE bit from the saved MSR */ FINISH_EXCEPTION(do_page_fault) /* do_page_fault(regs, SRR0, SRR1) *//* 0x0500 - External Interrupt Exception*/ START_EXCEPTION(0x0500, HardwareInterrupt) STND_EXCEPTION_PROLOG(0x0500) addi r3,r1,STACK_FRAME_OVERHEAD li r7,STND_EXC li r20,MSR_KERNEL li r4,0 bl transfer_to_handler_GLOBAL(do_IRQ_intercept) .long do_IRQ .long ret_from_intercept/* 0x0600 - Alignment Exception*/ START_EXCEPTION(0x0600, Alignment) STND_EXCEPTION_PROLOG(0x0600) mfspr r4,SPRN_DEAR /* Grab the DEAR and save it */ stw r4,_DEAR(r21) addi r3,r1,STACK_FRAME_OVERHEAD li r7,STND_EXC li r20,MSR_KERNEL rlwimi r20,r23,0,16,16 /* Copy EE bit from the saved MSR */ FINISH_EXCEPTION(AlignmentException)/* 0x0700 - Program Exception*/ START_EXCEPTION(0x0700, ProgramCheck) STND_EXCEPTION_PROLOG(0x0700) addi r3,r1,STACK_FRAME_OVERHEAD li r7,STND_EXC li r20,MSR_KERNEL rlwimi r20,r23,0,16,16 /* Copy EE bit from the saved MSR */ FINISH_EXCEPTION(ProgramCheckException)/* I'm stealing this unused vector location to build a standard exception * frame for Data TLB Access errors. The other Data TLB exceptions will bail * out to this point if they can't resolve the lightweight TLB fault. */ START_EXCEPTION(0x0800, DataAccess) STND_EXCEPTION_PROLOG(0x0800) mfspr r5,SPRN_ESR /* Grab the ESR, save it, pass arg3 */ stw r5,_ESR(r21) mfspr r4,SPRN_DEAR /* Grab the DEAR, save it, pass arg2 */ stw r4,_DEAR(r21) addi r3,r1,STACK_FRAME_OVERHEAD li r7,STND_EXC li r20,MSR_KERNEL rlwimi r20,r23,0,16,16 /* Copy EE bit from the saved MSR */ FINISH_EXCEPTION(do_page_fault) /* do_page_fault(regs, ESR, DEAR) */ STND_EXCEPTION(0x0900, Trap_09, UnknownException) STND_EXCEPTION(0x0A00, Trap_0A, UnknownException) STND_EXCEPTION(0x0B00, Trap_0B, UnknownException)/* 0x0C00 - System Call Exception*/ START_EXCEPTION(0x0C00, SystemCall) STND_EXCEPTION_PROLOG(0x0C00) stw r3,ORIG_GPR3(r21) li r7,STND_EXC li r20,MSR_KERNEL rlwimi r20,r23,0,16,16 /* Copy EE bit from the saved MSR */ FINISH_EXCEPTION(DoSyscall) STND_EXCEPTION(0x0D00, Trap_0D, UnknownException) STND_EXCEPTION(0x0E00, Trap_0E, UnknownException) STND_EXCEPTION(0x0F00, Trap_0F, UnknownException)/* 0x1000 - Programmable Interval Timer (PIT) Exception*/ START_EXCEPTION(0x1000, Decrementer) STND_EXCEPTION_PROLOG(0x1000) lis r0,TSR_PIS@h /* Set-up the PIT exception mask */ mtspr SPRN_TSR,r0 /* Clear the PIT exception */ addi r3,r1,STACK_FRAME_OVERHEAD li r7,STND_EXC li r20,MSR_KERNEL bl transfer_to_handler_GLOBAL(timer_interrupt_intercept) .long timer_interrupt .long ret_from_intercept#if 0/* NOTE: * FIT and WDT handlers are not implemented yet. *//* 0x1010 - Fixed Interval Timer (FIT) Exception*/ STND_EXCEPTION(0x1010, FITException, UnknownException)/* 0x1020 - Watchdog Timer (WDT) Exception*/ CRIT_EXCEPTION(0x1020, WDTException, UnknownException)#endif/* 0x1100 - Data TLB Miss Exception * As the name implies, translation is not in the MMU, so search the * page tables and fix it. The only purpose of this function is to * load TLB entries from the page table if they exist. */ START_EXCEPTION(0x1100, DTLBMiss) mtspr SPRG0, r20 /* Save some working registers */ mtspr SPRG1, r21#ifdef CONFIG_403GCX stw r22, 0(r0) stw r23, 4(r0) mfcr r21 mfspr r22, SPRN_PID stw r21, 8(r0) stw r22, 12(r0)#else mtspr SPRG4, r22 mtspr SPRG5, r23 mfcr r21 mfspr r22, SPRN_PID mtspr SPRG7, r21 mtspr SPRG6, r22#endif mfspr r20, SPRN_DEAR /* Get faulting address */ /* If we are faulting a kernel address, we have to use the * kernel page tables. */ andis. r21, r20, 0x8000 beq 3f lis r21, swapper_pg_dir@h ori r21, r21, swapper_pg_dir@l li r23, 0 mtspr SPRN_PID, r23 /* TLB will have 0 TID */ b 4f /* Get the PGD for the current thread. */3: mfspr r21,SPRG3 lwz r21,PGDIR(r21)4: tophys(r21, r21) rlwimi r21, r20, 12, 20, 29 /* Create L1 (pgdir/pmd) address */ lwz r21, 0(r21) /* Get L1 entry */ rlwinm. r22, r21, 0, 0, 19 /* Extract L2 (pte) base address */ beq 2f /* Bail if no table */ tophys(r22, r22) rlwimi r22, r20, 22, 20, 29 /* Compute PTE address */ lwz r21, 0(r22) /* Get Linux PTE */ andi. r23, r21, _PAGE_PRESENT beq 2f
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -