📄 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 <asm/asm-offsets.h>/* 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 /* 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: lis r0,MSR_KERNEL@h ori r0,r0,MSR_KERNEL@l mtspr SPRN_SRR1,r0 lis r0,start_here@h ori r0,r0,start_here@l mtspr SPRN_SRR0,r0 SYNC rfi /* enables MMU */ b . /* prevent prefetch past rfi *//* * This area is used for temporarily saving registers during the * critical exception prolog. */ . = 0xc0crit_save:_GLOBAL(crit_r10) .space 4_GLOBAL(crit_r11) .space 4/* * 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. * Note that we have to have decremented r1 before we write to any fields * of the exception frame, since a critical interrupt could occur at any * time, and it will write to the area immediately below the current r1. */#define NORMAL_EXCEPTION_PROLOG \ mtspr SPRN_SPRG0,r10; /* save two registers to work with */\ mtspr SPRN_SPRG1,r11; \ mtspr SPRN_SPRG2,r1; \ mfcr r10; /* save CR in r10 for now */\ mfspr r11,SPRN_SRR1; /* check whether user or kernel */\ andi. r11,r11,MSR_PR; \ beq 1f; \ mfspr r1,SPRN_SPRG3; /* if from user, start at top of */\ lwz r1,THREAD_INFO-THREAD(r1); /* this thread's kernel stack */\ addi r1,r1,THREAD_SIZE; \1: subi r1,r1,INT_FRAME_SIZE; /* Allocate an exception frame */\ tophys(r11,r1); \ stw r10,_CCR(r11); /* save various registers */\ stw r12,GPR12(r11); \ stw r9,GPR9(r11); \ mfspr r10,SPRN_SPRG0; \ stw r10,GPR10(r11); \ mfspr r12,SPRN_SPRG1; \ stw r12,GPR11(r11); \ mflr r10; \ stw r10,_LINK(r11); \ mfspr r10,SPRN_SPRG2; \ mfspr r12,SPRN_SRR0; \ stw r10,GPR1(r11); \ mfspr r9,SPRN_SRR1; \ stw r10,0(r11); \ rlwinm r9,r9,0,14,12; /* clear MSR_WE (necessary?) */\ stw r0,GPR0(r11); \ SAVE_4GPRS(3, r11); \ SAVE_2GPRS(7, r11)/* * Exception prolog for critical exceptions. This is a little different * from the normal exception prolog above since a critical exception * can potentially occur at any point during normal exception processing. * Thus we cannot use the same SPRG registers as the normal prolog above. * Instead we use a couple of words of memory at low physical addresses. * This is OK since we don't support SMP on these processors. */#define CRITICAL_EXCEPTION_PROLOG \ stw r10,crit_r10@l(0); /* save two registers to work with */\ stw r11,crit_r11@l(0); \ mfcr r10; /* save CR in r10 for now */\ mfspr r11,SPRN_SRR3; /* check whether user or kernel */\ andi. r11,r11,MSR_PR; \ lis r11,critical_stack_top@h; \ ori r11,r11,critical_stack_top@l; \ beq 1f; \ /* COMING FROM USER MODE */ \ mfspr r11,SPRN_SPRG3; /* if from user, start at top of */\ lwz r11,THREAD_INFO-THREAD(r11); /* this thread's kernel stack */\ addi r11,r11,THREAD_SIZE; \1: subi r11,r11,INT_FRAME_SIZE; /* Allocate an exception frame */\ tophys(r11,r11); \ stw r10,_CCR(r11); /* save various registers */\ stw r12,GPR12(r11); \ stw r9,GPR9(r11); \ mflr r10; \ stw r10,_LINK(r11); \ mfspr r12,SPRN_DEAR; /* save DEAR and ESR in the frame */\ stw r12,_DEAR(r11); /* since they may have had stuff */\ mfspr r9,SPRN_ESR; /* in them at the point where the */\ stw r9,_ESR(r11); /* exception was taken */\ mfspr r12,SPRN_SRR2; \ stw r1,GPR1(r11); \ mfspr r9,SPRN_SRR3; \ stw r1,0(r11); \ tovirt(r1,r11); \ rlwinm r9,r9,0,14,12; /* clear MSR_WE (necessary?) */\ stw r0,GPR0(r11); \ SAVE_4GPRS(3, r11); \ SAVE_2GPRS(7, r11) /* * State at this point: * r9 saved in stack frame, now saved SRR3 & ~MSR_WE * r10 saved in crit_r10 and in stack frame, trashed * r11 saved in crit_r11 and in stack frame, * now phys stack/exception frame pointer * r12 saved in stack frame, now saved SRR2 * CR saved in stack frame, CR0.EQ = !SRR3.PR * LR, DEAR, ESR in stack frame * r1 saved in stack frame, now virt stack/excframe pointer * r0, r3-r8 saved in stack frame *//* * Exception vectors. */#define START_EXCEPTION(n, label) \ . = n; \label:#define EXCEPTION(n, label, hdlr, xfer) \ START_EXCEPTION(n, label); \ NORMAL_EXCEPTION_PROLOG; \ addi r3,r1,STACK_FRAME_OVERHEAD; \ xfer(n, hdlr)#define CRITICAL_EXCEPTION(n, label, hdlr) \ START_EXCEPTION(n, label); \ CRITICAL_EXCEPTION_PROLOG; \ addi r3,r1,STACK_FRAME_OVERHEAD; \ EXC_XFER_TEMPLATE(hdlr, n+2, (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), \ NOCOPY, crit_transfer_to_handler, \ ret_from_crit_exc)#define EXC_XFER_TEMPLATE(hdlr, trap, msr, copyee, tfer, ret) \ li r10,trap; \ stw r10,_TRAP(r11); \ lis r10,msr@h; \ ori r10,r10,msr@l; \ copyee(r10, r9); \ bl tfer; \ .long hdlr; \ .long ret#define COPY_EE(d, s) rlwimi d,s,0,16,16#define NOCOPY(d, s)#define EXC_XFER_STD(n, hdlr) \ EXC_XFER_TEMPLATE(hdlr, n, MSR_KERNEL, NOCOPY, transfer_to_handler_full, \ ret_from_except_full)#define EXC_XFER_LITE(n, hdlr) \ EXC_XFER_TEMPLATE(hdlr, n+1, MSR_KERNEL, NOCOPY, transfer_to_handler, \ ret_from_except)#define EXC_XFER_EE(n, hdlr) \ EXC_XFER_TEMPLATE(hdlr, n, MSR_KERNEL, COPY_EE, transfer_to_handler_full, \ ret_from_except_full)#define EXC_XFER_EE_LITE(n, hdlr) \ EXC_XFER_TEMPLATE(hdlr, n+1, MSR_KERNEL, COPY_EE, transfer_to_handler, \ ret_from_except)/* * 0x0100 - Critical Interrupt Exception */ CRITICAL_EXCEPTION(0x0100, CriticalInterrupt, unknown_exception)/* * 0x0200 - Machine Check Exception */ CRITICAL_EXCEPTION(0x0200, MachineCheck, machine_check_exception)/* * 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, DataStorage) mtspr SPRN_SPRG0, r10 /* Save some working registers */ mtspr SPRN_SPRG1, r11#ifdef CONFIG_403GCX stw r12, 0(r0) stw r9, 4(r0) mfcr r11 mfspr r12, SPRN_PID stw r11, 8(r0) stw r12, 12(r0)#else mtspr SPRN_SPRG4, r12 mtspr SPRN_SPRG5, r9 mfcr r11 mfspr r12, SPRN_PID mtspr SPRN_SPRG7, r11 mtspr SPRN_SPRG6, r12#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 r10, SPRN_ESR andis. r10, r10, ESR_DIZ@h bne 2f mfspr r10, SPRN_DEAR /* Get faulting address */ /* If we are faulting a kernel address, we have to use the * kernel page tables. */ lis r11, TASK_SIZE@h cmplw r10, r11 blt+ 3f lis r11, swapper_pg_dir@h ori r11, r11, swapper_pg_dir@l li r9, 0 mtspr SPRN_PID, r9 /* TLB will have 0 TID */ b 4f /* Get the PGD for the current thread. */3: mfspr r11,SPRN_SPRG3 lwz r11,PGDIR(r11)4: tophys(r11, r11) rlwimi r11, r10, 12, 20, 29 /* Create L1 (pgdir/pmd) address */ lwz r11, 0(r11) /* Get L1 entry */ rlwinm. r12, r11, 0, 0, 19 /* Extract L2 (pte) base address */ beq 2f /* Bail if no table */ rlwimi r12, r10, 22, 20, 29 /* Compute PTE address */ lwz r11, 0(r12) /* Get Linux PTE */ andi. r9, r11, _PAGE_RW /* Is it writeable? */ beq 2f /* Bail if not */ /* Update 'changed'. */ ori r11, r11, _PAGE_DIRTY|_PAGE_ACCESSED|_PAGE_HWWRITE stw r11, 0(r12) /* 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 r12, 0x0ce2 andc r11, r11, r12 /* Make sure 20, 21 are zero */ /* find the TLB index that caused the fault. It has to be here. */ tlbsx r9, 0, r10 tlbwe r11, r9, TLB_DATA /* Load TLB LO */ /* Done...restore registers and get out of here. */#ifdef CONFIG_403GCX lwz r12, 12(r0) lwz r11, 8(r0) mtspr SPRN_PID, r12 mtcr r11 lwz r9, 4(r0) lwz r12, 0(r0)#else mfspr r12, SPRN_SPRG6 mfspr r11, SPRN_SPRG7 mtspr SPRN_PID, r12 mtcr r11 mfspr r9, SPRN_SPRG5 mfspr r12, SPRN_SPRG4#endif mfspr r11, SPRN_SPRG1 mfspr r10, SPRN_SPRG0 PPC405_ERR77_SYNC rfi /* Should sync shadow TLBs */ b . /* prevent prefetch past rfi */2: /* The bailout. Restore registers to pre-exception conditions * and call the heavyweights to help us out. */#ifdef CONFIG_403GCX lwz r12, 12(r0) lwz r11, 8(r0) mtspr SPRN_PID, r12 mtcr r11 lwz r9, 4(r0) lwz r12, 0(r0)#else mfspr r12, SPRN_SPRG6 mfspr r11, SPRN_SPRG7 mtspr SPRN_PID, r12 mtcr r11 mfspr r9, SPRN_SPRG5 mfspr r12, SPRN_SPRG4#endif mfspr r11, SPRN_SPRG1 mfspr r10, SPRN_SPRG0 b DataAccess/* * 0x0400 - Instruction Storage Exception * This is caused by a fetch from non-execute or guarded pages. */ START_EXCEPTION(0x0400, InstructionAccess) NORMAL_EXCEPTION_PROLOG mr r4,r12 /* Pass SRR0 as arg2 */ li r5,0 /* Pass zero as arg3 */ EXC_XFER_EE_LITE(0x400, handle_page_fault)/* 0x0500 - External Interrupt Exception */ EXCEPTION(0x0500, HardwareInterrupt, do_IRQ, EXC_XFER_LITE)/* 0x0600 - Alignment Exception */ START_EXCEPTION(0x0600, Alignment) NORMAL_EXCEPTION_PROLOG mfspr r4,SPRN_DEAR /* Grab the DEAR and save it */ stw r4,_DEAR(r11) addi r3,r1,STACK_FRAME_OVERHEAD EXC_XFER_EE(0x600, alignment_exception)/* 0x0700 - Program Exception */ START_EXCEPTION(0x0700, ProgramCheck) NORMAL_EXCEPTION_PROLOG mfspr r4,SPRN_ESR /* Grab the ESR and save it */ stw r4,_ESR(r11) addi r3,r1,STACK_FRAME_OVERHEAD EXC_XFER_STD(0x700, program_check_exception) EXCEPTION(0x0800, Trap_08, unknown_exception, EXC_XFER_EE) EXCEPTION(0x0900, Trap_09, unknown_exception, EXC_XFER_EE) EXCEPTION(0x0A00, Trap_0A, unknown_exception, EXC_XFER_EE) EXCEPTION(0x0B00, Trap_0B, unknown_exception, EXC_XFER_EE)/* 0x0C00 - System Call Exception */ START_EXCEPTION(0x0C00, SystemCall) NORMAL_EXCEPTION_PROLOG EXC_XFER_EE_LITE(0xc00, DoSyscall) EXCEPTION(0x0D00, Trap_0D, unknown_exception, EXC_XFER_EE) EXCEPTION(0x0E00, Trap_0E, unknown_exception, EXC_XFER_EE) EXCEPTION(0x0F00, Trap_0F, unknown_exception, EXC_XFER_EE)/* 0x1000 - Programmable Interval Timer (PIT) Exception */ START_EXCEPTION(0x1000, Decrementer) NORMAL_EXCEPTION_PROLOG lis r0,TSR_PIS@h mtspr SPRN_TSR,r0 /* Clear the PIT exception */ addi r3,r1,STACK_FRAME_OVERHEAD EXC_XFER_LITE(0x1000, timer_interrupt)#if 0/* NOTE: * FIT and WDT handlers are not implemented yet. *//* 0x1010 - Fixed Interval Timer (FIT) Exception*/ STND_EXCEPTION(0x1010, FITException, unknown_exception)/* 0x1020 - Watchdog Timer (WDT) Exception*/#ifdef CONFIG_BOOKE_WDT CRITICAL_EXCEPTION(0x1020, WDTException, WatchdogException)#else CRITICAL_EXCEPTION(0x1020, WDTException, unknown_exception)#endif#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 SPRN_SPRG0, r10 /* Save some working registers */ mtspr SPRN_SPRG1, r11#ifdef CONFIG_403GCX stw r12, 0(r0) stw r9, 4(r0) mfcr r11 mfspr r12, SPRN_PID stw r11, 8(r0) stw r12, 12(r0)#else mtspr SPRN_SPRG4, r12 mtspr SPRN_SPRG5, r9 mfcr r11 mfspr r12, SPRN_PID mtspr SPRN_SPRG7, r11 mtspr SPRN_SPRG6, r12#endif mfspr r10, SPRN_DEAR /* Get faulting address */ /* If we are faulting a kernel address, we have to use the * kernel page tables. */ lis r11, TASK_SIZE@h cmplw r10, r11 blt+ 3f lis r11, swapper_pg_dir@h ori r11, r11, swapper_pg_dir@l li r9, 0 mtspr SPRN_PID, r9 /* TLB will have 0 TID */ b 4f /* Get the PGD for the current thread. */3: mfspr r11,SPRN_SPRG3 lwz r11,PGDIR(r11)4: tophys(r11, r11) rlwimi r11, r10, 12, 20, 29 /* Create L1 (pgdir/pmd) address */ lwz r12, 0(r11) /* Get L1 entry */ andi. r9, r12, _PMD_PRESENT /* Check if it points to a PTE page */ beq 2f /* Bail if no table */ rlwimi r12, r10, 22, 20, 29 /* Compute PTE address */ lwz r11, 0(r12) /* Get Linux PTE */ andi. r9, r11, _PAGE_PRESENT beq 5f ori r11, r11, _PAGE_ACCESSED
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -