/*	$NetBSD: locore.s,v 1.114 2014/03/15 12:04:22 tsutsui Exp $	*/

/*
 * Copyright (c) 1988 University of Utah.
 * Copyright (c) 1980, 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.
 *
 * from: Utah $Hdr: locore.s 1.66 92/12/22$
 *
 *	@(#)locore.s	8.6 (Berkeley) 5/27/94
 */

#include "opt_compat_netbsd.h"
#include "opt_compat_svr4.h"
#include "opt_compat_sunos.h"
#include "opt_fpsp.h"
#include "opt_ddb.h"
#include "opt_kgdb.h"
#include "opt_lockdebug.h"
#include "opt_m68k_arch.h"

#include "assym.h"
#include <machine/asm.h>
#include <machine/trap.h>

#include "ksyms.h"

/*
 * Temporary stack for a variety of purposes.
 * Try and make this the first thing is the data segment so it
 * is page aligned.  Note that if we overflow here, we run into
 * our text segment.
 */
	.data
	.space	PAGE_SIZE
ASLOCAL(tmpstk)

ASLOCAL(bug_vbr)
	.long	0

#include <mvme68k/mvme68k/vectors.s>


/*
 * Macro to relocate a symbol, used before MMU is enabled.
 */
#define	_RELOC(var, ar)		\
	lea	var,ar

#define	RELOC(var, ar)		_RELOC(_C_LABEL(var), ar)
#define	ASRELOC(var, ar)	_RELOC(_ASM_LABEL(var), ar)

/*
 * Macro to call into the Bug ROM monitor
 */
#define	CALLBUG(func)	\
	trap #15; .short func

/*
 * Initialization
 *
 * The bootstrap loader loads us in starting at 0, and VBR is non-zero.
 * On entry, args on stack are boot device, boot filename, console unit,
 * boot flags (howto), boot device name, filesystem type name.
 */
BSS(lowram,4)
BSS(esym,4)

	.globl	_C_LABEL(edata)
	.globl	_C_LABEL(etext),_C_LABEL(end)


/*
 * This is for kvm_mkdb, and should be the address of the beginning
 * of the kernel text segment (not necessarily the same as kernbase).
 */
	.text
GLOBAL(kernel_text)

/*
 * start of kernel and .text!
 */
ASENTRY_NOPROFILE(start)
	movw	#PSL_HIGHIPL,%sr	| no interrupts
	movl	#0,%a5			| RAM starts at 0 (a5)
	movl	%sp@(4), %d7		| get boothowto
	movl	%sp@(8), %d6		| get bootaddr
	movl	%sp@(12),%d5		| get bootctrllun
	movl	%sp@(16),%d4		| get bootdevlun
	movl	%sp@(20),%d3		| get bootpart
	movl	%sp@(24),%d2		| get esyms

	RELOC(bootpart,%a0)
	movl	%d3, %a0@		| save bootpart
	RELOC(bootdevlun,%a0)
	movl	%d4, %a0@		| save bootdevlun
	RELOC(bootctrllun,%a0)
	movl	%d5, %a0@		| save booctrllun
	RELOC(bootaddr,%a0)
	movl	%d6, %a0@		| save bootaddr
	RELOC(boothowto,%a0)
	movl	%d7, %a0@		| save boothowto
	/* note: d3-d7 free, d2 still in use */

	ASRELOC(tmpstk, %a0)
	movl	%a0,%sp			| give ourselves a temporary stack

	RELOC(edata,%a0)		| clear out BSS
	movl	#_C_LABEL(end) - 4, %d0	| (must be <= 256 kB)
	subl	#_C_LABEL(edata), %d0
	lsrl	#2,%d0
1:	clrl	%a0@+
	dbra	%d0,1b

	RELOC(esym, %a0)
	movl	%d2,%a0@		| store end of symbol table
	/* d2 now free */
	RELOC(lowram, %a0)
	movl	%a5,%a0@		| store start of physical memory
	movl	#CACHE_OFF,%d0
	movc	%d0,%cacr		| clear and disable on-chip cache(s)

	/* ask the Bug what we are... */
	clrl	%sp@-
	CALLBUG(MVMEPROM_GETBRDID)
	movl	%sp@+,%a1

	/* copy to a struct mvmeprom_brdid */
	movl	#MVMEPROM_BRDID_SIZE,%d0
	RELOC(boardid,%a0)
1:	movb	%a1@+,%a0@+
	subql	#1,%d0
	jbne	1b

	/*
	 * Grab the model number from _boardid and use the value
	 * to setup machineid, cputype, and mmutype.
	 */
	clrl	%d0
	RELOC(boardid,%a1)
	movw	%a1@(MVMEPROM_BRDID_MODEL_OFFSET),%d0
	RELOC(machineid,%a0)
	movl	%d0,%a0@

	ASRELOC(Lbrdid2mach,%a0)
Lbrdmatch:
	cmpw	%a0@+,%d0
	jbeq	Lgotmatch
	addw	#0x12,%a0		| Each entry is 20-2 bytes long
	tstw	%a0@
	jbne	Lbrdmatch

	/*
	 * If we fall to here, the board is not supported.
	 * Print a warning, then drop out to the Bug.
	 */
	movl	#Lenotconf,%sp@-
	movl	#Lnotconf,%sp@-
	CALLBUG(MVMEPROM_OUTSTRCRLF)
	addql	#8,%sp			| clean up stack after call

	CALLBUG(MVMEPROM_EXIT)
	/* NOTREACHED */

	.data
Lnotconf:
	.ascii	"Sorry, the kernel isn't configured for this model."
Lenotconf:
	.even

ASLOCAL(Lbrdid2mach)
#ifdef MVME147
	.word	MVME_147
	.word		CPU_68030
	.word		MMU_68030
	.word		FPU_68882
	.long		_C_LABEL(busaddrerr2030)
	.long		_C_LABEL(busaddrerr2030)
	.long		Linit147
