📄 head_32.s
字号:
/* * PowerPC version * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) * * Rewritten by Cort Dougan (cort@cs.nmt.edu) for PReP * Copyright (C) 1996 Cort Dougan <cort@cs.nmt.edu> * Adapted for Power Macintosh by Paul Mackerras. * Low-level exception handlers and MMU support * rewritten by Paul Mackerras. * Copyright (C) 1996 Paul Mackerras. * MPC8xx modifications Copyright (C) 1997 Dan Malek (dmalek@jlc.net). * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * This file contains the low-level support and setup for the * PowerPC platform, including trap and interrupt dispatch. * (The PPC 8xx embedded CPUs use head_8xx.S instead.) * * 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/reg.h>#include <asm/page.h>#include <asm/mmu.h>#include <asm/pgtable.h>#include <asm/cputable.h>#include <asm/cache.h>#include <asm/thread_info.h>#include <asm/ppc_asm.h>#include <asm/asm-offsets.h>#ifdef CONFIG_APUS#include <asm/amigappc.h>#endif/* 601 only have IBAT; cr0.eq is set on 601 when using this macro */#define LOAD_BAT(n, reg, RA, RB) \ /* see the comment for clear_bats() -- Cort */ \ li RA,0; \ mtspr SPRN_IBAT##n##U,RA; \ mtspr SPRN_DBAT##n##U,RA; \ lwz RA,(n*16)+0(reg); \ lwz RB,(n*16)+4(reg); \ mtspr SPRN_IBAT##n##U,RA; \ mtspr SPRN_IBAT##n##L,RB; \ beq 1f; \ lwz RA,(n*16)+8(reg); \ lwz RB,(n*16)+12(reg); \ mtspr SPRN_DBAT##n##U,RA; \ mtspr SPRN_DBAT##n##L,RB; \1: .text .stabs "arch/powerpc/kernel/",N_SO,0,0,0f .stabs "head_32.S",N_SO,0,0,0f0: .globl _stext_stext:/* * _start is defined this way because the XCOFF loader in the OpenFirmware * on the powermac expects the entry point to be a procedure descriptor. */ .text .globl _start_start: /* * These are here for legacy reasons, the kernel used to * need to look like a coff function entry for the pmac * but we're always started by some kind of bootloader now. * -- Cort */ nop /* used by __secondary_hold on prep (mtx) and chrp smp */ nop /* used by __secondary_hold on prep (mtx) and chrp smp */ nop/* PMAC * Enter here with the kernel text, data and bss loaded starting at * 0, running with virtual == physical mapping. * r5 points to the prom entry point (the client interface handler * address). Address translation is turned on, with the prom * managing the hash table. Interrupts are disabled. The stack * pointer (r1) points to just below the end of the half-meg region * from 0x380000 - 0x400000, which is mapped in already. * * If we are booted from MacOS via BootX, we enter with the kernel * image loaded somewhere, and the following values in registers: * r3: 'BooX' (0x426f6f58) * r4: virtual address of boot_infos_t * r5: 0 * * APUS * r3: 'APUS' * r4: physical address of memory base * Linux/m68k style BootInfo structure at &_end. * * PREP * This is jumped to on prep systems right after the kernel is relocated * to its proper place in memory by the boot loader. The expected layout * of the regs is: * r3: ptr to residual data * r4: initrd_start or if no initrd then 0 * r5: initrd_end - unused if r4 is 0 * r6: Start of command line string * r7: End of command line string * * This just gets a minimal mmu environment setup so we can call * start_here() to do the real work. * -- Cort */ .globl __start__start:/* * We have to do any OF calls before we map ourselves to KERNELBASE, * because OF may have I/O devices mapped into that area * (particularly on CHRP). */ cmpwi 0,r5,0 beq 1f bl prom_init trap1: mr r31,r3 /* save parameters */ mr r30,r4 li r24,0 /* cpu # *//* * early_init() does the early machine identification and does * the necessary low-level setup and clears the BSS * -- Cort <cort@fsmlabs.com> */ bl early_init#ifdef CONFIG_APUS/* On APUS the __va/__pa constants need to be set to the correct * values before continuing. */ mr r4,r30 bl fix_mem_constants#endif /* CONFIG_APUS *//* Switch MMU off, clear BATs and flush TLB. At this point, r3 contains * the physical address we are running at, returned by early_init() */ bl mmu_off__after_mmu_off: bl clear_bats bl flush_tlbs bl initial_bats/* * Call setup_cpu for CPU 0 and initialize 6xx Idle */ bl reloc_offset li r24,0 /* cpu# */ bl call_setup_cpu /* Call setup_cpu for this CPU */#ifdef CONFIG_6xx bl reloc_offset bl init_idle_6xx#endif /* CONFIG_6xx */#ifndef CONFIG_APUS/* * We need to run with _start at physical address 0. * On CHRP, we are loaded at 0x10000 since OF on CHRP uses * the exception vectors at 0 (and therefore this copy * overwrites OF's exception vectors with our own). * The MMU is off at this point. */ bl reloc_offset mr r26,r3 addis r4,r3,KERNELBASE@h /* current address of _start */ cmpwi 0,r4,0 /* are we already running at 0? */ bne relocate_kernel#endif /* CONFIG_APUS *//* * we now have the 1st 16M of ram mapped with the bats. * prep needs the mmu to be turned on here, but pmac already has it on. * this shouldn't bother the pmac since it just gets turned on again * as we jump to our code at KERNELBASE. -- Cort * Actually no, pmac doesn't have it on any more. BootX enters with MMU * off, and in other cases, we now turn it off before changing BATs above. */turn_on_mmu: mfmsr r0 ori r0,r0,MSR_DR|MSR_IR mtspr SPRN_SRR1,r0 lis r0,start_here@h ori r0,r0,start_here@l mtspr SPRN_SRR0,r0 SYNC RFI /* enables MMU *//* * We need __secondary_hold as a place to hold the other cpus on * an SMP machine, even when we are running a UP kernel. */ . = 0xc0 /* for prep bootloader */ li r3,1 /* MTX only has 1 cpu */ .globl __secondary_hold__secondary_hold: /* tell the master we're here */ stw r3,__secondary_hold_acknowledge@l(0)#ifdef CONFIG_SMP100: lwz r4,0(0) /* wait until we're told to start */ cmpw 0,r4,r3 bne 100b /* our cpu # was at addr 0 - go */ mr r24,r3 /* cpu # */ b __secondary_start#else b .#endif /* CONFIG_SMP */ .globl __secondary_hold_spinloop__secondary_hold_spinloop: .long 0 .globl __secondary_hold_acknowledge__secondary_hold_acknowledge: .long -1/* * Exception 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's thread_struct. */#define EXCEPTION_PROLOG \ mtspr SPRN_SPRG0,r10; \ mtspr SPRN_SPRG1,r11; \ mfcr r10; \ EXCEPTION_PROLOG_1; \ EXCEPTION_PROLOG_2#define EXCEPTION_PROLOG_1 \ mfspr r11,SPRN_SRR1; /* check whether user or kernel */ \ andi. r11,r11,MSR_PR; \ tophys(r11,r1); /* use tophys(r1) if kernel */ \ beq 1f; \ mfspr r11,SPRN_SPRG3; \ lwz r11,THREAD_INFO-THREAD(r11); \ addi r11,r11,THREAD_SIZE; \ tophys(r11,r11); \1: subi r11,r11,INT_FRAME_SIZE /* alloc exc. frame */#define EXCEPTION_PROLOG_2 \ CLR_TOP32(r11); \ stw r10,_CCR(r11); /* save 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 r12,SPRN_SRR0; \ mfspr r9,SPRN_SRR1; \ stw r1,GPR1(r11); \ stw r1,0(r11); \ tovirt(r1,r11); /* set new kernel sp */ \ li r10,MSR_KERNEL & ~(MSR_IR|MSR_DR); /* can take exceptions */ \ MTMSRD(r10); /* (except for mach check in rtas) */ \ stw r0,GPR0(r11); \ lis r10,0x7265; /* put exception frame marker */ \ addi r10,r10,0x6773; \ stw r10,8(r11); \ SAVE_4GPRS(3, r11); \ SAVE_2GPRS(7, r11)/* * Note: code which follows this uses cr0.eq (set if from kernel), * r11, r12 (SRR0), and r9 (SRR1). * * Note2: once we have set r1 we are in a position to take exceptions * again, and we could thus set MSR:RI at that point. *//* * Exception vectors. */#define EXCEPTION(n, label, hdlr, xfer) \ . = n; \label: \ EXCEPTION_PROLOG; \ addi r3,r1,STACK_FRAME_OVERHEAD; \ xfer(n, hdlr)#define EXC_XFER_TEMPLATE(n, hdlr, trap, copyee, tfer, ret) \ li r10,trap; \ stw r10,_TRAP(r11); \ li r10,MSR_KERNEL; \ copyee(r10, r9); \ bl tfer; \i##n: \ .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(n, hdlr, n, NOCOPY, transfer_to_handler_full, \ ret_from_except_full)#define EXC_XFER_LITE(n, hdlr) \ EXC_XFER_TEMPLATE(n, hdlr, n+1, NOCOPY, transfer_to_handler, \ ret_from_except)#define EXC_XFER_EE(n, hdlr) \ EXC_XFER_TEMPLATE(n, hdlr, n, COPY_EE, transfer_to_handler_full, \ ret_from_except_full)#define EXC_XFER_EE_LITE(n, hdlr) \ EXC_XFER_TEMPLATE(n, hdlr, n+1, COPY_EE, transfer_to_handler, \ ret_from_except)/* System reset *//* core99 pmac starts the seconary here by changing the vector, and putting it back to what it was (unknown_exception) when done. */#if defined(CONFIG_GEMINI) && defined(CONFIG_SMP) . = 0x100 b __secondary_start_gemini#else EXCEPTION(0x100, Reset, unknown_exception, EXC_XFER_STD)#endif/* Machine check *//* * On CHRP, this is complicated by the fact that we could get a * machine check inside RTAS, and we have no guarantee that certain * critical registers will have the values we expect. The set of * registers that might have bad values includes all the GPRs * and all the BATs. We indicate that we are in RTAS by putting * a non-zero value, the address of the exception frame to use, * in SPRG2. The machine check handler checks SPRG2 and uses its * value if it is non-zero. If we ever needed to free up SPRG2, * we could use a field in the thread_info or thread_struct instead. * (Other exception handlers assume that r1 is a valid kernel stack * pointer when we take an exception from supervisor mode.) * -- paulus. */ . = 0x200 mtspr SPRN_SPRG0,r10 mtspr SPRN_SPRG1,r11 mfcr r10#ifdef CONFIG_PPC_CHRP mfspr r11,SPRN_SPRG2 cmpwi 0,r11,0 bne 7f#endif /* CONFIG_PPC_CHRP */ EXCEPTION_PROLOG_17: EXCEPTION_PROLOG_2 addi r3,r1,STACK_FRAME_OVERHEAD#ifdef CONFIG_PPC_CHRP mfspr r4,SPRN_SPRG2 cmpwi cr1,r4,0 bne cr1,1f#endif EXC_XFER_STD(0x200, machine_check_exception)#ifdef CONFIG_PPC_CHRP1: b machine_check_in_rtas#endif/* Data access exception. */ . = 0x300DataAccess: EXCEPTION_PROLOG mfspr r10,SPRN_DSISR andis. r0,r10,0xa470 /* weird error? */ bne 1f /* if not, try to put a PTE */ mfspr r4,SPRN_DAR /* into the hash table */ rlwinm r3,r10,32-15,21,21 /* DSISR_STORE -> _PAGE_RW */ bl hash_page1: stw r10,_DSISR(r11) mr r5,r10 mfspr r4,SPRN_DAR EXC_XFER_EE_LITE(0x300, handle_page_fault)/* Instruction access exception. */ . = 0x400InstructionAccess: EXCEPTION_PROLOG andis. r0,r9,0x4000 /* no pte found? */ beq 1f /* if so, try to put a PTE */ li r3,0 /* into the hash table */ mr r4,r12 /* SRR0 is fault address */ bl hash_page1: mr r4,r12 mr r5,r9 EXC_XFER_EE_LITE(0x400, handle_page_fault)/* External interrupt */ EXCEPTION(0x500, HardwareInterrupt, do_IRQ, EXC_XFER_LITE)/* Alignment exception */ . = 0x600Alignment: EXCEPTION_PROLOG mfspr r4,SPRN_DAR stw r4,_DAR(r11) mfspr r5,SPRN_DSISR stw r5,_DSISR(r11) addi r3,r1,STACK_FRAME_OVERHEAD EXC_XFER_EE(0x600, alignment_exception)/* Program check exception */ EXCEPTION(0x700, ProgramCheck, program_check_exception, EXC_XFER_STD)/* Floating-point unavailable */ . = 0x800FPUnavailable: EXCEPTION_PROLOG bne load_up_fpu /* if from user, just load it up */ addi r3,r1,STACK_FRAME_OVERHEAD EXC_XFER_EE_LITE(0x800, kernel_fp_unavailable_exception)/* Decrementer */ EXCEPTION(0x900, Decrementer, timer_interrupt, EXC_XFER_LITE) EXCEPTION(0xa00, Trap_0a, unknown_exception, EXC_XFER_EE) EXCEPTION(0xb00, Trap_0b, unknown_exception, EXC_XFER_EE)/* System call */ . = 0xc00SystemCall: EXCEPTION_PROLOG EXC_XFER_EE_LITE(0xc00, DoSyscall)/* Single step - not used on 601 */ EXCEPTION(0xd00, SingleStep, single_step_exception, EXC_XFER_STD) EXCEPTION(0xe00, Trap_0e, unknown_exception, EXC_XFER_EE)/* * The Altivec unavailable trap is at 0x0f20. Foo. * We effectively remap it to 0x3000. * We include an altivec unavailable exception vector even if * not configured for Altivec, so that you can't panic a * non-altivec kernel running on a machine with altivec just * by executing an altivec instruction. */ . = 0xf00 b Trap_0f . = 0xf20 b AltiVecUnavailableTrap_0f: EXCEPTION_PROLOG addi r3,r1,STACK_FRAME_OVERHEAD EXC_XFER_EE(0xf00, unknown_exception)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -