root/sys/arch/sparc/sparc/trap.c
/*      $NetBSD: trap.c,v 1.201 2023/10/05 19:41:05 ad Exp $ */

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

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.201 2023/10/05 19:41:05 ad Exp $");

#include "opt_ddb.h"
#include "opt_compat_sunos.h"
#include "opt_sparc_arch.h"
#include "opt_multiprocessor.h"

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/kernel.h>
#include <sys/kmem.h>
#include <sys/resource.h>
#include <sys/signal.h>
#include <sys/wait.h>
#include <sys/syscall.h>
#include <sys/syslog.h>
#include <sys/kauth.h>

#include <uvm/uvm_extern.h>

#include <sparc/sparc/asm.h>
#include <machine/cpu.h>
#include <machine/ctlreg.h>
#include <machine/trap.h>
#include <machine/instr.h>
#include <machine/pcb.h>
#include <machine/pmap.h>
#include <machine/userret.h>
#include <machine/locore.h>

#ifdef DDB
#include <machine/db_machdep.h>
#else
#include <machine/frame.h>
#endif
#ifdef COMPAT_SUNOS
#include <compat/sunos/sunos_exec.h>
#define SUNOS_MAXSADDR_SLOP (32 * 1024)
#endif

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

#ifdef DEBUG
int     rwindow_debug = 0;
#endif

/*
 * Initial FPU state is all registers == all 1s, everything else == all 0s.
 * This makes every floating point register a signalling NaN, with sign bit
 * set, no matter how it is interpreted.  Appendix N of the Sparc V8 document
 * seems to imply that we should do this, and it does make sense.
 */
struct fpstate initfpstate = {
    .fs_reg = { 
        .fr_regs = { 
            ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0,
            ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0,
        },
        .fr_fsr = 0,
    },
    .fs_qsize = 0,
    .fs_queue = { {
        .fq_addr = NULL,
        .fq_instr = 0,
    }, },
};

/*
 * There are more than 100 trap types, but most are unused.
 *
 * Trap type 0 is taken over as an `Asynchronous System Trap'.
 * This is left-over Vax emulation crap that should be fixed.
 */
