/*	$OpenBSD: locore.S,v 1.18 1998/09/15 10:58:53 pefo Exp $	*/
/*-
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Digital Equipment Corporation and Ralph Campbell.
 *
 * 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.
 * 4. 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.
 *
 * Copyright (C) 1989 Digital Equipment Corporation.
 * Permission to use, copy, modify, and distribute this software and
 * its documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appears in all copies.
 * Digital Equipment Corporation makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * from: Header: /sprite/src/kernel/mach/ds3100.md/RCS/loMem.s,
 *	v 1.1 89/07/11 17:55:04 nelson Exp  SPRITE (DECWRL)
 * from: Header: /sprite/src/kernel/mach/ds3100.md/RCS/machAsm.s,
 *	v 9.2 90/01/29 18:00:39 shirriff Exp  SPRITE (DECWRL)
 * from: Header: /sprite/src/kernel/vm/ds3100.md/vmPmaxAsm.s,
 *	v 1.1 89/07/10 14:27:41 nelson Exp  SPRITE (DECWRL)
 *
 *	from: @(#)locore.s	8.5 (Berkeley) 1/4/94
 *	JNPR: swtch.S,v 1.6.2.1 2007/09/10 10:36:50 girish
 * $FreeBSD: head/sys/mips/mips/swtch.S 210699 2010-07-31 19:13:08Z jchandra $
 */

/*
 *	Contains code that is the first executed at boot time plus
 *	assembly language support routines.
 */

#include "opt_cputype.h"
#include <sys/syscall.h>
#include <machine/asm.h>
#include <machine/cpu.h>
#include <machine/cpuregs.h>
#include <machine/regnum.h>
#include <machine/pte.h>

#include "assym.s"

	.set	noreorder			# Noreorder is default style!

#define	SAVE_U_PCB_REG(reg, offs, base) \
	REG_S	reg, U_PCB_REGS + (SZREG * offs) (base)

#define	RESTORE_U_PCB_REG(reg, offs, base) \
	REG_L	reg, U_PCB_REGS + (SZREG * offs) (base)

#define	SAVE_U_PCB_FPREG(reg, offs, base) \
	FP_S	reg, U_PCB_FPREGS + (SZFPREG * offs) (base)

#define	RESTORE_U_PCB_FPREG(reg, offs, base) \
	FP_L	reg, U_PCB_FPREGS + (SZFPREG * offs) (base)

#define	SAVE_U_PCB_FPSR(reg, offs, base) \
	REG_S	reg, U_PCB_FPREGS + (SZFPREG * offs) (base)

#define	RESTORE_U_PCB_FPSR(reg, offs, base) \
	REG_L	reg, U_PCB_FPREGS + (SZFPREG * offs) (base)

#define	SAVE_U_PCB_CONTEXT(reg, offs, base) \
	REG_S	reg, U_PCB_CONTEXT + (SZREG * offs) (base)

#define	RESTORE_U_PCB_CONTEXT(reg, offs, base) \
	REG_L	reg, U_PCB_CONTEXT + (SZREG * offs) (base)

/*
 * Setup for and return to user.
 */
LEAF(fork_trampoline)
	move	a0,s0
	move	a1,s1
	jal	_C_LABEL(fork_exit)
	move	a2,s2			  #BDSlot

	DO_AST

	mfc0	v0, MIPS_COP_0_STATUS
	and     v0, ~(MIPS_SR_INT_IE)
	mtc0	v0, MIPS_COP_0_STATUS	# disable interrupts
	COP0_SYNC
