code:
/*	$OpenBSD: process_machdep.c,v 1.12 2009/03/05 19:52:23 kettenis Exp $	*/
/*	$NetBSD: process_machdep.c,v 1.10 2000/09/26 22:05:50 eeh Exp $ */

/*
 * Copyright (c) 1993 The Regents of the University of California.
 * Copyright (c) 1993 Jan-Simon Pendry
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Jan-Simon Pendry.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * from: Id: procfs_i386.c,v 4.1 1993/12/17 10:47:45 jsp Rel
 */

/*
 * This file may seem a bit stylized, but that so that it's easier to port.
 * Functions to be implemented here are:
 *
 * process_read_regs(proc, regs)
 *	Get the current user-visible register set from the process
 *	and copy it into the regs structure (<machine/reg.h>).
 *	The process is stopped at the time read_regs is called.
 *
 * process_write_regs(proc, regs)
 *	Update the current register set from the passed in regs
 *	structure.  Take care to avoid clobbering special CPU
 *	registers or privileged bits in the PSL.
 *	The process is stopped at the time write_regs is called.
 *
 * process_sstep(proc)
 *	Arrange for the process to trap after executing a single instruction.
 *
 * process_set_pc(proc)
 *	Set the process's program counter.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/vnode.h>
#include <machine/psl.h>
#include <machine/reg.h>
#include <machine/frame.h>
#include <sys/ptrace.h>

#ifndef P_32
#define P_32 0
#endif
/* Unfortunately we need to convert v9 trapframe to v8 regs */
int
process_read_regs(p, regs)
	struct proc *p;
	struct reg *regs;
{
	struct trapframe64* tf = p->p_md.md_tf;
	struct reg32* regp = (struct reg32*)regs;
	int i;

	if (!(curproc->p_flag & P_32)) {
		/* 64-bit mode -- copy out regs */
		regs->r_tstate = tf->tf_tstate;
		regs->r_pc = tf->tf_pc;
		regs->r_npc = tf->tf_npc;
		regs->r_y = tf->tf_y;
		for (i = 0; i < 8; i++) {
			regs->r_global[i] = tf->tf_global[i];
			regs->r_out[i] = tf->tf_out[i];
			regs->r_local[i] = tf->tf_local[i];
			regs->r_in[i] = tf->tf_in[i];
		}
		return (0);
	}

	/* 32-bit mode -- copy out & convert 32-bit regs */
	regp->r_psr = TSTATECCR_TO_PSR(tf->tf_tstate);
	regp->r_pc = tf->tf_pc;
	regp->r_npc = tf->tf_npc;
	regp->r_y = tf->tf_y;
	for (i = 0; i < 8; i++) {
		regp->r_global[i] = tf->tf_global[i];
		regp->r_out[i] = tf->tf_out[i];
	}
	/* We should also write out the ins and locals.  See signal stuff */
	return (0);
}

int
process_read_fpregs(p, regs)
	struct proc	*p;
	struct fpreg	*regs;
{
	extern struct fpstate64	initfpstate;
	struct fpstate64 *statep = &initfpstate;
	struct fpreg32 *regp = (struct fpreg32 *)regs;
	int i;

	/* NOTE: struct fpreg == struct fpstate */
	if (p->p_md.md_fpstate) {
		fpusave_proc(p, 1);
		statep = p->p_md.md_fpstate;
	}

	if (!(curproc->p_flag & P_32)) {
		/* 64-bit mode -- copy in fregs */
		bcopy(statep, regs, sizeof(struct fpreg64));
		return 0;
	}
	/* 32-bit mode -- copy out & convert 32-bit fregs */
	for (i = 0; i < 32; i++)
		regp->fr_regs[i] = statep->fs_regs[i];

	return 0;
}

#ifdef PTRACE

int
process_write_regs(p, regs)
	struct proc *p;
	struct reg *regs;
{
	struct trapframe64* tf = p->p_md.md_tf;
	struct reg32* regp = (struct reg32*)regs;
	int i;

	if (!(curproc->p_flag & P_32)) {
		/* 64-bit mode -- copy in regs */
		tf->tf_pc = regs->r_pc;
		tf->tf_npc = regs->r_npc;
		tf->tf_y = regs->r_y;
		for (i = 0; i < 8; i++) {
			tf->tf_global[i] = regs->r_global[i];
			tf->tf_out[i] = regs->r_out[i];
		}
		/* We should also read in the ins and locals.  See signal stuff */
		tf->tf_tstate = (tf->tf_tstate & ~TSTATE_CCR) |
			(regs->r_tstate & TSTATE_CCR);
		return (0);
	}

	/* 32-bit mode -- copy in & convert 32-bit regs */
	tf->tf_pc = regp->r_pc;
	tf->tf_npc = regp->r_npc;
	tf->tf_y = regp->r_y;
	for (i = 0; i < 8; i++) {
		tf->tf_global[i] = regp->r_global[i];
		tf->tf_out[i] = regp->r_out[i];
	}
	/* We should also read in the ins and locals.  See signal stuff */
	tf->tf_tstate = (int64_t)(tf->tf_tstate & ~TSTATE_CCR) |
		PSRCC_TO_TSTATE(regp->r_psr);
	return (0);
}

#ifdef PT_STEP
int
process_sstep(p, sstep)
	struct proc *p;
	int sstep;
{
	if (sstep)
		return EINVAL;
	return (0);
}
#endif

int
process_set_pc(p, addr)
	struct proc *p;
	caddr_t addr;
{
	p->p_md.md_tf->tf_pc = (vaddr_t)addr;
	p->p_md.md_tf->tf_npc = (vaddr_t)addr + 4;
	return (0);
}

int
process_write_fpregs(p, regs)
	struct proc	*p;
	struct fpreg	*regs;
{
	struct fpreg32 *regp = (struct fpreg32 *)regs;
	int i;

	if (p->p_md.md_fpstate == NULL) {
		p->p_md.md_fpstate = malloc(sizeof(struct fpstate64),
		    M_SUBPROC, M_WAITOK);
	} else
		fpusave_proc(p, 1);

	if (!(curproc->p_flag & P_32)) {
		/* 64-bit mode -- copy in fregs */
		bcopy(regs, p->p_md.md_fpstate, sizeof(struct fpreg64));
		p->p_md.md_fpstate->fs_qsize = 0;
		return 0;
	}
	/* 32-bit mode -- copy in & convert 32-bit fregs */
	for (i = 0; i < 32; i++)
		p->p_md.md_fpstate->fs_regs[i] = regp->fr_regs[i];
	p->p_md.md_fpstate->fs_fsr = regp->fr_fsr;
	p->p_md.md_fpstate->fs_qsize = 0;

	return 0;
}

#endif	/* PTRACE */

register_t
process_get_wcookie(struct proc *p)
{
	return p->p_addr->u_pcb.pcb_wcookie;
}


code:
/*	$OpenBSD: rbus_machdep.c,v 1.5 2010/09/22 02:28:37 jsg Exp $	*/

/*
 * Copyright (c) 2007 Mark Kettenis
 *
 * 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/device.h>
#include <sys/systm.h>

#include <machine/bus.h>

#include <dev/cardbus/rbus.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_pci.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pccbbreg.h>

struct rbustag rbus_null;

/*
 * The PROM doesn't really understand PCMCIA/CardBus bridges, and
 * leaves them mostly alone.  However, on the UltraBook machines, it
 * treats the memory and IO window register as ordinary BARs and
 * assigns address space to them.  We re-use that address space for
 * rbus.  This is a bit of a hack, but it seems to work and saves us
 * from tracking down available address space globally.
 */

rbus_tag_t
rbus_pccbb_parent_mem(struct device *self, struct pci_attach_args *pa)
{
	struct ofw_pci_register addr[5];
	int naddr, len, i;
	int space, reg;
	int node = PCITAG_NODE(pa->pa_tag);
	char buf[32];

	/* Check for the UltraBook PCMCIA controller. */
	if (OF_getprop(node, "name", &buf, sizeof(buf)) > 0 &&
	    strcmp(buf, "pcma") == 0) {
		len = OF_getprop(PCITAG_NODE(pa->pa_tag), "assigned-addresses",
		    &addr, sizeof(addr));
		naddr = len / sizeof(struct ofw_pci_register);

		for (i = 0; i < naddr; i++) {
			space = addr[i].phys_hi & OFW_PCI_PHYS_HI_SPACEMASK;
			if (space != OFW_PCI_PHYS_HI_SPACE_MEM32)
				continue;
			reg = addr[i].phys_hi & OFW_PCI_PHYS_HI_REGISTERMASK;
			if (reg < PCI_CB_MEMBASE0 || reg > PCI_CB_IOLIMIT1)
				continue;

			return (rbus_new_root_delegate(pa->pa_memt,
			    addr[i].phys_lo, addr[i].size_lo));
		}
	}

	len = OF_getprop(OF_parent(node), "available", &addr, sizeof(addr));
	naddr = len / sizeof(struct ofw_pci_register);

	for (i = 0; i < naddr; i++) {
		space = addr[i].phys_hi & OFW_PCI_PHYS_HI_SPACEMASK;
		if (space != OFW_PCI_PHYS_HI_SPACE_MEM32)
			continue;
		if (addr[i].size_hi == 0 && addr[i].size_lo < 0x10000000)
			continue;

		return (rbus_new_root_delegate(pa->pa_memt,
		    addr[i].phys_lo, addr[i].size_lo));
	}

	return &rbus_null;
}

rbus_tag_t
rbus_pccbb_parent_io(struct device *self, struct pci_attach_args *pa)
{
	struct ofw_pci_register addr[5];
	int naddr, len, i;
	int space, reg;
	int node = PCITAG_NODE(pa->pa_tag);
	char buf[32];

	/* Check for the UltraBook PCMCIA controller. */
	if (OF_getprop(node, "name", &buf, sizeof(buf)) > 0 &&
	    strcmp(buf, "pcma") == 0) {
		len = OF_getprop(PCITAG_NODE(pa->pa_tag), "assigned-addresses",
		    &addr, sizeof(addr));
		naddr = len / sizeof(struct ofw_pci_register);

		for (i = 0; i < naddr; i++) {
			space = addr[i].phys_hi & OFW_PCI_PHYS_HI_SPACEMASK;
			if (space != OFW_PCI_PHYS_HI_SPACE_IO)
				continue;
			reg = addr[i].phys_hi & OFW_PCI_PHYS_HI_REGISTERMASK;
			if (reg < PCI_CB_MEMBASE0 || reg > PCI_CB_IOLIMIT1)
				continue;

			return (rbus_new_root_delegate(pa->pa_iot,
			    addr[i].phys_lo, addr[i].size_lo));
		}
	}

	len = OF_getprop(OF_parent(node), "available", &addr, sizeof(addr));
	naddr = len / sizeof(struct ofw_pci_register);

	for (i = 0; i < naddr; i++) {
		space = addr[i].phys_hi & OFW_PCI_PHYS_HI_SPACEMASK;
		if (space != OFW_PCI_PHYS_HI_SPACE_IO)
			continue;
		if (addr[i].size_hi == 0 && addr[i].size_lo < 0x00001000)
			continue;

		return (rbus_new_root_delegate(pa->pa_iot,
		    addr[i].phys_lo, addr[i].size_lo));
	}

	return &rbus_null;
}

void
pccbb_attach_hook(struct device *parent, struct device *self,
    struct pci_attach_args *pa)
{
	pci_chipset_tag_t pc = pa->pa_pc;
	int node = PCITAG_NODE(pa->pa_tag);
	int bus, busrange[2];
	pcireg_t bir;

	bir = pci_conf_read(pc, pa->pa_tag, PCI_BUSNUM);
	if (((bir >> 8) & 0xff) != 0)
		return;

	if (OF_getprop(OF_parent(node), "bus-range", &busrange,
	    sizeof(busrange)) != sizeof(busrange))
		return;

	bus = busrange[0] + 1;
	while (bus < 256 && pc->busnode[bus])
		bus++;
	if (bus == 256)
		return;
	pc->busnode[bus] = node;

	bir &= ~0x0000ff00;
	bir |= (bus << 8);
	pci_conf_write(pc, pa->pa_tag, PCI_BUSNUM, bir);
}

license: unknown


code:
/*	$OpenBSD: sys_machdep.c,v 1.3 2003/06/02 23:27:56 millert Exp $	*/
/*	$NetBSD: sys_machdep.c,v 1.3 2000/12/13 18:13:11 jdolecek Exp $ */

/*
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This software was developed by the Computer Systems Engineering group
 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
 * contributed to Berkeley.
 *
 * 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, Lawrence Berkeley Laboratory.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)sys_machdep.c	8.1 (Berkeley) 6/11/93
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/ioctl.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/proc.h>
#include <sys/uio.h>
#include <sys/kernel.h>
#include <sys/buf.h>

#include <sys/mount.h>
#include <sys/syscallargs.h>

int
sys_sysarch(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct sys_sysarch_args /* {
		syscallarg(int) op;
		syscallarg(char *) parms;
	} */ *uap = v;
	int error = 0;

	switch(SCARG(uap, op)) {
	default:
		error = EINVAL;
		break;
	}
	return(error);
}


