code:
/*	$OpenBSD: tlbhandler.S,v 1.44 2014/08/12 19:33:59 miod Exp $ */

/*
 * Copyright (c) 1995-2004 Opsycon AB  (www.opsycon.se / www.opsycon.com)
 *
 * 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.
 *
 */

/*
 *  This code handles TLB exceptions and updates.
 */

#include <machine/param.h>
#include <machine/pte.h>
#include <machine/asm.h>
#include <machine/cpu.h>
#include <mips64/mips_cpu.h>
#include <machine/regnum.h>
#include <machine/cpustate.h>
#ifdef CPU_LOONGSON2
#include <machine/loongson2.h>
#endif

#include "assym.h"

#ifdef MIPS_PTE64
#define	PTE_LOG		3
#define	PTE_LOAD	ld
#define	PTE_OFFS	8
#else
#define	PTE_LOG		2
#define	PTE_LOAD	lw
#define	PTE_OFFS	4
#endif

	.set	mips3

	.set	noreorder	/* Default reorder mode */

/*---------------------------------------------------------------- tlb_miss
 *	Low level TLB exception handler. TLB and XTLB code are
 *	identical at the moment.
 *	This code is not copied to the exception area, instead
 *	trampolines branching to the appropriate code are built.
 */
	.set	noat

#ifdef CPU_R4000
	.globl	xtlb_miss_err_r4k
	.ent	xtlb_miss_err_r4k, 0
xtlb_miss_err_r4k:
	/*
	 * R4000 errata #27: TLB miss exception may be invoked with BadVAddr
	 * being incorrect.
	 */
	tlbp
	mfc0	k1, COP_0_TLB_INDEX
	bltz	k1, xtlb_miss			# missing!
	 nop
	ERET
	 nop
	.end	xtlb_miss_err_r4k

	.globl	xtlb_miss_err_r4000SC
	.ent	xtlb_miss_err_r4000SC, 0
xtlb_miss_err_r4000SC:
	/*
	 * R4000SC errata #12: In addition to errata #27 above, the tlbp
	 * instruction needs to be issued in uncached space, to prevent
	 * BadVAddr to be further corrupted if a VCE exception occurs
	 * immediately after the TLB miss exception has been serviced.
	 */
	tlbp
	mfc0	k1, COP_0_TLB_INDEX
	bltz	k1, 1f		# missing!
	 nop
	ERET
	 nop
1:
	LA	k1, xtlb_miss
	jr	k1
	 nop
	.end	xtlb_miss_err_r4000SC
#endif	/* CPU_R4000 */
#if defined(CPU_R5000) || defined(CPU_RM7000)
	.globl	xtlb_miss_err_r5k
	.ent	xtlb_miss_err_r5k, 0
xtlb_miss_err_r5k:
	/*
	 * R5000 errata: edge cases can trigger a TLB miss exception
	 * instead of an invalid TLB exception. Servicing the TLB miss
	 * exception would cause a duplicate TLB to be inserted, which
	 * causes the processor operation to become unpredictable (but
	 * very bad for the kernel's health, ten times out of nine).
	 *
	 * More details about the problem can be found in:
	 *  http://www.linux-mips.org/archives/linux-mips/2000-02/msg00040.html
	 *
	 * We work around the issue by checking for an existing TLB entry,
	 * and handling this as an invalid TLB exception (as it was intended
	 * to be), in this case.
	 */
	tlbp
	mfc0	k1, COP_0_TLB_INDEX
	bltz	k1, xtlb_miss		# missing!
	 nop
	b	k_tlb_inv
	 nop
	.end	xtlb_miss_err_r5k
#endif	/* CPU_R5000 || CPU_RM7000 */

	.globl	xtlb_miss
	.ent	xtlb_miss, 0
xtlb_miss:
	dmfc0	k0, COP_0_BAD_VADDR
	bltz	k0, _k_miss		# kernel address space
	PTR_SRL	k0, k0, SEGSHIFT
	sltiu	k1, k0, PMAP_SEGTABSIZE
	beqz	k1, _inv_seg		# wrong if outside pm_segtab
	nop
	GET_CPU_INFO(k1, k0)
	PTR_L	k1, CI_CURPROCPADDR(k1)
	dmfc0	k0, COP_0_BAD_VADDR
	PTR_SRL	k0, k0, SEGSHIFT
	PTR_SLL	k0, k0, LOGREGSZ
	PTR_L	k1, PCB_SEGTAB(k1)
	PTR_ADDU k1, k1, k0
	PTR_L	k1, 0(k1)		# get pointer to page table
	dmfc0	k0, COP_0_BAD_VADDR
	PTR_SRL	k0, k0, PGSHIFT - PTE_LOG
	beqz	k1, _inv_seg
	andi	k0, k0, ((NPTEPG >> 1) - 1) << (PTE_LOG + 1)
	PTR_ADDU k1, k1, k0
	PTE_LOAD	k0, 0(k1)
	PTE_LOAD	k1, PTE_OFFS(k1)
#ifdef CPU_R4000
	dsll	k0, k0, (64 - PG_FRAMEBITS - 1)	# clear bits left of PG_FRAME...
	dsll	k1, k1, (64 - PG_FRAMEBITS - 1)	# ...but PG_SP
	bltz	k0, _inv_seg			# defer to trap() if PG_SP set.
	bltz	k1, _inv_seg
	dsll	k0, k0, 1
	dsll	k1, k1, 1
#else
	dsll	k0, k0, (64 - PG_FRAMEBITS)	# clear bits left of PG_FRAME
	dsll	k1, k1, (64 - PG_FRAMEBITS)
#endif
	dsrl	k0, k0, (64 - PG_FRAMEBITS)
	dsrl	k1, k1, (64 - PG_FRAMEBITS)
	dmtc0	k0, COP_0_TLB_LO0
	dmtc0	k1, COP_0_TLB_LO1
	TLB_HAZARD
	tlbwr				# update TLB
	TLB_HAZARD

#ifdef CPU_LOONGSON2
	li	k0, COP_0_DIAG_ITLB_CLEAR | COP_0_DIAG_BTB_CLEAR | COP_0_DIAG_RAS_DISABLE
	dmtc0	k0, COP_0_DIAG
#endif

	ERET

_k_miss:
	dmfc0	k0, COP_0_BAD_VADDR	# must reload.
	b	k_tlb_miss		# kernel tlbmiss.
	nop

_inv_seg:				# No page table for this segment,
					# or processing is too complex to be
					# done here: invoke C code.
	mfc0	k0, COP_0_STATUS_REG
	andi	k0, SR_KSU_USER
	bne	k0, zero, go_u_general
	nop

go_k_general:
	j	k_general
	nop
go_u_general:
#ifdef CPU_LOONGSON2
	/*
	 * To work around a branch prediction issue on earlier LS2F
	 * chips, it is necessary to clear the BTB upon
	 * userland->kernel boundaries.
	 */
	li	k0, COP_0_DIAG_BTB_CLEAR | COP_0_DIAG_RAS_DISABLE
	dmtc0	k0, COP_0_DIAG
#endif
	j	u_general
	nop
	.end	xtlb_miss

	.set	at

/*---------------------------------------------------------------- k_tlb_inv
 *	Handle a TLB invalid exception from kernel mode in kernel
 *	space. This happens when we have a TLB match but an invalid
 *	entry. Try to reload.
 */
NLEAF(k_tlb_inv, 0)
	.set	noat
	LA	k1, (VM_MIN_KERNEL_ADDRESS)	# compute index
	dmfc0	k0, COP_0_BAD_VADDR		# get the fault address
	PTR_SUBU k0, k0, k1
	lw	k1, Sysmapsize			# index within range?
	PTR_SRL	k0, k0, PGSHIFT
	sltu	k1, k0, k1
	beq	k1, zero, sys_stk_chk		# No. check for valid stack
	nop

	PTR_L	k1, Sysmap
	PTR_SLL	k0, k0, PTE_LOG			# compute offset from index
	TLB_HAZARD
	tlbp					# Probe the invalid entry
	TLB_HAZARD				# necessary?
	PTR_ADDU k1, k1, k0
	and	k0, k0, PTE_OFFS		# check even/odd page
	bne	k0, zero, k_tlb_inv_odd
	nop

	mfc0	k0, COP_0_TLB_INDEX
	bltz	k0, sys_stk_chk			# probe fail
	PTE_LOAD k0, 0(k1)			# get PTE entry

	dsll	k0, k0, (64 - PG_FRAMEBITS)	# clear bits left of PG_FRAME
	dsrl	k0, k0, (64 - PG_FRAMEBITS)
	dmtc0	k0, COP_0_TLB_LO0		# load PTE entry
	and	k0, k0, PG_V			# check for valid entry
	beq	k0, zero, go_k_general		# PTE invalid
	PTE_LOAD k0, PTE_OFFS(k1)		# get odd PTE entry
	dsll	k0, k0, (64 - PG_FRAMEBITS)
	dsrl	k0, k0, (64 - PG_FRAMEBITS)
	dmtc0	k0, COP_0_TLB_LO1		# load PTE entry
	TLB_HAZARD
	tlbwi					# write TLB
	TLB_HAZARD

#ifdef CPU_LOONGSON2
	li	k0, COP_0_DIAG_ITLB_CLEAR | COP_0_DIAG_BTB_CLEAR | COP_0_DIAG_RAS_DISABLE
	dmtc0	k0, COP_0_DIAG
#endif

	ERET

k_tlb_inv_odd:
	mfc0	k0, COP_0_TLB_INDEX
	bltz	k0, sys_stk_chk			# probe fail
	PTE_LOAD k0, 0(k1)			# get PTE entry

	dsll	k0, k0, (64 - PG_FRAMEBITS)	# clear bits left of PG_FRAME
	dsrl	k0, k0, (64 - PG_FRAMEBITS)
	dmtc0	k0, COP_0_TLB_LO1		# save PTE entry
	and	k0, k0, PG_V			# check for valid entry
	beq	k0, zero, go_k_general		# PTE invalid
	PTE_LOAD k0, -PTE_OFFS(k1)		# get even PTE entry
	dsll	k0, k0, (64 - PG_FRAMEBITS)
	dsrl	k0, k0, (64 - PG_FRAMEBITS)
	dmtc0	k0, COP_0_TLB_LO0		# save PTE entry
	TLB_HAZARD
	tlbwi					# update TLB
	TLB_HAZARD

#ifdef CPU_LOONGSON2
	li	k0, COP_0_DIAG_ITLB_CLEAR | COP_0_DIAG_BTB_CLEAR | COP_0_DIAG_RAS_DISABLE
	dmtc0	k0, COP_0_DIAG
#endif

	ERET
END(k_tlb_inv)

/*---------------------------------------------------------------- k_tlb_miss
 *
 *	Handle a TLB miss exception from kernel mode in kernel space.
 *	We must check that this is coming from kernel mode. If not
 *	it's a bad address from user mode so handle properly.
 *	Load up the correct entry contents from the kernel map.
 *	k0 has bad address.
 */
NLEAF(k_tlb_miss, 0)
	.set	noat
	mfc0	k1, COP_0_STATUS_REG
	andi	k1, SR_KSU_USER
	bne	k1, zero, go_u_general
	LA	k1, (VM_MIN_KERNEL_ADDRESS)	# compute index
						# (safe if expands to > 1 insn)
	PTR_SUBU k0, k0, k1
	lw	k1, Sysmapsize			# index within range?
	PTR_SRL	k0, k0, PGSHIFT
	sltu	k1, k0, k1
	beq	k1, zero, sys_stk_chk		# No. check for valid stack
	PTR_SRL	k0, k0, 1
	PTR_L	k1, Sysmap
	PTR_SLL	k0, k0, (PTE_LOG + 1)		# compute offset from index
	PTR_ADDU k1, k1, k0
	PTE_LOAD k0, 0(k1)			# get PTE entry
	PTE_LOAD k1, PTE_OFFS(k1)		# get odd PTE entry
	dsll	k0, k0, (64 - PG_FRAMEBITS)	# clear bits left of PG_FRAME
	dsrl	k0, k0, (64 - PG_FRAMEBITS)
	dmtc0	k0, COP_0_TLB_LO0		# load PTE entry
	dsll	k1, k1, (64 - PG_FRAMEBITS)
	dsrl	k1, k1, (64 - PG_FRAMEBITS)
	dmtc0	k1, COP_0_TLB_LO1		# load PTE entry
	TLB_HAZARD
	tlbwr					# write TLB
	TLB_HAZARD

#ifdef CPU_LOONGSON2
	li	k0, COP_0_DIAG_ITLB_CLEAR | COP_0_DIAG_BTB_CLEAR | COP_0_DIAG_RAS_DISABLE
	dmtc0	k0, COP_0_DIAG
#endif

	ERET

sys_stk_chk:
	GET_CPU_INFO(k1, k0)
	PTR_L	k1, CI_CURPROCPADDR(k1)
	PTR_SUBU k0, sp, k1			# check to see if we have a
	sltiu	k0, 2048			#  valid kernel stack
	beqz	k0, go_k_general		# yes, handle.
	nop

	LA	a0, start-FRAMESZ(CF_SZ)-4*REGSZ # set sp to a valid place
#ifdef __mips_n64
	mfc0	a4, COP_0_STATUS_REG
	mfc0	a5, COP_0_CAUSE_REG
	move	a6, sp
#else
	mfc0	a2, COP_0_STATUS_REG
	mfc0	a3, COP_0_CAUSE_REG
	REG_S	a2, CF_ARGSZ+0*REGSZ(sp)
	REG_S	a3, CF_ARGSZ+1*REGSZ(sp)
	PTR_S	sp, CF_ARGSZ+2*REGSZ(a0)
#endif
	move	sp, a0
	dmfc0	a3, COP_0_BAD_VADDR
	move	a2, ra
	dmfc0	a1, COP_0_EXC_PC
	LA	a0, 1f
	jal	printf
	nop

	LA	sp, start-FRAMESZ(CF_SZ)	# set sp to a valid place

#ifdef DDB
	LA	a1, db_printf
	LA	a0, 2f
	jal	trapDump
	nop
#endif

	PANIC("kernel stack overflow")
	/*noreturn*/

	.data
1:
	.asciiz	"\rktlbmiss: PC %p RA %p ADR %p\nSR %p CR %p SP %p\n"
2:
	.asciiz	"stack ovf"
	.text

	.set	at
END(k_tlb_miss)

/*---------------------------------------------------------------- tlb_flush
 *	Flush the "random" entries from the TLB.
 *	Uses "wired" register to determine what register to start with.
 *	Arg "tlbsize" is the number of entries to flush.
 */
LEAF(tlb_flush, 0)
	mfc0	v1, COP_0_STATUS_REG	# Save the status register.
	ori	v0, v1, SR_INT_ENAB
	xori	v0, v0, SR_INT_ENAB
	mtc0	v0, COP_0_STATUS_REG	# Disable interrupts
	MTC0_SR_IE_HAZARD
	mfc0	ta1, COP_0_TLB_WIRED
	LA	v0, CKSEG0_BASE		# invalid address
	dmfc0	ta0, COP_0_TLB_HI	# Save the PID

#ifdef CPU_OCTEON
	PTR_SLL	ta2, ta1, PAGE_SHIFT + 1
	PTR_ADDU v0, v0, ta2
#else
	dmtc0	v0, COP_0_TLB_HI	# Mark entry high as invalid
#endif
	dmtc0	zero, COP_0_TLB_LO0	# Zero out low entry0.
	dmtc0	zero, COP_0_TLB_LO1	# Zero out low entry1.
	mtc0	zero, COP_0_TLB_PG_MASK	# Zero out mask entry.
/*
 * Align the starting value (ta1) and the upper bound (a0).
 */
1:
	mtc0	ta1, COP_0_TLB_INDEX	# Set the index register.
#ifdef CPU_OCTEON
	dmtc0	v0, COP_0_TLB_HI	# Mark entry high as invalid
	PTR_ADDU v0, v0, 2 * PAGE_SIZE