/*
 * The use of k1 for storing the PCB pointer must be done only
 * after interrupts are disabled.  Otherwise it will get overwritten
 * by the interrupt code.
 */
	.set	noat
	GET_CPU_PCPU(k1)
	PTR_L	k1, PC_CURPCB(k1)

	RESTORE_U_PCB_REG(t0, MULLO, k1)
	RESTORE_U_PCB_REG(t1, MULHI, k1)
	mtlo	t0
	mthi	t1
	RESTORE_U_PCB_REG(a0, PC, k1)
	RESTORE_U_PCB_REG(AT, AST, k1)
	RESTORE_U_PCB_REG(v0, V0, k1)
	MTC0	a0, MIPS_COP_0_EXC_PC	# set return address

	RESTORE_U_PCB_REG(v1, V1, k1)
	RESTORE_U_PCB_REG(a0, A0, k1)
	RESTORE_U_PCB_REG(a1, A1, k1)
	RESTORE_U_PCB_REG(a2, A2, k1)
	RESTORE_U_PCB_REG(a3, A3, k1)
	RESTORE_U_PCB_REG(t0, T0, k1)
	RESTORE_U_PCB_REG(t1, T1, k1)
	RESTORE_U_PCB_REG(t2, T2, k1)
	RESTORE_U_PCB_REG(t3, T3, k1)
	RESTORE_U_PCB_REG(ta0, TA0, k1)
	RESTORE_U_PCB_REG(ta1, TA1, k1)
	RESTORE_U_PCB_REG(ta2, TA2, k1)
	RESTORE_U_PCB_REG(ta3, TA3, k1)
	RESTORE_U_PCB_REG(s0, S0, k1)
	RESTORE_U_PCB_REG(s1, S1, k1)
	RESTORE_U_PCB_REG(s2, S2, k1)
	RESTORE_U_PCB_REG(s3, S3, k1)
	RESTORE_U_PCB_REG(s4, S4, k1)
	RESTORE_U_PCB_REG(s5, S5, k1)
	RESTORE_U_PCB_REG(s6, S6, k1)
	RESTORE_U_PCB_REG(s7, S7, k1)
	RESTORE_U_PCB_REG(t8, T8, k1)
	RESTORE_U_PCB_REG(t9, T9, k1)
	RESTORE_U_PCB_REG(k0, SR, k1)
	RESTORE_U_PCB_REG(gp, GP, k1)
	RESTORE_U_PCB_REG(s8, S8, k1)
	RESTORE_U_PCB_REG(ra, RA, k1)
	RESTORE_U_PCB_REG(sp, SP, k1)
	li	k1, ~MIPS_SR_INT_MASK
	and	k0, k0, k1
	mfc0	k1, MIPS_COP_0_STATUS
	and	k1, k1, MIPS_SR_INT_MASK
	or	k0, k0, k1
	mtc0	k0, MIPS_COP_0_STATUS	# switch to user mode (when eret...)
	HAZARD_DELAY
	sync
	eret
	.set	at
END(fork_trampoline)

/*
 * Update pcb, saving current processor state.
 * Note: this only works if pcbp != curproc's pcb since
 * cpu_switch() will copy over pcb_context.
 *
 *	savectx(struct pcb *pcbp);
 */
LEAF(savectx)
	SAVE_U_PCB_CONTEXT(s0, PREG_S0, a0)
	SAVE_U_PCB_CONTEXT(s1, PREG_S1, a0)
	SAVE_U_PCB_CONTEXT(s2, PREG_S2, a0)
	SAVE_U_PCB_CONTEXT(s3, PREG_S3, a0)
	mfc0	v0, MIPS_COP_0_STATUS
	SAVE_U_PCB_CONTEXT(s4, PREG_S4, a0)
	SAVE_U_PCB_CONTEXT(s5, PREG_S5, a0)
	SAVE_U_PCB_CONTEXT(s6, PREG_S6, a0)
	SAVE_U_PCB_CONTEXT(s7, PREG_S7, a0)
	SAVE_U_PCB_CONTEXT(sp, PREG_SP, a0)
	SAVE_U_PCB_CONTEXT(s8, PREG_S8, a0)
	SAVE_U_PCB_CONTEXT(ra, PREG_RA, a0)
	SAVE_U_PCB_CONTEXT(v0, PREG_SR, a0)
	SAVE_U_PCB_CONTEXT(gp, PREG_GP, a0)

	move	v0, ra			/* save 'ra' before we trash it */
	jal	1f
	nop
