/* * 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 * 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 #include #include #include #include #include #include #include #include #include "ppc_defs.h" #ifdef CONFIG_APUS #include #endif #ifdef CONFIG_PPC64BRIDGE #define LOAD_BAT(n, reg, RA, RB) \ ld RA,(n*32)+0(reg); \ ld RB,(n*32)+8(reg); \ mtspr IBAT##n##U,RA; \ mtspr IBAT##n##L,RB; \ ld RA,(n*32)+16(reg); \ ld RB,(n*32)+24(reg); \ mtspr DBAT##n##U,RA; \ mtspr DBAT##n##L,RB; \ #else /* CONFIG_PPC64BRIDGE */ /* 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 IBAT##n##U,RA; \ mtspr DBAT##n##U,RA; \ lwz RA,(n*16)+0(reg); \ lwz RB,(n*16)+4(reg); \ mtspr IBAT##n##U,RA; \ mtspr IBAT##n##L,RB; \ beq 1f; \ lwz RA,(n*16)+8(reg); \ lwz RB,(n*16)+12(reg); \ mtspr DBAT##n##U,RA; \ mtspr DBAT##n##L,RB; \ 1: #endif /* CONFIG_PPC64BRIDGE */ .text .stabs "arch/ppc/kernel/",N_SO,0,0,0f .stabs "head.S",N_SO,0,0,0f 0: .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 nop 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). */ mr r31,r3 /* save parameters */ mr r30,r4 mr r29,r5 mr r28,r6 mr r27,r7 li r24,0 /* cpu # */ #ifdef CONFIG_POWER4 /* * On the PPC970, we have to turn off real-mode cache inhibit * early, before we first turn the MMU off. */ mfspr r0,SPRN_PVR srwi r0,r0,16 cmpwi r0,0x39 beql ppc970_setup_hid #endif /* * early_init() does the early machine identification and does * the necessary low-level setup and clears the BSS * -- Cort */ 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: #ifndef CONFIG_POWER4 bl clear_bats bl flush_tlbs bl initial_bats #if !defined(CONFIG_APUS) && defined(CONFIG_BOOTX_TEXT) bl setup_disp_bat #endif #else /* CONFIG_POWER4 */ /* * Load up the SDR1 and segment register values now * since we don't have the BATs. * Also make sure we are running in 32-bit mode. */ bl reloc_offset addis r14,r3,_SDR1@ha /* get the value from _SDR1 */ lwz r14,_SDR1@l(r14) /* assume hash table below 4GB */ mtspr SDR1,r14 slbia lis r4,0x2000 /* set pseudo-segment reg 12 */ ori r5,r4,0x0ccc mtsr 12,r5 ori r4,r4,0x0888 /* set pseudo-segment reg 8 */ mtsr 8,r4 /* (for access to serial port) */ mfmsr r0 clrldi r0,r0,1 sync mtmsr r0 isync #endif /* CONFIG_POWER4 */ /* * Call setup_cpu for CPU 0 */ 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 SRR1,r0 lis r0,start_here@h ori r0,r0,start_here@l mtspr 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,4(0) #ifdef CONFIG_SMP 100: 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 */ /* * 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 SPRG0,r20; \ mtspr SPRG1,r21; \ mfcr r20; \ mfspr r21,SPRG2; /* exception stack to use from */ \ cmpwi 0,r21,0; /* user mode or RTAS */ \ bne 1f; \ tophys(r21,r1); /* use tophys(kernel sp) otherwise */ \ subi r21,r21,INT_FRAME_SIZE; /* alloc exc. frame */\ 1: CLR_TOP32(r21); \ stw r20,_CCR(r21); /* save registers */ \ stw r22,GPR22(r21); \ stw r23,GPR23(r21); \ mfspr r20,SPRG0; \ stw r20,GPR20(r21); \ mfspr r22,SPRG1; \ stw r22,GPR21(r21); \ mflr r20; \ stw r20,_LINK(r21); \ mfctr r22; \ stw r22,_CTR(r21); \ mfspr r20,XER; \ stw r20,_XER(r21); \ mfspr r22,SRR0; \ mfspr r23,SRR1; \ stw r0,GPR0(r21); \ stw r1,GPR1(r21); \ stw r2,GPR2(r21); \ stw r1,0(r21); \ tovirt(r1,r21); /* set new kernel sp */ \ SAVE_4GPRS(3, r21); \ SAVE_GPR(7, r21); /* * Note: code which follows this uses cr0.eq (set if from kernel), * r21, r22 (SRR0), and r23 (SRR1). */ /* * Exception vectors. */ #define STD_EXCEPTION(n, label, hdlr) \ . = n; \ label: \ EXCEPTION_PROLOG; \ addi r3,r1,STACK_FRAME_OVERHEAD; \ li r20,MSR_KERNEL; \ bl transfer_to_handler; \ i##n: \ .long hdlr; \ .long ret_from_except /* System reset */ #ifdef CONFIG_SMP /* MVME/MTX and gemini start the secondary here */ #ifdef CONFIG_GEMINI . = 0x100 b __secondary_start_gemini #else /* CONFIG_GEMINI */ STD_EXCEPTION(0x100, Reset, __secondary_start_psurge) #endif /* CONFIG_GEMINI */ #else STD_EXCEPTION(0x100, Reset, UnknownException) #endif /* Machine check */ BEGIN_FTR_SECTION DSSALL sync END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) STD_EXCEPTION(0x200, MachineCheck, MachineCheckException) /* Data access exception. */ . = 0x300 #ifdef CONFIG_PPC64BRIDGE b DataAccess DataAccessCont: #else DataAccess: EXCEPTION_PROLOG #endif /* CONFIG_PPC64BRIDGE */ mfspr r20,DSISR BEGIN_FTR_SECTION andis. r0,r20,0xa470 /* weird error? */ bne 1f /* if not, try to put a PTE */ mfspr r4,DAR /* into the hash table */ rlwinm r3,r20,32-15,21,21 /* DSISR_STORE -> _PAGE_RW */ bl hash_page END_FTR_SECTION_IFSET(CPU_FTR_HPTE_TABLE) 1: stw r20,_DSISR(r21) mr r5,r20 mfspr r4,DAR stw r4,_DAR(r21) addi r3,r1,STACK_FRAME_OVERHEAD li r20,MSR_KERNEL rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ bl transfer_to_handler i0x300: .long do_page_fault .long ret_from_except #ifdef CONFIG_PPC64BRIDGE /* SLB fault on data access. */ . = 0x380 b DataSegment DataSegmentCont: mfspr r4,DAR stw r4,_DAR(r21) addi r3,r1,STACK_FRAME_OVERHEAD li r20,MSR_KERNEL rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ bl transfer_to_handler .long UnknownException .long ret_from_except #endif /* CONFIG_PPC64BRIDGE */ /* Instruction access exception. */ . = 0x400 #ifdef CONFIG_PPC64BRIDGE b InstructionAccess InstructionAccessCont: #else InstructionAccess: EXCEPTION_PROLOG #endif /* CONFIG_PPC64BRIDGE */ BEGIN_FTR_SECTION andis. r0,r23,0x4000 /* no pte found? */ beq 1f /* if so, try to put a PTE */ li r3,0 /* into the hash table */ mr r4,r22 /* SRR0 is fault address */ bl hash_page END_FTR_SECTION_IFSET(CPU_FTR_HPTE_TABLE) 1: addi r3,r1,STACK_FRAME_OVERHEAD mr r4,r22 mr r5,r23 li r20,MSR_KERNEL rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ bl transfer_to_handler i0x400: .long do_page_fault .long ret_from_except #ifdef CONFIG_PPC64BRIDGE /* SLB fault on instruction access. */ . = 0x480 b InstructionSegment InstructionSegmentCont: addi r3,r1,STACK_FRAME_OVERHEAD li r20,MSR_KERNEL rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ bl transfer_to_handler .long UnknownException .long ret_from_except #endif /* CONFIG_PPC64BRIDGE */ /* External interrupt */ . = 0x500; HardwareInterrupt: EXCEPTION_PROLOG; addi r3,r1,STACK_FRAME_OVERHEAD li r20,MSR_KERNEL li r4,0 bl transfer_to_handler .globl do_IRQ_intercept do_IRQ_intercept: .long do_IRQ; .long ret_from_intercept /* Alignment exception */ . = 0x600 Alignment: EXCEPTION_PROLOG mfspr r4,DAR stw r4,_DAR(r21) mfspr r5,DSISR stw r5,_DSISR(r21) addi r3,r1,STACK_FRAME_OVERHEAD li r20,MSR_KERNEL rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ bl transfer_to_handler i0x600: .long AlignmentException .long ret_from_except /* Program check exception */ . = 0x700 ProgramCheck: EXCEPTION_PROLOG addi r3,r1,STACK_FRAME_OVERHEAD li r20,MSR_KERNEL rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ bl transfer_to_handler i0x700: .long ProgramCheckException .long ret_from_except /* Floating-point unavailable */ . = 0x800 FPUnavailable: EXCEPTION_PROLOG bne load_up_fpu /* if from user, just load it up */ li r20,MSR_KERNEL bl transfer_to_handler /* if from kernel, take a trap */ i0x800: .long KernelFP .long ret_from_except . = 0x900 Decrementer: EXCEPTION_PROLOG addi r3,r1,STACK_FRAME_OVERHEAD li r20,MSR_KERNEL bl transfer_to_handler .globl timer_interrupt_intercept timer_interrupt_intercept: .long timer_interrupt .long ret_from_intercept STD_EXCEPTION(0xa00, Trap_0a, UnknownException) STD_EXCEPTION(0xb00, Trap_0b, UnknownException) /* System call */ . = 0xc00 SystemCall: EXCEPTION_PROLOG stw r3,ORIG_GPR3(r21) li r20,MSR_KERNEL rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ bl transfer_to_handler .long DoSyscall .long ret_from_except /* Single step - not used on 601 */ STD_EXCEPTION(0xd00, SingleStep, SingleStepException) STD_EXCEPTION(0xe00, Trap_0e, UnknownException) /* * The Altivec unavailable trap is at 0x0f20. Foo. * We effectively remap it to 0x3000. */ . = 0xf00 b Trap_0f trap_0f_cont: addi r3,r1,STACK_FRAME_OVERHEAD li r20,MSR_KERNEL bl transfer_to_handler .long UnknownException .long ret_from_except . = 0xf20 #ifdef CONFIG_ALTIVEC b AltiVecUnavailable #endif Trap_0f: EXCEPTION_PROLOG b trap_0f_cont /* * Handle TLB miss for instruction on 603/603e. * Note: we get an alternate set of r0 - r3 to use automatically. */ . = 0x1000 InstructionTLBMiss: /* * r0: stored ctr * r1: linux style pte ( later becomes ppc hardware pte ) * r2: ptr to linux-style pte * r3: scratch */ mfctr r0 /* Get PTE (linux-style) and check access */ mfspr r3,IMISS lis r1,KERNELBASE@h /* check if kernel address */ cmplw 0,r3,r1 mfspr r2,SPRG3 li r1,_PAGE_USER|_PAGE_PRESENT /* low addresses tested as user */ lwz r2,PGDIR(r2) blt+ 112f lis r2,swapper_pg_dir@ha /* if kernel address, use */ addi r2,r2,swapper_pg_dir@l /* kernel page table */ mfspr r1,SRR1 /* and MSR_PR bit from SRR1 */ rlwinm r1,r1,32-12,29,29 /* shift MSR_PR to _PAGE_USER posn */ 112: tophys(r2,r2) rlwimi r2,r3,12,20,29 /* insert top 10 bits of address */ lwz r2,0(r2) /* get pmd entry */ rlwinm. r2,r2,0,0,19 /* extract address of pte page */ beq- InstructionAddressInvalid /* return if no mapping */ tophys(r2,r2) rlwimi r2,r3,22,20,29 /* insert next 10 bits of address */ lwz r3,0(r2) /* get linux-style pte */ andc. r1,r1,r3 /* check access & ~permission */ bne- InstructionAddressInvalid /* return if access not permitted */ ori r3,r3,_PAGE_ACCESSED /* set _PAGE_ACCESSED in pte */ /* * NOTE! We are assuming this is not an SMP system, otherwise * we would need to update the pte atomically with lwarx/stwcx. */ stw r3,0(r2) /* update PTE (accessed bit) */ /* Convert linux-style PTE to low word of PPC-style PTE */ rlwinm r1,r3,32-10,31,31 /* _PAGE_RW -> PP lsb */ rlwinm r2,r3,32-7,31,31 /* _PAGE_DIRTY -> PP lsb */ and r1,r1,r2 /* writable if _RW and _DIRTY */ rlwimi r3,r3,32-1,30,30 /* _PAGE_USER -> PP msb */ rlwimi r3,r3,32-1,31,31 /* _PAGE_USER -> PP lsb */ ori r1,r1,0xe14 /* clear out reserved bits and M */ andc r1,r3,r1 /* PP = user? (rw&dirty? 2: 3): 0 */ mtspr RPA,r1 mfspr r3,IMISS tlbli r3 mfspr r3,SRR1 /* Need to restore CR0 */ mtcrf 0x80,r3 rfi InstructionAddressInvalid: mfspr r3,SRR1 rlwinm r1,r3,9,6,6 /* Get load/store bit */ addis r1,r1,0x2000 mtspr DSISR,r1 /* (shouldn't be needed) */ mtctr r0 /* Restore CTR */ andi. r2,r3,0xFFFF /* Clear upper bits of SRR1 */ or r2,r2,r1 mtspr SRR1,r2 mfspr r1,IMISS /* Get failing address */ rlwinm. r2,r2,0,31,31 /* Check for little endian access */ rlwimi r2,r2,1,30,30 /* change 1 -> 3 */ xor r1,r1,r2 mtspr DAR,r1 /* Set fault address */ mfmsr r0 /* Restore "normal" registers */ xoris r0,r0,MSR_TGPR>>16 mtcrf 0x80,r3 /* Restore CR0 */ mtmsr r0 b InstructionAccess /* * Handle TLB miss for DATA Load operation on 603/603e */ . = 0x1100 DataLoadTLBMiss: /* * r0: stored ctr * r1: linux style pte ( later becomes ppc hardware pte ) * r2: ptr to linux-style pte * r3: scratch */ mfctr r0 /* Get PTE (linux-style) and check access */ mfspr r3,DMISS lis r1,KERNELBASE@h /* check if kernel address */ cmplw 0,r3,r1 mfspr r2,SPRG3 li r1,_PAGE_USER|_PAGE_PRESENT /* low addresses tested as user */ lwz r2,PGDIR(r2) blt+ 112f lis r2,swapper_pg_dir@ha /* if kernel address, use */ addi r2,r2,swapper_pg_dir@l /* kernel page table */ mfspr r1,SRR1 /* and MSR_PR bit from SRR1 */ rlwinm r1,r1,32-12,29,29 /* shift MSR_PR to _PAGE_USER posn */ 112: tophys(r2,r2) rlwimi r2,r3,12,20,29 /* insert top 10 bits of address */ lwz r2,0(r2) /* get pmd entry */ rlwinm. r2,r2,0,0,19 /* extract address of pte page */ beq- DataAddressInvalid /* return if no mapping */ tophys(r2,r2) rlwimi r2,r3,22,20,29 /* insert next 10 bits of address */ lwz r3,0(r2) /* get linux-style pte */ andc. r1,r1,r3 /* check access & ~permission */ bne- DataAddressInvalid /* return if access not permitted */ ori r3,r3,_PAGE_ACCESSED /* set _PAGE_ACCESSED in pte */ /* * NOTE! We are assuming this is not an SMP system, otherwise * we would need to update the pte atomically with lwarx/stwcx. */ stw r3,0(r2) /* update PTE (accessed bit) */ /* Convert linux-style PTE to low word of PPC-style PTE */ rlwinm r1,r3,32-10,31,31 /* _PAGE_RW -> PP lsb */ rlwinm r2,r3,32-7,31,31 /* _PAGE_DIRTY -> PP lsb */ and r1,r1,r2 /* writable if _RW and _DIRTY */ rlwimi r3,r3,32-1,30,30 /* _PAGE_USER -> PP msb */ rlwimi r3,r3,32-1,31,31 /* _PAGE_USER -> PP lsb */ ori r1,r1,0xe14 /* clear out reserved bits and M */ andc r1,r3,r1 /* PP = user? (rw&dirty? 2: 3): 0 */ mtspr RPA,r1 mfspr r3,DMISS tlbld r3 mfspr r3,SRR1 /* Need to restore CR0 */ mtcrf 0x80,r3 rfi DataAddressInvalid: mfspr r3,SRR1 rlwinm r1,r3,9,6,6 /* Get load/store bit */ addis r1,r1,0x2000 mtspr DSISR,r1 mtctr r0 /* Restore CTR */ andi. r2,r3,0xFFFF /* Clear upper bits of SRR1 */ mtspr SRR1,r2 mfspr r1,DMISS /* Get failing address */ rlwinm. r2,r2,0,31,31 /* Check for little endian access */ beq 20f /* Jump if big endian */ xori r1,r1,3 20: mtspr DAR,r1 /* Set fault address */ mfmsr r0 /* Restore "normal" registers */ xoris r0,r0,MSR_TGPR>>16 mtcrf 0x80,r3 /* Restore CR0 */ mtmsr r0 b DataAccess /* * Handle TLB miss for DATA Store on 603/603e */ . = 0x1200 DataStoreTLBMiss: /* * r0: stored ctr * r1: linux style pte ( later becomes ppc hardware pte ) * r2: ptr to linux-style pte * r3: scratch */ mfctr r0 /* Get PTE (linux-style) and check access */ mfspr r3,DMISS lis r1,KERNELBASE@h /* check if kernel address */ cmplw 0,r3,r1 mfspr r2,SPRG3 li r1,_PAGE_RW|_PAGE_USER|_PAGE_PRESENT /* access flags */ lwz r2,PGDIR(r2) blt+ 112f lis r2,swapper_pg_dir@ha /* if kernel address, use */ addi r2,r2,swapper_pg_dir@l /* kernel page table */ mfspr r1,SRR1 /* and MSR_PR bit from SRR1 */ rlwinm r1,r1,32-12,29,29 /* shift MSR_PR to _PAGE_USER posn */ 112: tophys(r2,r2) rlwimi r2,r3,12,20,29 /* insert top 10 bits of address */ lwz r2,0(r2) /* get pmd entry */ rlwinm. r2,r2,0,0,19 /* extract address of pte page */ beq- DataAddressInvalid /* return if no mapping */ tophys(r2,r2) rlwimi r2,r3,22,20,29 /* insert next 10 bits of address */ lwz r3,0(r2) /* get linux-style pte */ andc. r1,r1,r3 /* check access & ~permission */ bne- DataAddressInvalid /* return if access not permitted */ ori r3,r3,_PAGE_ACCESSED|_PAGE_DIRTY /* * NOTE! We are assuming this is not an SMP system, otherwise * we would need to update the pte atomically with lwarx/stwcx. */ stw r3,0(r2) /* update PTE (accessed/dirty bits) */ /* Convert linux-style PTE to low word of PPC-style PTE */ rlwimi r3,r3,32-1,30,30 /* _PAGE_USER -> PP msb */ li r1,0xe15 /* clear out reserved bits and M */ andc r1,r3,r1 /* PP = user? 2: 0 */ mtspr RPA,r1 mfspr r3,DMISS tlbld r3 mfspr r3,SRR1 /* Need to restore CR0 */ mtcrf 0x80,r3 rfi #ifndef CONFIG_ALTIVEC #define AltivecAssistException UnknownException #endif STD_EXCEPTION(0x1300, Trap_13, InstructionBreakpoint) STD_EXCEPTION(0x1400, SMI, SMIException) STD_EXCEPTION(0x1500, Trap_15, UnknownException) #ifdef CONFIG_POWER4 STD_EXCEPTION(0x1600, Trap_16, UnknownException) STD_EXCEPTION(0x1700, Trap_17, AltivecAssistException) STD_EXCEPTION(0x1800, Trap_18, TAUException) #else /* !CONFIG_POWER4 */ STD_EXCEPTION(0x1600, Trap_16, AltivecAssistException) STD_EXCEPTION(0x1700, Trap_17, TAUException) STD_EXCEPTION(0x1800, Trap_18, UnknownException) #endif STD_EXCEPTION(0x1900, Trap_19, UnknownException) STD_EXCEPTION(0x1a00, Trap_1a, UnknownException) STD_EXCEPTION(0x1b00, Trap_1b, UnknownException) STD_EXCEPTION(0x1c00, Trap_1c, UnknownException) STD_EXCEPTION(0x1d00, Trap_1d, UnknownException) STD_EXCEPTION(0x1e00, Trap_1e, UnknownException) STD_EXCEPTION(0x1f00, Trap_1f, UnknownException) STD_EXCEPTION(0x2000, RunMode, RunModeException) STD_EXCEPTION(0x2100, Trap_21, UnknownException) STD_EXCEPTION(0x2200, Trap_22, UnknownException) STD_EXCEPTION(0x2300, Trap_23, UnknownException) STD_EXCEPTION(0x2400, Trap_24, UnknownException) STD_EXCEPTION(0x2500, Trap_25, UnknownException) STD_EXCEPTION(0x2600, Trap_26, UnknownException) STD_EXCEPTION(0x2700, Trap_27, UnknownException) STD_EXCEPTION(0x2800, Trap_28, UnknownException) STD_EXCEPTION(0x2900, Trap_29, UnknownException) STD_EXCEPTION(0x2a00, Trap_2a, UnknownException) STD_EXCEPTION(0x2b00, Trap_2b, UnknownException) STD_EXCEPTION(0x2c00, Trap_2c, UnknownException) STD_EXCEPTION(0x2d00, Trap_2d, UnknownException) STD_EXCEPTION(0x2e00, Trap_2e, UnknownException) STD_EXCEPTION(0x2f00, Trap_2f, UnknownException) . = 0x3000 #ifdef CONFIG_ALTIVEC AltiVecUnavailable: EXCEPTION_PROLOG bne load_up_altivec /* if from user, just load it up */ li r20,MSR_KERNEL bl transfer_to_handler /* if from kernel, take a trap */ .long KernelAltiVec .long ret_from_except #endif /* CONFIG_ALTIVEC */ #ifdef CONFIG_PPC64BRIDGE DataAccess: EXCEPTION_PROLOG b DataAccessCont InstructionAccess: EXCEPTION_PROLOG b InstructionAccessCont DataSegment: EXCEPTION_PROLOG b DataSegmentCont InstructionSegment: EXCEPTION_PROLOG b InstructionSegmentCont #endif /* CONFIG_PPC64BRIDGE */ /* * This code finishes saving the registers to the exception frame * and jumps to the appropriate handler for the exception, turning * on address translation. */ .globl transfer_to_handler transfer_to_handler: stw r22,_NIP(r21) stw r23,_MSR(r21) SAVE_4GPRS(8, r21) SAVE_8GPRS(12, r21) SAVE_8GPRS(24, r21) andi. r23,r23,MSR_PR mfspr r23,SPRG3 /* if from user, fix up THREAD.regs */ addi r2,r23,-THREAD /* set r2 to current */ beq 2f addi r24,r1,STACK_FRAME_OVERHEAD stw r24,PT_REGS(r23) #ifdef CONFIG_ALTIVEC BEGIN_FTR_SECTION mfspr r22,SPRN_VRSAVE /* if G4, save vrsave register value */ stw r22,THREAD_VRSAVE(r23) END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) #endif /* CONFIG_ALTIVEC */ #ifndef CONFIG_6xx 2: #endif .globl transfer_to_handler_cont transfer_to_handler_cont: tovirt(r2,r2) mflr r23 andi. r24,r23,0x3f00 /* get vector offset */ stw r24,TRAP(r21) li r22,0 stw r22,RESULT(r21) mtspr SPRG2,r22 /* r1 is now kernel sp */ addi r24,r2,TASK_STRUCT_SIZE /* check for kernel stack overflow */ cmplw 0,r1,r2 cmplw 1,r1,r24 crand 1,1,4 bgt- stack_ovf /* if r2 < r1 < r2+TASK_STRUCT_SIZE */ lwz r24,0(r23) /* virtual address of handler */ lwz r23,4(r23) /* where to go when done */ FIX_SRR1(r20,r22) mtspr SRR0,r24 mtspr SRR1,r20 mtlr r23 SYNC RFI /* jump to handler, enable MMU */ #ifdef CONFIG_6xx 2: /* Out of line case when returning to kernel, * check return from power_save_6xx */ mfspr r24,SPRN_HID0 mtcr r24 BEGIN_FTR_SECTION bt- 8,power_save_6xx_restore /* Check DOZE */ END_FTR_SECTION_IFSET(CPU_FTR_CAN_DOZE) BEGIN_FTR_SECTION bt- 9,power_save_6xx_restore /* Check NAP */ END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP) b transfer_to_handler_cont #endif /* CONFIG_6xx */ /* * On kernel stack overflow, load up an initial stack pointer * and call StackOverflow(regs), which should not return. */ stack_ovf: addi r3,r1,STACK_FRAME_OVERHEAD lis r1,init_task_union@ha addi r1,r1,init_task_union@l addi r1,r1,TASK_UNION_SIZE-STACK_FRAME_OVERHEAD lis r24,StackOverflow@ha addi r24,r24,StackOverflow@l li r20,MSR_KERNEL FIX_SRR1(r20,r22) mtspr SRR0,r24 mtspr SRR1,r20 SYNC RFI /* * This task wants to use the FPU now. * On UP, disable FP for the task which had the FPU previously, * and save its floating-point registers in its thread_struct. * Load up this task's FP registers from its thread_struct, * enable the FPU for the current task and return to the task. */ load_up_fpu: mfmsr r5 ori r5,r5,MSR_FP #ifdef CONFIG_PPC64BRIDGE clrldi r5,r5,1 /* turn off 64-bit mode */ #endif /* CONFIG_PPC64BRIDGE */ SYNC MTMSRD(r5) /* enable use of fpu now */ isync /* * For SMP, we don't do lazy FPU switching because it just gets too * horrendously complex, especially when a task switches from one CPU * to another. Instead we call giveup_fpu in switch_to. */ #ifndef CONFIG_SMP tophys(r6,0) /* get __pa constant */ addis r3,r6,last_task_used_math@ha lwz r4,last_task_used_math@l(r3) cmpi 0,r4,0 beq 1f add r4,r4,r6 addi r4,r4,THREAD /* want last_task_used_math->thread */ SAVE_32FPRS(0, r4) mffs fr0 stfd fr0,THREAD_FPSCR-4(r4) lwz r5,PT_REGS(r4) add r5,r5,r6 lwz r4,_MSR-STACK_FRAME_OVERHEAD(r5) li r20,MSR_FP|MSR_FE0|MSR_FE1 andc r4,r4,r20 /* disable FP for previous task */ stw r4,_MSR-STACK_FRAME_OVERHEAD(r5) 1: #endif /* CONFIG_SMP */ /* enable use of FP after return */ mfspr r5,SPRG3 /* current task's THREAD (phys) */ lwz r4,THREAD_FPEXC_MODE(r5) ori r23,r23,MSR_FP /* enable FP for current */ or r23,r23,r4 lfd fr0,THREAD_FPSCR-4(r5) mtfsf 0xff,fr0 REST_32FPRS(0, r5) #ifndef CONFIG_SMP subi r4,r5,THREAD sub r4,r4,r6 stw r4,last_task_used_math@l(r3) #endif /* CONFIG_SMP */ /* restore registers and return */ lwz r3,_CCR(r21) lwz r4,_LINK(r21) mtcrf 0xff,r3 mtlr r4 REST_GPR(1, r21) REST_4GPRS(3, r21) /* we haven't used ctr or xer */ mtspr SRR1,r23 mtspr SRR0,r22 REST_GPR(20, r21) REST_2GPRS(22, r21) lwz r21,GPR21(r21) SYNC RFI /* * FP unavailable trap from kernel - print a message, but let * the task use FP in the kernel until it returns to user mode. */ KernelFP: lwz r3,_MSR(r1) ori r3,r3,MSR_FP stw r3,_MSR(r1) /* enable use of FP after return */ lis r3,86f@h ori r3,r3,86f@l mr r4,r2 /* current */ lwz r5,_NIP(r1) bl printk b ret_from_except 86: .string "floating point used in kernel (task=%p, pc=%x)\n" .align 4 #ifdef CONFIG_ALTIVEC /* Note that the AltiVec support is closely modeled after the FP * support. Changes to one are likely to be applicable to the * other! */ load_up_altivec: /* * Disable AltiVec for the task which had AltiVec previously, * and save its AltiVec registers in its thread_struct. * Enables AltiVec for use in the kernel on return. * On SMP we know the AltiVec units are free, since we give it up every * switch. -- Kumar */ mfmsr r5 oris r5,r5,MSR_VEC@h mtmsr r5 /* enable use of AltiVec now */ isync /* * For SMP, we don't do lazy AltiVec switching because it just gets too * horrendously complex, especially when a task switches from one CPU * to another. Instead we call giveup_altivec in switch_to. */ #ifndef CONFIG_SMP #ifndef CONFIG_APUS lis r6,-KERNELBASE@h #else lis r6,CYBERBASEp@h lwz r6,0(r6) #endif addis r3,r6,last_task_used_altivec@ha lwz r4,last_task_used_altivec@l(r3) cmpi 0,r4,0 beq 1f add r4,r4,r6 addi r4,r4,THREAD /* want THREAD of last_task_used_altivec */ SAVE_32VR(0,r20,r4) MFVSCR(vr0) li r20,THREAD_VSCR STVX(vr0,r20,r4) lwz r5,PT_REGS(r4) add r5,r5,r6 lwz r4,_MSR-STACK_FRAME_OVERHEAD(r5) lis r20,MSR_VEC@h andc r4,r4,r20 /* disable altivec for previous task */ stw r4,_MSR-STACK_FRAME_OVERHEAD(r5) 1: #endif /* CONFIG_SMP */ /* enable use of AltiVec after return */ oris r23,r23,MSR_VEC@h mfspr r5,SPRG3 /* current task's THREAD (phys) */ li r4,1 li r20,THREAD_VSCR stw r4,THREAD_USED_VR(r5) LVX(vr0,r20,r5) MTVSCR(vr0) REST_32VR(0,r20,r5) #ifndef CONFIG_SMP subi r4,r5,THREAD sub r4,r4,r6 stw r4,last_task_used_altivec@l(r3) #endif /* CONFIG_SMP */ /* restore registers and return */ lwz r3,_CCR(r21) lwz r4,_LINK(r21) mtcrf 0xff,r3 mtlr r4 REST_GPR(1, r21) REST_4GPRS(3, r21) /* we haven't used ctr or xer */ mtspr SRR1,r23 mtspr SRR0,r22 REST_GPR(20, r21) REST_2GPRS(22, r21) lwz r21,GPR21(r21) SYNC RFI /* * AltiVec unavailable trap from kernel - print a message, but let * the task use AltiVec in the kernel until it returns to user mode. */ KernelAltiVec: lwz r3,_MSR(r1) oris r3,r3,MSR_VEC@h stw r3,_MSR(r1) /* enable use of AltiVec after return */ lis r3,87f@h ori r3,r3,87f@l mr r4,r2 /* current */ lwz r5,_NIP(r1) bl printk b ret_from_except 87: .string "AltiVec used in kernel (task=%p, pc=%x) \n" .align 4 /* * giveup_altivec(tsk) * Disable AltiVec for the task given as the argument, * and save the AltiVec registers in its thread_struct. * Enables AltiVec for use in the kernel on return. */ .globl giveup_altivec giveup_altivec: mfmsr r5 oris r5,r5,MSR_VEC@h SYNC mtmsr r5 /* enable use of AltiVec now */ isync cmpi 0,r3,0 beqlr- /* if no previous owner, done */ addi r3,r3,THREAD /* want THREAD of task */ lwz r5,PT_REGS(r3) cmpi 0,r5,0 SAVE_32VR(0, r4, r3) MFVSCR(vr0) li r4,THREAD_VSCR STVX(vr0, r4, r3) beq 1f lwz r4,_MSR-STACK_FRAME_OVERHEAD(r5) lis r3,MSR_VEC@h andc r4,r4,r3 /* disable AltiVec for previous task */ stw r4,_MSR-STACK_FRAME_OVERHEAD(r5) 1: #ifndef CONFIG_SMP li r5,0 lis r4,last_task_used_altivec@ha stw r5,last_task_used_altivec@l(r4) #endif /* CONFIG_SMP */ blr #endif /* CONFIG_ALTIVEC */ /* * giveup_fpu(tsk) * Disable FP for the task given as the argument, * and save the floating-point registers in its thread_struct. * Enables the FPU for use in the kernel on return. */ .globl giveup_fpu giveup_fpu: mfmsr r5 ori r5,r5,MSR_FP SYNC_601 ISYNC_601 mtmsr r5 /* enable use of fpu now */ SYNC_601 isync cmpi 0,r3,0 beqlr- /* if no previous owner, done */ addi r3,r3,THREAD /* want THREAD of task */ lwz r5,PT_REGS(r3) cmpi 0,r5,0 SAVE_32FPRS(0, r3) mffs fr0 stfd fr0,THREAD_FPSCR-4(r3) beq 1f lwz r4,_MSR-STACK_FRAME_OVERHEAD(r5) li r3,MSR_FP|MSR_FE0|MSR_FE1 andc r4,r4,r3 /* disable FP for previous task */ stw r4,_MSR-STACK_FRAME_OVERHEAD(r5) 1: #ifndef CONFIG_SMP li r5,0 lis r4,last_task_used_math@ha stw r5,last_task_used_math@l(r4) #endif /* CONFIG_SMP */ blr /* * This code is jumped to from the startup code to copy * the kernel image to physical address 0. */ relocate_kernel: addis r9,r26,klimit@ha /* fetch klimit */ lwz r25,klimit@l(r9) addis r25,r25,-KERNELBASE@h li r3,0 /* Destination base address */ li r6,0 /* Destination offset */ li r5,0x4000 /* # bytes of memory to copy */ bl copy_and_flush /* copy the first 0x4000 bytes */ addi r0,r3,4f@l /* jump to the address of 4f */ mtctr r0 /* in copy and do the rest. */ bctr /* jump to the copy */ 4: mr r5,r25 bl copy_and_flush /* copy the rest */ b turn_on_mmu /* * Copy routine used to copy the kernel to start at physical address 0 * and flush and invalidate the caches as needed. * r3 = dest addr, r4 = source addr, r5 = copy limit, r6 = start offset * on exit, r3, r4, r5 are unchanged, r6 is updated to be >= r5. */ copy_and_flush: addi r5,r5,-4 addi r6,r6,-4 4: li r0,L1_CACHE_LINE_SIZE/4 mtctr r0 3: addi r6,r6,4 /* copy a cache line */ lwzx r0,r6,r4 stwx r0,r6,r3 bdnz 3b dcbst r6,r3 /* write it to memory */ sync icbi r6,r3 /* flush the icache line */ cmplw 0,r6,r5 blt 4b sync /* additional sync needed on g4 */ isync addi r5,r5,4 addi r6,r6,4 blr #ifdef CONFIG_APUS /* * On APUS the physical base address of the kernel is not known at compile * time, which means the __pa/__va constants used are incorrect. In the * __init section is recorded the virtual addresses of instructions using * these constants, so all that has to be done is fix these before * continuing the kernel boot. * * r4 = The physical address of the kernel base. */ fix_mem_constants: mr r10,r4 addis r10,r10,-KERNELBASE@h /* virt_to_phys constant */ neg r11,r10 /* phys_to_virt constant */ lis r12,__vtop_table_begin@h ori r12,r12,__vtop_table_begin@l add r12,r12,r10 /* table begin phys address */ lis r13,__vtop_table_end@h ori r13,r13,__vtop_table_end@l add r13,r13,r10 /* table end phys address */ subi r12,r12,4 subi r13,r13,4 1: lwzu r14,4(r12) /* virt address of instruction */ add r14,r14,r10 /* phys address of instruction */ lwz r15,0(r14) /* instruction, now insert top */ rlwimi r15,r10,16,16,31 /* half of vp const in low half */ stw r15,0(r14) /* of instruction and restore. */ dcbst r0,r14 /* write it to memory */ sync icbi r0,r14 /* flush the icache line */ cmpw r12,r13 bne 1b sync /* additional sync needed on g4 */ isync /* * Map the memory where the exception handlers will * be copied to when hash constants have been patched. */ #ifdef CONFIG_APUS_FAST_EXCEPT lis r8,0xfff0 #else lis r8,0 #endif ori r8,r8,0x2 /* 128KB, supervisor */ mtspr DBAT3U,r8 mtspr DBAT3L,r8 lis r12,__ptov_table_begin@h ori r12,r12,__ptov_table_begin@l add r12,r12,r10 /* table begin phys address */ lis r13,__ptov_table_end@h ori r13,r13,__ptov_table_end@l add r13,r13,r10 /* table end phys address */ subi r12,r12,4 subi r13,r13,4 1: lwzu r14,4(r12) /* virt address of instruction */ add r14,r14,r10 /* phys address of instruction */ lwz r15,0(r14) /* instruction, now insert top */ rlwimi r15,r11,16,16,31 /* half of pv const in low half*/ stw r15,0(r14) /* of instruction and restore. */ dcbst r0,r14 /* write it to memory */ sync icbi r0,r14 /* flush the icache line */ cmpw r12,r13 bne 1b sync /* additional sync needed on g4 */ isync /* No speculative loading until now */ blr /*********************************************************************** * Please note that on APUS the exception handlers are located at the * physical address 0xfff0000. For this reason, the exception handlers * cannot use relative branches to access the code below. ***********************************************************************/ #endif /* CONFIG_APUS */ #ifdef CONFIG_SMP #ifdef CONFIG_GEMINI .globl __secondary_start_gemini __secondary_start_gemini: mfspr r4,HID0 ori r4,r4,HID0_ICFI li r3,0 ori r3,r3,HID0_ICE andc r4,r4,r3 mtspr HID0,r4 sync b __secondary_start #endif /* CONFIG_GEMINI */ .globl __secondary_start_psurge __secondary_start_psurge: li r24,1 /* cpu # */ b __secondary_start_psurge99 .globl __secondary_start_psurge2 __secondary_start_psurge2: li r24,2 /* cpu # */ b __secondary_start_psurge99 .globl __secondary_start_psurge3 __secondary_start_psurge3: li r24,3 /* cpu # */ b __secondary_start_psurge99 __secondary_start_psurge99: /* we come in here with IR=0 and DR=1, and DBAT 0 set to map the 0xf0000000 - 0xffffffff region */ mfmsr r0 rlwinm r0,r0,0,28,26 /* clear DR (0x10) */ SYNC mtmsr r0 isync .globl __secondary_start __secondary_start: #ifdef CONFIG_PPC64BRIDGE mfmsr r0 clrldi r0,r0,1 /* make sure it's in 32-bit mode */ SYNC MTMSRD(r0) isync #endif /* Copy some CPU settings from CPU 0 */ bl __restore_cpu_setup lis r3,-KERNELBASE@h mr r4,r24 bl identify_cpu bl call_setup_cpu /* Call setup_cpu for this CPU */ #ifdef CONFIG_6xx lis r3,-KERNELBASE@h bl init_idle_6xx #endif /* CONFIG_6xx */ /* get current */ lis r2,current_set@h ori r2,r2,current_set@l tophys(r2,r2) slwi r24,r24,2 /* get current_set[cpu#] */ lwzx r2,r2,r24 /* stack */ addi r1,r2,TASK_UNION_SIZE-STACK_FRAME_OVERHEAD li r0,0 tophys(r3,r1) stw r0,0(r3) /* load up the MMU */ bl load_up_mmu /* ptr to phys current thread */ tophys(r4,r2) addi r4,r4,THREAD /* phys address of our thread_struct */ CLR_TOP32(r4) mtspr SPRG3,r4 li r3,0 mtspr SPRG2,r3 /* 0 => r1 has kernel sp */ stw r3,PT_REGS(r4) /* set thread.regs to 0 for kernel thread */ /* enable MMU and jump to start_secondary */ li r4,MSR_KERNEL FIX_SRR1(r4,r5) lis r3,start_secondary@h ori r3,r3,start_secondary@l mtspr SRR0,r3 mtspr SRR1,r4 SYNC RFI #endif /* CONFIG_SMP */ /* * Those generic dummy functions are kept for CPUs not * included in CONFIG_6xx */ _GLOBAL(__setup_cpu_power3) blr _GLOBAL(__setup_cpu_power4) blr _GLOBAL(__setup_cpu_ppc970) blr _GLOBAL(__setup_cpu_generic) blr #ifndef CONFIG_6xx _GLOBAL(__save_cpu_setup) blr _GLOBAL(__restore_cpu_setup) #ifdef CONFIG_POWER4 /* turn off real-mode cache inhibit on the PPC970 */ mfspr r0,SPRN_PVR srwi r0,r0,16 cmpwi r0,0x39 beq ppc970_setup_hid blr #endif #endif /* CONFIG_6xx */ /* * Load stuff into the MMU. Intended to be called with * IR=0 and DR=0. */ load_up_mmu: sync /* Force all PTE updates to finish */ isync tlbia /* Clear all TLB entries */ sync /* wait for tlbia/tlbie to finish */ TLBSYNC /* ... on all CPUs */ /* Load the SDR1 register (hash table base & size) */ lis r6,_SDR1@ha tophys(r6,r6) lwz r6,_SDR1@l(r6) mtspr SDR1,r6 #ifdef CONFIG_PPC64BRIDGE /* clear the ASR so we only use the pseudo-segment registers. */ li r6,0 mtasr r6 #endif /* CONFIG_PPC64BRIDGE */ li r0,16 /* load up segment register values */ mtctr r0 /* for context 0 */ lis r3,0x2000 /* Ku = 1, VSID = 0 */ li r4,0 3: mtsrin r3,r4 addi r3,r3,0x111 /* increment VSID */ addis r4,r4,0x1000 /* address of next segment */ bdnz 3b #ifndef CONFIG_POWER4 /* Load the BAT registers with the values set up by MMU_init. MMU_init takes care of whether we're on a 601 or not. */ mfpvr r3 srwi r3,r3,16 cmpwi r3,1 lis r3,BATS@ha addi r3,r3,BATS@l tophys(r3,r3) LOAD_BAT(0,r3,r4,r5) LOAD_BAT(1,r3,r4,r5) LOAD_BAT(2,r3,r4,r5) LOAD_BAT(3,r3,r4,r5) #endif /* CONFIG_POWER4 */ blr /* * This is where the main kernel code starts. */ start_here: /* ptr to current */ lis r2,init_task_union@h ori r2,r2,init_task_union@l /* Set up for using our exception vectors */ /* ptr to phys current thread */ tophys(r4,r2) addi r4,r4,THREAD /* init task's THREAD */ CLR_TOP32(r4) mtspr SPRG3,r4 li r3,0 mtspr SPRG2,r3 /* 0 => r1 has kernel sp */ /* stack */ addi r1,r2,TASK_UNION_SIZE li r0,0 stwu r0,-STACK_FRAME_OVERHEAD(r1) /* * Do early bootinfo parsing, platform-specific initialization, * and set up the MMU. */ mr r3,r31 mr r4,r30 mr r5,r29 mr r6,r28 mr r7,r27 bl machine_init bl MMU_init #ifdef CONFIG_APUS /* Copy exception code to exception vector base on APUS. */ lis r4,KERNELBASE@h #ifdef CONFIG_APUS_FAST_EXCEPT lis r3,0xfff0 /* Copy to 0xfff00000 */ #else lis r3,0 /* Copy to 0x00000000 */ #endif li r5,0x4000 /* # bytes of memory to copy */ li r6,0 bl copy_and_flush /* copy the first 0x4000 bytes */ #endif /* CONFIG_APUS */ /* * Go back to running unmapped so we can load up new values * for SDR1 (hash table pointer) and the segment registers * and change to using our exception vectors. */ lis r4,2f@h ori r4,r4,2f@l tophys(r4,r4) li r3,MSR_KERNEL & ~(MSR_IR|MSR_DR) FIX_SRR1(r3,r5) mtspr SRR0,r4 mtspr SRR1,r3 SYNC RFI /* Load up the kernel context */ 2: bl load_up_mmu #ifdef CONFIG_BDI_SWITCH /* Add helper information for the Abatron bdiGDB debugger. * We do this here because we know the mmu is disabled, and * will be enabled for real in just a few instructions. */ lis r5, abatron_pteptrs@h ori r5, r5, abatron_pteptrs@l stw r5, 0xf0(r0) /* This much match your Abatron config */ lis r6, swapper_pg_dir@h ori r6, r6, swapper_pg_dir@l tophys(r5, r5) stw r6, 0(r5) #endif /* Now turn on the MMU for real! */ li r4,MSR_KERNEL FIX_SRR1(r4,r5) lis r3,start_kernel@h ori r3,r3,start_kernel@l mtspr SRR0,r3 mtspr SRR1,r4 SYNC RFI /* * Set up the segment registers for a new context. */ _GLOBAL(set_context) mulli r3,r3,897 /* multiply context by skew factor */ rlwinm r3,r3,4,8,27 /* VSID = (context & 0xfffff) << 4 */ addis r3,r3,0x6000 /* Set Ks, Ku bits */ li r0,NUM_USER_SEGMENTS mtctr r0 #ifdef CONFIG_BDI_SWITCH /* Context switch the PTE pointer for the Abatron BDI2000. * The PGDIR is passed as second argument. */ lis r5, KERNELBASE@h lwz r5, 0xf0(r5) stw r4, 0x4(r5) #endif li r4,0 BEGIN_FTR_SECTION DSSALL sync END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) 3: isync #ifdef CONFIG_PPC64BRIDGE slbie r4 #endif /* CONFIG_PPC64BRIDGE */ mtsrin r3,r4 addi r3,r3,0x111 /* next VSID */ rlwinm r3,r3,0,8,3 /* clear out any overflow from VSID field */ addis r4,r4,0x1000 /* address of next segment */ bdnz 3b sync isync blr /* * An undocumented "feature" of 604e requires that the v bit * be cleared before changing BAT values. * * Also, newer IBM firmware does not clear bat3 and 4 so * this makes sure it's done. * -- Cort */ clear_bats: li r20,0 mfspr r9,PVR rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */ cmpwi r9, 1 beq 1f mtspr DBAT0U,r20 mtspr DBAT0L,r20 mtspr DBAT1U,r20 mtspr DBAT1L,r20 mtspr DBAT2U,r20 mtspr DBAT2L,r20 mtspr DBAT3U,r20 mtspr DBAT3L,r20 1: mtspr IBAT0U,r20 mtspr IBAT0L,r20 mtspr IBAT1U,r20 mtspr IBAT1L,r20 mtspr IBAT2U,r20 mtspr IBAT2L,r20 mtspr IBAT3U,r20 mtspr IBAT3L,r20 BEGIN_FTR_SECTION /* Here's a tweak: at this point, CPU setup have * not been called yet, so HIGH_BAT_EN may not be * set in HID0 for the 745x processors. However, it * seems that doesn't affect our ability to actually * write to these SPRs. */ mtspr SPRN_DBAT4U,r20 mtspr SPRN_DBAT4L,r20 mtspr SPRN_DBAT5U,r20 mtspr SPRN_DBAT5L,r20 mtspr SPRN_DBAT6U,r20 mtspr SPRN_DBAT6L,r20 mtspr SPRN_DBAT7U,r20 mtspr SPRN_DBAT7L,r20 mtspr SPRN_IBAT4U,r20 mtspr SPRN_IBAT4L,r20 mtspr SPRN_IBAT5U,r20 mtspr SPRN_IBAT5L,r20 mtspr SPRN_IBAT6U,r20 mtspr SPRN_IBAT6L,r20 mtspr SPRN_IBAT7U,r20 mtspr SPRN_IBAT7L,r20 END_FTR_SECTION_IFSET(CPU_FTR_HAS_HIGH_BATS) blr flush_tlbs: lis r20, 0x40 1: addic. r20, r20, -0x1000 tlbie r20 blt 1b sync blr mmu_off: addi r4, r3, __after_mmu_off - _start mfmsr r3 andi. r0,r3,MSR_DR|MSR_IR /* MMU enabled? */ beqlr andc r3,r3,r0 mtspr SRR0,r4 mtspr SRR1,r3 sync RFI #ifndef CONFIG_POWER4 /* * Use the first pair of BAT registers to map the 1st 16MB * of RAM to KERNELBASE. From this point on we can't safely * call OF any more. */ initial_bats: lis r11,KERNELBASE@h #ifndef CONFIG_PPC64BRIDGE mfspr r9,PVR rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */ cmpi 0,r9,1 bne 4f ori r11,r11,4 /* set up BAT registers for 601 */ li r8,0x7f /* valid, block length = 8MB */ oris r9,r11,0x800000@h /* set up BAT reg for 2nd 8M */ oris r10,r8,0x800000@h /* set up BAT reg for 2nd 8M */ mtspr IBAT0U,r11 /* N.B. 601 has valid bit in */ mtspr IBAT0L,r8 /* lower BAT register */ mtspr IBAT1U,r9 mtspr IBAT1L,r10 isync blr #endif /* CONFIG_PPC64BRIDGE */ 4: tophys(r8,r11) #ifdef CONFIG_SMP ori r8,r8,0x12 /* R/W access, M=1 */ #else ori r8,r8,2 /* R/W access */ #endif /* CONFIG_SMP */ #ifdef CONFIG_APUS ori r11,r11,BL_8M<<2|0x2 /* set up 8MB BAT registers for 604 */ #else ori r11,r11,BL_256M<<2|0x2 /* set up BAT registers for 604 */ #endif /* CONFIG_APUS */ #ifdef CONFIG_PPC64BRIDGE /* clear out the high 32 bits in the BAT */ clrldi r11,r11,32 clrldi r8,r8,32 #endif /* CONFIG_PPC64BRIDGE */ mtspr DBAT0L,r8 /* N.B. 6xx (not 601) have valid */ mtspr DBAT0U,r11 /* bit in upper BAT register */ mtspr IBAT0L,r8 mtspr IBAT0U,r11 isync blr #if !defined(CONFIG_APUS) && defined(CONFIG_BOOTX_TEXT) setup_disp_bat: /* * setup the display bat prepared for us in prom.c */ mflr r8 bl reloc_offset mtlr r8 addis r8,r3,disp_BAT@ha addi r8,r8,disp_BAT@l lwz r11,0(r8) lwz r8,4(r8) mfspr r9,PVR rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */ cmpi 0,r9,1 beq 1f mtspr DBAT3L,r8 mtspr DBAT3U,r11 blr 1: mtspr IBAT3L,r8 mtspr IBAT3U,r11 blr #endif /* !defined(CONFIG_APUS) && defined(CONFIG_BOOTX_TEXT) */ #else /* CONFIG_POWER4 */ ppc970_setup_hid: li r0,0 sync mtspr 0x3f4,r0 isync sync mtspr 0x3f6,r0 isync mfspr r0,SPRN_HID0 li r11,5 /* clear DOZE and SLEEP */ rldimi r0,r11,52,8 /* and set NAP and DPM */ li r11,0 rldimi r0,r11,32,31 /* clear EN_ATTN */ mtspr SPRN_HID0,r0 mfspr r0,SPRN_HID0 mfspr r0,SPRN_HID0 mfspr r0,SPRN_HID0 mfspr r0,SPRN_HID0 mfspr r0,SPRN_HID0 mfspr r0,SPRN_HID0 sync isync mfspr r0,SPRN_HID1 li r11,0x1200 /* enable i-fetch cacheability */ sldi r11,r11,44 /* and prefetch */ or r0,r0,r11 mtspr SPRN_HID1,r0 mtspr SPRN_HID1,r0 isync li r0,0 sync mtspr 0x137,0 isync blr #endif /* CONFIG_POWER4 */ #ifdef CONFIG_8260 /* Jump into the system reset for the rom. * We first disable the MMU, and then jump to the ROM reset address. * * r3 is the board info structure, r4 is the location for starting. * I use this for building a small kernel that can load other kernels, * rather than trying to write or rely on a rom monitor that can tftp load. */ .globl m8260_gorom m8260_gorom: mfmsr r0 rlwinm r0,r0,0,17,15 /* clear MSR_EE in r0 */ sync mtmsr r0 sync mfspr r11, HID0 lis r10, 0 ori r10,r10,HID0_ICE|HID0_DCE andc r11, r11, r10 mtspr HID0, r11 isync li r5, MSR_ lis r6,2f@h addis r6,r6,-KERNELBASE@h ori r6,r6,2f@l mtspr SRR0,r6 mtspr SRR1,r5 isync sync rfi 2: mtlr r4 blr #endif /* * We put a few things here that have to be page-aligned. * This stuff goes at the beginning of the data segment, * which is page-aligned. */ .data .globl sdata sdata: .globl empty_zero_page empty_zero_page: .space 4096 .globl swapper_pg_dir swapper_pg_dir: .space 4096 /* * This space gets a copy of optional info passed to us by the bootstrap * Used to pass parameters into the kernel like root=/dev/sda1, etc. */ .globl cmd_line cmd_line: .space 512 .globl intercept_table intercept_table: .long 0, 0, i0x200, i0x300, i0x400, 0, i0x600, i0x700 .long i0x800, 0, 0, 0, 0, i0xd00, 0, 0 .long 0, 0, 0, i0x1300, 0, 0, 0, 0 .long 0, 0, 0, 0, 0, 0, 0, 0 .long 0, 0, 0, 0, 0, 0, 0, 0 .long 0, 0, 0, 0, 0, 0, 0, 0 #ifdef CONFIG_BDI_SWITCH /* Room for two PTE pointers, usually the kernel and current user pointers * to their respective root page table. */ abatron_pteptrs: .space 8 #endif