root/sys/arch/sparc64/sparc64/machdep.c
/*      $NetBSD: machdep.c,v 1.309 2026/04/08 04:06:41 thorpej Exp $ */

/*-
 * Copyright (c) 1996, 1997, 1998, 2019, 2023 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.309 2026/04/08 04:06:41 thorpej 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);
                }

                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)
{
        int idepth;
        long pctr;
        lwp_t *l;

        l = curlwp;
        do {
                pctr = lwp_pctr();
                idepth = l->l_cpu->ci_idepth;
        } while (__predict_false(pctr != lwp_pctr()));

        return idepth >= 0;
}

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