code:
/*	$OpenBSD: locore.S,v 1.50 2015/07/29 18:52:44 miod Exp $	*/
/*	$NetBSD: locore.S,v 1.2 1996/10/16 19:33:09 ws Exp $	*/

/*
 * Copyright (C) 1995, 1996 Wolfgang Solfrank.
 * Copyright (C) 1995, 1996 TooLs GmbH.
 * 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.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by TooLs GmbH.
 * 4. The name of TooLs GmbH may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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.h"

#include <sys/syscall.h>

#include <machine/asm.h>
#include <machine/param.h>
#include <machine/pmap.h>
#include <machine/psl.h>
#include <machine/trap.h>

#define GET_CPUINFO(r)	mfsprg r,0

#define	INTSTK		(8*1024)	/* 8K interrupt stack */
#define	SPILLSTK	(1*1024)
#define	DDBSTK		(7*1024)

/*
 * Globals
 */
	.globl	_C_LABEL(esym),_C_LABEL(proc0paddr)
	.type	_C_LABEL(esym),@object
	.type	_C_LABEL(proc0paddr),@object
	.data
_C_LABEL(esym):		.long	0	/* end of symbol table */
_C_LABEL(proc0paddr):	.long	0	/* proc0 p_addr */

fwargsave:
			.long 0
			.long 0

/*
 * Startup entry
 */
_ENTRY(_C_LABEL(kernel_text))
_ENTRY(_ASM_LABEL(start))
/* arguments to start
 * r1 - stack provided by firmware/bootloader
 * r3 - unused
 * r4 - unused
 * r5 - firmware pointer (NULL for PPC1bug)
 * r6 - arg list
 * r7 - length
 */
	.globl	start
	.type start,@function
start:
	lis	%r3, fwargsave@ha
	stw	%r6, fwargsave@l(%r3)
	stw	%r7, fwargsave@l+4(%r3)
	bl	ofw_init
	li	%r0,0
	mtmsr	%r0			/* Disable FPU/MMU/exceptions */
	isync

/* compute end of kernel memory */
	lis	%r8,_end@ha
	addi	%r8,%r8,_end@l
	lis	%r3, fwargsave@ha
	lwz	%r6, fwargsave@l(%r3)
	lwz	%r7, fwargsave@l+4(%r3)
#if defined(DDB)
	cmpwi	%r6, 0
	beq	1f
	add	%r9,%r6,%r7
	lwz	%r9, -4(%r9)
	cmpwi   %r9,0
	beq	1f
	lis	%r8,_C_LABEL(esym)@ha
	stw	%r9,_C_LABEL(esym)@l(%r8)
	mr	%r8, %r9
1:
#endif
	li	%r9,PGOFSET
	add	%r8,%r8,%r9
	andc	%r8,%r8,%r9
	lis	%r9,_C_LABEL(cpu_info)@ha
	addi	%r9,%r9,_C_LABEL(cpu_info)@l
	mtsprg	0,%r9
	addi	%r8,%r8,INTSTK
	stw	%r8,CI_INTSTK(%r9)
	li	%r0,-1
	stw	%r0,CI_INTRDEPTH(%r9)
	addi	%r8,%r8,SPILLSTK+DDBSTK	/* leave room for spillstk and ddbstk */
	lis	%r9,_C_LABEL(proc0paddr)@ha
	stw	%r8,_C_LABEL(proc0paddr)@l(%r9)
	addi	%r1,%r8,USPACE-FRAMELEN	/* stackpointer for proc0 */
	mr	%r4,%r1			/* end of mem reserved for kernel */
	li	%r0,0
	stwu	%r0,-16(%r1)		/* end of stack chain */

	lis	%r3,start@ha
	addi	%r3,%r3,start@l
	mr	%r5,%r6			/* args string */
	bl	_C_LABEL(initppc)
	bl	_C_LABEL(main)
	b	_C_LABEL(OF_exit)

#ifdef MULTIPROCESSOR
_ENTRY(_C_LABEL(cpu_spinup_trampoline))
	lis	%r3,_C_LABEL(cpu_hatch_stack)@ha
	lwz	%r1,_C_LABEL(cpu_hatch_stack)@l(%r3)

	b	_C_LABEL(cpu_hatch)
	/* NOTREACHED */
#endif

/*
 * void cpu_switchto(struct proc *old, struct proc *new)
 * Switch from "old" proc to "new".
 */
_ENTRY(_C_LABEL(cpu_switchto_asm))
	mflr	%r0			/* save lr */
	stw	%r0,4(%r1)
	stwu	%r1,-16(%r1)
	stw	%r31,12(%r1)
	stw	%r30,8(%r1)

/*
 * r3 - old proc
 * r4 -	new proc
 * r5 - cpuinfo
 */
	GET_CPUINFO(%r5)

	li	%r31,SONPROC
	stb	%r31,P_STAT(%r4)

	or.	%r3,%r3,%r3		/* old process was exiting? */
	beq	switch_exited

	mfsr	%r10,PPC_USER_SR	/* save PPC_USER_SR for copyin/copyout*/
	mfcr	%r11			/* save cr */
	mr	%r12,%r2		/* save r2 */
	stwu	%r1,-SFRAMELEN(%r1)	/* still running on old stack */
	stmw	%r10,8(%r1)
	lwz	%r31,P_ADDR(%r3)
	stw	%r1,PCB_SP(%r31)	/* save SP */

switch_exited:
	/* disable interrupts while actually switching */
	mfmsr	%r30
	andi.	%r30,%r30,~PSL_EE@l
	mtmsr	%r30

	stw	%r4,CI_CURPROC(%r5)	/* record new process */

#ifdef MULTIPROCESSOR
	stw	%r5,P_CPU(%r4)
#endif

	lwz	%r31,P_ADDR(%r4)
	stw	%r31,CI_CURPCB(%r5)	/* indicate new pcb */

	lwz	%r6,PCB_PMR(%r31)

	/* save real pmap pointer for spill fill */
	stwu	%r6,CI_CURPM(%r5)
	stwcx.	%r6,%r0,%r5		/* clear possible reservation */

	addic.	%r6,%r6,64
	li	%r5,0

	lwz	%r1,PCB_SP(%r31)	/* get new procs SP */

	ori	%r30,%r30,PSL_EE	/* interrupts are okay again */
	mtmsr	%r30

	lmw	%r10,8(%r1)		/* get other regs */
	lwz	%r1,0(%r1)		/* get saved SP */
	mr	%r2,%r12		/* get saved r2 */
	mtcr	%r11			/* get saved cr */
	isync
	mtsr	PPC_USER_SR,%r10	/* get saved PPC_USER_SR */
	isync

	lwz	%r31,12(%r1)
	lwz	%r30,8(%r1)
	addi	%r1,%r1,16
	lwz	%r0,4(%r1)
	mtlr	%r0
	blr

_ENTRY(_C_LABEL(cpu_idle_enter))
	lis	%r4, _C_LABEL(ppc_cpuidle)@ha
	lwz	%r4, _C_LABEL(ppc_cpuidle)@l(%r4)
	cmpwi	%r4, 0
	beq	1f
	/* must disable external interrupts during idle queue checking */
	mfmsr	%r3
	andi.	%r3,%r3,~PSL_EE@l
	mtmsr	%r3
1:
	blr

_ENTRY(_C_LABEL(cpu_idle_cycle))
	lis	%r4, _C_LABEL(ppc_cpuidle)@ha
	lwz	%r4, _C_LABEL(ppc_cpuidle)@l(%r4)
	cmpwi	%r4, 0
	beq	idledone

	/*
	 * Interrupts were disabled in cpu_idle_enter, but must be
	 * enabled for sleeping, but before that indicate that we
	 * are entering sleep mode.
	 */
	GET_CPUINFO(%r4)
	lwz	%r6,CI_FLAGS(%r4)
	ori	%r6,%r6,CI_FLAGS_SLEEPING@l
	stw	%r6,CI_FLAGS(%r4)

	/* enable interrupts, required before setting POW */
	mfmsr	%r3
	ori	%r5,%r3,PSL_EE@l
	mtmsr	%r5
	oris	%r5, %r5, PSL_POW@h
	sync
	/* low power mode */
	mtmsr	%r5
	sync
	isync

	/*
	 * restore interrupts to disabled, so CI_FLAGS is only modified
	 * with interrupts disabled.
	 */
	mtmsr %r3
	isync

	/* clear CI_FLAGS_SLEEPING since sleeping is over */
	andi.	%r6,%r6,~CI_FLAGS_SLEEPING@l
	stw	%r6,CI_FLAGS(%r4)
idledone:
	blr

_ENTRY(_C_LABEL(cpu_idle_leave))
	lis	%r4, _C_LABEL(ppc_cpuidle)@ha
	lwz	%r4, _C_LABEL(ppc_cpuidle)@l(%r4)
	cmpwi	%r4, 0
	beq	1f
	/* enable interrupts disabled in cpu_idle_enter. */
	mfmsr	%r3
	ori	%r3,%r3,PSL_EE@l
	mtmsr	%r3
1:
	blr

/*
 * This code gets copied to all the trap vectors
 * except ISI/DSI, ALI, and the interrupts
 */
	.text
	.globl	_C_LABEL(trapcode),_C_LABEL(trapsize)
	.type	_C_LABEL(trapcode),@function
	.type	_C_LABEL(trapsize),@object
_C_LABEL(trapcode):
	mtsprg	1,%r1			/* save SP */
nop32_1s:
	mfmsr	%r1
	clrldi	%r1,%r1,1
	mtmsrd	%r1
nop32_1e:
	GET_CPUINFO(%r1)
	stmw	%r28,CI_TEMPSAVE(%r1)	/* free r28-r31 */
	mflr	%r28			/* save LR */
	mfcr	%r29			/* save CR */
	mfsprg	%r1,1			/* restore SP */

	/* Test whether we already had PR set */
	mfsrr1	%r31
	mtcr	%r31
	bc	4,17,1f			/* branch if PSL_PR is clear */
	GET_CPUINFO(%r1)
	lwz	%r1,CI_CURPCB(%r1)
	addi	%r1,%r1,USPACE		/* stack is top of user struct */
1:
	bla	s_trap
_C_LABEL(trapsize) =	.-_C_LABEL(trapcode)

/*
 * For ALI: has to save DSISR and DAR
 */
	.globl	_C_LABEL(alitrap),_C_LABEL(alisize)
_C_LABEL(alitrap):
	mtsprg	1,%r1			/* save SP */
nop32_2s:
	mfmsr	%r1
	clrldi	%r1,%r1,1
	mtmsrd	%r1
nop32_2e:
	GET_CPUINFO(%r1)
	stmw	%r28,CI_TEMPSAVE(%r1)	/* free r28-r31 */
	mfdar	%r30
	mfdsisr	%r31
	stmw	%r30,CI_TEMPSAVE+16(%r1)
	mflr	%r28			/* save LR */
	mfcr	%r29			/* save CR */
	mfsprg	%r1,1			/* restore SP */

	/* Test whether we already had PR set */
	mfsrr1	%r31
	mtcr	%r31
	bc	4,17,1f			/* branch if PSL_PR is clear */
	GET_CPUINFO(%r1)
	lwz	%r1,CI_CURPCB(%r1)
	addi	%r1,%r1,USPACE		/* stack is top of user struct */
1:
	bla	s_trap
_C_LABEL(alisize) =	.-_C_LABEL(alitrap)

/*
 * Similar to the above for DSI
 * Has to handle BAT spills
 * and standard pagetable spills
 */
	.globl	_C_LABEL(dsitrap),_C_LABEL(dsisize)
	.type	_C_LABEL(dsitrap),@function
	.type	_C_LABEL(dsisize),@object
_C_LABEL(dsitrap):
	mtsprg	1,%r1
	GET_CPUINFO(%r1)
	stmw	%r28,CI_DISISAVE(%r1)	/* free r28-r31 */
nop32_3s:
	mfmsr	%r28
	clrldi	%r28,%r28,1
	mtmsrd	%r28
nop32_3e:
	mfsprg	%r1,1
	mfcr	%r29			/* save CR */
	mfxer	%r30			/* save XER */
	mtsprg	2,%r30			/* in SPRG2 */
	mfsrr1	%r31			/* test kernel mode */
nopbat_1s:
	mtcr	%r31
	bc	12,17,1f		/* branch if PSL_PR is set */
	mfdar	%r31			/* get fault address */
	rlwinm	%r31,%r31,7,25,28	/* get segment * 8 */
	addis	%r31,%r31,_C_LABEL(battable)@ha
	lwz	%r30,_C_LABEL(battable)@l(%r31)	/* get batu */
	mtcr	%r30
	bc	4,30,1f			/* branch if supervisor valid is false */
	lwz	%r31,_C_LABEL(battable)+4@l(%r31)	/* get batl */
/* We randomly use the highest two bat registers here */
	mftb	%r28
	andi.	%r28,%r28,1
	bne	2f
	mtdbatu	2,%r30
	mtdbatl	2,%r31
	b	3f
2:
	mtdbatu	3,%r30
	mtdbatl	3,%r31
3:
	mfsprg	%r30,2			/* restore XER */
	mtxer	%r30
	mtcr	%r29			/* restore CR */
	mtsprg	1,%r1
	GET_CPUINFO(%r1)
	lmw	%r28,CI_DISISAVE(%r1)	/* restore r28-r31 */
	mfsprg	1,%r1
	rfi				/* return to trapped code */
1:
nopbat_1e:
	mflr	%r28			/* save LR */
	bla	s_dsitrap
_C_LABEL(dsisize) =	.-_C_LABEL(dsitrap)

/*
 * Similar to the above for ISI
 */
	.globl	_C_LABEL(isitrap),_C_LABEL(isisize)
	.type	_C_LABEL(isitrap),@function
	.type	_C_LABEL(isisize),@object
_C_LABEL(isitrap):
	mtsprg	1,%r1                   /* save SP */
nop32_4s:
	mfmsr	%r1
	clrldi	%r1,%r1,1
	mtmsrd	%r1
nop32_4e:
	GET_CPUINFO(%r1)
	stmw	%r28,CI_DISISAVE(%r1)	/* free r28-r31 */
	mflr	%r28			/* save LR */
	mfcr	%r29			/* save CR */
	mfsrr1	%r31			/* test kernel mode */
	mfsprg	%r1,1                   /* restore SP */
	bla	s_isitrap
_C_LABEL(isisize) =	.-_C_LABEL(isitrap)

/*
 * This one for the external interrupt handler.
 */
	.globl	_C_LABEL(extint),_C_LABEL(extsize)
	.type	_C_LABEL(extint),@function
	.type	_C_LABEL(extsize),@object
_C_LABEL(extint):
	mtsprg	1,%r1			/* save SP */
nop32_5s:
	mfmsr	%r1
	clrldi	%r1,%r1,1
	mtmsrd	%r1
nop32_5e:
	GET_CPUINFO(%r1)
	stmw	%r28,CI_TEMPSAVE(%r1)	/* free r28-r31 */
	mflr	%r28			/* save LR */
	mfcr	%r29			/* save CR */
	mfxer	%r30			/* save XER */
	lwz	%r31,CI_INTRDEPTH(%r1)	/* were we already running on intstk? */
	addic.	%r31,%r31,1
	stw	%r31,CI_INTRDEPTH(%r1)
	lwz	%r1,CI_INTSTK(%r1)	/* get interrupt stack */
	beq	1f
	mfsprg	%r1,1			/* yes, get old SP */
1:
	ba	extintr
_C_LABEL(extsize) =	.-_C_LABEL(extint)

/*
 * And this one for the decrementer interrupt handler.
 */
	.globl	_C_LABEL(decrint),_C_LABEL(decrsize)
	.type	_C_LABEL(decrint),@function
	.type	_C_LABEL(decrsize),@object
