code:
/*	$OpenBSD: machdep.c,v 1.46 2015/01/16 20:17:07 kettenis Exp $	*/
/*	OpenBSD: machdep.c,v 1.105 2005/04/11 15:13:01 deraadt 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.
 *
 *	@(#)machdep.c	8.6 (Berkeley) 1/14/94
 */

#include <sys/param.h>
#include <sys/signal.h>
#include <sys/signalvar.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/buf.h>
#include <sys/device.h>
#include <sys/reboot.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/timeout.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/mount.h>
#include <sys/msgbuf.h>
#include <sys/syscallargs.h>
#include <sys/exec.h>
#include <sys/sysctl.h>
#include <sys/extent.h>

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

#include <dev/rndvar.h>

#include <machine/autoconf.h>
#include <machine/frame.h>
#include <machine/cpu.h>
#include <machine/pmap.h>
#include <machine/oldmon.h>
#include <machine/bsd_openprom.h>

#include <machine/idt.h>
#include <machine/kap.h>
#include <machine/prom.h>

#include <sparc/sparc/asm.h>
#include <sparc/sparc/cache.h>
#include <sparc/sparc/cpuvar.h>

#include "auxreg.h"

#include <sparc/sparc/intreg.h>

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

int	physmem;

/* sysctl settable */
int	sparc_led_blink = 1;

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

/*
 * dvmamap is used to manage DVMA memory. Note: this coincides with
 * the memory range in `phys_map' (which is mostly a place-holder).
 */
vaddr_t dvma_base, dvma_end;
struct extent *dvmamap_extent;

void	dumpsys(void);
static int kap_maskcheck(void);

/*
 * Machine-dependent startup code
 */
void
cpu_startup()
{
#ifdef DEBUG
	extern int pmapdebug;
	int opmapdebug = pmapdebug;
#endif
	vaddr_t minaddr, maxaddr;
	extern struct user *proc0paddr;

#ifdef DEBUG
	pmapdebug = 0;
#endif

	/*
	 * fix message buffer mapping
	 */
	pmap_map(MSGBUF_VA, MSGBUF_PA, MSGBUF_PA + MSGBUFSIZE,
	    PROT_READ | PROT_WRITE);
	initmsgbuf((caddr_t)(MSGBUF_VA + (CPU_ISSUN4 ? 4096 : 0)), MSGBUFSIZE);

	proc0.p_addr = proc0paddr;

	/* I would print this earlier, but I want it in the message buffer */
	if (kap_maskcheck() == 0) {
		printf("WARNING: KAP M2C3 or earlier mask detected.\n"
"THE PROCESSOR IN THIS MACHINE SUFFERS FROM SEVERE HARDWARE ISSUES.\n"
"M2C3 PROCESSORS MAY RUN RELIABLY ENOUGH, OLDER WILL DEFINITELY NOT.\n\n");
	}

	/*
	 * Good {morning,afternoon,evening,night}.
	 */
	printf(version);
	/*identifycpu();*/
	printf("real mem = %d (%dMB)\n", ptoa(physmem),
	    ptoa(physmem) / 1024 / 1024);

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

	/*
	 * Allocate a map for physio.  Others use a submap of the kernel
	 * map, but we want one completely separate, even though it uses
	 * the same pmap.
	 */
	dvma_base = CPU_ISSUN4M ? DVMA4M_BASE : DVMA_BASE;
	dvma_end = CPU_ISSUN4M ? DVMA4M_END : DVMA_END;
	phys_map = uvm_map_create(pmap_kernel(), dvma_base, dvma_end,
	    VM_MAP_INTRSAFE);
	if (phys_map == NULL)
		panic("unable to create DVMA map");

	/*
	 * Allocate DVMA space and dump into a privately managed
	 * extent for double mappings which is usable from
	 * interrupt contexts.
	 */
	if (uvm_km_valloc_wait(phys_map, (dvma_end-dvma_base)) != dvma_base)
		panic("unable to allocate from DVMA map");
	dvmamap_extent = extent_create("dvmamap", dvma_base, dvma_end,
				       M_DEVBUF, NULL, 0, EX_NOWAIT);
	if (dvmamap_extent == 0)
		panic("unable to allocate extent for dvma");

#ifdef DEBUG
	pmapdebug = opmapdebug;
#endif
	printf("avail mem = %lu (%luMB)\n", ptoa(uvmexp.free),
	    ptoa(uvmexp.free) / 1024 / 1024);

	/*
	 * Set up buffers, so they can be used to read disk labels.
	 */
	bufinit();

	/* Early interrupt handlers initialization */
	intr_init();
}

/*
 * Set up registers on exec.
 *
 * XXX this entire mess must be fixed
 */
/* ARGSUSED */
void
setregs(p, pack, stack, retval)
	struct proc *p;
	struct exec_package *pack;
	u_long stack;
	register_t *retval;
{
	struct trapframe *tf = p->p_md.md_tf;
	struct fpstate *fs;
	int psr;

	/*
	 * Setup the process StackGhost cookie which will be XORed into
	 * the return pointer as register windows are over/underflowed
	 */
	p->p_addr->u_pcb.pcb_wcookie = arc4random();

	/* The cookie needs to guarantee invalid alignment after the XOR */
	switch (p->p_addr->u_pcb.pcb_wcookie % 3) {
	case 0: /* Two lsb's already both set except if the cookie is 0 */
		p->p_addr->u_pcb.pcb_wcookie |= 0x3;
		break;
	case 1: /* Set the lsb */
		p->p_addr->u_pcb.pcb_wcookie = 1 |
			(p->p_addr->u_pcb.pcb_wcookie & ~0x3);
		break;
	case 2: /* Set the second most lsb */
		p->p_addr->u_pcb.pcb_wcookie = 2 |
			(p->p_addr->u_pcb.pcb_wcookie & ~0x3);
		break;
	}

	/* Don't allow misaligned code by default */
	p->p_md.md_flags &= ~MDP_FIXALIGN;

	/*
	 * The syscall will ``return'' to npc or %g7 or %g2; set them all.
	 * Set the rest of the registers to 0 except for %o6 (stack pointer,
	 * built in exec()) and psr (retain CWP and PSR_S bits).
	 */
	psr = tf->tf_psr & (PSR_S | PSR_CWP);
	if ((fs = p->p_md.md_fpstate) != NULL) {
		/*
		 * We hold an FPU state.  If we own *the* FPU chip state
		 * we must get rid of it, and the only way to do that is
		 * to save it.  In any case, get rid of our FPU state.
		 */
		if (p == cpuinfo.fpproc) {
			savefpstate(fs);
			cpuinfo.fpproc = NULL;
		}
		free((void *)fs, M_SUBPROC, 0);
		p->p_md.md_fpstate = NULL;
	}
	bzero((caddr_t)tf, sizeof *tf);
	tf->tf_psr = psr;
	tf->tf_npc = pack->ep_entry & ~3;
	tf->tf_global[2] = tf->tf_global[7] = tf->tf_npc;
	/* XXX exec of init(8) returns via proc_trampoline() */
	if (p->p_pid == 1) {
		tf->tf_pc = tf->tf_npc;
		tf->tf_npc += 4;
	}
	stack -= sizeof(struct rwindow);
	tf->tf_out[6] = stack;
	retval[1] = 0;
}

#ifdef DEBUG
int sigdebug = 0;
pid_t sigpid = 0;
#define SDB_FOLLOW	0x01
#define SDB_KSTACK	0x02
#define SDB_FPSTATE	0x04
#endif

struct sigframe {
	int	sf_signo;		/* signal number */
	siginfo_t *sf_sip;		/* points to siginfo_t */
	int	sf_xxx;			/* placeholder */
	caddr_t	sf_addr;		/* SunOS compat */
	struct	sigcontext sf_sc;	/* actual sigcontext */
	siginfo_t sf_si;
};

/*
 * machine dependent system variables.
 */
int
cpu_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p)
	int *name;
	u_int namelen;
	void *oldp;
	size_t *oldlenp;
	void *newp;
	size_t newlen;
	struct proc *p;
{
#if (NLED > 0) || (NAUXREG > 0) || (NSCF > 0)
	int oldval;
	int ret;
#endif
	extern int v8mul;

	/* all sysctl names are this level are terminal */
	if (namelen != 1)
		return (ENOTDIR);	/* overloaded */

	switch (name[0]) {
	case CPU_LED_BLINK:
#if (NLED > 0) || (NAUXREG > 0) || (NSCF > 0)
		oldval = sparc_led_blink;
		ret = sysctl_int(oldp, oldlenp, newp, newlen,
		    &sparc_led_blink);

		/*
		 * If we were false and are now true, call led_blink().
		 * led_blink() itself will catch the other case.
		 */
		if (!oldval && sparc_led_blink > oldval) {
#if NAUXREG > 0
			led_blink((caddr_t *)0);
#endif
#if NLED > 0
			led_cycle((caddr_t *)led_sc);
#endif
#if NSCF > 0
			scfblink((caddr_t *)0);
#endif
		}

		return (ret);
#else
		return (EOPNOTSUPP);
#endif
	case CPU_CPUTYPE:
		return (sysctl_rdint(oldp, oldlenp, newp, cputyp));
	case CPU_V8MUL:
		return (sysctl_rdint(oldp, oldlenp, newp, v8mul));
	default:
		return (EOPNOTSUPP);
	}
	/* NOTREACHED */
}

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

	tf = p->p_md.md_tf;
	oldsp = tf->tf_out[6];

	/*
	 * Compute new user stack addresses, subtract off
	 * one signal frame, and align.
	 */
	if ((p->p_sigstk.ss_flags & SS_DISABLE) == 0 &&
	    !sigonstack(oldsp) && (psp->ps_sigonstack & sigmask(sig)))
		fp = (struct sigframe *)(p->p_sigstk.ss_sp +
					 p->p_sigstk.ss_size);
	else
		fp = (struct sigframe *)oldsp;
	fp = (struct sigframe *)((int)(fp - 1) & ~7);

#ifdef DEBUG
	if ((sigdebug & SDB_KSTACK) && p->p_pid == sigpid)
		printf("sendsig: %s[%d] sig %d newusp %p scp %p\n",
		    p->p_comm, p->p_pid, sig, fp, &fp->sf_sc);
#endif
	/*
	 * Now set up the signal frame.  We build it in kernel space
	 * and then copy it out.  We probably ought to just build it
	 * directly in user space....
	 */
	bzero(&sf, sizeof(sf));
	sf.sf_signo = sig;
	sf.sf_sip = NULL;

	/*
	 * Build the signal context to be used by sigreturn.
	 */
	sf.sf_sc.sc_mask = mask;
	sf.sf_sc.sc_sp = oldsp;
	sf.sf_sc.sc_pc = tf->tf_pc;
	sf.sf_sc.sc_npc = tf->tf_npc;
	sf.sf_sc.sc_psr = tf->tf_psr;
	sf.sf_sc.sc_g1 = tf->tf_global[1];
	sf.sf_sc.sc_o0 = tf->tf_out[0];

	if (psp->ps_siginfo & sigmask(sig)) {
		sf.sf_sip = &fp->sf_si;
		initsiginfo(&sf.sf_si, sig, code, type, val);
	}

	/*
	 * Put the stack in a consistent state before we whack away
	 * at it.  Note that write_user_windows may just dump the
	 * registers into the pcb; we need them in the process's memory.
	 * We also need to make sure that when we start the signal handler,
	 * its %i6 (%fp), which is loaded from the newly allocated stack area,
	 * joins seamlessly with the frame it was in when the signal occurred,
	 * so that the debugger and _longjmp code can back up through it.
	 */
	newsp = (int)fp - sizeof(struct rwindow);
	write_user_windows();
	/* XXX do not copyout siginfo if not needed */
	if (rwindow_save(p) || copyout((caddr_t)&sf, (caddr_t)fp, sizeof sf) ||
	    copyout(&oldsp, &((struct rwindow *)newsp)->rw_in[6],
	      sizeof(register_t)) != 0) {
		/*
		 * Process has trashed its stack; give it an illegal
		 * instruction to halt it in its tracks.
		 */
#ifdef DEBUG
		if ((sigdebug & SDB_KSTACK) && p->p_pid == sigpid)
			printf("sendsig: window save or copyout error\n");
#endif
		sigexit(p, SIGILL);
		/* NOTREACHED */
	}
#ifdef DEBUG
	if (sigdebug & SDB_FOLLOW)
		printf("sendsig: %s[%d] sig %d scp %p\n",
		       p->p_comm, p->p_pid, sig, &fp->sf_sc);
