/*	$NetBSD: machdep.c,v 1.306 2022/10/26 23:38:08 riastradh Exp $ */

/*-
 * Copyright (c) 1996, 1997, 1998, 2019 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
 * NASA Ames Research Center.
 *
 * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Copyright (c) 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/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.306 2022/10/26 23:38:08 riastradh Exp $");

#include "opt_ddb.h"
#include "opt_multiprocessor.h"
#include "opt_modular.h"
#include "opt_compat_netbsd.h"
#include "opt_compat_sunos.h"

#include <sys/param.h>
#include <sys/extent.h>
#include <sys/signal.h>
#include <sys/signalvar.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <sys/device.h>
#include <sys/ras.h>
#include <sys/reboot.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/kmem.h>
#include <sys/mbuf.h>
#include <sys/mount.h>
#include <sys/msgbuf.h>
#include <sys/syscallargs.h>
#include <sys/exec.h>
#include <sys/ucontext.h>
#include <sys/cpu.h>
#include <sys/module.h>
#include <sys/ksyms.h>
#include <sys/pserialize.h>

#include <sys/exec_aout.h>

#include <ddb/db_active.h>

#include <dev/mm.h>

#include <uvm/uvm.h>

#include <sys/sysctl.h>
#ifndef	ELFSIZE
#ifdef __arch64__
#define	ELFSIZE	64
#else
#define	ELFSIZE	32
#endif
#endif
#include <sys/exec_elf.h>

#define _SPARC_BUS_DMA_PRIVATE
#include <machine/autoconf.h>
#include <sys/bus.h>
#include <sys/kprintf.h>
#include <machine/frame.h>
#include <machine/cpu.h>
#include <machine/pcb.h>
#include <machine/pmap.h>
#include <machine/openfirm.h>
#include <machine/sparc64.h>

#include <sparc64/sparc64/cache.h>

/* #include "fb.h" */
#include "ksyms.h"

int bus_space_debug = 0; /* This may be used by macros elsewhere. */
#ifdef DEBUG
#define DPRINTF(l, s)   do { if (bus_space_debug & l) printf s; } while (0)
#else
#define DPRINTF(l, s)
#endif

#if defined(COMPAT_16) || defined(COMPAT_SUNOS)
#ifdef DEBUG
/* See <sparc64/sparc64/sigdebug.h> */
int sigdebug = 0x0;
int sigpid = 0;
#endif
#endif

extern vaddr_t avail_end;
#ifdef MODULAR
vaddr_t module_start, module_end;
static struct vm_map module_map_store;
#endif

/*
 * Maximum number of DMA segments we'll allow in dmamem_load()
 * routines.  Can be overridden in config files, etc.
 */
#ifndef MAX_DMA_SEGS
#define MAX_DMA_SEGS	20
#endif

void	dumpsys(void);
void	stackdump(void);


/*
 * Machine-dependent startup code
 */
void
cpu_startup(void)
{
#ifdef DEBUG
	extern int pmapdebug;
	int opmapdebug = pmapdebug;
#endif
	char pbuf[9];

#ifdef DEBUG
	pmapdebug = 0;
#endif

	/*
	 * Good {morning,afternoon,evening,night}.
	 */
	printf("%s%s", copyright, version);
	/*identifycpu();*/
	format_bytes(pbuf, sizeof(pbuf), ctob((uint64_t)physmem));
	printf("total memory = %s\n", pbuf);

#ifdef DEBUG
	pmapdebug = opmapdebug;
#endif
	format_bytes(pbuf, sizeof(pbuf), ptoa(uvm_availmem(false)));
	printf("avail memory = %s\n", pbuf);

#if 0
	pmap_redzone();
#endif

#ifdef MODULAR
	uvm_map_setup(&module_map_store, module_start, module_end, 0);
	module_map_store.pmap = pmap_kernel();
	module_map = &module_map_store;
#endif
}

/*
 * Set up registers on exec.
 */

#ifdef __arch64__
#define STACK_OFFSET	BIAS
#undef CCFSZ
#define CCFSZ	CC64FSZ
#else
#define STACK_OFFSET	0
#endif