#endif
#ifdef MVME162
	.word	MVME_162
	.word		CPU_68040
	.word		MMU_68040
	.word		FPU_68040
	.long		_C_LABEL(buserr40)
	.long		_C_LABEL(addrerr4060)
	.long		Linit1x2
#endif
#ifdef MVME167
	.word	MVME_167
	.word		CPU_68040
	.word		MMU_68040
	.word		FPU_68040
	.long		_C_LABEL(buserr40)
	.long		_C_LABEL(addrerr4060)
	.long		Linit1x7
#endif
#ifdef MVME172
	.word	MVME_172
	.word		CPU_68060
	.word		MMU_68040
	.word		FPU_68060
	.long		_C_LABEL(buserr60)
	.long		_C_LABEL(addrerr4060)
	.long		Linit1x2
#endif
#ifdef MVME177
	.word	MVME_177
	.word		CPU_68060
	.word		MMU_68040
	.word		FPU_68060
	.long		_C_LABEL(buserr60)
	.long		_C_LABEL(addrerr4060)
	.long		Linit1x7
#endif
	.word	0
	.text
	.even

/*
 * We have a match, so the kernel should support this board.
 * a0 points to the matching entry in Lbrdid2mach.
 */
Lgotmatch:
	movew	%a0@+,%d1		| Copy the CPU type
	extl	%d1
	RELOC(cputype,%a1)
	movel	%d1,%a1@
	movew	%a0@+,%d1		| Copy the MMU type
	extl	%d1
	RELOC(mmutype,%a1)
	movel	%d1,%a1@
	movew	%a0@+,%d1		| Copy the FPU type
	extl	%d1
	RELOC(fputype,%a1)
	movel	%d1,%a1@
	movel	%a0@+,%a2		| Fetch the bus error vector
	RELOC(vectab,%a1)
	movl	%a2,%a1@(8)
	movel	%a0@+,%a2		| Fetch the address error vector
	movl	%a2,%a1@(12)
	movel	%a0@,%a0		| Finally, the board-specific init code
	jmp	%a0@


#ifdef MVME147
Linit147:
	/* MVME-147 - 68030 CPU/MMU, 68882 FPU */
	/* XXXCDC SHUTUP 147 CALL */
	movb	#0, 0xfffe1026		| serial interrupt off
	movb	#0, 0xfffe1018		| timer 1 off
	movb	#0, 0xfffe1028		| ethernet off
	/* XXXCDC SHUTUP 147 CALL */

	/* Save our ethernet address */
	RELOC(mvme_ea, %a0)
	lea	0xfffe0778,%a1		| XXXCDC -- HARDWIRED HEX
	movb	#0x08,%a0@+
	clrb	%a0@+
	movb	#0x3e,%a0@+
	movql	#0x0f,%d0
	andb	%a1@+,%d0
	orb	#0x20,%d0
	movb	%d0,%a0@+
	movb	%a1@+,%a0@+
	movb	%a1@,%a0@

	/*
	 * Fix up the physical addresses of the MVME147's onboard
	 * I/O registers.
	 */
	RELOC(intiobase_phys, %a0);
	movl	#INTIOBASE147,%a0@
	RELOC(intiotop_phys, %a0);
	movl	#INTIOTOP147,%a0@

	/* initialise list of physical memory segments for pmap_bootstrap */
	RELOC(phys_seg_list, %a0)
	movl	%a5,%a0@		| phys_seg_list[0].ps_start
	movl	0xfffe0774,%d1		| End + 1 of onboard memory
	movl	%d1,%a0@(4)		| phys_seg_list[0].ps_end
	clrl	%a0@(8)			| phys_seg_list[0].ps_startpage

	/* offboard RAM */
	clrl	%a0@(0x0c)		| phys_seg_list[1].ps_start
	movl	#PAGE_SIZE-1,%d0
	addl	0xfffe0764,%d0		| Start of offboard segment
	andl	#-PAGE_SIZE,%d0		| Round up to page boundary
	jbeq	Lsavmaxmem		| Jump if none defined
	movl	#PAGE_SIZE,%d1		| Note: implicit '+1'
	addl	0xfffe0768,%d1		| End of offboard segment
	andl	#-PAGE_SIZE,%d1		| Round up to page boundary
	cmpl	%d1,%d0			| Quick and dirty validity check
	jbcs	Loff_ok			| Yup, looks good.
	movel	%a0@(4),%d1		| Just use onboard RAM otherwise
	jbra	Lsavmaxmem
Loff_ok:
	movl	%d0,%a0@(0x0c)		| phys_seg_list[1].ps_start
	movl	%d1,%a0@(0x10)		| phys_seg_list[1].ps_end
	clrl	%a0@(0x14)		| phys_seg_list[1].ps_startpage

	/*
	 * Offboard RAM needs to be cleared to zero to initialise parity
	 * on most VMEbus RAM cards. Without this, some cards will buserr
	 * when first read.
	 */
	movel	%d0,%a0			| offboard start address again.
Lclearoff:
	clrl	%a0@+			| zap a word
	cmpl	%a0,%d1			| reached end?
	jbne	Lclearoff

Lsavmaxmem:
	moveq	#PGSHIFT,%d2
	lsrl	%d2,%d1			| convert to page (click) number
	RELOC(maxmem, %a0)
	movl	%d1,%a0@		| save as maxmem
	jra	Lstart1
#endif

#if defined(MVME162) || defined(MVME172)
Linit1x2:
	/* MVME-162 - 68040 CPU/MMU/FPU */
	/* MVME-172 - 68060 CPU/MMU/FPU */

	/*
	 * Verify the user has removed the GPIO#0 jumper...
	 */
	btst	#0,0xfff4202d		| Clear == jumper installed
	jne	1f			| Ok.

	movl	#Le1x2jump,%sp@-
	movl	#L1x2jump,%sp@-
	CALLBUG(MVMEPROM_OUTSTRCRLF)
	addql	#8,%sp			| clean up stack after call

	CALLBUG(MVMEPROM_EXIT)
	/* NOTREACHED */

