patch-2.3.16 linux/arch/ppc/kernel/head_8xx.S

Next file: linux/arch/ppc/kernel/idle.c
Previous file: linux/arch/ppc/kernel/head.S
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.15/linux/arch/ppc/kernel/head_8xx.S linux/arch/ppc/kernel/head_8xx.S
@@ -0,0 +1,903 @@
+/*
+ *  arch/ppc/kernel/except_8xx.S
+ *
+ *  $Id: head_8xx.S,v 1.2 1999/08/23 02:53:19 paulus Exp $
+ *
+ *  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>
+ *  Low-level exception handlers and MMU support
+ *  rewritten by Paul Mackerras.
+ *    Copyright (C) 1996 Paul Mackerras.
+ *  MPC8xx modifications by Dan Malek
+ *    Copyright (C) 1997 Dan Malek (dmalek@jlc.net).
+ *
+ *  This file contains low-level support and setup for PowerPC 8xx
+ *  embedded processors, including trap and interrupt dispatch.
+ *
+ *  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 "ppc_asm.h"
+#include <asm/processor.h>
+#include <asm/page.h>
+#include <linux/config.h>
+#include <asm/mmu.h>
+	
+	.text
+	.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:
+
+/* MPC8xx
+ * This port was done on an MBX board with an 860.  Right now I only
+ * support an ELF compressed (zImage) boot from EPPC-Bug because the
+ * code there loads up some registers before calling us:
+ *   r3: ptr to board info 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
+ *
+ * I decided to use conditional compilation instead of checking PVR and
+ * adding more processor specific branches around code I don't need.
+ * Since this is an embedded processor, I also appreciate any memory
+ * savings I can get.
+ *
+ * The MPC8xx does not have any BATs, but it supports large page sizes.
+ * We first initialize the MMU to support 8M byte pages, then load one
+ * entry into each of the instruction and data TLBs to map the first
+ * 8M 1:1.  I also mapped an additional I/O space 1:1 so we can get to
+ * the "internal" processor registers before MMU_init is called.
+ *
+ * The TLB code currently contains a major hack.  Since I use the condition
+ * code register, I have to save and restore it.  I am out of registers, so
+ * I just store it in memory location 0 (the TLB handlers are not reentrant).
+ * To avoid making any decisions, I need to use the "segment" valid bit
+ * in the first level table, but that would require many changes to the
+ * Linux page directory/table functions that I don't want to do right now.
+ *
+ * I used to use SPRG2 for a temporary register in the TLB handler, but it
+ * has since been put to other uses.  I now use a hack to save a register
+ * and the CCR at memory location 0.....Someday I'll fix this.....
+ *	-- Dan
+ */
+	
+	.globl	__start
+__start:
+	mr	r31,r3			/* save parameters */
+	mr	r30,r4
+	mr	r29,r5
+	mr	r28,r6
+	mr	r27,r7
+	li	r24,0			/* cpu # */
+
+	tlbia			/* Invalidate all TLB entries */
+	li	r8, 0
+	mtspr	MI_CTR, r8	/* Set instruction control to zero */
+	lis	r8, MD_RESETVAL@h
+	mtspr	MD_CTR, r8	/* Set data TLB control */
+
+	/* Now map the lower 8 Meg into the TLBs.  For this quick hack,
+	 * we can load the instruction and data TLB registers with the
+	 * same values.
+	 */
+	lis	r8, KERNELBASE@h	/* Create vaddr for TLB */
+	ori	r8, r8, MI_EVALID	/* Mark it valid */
+	mtspr	MI_EPN, r8
+	mtspr	MD_EPN, r8
+	li	r8, MI_PS8MEG		/* Set 8M byte page */
+	ori	r8, r8, MI_SVALID	/* Make it valid */
+	mtspr	MI_TWC, r8
+	mtspr	MD_TWC, r8
+	li	r8, MI_BOOTINIT		/* Create RPN for address 0 */
+	mtspr	MI_RPN, r8		/* Store TLB entry */
+	mtspr	MD_RPN, r8
+	lis	r8, MI_Kp@h		/* Set the protection mode */
+	mtspr	MI_AP, r8
+	mtspr	MD_AP, r8
+
+/* We will get these from a configuration file as soon as I verify
+ * the extraneous bits don't cause problems in the TLB.
+ */
+#if defined(CONFIG_MBX) || defined(CONFIG_RPXLITE)
+#define BOOT_IMMR      0xfa000000
+#endif
+#ifdef CONFIG_BSEIP
+#define BOOT_IMMR      0xff000000
+#endif
+	/* Map another 8 MByte at 0xfa000000 to get the processor
+	 * internal registers (among other things).
+	 */
+	lis     r8, BOOT_IMMR@h         /* Create vaddr for TLB */
+	ori	r8, r8, MD_EVALID	/* Mark it valid */
+	mtspr	MD_EPN, r8
+	li	r8, MD_PS8MEG		/* Set 8M byte page */
+	ori	r8, r8, MD_SVALID	/* Make it valid */
+	mtspr	MD_TWC, r8
+	lis     r8, BOOT_IMMR@h         /* Create paddr for TLB */
+	ori	r8, r8, MI_BOOTINIT|0x2 /* Inhibit cache -- Cort */
+	mtspr	MD_RPN, r8
+
+	/* Since the cache is enabled according to the information we
+	 * just loaded into the TLB, invalidate and enable the caches here.
+	 * We should probably check/set other modes....later.
+	 */
+	lis	r8, IDC_INVALL@h
+	mtspr	IC_CST, r8
+	mtspr	DC_CST, r8
+	lis	r8, IDC_ENABLE@h
+	mtspr	IC_CST, r8
+#if 0
+	mtspr	DC_CST, r8
+#else
+	/* For a debug option, I left this here to easily enable
+	 * the write through cache mode
+	 */
+	lis	r8, DC_SFWT@h
+	mtspr	DC_CST, r8
+	lis	r8, IDC_ENABLE@h
+	mtspr	DC_CST, r8
+#endif
+
+/* We now have the lower 8 Meg mapped into TLB entries, and the caches
+ * ready to work.
+ */
+
+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 */
+
+/*
+ * 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:	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);
+/*
+ * 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; 		\
+	.long	hdlr;				\
+	.long	ret_from_except
+
+/* System reset */
+#ifdef CONFIG_SMP /* MVME/MTX start the secondary here */
+	STD_EXCEPTION(0x100, Reset, __secondary_start_psurge)
+#else
+	STD_EXCEPTION(0x100, Reset, UnknownException)
+#endif	
+
+/* Machine check */
+	STD_EXCEPTION(0x200, MachineCheck, MachineCheckException)
+
+/* Data access exception.
+ * This is "never generated" by the MPC8xx.  We jump to it for other
+ * translation errors.
+ */
+	. = 0x300
+DataAccess:
+	EXCEPTION_PROLOG
+	mfspr	r20,DSISR
+	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
+	.long	do_page_fault
+	.long	ret_from_except
+
+/* Instruction access exception.
+ * This is "never generated" by the MPC8xx.  We jump to it for other
+ * translation errors.
+ */
+	. = 0x400
+InstructionAccess:
+	EXCEPTION_PROLOG
+	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
+	.long	do_page_fault
+	.long	ret_from_except
+
+/* External interrupt */
+	. = 0x500;
+HardwareInterrupt:
+	EXCEPTION_PROLOG;
+#ifdef CONFIG_APUS
+	/* This is horrible, but there's no way around it. Enable the
+	data cache so the IRQ hardware register can be accessed
+	without cache intervention. Then disable interrupts and get
+	the current emulated m68k IPL value. */
+	
+	mfmsr	20
+	xori	r20,r20,MSR_DR
+	sync
+	mtmsr	r20
+	sync
+
+	lis	r3,APUS_IPL_EMU@h
+
+	li	r20,(IPLEMU_SETRESET|IPLEMU_DISABLEINT)
+	stb	r20,APUS_IPL_EMU@l(r3)
+	eieio
+
+	lbz	r3,APUS_IPL_EMU@l(r3)
+
+	mfmsr	r20
+	xori	r20,r20,MSR_DR
+	sync
+	mtmsr	r20
+	sync
+
+	stw	r3,(_CCR+4)(r21);
+#endif
+	addi	r3,r1,STACK_FRAME_OVERHEAD
+	li	r20,MSR_KERNEL
+	li	r4,0
+	bl	transfer_to_handler
+	.long	do_IRQ;
+	.long	ret_from_except
+	
+
+/* 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
+	.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
+	.long	ProgramCheckException
+	.long	ret_from_except
+
+/* No FPU on MPC8xx.  This exception is not supposed to happen.
+*/
+	STD_EXCEPTION(0x800, FPUnavailable, UnknownException)
+
+	STD_EXCEPTION(0x900, Decrementer, timer_interrupt)
+	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)
+	STD_EXCEPTION(0xf00, Trap_0f, UnknownException)
+
+/* On the MPC8xx, this is a software emulation interrupt.  It occurs
+ * for all unimplemented and illegal instructions.
+ */
+	STD_EXCEPTION(0x1000, SoftEmu, SoftwareEmulation)
+
+	. = 0x1100
+/*
+ * For the MPC8xx, this is a software tablewalk to load the instruction
+ * TLB.  It is modelled after the example in the Motorola manual.  The task
+ * switch loads the M_TWB register with the pointer to the first level table.
+ * If we discover there is no second level table (the value is zero), the
+ * plan was to load that into the TLB, which causes another fault into the
+ * TLB Error interrupt where we can handle such problems.  However, that did
+ * not work, so if we discover there is no second level table, we restore
+ * registers and branch to the error exception.  We have to use the MD_xxx
+ * registers for the tablewalk because the equivalent MI_xxx registers
+ * only perform the attribute functions.
+ */
+InstructionTLBMiss:
+	mtspr	M_TW, r20	/* Save a couple of working registers */
+	mfcr	r20
+	stw	r20, 0(r0)
+	stw	r21, 4(r0)
+	mfspr	r20, SRR0	/* Get effective address of fault */
+	mtspr	MD_EPN, r20	/* Have to use MD_EPN for walk, MI_EPN can't */
+	mfspr	r20, M_TWB	/* Get level 1 table entry address */
+	lwz	r21, 0(r20)	/* Get the level 1 entry */
+	rlwinm.	r20, r21,0,0,20	/* Extract page descriptor page address */
+	beq	2f		/* If zero, don't try to find a pte */
+
+	/* We have a pte table, so load the MI_TWC with the attributes
+	 * for this page, which has only bit 31 set.
+	 */
+	tophys(r21,r21)
+	ori	r21,r21,1		/* Set valid bit */
+	mtspr	MI_TWC, r21	/* Set page attributes */
+	mtspr	MD_TWC, r21	/* Load pte table base address */
+	mfspr	r21, MD_TWC	/* ....and get the pte address */
+	lwz	r21, 0(r21)	/* Get the pte */
+
+	/* Set four subpage valid bits (24, 25, 26, and 27).
+	 * Since we currently run MI_CTR.PPCS = 0, the manual says,
+	 *	"If the page size is larger than 4k byte, then all the
+	 *	 4 bits should have the same value."
+	 * I don't really know what to do if the page size is 4k Bytes,
+	 * but I know setting them all to 0 does not work, and setting them
+	 * all to 1 does, so that is the way it is right now.
+	 * BTW, these four bits map to the software only bits in the
+	 * linux page table.  I used to turn them all of, but now just
+	 * set them all for the hardware.
+	li	r20, 0x00f0
+	andc	r20, r21, r20
+	ori	r20, r20, 0x0080
+	 */
+	ori	r20, r21, 0x00f0
+
+	mtspr	MI_RPN, r20	/* Update TLB entry */
+
+	mfspr	r20, M_TW	/* Restore registers */
+	lwz	r21, 0(r0)
+	mtcr	r21
+	lwz	r21, 4(r0)
+	rfi
+
+2:	mfspr	r20, M_TW	/* Restore registers */
+	lwz	r21, 0(r0)
+	mtcr	r21
+	lwz	r21, 4(r0)
+	b	InstructionAccess
+
+	. = 0x1200
+DataStoreTLBMiss:
+	mtspr	M_TW, r20	/* Save a couple of working registers */
+	mfcr	r20
+	stw	r20, 0(r0)
+	stw	r21, 4(r0)
+	mfspr	r20, M_TWB	/* Get level 1 table entry address */
+	lwz	r21, 0(r20)	/* Get the level 1 entry */
+	rlwinm.	r20, r21,0,0,20	/* Extract page descriptor page address */
+	beq	2f		/* If zero, don't try to find a pte */
+
+	/* We have a pte table, so load fetch the pte from the table.
+	 */
+	tophys(r21, r21)
+	ori	r21, r21, 1	/* Set valid bit in physical L2 page */
+	mtspr	MD_TWC, r21	/* Load pte table base address */
+	mfspr	r21, MD_TWC	/* ....and get the pte address */
+	lwz	r21, 0(r21)	/* Get the pte */
+
+	/* Set four subpage valid bits (24, 25, 26, and 27).
+	 * Since we currently run MD_CTR.PPCS = 0, the manual says,
+	 *	"If the page size is larger than 4k byte, then all the
+	 *	 4 bits should have the same value."
+	 * I don't really know what to do if the page size is 4k Bytes,
+	 * but I know setting them all to 0 does not work, and setting them
+	 * all to 1 does, so that is the way it is right now.
+	 * BTW, these four bits map to the software only bits in the
+	 * linux page table.  I used to turn them all of, but now just
+	 * set them all for the hardware.
+	li	r20, 0x00f0
+	andc	r20, r21, r20
+	ori	r20, r20, 0x0080
+	 */
+	ori	r20, r21, 0x00f0
+
+	mtspr	MD_RPN, r20	/* Update TLB entry */
+
+	mfspr	r20, M_TW	/* Restore registers */
+	lwz	r21, 0(r0)
+	mtcr	r21
+	lwz	r21, 4(r0)
+	rfi
+
+2:	mfspr	r20, M_TW	/* Restore registers */
+	lwz	r21, 0(r0)
+	mtcr	r21
+	lwz	r21, 4(r0)
+	b	DataAccess
+
+/* This is an instruction TLB error on the MPC8xx.  This could be due
+ * to many reasons, such as executing guarded memory or illegal instruction
+ * addresses.  There is nothing to do but handle a big time error fault.
+ */
+	. = 0x1300
+InstructionTLBError:
+	b	InstructionAccess
+
+/* This is the data TLB error on the MPC8xx.  This could be due to
+ * many reasons, including a dirty update to a pte.  We can catch that
+ * one here, but anything else is an error.  First, we track down the
+ * Linux pte.  If it is valid, write access is allowed, but the
+ * page dirty bit is not set, we will set it and reload the TLB.  For
+ * any other case, we bail out to a higher level function that can
+ * handle it.
+ */
+	. = 0x1400
+DataTLBError:
+	mtspr	M_TW, r20	/* Save a couple of working registers */
+	mfcr	r20
+	stw	r20, 0(r0)
+	stw	r21, 4(r0)
+
+	/* First, make sure this was a store operation.
+	*/
+	mfspr	r20, DSISR
+	andis.	r21, r20, 0x0200	/* If set, indicates store op */
+	beq	2f
+
+	mfspr	r20, M_TWB	/* Get level 1 table entry address */
+	lwz	r21, 0(r20)	/* Get the level 1 entry */
+	rlwinm.	r20, r21,0,0,20	/* Extract page descriptor page address */
+	beq	2f		/* If zero, bail */
+
+	/* We have a pte table, so fetch the pte from the table.
+	 */
+	tophys(r21, r21)
+	ori	r21, r21, 1		/* Set valid bit in physical L2 page */
+	mtspr	MD_TWC, r21		/* Load pte table base address */
+	mfspr	r21, MD_TWC		/* ....and get the pte address */
+	lwz	r21, 0(r21)		/* Get the pte */
+
+	andi.	r20, r21, _PAGE_RW	/* Is it writeable? */
+	beq	2f			/* Bail out if not */
+
+	ori	r21, r21, _PAGE_DIRTY	/* Update changed bit */
+	mfspr	r20, MD_TWC		/* Get pte address again */
+	stw	r21, 0(r20)		/* and update pte in table */
+
+	/* Set four subpage valid bits (24, 25, 26, and 27).
+	 * Since we currently run MD_CTR.PPCS = 0, the manual says,
+	 *	"If the page size is larger than 4k byte, then all the
+	 *	 4 bits should have the same value."
+	 * I don't really know what to do if the page size is 4k Bytes,
+	 * but I know setting them all to 0 does not work, and setting them
+	 * all to 1 does, so that is the way it is right now.
+	 * BTW, these four bits map to the software only bits in the
+	 * linux page table.  I used to turn them all of, but now just
+	 * set them all for the hardware.
+	li	r20, 0x00f0
+	andc	r20, r21, r20
+	ori	r20, r20, 0x0080
+	 */
+	ori	r20, r21, 0x00f0
+
+	mtspr	MD_RPN, r20	/* Update TLB entry */
+
+	mfspr	r20, M_TW	/* Restore registers */
+	lwz	r21, 0(r0)
+	mtcr	r21
+	lwz	r21, 4(r0)
+	rfi
+2:
+	mfspr	r20, M_TW	/* Restore registers */
+	lwz	r21, 0(r0)
+	mtcr	r21
+	lwz	r21, 4(r0)
+	b	DataAccess
+#endif /* CONFIG_8xx */
+
+	STD_EXCEPTION(0x1500, Trap_15, UnknownException)
+	STD_EXCEPTION(0x1600, Trap_16, UnknownException)
+	STD_EXCEPTION(0x1700, Trap_17, TAUException)
+	STD_EXCEPTION(0x1800, Trap_18, UnknownException)
+	STD_EXCEPTION(0x1900, Trap_19, UnknownException)
+	STD_EXCEPTION(0x1a00, Trap_1a, UnknownException)
+	STD_EXCEPTION(0x1b00, Trap_1b, UnknownException)
+/* On the MPC8xx, these next four traps are used for development
+ * support of breakpoints and such.  Someday I will get around to
+ * using them.
+ */
+	STD_EXCEPTION(0x1c00, Trap_1c, UnknownException)
+	STD_EXCEPTION(0x1d00, Trap_1d, UnknownException)
+	STD_EXCEPTION(0x1e00, Trap_1e, UnknownException)
+	STD_EXCEPTION(0x1f00, Trap_1f, UnknownException)
+
+	. = 0x2000
+
+/*
+ * 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)
+	lis	r22,MSR_POW@h
+	andc	r23,r23,r22
+	stw	r23,_MSR(r21)
+	SAVE_GPR(7, 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 */
+	beq	2f
+	addi	r24,r1,STACK_FRAME_OVERHEAD
+	stw	r24,PT_REGS(r23)
+2:	addi	r2,r23,-THREAD		/* set r2 to current */
+	tovirt(r2,r2)
+	mflr	r23
+	andi.	r24,r23,0x3f00		/* get vector offset */
+	stw	r24,TRAP(r21)
+	li	r22,RESULT
+	stwcx.	r22,r22,r21		/* to clear the reservation */
+	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 */
+	mtspr	SRR0,r24
+	mtspr	SRR1,r20
+	mtlr	r23
+	SYNC
+	rfi				/* jump to handler, enable MMU */
+
+/*
+ * 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
+	mtspr	SRR0,r24
+	mtspr	SRR1,r20
+	SYNC
+	rfi
+
+	.globl	giveup_fpu
+giveup_fpu:
+	blr
+
+/*
+ * This code is jumped to from the startup code to copy
+ * the kernel image to physical address 0.
+ */
+relocate_kernel:
+	lis	r9,0x426f		/* if booted from BootX, don't */
+	addi	r9,r9,0x6f58		/* translate source addr */
+	cmpw	r31,r9			/* (we have to on chrp) */
+	beq	7f
+	rlwinm	r4,r4,0,8,31		/* translate source address */
+	add	r4,r4,r3		/* to region mapped with BATs */
+7:	addis	r9,r26,klimit@ha	/* fetch klimit */
+	lwz	r25,klimit@l(r9)
+	addis	r25,r25,-KERNELBASE@h
+	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,8
+	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
+	isync
+	addi	r5,r5,4
+	addi	r6,r6,4
+	blr
+
+#ifdef CONFIG_SMP
+	.globl	__secondary_start_psurge
+__secondary_start_psurge:
+	li	r24,1			/* cpu # */
+	b	__secondary_start
+
+	.globl	__secondary_hold
+__secondary_hold:
+	/* tell the master we're here */
+	lis	r5,0x4@h
+	ori	r5,r5,0x4@l
+	stw	r3,0(r5)
+	dcbf	0,r5
+100:
+	lis	r5,0
+	dcbi	0,r5
+	lwz	r4,0(r5)
+	/* wait until we're told to start */
+	cmp	0,r4,r3
+	bne	100b
+	/* our cpu # was at addr 0 - go */
+	lis	r5,__secondary_start@h
+	ori	r5,r5,__secondary_start@l
+	tophys(r5,r5)
+	mtlr	r5
+	mr	r24,r3			/* cpu # */
+	blr
+#endif /* CONFIG_SMP */
+	
+/*
+ * This is where the main kernel code starts.
+ */
+start_here:
+#ifdef __SMP__
+	/* if we're the second cpu stack and r2 are different
+	* and we want to not clear the bss -- Cort */
+	lis	r5,first_cpu_booted@h
+	ori	r5,r5,first_cpu_booted@l
+	lwz	r5,0(r5)
+	cmpi	0,r5,0
+	beq	99f
+
+	/* get current */
+	lis	r2,current_set@h
+	ori	r2,r2,current_set@l
+	slwi	r24,r24,2			/* cpu # to current_set[cpu#] */
+	add	r2,r2,r24
+	lwz	r2,0(r2)
+	b	10f
+99:	
+#endif /* __SMP__ */
+	/* ptr to current */
+	lis	r2,init_task_union@h
+	ori	r2,r2,init_task_union@l
+	/* Clear out the BSS */
+	lis	r11,_end@ha
+	addi	r11,r11,_end@l
+	lis	r8,__bss_start@ha
+	addi	r8,r8,__bss_start@l
+	subf	r11,r8,r11
+	addi	r11,r11,3
+	rlwinm.	r11,r11,30,2,31
+	beq	2f
+	addi	r8,r8,-4
+	mtctr	r11
+	li	r0,0
+3:	stwu	r0,4(r8)
+	bdnz	3b
+2:
+#ifdef __SMP__	
+10:
+#endif /* __SMP__ */
+	/* stack */
+	addi	r1,r2,TASK_UNION_SIZE
+	li	r0,0
+	stwu	r0,-STACK_FRAME_OVERHEAD(r1)
+/*
+ * Decide what sort of machine this is and initialize the MMU.
+ */
+	mr	r3,r31
+	mr	r4,r30
+	mr	r5,r29
+	mr	r6,r28
+	mr	r7,r27
+	bl	identify_machine
+	bl	MMU_init
+
+/*
+ * 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.
+ * On the 8xx, all we have to do is invalidate the TLB to clear
+ * the old 8M byte TLB mappings and load the page table base register.
+ */
+	/* The right way to do this would be to track it down through
+	 * init's THREAD like the context switch code does, but this is
+	 * easier......until someone changes init's static structures.
+	 */
+	lis	r6, swapper_pg_dir@h
+	tophys(r6,r6)
+	ori	r6, r6, swapper_pg_dir@l
+	mtspr	M_TWB, r6
+	lis	r4,2f@h
+	ori	r4,r4,2f@l
+	tophys(r4,r4)
+	li	r3,MSR_KERNEL & ~(MSR_IR|MSR_DR)
+	mtspr	SRR0,r4
+	mtspr	SRR1,r3
+	rfi
+/* Load up the kernel context */
+2:
+	SYNC			/* Force all PTE updates to finish */
+	tlbia			/* Clear all TLB entries */
+	sync			/* wait for tlbia/tlbie to finish */
+#ifdef __SMP__
+	tlbsync			/* ... on all CPUs */
+	sync
+#endif
+/* Set up for using our exception vectors */
+	/* ptr to phys current thread */
+	tophys(r4,r2)
+	addi	r4,r4,THREAD	/* init task's THREAD */
+	mtspr	SPRG3,r4
+	li	r3,0
+	mtspr	SPRG2,r3	/* 0 => r1 has kernel sp */
+/* Now turn on the MMU for real! */
+	li	r4,MSR_KERNEL
+	lis	r3,start_kernel@h
+	ori	r3,r3,start_kernel@l
+#ifdef __SMP__
+	/* the second time through here we go to
+	 * start_secondary(). -- Cort
+	 */
+	lis	r5,first_cpu_booted@h
+	ori	r5,r5,first_cpu_booted@l
+	tophys(r5,r5)
+	lwz	r5,0(r5)
+	cmpi	0,r5,0
+	beq	10f
+	lis	r3,start_secondary@h
+	ori	r3,r3,start_secondary@l
+10:	
+#endif /* __SMP__ */
+	mtspr	SRR0,r3
+	mtspr	SRR1,r4
+	rfi			/* enable MMU and jump to start_kernel */
+
+/*
+ * Set up to use a given MMU context.
+ *
+ * The MPC8xx has something that currently happens "automagically."
+ * Unshared user space address translations are subject to ASID (context)
+ * match.  During each task switch, the ASID is incremented.  We can
+ * guarantee (I hope :-) that no entries currently match this ASID
+ * because every task will cause at least a TLB entry to be loaded for
+ * the first instruction and data access, plus the kernel running will
+ * have displaced several more TLBs.  The MMU contains 32 entries for
+ * each TLB, and there are 16 contexts, so we just need to make sure
+ * two pages get replaced for every context switch, which currently
+ * happens.  There are other TLB management techniques that I will
+ * eventually implement, but this is the easiest for now.  -- Dan
+ *
+ * On the MPC8xx, we place the physical address of the new task
+ * page directory loaded into the MMU base register, and set the
+ * ASID compare register with the new "context".
+ */
+_GLOBAL(set_context)
+        mtspr   M_CASID,r3		/* Update context */
+        tlbia
+	SYNC
+	blr
+
+/* 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  m8xx_gorom
+m8xx_gorom:
+       li      r5,MSR_KERNEL & ~(MSR_IR|MSR_DR)
+       lis     r6,2f@h
+       addis   r6,r6,-KERNELBASE@h
+       ori     r6,r6,2f@l
+       mtspr   SRR0,r6
+       mtspr   SRR1,r5
+       rfi
+2:
+       mtlr    r4
+       blr
+	
+/*
+ * 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

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)