/* ARGSUSED */
void
setregs(struct lwp *l, struct exec_package *pack, vaddr_t stack)
{
	struct trapframe64 *tf = l->l_md.md_tf;
	struct fpstate64 *fs;
	int64_t tstate;
	int pstate = PSTATE_USER;
#ifdef __arch64__
	Elf_Ehdr *eh = pack->ep_hdr;
#endif

	/* Clear the P_32 flag. */
	l->l_proc->p_flag &= ~PK_32;

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

	/*
	 * Set the registers to 0 except for:
	 *	%o6: stack pointer, built in exec())
	 *	%tstate: (retain icc and xcc and cwp bits)
	 *	%g1: p->p_psstrp (used by crt0)
	 *	%tpc,%tnpc: entry point of program
	 */
#ifdef __arch64__
	/* Check what memory model is requested */
	switch ((eh->e_flags & EF_SPARCV9_MM)) {
	default:
		printf("Unknown memory model %d\n", 
		       (eh->e_flags & EF_SPARCV9_MM));
		/* FALLTHROUGH */
	case EF_SPARCV9_TSO:
		pstate = PSTATE_MM_TSO|PSTATE_IE;
		break;
	case EF_SPARCV9_PSO:
		pstate = PSTATE_MM_PSO|PSTATE_IE;
		break;
	case EF_SPARCV9_RMO:
		pstate = PSTATE_MM_RMO|PSTATE_IE;
		break;
	}
#endif
	tstate = ((int64_t)ASI_PRIMARY_NO_FAULT << TSTATE_ASI_SHIFT) |
	    (pstate << TSTATE_PSTATE_SHIFT) | (tf->tf_tstate & TSTATE_CWP);
	if ((fs = l->l_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.
		 */
		fpusave_lwp(l, false);
		pool_cache_put(fpstate_cache, fs);
		l->l_md.md_fpstate = NULL;
	}
	memset(tf, 0, sizeof *tf);
	tf->tf_tstate = tstate;
	tf->tf_global[1] = l->l_proc->p_psstrp;
	/* %g4 needs to point to the start of the data segment */
	tf->tf_global[4] = 0;
	tf->tf_pc = pack->ep_entry & ~3;
	tf->tf_npc = tf->tf_pc + 4;
	stack -= sizeof(struct rwindow);
	tf->tf_out[6] = stack - STACK_OFFSET;
	tf->tf_out[7] = 0UL;
#ifdef NOTDEF_DEBUG
	printf("setregs: setting tf %p sp %p pc %p\n", (long)tf, 
	       (long)tf->tf_out[6], (long)tf->tf_pc);
#ifdef DDB
	Debugger();
#endif
#endif
}

/*
 * machine dependent system variables.
 */
static int
sysctl_machdep_boot(SYSCTLFN_ARGS)
{
	struct sysctlnode node = *rnode;
	char bootpath[256];
	const char *cp = NULL;
	extern char ofbootpath[], *ofbootpartition, *ofbootfile, *ofbootflags;

	switch (node.sysctl_num) {
	case CPU_BOOTED_KERNEL:
		cp = ofbootfile;
                if (cp == NULL || cp[0] == '\0')
                        /* Unknown to firmware, return default name */
                        cp = "netbsd";
		break;
	case CPU_BOOT_ARGS:
		cp = ofbootflags;
		break;
	case CPU_BOOTED_DEVICE:
		if (ofbootpartition) {
			snprintf(bootpath, sizeof(bootpath), "%s:%s",
			    ofbootpath, ofbootpartition);
			cp = bootpath;
		} else {
			cp = ofbootpath;
		}
		break;
	}

	if (cp == NULL || cp[0] == '\0')
		return (ENOENT);

	/*XXXUNCONST*/
	node.sysctl_data = __UNCONST(cp);
	node.sysctl_size = strlen(cp) + 1;
	return (sysctl_lookup(SYSCTLFN_CALL(&node)));
}

/*
 * figure out which VIS version the CPU supports
 * this assumes all CPUs in the system are the same
 */
static int
get_vis(void)
{
	int vis = 0;

	if ( CPU_ISSUN4V ) {
		/*
		 * UA2005 and UA2007 supports VIS 1 and VIS 2.
		 * Oracle SPARC Architecture 2011 supports VIS 3.
		 *
		 * XXX Settle with VIS 2 until we can determite the
		 *     actual sun4v implementation.
		 */
		vis = 2;
	} else {
		if (GETVER_CPU_MANUF() == MANUF_FUJITSU) {
			/* as far as I can tell SPARC64-III and up have VIS 1.0 */
			if (GETVER_CPU_IMPL() >= IMPL_SPARC64_III) {
				vis = 1;
			}
			/* XXX - which, if any, SPARC64 support VIS 2.0? */
		} else { 
			/* this better be Sun */
			vis = 1;	/* all UltraSPARCs support at least VIS 1.0 */
			if (CPU_IS_USIII_UP()) {
				vis = 2;
			}
		}
	}
	return vis;
}

SYSCTL_SETUP(sysctl_machdep_setup, "sysctl machdep subtree setup")
{

	sysctl_createv(clog, 0, NULL, NULL,
		       CTLFLAG_PERMANENT,
		       CTLTYPE_NODE, "machdep", NULL,
		       NULL, 0, NULL, 0,
		       CTL_MACHDEP, CTL_EOL);

	sysctl_createv(clog, 0, NULL, NULL,
		       CTLFLAG_PERMANENT,
		       CTLTYPE_STRING, "booted_kernel", NULL,
		       sysctl_machdep_boot, 0, NULL, 0,
		       CTL_MACHDEP, CPU_BOOTED_KERNEL, CTL_EOL);
	sysctl_createv(clog, 0, NULL, NULL,
		       CTLFLAG_PERMANENT,
		       CTLTYPE_STRING, "boot_args", NULL,
		       sysctl_machdep_boot, 0, NULL, 0,
		       CTL_MACHDEP, CPU_BOOT_ARGS, CTL_EOL);
	sysctl_createv(clog, 0, NULL, NULL,
		       CTLFLAG_PERMANENT,
		       CTLTYPE_STRING, "booted_device", NULL,
		       sysctl_machdep_boot, 0, NULL, 0,
		       CTL_MACHDEP, CPU_BOOTED_DEVICE, CTL_EOL);
	sysctl_createv(clog, 0, NULL, NULL,
		       CTLFLAG_PERMANENT|CTLFLAG_IMMEDIATE,
		       CTLTYPE_INT, "cpu_arch", NULL,
		       NULL, 9, NULL, 0,
		       CTL_MACHDEP, CPU_ARCH, CTL_EOL);
	sysctl_createv(clog, 0, NULL, NULL,
	               CTLFLAG_PERMANENT|CTLFLAG_IMMEDIATE,
	               CTLTYPE_INT, "vis",
	               SYSCTL_DESCR("supported version of VIS instruction set"),
	               NULL, get_vis(), NULL, 0,
	               CTL_MACHDEP, CPU_VIS, CTL_EOL);
}

void *
getframe(struct lwp *l, int sig, int *onstack)
{
	struct proc *p = l->l_proc;
	struct trapframe64 *tf = l->l_md.md_tf;

	/*
	 * Compute new user stack addresses, subtract off
	 * one signal frame, and align.
	 */
	*onstack = (l->l_sigstk.ss_flags & (SS_DISABLE | SS_ONSTACK)) == 0
	    && (SIGACTION(p, sig).sa_flags & SA_ONSTACK) != 0;

	if (*onstack)
		return ((char *)l->l_sigstk.ss_sp + l->l_sigstk.ss_size);
	else
		return (void *)((uintptr_t)tf->tf_out[6] + STACK_OFFSET);
}

struct sigframe_siginfo {
	siginfo_t	sf_si;		/* saved siginfo */
	ucontext_t	sf_uc;		/* saved ucontext */
};

void
sendsig_siginfo(const ksiginfo_t *ksi, const sigset_t *mask)
{
	struct lwp *l = curlwp;
	struct proc *p = l->l_proc;
	struct sigacts *ps = p->p_sigacts;
	int onstack, error;
	int sig = ksi->ksi_signo;
	ucontext_t uc;
	long ucsz;
	struct sigframe_siginfo *fp = getframe(l, sig, &onstack);
	sig_t catcher = SIGACTION(p, sig).sa_handler;
	struct trapframe64 *tf = l->l_md.md_tf;
	struct rwindow *newsp;
	register_t sp;
	/* Allocate an aligned sigframe */
	fp = (void *)((u_long)(fp - 1) & ~0x0f);

	memset(&uc, 0, sizeof(uc));
	uc.uc_flags = _UC_SIGMASK |
	    ((l->l_sigstk.ss_flags & SS_ONSTACK)
		? _UC_SETSTACK : _UC_CLRSTACK);
	uc.uc_sigmask = *mask;
	uc.uc_link = l->l_ctxlink;

	sendsig_reset(l, sig);
	mutex_exit(p->p_lock);
	cpu_getmcontext(l, &uc.uc_mcontext, &uc.uc_flags);
	ucsz = (char *)&uc.__uc_pad - (char *)&uc;

	/*
	 * Now copy the stack contents out to user space.
	 * We 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.
	 * Since we're calling the handler directly, allocate a full size
	 * C stack frame.
	 */
	newsp = (struct rwindow *)((u_long)fp - CCFSZ);
	sp = (register_t)(uintptr_t)tf->tf_out[6];
	error = (copyout(&ksi->ksi_info, &fp->sf_si,
			sizeof(ksi->ksi_info)) != 0 ||
	    copyout(&uc, &fp->sf_uc, ucsz) != 0 ||
	    copyout(&sp, &newsp->rw_in[6], sizeof(sp)) != 0);
	mutex_enter(p->p_lock);

	if (error) {
		/*
		 * Process has trashed its stack; give it an illegal
		 * instruction to halt it in its tracks.
		 */
		sigexit(l, SIGILL);
		/* NOTREACHED */
	}

	tf->tf_pc = (const vaddr_t)catcher;
	tf->tf_npc = (const vaddr_t)catcher + 4;
	tf->tf_out[0] = sig;
	tf->tf_out[1] = (vaddr_t)&fp->sf_si;
	tf->tf_out[2] = (vaddr_t)&fp->sf_uc;
	tf->tf_out[6] = (vaddr_t)newsp - STACK_OFFSET;
	tf->tf_out[7] = (vaddr_t)ps->sa_sigdesc[sig].sd_tramp - 8;

	/* Remember that we're now on the signal stack. */
	if (onstack)
		l->l_sigstk.ss_flags |= SS_ONSTACK;
}

struct pcb dumppcb;

static void
maybe_dump(int howto)
{
	int s;

	/* Disable interrupts. */
	s = splhigh();

	/* Do a dump if requested. */
	if ((howto & (RB_DUMP | RB_HALT)) == RB_DUMP)
		dumpsys();

	splx(s);
}

void
cpu_reboot(int howto, char *user_boot_string)
{
	static bool syncdone = false;
	int i;
	static char str[128];
	struct lwp *l;

	l = (curlwp == NULL) ? &lwp0 : curlwp;

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

#if NFB > 0
	fb_unblank();
#endif
	boothowto = howto;

	/* If rebooting and a dump is requested, do it.
	 *
	 * XXX used to dump after vfs_shutdown() and before
	 * detaching devices / shutdown hooks / pmf_system_shutdown().
	 */
	maybe_dump(howto);

	/*
	 * If we've panic'd, don't make the situation potentially
	 * worse by syncing or unmounting the file systems.
	 */
	if ((howto & RB_NOSYNC) == 0 && panicstr == NULL) {
		if (!syncdone) {
			syncdone = true;
			/* XXX used to force unmount as well, here */
			vfs_sync_all(l);
			/*
			 * If we've been adjusting the clock, the todr
			 * will be out of synch; adjust it now.
			 *
			 * resettodr will only do this only if inittodr()
			 * has already been called.
			 *
			 * XXX used to do this after unmounting all
			 * filesystems with vfs_shutdown().
			 */
			resettodr();
		}

		while (vfs_unmountall1(l, false, false) ||
		       config_detach_all(boothowto) ||
		       vfs_unmount_forceone(l))
			;	/* do nothing */
	} else {
		if (!db_active)
			suspendsched();
	}

	pmf_system_shutdown(boothowto);

	splhigh();

haltsys:
	doshutdownhooks();

#ifdef MULTIPROCESSOR
	/* Stop all secondary cpus */
	mp_halt_cpus();
#endif

	/* If powerdown was requested, do it. */
	if ((howto & RB_POWERDOWN) == RB_POWERDOWN) {
#ifdef MULTIPROCESSOR
		printf("cpu%d: powered down\n\n", cpu_number());
#else
		printf("powered down\n\n");
#endif
		/* Let the OBP do the work. */
		OF_poweroff();
		printf("WARNING: powerdown failed!\n");
		/*
		 * RB_POWERDOWN implies RB_HALT... fall into it...
		 */
	}

	if (howto & RB_HALT) {
#ifdef MULTIPROCESSOR
		printf("cpu%d: halted\n\n", cpu_number());
#else
		printf("halted\n\n");
#endif
		OF_exit();
		panic("PROM exit failed");
	}

#ifdef MULTIPROCESSOR
	printf("cpu%d: rebooting\n\n", cpu_number());
#else
	printf("rebooting\n\n");
#endif
	if (user_boot_string && *user_boot_string) {
		i = strlen(user_boot_string);
		if (i > sizeof(str))
			OF_boot(user_boot_string);	/* XXX */
		memcpy(str, user_boot_string, i);
	} else {
		i = 1;
		str[0] = '\0';
	}
			
	if (howto & RB_SINGLE)
		str[i++] = 's';
	if (howto & RB_KDB)
		str[i++] = 'd';
	if (i > 1) {
		if (str[0] == '\0')
			str[0] = '-';
		str[i] = 0;
	} else
		str[0] = 0;
	OF_boot(str);
	panic("cpu_reboot -- failed");
	/*NOTREACHED*/
}

uint32_t dumpmag = 0x8fca0101;	/* magic number for savecore */
int	dumpsize = 0;		/* also for savecore */
long	dumplo = 0;

void
cpu_dumpconf(void)
{
	int nblks, dumpblks;

	if (dumpdev == NODEV)
		/* No usable dump device */
		return;
	nblks = bdev_size(dumpdev);

	dumpblks = ctod(physmem) + pmap_dumpsize();
	if (dumpblks > (nblks - ctod(1)))
		/*
		 * dump size is too big for the partition.
		 * Note, we safeguard a click at the front for a
		 * possible disk label.
		 */
		return;

	/* Put the dump at the end of the partition */
	dumplo = nblks - dumpblks;

	/*
	 * savecore(8) expects dumpsize to be the number of pages
	 * of actual core dumped (i.e. excluding the MMU stuff).
	 */
	dumpsize = physmem;
}

#define	BYTES_PER_DUMP	MAXPHYS		/* must be a multiple of pagesize */
static vaddr_t dumpspace;

void *
reserve_dumppages(void *p)
{

	dumpspace = (vaddr_t)p;
	return (char *)p + BYTES_PER_DUMP;
}

/*
 * Write a crash dump.
 */
void
dumpsys(void)
{
	const struct bdevsw *bdev;
	int psize;
	daddr_t blkno;
	int (*dump)(dev_t, daddr_t, void *, size_t);
	int j, error = 0;
	uint64_t todo;
	struct mem_region *mp;

	/* copy registers to dumppcb and flush windows */
	memset(&dumppcb, 0, sizeof(struct pcb));
	snapshot(&dumppcb);
	stackdump();

	if (dumpdev == NODEV)
		return;
	bdev = bdevsw_lookup(dumpdev);
	if (bdev == NULL || bdev->d_psize == NULL)
		return;

	/*
	 * For dumps during autoconfiguration,
	 * if dump device has already configured...
	 */
	if (dumpsize == 0)
		cpu_dumpconf();
	if (!dumpspace) {
		printf("\nno address space available, dump not possible\n");
		return;
	}
	if (dumplo <= 0) {
		printf("\ndump to dev %" PRId32 ",%" PRId32 " not possible ("
		    "partition too small?)\n", major(dumpdev), minor(dumpdev));
		return;
	}
	printf("\ndumping to dev %" PRId32 ",%" PRId32 " offset %ld\n",
	    major(dumpdev), minor(dumpdev), dumplo);

	psize = bdev_size(dumpdev);
	if (psize == -1) {
		printf("dump area unavailable\n");
		return;
	}
	blkno = dumplo;
	dump = bdev->d_dump;

	error = pmap_dumpmmu(dump, blkno);
	blkno += pmap_dumpsize();

	/* calculate total size of dump */
	for (todo = 0, j = 0; j < phys_installed_size; j++)
		todo += phys_installed[j].size;

	for (mp = &phys_installed[0], j = 0; j < phys_installed_size;
			j++, mp = &phys_installed[j]) {
		uint64_t i = 0, n, off;
		paddr_t maddr = mp->start;

		for (; i < mp->size; i += n) {
			n = mp->size - i;
			if (n > BYTES_PER_DUMP)
				 n = BYTES_PER_DUMP;

			/* print out how many MBs we still have to dump */
			if ((todo % (1024*1024)) == 0)
				printf_flags(TOCONS|NOTSTAMP, 
				    "\r%6" PRIu64 " M ",
				    todo / (1024*1024));
			for (off = 0; off < n; off += PAGE_SIZE)
				pmap_kenter_pa(dumpspace+off, maddr+off,
				    VM_PROT_READ, 0);
			error = (*dump)(dumpdev, blkno,
					(void *)dumpspace, (size_t)n);
			pmap_kremove(dumpspace, n);
			if (error)
				break;
			maddr += n;
			todo -= n;
			blkno += btodb(n);
		}
	}

	switch (error) {

	case ENXIO:
		printf("- device bad\n");
		break;

	case EFAULT:
		printf("- device not ready\n");
		break;

	case EINVAL:
		printf("- area improper\n");
		break;

	case EIO:
		printf("- i/o error\n");
		break;

	case 0:
		printf_flags(TOCONS|NOTSTAMP, "\r           ");
		printf("\ndump succeeded\n");
		break;

	default:
		printf("- error %d\n", error);
		break;
	}
}

void trapdump(struct trapframe64*);
/*
 * dump out a trapframe.
 */
void
trapdump(struct trapframe64* tf)
{
	printf("TRAPFRAME: tstate=%llx pc=%llx npc=%llx y=%x\n",
	       (unsigned long long)tf->tf_tstate, (unsigned long long)tf->tf_pc,
	       (unsigned long long)tf->tf_npc, (unsigned)tf->tf_y);
	printf("%%g1-7: %llx %llx %llx %llx %llx %llx %llx\n",
	       (unsigned long long)tf->tf_global[1],
	       (unsigned long long)tf->tf_global[2],
	       (unsigned long long)tf->tf_global[3], 
	       (unsigned long long)tf->tf_global[4],
	       (unsigned long long)tf->tf_global[5],
	       (unsigned long long)tf->tf_global[6], 
	       (unsigned long long)tf->tf_global[7]);
	printf("%%o0-7: %llx %llx %llx %llx\n %llx %llx %llx %llx\n",
	       (unsigned long long)tf->tf_out[0],
	       (unsigned long long)tf->tf_out[1],
	       (unsigned long long)tf->tf_out[2],
	       (unsigned long long)tf->tf_out[3], 
	       (unsigned long long)tf->tf_out[4],
	       (unsigned long long)tf->tf_out[5],
	       (unsigned long long)tf->tf_out[6],
	       (unsigned long long)tf->tf_out[7]);
}

static void
get_symbol_and_offset(const char **mod, const char **sym, vaddr_t *offset, vaddr_t pc)
{
	static char symbuf[256];
	unsigned long symaddr;
	int s, error;

#if NKSYMS || defined(DDB) || defined(MODULAR)
	s = pserialize_read_enter();
	if (ksyms_getname(mod, sym, pc,
			  KSYMS_CLOSEST|KSYMS_PROC|KSYMS_ANY) == 0) {
		error = ksyms_getval(*mod, *sym, &symaddr,
		    KSYMS_CLOSEST|KSYMS_PROC|KSYMS_ANY);
		pserialize_read_exit(s);
		if (error)
			goto failed;

		*offset = (vaddr_t)(pc - symaddr);
		return;
	}
	pserialize_read_exit(s);
#endif
 failed:
	snprintf(symbuf, sizeof symbuf, "%llx", (unsigned long long)pc);
	*mod = "netbsd";
	*sym = symbuf;
	*offset = 0;
}

/*
 * get the fp and dump the stack as best we can.  don't leave the
 * current stack page
 */
void
stackdump(void)
{
	struct frame32 *fp = (struct frame32 *)getfp(), *sfp;
	struct frame64 *fp64;
	const char *mod, *sym;
	vaddr_t offset;

	sfp = fp;
	printf("Frame pointer is at %p\n", fp);
	printf("Call traceback:\n");
	while (fp && ((u_long)fp >> PGSHIFT) == ((u_long)sfp >> PGSHIFT)) {
		if( ((long)fp) & 1 ) {
			fp64 = (struct frame64*)(((char*)fp)+BIAS);
			/* 64-bit frame */
			get_symbol_and_offset(&mod, &sym, &offset, fp64->fr_pc);
			printf(" %s:%s+%#llx(%llx, %llx, %llx, %llx, %llx, %llx) fp = %llx\n",
			       mod, sym,
			       (unsigned long long)offset,
			       (unsigned long long)fp64->fr_arg[0],
			       (unsigned long long)fp64->fr_arg[1],
			       (unsigned long long)fp64->fr_arg[2],
			       (unsigned long long)fp64->fr_arg[3],
			       (unsigned long long)fp64->fr_arg[4],
			       (unsigned long long)fp64->fr_arg[5],	
			       (unsigned long long)fp64->fr_fp);
			fp = (struct frame32 *)(u_long)fp64->fr_fp;
		} else {
			/* 32-bit frame */
			get_symbol_and_offset(&mod, &sym, &offset, fp->fr_pc);
			printf(" %s:%s+%#lx(%x, %x, %x, %x, %x, %x) fp = %x\n",
			       mod, sym,
			       (unsigned long)offset,
			       fp->fr_arg[0],
			       fp->fr_arg[1],
			       fp->fr_arg[2],
			       fp->fr_arg[3],
			       fp->fr_arg[4],
			       fp->fr_arg[5],
			       fp->fr_fp);
			fp = (struct frame32*)(u_long)fp->fr_fp;
		}
	}
}


int
cpu_exec_aout_makecmds(struct lwp *l, struct exec_package *epp)
{
	return (ENOEXEC);
}

static size_t 
_bus_dmamap_mapsize(int const nsegments)
{ 
	KASSERT(nsegments > 0);
	return sizeof(struct sparc_bus_dmamap) +
	    (sizeof(bus_dma_segment_t) * (nsegments - 1));
}

/*
 * Common function for DMA map creation.  May be called by bus-specific
 * DMA map creation functions.
 */
int
_bus_dmamap_create(bus_dma_tag_t t, bus_size_t size, int nsegments,
	bus_size_t maxsegsz, bus_size_t boundary, int flags,
	bus_dmamap_t *dmamp)
{
	struct sparc_bus_dmamap *map;
	void *mapstore;

	/*
	 * Allocate and initialize the DMA map.  The end of the map
	 * is a variable-sized array of segments, so we allocate enough
	 * room for them in one shot.
	 *
	 * Note we don't preserve the WAITOK or NOWAIT flags.  Preservation
	 * of ALLOCNOW notifies others that we've reserved these resources,
	 * and they are not to be freed.
	 *
	 * The bus_dmamap_t includes one bus_dma_segment_t, hence
	 * the (nsegments - 1).
	 */
	if ((mapstore = kmem_zalloc(_bus_dmamap_mapsize(nsegments),
	    (flags & BUS_DMA_NOWAIT) ? KM_NOSLEEP : KM_SLEEP)) == NULL)
		return (ENOMEM);

	map = (struct sparc_bus_dmamap *)mapstore;
	map->_dm_size = size;
	map->_dm_segcnt = nsegments;
	map->_dm_maxmaxsegsz = maxsegsz;
	map->_dm_boundary = boundary;
	map->_dm_flags = flags & ~(BUS_DMA_WAITOK|BUS_DMA_NOWAIT|BUS_DMA_COHERENT|
				   BUS_DMA_NOWRITE|BUS_DMA_NOCACHE);
	map->dm_maxsegsz = maxsegsz;
	map->dm_mapsize = 0;		/* no valid mappings */
	map->dm_nsegs = 0;

	*dmamp = map;
	return (0);
}

/*
 * Common function for DMA map destruction.  May be called by bus-specific
 * DMA map destruction functions.
 */
void
_bus_dmamap_destroy(bus_dma_tag_t t, bus_dmamap_t map)
{
	if (map->dm_nsegs)
		bus_dmamap_unload(t, map);
	kmem_free(map, _bus_dmamap_mapsize(map->_dm_segcnt));
}

/*
 * Common function for loading a DMA map with a linear buffer.  May
 * be called by bus-specific DMA map load functions.
 *
 * Most SPARCs have IOMMUs in the bus controllers.  In those cases
 * they only need one segment and will use virtual addresses for DVMA.
 * Those bus controllers should intercept these vectors and should
 * *NEVER* call _bus_dmamap_load() which is used only by devices that
 * bypass DVMA.
 */
int
_bus_dmamap_load(bus_dma_tag_t t, bus_dmamap_t map, void *sbuf,
	bus_size_t buflen, struct proc *p, int flags)
{
	bus_size_t sgsize;
	vaddr_t vaddr = (vaddr_t)sbuf;
	long incr;
	int i;

	/*
	 * Make sure that on error condition we return "no valid mappings".
	 */
	map->dm_nsegs = 0;
	KASSERT(map->dm_maxsegsz <= map->_dm_maxmaxsegsz);

	if (buflen > map->_dm_size)
	{ 
#ifdef DEBUG
		printf("_bus_dmamap_load(): error %lu > %lu -- map size exceeded!\n",
		    (unsigned long)buflen, (unsigned long)map->_dm_size);
#ifdef DDB
		Debugger();
#endif
#endif
		return (EINVAL);
	}		

	sgsize = round_page(buflen + ((int)vaddr & PGOFSET));

	/*
	 * We always use just one segment.
	 */
	i = 0;
	map->dm_segs[i].ds_addr = 0UL;
	map->dm_segs[i].ds_len = 0;

	incr = PAGE_SIZE - (vaddr & PGOFSET);
	while (sgsize > 0) {
		paddr_t pa;
	
		incr = uimin(sgsize, incr);

		(void) pmap_extract(pmap_kernel(), vaddr, &pa);
		if (map->dm_segs[i].ds_len == 0)
			map->dm_segs[i].ds_addr = pa;
		if (pa == (map->dm_segs[i].ds_addr + map->dm_segs[i].ds_len)
		    && ((map->dm_segs[i].ds_len + incr) <= map->dm_maxsegsz)) {
			/* Hey, waddyaknow, they're contiguous */
			map->dm_segs[i].ds_len += incr;
		} else {
			if (++i >= map->_dm_segcnt)
				return (EFBIG);
			map->dm_segs[i].ds_addr = pa;
			map->dm_segs[i].ds_len = incr;
		}
		sgsize -= incr;
		vaddr += incr;
		incr = PAGE_SIZE;
	}
	map->dm_nsegs = i + 1;
	map->dm_mapsize = buflen;
	/* Mapping is bus dependent */
	return (0);
}

/*
 * Like _bus_dmamap_load(), but for mbufs.
 */
int
_bus_dmamap_load_mbuf(bus_dma_tag_t t, bus_dmamap_t map, struct mbuf *m,
	int flags)
{
	bus_dma_segment_t segs[MAX_DMA_SEGS];
	int i;
	size_t len;

	/*
	 * Make sure that on error condition we return "no valid mappings".
	 */
	map->dm_nsegs = 0;
	KASSERT(map->dm_maxsegsz <= map->_dm_maxmaxsegsz);

	if (m->m_pkthdr.len > map->_dm_size)
		return EINVAL;

	/* Record mbuf for *_unload */
	map->_dm_type = _DM_TYPE_MBUF;
	map->_dm_source = (void *)m;

	i = 0;
	len = 0;
	while (m) {
		vaddr_t vaddr = mtod(m, vaddr_t);
		long buflen = (long)m->m_len;

		len += buflen;
		while (buflen > 0 && i < MAX_DMA_SEGS) {
			paddr_t pa;
			long incr;

			incr = PAGE_SIZE - (vaddr & PGOFSET);
			incr = uimin(buflen, incr);

			if (pmap_extract(pmap_kernel(), vaddr, &pa) == FALSE) {
#ifdef DIAGNOSTIC
				printf("_bus_dmamap_load_mbuf: pmap_extract failed %lx\n",
				       vaddr);
#endif
				map->_dm_type = 0;
				map->_dm_source = NULL;
				return EINVAL;
			}

			buflen -= incr;
			vaddr += incr;

			if (i > 0 && 
				pa == (segs[i-1].ds_addr + segs[i-1].ds_len) &&
				((segs[i-1].ds_len + incr) <= 
					map->dm_maxsegsz)) {
				/* Hey, waddyaknow, they're contiguous */
				segs[i-1].ds_len += incr;
				continue;
			}
			segs[i].ds_addr = pa;
			segs[i].ds_len = incr;
			segs[i]._ds_boundary = 0;
			segs[i]._ds_align = 0;
			segs[i]._ds_mlist = NULL;
			i++;
		}
		m = m->m_next;
		if (m && i >= MAX_DMA_SEGS) {
			/* Exceeded the size of our dmamap */
			map->_dm_type = 0;
			map->_dm_source = NULL;
			return EFBIG;
		}
	}

#ifdef DEBUG
	{
		size_t mbuflen, sglen;
		int j;
		int retval;

		mbuflen = 0;
		for (m = (struct mbuf *)map->_dm_source; m; m = m->m_next)
			mbuflen += (long)m->m_len;
		sglen = 0;
		for (j = 0; j < i; j++)
			sglen += segs[j].ds_len;
		if (sglen != mbuflen)
			panic("load_mbuf: sglen %ld != mbuflen %lx\n",
				sglen, mbuflen);
		if (sglen != len)
			panic("load_mbuf: sglen %ld != len %lx\n",
				sglen, len);
		retval = bus_dmamap_load_raw(t, map, segs, i,
			(bus_size_t)len, flags);
		if (retval == 0) {
			if (map->dm_mapsize != len)
				panic("load_mbuf: mapsize %ld != len %lx\n",
					(long)map->dm_mapsize, len);
			sglen = 0;
			for (j = 0; j < map->dm_nsegs; j++)
				sglen += map->dm_segs[j].ds_len;
			if (sglen != len)
				panic("load_mbuf: dmamap sglen %ld != len %lx\n",
					sglen, len);
		}
		return (retval);
	}
#endif
	return (bus_dmamap_load_raw(t, map, segs, i, (bus_size_t)len, flags));
}

/*
 * Like _bus_dmamap_load(), but for uios.
 */
int
_bus_dmamap_load_uio(bus_dma_tag_t t, bus_dmamap_t map, struct uio *uio,
	int flags)
{
/* 
 * XXXXXXX The problem with this routine is that it needs to 
 * lock the user address space that is being loaded, but there
 * is no real way for us to unlock it during the unload process.
 */
#if 0
	bus_dma_segment_t segs[MAX_DMA_SEGS];
	int i, j;
	size_t len;
	struct proc *p = uio->uio_lwp->l_proc;
	struct pmap *pm;

	/*
	 * Make sure that on error condition we return "no valid mappings".
	 */
	map->dm_nsegs = 0;
	KASSERT(map->dm_maxsegsz <= map->_dm_maxmaxsegsz);

	if (uio->uio_segflg == UIO_USERSPACE) {
		pm = p->p_vmspace->vm_map.pmap;
	} else
		pm = pmap_kernel();

	i = 0;
	len = 0;
	for (j = 0; j < uio->uio_iovcnt; j++) {
		struct iovec *iov = &uio->uio_iov[j];
		vaddr_t vaddr = (vaddr_t)iov->iov_base;
		bus_size_t buflen = iov->iov_len;

		/*
		 * Lock the part of the user address space involved
		 *    in the transfer.
		 */
		if (__predict_false(uvm_vslock(p->p_vmspace, vaddr, buflen,
			    (uio->uio_rw == UIO_WRITE) ?
			    VM_PROT_WRITE : VM_PROT_READ) != 0)) {
				goto after_vsunlock;
			}
		
		len += buflen;
		while (buflen > 0 && i < MAX_DMA_SEGS) {
			paddr_t pa;
			long incr;

			incr = uimin(buflen, PAGE_SIZE);
			(void) pmap_extract(pm, vaddr, &pa);
			buflen -= incr;
			vaddr += incr;
			if (segs[i].ds_len == 0)
				segs[i].ds_addr = pa;


			if (i > 0 && pa == (segs[i-1].ds_addr + segs[i-1].ds_len)
			    && ((segs[i-1].ds_len + incr) <= map->dm_maxsegsz)) {
				/* Hey, waddyaknow, they're contiguous */
				segs[i-1].ds_len += incr;
				continue;
			}
			segs[i].ds_addr = pa;
			segs[i].ds_len = incr;
			segs[i]._ds_boundary = 0;
			segs[i]._ds_align = 0;
			segs[i]._ds_mlist = NULL;
			i++;
		}
		uvm_vsunlock(p->p_vmspace, bp->b_data, todo);
 		if (buflen > 0 && i >= MAX_DMA_SEGS) 
			/* Exceeded the size of our dmamap */
			return EFBIG;
	}
	map->_dm_type = DM_TYPE_UIO;
	map->_dm_source = (void *)uio;
	return (bus_dmamap_load_raw(t, map, segs, i, 
				    (bus_size_t)len, flags));
#endif
	return 0;
}

/*
 * Like _bus_dmamap_load(), but for raw memory allocated with
 * bus_dmamem_alloc().
 */
int
_bus_dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map, bus_dma_segment_t *segs,
	int nsegs, bus_size_t size, int flags)
{

	panic("_bus_dmamap_load_raw: not implemented");
}