1:
	/*
	 * Determine if this board has a VMEchip2
	 */
	btst	#1,0xfff4202e		| VMEchip2 presence detect
	jne	2f			| Jump if it doesn't exist.

	/*
	 * Disable all interrupts from VMEchip2. This is especially
	 * useful when the kernel doesn't have the VMEchip2 driver
	 * configured. If we didn't do this, then we're at the mercy
	 * of whatever VMEchip2 interrupts the ROM set up. For example,
	 * hitting the ABORT switch could kill the system...
	 */
	movl	0xfff40088,%d0
	andl	#0xff7fffff,%d0		| Clear 'MIEN'
	movl	%d0,0xfff40088
2:
	/*
	 * Determine how much onboard memory is installed
	 */
	movql	#0x07,%d0
	andb	0xfff42024,%d0
	ASRELOC(Ldramsize1x2,%a0)
	movl	%a0@(%d0:w:4),%d1	| Lookup the size
	jeq	Lmemcquery		| Assume a MEMC chip if this is zero.
	jra	Lis1xx_common

	.data
	.even
	/*
	 * Table of DRAM register size values -> actual size in bytes
	 */
ASLOCAL(Ldramsize1x2)
	.long	0x00100000
	.long	0x00200000
	.long	0x00000000
	.long	0x00400000
	.long	0x00400000
	.long	0x00800000
	.long	0x00000000
	.long	0x01000000

L1x2jump:
	.ascii	"You must remove the jumper from pins 15-16 of J22 (mvme162)"
	.ascii	"or pins 1-2\015\012"
	.ascii	"J11 (mvme162-LX) first! See NetBSD/mvme68k FAQ for details."
Le1x2jump:
	.even

	.text
#endif

#if defined(MVME167) || defined(MVME177)
Linit1x7:
	/* MVME-167 - 68040 CPU/MMU/FPU */
	/* MVME-177 - 68060 CPU/MMU/FPU */

	/*
	 * Verify the user has removed the GPIO#0 jumper...
	 */
	movel	#0x00000001,%d0
	andl	0xfff40088,%d0		| Clear == jumper installed
	jne	1f			| Ok.

	movl	#Le1x7jump,%sp@-
	movl	#L1x7jump,%sp@-
	CALLBUG(MVMEPROM_OUTSTRCRLF)
	addql	#8,%sp			| clean up stack after call

	CALLBUG(MVMEPROM_EXIT)
	/* NOTREACHED */

1:
	/*
	 * Disable all interrupts from VMEchip2. This is especially
	 * useful when the kernel doesn't have the VMEchip2 driver
	 * configured. If we didn't do this, then we're at the mercy
	 * of whatever VMEchip2 interrupts the ROM set up. For example,
	 * hitting the ABORT switch could kill the system...
	 */
	movl	0xfff40088,%d0
	andl	#0xff7fffff,%d0		| Clear 'MIEN'
	movl	%d0,0xfff40088

	.data
	.even
L1x7jump:
	.ascii	"You must remove the jumper from pins 1-2 of J1!\015\012"
	.ascii	"See NetBSD/mvme68k FAQ for details."
Le1x7jump:
	.even

	.text
#endif

#if defined(MVME162) || defined(MVME167) || defined(MVME172) || defined(MVME177)
Lmemcquery:
	/*
	 * Figure out the size of onboard DRAM by querying
	 * the memory controller ASIC(s)
	 */
	lea	0xfff43008,%a0		| MEMC040/MEMECC Controller #1
	jbsr	memc040read
	movl	%d0,%d1

	lea	0xfff43108,%a0		| MEMC040/MEMECC Controller #2
	jbsr	memc040read
	addl	%d0,%d1

Lis1xx_common:
	/* Save our ethernet address */
	RELOC(mvme_ea, %a0)
	lea	0xfffc1f2c,%a1
	movb	%a1@+,%a0@+
	movb	%a1@+,%a0@+
	movb	%a1@+,%a0@+
	movb	%a1@+,%a0@+
	movb	%a1@+,%a0@+
	movb	%a1@,%a0@

	/*
	 * Fix up the physical addresses of the onboard
	 * I/O registers.
	 */
	RELOC(intiobase_phys, %a0);
	movl	#INTIOBASE1xx,%a0@
	RELOC(intiotop_phys, %a0);
	movl	#INTIOTOP1xx,%a0@

	/*
	 * Initialise first physical memory segment with onboard RAM details
	 */
	RELOC(phys_seg_list, %a0)
	movl	%a5,%a0@		| phys_seg_list[0].ps_start
	movl	%d1,%a0@(4)		| phys_seg_list[0].ps_end
	clrl	%a0@(8)			| phys_seg_list[0].ps_startpage

	/* offboard RAM */
	clrl	%a0@(0x0c)		| phys_seg_list[1].ps_start
	movl	#PAGE_SIZE-1,%d0
	addl	0xfffc0000,%d0		| Start of offboard segment
	andl	#-PAGE_SIZE,%d0		| Round up to page boundary
	jbeq	Ldone1xx		| Jump if none defined
	movl	#PAGE_SIZE,%d1		| Note: implicit '+1'
	addl	0xfffc0004,%d1		| End of offboard segment
	andl	#-PAGE_SIZE,%d1		| Round up to page boundary
	cmpl	%d1,%d0			| Quick and dirty validity check
	jbcs	Lramsave1xx		| Yup, looks good.
	movel	%a0@(4),%d1		| Just use onboard RAM otherwise
	jbra	Ldone1xx

Lramsave1xx:
	movl	%d0,%a0@(0x0c)		| phys_seg_list[1].ps_start
	movl	%d1,%a0@(0x10)		| phys_seg_list[1].ps_end
	clrl	%a0@(0x14)		| phys_seg_list[1].ps_startpage

	/*
	 * Offboard RAM needs to be cleared to zero to initialise parity
	 * on most VMEbus RAM cards. Without this, some cards will buserr
	 * when first read.
	 */
	movel	%d0,%a0			| offboard start address again.
Lramclr1xx:
	clrl	%a0@+			| zap a word
	cmpl	%a0,%d1			| reached end?
	jbne	Lramclr1xx