_C_LABEL(decrint):
	mtsprg	1,%r1			/* save SP */
nop32_6s:
	mfmsr	%r1
	clrldi	%r1,%r1,1
	mtmsrd	%r1
nop32_6e:
	GET_CPUINFO(%r1)
	stmw	%r28,CI_TEMPSAVE(%r1)	/* free r28-r31 */
	mflr	%r28			/* save LR */
	mfcr	%r29			/* save CR */
	mfxer	%r30			/* save XER */
	lwz	%r31,CI_INTRDEPTH(%r1)	/* were we already running on intstk? */
	addic.	%r31,%r31,1
	stw	%r31,CI_INTRDEPTH(%r1)
	lwz	%r1,CI_INTSTK(%r1)	/* get interrupt stack */
	beq	1f
	mfsprg	%r1,1			/* yes, get old SP */
1:
	ba	decrintr
_C_LABEL(decrsize) =	.-_C_LABEL(decrint)

/*
 * Now the tlb software load for 603 processors:
 * (Code essentially from the 603e User Manual, Chapter 5)
 */
#define	DMISS	976
#define	DCMP	977
#define	HASH1	978
#define	HASH2	979
#define	IMISS	980
#define	ICMP	981
#define	RPA	982

#define	bdneq	bdnzf 2,
#define	tlbli	.long	0x7c0007e4+0x800*
#define	tlbld	.long	0x7c0007a4+0x800*

	.globl	_C_LABEL(tlbimiss),_C_LABEL(tlbimsize)
	.type	_C_LABEL(tlbimiss),@function
	.type	_C_LABEL(tlbimsize),@object
_C_LABEL(tlbimiss):
	mfspr	%r2,HASH1		/* get first pointer */
	li	%r1,8
	mfctr	%r0			/* save counter */
	mfspr	%r3,ICMP		/* get first compare value */
	addi	%r2,%r2,-8		/* predec pointer */
1:
	mtctr	%r1			/* load counter */
2:
	lwzu	%r1,8(%r2)		/* get next pte */
	cmpl	0,%r1,%r3		/* see if found pte */
	bdneq	2b			/* loop if not eq */
	bne	3f			/* not found */
	lwz	%r1,4(%r2)		/* load tlb entry lower word */
	andi.	%r3,%r1,8		/* check G-bit */
	bne	4f			/* if guarded, take ISI */
	mtctr	%r0			/* restore counter */
	mfspr	%r0,IMISS		/* get the miss address for the tlbli */
	mfsrr1	%r3			/* get the saved cr0 bits */
	mtcrf	0x80,%r3		/* and restore */
	ori	%r1,%r1,0x100		/* set the reference bit */
	mtspr	RPA,%r1			/* set the pte */
	srwi	%r1,%r1,8		/* get byte 7 of pte */
	tlbli	0			/* load the itlb */
	stb	%r1,6(%r2)		/* update page table */
	rfi

3:	/* not found in pteg */
	andi.	%r1,%r3,0x40		/* have we already done second hash? */
	bne	5f
	mfspr	%r2,HASH2		/* get the second pointer */
	ori	%r3,%r3,0x40		/* change the compare value */
	li	%r1,8
	addi	%r2,%r2,-8		/* predec pointer */
	b	1b
4:	/* guarded */
	mfsrr1	%r3
	andi.	%r2,%r3,0xffff		/* clean upper srr1 */
	addis	%r2,%r2,0x800		/* set srr<4> to flag prot violation */
	b	6f
5:	/* not found anywhere */
	mfsrr1	%r3
	andi.	%r2,%r3,0xffff		/* clean upper srr1 */
	addis	%r2,%r2,0x4000		/* set srr1<1> to flag pte not found */
6:
	mtctr	%r0			/* restore counter */
	mtsrr1	%r2
	mfmsr	%r0
	xoris	%r0,%r0,2		/* flip the msr<tgpr> bit */
	mtcrf	0x80,%r3		/* restore cr0 */
	mtmsr	%r0			/* now with native gprs */
	isync
	ba	EXC_ISI
_C_LABEL(tlbimsize) =	.-_C_LABEL(tlbimiss)

	.globl	_C_LABEL(tlbdlmiss),_C_LABEL(tlbdlmsize)
	.type	_C_LABEL(tlbdlmiss),@function
	.type	_C_LABEL(tlbdlmsize),@object
_C_LABEL(tlbdlmiss):
	mfspr	%r2,HASH1		/* get first pointer */
	li	%r1,8
	mfctr	%r0			/* save counter */
	mfspr	%r3,DCMP		/* get first compare value */
	addi	%r2,%r2,-8		/* predec pointer */
1:
	mtctr	%r1			/* load counter */
2:
	lwzu	%r1,8(%r2)		/* get next pte */
	cmpl	0,%r1,%r3		/* see if found pte */
	bdneq	2b			/* loop if not eq */
	bne	3f			/* not found */
	lwz	%r1,4(%r2)		/* load tlb entry lower word */
	mtctr	%r0			/* restore counter */
	mfspr	%r0,DMISS		/* get the miss address for the tlbld */
	mfsrr1	%r3			/* get the saved cr0 bits */
	mtcrf	0x80,%r3		/* and restore */
	ori	%r1,%r1,0x100		/* set the reference bit */
	mtspr	RPA,%r1			/* set the pte */
	srwi	%r1,%r1,8		/* get byte 7 of pte */
	tlbld	0			/* load the dtlb */
	stb	%r1,6(%r2)		/* update page table */
	rfi

3:	/* not found in pteg */
	andi.	%r1,%r3,0x40		/* have we already done second hash? */
	bne	5f
	mfspr	%r2,HASH2		/* get the second pointer */
	ori	%r3,%r3,0x40		/* change the compare value */
	li	%r1,8
	addi	%r2,%r2,-8		/* predec pointer */
	b	1b
5:	/* not found anywhere */
	mfsrr1	%r3
	lis	%r1,0x4000		/* set dsisr<1> to flag pte not found */
	mtctr	%r0			/* restore counter */
	andi.	%r2,%r3,0xffff		/* clean upper srr1 */
	mtsrr1	%r2
	mtdsisr	%r1			/* load the dsisr */
	mfspr	%r1,DMISS		/* get the miss address */
	mtdar	%r1			/* put in dar */
	mfmsr	%r0
	xoris	%r0,%r0,2		/* flip the msr<tgpr> bit */
	mtcrf	0x80,%r3		/* restore cr0 */
	mtmsr	%r0			/* now with native gprs */
	isync
	ba	EXC_DSI
_C_LABEL(tlbdlmsize) =	.-_C_LABEL(tlbdlmiss)

	.globl	_C_LABEL(tlbdsmiss),_C_LABEL(tlbdsmsize)
	.type	_C_LABEL(tlbdsmiss),@function
	.type	_C_LABEL(tlbdsmsize),@object
_C_LABEL(tlbdsmiss):
	mfspr	%r2,HASH1		/* get first pointer */
	li	%r1,8
	mfctr	%r0			/* save counter */
	mfspr	%r3,DCMP		/* get first compare value */
	addi	%r2,%r2,-8		/* predec pointer */
1:
	mtctr	%r1			/* load counter */
2:
	lwzu	%r1,8(%r2)		/* get next pte */
	cmpl	0,%r1,%r3		/* see if found pte */
	bdneq	2b			/* loop if not eq */
	bne	3f			/* not found */
	lwz	%r1,4(%r2)		/* load tlb entry lower word */
	andi.	%r3,%r1,0x80		/* check the C-bit */
	beq	4f
5:
	mtctr	%r0			/* restore counter */
	mfspr	%r0,DMISS		/* get the miss address for the tlbld */
	mfsrr1	%r3			/* get the saved cr0 bits */
	mtcrf	0x80,%r3		/* and restore */
	mtspr	RPA,%r1			/* set the pte */
	tlbld	0			/* load the dtlb */
	rfi

3:	/* not found in pteg */
	andi.	%r1,%r3,0x40		/* have we already done second hash? */
	bne	5f
	mfspr	%r2,HASH2		/* get the second pointer */
	ori	%r3,%r3,0x40		/* change the compare value */
	li	%r1,8
	addi	%r2,%r2,-8		/* predec pointer */
	b	1b
4:	/* found, but C-bit = 0 */
	rlwinm.	%r3,%r1,30,0,1		/* test PP */
	bge-	7f
	andi.	%r3,%r1,1
	beq+	8f
9:	/* found, but protection violation (PP==00)*/
	mfsrr1	%r3
	lis	%r1,0xa00	/* indicate protection violation on store */
	b	1f
7:	/* found, PP=1x */
	mfspr	%r3,DMISS		/* get the miss address */
	mfsrin	%r1,%r3			/* get the segment register */
	mfsrr1	%r3
	rlwinm	%r3,%r3,18,31,31	/* get PR-bit */
	rlwnm.	%r1,%r1,%r3,1,1		/* get the key */
	bne-	9b			/* protection violation */
8:	/* found, set reference/change bits */
	lwz	%r1,4(%r2)		/* reload tlb entry */
	ori	%r1,%r1,0x180
	sth	%r1,6(%r2)
	b	5b
5:	/* not found anywhere */
	mfsrr1	%r3
	lis	%r1,0x4200		/* set dsisr<1> to flag pte not found */
					/* dsisr<6> to flag store */
1:
	mtctr	%r0			/* restore counter */
	andi.	%r2,%r3,0xffff		/* clean upper srr1 */
	mtsrr1	%r2
	mtdsisr	%r1			/* load the dsisr */
	mfspr	%r1,DMISS		/* get the miss address */
	mtdar	%r1			/* put in dar */
	mfmsr	%r0
	xoris	%r0,%r0,2		/* flip the msr<tgpr> bit */
	mtcrf	0x80,%r3		/* restore cr0 */
	mtmsr	%r0			/* now with native gprs */
	isync
	ba	EXC_DSI
_C_LABEL(tlbdsmsize) =	.-_C_LABEL(tlbdsmiss)

#ifdef DDB
/*
 * In case of DDB we want a separate trap catcher for it
 */
	.globl	_C_LABEL(ddblow),_C_LABEL(ddbsize)
_C_LABEL(ddblow):
	mtsprg	1,%r1			/* save SP */
nop32_7s:
	mfmsr	%r1
	clrldi	%r1,%r1,1
	mtmsrd	%r1
nop32_7e:
	GET_CPUINFO(%r1)
	stmw	%r28,CI_DDBSAVE(%r1)	/* free r28-r31 */
	mflr	%r28			/* save LR */
	mfcr	%r29			/* save CR */
	GET_CPUINFO(%r30)
	lwz	%r30,CI_INTSTK(%r30)	/* get interrupt stack */
	addi	%r1,%r30,(SPILLSTK+DDBSTK)
	bla	ddbtrap
_C_LABEL(ddbsize) =	.-_C_LABEL(ddblow)
#endif  /* DDB */

/*
 * If this interrupt occured between the runqueue check and the setting
 * of the POW bit, do no enter to sleep.  We do that in order to process
 * the result of this interrupt directly and not when coming back from
 * sleep, when the next clock tick or interrupt will fire.
 */
#define CPU_IDLE_CHECK(sr1,sr2,sr3,rSRR0)				\
	GET_CPUINFO(sr1);						\
	lwz	sr2,CI_FLAGS(sr1);					\
	andi.	sr3,sr2,CI_FLAGS_SLEEPING@l;				\
	beq	1f;							\
	andi.	sr2,sr2,~CI_FLAGS_SLEEPING@l;				\
	stw	sr2,CI_FLAGS(sr1);					\
	lis	rSRR0,_C_LABEL(idledone)@ha;				\
	addi	rSRR0,rSRR0,_C_LABEL(idledone)@l;			\
1:

/*
 * FRAME_SETUP assumes:
 *	SPRG1		SP (1)
 *	savearea	r28-r31,DAR,DSISR	(DAR & DSISR only for DSI traps)
 *	28		LR
 *	29		CR
 *	1		kernel stack
 *	LR		trap type
 *	SRR0/1		as at start of trap
 */
#define	FRAME_SETUP(savearea)						\
/* Have to enable translation to allow access of kernel stack: */	\
	GET_CPUINFO(%r31);						\
	mfsrr0	%r30;							\
	stw	%r30,savearea+24(%r31);					\
	mfsrr1	%r30;							\
	stw	%r30,savearea+28(%r31);					\
	/* load all kernel segment registers. */			\
	lis	%r31,_C_LABEL(kernel_pmap_)@ha;				\
	addi	%r31,%r31,_C_LABEL(kernel_pmap_)@l;			\
	lwz	%r30,0(%r31);	mtsr	0,%r30;				\
	lwz	%r30,4(%r31);	mtsr	1,%r30;				\
	lwz	%r30,8(%r31);	mtsr	2,%r30;				\
	lwz	%r30,12(%r31);	mtsr	3,%r30;				\
	lwz	%r30,16(%r31);	mtsr	4,%r30;				\
	lwz	%r30,20(%r31);	mtsr	5,%r30;				\
	lwz	%r30,24(%r31);	mtsr	6,%r30;				\
	lwz	%r30,28(%r31);	mtsr	7,%r30;				\
	lwz	%r30,32(%r31);	mtsr	8,%r30;				\
	lwz	%r30,36(%r31);	mtsr	9,%r30;				\
	lwz	%r30,40(%r31);	mtsr	10,%r30;			\
	lwz	%r30,44(%r31);	mtsr	11,%r30;			\
	lwz	%r30,48(%r31);	mtsr	12,%r30;			\
/*	lwz	%r30,52(%r31);	mtsr 13,%r30; - dont load user SR - XXX? */ \
	lwz	%r30,56(%r31);	mtsr	14,%r30;			\
	lwz	%r30,60(%r31);	mtsr	15,%r30;			\
	mfmsr	%r30;							\
	ori	%r30,%r30,(PSL_DR|PSL_IR);				\
	mtmsr	%r30;							\
	isync;								\
	mfsprg	%r31,1;							\
	stwu	%r31,-FRAMELEN(%r1);					\
	stw	%r0,FRAME_0+8(%r1);					\
	stw	%r31,FRAME_1+8(%r1);					\
	stw	%r2,FRAME_2+8(%r1);					\
	stw	%r28,FRAME_LR+8(%r1);					\
	stw	%r29,FRAME_CR+8(%r1);					\
	GET_CPUINFO(%r2);						\
	lmw	%r28,savearea(%r2);					\
	stmw	%r3,FRAME_3+8(%r1);					\
	lmw	%r28,savearea+16(%r2);					\
	mfxer	%r3;							\
	mfctr	%r4;							\
	mflr	%r5;							\
	andi.	%r5,%r5,0xff00;						\
	stw	%r3,FRAME_XER+8(%r1);					\
	stw	%r4,FRAME_CTR+8(%r1);					\
	stw	%r5,FRAME_EXC+8(%r1);					\
	stw	%r28,FRAME_DAR+8(%r1);					\
	stw	%r29,FRAME_DSISR+8(%r1);				\
	CPU_IDLE_CHECK(%r5,%r6,%r0,%r30)				\
	stw	%r30,FRAME_SRR0+8(%r1);					\
	stw	%r31,FRAME_SRR1+8(%r1)