1:
	SAVE_U_PCB_CONTEXT(ra, PREG_PC, a0)
	move	ra, v0			/* restore 'ra' before returning */

	/*
	 * FREEBSD_DEVELOPERS_FIXME:
	 * In case there are CPU-specific registers that need
	 * to be saved with the other registers do so here.
	 */
	j	ra
	move	v0, zero
END(savectx)

NON_LEAF(cpu_throw, CALLFRAME_SIZ, ra)
	mfc0	t0, MIPS_COP_0_STATUS		# t0 = saved status register
	nop
	nop
	and     a3, t0, ~(MIPS_SR_INT_IE)
	mtc0	a3, MIPS_COP_0_STATUS		# Disable all interrupts
	ITLBNOPFIX
	j	mips_sw1			# We're not interested in old 
						# thread's context, so jump 
						# right to action
	nop					# BDSLOT
END(cpu_throw)

/*
 * cpu_switch(struct thread *old, struct thread *new, struct mutex *mtx);
 *	a0 - old
 *	a1 - new
 *	a2 - mtx
 * Find the highest priority process and resume it.
 */
NON_LEAF(cpu_switch, CALLFRAME_SIZ, ra)
	mfc0	t0, MIPS_COP_0_STATUS		# t0 = saved status register
	nop
	nop
	and     a3, t0, ~(MIPS_SR_INT_IE)	
	mtc0	a3, MIPS_COP_0_STATUS		# Disable all interrupts
	ITLBNOPFIX
	beqz	a0, mips_sw1
	move	a3, a0
	PTR_L	a0, TD_PCB(a0)		# load PCB addr of curproc
	SAVE_U_PCB_CONTEXT(sp, PREG_SP, a0)		# save old sp
	PTR_SUBU	sp, sp, CALLFRAME_SIZ
	REG_S	ra, CALLFRAME_RA(sp)
	.mask	0x80000000, (CALLFRAME_RA - CALLFRAME_SIZ)
	SAVE_U_PCB_CONTEXT(s0, PREG_S0, a0)		# do a 'savectx()'
	SAVE_U_PCB_CONTEXT(s1, PREG_S1, a0)
	SAVE_U_PCB_CONTEXT(s2, PREG_S2, a0)
	SAVE_U_PCB_CONTEXT(s3, PREG_S3, a0)
	SAVE_U_PCB_CONTEXT(s4, PREG_S4, a0)
	SAVE_U_PCB_CONTEXT(s5, PREG_S5, a0)
	SAVE_U_PCB_CONTEXT(s6, PREG_S6, a0)
	SAVE_U_PCB_CONTEXT(s7, PREG_S7, a0)
	SAVE_U_PCB_CONTEXT(s8, PREG_S8, a0)
	SAVE_U_PCB_CONTEXT(ra, PREG_RA, a0)		# save return address
	SAVE_U_PCB_CONTEXT(t0, PREG_SR, a0)		# save status register
	SAVE_U_PCB_CONTEXT(gp, PREG_GP, a0)
	jal	getpc
	nop
getpc:
	SAVE_U_PCB_CONTEXT(ra, PREG_PC, a0)		# save return address
	/*
	 * FREEBSD_DEVELOPERS_FIXME:
	 * In case there are CPU-specific registers that need
	 * to be saved with the other registers do so here.
	 */

	PTR_S	a2, TD_LOCK(a3)			# Switchout td_lock 

mips_sw1:
#if defined(SMP) && defined(SCHED_ULE)
	PTR_LA	t0, _C_LABEL(blocked_lock)
blocked_loop:
	PTR_L	t1, TD_LOCK(a1)
	beq	t0, t1, blocked_loop
	nop
#endif
	move	s7, a1	# Store newthread
/*
 * Switch to new context.
 */
	GET_CPU_PCPU(a3)
	PTR_S	a1, PC_CURTHREAD(a3)
	PTR_L	a2, TD_PCB(a1)
	PTR_S	a2, PC_CURPCB(a3)
	PTR_L	v0, TD_KSTACK(a1)