#endif
	addu	ta1, ta1, 1		# Increment index.
	TLB_HAZARD
	tlbwi				# Write the TLB entry.
	TLB_HAZARD
	bne	ta1, a0, 1b
	nop

#ifdef CPU_LOONGSON2
	li	v0, COP_0_DIAG_ITLB_CLEAR | COP_0_DIAG_BTB_CLEAR | COP_0_DIAG_RAS_DISABLE
	dmtc0	v0, COP_0_DIAG
#endif

	dmtc0	ta0, COP_0_TLB_HI	# Restore the PID
	li	a0, TLB_PAGE_MASK
	mtc0	a0, COP_0_TLB_PG_MASK	# Restore default mask value.
	mtc0	v1, COP_0_STATUS_REG	# Restore the status register
	MTC0_SR_IE_HAZARD
	j	ra
	nop
END(tlb_flush)

/*--------------------------------------------------------------- tlb_flush_addr
 *	Flush any TLB entries for the given address and TLB PID.
 */
LEAF(tlb_flush_addr, 0)
	mfc0	v1, COP_0_STATUS_REG	# Save the status register.
	ori	v0, v1, SR_INT_ENAB
	xori	v0, v0, SR_INT_ENAB
	mtc0	v0, COP_0_STATUS_REG	# Disable interrupts
	MTC0_SR_IE_HAZARD
	dli	v0, (PG_HVPN | PG_ASID_MASK)
	and	a0, a0, v0		# Make sure valid hi value.
	dmfc0	ta0, COP_0_TLB_HI	# Get current PID
	dmtc0	a0, COP_0_TLB_HI	# look for addr & PID
	TLB_HAZARD
	tlbp				# Probe for the entry.
	TLB_HAZARD			# necessary?
	LA	ta1, CKSEG0_BASE	# Load invalid entry.
	mfc0	v0, COP_0_TLB_INDEX	# See what we got
	bltz	v0, 1f			# index < 0 => !found
	nop
#ifdef CPU_OCTEON
	PTR_SLL	ta2, v0, PAGE_SHIFT + 1
	PTR_ADDU ta1, ta1, ta2
#endif
	dmtc0	ta1, COP_0_TLB_HI	# Mark entry high as invalid

	dmtc0	zero, COP_0_TLB_LO0	# Zero out low entry.
	dmtc0	zero, COP_0_TLB_LO1	# Zero out low entry.
	TLB_HAZARD
	tlbwi
	TLB_HAZARD

#ifdef CPU_LOONGSON2
	li	v0, COP_0_DIAG_ITLB_CLEAR | COP_0_DIAG_BTB_CLEAR | COP_0_DIAG_RAS_DISABLE
	dmtc0	v0, COP_0_DIAG
#endif

1:
	dmtc0	ta0, COP_0_TLB_HI	# restore PID
	mtc0	v1, COP_0_STATUS_REG	# Restore the status register
	MTC0_SR_IE_HAZARD
	j	ra
	nop
END(tlb_flush_addr)

/*---------------------------------------------------------------- tlb_update
 *	Update the TLB if highreg is found; otherwise, enter the data.
 */
LEAF(tlb_update, 0)
	mfc0	v1, COP_0_STATUS_REG	# Save the status register.
	ori	v0, v1, SR_INT_ENAB
	xori	v0, v0, SR_INT_ENAB
	mtc0	v0, COP_0_STATUS_REG	# Disable interrupts
	MTC0_SR_IE_HAZARD
	and	ta1, a0, PG_ODDPG	# ta1 = Even/Odd flag
	dli	v0, (PG_HVPN | PG_ASID_MASK)
	and	a0, a0, v0
	dmfc0	ta0, COP_0_TLB_HI	# Save current PID
	dmtc0	a0, COP_0_TLB_HI	# Init high reg
	and	a2, a1, PG_G		# Copy global bit
	li	a3, TLB_PAGE_MASK
	TLB_HAZARD
	tlbp				# Probe for the entry.
	TLB_HAZARD			# necessary?
	dsll	a1, a1, (64 - PG_FRAMEBITS)	# clear bits left of PG_FRAME
	mfc0	v0, COP_0_TLB_INDEX	# See what we got
	bne	ta1, zero, 2f		# Decide even odd
	dsrl	a1, a1, (64 - PG_FRAMEBITS)
# EVEN
	bltz	v0, 1f			# index < 0 => !found
	nop

	TLB_HAZARD
	tlbr				# update, read entry first
	TLB_HAZARD			# necessary?
	dmtc0	a1, COP_0_TLB_LO0	# init low reg0.
	TLB_HAZARD
	tlbwi				# update slot found
	TLB_HAZARD
	b	4f
	li	v0, 1
1:
	mtc0	a3, COP_0_TLB_PG_MASK	# init mask.
	dmtc0	a0, COP_0_TLB_HI	# init high reg.
	dmtc0	a1, COP_0_TLB_LO0	# init low reg0.
	dmtc0	a2, COP_0_TLB_LO1	# init low reg1.
	TLB_HAZARD
	tlbwr				# enter into a random slot
	TLB_HAZARD
	b	4f
	li	v0, 0
# ODD
2:
	nop
	bltz	v0, 3f			# index < 0 => !found
	nop

	TLB_HAZARD
	tlbr				# read the entry first
	TLB_HAZARD			# necessary?
	dmtc0	a1, COP_0_TLB_LO1	# init low reg1.
	TLB_HAZARD
	tlbwi				# update slot found
	TLB_HAZARD
	b	4f
	li	v0, 1
3:
	mtc0	a3, COP_0_TLB_PG_MASK	# init mask.
	dmtc0	a0, COP_0_TLB_HI	# init high reg.
	dmtc0	a2, COP_0_TLB_LO0	# init low reg0.
	dmtc0	a1, COP_0_TLB_LO1	# init low reg1.
	TLB_HAZARD
	tlbwr				# enter into a random slot
	TLB_HAZARD
4:
#ifdef CPU_LOONGSON2
	li	v0, COP_0_DIAG_ITLB_CLEAR | COP_0_DIAG_BTB_CLEAR | COP_0_DIAG_RAS_DISABLE
	dmtc0	v0, COP_0_DIAG
#endif

	dmtc0	ta0, COP_0_TLB_HI	# restore PID
	mtc0	v1, COP_0_STATUS_REG	# Restore the status register
	MTC0_SR_IE_HAZARD
	j	ra
	li	v0, 0
END(tlb_update)

/*
 * int64_t tlb_probe(vaddr_t a0);
 * Probe for a TLB entry covering the given address, and return its index
 * (< 0 if no match)
 */
LEAF(tlb_probe, 0)
	mfc0	v1, COP_0_STATUS_REG	# Save the status register.
	ori	v0, v1, SR_INT_ENAB
	xori	v0, v0, SR_INT_ENAB
	mtc0	v0, COP_0_STATUS_REG	# Disable interrupts
	MTC0_SR_IE_HAZARD
	dli	v0, (PG_HVPN | PG_ASID_MASK)
	and	a0, a0, v0
	dmfc0	ta0, COP_0_TLB_HI	# Save current PID
	dmtc0	a0, COP_0_TLB_HI	# Init high reg
	TLB_HAZARD
	tlbp				# Probe for the entry.
	TLB_HAZARD			# necessary?
	mfc0	v0, COP_0_TLB_INDEX	# Return index
	dmtc0	ta0, COP_0_TLB_HI	# restore PID
	mtc0	v1, COP_0_STATUS_REG	# Restore the status register
	MTC0_SR_IE_HAZARD
	j	ra
	nop
END(tlb_probe)

/*
 * void tlb_update_indexed(vaddr_t a0, register_t a1, register_t a2, uint a3);
 * Update a TLB entry pair.
 */
LEAF(tlb_update_indexed, 0)
	mfc0	v1, COP_0_STATUS_REG	# Save the status register.
	ori	v0, v1, SR_INT_ENAB
	xori	v0, v0, SR_INT_ENAB
	mtc0	v0, COP_0_STATUS_REG	# Disable interrupts
	MTC0_SR_IE_HAZARD
	dmfc0	ta0, COP_0_TLB_HI	# Save current PID
	li	ta1, TLB_PAGE_MASK

	mtc0	a3, COP_0_TLB_INDEX
	mtc0	ta1, COP_0_TLB_PG_MASK	# init mask
	dmtc0	a0, COP_0_TLB_HI	# init high reg.

	dsll	a1, a1, (64 - PG_FRAMEBITS)	# clear bits left of PG_FRAME
	dsrl	a1, a1, (64 - PG_FRAMEBITS)
	dmtc0	a1, COP_0_TLB_LO0	# init low reg0.
	dsll	a2, a2, (64 - PG_FRAMEBITS)	# clear bits left of PG_FRAME
	dsrl	a2, a2, (64 - PG_FRAMEBITS)
	dmtc0	a2, COP_0_TLB_LO1	# init low reg1.

	TLB_HAZARD
	tlbwi				# update slot
	TLB_HAZARD

	dmtc0	ta0, COP_0_TLB_HI	# restore PID
	mtc0	v1, COP_0_STATUS_REG	# Restore the status register
	MTC0_SR_IE_HAZARD
	j	ra
	nop
END(tlb_update_indexed)

/*---------------------------------------------------------------- tlb_read
 *	Read the TLB entry.
 */
LEAF(tlb_read, 0)
	mfc0	v1, COP_0_STATUS_REG	# Save the status register.
	ori	v0, v1, SR_INT_ENAB
	xori	v0, v0, SR_INT_ENAB
	mtc0	v0, COP_0_STATUS_REG	# Disable interrupts
	MTC0_SR_IE_HAZARD
	dmfc0	v0, COP_0_TLB_HI	# Get current PID

	mtc0	a0, COP_0_TLB_INDEX	# Set the index register
	TLB_HAZARD
	tlbr				# Read from the TLB
	TLB_HAZARD			# necessary?
	mfc0	ta0, COP_0_TLB_PG_MASK	# fetch the size
	dmfc0	ta1, COP_0_TLB_HI	# fetch the hi entry
	dmfc0	ta2, COP_0_TLB_LO0	# See what we got
	dmfc0	ta3, COP_0_TLB_LO1	# See what we got
	dmtc0	v0, COP_0_TLB_HI	# restore PID
	nop
	nop
	nop				# wait for PID active
	li	a0, TLB_PAGE_MASK
	mtc0	a0, COP_0_TLB_PG_MASK	# Restore default mask value.
	mtc0	v1, COP_0_STATUS_REG	# Restore the status register
	MTC0_SR_IE_HAZARD
	sd	ta0, 0(a1)
	sd	ta1, 8(a1)
	sd	ta2, 16(a1)
	j	ra
	sd	ta3, 24(a1)
END(tlb_read)

/*---------------------------------------------------------------- tlb_get_pid
 *	Read the tlb pid value.
 */
LEAF(tlb_get_pid, 0)
	dmfc0	v0, COP_0_TLB_HI	# get PID
	li	v1, PG_ASID_MASK	# mask off PID
	j	ra
	and	v0, v0, v1		# mask off PID
END(tlb_get_pid)

/*---------------------------------------------------------------- tlb_set_pid
 *	Write the given pid into the TLB pid reg.
 */
LEAF(tlb_set_pid, 0)
	dmtc0	a0, COP_0_TLB_HI		# Write the hi reg value
	TLB_HAZARD
	j	ra
	nop
END(tlb_set_pid)

/*---------------------------------------------------------------- tlb_set_wired
 *	Write the given value into the TLB wired reg.
 */
LEAF(tlb_set_wired, 0)
	mtc0	a0, COP_0_TLB_WIRED
	j	ra
	nop
END(tlb_set_wired)

/*
 *	Initialize the TLB page mask.
 */
LEAF(tlb_set_page_mask, 0)
	mtc0	a0, COP_0_TLB_PG_MASK
	TLB_HAZARD
	j	ra
	nop
END(tlb_set_page_mask)

/*
 *	Initialize the kernel page table pointer.
 *	This is a no-op on non-R8000 processors.
 */
LEAF(tlb_set_gbase, 0)
	j	ra
	 nop
END(tlb_set_gbase)


code:
/*	$OpenBSD: trap.c,v 1.108 2015/08/03 13:32:15 visa Exp $	*/

/*
 * Copyright (c) 1988 University of Utah.
 * Copyright (c) 1992, 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 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.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 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.
 *
 * from: Utah Hdr: trap.c 1.32 91/04/06
 *
 *	from: @(#)trap.c	8.5 (Berkeley) 1/11/94
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/exec.h>
#include <sys/proc.h>
#include <sys/kernel.h>
#include <sys/signalvar.h>
#include <sys/user.h>
#include <sys/syscall.h>
#include <sys/syscall_mi.h>
#include <sys/buf.h>
#include <sys/device.h>
#include <sys/atomic.h>
#ifdef PTRACE
#include <sys/ptrace.h>
#endif

#include <uvm/uvm_extern.h>

#include <machine/autoconf.h>
#include <machine/cpu.h>
#include <mips64/mips_cpu.h>
#include <machine/fpu.h>
#include <machine/frame.h>
#include <machine/mips_opcode.h>
#include <machine/regnum.h>
#include <machine/trap.h>

#include <mips64/rm7000.h>

#ifdef DDB
#include <mips64/db_machdep.h>
#include <ddb/db_output.h>
#include <ddb/db_sym.h>
#endif

#include <sys/syslog.h>

#define	USERMODE(ps)	(((ps) & SR_KSU_MASK) == SR_KSU_USER)

const char *trap_type[] = {
	"external interrupt",
	"TLB modification",
	"TLB miss (load or instr. fetch)",
	"TLB miss (store)",
	"address error (load or I-fetch)",
	"address error (store)",
	"bus error (I-fetch)",
	"bus error (load or store)",
	"system call",
	"breakpoint",
	"reserved instruction",
	"coprocessor unusable",
	"arithmetic overflow",
	"trap",
	"virtual coherency instruction",
	"floating point",
	"reserved 16",
	"reserved 17",
	"reserved 18",
	"reserved 19",
	"reserved 20",
	"reserved 21",
	"reserved 22",
	"watch",
	"reserved 24",
	"reserved 25",
	"reserved 26",
	"reserved 27",
	"reserved 28",
	"reserved 29",
	"reserved 30",
	"virtual coherency data"
};

#if defined(DDB) || defined(DEBUG)
struct trapdebug trapdebug[MAXCPUS * TRAPSIZE];
uint trppos[MAXCPUS];

void	stacktrace(struct trap_frame *);
uint32_t kdbpeek(vaddr_t);
uint64_t kdbpeekd(vaddr_t);
#endif	/* DDB || DEBUG */

#if defined(DDB)
extern int kdb_trap(int, db_regs_t *);
#endif

void	ast(void);
extern void interrupt(struct trap_frame *);
void	itsa(struct trap_frame *, struct cpu_info *, struct proc *, int);
void	trap(struct trap_frame *);
#ifdef PTRACE
int	ptrace_read_insn(struct proc *, vaddr_t, uint32_t *);
int	ptrace_write_insn(struct proc *, vaddr_t, uint32_t);
int	process_sstep(struct proc *, int);
#endif

/*
 * Handle an AST for the current process.
 */
void
ast(void)
{
	struct cpu_info *ci = curcpu();
	struct proc *p = ci->ci_curproc;

	p->p_md.md_astpending = 0;

	atomic_inc_int(&uvmexp.softs);
	mi_ast(p, ci->ci_want_resched);
	userret(p);
}

/*
 * Handle an exception.
 * In the case of a kernel trap, we return the pc where to resume if
 * pcb_onfault is set, otherwise, return old pc.
 */