#define	FRAME_LEAVE(savearea)						\
/* Now restore regs: */							\
	lwz	%r2,FRAME_SRR0+8(%r1);					\
	lwz	%r3,FRAME_SRR1+8(%r1);					\
	lwz	%r4,FRAME_CTR+8(%r1);					\
	lwz	%r5,FRAME_XER+8(%r1);					\
	lwz	%r6,FRAME_LR+8(%r1);					\
	GET_CPUINFO(%r7);						\
	stw	%r2,savearea(%r7);					\
	stw	%r3,savearea+4(%r7);					\
	lwz	%r7,FRAME_CR+8(%r1);					\
	mtctr	%r4;							\
	mtxer	%r5;							\
	mtlr	%r6;							\
	mtsprg	1,%r7;			/* save cr */			\
	lmw	%r2,FRAME_2+8(%r1);					\
	lwz	%r0,FRAME_0+8(%r1);					\
	lwz	%r1,FRAME_1+8(%r1);					\
	mtsprg	2,%r2;			/* save r2 & r3 */		\
	mtsprg	3,%r3;							\
/* Disable translation, machine check and recoverability: */		\
	mfmsr	%r2;							\
	lis	%r3,(PSL_DR|PSL_IR|PSL_ME|PSL_RI)@ha;			\
	addi	%r3,%r3,(PSL_DR|PSL_IR|PSL_ME|PSL_RI)@l;		\
	andc	%r2,%r2,%r3;						\
	mtmsr	%r2;							\
	isync;								\
/* Decide whether we return to user mode: */				\
	GET_CPUINFO(%r2);						\
	lwz	%r3,savearea+4(%r2);					\
	mtcr	%r3;							\
	bc	4,17,1f;		/* branch if PSL_PR is false */	\
/* Restore user & kernel access SR: */					\
	lwz	%r2,CI_CURPM(%r2);	/* get real address of pmap */	\
	lwz	%r3,0(%r2);	mtsr	0,%r3;				\
	lwz	%r3,4(%r2);	mtsr	1,%r3;				\
	lwz	%r3,8(%r2);	mtsr	2,%r3;				\
	lwz	%r3,12(%r2);	mtsr	3,%r3;				\
	lwz	%r3,16(%r2);	mtsr	4,%r3;				\
	lwz	%r3,20(%r2);	mtsr	5,%r3;				\
	lwz	%r3,24(%r2);	mtsr	6,%r3;				\
	lwz	%r3,28(%r2);	mtsr	7,%r3;				\
	lwz	%r3,32(%r2);	mtsr	8,%r3;				\
	lwz	%r3,36(%r2);	mtsr	9,%r3;				\
	lwz	%r3,40(%r2);	mtsr	10,%r3;				\
	lwz	%r3,44(%r2);	mtsr	11,%r3;				\
	lwz	%r3,48(%r2);	mtsr	12,%r3;				\
	lwz	%r3,52(%r2);	mtsr	13,%r3;				\
	lwz	%r3,56(%r2);	mtsr	14,%r3;				\
	lwz	%r3,60(%r2);	mtsr	15,%r3;				\
1:	mfsprg	%r2,1;			/* restore cr */		\
	mtcr	%r2;							\
	GET_CPUINFO(%r2);						\
	lwz	%r3,savearea(%r2);					\
	mtsrr0	%r3;							\
	lwz	%r3,savearea+4(%r2);					\
	mtsrr1	%r3;							\
	mfsprg	%r2,2;			/* restore r2 & r3 */		\
	mfsprg	%r3,3

/*
 * Preamble code for DSI/ISI traps
 */
disitrap:
	GET_CPUINFO(%r1)
	lmw	%r30,CI_DISISAVE(%r1)
	stmw	%r30,CI_TEMPSAVE(%r1)
	lmw	%r30,CI_DISISAVE+8(%r1)
	stmw	%r30,CI_TEMPSAVE+8(%r1)
	mfdar	%r30
	mfdsisr	%r31
	stmw	%r30,CI_TEMPSAVE+16(%r1)
realtrap:
	/* Test whether we already had PR set */
	mfsrr1	%r1
	mtcr	%r1
	/* restore SP (might have been overwritten) */
	mfsprg	%r1,1
	bc	4,17,s_trap		/* branch if PSL_PR is false */
	GET_CPUINFO(%r1)
	lwz	%r1,CI_CURPCB(%r1)
	addi	%r1,%r1,USPACE		/* stack is top of user struct */
/*
 * Now the common trap catching code.
 */
	.globl	s_trap
s_trap:
	FRAME_SETUP(CI_TEMPSAVE)
/* Now we can recover interrupts again: */
	mfmsr	%r7
	mfsrr1	%r31
	andi.	%r31,%r31,PSL_EE	/* restore EE from previous context */
	or	%r7,%r7,%r31
	ori	%r7,%r7,(PSL_ME|PSL_RI)
	mtmsr	%r7
	isync
/* Call C trap code: */
trapagain:
	addi	%r3,%r1,8
	bl	_C_LABEL(trap)

	.globl	trapexit
trapexit:
/* Disable interrupts: */
	mfmsr	%r3
	andi.	%r3,%r3,~PSL_EE@l
	mtmsr	%r3
/* Test AST pending: */
	lwz	%r5,FRAME_SRR1+8(%r1)
	mtcr	%r5
	bc	4,17,1f			/* branch if PSL_PR is false */
	GET_CPUINFO(%r3)
	lwz	%r4,CI_CURPROC(%r3)
	lwz	%r4,P_MD_ASTPENDING(%r4)
	andi.	%r4,%r4,1
	beq	1f
	li	%r6,EXC_AST
	stw	%r6,FRAME_EXC+8(%r1)
	b	trapagain
1:
	FRAME_LEAVE(CI_TEMPSAVE)
rfi1:	rfi

/*
 * Child comes here at the end of a fork.
 * Mostly similar to the above.
 */
	.globl	_C_LABEL(fork_trampoline)
	.type	_C_LABEL(fork_trampoline),@function
_C_LABEL(fork_trampoline):
#ifdef MULTIPROCESSOR
	bl	_C_LABEL(proc_trampoline_mp)
#endif
	li	%r3,0
	bl	_C_LABEL(lcsplx)
	mtlr	%r31
	mr	%r3,%r30
	blrl				/* jump indirect to r31 */
	b	trapexit

/*
 * DSI second stage fault handler
 */
s_dsitrap:
	mfdsisr	%r31			/* test if this is spill fault */
	mtcr	%r31
	mtsprg	1,%r1			/* save SP */
	bc	4,1,disitrap		/* branch if table miss is false */
	GET_CPUINFO(%r30)
	lwz	%r30,CI_INTSTK(%r30)	/* get interrupt stack */
	addi	%r1,%r30,SPILLSTK
	stwu	%r1,-52(%r1)
	stw	%r0,48(%r1)		/* save non-volatile registers */
	stw	%r3,44(%r1)
	stw	%r4,40(%r1)
	stw	%r5,36(%r1)
	stw	%r6,32(%r1)
	stw	%r7,28(%r1)
	stw	%r8,24(%r1)
	stw	%r9,20(%r1)
	stw	%r10,16(%r1)
	stw	%r11,12(%r1)
	stw	%r12,8(%r1)
	mfxer	%r30			/* save XER */
	mtsprg	2,%r30
	mflr	%r30			/* save trap type */
	mfctr	%r31			/* & CTR */
	mfdar	%r7
	mfsrr1	%r4
	mfdsisr	%r5
	li	%r6, 0
s_pte_spill:
	andi.	%r0,%r4,PSL_PR
	li	%r3,0
	bne	1f
	mr	%r3,%r7
	bl	_C_LABEL(pte_spill_r)	/* try a spill */
1:
	cmpwi	0,%r3,0
	mtctr	%r31			/* restore CTR */
	mtlr	%r30			/* and trap type */
	mfsprg	%r31,2			/* get saved XER */
	mtxer	%r31			/* restore XER */
	lwz	%r12,8(%r1)		/* restore non-volatile registers */
	lwz	%r11,12(%r1)
	lwz	%r10,16(%r1)
	lwz	%r9,20(%r1)
	lwz	%r8,24(%r1)
	lwz	%r7,28(%r1)
	lwz	%r6,32(%r1)
	lwz	%r5,36(%r1)
	lwz	%r4,40(%r1)
	lwz	%r3,44(%r1)
	lwz	%r0,48(%r1)
	beq	disitrap
	mtcr	%r29			/* restore CR */
	mtlr	%r28			/* restore LR */
	GET_CPUINFO(%r1)
	lmw	%r28,CI_DISISAVE(%r1)	/* restore r28-r31 */
	mfsprg	%r1,1			/* restore SP */
rfi2:	rfi				/* return to trapped code */

/*
 * ISI second stage fault handler
 */
s_isitrap:
	mfsrr1	%r31			/* test if this may be a spill fault */
	mtcr	%r31
	mtsprg	1,%r1			/* save SP */
	bc	4,1,disitrap		/* branch if table miss is false */
	GET_CPUINFO(%r30)
	lwz	%r30,CI_INTSTK(%r30)	/* get interrupt stack */
	addi	%r1,%r30,SPILLSTK
	stwu	%r1,-52(%r1)
	stw	%r0,48(%r1)		/* save non-volatile registers */
	stw	%r3,44(%r1)
	stw	%r4,40(%r1)
	stw	%r5,36(%r1)
	stw	%r6,32(%r1)
	stw	%r7,28(%r1)
	stw	%r8,24(%r1)
	stw	%r9,20(%r1)
	stw	%r10,16(%r1)
	stw	%r11,12(%r1)
	stw	%r12,8(%r1)
	mfxer	%r30			/* save XER */
	mtsprg	2,%r30
	mflr	%r30			/* save trap type */
	mfctr	%r31			/* & ctr */
	mfsrr0	%r7
	mfsrr1	%r4
	li	%r5, 0
	li	%r6, 1
	b	s_pte_spill		/* above */

/*
 * External interrupt second level handler
 */
#define	INTRENTER							\
/* Save non-volatile registers: */					\
	stwu	%r1,-88(%r1);		/* temporarily */		\
	stw	%r0,84(%r1);						\
	mfsprg	%r0,1;			/* get original SP */		\
	stw	%r0,0(%r1);		/* and store it */		\
	stw	%r3,80(%r1);						\
	stw	%r4,76(%r1);						\
	stw	%r5,72(%r1);						\
	stw	%r6,68(%r1);						\
	stw	%r7,64(%r1);						\
	stw	%r8,60(%r1);						\
	stw	%r9,56(%r1);						\
	stw	%r10,52(%r1);						\
	stw	%r11,48(%r1);						\
	stw	%r12,44(%r1);						\
	stw	%r28,40(%r1);		/* saved LR */			\
	stw	%r29,36(%r1);		/* saved CR */			\
	stw	%r30,32(%r1);		/* saved XER */			\
	GET_CPUINFO(%r4);						\
	lmw	%r28,CI_TEMPSAVE(%r4);	/* restore r28-r31 */		\
	mfctr	%r6;							\
	lwz	%r5,CI_INTRDEPTH(%r4);			\
	mfsrr0	%r4;							\
	mfsrr1	%r3;							\
	stw	%r6,28(%r1);						\
	stw	%r5,20(%r1);						\
	stw	%r4,12(%r1);						\
	stw	%r3,8(%r1);						\
/* load all kernel segment registers. */				\
	lis	3,_C_LABEL(kernel_pmap_)@ha;                            \
	addi	3,3,_C_LABEL(kernel_pmap_)@l;                           \
	lwz	%r5,0(%r3);	mtsr 0,%r5;				\
	lwz	%r5,4(%r3);	mtsr 1,%r5;				\
	lwz	%r5,8(%r3);	mtsr 2,%r5;				\
	lwz	%r5,12(%r3);	mtsr 3,%r5;				\
	lwz	%r5,16(%r3);	mtsr 4,%r5;				\
	lwz	%r5,20(%r3);	mtsr 5,%r5;				\
	lwz	%r5,24(%r3);	mtsr 6,%r5;				\
	lwz	%r5,28(%r3);	mtsr 7,%r5;				\
	lwz	%r5,32(%r3);	mtsr 8,%r5;				\
	lwz	%r5,36(%r3);	mtsr 9,%r5;				\
	lwz	%r5,40(%r3);	mtsr 10,%r5;				\
	lwz	%r5,44(%r3);	mtsr 11,%r5;				\
	lwz	%r5,48(%r3);	mtsr 12,%r5;				\
/*	lwz	%r5,52(%r3);	mtsr 13,%r5;	- dont load user SR - XXX? */ \
	lwz	%r5,56(%r3);	mtsr 14,%r5;				\
	lwz	%r5,60(%r3);	mtsr 15,%r5;				\
/* interrupts are recoverable here, and enable translation */		\
	mfmsr	%r5;							\
	ori	%r5,%r5,(PSL_IR|PSL_DR|PSL_RI);				\
	mtmsr	%r5;							\
	isync

	.globl	_C_LABEL(extint_call)
	.type	_C_LABEL(extint_call),@function
extintr:
	INTRENTER
_C_LABEL(extint_call):
	bl	_C_LABEL(extint_call)		/* to be filled in later */
intr_exit:
/* Disable interrupts (should already be disabled) and MMU here: */
	mfmsr	%r3
	andi.	%r3,%r3,~(PSL_EE|PSL_ME|PSL_RI|PSL_DR|PSL_IR)@l
	mtmsr	%r3
	isync
/* restore possibly overwritten registers: */
	lwz	%r12,44(%r1)
	lwz	%r11,48(%r1)
	lwz	%r10,52(%r1)
	lwz	%r9,56(%r1)
	lwz	%r8,60(%r1)
	lwz	%r7,64(%r1)
	lwz	%r6,8(%r1)
	lwz	%r5,12(%r1)
	lwz	%r4,28(%r1)
	lwz	%r3,32(%r1)
	mtsrr1	%r6
	mtsrr0	%r5
	mtctr	%r4
	mtxer	%r3

	GET_CPUINFO(%r5)
	lwz	%r4,CI_INTRDEPTH(%r5)
	addi	%r4,%r4,-1		/* adjust reentrancy count */
	stw	%r4,CI_INTRDEPTH(%r5)

/* Returning to user mode? */
	mtcr	%r6			/* saved SRR1 */
	bc	4,17,1f			/* branch if PSL_PR is false */
	lwz	%r3,CI_CURPM(%r5)	/* get current pmap real address */
	/* reload all segment registers. */
	lwz	%r4,0(3);	mtsr 0,%r4;
	lwz	%r4,4(3);	mtsr 1,%r4;
	lwz	%r4,8(3);	mtsr 2,%r4;
	lwz	%r4,12(3);	mtsr 3,%r4;
	lwz	%r4,16(3);	mtsr 4,%r4;
	lwz	%r4,20(3);	mtsr 5,%r4;
	lwz	%r4,24(3);	mtsr 6,%r4;
	lwz	%r4,28(3);	mtsr 7,%r4;
	lwz	%r4,32(3);	mtsr 8,%r4;
	lwz	%r4,36(3);	mtsr 9,%r4;
	lwz	%r4,40(3);	mtsr 10,%r4;
	lwz	%r4,44(3);	mtsr 11,%r4;
	lwz	%r4,48(3);	mtsr 12,%r4;
	lwz	%r4,52(3);	mtsr 13,%r4;
	lwz	%r4,56(3);	mtsr 14,%r4;
	lwz	%r4,60(3);	mtsr 15,%r4;
	lwz	%r4,CI_CURPROC(%r5)
	lwz	%r4,P_MD_ASTPENDING(%r4)	/* Test AST pending */
	andi.	%r4,%r4,1
	beq	1f
/* Setup for entry to realtrap: */
	lwz	%r3,0(%r1)		/* get saved SP */
	mtsprg	1,%r3
	li	%r6,EXC_AST
	stmw	%r28,CI_TEMPSAVE(%r5)	/* establish tempsave again */
	mtlr	%r6
	lwz	%r28,40(%r1)		/* saved LR */
	lwz	%r29,36(%r1)		/* saved CR */
	lwz	%r6,68(%r1)
	lwz	%r5,72(%r1)
	lwz	%r4,76(%r1)
	lwz	%r3,80(%r1)
	lwz	%r0,84(%r1)
	b	realtrap