/*
 * Common function for unloading a DMA map.  May be called by
 * bus-specific DMA map unload functions.
 */
void
_bus_dmamap_unload(bus_dma_tag_t t, bus_dmamap_t map)
{
	int i;
	struct vm_page *pg;
	struct pglist *pglist;
	paddr_t pa;

	for (i = 0; i < map->dm_nsegs; i++) {
		if ((pglist = map->dm_segs[i]._ds_mlist) == NULL) {

			/*
			 * We were asked to load random VAs and lost the
			 * PA info so just blow the entire cache away.
			 */
			blast_dcache();
			break;
		}
		TAILQ_FOREACH(pg, pglist, pageq.queue) {
			pa = VM_PAGE_TO_PHYS(pg);

			/* 
			 * We should be flushing a subrange, but we
			 * don't know where the segments starts.
			 */
			dcache_flush_page_all(pa);
		}
	}

	/* Mark the mappings as invalid. */
	map->dm_maxsegsz = map->_dm_maxmaxsegsz;
	map->dm_mapsize = 0;
	map->dm_nsegs = 0;

}

/*
 * Common function for DMA map synchronization.  May be called
 * by bus-specific DMA map synchronization functions.
 */
void
_bus_dmamap_sync(bus_dma_tag_t t, bus_dmamap_t map, bus_addr_t offset,
	bus_size_t len, int ops)
{
	int i;
	struct vm_page *pg;
	struct pglist *pglist;

	/*
	 * We sync out our caches, but the bus must do the same.
	 *
	 * Actually a #Sync is expensive.  We should optimize.
	 */
	if ((ops & BUS_DMASYNC_PREREAD) || (ops & BUS_DMASYNC_PREWRITE)) {

		/* 
		 * Don't really need to do anything, but flush any pending
		 * writes anyway. 
		 */
		membar_Sync();
	}
	if (ops & BUS_DMASYNC_POSTREAD) {
		/* Invalidate the vcache */
		for (i = 0; i < map->dm_nsegs; i++) {
			if ((pglist = map->dm_segs[i]._ds_mlist) == NULL)
				/* Should not really happen. */
				continue;
			TAILQ_FOREACH(pg, pglist, pageq.queue) {
				paddr_t start;
				psize_t size = PAGE_SIZE;

				if (offset < PAGE_SIZE) {
					start = VM_PAGE_TO_PHYS(pg) + offset;
					size -= offset;
					if (size > len)
						size = len;
					cache_flush_phys(start, size, 0);
					len -= size;
					if (len == 0)
						goto done;
					offset = 0;
					continue;
				}
				offset -= size;
			}
		}
	}
 done:
	if (ops & BUS_DMASYNC_POSTWRITE) {
		/* Nothing to do.  Handled by the bus controller. */
	}
}