void
trap(struct trap_frame *trapframe)
{
	struct cpu_info *ci = curcpu();
	struct proc *p = ci->ci_curproc;
	int type;

	type = (trapframe->cause & CR_EXC_CODE) >> CR_EXC_CODE_SHIFT;

#if defined(CPU_R8000) && !defined(DEBUG_INTERRUPT)
	if (type != T_INT)
#endif
		trapdebug_enter(ci, trapframe, -1);

#ifdef CPU_R8000
	if (type != T_INT && type != T_SYSCALL)
#else
	if (type != T_SYSCALL)
#endif
		atomic_inc_int(&uvmexp.traps);
	if (USERMODE(trapframe->sr))
		type |= T_USER;

	/*
	 * Enable hardware interrupts if they were on before the trap;
	 * enable IPI interrupts only otherwise.
	 */
	switch (type) {
#ifdef CPU_R8000
	case T_INT:
	case T_INT | T_USER:
#endif
	case T_BREAK:
		break;
	default:
		if (ISSET(trapframe->sr, SR_INT_ENAB))
			enableintr();
		else {
#ifdef MULTIPROCESSOR
			ENABLEIPI();
#endif
		}
		break;
	}

	if (type & T_USER)
		refreshcreds(p);

#ifdef CPU_R8000
	/*
	 * Some exception causes on R8000 are actually detected by external
	 * circuitry, and as such are reported as external interrupts.
	 * On R8000 kernels, external interrupts vector to trap() instead of
	 * interrupt(), so that we can process these particular exceptions
	 * as if they were triggered as regular exceptions.
	 */
	if ((type & ~T_USER) == T_INT) {
		/*
		 * Similar reality check as done in interrupt(), in case
		 * an interrupt occured between a write to COP_0_STATUS_REG
		 * and it taking effect.
		 */
		if (!ISSET(trapframe->sr, SR_INT_ENAB))
			return;

		if (trapframe->cause & CR_VCE) {
#ifndef DEBUG_INTERRUPT
			trapdebug_enter(ci, trapframe, -1);
#endif
			panic("VCE or TLBX");
		}
		if (trapframe->cause & CR_FPE) {
#ifndef DEBUG_INTERRUPT
			trapdebug_enter(ci, trapframe, -1);
#endif
			itsa(trapframe, ci, p, T_FPE | (type & T_USER));
			cp0_reset_cause(CR_FPE);
		}
		if (trapframe->cause & CR_INT_MASK)
			interrupt(trapframe);

		return;	/* no userret */
	} else
#endif
		itsa(trapframe, ci, p, type);

	if (type & T_USER)
		userret(p);
}

/*
 * Handle a single exception.
 */
void
itsa(struct trap_frame *trapframe, struct cpu_info *ci, struct proc *p,
    int type)
{
	int i;
	unsigned ucode = 0;
	vm_prot_t ftype;
	extern vaddr_t onfault_table[];
	int onfault;
	int typ = 0;
	union sigval sv;
	struct pcb *pcb;

	switch (type) {
	case T_TLB_MOD:
		/* check for kernel address */
		if (trapframe->badvaddr < 0) {
			pt_entry_t *pte, entry;
			paddr_t pa;
			vm_page_t pg;

			pte = kvtopte(trapframe->badvaddr);
			entry = *pte;
#ifdef DIAGNOSTIC
			if (!(entry & PG_V) || (entry & PG_M))
				panic("trap: ktlbmod: invalid pte");
#endif
			if (pmap_is_page_ro(pmap_kernel(),
			    trunc_page(trapframe->badvaddr), entry)) {
				/* write to read only page in the kernel */
				ftype = PROT_WRITE;
				pcb = &p->p_addr->u_pcb;
				goto kernel_fault;
			}
			entry |= PG_M;
			*pte = entry;
			KERNEL_LOCK();
			pmap_update_kernel_page(trapframe->badvaddr & ~PGOFSET,
			    entry);
			pa = pfn_to_pad(entry);
			pg = PHYS_TO_VM_PAGE(pa);
			if (pg == NULL)
				panic("trap: ktlbmod: unmanaged page");
			pmap_set_modify(pg);
			KERNEL_UNLOCK();
			return;
		}
		/* FALLTHROUGH */

	case T_TLB_MOD+T_USER:
	    {
		pt_entry_t *pte, entry;
		paddr_t pa;
		vm_page_t pg;
		pmap_t pmap = p->p_vmspace->vm_map.pmap;

		if (!(pte = pmap_segmap(pmap, trapframe->badvaddr)))
			panic("trap: utlbmod: invalid segmap");
		pte += uvtopte(trapframe->badvaddr);
		entry = *pte;
#ifdef DIAGNOSTIC
		if (!(entry & PG_V) || (entry & PG_M))
			panic("trap: utlbmod: invalid pte");
#endif
		if (pmap_is_page_ro(pmap,
		    trunc_page(trapframe->badvaddr), entry)) {
			/* write to read only page */
			ftype = PROT_WRITE;
			pcb = &p->p_addr->u_pcb;
			goto fault_common_no_miss;
		}
		entry |= PG_M;
		*pte = entry;
		KERNEL_LOCK();
		pmap_update_user_page(pmap, (trapframe->badvaddr & ~PGOFSET),
		    entry);
		pa = pfn_to_pad(entry);
		pg = PHYS_TO_VM_PAGE(pa);
		if (pg == NULL)
			panic("trap: utlbmod: unmanaged page");
		pmap_set_modify(pg);
		KERNEL_UNLOCK();
		return;
	    }

	case T_TLB_LD_MISS:
	case T_TLB_ST_MISS:
		ftype = (type == T_TLB_ST_MISS) ? PROT_WRITE : PROT_READ;
		pcb = &p->p_addr->u_pcb;
		/* check for kernel address */
		if (trapframe->badvaddr < 0) {
			vaddr_t va;
			int rv;

	kernel_fault:
			va = trunc_page((vaddr_t)trapframe->badvaddr);
			onfault = pcb->pcb_onfault;
			pcb->pcb_onfault = 0;
			KERNEL_LOCK();
			rv = uvm_fault(kernel_map, va, 0, ftype);
			KERNEL_UNLOCK();
			pcb->pcb_onfault = onfault;
			if (rv == 0)
				return;
			if (onfault != 0) {
				pcb->pcb_onfault = 0;
				trapframe->pc = onfault_table[onfault];
				return;
			}
			goto err;
		}
		/*
		 * It is an error for the kernel to access user space except
		 * through the copyin/copyout routines.
		 */
		if (pcb->pcb_onfault != 0) {
			/*
			 * We want to resolve the TLB fault before invoking
			 * pcb_onfault if necessary.
			 */
			goto fault_common;
		} else {
			goto err;
		}

	case T_TLB_LD_MISS+T_USER:
		ftype = PROT_READ;
		pcb = &p->p_addr->u_pcb;
		goto fault_common;

	case T_TLB_ST_MISS+T_USER:
		ftype = PROT_WRITE;
		pcb = &p->p_addr->u_pcb;
fault_common:

#ifdef CPU_R4000
		if (r4000_errata != 0) {
			if (eop_tlb_miss_handler(trapframe, ci, p) != 0)
				return;
		}
#endif

fault_common_no_miss:

#ifdef CPU_R4000
		if (r4000_errata != 0) {
			eop_cleanup(trapframe, p);
		}
#endif

	    {
		vaddr_t va;
		struct vmspace *vm;
		vm_map_t map;
		int rv;

		vm = p->p_vmspace;
		map = &vm->vm_map;
		va = trunc_page((vaddr_t)trapframe->badvaddr);

		onfault = pcb->pcb_onfault;
		pcb->pcb_onfault = 0;
		KERNEL_LOCK();

		rv = uvm_fault(map, va, 0, ftype);
		pcb->pcb_onfault = onfault;

		/*
		 * If this was a stack access we keep track of the maximum
		 * accessed stack size.  Also, if vm_fault gets a protection
		 * failure it is due to accessing the stack region outside
		 * the current limit and we need to reflect that as an access
		 * error.
		 */
		if ((caddr_t)va >= vm->vm_maxsaddr) {
			if (rv == 0)
				uvm_grow(p, va);
			else if (rv == EACCES)
				rv = EFAULT;
		}
		KERNEL_UNLOCK();
		if (rv == 0)
			return;
		if (!USERMODE(trapframe->sr)) {
			if (onfault != 0) {
				pcb->pcb_onfault = 0;
				trapframe->pc =  onfault_table[onfault];
				return;
			}
			goto err;
		}

		ucode = ftype;
		i = SIGSEGV;
		typ = SEGV_MAPERR;
		break;
	    }

	case T_ADDR_ERR_LD+T_USER:	/* misaligned or kseg access */
	case T_ADDR_ERR_ST+T_USER:	/* misaligned or kseg access */
		ucode = 0;		/* XXX should be PROT_something */
		i = SIGBUS;
		typ = BUS_ADRALN;
		break;
	case T_BUS_ERR_IFETCH+T_USER:	/* BERR asserted to cpu */
	case T_BUS_ERR_LD_ST+T_USER:	/* BERR asserted to cpu */
		ucode = 0;		/* XXX should be PROT_something */
		i = SIGBUS;
		typ = BUS_OBJERR;
		break;

	case T_SYSCALL+T_USER:
	    {
		struct trap_frame *locr0 = p->p_md.md_regs;
		struct sysent *callp;
		unsigned int code;
		register_t tpc;
		int numsys, error;
		struct args {
			register_t i[8];
		} args;
		register_t rval[2];

		atomic_inc_int(&uvmexp.syscalls);

		/* compute next PC after syscall instruction */
		tpc = trapframe->pc; /* Remember if restart */
		if (trapframe->cause & CR_BR_DELAY)
			locr0->pc = MipsEmulateBranch(locr0,
			    trapframe->pc, 0, 0);
		else
			locr0->pc += 4;
		callp = p->p_p->ps_emul->e_sysent;
		numsys = p->p_p->ps_emul->e_nsysent;
		code = locr0->v0;
		switch (code) {
		case SYS_syscall:
		case SYS___syscall:
			/*
			 * Code is first argument, followed by actual args.
			 * __syscall provides the code as a quad to maintain
			 * proper alignment of 64-bit arguments on 32-bit
			 * platforms, which doesn't change anything here.
			 */
			code = locr0->a0;
			if (code >= numsys)
				callp += p->p_p->ps_emul->e_nosys; /* (illegal) */
			else
				callp += code;
			i = callp->sy_argsize / sizeof(register_t);
			args.i[0] = locr0->a1;
			args.i[1] = locr0->a2;
			args.i[2] = locr0->a3;
			if (i > 3) {
				args.i[3] = locr0->a4;
				args.i[4] = locr0->a5;
				args.i[5] = locr0->a6;
				args.i[6] = locr0->a7;
				if (i > 7)
					if ((error = copyin((void *)locr0->sp,
					    &args.i[7], sizeof(register_t))))
						goto bad;
			}
			break;
		default:
			if (code >= numsys)
				callp += p->p_p->ps_emul->e_nosys; /* (illegal) */
			else
				callp += code;

			i = callp->sy_narg;
			args.i[0] = locr0->a0;
			args.i[1] = locr0->a1;
			args.i[2] = locr0->a2;
			args.i[3] = locr0->a3;
			if (i > 4) {
				args.i[4] = locr0->a4;
				args.i[5] = locr0->a5;
				args.i[6] = locr0->a6;
				args.i[7] = locr0->a7;
			}
		}

		rval[0] = 0;
		rval[1] = locr0->v1;

#if defined(DDB) || defined(DEBUG)
		trapdebug[TRAPSIZE * ci->ci_cpuid + (trppos[ci->ci_cpuid] == 0 ?
		    TRAPSIZE : trppos[ci->ci_cpuid]) - 1].code = code;
#endif

		error = mi_syscall(p, code, callp, args.i, rval);

		switch (error) {
		case 0:
			locr0->v0 = rval[0];
			locr0->v1 = rval[1];
			locr0->a3 = 0;
			break;

		case ERESTART:
			locr0->pc = tpc;
			break;

		case EJUSTRETURN:
			break;	/* nothing to do */

		default:
		bad:
			locr0->v0 = error;
			locr0->a3 = 1;
		}

		mi_syscall_return(p, code, error, rval);

		return;
	    }

	case T_BREAK:
#ifdef DDB
		kdb_trap(type, trapframe);
#endif
		/* Reenable interrupts if necessary */
		if (trapframe->sr & SR_INT_ENAB) {
			enableintr();
		}
		return;

	case T_BREAK+T_USER:
	    {
		caddr_t va;
		u_int32_t instr;
		struct trap_frame *locr0 = p->p_md.md_regs;

		/* compute address of break instruction */
		va = (caddr_t)trapframe->pc;
		if (trapframe->cause & CR_BR_DELAY)
			va += 4;

		/* read break instruction */
		copyin(va, &instr, sizeof(int32_t));

		switch ((instr & BREAK_VAL_MASK) >> BREAK_VAL_SHIFT) {
		case 6:	/* gcc range error */
			i = SIGFPE;
			typ = FPE_FLTSUB;
			/* skip instruction */
			if (trapframe->cause & CR_BR_DELAY)
				locr0->pc = MipsEmulateBranch(locr0,
				    trapframe->pc, 0, 0);
			else
				locr0->pc += 4;
			break;
		case 7:	/* gcc3 divide by zero */
			i = SIGFPE;
			typ = FPE_INTDIV;
			/* skip instruction */
			if (trapframe->cause & CR_BR_DELAY)
				locr0->pc = MipsEmulateBranch(locr0,
				    trapframe->pc, 0, 0);
			else
				locr0->pc += 4;
			break;
#ifdef PTRACE
		case BREAK_SSTEP_VAL:
			if (p->p_md.md_ss_addr == (long)va) {
#ifdef DEBUG
				printf("trap: %s (%d): breakpoint at %p "
				    "(insn %08x)\n",
				    p->p_comm, p->p_pid,
				    (void *)p->p_md.md_ss_addr,
				    p->p_md.md_ss_instr);
#endif

				/* Restore original instruction and clear BP */
				KERNEL_LOCK();
				process_sstep(p, 0);
				KERNEL_UNLOCK();
				typ = TRAP_BRKPT;
			} else {
				typ = TRAP_TRACE;
			}
			i = SIGTRAP;
			break;
#endif
#ifdef FPUEMUL
		case BREAK_FPUEMUL_VAL:
			/*
			 * If this is a genuine FP emulation break,
			 * resume execution to our branch destination.
			 */
			if ((p->p_md.md_flags & MDP_FPUSED) != 0 &&
			    p->p_md.md_fppgva + 4 == (vaddr_t)va) {
				struct vm_map *map = &p->p_vmspace->vm_map;

				p->p_md.md_flags &= ~MDP_FPUSED;
				locr0->pc = p->p_md.md_fpbranchva;

				/*
				 * Prevent access to the relocation page.
				 * XXX needs to be fixed to work with rthreads
				 */
				uvm_fault_unwire(map, p->p_md.md_fppgva,
				    p->p_md.md_fppgva + PAGE_SIZE);
				(void)uvm_map_protect(map, p->p_md.md_fppgva,
				    p->p_md.md_fppgva + PAGE_SIZE,
				    PROT_NONE, FALSE);
				return;
			}
			/* FALLTHROUGH */
#endif
		default:
			typ = TRAP_TRACE;
			i = SIGTRAP;
			break;
		}
		break;
	    }

	case T_IWATCH+T_USER:
	case T_DWATCH+T_USER:
	    {
		caddr_t va;
		/* compute address of trapped instruction */
		va = (caddr_t)trapframe->pc;
		if (trapframe->cause & CR_BR_DELAY)
			va += 4;
		printf("watch exception @ %p\n", va);
#ifdef RM7K_PERFCNTR
		if (rm7k_watchintr(trapframe)) {
			/* Return to user, don't add any more overhead */
			return;
		}
#endif
		i = SIGTRAP;
		typ = TRAP_BRKPT;
		break;
	    }

	case T_TRAP+T_USER:
	    {
		caddr_t va;
		u_int32_t instr;
		struct trap_frame *locr0 = p->p_md.md_regs;

		/* compute address of trap instruction */
		va = (caddr_t)trapframe->pc;
		if (trapframe->cause & CR_BR_DELAY)
			va += 4;
		/* read break instruction */
		copyin(va, &instr, sizeof(int32_t));

		if (trapframe->cause & CR_BR_DELAY)
			locr0->pc = MipsEmulateBranch(locr0,
			    trapframe->pc, 0, 0);
		else
			locr0->pc += 4;
#ifdef RM7K_PERFCNTR
		if (instr == 0x040c0000) { /* Performance cntr trap */
			int result;

			result = rm7k_perfcntr(trapframe->a0, trapframe->a1,
						trapframe->a2, trapframe->a3);
			locr0->v0 = -result;
			/* Return to user, don't add any more overhead */
			return;
		} else
#endif
		/*
		 * GCC 4 uses teq with code 7 to signal divide by
	 	 * zero at runtime. This is one instruction shorter
		 * than the BEQ + BREAK combination used by gcc 3.
		 */
		if ((instr & 0xfc00003f) == 0x00000034 /* teq */ &&
		    (instr & 0x001fffc0) == ((ZERO << 16) | (7 << 6))) {
			i = SIGFPE;
			typ = FPE_INTDIV;
		} else {
			i = SIGEMT;	/* Stuff it with something for now */
			typ = 0;
		}
		break;
	    }

	case T_RES_INST+T_USER:
		i = SIGILL;
		typ = ILL_ILLOPC;
		break;

	case T_COP_UNUSABLE+T_USER:
		/*
		 * Note MIPS IV COP1X instructions issued with FPU
		 * disabled correctly report coprocessor 1 as the
		 * unusable coprocessor number.
		 */
		if ((trapframe->cause & CR_COP_ERR) != CR_COP1_ERR) {
			i = SIGILL;	/* only FPU instructions allowed */
			typ = ILL_ILLOPC;
			break;
		}
#ifdef FPUEMUL
		MipsFPTrap(trapframe);
#else
		enable_fpu(p);
#endif
		return;

	case T_FPE:
		printf("FPU Trap: PC %lx CR %lx SR %lx\n",
			trapframe->pc, trapframe->cause, trapframe->sr);
		goto err;

	case T_FPE+T_USER:
		MipsFPTrap(trapframe);
		return;

	case T_OVFLOW+T_USER:
		i = SIGFPE;
		typ = FPE_FLTOVF;
		break;

	case T_ADDR_ERR_LD:	/* misaligned access */
	case T_ADDR_ERR_ST:	/* misaligned access */
	case T_BUS_ERR_LD_ST:	/* BERR asserted to cpu */
		pcb = &p->p_addr->u_pcb;
		if ((onfault = pcb->pcb_onfault) != 0) {
			pcb->pcb_onfault = 0;
			trapframe->pc = onfault_table[onfault];
			return;
		}
		goto err;

#ifdef CPU_R10000
	case T_BUS_ERR_IFETCH:
		/*
		 * At least R16000 processor have been found triggering
		 * reproduceable bus error on instruction fetch in the
		 * kernel code, which are trivially recoverable (and
		 * look like an obscure errata to me).
		 *
		 * Thus, ignore these exceptions if the faulting address
		 * is in the kernel.
		 */
	    {
		extern void *kernel_text;
		extern void *etext;
		vaddr_t va;

		va = (vaddr_t)trapframe->pc;
		if (trapframe->cause & CR_BR_DELAY)
			va += 4;
		if (va > (vaddr_t)&kernel_text && va < (vaddr_t)&etext)
			return;
	    }
		goto err;
#endif

	default:
	err:
		disableintr();
#if !defined(DDB) && defined(DEBUG)
		trapDump("trap", printf);
#endif
		printf("\nTrap cause = %d Frame %p\n", type, trapframe);
		printf("Trap PC %p RA %p fault %p\n",
		    (void *)trapframe->pc, (void *)trapframe->ra,
		    (void *)trapframe->badvaddr);
#ifdef DDB
		stacktrace(!USERMODE(trapframe->sr) ? trapframe : p->p_md.md_regs);
		kdb_trap(type, trapframe);
#endif
		panic("trap");
	}

#ifdef FPUEMUL
	/*
	 * If a relocated delay slot causes an exception, blame the
	 * original delay slot address - userland is not supposed to
	 * know anything about emulation bowels.
	 */
	if ((p->p_md.md_flags & MDP_FPUSED) != 0 &&
	    trapframe->badvaddr == p->p_md.md_fppgva)
		trapframe->badvaddr = p->p_md.md_fpslotva;
#endif
	p->p_md.md_regs->pc = trapframe->pc;
	p->p_md.md_regs->cause = trapframe->cause;
	p->p_md.md_regs->badvaddr = trapframe->badvaddr;
	sv.sival_ptr = (void *)trapframe->badvaddr;
	KERNEL_LOCK();
	trapsignal(p, i, ucode, typ, sv);
	KERNEL_UNLOCK();
}