code:
/*	$OpenBSD: timerreg.h,v 1.5 2007/05/29 09:54:23 sobrado Exp $	*/
/*	$NetBSD: timerreg.h,v 1.3 1999/06/05 05:10:01 mrg Exp $ */

/*
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This software was developed by the Computer Systems Engineering group
 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
 * contributed to Berkeley.
 *
 * 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, Lawrence Berkeley Laboratory.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)timerreg.h	8.1 (Berkeley) 6/11/93
 */

/*
 * These timers work in a rather peculiar fashion.  Most clock counters
 * run to 0 (as, e.g., on the VAX, where the ICR counts up to 0 from a
 * large unsigned number).  On the Sun-4c, it counts up to a limit.  But
 * for some reason, when it reaches the limit, it resets to 1, not 0.
 * Thus, if the limit is set to 4, the counter counts like this:
 *
 *	1, 2, 3, 1, 2, 3, ...
 *
 * and if we want to divide by N we must set the limit register to N+1.
 *
 * Sun-4u counters/timer are similar but:
 *
 *	- the registers have been shuffled around once again.  We need
 *	  to use offsets from the 3 addresses the ROM provides us.
 *	- The counters are 29 bits wide with 1us accuracy.
 *	- You can make them do funky things with the limit register
 *	- They have standard 64-bit SBus control registers.
 *
 * There is a problem on the Ultra5 and Ultra10.  As the PCI controller
 * doesn't include the timer, there are no `counter-timer' nodes here
 * and so we must use %tick.
 */
#ifndef _LOCORE
struct timer_4u {
	volatile int64_t t_count;		/* counter reg */
	volatile int64_t t_limit;		/* limit reg */

#define TMR_LIM_IEN		0x80000000	/* interrupt enable bit */
#define TMR_LIM_RELOAD		0x40000000	/* reload counter to 0 */
#define TMR_LIM_PERIODIC	0x20000000	/* reset at limit */
#define TMR_LIM_MASK		0x1fffffff
};

struct timerreg_4u {
	struct timer_4u		*t_timer;	/* There are two of them. */
	volatile int64_t	*t_clrintr;	/* There are two of these. */
	volatile int64_t	*t_mapintr;	/* Same here. */
};

#endif /* _LOCORE */

/* Compute a limit that causes the timer to fire every n microseconds. */
#define	tmr_ustolim(n)	(((n) - 1) & TMR_LIM_MASK)


code:
/*	$OpenBSD: trap.c,v 1.86 2015/06/05 16:07:24 kettenis Exp $	*/
/*	$NetBSD: trap.c,v 1.73 2001/08/09 01:03:01 eeh Exp $ */

/*
 * Copyright (c) 1996
 *	The President and Fellows of Harvard College. All rights reserved.
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This software was developed by the Computer Systems Engineering group
 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
 * contributed to Berkeley.
 *
 * 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, Lawrence Berkeley Laboratory.
 *	This product includes software developed by Harvard University.
 *
 * 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.
 *	This product includes software developed by Harvard University.
 * 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.
 *
 *	@(#)trap.c	8.4 (Berkeley) 9/23/93
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/signalvar.h>
#include <sys/user.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/resource.h>
#include <sys/signal.h>
#include <sys/wait.h>
#include <sys/syscall.h>
#include <sys/syscall_mi.h>
#include <sys/syslog.h>

#include <uvm/uvm_extern.h>

#include <machine/cpu.h>
#include <machine/ctlreg.h>
#include <machine/trap.h>
#include <machine/instr.h>
#include <machine/pmap.h>

#ifdef DDB
#include <machine/db_machdep.h>
#else
#include <machine/frame.h>
#endif

#include <sparc64/fpu/fpu_extern.h>
#include <sparc64/sparc64/cache.h>

#ifndef offsetof
#define	offsetof(s, f) ((int)&((s *)0)->f)
#endif

/* trapstats */
int trapstats = 0;
int protfix = 0;
int udmiss = 0;	/* Number of normal/nucleus data/text miss/protection faults */
int udhit = 0;
int udprot = 0;
int utmiss = 0;
int kdmiss = 0;
int kdhit = 0;
int kdprot = 0;
int ktmiss = 0;
int iveccnt = 0; /* number if normal/nucleus interrupt/interrupt vector faults */
int uintrcnt = 0;
int kiveccnt = 0;
int kintrcnt = 0;
int intristk = 0; /* interrupts when already on intrstack */
int intrpoll = 0; /* interrupts not using vector lists */
int wfill = 0;
int kwfill = 0;
int wspill = 0;
int wspillskip = 0;
int rftucnt = 0;
int rftuld = 0;
int rftudone = 0;
int rftkcnt[5] = { 0, 0, 0, 0, 0 };

/*
 * Initial FPU state is all registers == all 1s, everything else == all 0s.
 * This makes every floating point register a signalling NaN, with sign bit
 * set, no matter how it is interpreted.  Appendix N of the Sparc V8 document
 * seems to imply that we should do this, and it does make sense.
 */
__asm(".align 64");
struct	fpstate64 initfpstate = {
	{ ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0,
	  ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0 }
};

/*
 * There are more than 100 trap types, but most are unused.
 *
 * Trap type 0 is taken over as an `Asynchronous System Trap'.
 * This is left-over Vax emulation crap that should be fixed.
 *
 * Traps not supported on the spitfire are marked with `*',
 * and additions are marked with `+'
 */
static const char T[] = "*trap";
const char *trap_type[] = {
	/* non-user vectors */
	"ast",			/* 0 */
	"power on reset",	/* 1 */
	"watchdog reset",	/* 2 */
	"externally initiated reset",/*3 */
	"software initiated reset",/* 4 */
	"RED state exception",	/* 5 */
	T, T,			/* 6..7 */
	"instruction access exception",	/* 8 */
	"*instruction MMU miss",/* 9 */
	"instruction access error",/* 0a */
	T, T, T, T, T,		/* 0b..0f */
	"illegal instruction",	/* 10 */
	"privileged opcode",	/* 11 */
	"*unimplemented LDD",	/* 12 */
	"*unimplemented STD",	/* 13 */
	T, T, T, T,		/* 14..17 */
	T, T, T, T, T, T, T, T, /* 18..1f */
	"fp disabled",		/* 20 */
	"fp exception ieee 754",/* 21 */
	"fp exception other",	/* 22 */
	"tag overflow",		/* 23 */
	"clean window",		/* 24 */
	T, T, T,		/* 25..27 -- trap continues */
	"division by zero",	/* 28 */
	"*internal processor error",/* 29 */
	T, T, T, T, T, T,	/* 2a..2f */
	"data access exception",/* 30 */
	"*data access MMU miss",/* 31 */
	"data access error",	/* 32 */
	"*data access protection",/* 33 */
	"mem address not aligned",	/* 34 */
	"LDDF mem address not aligned",/* 35 */
	"STDF mem address not aligned",/* 36 */
	"privileged action",	/* 37 */
	"LDQF mem address not aligned",/* 38 */
	"STQF mem address not aligned",/* 39 */
	T, T, T, T, T, T,	/* 3a..3f */
	"*async data error",	/* 40 */
	"level 1 int",		/* 41 */
	"level 2 int",		/* 42 */
	"level 3 int",		/* 43 */
	"level 4 int",		/* 44 */
	"level 5 int",		/* 45 */
	"level 6 int",		/* 46 */
	"level 7 int",		/* 47 */
	"level 8 int",		/* 48 */
	"level 9 int",		/* 49 */
	"level 10 int",		/* 4a */
	"level 11 int",		/* 4b */
	"level 12 int",		/* 4c */
	"level 13 int",		/* 4d */
	"level 14 int",		/* 4e */
	"level 15 int",		/* 4f */
	T, T, T, T, T, T, T, T, /* 50..57 */
	T, T, T, T, T, T, T, T, /* 58..5f */
	"+interrupt vector",	/* 60 */
	"+PA_watchpoint",	/* 61 */
	"+VA_watchpoint",	/* 62 */
	"+corrected ECC error",	/* 63 */
	"+fast instruction access MMU miss",/* 64 */
	T, T, T,		/* 65..67 -- trap continues */
	"+fast data access MMU miss",/* 68 */
	T, T, T,		/* 69..6b -- trap continues */
	"+fast data access protection",/* 6c */
	T, T, T,		/* 6d..6f -- trap continues */
	T, T, T, T, T, T, T, T, /* 70..77 */
	T, T, T, T, T, T, T, T, /* 78..7f */
	"spill 0 normal",	/* 80 */
	T, T, T,		/* 81..83 -- trap continues */
	"spill 1 normal",	/* 84 */
	T, T, T,		/* 85..87 -- trap continues */
	"spill 2 normal",	/* 88 */
	T, T, T,		/* 89..8b -- trap continues */
	"spill 3 normal",	/* 8c */
	T, T, T,		/* 8d..8f -- trap continues */
	"spill 4 normal",	/* 90 */
	T, T, T,		/* 91..93 -- trap continues */
	"spill 5 normal",	/* 94 */
	T, T, T,		/* 95..97 -- trap continues */
	"spill 6 normal",	/* 98 */
	T, T, T,		/* 99..9b -- trap continues */
	"spill 7 normal",	/* 9c */
	T, T, T,		/* 9c..9f -- trap continues */
	"spill 0 other",	/* a0 */
	T, T, T,		/* a1..a3 -- trap continues */
	"spill 1 other",	/* a4 */
	T, T, T,		/* a5..a7 -- trap continues */
	"spill 2 other",	/* a8 */
	T, T, T,		/* a9..ab -- trap continues */
	"spill 3 other",	/* ac */
	T, T, T,		/* ad..af -- trap continues */
	"spill 4 other",	/* b0 */
	T, T, T,		/* b1..b3 -- trap continues */
	"spill 5 other",	/* b4 */
	T, T, T,		/* b5..b7 -- trap continues */
	"spill 6 other",	/* b8 */
	T, T, T,		/* b9..bb -- trap continues */
	"spill 7 other",	/* bc */
	T, T, T,		/* bc..bf -- trap continues */
	"fill 0 normal",	/* c0 */
	T, T, T,		/* c1..c3 -- trap continues */
	"fill 1 normal",	/* c4 */
	T, T, T,		/* c5..c7 -- trap continues */
	"fill 2 normal",	/* c8 */
	T, T, T,		/* c9..cb -- trap continues */
	"fill 3 normal",	/* cc */
	T, T, T,		/* cd..cf -- trap continues */
	"fill 4 normal",	/* d0 */
	T, T, T,		/* d1..d3 -- trap continues */
	"fill 5 normal",	/* d4 */
	T, T, T,		/* d5..d7 -- trap continues */
	"fill 6 normal",	/* d8 */
	T, T, T,		/* d9..db -- trap continues */
	"fill 7 normal",	/* dc */
	T, T, T,		/* dc..df -- trap continues */
	"fill 0 other",		/* e0 */
	T, T, T,		/* e1..e3 -- trap continues */
	"fill 1 other",		/* e4 */
	T, T, T,		/* e5..e7 -- trap continues */
	"fill 2 other",		/* e8 */
	T, T, T,		/* e9..eb -- trap continues */
	"fill 3 other",		/* ec */
	T, T, T,		/* ed..ef -- trap continues */
	"fill 4 other",		/* f0 */
	T, T, T,		/* f1..f3 -- trap continues */
	"fill 5 other",		/* f4 */
	T, T, T,		/* f5..f7 -- trap continues */
	"fill 6 other",		/* f8 */
	T, T, T,		/* f9..fb -- trap continues */
	"fill 7 other",		/* fc */
	T, T, T,		/* fc..ff -- trap continues */

	/* user (software trap) vectors */
	"syscall",		/* 100 */
	"breakpoint",		/* 101 */
	"zero divide",		/* 102 */
	"flush windows",	/* 103 */
	"clean windows",	/* 104 */
	"range check",		/* 105 */
	"fix align",		/* 106 */
	"integer overflow",	/* 107 */
	"svr4 syscall",		/* 108 */
	"4.4 syscall",		/* 109 */
	"kgdb exec",		/* 10a */
	T, T, T, T, T,		/* 10b..10f */
	T, T, T, T, T, T, T, T,	/* 11a..117 */
	T, T, T, T, T, T, T, T,	/* 118..11f */
	"svr4 getcc",		/* 120 */
	"svr4 setcc",		/* 121 */
	"svr4 getpsr",		/* 122 */
	"svr4 setpsr",		/* 123 */
	"svr4 gethrtime",	/* 124 */
	"svr4 gethrvtime",	/* 125 */
	T,			/* 126 */
	"svr4 gethrestime",	/* 127 */
	T, T, T, T, T, T, T, T, /* 128..12f */
	T, T,			/* 130..131 */
	"get condition codes",	/* 132 */
	"set condition codes",	/* 133 */
	T, T, T, T,		/* 134..137 */
	T, T, T, T, T, T, T, T, /* 138..13f */
	T, T, T, T, T, T, T, T, /* 140..147 */
	T, T, T, T, T, T, T, T, /* 148..14f */
	T, T, T, T, T, T, T, T, /* 150..157 */
	T, T, T, T, T, T, T, T, /* 158..15f */
	T, T, T, T,		/* 160..163 */
	"SVID syscall64",	/* 164 */
	"SPARC Intl syscall64",	/* 165 */
	"OS vendor spec syscall",	/* 166 */
	"HW OEM syscall",	/* 167 */
	"ret from deferred trap",	/* 168 */
};