#endif
	/*
	 * Arrange to continue execution at the code copied out in exec().
	 * It needs the function to call in %g1, and a new stack pointer.
	 */
	caddr = p->p_p->ps_sigcode;
	tf->tf_global[1] = (int)catcher;
	tf->tf_pc = caddr;
	tf->tf_npc = caddr + 4;
	tf->tf_out[6] = newsp;
#ifdef DEBUG
	if ((sigdebug & SDB_KSTACK) && p->p_pid == sigpid)
		printf("sendsig: about to return to catcher\n");
#endif
}

/*
 * System call to cleanup state after a signal
 * has been taken.  Reset signal mask and
 * stack state from context left by sendsig (above),
 * and return to the given trap frame (if there is one).
 * Check carefully to make sure that the user has not
 * modified the state to gain improper privileges or to cause
 * a machine fault.
 */
/* ARGSUSED */
int
sys_sigreturn(p, v, retval)
	struct proc *p;
	void *v;
	register_t *retval;
{
	struct sys_sigreturn_args /* {
		syscallarg(struct sigcontext *) sigcntxp;
	} */ *uap = v;
	struct sigcontext ksc;
	struct trapframe *tf;
	int error;

	/* First ensure consistent stack state (see sendsig). */
	write_user_windows();
	if (rwindow_save(p))
		sigexit(p, SIGILL);
#ifdef DEBUG
	if (sigdebug & SDB_FOLLOW)
		printf("sigreturn: %s[%d], sigcntxp %p\n",
		    p->p_comm, p->p_pid, SCARG(uap, sigcntxp));
#endif
	if ((error = copyin(SCARG(uap, sigcntxp), &ksc, sizeof(ksc))) != 0)
		return (error);
	tf = p->p_md.md_tf;
	/*
	 * Only the icc bits in the psr are used, so it need not be
	 * verified.  pc and npc must be multiples of 4.  This is all
	 * that is required; if it holds, just do it.
	 */
	if (((ksc.sc_pc | ksc.sc_npc) & 3) != 0)
		return (EINVAL);
	/* take only psr ICC field */
	tf->tf_psr = (tf->tf_psr & ~PSR_ICC) | (ksc.sc_psr & PSR_ICC);
	tf->tf_pc = ksc.sc_pc;
	tf->tf_npc = ksc.sc_npc;
	tf->tf_global[1] = ksc.sc_g1;
	tf->tf_out[0] = ksc.sc_o0;
	tf->tf_out[6] = ksc.sc_sp;
	p->p_sigmask = ksc.sc_mask & ~sigcantmask;
	return (EJUSTRETURN);
}

int	waittime = -1;

__dead void
boot(int howto)
{
	int i;
	static char str[4];	/* room for "-sd\0" */

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

	fb_unblank();
	boothowto = howto;
	if ((howto & RB_NOSYNC) == 0 && waittime < 0) {
		waittime = 0;
		vfs_shutdown();

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

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

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

haltsys:
	config_suspend_all(DVACT_POWERDOWN);

	if ((howto & RB_HALT) != 0 || (howto & RB_POWERDOWN) != 0) {
		printf("halted\n\n");
		romhalt();
	}

	printf("rebooting\n\n");
	i = 1;
	if ((howto & RB_SINGLE) != 0)
		str[i++] = 's';
	if ((howto & RB_KDB) != 0)
		str[i++] = 'd';
	if (i > 1) {
		str[0] = '-';
		str[i] = 0;
	} else
		str[0] = 0;
	romboot(str);
	for (;;) ;
	/* NOTREACHED */
}

/* XXX - needs to be written */
void
dumpconf(void)
{
}

/*
 * Write a crash dump.
 */
void
dumpsys()
{
	printf("dump: TBD\n");
}

/*
 * Map an I/O device given physical address and size in bytes, e.g.,
 *
 *	mydev = (struct mydev *)mapdev(myioaddr, 0,
 *				       0, sizeof(struct mydev));
 *
 * See also machine/autoconf.h.
 *
 * XXXART - verify types (too tired now).
 */
void *
mapdev(phys, virt, offset, size)
	struct rom_reg *phys;
	int offset, virt, size;
{
	vaddr_t va;
	paddr_t pa;
	void *ret;
	static vaddr_t iobase;
	unsigned int pmtype;

	if (iobase == NULL)
		iobase = IODEV_BASE;

	size = round_page(size);
	if (size == 0)
		panic("mapdev: zero size");

	if (virt)
		va = trunc_page(virt);
	else {
		va = iobase;
		iobase += size;
		if (iobase > IODEV_END)	/* unlikely */
			panic("mapiodev");
	}
	ret = (void *)(va | (((u_long)phys->rr_paddr + offset) & PGOFSET));
			/* note: preserve page offset */

	pa = trunc_page((vaddr_t)phys->rr_paddr + offset);
	pmtype = PMAP_IOENC(phys->rr_iospace);

	do {
		pmap_kenter_pa(va, pa | pmtype | PMAP_NC,
		    PROT_READ | PROT_WRITE);
		va += PAGE_SIZE;
		pa += PAGE_SIZE;
	} while ((size -= PAGE_SIZE) > 0);
	pmap_update(pmap_kernel());
	return (ret);
}

/*
 * Soft interrupt handling
 */

int	kap_sir;

void
ienab_bis(int bis)
{
	int s;
	int mask = 1 << (bis - 1);
	u_int32_t icr;

	s = splhigh();
	if (kap_sir < mask) {
		/*
		 * We become the most important bit in kap_sir. Reprogram
		 * the GLU_ICR soft interrupt dispatcher.
		 */
		icr = lda(GLU_ICR, ASI_PHYS_IO) >> 24;
		icr = (icr & ~GICR_DISPATCH_MASK) | bis;
		sta(GLU_ICR, ASI_PHYS_IO, icr << 24);
	}
	kap_sir |= mask;
	splx(s);
}

/*
 * minimal console routines
 */

#include <sys/conf.h>
#include <dev/cons.h>

cons_decl(early);

struct consdev consdev_early = {
	earlycnprobe,
	earlycninit,
	earlycngetc,
	earlycnputc,
	nullcnpollc
};

struct consdev *cn_tab = &consdev_early;

void
earlycnprobe(struct consdev *cn)
{
	cn->cn_dev = makedev(0, 0);
	cn->cn_pri = CN_MIDPRI;
}

void
earlycninit(struct consdev *cn)
{
}

/* getc, putc in locore.s */

int	kapmask_m2c4;
static void kap_maskfault(void);

void
kap_maskfault()
{
	kapmask_m2c4 = 1;
}

/*
 * This routine checks whether we are running on a M2C4 or later mask, by
 * checking for M2C4 behaviour.
 *
 * After mapping a kernel text page with the ``byte-writeable shared''
 * memory attribute, we will attempt to execute code from the new mapping.
 *
 * On M2C4 and later masks, this will cause a text fault, supposedly for
 * us to be able to invalidate the instruction cache first, before resuming
 * execution; while M2C3 and earlier masks will not fault.
 *
 * Since OpenBSD does not use BWS pages and does explicit instruction cache
 * invalidation in ddb and the ptrace interface, this fault never happens in
 * real life. mem_access_fault() in trap.c knows this and will direct
 * execution to kap_maskfault(). Since the test code we have been invoking
 * is a simple empty function, kap_maskfault() will return here.
 *
 * XXX Find some way to identify M2C3, which _should_ run.
 */
int
kap_maskcheck()
{
	extern void masktest(void);
	void (*test)(void);

	pmap_enter(pmap_kernel(), TMPMAP_VA,
	    trunc_page((vaddr_t)masktest) | PMAP_BWS, PROT_READ, 0);
	test = (void (*)(void))(TMPMAP_VA + ((vaddr_t)masktest & PAGE_MASK));

	cpcb->pcb_onfault = (caddr_t)kap_maskfault;
	(*test)();
	cpcb->pcb_onfault = NULL;

	pmap_remove(pmap_kernel(), TMPMAP_VA, TMPMAP_VA + PAGE_SIZE);

	return (kapmask_m2c4);
}

license: unknown


code:
/*	$OpenBSD: mem.c,v 1.5 2015/02/10 22:44:35 miod Exp $	*/
/*	OpenBSD: mem.c,v 1.21 2003/06/02 23:27:55 millert Exp 	*/

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

/*
 * Memory special file
 */

#include <sys/param.h>
#include <sys/buf.h>
#include <sys/systm.h>
#include <sys/uio.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/conf.h>

#include <machine/eeprom.h>
#include <machine/conf.h>

#include <uvm/uvm_extern.h>

extern vaddr_t prom_vstart;
extern vaddr_t prom_vend;
caddr_t zeropage;
vaddr_t mem_page;

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

	switch (minor(dev)) {
		case 0:
		case 1:
		case 2:
		case 11:
		case 12:
			return (0);
		default:
			return (ENXIO);
	}
}

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

	return (0);
}

/*ARGSUSED*/
int
mmrw(dev, uio, flags)
	dev_t dev;
	struct uio *uio;
	int flags;
{
	paddr_t pa;
	vaddr_t va;
	size_t c, o;
	struct iovec *iov;
	int error = 0;
	static int physlock;

	if (minor(dev) == 0) {
		/* lock against other uses of shared mem_page */
		while (physlock > 0) {
			physlock++;
			error = tsleep((caddr_t)&physlock, PZERO | PCATCH,
			    "mmrw", 0);
			if (error)
				return (error);
		}
		physlock = 1;
		if (mem_page == 0)
			mem_page = uvm_km_valloc_wait(kernel_map, NBPG);
		if (mem_page == 0)
			panic("mmrw: out of space in kernel_map");
	}
	while (uio->uio_resid > 0 && error == 0) {
		iov = uio->uio_iov;
		if (iov->iov_len == 0) {
			uio->uio_iov++;
			uio->uio_iovcnt--;
			if (uio->uio_iovcnt < 0)
				panic("mmrw");
			continue;
		}
		switch (minor(dev)) {

		/* minor device 0 is physical memory */
		case 0:
			pa = (paddr_t)uio->uio_offset;
			if (!pmap_pa_exists(pa)) {
				error = EFAULT;
				goto unlock;
			}
			pmap_enter(pmap_kernel(), mem_page,
			    trunc_page(pa), uio->uio_rw == UIO_READ ?
			    PROT_READ : PROT_WRITE, PMAP_WIRED);
			pmap_update(pmap_kernel());
			o = uio->uio_offset & PGOFSET;
			c = ulmin(uio->uio_resid, NBPG - o);
			error = uiomove((caddr_t)mem_page + o, c, uio);
			pmap_remove(pmap_kernel(), mem_page, mem_page + NBPG);
			pmap_update(pmap_kernel());
			continue;

		/* minor device 1 is kernel memory */
		case 1:
			va = (vaddr_t)uio->uio_offset;
			if (va >= MSGBUF_VA && va < MSGBUF_VA+MSGBUFSIZE) {
				c = ulmin(iov->iov_len, MSGBUFSIZE);
#if 0
			} else if (va >= prom_vstart && va < prom_vend &&
				   uio->uio_rw == UIO_READ) {
				/* Allow read-only access to the PROM */
				c = ulmin(iov->iov_len, prom_vend - prom_vstart);
#endif
			} else {
				c = ulmin(iov->iov_len, MAXPHYS);
				if (!uvm_kernacc((caddr_t)va, c,
				    uio->uio_rw == UIO_READ ? B_READ : B_WRITE))
					return (EFAULT);
			}
			error = uiomove((caddr_t)va, c, uio);
			continue;

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

/* XXX should add sbus, etc */

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

		default:
			return (ENXIO);
		}
	}
	if (minor(dev) == 0) {
unlock:
		if (physlock > 1)
			wakeup((caddr_t)&physlock);
		physlock = 0;
	}
	return (error);
}

paddr_t
mmmmap(dev, off, prot)
        dev_t dev;
        off_t off;
	int prot;
{

	return (-1);
}

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


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

