freebsd-dev/sys/powerpc/booke/locore.S
Conrad Meyer 78599c32ef Add CFI start/end proc directives to arm64, i386, and ppc
Follow-up to r353959 and r368070: do the same for other architectures.

arm32 already seems to use its own .fnstart/.fnend directives, which
appear to be ARM-specific variants of the same thing.  Likewise, MIPS
uses .frame directives.

Reviewed by:	arichardson
Differential Revision:	https://reviews.freebsd.org/D27387
2020-12-05 00:33:28 +00:00

980 lines
21 KiB
ArmAsm

/*-
* 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.
*
* $FreeBSD$
*/
#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>