1:
/* Here is the normal exit of extintr: */
	lwz	%r5,36(%r1)
	lwz	%r6,40(%r1)
	mtcr	%r5
	mtlr	%r6
	lwz	%r6,68(%r1)
	lwz	%r5,72(%r1)
	lwz	%r4,76(%r1)
	lwz	%r3,80(%r1)
	lwz	%r0,84(%r1)
	lwz	%r1,0(%r1)
rfi3:	rfi

/*
 * Decrementer interrupt second level handler
 */
	.globl	decrintr
decrintr:
	INTRENTER
	addi	%r3,%r1,8			/* intr frame */
	bl	_C_LABEL(decr_intr)
	b	intr_exit

/*
 * int setfault()
 *
 * Similar to setjmp to setup for handling faults on accesses to user memory.
 * Any routine using this may only call bcopy, either the form below,
 * or the (currently used) C code optimized, so it doesn't use any non-volatile
 * registers.
 */
	.globl	_C_LABEL(setfault)
	.type	_C_LABEL(setfault),@function
_C_LABEL(setfault):
	mflr	%r0
	mfcr	%r12
	GET_CPUINFO(%r4)
	lwz	%r4,CI_CURPCB(%r4)
	stw	%r3,PCB_FAULT(%r4)
	stw	%r0,0(%r3)
	stw	%r1,4(%r3)
	stmw	%r12,8(%r3)
	li	%r3,0
	blr

/*
 * The following code gets copied to the top of the user stack on process
 * execution.  It does signal trampolining on signal delivery.
 *
 * On entry r1 points to a struct sigframe at bottom of current stack.
 * All other registers are unchanged.
 */
	.globl	_C_LABEL(sigcode),_C_LABEL(esigcode)
	.type	_C_LABEL(sigcode),@function
	.type	_C_LABEL(esigcode),@function
_C_LABEL(sigcode):
	addi	%r1,%r1,-((16+FPSIG_SIZEOF+15)& ~0xf)		/* reserved space for callee */
	addi	%r6,%r1,8
	stfd	%f0,0(%r6)
	stfd	%f1,8(%r6)
	stfd	%f2,16(%r6)
	stfd	%f3,24(%r6)
	stfd	%f4,32(%r6)
	stfd	%f5,40(%r6)
	stfd	%f6,48(%r6)
	stfd	%f7,56(%r6)
	stfd	%f8,64(%r6)
	stfd	%f9,72(%r6)
	stfd	%f10,80(%r6)
	stfd	%f11,88(%r6)
	stfd	%f12,96(%r6)
	stfd	%f13,104(%r6)
	mffs	%f0
	stfd	%f0,112(%r6)
	lfd	%f0,0(%r6)	/* restore the clobbered register */
	blrl
	addi	%r6,%r1,8
	lfd	%f0,112(%r6)
	mtfsf	0xff,%f0
	lfd	%f0,0(%r6)
	lfd	%f1,8(%r6)
	lfd	%f2,16(%r6)
	lfd	%f3,24(%r6)
	lfd	%f4,32(%r6)
	lfd	%f5,40(%r6)
	lfd	%f6,48(%r6)
	lfd	%f7,56(%r6)
	lfd	%f8,64(%r6)
	lfd	%f9,72(%r6)
	lfd	%f10,80(%r6)
	lfd	%f11,88(%r6)
	lfd	%f12,96(%r6)
	lfd	%f13,104(%r6)
	addi	%r3,%r1,((16+FPSIG_SIZEOF+15)&~0xf)+SF_SC	/* compute &sf_sc */
	li	%r0,SYS_sigreturn
	sc				/* sigreturn(scp) */
	li	%r0,SYS_exit
	sc				/* exit(errno) */
_C_LABEL(esigcode):

#ifdef DDB
/*
 * Deliberate entry to ddbtrap
 */
	.globl	_C_LABEL(ddb_trap)
_C_LABEL(ddb_trap):
	mtsprg	1,%r1
	mfmsr	%r3
	mtsrr1	%r3
	andi.	%r3,%r3,~(PSL_EE|PSL_ME)@l
	mtmsr	%r3				/* disable interrupts */
	isync
	GET_CPUINFO(%r3)
	stmw	%r28,CI_DDBSAVE(%r3)

	/*
	 * If we are already running in interrupt context, the CPU
	 * problably got interrupted while idle.  But since we are
	 * about to enter to ddb(8), do not let FRAME_SETUP() below
	 * change the return address of, and corrupt, this frame.
	 */
	lwz	%r28,CI_FLAGS(%r3)
	andi.	%r28,%r28,~CI_FLAGS_SLEEPING@l
	stw	%r28,CI_FLAGS(%r3)

	mflr	%r28
	li	%r29,EXC_BPT
	mtlr	%r29
	mfcr	%r29
	mtsrr0	%r28

/*
 * Now the ddb trap catching code.
 */
ddbtrap:
	FRAME_SETUP(CI_DDBSAVE)
/* Call C trap code: */
	addi	%r3,%r1,8
	bl	_C_LABEL(ddb_trap_glue)
	or.	%r3,%r3,%r3
	bne	ddbleave
/* This wasn't for DDB, so switch to real trap: */
	lwz	%r3,FRAME_EXC+8(%r1)	/* save exception */
	GET_CPUINFO(%r4)
	stw	%r3,CI_DDBSAVE+8(%r4)
	FRAME_LEAVE(CI_DDBSAVE)
	mtsprg	1,%r1			/* prepare for entrance to realtrap */
	GET_CPUINFO(%r1)
	stmw	%r28,CI_TEMPSAVE(%r1)
	mflr	%r28
	mfcr	%r29
	lwz	%r31,CI_DDBSAVE+8(%r1)
	mtlr	%r31
	b	realtrap
ddbleave:
	FRAME_LEAVE(CI_DDBSAVE)
rfi4:	rfi
#endif /* DDB */

	.globl  _C_LABEL(rfi_inst)
_C_LABEL(rfi_inst):
	rfi
	.globl  _C_LABEL(rfid_inst)
_C_LABEL(rfid_inst):
	rfid
	.globl  _C_LABEL(nop_inst)
 _C_LABEL(nop_inst):
	nop

	.globl  _C_LABEL(rfi_start)
_C_LABEL(rfi_start):
	.long	rfi1, rfi1 + 4
	.long	rfi2, rfi2 + 4
	.long	rfi3, rfi3 + 4
#ifdef DDB
	.long	rfi4, rfi4 + 4
#endif
	.long	0, 0

	.globl  _C_LABEL(nopbat_start)
_C_LABEL(nopbat_start):
	.long	nopbat_1s, nopbat_1e
	.long	0, 0

	.globl  _C_LABEL(nop32_start)
_C_LABEL(nop32_start):
	.long	nop32_1s, nop32_1e
	.long	nop32_2s, nop32_2e
	.long	nop32_3s, nop32_3e
	.long	nop32_4s, nop32_4e
	.long	nop32_5s, nop32_5e
	.long	nop32_6s, nop32_6e
#ifdef DDB
	.long	nop32_7s, nop32_7e
#endif
	.long	0, 0


code:
/*	$OpenBSD: machdep.c,v 1.171 2015/04/08 15:58:25 mpi Exp $	*/
/*	$NetBSD: machdep.c,v 1.4 1996/10/16 19:33:11 ws Exp $	*/

/*
 * Copyright (C) 1995, 1996 Wolfgang Solfrank.
 * Copyright (C) 1995, 1996 TooLs GmbH.
 * 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.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by TooLs GmbH.
 * 4. The name of TooLs GmbH may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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 <sys/param.h>
#include <sys/buf.h>
#include <sys/timeout.h>
#include <sys/exec.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/mount.h>
#include <sys/msgbuf.h>
#include <sys/pool.h>
#include <sys/sysctl.h>
#include <sys/proc.h>
#include <sys/signalvar.h>
#include <sys/reboot.h>
#include <sys/syscallargs.h>
#include <sys/syslog.h>
#include <sys/extent.h>
#include <sys/systm.h>
#include <sys/user.h>
#include <sys/conf.h>
#include <sys/core.h>
#include <sys/kcore.h>

#include <net/if.h>
#include <uvm/uvm_extern.h>

#include <dev/cons.h>

#include <machine/pmap.h>
#include <powerpc/powerpc.h>
#include <machine/trap.h>
#include <machine/autoconf.h>
#include <machine/bus.h>
#include <machine/pio.h>
#include <machine/intr.h>

#include <dev/pci/pcivar.h>

#include <arch/macppc/macppc/ofw_machdep.h>
#include <dev/ofw/openfirm.h>

#include "adb.h"
#if NADB > 0
#include <arch/macppc/dev/adbvar.h>
#endif

#ifdef DDB
#include <machine/db_machdep.h>
#include <ddb/db_interface.h>
#include <ddb/db_access.h>
#include <ddb/db_sym.h>
#include <ddb/db_extern.h>
#endif

#include <powerpc/reg.h>
#include <powerpc/fpu.h>

/*
 * Global variables used here and there
 */
extern struct user *proc0paddr;
struct pool ppc_vecpl;

struct uvm_constraint_range  dma_constraint = { 0x0, (paddr_t)-1 };
struct uvm_constraint_range *uvm_md_constraints[] = { NULL };

struct vm_map *exec_map = NULL;
struct vm_map *phys_map = NULL;

int ppc_malloc_ok = 0;

char *bootpath;
char bootpathbuf[512];

/* from autoconf.c */
extern void parseofwbp(char *);

struct firmware *fw = NULL;

#ifdef DDB
void * startsym, *endsym;
#endif

#ifdef APERTURE
#ifdef INSECURE
int allowaperture = 1;
#else
int allowaperture = 0;
#endif
#endif

void dumpsys(void);
int lcsplx(int ipl);	/* called from LCore */
void *ppc_intr_establish(void *lcv, pci_intr_handle_t ih, int type,
    int level, int (*func)(void *), void *arg, const char *name);

/*
 * Extent maps to manage I/O. Allocate storage for 8 regions in each.
 */
static long devio_ex_storage[EXTENT_FIXED_STORAGE_SIZE(8) / sizeof (long)];
struct extent *devio_ex;

/* XXX, called from asm */
void initppc(u_int startkernel, u_int endkernel, char *args);

void
initppc(startkernel, endkernel, args)
	u_int startkernel, endkernel;
	char *args;
{
	extern void *trapcode; extern int trapsize;
	extern void *dsitrap; extern int dsisize;
	extern void *isitrap; extern int isisize;
	extern void *alitrap; extern int alisize;
	extern void *decrint; extern int decrsize;
	extern void *tlbimiss; extern int tlbimsize;
	extern void *tlbdlmiss; extern int tlbdlmsize;
	extern void *tlbdsmiss; extern int tlbdsmsize;
#ifdef DDB
	extern void *ddblow; extern int ddbsize;
#endif
	extern void callback(void *);
	extern void *msgbuf_addr;
	int exc;

	proc0.p_cpu = &cpu_info[0];
	proc0.p_addr = proc0paddr;
	bzero(proc0.p_addr, sizeof *proc0.p_addr);

	curpcb = &proc0paddr->u_pcb;

	curpm = curpcb->pcb_pmreal = curpcb->pcb_pm = pmap_kernel();

	cpu_bootstrap();

	/*
	 * Initialize pmap module.
	 */
	pmap_bootstrap(startkernel, endkernel);

	/*
	 * Set up trap vectors
	 */
	for (exc = EXC_RSVD; exc <= EXC_LAST; exc += 0x100) {
		switch (exc) {
		default:
			bcopy(&trapcode, (void *)exc, (size_t)&trapsize);
			break;
		case EXC_EXI:
			/*
			 * This one is (potentially) installed during autoconf
			 */
			break;

		case EXC_DSI:
			bcopy(&dsitrap, (void *)EXC_DSI, (size_t)&dsisize);
			break;
		case EXC_ISI:
			bcopy(&isitrap, (void *)EXC_ISI, (size_t)&isisize);
			break;
		case EXC_ALI:
			bcopy(&alitrap, (void *)EXC_ALI, (size_t)&alisize);
			break;
		case EXC_DECR:
			bcopy(&decrint, (void *)EXC_DECR, (size_t)&decrsize);
			break;
		case EXC_IMISS:
			bcopy(&tlbimiss, (void *)EXC_IMISS, (size_t)&tlbimsize);
			break;
		case EXC_DLMISS:
			bcopy(&tlbdlmiss, (void *)EXC_DLMISS, (size_t)&tlbdlmsize);
			break;
		case EXC_DSMISS:
			bcopy(&tlbdsmiss, (void *)EXC_DSMISS, (size_t)&tlbdsmsize);
			break;
		case EXC_PGM:
		case EXC_TRC:
		case EXC_BPT:
#if defined(DDB)
			bcopy(&ddblow, (void *)exc, (size_t)&ddbsize);
#endif
			break;
		}
	}

	/* Grr, ALTIVEC_UNAVAIL is a vector not ~0xff aligned: 0x0f20 */
	bcopy(&trapcode, (void *)0xf20, (size_t)&trapsize);

	/*
	 * since trapsize is > 0x20, we just overwrote the EXC_PERF handler
	 * since we do not use it, we will "share" it with the EXC_VEC,
	 * we dont support EXC_VEC either.
	 * should be a 'ba 0xf20 written' at address 0xf00, but we
	 * do not generate EXC_PERF exceptions...
	 */

	syncicache((void *)EXC_RST, EXC_LAST - EXC_RST + 0x100);

	/*
	 * Now enable translation (and machine checks/recoverable interrupts).
	 */
	pmap_enable_mmu();

	/*
	 * use the memory provided by pmap_bootstrap for message buffer
	 */
	initmsgbuf(msgbuf_addr, MSGBUFSIZE);

	/*
	 * Look at arguments passed to us and compute boothowto.
	 */
	boothowto = RB_AUTOBOOT;

	/*
	 * Parse arg string.
	 */

	/* make a copy of the args! */
	strncpy(bootpathbuf, args, 512);
	bootpath= &bootpathbuf[0];
	while ( *++bootpath && *bootpath != ' ');
	if (*bootpath) {
		*bootpath++ = 0;
		while (*bootpath) {
			switch (*bootpath++) {
			case 'a':
				boothowto |= RB_ASKNAME;
				break;
			case 's':
				boothowto |= RB_SINGLE;
				break;
			case 'd':
				boothowto |= RB_KDB;
				break;
			case 'c':
				boothowto |= RB_CONFIG;
				break;
			default:
				break;
			}
		}
	}
	bootpath = &bootpathbuf[0];
	parseofwbp(bootpath);

#ifdef DDB
	ddb_init();
	db_machine_init();
#endif

	/*
	 * Set up extents for pci mappings
	 * Is this too late?
	 *
	 * what are good start and end values here??
	 * 0x0 - 0x80000000 mcu bus
	 * MAP A				MAP B
	 * 0x80000000 - 0xbfffffff io		0x80000000 - 0xefffffff mem
	 * 0xc0000000 - 0xffffffff mem		0xf0000000 - 0xffffffff io
	 *
	 * of course bsd uses 0xe and 0xf
	 * So the BSD PPC memory map will look like this
	 * 0x0 - 0x80000000 memory (whatever is filled)
	 * 0x80000000 - 0xdfffffff (pci space, memory or io)
	 * 0xe0000000 - kernel vm segment
	 * 0xf0000000 - kernel map segment (user space mapped here)
	 */

	devio_ex = extent_create("devio", 0x80000000, 0xffffffff, M_DEVBUF,
		(caddr_t)devio_ex_storage, sizeof(devio_ex_storage),
		EX_NOCOALESCE|EX_NOWAIT);

	/* while using openfirmware, run userconfig */
	if (boothowto & RB_CONFIG) {
#ifdef BOOT_CONFIG
		user_config();
#else
		printf("kernel does not support -c; continuing..\n");
#endif
	}

#ifdef DDB
	if (boothowto & RB_KDB)
		Debugger();
#endif

	/*
	 * Replace with real console.
	 */
	ofwconprobe();
	consinit();

        pool_init(&ppc_vecpl, sizeof(struct vreg), 16, 0, 0, "ppcvec", NULL);

}