void
child_return(void *arg)
{
	struct proc *p = arg;
	struct trap_frame *trapframe;

	trapframe = p->p_md.md_regs;
	trapframe->v0 = 0;
	trapframe->v1 = 1;
	trapframe->a3 = 0;

	KERNEL_UNLOCK();

	mi_child_return(p);
}

#if defined(DDB) || defined(DEBUG)
void
trapDump(const char *msg, int (*pr)(const char *, ...))
{
#ifdef MULTIPROCESSOR
	CPU_INFO_ITERATOR cii;
#endif
	struct cpu_info *ci;
	struct trapdebug *base, *ptrp;
	int i;
	uint pos;
	int s;

	s = splhigh();
	(*pr)("trapDump(%s)\n", msg);
#ifndef MULTIPROCESSOR
	ci = curcpu();
#else
	CPU_INFO_FOREACH(cii, ci)
#endif
	{
#ifdef MULTIPROCESSOR
		(*pr)("cpu%d\n", ci->ci_cpuid);
#endif
		/* walk in reverse order */
		pos = trppos[ci->ci_cpuid];
		base = trapdebug + ci->ci_cpuid * TRAPSIZE;
		for (i = TRAPSIZE - 1; i >= 0; i--) {
			if (pos + i >= TRAPSIZE)
				ptrp = base + pos + i - TRAPSIZE;
			else
				ptrp = base + pos + i;

			if (ptrp->cause == 0)
				break;

#ifdef CPU_R8000
			(*pr)("%s: PC %p CR 0x%016lx SR 0x%011lx\n",
			    trap_type[(ptrp->cause & CR_EXC_CODE) >>
			      CR_EXC_CODE_SHIFT],
			    ptrp->pc, ptrp->cause, ptrp->status);
#else
			(*pr)("%s: PC %p CR 0x%08lx SR 0x%08lx\n",
			    trap_type[(ptrp->cause & CR_EXC_CODE) >>
			      CR_EXC_CODE_SHIFT],
			    ptrp->pc, ptrp->cause & 0xffffffff,
			    ptrp->status & 0xffffffff);
#endif
			(*pr)(" RA %p SP %p ADR %p\n",
			    ptrp->ra, ptrp->sp, ptrp->vadr);
		}
	}

	splx(s);
}
#endif

/*
 * Return the resulting PC as if the branch was executed.
 */
register_t
MipsEmulateBranch(struct trap_frame *tf, vaddr_t instPC, uint32_t fsr,
    uint32_t curinst)
{
	register_t *regsPtr = (register_t *)tf;
	InstFmt inst;
	vaddr_t retAddr;
	int condition;
	uint cc;

#define	GetBranchDest(InstPtr, inst) \
	    (InstPtr + 4 + ((short)inst.IType.imm << 2))

	if (curinst != 0)
		inst = *(InstFmt *)&curinst;
	else
		inst = *(InstFmt *)instPC;

	regsPtr[ZERO] = 0;	/* Make sure zero is 0x0 */

	switch ((int)inst.JType.op) {
	case OP_SPECIAL:
		switch ((int)inst.RType.func) {
		case OP_JR:
		case OP_JALR:
			retAddr = (vaddr_t)regsPtr[inst.RType.rs];
			break;
		default:
			retAddr = instPC + 4;
			break;
		}
		break;
	case OP_BCOND:
		switch ((int)inst.IType.rt) {
		case OP_BLTZ:
		case OP_BLTZL:
		case OP_BLTZAL:
		case OP_BLTZALL:
			if ((int64_t)(regsPtr[inst.RType.rs]) < 0)
				retAddr = GetBranchDest(instPC, inst);
			else
				retAddr = instPC + 8;
			break;
		case OP_BGEZ:
		case OP_BGEZL:
		case OP_BGEZAL:
		case OP_BGEZALL:
			if ((int64_t)(regsPtr[inst.RType.rs]) >= 0)
				retAddr = GetBranchDest(instPC, inst);
			else
				retAddr = instPC + 8;
			break;
		default:
			retAddr = instPC + 4;
			break;
		}
		break;
	case OP_J:
	case OP_JAL:
		retAddr = (inst.JType.target << 2) | (instPC & ~0x0fffffffUL);
		break;
	case OP_BEQ:
	case OP_BEQL:
		if (regsPtr[inst.RType.rs] == regsPtr[inst.RType.rt])
			retAddr = GetBranchDest(instPC, inst);
		else
			retAddr = instPC + 8;
		break;
	case OP_BNE:
	case OP_BNEL:
		if (regsPtr[inst.RType.rs] != regsPtr[inst.RType.rt])
			retAddr = GetBranchDest(instPC, inst);
		else
			retAddr = instPC + 8;
		break;
	case OP_BLEZ:
	case OP_BLEZL:
		if ((int64_t)(regsPtr[inst.RType.rs]) <= 0)
			retAddr = GetBranchDest(instPC, inst);
		else
			retAddr = instPC + 8;
		break;
	case OP_BGTZ:
	case OP_BGTZL:
		if ((int64_t)(regsPtr[inst.RType.rs]) > 0)
			retAddr = GetBranchDest(instPC, inst);
		else
			retAddr = instPC + 8;
		break;
	case OP_COP1:
		switch (inst.RType.rs) {
		case OP_BC:
			cc = (inst.RType.rt & COPz_BC_CC_MASK) >>
			    COPz_BC_CC_SHIFT;
			if ((inst.RType.rt & COPz_BC_TF_MASK) == COPz_BC_TRUE)
				condition = fsr & FPCSR_CONDVAL(cc);
			else
				condition = !(fsr & FPCSR_CONDVAL(cc));
			if (condition)
				retAddr = GetBranchDest(instPC, inst);
			else
				retAddr = instPC + 8;
			break;
		default:
			retAddr = instPC + 4;
		}
		break;
	default:
		retAddr = instPC + 4;
	}

	return (register_t)retAddr;
#undef	GetBranchDest
}

#ifdef PTRACE

int
ptrace_read_insn(struct proc *p, vaddr_t va, uint32_t *insn)
{
	struct iovec iov;
	struct uio uio;

	iov.iov_base = (caddr_t)insn;
	iov.iov_len = sizeof(uint32_t);
	uio.uio_iov = &iov;
	uio.uio_iovcnt = 1;
	uio.uio_offset = (off_t)va;
	uio.uio_resid = sizeof(uint32_t);
	uio.uio_segflg = UIO_SYSSPACE;
	uio.uio_rw = UIO_READ;
	uio.uio_procp = p;
	return process_domem(p, p, &uio, PT_READ_I);
}

int
ptrace_write_insn(struct proc *p, vaddr_t va, uint32_t insn)
{
	struct iovec iov;
	struct uio uio;

	iov.iov_base = (caddr_t)&insn;
	iov.iov_len = sizeof(uint32_t);
	uio.uio_iov = &iov;
	uio.uio_iovcnt = 1;
	uio.uio_offset = (off_t)va;
	uio.uio_resid = sizeof(uint32_t);
	uio.uio_segflg = UIO_SYSSPACE;
	uio.uio_rw = UIO_WRITE;
	uio.uio_procp = p;
	return process_domem(p, p, &uio, PT_WRITE_I);
}

/*
 * This routine is called by procxmt() to single step one instruction.
 * We do this by storing a break instruction after the current instruction,
 * resuming execution, and then restoring the old instruction.
 */
int
process_sstep(struct proc *p, int sstep)
{
	struct trap_frame *locr0 = p->p_md.md_regs;
	int rc;
	uint32_t curinstr;
	vaddr_t va;

	if (sstep == 0) {
		/* clear the breakpoint */
		if (p->p_md.md_ss_addr != 0) {
			rc = ptrace_write_insn(p, p->p_md.md_ss_addr,
			    p->p_md.md_ss_instr);
#ifdef DIAGNOSTIC
			if (rc != 0)
				printf("WARNING: %s (%d): can't restore "
				    "instruction at %p: %08x\n",
				    p->p_comm, p->p_pid,
				    (void *)p->p_md.md_ss_addr,
				    p->p_md.md_ss_instr);
#endif
			p->p_md.md_ss_addr = 0;
		} else
			rc = 0;
		return rc;
	}

	/* read current instruction */
	rc = ptrace_read_insn(p, locr0->pc, &curinstr);
	if (rc != 0)
		return rc;

	/* compute next address after current location */
	if (curinstr != 0 /* nop */)
		va = (vaddr_t)MipsEmulateBranch(locr0,
		    locr0->pc, locr0->fsr, curinstr);
	else
		va = locr0->pc + 4;
#ifdef DIAGNOSTIC
	/* should not happen */
	if (p->p_md.md_ss_addr != 0) {
		printf("WARNING: %s (%d): breakpoint request "
		    "at %p, already set at %p\n",
		    p->p_comm, p->p_pid, (void *)va, (void *)p->p_md.md_ss_addr);
		return EFAULT;
	}
#endif

	/* read next instruction */
	rc = ptrace_read_insn(p, va, &p->p_md.md_ss_instr);
	if (rc != 0)
		return rc;

	/* replace with a breakpoint instruction */
	rc = ptrace_write_insn(p, va, BREAK_SSTEP);
	if (rc != 0)
		return rc;

	p->p_md.md_ss_addr = va;

#ifdef DEBUG
	printf("%s (%d): breakpoint set at %p: %08x (pc %p %08x)\n",
		p->p_comm, p->p_pid, (void *)p->p_md.md_ss_addr,
		p->p_md.md_ss_instr, (void *)locr0->pc, curinstr);
#endif
	return 0;
}

#endif /* PTRACE */

#if defined(DDB) || defined(DEBUG)
#define MIPS_JR_RA	0x03e00008	/* instruction code for jr ra */

/* forward */
#if !defined(DDB)
const char *fn_name(vaddr_t);
#endif
void stacktrace_subr(struct trap_frame *, int, int (*)(const char*, ...));

/*
 * Print a stack backtrace.
 */
void
stacktrace(struct trap_frame *regs)
{
	stacktrace_subr(regs, 6, printf);
}

#ifdef CPU_R8000
#define	VALID_ADDRESS(va) \
	(((va) >= VM_MIN_KERNEL_ADDRESS && (va) < VM_MAX_KERNEL_ADDRESS) || \
	 IS_XKPHYS(va))
#else
#define	VALID_ADDRESS(va) \
	(((va) >= VM_MIN_KERNEL_ADDRESS && (va) < VM_MAX_KERNEL_ADDRESS) || \
	 IS_XKPHYS(va) || ((va) >= CKSEG0_BASE && (va) < CKSEG1_BASE))
#endif