#if defined(__mips_n64)
	PTR_LI	s0, MIPS_XKSEG_START
#else
	PTR_LI	s0, MIPS_KSEG2_START		# If Uarea addr is below kseg2,
#endif
	bltu	v0, s0, sw2			# no need to insert in TLB.
	lw	a1, TD_UPTE + 0(s7)		# a1 = u. pte #0
	lw	a2, TD_UPTE + 4(s7)		# a2 = u. pte #1
/*
 * Wiredown the USPACE of newproc in TLB entry#0.  Check whether target
 * USPACE is already in another place of TLB before that, and if so
 * invalidate that TLB entry.
 * NOTE: This is hard coded to UPAGES == 2.
 * Also, there should be no TLB faults at this point.
 */
	MTC0	v0, MIPS_COP_0_TLB_HI		# VPN = va
	HAZARD_DELAY
	tlbp					# probe VPN
	HAZARD_DELAY
	mfc0	s0, MIPS_COP_0_TLB_INDEX
	HAZARD_DELAY

	PTR_LI	t1, MIPS_KSEG0_START		# invalidate tlb entry
	bltz	s0, entry0set
	nop
	sll	s0, PAGE_SHIFT + 1
	addu	t1, s0
	MTC0	t1, MIPS_COP_0_TLB_HI
	mtc0	zero, MIPS_COP_0_TLB_LO0
	mtc0	zero, MIPS_COP_0_TLB_LO1
	HAZARD_DELAY
	tlbwi
	HAZARD_DELAY
	MTC0	v0, MIPS_COP_0_TLB_HI		# set VPN again

entry0set:
/* SMP!! - Works only for  unshared TLB case - i.e. no v-cpus */
	mtc0	zero, MIPS_COP_0_TLB_INDEX		# TLB entry #0
	HAZARD_DELAY
	mtc0	a1, MIPS_COP_0_TLB_LO0		# upte[0]
	HAZARD_DELAY
	mtc0	a2, MIPS_COP_0_TLB_LO1		# upte[1]
	HAZARD_DELAY
	tlbwi					# set TLB entry #0
	HAZARD_DELAY
/*
 * Now running on new u struct.
 */
sw2:
	PTR_LA	t1, _C_LABEL(pmap_activate)	# s7 = new proc pointer
	jalr	t1				# s7 = new proc pointer
	move	a0, s7				# BDSLOT
/*
 * Restore registers and return.
 */
	PTR_L	a0, TD_PCB(s7)
	RESTORE_U_PCB_CONTEXT(gp, PREG_GP, a0)
	RESTORE_U_PCB_CONTEXT(v0, PREG_SR, a0)	# restore kernel context
	RESTORE_U_PCB_CONTEXT(ra, PREG_RA, a0)
	RESTORE_U_PCB_CONTEXT(s0, PREG_S0, a0)
	RESTORE_U_PCB_CONTEXT(s1, PREG_S1, a0)
	RESTORE_U_PCB_CONTEXT(s2, PREG_S2, a0)
	RESTORE_U_PCB_CONTEXT(s3, PREG_S3, a0)
	RESTORE_U_PCB_CONTEXT(s4, PREG_S4, a0)
	RESTORE_U_PCB_CONTEXT(s5, PREG_S5, a0)
	RESTORE_U_PCB_CONTEXT(s6, PREG_S6, a0)
	RESTORE_U_PCB_CONTEXT(s7, PREG_S7, a0)
	RESTORE_U_PCB_CONTEXT(sp, PREG_SP, a0)
	RESTORE_U_PCB_CONTEXT(s8, PREG_S8, a0)
	/*
	 * FREEBSD_DEVELOPERS_FIXME:
	 * In case there are CPU-specific registers that need
	 * to be restored with the other registers do so here.
	 */
	mfc0	t0, MIPS_COP_0_STATUS
	and	t0, t0, MIPS_SR_INT_MASK
	and	v0, v0, ~MIPS_SR_INT_MASK
	or	v0, v0, t0
	mtc0	v0, MIPS_COP_0_STATUS
	ITLBNOPFIX

	j	ra
	nop