extern paddr_t   vm_first_phys, vm_num_phys;
/*
 * Common function for DMA-safe memory allocation.  May be called
 * by bus-specific DMA memory allocation functions.
 */
int
_bus_dmamem_alloc(bus_dma_tag_t t, bus_size_t size, bus_size_t alignment,
	bus_size_t boundary, bus_dma_segment_t *segs, int nsegs, int *rsegs,
	int flags)
{
	vaddr_t low, high;
	struct pglist *pglist;
	int error;

	/* Always round the size. */
	size = round_page(size);
	low = vm_first_phys;
	high = vm_first_phys + vm_num_phys - PAGE_SIZE;

	if ((pglist = kmem_alloc(sizeof(*pglist),
	    (flags & BUS_DMA_NOWAIT) ? KM_NOSLEEP : KM_SLEEP)) == NULL)
		return (ENOMEM);

	/*
	 * If the bus uses DVMA then ignore boundary and alignment.
	 */
	segs[0]._ds_boundary = boundary;
	segs[0]._ds_align = alignment;
	if (flags & BUS_DMA_DVMA) {
		boundary = 0;
		alignment = 0;
	}

	/*
	 * Allocate pages from the VM system.
	 */
	error = uvm_pglistalloc(size, low, high,
	    alignment, boundary, pglist, nsegs, (flags & BUS_DMA_NOWAIT) == 0);
	if (error) {
		kmem_free(pglist, sizeof(*pglist));
		return (error);
	}

	/*
	 * Compute the location, size, and number of segments actually
	 * returned by the VM code.
	 */
	segs[0].ds_addr = 0UL; /* UPA does not map things */
	segs[0].ds_len = size;
	*rsegs = 1;

	/*
	 * Simply keep a pointer around to the linked list, so
	 * bus_dmamap_free() can return it.
	 *
	 * NOBODY SHOULD TOUCH THE pageq.queue FIELDS WHILE THESE PAGES
	 * ARE IN OUR CUSTODY.
	 */
	segs[0]._ds_mlist = pglist;

	/* The bus driver should do the actual mapping */
	return (0);
}

/*
 * Common function for freeing DMA-safe memory.  May be called by
 * bus-specific DMA memory free functions.
 */
void
_bus_dmamem_free(bus_dma_tag_t t, bus_dma_segment_t *segs, int nsegs)
{
	struct pglist *pglist = segs[0]._ds_mlist;

	if (nsegs != 1)
		panic("bus_dmamem_free: nsegs = %d", nsegs);

	/*
	 * Return the list of pages back to the VM system.
	 */
	uvm_pglistfree(pglist);
	kmem_free(pglist, sizeof(*pglist));
}

/*
 * Common function for mapping DMA-safe memory.  May be called by
 * bus-specific DMA memory map functions.
 */