void
stacktrace_subr(struct trap_frame *regs, int count,
    int (*pr)(const char*, ...))
{
	vaddr_t pc, sp, ra, va, subr;
	register_t a0, a1, a2, a3;
	uint32_t instr, mask;
	InstFmt i;
	int more, stksize;
	extern char k_intr[];
	extern char k_general[];
#ifdef DDB
	db_expr_t diff;
	db_sym_t sym;
	char *symname;
#endif

	/* get initial values from the exception frame */
	sp = (vaddr_t)regs->sp;
	pc = (vaddr_t)regs->pc;
	ra = (vaddr_t)regs->ra;		/* May be a 'leaf' function */
	a0 = regs->a0;
	a1 = regs->a1;
	a2 = regs->a2;
	a3 = regs->a3;

/* Jump here when done with a frame, to start a new one */
loop:
#ifdef DDB
	symname = NULL;
#endif
	subr = 0;
	stksize = 0;

	if (count-- == 0) {
		ra = 0;
		goto end;
	}

	/* check for bad SP: could foul up next frame */
	if (sp & 3 || !VALID_ADDRESS(sp)) {
		(*pr)("SP %p: not in kernel\n", sp);
		ra = 0;
		goto end;
	}

	/* check for bad PC */
	if (pc & 3 || !VALID_ADDRESS(pc)) {
		(*pr)("PC %p: not in kernel\n", pc);
		ra = 0;
		goto end;
	}

#ifdef DDB
	/*
	 * Dig out the function from the symbol table.
	 * Watch out for function tail optimizations.
	 */
	sym = db_search_symbol(pc, DB_STGY_ANY, &diff);
	if (sym != DB_SYM_NULL && diff == 0) {
		instr = kdbpeek(pc - 2 * sizeof(int));
		i.word = instr;
		if (i.JType.op == OP_JAL) {
			sym = db_search_symbol(pc - sizeof(int),
			    DB_STGY_ANY, &diff);
			if (sym != DB_SYM_NULL && diff != 0)
				diff += sizeof(int);
		}
	}
	if (sym != DB_SYM_NULL) {
		db_symbol_values(sym, &symname, 0);
		subr = pc - (vaddr_t)diff;
	}
#endif

	/*
	 * Find the beginning of the current subroutine by scanning backwards
	 * from the current PC for the end of the previous subroutine.
	 */
	if (!subr) {
		va = pc - sizeof(int);
		while ((instr = kdbpeek(va)) != MIPS_JR_RA)
			va -= sizeof(int);
		va += 2 * sizeof(int);	/* skip back over branch & delay slot */
		/* skip over nulls which might separate .o files */
		while ((instr = kdbpeek(va)) == 0)
			va += sizeof(int);
		subr = va;
	}

	/*
	 * Jump here for locore entry points for which the preceding
	 * function doesn't end in "j ra"
	 */
	/* scan forwards to find stack size and any saved registers */
	stksize = 0;
	more = 3;
	mask = 0;
	for (va = subr; more; va += sizeof(int),
	    more = (more == 3) ? 3 : more - 1) {
		/* stop if hit our current position */
		if (va >= pc)
			break;
		instr = kdbpeek(va);
		i.word = instr;
		switch (i.JType.op) {
		case OP_SPECIAL:
			switch (i.RType.func) {
			case OP_JR:
			case OP_JALR:
				more = 2; /* stop after next instruction */
				break;

			case OP_SYSCALL:
			case OP_BREAK:
				more = 1; /* stop now */
			};
			break;

		case OP_BCOND:
		case OP_J:
		case OP_JAL:
		case OP_BEQ:
		case OP_BNE:
		case OP_BLEZ:
		case OP_BGTZ:
			more = 2; /* stop after next instruction */
			break;

		case OP_COP0:
		case OP_COP1:
		case OP_COP2:
		case OP_COP3:
			switch (i.RType.rs) {
			case OP_BC:
				more = 2; /* stop after next instruction */
			};
			break;

		case OP_SD:
			/* look for saved registers on the stack */
			if (i.IType.rs != SP)
				break;
			/* only restore the first one */
			if (mask & (1 << i.IType.rt))
				break;
			mask |= (1 << i.IType.rt);
			switch (i.IType.rt) {
			case A0:
				a0 = kdbpeekd(sp + (int16_t)i.IType.imm);
				break;
			case A1:
				a1 = kdbpeekd(sp + (int16_t)i.IType.imm);
				break;
			case A2:
				a2 = kdbpeekd(sp + (int16_t)i.IType.imm);
				break;
			case A3:
				a3 = kdbpeekd(sp + (int16_t)i.IType.imm);
				break;
			case RA:
				ra = kdbpeekd(sp + (int16_t)i.IType.imm);
				break;
			}
			break;

		case OP_DADDI:
		case OP_DADDIU:
			/* look for stack pointer adjustment */
			if (i.IType.rs != SP || i.IType.rt != SP)
				break;
			stksize = -((int16_t)i.IType.imm);
		}
	}

#ifdef DDB
	if (symname == NULL)
		(*pr)("%p ", subr);
	else
		(*pr)("%s+%p ", symname, diff);
#else
	(*pr)("%s+%p ", fn_name(subr), pc - subr);
#endif
	(*pr)("(%llx,%llx,%llx,%llx) ", a0, a1, a2, a3);
	(*pr)(" ra %p sp %p, sz %d\n", ra, sp, stksize);

	if (subr == (vaddr_t)k_intr || subr == (vaddr_t)k_general) {
		if (subr == (vaddr_t)k_general)
			(*pr)("(KERNEL TRAP)\n");
		else
			(*pr)("(KERNEL INTERRUPT)\n");
		sp = *(register_t *)sp;
		pc = ((struct trap_frame *)sp)->pc;
		ra = ((struct trap_frame *)sp)->ra;
		sp = ((struct trap_frame *)sp)->sp;
		goto loop;
	}

end:
	if (ra) {
		extern void *kernel_text;
		extern void *etext;

		if (pc == ra && stksize == 0)
			(*pr)("stacktrace: loop!\n");
		else if (ra < (vaddr_t)&kernel_text || ra > (vaddr_t)&etext)
			(*pr)("stacktrace: ra corrupted!\n");
		else {
			pc = ra;
			sp += stksize;
			ra = 0;
			goto loop;
		}
	} else {
		if (curproc)
			(*pr)("User-level: pid %d\n", curproc->p_pid);
		else
			(*pr)("User-level: curproc NULL\n");
	}
}

#undef	VALID_ADDRESS

#if !defined(DDB)
/*
 * Functions ``special'' enough to print by name
 */
#ifdef __STDC__
#define Name(_fn)  { (void*)_fn, # _fn }
#else
#define Name(_fn) { _fn, "_fn"}
#endif
static const struct { void *addr; const char *name;} names[] = {
	Name(trap),
	{ 0, NULL }
};

/*
 * Map a function address to a string name, if known; or a hex string.
 */
const char *
fn_name(vaddr_t addr)
{
	static char buf[19];
	int i = 0;

	for (i = 0; names[i].name != NULL; i++)
		if (names[i].addr == (void*)addr)
			return (names[i].name);
	snprintf(buf, sizeof(buf), "%p", addr);
	return (buf);
}
#endif	/* !DDB */

#endif /* DDB || DEBUG */

#ifdef FPUEMUL
/*
 * Set up a successful branch emulation.
 * The delay slot instruction is copied to a reserved page, followed by a
 * trap instruction to get control back, and resume at the branch
 * destination.
 */
int
fpe_branch_emulate(struct proc *p, struct trap_frame *tf, uint32_t insn,
    vaddr_t dest)
{
	struct vm_map *map = &p->p_vmspace->vm_map;
	InstFmt inst;
	int rc;

	/*
	 * Check the delay slot instruction: since it will run as a
	 * non-delay slot instruction, we want to reject branch instructions
	 * (which behaviour, when in a delay slot, is undefined anyway).
	 */

	inst = *(InstFmt *)&insn;
	rc = 0;
	switch ((int)inst.JType.op) {
	case OP_SPECIAL:
		switch ((int)inst.RType.func) {
		case OP_JR:
		case OP_JALR:
			rc = EINVAL;
			break;
		}
		break;
	case OP_BCOND:
		switch ((int)inst.IType.rt) {
		case OP_BLTZ:
		case OP_BLTZL:
		case OP_BLTZAL:
		case OP_BLTZALL:
		case OP_BGEZ:
		case OP_BGEZL:
		case OP_BGEZAL:
		case OP_BGEZALL:
			rc = EINVAL;
			break;
		}
		break;
	case OP_J:
	case OP_JAL:
	case OP_BEQ:
	case OP_BEQL:
	case OP_BNE:
	case OP_BNEL:
	case OP_BLEZ:
	case OP_BLEZL:
	case OP_BGTZ:
	case OP_BGTZL:
		rc = EINVAL;
		break;
	case OP_COP1:
		if (inst.RType.rs == OP_BC)	/* oh the irony */
			rc = EINVAL;
		break;
	}

	if (rc != 0) {
#ifdef DEBUG
		printf("%s: bogus delay slot insn %08x\n", __func__, insn);
#endif
		return rc;
	}

	/*
	 * Temporarily change protection over the page used to relocate
	 * the delay slot, and fault it in.
	 */

	rc = uvm_map_protect(map, p->p_md.md_fppgva,
	    p->p_md.md_fppgva + PAGE_SIZE, PROT_MASK, FALSE);
	if (rc != 0) {
#ifdef DEBUG
		printf("%s: uvm_map_protect on %p failed: %d\n",
		    __func__, (void *)p->p_md.md_fppgva, rc);
#endif
		return rc;
	}
	KERNEL_LOCK();
	rc = uvm_fault_wire(map, p->p_md.md_fppgva,
	    p->p_md.md_fppgva + PAGE_SIZE, PROT_MASK);
	KERNEL_UNLOCK();
	if (rc != 0) {
#ifdef DEBUG
		printf("%s: uvm_fault_wire on %p failed: %d\n",
		    __func__, (void *)p->p_md.md_fppgva, rc);
#endif
		goto err2;
	}

	rc = copyout(&insn, (void *)p->p_md.md_fppgva, sizeof insn);
	if (rc != 0) {
#ifdef DEBUG
		printf("%s: copyout %p failed %d\n",
		    __func__, (void *)p->p_md.md_fppgva, rc);
#endif
		goto err;
	}
	insn = BREAK_FPUEMUL;
	rc = copyout(&insn, (void *)(p->p_md.md_fppgva + 4), sizeof insn);
	if (rc != 0) {
#ifdef DEBUG
		printf("%s: copyout %p failed %d\n",
		    __func__, (void *)(p->p_md.md_fppgva + 4), rc);
#endif
		goto err;
	}

	(void)uvm_map_protect(map, p->p_md.md_fppgva,
	    p->p_md.md_fppgva + PAGE_SIZE, PROT_READ | PROT_EXEC, FALSE);
	p->p_md.md_fpbranchva = dest;
	p->p_md.md_fpslotva = (vaddr_t)tf->pc + 4;
	p->p_md.md_flags |= MDP_FPUSED;
	tf->pc = p->p_md.md_fppgva;
	pmap_proc_iflush(p, tf->pc, 2 * 4);

	return 0;

err:
	uvm_fault_unwire(map, p->p_md.md_fppgva, p->p_md.md_fppgva + PAGE_SIZE);
err2:
	(void)uvm_map_protect(map, p->p_md.md_fppgva,
	    p->p_md.md_fppgva + PAGE_SIZE, PROT_NONE, FALSE);
	return rc;
}
#endif

license: unknown


code:
/*	$OpenBSD: vm_machdep.c,v 1.32 2015/05/05 02:13:47 guenther Exp $	*/
/*
 * Copyright (c) 1988 University of Utah.
 * Copyright (c) 1992, 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 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.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 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.
 *
 * from: Utah Hdr: vm_machdep.c 1.21 91/04/06
 *
 *	from: @(#)vm_machdep.c	8.3 (Berkeley) 1/4/94
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/malloc.h>
#include <sys/buf.h>
#include <sys/vnode.h>
#include <sys/user.h>
#include <sys/exec.h>
#include <sys/signalvar.h>

#include <uvm/uvm_extern.h>

#include <machine/cpu.h>
#include <mips64/mips_cpu.h>

extern void proc_trampoline(void);
/*
 * Finish a fork operation, with process p2 nearly set up.
 */
void
cpu_fork(p1, p2, stack, stacksize, func, arg)
	struct proc *p1, *p2;
	void *stack;
	size_t stacksize;
	void (*func)(void *);
	void *arg;
{
	struct cpu_info *ci = curcpu();
	struct pcb *pcb;
#if UPAGES == 1
	paddr_t pa;

	/* replace p_addr with a direct translation address */
	p2->p_md.md_uarea = (vaddr_t)p2->p_addr;
	pmap_extract(pmap_kernel(), p2->p_md.md_uarea, &pa);
#ifdef __sgi__
#ifndef CPU_R8000
	/*
	 * Return a CKSEG0 address whenever possible.
	 */
	if (pa < CKSEG_SIZE)
		p2->p_addr = (void *)PHYS_TO_CKSEG0(pa);
	else
#endif
		p2->p_addr = (void *)PHYS_TO_XKPHYS(pa, CCA_CACHED);
#else
	p2->p_addr = (void *)PHYS_TO_XKPHYS(pa, CCA_CACHED);
#endif
#endif
	pcb = &p2->p_addr->u_pcb;

	/*
	 * If we own the FPU, save its state before copying the PCB.
	 */
	if (p1 == ci->ci_fpuproc)
		save_fpu();

	p2->p_md.md_flags = p1->p_md.md_flags & MDP_FORKSAVE;
#ifdef FPUEMUL
	p2->p_md.md_fppgva = p1->p_md.md_fppgva;
	KASSERT((p2->p_md.md_flags & MDP_FPUSED) == 0);
#endif

	/* Copy pcb from p1 to p2 */
	if (p1 == curproc) {
		/* Sync the PCB before we copy it. */
		savectx(p1->p_addr, 0);
	}
#ifdef DIAGNOSTIC
	else if (p1 != &proc0)
		panic("cpu_fork: curproc");
#endif
	*pcb = p1->p_addr->u_pcb;
	p2->p_md.md_regs = &p2->p_addr->u_pcb.pcb_regs;

	/*
	 * If specified, give the child a different stack.
	 */
	if (stack != NULL)
		p2->p_md.md_regs->sp = (u_int64_t)stack + stacksize;

	/*
	 * Copy the process control block to the new proc and
	 * create a clean stack for exit through trampoline.
	 * pcb_context has s0-s7, sp, s8, ra, sr, icr.
	 */
	if (p1 != curproc) {
#ifdef RM7000_ICR
		pcb->pcb_context.val[12] = (idle_mask << 8) & IC_INT_MASK;
#endif
		pcb->pcb_context.val[11] = (pcb->pcb_regs.sr & ~SR_INT_MASK) |
		    (idle_mask & SR_INT_MASK);
	}
	pcb->pcb_context.val[10] = (register_t)proc_trampoline;
	pcb->pcb_context.val[8] = (register_t)pcb +
	    USPACE - sizeof(struct trap_frame);
	pcb->pcb_context.val[1] = (register_t)arg;
	pcb->pcb_context.val[0] = (register_t)func;
}

/*
 * cpu_exit is called as the last action during exit.
 */
void
cpu_exit(p)
	struct proc *p;
{
	struct cpu_info *ci = curcpu();

	if (ci->ci_fpuproc == p)
		ci->ci_fpuproc = NULL;

	pmap_deactivate(p);
#if UPAGES == 1
	/* restore p_addr for proper deallocation */
	p->p_addr = (void *)p->p_md.md_uarea;
#endif
	sched_exit(p);
}

extern vm_map_t phys_map;

/*
 * Map an user IO request into kernel virtual address space.
 */

void
vmapbuf(bp, len)
	struct buf *bp;
	vsize_t len;
{
	vaddr_t uva, kva;
	vsize_t sz, off;
	paddr_t pa;
	struct pmap *pmap;

	if ((bp->b_flags & B_PHYS) == 0) {
		panic("vmapbuf");
	}

	pmap = vm_map_pmap(&bp->b_proc->p_vmspace->vm_map);
	bp->b_saveaddr = bp->b_data;
	uva = trunc_page((vaddr_t)bp->b_saveaddr);
	off = (vaddr_t)bp->b_saveaddr - uva;
	sz = round_page(off + len);

	kva = uvm_km_valloc_prefer_wait(phys_map, sz, uva);
	bp->b_data = (caddr_t) (kva + off);

	while (sz > 0) {
		if (pmap_extract(pmap, uva, &pa) == FALSE)
			panic("vmapbuf: pmap_extract(%p, %lx) failed!",
			    pmap, uva);

		pmap_enter(vm_map_pmap(phys_map), kva, trunc_page(pa),
		    PROT_READ | PROT_WRITE,
		    PROT_READ | PROT_WRITE | PMAP_WIRED);
		uva += PAGE_SIZE;
		kva += PAGE_SIZE;
		sz -= PAGE_SIZE;
	}
	pmap_update(vm_map_pmap(phys_map));
}