Ldone1xx:
	moveq	#PGSHIFT,%d2
	lsrl	%d2,%d1			| convert to page (click) number
	RELOC(maxmem, %a0)
	movl	%d1,%a0@		| save as maxmem

	/* FALLTHROUGH to Lstart1 */
#endif


Lstart1:
/* initialize source/destination control registers for movs */
	moveq	#FC_USERD,%d0		| user space
	movc	%d0,%sfc		|   as source
	movc	%d0,%dfc		|   and destination of transfers
/*
 * configure kernel and lwp0 VA space so we can get going
 */
#if NKSYMS || defined(DDB) || defined(MODULAR)
	RELOC(esym,%a0)			| end of static kernel text/data syms
	movl	%a0@,%d2
	jne	Lstart2
#endif
	movl	#_C_LABEL(end),%d2	| end of static kernel text/data
Lstart2:
	addl	#PAGE_SIZE-1,%d2
	andl	#PG_FRAME,%d2		| round to a page
	movl	%d2,%a4
	addl	%a5,%a4			| convert to PA
	pea	%a5@			| firstpa
	pea	%a4@			| nextpa
	RELOC(pmap_bootstrap,%a0)
	jbsr	%a0@			| pmap_bootstrap(firstpa, nextpa)
	addql	#8,%sp

/*
 * Enable the MMU.
 * Since the kernel is mapped logical == physical, we just turn it on.
 */
	RELOC(Sysseg_pa, %a0)		| system segment table addr
	movl	%a0@,%d1		| read value (a PA)
	RELOC(mmutype, %a0)
	cmpl	#MMU_68040,%a0@		| 68040?
	jne	Lmotommu1		| no, skip
	.long	0x4e7b1807		| movc d1,srp
	jra	Lstploaddone
Lmotommu1:
	RELOC(protorp, %a0)
	movl	#0x80000202,%a0@	| nolimit + share global + 4 byte PTEs
	movl	%d1,%a0@(4)		| + segtable address
	pmove	%a0@,%srp		| load the supervisor root pointer
	movl	#0x80000002,%a0@	| reinit upper half for CRP loads
Lstploaddone:
	RELOC(mmutype, %a0)
	cmpl	#MMU_68040,%a0@		| 68040?
	jne	Lmotommu2		| no, skip
	moveq	#0,%d0			| ensure TT regs are disabled
	.long	0x4e7b0004		| movc d0,itt0
	.long	0x4e7b0005		| movc d0,itt1
	.long	0x4e7b0006		| movc d0,dtt0
	.long	0x4e7b0007		| movc d0,dtt1
	.word	0xf4d8			| cinva bc
	.word	0xf518			| pflusha
	movl	#0x8000,%d0
	.long	0x4e7b0003		| movc d0,tc
#ifdef M68060
	RELOC(cputype, %a0)
	cmpl	#CPU_68060,%a0@		| 68060?
	jne	Lnot060cache
	movl	#1,%d0
	.long	0x4e7b0808		| movcl d0,pcr
	movl	#0xa0808000,%d0
	movc	%d0,%cacr		| enable store buffer, both caches
	jmp	Lenab1
Lnot060cache:
#endif
	movl	#0x80008000,%d0
	movc	%d0,%cacr		| turn on both caches
	jmp	Lenab1
Lmotommu2:
	pflusha
	movl	#0x82c0aa00,%sp@-	| value to load TC with
	pmove	%sp@,%tc		| load it

/*
 * Should be running mapped from this point on
 */
Lenab1:
/* Point the CPU VBR at our vector table */
	movc	%vbr,%d0		| Preserve Bug's VBR address
	movl	%d0,_ASM_LABEL(bug_vbr)
	movl	#_C_LABEL(vectab),%d0	| get our VBR address
	movc	%d0,%vbr
	lea	_ASM_LABEL(tmpstk),%sp	| temporary stack
/* call final pmap setup */
	jbsr	_C_LABEL(pmap_bootstrap_finalize)
/* set kernel stack, user SP */
	movl	_C_LABEL(lwp0uarea),%a1	| get lwp0 uarea
	lea	%a1@(USPACE-4),%sp	| set kernel stack to end of area
	movl	#USRSTACK-4,%a2
	movl	%a2,%usp		| init user SP
	tstl	_C_LABEL(fputype)	| Have an FPU?
	jeq	Lenab2			| No, skip.
	clrl	%a1@(PCB_FPCTX)		| ensure null FP context
	movl	%a1,%sp@-
	jbsr	_C_LABEL(m68881_restore) | restore it (does not kill a1)
	addql	#4,%sp
Lenab2:
	cmpl	#MMU_68040,_C_LABEL(mmutype)	| 68040?
	jeq	Ltbia040		| yes, cache already on
	pflusha
	movl	#CACHE_ON,%d0
	movc	%d0,%cacr		| clear cache(s)
	jra	Lenab3
Ltbia040:
	.word	0xf518
Lenab3:
/*
 * final setup for C code:
 * Create a fake exception frame so that cpu_lwp_fork() can copy it.
 * main() nevers returns; we exit to user mode from a forked process
 * later on.
 */
	jbsr	_C_LABEL(mvme68k_init)	| additional pre-main initialization
	movw	#PSL_LOWIPL,%sr		| lower SPL
	clrw	%sp@-			| vector offset/frame type
	clrl	%sp@-			| PC - filled in by "execve"
	movw	#PSL_USER,%sp@-		| in user mode
	clrl	%sp@-			| stack adjust count and padding
	lea	%sp@(-64),%sp		| construct space for D0-D7/A0-A7
	lea	_C_LABEL(lwp0),%a0	| save pointer to frame
	movl	%sp,%a0@(L_MD_REGS)	|   in lwp0.l_md.md_regs

	jra	_C_LABEL(main)		| main()

#if defined(MVME162) || defined(MVME167) || defined(MVME172) || defined(MVME177)
/*
 * Probe for a memory controller ASIC (MEMC040 or MEMECC) at the
 * address in a0. If found, return the size in bytes of any RAM
 * controlled by the ASIC in d0. Otherwise return zero.
 */