static const char T[] = "trap";
const char *trap_type[] = {
        /* non-user vectors */
        "ast",                  /* 0 */
        "text fault",           /* 1 */
        "illegal instruction",  /* 2 */
        "privileged instruction",/*3 */
        "fp disabled",          /* 4 */
        "window overflow",      /* 5 */
        "window underflow",     /* 6 */
        "alignment fault",      /* 7 */
        "fp exception",         /* 8 */
        "data fault",           /* 9 */
        "tag overflow",         /* 0a */
        "watchpoint",           /* 0b */
        T, T, T, T, T,          /* 0c..10 */
        "level 1 int",          /* 11 */
        "level 2 int",          /* 12 */
        "level 3 int",          /* 13 */
        "level 4 int",          /* 14 */
        "level 5 int",          /* 15 */
        "level 6 int",          /* 16 */
        "level 7 int",          /* 17 */
        "level 8 int",          /* 18 */
        "level 9 int",          /* 19 */
        "level 10 int",         /* 1a */
        "level 11 int",         /* 1b */
        "level 12 int",         /* 1c */
        "level 13 int",         /* 1d */
        "level 14 int",         /* 1e */
        "level 15 int",         /* 1f */
        "register access error",/* 20 */
        "instruction access error",/* 21 */
        T, T,                   /* 22..23 */
        "cp disabled",          /* 24 */
        "unimplemented flush",  /* 25 */
        T, T,                   /* 26..27 */
        "cp exception",         /* 28 */
        "data access error",    /* 29 */
        "hw zero divide",       /* 2a */
        "data store error",     /* 2b */
        "data access MMU miss", /* 2c */
        T, T, T,                /* 2d..2f */
        T, T, T, T, T, T, T, T, /* 30..37 */
        T, T, T, T,             /* 38..3b */
        "insn access MMU miss", /* 3c */
        T, T, T,                /* 3d..3f */
        T, T, T, T, T, T, T, T, /* 40..47 */
        T, T, T, T, T, T, T, T, /* 48..4f */
        T, T, T, T, T, T, T, T, /* 50..57 */
        T, T, T, T, T, T, T, T, /* 58..5f */
        T, T, T, T, T, T, T, T, /* 60..67 */
        T, T, T, T, T, T, T, T, /* 68..6f */
        T, T, T, T, T, T, T, T, /* 70..77 */
        T, T, T, T, T, T, T, T, /* 78..7f */

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

#define N_TRAP_TYPES    (sizeof trap_type / sizeof *trap_type)

void trap(unsigned, int, int, struct trapframe *);
void mem_access_fault(unsigned, int, u_int, int, int, struct trapframe *);
void mem_access_fault4m(unsigned, u_int, u_int, struct trapframe *);

int ignore_bogus_traps = 1;

/*
 * Called from locore.s trap handling, for non-MMU-related traps.
 * (MMU-related traps go through mem_access_fault, below.)
 */
void
trap(unsigned type, int psr, int pc, struct trapframe *tf)
{
        struct proc *p;
        struct lwp *l;
        struct pcb *pcb;
        int n, s;
        char bits[64];
        u_quad_t sticks;
        ksiginfo_t ksi;
        int code, sig;

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

        curcpu()->ci_data.cpu_ntrap++;
        /*
         * Generally, kernel traps cause a panic.  Any exceptions are
         * handled early here.
         */
        if (psr & PSR_PS) {
#ifdef DDB
                if (type == T_BREAKPOINT) {
                        write_all_windows();
                        if (kdb_trap(type, tf)) {
                                return;
                        }
                }
#if defined(MULTIPROCESSOR)
                if (type == T_DBPAUSE) {
                        /* XXX - deal with kgdb too */
                        write_all_windows();
                        ddb_suspend(tf);
                        ADVANCE;
                        return;
                }
#endif
#endif
#ifdef DIAGNOSTIC
                /*
                 * Currently, we allow DIAGNOSTIC kernel code to
                 * flush the windows to record stack traces.
                 */
                if (type == T_FLUSHWIN) {
                        write_all_windows();
                        ADVANCE;
                        return;
                }
#endif
                if (type == T_UNIMPLFLUSH) {
                        /*
                         * This should happen only on hypersparc.
                         * It also is a rare event to get this trap
                         * from kernel space. For now, just flush the
                         * entire I-cache.
                         */
#if defined(MULTIPROCESSOR)
                        /* Broadcast to all CPUs */
                        XCALL0(*cpuinfo.pure_vcache_flush, CPUSET_ALL);
#else
                        (*cpuinfo.pure_vcache_flush)();
#endif
                        ADVANCE;
                        return;
                }

                /*
                 * Storing %fsr in cpu_attach will cause this trap
                 * even though the fpu has been enabled, if and only
                 * if there is no FPU.
                 */
                if (type == T_FPDISABLED && cold) {
                        ADVANCE;
                        return;
                }
        dopanic:
                snprintb(bits, sizeof(bits), PSR_BITS, psr);
                printf("trap type 0x%x: pc=0x%x npc=0x%x psr=%s\n",
                       type, pc, tf->tf_npc, bits);
#ifdef DDB
                write_all_windows();
                (void) kdb_trap(type, tf);
#endif
                panic("%s", type < N_TRAP_TYPES ? trap_type[type] : T);
                /* NOTREACHED */
        }
        if ((l = curlwp) == NULL)
                l = &lwp0;
        p = l->l_proc;
        sticks = p->p_sticks;
        pcb = lwp_getpcb(l);
        l->l_md.md_tf = tf;     /* for ptrace/signals */

#ifdef FPU_DEBUG
        if (type != T_FPDISABLED && (tf->tf_psr & PSR_EF) != 0) {
                if (cpuinfo.fplwp != l)
                        panic("FPU enabled but wrong proc (0) [l=%p, fwlp=%p]",
                                l, cpuinfo.fplwp);
                savefpstate(l->l_md.md_fpstate);
                l->l_md.md_fpu = NULL;
                cpuinfo.fplwp = NULL;
                tf->tf_psr &= ~PSR_EF;
                setpsr(getpsr() & ~PSR_EF);
        }
#endif

        sig = 0;

        switch (type) {

        default:
                if (type < 0x80) {
                        if (!ignore_bogus_traps)
                                goto dopanic;
                        snprintb(bits, sizeof(bits), PSR_BITS, psr);
                        printf("trap type 0x%x: pc=0x%x npc=0x%x psr=%s\n",
                               type, pc, tf->tf_npc, bits);
                        sig = SIGILL;
                        KSI_INIT_TRAP(&ksi);
                        ksi.ksi_trap = type;
                        ksi.ksi_code = ILL_ILLTRP;
                        ksi.ksi_addr = (void *)pc;
                        break;
                }

#ifdef DIAGNOSTIC
                if (type < 0x90 || type > 0x9f) {
                        /* the following message is gratuitous */
                        /* ... but leave it in until we find anything */
                        uprintf("%s[%d]: unimplemented software trap 0x%x\n",
                                p->p_comm, p->p_pid, type);
                }
#endif
                sig = SIGILL;
                KSI_INIT_TRAP(&ksi);
                ksi.ksi_trap = type;
                ksi.ksi_code = ILL_ILLTRP;
                ksi.ksi_addr = (void *)pc;
                break;

        case T_AST:
                break;  /* the work is all in userret() */

        case T_UNIMPLFLUSH:
                /* Invalidate the entire I-cache */
#if defined(MULTIPROCESSOR)
                /* Broadcast to all CPUs */
                XCALL0(*cpuinfo.pure_vcache_flush, CPUSET_ALL);
#else
                (*cpuinfo.pure_vcache_flush)();
#endif
                ADVANCE;
                break;

        case T_ILLINST:
                /* Note: Cypress generates a T_ILLINST on FLUSH instructions */
                if ((sig = emulinstr(pc, tf)) == 0) {
                        ADVANCE;
                        break;
                }
                KSI_INIT_TRAP(&ksi);
                ksi.ksi_trap = type;
                ksi.ksi_code = ILL_ILLOPC;
                ksi.ksi_addr = (void *)pc;
                break;

        case T_PRIVINST:
                sig = SIGILL;
                KSI_INIT_TRAP(&ksi);
                ksi.ksi_trap = type;
                ksi.ksi_code = ILL_PRVOPC;
                ksi.ksi_addr = (void *)pc;
                break;

        case T_FPDISABLED: {
                struct fpstate *fs = l->l_md.md_fpstate;

#ifdef FPU_DEBUG
                if ((tf->tf_psr & PSR_PS) != 0) {
                        printf("FPU fault from kernel mode, pc=%x\n", pc);
#ifdef DDB
                        Debugger();
#endif
                }
#endif

                if (fs == NULL) {
                        fs = kmem_alloc(sizeof(struct fpstate), KM_SLEEP);
                        *fs = initfpstate;
                        l->l_md.md_fpstate = fs;
                }
                /*
                 * If we have not found an FPU, we have to emulate it.
                 */
                if (!cpuinfo.fpupresent) {
#ifdef notyet
                        fpu_emulate(l, tf, fs);
#else
                        sig = SIGFPE;
                        KSI_INIT_TRAP(&ksi);
                        ksi.ksi_trap = type;
                        ksi.ksi_code = SI_NOINFO;
                        ksi.ksi_addr = (void *)pc;
#endif
                        break;
                }
                /*
                 * We may have more FPEs stored up and/or ops queued.
                 * If they exist, handle them and get out.  Otherwise,
                 * resolve the FPU state, turn it on, and try again.
                 */
                if (fs->fs_qsize) {
                        if ((code = fpu_cleanup(l, fs)) != 0) {
                                sig = SIGFPE;
                                KSI_INIT_TRAP(&ksi);
                                ksi.ksi_trap = type;
                                ksi.ksi_code = code;
                                ksi.ksi_addr = (void *)pc;
                        }
                        break;
                }

                /*
                 * If we do not own the FPU state on this CPU, we must
                 * now acquire it.
                 */
                if (cpuinfo.fplwp != l) {
                        struct cpu_info *cpi;

                        FPU_LOCK(s);
                        if (cpuinfo.fplwp != NULL) {
                                /* someone else had it*/
                                savefpstate(cpuinfo.fplwp->l_md.md_fpstate);
                                cpuinfo.fplwp->l_md.md_fpu = NULL;
                        }

                        /*
                         * On MP machines, some of the other FPUs might
                         * still have our state. Tell the owning processor
                         * to save the process' FPU state.
                         */
                        if ((cpi = l->l_md.md_fpu) != NULL) {
                                if (cpi->ci_cpuid == cpuinfo.ci_cpuid)
                                        panic("FPU(%d): state for %p",
                                                        cpi->ci_cpuid, l);
#if defined(MULTIPROCESSOR)
                                XCALL1(ipi_savefpstate, fs, 1 << cpi->ci_cpuid);
#endif
                                cpi->fplwp = NULL;
                        }
                        loadfpstate(fs);

                        /* now we do have it */
                        cpuinfo.fplwp = l;
                        l->l_md.md_fpu = curcpu();
                        FPU_UNLOCK(s);
                }

                tf->tf_psr |= PSR_EF;
                break;
        }

        case T_WINOF:
                if (rwindow_save(l)) {
                        mutex_enter(p->p_lock);
                        sigexit(l, SIGILL);
                }
                break;

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

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

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

        case T_ALIGN:
                if ((p->p_md.md_flags & MDP_FIXALIGN) != 0) {
                        n = fixalign(l, tf, NULL);
                        if (n == 0) {
                                ADVANCE;
                                break;
                        }
                }
                sig = SIGBUS;
                KSI_INIT_TRAP(&ksi);
                ksi.ksi_trap = type;
                ksi.ksi_code = BUS_ADRALN;
                fixalign(l, tf, &ksi.ksi_addr);
                break;

        case T_FPE:
                /*
                 * Clean up after a floating point exception.
                 * fpu_cleanup can (and usually does) modify the
                 * state we save here, so we must `give up' the FPU
                 * chip context.  (The software and hardware states
                 * will not match once fpu_cleanup does its job, so
                 * we must not save again later.)
                 */
                if (l != cpuinfo.fplwp)
                        panic("fpe without being the FP user");
                FPU_LOCK(s);
                savefpstate(l->l_md.md_fpstate);
                cpuinfo.fplwp = NULL;
                l->l_md.md_fpu = NULL;
                FPU_UNLOCK(s);
                /* tf->tf_psr &= ~PSR_EF; */    /* share_fpu will do this */
                if ((code = fpu_cleanup(l, l->l_md.md_fpstate)) != 0) {
                        sig = SIGFPE;
                        KSI_INIT_TRAP(&ksi);
                        ksi.ksi_trap = type;
                        ksi.ksi_code = code;
                        ksi.ksi_addr = (void *)pc;
                }
#if 0           /* ??? really never??? */
                ADVANCE;
#endif
                break;

        case T_TAGOF:
                sig = SIGEMT;
                KSI_INIT_TRAP(&ksi);
                ksi.ksi_trap = type;
                ksi.ksi_code = SI_NOINFO;
                ksi.ksi_addr = (void *)pc;
                break;

        case T_CPDISABLED:
                sig = SIGILL;
                KSI_INIT_TRAP(&ksi);
                ksi.ksi_trap = type;
                ksi.ksi_code = ILL_COPROC;
                ksi.ksi_addr = (void *)pc;
                break;

        case T_BREAKPOINT:
                sig = SIGTRAP;
                KSI_INIT_TRAP(&ksi);
                ksi.ksi_trap = type;
                ksi.ksi_code = TRAP_BRKPT;
                ksi.ksi_addr = (void *)pc;
                break;

        case T_DIV0:
        case T_IDIV0:
                ADVANCE;
                sig = SIGFPE;
                KSI_INIT_TRAP(&ksi);
                ksi.ksi_trap = type;
                ksi.ksi_code = FPE_INTDIV;
                ksi.ksi_addr = (void *)pc;
                break;

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

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

        case T_RANGECHECK:
                uprintf("T_RANGECHECK\n");      /* XXX */
                ADVANCE;
                sig = SIGILL;
                KSI_INIT_TRAP(&ksi);
                ksi.ksi_trap = type;
                ksi.ksi_code = ILL_ILLOPN;
                ksi.ksi_addr = (void *)pc;
                break;

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

        case T_INTOF:
                uprintf("T_INTOF\n");           /* XXX */
                ADVANCE;
                sig = SIGFPE;
                KSI_INIT_TRAP(&ksi);
                ksi.ksi_trap = type;
                ksi.ksi_code = FPE_INTOVF;
                ksi.ksi_addr = (void *)pc;
                break;
        }
        if (sig != 0) {
                ksi.ksi_signo = sig;
                trapsignal(l, &ksi);
        }
        userret(l, pc, sticks);
        share_fpu(l, tf);
#undef ADVANCE
}

/*
 * Save windows from PCB into user stack, and return 0.  This is used on
 * window overflow pseudo-traps (from locore.s, just before returning to
 * user mode) and when ptrace or sendsig needs a consistent state.
 * As a side effect, rwindow_save() always sets pcb_nsaved to 0,
 * clobbering the `underflow restore' indicator if it was -1.
 *
 * If the windows cannot be saved, pcb_nsaved is restored and we return -1.
 */
int
rwindow_save(struct lwp *l)
{
        struct pcb *pcb = lwp_getpcb(l);
        struct rwindow *rw = &pcb->pcb_rw[0];
        int i;

        i = pcb->pcb_nsaved;
        if (i < 0) {
                pcb->pcb_nsaved = 0;
                return (0);
        }
        if (i == 0)
                return (0);
#ifdef DEBUG
        if (rwindow_debug)
                printf("cpu%d:%s[%d]: rwindow: pcb->stack:",
                        cpuinfo.ci_cpuid, l->l_proc->p_comm, l->l_proc->p_pid);
#endif
        do {
#ifdef DEBUG
                if (rwindow_debug)
                        printf(" [%d]0x%x", cpuinfo.ci_cpuid, rw[1].rw_in[6]);
#endif
                if (copyout((void *)rw, (void *)rw[1].rw_in[6],
                    sizeof *rw))
                        return (-1);
                rw++;
        } while (--i > 0);
#ifdef DEBUG
        if (rwindow_debug)
                printf("\n");
#endif
        pcb->pcb_nsaved = 0;
        return (0);
}

/*
 * Kill user windows (before exec) by writing back to stack or pcb
 * and then erasing any pcb tracks.  Otherwise we might try to write
 * the registers into the new process after the exec.
 */
void
cpu_vmspace_exec(struct lwp *l, vaddr_t vstart, vaddr_t vend)
{
        struct pcb *pcb = lwp_getpcb(l);

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

/*
 * Called from locore.s trap handling, for synchronous memory faults.
 *
 * This duplicates a lot of logic in trap() and perhaps should be
 * moved there; but the bus-error-register parameters are unique to
 * this routine.
 *
 * Since synchronous errors accumulate during prefetch, we can have
 * more than one `cause'.  But we do not care what the cause, here;
 * we just want to page in the page and try again.
 */
void
mem_access_fault(unsigned type, int ser, u_int v, int pc, int psr,
                 struct trapframe *tf)
{
#if defined(SUN4) || defined(SUN4C)
        struct proc *p;
        struct lwp *l;
        struct pcb *pcb;
        struct vmspace *vm;
        vaddr_t va;
        int rv;
        vm_prot_t atype;
        vaddr_t onfault;
        u_quad_t sticks;
        char bits[64];
        ksiginfo_t ksi;

        curcpu()->ci_data.cpu_ntrap++;
        l = curlwp;
        p = l->l_proc;
        pcb = lwp_getpcb(l);
        onfault = (vaddr_t)pcb->pcb_onfault;

        sticks = p->p_sticks;

#ifdef FPU_DEBUG
        if ((tf->tf_psr & PSR_EF) != 0) {
                if (cpuinfo.fplwp != l)
                        panic("FPU enabled but wrong proc (1) [l=%p, fwlp=%p]",
                                l, cpuinfo.fplwp);
                savefpstate(l->l_md.md_fpstate);
                l->l_md.md_fpu = NULL;
                cpuinfo.fplwp = NULL;
                tf->tf_psr &= ~PSR_EF;
                setpsr(getpsr() & ~PSR_EF);
        }
#endif

        /*
         * Figure out what to pass the VM code, and ignore the sva register
         * value in v on text faults (text faults are always at pc).
         * Kernel faults are somewhat different: text faults are always
         * illegal, and data faults are extra complex.  User faults must
         * set p->p_md.md_tf, in case we decide to deliver a signal.  Check
         * for illegal virtual addresses early since those can induce more
         * faults.
         */
        if (type == T_TEXTFAULT)
                v = pc;
        if (VA_INHOLE(v)) {
                rv = EACCES;
                goto fault;
        }
        atype = ser & SER_WRITE ? VM_PROT_WRITE : VM_PROT_READ;
        if ((ser & SER_PROT) && atype == VM_PROT_READ && type != T_TEXTFAULT) {

                /*
                 * The hardware reports faults by the atomic load/store
                 * instructions as read faults, so if the faulting instruction
                 * is one of those, relabel this fault as both read and write.
                 */
                u_int insn;
                if (ufetch_int((void *)pc, &insn) == 0 &&
                    (insn & 0xc1680000) == 0xc0680000) {
                        atype = VM_PROT_READ | VM_PROT_WRITE;
                }
        }
        va = trunc_page(v);
        if (psr & PSR_PS) {
                if (type == T_TEXTFAULT) {
                        (void) splhigh();
                        snprintb(bits, sizeof(bits), SER_BITS, ser);
                        printf("cpu%d: text fault: pc=0x%x ser=%s\n",
                               cpu_number(), pc, bits);
                        panic("kernel fault");
                        /* NOTREACHED */
                }
                /*
                 * If this was an access that we shouldn't try to page in,
                 * resume at the fault handler without any action.
                 */
                if (onfault == (vaddr_t)sparc_fsbail) {
                        rv = EFAULT;
                        goto kfault;
                }

                /*
                 * During autoconfiguration, faults are never OK unless
                 * pcb_onfault is set.  Once running normally we must allow
                 * exec() to cause copy-on-write faults to kernel addresses.
                 */
                if (cold) {
                        rv = EFAULT;
                        goto kfault;
                }
                if (va >= KERNBASE) {
                        rv = mmu_pagein(pmap_kernel(), va, atype);
                        if (rv < 0) {
                                rv = EACCES;
                                goto kfault;
                        }
                        if (rv > 0)
                                return;
                        pcb->pcb_onfault = NULL;
                        rv = uvm_fault(kernel_map, va, atype);
                        pcb->pcb_onfault = (void *)onfault;
                        if (rv == 0)
                                return;
                        goto kfault;
                }
        } else {
                l->l_md.md_tf = tf;
        }

        /*
         * mmu_pagein returns -1 if the page is already valid, in which
         * case we have a hard fault; it returns 1 if it loads a segment
         * that got bumped out via LRU replacement.
         */
        vm = p->p_vmspace;
        rv = mmu_pagein(vm->vm_map.pmap, va, atype);
        if (rv < 0) {
                rv = EACCES;
                goto fault;
        }
        if (rv > 0)
                goto out;

        /* alas! must call the horrible vm code */
        pcb->pcb_onfault = NULL;
        rv = uvm_fault(&vm->vm_map, (vaddr_t)va, atype);
        pcb->pcb_onfault = (void *)onfault;

        /*
         * If this was a stack access we keep track of the maximum
         * accessed stack size.  Also, if vm_fault gets a protection
         * failure it is due to accessing the stack region outside
         * the current limit and we need to reflect that as an access
         * error.
         */
        if ((void *)va >= vm->vm_maxsaddr
#ifdef COMPAT_SUNOS
            && !(p->p_emul == &emul_sunos && va < USRSTACK -
                 (vaddr_t)p->p_limit->pl_rlimit[RLIMIT_STACK].rlim_cur +
                 SUNOS_MAXSADDR_SLOP)
#endif
            && rv == 0)
                uvm_grow(p, va);

        if (rv == 0) {
                /*
                 * pmap_enter() does not enter all requests made from
                 * vm_fault into the MMU (as that causes unnecessary
                 * entries for `wired' pages).  Instead, we call
                 * mmu_pagein here to make sure the new PTE gets installed.
                 */
                (void) mmu_pagein(vm->vm_map.pmap, va, VM_PROT_NONE);
        } else {
                /*
                 * Pagein failed.  If doing copyin/out, return to onfault
                 * address.  Any other page fault in kernel, die; if user
                 * fault, deliver SIGSEGV.
                 */
fault:
                if (psr & PSR_PS) {
kfault:
                        if (!onfault) {
                                (void) splhigh();
                                snprintb(bits, sizeof(bits), SER_BITS, ser);
                                printf("cpu%d: data fault: pc=0x%x "
                                       "addr=0x%x ser=%s\n",
                                       cpu_number(), pc, v, bits);
                                panic("kernel fault");
                                /* NOTREACHED */
                        }
                        tf->tf_pc = onfault;
                        tf->tf_npc = onfault + 4;
                        tf->tf_out[0] = (rv == EACCES) ? EFAULT : rv;
                        return;
                }
                KSI_INIT_TRAP(&ksi);
                switch (rv) {
                case ENOMEM:
                        printf("UVM: pid %d (%s), uid %d killed: out of swap\n",
                               p->p_pid, p->p_comm,
                               l->l_cred ?
                               kauth_cred_geteuid(l->l_cred) : -1);
                        ksi.ksi_signo = SIGKILL;
                        break;
                case EINVAL:
                        ksi.ksi_signo = SIGBUS;
                        ksi.ksi_code = BUS_ADRERR;
                        break;
                case EACCES:
                        ksi.ksi_signo = SIGSEGV;
                        ksi.ksi_code = SEGV_ACCERR;
                        break;
                default:
                        ksi.ksi_signo = SIGSEGV;
                        ksi.ksi_code = SEGV_MAPERR;
                        break;
                }
                ksi.ksi_trap = type;
                ksi.ksi_addr = (void *)v;
                trapsignal(l, &ksi);
        }
out:
        if ((psr & PSR_PS) == 0) {
                userret(l, pc, sticks);
                share_fpu(l, tf);
        }
#endif /* SUN4 || SUN4C */
}

#if defined(SUN4M)      /* 4m version of mem_access_fault() follows */
static int tfaultaddr = (int) 0xdeadbeef;

void
mem_access_fault4m(unsigned type, u_int sfsr, u_int sfva, struct trapframe *tf)
{
        int pc, psr;
        struct proc *p;
        struct lwp *l;
        struct pcb *pcb;
        struct vmspace *vm;
        vaddr_t va;
        int rv;
        vm_prot_t atype;
        int onfault;
        u_quad_t sticks;
        char bits[64];
        ksiginfo_t ksi;

        curcpu()->ci_data.cpu_ntrap++;

        l = curlwp;
        p = l->l_proc;
        sticks = p->p_sticks;
        pcb = lwp_getpcb(l);
        onfault = (vaddr_t)pcb->pcb_onfault;

#ifdef FPU_DEBUG
        if ((tf->tf_psr & PSR_EF) != 0) {
                if (cpuinfo.fplwp != l)
                        panic("FPU enabled but wrong proc (2) [l=%p, fwlp=%p]",
                                l, cpuinfo.fplwp);
                savefpstate(l->l_md.md_fpstate);
                l->l_md.md_fpu = NULL;
                cpuinfo.fplwp = NULL;
                tf->tf_psr &= ~PSR_EF;
                setpsr(getpsr() & ~PSR_EF);
        }
#endif

        pc = tf->tf_pc;                 /* These are needed below */
        psr = tf->tf_psr;

#if /*DIAGNOSTICS*/1
        if (type == T_DATAERROR || type == T_TEXTERROR)
                printf("%s[%d]: trap 0x%x: pc=0x%x sfsr=0x%x sfva=0x%x\n",
                        p->p_comm, p->p_pid, type, pc, sfsr, sfva);
#endif

        /*
         * Our first priority is handling serious faults, such as
         * parity errors or async faults that might have come through here.
         * If afsr & AFSR_AFO != 0, then we're on a HyperSPARC and we
         * got an async fault. We pass it on to memerr4m. Similarly, if
         * the trap was T_STOREBUFFAULT, we pass it on to memerr4m.
         * If we have a data fault, but SFSR_FAV is not set in the sfsr,
         * then things are really bizarre, and we treat it as a hard
         * error and pass it on to memerr4m. See section 8.12.4 in the
         * SuperSPARC user's guide for more info, and for a possible
         * solution which we don't implement here.
         * Note: store buffer faults may also lead to a level 15 interrupt
         * being posted to the module (see sun4m system architecture,
         * section B.I.9).
         */
        if (type == T_STOREBUFFAULT ||
            (type == T_DATAFAULT && (sfsr & SFSR_FAV) == 0)) {
                (*cpuinfo.memerr)(type, sfsr, sfva, tf);
                /*
                 * If we get here, exit the trap handler and wait for the
                 * trap to re-occur.
                 */
                goto out_nounlock;
        }

        /*
         * Figure out what to pass the VM code. We cannot ignore the sfva
         * register on text faults, since this might be a trap on an
         * alternate-ASI access to code space. However, if we're on a
         * supersparc, we can't help using PC, since we don't get a VA in
         * sfva.
         * Kernel faults are somewhat different: text faults are always
         * illegal, and data faults are extra complex.  User faults must
         * set p->p_md.md_tf, in case we decide to deliver a signal.  Check
         * for illegal virtual addresses early since those can induce more
         * faults.
         * All translation faults are illegal, and result in a SIGSEGV
         * being delivered to the running process (or a kernel panic, for
         * a kernel fault). We check the translation first to make sure
         * it is not spurious.
         * Also, note that in the case where we have an overwritten
         * text fault (OW==1, AT==2,3), we attempt to service the
         * second (overwriting) fault, then restart the instruction
         * (which is from the first fault) and allow the first trap
         * to reappear. XXX is this right? It will probably change...
         */
        if ((sfsr & SFSR_FT) == SFSR_FT_NONE)
                goto out;       /* No fault. Why were we called? */

        /*
         * NOTE: the per-CPU fault status register readers (in locore)
         * may already have decided to pass `pc' in `sfva', so we avoid
         * testing CPU types here.
         * Q: test SFSR_FAV in the locore stubs too?
         */
        if ((sfsr & SFSR_FAV) == 0) {
                /* note: T_TEXTERROR == T_TEXTFAULT | 0x20 */
                if ((type & ~0x20) == T_TEXTFAULT)
                        sfva = pc;
                else {
                        rv = EACCES;
                        goto fault;
                }
        }

        if ((sfsr & SFSR_FT) == SFSR_FT_TRANSERR) {
                /*
                 * Translation errors are always fatal, as they indicate
                 * a corrupt translation (page) table hierarchy.
                 */
                rv = EACCES;

                /* XXXSMP - why bother with this anyway? */
                if (tfaultaddr == sfva) /* Prevent infinite loops w/a static */
                        goto fault;
                tfaultaddr = sfva;
                if ((lda((sfva & 0xFFFFF000) | ASI_SRMMUFP_LN, ASI_SRMMUFP) &
                    SRMMU_TETYPE) != SRMMU_TEPTE)
                        goto fault;     /* Translation bad */
                lda(SRMMU_SFSR, ASI_SRMMU);
#ifdef DEBUG
                printf("mem_access_fault4m: SFSR_FT_TRANSERR: "
                        "pid %d, va 0x%x: retrying\n", p->p_pid, sfva);
#endif
                goto out;       /* Translation OK, retry operation */
        }

        va = trunc_page(sfva);

        if (((sfsr & SFSR_AT_TEXT) || type == T_TEXTFAULT) &&
            !(sfsr & SFSR_AT_STORE) && (sfsr & SFSR_OW)) {
                if (psr & PSR_PS) {     /* never allow in kernel */
                        rv = EFAULT;
                        goto kfault;
                }
#if 0
                /*
                 * Double text fault. The evil "case 5" from the HS manual...
                 * Attempt to handle early fault. Ignores ASI 8,9 issue...may
                 * do a useless VM read.
                 * XXX: Is this really necessary?
                 * XXX: If it's necessary, add SA_PAGEFAULT handling
                 */
                if (cpuinfo.cpu_type == CPUTYP_HS_MBUS) {
                        /* On HS, we have va for both */
                        vm = p->p_vmspace;
                        pcb->pcb_onfault = NULL;
                        rv = uvm_fault(&vm->vm_map, trunc_page(pc),
                                      VM_PROT_READ);
                        pcb->pcb_onfault = onfault;
                        if (rv != 0)
#ifdef DEBUG
                                printf("mem_access_fault: "
                                        "can't pagein 1st text fault.\n")
#endif
                                ;
                }
#endif
        }

        /* Now munch on protections... */
        if (sfsr & SFSR_AT_STORE) {
                /* stores are never text faults. */
                atype = VM_PROT_WRITE;
        } else {
                if ((sfsr & SFSR_AT_TEXT) || (type & ~0x20) == T_TEXTFAULT) {
                        atype = VM_PROT_EXECUTE;
                } else {
                        atype = VM_PROT_READ;
                }
        }

        if (psr & PSR_PS) {
                if (sfsr & SFSR_AT_TEXT || type == T_TEXTFAULT) {
                        (void) splhigh();
                        snprintb(bits, sizeof(bits), SFSR_BITS, sfsr);
                        printf("cpu%d text fault: pc=0x%x sfsr=%s sfva=0x%x\n",
                               cpu_number(), pc, bits, sfva);
                        panic("kernel fault");
                        /* NOTREACHED */
                }
                /*
                 * If this was an access that we shouldn't try to page in,
                 * resume at the fault handler without any action.
                 */
                if (onfault == (vaddr_t)sparc_fsbail) {
                        rv = EFAULT;
                        goto kfault;
                }

                /*
                 * During autoconfiguration, faults are never OK unless
                 * pcb_onfault is set.  Once running normally we must allow
                 * exec() to cause copy-on-write faults to kernel addresses.
                 */
                if (cold) {
                        rv = EFAULT;
                        goto kfault;
                }
                if (va >= KERNBASE) {
                        pcb->pcb_onfault = NULL;
                        rv = uvm_fault(kernel_map, va, atype);
                        pcb->pcb_onfault = (void *)onfault;
                        if (rv == 0) {
                                return;
                        }
                        goto kfault;
                }
        } else {
                l->l_md.md_tf = tf;
        }

        vm = p->p_vmspace;

        /* alas! must call the horrible vm code */
        pcb->pcb_onfault = NULL;
        rv = uvm_fault(&vm->vm_map, (vaddr_t)va, atype);
        pcb->pcb_onfault = (void *)onfault;

        /*
         * If this was a stack access we keep track of the maximum
         * accessed stack size.  Also, if vm_fault gets a protection
         * failure it is due to accessing the stack region outside
         * the current limit and we need to reflect that as an access
         * error.
         */
        if (rv == 0 && (void *)va >= vm->vm_maxsaddr)
                uvm_grow(p, va);
        if (rv != 0) {
                /*
                 * Pagein failed.  If doing copyin/out, return to onfault
                 * address.  Any other page fault in kernel, die; if user
                 * fault, deliver SIGSEGV.
                 */
fault:
                if (psr & PSR_PS) {
kfault:
                        if (!onfault) {
                                (void) splhigh();
                                snprintb(bits, sizeof(bits), SFSR_BITS, sfsr);
                                printf("cpu%d: data fault: pc=0x%x "
                                       "addr=0x%x sfsr=%s\n",
                                       cpu_number(), pc, sfva, bits);
                                panic("kernel fault");
                                /* NOTREACHED */
                        }
                        tf->tf_pc = onfault;
                        tf->tf_npc = onfault + 4;
                        tf->tf_out[0] = (rv == EACCES) ? EFAULT : rv;
                        return;
                }
                KSI_INIT_TRAP(&ksi);
                switch (rv) {
                case ENOMEM:
                        printf("UVM: pid %d (%s), uid %d killed: out of swap\n",
                               p->p_pid, p->p_comm,
                               l->l_cred ?
                               kauth_cred_geteuid(l->l_cred) : -1);
                        ksi.ksi_signo = SIGKILL;
                        break;
                case EINVAL:
                        ksi.ksi_signo = SIGBUS;
                        ksi.ksi_code = BUS_ADRERR;
                        break;
                case EACCES:
                        ksi.ksi_signo = SIGSEGV;
                        ksi.ksi_code = SEGV_ACCERR;
                        break;
                default:
                        ksi.ksi_signo = SIGSEGV;
                        ksi.ksi_code = SEGV_MAPERR;
                        break;
                }
                ksi.ksi_trap = type;
                ksi.ksi_addr = (void *)sfva;
                trapsignal(l, &ksi);
        }
out:
        if ((psr & PSR_PS) == 0) {
out_nounlock:
                userret(l, pc, sticks);
                share_fpu(l, tf);
        }
}
#endif /* SUN4M */

/*
 * Start a new LWP
 */
void
startlwp(void *arg)
{
        ucontext_t *uc = arg;
        lwp_t *l = curlwp;
        int error __diagused;

        error = cpu_setmcontext(l, &uc->uc_mcontext, uc->uc_flags);
        KASSERT(error == 0);

        kmem_free(uc, sizeof(ucontext_t));
        userret(l, l->l_md.md_tf->tf_pc, 0);
}