/*
 * KAP physical memory management code.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/proc.h>

#include <uvm/uvm.h>

#include <machine/idt.h>
#include <machine/kap.h>
#include <machine/prom.h>

#include <sparc/sparc/asm.h>
#include <sparc/sparc/cache.h>
#include <sparc/sparc/cpuvar.h>

#include <sparc/dev/if_lereg.h>

#ifdef PMAPDEBUG
#define	PDB_ACTIVATE	0x000001
#define	PDB_CLEAR_M	0x000002
#define	PDB_CLEAR_U	0x000004
#define	PDB_COLLECT	0x000008
#define	PDB_COPY	0x000010
#define	PDB_CREATE	0x000020
#define	PDB_DESTROY	0x000040
#define	PDB_ENTER	0x000080
#define	PDB_EXTRACT	0x000100
#define	PDB_IS_M	0x000200
#define	PDB_IS_U	0x000400
#define	PDB_KENTER	0x000800
#define	PDB_KREMOVE	0x001000
#define	PDB_PROTECT	0x002000
#define	PDB_REFERENCE	0x004000
#define	PDB_RELEASE	0x008000
#define	PDB_REMOVE	0x010000
#define	PDB_UNWIRE	0x020000
#define	PDB_ZERO	0x040000

#define	DPRINTF(flg,stmt) \
do { \
	if (pmapdebug & (flg))	\
		printf stmt;	\
} while (0)

u_int	_pmapdebug_cold = 0;
u_int	_pmapdebug = -1;
#define	pmapdebug	((cold) ? _pmapdebug_cold : _pmapdebug)
#else
#define	DPRINTF(flg,stmt) do { } while (0)
#endif

/* pmap and pde/pte pool allocators */
struct pool pmappool, pvpool;

struct pmap kernel_pmap_store;

pt_entry_t *pmap_grow_pte(struct pmap *, vaddr_t);
static pd_entry_t *pmap_pde(pmap_t, vaddr_t);
static pt_entry_t *pde_pte(pd_entry_t *, vaddr_t);
pt_entry_t *pmap_pte(pmap_t, vaddr_t);

void	pg_flushcache(struct vm_page *);

void	tlb_flush(vaddr_t);
void	tlb_flush_all(void);

vaddr_t	virtual_avail;
vaddr_t	virtual_end;

vaddr_t	vreserve;	/* two reserved pages for copy and zero operations... */
pt_entry_t *ptereserve;	/* ...and their PTEs */

vaddr_t	lance_va;	/* a fixed buffer for the on-board lance */

/*
 * Attribute caching
 */

typedef struct pvlist *pv_entry_t;

static pv_entry_t pg_to_pvl(struct vm_page *);

static __inline__
pv_entry_t
pg_to_pvl(struct vm_page *pg)
{
	return (&pg->mdpage.pv_head);
}

/*
 * TLB operations
 */

void
tlb_flush(vaddr_t va)
{
#if 0
	u_int32_t fvar;

	fvar = lda(0, ASI_FVAR);
#endif

	sta(0, ASI_PID, 0);
	sta(0, ASI_FVAR, va);
	sta(0, ASI_GTLB_INVAL_ENTRY, 0);
#if 0
	sta(0, ASI_FVAR, fvar);
#endif
}

void
tlb_flush_all()
{
	/*
	 * Note that loaded TLB for PTEs with PG_G do NOT get invalidated
	 * by this command (because they are common to all PID), and need
	 * to be invalidated with ASI_GTLB_INVAL_ENTRY.
	 * This does not matter to us, as we don't use PG_G for now.
	 */
	sta(0, ASI_PID, 0);
	sta(0, ASI_GTLB_INVALIDATE, 0);
}

/*
 * Simple pde and pte access routines.
 */

#define	trunc_seg(va)		((va) & PDT_INDEX_MASK)

static __inline__
pd_entry_t *
pmap_pde(pmap_t pmap, vaddr_t va)
{
	return (&pmap->pm_segtab[va >> PDT_INDEX_SHIFT]);
}

static __inline__
pt_entry_t *
pde_pte(pd_entry_t *pde, vaddr_t va)
{
	pt_entry_t *pte;

	pte = (pt_entry_t *)pde->pde_va;
	pte += (va & PT_INDEX_MASK) >> PT_INDEX_SHIFT;

	return (pte);
}

pt_entry_t *
pmap_pte(pmap_t pmap, vaddr_t va)
{
	pd_entry_t *pde;

	pde = pmap_pde(pmap, va);
	if (pde->pde_va == NULL)
		return (NULL);

	return (pde_pte(pde, va));
}

/*
 * Setup virtual memory for the kernel. The new tables are not activated yet,
 * they will be in locore.s after bootstrap() returns.
 */
void
pmap_bootstrap(size_t promdata)
{
	extern caddr_t end;
	extern vaddr_t esym;
	u_int32_t icuconf;
	u_int8_t imcmcr;
	vaddr_t ekern;
	vaddr_t va, eva;
	paddr_t pa;
	unsigned int tabidx;
	pd_entry_t *pde;
	pt_entry_t *pte;
	struct sb_prom *sp;
	paddr_t prompa;
	psize_t promlen;
	extern vaddr_t prom_data;

	/*
	 * Compute memory size by checking the iCU for the number of iMC,
	 * then each iMC for its status.
	 */

	icuconf = lda(ICU_CONF, ASI_PHYS_IO);
	physmem = 0;

#if 0
	imcmcr = lduba(MC0_MCR, ASI_PHYS_IO);
#else
	imcmcr = *(u_int8_t *)MC0_MCR;
#endif
	if (imcmcr & MCR_BANK0_AVAIL)
		physmem += (imcmcr & MCR_BANK0_32M) ? 32 : 8;
	if (imcmcr & MCR_BANK1_AVAIL)
		physmem += (imcmcr & MCR_BANK1_32M) ? 32 : 8;

	if ((icuconf & CONF_NO_EXTRA_MEMORY) == 0) {
#if 0
		imcmcr = lduba(MC1_MCR, ASI_PHYS_IO);
#else
		imcmcr = *(u_int8_t *)MC1_MCR;
#endif
		if (imcmcr & MCR_BANK0_AVAIL)
			physmem += (imcmcr & MCR_BANK0_32M) ? 32 : 8;
		if (imcmcr & MCR_BANK1_AVAIL)
			physmem += (imcmcr & MCR_BANK1_32M) ? 32 : 8;
	}

	/* scale to pages */
	physmem <<= (20 - PAGE_SHIFT);

	/*
	 * Get a grip on the PROM communication area.
	 */
	sp = (struct sb_prom *)PROM_DATA_VA;

	/*
	 * Set virtual page size.
	 */
	uvmexp.pagesize = PAGE_SIZE;
	uvm_setpagesize();

	/*
	 * Initialize kernel pmap.
	 */
	pmap_kernel()->pm_refcount = 1;

	/*
	 * Compute kernel fixed memory usage.
	 */
	ekern = (vaddr_t)&end;
#if defined(DDB) || NKSYMS > 0
	if (esym != 0)
		ekern = esym;
#endif

	/*
	 * Reserve room for the prom data we're interested in.
	 */
	prom_data = ekern;
	ekern += promdata;

	/*
	 * From then on, all allocations will be multiples of the
	 * page size.
	 */
	ekern = round_page(ekern);

	/*
	 * Reserve buffers for the on-board Lance chip - the whole buffer
	 * must be in the same 128KB segment.
	 * This should disappear once iCU is tamed...
	 */
	if ((ekern >> 17) != ((ekern + MEMSIZE) >> 17))
		ekern = roundup(ekern, 1 << 17);
	lance_va = ekern;
	ekern += MEMSIZE;

	/*
	 * Initialize fixed mappings.
	 * We want to keep the PTW mapping the kernel for now, but all
	 * devices needed during early bootstrap needs to have their own
	 * mappings.
	 */

	/*
	 * Step 1: reserve memory for the kernel pde.
	 */

	bzero((caddr_t)ekern, PDT_SIZE);
	pmap_kernel()->pm_segtab = (pd_entry_t *)ekern;
	pmap_kernel()->pm_psegtab = PTW1_TO_PHYS(ekern);
	ekern += PDT_SIZE;	/* not rounded anymore ! */

	/*
	 * Step 2: create as many pages tables as necessary.
	 * We'll provide page tables for the kernel virtual memory range
	 * and the top of memory (i.e. PTW1, PTW2 and I/O maps), so that
	 * we can invoke mapdev() early in the boot process.
	 *
	 * For the early console, we will also provide an 1:1 mapping
	 * of the I/O space.
	 */

	tabidx = 0;

	va = VM_MIN_KERNEL_ADDRESS;
	while (va != 0) {
		pde = pmap_pde(pmap_kernel(), va);

		pde->pde_va = (pt_entry_t *)(ekern + tabidx * PT_SIZE);
		pde->pde_pa = PTW1_TO_PHYS((vaddr_t)pde->pde_va);

		tabidx++;
		va += NBSEG;
	}

	va = (vaddr_t)OBIO_PA_START;
	while (va < (vaddr_t)OBIO_PA_END) {
		pde = pmap_pde(pmap_kernel(), va);

		pde->pde_va = (pt_entry_t *)(ekern + tabidx * PT_SIZE);
		pde->pde_pa = PTW1_TO_PHYS((vaddr_t)pde->pde_va);

		tabidx++;
		va += NBSEG;
	}

	va = IOSPACE_BASE;
	while (va < IOSPACE_BASE + IOSPACE_LEN) {
		pde = pmap_pde(pmap_kernel(), va);

		pde->pde_va = (pt_entry_t *)(ekern + tabidx * PT_SIZE);
		pde->pde_pa = PTW1_TO_PHYS((vaddr_t)pde->pde_va);

		tabidx++;
		/* watch out for wraparound! */
		if ((va += NBSEG) == 0)
			break;
	}

	bzero((caddr_t)ekern, tabidx * PT_SIZE);
	ekern += tabidx * PT_SIZE;
	ekern = round_page(ekern);

	/*
	 * Step 3: fill them. We fill the page tables backing PTW1 and
	 * PTW2 to simplify pmap_extract(), by not having to check if
	 * the va is in a PTW.
	 */

	va = PTW1_BASE;
	pa = PHYSMEM_BASE;
	while (va < PTW1_BASE + PTW_WINDOW_SIZE) {
		pde = pmap_pde(pmap_kernel(), va);
		eva = trunc_seg(va) + NBSEG;
		if (eva > PTW1_BASE + PTW_WINDOW_SIZE)
			eva = PTW1_BASE + PTW_WINDOW_SIZE;
		pte = pde_pte(pde, va);
		while (va < eva) {
			*pte++ = pa | PG_V | PG_S | PG_U | PG_CACHE;
			va += PAGE_SIZE;
			pa += PAGE_SIZE;
		}
	}

	va = PTW2_BASE;
	pa = PHYSMEM_BASE;
	while (va < PTW2_BASE + PTW_WINDOW_SIZE) {
		pde = pmap_pde(pmap_kernel(), va);
		eva = trunc_seg(va) + NBSEG;
		if (eva > PTW2_BASE + PTW_WINDOW_SIZE)
			eva = PTW2_BASE + PTW_WINDOW_SIZE;
		pte = pde_pte(pde, va);
		while (va < eva) {
			*pte++ = pa | PG_V | PG_S | PG_U | PG_SHARED;
			va += PAGE_SIZE;
			pa += PAGE_SIZE;
		}
	}

	va = (vaddr_t)OBIO_PA_START;
	while (va < (vaddr_t)OBIO_PA_END) {
		pde = pmap_pde(pmap_kernel(), va);
		eva = trunc_seg(va) + NBSEG;
		if (eva > OBIO_PA_END)
			eva = OBIO_PA_END;
		pte = pde_pte(pde, va);
		for (; va < eva; va += PAGE_SIZE)
			*pte++ = va | PG_V | PG_S | PG_U | PG_IO;
	}

	/*
	 * Compute the virtual memory space.
	 * Note that the kernel is mapped by PTW1 and PTW2, and is outside
	 * this range.
	 */

	virtual_avail = VM_MIN_KERNEL_ADDRESS;
	virtual_end = VM_MAX_KERNEL_ADDRESS;

	/*
	 * Reserve two _virtual_ pages for copy and zero operations.
	 * Since we need to be able to tweak their PTE, they need to be
	 * outside PTW1 and PTW2. We'll steal them from the top of the
	 * virtual space; thus we are sure they will be in the same
	 * segment as well.
	 */

	virtual_end -= 2* PAGE_SIZE;
	vreserve = virtual_end;
	ptereserve = pmap_pte(pmap_kernel(), vreserve);

	/*
	 * Tell the VM system about the available memory.
	 * Physical memory starts at PHYSMEM_BASE; kernel uses space
	 * from PTW1_TO_PHYS(KERNBASE) to ekern at this point.
	 *
	 * The physical memory below the kernel is reserved for the PROM
	 * data and bss, and need to be left intact when invoking it, so
	 * we do not upload (manage) it.
	 *
	 * The PROM communication area may claim another area, way above
	 * the kernel (usually less than 200 KB, immediately under 8MB
	 * physical).
	 */

	if (sp->sp_interface >= PROM_INTERFACE) {
		prompa = atop(PHYSMEM_BASE) + sp->sp_reserve_start;
		promlen = sp->sp_reserve_len;
	} else
		promlen = 0;

	if (promlen != 0) {
#ifdef DIAGNOSTIC
		if (PTW1_TO_PHYS(ekern) > ptoa(prompa))
			panic("kernel overlaps PROM reserved area");
#endif
		uvm_page_physload(
		    atop(PTW1_TO_PHYS(ekern)), prompa,
		    atop(PTW1_TO_PHYS(ekern)), prompa, 0);
		uvm_page_physload(
		    prompa + promlen, atop(PHYSMEM_BASE) + physmem,
		    prompa + promlen, atop(PHYSMEM_BASE) + physmem, 0);
	} else {
		uvm_page_physload(
		    atop(PTW1_TO_PHYS(ekern)), atop(PHYSMEM_BASE) + physmem,
		    atop(PTW1_TO_PHYS(ekern)), atop(PHYSMEM_BASE) + physmem, 0);
	}
}