ASLOCAL(memc040read)
	moveml	%d1-%d2/%a1-%a2,%sp@-	| save scratch regs
	movc	%vbr,%d2		| Save vbr
	RELOC(vectab,%a2)		| Install our own vectab, temporarily
	movc	%a2,%vbr
	ASRELOC(Lmemc040berr,%a1)	| get address of bus error handler
	movl	%a2@(8),%sp@-		| Save current bus error handler addr
	movl	%a1,%a2@(8)		| Install our own handler
	movl	%sp,%d0			| Save current stack pointer value
	movql	#0x07,%d1
	andb	%a0@,%d1		| Access MEMC040/MEMECC
	movl	#0x400000,%d0
	lsll	%d1,%d0			| Convert to memory size, in bytes
Lmemc040ret:
	movc	%d2,%vbr		| Restore original vbr
	movl	%sp@+,%a2@(8)		| Restore original bus error handler
	moveml  %sp@+,%d1-%d2/%a1-%a2
	rts
/*
 * If the memory controller doesn't exist, we get a bus error trying
 * to access a0@ above. Control passes here, where we flag 'no bytes',
 * ditch the exception frame and return as normal.
 */
Lmemc040berr:
	movl	%d0,%sp			| Get rid of the exception frame
	movql	#0,%d0			| No ASIC at this location, then!
	jbra	Lmemc040ret		| Done
#endif

/*
 * Trap/interrupt vector routines
 */
#include <m68k/m68k/trap_subr.s>

/*
 * Use common m68k bus error and address error handlers.
 */
#include <m68k/m68k/busaddrerr.s>

/*
 * FP exceptions.
 */
ENTRY_NOPROFILE(fpfline)
#if defined(M68040)
	cmpl	#FPU_68040,_C_LABEL(fputype) | 68040 FPU?
	jne	Lfp_unimp		| no, skip FPSP
	cmpw	#0x202c,%sp@(6)		| format type 2?
	jne	_C_LABEL(illinst)	| no, not an FP emulation
#ifdef FPSP
	jmp	_ASM_LABEL(fpsp_unimp)	| yes, go handle it
#else
	clrl	%sp@-			| stack adjust count
	moveml	#0xFFFF,%sp@-		| save registers
	moveq	#T_FPEMULD,%d0		| denote as FP emulation trap
	jra	_ASM_LABEL(fault)	| do it
#endif
Lfp_unimp:
#endif /* M68040 */
	jra	_C_LABEL(illinst)

ENTRY_NOPROFILE(fpunsupp)
#if defined(M68040)
	cmpl	#FPU_68040,_C_LABEL(fputype) | 68040 FPU?
	jne	Lfp_unsupp		| No, skip FPSP
#ifdef FPSP
	jmp	_ASM_LABEL(fpsp_unsupp)	| yes, go handle it
#else
	clrl	%sp@-			| stack adjust count
	moveml	#0xFFFF,%sp@-		| save registers
	moveq	#T_FPEMULD,%d0		| denote as FP emulation trap
	jra	_ASM_LABEL(fault)	| do it
#endif
Lfp_unsupp:
#endif /* M68040 */
	jra	_C_LABEL(illinst)

/*
 * Handles all other FP coprocessor exceptions.
 * Note that since some FP exceptions generate mid-instruction frames
 * and may cause signal delivery, we need to test for stack adjustment
 * after the trap call.
 */
ENTRY_NOPROFILE(fpfault)
	clrl	%sp@-		| stack adjust count
	moveml	#0xFFFF,%sp@-	| save user registers
	movl	%usp,%a0	| and save
	movl	%a0,%sp@(FR_SP)	|   the user stack pointer
	clrl	%sp@-		| no VA arg
	movl	_C_LABEL(curpcb),%a0 | current pcb
	lea	%a0@(PCB_FPCTX),%a0 | address of FP savearea
	fsave	%a0@		| save state
#if defined(M68040) || defined(M68060)
	/* always null state frame on 68040, 68060 */
	cmpl	#FPU_68040,_C_LABEL(fputype)
	jge	Lfptnull
#endif
	tstb	%a0@		| null state frame?
	jeq	Lfptnull	| yes, safe
	clrw	%d0		| no, need to tweak BIU
	movb	%a0@(1),%d0	| get frame size
	bset	#3,%a0@(0,%d0:w) | set exc_pend bit of BIU
Lfptnull:
	fmovem	%fpsr,%sp@-	| push fpsr as code argument
	frestore %a0@		| restore state
	movl	#T_FPERR,%sp@-	| push type arg
	jra	_ASM_LABEL(faultstkadj) | call trap and deal with stack cleanup


/*
 * Other exceptions only cause four and six word stack frame and require
 * no post-trap stack adjustment.
 */

ENTRY_NOPROFILE(badtrap)
	moveml	#0xC0C0,%sp@-		| save scratch regs
	movw	%sp@(22),%sp@-		| push exception vector info
	clrw	%sp@-
	movl	%sp@(22),%sp@-		| and PC
	jbsr	_C_LABEL(straytrap)	| report
	addql	#8,%sp			| pop args
	moveml	%sp@+,#0x0303		| restore regs
	jra	_ASM_LABEL(rei)		| all done

ENTRY_NOPROFILE(trap0)
	clrl	%sp@-			| stack adjust count
	moveml	#0xFFFF,%sp@-		| save user registers
	movl	%usp,%a0		| save the user SP
	movl	%a0,%sp@(FR_SP)		|   in the savearea
	movl	%d0,%sp@-		| push syscall number
	jbsr	_C_LABEL(syscall)	| handle it
	addql	#4,%sp			| pop syscall arg
	tstl	_C_LABEL(astpending)	| AST pending?
	jne	Lrei1			| Yup, go deal with it.
	movl	%sp@(FR_SP),%a0		| grab and restore
	movl	%a0,%usp		|   user SP
	moveml	%sp@+,#0x7FFF		| restore most registers
	addql	#8,%sp			| pop SP and stack adjust
	rte