END(cpu_switch)

/*----------------------------------------------------------------------------
 *
 * MipsSwitchFPState --
 *
 *	Save the current state into 'from' and restore it from 'to'.
 *
 *	MipsSwitchFPState(from, to)
 *		struct thread *from;
 *		struct trapframe *to;
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------------
 */
LEAF(MipsSwitchFPState)
	mfc0	t1, MIPS_COP_0_STATUS	# Save old SR
	li	t0, MIPS_SR_COP_1_BIT	# enable the coprocessor
	mtc0	t0, MIPS_COP_0_STATUS
	ITLBNOPFIX

	beq	a0, zero, 1f		# skip save if NULL pointer
	nop
/*
 * First read out the status register to make sure that all FP operations
 * have completed.
 */
	PTR_L	a0, TD_PCB(a0)			# get pointer to pcb for proc
	cfc1	t0, MIPS_FPU_CSR		# stall til FP done
	cfc1	t0, MIPS_FPU_CSR		# now get status
	li	t3, ~MIPS_SR_COP_1_BIT
	RESTORE_U_PCB_REG(t2, PS, a0)		# get CPU status register
	SAVE_U_PCB_FPSR(t0, FSR_NUM, a0)	# save FP status
	and	t2, t2, t3			# clear COP_1 enable bit
	SAVE_U_PCB_REG(t2, PS, a0)		# save new status register
/*
 * Save the floating point registers.
 */
	SAVE_U_PCB_FPREG($f0, F0_NUM, a0)
	SAVE_U_PCB_FPREG($f1, F1_NUM, a0)
	SAVE_U_PCB_FPREG($f2, F2_NUM, a0)
	SAVE_U_PCB_FPREG($f3, F3_NUM, a0)
	SAVE_U_PCB_FPREG($f4, F4_NUM, a0)
	SAVE_U_PCB_FPREG($f5, F5_NUM, a0)
	SAVE_U_PCB_FPREG($f6, F6_NUM, a0)
	SAVE_U_PCB_FPREG($f7, F7_NUM, a0)
	SAVE_U_PCB_FPREG($f8, F8_NUM, a0)
	SAVE_U_PCB_FPREG($f9, F9_NUM, a0)
	SAVE_U_PCB_FPREG($f10, F10_NUM, a0)
	SAVE_U_PCB_FPREG($f11, F11_NUM, a0)
	SAVE_U_PCB_FPREG($f12, F12_NUM, a0)
	SAVE_U_PCB_FPREG($f13, F13_NUM, a0)
	SAVE_U_PCB_FPREG($f14, F14_NUM, a0)
	SAVE_U_PCB_FPREG($f15, F15_NUM, a0)
	SAVE_U_PCB_FPREG($f16, F16_NUM, a0)
	SAVE_U_PCB_FPREG($f17, F17_NUM, a0)
	SAVE_U_PCB_FPREG($f18, F18_NUM, a0)
	SAVE_U_PCB_FPREG($f19, F19_NUM, a0)
	SAVE_U_PCB_FPREG($f20, F20_NUM, a0)
	SAVE_U_PCB_FPREG($f21, F21_NUM, a0)
	SAVE_U_PCB_FPREG($f22, F22_NUM, a0)
	SAVE_U_PCB_FPREG($f23, F23_NUM, a0)
	SAVE_U_PCB_FPREG($f24, F24_NUM, a0)
	SAVE_U_PCB_FPREG($f25, F25_NUM, a0)
	SAVE_U_PCB_FPREG($f26, F26_NUM, a0)
	SAVE_U_PCB_FPREG($f27, F27_NUM, a0)
	SAVE_U_PCB_FPREG($f28, F28_NUM, a0)
	SAVE_U_PCB_FPREG($f29, F29_NUM, a0)
	SAVE_U_PCB_FPREG($f30, F30_NUM, a0)
	SAVE_U_PCB_FPREG($f31, F31_NUM, a0)