/*
 * Return the virtual area range available to the kernel.
 */
void
pmap_virtual_space(vaddr_t *v_start, vaddr_t *v_end)
{
	*v_start = virtual_avail;
	*v_end = virtual_end;
}

/*
 * Secondary initialization, at uvm_init() time.
 * We can now create the pools we'll use for pmap and pvlist allocations.
 */
void
pmap_init()
{
	pool_init(&pmappool, sizeof(struct pmap), 0, 0, 0, "pmappl",
	    &pool_allocator_nointr);
	pool_init(&pvpool, sizeof(struct pvlist), 0, 0, 0, "pvpl", NULL);
}

/*
 * Create a new pmap.
 *
 * We initialize pmaps with an empty pde, and a shadow of the kernel
 * space (VM_MIN_KERNEL_ADDRESS onwards).
 */
pmap_t
pmap_create()
{
	pmap_t pmap;
	u_int pde;

	DPRINTF(PDB_CREATE, ("pmap_create()"));

	pmap = pool_get(&pmappool, PR_WAITOK | PR_ZERO);

	pmap->pm_refcount = 1;

	/*
	 * Allocate the page directory.
	 */
	pmap->pm_segtab = (pd_entry_t *)uvm_km_zalloc(kernel_map, PDT_SIZE);
	if (pmap_extract(pmap_kernel(), (vaddr_t)pmap->pm_segtab,
	    &pmap->pm_psegtab) == FALSE)
		panic("pmap_create: pmap_extract failed!");

	/*
	 * Shadow the kernel map in all user pmaps.
	 */
	for (pde = (VM_MIN_KERNEL_ADDRESS >> PDT_INDEX_SHIFT);
	    pde < NBR_PDE; pde++) {
		pmap->pm_segtab[pde].pde_pa =
		    pmap_kernel()->pm_segtab[pde].pde_pa;
		pmap->pm_segtab[pde].pde_va =
		    pmap_kernel()->pm_segtab[pde].pde_va;
	}

	DPRINTF(PDB_CREATE, (" -> %p\n", pmap));

	return (pmap);
}

/*
 * Destroy a pmap.
 * Its mappings will not actually be removed until the reference count
 * drops to zero.
 */
void
pmap_destroy(struct pmap *pmap)
{
	int count;

	DPRINTF(PDB_DESTROY, ("pmap_destroy(%p)\n", pmap));

	count = --pmap->pm_refcount;
	if (count == 0) {
		pmap_release(pmap);
		pool_put(&pmappool, pmap);
	}
}

/*
 * Release all mappings and resources associated to a given pmap.
 */
void
pmap_release(struct pmap *pmap)
{
	u_int pde;
	pt_entry_t *pdeva;
#ifdef DIAGNOSTIC
	u_int pte;
#endif

	DPRINTF(PDB_RELEASE, ("pmap_release(%p)\n", pmap));

	/*
	 * Free all page tables.
	 */
	for (pde = 0; pde < (VM_MIN_KERNEL_ADDRESS >> PDT_INDEX_SHIFT); pde++) {
		if ((pdeva = pmap->pm_segtab[pde].pde_va) != NULL) {
#ifdef DIAGNOSTIC
			for (pte = 0; pte < NBR_PTE; pte++)
				if (pdeva[pte] & PG_V) {
					DPRINTF(PDB_RELEASE,
					    ("pmap_release: unreleased pte "
					    "%p (%08x)\n",
					    pdeva + pte, pdeva[pte]));
				}
#endif
			uvm_km_free(kernel_map, (vaddr_t)pdeva, PT_SIZE);
		}
	}

	/*
	 * Free the page directory.
	 */
	uvm_km_free(kernel_map, (vaddr_t)pmap->pm_segtab, PDT_SIZE);
}

/*
 * Returns a preferred virtual address for the given address, which
 * does not cause a VAC aliasing situation.
 */
vaddr_t
pmap_prefer(vaddr_t foff, vaddr_t va)
{
	/* XXX assume no cache aliasing yet */
	return va;
}

/*
 * Activate the pmap associated to a given process.
 * Called from the scheduler.
 */
void
pmap_activate(struct proc *p)
{
	pmap_t pmap = p->p_vmspace->vm_map.pmap;
	int s;

	DPRINTF(PDB_ACTIVATE,
	    ("pmap_activate(%p/pmap %p/segtab pa %08x va %08x)\n",
	    p, pmap, pmap->pm_psegtab, (vaddr_t)pmap->pm_segtab));

	s = splvm();

	if (p == curproc) {
		write_user_windows();
		cache_flush_context();
		sta(0, ASI_PID, 0);
		sta(0, ASI_PDBR, pmap->pm_psegtab);
		tlb_flush_all();
	}

	splx(s);
}

/*
 * Increment the pmap reference counter.
 */
void
pmap_reference(struct pmap *pmap)
{
	DPRINTF(PDB_REFERENCE, ("pmap_reference(%p)\n", pmap));

	pmap->pm_refcount++;
}

/*
 * Remove a range of virtual addresses from the given pmap.
 * Addresses are expected to be page-aligned.
 */
void
pmap_remove(struct pmap *pmap, vaddr_t sva, vaddr_t e)
{
	vaddr_t va, eva;
	paddr_t pa;
	pd_entry_t *pde;
	pt_entry_t *pte, opte;
	struct vm_page *pg;
	struct pvlist *pvl, *prev, *cur;
	int s;

	s = splvm();

	DPRINTF(PDB_REMOVE, ("pmap_remove(%p,%08x,%08x)\n", pmap, sva, e));

	va = sva;
	while (va != e) {
		pde = pmap_pde(pmap, va);
		eva = trunc_seg(va) + NBSEG;
		if (eva > e || eva == 0)
			eva = e;

		if (pde == NULL) {
			va = eva;
			continue;
		}

		pte = pde_pte(pde, va);
		for (; va != eva; va += PAGE_SIZE, pte++) {
			opte = *pte;
			if ((opte & PG_V) == 0)
				continue;

			pmap->pm_stats.resident_count--;

			pa = opte & PG_FRAME;

#ifdef DIAGNOSTIC
			if (opte & PG_W) {
				printf("pmap_remove(%p): wired mapping for %08x",
				    pmap, va);
				pmap->pm_stats.wired_count--;
			}
#endif

			*pte = PG_NV;
			tlb_flush(va);

			pg = PHYS_TO_VM_PAGE(pa);
			if (pg == NULL)
				continue;

			/*
			 * Remove the mapping from the pvlist for this
			 * physical page.
			 */
			pvl = pg_to_pvl(pg);
#ifdef DIAGNOSTIC
			if (pvl->pv_pmap == NULL)
				panic("pmap_remove: NULL pmap in pvlist");
#endif
			prev = NULL;
			for (cur = pvl; cur != NULL; cur = cur->pv_next) {
				if (cur->pv_va == va && cur->pv_pmap == pmap)
					break;
				prev = cur;
			}
#ifdef DIAGNOSTIC
			if (cur == NULL) {
				panic("pmap_remove: va not in pvlist");
			}
#endif
			if (prev == NULL) {
				cur = cur->pv_next;
				if (cur != NULL) {
					cur->pv_flags = pvl->pv_flags;
					*pvl = *cur;
					pool_put(&pvpool, cur);
				} else {
					pvl->pv_pmap = NULL;
				}
			} else {
				prev->pv_next = cur->pv_next;
				pool_put(&pvpool, cur);
			}

			/* update saved attributes for managed page */
			pvl->pv_flags |= (opte & (PG_U | PG_M));
		}
	}

	splx(s);
}

/*
 * Release any unnecessary management resources for the given pmap,
 * before swapping it out.
 */
void
pmap_collect(struct pmap *pmap)
{
	u_int pde, pte;
	pt_entry_t *pdeva;
	int s;

	s = splvm();

	DPRINTF(PDB_COLLECT, ("pmap_collect(%p)\n", pmap));

	/*
	 * Free all empty page tables.
	 */
	for (pde = 0; pde < (VM_MIN_KERNEL_ADDRESS >> PDT_INDEX_SHIFT); pde++) {
		if ((pdeva = pmap->pm_segtab[pde].pde_va) == NULL)
			continue;
		for (pte = 0; pte < NBR_PTE; pte++)
			if (pdeva[pte] & PG_V)
				break;
		if (pte != NBR_PTE)
			continue;

		/*
		 * Free the unused page table.
		 */
		pmap->pm_segtab[pde].pde_va = NULL;
		pmap->pm_segtab[pde].pde_pa = 0;
		uvm_km_free(kernel_map, (vaddr_t)pdeva, PT_SIZE);
	}

	splx(s);
}

/*
 * Change the protection for a given vm_page. The protection can only
 * become more strict, i.e. protection rights get removed.
 *
 * Note that this pmap does not manage execution protection yet.
 */
void
pmap_page_protect(struct vm_page *pg, vm_prot_t prot)
{
	struct pvlist *pvl;
	int s;

	if ((prot & PROT_READ) == PROT_NONE) {	/* remove all */
		s = splvm();
		pvl = pg_to_pvl(pg);

		DPRINTF(PDB_REMOVE, ("pmap_page_protect(%p/pmap %p,%x)\n",
		    pg, pvl->pv_pmap, prot));

		while (pvl->pv_pmap != NULL) {
			pmap_remove(pvl->pv_pmap, pvl->pv_va,
			    pvl->pv_va + PAGE_SIZE);
		}

		splx(s);
	} else if ((prot & PROT_WRITE) == PROT_NONE) {
		s = splvm();
		pvl = pg_to_pvl(pg);

		DPRINTF(PDB_REMOVE, ("pmap_page_protect(%p/pmap %p,%x)\n",
		    pg, pvl->pv_pmap, prot));

		if (pvl->pv_pmap != NULL)
			for (; pvl != NULL; pvl = pvl->pv_next)
				pmap_protect(pvl->pv_pmap, pvl->pv_va,
				    pvl->pv_va + PAGE_SIZE, prot);

		splx(s);
	} else {
		DPRINTF(PDB_REMOVE, ("pmap_page_protect(%p,%x)\n", pg, prot));
	}
}

/*
 * Set the protection for a virtual address range in the given pmap.
 *
 * Note that this pmap does not manage execution protection yet.
 */
void
pmap_protect(struct pmap *pmap, vaddr_t sva, vaddr_t e, vm_prot_t prot)
{
	vaddr_t va, eva;
	pd_entry_t *pde;
	pt_entry_t *pte, opte, npte;
	int s;

	s = splvm();

	DPRINTF(PDB_PROTECT,
	    ("pmap_protect(%p,%08x,%08x,%x)\n", pmap, sva, e, prot));

	if ((prot & PROT_READ) == PROT_NONE) {
		pmap_remove(pmap, sva, e);
		splx(s);
		return;
	}

	va = sva;
	while (va != e) {
		pde = pmap_pde(pmap, va);
		eva = trunc_seg(va) + NBSEG;
		if (eva > e || eva == 0)
			eva = e;

		if (pde == NULL) {
			va = eva;
			continue;
		}

		pte = pde_pte(pde, va);
		for (; va != eva; va += PAGE_SIZE, pte++) {
			opte = *pte;
			if ((opte & PG_V) == 0)
				continue;

			npte = (opte & ~PG_RO) |
			    (prot & PROT_WRITE) ? PG_RW : PG_RO;
			if (opte != npte) {
				*pte = npte;
				tlb_flush(va);
			}
		}
	}

	splx(s);
}