/*
 * Free the io map PTEs associated with this IO operation.
 * We also invalidate the TLB entries and restore the original b_addr.
 */
void
vunmapbuf(bp, len)
	struct buf *bp;
	vsize_t len;
{
	vsize_t sz;
	vaddr_t addr;

	if ((bp->b_flags & B_PHYS) == 0) {
		panic("vunmapbuf");
	}
	addr = trunc_page((vaddr_t)bp->b_data);
	sz = round_page(len + ((vaddr_t)bp->b_data - addr));
	pmap_remove(vm_map_pmap(phys_map), addr, addr + sz);
	pmap_update(vm_map_pmap(phys_map));
	uvm_km_free_wakeup(phys_map, addr, sz);
	bp->b_data = bp->b_saveaddr;
	bp->b_saveaddr = NULL;
}


code:
/Makefile/1.5/Sun Dec  8 14:46:39 2013//TOPENBSD_5_8_BASE
D/compile////
D/conf////
D/dev////
D/include////
D/octeon////
D/stand////

path: sys/arch/octeon/CVS/Entries


code: src/sys/arch/octeon

path: sys/arch/octeon/CVS/Repository


code:
#	$OpenBSD: Makefile,v 1.5 2013/12/08 14:46:39 espie Exp $

S=	${.CURDIR}/../..
KFILE=	GENERIC
.if exists(conf/GENERIC.MP)
KFILE=	GENERIC.MP
.endif
TDIRS=	${_arch} include
TAGS=	${.CURDIR}/tags

NOPROG=
NOMAN=
NOOBJ=
SUBDIR=

# config the fattest kernel we can find into a temporary dir
# to create a Makefile.  Then use make to pull some variables
# out and push them into the sub-shell to expand the paths,
# and finally run ctags.
tags::
	TDIR=`mktemp -d /tmp/_tagXXXXXXXXXX` || exit 1; \
	eval "S=${S}" && \
	config -s ${S} -b $${TDIR} ${.CURDIR}/conf/${KFILE} && \
	eval "_arch=\"`make -V _arch -f $${TDIR}/Makefile`\"" && \
	eval "_mach=\"`make -V _mach -f $${TDIR}/Makefile`\"" && \
	eval "_machdir=\$S/arch/$${_mach}" && \
	eval "_archdir=\$S/arch/$${_arch}" && \
	eval "HFILES=\"`find $S \( -path $S/'arch' -o -path $S/stand -o -path $S/lib/libsa -o -path $S'/lib/libkern/arch' \) -prune -o -name '*.h'; find $${_machdir} $${_archdir} $S/lib/libkern/arch/$${_mach} \( -name boot -o -name stand \) -prune -o -name '*.h'`\"" && \
	eval "SFILES=\"`make -V SFILES -f $${TDIR}/Makefile`\"" && \
	eval "CFILES=\"`make -V CFILES -f $${TDIR}/Makefile`\"" && \
	eval "AFILES=\"`make -V AFILES -f $${TDIR}/Makefile`\"" && \
	ctags -wd -f ${TAGS} $${CFILES} $${HFILES} && \
	egrep "^[_A-Z]*ENTRY[_A-Z]*\(.*\)" $${SFILES} $${AFILES} | \
	    sed "s;\\([^:]*\\):\\([^(]*\\)(\\([^, )]*\\)\\(.*\\);\\3	\\1	/^\\2(\\3\\4$$/;" \
	    >> ${TAGS} && \
	sort -o ${TAGS} ${TAGS} && \
	rm -rf $${TDIR}

links:
	-for i in conf ${TDIRS}; do \
	    (cd $$i && rm -f tags; ln -s tags tags); done

obj:	_SUBDIRUSE

.include <bsd.prog.mk>

path: sys/arch/octeon/Makefile


code:
/.cvsignore/1.2/Sat Jun  1 17:09:41 2013//TOPENBSD_5_8_BASE
D

path: sys/arch/octeon/compile/CVS/Entries


code: src/sys/arch/octeon/compile

path: sys/arch/octeon/compile/CVS/Repository


code: src/sys/arch/octeon/conf

path: sys/arch/octeon/conf/CVS/Repository


code:
#	$OpenBSD: GENERIC,v 1.22 2015/07/20 19:44:32 pirofti Exp $
#
# For further information on compiling OpenBSD kernels, see the config(8)
# man page.
#
# For further information on hardware support for this architecture, see
# the intro(4) man page.  For further information about kernel options
# for this architecture, see the options(4) man page.  For an explanation
# of each device driver in this file see the section 4 man page for the
# device.

machine		octeon mips64
include		"../../../conf/GENERIC"
maxusers	32

option		CPU_OCTEON
option		FPUEMUL
makeoption	LINK_ADDRESS="0xffffffff81000000"

option		PCIVERBOSE
option		USER_PCICONF
option		USBVERBOSE

config	bsd	swap generic

#
# Definition of system
#
mainbus0	at root
cpu0		at mainbus0
clock0		at mainbus0
iobus0		at mainbus0
uartbus0	at mainbus0

octrtc0		at mainbus0

octcf0		at iobus0
amdcf0		at iobus0
octrng0		at iobus0

com0		at uartbus0
com1		at uartbus0

pcibus*		at iobus0
pci*		at pcibus?

ppb*		at pci?		# PCI-PCI bridges
pci*		at ppb?

cn30xxgmx*	at iobus? irq 48
cnmac*		at cn30xxgmx?

rgephy*		at mii?
ukphy*		at mii?
atphy*		at mii?
brswphy*	at mii?

# IDE Controller
pciide*		at pci? flags 0x0000

# IDE hard drives
wd*		at pciide? flags 0x0000

# USB Controllers
dwctwo0 	at iobus? irq 56

# USB bus support
usb*		at dwctwo?

# USB devices
uhub*		at usb?		# USB Hubs
umass*		at uhub?

scsibus*	at scsi?
sd*		at scsibus?

path: sys/arch/octeon/conf/GENERIC


code:
#	$OpenBSD: GENERIC.MP,v 1.1 2013/06/01 17:09:41 jasper Exp $

include "arch/octeon/conf/GENERIC"

option MULTIPROCESSOR
#option MP_LOCKDEBUG

cpu*   at mainbus?


code:
#	$OpenBSD: Makefile.octeon,v 1.21 2015/01/13 01:12:50 deraadt Exp $

# For instructions on building kernels consult the config(8) and options(4)
# manual pages.
#
# N.B.: NO DEPENDENCIES ON FOLLOWING FLAGS ARE VISIBLE TO MAKEFILE
#	IF YOU CHANGE THE DEFINITION OF ANY OF THESE RECOMPILE EVERYTHING
# DEBUG is set to -g by config if debugging is requested (config -g).
# PROF is set to -pg by config if profiling is requested (config -p).

.include <bsd.own.mk>

SIZE?=	size
STRIP?=	strip
AS?=	as
CC?=	cc
LD?=	ld ${ENDIAN}

AS+=${ENDIAN}
CC+=${ENDIAN}
LD+=${ENDIAN}

# source tree is located via $S relative to the compilation directory
.ifndef S
S!=	cd ../../../..; pwd
.endif

_machdir?=	$S/arch/${_mach}
_archdir?=	$S/arch/${_arch}

INCLUDES=	-nostdinc -I$S -I. -I$S/arch
CPPFLAGS=	${INCLUDES} ${IDENT} ${PARAM} -D_KERNEL -D__${_mach}__ -MD -MP
CWARNFLAGS=	-Werror -Wall -Wimplicit-function-declaration \
		-Wno-main -Wno-uninitialized \
		-Wframe-larger-than=2047

CMACHFLAGS=	-mno-abicalls ${ABI} -msoft-float -G 0
CMACHFLAGS+=	-fno-builtin-printf -fno-builtin-snprintf \
		-fno-builtin-vsnprintf -fno-builtin-log \
		-fno-builtin-log2 -fno-builtin-malloc ${NOPIE_FLAGS}
.if ${IDENT:M-DNO_PROPOLICE}
CMACHFLAGS+=	-fno-stack-protector
.endif

COPTS?=		-O2
CFLAGS=		${DEBUG} ${CWARNFLAGS} ${CMACHFLAGS} ${COPTS} ${PIPE}
AFLAGS=		-D_LOCORE -x assembler-with-cpp ${CWARNFLAGS} ${CMACHFLAGS}
LDSCRIPT=	${_machdir}/conf/ld.script
LINKFLAGS=	-e start -T ${LDSCRIPT} -Ttext=${LINK_ADDRESS} -nopie
STRIPFLAGS=	-g -x

.if ${IDENT:M-DDDB_STRUCT}
DB_STRUCTINFO=	db_structinfo.h
.else
DB_STRUCTINFO=
.endif

HOSTCC?=	${CC}
HOSTED_CPPFLAGS=${CPPFLAGS:S/^-nostdinc$//}
HOSTED_CFLAGS=	${CFLAGS}
HOSTED_C=	${HOSTCC} ${HOSTED_CFLAGS} ${HOSTED_CPPFLAGS} -c $<

NORMAL_C_NOP=	${CC} ${CFLAGS} ${CPPFLAGS} -c $<
NORMAL_C=	${CC} ${CFLAGS} ${CPPFLAGS} ${PROF} -c $<
NORMAL_S=	${CC} ${AFLAGS} ${CPPFLAGS} -c $<

%OBJS

%CFILES

%SFILES

# load lines for config "xxx" will be emitted as:
# xxx: ${SYSTEM_DEP} swapxxx.o
#	${SYSTEM_LD_HEAD}
#	${SYSTEM_LD} swapxxx.o
#	${SYSTEM_LD_TAIL}
SYSTEM_HEAD=	locore.o param.o ioconf.o
SYSTEM_OBJ=	${SYSTEM_HEAD} ${OBJS}
SYSTEM_DEP=	Makefile ${SYSTEM_OBJ} ${LDSCRIPT}
SYSTEM_LD_HEAD=	@rm -f $@
SYSTEM_LD=	@echo ${LD} ${LINKFLAGS} -o $@ '$${SYSTEM_HEAD} vers.o $${OBJS}'; \
		${LD} ${LINKFLAGS} -o $@ ${SYSTEM_HEAD} vers.o ${OBJS}
SYSTEM_LD_TAIL=	@${SIZE} $@; chmod 755 $@

DEBUG?=
.if ${DEBUG} == "-g"
LINKFLAGS+=	-X
SYSTEM_LD_TAIL+=; \
		echo mv $@ $@.gdb; rm -f $@.gdb; mv $@ $@.gdb; \
		echo ${STRIP} ${STRIPFLAGS} -o $@ $@.gdb; \
		${STRIP} ${STRIPFLAGS} -o $@ $@.gdb
.else
LINKFLAGS+=	-x
.endif

%LOAD

# cc's -MD puts the source and output paths in the dependency file;
# since those are temp files here we need to fix it up.  It also
# puts the file in /tmp, so we use -MF to put it in the current
# directory as assym.P and then generate assym.d from it with a
# good target name
assym.h: $S/kern/genassym.sh Makefile \
	 ${_archdir}/${_arch}/genassym.cf ${_machdir}/${_mach}/genassym.cf
	cat ${_archdir}/${_arch}/genassym.cf ${_machdir}/${_mach}/genassym.cf | \
	    sh $S/kern/genassym.sh ${CC} ${CFLAGS} ${CPPFLAGS} -MF assym.P > assym.h.tmp
	sed '1s/.*/assym.h: \\/' assym.P > assym.d
	sort -u assym.h.tmp > assym.h

param.c: $S/conf/param.c
	rm -f param.c
	cp $S/conf/param.c .

param.o: param.c Makefile
	${NORMAL_C}

mcount.o: $S/lib/libkern/mcount.c Makefile
	${NORMAL_C_NOP}

ioconf.o: ioconf.c
	${NORMAL_C}

vers.o: ${SYSTEM_DEP} ${SYSTEM_SWAP_DEP}
	sh $S/conf/newvers.sh
	${CC} ${CFLAGS} ${CPPFLAGS} ${PROF} -c vers.c

clean::
	rm -f eddep *bsd *bsd.gdb tags *.[dio] [a-z]*.s \
	    [Ee]rrs linterrs assym.h ${DB_STRUCTINFO}

lint:
	@lint -hbxncez -Dvolatile= ${CPPFLAGS} -UKGDB \
	    ${CFILES} ioconf.c param.c | \
	    grep -v 'static function .* unused'

symbols.sort: ${_machdir}/${_mach}/symbols.raw
	grep -v '^#' ${_machdir}/${_mach}/symbols.raw \
	    | sed 's/^	//' | sort -u > symbols.sort

depend:
	@touch $@

tags:
	@echo "see $S/kern/Makefile for tags"

db_structinfo.h: $S/ddb/db_structinfo.c $S/ddb/parse_structinfo.pl
	${CC} ${CFLAGS} ${CPPFLAGS} -MT $@ -gstabs -c $S/ddb/db_structinfo.c
	objdump -g db_structinfo.o | perl $S/ddb/parse_structinfo.pl > $@
	rm -f db_structinfo.o

locore.o: ${_machdir}/${_mach}/locore.S assym.h
context.o cp0access.o exception.o: assym.h
lcore_access.o lcore_ddb.o lcore_float.o tlbhandler.o: assym.h
mips64r2.o: assym.h

# The install target can be redefined by putting a
# install-kernel-${MACHINE_NAME} target into /etc/mk.conf
MACHINE_NAME!=  uname -n
install: install-kernel-${MACHINE_NAME}
.if !target(install-kernel-${MACHINE_NAME}})
install-kernel-${MACHINE_NAME}:
	cmp -s bsd /bsd || ln -f /bsd /obsd
	cp bsd /nbsd
	mv /nbsd /bsd
.endif

# pull in the dependency information
.if !empty(DB_STRUCTINFO) && !exists(${DB_STRUCTINFO})
 ${SYSTEM_OBJ}: ${DB_STRUCTINFO}
.endif
.ifnmake clean
. for o in ${SYSTEM_OBJ} assym.h ${DB_STRUCTINFO}
.  if exists(${o:R}.d)
.   include "${o:R}.d"
.  elif exists($o)
    .PHONY: $o
.  endif
. endfor
.endif

%RULES


code:
#	$OpenBSD: RAMDISK,v 1.21 2015/08/05 12:07:11 pirofti Exp $

machine		octeon mips64
maxusers	4

option		TIMEZONE=0
option		DST=0
option		SMALL_KERNEL
option		NO_PROPOLICE
option		BOOT_CONFIG

option		RAMDISK_HOOKS
option		MINIROOTSIZE=10240

option		PCIVERBOSE
option		USBVERBOSE

option		FFS
option		FFS2
option		CD9660
option		UDF
option		EXT2FS
option		MSDOSFS
option		NFSCLIENT
option		INET6

option		CPU_OCTEON
option		FPUEMUL
makeoption	LINK_ADDRESS="0xffffffff81000000"

config		bsd root on rd0a swap on rd0b

mainbus0	at root
cpu0		at mainbus0
clock0		at mainbus0
iobus0		at mainbus0
octcf0		at iobus0
amdcf0		at iobus0
uartbus0	at mainbus0
com0		at uartbus0
com1		at uartbus0

octrtc0		at mainbus0

pcibus*		at iobus0
pci*		at pcibus?
ppb*		at pci?
pci*		at ppb?

cn30xxgmx*	at iobus? irq 48
cnmac*		at cn30xxgmx?

pciide*		at pci? flags 0x0000
wd*		at pciide? flags 0x0000

dwctwo0		at iobus0 irq 56
usb*		at dwctwo?
uhub*		at usb?
umass*		at uhub?

scsibus*	at scsi?
sd*		at scsibus?

rgephy*		at mii?
ukphy*		at mii?
atphy*		at mii?
brswphy*	at mii?