int
_bus_dmamem_map(bus_dma_tag_t t, bus_dma_segment_t *segs, int nsegs,
	size_t size, void **kvap, int flags)
{
	vaddr_t va, sva;
	int r;
	size_t oversize;
	u_long align;

	if (nsegs != 1)
		panic("_bus_dmamem_map: nsegs = %d", nsegs);

	align = PAGE_SIZE;

	size = round_page(size);

	/*
	 * Find a region of kernel virtual addresses that can accommodate
	 * our alignment requirements.
	 */
	oversize = size + align - PAGE_SIZE;
	r = uvm_map(kernel_map, &sva, oversize, NULL, UVM_UNKNOWN_OFFSET, 0,
	    UVM_MAPFLAG(UVM_PROT_NONE, UVM_PROT_NONE, UVM_INH_NONE,
	    UVM_ADV_NORMAL, 0));
	if (r != 0)
		return (ENOMEM);

	/* Compute start of aligned region */
	va = sva;
	va += ((segs[0].ds_addr & (align - 1)) + align - va) & (align - 1);

	/* Return excess virtual addresses */
	if (va != sva)
		uvm_unmap(kernel_map, sva, va);
	if (va + size != sva + oversize)
		uvm_unmap(kernel_map, va + size, sva + oversize);

	*kvap = (void *)va;
	return (0);
}

/*
 * Common function for unmapping DMA-safe memory.  May be called by
 * bus-specific DMA memory unmapping functions.
 */
void
_bus_dmamem_unmap(bus_dma_tag_t t, void *kva, size_t size)
{

#ifdef DIAGNOSTIC
	if ((u_long)kva & PGOFSET)
		panic("_bus_dmamem_unmap");
#endif

	size = round_page(size);
	uvm_unmap(kernel_map, (vaddr_t)kva, (vaddr_t)kva + size);
}

/*
 * Common function for mmap(2)'ing DMA-safe memory.  May be called by
 * bus-specific DMA mmap(2)'ing functions.
 */
paddr_t
_bus_dmamem_mmap(bus_dma_tag_t t, bus_dma_segment_t *segs, int nsegs, off_t off,
	int prot, int flags)
{
	int i;

	for (i = 0; i < nsegs; i++) {
#ifdef DIAGNOSTIC
		if (off & PGOFSET)
			panic("_bus_dmamem_mmap: offset unaligned");
		if (segs[i].ds_addr & PGOFSET)
			panic("_bus_dmamem_mmap: segment unaligned");
		if (segs[i].ds_len & PGOFSET)
			panic("_bus_dmamem_mmap: segment size not multiple"
			    " of page size");
#endif
		if (off >= segs[i].ds_len) {
			off -= segs[i].ds_len;
			continue;
		}

		return (atop(segs[i].ds_addr + off));
	}

	/* Page not found. */
	return (-1);
}


struct sparc_bus_dma_tag mainbus_dma_tag = {
	NULL,
	NULL,
	_bus_dmamap_create,
	_bus_dmamap_destroy,
	_bus_dmamap_load,
	_bus_dmamap_load_mbuf,
	_bus_dmamap_load_uio,
	_bus_dmamap_load_raw,
	_bus_dmamap_unload,
	_bus_dmamap_sync,

	_bus_dmamem_alloc,
	_bus_dmamem_free,
	_bus_dmamem_map,
	_bus_dmamem_unmap,
	_bus_dmamem_mmap
};


/*
 * Base bus space handlers.
 */
static int	sparc_bus_map(bus_space_tag_t, bus_addr_t, bus_size_t, int,
	vaddr_t, bus_space_handle_t *);
static int	sparc_bus_unmap(bus_space_tag_t, bus_space_handle_t, bus_size_t);
static int	sparc_bus_subregion(bus_space_tag_t, bus_space_handle_t, bus_size_t,
	bus_size_t, bus_space_handle_t *);
static paddr_t	sparc_bus_mmap(bus_space_tag_t, bus_addr_t, off_t, int, int);
static void	*sparc_mainbus_intr_establish(bus_space_tag_t, int, int,
	int (*)(void *), void *, void (*)(void));
static int	sparc_bus_alloc(bus_space_tag_t, bus_addr_t, bus_addr_t, bus_size_t,
	bus_size_t, bus_size_t, int, bus_addr_t *, bus_space_handle_t *);
static void	sparc_bus_free(bus_space_tag_t, bus_space_handle_t, bus_size_t);

struct extent *io_space = NULL;

int
bus_space_alloc(bus_space_tag_t t, bus_addr_t rs, bus_addr_t re, bus_size_t s,
	bus_size_t a, bus_size_t b, int f, bus_addr_t *ap,
	bus_space_handle_t *hp)
{
	_BS_CALL(t, sparc_bus_alloc)(t, rs, re, s, a, b, f, ap, hp);
}

void
bus_space_free(bus_space_tag_t t, bus_space_handle_t h, bus_size_t s)
{
	_BS_CALL(t, sparc_bus_free)(t, h, s);
}

int
bus_space_map(bus_space_tag_t t, bus_addr_t a, bus_size_t s, int f,
	bus_space_handle_t *hp)
{
	_BS_CALL(t, sparc_bus_map)(t, a, s, f, 0, hp);
}

void
bus_space_unmap(bus_space_tag_t t, bus_space_handle_t h, bus_size_t s)
{
	_BS_VOID_CALL(t, sparc_bus_unmap)(t, h, s);
}

int
bus_space_subregion(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
	bus_size_t s, bus_space_handle_t *hp)
{
	_BS_CALL(t, sparc_bus_subregion)(t, h, o, s, hp);
}

paddr_t
bus_space_mmap(bus_space_tag_t t, bus_addr_t a, off_t o, int p, int f)
{
	_BS_CALL(t, sparc_bus_mmap)(t, a, o, p, f);
}

/*
 *	void bus_space_read_multi_N(bus_space_tag_t tag,
 *	    bus_space_handle_t bsh, bus_size_t offset,
 *	    uintN_t *addr, bus_size_t count);
 *
 * Read `count' 1, 2, 4, or 8 byte quantities from bus space
 * described by tag/handle/offset and copy into buffer provided.
 */
void
bus_space_read_multi_1(bus_space_tag_t t, bus_space_handle_t h,
	bus_size_t o, uint8_t * a, bus_size_t c)
{
	while (c-- > 0)
		*a++ = bus_space_read_1(t, h, o);
}

void
bus_space_read_multi_2(bus_space_tag_t t, bus_space_handle_t h,
	bus_size_t o, uint16_t * a, bus_size_t c)
{
	while (c-- > 0)
		*a++ = bus_space_read_2(t, h, o);
}

void
bus_space_read_multi_4(bus_space_tag_t t, bus_space_handle_t h,
	bus_size_t o, uint32_t * a, bus_size_t c)
{
	while (c-- > 0)
		*a++ = bus_space_read_4(t, h, o);
}

void
bus_space_read_multi_8(bus_space_tag_t t, bus_space_handle_t h,
	bus_size_t o, uint64_t * a, bus_size_t c)
{
	while (c-- > 0)
		*a++ = bus_space_read_8(t, h, o);
}

/*
 *	void bus_space_write_multi_N(bus_space_tag_t tag,
 *	    bus_space_handle_t bsh, bus_size_t offset,
 *	    const uintN_t *addr, bus_size_t count);
 *
 * Write `count' 1, 2, 4, or 8 byte quantities from the buffer
 * provided to bus space described by tag/handle/offset.
 */
void
bus_space_write_multi_1(bus_space_tag_t t,
	bus_space_handle_t h, bus_size_t o,
	const uint8_t *a, bus_size_t c)
{
	while (c-- > 0)
		bus_space_write_1(t, h, o, *a++);
}

void
bus_space_write_multi_2(bus_space_tag_t t,
	bus_space_handle_t h, bus_size_t o,
	const uint16_t *a, bus_size_t c)
{
	while (c-- > 0)
		bus_space_write_2(t, h, o, *a++);
}

void
bus_space_write_multi_4(bus_space_tag_t t,
	bus_space_handle_t h, bus_size_t o,
	const uint32_t *a, bus_size_t c)
{
	while (c-- > 0)
		bus_space_write_4(t, h, o, *a++);
}

void
bus_space_write_multi_8(bus_space_tag_t t,
	bus_space_handle_t h, bus_size_t o,
	const uint64_t *a, bus_size_t c)
{
	while (c-- > 0)
		bus_space_write_8(t, h, o, *a++);
}

/*
 *	void bus_space_set_multi_stream_N(bus_space_tag_t tag,
 *	    bus_space_handle_t bsh, bus_size_t offset, uintN_t val,
 *	    bus_size_t count);
 *
 * Write the 1, 2, 4, or 8 byte value `val' to bus space described
 * by tag/handle/offset `count' times.
 */
void
bus_space_set_multi_stream_1(bus_space_tag_t t,
	bus_space_handle_t h, bus_size_t o, uint8_t v,
	bus_size_t c)
{
	while (c-- > 0)
		bus_space_write_stream_1(t, h, o, v);
}

void
bus_space_set_multi_stream_2(bus_space_tag_t t,
	bus_space_handle_t h, bus_size_t o, uint16_t v,
	bus_size_t c)
{
	while (c-- > 0)
		bus_space_write_stream_2(t, h, o, v);
}

void
bus_space_set_multi_stream_4(bus_space_tag_t t,
	bus_space_handle_t h, bus_size_t o, uint32_t v,
	bus_size_t c)
{
	while (c-- > 0)
		bus_space_write_stream_4(t, h, o, v);
}

void
bus_space_set_multi_stream_8(bus_space_tag_t t,
	bus_space_handle_t h, bus_size_t o, uint64_t v,
	bus_size_t c)
{
	while (c-- > 0)
		bus_space_write_stream_8(t, h, o, v);
}

/*
 *	void bus_space_copy_region_stream_N(bus_space_tag_t tag,
 *	    bus_space_handle_t bsh1, bus_size_t off1,
 *	    bus_space_handle_t bsh2, bus_size_t off2,
 *	    bus_size_t count);
 *
 * Copy `count' 1, 2, 4, or 8 byte values from bus space starting
 * at tag/bsh1/off1 to bus space starting at tag/bsh2/off2.
 */
void
bus_space_copy_region_stream_1(bus_space_tag_t t, bus_space_handle_t h1,
	bus_size_t o1, bus_space_handle_t h2, bus_size_t o2, bus_size_t c)
{
	for (; c; c--, o1++, o2++)
	    bus_space_write_stream_1(t, h1, o1, bus_space_read_stream_1(t, h2, o2));
}

void
bus_space_copy_region_stream_2(bus_space_tag_t t, bus_space_handle_t h1,
	bus_size_t o1, bus_space_handle_t h2, bus_size_t o2, bus_size_t c)
{
	for (; c; c--, o1+=2, o2+=2)
	    bus_space_write_stream_2(t, h1, o1, bus_space_read_stream_2(t, h2, o2));
}