#define	N_TRAP_TYPES	(sizeof trap_type / sizeof *trap_type)

static __inline void share_fpu(struct proc *, struct trapframe64 *);

void trap(struct trapframe64 *tf, unsigned type, vaddr_t pc, long tstate);
void data_access_fault(struct trapframe64 *tf, unsigned type, vaddr_t pc,
	vaddr_t va, vaddr_t sfva, u_long sfsr);
void data_access_error(struct trapframe64 *tf, unsigned type,
	vaddr_t afva, u_long afsr, vaddr_t sfva, u_long sfsr);
void text_access_fault(struct trapframe64 *tf, unsigned type,
	vaddr_t pc, u_long sfsr);
void text_access_error(struct trapframe64 *tf, unsigned type,
	vaddr_t pc, u_long sfsr, vaddr_t afva, u_long afsr);
void syscall(struct trapframe64 *, register_t code, register_t pc);

/*
 * If someone stole the FPU while we were away, do not enable it
 * on return.  This is not done in userret() above as it must follow
 * the ktrsysret() in syscall().  Actually, it is likely that the
 * ktrsysret should occur before the call to userret.
 *
 * Oh, and don't touch the FPU bit if we're returning to the kernel.
 */
static __inline void share_fpu(p, tf)
	struct proc *p;
	struct trapframe64 *tf;
{
	if (!(tf->tf_tstate & TSTATE_PRIV) &&
	    (tf->tf_tstate & TSTATE_PEF) && fpproc != p)
		tf->tf_tstate &= ~TSTATE_PEF;
}

/*
 * Called from locore.s trap handling, for non-MMU-related traps.
 * (MMU-related traps go through mem_access_fault, below.)
 */
void
trap(tf, type, pc, tstate)
	struct trapframe64 *tf;
	unsigned type;
	vaddr_t pc;
	long tstate;
{
	struct proc *p;
	struct pcb *pcb;
	int pstate = (tstate>>TSTATE_PSTATE_SHIFT);
	u_int64_t s;
	int64_t n;
	union sigval sv;

	sv.sival_ptr = (void *)pc;

	/* This steps the PC over the trap. */
#define	ADVANCE (n = tf->tf_npc, tf->tf_pc = n, tf->tf_npc = n + 4)

	uvmexp.traps++;
	/*
	 * Generally, kernel traps cause a panic.  Any exceptions are
	 * handled early here.
	 */
	if (pstate & PSTATE_PRIV) {
#ifdef DDB
		if (type == T_BREAKPOINT) {
			write_all_windows();
			if (kdb_trap(type, tf)) {
				/* ADVANCE; */
				return;
			}
		}
		if (type == T_PA_WATCHPT || type == T_VA_WATCHPT) {
			if (kdb_trap(type, tf)) {
				/* DDB must turn off watchpoints or something */
				return;
			}
		}
#endif
		/*
		 * The kernel needs to use FPU registers for block
		 * load/store.  If we trap in priviliged code, save
		 * the FPU state if there is any and enable the FPU.
		 *
		 * We rely on the kernel code properly enabling the FPU
		 * in %fprs, otherwise we'll hang here trying to enable
		 * the FPU.
		 */
		if (type == T_FPDISABLED) {
			struct proc *newfpproc;

			if (CLKF_INTR((struct clockframe *)tf) || !curproc)
				newfpproc = &proc0;
			else {
				newfpproc = curproc;
				/* force other cpus to give up this fpstate */
				if (newfpproc->p_md.md_fpstate)
					fpusave_proc(newfpproc, 1);
			}
			if (fpproc != newfpproc) {
				s = intr_disable();
				if (fpproc != NULL) {
					/* someone else had it, maybe? */
					savefpstate(fpproc->p_md.md_fpstate);
					fpproc = NULL;
				}
				intr_restore(s);

				/* If we have an allocated fpstate, load it */
				if (newfpproc->p_md.md_fpstate != 0) {
					fpproc = newfpproc;
					loadfpstate(fpproc->p_md.md_fpstate);
				} else
					fpproc = NULL;
			}
			/* Enable the FPU */
			tf->tf_tstate |= (PSTATE_PEF<<TSTATE_PSTATE_SHIFT);
			return;
		}
		if (type != T_SPILL_N_NORM && type != T_FILL_N_NORM)
			goto dopanic;
	}
	if ((p = curproc) == NULL)
		p = &proc0;
	pcb = &p->p_addr->u_pcb;
	p->p_md.md_tf = tf;	/* for ptrace/signals */
	refreshcreds(p);

	switch (type) {

	default:
		if (type < 0x100) {
			extern int trap_trace_dis;
dopanic:
			trap_trace_dis = 1;

			panic("trap type 0x%x (%s): pc=%lx npc=%lx pstate=%b",
			    type, type < N_TRAP_TYPES ? trap_type[type] : T,
			    pc, (long)tf->tf_npc, pstate, PSTATE_BITS);
			/* NOTREACHED */
		}
		KERNEL_LOCK();
		trapsignal(p, SIGILL, type, ILL_ILLOPC, sv);
		KERNEL_UNLOCK();
		break;

	case T_AST:
		p->p_md.md_astpending = 0;
		uvmexp.softs++;
		mi_ast(p, curcpu()->ci_want_resched);
		break;

	case T_RWRET:
		/*
		 * XXX Flushing the user windows here should not be
		 * necessary, but not doing so here causes corruption
		 * of user windows on sun4v.  Flushing them shouldn't
		 * be much of a prefermance penalty since we're
		 * probably going to spill any remaining user windows
		 * anyhow.
		 */
		write_user_windows();
		if (rwindow_save(p) == -1) {
			KERNEL_LOCK();
			trapsignal(p, SIGILL, 0, ILL_BADSTK, sv);
			KERNEL_UNLOCK();
		}
		break;

	case T_ILLINST:
	{
		union instr ins;

		if (copyin((caddr_t)pc, &ins, sizeof(ins)) != 0) {
			/* XXX Can this happen? */
			KERNEL_LOCK();
			trapsignal(p, SIGILL, 0, ILL_ILLOPC, sv);
			KERNEL_UNLOCK();
			break;
		}
		if (ins.i_any.i_op == IOP_mem &&
		    (ins.i_op3.i_op3 == IOP3_LDQF ||
		     ins.i_op3.i_op3 == IOP3_STQF ||
		     ins.i_op3.i_op3 == IOP3_LDQFA ||
		     ins.i_op3.i_op3 == IOP3_STQFA)) {
			if (emul_qf(ins.i_int, p, sv, tf))
				ADVANCE;
			break;
		}
		if (ins.i_any.i_op == IOP_reg &&
		    ins.i_op3.i_op3 == IOP3_POPC &&
		    ins.i_op3.i_rs1 == 0) {
			if (emul_popc(ins.i_int, p, sv, tf))
				ADVANCE;
			break;
		}
		KERNEL_LOCK();
		trapsignal(p, SIGILL, 0, ILL_ILLOPC, sv);	/* XXX code? */
		KERNEL_UNLOCK();
		break;
	}

	case T_INST_EXCEPT:
	case T_TEXTFAULT:
	case T_PRIVINST:
	case T_PRIVACT:
		KERNEL_LOCK();
		trapsignal(p, SIGILL, 0, ILL_ILLOPC, sv);	/* XXX code? */
		KERNEL_UNLOCK();
		break;

	case T_FPDISABLED: {
		struct fpstate64 *fs = p->p_md.md_fpstate;

		if (fs == NULL) {
			KERNEL_LOCK();
			/* NOTE: fpstate must be 64-bit aligned */
			fs = malloc((sizeof *fs), M_SUBPROC, M_WAITOK);
			*fs = initfpstate;
			fs->fs_qsize = 0;
			p->p_md.md_fpstate = fs;
			KERNEL_UNLOCK();
		}

		/*
		 * We may have more FPEs stored up and/or ops queued.
		 * If they exist, handle them and get out.  Otherwise,
		 * resolve the FPU state, turn it on, and try again.
		 *
		 * Ultras should never have a FPU queue.
		 */
		if (fs->fs_qsize) {
			printf("trap: Warning fs_qsize is %d\n",fs->fs_qsize);
			fpu_cleanup(p, fs);
			break;
		}
		if (fpproc != p) {		/* we do not have it */
			/* but maybe another CPU has it? */
			fpusave_proc(p, 1);
			s = intr_disable();
			if (fpproc != NULL)	/* someone else had it */
				savefpstate(fpproc->p_md.md_fpstate);
			loadfpstate(fs);
			fpproc = p;		/* now we do have it */
			intr_restore(s);
			uvmexp.fpswtch++;
		}
		tf->tf_tstate |= (PSTATE_PEF<<TSTATE_PSTATE_SHIFT);
		sparc_wr(fprs, FPRS_FEF, 0);
		break;
	}

	case T_LDQF_ALIGN:
	case T_STQF_ALIGN:
	{
		union instr ins;

		if (copyin((caddr_t)pc, &ins, sizeof(ins)) != 0) {
			/* XXX Can this happen? */
			KERNEL_LOCK();
			trapsignal(p, SIGILL, 0, ILL_ILLOPC, sv);
			KERNEL_UNLOCK();
			break;
		}
		if (ins.i_any.i_op == IOP_mem &&
		    (ins.i_op3.i_op3 == IOP3_LDQF ||
		     ins.i_op3.i_op3 == IOP3_STQF ||
		     ins.i_op3.i_op3 == IOP3_LDQFA ||
		     ins.i_op3.i_op3 == IOP3_STQFA)) {
			if (emul_qf(ins.i_int, p, sv, tf))
				ADVANCE;
		} else {
			KERNEL_LOCK();
			trapsignal(p, SIGILL, 0, ILL_ILLOPC, sv);
			KERNEL_UNLOCK();
		}
		break;
	}

	case T_SPILL_N_NORM:
	case T_FILL_N_NORM:
		/*
		 * We got an alignment trap in the spill/fill handler.
		 *
		 * XXX We really should generate a bus error here, but
		 * we could be on the interrupt stack, and dumping
		 * core from the interrupt stack is not a good idea.
		 * It causes random crashes.
		 */
		KERNEL_LOCK();
		sigexit(p, SIGKILL);
		/* NOTREACHED */
		break;

	case T_ALIGN:
	case T_LDDF_ALIGN:
	case T_STDF_ALIGN:
#if 0
	{
		int64_t dsfsr, dsfar=0, isfsr;

		dsfsr = ldxa(SFSR, ASI_DMMU);
		if (dsfsr & SFSR_FV)
			dsfar = ldxa(SFAR, ASI_DMMU);
		isfsr = ldxa(SFSR, ASI_IMMU);
	}
#endif
		/*
		 * If we're busy doing copyin/copyout continue
		 */
		if (p->p_addr->u_pcb.pcb_onfault) {
			tf->tf_pc = (vaddr_t)p->p_addr->u_pcb.pcb_onfault;
			tf->tf_npc = tf->tf_pc + 4;
			break;
		}

		/* XXX sv.sival_ptr should be the fault address! */
		KERNEL_LOCK();
		trapsignal(p, SIGBUS, 0, BUS_ADRALN, sv);	/* XXX code? */
		KERNEL_UNLOCK();
		break;

	case T_FP_IEEE_754:
	case T_FP_OTHER:
		/*
		 * Clean up after a floating point exception.
		 * fpu_cleanup can (and usually does) modify the
		 * state we save here, so we must `give up' the FPU
		 * chip context.  (The software and hardware states
		 * will not match once fpu_cleanup does its job, so
		 * we must not save again later.)
		 */
		if (p != fpproc)
			panic("fpe without being the FP user");
		s = intr_disable();
		savefpstate(p->p_md.md_fpstate);
		fpproc = NULL;
		intr_restore(s);
		/* tf->tf_psr &= ~PSR_EF; */	/* share_fpu will do this */
		if (type == T_FP_OTHER && p->p_md.md_fpstate->fs_qsize == 0) {
			/*
			 * Push the faulting instruction on the queue;
			 * we might need to emulate it.
			 */
			copyin((caddr_t)pc, &p->p_md.md_fpstate->fs_queue[0].fq_instr, sizeof(int));
			p->p_md.md_fpstate->fs_queue[0].fq_addr = (int *)pc;
			p->p_md.md_fpstate->fs_qsize = 1;
		}
		ADVANCE;
		fpu_cleanup(p, p->p_md.md_fpstate);
		/* fpu_cleanup posts signals if needed */
		break;

	case T_TAGOF:
		KERNEL_LOCK();
		trapsignal(p, SIGEMT, 0, EMT_TAGOVF, sv);	/* XXX code? */
		KERNEL_UNLOCK();
		break;

	case T_BREAKPOINT:
		KERNEL_LOCK();
		trapsignal(p, SIGTRAP, 0, TRAP_BRKPT, sv);
		KERNEL_UNLOCK();
		break;

	case T_DIV0:
		ADVANCE;
		KERNEL_LOCK();
		trapsignal(p, SIGFPE, 0, FPE_INTDIV, sv);
		KERNEL_UNLOCK();
		break;

	case T_CLEANWIN:
		uprintf("T_CLEANWIN\n");	/* XXX Should not get this */
		ADVANCE;
		break;

	case T_FLUSHWIN:
		/* Software window flush for v8 software */
		write_all_windows();
		ADVANCE;
		break;

	case T_RANGECHECK:
		ADVANCE;
		KERNEL_LOCK();
		trapsignal(p, SIGILL, 0, ILL_ILLOPN, sv);	/* XXX code? */
		KERNEL_UNLOCK();
		break;

	case T_FIXALIGN:
		uprintf("T_FIXALIGN\n");
		ADVANCE;
		KERNEL_LOCK();
		trapsignal(p, SIGILL, 0, ILL_ILLOPN, sv);	/* XXX code? */
		KERNEL_UNLOCK();
		break;

	case T_INTOF:
		uprintf("T_INTOF\n");		/* XXX */
		ADVANCE;
		KERNEL_LOCK();
		trapsignal(p, SIGFPE, FPE_INTOVF_TRAP, FPE_INTOVF, sv);
		KERNEL_UNLOCK();
		break;
	}
	userret(p);
	share_fpu(p, tf);
#undef ADVANCE
}