1:
/*
 *  Restore the floating point registers.
 */
	RESTORE_U_PCB_FPSR(t0, FSR_NUM, a1)	# get status register
	RESTORE_U_PCB_FPREG($f0, F0_NUM, a1)
	RESTORE_U_PCB_FPREG($f1, F1_NUM, a1)
	RESTORE_U_PCB_FPREG($f2, F2_NUM, a1)
	RESTORE_U_PCB_FPREG($f3, F3_NUM, a1)
	RESTORE_U_PCB_FPREG($f4, F4_NUM, a1)
	RESTORE_U_PCB_FPREG($f5, F5_NUM, a1)
	RESTORE_U_PCB_FPREG($f6, F6_NUM, a1)
	RESTORE_U_PCB_FPREG($f7, F7_NUM, a1)
	RESTORE_U_PCB_FPREG($f8, F8_NUM, a1)
	RESTORE_U_PCB_FPREG($f9, F9_NUM, a1)
	RESTORE_U_PCB_FPREG($f10, F10_NUM, a1)
	RESTORE_U_PCB_FPREG($f11, F11_NUM, a1)
	RESTORE_U_PCB_FPREG($f12, F12_NUM, a1)
	RESTORE_U_PCB_FPREG($f13, F13_NUM, a1)
	RESTORE_U_PCB_FPREG($f14, F14_NUM, a1)
	RESTORE_U_PCB_FPREG($f15, F15_NUM, a1)
	RESTORE_U_PCB_FPREG($f16, F16_NUM, a1)
	RESTORE_U_PCB_FPREG($f17, F17_NUM, a1)
	RESTORE_U_PCB_FPREG($f18, F18_NUM, a1)
	RESTORE_U_PCB_FPREG($f19, F19_NUM, a1)
	RESTORE_U_PCB_FPREG($f20, F20_NUM, a1)
	RESTORE_U_PCB_FPREG($f21, F21_NUM, a1)
	RESTORE_U_PCB_FPREG($f22, F22_NUM, a1)
	RESTORE_U_PCB_FPREG($f23, F23_NUM, a1)
	RESTORE_U_PCB_FPREG($f24, F24_NUM, a1)
	RESTORE_U_PCB_FPREG($f25, F25_NUM, a1)
	RESTORE_U_PCB_FPREG($f26, F26_NUM, a1)
	RESTORE_U_PCB_FPREG($f27, F27_NUM, a1)
	RESTORE_U_PCB_FPREG($f28, F28_NUM, a1)
	RESTORE_U_PCB_FPREG($f29, F29_NUM, a1)
	RESTORE_U_PCB_FPREG($f30, F30_NUM, a1)
	RESTORE_U_PCB_FPREG($f31, F31_NUM, a1)

	and	t0, t0, ~MIPS_FPU_EXCEPTION_BITS
	ctc1	t0, MIPS_FPU_CSR
	nop

	mtc0	t1, MIPS_COP_0_STATUS		# Restore the status register.
	ITLBNOPFIX
	j	ra
	nop
END(MipsSwitchFPState)

/*----------------------------------------------------------------------------
 *
 * MipsSaveCurFPState --
 *
 *	Save the current floating point coprocessor state.
 *
 *	MipsSaveCurFPState(td)
 *		struct thread *td;
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	machFPCurProcPtr is cleared.
 *
 *----------------------------------------------------------------------------
 */
LEAF(MipsSaveCurFPState)
	PTR_L	a0, TD_PCB(a0)			# get pointer to pcb for thread
	mfc0	t1, MIPS_COP_0_STATUS		# Disable interrupts and
	li	t0, MIPS_SR_COP_1_BIT		#  enable the coprocessor
	mtc0	t0, MIPS_COP_0_STATUS
	ITLBNOPFIX
	GET_CPU_PCPU(a1)
	PTR_S	zero, PC_FPCURTHREAD(a1)	# indicate state has been saved