/*
 * Expand a pmap, if necessary, to include a pte.
 */
pt_entry_t *
pmap_grow_pte(struct pmap *pmap, vaddr_t va)
{
	pd_entry_t *pde;

	pde = pmap_pde(pmap, va);
	if (pde->pde_va == NULL) {
		pde->pde_va = (pt_entry_t *)uvm_km_zalloc(kernel_map, PT_SIZE);
		if (pde->pde_va == NULL)
			return (NULL);
		if (pmap_extract(pmap_kernel(), (vaddr_t)pde->pde_va,
		    (paddr_t *)&pde->pde_pa) == FALSE)
			panic("pmap_grow_pte: pmap_extract on PT failed!");
		tlb_flush((vaddr_t)pmap->pm_segtab);
	}

	return (pde_pte(pde, va));
}

/*
 * Create or update a mapping for the page at the given physical and
 * virtual addresses, for the given pmap.
 */
int
pmap_enter(struct pmap *pmap, vaddr_t va, paddr_t pa, vm_prot_t prot, int flags)
{
	pt_entry_t *pte, opte, npte;
	struct vm_page *pg;
	struct pvlist *pvl, *cur;
	int s;

	s = splvm();

	DPRINTF(PDB_ENTER,
	    ("pmap_enter(%p,%08x,%08x,%x,%x)", pmap, va, pa, prot, flags));

	if ((pte = pmap_grow_pte(pmap, va)) == NULL) {
		DPRINTF(PDB_ENTER, (" -> pmap_grow_pte failed\n"));
		if (flags & PMAP_CANFAIL) {
			splx(s);
			return (ENOMEM);
		} else
			panic("pmap_enter: unable to allocate PT");
	}

	opte = *pte;
	DPRINTF(PDB_ENTER, (" opte %08x", opte));

	/*
	 * Enable cache, by default, if on physical memory, unless
	 * PMAP_NC has been passed in pa.
	 */
	switch (pa & PAGE_MASK) {
	case PMAP_NC:
		npte = PG_IO;
		break;
	case PMAP_BWS:
		npte = PG_BYTE_SHARED;
		break;
	default:
		if (pa >= PHYSMEM_BASE && pa < PHYSMEM_BASE + ptoa(physmem))
			npte = PG_CACHE;
		else
			npte = PG_IO;
		break;
	}

	pa = trunc_page(pa);
	npte |= pa | PG_V | (prot & PROT_WRITE ? PG_RW : PG_RO);

	pg = PHYS_TO_VM_PAGE(pa);
	if (pg != NULL) {
		/*
		 * For a managed page, enter the mapping in the pvlist.
		 */
		pvl = pg_to_pvl(pg);

		if (pvl->pv_pmap == NULL) {
			/*
			 * We are the first mapping.
			 */
			pvl->pv_pmap = pmap;
			pvl->pv_va = va;
			pvl->pv_next = NULL;
		} else {
			/*
			 * Add ourselves to the list.
			 * Note that, if we are only changing attributes
			 * and/or protection, we are already in the list!
			 */
			for (cur = pvl; cur != NULL; cur = cur->pv_next) {
				if (pmap == cur->pv_pmap && va == cur->pv_va)
					break;
			}

			if (cur == NULL) {
				cur = pool_get(&pvpool, PR_NOWAIT);
				if (cur == NULL) {
					if (flags & PMAP_CANFAIL)
						return (ENOMEM);
					else
						panic("pmap_enter: "
						    "pvlist pool exhausted");
				}
				/*
				 * Add the new entry after the header.
				 */
				cur->pv_pmap = pmap;
				cur->pv_va = va;
				cur->pv_flags = 0;
				cur->pv_next = pvl->pv_next;
				pvl->pv_next = cur;
			}
		}
	}

	if (flags & PMAP_WIRED) {
		npte |= PG_W;
		if ((opte & PG_W) == 0)
			pmap->pm_stats.wired_count++;
	} else {
		if ((opte & PG_W) != 0)
			pmap->pm_stats.wired_count--;
	}
	if ((opte & PG_V) == 0)
		pmap->pm_stats.resident_count++;
	if (pa >= VM_MIN_KERNEL_ADDRESS)
		npte |= PG_S;

	/*
	 * Now update the pte.
	 */
	if (opte != npte) {
		DPRINTF(PDB_ENTER, (" -> npte %08x", npte));
		*pte = npte;
		tlb_flush(va);
	}

	DPRINTF(PDB_ENTER, ("\n"));

	splx(s);

	return (0);
}

/*
 * Specific flavour of pmap_enter() for unmanaged wired mappings in the
 * kernel pmap.
 */
void
pmap_kenter_pa(vaddr_t va, paddr_t pa, vm_prot_t prot)
{
	pt_entry_t *pte, opte, npte;
	int s;

	s = splvm();

	DPRINTF(PDB_KENTER,
	    ("pmap_kenter_pa(%08x,%08x,%x)", va, pa, prot));

	if ((pte = pmap_grow_pte(pmap_kernel(), va)) == NULL) {
		DPRINTF(PDB_KENTER, (" -> pmap_grow_pte failed\n"));
		panic("pmap_kenter_pa: unable to allocate PT");
	}

	opte = *pte;
	DPRINTF(PDB_KENTER, (" opte %08x", opte));

	/*
	 * Enable cache, by default, if on physical memory, unless
	 * PMAP_NC has been passed in pa.
	 */
	switch (pa & PAGE_MASK) {
	case PMAP_NC:
		npte = PG_IO;
		break;
	case PMAP_BWS:
		npte = PG_BYTE_SHARED;
		break;
	default:
		if (pa >= PHYSMEM_BASE && pa < PHYSMEM_BASE + ptoa(physmem))
			npte = PG_CACHE;
		else
			npte = PG_IO;
		break;
	}

	pa = trunc_page(pa);
	npte |= pa | PG_V | PG_W | (prot & PROT_WRITE ? PG_RW : PG_RO);

	if ((opte & PG_W) == 0)
		pmap_kernel()->pm_stats.wired_count++;
	if ((opte & PG_V) == 0)
		pmap_kernel()->pm_stats.resident_count++;
	if (pa >= VM_MIN_KERNEL_ADDRESS)
		npte |= PG_S;

	/*
	 * Now update the pte.
	 */
	if (opte != npte) {
		DPRINTF(PDB_KENTER, (" -> npte %08x", npte));
		*pte = npte;
		tlb_flush(va);
	}

	DPRINTF(PDB_KENTER, ("\n"));

	splx(s);
}

/*
 * Specific flavour of pmap_remove for unmanaged wired mappings in the
 * kernel pmap.
 */
void
pmap_kremove(vaddr_t va, vsize_t len)
{
	vaddr_t e, eva;
	pd_entry_t *pde;
	pt_entry_t *pte, opte;
	int s;

	s = splvm();

	DPRINTF(PDB_KREMOVE, ("pmap_kremove(%08x,%08x)\n", va, len));

	e = va + len;
	while (va != e) {
		pde = pmap_pde(pmap_kernel(), va);
		eva = trunc_seg(va) + NBSEG;
		if (eva > e || eva == 0)
			eva = e;

		if (pde == NULL) {
			va = eva;
			continue;
		}

		pte = pde_pte(pde, va);
		for (; va != eva; va += PAGE_SIZE, pte++) {
			opte = *pte;
			if ((opte & PG_V) == 0)
				continue;

			pmap_kernel()->pm_stats.resident_count--;

#ifdef DIAGNOSTIC
			if (!(opte & PG_W)) {
				printf("pmap_kremove: non-wired mapping for %08x",
				    va);
			} else
#endif
			pmap_kernel()->pm_stats.wired_count--;

			*pte = PG_NV;
			tlb_flush(va);
		}
	}

	splx(s);
}

/*
 * Remove the wiring state of a page in the given pmap.
 */
void
pmap_unwire(struct pmap *pmap, vaddr_t va)
{
	pt_entry_t *pte;
	int s;

	s = splvm();

	DPRINTF(PDB_UNWIRE, ("pmap_unwire(%p,%08x)\n", pmap, va));

	pte = pmap_pte(pmap, va);

	if (*pte & PG_V)
		if (*pte & PG_W) {
			pmap->pm_stats.wired_count--;
			/* No need to flush TLB, it's a software flag */
			*pte &= ~PG_W;
		}

	splx(s);
}

/*
 * Compute the physical address of a given virtual address in the given pmap.
 * If the physical address is not mapped by this pmap, FALSE is returned.
 */
boolean_t
pmap_extract(struct pmap *pmap, vaddr_t va, paddr_t *pap)
{
	pt_entry_t *pte;
	paddr_t pa;
	boolean_t rv;
	int s;

	DPRINTF(PDB_EXTRACT, ("pmap_extract(%p,%08x)", pmap, va));

	s = splvm();

	pte = pmap_pte(pmap, va);
	if (pte != NULL && (*pte & PG_V) != 0) {
		rv = TRUE;
		pa = (*pte & PG_FRAME) | (va & PAGE_MASK);
		DPRINTF(PDB_EXTRACT, (" -> %08x\n", pa));
		if (pap != NULL)
			*pap = pa;
	} else {
		DPRINTF(PDB_EXTRACT, (" -> FALSE\n"));
		rv = FALSE;
	}

	splx(s);

	return (rv);
}

/*
 * Walk a vm_page and flush all existing mappings.
 */
void
pg_flushcache(struct vm_page *pg)
{
	struct pvlist *pvl;
	int s;

	s = splvm();

	pvl = pg_to_pvl(pg);
	if (pvl->pv_pmap == NULL) {
		splx(s);
		return;
	}

	/*
	 * Since cache_flush_page() causes the whole cache to be flushed,
	 * there is no need to loop - flush once.
	 */
	/* for (; pvl != NULL; pvl = pvl->pv_next) */
		cache_flush_page(pvl->pv_va);

	splx(s);
}

/*
 * Fill a vm_page with zeroes.
 */
void
pmap_zero_page(struct vm_page *pg)
{
	paddr_t pa;
	vaddr_t va;
	pt_entry_t *pte;
	int s;

	s = splvm();

	pa = VM_PAGE_TO_PHYS(pg);
	va = vreserve;
	pte = ptereserve;

	DPRINTF(PDB_ZERO, ("pmap_zero_page(%p/pa %x) pte %p\n", pg, pa, pte));

	pg_flushcache(pg);

	*pte = PG_V | PG_S | (pa & PG_FRAME);
	tlb_flush(va);

	qzero((caddr_t)va, PAGE_SIZE);
	cache_flush_page(va);

	/* paranoia */
	*pte = PG_NV;
	tlb_flush(va);

	splx(s);
}

/*
 * Copy the contents of a vm_page to another.
 */
void
pmap_copy_page(struct vm_page *srcpg, struct vm_page *dstpg)
{
	paddr_t srcpa, dstpa;
	vaddr_t srcva, dstva;
	pt_entry_t *srcpte, *dstpte;
	int s;

	s = splvm();

	DPRINTF(PDB_COPY, ("pmap_copy_page(%p,%p)\n", srcpg, dstpg));

	srcpa = VM_PAGE_TO_PHYS(srcpg);
	dstpa = VM_PAGE_TO_PHYS(dstpg);
	srcva = vreserve;
	dstva = srcva + PAGE_SIZE;

	dstpte = ptereserve;
	srcpte = dstpte++;

	pg_flushcache(srcpg);
	/*
	 * Since pg_flushcache() causes the whole cache to be flushed,
	 * there is no need flush dstpg.
	 */
	/* pg_flushcache(dstpg); */

	*srcpte = PG_V | PG_S | PG_RO | (srcpa & PG_FRAME);
	*dstpte = PG_V | PG_S | (dstpa & PG_FRAME);

	tlb_flush(srcva);
	tlb_flush(dstva);

	qcopy((caddr_t)srcva, (caddr_t)dstva, PAGE_SIZE);
	cache_flush_page(srcva);

	*srcpte = *dstpte = PG_NV;
	tlb_flush(srcva);
	tlb_flush(dstva);

	splx(s);
}

/*
 * Clear the modify bits on all mappings associated to the given vm_page.
 */