/*
 * Save windows from PCB into user stack, and return 0.  This is used on
 * window overflow pseudo-traps (from locore.s, just before returning to
 * user mode) and when ptrace or sendsig needs a consistent state.
 * As a side effect, rwindow_save() always sets pcb_nsaved to 0.
 *
 * If the windows cannot be saved, pcb_nsaved is restored and we return -1.
 */
int
rwindow_save(struct proc *p)
{
	struct pcb *pcb = &p->p_addr->u_pcb;
	int i;

	for (i = 0; i < pcb->pcb_nsaved; i++) {
		pcb->pcb_rw[i].rw_in[7] ^= pcb->pcb_wcookie;
		if (copyout(&pcb->pcb_rw[i], (void *)(pcb->pcb_rwsp[i] + BIAS),
		    sizeof(struct rwindow64)))
			return (-1);
	}

	pcb->pcb_nsaved = 0;
	return (0);
}

/*
 * Kill user windows (before exec) by writing back to stack or pcb
 * and then erasing any pcb tracks.  Otherwise we might try to write
 * the registers into the new process after the exec.
 */
void
pmap_unuse_final(p)
	struct proc *p;
{

	write_user_windows();
	p->p_addr->u_pcb.pcb_nsaved = 0;
}

/*
 * This routine handles MMU generated faults.  About half
 * of them could be recoverable through uvm_fault.
 */
void
data_access_fault(tf, type, pc, addr, sfva, sfsr)
	struct trapframe64 *tf;
	unsigned type;
	vaddr_t pc;
	vaddr_t addr;
	vaddr_t sfva;
	u_long sfsr;
{
	u_int64_t tstate;
	struct proc *p;
	struct vmspace *vm;
	vaddr_t va;
	int rv;
	vm_prot_t access_type;
	vaddr_t onfault;
	union sigval sv;

	uvmexp.traps++;
	if ((p = curproc) == NULL)	/* safety check */
		p = &proc0;

	tstate = tf->tf_tstate;

	/* Find the faulting va to give to uvm_fault */
	va = trunc_page(addr);

	/*
	 * Now munch on protections.
	 *
	 * If it was a FAST_DATA_ACCESS_MMU_MISS we have no idea what the
	 * access was since the SFSR is not set.  But we should never get
	 * here from there.
	 */
	if (type == T_FDMMU_MISS || (sfsr & SFSR_FV) == 0) {
		/* Punt */
		access_type = PROT_READ;
	} else {
		access_type = (sfsr & SFSR_W) ? PROT_READ | PROT_WRITE
			: PROT_READ;
	}
	if (tstate & TSTATE_PRIV) {
		KERNEL_LOCK();
#ifdef DDB
		extern char Lfsprobe[];
		/*
		 * If this was an access that we shouldn't try to page in,
		 * resume at the fault handler without any action.
		 */
		if (p->p_addr->u_pcb.pcb_onfault == Lfsprobe)
			goto kfault;
#endif

		/*
		 * During autoconfiguration, faults are never OK unless
		 * pcb_onfault is set.  Once running normally we must allow
		 * exec() to cause copy-on-write faults to kernel addresses.
		 */
		if (cold)
			goto kfault;
		if (!(addr & TLB_TAG_ACCESS_CTX)) {
			/* CTXT == NUCLEUS */
			rv = uvm_fault(kernel_map, va, 0, access_type);
			if (rv == 0) {
				KERNEL_UNLOCK();
				return;
			}
			goto kfault;
		}
	} else {
		KERNEL_LOCK();
		p->p_md.md_tf = tf;
	}

	vm = p->p_vmspace;
	/* alas! must call the horrible vm code */
	onfault = (vaddr_t)p->p_addr->u_pcb.pcb_onfault;
	p->p_addr->u_pcb.pcb_onfault = NULL;
	rv = uvm_fault(&vm->vm_map, (vaddr_t)va, 0, access_type);
	p->p_addr->u_pcb.pcb_onfault = (void *)onfault;

	/*
	 * If this was a stack access we keep track of the maximum
	 * accessed stack size.  Also, if uvm_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;
	}
	if (rv != 0) {
		/*
		 * Pagein failed.  If doing copyin/out, return to onfault
		 * address.  Any other page fault in kernel, die; if user
		 * fault, deliver SIGSEGV.
		 */
		if (tstate & TSTATE_PRIV) {
kfault:
			onfault = (long)p->p_addr->u_pcb.pcb_onfault;
			if (!onfault) {
				extern int trap_trace_dis;
				trap_trace_dis = 1; /* Disable traptrace for printf */
				(void) splhigh();
				panic("kernel data fault: pc=%lx addr=%lx",
				    pc, addr);
				/* NOTREACHED */
			}
			tf->tf_pc = onfault;
			tf->tf_npc = onfault + 4;
			KERNEL_UNLOCK();
			return;
		}

		if (type == T_FDMMU_MISS || (sfsr & SFSR_FV) == 0)
			sv.sival_ptr = (void *)va;
		else
			sv.sival_ptr = (void *)sfva;

		if (rv == ENOMEM) {
			printf("UVM: pid %d (%s), uid %d killed: out of swap\n",
			       p->p_pid, p->p_comm,
			       p->p_ucred ? (int)p->p_ucred->cr_uid : -1);
			trapsignal(p, SIGKILL, access_type, SEGV_MAPERR, sv);
		} else {
			trapsignal(p, SIGSEGV, access_type, SEGV_MAPERR, sv);
		}
	}

	if ((tstate & TSTATE_PRIV) == 0) {
		KERNEL_UNLOCK();
		userret(p);
		share_fpu(p, tf);
	} else {
		KERNEL_UNLOCK();
	}
}

/*
 * This routine handles deferred errors caused by the memory
 * or I/O bus subsystems.  Most of these are fatal, and even
 * if they are not, recovery is painful.  Also, the TPC and
 * TNPC values are probably not valid if we're not doing a
 * special PEEK/POKE code sequence.
 */
void
data_access_error(tf, type, afva, afsr, sfva, sfsr)
	struct trapframe64 *tf;
	unsigned type;
	vaddr_t sfva;
	u_long sfsr;
	vaddr_t afva;
	u_long afsr;
{
	u_long pc;
	u_int64_t tstate;
	struct proc *p;
	vaddr_t onfault;
	union sigval sv;

	uvmexp.traps++;
	if ((p = curproc) == NULL)	/* safety check */
		p = &proc0;

	/*
	 * Catch PCI config space reads.
	 */
	if (curcpu()->ci_pci_probe) {
		curcpu()->ci_pci_fault = 1;
		goto out;
	}

	pc = tf->tf_pc;
	tstate = tf->tf_tstate;

	sv.sival_ptr = (void *)pc;

	onfault = (long)p->p_addr->u_pcb.pcb_onfault;
	printf("data error type %x sfsr=%lx sfva=%lx afsr=%lx afva=%lx tf=%p\n",
		type, sfsr, sfva, afsr, afva, tf);

	if (afsr == 0 && sfsr == 0) {
		printf("data_access_error: no fault\n");
		goto out;	/* No fault. Why were we called? */
	}

	if (tstate & TSTATE_PRIV) {

		if (!onfault) {
			extern int trap_trace_dis;

			trap_trace_dis = 1; /* Disable traptrace for printf */
			(void) splhigh();
			panic("data fault: pc=%lx addr=%lx sfsr=%lb",
				(u_long)pc, (long)sfva, sfsr, SFSR_BITS);
			/* NOTREACHED */
		}

		/*
		 * If this was a priviliged error but not a probe, we
		 * cannot recover, so panic.
		 */
		if (afsr & ASFR_PRIV) {
			panic("Privileged Async Fault: AFAR %p AFSR %lx\n%lb",
				(void *)afva, afsr, afsr, AFSR_BITS);
			/* NOTREACHED */
		}
		tf->tf_pc = onfault;
		tf->tf_npc = onfault + 4;
		return;
	}

	KERNEL_LOCK();
	trapsignal(p, SIGSEGV, PROT_READ | PROT_WRITE, SEGV_MAPERR, sv);
	KERNEL_UNLOCK();
out:

	if ((tstate & TSTATE_PRIV) == 0) {
		userret(p);
		share_fpu(p, tf);
	}
}

/*
 * This routine handles MMU generated faults.  About half
 * of them could be recoverable through uvm_fault.
 */
void
text_access_fault(tf, type, pc, sfsr)
	unsigned type;
	vaddr_t pc;
	struct trapframe64 *tf;
	u_long sfsr;
{
	u_int64_t tstate;
	struct proc *p;
	struct vmspace *vm;
	vaddr_t va;
	int rv;
	vm_prot_t access_type;
	union sigval sv;

	sv.sival_ptr = (void *)pc;

	uvmexp.traps++;
	if ((p = curproc) == NULL)	/* safety check */
		panic("text_access_fault: no curproc");

	tstate = tf->tf_tstate;

	va = trunc_page(pc);

	/* Now munch on protections... */

	access_type = PROT_EXEC;
	if (tstate & TSTATE_PRIV) {
		extern int trap_trace_dis;
		trap_trace_dis = 1; /* Disable traptrace for printf */
		(void) splhigh();
		panic("kernel text_access_fault: pc=%lx va=%lx", pc, va);
		/* NOTREACHED */
	} else
		p->p_md.md_tf = tf;

	KERNEL_LOCK();

	vm = p->p_vmspace;
	/* alas! must call the horrible vm code */
	rv = uvm_fault(&vm->vm_map, va, 0, access_type);

	/*
	 * If this was a stack access we keep track of the maximum
	 * accessed stack size.  Also, if uvm_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;
	}
	if (rv != 0) {
		/*
		 * Pagein failed. Any other page fault in kernel, die; if user
		 * fault, deliver SIGSEGV.
		 */
		if (tstate & TSTATE_PRIV) {
			extern int trap_trace_dis;
			trap_trace_dis = 1; /* Disable traptrace for printf */
			(void) splhigh();
			panic("kernel text fault: pc=%llx", (unsigned long long)pc);
			/* NOTREACHED */
		}
		trapsignal(p, SIGSEGV, access_type, SEGV_MAPERR, sv);
	}

	KERNEL_UNLOCK();

	if ((tstate & TSTATE_PRIV) == 0) {
		userret(p);
		share_fpu(p, tf);
	}
}

/*
 * This routine handles deferred errors caused by the memory
 * or I/O bus subsystems.  Most of these are fatal, and even
 * if they are not, recovery is painful.  Also, the TPC and
 * TNPC values are probably not valid if we're not doing a
 * special PEEK/POKE code sequence.
 */
void
text_access_error(tf, type, pc, sfsr, afva, afsr)
	struct trapframe64 *tf;
	unsigned type;
	vaddr_t pc;
	u_long sfsr;
	vaddr_t afva;
	u_long afsr;
{
	int64_t tstate;
	struct proc *p;
	struct vmspace *vm;
	vaddr_t va;
	int rv;
	vm_prot_t access_type;
	union sigval sv;

	sv.sival_ptr = (void *)pc;
	uvmexp.traps++;
	if ((p = curproc) == NULL)	/* safety check */
		p = &proc0;

	tstate = tf->tf_tstate;

	if ((afsr) != 0) {
		extern int trap_trace_dis;

		trap_trace_dis++; /* Disable traptrace for printf */
		printf("text_access_error: memory error...\n");
		printf("text memory error type %d sfsr=%lx sfva=%lx afsr=%lx afva=%lx tf=%p\n",
		       type, sfsr, pc, afsr, afva, tf);
		trap_trace_dis--; /* Reenable traptrace for printf */

		if (tstate & TSTATE_PRIV)
			panic("text_access_error: kernel memory error");

		/* User fault -- Berr */
		KERNEL_LOCK();
		trapsignal(p, SIGBUS, 0, BUS_ADRALN, sv);
		KERNEL_UNLOCK();
	}

	if ((sfsr & SFSR_FV) == 0 || (sfsr & SFSR_FT) == 0)
		goto out;	/* No fault. Why were we called? */

	va = trunc_page(pc);

	/* Now munch on protections... */
	access_type = PROT_EXEC;
	if (tstate & TSTATE_PRIV) {
		extern int trap_trace_dis;
		trap_trace_dis = 1; /* Disable traptrace for printf */
		(void) splhigh();
		panic("kernel text error: pc=%lx sfsr=%lb", pc,
		    sfsr, SFSR_BITS);
		/* NOTREACHED */
	} else
		p->p_md.md_tf = tf;

	KERNEL_LOCK();

	vm = p->p_vmspace;
	/* alas! must call the horrible vm code */
	rv = uvm_fault(&vm->vm_map, va, 0, access_type);

	/*
	 * If this was a stack access we keep track of the maximum
	 * accessed stack size.  Also, if uvm_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;
	}
	if (rv != 0) {
		/*
		 * Pagein failed.  If doing copyin/out, return to onfault
		 * address.  Any other page fault in kernel, die; if user
		 * fault, deliver SIGSEGV.
		 */
		if (tstate & TSTATE_PRIV) {
			extern int trap_trace_dis;
			trap_trace_dis = 1; /* Disable traptrace for printf */
			(void) splhigh();
			panic("kernel text error: pc=%lx sfsr=%lb", pc,
			    sfsr, SFSR_BITS);
			/* NOTREACHED */
		}
		trapsignal(p, SIGSEGV, access_type, SEGV_MAPERR, sv);
	}

	KERNEL_UNLOCK();

