aboutsummaryrefslogtreecommitdiff
path: root/sys/powerpc/booke/locore.S
diff options
context:
space:
mode:
Diffstat (limited to 'sys/powerpc/booke/locore.S')
-rw-r--r--sys/powerpc/booke/locore.S977
1 files changed, 977 insertions, 0 deletions
diff --git a/sys/powerpc/booke/locore.S b/sys/powerpc/booke/locore.S
new file mode 100644
index 000000000000..a86dd2a575ff
--- /dev/null
+++ b/sys/powerpc/booke/locore.S
@@ -0,0 +1,977 @@
+/*-
+ * Copyright (C) 2007-2009 Semihalf, Rafal Jaworowski <raj@semihalf.com>
+ * Copyright (C) 2006 Semihalf, Marian Balakowicz <m8@semihalf.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "assym.inc"
+
+#include "opt_hwpmc_hooks.h"
+
+#include <machine/asm.h>
+#include <machine/hid.h>
+#include <machine/param.h>
+#include <machine/spr.h>
+#include <machine/pte.h>
+#include <machine/trap.h>
+#include <machine/vmparam.h>
+#include <machine/tlb.h>
+
+#ifdef _CALL_ELF
+.abiversion _CALL_ELF
+#endif
+
+#define TMPSTACKSZ 16384
+
+#ifdef __powerpc64__
+#define GET_TOCBASE(r) \
+ mfspr r, SPR_SPRG8
+#define TOC_RESTORE nop
+#define CMPI cmpdi
+#define CMPL cmpld
+#define LOAD ld
+#define LOADX ldarx
+#define STORE std
+#define STOREX stdcx.
+#define STU stdu
+#define CALLSIZE 48
+#define REDZONE 288
+#define THREAD_REG %r13
+#define ADDR(x) \
+ .llong x
+#define WORD_SIZE 8
+#else
+#define GET_TOCBASE(r)
+#define TOC_RESTORE
+#define CMPI cmpwi
+#define CMPL cmplw
+#define LOAD lwz
+#define LOADX lwarx
+#define STOREX stwcx.
+#define STORE stw
+#define STU stwu
+#define CALLSIZE 8
+#define REDZONE 0
+#define THREAD_REG %r2
+#define ADDR(x) \
+ .long x
+#define WORD_SIZE 4
+#endif
+
+#ifdef __powerpc64__
+ /* Placate lld by creating a kboot stub. */
+ .section ".text.kboot", "x", @progbits
+ b __start
+#endif
+
+ .text
+ .globl btext
+btext:
+
+/*
+ * This symbol is here for the benefit of kvm_mkdb, and is supposed to
+ * mark the start of kernel text.
+ */
+ .globl kernel_text
+kernel_text:
+
+/*
+ * Startup entry. Note, this must be the first thing in the text segment!
+ */
+ .text
+ .globl __start
+__start:
+
+/*
+ * Assumptions on the boot loader:
+ * - System memory starts from physical address 0
+ * - It's mapped by a single TLB1 entry
+ * - TLB1 mapping is 1:1 pa to va
+ * - Kernel is loaded at 64MB boundary
+ * - All PID registers are set to the same value
+ * - CPU is running in AS=0
+ *
+ * Registers contents provided by the loader(8):
+ * r1 : stack pointer
+ * r3 : metadata pointer
+ *
+ * We rearrange the TLB1 layout as follows:
+ * - Find TLB1 entry we started in
+ * - Make sure it's protected, invalidate other entries
+ * - Create temp entry in the second AS (make sure it's not TLB[1])
+ * - Switch to temp mapping
+ * - Map 64MB of RAM in TLB1[1]
+ * - Use AS=0, set EPN to VM_MIN_KERNEL_ADDRESS and RPN to kernel load address
+ * - Switch to TLB1[1] mapping
+ * - Invalidate temp mapping
+ *
+ * locore registers use:
+ * r1 : stack pointer
+ * r2 : trace pointer (AP only, for early diagnostics)
+ * r3-r27 : scratch registers
+ * r28 : temp TLB1 entry
+ * r29 : initial TLB1 entry we started in
+ * r30-r31 : arguments (metadata pointer)
+ */
+
+/*
+ * Keep arguments in r30 & r31 for later use.
+ */
+ mr %r30, %r3
+ mr %r31, %r4
+
+/*
+ * Initial cleanup
+ */
+ li %r3, PSL_DE /* Keep debug exceptions for CodeWarrior. */
+#ifdef __powerpc64__
+ oris %r3, %r3, PSL_CM@h
+#endif
+ mtmsr %r3
+ isync
+
+/*
+ * Initial HIDs configuration
+ */
+1:
+ mfpvr %r3
+ rlwinm %r3, %r3, 16, 16, 31
+
+ lis %r4, HID0_E500_DEFAULT_SET@h
+ ori %r4, %r4, HID0_E500_DEFAULT_SET@l
+
+ /* Check for e500mc and e5500 */
+ cmpli 0, 0, %r3, FSL_E500mc
+ bne 2f
+
+ lis %r4, HID0_E500MC_DEFAULT_SET@h
+ ori %r4, %r4, HID0_E500MC_DEFAULT_SET@l
+ b 3f
+2:
+ cmpli 0, 0, %r3, FSL_E5500
+ bne 3f
+
+ lis %r4, HID0_E5500_DEFAULT_SET@h
+ ori %r4, %r4, HID0_E5500_DEFAULT_SET@l
+
+3:
+ mtspr SPR_HID0, %r4
+ isync
+
+/*
+ * E500mc and E5500 do not have HID1 register, so skip HID1 setup on
+ * this core.
+ */
+ cmpli 0, 0, %r3, FSL_E500mc
+ beq 1f
+ cmpli 0, 0, %r3, FSL_E5500
+ beq 1f
+ cmpli 0, 0, %r3, FSL_E6500
+ beq 1f
+
+ lis %r3, HID1_E500_DEFAULT_SET@h
+ ori %r3, %r3, HID1_E500_DEFAULT_SET@l
+ mtspr SPR_HID1, %r3
+ isync
+1:
+ /* Invalidate all entries in TLB0 */
+ li %r3, 0
+ bl tlb_inval_all
+
+ cmpwi %r30, 0
+ beq done_mapping
+
+/*
+ * Locate the TLB1 entry that maps this code
+ */
+ bl 1f
+1: mflr %r3
+ bl tlb1_find_current /* the entry found is returned in r29 */
+
+ bl tlb1_inval_all_but_current
+
+/*
+ * Create temporary mapping in AS=1 and switch to it
+ */
+ bl tlb1_temp_mapping_as1
+
+ mfmsr %r3
+ ori %r3, %r3, (PSL_IS | PSL_DS)
+ bl 2f
+2: mflr %r4
+ addi %r4, %r4, (3f - 2b)
+ mtspr SPR_SRR0, %r4
+ mtspr SPR_SRR1, %r3
+ rfi /* Switch context */
+
+/*
+ * Invalidate initial entry
+ */
+3:
+ mr %r3, %r29
+ bl tlb1_inval_entry
+
+/*
+ * Setup final mapping in TLB1[1] and switch to it
+ */
+ /* Final kernel mapping, map in 64 MB of RAM */
+ lis %r3, MAS0_TLBSEL1@h /* Select TLB1 */
+ li %r4, 0 /* Entry 0 */
+ rlwimi %r3, %r4, 16, 10, 15
+ mtspr SPR_MAS0, %r3
+ isync
+
+ li %r3, (TLB_SIZE_64M << MAS1_TSIZE_SHIFT)@l
+ oris %r3, %r3, (MAS1_VALID | MAS1_IPROT)@h
+ mtspr SPR_MAS1, %r3 /* note TS was not filled, so it's TS=0 */
+ isync
+
+ LOAD_ADDR(%r3, VM_MIN_KERNEL_ADDRESS)
+ ori %r3, %r3, (_TLB_ENTRY_SHARED | MAS2_M)@l /* WIMGE = 0b00100 */
+ mtspr SPR_MAS2, %r3
+ isync
+
+ /* Discover phys load address */
+ bl 3f
+3: mflr %r4 /* Use current address */
+ rlwinm %r4, %r4, 0, 0, 5 /* 64MB alignment mask */
+ ori %r4, %r4, (MAS3_SX | MAS3_SW | MAS3_SR)@l
+ mtspr SPR_MAS3, %r4 /* Set RPN and protection */
+ isync
+ li %r4, 0
+ mtspr SPR_MAS7, %r4
+ isync
+ tlbwe
+ isync
+ msync
+
+ /* Switch to the above TLB1[1] mapping */
+ bl 4f
+4: mflr %r4
+#ifdef __powerpc64__
+ clrldi %r4, %r4, 38
+ clrrdi %r3, %r3, 12
+#else
+ rlwinm %r4, %r4, 0, 6, 31 /* Current offset from kernel load address */
+ rlwinm %r3, %r3, 0, 0, 19
+#endif
+ add %r4, %r4, %r3 /* Convert to kernel virtual address */
+ addi %r4, %r4, (5f - 4b)
+ li %r3, PSL_DE /* Note AS=0 */
+#ifdef __powerpc64__
+ oris %r3, %r3, PSL_CM@h
+#endif
+ mtspr SPR_SRR0, %r4
+ mtspr SPR_SRR1, %r3
+ rfi
+
+/*
+ * Invalidate temp mapping
+ */
+5:
+ mr %r3, %r28
+ bl tlb1_inval_entry
+
+done_mapping:
+
+#ifdef __powerpc64__
+ /* Set up the TOC pointer */
+ b 0f
+ .align 3
+0: nop
+ bl 1f
+ .llong __tocbase + 0x8000 - .
+1: mflr %r2
+ ld %r1,0(%r2)
+ add %r2,%r1,%r2
+ mtspr SPR_SPRG8, %r2
+ nop
+
+ /* Get load offset */
+ ld %r31,-0x8000(%r2) /* First TOC entry is TOC base */
+ subf %r31,%r31,%r2 /* Subtract from real TOC base to get base */
+
+ /* Set up the stack pointer */
+ bl 1f
+ .llong tmpstack + TMPSTACKSZ - 96 - .
+1: mflr %r3
+ ld %r1,0(%r3)
+ add %r1,%r1,%r3
+/*
+ * Relocate kernel
+ */
+ bl 1f
+ .llong _DYNAMIC-.
+1: mflr %r3
+ ld %r4,0(%r3)
+ add %r3,%r4,%r3
+ mr %r4,%r31
+#else
+/*
+ * Setup a temporary stack
+ */
+ bl 1f
+ .long tmpstack-.
+1: mflr %r1
+ lwz %r2,0(%r1)
+ add %r1,%r1,%r2
+ addi %r1, %r1, (TMPSTACKSZ - 16)
+
+/*
+ * Relocate kernel
+ */
+ bl 1f
+ .long _DYNAMIC-.
+ .long _GLOBAL_OFFSET_TABLE_-.
+1: mflr %r5
+ lwz %r3,0(%r5) /* _DYNAMIC in %r3 */
+ add %r3,%r3,%r5
+ lwz %r4,4(%r5) /* GOT pointer */
+ add %r4,%r4,%r5
+ lwz %r4,4(%r4) /* got[0] is _DYNAMIC link addr */
+ subf %r4,%r4,%r3 /* subtract to calculate relocbase */
+#endif
+ bl CNAME(elf_reloc_self)
+ TOC_RESTORE
+
+/*
+ * Initialise exception vector offsets
+ */
+ bl CNAME(ivor_setup)
+ TOC_RESTORE
+
+/*
+ * Set up arguments and jump to system initialization code
+ */
+ mr %r3, %r30
+ mr %r4, %r31
+
+ /* Prepare core */
+ bl CNAME(booke_init)
+ TOC_RESTORE
+
+ /* Switch to thread0.td_kstack now */
+ mr %r1, %r3
+ li %r3, 0
+ STORE %r3, 0(%r1)
+
+ /* Machine independet part, does not return */
+ bl CNAME(mi_startup)
+ TOC_RESTORE
+ /* NOT REACHED */
+5: b 5b
+
+
+#ifdef SMP
+/************************************************************************/
+/* AP Boot page */
+/************************************************************************/
+ .text
+ .globl __boot_page
+ .align 12
+__boot_page:
+ /*
+ * The boot page is a special page of memory used during AP bringup.
+ * Before the AP comes out of reset, the physical 4K page holding this
+ * code is arranged to be mapped at 0xfffff000 by use of
+ * platform-dependent registers.
+ *
+ * Alternatively, this page may be executed using an ePAPR-standardized
+ * method -- writing to the address specified in "cpu-release-addr".
+ *
+ * In either case, execution begins at the last instruction of the
+ * page, which is a branch back to the start of the page.
+ *
+ * The code in the page must do initial MMU setup and normalize the
+ * TLBs for regular operation in the correct address space before
+ * reading outside the page.
+ *
+ * This implementation accomplishes this by:
+ * 1) Wiping TLB0 and all TLB1 entries but the one currently in use.
+ * 2) Establishing a temporary 4K TLB1 mapping in AS=1, and switching
+ * to it with rfi. This entry must NOT be in TLB1 slot 0.
+ * (This is needed to give the code freedom to clean up AS=0.)
+ * 3) Removing the initial TLB1 entry, leaving us with a single valid
+ * TLB1 entry, NOT in slot 0.
+ * 4) Installing an AS0 entry in TLB1 slot 0 mapping the 64MB kernel
+ * segment at its final virtual address. A second rfi is done to
+ * switch to the final address space. At this point we can finally
+ * access the rest of the kernel segment safely.
+ * 5) The temporary TLB1 AS=1 entry is removed, finally leaving us in
+ * a consistent (but minimal) state.
+ * 6) Set up TOC, stack, and pcpu registers.
+ * 7) Now that we can finally call C code, call pmap_boostrap_ap(),
+ * which finishes copying in the shared TLB1 entries.
+ *
+ * At this point, the MMU is fully set up, and we can proceed with
+ * running the actual AP bootstrap code.
+ *
+ * Pieces of this code are also used for UP kernel, but in this case
+ * the sections specific to boot page functionality are dropped by
+ * the preprocessor.
+ */
+#ifdef __powerpc64__
+ nop /* PPC64 alignment word. 64-bit target. */
+#endif
+ bl 1f /* 32-bit target. */
+
+ .globl bp_trace
+bp_trace:
+ ADDR(0) /* Trace pointer (%r31). */
+
+ .globl bp_kernload
+bp_kernload:
+ .llong 0 /* Kern phys. load address. */
+
+ .globl bp_virtaddr
+bp_virtaddr:
+ ADDR(0) /* Virt. address of __boot_page. */
+
+/*
+ * Initial configuration
+ */
+1:
+ mflr %r31 /* r31 hold the address of bp_trace */
+
+ /* Set HIDs */
+ mfpvr %r3
+ rlwinm %r3, %r3, 16, 16, 31
+
+ /* HID0 for E500 is default */
+ lis %r4, HID0_E500_DEFAULT_SET@h
+ ori %r4, %r4, HID0_E500_DEFAULT_SET@l
+
+ cmpli 0, 0, %r3, FSL_E500mc
+ bne 2f
+ lis %r4, HID0_E500MC_DEFAULT_SET@h
+ ori %r4, %r4, HID0_E500MC_DEFAULT_SET@l
+ b 3f
+2:
+ cmpli 0, 0, %r3, FSL_E5500
+ bne 3f
+ lis %r4, HID0_E5500_DEFAULT_SET@h
+ ori %r4, %r4, HID0_E5500_DEFAULT_SET@l
+3:
+ mtspr SPR_HID0, %r4
+ isync
+
+ /* Enable branch prediction */
+ li %r3, BUCSR_BPEN
+ mtspr SPR_BUCSR, %r3
+ isync
+
+ /* Invalidate all entries in TLB0 */
+ li %r3, 0
+ bl tlb_inval_all
+
+/*
+ * Find TLB1 entry which is translating us now
+ */
+ bl 2f
+2: mflr %r3
+ bl tlb1_find_current /* the entry number found is in r29 */
+
+ bl tlb1_inval_all_but_current
+
+/*
+ * Create temporary translation in AS=1 and switch to it
+ */
+
+ bl tlb1_temp_mapping_as1
+
+ mfmsr %r3
+ ori %r3, %r3, (PSL_IS | PSL_DS)
+#ifdef __powerpc64__
+ oris %r3, %r3, PSL_CM@h /* Ensure we're in 64-bit after RFI */
+#endif
+ bl 3f
+3: mflr %r4
+ addi %r4, %r4, (4f - 3b)
+ mtspr SPR_SRR0, %r4
+ mtspr SPR_SRR1, %r3
+ rfi /* Switch context */
+
+/*
+ * Invalidate initial entry
+ */
+4:
+ mr %r3, %r29
+ bl tlb1_inval_entry
+
+/*
+ * Setup final mapping in TLB1[0] and switch to it
+ */
+ /* Final kernel mapping, map in 64 MB of RAM */
+ lis %r3, MAS0_TLBSEL1@h /* Select TLB1 */
+ li %r4, 0 /* Entry 0 */
+ rlwimi %r3, %r4, 16, 4, 15
+ mtspr SPR_MAS0, %r3
+ isync
+
+ li %r3, (TLB_SIZE_64M << MAS1_TSIZE_SHIFT)@l
+ oris %r3, %r3, (MAS1_VALID | MAS1_IPROT)@h
+ mtspr SPR_MAS1, %r3 /* note TS was not filled, so it's TS=0 */
+ isync
+
+ LOAD_ADDR(%r3, VM_MIN_KERNEL_ADDRESS)
+ ori %r3, %r3, (_TLB_ENTRY_SHARED | MAS2_M)@l /* WIMGE = 0b00100 */
+ mtspr SPR_MAS2, %r3
+ isync
+
+ /* Retrieve kernel load [physical] address from bp_kernload */
+5:
+ mflr %r3
+#ifdef __powerpc64__
+ clrrdi %r3, %r3, PAGE_SHIFT /* trunc_page(%r3) */
+#else
+ clrrwi %r3, %r3, PAGE_SHIFT /* trunc_page(%r3) */
+#endif
+ /* Load lower half of the kernel loadaddr. */
+ lwz %r4, (bp_kernload - __boot_page + 4)(%r3)
+ LOAD %r5, (bp_virtaddr - __boot_page)(%r3)
+
+ /* Set RPN and protection */
+ ori %r4, %r4, (MAS3_SX | MAS3_SW | MAS3_SR)@l
+ mtspr SPR_MAS3, %r4
+ isync
+ lwz %r4, (bp_kernload - __boot_page)(%r3)
+ mtspr SPR_MAS7, %r4
+ isync
+ tlbwe
+ isync
+ msync
+
+ /* Switch to the final mapping */
+ bl 6f
+6: mflr %r3
+ rlwinm %r3, %r3, 0, 0xfff /* Offset from boot page start */
+ add %r3, %r3, %r5 /* Make this a virtual address */
+ addi %r3, %r3, (7f - 6b) /* And figure out return address. */
+#ifdef __powerpc64__
+ lis %r4, PSL_CM@h /* Note AS=0 */
+#else
+ li %r4, 0 /* Note AS=0 */
+#endif
+ mtspr SPR_SRR0, %r3
+ mtspr SPR_SRR1, %r4
+ rfi
+7:
+
+/*
+ * At this point we're running at virtual addresses VM_MIN_KERNEL_ADDRESS and
+ * beyond so it's allowed to directly access all locations the kernel was linked
+ * against.
+ */
+
+/*
+ * Invalidate temp mapping
+ */
+ mr %r3, %r28
+ bl tlb1_inval_entry
+
+#ifdef __powerpc64__
+ /* Set up the TOC pointer */
+ b 0f
+ .align 3
+0: nop
+ bl 1f
+ .llong __tocbase + 0x8000 - .
+1: mflr %r2
+ ld %r1,0(%r2)
+ add %r2,%r1,%r2
+ mtspr SPR_SPRG8, %r2
+
+ /* Set up the stack pointer */
+ addis %r1,%r2,TOC_REF(tmpstack)@ha
+ ld %r1,TOC_REF(tmpstack)@l(%r1)
+ addi %r1,%r1,TMPSTACKSZ-96
+#else
+/*
+ * Setup a temporary stack
+ */
+ bl 1f
+ .long tmpstack-.
+1: mflr %r1
+ lwz %r2,0(%r1)
+ add %r1,%r1,%r2
+ stw %r1, 0(%r1)
+ addi %r1, %r1, (TMPSTACKSZ - 16)
+#endif
+
+/*
+ * Initialise exception vector offsets
+ */
+ bl CNAME(ivor_setup)
+ TOC_RESTORE
+
+ /*
+ * Assign our pcpu instance
+ */
+ bl 1f
+ .long ap_pcpu-.
+1: mflr %r4
+ lwz %r3, 0(%r4)
+ add %r3, %r3, %r4
+ LOAD %r3, 0(%r3)
+ mtsprg0 %r3
+
+ bl CNAME(pmap_bootstrap_ap)
+ TOC_RESTORE
+
+ bl CNAME(cpudep_ap_bootstrap)
+ TOC_RESTORE
+ /* Switch to the idle thread's kstack */
+ mr %r1, %r3
+
+ bl CNAME(machdep_ap_bootstrap)
+ TOC_RESTORE
+
+ /* NOT REACHED */
+6: b 6b
+#endif /* SMP */
+
+#if defined (BOOKE_E500)
+/*
+ * Invalidate all entries in the given TLB.
+ *
+ * r3 TLBSEL
+ */
+tlb_inval_all:
+ rlwinm %r3, %r3, 3, (1 << 3) /* TLBSEL */
+ ori %r3, %r3, (1 << 2) /* INVALL */
+ tlbivax 0, %r3
+ isync
+ msync
+
+ tlbsync
+ msync
+ blr
+
+/*
+ * expects address to look up in r3, returns entry number in r29
+ *
+ * FIXME: the hidden assumption is we are now running in AS=0, but we should
+ * retrieve actual AS from MSR[IS|DS] and put it in MAS6[SAS]
+ */
+tlb1_find_current:
+ mfspr %r17, SPR_PID0
+ slwi %r17, %r17, MAS6_SPID0_SHIFT
+ mtspr SPR_MAS6, %r17
+ isync
+ tlbsx 0, %r3
+ mfspr %r17, SPR_MAS0
+ rlwinm %r29, %r17, 16, 26, 31 /* MAS0[ESEL] -> r29 */
+
+ /* Make sure we have IPROT set on the entry */
+ mfspr %r17, SPR_MAS1
+ oris %r17, %r17, MAS1_IPROT@h
+ mtspr SPR_MAS1, %r17
+ isync
+ tlbwe
+ isync
+ msync
+ blr
+
+/*
+ * Invalidates a single entry in TLB1.
+ *
+ * r3 ESEL
+ * r4-r5 scratched
+ */
+tlb1_inval_entry:
+ lis %r4, MAS0_TLBSEL1@h /* Select TLB1 */
+ rlwimi %r4, %r3, 16, 10, 15 /* Select our entry */
+ mtspr SPR_MAS0, %r4
+ isync
+ tlbre
+ li %r5, 0 /* MAS1[V] = 0 */
+ mtspr SPR_MAS1, %r5
+ isync
+ tlbwe
+ isync
+ msync
+ blr
+
+/*
+ * r29 current entry number
+ * r28 returned temp entry
+ * r3-r5 scratched
+ */
+tlb1_temp_mapping_as1:
+ /* Read our current translation */
+ lis %r3, MAS0_TLBSEL1@h /* Select TLB1 */
+ rlwimi %r3, %r29, 16, 10, 15 /* Select our current entry */
+ mtspr SPR_MAS0, %r3
+ isync
+ tlbre
+
+ /*
+ * Prepare and write temp entry
+ *
+ * FIXME this is not robust against overflow i.e. when the current
+ * entry is the last in TLB1
+ */
+ lis %r3, MAS0_TLBSEL1@h /* Select TLB1 */
+ addi %r28, %r29, 1 /* Use next entry. */
+ rlwimi %r3, %r28, 16, 10, 15 /* Select temp entry */
+ mtspr SPR_MAS0, %r3
+ isync
+ mfspr %r5, SPR_MAS1
+ li %r4, 1 /* AS=1 */
+ rlwimi %r5, %r4, 12, 19, 19
+ li %r4, 0 /* Global mapping, TID=0 */
+ rlwimi %r5, %r4, 16, 8, 15
+ oris %r5, %r5, (MAS1_VALID | MAS1_IPROT)@h
+ mtspr SPR_MAS1, %r5
+ isync
+ mflr %r3
+ li %r4, 0
+ mtspr SPR_MAS7, %r4
+ mtlr %r3
+ isync
+ tlbwe
+ isync
+ msync
+ blr
+
+/*
+ * Loops over TLB1, invalidates all entries skipping the one which currently
+ * maps this code.
+ *
+ * r29 current entry
+ * r3-r5 scratched
+ */
+tlb1_inval_all_but_current:
+ mfspr %r3, SPR_TLB1CFG /* Get number of entries */
+ andi. %r3, %r3, TLBCFG_NENTRY_MASK@l
+ li %r4, 0 /* Start from Entry 0 */
+1: lis %r5, MAS0_TLBSEL1@h
+ rlwimi %r5, %r4, 16, 10, 15
+ mtspr SPR_MAS0, %r5
+ isync
+ tlbre
+ mfspr %r5, SPR_MAS1
+ cmpw %r4, %r29 /* our current entry? */
+ beq 2f
+ rlwinm %r5, %r5, 0, 2, 31 /* clear VALID and IPROT bits */
+ mtspr SPR_MAS1, %r5
+ isync
+ tlbwe
+ isync
+ msync
+2: addi %r4, %r4, 1
+ cmpw %r4, %r3 /* Check if this is the last entry */
+ bne 1b
+ blr
+#endif
+
+#ifdef SMP
+.globl __boot_tlb1
+ /*
+ * The __boot_tlb1 table is used to hold BSP TLB1 entries
+ * marked with _TLB_ENTRY_SHARED flag during AP bootstrap.
+ * The BSP fills in the table in tlb_ap_prep() function. Next,
+ * AP loads its contents to TLB1 hardware in pmap_bootstrap_ap().
+ */
+__boot_tlb1:
+ .space TLB1_MAX_ENTRIES * TLB_ENTRY_SIZE
+
+__boot_page_padding:
+ /*
+ * Boot page needs to be exactly 4K, with the last word of this page
+ * acting as the reset vector, so we need to stuff the remainder.
+ * Upon release from holdoff CPU fetches the last word of the boot
+ * page.
+ */
+ .space 4092 - (__boot_page_padding - __boot_page)
+ b __boot_page
+ /*
+ * This is the end of the boot page.
+ * During AP startup, the previous instruction is at 0xfffffffc
+ * virtual (i.e. the reset vector.)
+ */
+#endif /* SMP */
+
+/************************************************************************/
+/* locore subroutines */
+/************************************************************************/
+
+/*
+ * Cache disable/enable/inval sequences according
+ * to section 2.16 of E500CORE RM.
+ */
+ENTRY(dcache_inval)
+ /* Invalidate d-cache */
+ mfspr %r3, SPR_L1CSR0
+ ori %r3, %r3, (L1CSR0_DCFI | L1CSR0_DCLFR)@l
+ msync
+ isync
+ mtspr SPR_L1CSR0, %r3
+ isync
+1: mfspr %r3, SPR_L1CSR0
+ andi. %r3, %r3, L1CSR0_DCFI
+ bne 1b
+ blr
+END(dcache_inval)
+
+ENTRY(dcache_disable)
+ /* Disable d-cache */
+ mfspr %r3, SPR_L1CSR0
+ li %r4, L1CSR0_DCE@l
+ not %r4, %r4
+ and %r3, %r3, %r4
+ msync
+ isync
+ mtspr SPR_L1CSR0, %r3
+ isync
+ blr
+END(dcache_disable)
+
+ENTRY(dcache_enable)
+ /* Enable d-cache */
+ mfspr %r3, SPR_L1CSR0
+ oris %r3, %r3, (L1CSR0_DCPE | L1CSR0_DCE)@h
+ ori %r3, %r3, (L1CSR0_DCPE | L1CSR0_DCE)@l
+ msync
+ isync
+ mtspr SPR_L1CSR0, %r3
+ isync
+ blr
+END(dcache_enable)
+
+ENTRY(icache_inval)
+ /* Invalidate i-cache */
+ mfspr %r3, SPR_L1CSR1
+ ori %r3, %r3, (L1CSR1_ICFI | L1CSR1_ICLFR)@l
+ isync
+ mtspr SPR_L1CSR1, %r3
+ isync
+1: mfspr %r3, SPR_L1CSR1
+ andi. %r3, %r3, L1CSR1_ICFI
+ bne 1b
+ blr
+END(icache_inval)
+
+ENTRY(icache_disable)
+ /* Disable i-cache */
+ mfspr %r3, SPR_L1CSR1
+ li %r4, L1CSR1_ICE@l
+ not %r4, %r4
+ and %r3, %r3, %r4
+ isync
+ mtspr SPR_L1CSR1, %r3
+ isync
+ blr
+END(icache_disable)
+
+ENTRY(icache_enable)
+ /* Enable i-cache */
+ mfspr %r3, SPR_L1CSR1
+ oris %r3, %r3, (L1CSR1_ICPE | L1CSR1_ICE)@h
+ ori %r3, %r3, (L1CSR1_ICPE | L1CSR1_ICE)@l
+ isync
+ mtspr SPR_L1CSR1, %r3
+ isync
+ blr
+END(icache_enable)
+
+/*
+ * L2 cache disable/enable/inval sequences for E500mc.
+ */
+
+ENTRY(l2cache_inval)
+ mfspr %r3, SPR_L2CSR0
+ oris %r3, %r3, (L2CSR0_L2FI | L2CSR0_L2LFC)@h
+ ori %r3, %r3, (L2CSR0_L2FI | L2CSR0_L2LFC)@l
+ isync
+ mtspr SPR_L2CSR0, %r3
+ isync
+1: mfspr %r3, SPR_L2CSR0
+ andis. %r3, %r3, L2CSR0_L2FI@h
+ bne 1b
+ blr
+END(l2cache_inval)
+
+ENTRY(l2cache_enable)
+ mfspr %r3, SPR_L2CSR0
+ oris %r3, %r3, (L2CSR0_L2E | L2CSR0_L2PE)@h
+ isync
+ mtspr SPR_L2CSR0, %r3
+ isync
+ blr
+END(l2cache_enable)
+
+/*
+ * Branch predictor setup.
+ */
+ENTRY(bpred_enable)
+ mfspr %r3, SPR_BUCSR
+ ori %r3, %r3, BUCSR_BBFI
+ isync
+ mtspr SPR_BUCSR, %r3
+ isync
+ ori %r3, %r3, BUCSR_BPEN
+ isync
+ mtspr SPR_BUCSR, %r3
+ isync
+ blr
+END(bpred_enable)
+
+/*
+ * XXX: This should be moved to a shared AIM/booke asm file, if one ever is
+ * created.
+ */
+ENTRY(get_spr)
+ /* Note: The spr number is patched at runtime */
+ mfspr %r3, 0
+ blr
+END(get_spr)
+
+/************************************************************************/
+/* Data section */
+/************************************************************************/
+ .data
+ .align 3
+GLOBAL(__startkernel)
+ ADDR(begin)
+GLOBAL(__endkernel)
+ ADDR(end)
+ .align 4
+tmpstack:
+ .space TMPSTACKSZ
+tmpstackbound:
+ .space 10240 /* XXX: this really should not be necessary */
+#ifdef __powerpc64__
+TOC_ENTRY(tmpstack)
+#ifdef SMP
+TOC_ENTRY(bp_kernload)
+#endif
+#endif
+
+/*
+ * Compiled KERNBASE locations
+ */
+ .globl kernbase
+ .set kernbase, KERNBASE
+
+#include <powerpc/booke/trap_subr.S>