void
bus_space_copy_region_stream_4(bus_space_tag_t t, bus_space_handle_t h1,
	bus_size_t o1, bus_space_handle_t h2, bus_size_t o2, bus_size_t c)
{
	for (; c; c--, o1+=4, o2+=4)
	    bus_space_write_stream_4(t, h1, o1, bus_space_read_stream_4(t, h2, o2));
}

void
bus_space_copy_region_stream_8(bus_space_tag_t t, bus_space_handle_t h1,
	bus_size_t o1, bus_space_handle_t h2, bus_size_t o2, bus_size_t c)
{
	for (; c; c--, o1+=8, o2+=8)
	    bus_space_write_stream_8(t, h1, o1, bus_space_read_8(t, h2, o2));
}

/*
 *	void bus_space_set_region_stream_N(bus_space_tag_t tag,
 *	    bus_space_handle_t bsh, bus_size_t off,
 *	    uintN_t *addr, bus_size_t count);
 *
 */
void
bus_space_set_region_stream_1(bus_space_tag_t t, bus_space_handle_t h,
	bus_size_t o, const uint8_t v, bus_size_t c)
{
	for (; c; c--, o++)
		bus_space_write_stream_1(t, h, o, v);
}

void
bus_space_set_region_stream_2(bus_space_tag_t t, bus_space_handle_t h,
	bus_size_t o, const uint16_t v, bus_size_t c)
{
	for (; c; c--, o+=2)
		bus_space_write_stream_2(t, h, o, v);
}

void
bus_space_set_region_stream_4(bus_space_tag_t t, bus_space_handle_t h,
	bus_size_t o, const uint32_t v, bus_size_t c)
{
	for (; c; c--, o+=4)
		bus_space_write_stream_4(t, h, o, v);
}

void
bus_space_set_region_stream_8(bus_space_tag_t t, bus_space_handle_t h,
	bus_size_t o, const uint64_t v, bus_size_t c)
{
	for (; c; c--, o+=8)
		bus_space_write_stream_8(t, h, o, v);
}


/*
 *	void bus_space_read_multi_stream_N(bus_space_tag_t tag,
 *	    bus_space_handle_t bsh, bus_size_t offset,
 *	    uintN_t *addr, bus_size_t count);
 *
 * Read `count' 1, 2, 4, or 8 byte quantities from bus space
 * described by tag/handle/offset and copy into buffer provided.
 */
void
bus_space_read_multi_stream_1(bus_space_tag_t t,
	bus_space_handle_t h, bus_size_t o,
	uint8_t *a, bus_size_t c)
{
	while (c-- > 0)
		*a++ = bus_space_read_stream_1(t, h, o);
}

void
bus_space_read_multi_stream_2(bus_space_tag_t t,
	bus_space_handle_t h, bus_size_t o,
	uint16_t *a, bus_size_t c)
{
	while (c-- > 0)
		*a++ = bus_space_read_stream_2(t, h, o);
}

void
bus_space_read_multi_stream_4(bus_space_tag_t t,
	bus_space_handle_t h, bus_size_t o,
	uint32_t *a, bus_size_t c)
{
	while (c-- > 0)
		*a++ = bus_space_read_stream_4(t, h, o);
}

void
bus_space_read_multi_stream_8(bus_space_tag_t t,
	bus_space_handle_t h, bus_size_t o,
	uint64_t *a, bus_size_t c)
{
	while (c-- > 0)
		*a++ = bus_space_read_stream_8(t, h, o);
}

/*
 *	void bus_space_read_region_stream_N(bus_space_tag_t tag,
 *	    bus_space_handle_t bsh, bus_size_t off,
 *	    uintN_t *addr, bus_size_t count);
 *
 */
void
bus_space_read_region_stream_1(bus_space_tag_t t, bus_space_handle_t h,
	bus_size_t o, uint8_t *a, bus_size_t c)
{
	for (; c; a++, c--, o++)
		*a = bus_space_read_stream_1(t, h, o);
}
void
bus_space_read_region_stream_2(bus_space_tag_t t, bus_space_handle_t h,
	bus_size_t o, uint16_t *a, bus_size_t c)
{
	for (; c; a++, c--, o+=2)
		*a = bus_space_read_stream_2(t, h, o);
 }
void
bus_space_read_region_stream_4(bus_space_tag_t t, bus_space_handle_t h,
	bus_size_t o, uint32_t *a, bus_size_t c)
{
	for (; c; a++, c--, o+=4)
		*a = bus_space_read_stream_4(t, h, o);
}
void
bus_space_read_region_stream_8(bus_space_tag_t t, bus_space_handle_t h,
	bus_size_t o, uint64_t *a, bus_size_t c)
{
	for (; c; a++, c--, o+=8)
		*a = bus_space_read_stream_8(t, h, o);
}

/*
 *	void bus_space_write_multi_stream_N(bus_space_tag_t tag,
 *	    bus_space_handle_t bsh, bus_size_t offset,
 *	    const uintN_t *addr, bus_size_t count);
 *
 * Write `count' 1, 2, 4, or 8 byte quantities from the buffer
 * provided to bus space described by tag/handle/offset.
 */
void
bus_space_write_multi_stream_1(bus_space_tag_t t,
	bus_space_handle_t h, bus_size_t o,
	const uint8_t *a, bus_size_t c)
{
	while (c-- > 0)
		bus_space_write_stream_1(t, h, o, *a++);
}

void
bus_space_write_multi_stream_2(bus_space_tag_t t,
	bus_space_handle_t h, bus_size_t o,
	const uint16_t *a, bus_size_t c)
{
	while (c-- > 0)
		bus_space_write_stream_2(t, h, o, *a++);
}

void
bus_space_write_multi_stream_4(bus_space_tag_t t,
	bus_space_handle_t h, bus_size_t o,
	const uint32_t *a, bus_size_t c)
{
	while (c-- > 0)
		bus_space_write_stream_4(t, h, o, *a++);
}

void
bus_space_write_multi_stream_8(bus_space_tag_t t,
	bus_space_handle_t h, bus_size_t o,
	const uint64_t *a, bus_size_t c)
{
	while (c-- > 0)
		bus_space_write_stream_8(t, h, o, *a++);
}

/*
 *	void bus_space_copy_region_N(bus_space_tag_t tag,
 *	    bus_space_handle_t bsh1, bus_size_t off1,
 *	    bus_space_handle_t bsh2, bus_size_t off2,
 *	    bus_size_t count);
 *
 * Copy `count' 1, 2, 4, or 8 byte values from bus space starting
 * at tag/bsh1/off1 to bus space starting at tag/bsh2/off2.
 */
void
bus_space_copy_region_1(bus_space_tag_t t, bus_space_handle_t h1, bus_size_t o1,
	bus_space_handle_t h2, bus_size_t o2, bus_size_t c)
{
	for (; c; c--, o1++, o2++)
	    bus_space_write_1(t, h1, o1, bus_space_read_1(t, h2, o2));
}

void
bus_space_copy_region_2(bus_space_tag_t t, bus_space_handle_t h1, bus_size_t o1,
	bus_space_handle_t h2, bus_size_t o2, bus_size_t c)
{
	for (; c; c--, o1+=2, o2+=2)
	    bus_space_write_2(t, h1, o1, bus_space_read_2(t, h2, o2));
}

void
bus_space_copy_region_4(bus_space_tag_t t, bus_space_handle_t h1, bus_size_t o1,
	bus_space_handle_t h2, bus_size_t o2, bus_size_t c)
{
	for (; c; c--, o1+=4, o2+=4)
	    bus_space_write_4(t, h1, o1, bus_space_read_4(t, h2, o2));
}

void
bus_space_copy_region_8(bus_space_tag_t t, bus_space_handle_t h1, bus_size_t o1,
	bus_space_handle_t h2, bus_size_t o2, bus_size_t c)
{
	for (; c; c--, o1+=8, o2+=8)
	    bus_space_write_8(t, h1, o1, bus_space_read_8(t, h2, o2));
}

/*
 *	void bus_space_set_region_N(bus_space_tag_t tag,
 *	    bus_space_handle_t bsh, bus_size_t off,
 *	    uintN_t *addr, bus_size_t count);
 *
 */
void
bus_space_set_region_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
	const uint8_t v, bus_size_t c)
{
	for (; c; c--, o++)
		bus_space_write_1(t, h, o, v);
}

void
bus_space_set_region_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
	const uint16_t v, bus_size_t c)
{
	for (; c; c--, o+=2)
		bus_space_write_2(t, h, o, v);
}

void
bus_space_set_region_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
	const uint32_t v, bus_size_t c)
{
	for (; c; c--, o+=4)
		bus_space_write_4(t, h, o, v);
}

void
bus_space_set_region_8(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
	const uint64_t v, bus_size_t c)
{
	for (; c; c--, o+=8)
		bus_space_write_8(t, h, o, v);
}


/*
 *	void bus_space_set_multi_N(bus_space_tag_t tag,
 *	    bus_space_handle_t bsh, bus_size_t offset, uintN_t val,
 *	    bus_size_t count);
 *
 * Write the 1, 2, 4, or 8 byte value `val' to bus space described
 * by tag/handle/offset `count' times.
 */
void
bus_space_set_multi_1(bus_space_tag_t t,
	bus_space_handle_t h, bus_size_t o, uint8_t v,
	bus_size_t c)
{
	while (c-- > 0)
		bus_space_write_1(t, h, o, v);
}

void
bus_space_set_multi_2(bus_space_tag_t t,
	bus_space_handle_t h, bus_size_t o, uint16_t v,
	bus_size_t c)
{
	while (c-- > 0)
		bus_space_write_2(t, h, o, v);
}

void
bus_space_set_multi_4(bus_space_tag_t t,
	bus_space_handle_t h, bus_size_t o, uint32_t v,
	bus_size_t c)
{
	while (c-- > 0)
		bus_space_write_4(t, h, o, v);
}

void
bus_space_set_multi_8(bus_space_tag_t t,
	bus_space_handle_t h, bus_size_t o, uint64_t v,
	bus_size_t c)
{
	while (c-- > 0)
		bus_space_write_8(t, h, o, v);
}

/*
 *	void bus_space_write_region_N(bus_space_tag_t tag,
 *	    bus_space_handle_t bsh, bus_size_t off,
 *	    uintN_t *addr, bus_size_t count);
 *
 */
void
bus_space_write_region_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
	const uint8_t *a, bus_size_t c)
{
	for (; c; a++, c--, o++)
		bus_space_write_1(t, h, o, *a);
}

void
bus_space_write_region_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
	const uint16_t *a, bus_size_t c)
{
	for (; c; a++, c--, o+=2)
		bus_space_write_2(t, h, o, *a);
}

void
bus_space_write_region_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
	const uint32_t *a, bus_size_t c)
{
	for (; c; a++, c--, o+=4)
		bus_space_write_4(t, h, o, *a);
}

void
bus_space_write_region_8(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
	const uint64_t *a, bus_size_t c)
{
	for (; c; a++, c--, o+=8)
		bus_space_write_8(t, h, o, *a);
}


/*
 *	void bus_space_read_region_N(bus_space_tag_t tag,
 *	    bus_space_handle_t bsh, bus_size_t off,
 *	    uintN_t *addr, bus_size_t count);
 *
 */