out:
	if ((tstate & TSTATE_PRIV) == 0) {
		userret(p);
		share_fpu(p, tf);
	}
}

/*
 * System calls.  `pc' is just a copy of tf->tf_pc.
 *
 * Note that the things labelled `out' registers in the trapframe were the
 * `in' registers within the syscall trap code (because of the automatic
 * `save' effect of each trap).  They are, however, the %o registers of the
 * thing that made the system call, and are named that way here.
 *
 * 32-bit system calls on a 64-bit system are a problem.  Each system call
 * argument is stored in the smaller of the argument's true size or a
 * `register_t'.  Now on a 64-bit machine all normal types can be stored in a
 * `register_t'.  (The only exceptions would be 128-bit `quad's or 128-bit
 * extended precision floating point values, which we don't support.)  For
 * 32-bit syscalls, 64-bit integers like `off_t's, double precision floating
 * point values, and several other types cannot fit in a 32-bit `register_t'.
 * These will require reading in two `register_t' values for one argument.
 *
 * In order to calculate the true size of the arguments and therefore whether
 * any argument needs to be split into two slots, the system call args
 * structure needs to be built with the appropriately sized register_t.
 * Otherwise the emul needs to do some magic to split oversized arguments.
 *
 * We can handle most this stuff for normal syscalls by using either a 32-bit
 * or 64-bit array of `register_t' arguments.  Unfortunately ktrace always
 * expects arguments to be `register_t's, so it loses badly.  What's worse,
 * ktrace may need to do size translations to massage the argument array
 * appropriately according to the emulation that is doing the ktrace.
 *
 */
void
syscall(tf, code, pc)
	register_t code;
	struct trapframe64 *tf;
	register_t pc;
{
	int i, nsys, nap;
	int64_t *ap;
	const struct sysent *callp;
	struct proc *p;
	int error, new;
	register_t args[8];
	register_t rval[2];

	uvmexp.syscalls++;
	p = curproc;
#ifdef DIAGNOSTIC
	if (tf->tf_tstate & TSTATE_PRIV)
		panic("syscall from kernel");
	if (curpcb != &p->p_addr->u_pcb)
		panic("syscall: cpcb/ppcb mismatch");
	if (tf != (struct trapframe64 *)((caddr_t)curpcb + USPACE) - 1)
		panic("syscall: trapframe");
#endif
	p->p_md.md_tf = tf;
	new = code & SYSCALL_G2RFLAG;
	code &= ~SYSCALL_G2RFLAG;

	callp = p->p_p->ps_emul->e_sysent;
	nsys = p->p_p->ps_emul->e_nsysent;

	/*
	 * The first six system call arguments are in the six %o registers.
	 * Any arguments beyond that are in the `argument extension' area
	 * of the user's stack frame (see <machine/frame.h>).
	 *
	 * Check for ``special'' codes that alter this, namely syscall and
	 * __syscall.  The latter takes a quad syscall number, so that other
	 * arguments are at their natural alignments.  Adjust the number
	 * of ``easy'' arguments as appropriate; we will copy the hard
	 * ones later as needed.
	 */
	ap = &tf->tf_out[0];
	nap = 6;

	switch (code) {
	case SYS_syscall:
		code = *ap++;
		nap--;
		break;
	case SYS___syscall:
		if (code < nsys && callp[code].sy_call !=
		    callp[p->p_p->ps_emul->e_nosys].sy_call)
			break; /* valid system call */
		if (tf->tf_out[6] & 1L) {
			/* longs *are* quadwords */
			code = ap[0];
			ap += 1;
			nap -= 1;
		} else {
			code = ap[_QUAD_LOWWORD];
			ap += 2;
			nap -= 2;
		}
		break;
	}

	if (code < 0 || code >= nsys)
		callp += p->p_p->ps_emul->e_nosys;
	else if (tf->tf_out[6] & 1L) {
		register_t *argp;

		callp += code;
		i = callp->sy_narg; /* Why divide? */
		if (i > nap) {	/* usually false */
			if (i > 8)
				panic("syscall nargs");
			/* Read the whole block in */
			if ((error = copyin((caddr_t)(u_long)tf->tf_out[6]
			    + BIAS + offsetof(struct frame64, fr_argx),
			    &args[nap], (i - nap) * sizeof(register_t))))
				goto bad;
			i = nap;
		}
		/*
		 * It should be faster to do <= 6 longword copies than
		 * to call bcopy
		 */
		for (argp = args; i--;)
			*argp++ = *ap++;
	} else {
		error = EFAULT;
		goto bad;
	}

	rval[0] = 0;
	rval[1] = tf->tf_out[1];

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

	switch (error) {
		vaddr_t dest;
	case 0:
		/* Note: fork() does not return here in the child */
		tf->tf_out[0] = rval[0];
		tf->tf_out[1] = rval[1];
		if (new) {
			/* jmp %g2 on success */
			dest = tf->tf_global[2];
			if (dest & 3) {
				error = EINVAL;
				goto bad;
			}
		} else {
			/* old system call convention: clear C on success */
			tf->tf_tstate &= ~(((int64_t)(ICC_C|XCC_C))<<TSTATE_CCR_SHIFT);	/* success */
			dest = tf->tf_npc;
		}
		tf->tf_pc = dest;
		tf->tf_npc = dest + 4;
		break;

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

	default:
	bad:
		tf->tf_out[0] = error;
		tf->tf_tstate |= (((int64_t)(ICC_C|XCC_C))<<TSTATE_CCR_SHIFT);	/* fail */
		dest = tf->tf_npc;
		tf->tf_pc = dest;
		tf->tf_npc = dest + 4;
		break;
	}

	mi_syscall_return(p, code, error, rval);
	share_fpu(p, tf);
}

/*
 * Process the tail end of a fork() for the child.
 */
void
child_return(arg)
	void *arg;
{
	struct proc *p = (struct proc *)arg;
	struct trapframe64 *tf = p->p_md.md_tf;

	/*
	 * Return values in the frame set by cpu_fork().
	 */
	tf->tf_out[0] = 0;
	tf->tf_out[1] = 0;
	tf->tf_tstate &= ~(((int64_t)(ICC_C|XCC_C))<<TSTATE_CCR_SHIFT);

	KERNEL_UNLOCK();

	mi_child_return(p);
}


code:
/*	$OpenBSD: vm_machdep.c,v 1.33 2015/05/05 02:13:47 guenther Exp $	*/
/*	$NetBSD: vm_machdep.c,v 1.38 2001/06/30 00:02:20 eeh Exp $ */

/*
 * Copyright (c) 1996
 *	The President and Fellows of Harvard College. All rights reserved.
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This software was developed by the Computer Systems Engineering group
 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
 * contributed to Berkeley.
 *
 * 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, Lawrence Berkeley Laboratory.
 *	This product includes software developed by Harvard University.
 *
 * 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 Harvard University.
 *	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.
 *
 *	@(#)vm_machdep.c	8.2 (Berkeley) 9/23/93
 */

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

#include <uvm/uvm_extern.h>

#include <machine/cpu.h>
#include <machine/frame.h>
#include <machine/trap.h>
#include <machine/bus.h>

#include <sparc64/sparc64/cache.h>

/*
 * Map a user I/O request into kernel virtual address space.
 * Note: the pages are already locked by uvm_vslock(), so we
 * do not need to pass an access_type to pmap_enter().
 */
void
vmapbuf(struct buf *bp, vsize_t len)
{
	struct pmap *upmap, *kpmap;
	vaddr_t uva;	/* User VA (map from) */
	vaddr_t kva;	/* Kernel VA (new to) */
	paddr_t pa; 	/* physical address */
	vsize_t off;

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

	/*
	 * XXX:  It might be better to round/trunc to a
	 * segment boundary to avoid VAC problems!
	 */
	bp->b_saveaddr = bp->b_data;
	uva = trunc_page((vaddr_t)bp->b_data);
	off = (vaddr_t)bp->b_data - uva;
	len = round_page(off + len);
	kva = uvm_km_valloc_prefer_wait(phys_map, len, uva);
	bp->b_data = (caddr_t)(kva + off);

	upmap = vm_map_pmap(&bp->b_proc->p_vmspace->vm_map);
	kpmap = vm_map_pmap(kernel_map);
	do {
		if (pmap_extract(upmap, uva, &pa) == FALSE)
			panic("vmapbuf: null page frame");
		/* Now map the page into kernel space. */
		pmap_enter(pmap_kernel(), kva,
		    pa /* | PMAP_NC */,
		    PROT_READ | PROT_WRITE,
		    PROT_READ | PROT_WRITE | PMAP_WIRED);

		uva += PAGE_SIZE;
		kva += PAGE_SIZE;
		len -= PAGE_SIZE;
	} while (len);
	pmap_update(pmap_kernel());
}

/*
 * Unmap a previously-mapped user I/O request.
 */
void
vunmapbuf(struct buf *bp, vsize_t len)
{
	vaddr_t kva;
	vsize_t off;

	if ((bp->b_flags & B_PHYS) == 0)
		panic("vunmapbuf");

	kva = trunc_page((vaddr_t)bp->b_data);
	off = (vaddr_t)bp->b_data - kva;
	len = round_page(off + len);

	pmap_remove(pmap_kernel(), kva, kva + len);
	pmap_update(pmap_kernel());
	uvm_km_free_wakeup(phys_map, kva, len);
	bp->b_data = bp->b_saveaddr;
	bp->b_saveaddr = NULL;
}

/*
 * The offset of the topmost frame in the kernel stack.
 */
#define	TOPFRAMEOFF (USPACE-sizeof(struct trapframe)-CC64FSZ)
#define	STACK_OFFSET	BIAS

#ifdef DEBUG
char cpu_forkname[] = "cpu_fork()";
#endif

/*
 * Finish a fork operation, with process p2 nearly set up.
 * Copy and update the pcb and trap frame, making the child ready to run.
 *
 * Rig the child's kernel stack so that it will start out in
 * proc_trampoline() and call child_return() with p2 as an
 * argument. This causes the newly-created child process to go
 * directly to user level with an apparent return value of 0 from
 * fork(), while the parent process returns normally.
 *
 * p1 is the process being forked; if p1 == &proc0, we are creating
 * a kernel thread, and the return path and argument are specified with
 * `func' and `arg'.
 *
 * If an alternate user-level stack is requested (with non-zero values
 * in both the stack and stacksize args), set up the user stack pointer
 * accordingly.
 */