void
install_extint(void (*handler)(void))
{
	void extint(void);
	void extsize(void);
	extern u_long extint_call;
	u_long offset = (u_long)handler - (u_long)&extint_call;
	int omsr, msr;

#ifdef	DIAGNOSTIC
	if (offset > 0x1ffffff)
		panic("install_extint: too far away");
#endif
	omsr = ppc_mfmsr();
	msr = omsr & ~PSL_EE;
	ppc_mtmsr(msr);
	extint_call = (extint_call & 0xfc000003) | offset;
	bcopy(&extint, (void *)EXC_EXI, (size_t)&extsize);
	syncicache((void *)&extint_call, sizeof extint_call);
	syncicache((void *)EXC_EXI, (int)&extsize);
	ppc_mtmsr(omsr);
}

/*
 * safepri is a safe priority for sleep to set for a spin-wait
 * during autoconfiguration or after a panic.
 */
int   safepri = 0;

/*
 * Machine dependent startup code.
 */
void
cpu_startup()
{
	vaddr_t minaddr, maxaddr;

	proc0.p_addr = proc0paddr;

	printf("%s", version);

	printf("real mem = %llu (%lluMB)\n",
	    (unsigned long long)ptoa((psize_t)physmem),
	    (unsigned long long)ptoa((psize_t)physmem)/1024U/1024U);

	/*
	 * Allocate a submap for exec arguments.  This map effectively
	 * limits the number of processes exec'ing at any time.
	 */
	minaddr = vm_map_min(kernel_map);
	exec_map = uvm_km_suballoc(kernel_map, &minaddr, &maxaddr, 16 * NCARGS,
	    VM_MAP_PAGEABLE, FALSE, NULL);

	/*
	 * Allocate a submap for physio
	 */
	phys_map = uvm_km_suballoc(kernel_map, &minaddr, &maxaddr,
	    VM_PHYS_SIZE, 0, FALSE, NULL);
	ppc_malloc_ok = 1;

	printf("avail mem = %lu (%luMB)\n", ptoa(uvmexp.free),
	    ptoa(uvmexp.free) / 1024 / 1024);

	/*
	 * Set up the buffers.
	 */
	bufinit();
}

/*
 * consinit
 * Initialize system console.
 */
void
consinit()
{
	static int cons_initted = 0;

	if (cons_initted)
		return;
	cninit();
	cons_initted = 1;
}

/*
 * Clear registers on exec
 */
void
setregs(struct proc *p, struct exec_package *pack, u_long stack,
    register_t *retval)
{
	u_int32_t newstack;
	u_int32_t pargs;
	u_int32_t args[4];

	struct trapframe *tf = trapframe(p);
	pargs = -roundup(-stack + 8, 16);
	newstack = (u_int32_t)(pargs - 32);

	copyin ((void *)p->p_p->ps_strings, &args, 0x10);

	bzero(tf, sizeof *tf);
	tf->fixreg[1] = newstack;
	tf->fixreg[3] = retval[0] = args[1];	/* XXX */
	tf->fixreg[4] = retval[1] = args[0];	/* XXX */
	tf->fixreg[5] = args[2];		/* XXX */
	tf->fixreg[6] = args[3];		/* XXX */
	tf->srr0 = pack->ep_entry;
	tf->srr1 = PSL_MBO | PSL_USERSET | PSL_FE_DFLT;
	p->p_addr->u_pcb.pcb_flags = 0;
}

/*
 * Send a signal to process.
 */
void
sendsig(sig_t catcher, int sig, int mask, u_long code, int type,
    union sigval val)
{
	struct proc *p = curproc;
	struct trapframe *tf;
	struct sigframe *fp, frame;
	struct sigacts *psp = p->p_p->ps_sigacts;

	bzero(&frame, sizeof(frame));
	frame.sf_signum = sig;

	tf = trapframe(p);

	/*
	 * Allocate stack space for signal handler.
	 */
	if ((p->p_sigstk.ss_flags & SS_DISABLE) == 0 &&
	    !sigonstack(tf->fixreg[1]) &&
	    (psp->ps_sigonstack & sigmask(sig)))
		fp = (struct sigframe *)(p->p_sigstk.ss_sp
					 + p->p_sigstk.ss_size);
	else
		fp = (struct sigframe *)tf->fixreg[1];

	fp = (struct sigframe *)((int)(fp - 1) & ~0xf);

	/*
	 * Generate signal context for SYS_sigreturn.
	 */
	frame.sf_sc.sc_mask = mask;
	frame.sf_sip = NULL;
	bcopy(tf, &frame.sf_sc.sc_frame, sizeof *tf);
	if (psp->ps_siginfo & sigmask(sig)) {
		frame.sf_sip = &fp->sf_si;
		initsiginfo(&frame.sf_si, sig, code, type, val);
	}
	if (copyout(&frame, fp, sizeof frame) != 0)
		sigexit(p, SIGILL);

	tf->fixreg[1] = (int)fp;
	tf->lr = (int)catcher;
	tf->fixreg[3] = (int)sig;
	tf->fixreg[4] = (psp->ps_siginfo & sigmask(sig)) ? (int)&fp->sf_si : 0;
	tf->fixreg[5] = (int)&fp->sf_sc;
	tf->srr0 = p->p_p->ps_sigcode;

#if WHEN_WE_ONLY_FLUSH_DATA_WHEN_DOING_PMAP_ENTER
	pmap_extract(vm_map_pmap(&p->p_vmspace->vm_map),tf->srr0, &pa);
	syncicache(pa, (p->p_p->ps_emul->e_esigcode -
	    p->p_p->ps_emul->e_sigcode));
#endif
}

/*
 * System call to cleanup state after a signal handler returns.
 */
int
sys_sigreturn(struct proc *p, void *v, register_t *retval)
{
	struct sys_sigreturn_args /* {
		syscallarg(struct sigcontext *) sigcntxp;
	} */ *uap = v;
	struct sigcontext sc;
	struct trapframe *tf;
	int error;

	if ((error = copyin(SCARG(uap, sigcntxp), &sc, sizeof sc)))
		return error;
	tf = trapframe(p);
	sc.sc_frame.srr1 &= ~PSL_VEC;
	sc.sc_frame.srr1 |= (tf->srr1 & PSL_VEC);
	if ((sc.sc_frame.srr1 & PSL_USERSTATIC) != (tf->srr1 & PSL_USERSTATIC))
		return EINVAL;
	bcopy(&sc.sc_frame, tf, sizeof *tf);
	p->p_sigmask = sc.sc_mask & ~sigcantmask;
	return EJUSTRETURN;
}

/*
 * Machine dependent system variables.
 * None for now.
 */
int
cpu_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
    size_t newlen, struct proc *p)
{
	/* all sysctl names at this level are terminal */
	if (namelen != 1)
		return ENOTDIR;
	switch (name[0]) {
	case CPU_ALLOWAPERTURE:
#ifdef APERTURE
		if (securelevel > 0)
			return (sysctl_int_lower(oldp, oldlenp, newp, newlen,
			    &allowaperture));
		else
			return (sysctl_int(oldp, oldlenp, newp, newlen,
			    &allowaperture));
#else
		return (sysctl_rdint(oldp, oldlenp, newp, 0));
#endif
	case CPU_ALTIVEC:
		return (sysctl_rdint(oldp, oldlenp, newp, ppc_altivec));
	default:
		return EOPNOTSUPP;
	}
}

u_long dumpmag = 0x04959fca;			/* magic number */
int dumpsize = 0;			/* size of dump in pages */
long dumplo = -1;			/* blocks */

/*
 * This is called by configure to set dumplo and dumpsize.
 * Dumps always skip the first CLBYTES of disk space
 * in case there might be a disk label stored there.
 * If there is extra space, put dump at the end to
 * reduce the chance that swapping trashes it.
 */
void dumpconf(void);

void
dumpconf(void)
{
	int nblks;	/* size of dump area */
	int i;

	if (dumpdev == NODEV ||
	    (nblks = (bdevsw[major(dumpdev)].d_psize)(dumpdev)) == 0)
		return;
	if (nblks <= ctod(1))
		return;

	/* Always skip the first block, in case there is a label there. */
	if (dumplo < ctod(1))
		dumplo = ctod(1);

        for (i = 0; i < ndumpmem; i++)
		dumpsize = max(dumpsize, dumpmem[i].end);

	/* Put dump at end of partition, and make it fit. */
	if (dumpsize > dtoc(nblks - dumplo - 1))
		dumpsize = dtoc(nblks - dumplo - 1);
	if (dumplo < nblks - ctod(dumpsize) - 1)
		dumplo = nblks - ctod(dumpsize) - 1;

}

#define BYTES_PER_DUMP  (PAGE_SIZE)  /* must be a multiple of pagesize */
static vaddr_t dumpspace;

int
reserve_dumppages(caddr_t p)
{
	dumpspace = (vaddr_t)p;
	return BYTES_PER_DUMP;
}

/*
 * cpu_dump: dump machine-dependent kernel core dump headers.
 */
int cpu_dump(void);
int
cpu_dump()
{
	int (*dump) (dev_t, daddr_t, caddr_t, size_t);
	long buf[dbtob(1) / sizeof (long)];
	kcore_seg_t	*segp;

	dump = bdevsw[major(dumpdev)].d_dump;

	segp = (kcore_seg_t *)buf;

	/*
	 * Generate a segment header.
	 */
	CORE_SETMAGIC(*segp, KCORE_MAGIC, MID_MACHINE, CORE_CPU);
	segp->c_size = dbtob(1) - ALIGN(sizeof(*segp));

	return (dump(dumpdev, dumplo, (caddr_t)buf, dbtob(1)));
}

void
dumpsys()
{
#if 0
	u_int npg;
	u_int i, j;
	daddr_t blkno;
	int (*dump) (dev_t, daddr_t, caddr_t, size_t);
	char *str;
	int maddr;
	extern int msgbufmapped;
	int error;

	/* save registers */

	msgbufmapped = 0;	/* don't record dump msgs in msgbuf */
	if (dumpdev == NODEV)
		return;
	/*
	 * For dumps during autoconfiguration,
	 * if dump device has already configured...
	 */
	if (dumpsize == 0)
		dumpconf();
	if (dumplo < 0)
		return;
	printf("dumping to dev %x, offset %ld\n", dumpdev, dumplo);

#ifdef UVM_SWAP_ENCRYPT
	uvm_swap_finicrypt_all();
#endif

	error = (*bdevsw[major(dumpdev)].d_psize)(dumpdev);
	if (error == -1) {
		printf("area unavailable\n");
		delay (10000000);
		return;
	}

	dump = bdevsw[major(dumpdev)].d_dump;
	error = cpu_dump();
	for (i = 0; !error && i < ndumpmem; i++) {
		npg = dumpmem[i].end - dumpmem[i].start;
		maddr = ptoa(dumpmem[i].start);
		blkno = dumplo + btodb(maddr) + 1;

		for (j = npg; j;
			j--, maddr += PAGE_SIZE, blkno+= btodb(PAGE_SIZE))
		{
			/* Print out how many MBs we have to go. */
                        if (dbtob(blkno - dumplo) % (1024 * 1024) < NBPG)
                                printf("%d ",
                                    (ptoa(dumpsize) - maddr) / (1024 * 1024));

			pmap_enter(pmap_kernel(), dumpspace, maddr,
				PROT_READ, PMAP_WIRED);
			if ((error = (*dump)(dumpdev, blkno,
			    (caddr_t)dumpspace, PAGE_SIZE)) != 0)
				break;
		}
	}

	switch (error) {

	case 0:         str = "succeeded\n\n";                  break;
	case ENXIO:     str = "device bad\n\n";                 break;
	case EFAULT:    str = "device not ready\n\n";           break;
	case EINVAL:    str = "area improper\n\n";              break;
	case EIO:       str = "i/o error\n\n";                  break;
	case EINTR:     str = "aborted from console\n\n";       break;
	default:        str = "error %d\n\n";                   break;
	}
	printf(str, error);

#else
	printf("dumpsys() - no yet supported\n");

#endif
	delay(5000000);         /* 5 seconds */

}

int
lcsplx(int ipl)
{
	return spllower(ipl);
}

/*
 * Halt or reboot the machine after syncing/dumping according to howto.
 */
__dead void
boot(int howto)
{
	static int syncing;

	if (cold) {
		if ((howto & RB_USERREQ) == 0)
			howto |= RB_HALT;
		goto haltsys;
	}

	boothowto = howto;
	if ((howto & RB_NOSYNC) == 0 && !syncing) {
		syncing = 1;
		vfs_shutdown();

		if ((howto & RB_TIMEBAD) == 0) {
			resettodr();
		} else {
			printf("WARNING: not updating battery clock\n");
		}
	}
	if_downall();

	uvm_shutdown();
	splhigh();
	cold = 1;

	if ((howto & RB_DUMP) != 0)
		dumpsys();

haltsys:
	config_suspend_all(DVACT_POWERDOWN);

	if ((howto & RB_HALT) != 0) {
		if ((howto & RB_POWERDOWN) != 0) {
#if NADB > 0
			delay(1000000);
			adb_poweroff();
			printf("WARNING: adb powerdown failed!\n");
#endif
			OF_interpret("shut-down", 0);
		}

		printf("halted\n\n");
		OF_exit();
	}
	printf("rebooting\n\n");

#if NADB > 0
	adb_restart();  /* not return */
#endif

	OF_interpret("reset-all", 0);
	OF_exit();
	printf("boot failed, spinning\n");
	for (;;) ;
	/* NOTREACHED */
}

typedef void  (void_f) (void);
void_f *pending_int_f = NULL;

/* call the bus/interrupt controller specific pending interrupt handler
 * would be nice if the offlevel interrupt code was handled here
 * instead of being in each of the specific handler code
 */
void
do_pending_int()
{
	if (pending_int_f != NULL) {
		(*pending_int_f)();
	}
}

/*
 * Notify the current process (p) that it has a signal pending,
 * process as soon as possible.
 */
void
signotify(struct proc *p)
{
	aston(p);
	cpu_unidle(p->p_cpu);
}

#ifdef MULTIPROCESSOR
void
cpu_unidle(struct cpu_info *ci)
{
	if (ci != curcpu())
		ppc_send_ipi(ci, PPC_IPI_NOP);
}
#endif

int ppc_configed_intr_cnt = 0;
struct intrhand ppc_configed_intr[MAX_PRECONF_INTR];

/*
 * True if the system has any non-level interrupts which are shared
 * on the same pin.
 */
int	intr_shared_edge;

void *
ppc_intr_establish(void *lcv, pci_intr_handle_t ih, int type, int level,
    int (*func)(void *), void *arg, const char *name)
{
	if (ppc_configed_intr_cnt < MAX_PRECONF_INTR) {
		ppc_configed_intr[ppc_configed_intr_cnt].ih_fun = func;
		ppc_configed_intr[ppc_configed_intr_cnt].ih_arg = arg;
		ppc_configed_intr[ppc_configed_intr_cnt].ih_type = type;
		ppc_configed_intr[ppc_configed_intr_cnt].ih_level = level;
		ppc_configed_intr[ppc_configed_intr_cnt].ih_irq = ih;
		ppc_configed_intr[ppc_configed_intr_cnt].ih_what = name;
		ppc_configed_intr_cnt++;
	} else {
		panic("ppc_intr_establish called before interrupt controller"
			" configured: driver %s too many interrupts", name);
	}
	/* disestablish is going to be tricky to supported for these :-) */
	return (void *)ppc_configed_intr_cnt;
}