pseudo-device	loop 1
pseudo-device	bpfilter 1
pseudo-device	rd 1
pseudo-device	wsmux 2

path: sys/arch/octeon/conf/RAMDISK


code:
#	$OpenBSD: files.octeon,v 1.24 2015/07/20 19:44:32 pirofti Exp $

# Standard stanzas config(8) can't run without
maxpartitions 16
maxusers 2 8 64

# Major number for block devices, for ``root on'' lines
major	{ sd = 0 }
major	{ cd = 3 }
major	{ wd = 4 }
major	{ rd = 8 }
major	{ octcf = 15 }
major	{ amdcf = 19 }

file	dev/cninit.c
file	arch/octeon/octeon/autoconf.c
file	arch/octeon/octeon/bus_dma.c
file	arch/octeon/octeon/conf.c
file	arch/octeon/octeon/disksubr.c		disk
file	arch/octeon/octeon/lock_machdep.c	multiprocessor
file	arch/octeon/octeon/machdep.c
file	arch/mips64/mips64/mips64r2.S

include "dev/ata/files.ata"
include "dev/atapiscsi/files.atapiscsi"
include	"dev/i2c/files.i2c"
include	"dev/gpio/files.gpio"
include	"dev/isa/files.isa"
include	"dev/isa/files.isapnp"
include	"dev/mii/files.mii"
include	"dev/pci/files.pci"
include	"dev/pckbc/files.pckbc"
include	"dev/usb/files.usb"
include	"dev/usb/dwc2/files.dwc2"
include "dev/rasops/files.rasops"
include "dev/wscons/files.wscons"
include "dev/wsfont/files.wsfont"
include	"scsi/files.scsi"

define	mainbus {}
device	mainbus
attach	mainbus at root
file	arch/octeon/dev/mainbus.c			mainbus

device	cpu
attach	cpu at mainbus

device	clock
attach	clock at mainbus

# TOD clock
device	octrtc
attach	octrtc at mainbus
file	arch/octeon/dev/octrtc.c			octrtc

define	iobus {[base = -1], [irq = 0]}
device	iobus
attach	iobus at mainbus
file	arch/octeon/dev/octeon_iobus.c			iobus
file	arch/octeon/dev/octeon_intr.c			iobus
file	arch/octeon/dev/cn30xxfpa.c			iobus
file	arch/octeon/dev/cn30xxpow.c			iobus
file	arch/octeon/dev/cn30xxfau.c			iobus
file	arch/octeon/dev/cn30xxpip.c			iobus
file	arch/octeon/dev/cn30xxipd.c			iobus
file	arch/octeon/dev/cn30xxpko.c			iobus
file	arch/octeon/dev/cn30xxasx.c			iobus
file	arch/octeon/dev/cn30xxsmi.c			iobus

# On-board USB
attach	dwctwo at iobus with octdwctwo
file	arch/octeon/dev/octdwctwo.c			octdwctwo	needs-flag

# On-board CF
device	octcf: disk
attach	octcf at iobus
file	arch/octeon/dev/octcf.c				octcf

device	amdcf: disk
attach	amdcf at iobus
file	arch/octeon/dev/amdcf.c				amdcf

# On-board RNG
device	octrng
attach	octrng at iobus
file	arch/octeon/dev/octrng.c			octrng

device	cn30xxgmx {}
attach	cn30xxgmx at iobus
file	arch/octeon/dev/cn30xxgmx.c			cn30xxgmx

device	cnmac: ether, ifnet, mii, ifmedia
attach	cnmac at cn30xxgmx
file	arch/octeon/dev/if_cnmac.c			cnmac

define	uartbus {[base = -1]}
device	uartbus
attach	uartbus at mainbus
file	arch/octeon/dev/octeon_uartbus.c		uartbus

attach	com at uartbus with cn30xxuart
file	arch/octeon/dev/cn30xxuart.c			cn30xxuart

device	pcibus
attach	pcibus at iobus
file	arch/octeon/dev/octeon_pcibus.c			pcibus
file	arch/octeon/dev/octeon_bus_space.c

file	arch/octeon/octeon/pciide_machdep.c		pciide


code:
OUTPUT_FORMAT("elf64-tradbigmips")
OUTPUT_ARCH(mips)
ENTRY(__start)

SECTIONS
{
	.text :		{ *(.text .text.* .gnu.linkonce.t.*) }
	PROVIDE (etext = .);
	.rodata :	{ *(.rodata .rodata.* .gnu.linkonce.r.*) }
	_gp = ALIGN(16) + 0x7ff0;
	.data :		{ *(.data .data.* .gnu.linkonce.d.*) }
	PROVIDE (edata = .);
	.sbss :		{ *(.sbss .sbss.* .gnu.linkonce.sb.* .scommon) }
	.bss :		{ *(.bss .bss.* .gnu.linkonce.b.* COMMON) }
	PROVIDE (_end = .);
	PROVIDE (end = .);
	/DISCARD/ :
	{
		*(.pdr)
		*(.mdebug.abi64)
		*(.MIPS.options)
	}
}


code: src/sys/arch/octeon/dev

path: sys/arch/octeon/dev/CVS/Repository


code:
/*	$OpenBSD: amdcf.c,v 1.1 2015/07/20 19:44:32 pirofti Exp $	*/

/*
 * Copyright (c) 2007, Juniper Networks, Inc.
 * 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. Neither the name of the author nor the names of any co-contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * 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.
 */

/*
 * Copyright (c) 2009 Sam Leffler, Errno Consulting
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Copyright (c) 2015 Paul Irofti.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mutex.h>
#include <sys/buf.h>
#include <sys/uio.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/disklabel.h>
#include <sys/disk.h>
#include <sys/syslog.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/dkio.h>

#include <machine/intr.h>
#include <machine/bus.h>
#include <machine/autoconf.h>

#include <octeon/dev/iobusvar.h>
#include <machine/octeonreg.h>
#include <machine/octeonvar.h>

#define CFI_QRY_CMD_ADDR	0x55
#define CFI_QRY_CMD_DATA	0x98

#define CFI_QRY_TTO_WRITE	0x1f
#define CFI_QRY_TTO_ERASE	0x21
#define CFI_QRY_MTO_WRITE	0x23
#define CFI_QRY_MTO_ERASE	0x25

#define CFI_QRY_SIZE		0x27
#define	CFI_QRY_NREGIONS	0x2c
#define CFI_QRY_REGION0 	0x31
#define CFI_QRY_REGION(x)	(CFI_QRY_REGION0 + (x) * 4)

#define CFI_BCS_READ_ARRAY	0xff

#define CFI_DISK_SECSIZE	512
#define CFI_DISK_MAXIOSIZE	65536

#define AMDCF_MAP_SIZE		0x02000000

#define CFI_AMD_BLOCK_ERASE	0x30
#define CFI_AMD_UNLOCK		0xaa
#define CFI_AMD_UNLOCK_ACK	0x55
#define CFI_AMD_PROGRAM		0xa0
#define CFI_AMD_RESET		0xf0

#define AMD_ADDR_START		0x555
#define AMD_ADDR_ACK		0x2aa

#define BOOTLOADER_ADDR		0xa0000

struct cfi_region {
	u_int r_blocks;
	u_int r_blksz;
};

struct amdcf_softc {
	/* General disk infos */
	struct device sc_dev;
	struct disk sc_dk;
	struct bufq sc_bufq;
	struct buf *sc_bp;

	int sc_flags;
#define AMDCF_LOADED	0x10

	struct iobus_attach_args *sc_io;
	bus_space_tag_t sc_iot;
	bus_space_handle_t sc_ioh;

	size_t sc_size;		/* Disk size in bytes */
	u_int sc_regions;	/* Erase regions. */
	struct cfi_region *sc_region;	/* Array of region info. */

	u_int sc_width;
	u_int sc_shift;
	u_int sc_mask;

	u_int sc_erase_timeout;
	u_int sc_erase_max_timeout;
	u_int sc_write_timeout;
	u_int sc_write_max_timeout;
	u_int sc_rstcmd;

	u_char *sc_wrbuf;
	u_int sc_wrbufsz;
	u_int sc_wrofs;
	u_int sc_writing;
};

int	amdcf_match(struct device *, void *, void *);
void	amdcf_attach(struct device *, struct device *, void *);
int	amdcf_detach(struct device *, int);

struct cfattach amdcf_ca = {
	sizeof(struct amdcf_softc), amdcf_match, amdcf_attach, amdcf_detach
};

struct cfdriver amdcf_cd = {
	NULL, "amdcf", DV_DISK
};

cdev_decl(amdcf);
bdev_decl(amdcf);

#define amdcflookup(unit) (struct amdcf_softc *)disk_lookup(&amdcf_cd, (unit))
int amdcfgetdisklabel(dev_t, struct amdcf_softc *, struct disklabel *, int);

void amdcfstart(void *);
void _amdcfstart(struct amdcf_softc *, struct buf *);
void amdcfdone(void *);

void amdcf_disk_read(struct amdcf_softc *, struct buf *, off_t);
void amdcf_disk_write(struct amdcf_softc *, struct buf *, off_t);

int cfi_block_start(struct amdcf_softc *, u_int);
int cfi_write_block(struct amdcf_softc *);
int cfi_erase_block(struct amdcf_softc *, u_int);
int cfi_block_finish(struct amdcf_softc *);

void cfi_array_write(struct amdcf_softc *sc, u_int, u_int, u_int);
void cfi_amd_write(struct amdcf_softc *, u_int, u_int, u_int);

uint8_t cfi_read_qry(struct amdcf_softc *, uint64_t);
uint8_t cfi_read(struct amdcf_softc *, bus_size_t, bus_size_t);
void cfi_write(struct amdcf_softc *, bus_size_t, bus_size_t, uint8_t);
int cfi_wait_ready(struct amdcf_softc *, u_int, u_int, u_int);
int cfi_make_cmd(uint8_t, u_int);

int
amdcf_match(struct device *parent, void *match, void *aux)
{
	struct mainbus_attach_args *maa = aux;
	struct cfdata *cf = match;

	if (strcmp(maa->maa_name, cf->cf_driver->cd_name) != 0)
		return 0;

	/* Only for DSR machines */
	if (octeon_boot_info->board_type != BOARD_TYPE_DSR_500)
		return 0;

	return 1;
}

void
amdcf_attach(struct device *parent, struct device *self, void *aux)
{
	struct amdcf_softc *sc = (void *)self;
	u_int blksz, blocks, r;

	sc->sc_io = aux;
	sc->sc_iot = sc->sc_io->aa_bust;

	if (bus_space_map(sc->sc_iot, OCTEON_AMDCF_BASE, AMDCF_MAP_SIZE, 0,
	    &sc->sc_ioh)) {
		printf(": can't map registers");
	}

	/* should be detected in the generic driver */
	sc->sc_width = 1;
	sc->sc_shift = 2;
	sc->sc_mask = 0x000000ff;
	sc->sc_rstcmd = CFI_AMD_RESET;

	/* Initialize the Query Database from the CF */
	cfi_array_write(sc, 0, 0, sc->sc_rstcmd);
	cfi_write(sc, 0, CFI_QRY_CMD_ADDR, CFI_QRY_CMD_DATA);

	/* Get time-out values for erase and write. */
	sc->sc_write_timeout = 1 << cfi_read(sc, 0, CFI_QRY_TTO_WRITE);
	sc->sc_erase_timeout = 1 << cfi_read(sc, 0, CFI_QRY_TTO_ERASE);
	sc->sc_write_max_timeout = 1 << cfi_read(sc, 0, CFI_QRY_MTO_WRITE);
	sc->sc_erase_max_timeout = 1 << cfi_read(sc, 0, CFI_QRY_MTO_ERASE);

	/* Get the device size. */
	sc->sc_size = 1U << cfi_read(sc, 0, CFI_QRY_SIZE);
	printf(": AMD/Fujitsu %zu bytes\n", sc->sc_size);

	/* Get erase regions. */
	sc->sc_regions = cfi_read(sc, 0, CFI_QRY_NREGIONS);
	sc->sc_region = malloc(sc->sc_regions *
	    sizeof(struct cfi_region), M_TEMP, M_WAITOK | M_ZERO);

	for (r = 0; r < sc->sc_regions; r++) {
		blocks = cfi_read(sc, 0, CFI_QRY_REGION(r)) |
		    (cfi_read(sc, 0, CFI_QRY_REGION(r) + 1) << 8);
		sc->sc_region[r].r_blocks = blocks + 1;

		blksz = cfi_read(sc, 0, CFI_QRY_REGION(r) + 2) |
		    (cfi_read(sc, 0, CFI_QRY_REGION(r) + 3) << 8);
		sc->sc_region[r].r_blksz = (blksz == 0) ? 128 :
		    blksz * 256;
	}

	/* Reset the device to the default state */
	cfi_array_write(sc, 0, 0, sc->sc_rstcmd);

	/*
	 * Initialize disk structures.
	 */
	sc->sc_dk.dk_name = sc->sc_dev.dv_xname;
	bufq_init(&sc->sc_bufq, BUFQ_DEFAULT);

	/* Attach disk. */
	disk_attach(&sc->sc_dev, &sc->sc_dk);

}

int
amdcf_detach(struct device *self, int flags)
{
	struct amdcf_softc *sc = (struct amdcf_softc *)self;

	bufq_drain(&sc->sc_bufq);

	disk_gone(amdcfopen, self->dv_unit);

	/* Detach disk. */
	bufq_destroy(&sc->sc_bufq);
	disk_detach(&sc->sc_dk);

	return 0;
}

int
amdcfopen(dev_t dev, int flag, int fmt, struct proc *p)
{
	struct amdcf_softc *sc;
	int unit, part;
	int error;

	unit = DISKUNIT(dev);
	sc = amdcflookup(unit);
	if (sc == NULL)
		return ENXIO;

	/*
	 * If this is the first open of this device, add a reference
	 * to the adapter.
	 */
	if ((error = disk_lock(&sc->sc_dk)) != 0)
		goto out1;

	if (sc->sc_dk.dk_openmask != 0) {
		/*
		 * If any partition is open, but the disk has been invalidated,
		 * disallow further opens.
		 */
		if ((sc->sc_flags & AMDCF_LOADED) == 0) {
			error = EIO;
			goto out;
		}
	} else {
		if ((sc->sc_flags & AMDCF_LOADED) == 0) {
			sc->sc_flags |= AMDCF_LOADED;

			/* Load the partition info if not already loaded. */
			if (amdcfgetdisklabel(dev, sc,
			    sc->sc_dk.dk_label, 0) == EIO) {
				error = EIO;
				goto out;
			}
		}
	}

	part = DISKPART(dev);

	if ((error = disk_openpart(&sc->sc_dk, part, fmt, 1)) != 0)
		goto out;

	disk_unlock(&sc->sc_dk);
	device_unref(&sc->sc_dev);
	return 0;

out:
	disk_unlock(&sc->sc_dk);
out1:
	device_unref(&sc->sc_dev);
	return error;
}

/*
 * Load the label information on the named device
 */
int
amdcfgetdisklabel(dev_t dev, struct amdcf_softc *sc, struct disklabel *lp,
    int spoofonly)
{
	memset(lp, 0, sizeof(struct disklabel));

	lp->d_secsize = DEV_BSIZE;
	lp->d_nsectors = 1;	/* bogus */
	lp->d_ntracks = 1;	/* bogus */
	lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
	lp->d_ncylinders = sc->sc_size / lp->d_secpercyl;

	strlcpy(lp->d_typename, "amdcf device", sizeof(lp->d_typename));
	lp->d_type = DTYPE_SCSI;	/* bogus type, can be anything */
	strlcpy(lp->d_packname, "CFI Disk", sizeof(lp->d_packname));
	DL_SETDSIZE(lp, sc->sc_size / DEV_BSIZE);
	lp->d_flags = 0;
	lp->d_version = 1;

	lp->d_magic = DISKMAGIC;
	lp->d_magic2 = DISKMAGIC;
	lp->d_checksum = dkcksum(lp);

	/* Call the generic disklabel extraction routine */
	return readdisklabel(DISKLABELDEV(dev), amdcfstrategy, lp, spoofonly);
}