void
cpu_fork(p1, p2, stack, stacksize, func, arg)
	struct proc *p1, *p2;
	void *stack;
	size_t stacksize;
	void (*func)(void *);
	void *arg;
{
	struct pcb *opcb = &p1->p_addr->u_pcb;
	struct pcb *npcb = &p2->p_addr->u_pcb;
	struct trapframe *tf2;
	struct rwindow *rp;
	extern struct proc proc0;

	/*
	 * Save all user registers to p1's stack or, in the case of
	 * user registers and invalid stack pointers, to opcb.
	 * We then copy the whole pcb to p2; when switch() selects p2
	 * to run, it will run at the `proc_trampoline' stub, rather
	 * than returning at the copying code below.
	 *
	 * If process p1 has an FPU state, we must copy it.  If it is
	 * the FPU user, we must save the FPU state first.
	 */

#ifdef NOTDEF_DEBUG
	printf("cpu_fork()\n");
#endif
	if (p1 == curproc) {
		write_user_windows();

		/*
		 * We're in the kernel, so we don't really care about
		 * %ccr or %asi.  We do want to duplicate %pstate and %cwp.
		 */
		opcb->pcb_pstate = getpstate();
		opcb->pcb_cwp = getcwp();
	}
#ifdef DIAGNOSTIC
	else if (p1 != &proc0)
		panic("cpu_fork: curproc");
#endif
#ifdef DEBUG
	/* prevent us from having NULL lastcall */
	opcb->lastcall = cpu_forkname;
#else
	opcb->lastcall = NULL;
#endif
	bcopy((caddr_t)opcb, (caddr_t)npcb, sizeof(struct pcb));
	if (p1->p_md.md_fpstate) {
		fpusave_proc(p1, 1);
		p2->p_md.md_fpstate = malloc(sizeof(struct fpstate64),
		    M_SUBPROC, M_WAITOK);
		bcopy(p1->p_md.md_fpstate, p2->p_md.md_fpstate,
		    sizeof(struct fpstate64));
	} else
		p2->p_md.md_fpstate = NULL;

	/*
	 * Setup (kernel) stack frame that will by-pass the child
	 * out of the kernel. (The trap frame invariably resides at
	 * the tippity-top of the u. area.)
	 */
	tf2 = p2->p_md.md_tf = (struct trapframe *)
			((long)npcb + USPACE - sizeof(*tf2));

	/* Copy parent's trapframe */
	*tf2 = *(struct trapframe *)((long)opcb + USPACE - sizeof(*tf2));

	/*
	 * If specified, give the child a different stack, offset and
	 * with space reserved for the frame, and zero the frame pointer.
	 */
	if (stack != NULL) {
		tf2->tf_out[6] = (u_int64_t)(u_long)stack + stacksize
		    - (BIAS + CC64FSZ);
		tf2->tf_in[6] = 0;
	}

	/* Duplicate efforts of syscall(), but slightly differently */
	if (tf2->tf_global[1] & SYSCALL_G2RFLAG) {
		/* jmp %g2 (or %g7, deprecated) on success */
		tf2->tf_npc = tf2->tf_global[2];
	} else {
		/*
		 * old system call convention: clear C on success
		 * note: proc_trampoline() sets a fresh psr when
		 * returning to user mode.
		 */
		/*tf2->tf_psr &= ~PSR_C;   -* success */
	}

	/* Set return values in child mode */
	tf2->tf_out[0] = 0;
	tf2->tf_out[1] = 1;

	/* Skip trap instruction. */
	tf2->tf_pc = tf2->tf_npc;
	tf2->tf_npc += 4;

	/* Construct kernel frame to return to in cpu_switch() */
	rp = (struct rwindow *)((u_long)npcb + TOPFRAMEOFF);
	*rp = *(struct rwindow *)((u_long)opcb + TOPFRAMEOFF);
	rp->rw_local[0] = (long)func;		/* Function to call */
	rp->rw_local[1] = (long)arg;		/* and its argument */

	npcb->pcb_pc = (long)proc_trampoline - 8;
	npcb->pcb_sp = (long)rp - STACK_OFFSET;

	/* Need to create a %tstate if we're forking from proc0. */
	if (p1 == &proc0)
		tf2->tf_tstate =
		    ((u_int64_t)ASI_PRIMARY_NO_FAULT << TSTATE_ASI_SHIFT) |
		    ((PSTATE_USER) << TSTATE_PSTATE_SHIFT);
	else
		/* Clear condition codes and disable FPU. */
		tf2->tf_tstate &=
		    ~((PSTATE_PEF << TSTATE_PSTATE_SHIFT) | TSTATE_CCR);

#ifdef NOTDEF_DEBUG
	printf("cpu_fork: Copying over trapframe: otf=%p ntf=%p sp=%p opcb=%p npcb=%p\n",
	       (struct trapframe *)((char *)opcb + USPACE - sizeof(*tf2)), tf2, rp, opcb, npcb);
	printf("cpu_fork: tstate=%lx pc=%lx npc=%lx rsp=%lx\n",
	       (long)tf2->tf_tstate, (long)tf2->tf_pc, (long)tf2->tf_npc,
	       (long)(tf2->tf_out[6]));
	Debugger();
#endif
}

/*
 * These are the "function" entry points in locore.s to handle IPI's.
 */
void	ipi_save_fpstate(void);
void	ipi_drop_fpstate(void);

void
fpusave_cpu(struct cpu_info *ci, int save)
{
	struct proc *p;

	KDASSERT(ci == curcpu());

	p = ci->ci_fpproc;
	if (p == NULL)
		return;

	if (save)
		savefpstate(p->p_md.md_fpstate);
	else
		clearfpstate();

	ci->ci_fpproc = NULL;
}

void
fpusave_proc(struct proc *p, int save)
{
	struct cpu_info *ci = curcpu();

#ifdef MULTIPROCESSOR
	if (p == ci->ci_fpproc) {
		u_int64_t s = intr_disable();
		fpusave_cpu(ci, save);
		intr_restore(s);
		return;
	}

	for (ci = cpus; ci != NULL; ci = ci->ci_next) {
		if (ci == curcpu())
			continue;
		if (ci->ci_fpproc != p)
			continue;
		sparc64_send_ipi(ci->ci_itid,
		    save ? ipi_save_fpstate : ipi_drop_fpstate, (vaddr_t)p, 0);
		while(ci->ci_fpproc == p)
			sparc_membar(Sync);
		break;
	}
#else
	if (p == ci->ci_fpproc)
		fpusave_cpu(ci, save);
#endif
}

/*
 * cpu_exit is called as the last action during exit.
 *
 * We clean up a little and then call sched_exit() with the old proc
 * as an argument.  sched_exit() schedules the old vmspace and stack
 * to be freed, then selects a new process to run.
 */
void
cpu_exit(struct proc *p)
{
	if (p->p_md.md_fpstate != NULL) {
		fpusave_proc(p, 0);
		free(p->p_md.md_fpstate, M_SUBPROC, 0);
	}

	pmap_deactivate(p);
	sched_exit(p);
}


code:
/Makefile/1.7/Wed Nov 26 08:15:41 2014//TOPENBSD_5_8_BASE
/Makefile.inc/1.10/Sun Apr 10 12:32:11 2011//TOPENBSD_5_8_BASE
D/bootblk////
D/libsa////
D/libz////
D/ofwboot////
D/ofwboot.net////
D/ofwbootfd////


code:
#	$OpenBSD: Makefile,v 1.7 2014/11/26 08:15:41 stsp Exp $

.if ${MACHINE} == "sparc64"
SUBDIR= bootblk libsa libz ofwboot ofwboot.net ofwbootfd
.endif

.include <bsd.subdir.mk>


code:
#	$OpenBSD: Makefile.inc,v 1.10 2011/04/10 12:32:11 miod Exp $
#	$NetBSD: Makefile.inc,v 1.7 2000/08/20 14:57:16 mrg Exp $

.ifndef __INCLUDED_STAND_MAKEFILE_INC
__INCLUDED_STAND_MAKEFILE_INC=

CEXTRAFLAGS?=-D_LP64
AFLAGS+= -Wa,-Av9a

BINDIR=		/usr/mdec

STANDALONE?=	-D_STANDALONE

.if ${MACHINE} == "sparc64"
CPPFLAGS+=	${STANDALONE} -DSUN4U
CPPFLAGS+=	-I.

CFLAGS+=	-fno-stack-protector
CFLAGS+=	-msoft-float
CFLAGS+=	-fno-builtin-vprintf -fno-builtin-printf -fno-builtin-putchar
CFLAGS+=	-fno-builtin-exit
.endif

### Figure out what to use for libsa
LIBSADIR=               ${.CURDIR}/../libsa

.if exists(${LIBSADIR}/${__objdir})
LIBSAOBJDIR=            ${LIBSADIR}/${__objdir}
.else
LIBSAOBJDIR=            ${LIBSADIR}
.endif

LIBSA=                  ${LIBSAOBJDIR}/libsa.a

### Figure out what to use for libz
LIBZDIR=                ${.CURDIR}/../libz

.if exists(${LIBZDIR}/${__objdir})
LIBZOBJDIR=             ${LIBZDIR}/${__objdir}
.else
LIBZOBJDIR=             ${LIBZDIR}
.endif

LIBZ=                   ${LIBZOBJDIR}/libz.a

.endif


code:
#	$OpenBSD: Makefile,v 1.12 2014/07/15 22:14:37 deraadt Exp $
#	$NetBSD: Makefile,v 1.1 2000/08/20 14:58:45 mrg Exp $

CURDIR=	${.CURDIR}
S=	${CURDIR}/../../../..

#
# Override normal settings
#

CLEANFILES=	assym.fth.h assym.fth.h.tmp machine \
		bootblk bootblk.text bootblk.text.tmp

NOMAN=
STRIPFLAG=
BINMODE=644

INCLUDES=	-I. -I$S/arch -I$S -nostdinc
CPPFLAGS=	${INCLUDES} ${IDENT} ${PARAM}

.if !make(libdep) && !make(sadep) && !make(salibdir) && !make(obj)
.BEGIN:
	@([ -h machine ] || ln -s ${.CURDIR}/../../include machine)
.endif

all: bootblk.text bootblk

assym.fth.h: ${.CURDIR}/genassym.sh genfth.cf
	sh ${.CURDIR}/genassym.sh ${CC} ${CFLAGS} \
	    ${CPPFLAGS} ${PROF} <${.CURDIR}/genfth.cf >assym.fth.h.tmp && \
	    mv -f assym.fth.h.tmp assym.fth.h

bootblk.text: bootblk.fth assym.fth.h
	awk '/fload/ { file=$$2; while ((ret = getline <file) == 1) print; close(file); if (ret == -1) print "unable to read " file >"/dev/stderr"; next }; !/fload/' \
	    ${.CURDIR}/bootblk.fth >bootblk.text.tmp && \
	    mv -f bootblk.text.tmp bootblk.text

bootblk: bootblk.fth assym.fth.h
	fgen -o bootblk ${.CURDIR}/bootblk.fth

beforedepend:
	@touch .depend

beforeinstall:
	${INSTALL} ${INSTALL_COPY} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
	    bootblk ${DESTDIR}/usr/mdec

#
# The following are if you grab the fakeboot program from the Sun website
#

fake: bootblk bootblk.text
	../fakeboot/fakeboot -elf32 <bootblk >/bootblk
	../fakeboot/fakeboot -elf32 <bootblk.text >/bootblk.text

.include <bsd.prog.mk>