intr_establish_t *intr_establish_func = (intr_establish_t *)ppc_intr_establish;
intr_disestablish_t *intr_disestablish_func;

intr_send_ipi_t ppc_no_send_ipi;
intr_send_ipi_t *intr_send_ipi_func = ppc_no_send_ipi;

void
ppc_no_send_ipi(struct cpu_info *ci, int id)
{
	panic("ppc_send_ipi called: no ipi function");
}

void
ppc_send_ipi(struct cpu_info *ci, int id)
{
	(*intr_send_ipi_func)(ci, id);
}

/* bcopy(), error on fault */
int
kcopy(const void *from, void *to, size_t size)
{
	faultbuf env;
	void *oldh = curproc->p_addr->u_pcb.pcb_onfault;

	if (setfault(&env)) {
		curproc->p_addr->u_pcb.pcb_onfault = oldh;
		return EFAULT;
	}
	bcopy(from, to, size);
	curproc->p_addr->u_pcb.pcb_onfault = oldh;

	return 0;
}

/* prototype for locore function */
void cpu_switchto_asm(struct proc *oldproc, struct proc *newproc);

void
cpu_switchto(struct proc *oldproc, struct proc *newproc)
{
	/*
	 * if this CPU is running a new process, flush the
	 * FPU/Altivec context to avoid an IPI.
	 */
#ifdef MULTIPROCESSOR
	struct cpu_info *ci = curcpu();
	if (ci->ci_fpuproc)
		save_fpu();
	if (ci->ci_vecproc)
		save_vec(ci->ci_vecproc);
#endif

	cpu_switchto_asm(oldproc, newproc);
}

license: unknown


code:
/*	$OpenBSD: mainbus.c,v 1.24 2015/05/06 02:36:01 jsg Exp $	*/

/*
 * Copyright (c) 1994, 1995 Carnegie-Mellon University.
 * All rights reserved.
 *
 * Author: Chris G. Demetriou
 *
 * Permission to use, copy, modify and distribute this software and
 * its documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 *
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 *
 * Carnegie Mellon requests users of this software to return to
 *
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 *
 * any improvements or extensions that they make and grant Carnegie the
 * rights to redistribute these changes.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>

#include <machine/autoconf.h>
#include <dev/ofw/openfirm.h>

/* Definition of the mainbus driver. */
static int	mbmatch(struct device *, void *, void *);
static void	mbattach(struct device *, struct device *, void *);
static int	mbprint(void *, const char *);

struct cfattach mainbus_ca = {
	sizeof(struct device), mbmatch, mbattach
};
struct cfdriver mainbus_cd = {
	NULL, "mainbus", DV_DULL
};

/* hw.product sysctl see sys/kern/kern_sysctl.c */
extern char *hw_prod, *hw_ver, *hw_vendor;

#define HH_REG_CONF 	0x90

/*ARGSUSED*/
static int
mbmatch(struct device *parent, void *cfdata, void *aux)
{

	/*
	 * That one mainbus is always here.
	 */
	return(1);
}

static void
mbattach(struct device *parent, struct device *self, void *aux)
{
	struct confargs nca;
	char name[64], *t = NULL;
	int reg[4], cpucnt;
	int node, len, slen;

	node = OF_peer(0);
	len = OF_getprop(node, "model", name, sizeof(name));
	if (len > 1) {
		name[len] = '\0';
		slen = strlen(name)+1;
		if ((t = malloc(slen, M_DEVBUF, M_NOWAIT)) != NULL)
			strlcpy(t, name, slen);

	}

	len = OF_getprop(node, "compatible", name, sizeof(name));
	if (len > 1) {
		name[len] = '\0';
		/* Old World Macintosh */
		if ((strncmp(name, "AAPL", 4)) == 0) {
			hw_vendor = "Apple Computer, Inc.";
			slen = strlen(t) + strlen(name) - 3;
			if ((hw_prod = malloc(slen, M_DEVBUF, M_NOWAIT)) != NULL) {
				snprintf(hw_prod, slen, "%s %s", t, name + 5);
				free(t, M_DEVBUF, 0);
			}
		} else {
			/* New World Macintosh or Unknown */
			hw_vendor = "Apple Computer, Inc.";
			hw_prod = t;
		}
	}
	printf(": model %s\n", hw_prod);

	/*
	 * Try to find and attach all of the CPUs in the machine.
	 */

	cpucnt = 0;
	ncpusfound = 0;
	node = OF_finddevice("/cpus");
	if (node != -1) {
		for (node = OF_child(node); node != 0; node = OF_peer(node)) {
			u_int32_t cpunum;
			int len;
			len = OF_getprop(node, "reg", &cpunum, sizeof cpunum);
			if (len == 4 && cpucnt == cpunum) {
				nca.ca_name = "cpu";
				nca.ca_reg = reg;
				reg[0] = cpucnt;
				config_found(self, &nca, mbprint);
				ncpusfound++;
				cpucnt++;
			}
		}
	}
	if (cpucnt == 0) {
		nca.ca_name = "cpu";
		nca.ca_reg = reg;
		reg[0] = 0;
		ncpusfound++;
		config_found(self, &nca, mbprint);
	}

	/*
	 * Special hack for SMP old world macs which lack /cpus and only have
	 * one cpu node.
	 */
	node = OF_finddevice("/hammerhead");
	if (node != -1) {
		len = OF_getprop(node, "reg", reg, sizeof(reg));
		if (len >= 2) {
			u_char *hh_base;
			int twoway = 0;

			if ((hh_base = mapiodev(reg[0], reg[1])) != NULL) {
				twoway = in32rb(hh_base + HH_REG_CONF) & 0x02;
				unmapiodev(hh_base, reg[1]);
			}
			if (twoway) {
				nca.ca_name = "cpu";
				nca.ca_reg = reg;
				reg[0] = 1;
				ncpusfound++;
				config_found(self, &nca, mbprint);
			}
		}
	}

	for (node = OF_child(OF_peer(0)); node; node=OF_peer(node)) {
		bzero (name, sizeof(name));
		if (OF_getprop(node, "device_type", name, sizeof(name)) <= 0) {
			if (OF_getprop(node, "name", name, sizeof(name)) <= 0) {
				printf ("name not found on node %x\n", node);
				continue;
			}
		}
		if (strcmp(name, "memory") == 0) {
			nca.ca_name = "mem";
			nca.ca_node = node;
			config_found(self, &nca, mbprint);
		}
		if (strcmp(name, "memory-controller") == 0) {
			nca.ca_name = "memc";
			nca.ca_node = node;
			config_found(self, &nca, mbprint);
		}
		if (strcmp(name, "pci") == 0) {
			nca.ca_name = "mpcpcibr";
			nca.ca_node = node;
			config_found(self, &nca, mbprint);
		}
		if (strcmp(name, "ht") == 0) {
			nca.ca_name = "ht";
			nca.ca_node = node;
			config_found(self, &nca, mbprint);
		}
		if (strcmp(name, "smu") == 0) {
			nca.ca_name = "smu";
			nca.ca_node = node;
			config_found(self, &nca, mbprint);
		}
	}
}

static int
mbprint(void *aux, const char *pnp)
{
	struct confargs *ca = aux;
	if (pnp)
		printf("%s at %s", ca->ca_name, pnp);

	return (UNCONF);
}


code:
/*	$OpenBSD: mem.c,v 1.21 2015/05/28 20:53:05 jcs Exp $	*/
/*	$NetBSD: mem.c,v 1.1 1996/09/30 16:34:50 ws Exp $ */

/*
 * Copyright (c) 1988 University of Utah.
 * Copyright (c) 1982, 1986, 1990, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * the Systems Programming Group of the University of Utah Computer
 * Science Department.
 *
 * 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.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
 *
 *	@(#)mem.c	8.3 (Berkeley) 1/12/94
 */

/*
 * Memory special file
 */

#include <sys/param.h>
#include <sys/buf.h>
#include <sys/systm.h>
#include <sys/ioccom.h>
#include <sys/uio.h>
#include <sys/malloc.h>
#include <sys/types.h>

#include <machine/cpu.h>

#include <uvm/uvm_extern.h>

#include <machine/conf.h>

/* open counter for aperture */
#ifdef APERTURE
static int ap_open_count = 0;
extern int allowaperture;
#endif

#ifndef SMALL_KERNEL

/*
 * The EEPROMs for Serial Presence Detect don't show up in the
 * OpenFirmware tree, but their contents are available through the
 * "dimm-info" property of the "/memory" node.  To make the
 * information available, we fake up an I2C bus with EEPROMs
 * containing the appropriate slices of the "dimm-info" property.
 */

#include <machine/autoconf.h>

#include <dev/i2c/i2cvar.h>
#include <dev/ofw/openfirm.h>

struct mem_softc {
	struct device sc_dev;

	uint8_t *sc_buf;
	int sc_len;
};

/* Size of a single SPD entry in "dimm-info" property. */
#define SPD_SIZE	128

int	mem_match(struct device *, void *, void *);
void	mem_attach(struct device *, struct device *, void *);

struct cfdriver mem_cd = {
	NULL, "mem", DV_DULL
};

struct cfattach mem_ca = {
	sizeof(struct mem_softc), mem_match, mem_attach
};

int	mem_i2c_acquire_bus(void *, int);
void	mem_i2c_release_bus(void *, int);
int	mem_i2c_exec(void *, i2c_op_t, i2c_addr_t,
	    const void *, size_t, void *, size_t, int);

int
mem_match(struct device *parent, void *cf, void *aux)
{
	struct confargs *ca = aux;

	if (strcmp(ca->ca_name, "mem") == 0)
		return (1);
	return (0);
}

void
mem_attach(struct device *parent, struct device *self, void *aux)
{
	struct mem_softc *sc = (struct mem_softc *)self;
	struct confargs *ca = aux;
	struct i2c_controller ic;
	struct i2c_attach_args ia;

	sc->sc_len = OF_getproplen(ca->ca_node, "dimm-info");
	if (sc->sc_len > 0) {
		sc->sc_buf = malloc(sc->sc_len, M_DEVBUF, M_NOWAIT);
		if (sc->sc_buf == NULL) {
			printf(": can't allocate memory\n");
			return;
		}
	}

	printf("\n");

	if (sc->sc_len > 0) {
		OF_getprop(ca->ca_node, "dimm-info", sc->sc_buf, sc->sc_len);

		memset(&ic, 0, sizeof ic);
		ic.ic_cookie = sc;
		ic.ic_acquire_bus = mem_i2c_acquire_bus;
		ic.ic_release_bus = mem_i2c_release_bus;
		ic.ic_exec = mem_i2c_exec;

		memset(&ia, 0, sizeof ia);
		ia.ia_tag =
		ia.ia_name = "spd";
		ia.ia_addr = 0;
		while (ia.ia_addr * SPD_SIZE < sc->sc_len) {
			/* Skip entries that have not been filled in. */
			if (sc->sc_buf[ia.ia_addr * SPD_SIZE] != 0)
				config_found(self, &ia, NULL);
			ia.ia_addr++;
		}

		/* No need to keep the "dimm-info" contents around. */
		free(sc->sc_buf, M_DEVBUF, 0);
		sc->sc_len = -1;
	}
}

int
mem_i2c_acquire_bus(void *cookie, int flags)
{
	return (0);
}

void
mem_i2c_release_bus(void *cookie, int flags)
{
}

int
mem_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
    const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
{
	struct mem_softc *sc = cookie;
	size_t off;

	if (op != I2C_OP_READ_WITH_STOP || cmdlen != 1)
		return (EINVAL);

	off = addr * SPD_SIZE + *(const uint8_t *)cmdbuf;
	if (off + len > sc->sc_len)
		return (EIO);

	memcpy(buf, &sc->sc_buf[off], len);
	return (0);
}

#endif /* SMALL_KERNEL */

/*ARGSUSED*/
int
mmopen(dev_t dev, int flag, int mode, struct proc *p)
{

	switch (minor(dev)) {
		case 0:
		case 1:
		case 2:
		case 12:
			break;
#ifdef APERTURE
	case 4:
	        if (suser(p, 0) != 0 || !allowaperture)
			return (EPERM);

		/* authorize only one simultaneous open() unless
		 * allowaperture=3 */
		if (ap_open_count > 0 && allowaperture < 3)
			return(EPERM);
		ap_open_count++;
		break;
#endif
		default:
			return (ENXIO);
	}
	return (0);
}

/*ARGSUSED*/
int
mmclose(dev_t dev, int flag, int mode, struct proc *p)
{
#ifdef APERTURE
	if (minor(dev) == 4)
		ap_open_count = 0;
#endif
	return 0;
}

/*ARGSUSED*/
int
mmrw(dev_t dev, struct uio *uio, int flags)
{
	vaddr_t v;
	vsize_t c;
	struct iovec *iov;
	int error = 0;
	static caddr_t zeropage;

	while (uio->uio_resid > 0 && error == 0) {
		iov = uio->uio_iov;
		if (iov->iov_len == 0) {
			uio->uio_iov++;
			uio->uio_iovcnt--;
			if (uio->uio_iovcnt < 0)
				panic("mmrw");
			continue;
		}
		switch (minor(dev)) {

		/* minor device 0 is physical memory */
		case 0:
			v = uio->uio_offset;
			c = uio->uio_resid;
			/* This doesn't allow device mapping!	XXX */
			pmap_real_memory(&v, &c);
			error = uiomove((caddr_t)v, c, uio);
			continue;

		/* minor device 1 is kernel memory */
		case 1:
			v = uio->uio_offset;
			c = ulmin(iov->iov_len, MAXPHYS);
			error = uiomove((caddr_t)v, c, uio);
			continue;

		/* minor device 2 is EOF/RATHOLE */
		case 2:
			if (uio->uio_rw == UIO_WRITE)
				uio->uio_resid = 0;
			return 0;

		/* minor device 12 (/dev/zero) is source of nulls on read,
		 * rathole on write
		 */
		case 12:
			if (uio->uio_rw == UIO_WRITE) {
				c = iov->iov_len;
				break;
			}
			if (zeropage == NULL)
				zeropage = malloc(PAGE_SIZE, M_TEMP,
				    M_WAITOK | M_ZERO);
			c = ulmin(iov->iov_len, PAGE_SIZE);
			error = uiomove(zeropage, c, uio);
			continue;

		default:
			return ENXIO;
		}
		if (error)
			break;
		iov->iov_base += c;
		iov->iov_len -= c;
		uio->uio_offset += c;
		uio->uio_resid -= c;
	}
	return error;
}

paddr_t
mmmmap(dev_t dev, off_t off, int prot)
{
	return (-1);
}

/*ARGSUSED*/
int
mmioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
{
	return (EOPNOTSUPP);
}


code:
/*	$OpenBSD: ofw_machdep.c,v 1.55 2015/07/21 05:58:34 jsg Exp $	*/
/*	$NetBSD: ofw_machdep.c,v 1.1 1996/09/30 16:34:50 ws Exp $	*/