boolean_t
pmap_clear_modify(struct vm_page *pg)
{
	struct pvlist *pvl;
	pt_entry_t *pte;
	boolean_t rv;
	int s;
	int flushed;

	s = splvm();

	pvl = pg_to_pvl(pg);

	DPRINTF(PDB_CLEAR_M,
	    ("pmap_clear_modify(%p/pmap %p)\n", pg, pvl->pv_pmap));

	if (pvl->pv_flags & PG_M) {
		pvl->pv_flags &= ~PG_M;
		rv = TRUE;
	}

	if (pvl->pv_pmap != NULL) {
		flushed = 0;
		for (; pvl != NULL; pvl = pvl->pv_next) {
			pte = pmap_pte(pvl->pv_pmap, pvl->pv_va);
			if ((*pte & PG_V) != 0 && (*pte & PG_M) != 0) {
				/*
				 * Since cache_flush_page() causes the whole
				 * cache to be flushed, only flush once.
				 */
				if (flushed == 0) {
					cache_flush_page(pvl->pv_va);
					flushed = 1;
				}

				rv = TRUE;
				/* No need to flush TLB, it's a software flag */
				*pte &= ~PG_M;
			}
		}
	}

	splx(s);

	return (rv);
}

/*
 * Clear the reference bits on all mappings associated to the given vm_page.
 */
boolean_t
pmap_clear_reference(struct vm_page *pg)
{
	struct pvlist *pvl;
	pt_entry_t *pte;
	boolean_t rv;
	int s;

	s = splvm();

	pvl = pg_to_pvl(pg);

	DPRINTF(PDB_CLEAR_U,
	    ("pmap_clear_reference(%p/pmap %p)\n", pg, pvl->pv_pmap));

	if (pvl->pv_flags & PG_U) {
		pvl->pv_flags &= ~PG_U;
		rv = TRUE;
	}

	if (pvl->pv_pmap != NULL)
	for (; pvl != NULL; pvl = pvl->pv_next) {
		pte = pmap_pte(pvl->pv_pmap, pvl->pv_va);
		if ((*pte & PG_V) != 0 && (*pte & PG_U) != 0) {
			rv = TRUE;
			/* No need to flush TLB, it's a software flag */
			*pte &= ~PG_U;
		}
	}

	splx(s);

	return (rv);
}

/*
 * Check the reference bit attribute for the given vm_page.
 */
boolean_t
pmap_is_referenced(struct vm_page *pg)
{
	struct pvlist *pvl;
	boolean_t rv;
	int s;

	s = splvm();

	pvl = pg_to_pvl(pg);
	rv = (pvl->pv_flags & PG_U) != 0;

	DPRINTF(PDB_IS_U,
	    ("pmap_is_referenced(%p/pmap %p) -> %d\n", pg, pvl->pv_pmap, rv));

	splx(s);

	return (rv);
}

/*
 * Check the modify bit attribute for the given vm_page.
 */
boolean_t
pmap_is_modified(struct vm_page *pg)
{
	struct pvlist *pvl;
	boolean_t rv;
	int s;

	s = splvm();

	pvl = pg_to_pvl(pg);
	rv = (pvl->pv_flags & PG_M) != 0;

	DPRINTF(PDB_IS_M,
	    ("pmap_is_modified(%p/pmap %p) -> %d\n", pg, pvl->pv_pmap, rv));

	splx(s);

	return (rv);
}

/*
 * Flush instruction cache on the given dirty area.
 *
 * The KAP is the only sparc implementation OpenBSD runs on with independent
 * instruction and data caches; for now, we won't add a function pointer
 * to the cpu structure, but will directly invoke the necessary operation.
 */
void
pmap_proc_iflush(struct proc *p, vaddr_t va, vsize_t len)
{
	/* There is no way to invalidate a subset of the icache */
	sta(0, ASI_ICACHE_INVAL, 0);
}

/*
 * The following routines are not part of the MI pmap API, but are
 * necessary to use the common sparc code.
 */

/*
 * Enable caching of the page tables if necessary.
 */
void
pmap_cache_enable()
{
	/* nothing to do */
}

/*
 * Change the protection for a specific kernel mapping.
 * Used by machdep.c only.
 */
void
pmap_changeprot(struct pmap *pmap, vaddr_t va, vm_prot_t prot, int wired)
{
	pt_entry_t *pte, npte;
	int s;

	s = splvm();

	npte = PG_S | (prot & PROT_WRITE ? PG_RW : PG_RO);

	pte = pmap_pte(pmap, va);
	if ((*pte & PG_PROT) != npte) {
		*pte = (*pte & ~PG_PROT) | npte;
		tlb_flush(va);
	}

	splx(s);
}

/*
 * Set a ``red zone'' below the kernel.
 */
void
pmap_redzone()
{
}

/*
 * Write a given byte in a protected page; used by the ddb breakpoints.
 */
void
pmap_writetext(unsigned char *dst, int ch)
{
	pt_entry_t *pte, opte;
	int s;

	/*
	 * Check for a PTW hit first.
	 */
	switch ((vaddr_t)dst >> PTW_WINDOW_SHIFT) {
	case PTW1_WINDOW:
	case PTW2_WINDOW:
		*dst = (unsigned char)ch;
		cpuinfo.cache_flush(dst, 1);
		return;
	}

	s = splvm();

	pte = pmap_pte(pmap_kernel(), (vaddr_t)dst);
	if (pte != NULL) {
		opte = *pte;
		if ((opte & PG_V) != 0) {
			cpuinfo.cache_flush(dst, 1);

			if ((opte & PG_RO) != 0) {
				*pte &= ~PG_RO;
				tlb_flush(trunc_page((vaddr_t)dst));
			}

			*dst = (unsigned char)ch;

			if ((opte & PG_RO) != 0) {
				*pte = opte;
				tlb_flush(trunc_page((vaddr_t)dst));
			}

			cpuinfo.cache_flush(dst, 1);
		}
	}

	splx(s);
}

/*
 * Enable or disable cache for the given number of pages at the given
 * virtual address.
 */
void
kvm_setcache(caddr_t addr, int npages, int cached)
{
	pt_entry_t *pte, opte;
	vaddr_t va = (vaddr_t)addr;
	int s;
	int flushed;

#ifdef DIAGNOSTIC
	if (va & PAGE_MASK) {
		printf("kvm_setcache: unaligned va %08x\n", va);
		va = trunc_page(va);
	}
#endif

#ifdef DIAGNOSTIC
	/*
	 * Check for a PTW hit first.
	 */
	switch (va >> PTW_WINDOW_SHIFT) {
	case PTW1_WINDOW:
	case PTW2_WINDOW:
		printf("kvm_setcache(%08x, %08x, %d) in a PTW\n",
		    va, npages << PAGE_SHIFT, cached);
		return;
	}
#endif

	s = splvm();

	pte = pmap_pte(pmap_kernel(), va);
	flushed = 0;
	for (; --npages >= 0; va += PAGE_SIZE, pte++) {
		opte = *pte & ~PG_MA;

		if (cached)
			opte |= PG_CACHE;
		else
			opte |= PG_IO;

		*pte = opte;
		tlb_flush(va);

		/*
		 * Since cache_flush_page() causes the whole
		 * cache to be flushed, only flush once.
		 */
		if (flushed == 0) {
			cache_flush_page(va);
			flushed = 1;
		}
	}

	splx(s);
}

/*
 * Simple wrapper around pmap_kenter_pa() for multiple pages.
 */
vaddr_t
pmap_map(vaddr_t va, paddr_t pa, paddr_t epa, int prot)
{
	while (pa < epa) {
		pmap_kenter_pa(va, pa, (vm_prot_t)prot);
		va += PAGE_SIZE;
		pa += PAGE_SIZE;
	}
	return (va);
}

/*
 * Checks whether a given physical address is in physical memory or
 * in device space.
 * Used by mem.c.
 */
int
pmap_pa_exists(paddr_t pa)
{
	return (pa >= PHYSMEM_BASE && pa < PHYSMEM_BASE + ptoa(physmem));
}


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

/*
 * Routines to hide the Solbourne PROM specifics.
 */

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

#include <machine/autoconf.h>
#include <machine/bsd_openprom.h>	/* romboot() prototype */
#include <machine/idt.h>
#include <machine/kap.h>
#include <machine/prom.h>

#include <uvm/uvm_extern.h>

#include <sparc/sparc/asm.h>

int	sysmodel;

void	myetheraddr(u_char *);
void	prom_map(void);
void	prom_unmap(void);

extern void tlb_flush_all(void);

/*
 * Lookup a variable in the environment strings.
 */
const char *
prom_getenv(const char *var)
{
	u_int i;
	const char *eq, *env;
	size_t len;
	extern char **prom_environ;

	len = strlen(var);

	for (i = 0; (env = prom_environ[i]) != NULL; i++) {
		eq = strchr(env, '=');
#ifdef DIAGNOSTIC
		if (eq == NULL)
			continue;	/* can't happen */
#endif
		if (eq - env != len)
			continue;

		if (strncasecmp(var, env, len) == 0) {
			return (eq + 1);
		}
	}

	return (NULL);
}

void
myetheraddr(u_char *cp)
{
	const char *enetaddr;
	int i;

	enetaddr = prom_getenv(ENV_ETHERADDR);
	if (enetaddr == NULL) {
		cp[0] = cp[1] = cp[2] = cp[3] = cp[4] = cp[5] = 0xff;
	} else {
		for (i = 0; i < 6; i++) {
			cp[i] = 0;
			for (;;) {
				if (*enetaddr >= '0' && *enetaddr <= '9')
					cp[i] = cp[i] * 0x10 +
					    (*enetaddr - '0');
				else if (*enetaddr >= 'A' && *enetaddr <= 'F')
					cp[i] = cp[i] * 0x10 +
					    (*enetaddr + 10 - 'A');
				else if (*enetaddr >= 'a' && *enetaddr <= 'f')
					cp[i] = cp[i] * 0x10 +
					    (*enetaddr + 10 - 'a');
				else
					break;

				enetaddr++;
			}
			if (*enetaddr++ != ':')
				break;
		}
		/* fill remaining digits if necessary */
		while (i++ < 6)
			cp[i] = 0;
	}
}

/*
 * Set up PROM-friendly mappings
 */

void
prom_map(void)
{
	sta(0, ASI_PTW0, PTW0_DEFAULT);
	tlb_flush_all();
}

void
prom_unmap(void)
{
	sta(0, ASI_PTW0, 0);
	tlb_flush_all();
}

/*
 * Prom property access
 *
 * Note that if we are passed addresses in the fd va window, we need to
 * temporarily copy these pointers to a ``safe'' address (in this case,
 * a global variable, thus in the f0 or f1 window).
 */

int
getprop(int node, char *name, void *buf, int bufsiz)
{
	struct prom_node *n = (struct prom_node *)node;
	struct prom_prop *p;
	char *propname, *eq;
	int len, proplen;

	len = strlen(name);

#ifdef DIAGNOSTIC
	if (node == 0)
#if 0
		node = findroot();
#else
		panic("getprop(%s) invoked on invalid node", name);
#endif
#endif

	for (p = (struct prom_prop *)n->pn_props; p != NULL; p = p->pp_next) {
		propname = p->pp_data;
		eq = strchr(propname, '=');
#ifdef DIAGNOSTIC
		if (eq == NULL)
			continue;	/* can't happen */
#endif
		if (eq - propname != len)
			continue;

		if (strncmp(name, propname, len) == 0) {
			proplen = p->pp_size;
			if (proplen > bufsiz) {
				printf("node %p property %s length %d > %d",
				    node, name, proplen, bufsiz);
#ifdef DEBUG
				panic("getprop");
#else
				return (0);
#endif
			} else
				bcopy(eq + 1, buf, proplen);
			break;
		}
	}

	if (p == NULL)
		proplen = -1;

	return (proplen);
}

int
getproplen(int node, char *name)
{
	struct prom_node *n = (struct prom_node *)node;
	struct prom_prop *p;
	char *propname, *eq;
	int len, proplen;

#ifdef DIAGNOSTIC
	if (node == 0)
		panic("getproplen(%s) invoked on invalid node", name);
#endif

	len = strlen(name);

	for (p = (struct prom_prop *)n->pn_props; p != NULL; p = p->pp_next) {
		propname = p->pp_data;
		eq = strchr(propname, '=');
#ifdef DIAGNOSTIC
		if (eq == NULL)
			continue;	/* can't happen */
#endif
		if (eq - propname != len)
			continue;

		if (strncmp(name, propname, len) == 0) {
			proplen = p->pp_size;
			break;
		}
	}

	if (p == NULL)
		proplen = -1;

	return (proplen);
}