/*
 * Trap 12 is the entry point for the cachectl "syscall" (both HPUX & BSD)
 *	cachectl(command, addr, length)
 * command in d0, addr in a1, length in d1
 */
ENTRY_NOPROFILE(trap12)
	movl	_C_LABEL(curlwp),%a0
	movl	%a0@(L_PROC),%sp@-	| push current proc pointer
	movl	%d1,%sp@-		| push length
	movl	%a1,%sp@-		| push addr
	movl	%d0,%sp@-		| push command
	jbsr	_C_LABEL(cachectl1)	| do it
	lea	%sp@(16),%sp		| pop args
	jra	_ASM_LABEL(rei)		| all done

/*
 * Trace (single-step) trap.  Kernel-mode is special.
 * User mode traps are simply passed on to trap().
 */
ENTRY_NOPROFILE(trace)
	clrl	%sp@-			| stack adjust count
	moveml	#0xFFFF,%sp@-
	moveq	#T_TRACE,%d0

	| Check PSW and see what happen.
	|   T=0 S=0	(should not happen)
	|   T=1 S=0	trace trap from user mode
	|   T=0 S=1	trace trap on a trap instruction
	|   T=1 S=1	trace trap from system mode (kernel breakpoint)

	movw	%sp@(FR_HW),%d1		| get PSW
	notw	%d1			| XXX no support for T0 on 680[234]0
	andw	#PSL_TS,%d1		| from system mode (T=1, S=1)?
	jeq	Lkbrkpt			| yes, kernel breakpoint
	jra	_ASM_LABEL(fault)	| no, user-mode fault

/*
 * Trap 15 is used for:
 *	- GDB breakpoints (in user programs)
 *	- KGDB breakpoints (in the kernel)
 *	- trace traps for SUN binaries (not fully supported yet)
 * User mode traps are simply passed to trap().
 */
ENTRY_NOPROFILE(trap15)
	clrl	%sp@-			| stack adjust count
	moveml	#0xFFFF,%sp@-
	moveq	#T_TRAP15,%d0
	movw	%sp@(FR_HW),%d1		| get PSW
	andw	#PSL_S,%d1		| from system mode?
	jne	Lkbrkpt			| yes, kernel breakpoint
	jra	_ASM_LABEL(fault)	| no, user-mode fault

Lkbrkpt: | Kernel-mode breakpoint or trace trap. (d0=trap_type)
	| Save the system sp rather than the user sp.
	movw	#PSL_HIGHIPL,%sr	| lock out interrupts
	lea	%sp@(FR_SIZE),%a6	| Save stack pointer
	movl	%a6,%sp@(FR_SP)		|  from before trap

	| If were are not on tmpstk switch to it.
	| (so debugger can change the stack pointer)
	movl	%a6,%d1
	cmpl	#_ASM_LABEL(tmpstk),%d1
	jls	Lbrkpt2			| already on tmpstk
	| Copy frame to the temporary stack
	movl	%sp,%a0			| a0=src
	lea	_ASM_LABEL(tmpstk)-96,%a1 | a1=dst
	movl	%a1,%sp			| sp=new frame
	movql	#FR_SIZE,%d1
Lbrkpt1:
	movl	%a0@+,%a1@+
	subql	#4,%d1
	jbgt	Lbrkpt1

Lbrkpt2:
	| Call the trap handler for the kernel debugger.
	| Do not call trap() to do it, so that we can
	| set breakpoints in trap() if we want.  We know
	| the trap type is either T_TRACE or T_BREAKPOINT.
	| If we have both DDB and KGDB, let KGDB see it first,
	| because KGDB will just return 0 if not connected.
	| Save args in d2, a2
	movl	%d0,%d2			| trap type
	movl	%sp,%a2			| frame ptr
#ifdef KGDB
	| Let KGDB handle it (if connected)
	movl	%a2,%sp@-		| push frame ptr
	movl	%d2,%sp@-		| push trap type
	jbsr	_C_LABEL(kgdb_trap)	| handle the trap
	addql	#8,%sp			| pop args
	cmpl	#0,%d0			| did kgdb handle it?
	jne	Lbrkpt3			| yes, done
#endif
#ifdef DDB
	| Let DDB handle it
	movl	%a2,%sp@-		| push frame ptr
	movl	%d2,%sp@-		| push trap type
	jbsr	_C_LABEL(kdb_trap)	| handle the trap
	addql	#8,%sp			| pop args
#endif
	/* Sun 3 drops into PROM here. */
Lbrkpt3:
	| The stack pointer may have been modified, or
	| data below it modified (by kgdb push call),
	| so push the hardware frame at the current sp
	| before restoring registers and returning.

	movl	%sp@(FR_SP),%a0		| modified sp
	lea	%sp@(FR_SIZE),%a1	| end of our frame
	movl	%a1@-,%a0@-		| copy 2 longs with
	movl	%a1@-,%a0@-		| ... predecrement
	movl	%a0,%sp@(FR_SP)		| sp = h/w frame
	moveml	%sp@+,#0x7FFF		| restore all but sp
	movl	%sp@,%sp		| ... and sp
	rte				| all done

/*
 * Use common m68k sigreturn routine.
 */
#include <m68k/m68k/sigreturn.s>

/*
 * Interrupt handlers.
 *
 * For auto-vectored interrupts, the CPU provides the
 * vector 0x18+level.
 *
 * intrhand_autovec is the entry point for auto-vectored
 * interrupts.
 *
 * For vectored interrupts, we pull the pc, evec, and exception frame
 * and pass them to the vectored interrupt dispatcher.  The vectored
 * interrupt dispatcher will deal with strays.
 *
 * intrhand_vectored is the entry point for vectored interrupts.
 */