void
bus_space_read_region_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
	uint8_t *a, bus_size_t c)
{
	for (; c; a++, c--, o++)
		*a = bus_space_read_1(t, h, o);
}
void
bus_space_read_region_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
	uint16_t *a, bus_size_t c)
{
	for (; c; a++, c--, o+=2)
		*a = bus_space_read_2(t, h, o);
 }
void
bus_space_read_region_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
	uint32_t *a, bus_size_t c)
{
	for (; c; a++, c--, o+=4)
		*a = bus_space_read_4(t, h, o);
}
void
bus_space_read_region_8(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
	uint64_t *a, bus_size_t c)
{
	for (; c; a++, c--, o+=8)
		*a = bus_space_read_8(t, h, o);
}

/*
 *	void bus_space_write_region_stream_N(bus_space_tag_t tag,
 *	    bus_space_handle_t bsh, bus_size_t off,
 *	    uintN_t *addr, bus_size_t count);
 *
 */
void
bus_space_write_region_stream_1(bus_space_tag_t t, bus_space_handle_t h,
	bus_size_t o, const uint8_t *a, bus_size_t c)
{
	for (; c; a++, c--, o++)
		bus_space_write_stream_1(t, h, o, *a);
}

void
bus_space_write_region_stream_2(bus_space_tag_t t, bus_space_handle_t h,
	bus_size_t o, const uint16_t *a, bus_size_t c)
{
	for (; c; a++, c--, o+=2)
		bus_space_write_stream_2(t, h, o, *a);
}

void
bus_space_write_region_stream_4(bus_space_tag_t t, bus_space_handle_t h,
	bus_size_t o, const uint32_t *a, bus_size_t c)
{
	for (; c; a++, c--, o+=4)
		bus_space_write_stream_4(t, h, o, *a);
}

void
bus_space_write_region_stream_8(bus_space_tag_t t, bus_space_handle_t h,
	bus_size_t o, const uint64_t *a, bus_size_t c)
{
	for (; c; a++, c--, o+=8)
		bus_space_write_stream_8(t, h, o, *a);
}

/*
 * Allocate a new bus tag and have it inherit the methods of the
 * given parent.
 */
bus_space_tag_t
bus_space_tag_alloc(bus_space_tag_t parent, void *cookie)
{
	struct sparc_bus_space_tag *sbt;

	sbt = kmem_zalloc(sizeof(*sbt), KM_SLEEP);

	if (parent) {
		memcpy(sbt, parent, sizeof(*sbt));
		sbt->parent = parent;
		sbt->ranges = NULL;
		sbt->nranges = 0;
	}

	sbt->cookie = cookie;
	return (sbt);
}

/*
 * Generic routine to translate an address using OpenPROM `ranges'.
 */
int
bus_space_translate_address_generic(struct openprom_range *ranges, int nranges,
    bus_addr_t *bap)
{
	int i, space = BUS_ADDR_IOSPACE(*bap);

	for (i = 0; i < nranges; i++) {
		struct openprom_range *rp = &ranges[i];

		if (rp->or_child_space != space)
			continue;

		/* We've found the connection to the parent bus. */
		*bap = BUS_ADDR(rp->or_parent_space,
		    rp->or_parent_base + BUS_ADDR_PADDR(*bap));
		return (0);
	}

	return (EINVAL);
}

int
sparc_bus_map(bus_space_tag_t t, bus_addr_t addr, bus_size_t size,
	int flags, vaddr_t unused, bus_space_handle_t *hp)
{
	vaddr_t v;
	uint64_t pa;
	paddr_t	pm_flags = 0;
	vm_prot_t pm_prot = VM_PROT_READ;
	int err, map_little = 0;

	if (io_space == NULL)
		/*
		 * And set up IOSPACE extents.
		 */
		io_space = extent_create("IOSPACE",
					 (u_long)IODEV_BASE, (u_long)IODEV_END,
					 0, 0, EX_NOWAIT);


	size = round_page(size);
	if (size == 0) {
		printf("sparc_bus_map: zero size\n");
		return (EINVAL);
	}
	switch (t->type) {
	case PCI_CONFIG_BUS_SPACE:
		/* 
		 * PCI config space is special.
		 *
		 * It's really big and seldom used.  In order not to run
		 * out of IO mappings, config space will not be mapped in,
		 * rather it will be accessed through MMU bypass ASI accesses.
		 */
		if (flags & BUS_SPACE_MAP_LINEAR)
			return (-1);
		hp->_ptr = addr;
		hp->_asi = ASI_PHYS_NON_CACHED_LITTLE;
		hp->_sasi = ASI_PHYS_NON_CACHED;
		DPRINTF(BSDB_MAP, ("\n%s: config type %x flags %x "
			"addr %016llx size %016llx virt %llx\n", __func__,
			(int)t->type, (int) flags, (unsigned long long)addr,
			(unsigned long long)size,
			(unsigned long long)hp->_ptr));
		return (0);
	case PCI_IO_BUS_SPACE:
		map_little = 1;
		break;
	case PCI_MEMORY_BUS_SPACE:
		map_little = 1;
		break;
	default:
		map_little = 0;
		break;
	}

#ifdef _LP64
	if (!CPU_ISSUN4V) {
		/* If it's not LINEAR don't bother to map it.  Use phys accesses. */
		if ((flags & BUS_SPACE_MAP_LINEAR) == 0) {
			hp->_ptr = addr;
			if (map_little)
				hp->_asi = ASI_PHYS_NON_CACHED_LITTLE;
			else
				hp->_asi = ASI_PHYS_NON_CACHED;
			hp->_sasi = ASI_PHYS_NON_CACHED;
			return (0);
		}
	}
#endif

	if (!(flags & BUS_SPACE_MAP_CACHEABLE))
		pm_flags |= PMAP_NC;

	if ((flags & BUS_SPACE_MAP_PREFETCHABLE))
		pm_flags |= PMAP_WC;

	if ((err = extent_alloc(io_space, size, PAGE_SIZE,
		0, EX_NOWAIT|EX_BOUNDZERO, (u_long *)&v)))
			panic("sparc_bus_map: cannot allocate io_space: %d", err);

	/* note: preserve page offset */
	hp->_ptr = (v | ((u_long)addr & PGOFSET));
	hp->_sasi = ASI_PRIMARY;
	if (map_little)
		hp->_asi = ASI_PRIMARY_LITTLE;
	else
		hp->_asi = ASI_PRIMARY;

	pa = trunc_page(addr);
	if (!(flags&BUS_SPACE_MAP_READONLY))
		pm_prot |= VM_PROT_WRITE;

	DPRINTF(BSDB_MAP, ("\n%s: type %x flags %x addr %016llx prot %02x "
		"pm_flags %x size %016llx virt %llx paddr %016llx\n", __func__,
		(int)t->type, (int)flags, (unsigned long long)addr, pm_prot,
		(int)pm_flags, (unsigned long long)size,
		(unsigned long long)hp->_ptr, (unsigned long long)pa));

	do {
		DPRINTF(BSDB_MAP, ("%s: phys %llx virt %p hp %llx\n",
			__func__, 
			(unsigned long long)pa, (char *)v,
			(unsigned long long)hp->_ptr));
		pmap_kenter_pa(v, pa | pm_flags, pm_prot, 0);
		v += PAGE_SIZE;
		pa += PAGE_SIZE;
	} while ((size -= PAGE_SIZE) > 0);
	return (0);
}

int
sparc_bus_subregion(bus_space_tag_t tag, bus_space_handle_t handle,
	bus_size_t offset, bus_size_t size, bus_space_handle_t *nhandlep)
{
	nhandlep->_ptr = handle._ptr + offset;
	nhandlep->_asi = handle._asi;
	nhandlep->_sasi = handle._sasi;
	return (0);
}

int
sparc_bus_unmap(bus_space_tag_t t, bus_space_handle_t bh, bus_size_t size)
{
	vaddr_t va = trunc_page((vaddr_t)bh._ptr);
	vaddr_t endva = va + round_page(size);
	int error = 0;

	if (PHYS_ASI(bh._asi)) return (0);

	error = extent_free(io_space, va, size, EX_NOWAIT);
	if (error) printf("sparc_bus_unmap: extent_free returned %d\n", error);

	pmap_remove(pmap_kernel(), va, endva);
	return (0);
}

paddr_t
sparc_bus_mmap(bus_space_tag_t t, bus_addr_t paddr, off_t off, int prot,
	int flags)
{
	paddr_t pa;
	/* Devices are un-cached... although the driver should do that */
	pa = (paddr + off) | PMAP_NC;
	if (flags & BUS_SPACE_MAP_LITTLE)
		pa |= PMAP_LITTLE;
	if (flags & BUS_SPACE_MAP_PREFETCHABLE)
		pa |= PMAP_WC;	
	return pa;
}


void *
sparc_mainbus_intr_establish(bus_space_tag_t t, int pil, int level,
	int	(*handler)(void *), void *arg, void	(*fastvec)(void) /* ignored */)
{
	struct intrhand *ih;

	ih = intrhand_alloc();
	ih->ih_fun = handler;
	ih->ih_arg = arg;
	intr_establish(pil, level != IPL_VM, ih);
	return (ih);
}

int
sparc_bus_alloc(bus_space_tag_t t, bus_addr_t rs, bus_addr_t re, bus_size_t s,
	bus_size_t a, bus_size_t b, int f, bus_addr_t *ap, bus_space_handle_t *hp)
{
	return (ENOTTY);
}

void
sparc_bus_free(bus_space_tag_t t, bus_space_handle_t h, bus_size_t s)
{
	return;
}

struct sparc_bus_space_tag mainbus_space_tag = {
	NULL,				/* cookie */
	NULL,				/* parent bus tag */
	NULL,				/* ranges */
	0,				/* nranges */
	UPA_BUS_SPACE,			/* type */
	sparc_bus_alloc,
	sparc_bus_free,
	sparc_bus_map,			/* bus_space_map */
	sparc_bus_unmap,		/* bus_space_unmap */
	sparc_bus_subregion,		/* bus_space_subregion */
	sparc_bus_mmap,			/* bus_space_mmap */
	sparc_mainbus_intr_establish	/* bus_intr_establish */
};