int
firstchild(int node)
{
	return ((struct prom_node *)node)->pn_child;
}

int
nextsibling(int node)
{
	if (node == 0)
		return (findroot());
	else
		return (((struct prom_node *)node)->pn_sibling);
}

int
findroot()
{
	struct sb_prom *sp;

	sp = (struct sb_prom *)PROM_DATA_VA;
	if (sp->sp_interface >= PROM_INTERFACE)
		return (sp->sp_rootnode);

	panic("findroot: PROM communication interface is too old (%d)",
	    sp->sp_interface);
	/* NOTREACHED */
}

/*
 * Shutdown and reboot interface
 */

void
romhalt()
{
	struct sb_prom *sp;

	sp = (struct sb_prom *)PROM_DATA_VA;
	if (sp->sp_interface >= PROM_INTERFACE) {
		prom_map();
		(*sp->sp_interp)("reset " PROM_RESET_HALT);
		prom_unmap();
	}

	panic("PROM exit failed");
}

void
romboot(char *str)
{
	char command[256];
	struct sb_prom *sp;

	if (*str != '\0') {
		strlcpy(command, "boot ", sizeof command);
		strlcat(command, str, sizeof command);
	} else {
		strlcpy(command, "reset ", sizeof command);
		strlcat(command, PROM_RESET_WARM, sizeof command);
	}

	sp = (struct sb_prom *)PROM_DATA_VA;
	if (sp->sp_interface >= PROM_INTERFACE) {
		prom_map();
		(*sp->sp_interp)(command);
		prom_unmap();
	}

	panic("PROM boot failed");
}


code:
/*	$OpenBSD: trap.c,v 1.22 2014/11/16 12:30:58 deraadt Exp $	*/
/*	OpenBSD: trap.c,v 1.42 2004/12/06 20:12:25 miod 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 <sparc/sparc/asm.h>
#include <machine/cpu.h>
#include <machine/ctlreg.h>
#include <machine/trap.h>
#include <machine/instr.h>
#include <machine/pmap.h>

#include <machine/idt.h>
#include <machine/kap.h>

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

#include <sparc/fpu/fpu_extern.h>
#include <sparc/sparc/memreg.h>
#include <sparc/sparc/cpuvar.h>

#ifdef DEBUG
int	rwindow_debug = 0;
#endif

/*
 * 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.
 */
struct	fpstate 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.
 *
 * Note that some of the Sparc v8 traps are actually handled by
 * the corresponding v7 routine, but listed here for completeness.
 * The Fujitsu Turbo-Sparc Guide also alludes to several more
 * unimplemented trap types, but doesn't give the nominal coding.
 */
static const char T[] = "trap";
const char *trap_type[] = {
	/* non-user vectors */
	"ast",			/* 0 */
	"text fault",		/* 1 */
	"illegal instruction",	/* 2 */
	"privileged instruction",/*3 */
	"fp disabled",		/* 4 */
	"window overflow",	/* 5 */
	"window underflow",	/* 6 */
	"alignment fault",	/* 7 */
	"fp exception",		/* 8 */
	"data fault",		/* 9 */
	"tag overflow",		/* 0a */
	"watchpoint",		/* 0b */
	T, T, T, T, T,		/* 0c..10 */
	"level 1 int",		/* 11 */
	"level 2 int",		/* 12 */
	"level 3 int",		/* 13 */
	"level 4 int",		/* 14 */
	"level 5 int",		/* 15 */
	"level 6 int",		/* 16 */
	"level 7 int",		/* 17 */
	"level 8 int",		/* 18 */
	"level 9 int",		/* 19 */
	"level 10 int",		/* 1a */
	"level 11 int",		/* 1b */
	"level 12 int",		/* 1c */
	"level 13 int",		/* 1d */
	"level 14 int",		/* 1e */
	"level 15 int",		/* 1f */
	"double trap",		/* 20 */
	"v8 text error",	/* 21 */
	T, T,			/* 22..23 */
	"v8 cp disabled",	/* 24 */
	"v8 unimp flush",	/* 25 */
	T, T,			/* 26..27 */
	"v8 cp exception",	/* 28 */
	"v8 data error",	/* 29 */
	"v8 idiv by zero",	/* 2a */
	"v8 store error",	/* 2b */
	"dtlb miss",		/* 2c */
	T, T, T,		/* 2d..2f */
	T, T, T, T, T, T, T, T,	/* 30..37 */
	T, T, T, T,		/* 38..3b */
	"itlb miss",		/* 3c */
	T, T, T,	/* 3d..3f */
	T, T, T, T, T, T, T, T,	/* 40..48 */
	T, T, T, T, T, T, T, T,	/* 48..4f */
	T, T, T, T, T, T, T, T,	/* 50..57 */
	T, T, T, T, T, T, T, T,	/* 58..5f */
	T, T, T, T, T, T, T, T,	/* 60..67 */
	T, T, T, T, T, T, T, T,	/* 68..6f */
	T, T, T, T, T, T, T, T,	/* 70..77 */
	T, T, T, T, T, T, T, T,	/* 78..7f */

	/* user (software trap) vectors */
	"syscall",		/* 80 */
	"breakpoint",		/* 81 */
	"zero divide",		/* 82 */
	"flush windows",	/* 83 */
	"clean windows",	/* 84 */
	"range check",		/* 85 */
	"fix align",		/* 86 */
	"integer overflow",	/* 87 */
	"svr4 syscall",		/* 88 */
	"4.4 syscall",		/* 89 */
	"kgdb exec",		/* 8a */
	T, T, T, T, T,		/* 8b..8f */
	T, T, T, T, T, T, T, T,	/* 9a..97 */
	T, T, T, T, T, T, T, T,	/* 98..9f */
	"svr4 getcc",		/* a0 */
	"svr4 setcc",		/* a1 */
	"svr4 getpsr",		/* a2 */
	"svr4 setpsr",		/* a3 */
	"svr4 gethrtime",	/* a4 */
	"svr4 gethrvtime",	/* a5 */
	T,			/* a6 */
	"svr4 gethrestime",	/* a7 */
};

#define	N_TRAP_TYPES	(sizeof trap_type / sizeof *trap_type)

void trap(unsigned, int, int, struct trapframe *);
static __inline void share_fpu(struct proc *, struct trapframe *);
void mem_access_fault(unsigned, int, u_int, int, int, struct trapframe *);
void ecc_fault(unsigned, int, u_int, int, int, struct trapframe *);
void syscall(register_t, struct trapframe *, register_t);

int ignore_bogus_traps = 0;

int want_ast = 0;

/*
 * 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.
 */
static __inline void share_fpu(p, tf)
	struct proc *p;
	struct trapframe *tf;
{
	if ((tf->tf_psr & PSR_EF) != 0 && cpuinfo.fpproc != p)
		tf->tf_psr &= ~PSR_EF;
}

/*
 * Called from locore.s trap handling, for non-MMU-related traps.
 * (MMU-related traps go through mem_access_fault, below.)
 */
void
trap(type, psr, pc, tf)
	unsigned type;
	int psr, pc;
	struct trapframe *tf;
{
	struct proc *p;
	struct pcb *pcb;
	int n;
	union sigval sv;

        sv.sival_int = pc; /* XXX fix for parm five of trapsignal() */

	/* 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 (psr & PSR_PS) {
#ifdef DDB
		if (type == T_BREAKPOINT) {
			write_all_windows();
			if (kdb_trap(type, tf)) {
				return;
			}
		}
#endif
#ifdef DIAGNOSTIC
		/*
		 * Currently, we allow DIAGNOSTIC kernel code to
		 * flush the windows to record stack traces.
		 */
		if (type == T_FLUSHWIN) {
			write_all_windows();
			ADVANCE;
			return;
		}
#endif
		/*
		 * Storing %fsr in cpu_attach will cause this trap
		 * even though the fpu has been enabled, if and only
		 * if there is no FPU.
		 */
		if (type == T_FPDISABLED && cold) {
			ADVANCE;
			return;
		}
	dopanic:
		printf("trap type 0x%x: pc=0x%x npc=0x%x psr=%b\n",
		       type, pc, tf->tf_npc, psr, PSR_BITS);
		if (type == T_RREGERROR)	/* 0x20 double fault */
			printf("fcr %b fvar %08x fpar %08x fpsr %08x pdbr %08x\n",
			    lda(0, ASI_FCR), FCR_BITS, lda(0, ASI_FPAR),
			    lda(0, ASI_FPSR), lda(0, ASI_PDBR));
		panic(type < N_TRAP_TYPES ? trap_type[type] : T);
		/* NOTREACHED */
	}
	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 < 0x80) {
			if (!ignore_bogus_traps)
				goto dopanic;
			printf("trap type 0x%x: pc=0x%x npc=0x%x psr=%b\n",
			       type, pc, tf->tf_npc, psr, PSR_BITS);
			trapsignal(p, SIGILL, type, ILL_ILLOPC, sv);
			break;
		}
		/* the following message is gratuitous */
		/* ... but leave it in until we find anything */
		printf("%s[%d]: unimplemented software trap 0x%x\n",
			p->p_comm, p->p_pid, type);
		trapsignal(p, SIGILL, type, ILL_ILLOPC, sv);
		break;

	case T_AST:
		want_ast = 0;
		uvmexp.softs++;
		mi_ast(p, want_resched);
		break;

	case T_ILLINST:
		if ((n = emulinstr(pc, tf)) == 0) {
			ADVANCE;
			break;
		}
		trapsignal(p, SIGILL, 0, ILL_ILLOPC, sv);
		break;

	case T_PRIVINST:
		trapsignal(p, SIGILL, 0, ILL_PRVOPC, sv);
		break;

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

		if (fs == NULL) {
			fs = malloc(sizeof *fs, M_SUBPROC, M_WAITOK);
			*fs = initfpstate;
			p->p_md.md_fpstate = fs;
		}
		/*
		 * If we have not found an FPU, we have to emulate it.
		 */
		if (!foundfpu) {
#ifdef notyet
			fpu_emulate(p, tf, fs);
			break;
#else
			trapsignal(p, SIGFPE, 0, FPE_FLTINV, sv);
			break;
#endif
		}
		/*
		 * 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.
		 */
		if (fs->fs_qsize) {
			fpu_cleanup(p, fs);
			break;
		}
		if (cpuinfo.fpproc != p) {	/* we do not have it */
			if (cpuinfo.fpproc != NULL) /* someone else had it */
				savefpstate(cpuinfo.fpproc->p_md.md_fpstate);
			loadfpstate(fs);
			cpuinfo.fpproc = p;	/* now we do have it */
			uvmexp.fpswtch++;
		}
		tf->tf_psr |= PSR_EF;
		break;
	}

	case T_WINOF:
		if (rwindow_save(p))
			sigexit(p, SIGILL);
		break;

#define read_rw(src, dst) \
	copyin((caddr_t)(src), (caddr_t)(dst), sizeof(struct rwindow))

	case T_RWRET:
		/*
		 * T_RWRET is a window load needed in order to rett.
		 * It simply needs the window to which tf->tf_out[6]
		 * (%sp) points.  There are no user or saved windows now.
		 * Copy the one from %sp into pcb->pcb_rw[0] and set
		 * nsaved to -1.  If we decide to deliver a signal on
		 * our way out, we will clear nsaved.
		 */
		if (pcb->pcb_uw || pcb->pcb_nsaved)
			panic("trap T_RWRET 1");
#ifdef DEBUG
		if (rwindow_debug)
			printf("%s[%d]: rwindow: pcb<-stack: 0x%x\n",
				p->p_comm, p->p_pid, tf->tf_out[6]);
#endif
		if (read_rw(tf->tf_out[6], &pcb->pcb_rw[0]))
			sigexit(p, SIGILL);
		if (pcb->pcb_nsaved)
			panic("trap T_RWRET 2");
		pcb->pcb_nsaved = -1;		/* mark success */
		break;

	case T_WINUF:
		/*
		 * T_WINUF is a real window underflow, from a restore
		 * instruction.  It needs to have the contents of two
		 * windows---the one belonging to the restore instruction
		 * itself, which is at its %sp, and the one belonging to
		 * the window above, which is at its %fp or %i6---both
		 * in the pcb.  The restore's window may still be in
		 * the cpu; we need to force it out to the stack.
		 */
#ifdef DEBUG
		if (rwindow_debug)
			printf("%s[%d]: rwindow: T_WINUF 0: pcb<-stack: 0x%x\n",
				p->p_comm, p->p_pid, tf->tf_out[6]);