ENTRY_NOPROFILE(intrhand_autovec)
	addql	#1,_C_LABEL(interrupt_depth)
	INTERRUPT_SAVEREG
	lea	%sp@(16),%a1		| get pointer to frame
	movl	%a1,%sp@-
	jbsr	_C_LABEL(isrdispatch_autovec)  | call dispatcher
	addql	#4,%sp
	jbra	Lintrhand_exit

ENTRY_NOPROFILE(intrhand_vectored)
	addql	#1,_C_LABEL(interrupt_depth)
	INTERRUPT_SAVEREG
	lea	%sp@(16),%a1		| get pointer to frame
	movl	%a1,%sp@-
	movw	%sr,%d0
	bfextu	%d0,21,3,%d0		| Get current ipl
	movl	%d0,%sp@-		| Push it
	jbsr	_C_LABEL(isrdispatch_vectored) | call dispatcher
	addql	#8,%sp
Lintrhand_exit:
	INTERRUPT_RESTOREREG
	subql	#1,_C_LABEL(interrupt_depth)

	/* FALLTHROUGH to rei */

#undef INTERRUPT_SAVEREG
#undef INTERRUPT_RESTOREREG

/*
 * Emulation of VAX REI instruction.
 *
 * This code deals with checking for and servicing ASTs
 * (profiling, scheduling).
 * After identifing that we need an AST we drop the IPL to allow device
 * interrupts.
 *
 * This code is complicated by the fact that sendsig may have been called
 * necessitating a stack cleanup.
 */
ASENTRY_NOPROFILE(rei)
	tstl	_C_LABEL(astpending)	| AST pending?
	jeq	Ldorte			| Nope. Just return.
	btst	#5,%sp@			| Returning to kernel mode?
	jne	Ldorte			| Yup. Can't do ASTs
	movw	#PSL_LOWIPL,%sr		| lower SPL
	clrl	%sp@-			| stack adjust
	moveml	#0xFFFF,%sp@-		| save all registers
	movl	%usp,%a1		| including
	movl	%a1,%sp@(FR_SP)		|    the users SP
Lrei1:	clrl	%sp@-			| VA == none
	clrl	%sp@-			| code == none
	movl	#T_ASTFLT,%sp@-		| type == async system trap
	pea	%sp@(12)		| fp == address of trap frame
	jbsr	_C_LABEL(trap)		| go handle it
	lea	%sp@(16),%sp		| pop value args
	movl	%sp@(FR_SP),%a0		| restore user SP
	movl	%a0,%usp		|   from save area
	movw	%sp@(FR_ADJ),%d0	| need to adjust stack?
	jne	Laststkadj		| yes, go to it
	moveml	%sp@+,#0x7FFF		| no, restore most user regs
	addql	#8,%sp			| toss SP and stack adjust
Ldorte:	rte				| and do real RTE

Laststkadj:
	lea	%sp@(FR_HW),%a1		| pointer to HW frame
	addql	#8,%a1			| source pointer
	movl	%a1,%a0			| source
	addw	%d0,%a0			|  + hole size = dest pointer
	movl	%a1@-,%a0@-		| copy
	movl	%a1@-,%a0@-		|  8 bytes
	movl	%a0,%sp@(FR_SP)		| new SSP
	moveml	%sp@+,#0x7FFF		| restore user registers
	movl	%sp@,%sp		| and our SP
	rte				| and do real RTE

/*
 * Use common m68k sigcode.
 */
#include <m68k/m68k/sigcode.s>
#ifdef COMPAT_SUNOS
#include <m68k/m68k/sunos_sigcode.s>
#endif
#ifdef COMPAT_SVR4
#include <m68k/m68k/svr4_sigcode.s>
#endif

/*
 * Primitives
 */

/*
 * Use common m68k support routines.
 */
#include <m68k/m68k/support.s>

/*
 * Use common m68k process/lwp switch and context save subroutines.
 */
#define	FPCOPROC	/* XXX: Temp. Reqd. */
#include <m68k/m68k/switch_subr.s>


#if defined(M68040) || defined(M68060)
ENTRY(suline)
	movl	%sp@(4),%a0		| address to write
	movl	_C_LABEL(curpcb),%a1	| current pcb
	movl	#Lslerr,%a1@(PCB_ONFAULT) | where to return to on a fault
	movl	%sp@(8),%a1		| address of line
	movl	%a1@+,%d0		| get lword
	movsl	%d0,%a0@+		| put lword
	nop				| sync
	movl	%a1@+,%d0		| get lword
	movsl	%d0,%a0@+		| put lword
	nop				| sync
	movl	%a1@+,%d0		| get lword
	movsl	%d0,%a0@+		| put lword
	nop				| sync
	movl	%a1@+,%d0		| get lword
	movsl	%d0,%a0@+		| put lword
	nop				| sync
	moveq	#0,%d0			| indicate no fault
	jra	Lsldone
Lslerr:
	moveq	#-1,%d0
Lsldone:
	movl	_C_LABEL(curpcb),%a1	| current pcb
	clrl	%a1@(PCB_ONFAULT)	| clear fault address
	rts
#endif


ENTRY(ecacheon)
	rts

ENTRY(ecacheoff)
	rts

/*
 * Get callers current SP value.
 * Note that simply taking the address of a local variable in a C function
 * doesn't work because callee saved registers may be outside the stack frame
 * defined by A6 (e.g. GCC generated code).
 */
ENTRY_NOPROFILE(getsp)
	movl	%sp,%d0			| get current SP
	addql	#4,%d0			| compensate for return address
	movl	%d0,%a0
	rts

/*
 * Load a new user segment table pointer.
 */
ENTRY(loadustp)
	movl	%sp@(4),%d0		| new USTP
	moveq	#PGSHIFT, %d1
	lsll	%d1,%d0			| convert to addr
#if defined(M68040) || defined(M68060)
	cmpl    #MMU_68040,_C_LABEL(mmutype) | 68040?
	jne     LmotommuC               | no, skip
	.word	0xf518			| pflusha
	.long   0x4e7b0806              | movc d0,urp
#ifdef M68060
	cmpl	#CPU_68060,_C_LABEL(cputype)
	jne	Lldno60
	movc	%cacr,%d0
	orl	#IC60_CUBC,%d0		| clear user branch cache entries
	movc	%d0,%cacr