/*
 * Copyright (C) 1996 Wolfgang Solfrank.
 * Copyright (C) 1996 TooLs GmbH.
 * 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.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by TooLs GmbH.
 * 4. The name of TooLs GmbH may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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 "akbd.h"
#include "ukbd.h"
#include "wsdisplay.h"
#include "zstty.h"

#include <sys/param.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/systm.h>

#include <uvm/uvm_extern.h>

#include <powerpc/powerpc.h>
#include <machine/autoconf.h>

#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_pci.h>

#include <macppc/macppc/ofw_machdep.h>

#if NAKBD > 0
#include <dev/adb/akbdvar.h>
#endif

#if NUKBD > 0
#include <dev/usb/ukbdvar.h>
#endif

#if NWSDISPLAY > 0
#include <dev/wscons/wsdisplayvar.h>
#include <dev/rasops/rasops.h>
#endif

struct mem_region64 {
	uint64_t start;
	uint32_t size;
} __packed;

#define	OFMEM_REGIONS	32
static struct mem_region   OFmem[OFMEM_REGIONS + 1], OFavail[OFMEM_REGIONS + 3];
static struct mem_region64 OFmem64[OFMEM_REGIONS + 1];

/*
 * Section 5.1.7. Memory Management Unit properties.
 */
struct ofw_map {
	uint32_t	om_virt;
	uint32_t	om_size;
	uint32_t	om_phys;
	uint32_t	om_mode;
} __packed;

struct ofw_map64 {
	uint32_t	om_virt;
	uint32_t	om_size;
	uint64_t	om_phys;
	uint32_t	om_mode;
} __packed;

static struct ofw_map	ofw_maps[OFMEM_REGIONS];
static struct ofw_map64	ofw_maps64[OFMEM_REGIONS];

#if NWSDISPLAY > 0
struct ofwfb {
	struct rasops_info	ofw_ri;
	struct wsscreen_descr	ofw_wsd;
};

/* Early boot framebuffer */
static struct ofwfb ofwfb;
#endif

int	save_ofw_mapping(void);
void	ofw_consinit(int);
void	ofw_read_mem_regions(int, int, int);

/*
 * This is called during initppc, before the system is really initialized.
 * It shall provide the total and the available regions of RAM.
 * Both lists must have a zero-size entry as terminator.
 * The available regions need not take the kernel into account, but needs
 * to provide space for two additional entry beyond the terminating one.
 */
void
ppc_mem_regions(struct mem_region **memp, struct mem_region **availp)
{
	*memp = OFmem;

	/* HACK */
	if (OFmem[0].size == 0) {
		*memp = OFavail;
	}

	*availp = OFavail;
}

void
ofw_read_mem_regions(int phandle, int address_cells, int size_cells)
{
	int nreg, navail;
	int i, j;
	uint physpages;

	switch (address_cells) {
	default:
	case 1:
		nreg = OF_getprop(phandle, "reg", OFmem,
		    sizeof(OFmem[0]) * OFMEM_REGIONS) / sizeof(OFmem[0]);
		break;
	case 2:
		nreg = OF_getprop(phandle, "reg", OFmem64,
		    sizeof(OFmem64[0]) * OFMEM_REGIONS) / sizeof(OFmem64[0]);
		break;
	}

	navail = OF_getprop(phandle, "available", OFavail,
	    sizeof(OFavail[0]) * OFMEM_REGIONS) / sizeof(OFavail[0]);
	if (nreg <= 0 || navail <= 0)
		panic("no memory?");

	/* Eliminate empty or unreachable regions. */
	switch (address_cells) {
	default:
	case 1:
		for (i = 0, j = 0; i < nreg; i++) {
			if (OFmem[i].size == 0)
				continue;
			if (i != j) {
				OFmem[j].start = OFmem[i].start;
				OFmem[j].size = OFmem[i].size;
				OFmem[i].start = 0;
				OFmem[i].size = 0;
			}
			j++;
		}
		break;
	case 2:
		physpages = 0;
		for (i = 0, j = 0; i < nreg; i++) {
			if (OFmem64[i].size == 0)
				continue;
			physpages += atop(OFmem64[i].size);
			if (OFmem64[i].start >= 1ULL << 32)
				continue;
			OFmem[j].start = OFmem64[i].start;
			if (OFmem64[i].start + OFmem64[i].size >= 1ULL << 32)
				OFmem[j].size = (1ULL << 32) - OFmem64[i].start;
			else
				OFmem[j].size = OFmem64[i].size;
			j++;
		}
		physmem = physpages;
		break;
	}
}

typedef void (fwcall_f)(int, int);
extern fwcall_f *fwcall;
fwcall_f fwentry;

int OF_stdout;
int OF_stdin;

/*
 * Called early in the boot process, we are still running on the stack
 * provided by the bootloader using the firmware's page table.
 */
int
save_ofw_mapping(void)
{
	int chosen, memory, root, mmui, mmu = -1;
	int acells, scells;
	int i, len;

	if ((chosen = OF_finddevice("/chosen")) == -1)
		return (0);

	ofw_consinit(chosen);

	/* Get memory node. */
	memory = OF_finddevice("/memory");
	if (memory == -1)
		panic("no memory?");

	/* Are physical addresses encoded in 32 or 64 bits? */
	root = OF_parent(memory);
	if (OF_getprop(root, "#address-cells", &acells, 4) <= 0)
		acells = 1;
	if (OF_getprop(root, "#size-cells", &scells, 4) <= 0)
		scells = 1;

	if (scells != 1)
		panic("unexpected memory layout %d:%d", acells, scells);

	ofw_read_mem_regions(memory, acells, scells);

	/* Get firmware mappings. */
	if (OF_getprop(chosen, "mmu", &mmui, sizeof(int)) != -1)
		mmu = OF_instance_to_package(mmui);
	if (mmu != -1) {
		len = OF_getproplen(mmu, "translations");
		if (len <= 0)
			return (0);
	}

	switch (acells) {
	case 2:
		OF_getprop(mmu, "translations", ofw_maps64, sizeof(ofw_maps64));

		for (i = 0; i < nitems(ofw_maps64); i++) {
			if (ofw_maps64[i].om_phys >= 1ULL << 32)
				continue;

			ofw_maps[i].om_virt = ofw_maps64[i].om_virt;
			ofw_maps[i].om_size = ofw_maps64[i].om_size;
			ofw_maps[i].om_phys = (uint32_t)ofw_maps64[i].om_phys;
			ofw_maps[i].om_mode = ofw_maps64[i].om_mode;
		}
		break;
	case 1:
	default:
		OF_getprop(mmu, "translations", ofw_maps, sizeof(ofw_maps));
		break;
	}

	/*
	 * Next time we'll call the firmware make sure we save and
	 * restore our MMU settings.
	 */
	fwcall = &fwentry;

	return (0);
}

static int display_ofh;
int cons_brightness;
int cons_backlight_available;
int fbnode;

void of_display_console(void);

void
ofwconprobe()
{
	char type[32];
	int stdout_node;

	stdout_node = OF_instance_to_package(OF_stdout);

	/* handle different types of console */

	bzero(type, sizeof(type));
	if (OF_getprop(stdout_node,  "device_type", type, sizeof(type)) == -1) {
		return; /* XXX */
	}
	if (strcmp(type, "display") == 0) {
		of_display_console();
		return;
	}
	if (strcmp(type, "serial") == 0) {
#if NZSTTY > 0
		/* zscnprobe/zscninit do all the required initialization */
		return;
#endif
	}

	OF_stdout = OF_open("screen");
	OF_stdin = OF_open("keyboard");

	/* cross fingers that this works. */
	of_display_console();

	return;
}

#define DEVTREE_UNKNOWN 0
#define DEVTREE_USB	1
#define DEVTREE_ADB	2
int ofw_devtree = DEVTREE_UNKNOWN;

#define OFW_HAVE_USBKBD 1
#define OFW_HAVE_ADBKBD 2
int ofw_have_kbd = 0;

void ofw_recurse_keyboard(int pnode);
void ofw_find_keyboard(void);

void
ofw_recurse_keyboard(int pnode)
{
	char name[32];
	int old_devtree;
	int len;
	int node;

	for (node = OF_child(pnode); node != 0; node = OF_peer(node)) {

		len = OF_getprop(node, "name", name, 20);
		if (len == 0)
			continue;
		name[len] = 0;
		if (strcmp(name, "keyboard") == 0) {
			/* found a keyboard node, where is it? */
			if (ofw_devtree == DEVTREE_USB) {
				ofw_have_kbd |= OFW_HAVE_USBKBD;
			} else if (ofw_devtree == DEVTREE_ADB) {
				ofw_have_kbd |= OFW_HAVE_ADBKBD;
			} else {
				/* hid or some other keyboard? igore */
			}
			continue;
		}

		old_devtree = ofw_devtree;

		if (strcmp(name, "adb") == 0) {
			ofw_devtree = DEVTREE_ADB;
		}
		if (strcmp(name, "usb") == 0) {
			ofw_devtree = DEVTREE_USB;
		}

		ofw_recurse_keyboard(node);

		ofw_devtree = old_devtree; /* nest? */
	}
}

void
ofw_find_keyboard()
{
	int stdin_node;
	char iname[32];
	int len, attach = 0;

	stdin_node = OF_instance_to_package(OF_stdin);
	len = OF_getprop(stdin_node, "name", iname, 20);
	iname[len] = 0;
	printf(" console in [%s]", iname);

	/* GRR, apple removed the interface once used for keyboard
	 * detection walk the OFW tree to find keyboards and what type.
	 */

	ofw_recurse_keyboard(OF_peer(0));

	len = OF_getprop(OF_peer(0), "model", iname, sizeof(iname));
	iname[len] = 0;

	if (ofw_have_kbd == (OFW_HAVE_USBKBD | OFW_HAVE_ADBKBD)) {
		/*
		 * If a PowerBook reports having ABD and USB keyboards,
		 * use the builtin ADB one for console, the USB one is
		 * certainly a HID device.
		 */
		 if (strncmp(iname, "PowerBook", 9) == 0)
			ofw_have_kbd = OFW_HAVE_ADBKBD;
		else
			ofw_have_kbd = OFW_HAVE_USBKBD;
		printf("USB and ADB found");
	}
	if (ofw_have_kbd == OFW_HAVE_USBKBD) {
#if NUKBD > 0
		printf(", using USB\n");
		ukbd_cnattach();
		attach = 1;
#endif
	} else if (ofw_have_kbd == OFW_HAVE_ADBKBD) {
#if NAKBD >0
		printf(", using ADB\n");
		akbd_cnattach();
		attach = 1;
#endif
	}
	if (attach == 0) {
#if NUKBD > 0
		printf(", no keyboard attached, trying usb anyway\n");
		ukbd_cnattach();
#else
		printf(", no keyboard found!\n");
#endif
	}
}

void
of_display_console(void)
{
	struct ofw_pci_register addr[8];
	int cons_height, cons_width, cons_linebytes, cons_depth;
	uint32_t cons_addr;
	char name[32];
	int len, err;
	int stdout_node;

	stdout_node = OF_instance_to_package(OF_stdout);
	len = OF_getprop(stdout_node, "name", name, 20);
	name[len] = 0;
	printf("console out [%s]", name);
	display_ofh = OF_stdout;
	err = OF_getprop(stdout_node, "width", &cons_width, 4);
	if ( err != 4) {
		cons_width = 0;
	}
	err = OF_getprop(stdout_node, "linebytes", &cons_linebytes, 4);
	if ( err != 4) {
		cons_linebytes = cons_width;
	}
	err = OF_getprop(stdout_node, "height", &cons_height, 4);
	if ( err != 4) {
		cons_height = 0;
	}
	err = OF_getprop(stdout_node, "depth", &cons_depth, 4);
	if ( err != 4) {
		cons_depth = 0;
	}
	err = OF_getprop(stdout_node, "address", &cons_addr, 4);
	if ( err != 4) {
		OF_interpret("frame-buffer-adr", 1, &cons_addr);
	}

	ofw_find_keyboard();

	fbnode = stdout_node;
	len = OF_getprop(stdout_node, "assigned-addresses", addr, sizeof(addr));
	if (len == -1) {
		fbnode = OF_parent(stdout_node);
		len = OF_getprop(fbnode, "name", name, 20);
		name[len] = 0;

		printf("using parent %s:", name);
		len = OF_getprop(fbnode, "assigned-addresses",
			addr, sizeof(addr));
		if (len < sizeof(addr[0])) {
			panic(": no address");
		}
	}

	if (OF_getnodebyname(0, "backlight") != 0)
		cons_backlight_available = 1;

#if 1
	printf(": memaddr %x, size %x ", addr[0].phys_lo, addr[0].size_lo);
	printf(": consaddr %x ", cons_addr);
	printf(": ioaddr %x, size %x", addr[1].phys_lo, addr[1].size_lo);
	printf(": width %d linebytes %d height %d depth %d\n",
		cons_width, cons_linebytes, cons_height, cons_depth);
#endif

#if NWSDISPLAY > 0
{
	struct ofwfb *fb = &ofwfb;
	struct rasops_info *ri = &fb->ofw_ri;
	long defattr;

	ri->ri_width = cons_width;
	ri->ri_height = cons_height;
	ri->ri_depth = cons_depth;
	ri->ri_stride = cons_linebytes;
	ri->ri_flg = RI_CENTER | RI_FULLCLEAR | RI_CLEAR;
	ri->ri_bits = (void *)mapiodev(cons_addr, cons_linebytes * cons_height);
	ri->ri_hw = fb;

	if (cons_depth == 8)
		of_setcolors(rasops_cmap, 0, 256);

	rasops_init(ri, 160, 160);

	strlcpy(fb->ofw_wsd.name, "std", sizeof(fb->ofw_wsd.name));
	fb->ofw_wsd.capabilities = ri->ri_caps;
	fb->ofw_wsd.ncols = ri->ri_cols;
	fb->ofw_wsd.nrows = ri->ri_rows;
	fb->ofw_wsd.textops = &ri->ri_ops;
#if 0
	fb->ofw_wsd.fontwidth = ri->ri_font->fontwidth;
	fb->ofw_wsd.fontheight = ri->ri_font->fontheight;
#endif

	ri->ri_ops.alloc_attr(ri, 0, 0, 0, &defattr);
	wsdisplay_cnattach(&fb->ofw_wsd, ri, 0, 0, defattr);
}
#endif
}

void
ofwconsswitch(struct rasops_info *ri)
{
#if NWSDISPLAY > 0
	ri->ri_width = ofwfb.ofw_ri.ri_width;
	ri->ri_height = ofwfb.ofw_ri.ri_height;
	ri->ri_depth = ofwfb.ofw_ri.ri_depth;
	ri->ri_stride = ofwfb.ofw_ri.ri_stride;

	ri->ri_bits = ofwfb.ofw_ri.ri_bits /* XXX */;
#endif
}

void
of_setbacklight(int on)
{
	if (cons_backlight_available == 0)
		return;

	if (on)
		OF_call_method_1("backlight-on", display_ofh, 0);
	else
		OF_call_method_1("backlight-off", display_ofh, 0);
}

void
of_setbrightness(int brightness)
{
	if (cons_backlight_available == 0)
		return;

	if (brightness < MIN_BRIGHTNESS)
		brightness = MIN_BRIGHTNESS;
	else if (brightness > MAX_BRIGHTNESS)
		brightness = MAX_BRIGHTNESS;

	cons_brightness = brightness;

	/*
	 * The OF method is called "set-contrast" but affects brightness.
	 * Don't ask.
	 */
	OF_call_method_1("set-contrast", display_ofh, 1, cons_brightness);

	/* XXX this routine should also save the brightness settings in the nvram */
}

uint8_t of_cmap[256 * 3];

void
of_setcolors(const uint8_t *cmap, unsigned int index, unsigned int count)
{
	bcopy(cmap, of_cmap, sizeof(of_cmap));
	OF_call_method_1("set-colors", display_ofh, 3, &of_cmap, index, count);
}

#include <dev/cons.h>

cons_decl(ofw);

/*
 * Console support functions
 */
void
ofwcnprobe(struct consdev *cd)
{
}