code:
\	$OpenBSD: bootblk.fth,v 1.8 2014/11/26 19:57:41 stsp Exp $
\	$NetBSD: bootblk.fth,v 1.3 2001/08/15 20:10:24 eeh Exp $
\
\	IEEE 1275 Open Firmware Boot Block
\
\	Parses disklabel and UFS and loads the file called `ofwboot'
\
\
\	Copyright (c) 1998 Eduardo Horvath.
\	All rights reserved.
\
\	Redistribution and use in source and binary forms, with or without
\	modification, are permitted provided that the following conditions
\	are met:
\	1. Redistributions of source code must retain the above copyright
\	   notice, this list of conditions and the following disclaimer.
\	2. Redistributions in binary form must reproduce the above copyright
\	   notice, this list of conditions and the following disclaimer in the
\	   documentation and/or other materials provided with the distribution.
\	3. All advertising materials mentioning features or use of this software
\	   must display the following acknowledgement:
\	     This product includes software developed by Eduardo Horvath.
\	4. The name of the author may not 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.
\

offset16
hex
headers

false value boot-debug?

\
\ First some housekeeping:  Open /chosen and set up vectors into
\	client-services

" /chosen" find-package 0=  if ." Cannot find /chosen" 0 then
constant chosen-phandle

" /openprom/client-services" find-package 0=  if
	." Cannot find client-services" cr abort
then constant cif-phandle

defer cif-claim ( align size virt -- base )
defer cif-release ( size virt -- )
defer cif-open ( cstr -- ihandle|0 )
defer cif-close ( ihandle -- )
defer cif-read ( len adr ihandle -- #read )
defer cif-seek ( low high ihandle -- -1|0|1 )
\ defer cif-peer ( phandle -- phandle )
\ defer cif-getprop ( len adr cstr phandle -- )

: find-cif-method ( method,len -- xf )
   cif-phandle find-method drop
;

" claim" find-cif-method to cif-claim
" open" find-cif-method to cif-open
" close" find-cif-method to cif-close
" read" find-cif-method to cif-read
" seek" find-cif-method to cif-seek

: twiddle ( -- ) ." ." ; \ Need to do this right.  Just spit out periods for now.

\
\ Support routines
\

: strcmp ( s1 l1 s2 l2 -- true:false )
   rot tuck <> if  3drop false exit then
   comp 0=
;

\ Move string into buffer

: strmov ( s1 l1 d -- d l1 )
   dup 2over swap -rot		( s1 l1 d s1 d l1 )
   move				( s1 l1 d )
   rot drop swap
;

\ Move s1 on the end of s2 and return the result

: strcat ( s1 l1 s2 l2 -- d tot )
   2over swap 				( s1 l1 s2 l2 l1 s1 )
   2over + rot				( s1 l1 s2 l2 s1 d l1 )
   move rot + 				( s1 s2 len )
   rot drop				( s2 len )
;

: strchr ( s1 l1 c -- s2 l2 )
   begin
      dup 2over 0= if			( s1 l1 c c s1  )
         2drop drop exit then
      c@ = if				( s1 l1 c )
         drop exit then
      -rot /c - swap ca1+		( c l2 s2 )
     swap rot
  again
;

: cstr ( ptr -- str len )
   dup
   begin dup c@ 0<>  while + repeat
   over -
;

\
\ BSD FFS parameters
\

fload	assym.fth.h

sbsize buffer: sb-buf
-1 value boot-ihandle
dev_bsize value bsize

: strategy ( addr size start -- nread )
   bsize * 0 " seek" boot-ihandle $call-method
   -1 = if
      ." strategy: Seek failed" cr
      abort
   then
   " read" boot-ihandle $call-method
;

\
\ Cylinder group macros
\

: cgbase ( cg fs -- cgbase ) fs_fpg l@ * ;
: cgstart ( cg fs -- cgstart )
   2dup fs_cgmask l@ not and		( cg fs stuff -- )
   over fs_cgoffset l@ * -rot		( stuffcg fs -- )
   cgbase +
;
: cgdmin ( cg fs -- 1st-data-block ) dup fs_dblkno l@ -rot cgstart + ;
: cgimin ( cg fs -- inode-block ) dup fs_iblkno l@ -rot cgstart + ;
: cgsblock ( cg fs -- super-block ) dup fs_sblkno l@ -rot cgstart + ;
: cgstod ( cg fs -- cg-block ) dup fs_cblkno l@ -rot cgstart + ;

\
\ Block and frag position macros
\

: blkoff ( pos fs -- off ) fs_qbmask x@ and ;
: fragoff ( pos fs -- off ) fs_qfmask x@ and ;
: lblktosize ( blk fs -- off ) fs_bshift l@ << ;
: lblkno ( pos fs -- off ) fs_bshift l@ >> ;
: numfrags ( pos fs -- off ) fs_fshift l@ >> ;
: blkroundup ( pos fs -- off ) dup fs_bmask l@ -rot fs_qbmask x@ + and ;
: fragroundup ( pos fs -- off ) dup fs_fmask l@ -rot fs_qfmask x@ + and ;
\ : fragroundup ( pos fs -- off ) tuck fs_qfmask x@ + swap fs_fmask l@ and ;
: fragstoblks ( pos fs -- off ) fs_fragshift l@ >> ;
: blkstofrags ( blk fs -- frag ) fs_fragshift l@ << ;
: fragnum ( fsb fs -- off ) fs_frag l@ 1- and ;
: blknum ( fsb fs -- off ) fs_frag l@ 1- not and ;
: dblksize ( lbn dino fs -- size )
   -rot 				( fs lbn dino )
   di_size x@				( fs lbn di_size )
   -rot dup 1+				( di_size fs lbn lbn+1 )
   2over fs_bshift l@			( di_size fs lbn lbn+1 di_size b_shift )
   rot swap <<	>=			( di_size fs lbn res1 )
   swap ndaddr >= or if			( di_size fs )
      swap drop fs_bsize l@ exit	( size )
   then	tuck blkoff swap fragroundup	( size )
;

: ino-to-cg ( ino fs -- cg ) fs_ipg l@ / ;
: ino-to-fsbo ( ino fs -- fsb0 ) fs_inopb l@ mod ;
: ino-to-fsba ( ino fs -- ba )	\ Need to remove the stupid stack diags someday
   2dup 				( ino fs ino fs )
   ino-to-cg				( ino fs cg )
   over					( ino fs cg fs )
   cgimin				( ino fs inode-blk )
   -rot					( inode-blk ino fs )
   tuck 				( inode-blk fs ino fs )
   fs_ipg l@ 				( inode-blk fs ino ipg )
   mod					( inode-blk fs mod )
   swap					( inode-blk mod fs )
   dup 					( inode-blk mod fs fs )
   fs_inopb l@ 				( inode-blk mod fs inopb )
   rot 					( inode-blk fs inopb mod )
   swap					( inode-blk fs mod inopb )
   /					( inode-blk fs div )
   swap					( inode-blk div fs )
   blkstofrags				( inode-blk frag )
   +
;
: fsbtodb ( fsb fs -- db ) fs_fsbtodb l@ << ;

\
\ File stuff
\

niaddr /w* constant narraysize

struct
   8		field	>f_ihandle	\ device handle
   8 		field 	>f_seekp	\ seek pointer
   8 		field 	>f_fs		\ pointer to super block
   ufs1_dinode_SIZEOF 	field 	>f_di	\ copy of on-disk inode
   8		field	>f_buf		\ buffer for data block
   4		field 	>f_buf_size	\ size of data block
   4		field	>f_buf_blkno	\ block number of data block
constant file_SIZEOF

file_SIZEOF buffer: the-file
sb-buf the-file >f_fs x!

ufs1_dinode_SIZEOF buffer: cur-inode
h# 2000 buffer: indir-block
-1 value indir-addr

\
\ Translate a fileblock to a disk block
\
\ We only allow single indirection
\

: block-map ( fileblock -- diskblock )
   \ Direct block?
   dup ndaddr <  if 			( fileblock )
      cur-inode di_db			( arr-indx arr-start )
      swap la+ l@ exit			( diskblock )
   then 				( fileblock )
   ndaddr -				( fileblock' )
   \ Now we need to check the indirect block
   dup sb-buf fs_nindir l@ <  if	( fileblock' )
      cur-inode di_ib l@ dup		( fileblock' indir-block indir-block )
      indir-addr <>  if 		( fileblock' indir-block )
         to indir-addr			( fileblock' )
         indir-block 			( fileblock' indir-block )
         sb-buf dup fs_bsize l@		( fileblock' indir-block fs fs_bsize )
         swap indir-addr swap		( fileblock' indir-block fs_bsize indiraddr fs )
         fsbtodb 			( fileblock' indir-block fs_bsize db )
         strategy			( fileblock' nread )
      then				( fileblock' nread|indir-block )
      drop \ Really should check return value
      indir-block swap la+ l@ exit
   then
   dup sb-buf fs_nindir -		( fileblock'' )
   \ Now try 2nd level indirect block -- just read twice
   dup sb-buf fs_nindir l@ dup * < if	( fileblock'' )
      cur-inode di_ib 1 la+ l@		( fileblock'' indir2-block )
      to indir-addr			( fileblock'' )
      \ load 1st level indir block
      indir-block 			( fileblock'' indir-block )
      sb-buf dup fs_bsize l@		( fileblock'' indir-block fs fs_bsize )
      swap indir-addr swap		( fileblock'' indir-block fs_bsize indiraddr fs )
      fsbtodb 				( fileblock'' indir-block fs_bsize db )
      strategy				( fileblock'' nread )
      drop				( fileblock'' )
      dup sb-buf fs_nindir /		( fileblock'' indir-offset )
      indir-block swap la+ l@		( fileblock'' indirblock )
      to indir-addr			( fileblock'' )
      \ load 2nd level indir block
      indir-block 			( fileblock'' indir-block )
      sb-buf dup fs_bsize l@		( fileblock'' indir-block fs fs_bsize )
      swap indir-addr swap		( fileblock'' indir-block fs_bsize indiraddr fs )
      fsbtodb 				( fileblock'' indir-block fs_bsize db )
      strategy				( fileblock'' nread )
      drop				( fileblock'' )
      sb-buf fs_nindir l@ mod indir-block swap la+ l@ exit
   then
   ." block-map: exceeded max file size" cr
   abort
;

\
\ Read file into internal buffer and return pointer and len
\

0 value cur-block			\ allocated dynamically in ufs-open
0 value cur-blocksize			\ size of cur-block
-1 value cur-blockno
0 value cur-offset

: buf-read-file ( fs -- len buf )
   cur-offset swap			( seekp fs )
   2dup blkoff				( seekp fs off )
   -rot 2dup lblkno			( off seekp fs block )
   swap 2dup cur-inode			( off seekp block fs block fs inop )
   swap dblksize			( off seekp block fs size )
   rot dup cur-blockno			( off seekp fs size block block cur )
   <>  if 				( off seekp fs size block )
      block-map				( off seekp fs size diskblock )
      dup 0=  if			( off seekp fs size diskblock )
         over cur-block swap 0 fill	( off seekp fs size diskblock )
         boot-debug?  if ." buf-read-file fell off end of file" cr then
      else
         2dup sb-buf fsbtodb cur-block -rot strategy	( off seekp fs size diskblock nread )
         rot 2dup <>  if " buf-read-file: short read." cr abort then
      then				( off seekp fs diskblock nread size )
      nip nip				( off seekp fs size )
   else					( off seekp fs size block block cur )
      2drop				( off seekp fs size )
   then
\   dup cur-offset + to cur-offset	\ Set up next xfer -- not done
   nip nip swap -			( len )
   cur-block
;

\
\ Read inode into cur-inode -- uses cur-block
\

: read-inode ( inode fs -- )
   twiddle				( inode fs -- inode fs )

   cur-block				( inode fs -- inode fs buffer )

   over					( inode fs buffer -- inode fs buffer fs )
   fs_bsize l@				( inode fs buffer -- inode fs buffer size )

   2over				( inode fs buffer size -- inode fs buffer size inode fs )
   2over				( inode fs buffer size inode fs -- inode fs buffer size inode fs buffer size )
   2swap tuck				( inode fs buffer size inode fs buffer size -- inode fs buffer size buffer size fs inode fs )

   ino-to-fsba 				( inode fs buffer size buffer size fs inode fs -- inode fs buffer size buffer size fs fsba )
   swap					( inode fs buffer size buffer size fs fsba -- inode fs buffer size buffer size fsba fs )
   fsbtodb				( inode fs buffer size buffer size fsba fs -- inode fs buffer size buffer size db )

   dup to cur-blockno			( inode fs buffer size buffer size dstart -- inode fs buffer size buffer size dstart )
   strategy				( inode fs buffer size buffer size dstart -- inode fs buffer size nread )
   <>  if ." read-inode - residual" cr abort then
   dup 2over				( inode fs buffer -- inode fs buffer buffer inode fs )
   ino-to-fsbo				( inode fs buffer -- inode fs buffer buffer fsbo )
   ufs1_dinode_SIZEOF * +			( inode fs buffer buffer fsbo -- inode fs buffer dinop )
   cur-inode ufs1_dinode_SIZEOF move 	( inode fs buffer dinop -- inode fs buffer )
	\ clear out the old buffers
   drop					( inode fs buffer -- inode fs )
   2drop
;

\ Identify inode type

: is-dir? ( dinode -- true:false ) di_mode w@ ifmt and ifdir = ;
: is-symlink? ( dinode -- true:false ) di_mode w@ ifmt and iflnk = ;

\
\ Hunt for directory entry:
\
\ repeat
\    load a buffer
\    while entries do
\       if entry == name return
\       next entry
\ until no buffers
\

: search-directory ( str len -- ino|0 )
   0 to cur-offset
   begin cur-offset cur-inode di_size x@ < while	( str len )
      sb-buf buf-read-file		( str len len buf )
      over 0=  if ." search-directory: buf-read-file zero len" cr abort then
      swap dup cur-offset + to cur-offset	( str len buf len )
      2dup + nip			( str len buf bufend )
      swap 2swap rot			( bufend str len buf )
      begin dup 4 pick < while		( bufend str len buf )
         dup d_ino l@ 0<>  if 		( bufend str len buf )
            boot-debug?  if dup dup d_name swap d_namlen c@ type cr then
            2dup d_namlen c@ =  if	( bufend str len buf )
               dup d_name 2over		( bufend str len buf dname str len )
               comp 0= if		( bufend str len buf )
                  \ Found it -- return inode
                  d_ino l@ nip nip nip	( dino )
                  boot-debug?  if ." Found it" cr then
                  exit 			( dino )
               then
            then			( bufend str len buf )
         then				( bufend str len buf )
         dup d_reclen w@ +		( bufend str len nextbuf )
      repeat
      drop rot drop			( str len )
   repeat
   2drop 2drop 0			( 0 )
;

: ffs_oldcompat ( -- )
\ Make sure old ffs values in sb-buf are sane
   sb-buf fs_npsect dup l@ sb-buf fs_nsect l@ max swap l!
   sb-buf fs_interleave dup l@ 1 max swap l!
   sb-buf fs_postblformat l@ fs_42postblfmt =  if
      8 sb-buf fs_nrpos l!
   then
   sb-buf fs_inodefmt l@ fs_44inodefmt <  if
      sb-buf fs_bsize l@
      dup ndaddr * 1- sb-buf fs_maxfilesize x!
      niaddr 0 ?do
	sb-buf fs_nindir l@ * dup	( sizebp sizebp -- )
	sb-buf fs_maxfilesize dup x@	( sizebp sizebp *fs_maxfilesize fs_maxfilesize -- )
	rot 				( sizebp *fs_maxfilesize fs_maxfilesize sizebp -- )
	+ 				( sizebp *fs_maxfilesize new_fs_maxfilesize  -- )
        swap x! 			( sizebp -- )
      loop drop 			( -- )
      sb-buf dup fs_bmask l@ not swap fs_qbmask x!
      sb-buf dup fs_fmask l@ not swap fs_qfmask x!
   then
;

: read-super ( sector -- )
   0 " seek" boot-ihandle $call-method
   -1 = if
      ." Seek failed" cr
      abort
   then
   sb-buf sbsize " read" boot-ihandle $call-method
   dup sbsize <>  if
      ." Read of superblock failed" cr
      ." requested" space sbsize .
      ." actual" space . cr
      abort
   else
      drop
   then
;

: ufs-open ( bootpath,len -- )
   sboff read-super
   sb-buf fs_magic l@ fs_magic_value <>  if
      ." Invalid superblock magic" cr
      abort
   then
   sb-buf fs_bsize l@ dup maxbsize >  if
      ." Superblock bsize" space . ." too large" cr
      abort
   then
   dup fs_SIZEOF <  if
      ." Superblock bsize < size of superblock" cr
      abort
   then
   ffs_oldcompat	( fs_bsize -- fs_bsize )
   dup to cur-blocksize alloc-mem to cur-block	\ Allocate cur-block
   boot-debug?  if ." ufs-open complete" cr then
;

: ufs-close ( -- )
   cur-block 0<> if
      cur-block cur-blocksize free-mem
   then
;

: boot-path ( -- boot-path )
   " bootpath" chosen-phandle get-package-property  if
      ." Could not find bootpath in /chosen" cr
      abort
   else
      decode-string 2swap 2drop
   then
;

: boot-args ( -- boot-args )
   " bootargs" chosen-phandle get-package-property  if
      ." Could not find bootargs in /chosen" cr
      abort
   else
      decode-string 2swap 2drop
   then
;

2000 buffer: boot-path-str
2000 buffer: boot-path-tmp

: split-path ( path len -- right len left len )
\ Split a string at the `/'
   begin
      dup -rot				( oldlen right len left )
      ascii / left-parse-string		( oldlen right len left len )
      dup 0<>  if 4 roll drop exit then
      2drop				( oldlen right len )
      rot over =			( right len diff )
   until