Lldno60:
#endif
	rts
LmotommuC:
#endif
	pflusha				| flush entire TLB
	lea	_C_LABEL(protorp),%a0	| CRP prototype
	movl	%d0,%a0@(4)		| stash USTP
	pmove	%a0@,%crp		| load root pointer
	movl	#CACHE_CLR,%d0
	movc	%d0,%cacr		| invalidate cache(s)
	rts

ENTRY(ploadw)
#ifdef M68030
#if defined(M68040) || defined(M68060)
	cmpl	#MMU_68040,_C_LABEL(mmutype) | 68040?
	jeq	Lploadwskp		| yes, skip
#endif
	movl	%sp@(4),%a0		| address to load
	ploadw	#1,%a0@			| pre-load translation
Lploadwskp:
#endif
	rts

ENTRY(getsr)
	moveq	#0,%d0
	movw	%sr,%d0
	rts

/*
 * _delay(unsigned N)
 *
 * Delay for at least (N/1024) microseconds.
 * This routine depends on the variable:  delay_divisor
 * which should be set based on the CPU clock rate.
 */
ENTRY_NOPROFILE(_delay)
	| d0 = arg = (usecs << 10)
	movl	%sp@(4),%d0
	| d1 = delay_divisor
	movl	_C_LABEL(delay_divisor),%d1
	jra	L_delay			/* Jump into the loop! */

	/*
	 * Align the branch target of the loop to a half-line (8-byte)
	 * boundary to minimize cache effects.  This guarantees both
	 * that there will be no prefetch stalls due to cache line burst
	 * operations and that the loop will run from a single cache
	 * half-line.
	 */
#ifdef __ELF__
	.align	8
#else
	.align	3
#endif
L_delay:
	subl	%d1,%d0
	jgt	L_delay
	rts

/*
 * Handle the nitty-gritty of rebooting the machine.
 * Basically we just turn off the MMU, restore the Bug's initial VBR
 * and either return to Bug or jump through the ROM reset vector
 * depending on how the system was halted.
 */
ENTRY_NOPROFILE(doboot)
	movw	#PSL_HIGHIPL,%sr
	movl	_C_LABEL(boothowto),%d1	| load howto
	movl	%sp@(4),%d2		| arg
	movl	_ASM_LABEL(bug_vbr),%d3	| Fetch Bug's original VBR value
	movl	_C_LABEL(machineid),%d4	| What type of board is this?
	movl	#CACHE_OFF,%d0
#if defined(M68040) || defined(M68060)
	cmpl	#MMU_68040,_C_LABEL(mmutype)	| 68040/68060?
	jne	Lnocache0		| no, skip
	.word	0xf4f8			| cpusha bc - push and invalidate caches
	nop
	movl	#CACHE40_OFF,%d0
#endif
Lnocache0:
	movc	%d0,%cacr		| disable on-chip cache(s)

#if defined(M68040) || defined(M68060)
	cmpl	#MMU_68040,_C_LABEL(mmutype)
	jne	LmotommuF
	movql	#0,%d0
	movc	%d0,%cacr
	.long	0x4e7b0003              | movc d0,tc
	jra	Lbootcommon
LmotommuF:
#endif
	clrl	%sp@-			| value for pmove to TC (turn off MMU)
	pmove	%sp@,%tc		| disable MMU
	addql	#4,%sp

Lbootcommon:
	/*
	 * MMU Switched off by now, so relocate all absolute references
	 */
	ASRELOC(tmpstk, %sp)		| physical SP in case of NMI
	movc	%d3,%vbr		| Restore Bug's VBR
	andl	#RB_SBOOT, %d1		| mask off
	jbne	Lsboot			| sboot?
	/* NOT sboot */
	tstl	%d2			| autoboot?
	jbeq	Ldoreset		| yes!
	CALLBUG(MVMEPROM_EXIT)		| return to bug
	/* NOTREACHED */

Ldoreset:
	movl	#0xff800000,%a0		| Bug's reset vector address
	movl	%a0@+, %a7		| get SP
	movl	%a0@, %a0		| get PC
	jmp	%a0@			| go!

Lsboot: /* sboot */
	tstl	%d2			| autoboot?
	jbeq	1f			| yes!
	jmp 	0x4000			| back to sboot
1:	jmp	0x400a			| tell sboot to reboot us


/*
 * Misc. global variables.
 */
	.data

GLOBAL(machineid)
	.long	MVME_147	| default to MVME_147

GLOBAL(mmutype)
	.long	MMU_68030	| default to MMU_68030

GLOBAL(cputype)
	.long	CPU_68030	| default to CPU_68030

GLOBAL(fputype)
	.long	FPU_68882	| default to FPU_68882

GLOBAL(protorp)
	.long	0,0		| prototype root pointer

/*
 * Information from first stage boot program
 */
GLOBAL(bootpart)
	.long	0
GLOBAL(bootdevlun)
	.long	0
GLOBAL(bootctrllun)
	.long	0
GLOBAL(bootaddr)
	.long	0

GLOBAL(intiobase)
	.long	0		| KVA of base of internal IO space

GLOBAL(intiolimit)
	.long	0		| KVA of end of internal IO space

GLOBAL(intiobase_phys)
	.long	0		| PA of board's I/O registers

GLOBAL(intiotop_phys)
	.long	0		| PA of top of board's I/O registers

/*
 * interrupt counters.
 * XXXSCW: Will go away soon; kept here to keep vmstat happy
 */
GLOBAL(intrnames)
	.asciz	"spur"
	.asciz	"lev1"
	.asciz	"lev2"
	.asciz	"lev3"
	.asciz	"lev4"
	.asciz	"clock"
	.asciz	"lev6"
	.asciz	"nmi"
	.asciz	"statclock"
GLOBAL(eintrnames)
	.even

GLOBAL(intrcnt)
	.long	0,0,0,0,0,0,0,0,0,0
GLOBAL(eintrcnt)