void
cpu_getmcontext(struct lwp *l, mcontext_t *mcp, unsigned int *flags)
{
	__greg_t *gr = mcp->__gregs;
	__greg_t ras_pc;
	const struct trapframe64 *tf = l->l_md.md_tf;

	/* First ensure consistent stack state (see sendsig). */ /* XXX? */
	write_user_windows();
	if (rwindow_save(l)) {
		mutex_enter(l->l_proc->p_lock);
		sigexit(l, SIGILL);
	}

	/* For now: Erase any random indicators for optional state. */
	(void)memset(mcp, 0, sizeof (*mcp));

	/* Save general register context. */
#ifdef __arch64__
	gr[_REG_CCR] = (tf->tf_tstate & TSTATE_CCR) >> TSTATE_CCR_SHIFT;
#else
	gr[_REG_PSR] = TSTATECCR_TO_PSR(tf->tf_tstate);
#endif
	gr[_REG_PC]  = tf->tf_pc;
	gr[_REG_nPC] = tf->tf_npc;
	gr[_REG_Y]   = tf->tf_y;
	gr[_REG_G1]  = tf->tf_global[1];
	gr[_REG_G2]  = tf->tf_global[2];
	gr[_REG_G3]  = tf->tf_global[3];
	gr[_REG_G4]  = tf->tf_global[4];
	gr[_REG_G5]  = tf->tf_global[5];
	gr[_REG_G6]  = tf->tf_global[6];
	gr[_REG_G7]  = tf->tf_global[7];
	gr[_REG_O0]  = tf->tf_out[0];
	gr[_REG_O1]  = tf->tf_out[1];
	gr[_REG_O2]  = tf->tf_out[2];
	gr[_REG_O3]  = tf->tf_out[3];
	gr[_REG_O4]  = tf->tf_out[4];
	gr[_REG_O5]  = tf->tf_out[5];
	gr[_REG_O6]  = tf->tf_out[6];
	gr[_REG_O7]  = tf->tf_out[7];
#ifdef __arch64__
	gr[_REG_ASI] = (tf->tf_tstate & TSTATE_ASI) >> TSTATE_ASI_SHIFT;
#if 0 /* not yet supported */
	gr[_REG_FPRS] = ;
#endif
#endif /* __arch64__ */

	if ((ras_pc = (__greg_t)ras_lookup(l->l_proc,
	    (void *) gr[_REG_PC])) != -1) {
		gr[_REG_PC] = ras_pc;
		gr[_REG_nPC] = ras_pc + 4;
	}

	*flags |= (_UC_CPU|_UC_TLSBASE);

	mcp->__gwins = NULL;


	/* Save FP register context, if any. */
	if (l->l_md.md_fpstate != NULL) {
		struct fpstate64 *fsp;
		__fpregset_t *fpr = &mcp->__fpregs;

		/*
		 * If our FP context is currently held in the FPU, take a
		 * private snapshot - lazy FPU context switching can deal
		 * with it later when it becomes necessary.
		 * Otherwise, get it from the process's save area.
		 */
		fpusave_lwp(l, true);
		fsp = l->l_md.md_fpstate;
		memcpy(&fpr->__fpu_fr, fsp->fs_regs, sizeof (fpr->__fpu_fr));
		mcp->__fpregs.__fpu_q = NULL;	/* `Need more info.' */
		mcp->__fpregs.__fpu_fsr = fsp->fs_fsr;
		mcp->__fpregs.__fpu_qcnt = 0 /*fs.fs_qsize*/; /* See above */
		mcp->__fpregs.__fpu_q_entrysize =
		    (unsigned char) sizeof (*mcp->__fpregs.__fpu_q);
		mcp->__fpregs.__fpu_en = 1;
		*flags |= _UC_FPU;
	} else {
		mcp->__fpregs.__fpu_en = 0;
	}

	mcp->__xrs.__xrs_id = 0;	/* Solaris extension? */
}

int
cpu_mcontext_validate(struct lwp *l, const mcontext_t *mc)
{
	const __greg_t *gr = mc->__gregs;

	/*
 	 * 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 (((gr[_REG_PC] | gr[_REG_nPC]) & 3) != 0 ||
	    gr[_REG_PC] == 0 || gr[_REG_nPC] == 0)
		return EINVAL;

	return 0;
}

int
cpu_setmcontext(struct lwp *l, const mcontext_t *mcp, unsigned int flags)
{
	const __greg_t *gr = mcp->__gregs;
	struct trapframe64 *tf = l->l_md.md_tf;
	struct proc *p = l->l_proc;
	int error;

	/* First ensure consistent stack state (see sendsig). */
	write_user_windows();
	if (rwindow_save(l)) {
		mutex_enter(p->p_lock);
		sigexit(l, SIGILL);
	}

	if ((flags & _UC_CPU) != 0) {
		error = cpu_mcontext_validate(l, mcp);
		if (error)
			return error;

		/* Restore general register context. */
		/* take only tstate CCR (and ASI) fields */
#ifdef __arch64__
		tf->tf_tstate = (tf->tf_tstate & ~(TSTATE_CCR | TSTATE_ASI)) |
		    ((gr[_REG_CCR] << TSTATE_CCR_SHIFT) & TSTATE_CCR) |
		    ((gr[_REG_ASI] << TSTATE_ASI_SHIFT) & TSTATE_ASI);
#else
		tf->tf_tstate = (tf->tf_tstate & ~TSTATE_CCR) |
		    PSRCC_TO_TSTATE(gr[_REG_PSR]);
#endif
		tf->tf_pc        = (uint64_t)gr[_REG_PC];
		tf->tf_npc       = (uint64_t)gr[_REG_nPC];
		tf->tf_y         = (uint64_t)gr[_REG_Y];
		tf->tf_global[1] = (uint64_t)gr[_REG_G1];
		tf->tf_global[2] = (uint64_t)gr[_REG_G2];
		tf->tf_global[3] = (uint64_t)gr[_REG_G3];
		tf->tf_global[4] = (uint64_t)gr[_REG_G4];
		tf->tf_global[5] = (uint64_t)gr[_REG_G5];
		tf->tf_global[6] = (uint64_t)gr[_REG_G6];
		/* done in lwp_setprivate */
		/* tf->tf_global[7] = (uint64_t)gr[_REG_G7]; */
		tf->tf_out[0]    = (uint64_t)gr[_REG_O0];
		tf->tf_out[1]    = (uint64_t)gr[_REG_O1];
		tf->tf_out[2]    = (uint64_t)gr[_REG_O2];
		tf->tf_out[3]    = (uint64_t)gr[_REG_O3];
		tf->tf_out[4]    = (uint64_t)gr[_REG_O4];
		tf->tf_out[5]    = (uint64_t)gr[_REG_O5];
		tf->tf_out[6]    = (uint64_t)gr[_REG_O6];
		tf->tf_out[7]    = (uint64_t)gr[_REG_O7];
		/* %asi restored above; %fprs not yet supported. */

		/* XXX mcp->__gwins */

		if (flags & _UC_TLSBASE)
			lwp_setprivate(l, (void *)(uintptr_t)gr[_REG_G7]);
	}

	/* Restore FP register context, if any. */
	if ((flags & _UC_FPU) != 0 && mcp->__fpregs.__fpu_en != 0) {
		struct fpstate64 *fsp;
		const __fpregset_t *fpr = &mcp->__fpregs;

		/*
		 * If we're the current FPU owner, simply reload it from
		 * the supplied context.  Otherwise, store it into the
		 * process' FPU save area (which is used to restore from
		 * by lazy FPU context switching); allocate it if necessary.
		 */
		if ((fsp = l->l_md.md_fpstate) == NULL) {
			fsp = pool_cache_get(fpstate_cache, PR_WAITOK);
			l->l_md.md_fpstate = fsp;
		} else {
			/* Drop the live context on the floor. */
			fpusave_lwp(l, false);
		}
		/* Note: sizeof fpr->__fpu_fr <= sizeof fsp->fs_regs. */
		memcpy(fsp->fs_regs, &fpr->__fpu_fr, sizeof (fpr->__fpu_fr));
		fsp->fs_fsr = mcp->__fpregs.__fpu_fsr;
		fsp->fs_qsize = 0;

#if 0
		/* Need more info! */
		mcp->__fpregs.__fpu_q = NULL;	/* `Need more info.' */
		mcp->__fpregs.__fpu_qcnt = 0 /*fs.fs_qsize*/; /* See above */
#endif
	}

	/* XXX mcp->__xrs */
	/* XXX mcp->__asrs */

	mutex_enter(p->p_lock);
	if (flags & _UC_SETSTACK)
		l->l_sigstk.ss_flags |= SS_ONSTACK;
	if (flags & _UC_CLRSTACK)
		l->l_sigstk.ss_flags &= ~SS_ONSTACK;
	mutex_exit(p->p_lock);

	return 0;
}

/*
 * Preempt the current process if in interrupt from user mode,
 * or after the current trap/syscall if in system mode.
 */
void
cpu_need_resched(struct cpu_info *ci, struct lwp *l, int flags)
{

	ci->ci_want_ast = 1;

#ifdef MULTIPROCESSOR
	if ((flags & RESCHED_REMOTE) != 0) {
		/* Just interrupt the target CPU, so it can notice its AST */
		sparc64_send_ipi(ci->ci_cpuid, sparc64_ipi_nop, 0, 0);
	}
#endif
}

/*
 * Notify an LWP that it has a signal pending, process as soon as possible.
 */
void
cpu_signotify(struct lwp *l)
{
	struct cpu_info *ci = l->l_cpu;

	ci->ci_want_ast = 1;
#ifdef MULTIPROCESSOR
	if (ci != curcpu()) {
		sparc64_send_ipi(ci->ci_cpuid, sparc64_ipi_nop, 0, 0);
	}
#endif
}

bool
cpu_intr_p(void)
{
	uint64_t ncsw;
	int idepth;
	lwp_t *l;

	l = curlwp;
	do {
		ncsw = l->l_ncsw;
		__insn_barrier();
		idepth = l->l_cpu->ci_idepth;
		__insn_barrier();
	} while (__predict_false(ncsw != l->l_ncsw));

	return idepth >= 0;
}

#ifdef MODULAR
void
module_init_md(void)
{
}
#endif

int
mm_md_physacc(paddr_t pa, vm_prot_t prot)
{

	return pmap_pa_exists(pa) ? 0 : EFAULT;
}

int
mm_md_kernacc(void *ptr, vm_prot_t prot, bool *handled)
{
	/* XXX: Don't know where PROMs are on Ultras.  Think it's at f000000 */
	const vaddr_t prom_vstart = 0xf000000, prom_vend = 0xf0100000;
	const vaddr_t msgbufpv = (vaddr_t)msgbufp, v = (vaddr_t)ptr;
	const size_t msgbufsz = msgbufp->msg_bufs +
	    offsetof(struct kern_msgbuf, msg_bufc);

	*handled = (v >= msgbufpv && v < msgbufpv + msgbufsz) ||
	    (v >= prom_vstart && v < prom_vend && (prot & VM_PROT_WRITE) == 0);
	return 0;
}

int
mm_md_readwrite(dev_t dev, struct uio *uio)
{

	return ENXIO;
}

#ifdef __arch64__
void
sparc64_elf_mcmodel_check(struct exec_package *epp, const char *model,
    size_t len)
{
	/* no model specific execution for 32bit processes */
	if (epp->ep_flags & EXEC_32)
		return;

#ifdef __USE_TOPDOWN_VM
	/*
	 * we allow TOPDOWN_VM for all processes where the binary is compiled
	 * with the medany or medmid code model.
	 */
	if (strncmp(model, "medany", len) == 0 ||
	    strncmp(model, "medmid", len) == 0)
		epp->ep_flags |= EXEC_TOPDOWN_VM;
#endif
}
#endif