int
amdcfclose(dev_t dev, int flag, int fmt, struct proc *p)
{
	struct amdcf_softc *sc;
	int part = DISKPART(dev);

	sc = amdcflookup(DISKUNIT(dev));
	if (sc == NULL)
		return ENXIO;

	disk_lock_nointr(&sc->sc_dk);

	disk_closepart(&sc->sc_dk, part, fmt);

	disk_unlock(&sc->sc_dk);

	device_unref(&sc->sc_dev);
	return 0;
}

int
amdcfread(dev_t dev, struct uio *uio, int flags)
{
	return (physio(amdcfstrategy, dev, B_READ, minphys, uio));
}

int
amdcfwrite(dev_t dev, struct uio *uio, int flags)
{
#ifdef AMDCF_DISK_WRITE_ENABLE
	return (physio(amdcfstrategy, dev, B_WRITE, minphys, uio));
#else
	return 0;
#endif
}

void
amdcfstrategy(struct buf *bp)
{
	struct amdcf_softc *sc;
	int s;

	sc = amdcflookup(DISKUNIT(bp->b_dev));
	if (sc == NULL) {
		bp->b_error = ENXIO;
		goto bad;
	}
	/* If device invalidated (e.g. media change, door open), error. */
	if ((sc->sc_flags & AMDCF_LOADED) == 0) {
		bp->b_error = EIO;
		goto bad;
	}

	/* Validate the request. */
	if (bounds_check_with_label(bp, sc->sc_dk.dk_label) == -1)
		goto done;

	/* Check that the number of sectors can fit in a byte. */
	if ((bp->b_bcount / sc->sc_dk.dk_label->d_secsize) >= (1 << NBBY)) {
		bp->b_error = EINVAL;
		goto bad;
	}

	/* Queue transfer on drive, activate drive and controller if idle. */
	bufq_queue(&sc->sc_bufq, bp);
	s = splbio();
	amdcfstart(sc);
	splx(s);
	device_unref(&sc->sc_dev);
	return;

 bad:
	bp->b_flags |= B_ERROR;
	bp->b_resid = bp->b_bcount;
 done:
	s = splbio();
	biodone(bp);
	splx(s);
	if (sc != NULL)
		device_unref(&sc->sc_dev);
}

int
amdcfioctl(dev_t dev, u_long xfer, caddr_t addr, int flag, struct proc *p)
{
	struct amdcf_softc *sc;
	struct disklabel *lp;
	int error = 0;

	sc = amdcflookup(DISKUNIT(dev));
	if (sc == NULL)
		return ENXIO;

	if ((sc->sc_flags & AMDCF_LOADED) == 0) {
		error = EIO;
		goto exit;
	}

	switch (xfer) {
	case DIOCRLDINFO:
		lp = malloc(sizeof(*lp), M_TEMP, M_WAITOK);
		amdcfgetdisklabel(dev, sc, lp, 0);
		bcopy(lp, sc->sc_dk.dk_label, sizeof(*lp));
		free(lp, M_TEMP, 0);
		goto exit;

	case DIOCGPDINFO:
		amdcfgetdisklabel(dev, sc, (struct disklabel *)addr, 1);
		goto exit;

	case DIOCGDINFO:
		*(struct disklabel *)addr = *(sc->sc_dk.dk_label);
		goto exit;

	case DIOCGPART:
		((struct partinfo *)addr)->disklab = sc->sc_dk.dk_label;
		((struct partinfo *)addr)->part =
		    &sc->sc_dk.dk_label->d_partitions[DISKPART(dev)];
		goto exit;

	case DIOCWDINFO:
	case DIOCSDINFO:
		if ((flag & FWRITE) == 0) {
			error = EBADF;
			goto exit;
		}

		if ((error = disk_lock(&sc->sc_dk)) != 0)
			goto exit;

		error = setdisklabel(sc->sc_dk.dk_label,
		    (struct disklabel *)addr, sc->sc_dk.dk_openmask);
		if (error == 0) {
			if (xfer == DIOCWDINFO)
				error = writedisklabel(DISKLABELDEV(dev),
				    amdcfstrategy, sc->sc_dk.dk_label);
		}

		disk_unlock(&sc->sc_dk);
		goto exit;

#ifdef notyet
	case DIOCWFORMAT:
		if ((flag & FWRITE) == 0)
			return EBADF;
		{
		struct format_op *fop;
		struct iovec aiov;
		struct uio auio;

		fop = (struct format_op *)addr;
		aiov.iov_base = fop->df_buf;
		aiov.iov_len = fop->df_count;
		auio.uio_iov = &aiov;
		auio.uio_iovcnt = 1;
		auio.uio_resid = fop->df_count;
		auio.uio_segflg = 0;
		auio.uio_offset =
			fop->df_startblk * sc->sc_dk.dk_label->d_secsize;
		auio.uio_procp = p;
		error = physio(scformat, dev, B_WRITE, minphys, &auio);
		fop->df_count -= auio.uio_resid;
		fop->df_reg[0] = scc->sc_status;
		fop->df_reg[1] = scc->sc_error;
		goto exit;
		}
#endif

	default:
		error = ENOTTY;
		goto exit;
	}

#ifdef DIAGNOSTIC
	panic("amdcfioctl: impossible");
#endif

 exit:
	device_unref(&sc->sc_dev);
	return error;
}

/*
 * Dump core after a system crash.
 */
int
amdcfdump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
{
	return ENXIO;
}

daddr_t
amdcfsize(dev_t dev)
{
	struct amdcf_softc *sc;
	struct disklabel *lp;
	int part, omask;
	daddr_t size;

	sc = amdcflookup(DISKUNIT(dev));
	if (sc == NULL)
		return (-1);

	part = DISKPART(dev);
	omask = sc->sc_dk.dk_openmask & (1 << part);

	if (omask == 0 && amdcfopen(dev, 0, S_IFBLK, NULL) != 0) {
		size = -1;
		goto exit;
	}

	lp = sc->sc_dk.dk_label;
	size = DL_SECTOBLK(lp, DL_GETPSIZE(&lp->d_partitions[part]));
	if (omask == 0 && amdcfclose(dev, 0, S_IFBLK, NULL) != 0)
		size = -1;

 exit:
	device_unref(&sc->sc_dev);
	return size;
}

/*
 * Queue a drive for I/O.
 */
void
amdcfstart(void *arg)
{
	struct amdcf_softc *sc = arg;
	struct buf *bp;

	while ((bp = bufq_dequeue(&sc->sc_bufq)) != NULL) {
		/* Transfer this buffer now. */
		_amdcfstart(sc, bp);
	}
}

void
_amdcfstart(struct amdcf_softc *sc, struct buf *bp)
{
	off_t off;
	struct partition *p;

	sc->sc_bp = bp;

	/* Fetch buffer's read/write offset */
	p = &sc->sc_dk.dk_label->d_partitions[DISKPART(bp->b_dev)];
	off = DL_GETPOFFSET(p) * sc->sc_dk.dk_label->d_secsize +
	    (u_int64_t)bp->b_blkno * DEV_BSIZE;
	if (off > sc->sc_size) {
		bp->b_flags |= B_ERROR;
		bp->b_error = EIO;
		return;
	}

	/* Instrumentation. */
	disk_busy(&sc->sc_dk);

	if (bp->b_flags & B_READ)
		amdcf_disk_read(sc, bp, off);
#ifdef AMDCF_DISK_WRITE_ENABLE
	else
		amdcf_disk_write(sc, bp, off);
#endif

	amdcfdone(sc);
}

void
amdcfdone(void *arg)
{
	struct amdcf_softc *sc = arg;
	struct buf *bp = sc->sc_bp;

	if (bp->b_error == 0)
		bp->b_resid = 0;
	else
		bp->b_flags |= B_ERROR;

	disk_unbusy(&sc->sc_dk, (bp->b_bcount - bp->b_resid),
	    (bp->b_flags & B_READ));
	biodone(bp);
}

void
amdcf_disk_read(struct amdcf_softc *sc, struct buf *bp, off_t off)
{
	long resid;

	if (sc->sc_writing) {
		bp->b_error = cfi_block_finish(sc);
		if (bp->b_error) {
			bp->b_flags |= B_ERROR;
			return;
		}
	}

	resid = bp->b_bcount;
	uint8_t *dp = (uint8_t *)bp->b_data;
	while (resid > 0 && off < sc->sc_size) {
		*dp++ = cfi_read(sc, off, 0);
		off += 1, resid -= 1;
	}
	bp->b_resid = resid;
}

void
amdcf_disk_write(struct amdcf_softc *sc, struct buf *bp, off_t off)
{
	long resid;
	u_int top;

	resid = bp->b_bcount;
	while (resid > 0) {
		/*
		 * Finish the current block if we're about to write
		 * to a different block.
		 */
		if (sc->sc_writing) {
			top = sc->sc_wrofs + sc->sc_wrbufsz;
			if (off < sc->sc_wrofs || off >= top)
				cfi_block_finish(sc);
		}

		/* Start writing to a (new) block if applicable. */
		if (!sc->sc_writing) {
			bp->b_error = cfi_block_start(sc, off);
			if (bp->b_error) {
				bp->b_flags |= B_ERROR;
				return;
			}
		}

		top = sc->sc_wrofs + sc->sc_wrbufsz;
		bcopy(bp->b_data,
		    sc->sc_wrbuf + off - sc->sc_wrofs,
		    MIN(top - off, resid));
		resid -= MIN(top - off, resid);
	}
	bp->b_resid = resid;
}

/*
 * Begin writing into a new block/sector.  We read the sector into
 * memory and keep updating that, until we move into another sector
 * or the process stops writing. At that time we write the whole
 * sector to flash (see cfi_block_finish).
 */
int
cfi_block_start(struct amdcf_softc *sc, u_int ofs)
{
	u_int rofs, rsz;
	int r;
	uint8_t *ptr;

	rofs = 0;
	for (r = 0; r < sc->sc_regions; r++) {
		rsz = sc->sc_region[r].r_blocks * sc->sc_region[r].r_blksz;
		if (ofs < rofs + rsz)
			break;
		rofs += rsz;
	}
	if (r == sc->sc_regions)
		return (EFAULT);

	sc->sc_wrbufsz = sc->sc_region[r].r_blksz;
	sc->sc_wrbuf = malloc(sc->sc_wrbufsz, M_TEMP, M_WAITOK);
	sc->sc_wrofs = ofs - (ofs - rofs) % sc->sc_wrbufsz;

	ptr = sc->sc_wrbuf;
	/* Read the block from flash for byte-serving. */
	for (r = 0; r < sc->sc_wrbufsz; r++)
		*(ptr)++ = cfi_read(sc, sc->sc_wrofs + r, 0);

	sc->sc_writing = 1;
	return (0);
}

/*
 * Finish updating the current block/sector by writing the compound
 * set of changes to the flash.
 */
int
cfi_block_finish(struct amdcf_softc *sc)
{
	int error;

	error = cfi_write_block(sc);
	free(sc->sc_wrbuf, M_TEMP, sc->sc_wrbufsz);
	sc->sc_wrbuf = NULL;
	sc->sc_wrbufsz = 0;
	sc->sc_wrofs = 0;
	sc->sc_writing = 0;
	return (error);
}

int
cfi_write_block(struct amdcf_softc *sc)
{
	uint8_t *ptr;
	int error, i, s;

	if (sc->sc_wrofs > sc->sc_size)
		panic("CFI: write offset (%x) bigger "
		    "than cfi array size (%zu)\n",
		    sc->sc_wrofs, sc->sc_size);

	if ((sc->sc_wrofs < BOOTLOADER_ADDR) ||
	    ((sc->sc_wrofs + sc->sc_wrbufsz) < BOOTLOADER_ADDR))
		return EOPNOTSUPP;

	error = cfi_erase_block(sc, sc->sc_wrofs);
	if (error)
		goto out;

	/* Write the block. */
	ptr = sc->sc_wrbuf;

	for (i = 0; i < sc->sc_wrbufsz; i += sc->sc_width) {

		/*
		 * Make sure the command to start a write and the
		 * actual write happens back-to-back without any
		 * excessive delays.
		 */
		s = splbio();

		cfi_amd_write(sc, sc->sc_wrofs, AMD_ADDR_START,
			    CFI_AMD_PROGRAM);
		/* Raw data do not use cfi_array_write */
		cfi_write(sc, sc->sc_wrofs + i, 0, *(ptr)++);

		splx(s);

		error = cfi_wait_ready(sc, sc->sc_wrofs + i,
		    sc->sc_write_timeout, sc->sc_write_max_timeout);
		if (error)
			goto out;
	}

out:
	cfi_array_write(sc, sc->sc_wrofs, 0, sc->sc_rstcmd);
	return error;
}

int
cfi_erase_block(struct amdcf_softc *sc, u_int offset)
{
	int error = 0;

	if (offset > sc->sc_size)
		panic("CFI: erase offset (%x) bigger "
		    "than cfi array size (%zu)\n",
		    sc->sc_wrofs, sc->sc_size);

	/* Erase the block. */
	cfi_amd_write(sc, offset, 0, CFI_AMD_BLOCK_ERASE);

	error = cfi_wait_ready(sc, offset, sc->sc_erase_timeout,
	    sc->sc_erase_max_timeout);

	return error;
}

int
cfi_wait_ready(struct amdcf_softc *sc, u_int ofs, u_int timeout, u_int count)
{
	int done, error;
	u_int st0 = 0, st = 0;

	done = 0;
	error = 0;

	if (!timeout)
		timeout = 100;  /* Default to 100 uS */
	if (!count)
		count = 100;    /* Max timeout is 10 mS */

	while (!done && !error && count) {
		DELAY(timeout);

		count--;

		/*
		 * read sc->sc_width bytes, and check for toggle bit.
		 */
		st0 = cfi_read(sc, ofs, 0);
		st = cfi_read(sc, ofs, 0);
		done = ((st & cfi_make_cmd(0x40, sc->sc_mask)) ==
		    (st0 & cfi_make_cmd(0x40, sc->sc_mask))) ? 1 : 0;

		break;
	}
	if (!done && !error)
		error = ETIMEDOUT;
	if (error)
		printf("\nerror=%d (st 0x%x st0 0x%x) at offset=%x\n",
		    error, st, st0, ofs);
	return error;
}

/*
 * cfi_array_write
 * fill "bus width" word with value of var data by array mask sc->sc_mask
 */
void
cfi_array_write(struct amdcf_softc *sc, u_int ofs, u_int addr, u_int data)
{
	data &= 0xff;
	cfi_write(sc, ofs, addr, cfi_make_cmd(data, sc->sc_mask));
}

void
cfi_amd_write(struct amdcf_softc *sc, u_int ofs, u_int addr, u_int data)
{
	cfi_array_write(sc, ofs, AMD_ADDR_START, CFI_AMD_UNLOCK);
	cfi_array_write(sc, ofs, AMD_ADDR_ACK, CFI_AMD_UNLOCK_ACK);
	cfi_array_write(sc, ofs, addr, data);
}

/*
 * The following routines assume width=1 and shift=2 as that is
 * the case on the Octeon DSR machines.
 * If this assumption fails a new detection routine should be written
 * and called during attach.
 */
uint8_t
cfi_read(struct amdcf_softc *sc, bus_size_t base, bus_size_t offset)
{
	return bus_space_read_1(sc->sc_iot, sc->sc_ioh,
	    base | (offset * sc->sc_shift));
}

void
cfi_write(struct amdcf_softc *sc, bus_size_t base, bus_size_t offset,
    uint8_t val)
{
	bus_space_write_1(sc->sc_iot, sc->sc_ioh,
	    base | (offset * sc->sc_shift), val);
}

int
cfi_make_cmd(uint8_t cmd, u_int mask)
{
	int i;
	u_int data = 0;

	for (i = 0; i < sizeof(int); i ++) {
		if (mask & (0xff << (i*8)))
			data |= cmd << (i*8);
	}

	return data;
}