;

: find-file ( load-file len -- )
   rootino dup sb-buf read-inode	( load-file len -- load-file len ino )
   -rot					( load-file len ino -- pino load-file len )
   \
   \ For each path component
   \
   begin split-path dup 0<> while	( pino right len left len -- )
      cur-inode is-dir? not  if ." Inode not directory" cr abort then
      boot-debug?  if ." Looking for" space 2dup type space ." in directory..." cr then
      search-directory			( pino right len left len -- pino right len ino|false )
      dup 0=  if ." Bad path" cr abort then	( pino right len cino )
      sb-buf read-inode			( pino right len )
      cur-inode is-symlink?  if		\ Symlink -- follow the damn thing
         \ Save path in boot-path-tmp
         boot-path-tmp strmov		( pino new-right len )

         \ Now deal with symlink
         cur-inode di_size x@		( pino right len linklen )
         dup sb-buf fs_maxsymlinklen l@	( pino right len linklen linklen maxlinklen )
         <  if				\ Now join the link to the path
            cur-inode di_shortlink l@	( pino right len linklen linkp )
            swap boot-path-str strmov	( pino right len new-linkp linklen )
         else				\ Read file for symlink -- Ugh
            \ Read link into boot-path-str
            boot-path-str dup sb-buf fs_bsize l@
            0 block-map			( pino right len linklen boot-path-str bsize blockno )
            strategy drop swap		( pino right len boot-path-str linklen )
         then 				( pino right len linkp linklen )
         \ Concatenate the two paths
         strcat				( pino new-right newlen )
         swap dup c@ ascii / =  if	\ go to root inode?
            rot drop rootino -rot	( rino len right )
         then
         rot dup sb-buf read-inode	( len right pino )
         -rot swap			( pino right len )
      then				( pino right len )
   repeat
   2drop drop
;

: read-file ( size addr -- )
   \ Read x bytes from a file to buffer
   begin over 0> while
      cur-offset cur-inode di_size x@ >  if ." read-file EOF exceeded" cr abort then
      sb-buf buf-read-file		( size addr len buf )
      over 2over drop swap		( size addr len buf addr len )
      move				( size addr len )
      dup cur-offset + to cur-offset	( size len newaddr )
      tuck +				( size len newaddr )
      -rot - swap			( newaddr newsize )
   repeat
   2drop
;

\
\ According to the 1275 addendum for SPARC processors:
\ Default load-base is 0x4000.  At least 0x8.0000 or
\ 512KB must be available at that address.
\
\ The Fcode bootblock can take up up to 8KB (O.K., 7.5KB)
\ so load programs at 0x4000 + 0x2000=> 0x6000
\

h# 6000 constant loader-base

\
\ Finally we finish it all off
\

: load-file-signon ( load-file len boot-path len -- load-file len boot-path len )
   ." Loading file" space 2over type cr ." from device" space 2dup type cr
;

: load-file-print-size ( size -- size )
   ." Loading" space dup . space ." bytes of file..." cr
;

: load-file ( load-file len boot-path len -- load-base )
   boot-debug?  if load-file-signon then
   the-file file_SIZEOF 0 fill		\ Clear out file structure

   ufs-open 				( load-file len )
   find-file				( )

   \
   \ Now we've found the file we should read it in in one big hunk
   \

   cur-inode di_size x@			( file-len )
   dup " to file-size" evaluate		( file-len )
   boot-debug?  if load-file-print-size then
   0 to cur-offset
   loader-base				( buf-len addr )
   2dup read-file			( buf-len addr )
   ufs-close				( buf-len addr )

   \ Luckily the prom should be able to handle ELF executables by itself

   nip					( addr )
;

0 value dev-block			\ Buffer for reading device blocks
0 value dev-blocksize			\ Size of device block buffer
-1 value dev-blockno

0 value part-type			\ Type of 'a' partition.

: read-disklabel ( )
   dev-block dev-blocksize 0		\ LABELSECTOR == 0
   strategy				( buf len start -- nread )
   dev-blocksize <> if
      ." Failed to read disklabel" cr
      abort
   then
   dev-block sl_magic w@ dup sun_dkmagic <> if
      ." Invalid disklabel magic" space . cr
      abort
   then drop
   dev-block sl_types c@ dup to part-type
   drop
;

: is-bootable-softraid? ( -- softraid? )
   part-type fs_raid <> if false exit then

   dev-block dev-blocksize sr_meta_offset
   strategy				( buf len block -- nread )
   dev-blocksize <> if
      ." Failed to read softraid metadata" cr
      abort
   then

   dev-block ssd_magic l@ sr_magic1 <> if false exit then
   dev-block ssd_magic 4 + l@ sr_magic2 <> if false exit then

   boot-debug? if ." found softraid metadata" cr then

   \ Metadata version must be 4 or greater.
   dev-block ssd_version l@ dup 4 < if
      ." softraid version " space . space ." does not support booting" cr
      abort
   then drop

   \ Is this softraid volume bootable?
   dev-block ssd_vol_flags l@ bioc_scbootable and bioc_scbootable <> if
      ." softraid volume is not bootable" cr
      abort
   then

   true
;

: softraid-boot ( offset size -- load-base )
   boot-debug? if ." softraid-boot " 2dup . . cr then
   swap to dev-blockno
   loader-base

   \ Load boot loader from softraid boot area
   begin over 0> while
      dev-block dev-blocksize dev-blockno
      strategy				( size addr buf len start -- nread )
      dup dev-blocksize <> if
         ." softraid-boot: block read failed" cr
         abort
      then
      dev-blockno 1 + to dev-blockno
      2dup dev-block rot rot		( size addr nread buf addr len )
      move				( size addr nread )
      dup rot +				( nread size newaddr )
      rot rot - swap			( newsize newaddr )
   repeat
   2drop

   loader-base
;

: do-boot ( bootfile -- )
   ." OpenBSD IEEE 1275 Bootblock 1.4" cr

   \ Open boot device
   boot-path				( boot-path len )
   boot-debug? if
      ." Booting from device" space 2dup type cr
   then
   drop
   cif-open dup 0= if			( ihandle? )
      ." Could not open device" space type cr
      abort
   then
   to boot-ihandle			\ Save ihandle to boot device

   \ Allocate memory for reading disk blocks
   dev_bsize dup to dev-blocksize	( blocksize )
   alloc-mem to dev-block

   \ Read disklabel
   read-disklabel

   \ Are we booting from a softraid volume?
   is-bootable-softraid? if
      sr_boot_offset sr_boot_size dev_bsize *
      softraid-boot			( blockno size -- load-base )
   else
      " /ofwboot" load-file		( -- load-base )
   then

   \ Free memory for reading disk blocks
   cur-block 0<> if
      dev-block dev-blocksize free-mem
   then

   \ Close boot device
   boot-ihandle dup -1 <> if
      cif-close -1 to boot-ihandle
   then

   dup 0<> if " to load-base init-program" evaluate then
;

boot-args ascii V strchr 0<> swap drop if
   true to boot-debug?
then

boot-args ascii D strchr 0= swap drop if
   do-boot
then exit


code:
#	$OpenBSD: genassym.sh,v 1.3 2003/07/02 00:37:31 avsm Exp $
#	$NetBSD: genassym.sh,v 1.1 2000/08/20 14:58:45 mrg Exp $

#
# Copyright (c) 1998 Eduardo E. Horvath.
# Copyright (c) 1997 Matthias Pfaller.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. All advertising materials mentioning features or use of this software
#    must display the following acknowledgement:
#	This product includes software developed by Matthias Pfaller.
# 4. The name of the author may not 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.
#

# If first argument is -c, create a temporary C file,
# compile it and execute the result.

awk=${AWK:-awk}
TMP1=`mktemp /tmp/genassym1.XXXXXXXXXX` || exit 1
TMP2=`mktemp /tmp/genassym2.XXXXXXXXXX` || {
	rm -f $TMP1
	exit 1
}

trap 'rm -f $TMP1 $TMP2' 0 1 2 3 13 15

if [ $1 = '-c' ] ; then
	shift
	ccode=1
else
	ccode=0
fi

$awk '
BEGIN {
	printf("#ifndef _KERNEL\n#define _KERNEL\n#endif\n");
	printf("#define	offsetof(type, member) ((size_t)(&((type *)0)->member))\n");
	defining = 0;
	type = "long";
	asmtype = "n";
	asmprint = "";
}

$0 ~ /^[ \t]*#.*/ || $0 ~ /^[ \t]*$/ {
	# Just ignore comments and empty lines
	next;
}

$0 ~ /^config[ \t]/ {
	type = $2;
	asmtype = $3;
	asmprint = $4;
	next;
}

/^include[ \t]/ {
	if (defining != 0) {
		defining = 0;
		printf("}\n");
	}
	printf("#%s\n", $0);
	next;
}

$0 ~ /^if[ \t]/ ||
$0 ~ /^ifdef[ \t]/ ||
$0 ~ /^ifndef[ \t]/ ||
$0 ~ /^else/ ||
$0 ~ /^elif[ \t]/ ||
$0 ~ /^endif/ {
	printf("#%s\n", $0);
	next;
}

/^struct[ \t]/ {
	structname = $2;
	$0 = "define " structname "_SIZEOF sizeof(struct " structname ")";
	# fall through
}

/^export[ \t]/ {
	$0 = "define " $2 " " $2;
	# fall through
}

/^define[ \t]/ {
	if (defining == 0) {
		defining = 1;
		printf("void f" FNR "(void);\n");
		printf("void f" FNR "() {\n");
		if (ccode)
			call[FNR] = "f" FNR;
		defining = 1;
	}
	value = $0
	gsub("^define[ \t]+[A-Za-z_][A-Za-z_0-9]*[ \t]+", "", value)
	if (ccode)
		printf("printf(\"#define " $2 " %%ld\\n\", (%s)" value ");\n", type);
	else
		printf("__asm(\"XYZZY d# %%%s0 constant %s\" : : \"%s\" (%s));\n", asmprint, $2, asmtype, value);
	next;
}

/^member[ \t]/ {
	$0 = "define " $2 " offsetof(struct " structname ", " $2 ")";
	if (defining == 0) {
		defining = 1;
		printf("void f" FNR "(void);\n");
		printf("void f" FNR "() {\n");
		if (ccode)
			call[FNR] = "f" FNR;
		defining = 1;
	}
	value = $0
	gsub("^define[ \t]+[A-Za-z_][A-Za-z_0-9]*[ \t]+", "", value)
	if (ccode)
		printf("printf(\"#define " $2 " %%ld\\n\", (%s)" value ");\n", type);
	else
		printf("__asm(\"XYZZY : %s d\# %%%s0 + ;\" : : \"%s\" (%s));\n", $2, asmprint, asmtype, value);
	next;
}

/^quote[ \t]/ {
	gsub("^quote[ \t]+", "");
	print;
	next;
}

{
	printf("syntax error in line %d\n", FNR) >"/dev/stderr";
	exit(1);
}

END {
	if (defining != 0) {
		defining = 0;
		printf("}\n");
	}
	if (ccode) {
		printf("int main(int argc, char **argv) {");
		for (i in call)
			printf(call[i] "();");
		printf("return(0); }\n");
	}
}
' ccode=$ccode > $TMP1 || exit 1

if [ $ccode = 1 ] ; then
	"$@" -x c $TMP1 -o $TMP2 && $TMP2
else
	# Kill all of the "#" and "$" modifiers; locore.s already
	# prepends the correct "constant" modifier.
	"$@" -x c -S ${TMP1} -o ${TMP2} || exit 1
	sed 's/\$//g' ${TMP2} | sed -n 's/.*XYZZY//gp'
fi