#endif
		write_user_windows();
		if (rwindow_save(p) || read_rw(tf->tf_out[6], &pcb->pcb_rw[0]))
			sigexit(p, SIGILL);
#ifdef DEBUG
		if (rwindow_debug)
			printf("%s[%d]: rwindow: T_WINUF 1: pcb<-stack: 0x%x\n",
				p->p_comm, p->p_pid, pcb->pcb_rw[0].rw_in[6]);
#endif
		if (read_rw(pcb->pcb_rw[0].rw_in[6], &pcb->pcb_rw[1]))
			sigexit(p, SIGILL);
		if (pcb->pcb_nsaved)
			panic("trap T_WINUF");
		pcb->pcb_nsaved = -1;		/* mark success */
		break;

	case T_ALIGN:
		if ((p->p_md.md_flags & MDP_FIXALIGN) != 0 &&
		    fixalign(p, tf) == 0) {
			ADVANCE;
			break;
		}
		trapsignal(p, SIGBUS, 0, BUS_ADRALN, sv);
		break;

	case T_FPE:
		/*
		 * 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 != cpuinfo.fpproc)
			panic("fpe without being the FP user");
		savefpstate(p->p_md.md_fpstate);
		cpuinfo.fpproc = NULL;
		/* tf->tf_psr &= ~PSR_EF; */	/* share_fpu will do this */
		fpu_cleanup(p, p->p_md.md_fpstate);
		/* fpu_cleanup posts signals if needed */
#if 0		/* ??? really never??? */
		ADVANCE;
#endif
		break;

	case T_TAGOF:
		trapsignal(p, SIGEMT, 0, EMT_TAGOVF, sv);
		break;

	case T_CPDISABLED:
		uprintf("coprocessor instruction\n");	/* XXX */
		trapsignal(p, SIGILL, 0, ILL_COPROC, sv);
		break;

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

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

	case T_FLUSHWIN:
		write_user_windows();
#ifdef probably_slower_since_this_is_usually_false
		if (pcb->pcb_nsaved && rwindow_save(p))
			sigexit(p, SIGILL);
#endif
		ADVANCE;
		break;

	case T_CLEANWIN:
		uprintf("T_CLEANWIN\n");	/* XXX */
		ADVANCE;
		break;

	case T_RANGECHECK:
		uprintf("T_RANGECHECK\n");	/* XXX */
		ADVANCE;
		trapsignal(p, SIGILL, 0, ILL_ILLOPN, sv);
		break;

	case T_FIXALIGN:
#ifdef DEBUG_ALIGN
		uprintf("T_FIXALIGN\n");
#endif
		/* User wants us to fix alignment faults */
		p->p_md.md_flags |= MDP_FIXALIGN;
		ADVANCE;
		break;

	case T_INTOF:
		uprintf("T_INTOF\n");		/* XXX */
		ADVANCE;
		trapsignal(p, SIGFPE, FPE_INTOVF_TRAP, FPE_INTOVF, sv);
		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,
 * clobbering the `underflow restore' indicator if it was -1.
 *
 * If the windows cannot be saved, pcb_nsaved is restored and we return -1.
 */
int
rwindow_save(p)
	struct proc *p;
{
	struct pcb *pcb = &p->p_addr->u_pcb;
	struct rwindow *rw = &pcb->pcb_rw[0];
	int i;

	i = pcb->pcb_nsaved;
	if (i < 0) {
		pcb->pcb_nsaved = 0;
		return (0);
	}
	if (i == 0)
		return (0);
#ifdef DEBUG
	if (rwindow_debug)
		printf("%s[%d]: rwindow: pcb->stack:", p->p_comm, p->p_pid);
#endif
	do {
#ifdef DEBUG
		if (rwindow_debug)
			printf(" 0x%x", rw[1].rw_in[6]);
#endif
		if (copyout((caddr_t)rw, (caddr_t)rw[1].rw_in[6],
		    sizeof *rw))
			return (-1);
		rw++;
	} while (--i > 0);
#ifdef DEBUG
	if (rwindow_debug)
		printf("\n");
#endif
	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;
}

/*
 * Called from locore.s trap handling, for synchronous memory faults.
 *
 * This duplicates a lot of logic in trap() and perhaps should be
 * moved there; but the bus-error-register parameters are unique to
 * this routine.
 *
 * Since synchronous errors accumulate during prefetch, we can have
 * more than one `cause'.  But we do not care what the cause, here;
 * we just want to page in the page and try again.
 */
void
mem_access_fault(type, ser, v, pc, psr, tf)
	unsigned type;
	int ser;
	u_int v;
	int pc, psr;
	struct trapframe *tf;
{
	struct proc *p;
	struct vmspace *vm;
	vaddr_t va;
	int rv;
	vm_prot_t ftype;
	int onfault;
	union sigval sv;
	u_int isr;

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

	if (type == T_DATAFAULT && (ser & FCR_EXTERNAL) != 0) {
		/*
		 * For external faults, check the iCU status.
		 */

		isr = lda(ICU_ISR, ASI_PHYS_IO);

		/*
		 * Sometimes the interrupt register is empty... and I have
		 * no idea what we are supposed to do in such situations.
		 */
		if (isr == 0) {
#ifdef DEBUG
			printf("external data fault, fcr %b pc %08x fvar %08x fpar %08x fpsr %08x pdbr %08x\n",
			    ser, FCR_BITS, pc, v, lda(0, ASI_FPAR), lda(0, ASI_FPSR), lda(0, ASI_PDBR));
#ifdef DDB
			Debugger();
#endif
#endif
			ser &= ~FCR_EXTERNAL;
			if (ser == 0)
				goto out;
		} else {
			/*
			 * This is either an unrecoverable DMA or ECC error,
			 * or a bus timeout.
			 * XXX should restart the operation if retry timeout.
			 */
			panic("data fault: fcr %b isr %b pc %08x addr %08x fpar %08x fpsr %08x",
			    ser, FCR_BITS, isr, ISR_BITS,
			    pc, v, lda(0, ASI_FPAR), lda(0, ASI_FPSR));
		}
	}

	/*
	 * Figure out what to pass the VM code, and ignore the sva register
	 * value in v on text faults (text faults are always at pc).
	 * Kernel faults are somewhat different: text faults are always
	 * illegal, and data faults are extra complex.  User faults must
	 * set p->p_md.md_tf, in case we decide to deliver a signal.  Check
	 * for illegal virtual addresses early since those can induce more
	 * faults.
	 */
	if (type == T_TEXTFAULT)
		v = pc;
	ftype = ser & FCR_RO ? PROT_WRITE : PROT_READ;
	va = trunc_page(v);
	if (psr & PSR_PS) {
		if (type == T_TEXTFAULT) {
			/*
			 * If we are trying to figure on which processor mask
			 * we run, we might trigger a text fault with
		 	 * pcb_onfault set.
			 * This is normal; don't panic there.
			 */
			if (cold && p->p_addr->u_pcb.pcb_onfault != NULL)
				goto kfault;
			(void) splhigh();
			printf("text fault: pc=0x%x fcr=%b\n", pc,
			       ser, FCR_BITS);
			panic("kernel fault");
			/* NOTREACHED */
		}

		/*
		 * 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 (va >= VM_MIN_KERNEL_ADDRESS) {
			if (uvm_fault(kernel_map, va, 0, ftype) == 0)
				return;
			goto kfault;
		}
	} else
		p->p_md.md_tf = tf;

	vm = p->p_vmspace;
	rv = uvm_fault(&vm->vm_map, (vaddr_t)va, 0, ftype);

	/*
	 * If this was a stack access we keep track of the maximum
	 * accessed stack size.  Also, if vm_fault gets a protection
	 * failure it is due to accessing the stack region outside
	 * the current limit and we need to reflect that as an access
	 * error.
	 */
	if ((caddr_t)va >= vm->vm_maxsaddr) {
		if (rv == 0)
			uvm_grow(p, va);
		else if (rv == EACCES)
			rv = EFAULT;
	}
	if (rv != 0) {
		/*
		 * If doing copyin/out, return to onfault address.  Any
		 * other page fault in kernel, die; if user fault, deliver
		 * SIGSEGV.
		 */
		if (psr & PSR_PS) {
kfault:
			onfault = p->p_addr ?
			    (int)p->p_addr->u_pcb.pcb_onfault : 0;
			if (!onfault) {
				(void) splhigh();
				printf("data fault: pc=0x%x addr=0x%x fcr=%b\n",
				       pc, v, ser, FCR_BITS);
				panic("kernel fault");
				/* NOTREACHED */
			}
			tf->tf_pc = onfault;
			tf->tf_npc = onfault + 4;
			return;
		}

		sv.sival_int = v;
		trapsignal(p, SIGSEGV, (ser & FCR_RO) ? PROT_WRITE :
		    PROT_READ, SEGV_MAPERR, sv);
	}
out:
	if ((psr & PSR_PS) == 0) {
		userret(p);
		share_fpu(p, tf);
	}
}

void
ecc_fault(type, ser, v, pc, psr, tf)
	unsigned type;
	int ser;
	u_int v;
	int pc, psr;
	struct trapframe *tf;
{
	/* XXX */
	panic("ecc_fault");
}

/*
 * 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.
 */
void
syscall(code, tf, pc)
	register_t code;
	struct trapframe *tf;
	register_t pc;
{
	int i, nsys, *ap, nap;
	struct sysent *callp;
	struct proc *p;
	int error, new;
	struct args {
		register_t i[8];
	} args;
	register_t rval[2];
#ifdef DIAGNOSTIC
	extern struct pcb *cpcb;
#endif

	uvmexp.syscalls++;
	p = curproc;
#ifdef DIAGNOSTIC
	if (tf->tf_psr & PSR_PS)
		panic("syscall");
	if (cpcb != &p->p_addr->u_pcb)
		panic("syscall cpcb/ppcb");
	if (tf != (struct trapframe *)((caddr_t)cpcb + USPACE) - 1)
		panic("syscall trapframe");
#endif
	p->p_md.md_tf = tf;
	new = code & (SYSCALL_G7RFLAG | SYSCALL_G2RFLAG);
	code &= ~(SYSCALL_G7RFLAG | 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 (callp != sysent)
			break;
		code = ap[_QUAD_LOWWORD];
		ap += 2;
		nap -= 2;
		break;
	}

	if (code < 0 || code >= nsys)
		callp += p->p_p->ps_emul->e_nosys;
	else {
		callp += code;
		i = callp->sy_argsize / sizeof(register_t);
		if (i > nap) {	/* usually false */
			if (i > 8)
				panic("syscall nargs");
			if ((error = copyin((caddr_t)tf->tf_out[6] +
			    offsetof(struct frame, fr_argx),
			    &args.i[nap], (i - nap) * sizeof(register_t))))
				goto bad;
			i = nap;
		}
		if (error == 0)
			copywords(ap, args.i, i * sizeof(register_t));
	}

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

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

	switch (error) {
	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 (or %g7, deprecated) on success */
			i = tf->tf_global[new & SYSCALL_G2RFLAG ? 2 : 7];
			if (i & 3) {
				error = EINVAL;
				goto bad;
			}
		} else {
			/* old system call convention: clear C on success */
			tf->tf_psr &= ~PSR_C;	/* success */
			i = tf->tf_npc;
		}
		tf->tf_pc = i;
		tf->tf_npc = i + 4;
		break;

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

	default:
	bad:
		tf->tf_out[0] = error;
		tf->tf_psr |= PSR_C;	/* fail */
		i = tf->tf_npc;
		tf->tf_pc = i;
		tf->tf_npc = i + 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 = arg;
	struct trapframe *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_psr &= ~PSR_C;

	mi_child_return(p);
}


code:
/Makefile/1.14/Sun Dec  8 14:46:39 2013//TOPENBSD_5_8_BASE
D/compile////
D/conf////
D/dev////
D/fpu////
D/include////
D/sparc////
D/stand////

path: sys/arch/sparc/CVS/Entries


code: src/sys/arch/sparc

path: sys/arch/sparc/CVS/Repository


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

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

NOPROG=
NOMAN=
NOOBJ=
SUBDIR=	stand

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

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

obj:	_SUBDIRUSE

.include <bsd.prog.mk>

path: sys/arch/sparc/Makefile


code:
/.cvsignore/1.5/Fri Apr 26 18:57:16 2002//TOPENBSD_5_8_BASE
D

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


code: src/sys/arch/sparc/compile

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


code: src/sys/arch/sparc/conf

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