void
ofwcninit(struct consdev *cd)
{
}
void
ofwcnputc(dev_t dev, int c)
{
	char ch = c;

	OF_write(OF_stdout, &ch, 1);
}
int
ofwcngetc(dev_t dev)
{
        unsigned char ch = '\0';
        int l;

        while ((l = OF_read(OF_stdin, &ch, 1)) != 1)
                if (l != -2 && l != 0)
                        return -1;
        return ch;
}

void
ofwcnpollc(dev_t dev, int on)
{
}

struct consdev consdev_ofw = {
        ofwcnprobe,
        ofwcninit,
        ofwcngetc,
        ofwcnputc,
        ofwcnpollc,
        NULL,
};

void
ofw_consinit(int chosen)
{
	struct consdev *cp = &consdev_ofw;

	OF_getprop(chosen, "stdin", &OF_stdin, sizeof(OF_stdin));
	OF_getprop(chosen, "stdout", &OF_stdout, sizeof(OF_stdout));

	/* If the screen is to be console, but not active, open it */
	if (OF_stdout == 0)
		OF_stdout = OF_open("screen");

	cn_tab = cp;
}


code:
/*	$OpenBSD: ofw_machdep.h,v 1.9 2015/04/07 14:36:34 mpi Exp $	*/

/*
 * Copyright (c) 2002, Miodrag Vallat.
 *
 * 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.
 *
 */

extern int cons_backlight_available;

void ofwconprobe(void);

struct rasops_info;
void ofwconsswitch(struct rasops_info *);

/*
 * For some reason, setting the brightness under 0x29 from OF switches the
 * backlight off, and it won't be switched on again until you set the
 * brightness above 0x33.  All hail hysteresis! -- miod
 */
#define	MIN_BRIGHTNESS		0x34
#define	MAX_BRIGHTNESS		0xff
#define	STEP_BRIGHTNESS		8
#define	DEFAULT_BRIGHTNESS	0x80
extern int cons_brightness;

void of_setbacklight(int);
void of_setbrightness(int);
void of_setcolors(const uint8_t *, unsigned int, unsigned int);

void OF_quiesce(void);


code:
/*	$OpenBSD: ofwreal.S,v 1.3 2003/10/16 05:03:22 deraadt Exp $	*/
/*	$NetBSD: ofwreal.S,v 1.1 1996/09/30 16:34:51 ws Exp $	*/

/*
 * Copyright (C) 1996 Wolfgang Solfrank.
 * Copyright (C) 1996 TooLs GmbH.
 * 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.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by TooLs GmbH.
 * 4. The name of TooLs GmbH may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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.
 */

/*
 * This file provides a real-mode client interface on machines, that
 * (incorrectly) only implement virtual mode client interface.
 *
 * It assumes though, that any actual memory in the machine is
 * mapped 1:1 even by the virtual mode OpenFirmware.
 * Furthermore it assumes that addresses returned by OpenFirmware are not
 * accessed by the client.
 *
 */
#include <machine/asm.h>
#include <machine/psl.h>
#include <machine/trap.h>
#include <machine/param.h>

#define	CACHELINE	32		/* Note that this value is really hardwired */

	.data
ofentry:	.long	0		/* actual entry to firmware in virtual mode */

#define	SRSIZE		(16*4+4)
#define	SPRGSIZE	(4*4)
#define	SDR1SIZE	4
#define MSRSIZE		4
#define	SVSIZE		(SRSIZE+SPRGSIZE+SDR1SIZE+MSRSIZE)
#define BATSIZE		(16*4)

	.global _C_LABEL(fwcall)
_C_LABEL(fwcall): .long 0

.lcomm	fwsave,SVSIZE,8
.lcomm	fwbatsave,BATSIZE,8
.lcomm	clsave,SVSIZE,8
.lcomm	clbatsave,BATSIZE,8
.lcomm	ofsrsave,16*4,4	/* 16 words of 4 bytes to store OF segment registers */
.lcomm	srsave,16*4,4	/* 16 words of 4 bytes to swap OF segment registers*/
	.globl _C_LABEL(ofmsr)
_C_LABEL(ofmsr):	.long	0	/* area to store msr for openfirmware*/

	.text
_ENTRY(_C_LABEL(ofw_init))
	mflr	%r31			/* save return address */

	mr	%r13,%r6		/* save args (only pointer used) */
	lis	%r8,ofentry@ha
	stw	%r5,ofentry@l(%r8)	/* save virtual mode firmware entry */

	lis	%r4,fwcall@ha		/* call ofw directly until vm setup */
	stw	%r5,fwcall@l(%r4)

	mfmsr	%r5
	lis	%r4,_C_LABEL(ofmsr)@ha	/* save msr from openfirmware */
	stw	%r5,_C_LABEL(ofmsr)@l(%r4)
#if 0
	lis	%r0,(0x80001ffe)@ha
	addi	%r0,%r0,(0x80001ffe)@l
	mtdbatu 0,%r0
	lis	%r0,(0x80000022)@ha
	addi	%r0,%r0,(0x80000022)@l
	mtdbatl 0,%r0
#endif

	lis	%r3,fwsave@ha	/* save the mmu values of the firmware */
	addi	%r3,%r3,fwsave@l
	lis	%r4,fwbatsave@ha
	addi	%r4,%r4,fwbatsave@l
	bl	savemmu

	/* save openfirmware address mappings */
	bl	_C_LABEL(save_ofw_mapping)

#if 0
	/* dont really need the bats from firmware saved, 0 to disable */
	lis	%r3,fwbatsave@ha
	addi	%r3,%r3,fwbatsave@l
	li	%r4,64
	li	%r5,0
1:	subi	%r4,%r4,%r4
	stwx	%r5,%r4,%r3
	cmpi	4,0,0
	bne	1b
#endif

	mr	%r6,%r13		/* restore args pointer */
	mtlr	%r31			/* restore return address */
	blr

/*
 * Save everyting related to the mmu to the saveare pointed to by r3.
 */
	.type	savemmu,@function
savemmu:

	mr	%r6,%r4	/* r4 holds pointer to BAT save area */

	li	%r4,0			/* save SRs */
1:
	addis	%r4,%r4,-0x10000000@ha
	or.	%r4,%r4,%r4
	mfsrin	%r5,%r4
	stwu	%r5,4(%r3)
	bne	1b

	mfibatl	%r4,0			/* save BATs */
	stw	%r4,0(%r6)
	mfibatu	%r4,0
	stw	%r4,4(%r6)
	mfibatl	%r4,1
	stw	%r4,8(%r6)
	mfibatu	%r4,1
	stw	%r4,0xc(%r6)
	mfibatl	%r4,2
	stw	%r4,0x10(%r6)
	mfibatu	%r4,2
	stw	%r4,0x14(%r6)
	mfibatl	%r4,3
	stw	%r4,0x18(%r6)
	mfibatu	%r4,3
	stw	%r4,0x1c(%r6)
	mfdbatl	%r4,0
	stw	%r4,0x20(%r6)
	mfdbatu	%r4,0
	stw	%r4,0x24(%r6)
	mfdbatl	%r4,1
	stw	%r4,0x28(%r6)
	mfdbatu	%r4,1
	stw	%r4,0x2c(%r6)
	mfdbatl	%r4,2
	stw	%r4,0x30(%r6)
	mfdbatu	%r4,2
	stw	%r4,0x34(%r6)
	mfdbatl	%r4,3
	stw	%r4,0x38(%r6)
	mfdbatu	%r4,3
	stw	%r4,0x3c(%r6)

	mfsprg	%r4,0			/* save SPRGs */
	stw	%r4,4(%r3)
	mfsprg	%r4,1
	stw	%r4,8(%r3)
	mfsprg	%r4,2
	stw	%r4,12(%r3)
	mfsprg	%r4,3
	stw	%r4,16(%r3)

	mfsdr1	%r4			/* save SDR1 */
	stw	%r4,20(%r3)

	addi	%r4,%r3,24

	mfmsr	%r4
	stw	%r4,24(%r3)

	sync
	isync

	blr

/*
 * Restore everyting related to the mmu from the savearea pointed to by r3.
 * and bats pointed to by r4.
 */
	.type	restoremmu,@function
restoremmu:

	li	%r0,0
	mtmsr	%r0
	mr	%r6,%r4			/* pointer to sr to restore */
	li	%r4,0			/* restore SRs */
1:
	lwzu	%r5,4(%r3)
	addis	%r4,%r4,-0x10000000@ha
	or.	%r4,%r4,%r4
	mtsrin	%r5,%r4
	bne	1b

	mfmsr	%r4
	lis	%r5,(PSL_IR|PSL_DR)@h		/* turn off MMU */
	ori	%r5,%r5,(PSL_IR|PSL_DR)@l	/* turn off MMU */
	andc	%r4,%r4,%r5				/* turn off MMU */
	mtmsr	%r4
	isync

	li	%r4,0			/* first, invalidate BATs */
	mtibatu	0,%r4
	mtibatu	1,%r4
	mtibatu	2,%r4
	mtibatu	3,%r4
	mtdbatu	0,%r4
	mtdbatu	1,%r4
	mtdbatu	2,%r4
	mtdbatu	3,%r4

	lwz	%r4,0(%r6)
	mtibatl	0,%r4			/* restore BATs */
	lwz	%r4,4(%r6)
	mtibatu	0,%r4
	lwz	%r4,8(%r6)
	mtibatl	1,%r4
	lwz	%r4,12(%r6)
	mtibatu	1,%r4
	lwz	%r4,16(%r6)
	mtibatl	2,%r4
	lwz	%r4,20(%r6)
	mtibatu	2,%r4
	lwz	%r4,24(%r6)
	mtibatl	3,%r4
	lwz	%r4,28(%r6)
	mtibatu	3,%r4
	lwz	%r4,32(%r6)
	mtdbatl	0,%r4
	lwz	%r4,36(%r6)
	mtdbatu	0,%r4
	lwz	%r4,40(%r6)
	mtdbatl	1,%r4
	lwz	%r4,44(%r6)
	mtdbatu	1,%r4
	lwz	%r4,48(%r6)
	mtdbatl	2,%r4
	lwz	%r4,52(%r6)
	mtdbatu	2,%r4
	lwz	%r4,56(%r6)
	mtdbatl	3,%r4
	lwz	%r4,60(%r6)
	mtdbatu	3,%r4

	lwz	%r4,4(%r3)
	mtsprg	0,4			/* restore SPRGs */
	lwz	%r4,8(%r3)
	mtsprg	1,4
	lwz	%r4,12(%r3)
	mtsprg	2,4
	lwz	%r4,16(%r3)
	mtsprg	3,4

	sync				/* remove everything from tlb */
	lis	%r4,0x40000@ha
	li	%r5,0x1000
1:
	subf.	%r4,%r5,%r4
	tlbie	%r4
	bne	1b

	sync
	tlbsync
	sync

	lwz	%r4,20(%r3)
	sync
	mtsdr1	%r4			/* restore SDR1 */

	/* tlbia */
	sync
	li	%r5,0x40
	mtctr	%r5
	li	%r4,0
    1:
	tlbie	%r4
	addi	%r4,%r4,0x1000
	bdnz	1b
	sync
	tlbsync
	sync

	lwz	%r4,24(%r3)
	mtmsr	%r4
	isync

	blr

_ENTRY(_C_LABEL(fwentry))
	stwu	%r1,-16(%r1)
	mflr	%r4
	stw	%r4,20(%r1)
	stw	%r3,12(%r1)			/* save arg */

	lis	%r3,clsave@ha		/* save mmu values of client */
	addi	%r3,%r3,clsave@l
	lis	%r4,clbatsave@ha	/* save mmu values of client */
	addi	%r4,%r4,clbatsave@l
	bl	savemmu

	lis	%r3,fwsave@ha		/* restore mmu values of firmware */
	addi	%r3,%r3,fwsave@l
	lis	%r4,fwbatsave@ha
	addi	%r4,%r4,fwbatsave@l
	bl	restoremmu

	lis	%r3,ofentry@ha
	lwz	%r3,ofentry@l(%r3)	/* get actual firmware entry */
	mtlr	%r3

	mfmsr	%r4
	ori	%r4,%r4,PSL_IR|PSL_DR	/* turn on MMU */
	mtmsr	%r4
	isync

	lwz	%r3,12(%r1)		/* restore arg */
	blrl				/* do actual firmware call */

	stw	%r3,12(%r1)		/* save return value */

	lis	%r3,fwsave@ha		/* save mmu values of firmare */
	addi	%r3,%r3,fwsave@l	/* (might not be necessary, but... */
	lis	%r4,fwbatsave@ha
	addi	%r4,%r4,fwbatsave@l
	bl	savemmu

	lis	%r3,clsave@ha		/* restore mmu values of client */
	addi	%r3,%r3,clsave@l
	lis	%r4,clbatsave@ha	/* save mmu values of client */
	addi	%r4,%r4,clbatsave@l
	bl	restoremmu

	lwz	%r4,20(%r1)
	lwz	%r3,12(%r1)		/* restore return value */

	mtlr	%r4
	addi	%r1,%r1,16
	blr

/*
 * OpenFirmware entry point
 */
_ENTRY(_C_LABEL(openfirmware))
	stwu	%r1,-16(%r1)
	mflr	%r0			/* save return address */
	stw	%r0,20(%r1)

	lis	%r4,fwcall@ha
	lwz	%r4,fwcall@l(%r4)

	mtlr	%r4
	blrl

	lwz	%r0,20(%r1)
	mtlr	%r0
	lwz	%r1,0(%r1)
	blr

/*
 * Switch to/from OpenFirmware real mode stack
 *
 * Note: has to be called as the very first thing in OpenFirmware interface routines.
 * E.g.:
 * int
 * OF_xxx(arg1, arg2)
 * type arg1, arg2;
 * {
 *	static struct {
 *		char *name;
 *		int nargs;
 *		int nreturns;
 *		char *method;
 *		int arg1;
 *		int arg2;
 *		int ret;
 *	} args = {
 *		"xxx",
 *		2,
 *		1,
 *	};
 *
 *	ofw_stack();
 *	args.arg1 = arg1;
 *	args.arg2 = arg2;
 *	if (openfirmware(&args) < 0)
 *		return -1;
 *	return args.ret;
 * }
 */
.lcomm	firmstk,NBPG,16
.comm	_C_LABEL(OF_buf),NBPG,PGOFSET

_ENTRY(_C_LABEL(ofw_stack))
	mfmsr	%r8			/* turn off interrupts */
	andi.	%r0,%r8,~(PSL_EE|PSL_RI)@l
	mtmsr	%r0
	stw	%r8,4(%r1)		/* abuse return address slot */

	lwz	%r5,0(%r1)		/* get length of stack frame */
	subf	%r5,%r1,%r5

	lis	%r7,firmstk+NBPG-8@ha
	addi	%r7,%r7,firmstk+NBPG-8@l
	li	%r6,0xf
	andc	%r7,%r7,%r6
	lis	%r6,ofw_back@ha
	addi	%r6,%r6,ofw_back@l
	subf	%r4,%r5,%r7		/* make room for stack frame on new stack */
	stwu	%r1,-16(%r7)
	stw	%r6,4(%r7)		/* setup return pointer */

	stw	%r7,-16(%r4)

	addi	%r3,%r1,%r8
	addi	%r1,%r4,-16
	subi	%r5,%r5,%r8
	subi	%r4,%r4,%r8

	b	_C_LABEL(ofbcopy)	/* and copy it */

	.type ofw_back,@function
ofw_back:
	lwz	%r1,0(%r1)		/* get callers original stack pointer */

	lwz	%r0,4(%r1)		/* get saved msr from abused slot */
	mtmsr	%r0

	lwz	%r1,0(%r1)		/* return */
	lwz	%r0,4(%r1)
	mtlr	%r0
	blr