/*
 * First read out the status register to make sure that all FP operations
 * have completed.
 */
	RESTORE_U_PCB_REG(t2, PS, a0)		# get CPU status register
	li	t3, ~MIPS_SR_COP_1_BIT
	and	t2, t2, t3			# clear COP_1 enable bit
	cfc1	t0, MIPS_FPU_CSR		# stall til FP done
	cfc1	t0, MIPS_FPU_CSR		# now get status
	SAVE_U_PCB_REG(t2, PS, a0)		# save new status register
	SAVE_U_PCB_FPSR(t0, FSR_NUM, a0)	# save FP status
/*
 * Save the floating point registers.
 */
	SAVE_U_PCB_FPREG($f0, F0_NUM, a0)
	SAVE_U_PCB_FPREG($f1, F1_NUM, a0)
	SAVE_U_PCB_FPREG($f2, F2_NUM, a0)
	SAVE_U_PCB_FPREG($f3, F3_NUM, a0)
	SAVE_U_PCB_FPREG($f4, F4_NUM, a0)
	SAVE_U_PCB_FPREG($f5, F5_NUM, a0)
	SAVE_U_PCB_FPREG($f6, F6_NUM, a0)
	SAVE_U_PCB_FPREG($f7, F7_NUM, a0)
	SAVE_U_PCB_FPREG($f8, F8_NUM, a0)
	SAVE_U_PCB_FPREG($f9, F9_NUM, a0)
	SAVE_U_PCB_FPREG($f10, F10_NUM, a0)
	SAVE_U_PCB_FPREG($f11, F11_NUM, a0)
	SAVE_U_PCB_FPREG($f12, F12_NUM, a0)
	SAVE_U_PCB_FPREG($f13, F13_NUM, a0)
	SAVE_U_PCB_FPREG($f14, F14_NUM, a0)
	SAVE_U_PCB_FPREG($f15, F15_NUM, a0)
	SAVE_U_PCB_FPREG($f16, F16_NUM, a0)
	SAVE_U_PCB_FPREG($f17, F17_NUM, a0)
	SAVE_U_PCB_FPREG($f18, F18_NUM, a0)
	SAVE_U_PCB_FPREG($f19, F19_NUM, a0)
	SAVE_U_PCB_FPREG($f20, F20_NUM, a0)
	SAVE_U_PCB_FPREG($f21, F21_NUM, a0)
	SAVE_U_PCB_FPREG($f22, F22_NUM, a0)
	SAVE_U_PCB_FPREG($f23, F23_NUM, a0)
	SAVE_U_PCB_FPREG($f24, F24_NUM, a0)
	SAVE_U_PCB_FPREG($f25, F25_NUM, a0)
	SAVE_U_PCB_FPREG($f26, F26_NUM, a0)
	SAVE_U_PCB_FPREG($f27, F27_NUM, a0)
	SAVE_U_PCB_FPREG($f28, F28_NUM, a0)
	SAVE_U_PCB_FPREG($f29, F29_NUM, a0)
	SAVE_U_PCB_FPREG($f30, F30_NUM, a0)
	SAVE_U_PCB_FPREG($f31, F31_NUM, a0)

	mtc0	t1, MIPS_COP_0_STATUS		# Restore the status register.
	ITLBNOPFIX
	j	ra
	nop
END(MipsSaveCurFPState)

/*
 * When starting init, call this to configure the process for user
 * mode.  This will be inherited by other processes.
 */
LEAF_NOPROFILE(prepare_usermode)
	j	ra
	nop
END(prepare_usermode)


/*
 * This code is copied the user's stack for returning from signal handlers
 * (see sendsig() and sigreturn()). We have to compute the address
 * of the sigcontext struct for the sigreturn call.
 */
	.globl	_C_LABEL(sigcode)
_C_LABEL(sigcode):
	PTR_ADDU	a0, sp, SIGF_UC		# address of ucontext
	li		v0, SYS_sigreturn
# sigreturn (ucp)
	syscall
	break	0				# just in case sigreturn fails
	.globl	_C_LABEL(esigcode)
_C_LABEL(esigcode):

	.data
	.globl	szsigcode
szsigcode:
	.long	esigcode-sigcode
	.text
