/* $NetBSD: machdep.c,v 1.328 2016/12/10 10:41:07 mrg Exp $ */ /*- * Copyright (c) 1996, 1997, 1998 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 __KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.328 2016/12/10 10:41:07 mrg Exp $"); #include "opt_compat_netbsd.h" #include "opt_compat_sunos.h" #include "opt_sparc_arch.h" #include "opt_modular.h" #include "opt_multiprocessor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* we use uvm.kernel_object */ #include #ifdef COMPAT_13 #include #include #endif #define _SPARC_BUS_DMA_PRIVATE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fb.h" #include "power.h" #if NPOWER > 0 #include #endif extern paddr_t avail_end; kmutex_t fpu_mtx; /* * dvmamap24 is used to manage DVMA memory for devices that have the upper * eight address bits wired to all-ones (e.g. `le' and `ie') */ struct extent *dvmamap24; void dumpsys(void); void stackdump(void); /* * Machine-dependent startup code */ void cpu_startup(void) { #ifdef DEBUG extern int pmapdebug; int opmapdebug = pmapdebug; #endif struct pcb *pcb; vsize_t size; paddr_t pa; char pbuf[9]; #ifdef DEBUG pmapdebug = 0; #endif /* XXX */ pcb = lwp_getpcb(&lwp0); if (pcb && pcb->pcb_psr == 0) pcb->pcb_psr = getpsr(); /* * Re-map the message buffer from its temporary address * at KERNBASE to MSGBUF_VA. */ #if !defined(MSGBUFSIZE) || MSGBUFSIZE <= 8192 /* * We use the free page(s) in front of the kernel load address. */ size = 8192; /* Get physical address of the message buffer */ pmap_extract(pmap_kernel(), (vaddr_t)KERNBASE, &pa); /* Invalidate the current mapping at KERNBASE. */ pmap_kremove((vaddr_t)KERNBASE, size); pmap_update(pmap_kernel()); /* Enter the new mapping */ pmap_map(MSGBUF_VA, pa, pa + size, VM_PROT_READ|VM_PROT_WRITE); /* * Re-initialize the message buffer. */ initmsgbuf((void *)MSGBUF_VA, size); #else /* MSGBUFSIZE */ { struct pglist mlist; struct vm_page *m; vaddr_t va0, va; /* * We use the free page(s) in front of the kernel load address, * and then allocate some more. */ size = round_page(MSGBUFSIZE); /* Get physical address of first 8192 chunk of the message buffer */ pmap_extract(pmap_kernel(), (vaddr_t)KERNBASE, &pa); /* Allocate additional physical pages */ if (uvm_pglistalloc(size - 8192, vm_first_phys, vm_first_phys+vm_num_phys, 0, 0, &mlist, 1, 0) != 0) panic("cpu_start: no memory for message buffer"); /* Invalidate the current mapping at KERNBASE. */ pmap_kremove((vaddr_t)KERNBASE, 8192); pmap_update(pmap_kernel()); /* Allocate virtual memory space */ va0 = va = uvm_km_alloc(kernel_map, size, 0, UVM_KMF_VAONLY); if (va == 0) panic("cpu_start: no virtual memory for message buffer"); /* Map first 8192 */ while (va < va0 + 8192) { pmap_kenter_pa(va, pa, VM_PROT_READ | VM_PROT_WRITE, 0); pa += PAGE_SIZE; va += PAGE_SIZE; } pmap_update(pmap_kernel()); /* Map the rest of the pages */ TAILQ_FOREACH(m, &mlist ,pageq.queue) { if (va >= va0 + size) panic("cpu_start: memory buffer size botch"); pa = VM_PAGE_TO_PHYS(m); pmap_kenter_pa(va, pa, VM_PROT_READ | VM_PROT_WRITE, 0); va += PAGE_SIZE; } pmap_update(pmap_kernel()); /* * Re-initialize the message buffer. */ initmsgbuf((void *)va0, size); } #endif /* MSGBUFSIZE */ /* * Good {morning,afternoon,evening,night}. */ printf("%s%s", copyright, version); /*identifycpu();*/ format_bytes(pbuf, sizeof(pbuf), ctob(physmem)); printf("total memory = %s\n", pbuf); /* * Tune buffer cache variables based on the capabilities of the MMU * to cut down on VM space allocated for the buffer caches that * would lead to MMU resource shortage. */ if (CPU_ISSUN4 || CPU_ISSUN4C) { /* Clip UBC windows */ if (cpuinfo.mmu_nsegment <= 128) { /* * ubc_nwins and ubc_winshift control the amount * of VM used by the UBC. Normally, this VM is * not wired in the kernel map, hence non-locked * `PMEGs' (see pmap.c) are used for this space. * We still limit possible fragmentation to prevent * the occasional wired UBC mappings from tying up * too many PMEGs. * * Set the upper limit to 9 segments (default * winshift = 13). */ ubc_nwins = 512; /* * buf_setvalimit() allocates a submap for buffer * allocation. We use it to limit the number of locked * `PMEGs' (see pmap.c) dedicated to the buffer cache. * * Set the upper limit to 12 segments (3MB), which * corresponds approximately to the size of the * traditional 5% rule (assuming a maximum 64MB of * memory in small sun4c machines). */ buf_setvalimit(12 * 256*1024); } /* Clip max data & stack to avoid running into the MMU hole */ #if MAXDSIZ > 256*1024*1024 maxdmap = 256*1024*1024; #endif #if MAXSSIZ > 256*1024*1024 maxsmap = 256*1024*1024; #endif } if (CPU_ISSUN4 || CPU_ISSUN4C) { /* * Allocate DMA map for 24-bit devices (le, ie) * [dvma_base - dvma_end] is for VME devices.. */ dvmamap24 = extent_create("dvmamap24", D24_DVMA_BASE, D24_DVMA_END, 0, 0, EX_NOWAIT); if (dvmamap24 == NULL) panic("unable to allocate DVMA map"); } #ifdef DEBUG pmapdebug = opmapdebug; #endif format_bytes(pbuf, sizeof(pbuf), ptoa(uvmexp.free)); printf("avail memory = %s\n", pbuf); pmap_redzone(); mutex_init(&fpu_mtx, MUTEX_DEFAULT, IPL_SCHED); } /* * Set up registers on exec. * * XXX this entire mess must be fixed */ /* ARGSUSED */ void setregs(struct lwp *l, struct exec_package *pack, vaddr_t stack) { struct trapframe *tf = l->l_md.md_tf; struct fpstate *fs; int psr; /* Don't allow unaligned data references by default */ l->l_proc->p_md.md_flags &= ~MDP_FIXALIGN; /* * Set the registers to 0 except for: * %o6: stack pointer, built in exec()) * %psr: (retain CWP and PSR_S bits) * %g1: p->p_psstrp (used by crt0) * %pc,%npc: entry point of program */ psr = tf->tf_psr & (PSR_S | PSR_CWP); if ((fs = l->l_md.md_fpstate) != NULL) { struct cpu_info *cpi; int s; /* * We hold an FPU state. If we own *some* 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. */ FPU_LOCK(s); if ((cpi = l->l_md.md_fpu) != NULL) { if (cpi->fplwp != l) panic("FPU(%d): fplwp %p", cpi->ci_cpuid, cpi->fplwp); if (l == cpuinfo.fplwp) savefpstate(fs); #if defined(MULTIPROCESSOR) else XCALL1(ipi_savefpstate, fs, 1 << cpi->ci_cpuid); #endif cpi->fplwp = NULL; } l->l_md.md_fpu = NULL; FPU_UNLOCK(s); kmem_free(fs, sizeof(struct fpstate)); l->l_md.md_fpstate = NULL; } memset((void *)tf, 0, sizeof *tf); tf->tf_psr = psr; tf->tf_global[1] = l->l_proc->p_psstrp; tf->tf_pc = pack->ep_entry & ~3; tf->tf_npc = tf->tf_pc + 4; stack -= sizeof(struct rwindow); tf->tf_out[6] = stack; } #ifdef DEBUG int sigdebug = 0; int sigpid = 0; #define SDB_FOLLOW 0x01 #define SDB_KSTACK 0x02 #define SDB_FPSTATE 0x04 #endif /* * machine dependent system variables. */ static int sysctl_machdep_boot(SYSCTLFN_ARGS) { struct sysctlnode node = *rnode; struct btinfo_kernelfile *bi_file; const char *cp; switch (node.sysctl_num) { case CPU_BOOTED_KERNEL: if ((bi_file = lookup_bootinfo(BTINFO_KERNELFILE)) != NULL) cp = bi_file->name; else cp = prom_getbootfile(); if (cp != NULL && cp[0] == '\0') cp = "netbsd"; break; case CPU_BOOTED_DEVICE: cp = prom_getbootpath(); break; case CPU_BOOT_ARGS: cp = prom_getbootargs(); break; default: return (EINVAL); } if (cp == NULL || cp[0] == '\0') return (ENOENT); node.sysctl_data = __UNCONST(cp); node.sysctl_size = strlen(cp) + 1; return (sysctl_lookup(SYSCTLFN_CALL(&node))); } 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, "booted_device", NULL, sysctl_machdep_boot, 0, NULL, 0, CTL_MACHDEP, CPU_BOOTED_DEVICE, 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_INT, "cpu_arch", NULL, NULL, 0, &cpu_arch, 0, CTL_MACHDEP, CPU_ARCH, CTL_EOL); } /* * Send an interrupt to process. */ struct sigframe { siginfo_t sf_si; ucontext_t sf_uc; }; 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; struct trapframe *tf; ucontext_t uc; struct sigframe *fp; u_int onstack, oldsp, newsp; u_int catcher; int sig, error; size_t ucsz; sig = ksi->ksi_signo; tf = l->l_md.md_tf; oldsp = tf->tf_out[6]; /* * 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) fp = (struct sigframe *) ((char *)l->l_sigstk.ss_sp + l->l_sigstk.ss_size); else fp = (struct sigframe *)oldsp; fp = (struct sigframe *)((int)(fp - 1) & ~7); #ifdef DEBUG if ((sigdebug & SDB_KSTACK) && p->p_pid == sigpid) printf("sendsig: %s[%d] sig %d newusp %p si %p uc %p\n", p->p_comm, p->p_pid, sig, fp, &fp->sf_si, &fp->sf_uc); #endif /* * Build the signal context to be used by sigreturn. */ 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; memset(&uc.uc_stack, 0, sizeof(uc.uc_stack)); /* * 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. */ sendsig_reset(l, sig); mutex_exit(p->p_lock); newsp = (int)fp - sizeof(struct frame); cpu_getmcontext(l, &uc.uc_mcontext, &uc.uc_flags); ucsz = (int)&uc.__uc_pad - (int)&uc; error = (copyout(&ksi->ksi_info, &fp->sf_si, sizeof ksi->ksi_info) || copyout(&uc, &fp->sf_uc, ucsz) || suword(&((struct rwindow *)newsp)->rw_in[6], oldsp)); mutex_enter(p->p_lock); if (error) { /* * Process has trashed its stack; give it an illegal * instruction to halt it in its tracks. */ #ifdef DEBUG if ((sigdebug & SDB_KSTACK) && p->p_pid == sigpid) printf("sendsig: window save or copyout error\n"); #endif sigexit(l, SIGILL); /* NOTREACHED */ } switch (ps->sa_sigdesc[sig].sd_vers) { default: /* Unsupported trampoline version; kill the process. */ sigexit(l, SIGILL); case 2: /* * Arrange to continue execution at the user's handler. * It needs a new stack pointer, a return address and * three arguments: (signo, siginfo *, ucontext *). */ catcher = (u_int)SIGACTION(p, sig).sa_handler; tf->tf_pc = catcher; tf->tf_npc = catcher + 4; tf->tf_out[0] = sig; tf->tf_out[1] = (int)&fp->sf_si; tf->tf_out[2] = (int)&fp->sf_uc; tf->tf_out[6] = newsp; tf->tf_out[7] = (int)ps->sa_sigdesc[sig].sd_tramp - 8; break; } /* Remember that we're now on the signal stack. */ if (onstack) l->l_sigstk.ss_flags |= SS_ONSTACK; #ifdef DEBUG if ((sigdebug & SDB_KSTACK) && p->p_pid == sigpid) printf("sendsig: about to return to catcher\n"); #endif } void cpu_getmcontext(struct lwp *l, mcontext_t *mcp, unsigned int *flags) { struct trapframe *tf = (struct trapframe *)l->l_md.md_tf; __greg_t *r = mcp->__gregs; __greg_t ras_pc; #ifdef FPU_CONTEXT __fpregset_t *f = &mcp->__fpregs; struct fpstate *fps = l->l_md.md_fpstate; #endif /* * Put the stack in a consistent state before we whack away * at it. Note that write_user_windows may just dump the * registers into the pcb; we need them in the process's memory. */ write_user_windows(); if (rwindow_save(l)) { mutex_enter(l->l_proc->p_lock); sigexit(l, SIGILL); } /* * Get the general purpose registers */ r[_REG_PSR] = tf->tf_psr; r[_REG_PC] = tf->tf_pc; r[_REG_nPC] = tf->tf_npc; r[_REG_Y] = tf->tf_y; r[_REG_G1] = tf->tf_global[1]; r[_REG_G2] = tf->tf_global[2]; r[_REG_G3] = tf->tf_global[3]; r[_REG_G4] = tf->tf_global[4]; r[_REG_G5] = tf->tf_global[5]; r[_REG_G6] = tf->tf_global[6]; r[_REG_G7] = tf->tf_global[7]; r[_REG_O0] = tf->tf_out[0]; r[_REG_O1] = tf->tf_out[1]; r[_REG_O2] = tf->tf_out[2]; r[_REG_O3] = tf->tf_out[3]; r[_REG_O4] = tf->tf_out[4]; r[_REG_O5] = tf->tf_out[5]; r[_REG_O6] = tf->tf_out[6]; r[_REG_O7] = tf->tf_out[7]; if ((ras_pc = (__greg_t)ras_lookup(l->l_proc, (void *) r[_REG_PC])) != -1) { r[_REG_PC] = ras_pc; r[_REG_nPC] = ras_pc + 4; } *flags |= (_UC_CPU|_UC_TLSBASE); #ifdef FPU_CONTEXT /* * Get the floating point registers */ memcpy(f->__fpu_regs, fps->fs_regs, sizeof(fps->fs_regs)); f->__fp_nqsize = sizeof(struct fp_qentry); f->__fp_nqel = fps->fs_qsize; f->__fp_fsr = fps->fs_fsr; if (f->__fp_q != NULL) { size_t sz = f->__fp_nqel * f->__fp_nqsize; if (sz > sizeof(fps->fs_queue)) { #ifdef DIAGNOSTIC printf("getcontext: fp_queue too large\n"); #endif return; } if (copyout(fps->fs_queue, f->__fp_q, sz) != 0) { #ifdef DIAGNOSTIC printf("getcontext: copy of fp_queue failed %d\n", error); #endif return; } } f->fp_busy = 0; /* XXX: How do we determine that? */ *flags |= _UC_FPU; #endif return; } 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; } /* * Set to mcontext specified. * Return to previous pc and psl as specified by * context left by sendsig. Check carefully to * make sure that the user has not modified the * psl to gain improper privileges or to cause * a machine fault. * This is almost like sigreturn() and it shows. */ int cpu_setmcontext(struct lwp *l, const mcontext_t *mcp, unsigned int flags) { struct trapframe *tf; const __greg_t *r = mcp->__gregs; struct proc *p = l->l_proc; int error; #ifdef FPU_CONTEXT __fpregset_t *f = &mcp->__fpregs; struct fpstate *fps = l->l_md.md_fpstate; #endif write_user_windows(); if (rwindow_save(l)) { mutex_enter(p->p_lock); sigexit(l, SIGILL); } #ifdef DEBUG if (sigdebug & SDB_FOLLOW) printf("__setmcontext: %s[%d], __mcontext %p\n", l->l_proc->p_comm, l->l_proc->p_pid, mcp); #endif if (flags & _UC_CPU) { /* Validate */ error = cpu_mcontext_validate(l, mcp); if (error) return error; /* Restore register context. */ tf = (struct trapframe *)l->l_md.md_tf; /* take only psr ICC field */ tf->tf_psr = (tf->tf_psr & ~PSR_ICC) | (r[_REG_PSR] & PSR_ICC); tf->tf_pc = r[_REG_PC]; tf->tf_npc = r[_REG_nPC]; tf->tf_y = r[_REG_Y]; /* Restore everything */ tf->tf_global[1] = r[_REG_G1]; tf->tf_global[2] = r[_REG_G2]; tf->tf_global[3] = r[_REG_G3]; tf->tf_global[4] = r[_REG_G4]; tf->tf_global[5] = r[_REG_G5]; tf->tf_global[6] = r[_REG_G6]; /* done in lwp_setprivate */ /* tf->tf_global[7] = r[_REG_G7]; */ tf->tf_out[0] = r[_REG_O0]; tf->tf_out[1] = r[_REG_O1]; tf->tf_out[2] = r[_REG_O2]; tf->tf_out[3] = r[_REG_O3]; tf->tf_out[4] = r[_REG_O4]; tf->tf_out[5] = r[_REG_O5]; tf->tf_out[6] = r[_REG_O6]; tf->tf_out[7] = r[_REG_O7]; if (flags & _UC_TLSBASE) lwp_setprivate(l, (void *)(uintptr_t)r[_REG_G7]); } #ifdef FPU_CONTEXT if (flags & _UC_FPU) { /* * Set the floating point registers */ int error; size_t sz = f->__fp_nqel * f->__fp_nqsize; if (sz > sizeof(fps->fs_queue)) { #ifdef DIAGNOSTIC printf("setmcontext: fp_queue too large\n"); #endif return (EINVAL); } memcpy(fps->fs_regs, f->__fpu_regs, sizeof(fps->fs_regs)); fps->fs_qsize = f->__fp_nqel; fps->fs_fsr = f->__fp_fsr; if (f->__fp_q != NULL) { if ((error = copyin(f->__fp_q, fps->fs_queue, sz)) != 0) { #ifdef DIAGNOSTIC printf("setmcontext: fp_queue copy failed\n"); #endif return (error); } } } #endif 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); } int waittime = -1; void cpu_reboot(int howto, char *user_boot_string) { int i; char opts[4]; static char str[128]; /* If system is cold, just halt. */ if (cold) { howto |= RB_HALT; goto haltsys; } #if NFB > 0 fb_unblank(); #endif boothowto = howto; if ((howto & RB_NOSYNC) == 0 && waittime < 0) { extern struct lwp lwp0; /* XXX protect against curlwp->p_stats.foo refs in sync() */ if (curlwp == NULL) curlwp = &lwp0; waittime = 0; vfs_shutdown(); /* * If we've been adjusting the clock, the todr * will be out of synch; adjust it now. * resettodr will only do this only if inittodr() * has already been called. */ resettodr(); } /* Disable interrupts. But still allow IPI on MP systems */ if (sparc_ncpus > 1) (void)splsched(); else (void)splhigh(); #if defined(MULTIPROCESSOR) /* Direct system interrupts to this CPU, since dump uses polled I/O */ if (CPU_ISSUN4M) *((u_int *)ICR_ITR) = cpuinfo.mid - 8; #endif /* If rebooting and a dump is requested, do it. */ #if 0 if ((howto & (RB_DUMP | RB_HALT)) == RB_DUMP) #else if (howto & RB_DUMP) #endif dumpsys(); haltsys: /* Run any shutdown hooks. */ doshutdownhooks(); pmf_system_shutdown(boothowto); /* If powerdown was requested, do it. */ if ((howto & RB_POWERDOWN) == RB_POWERDOWN) { prom_interpret("power-off"); #if NPOWER > 0 /* Fall back on `power' device if the PROM can't do it */ powerdown(); #endif printf("WARNING: powerdown not supported\n"); /* * RB_POWERDOWN implies RB_HALT... fall into it... */ } if (howto & RB_HALT) { #if defined(MULTIPROCESSOR) mp_halt_cpus(); printf("cpu%d halted\n\n", cpu_number()); #else printf("halted\n\n"); #endif prom_halt(); } printf("rebooting\n\n"); i = 1; if (howto & RB_SINGLE) opts[i++] = 's'; if (howto & RB_KDB) opts[i++] = 'd'; opts[i] = '\0'; opts[0] = (i > 1) ? '-' : '\0'; if (user_boot_string && *user_boot_string) { i = strlen(user_boot_string); if (i > sizeof(str) - sizeof(opts) - 1) prom_boot(user_boot_string); /* XXX */ memcpy(str, user_boot_string, i); if (opts[0] != '\0') str[i] = ' '; } strcat(str, opts); prom_boot(str); /*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) 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 (32 * 1024) /* must be a multiple of pagesize */ static vaddr_t dumpspace; struct pcb dumppcb; 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 error = 0; struct memarr *mp; int nmem; extern struct memarr pmemarr[]; extern int npmemarr; /* copy registers to memory */ snapshot(cpuinfo.curpcb); memcpy(&dumppcb, cpuinfo.curpcb, sizeof 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 (dumplo <= 0) { printf("\ndump to dev %u,%u not possible\n", major(dumpdev), minor(dumpdev)); return; } printf("\ndumping to dev %u,%u offset %ld\n", major(dumpdev), minor(dumpdev), dumplo); psize = bdev_size(dumpdev); printf("dump "); if (psize == -1) { printf("area unavailable\n"); return; } blkno = dumplo; dump = bdev->d_dump; error = pmap_dumpmmu(dump, blkno); blkno += pmap_dumpsize(); for (mp = pmemarr, nmem = npmemarr; --nmem >= 0 && error == 0; mp++) { unsigned i = 0, n; int maddr = mp->addr; if (maddr == 0) { /* Skip first page at physical address 0 */ maddr += PAGE_SIZE; i += PAGE_SIZE; blkno += btodb(PAGE_SIZE); } for (; i < mp->len; i += n) { n = mp->len - i; if (n > BYTES_PER_DUMP) n = BYTES_PER_DUMP; /* print out how many MBs we have dumped */ if (i && (i % (1024*1024)) == 0) printf_nolog("%d ", i / (1024*1024)); (void) pmap_map(dumpspace, maddr, maddr + n, VM_PROT_READ); error = (*dump)(dumpdev, blkno, (void *)dumpspace, (int)n); pmap_kremove(dumpspace, n); pmap_update(pmap_kernel()); if (error) break; maddr += 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("succeeded\n"); break; default: printf("error %d\n", error); break; } } /* * get the fp and dump the stack as best we can. don't leave the * current stack page */ void stackdump(void) { struct frame *fp = getfp(), *sfp; sfp = fp; printf("Frame pointer is at %p\n", fp); printf("Call traceback:\n"); while (fp && ((u_long)fp >> PGSHIFT) == ((u_long)sfp >> PGSHIFT)) { printf(" pc = 0x%x args = (0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x) fp = %p\n", fp->fr_pc, 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 = fp->fr_fp; } } int cpu_exec_aout_makecmds(struct lwp *l, struct exec_package *epp) { return (ENOEXEC); } #if defined(SUN4) void oldmon_w_trace(u_long va) { struct cpu_info * const ci = curcpu(); u_long stop; struct frame *fp; printf("curlwp = %p, pid %d\n", curlwp, curproc->p_pid); printf("uvm: cpu%u: swtch %"PRIu64", trap %"PRIu64", sys %"PRIu64", " "intr %"PRIu64", soft %"PRIu64", faults %"PRIu64"\n", cpu_index(ci), ci->ci_data.cpu_nswtch, ci->ci_data.cpu_ntrap, ci->ci_data.cpu_nsyscall, ci->ci_data.cpu_nintr, ci->ci_data.cpu_nsoft, ci->ci_data.cpu_nfault); write_user_windows(); #define round_up(x) (( (x) + (PAGE_SIZE-1) ) & (~(PAGE_SIZE-1)) ) printf("\nstack trace with sp = 0x%lx\n", va); stop = round_up(va); printf("stop at 0x%lx\n", stop); fp = (struct frame *) va; while (round_up((u_long) fp) == stop) { printf(" 0x%x(0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x) fp %p\n", fp->fr_pc, 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 = fp->fr_fp; if (fp == NULL) break; } printf("end of stack trace\n"); } void oldmon_w_cmd(u_long va, char *ar) { switch (*ar) { case '\0': switch (va) { case 0: panic("g0 panic"); case 4: printf("w: case 4\n"); break; default: printf("w: unknown case %ld\n", va); break; } break; case 't': oldmon_w_trace(va); break; default: printf("w: arg not allowed\n"); } } int ldcontrolb(void *addr) { struct pcb *xpcb; u_long saveonfault; int res; int s; if (CPU_ISSUN4M || CPU_ISSUN4D) { printf("warning: ldcontrolb called on sun4m/sun4d\n"); return 0; } s = splhigh(); xpcb = lwp_getpcb(curlwp); saveonfault = (u_long)xpcb->pcb_onfault; res = xldcontrolb(addr, xpcb); xpcb->pcb_onfault = (void *)saveonfault; splx(s); return (res); } #endif /* SUN4 */ void wzero(void *vb, u_int l) { u_char *b = vb; u_char *be = b + l; u_short *sp; if (l == 0) return; /* front, */ if ((u_long)b & 1) *b++ = 0; /* back, */ if (b != be && ((u_long)be & 1) != 0) { be--; *be = 0; } /* and middle. */ sp = (u_short *)b; while (sp != (u_short *)be) *sp++ = 0; } void wcopy(const void *vb1, void *vb2, u_int l) { const u_char *b1e, *b1 = vb1; u_char *b2 = vb2; const u_short *sp; int bstore = 0; if (l == 0) return; /* front, */ if ((u_long)b1 & 1) { *b2++ = *b1++; l--; } /* middle, */ sp = (const u_short *)b1; b1e = b1 + l; if (l & 1) b1e--; bstore = (u_long)b2 & 1; while (sp < (const u_short *)b1e) { if (bstore) { b2[1] = *sp & 0xff; b2[0] = *sp >> 8; } else *((short *)b2) = *sp; sp++; b2 += 2; } /* and back. */ if (l & 1) *b2 = *b1e; } #ifdef MODULAR void module_init_md(void) { } #endif /* * 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; size_t mapsize; /* * 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). */ mapsize = sizeof(struct sparc_bus_dmamap) + (sizeof(bus_dma_segment_t) * (nsegments - 1)); if ((mapstore = malloc(mapsize, M_DMAMAP, (flags & BUS_DMA_NOWAIT) ? M_NOWAIT : M_WAITOK)) == NULL) return (ENOMEM); memset(mapstore, 0, mapsize); map = (struct sparc_bus_dmamap *)mapstore; map->_dm_size = size; map->_dm_segcnt = nsegments; map->_dm_maxmaxsegsz = maxsegsz; map->_dm_boundary = boundary; map->_dm_align = PAGE_SIZE; map->_dm_flags = flags & ~(BUS_DMA_WAITOK|BUS_DMA_NOWAIT); 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) { free(map, M_DMAMAP); } /* * 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) { panic("_bus_dmamap_load_mbuf: not implemented"); } /* * 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) { panic("_bus_dmamap_load_uio: not implemented"); } /* * 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 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) { } /* * 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 *mlist; int error; /* Always round the size. */ size = round_page(size); low = vm_first_phys; high = vm_first_phys + vm_num_phys - PAGE_SIZE; if ((mlist = malloc(sizeof(*mlist), M_DEVBUF, (flags & BUS_DMA_NOWAIT) ? M_NOWAIT : M_WAITOK)) == NULL) return (ENOMEM); /* * Allocate pages from the VM system. */ error = uvm_pglistalloc(size, low, high, 0, 0, mlist, nsegs, (flags & BUS_DMA_NOWAIT) == 0); if (error) { free(mlist, M_DEVBUF); return (error); } /* * 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 = mlist; /* * We now have physical pages, but no DVMA addresses yet. These * will be allocated in bus_dmamap_load*() routines. Hence we * save any alignment and boundary requirements in this DMA * segment. */ segs[0].ds_addr = 0; segs[0].ds_len = 0; segs[0]._ds_va = 0; *rsegs = 1; 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) { if (nsegs != 1) panic("bus_dmamem_free: nsegs = %d", nsegs); /* * Return the list of pages back to the VM system. */ uvm_pglistfree(segs[0]._ds_mlist); free(segs[0]._ds_mlist, M_DEVBUF); } /* * 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 & PAGE_MASK) panic("_bus_dmamem_unmap"); #endif size = round_page(size); pmap_kremove((vaddr_t)kva, size); pmap_update(pmap_kernel()); uvm_km_free(kernel_map, (vaddr_t)kva, size, UVM_KMF_VAONLY); } /* * Common functin 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) { panic("_bus_dmamem_mmap: not implemented"); } /* * Utility to allocate an aligned kernel virtual address range */ vaddr_t _bus_dma_valloc_skewed(size_t size, u_long boundary, u_long align, u_long skew) { size_t oversize; vaddr_t va, sva; /* * Find a region of kernel virtual addresses that is aligned * to the given address modulo the requested alignment, i.e. * * (va - skew) == 0 mod align * * The following conditions apply to the arguments: * * - `size' must be a multiple of the VM page size * - `align' must be a power of two * and greater than or equal to the VM page size * - `skew' must be smaller than `align' * - `size' must be smaller than `boundary' */ #ifdef DIAGNOSTIC if ((size & PAGE_MASK) != 0) panic("_bus_dma_valloc_skewed: invalid size %lx", size); if ((align & PAGE_MASK) != 0) panic("_bus_dma_valloc_skewed: invalid alignment %lx", align); if (align < skew) panic("_bus_dma_valloc_skewed: align %lx < skew %lx", align, skew); #endif /* XXX - Implement this! */ if (boundary) { printf("_bus_dma_valloc_skewed: " "boundary check not implemented"); return (0); } /* * First, find a region large enough to contain any aligned chunk */ oversize = size + align - PAGE_SIZE; sva = vm_map_min(kernel_map); if (uvm_map(kernel_map, &sva, oversize, NULL, UVM_UNKNOWN_OFFSET, align, UVM_MAPFLAG(UVM_PROT_ALL, UVM_PROT_ALL, UVM_INH_NONE, UVM_ADV_RANDOM, UVM_FLAG_NOWAIT))) return (0); /* * Compute start of aligned region */ va = sva; va += (skew + align - va) & (align - 1); /* * Return excess virtual addresses */ if (va != sva) (void)uvm_unmap(kernel_map, sva, va); if (va + size != sva + oversize) (void)uvm_unmap(kernel_map, va + size, sva + oversize); return (va); } /* sun4/sun4c DMA map functions */ int sun4_dmamap_load(bus_dma_tag_t, bus_dmamap_t, void *, bus_size_t, struct proc *, int); int sun4_dmamap_load_raw(bus_dma_tag_t, bus_dmamap_t, bus_dma_segment_t *, int, bus_size_t, int); void sun4_dmamap_unload(bus_dma_tag_t, bus_dmamap_t); int sun4_dmamem_map(bus_dma_tag_t, bus_dma_segment_t *, int, size_t, void **, int); /* * sun4/sun4c: load DMA map with a linear buffer. */ int sun4_dmamap_load(bus_dma_tag_t t, bus_dmamap_t map, void *buf, bus_size_t buflen, struct proc *p, int flags) { bus_size_t sgsize; vaddr_t va = (vaddr_t)buf; int pagesz = PAGE_SIZE; vaddr_t dva; pmap_t pmap; /* * Make sure that on error condition we return "no valid mappings". */ map->dm_nsegs = 0; if (buflen > map->_dm_size) return (EINVAL); cache_flush(buf, buflen); if ((map->_dm_flags & BUS_DMA_24BIT) == 0) { /* * XXX Need to implement "don't DMA across this boundry". */ if (map->_dm_boundary != 0) { bus_addr_t baddr; /* Calculate first boundary line after `buf' */ baddr = ((bus_addr_t)va + map->_dm_boundary) & -map->_dm_boundary; /* * If the requested segment crosses the boundary, * we can't grant a direct map. For now, steal some * space from the `24BIT' map instead. * * (XXX - no overflow detection here) */ if (buflen > (baddr - (bus_addr_t)va)) goto no_fit; } map->dm_mapsize = buflen; map->dm_nsegs = 1; map->dm_segs[0].ds_addr = (bus_addr_t)va; map->dm_segs[0].ds_len = buflen; map->_dm_flags |= _BUS_DMA_DIRECTMAP; return (0); } no_fit: sgsize = round_page(buflen + (va & (pagesz - 1))); if (extent_alloc(dvmamap24, sgsize, pagesz, map->_dm_boundary, (flags & BUS_DMA_NOWAIT) == 0 ? EX_WAITOK : EX_NOWAIT, &dva) != 0) { return (ENOMEM); } /* * We always use just one segment. */ map->dm_mapsize = buflen; map->dm_segs[0].ds_addr = dva + (va & (pagesz - 1)); map->dm_segs[0].ds_len = buflen; map->dm_segs[0]._ds_sgsize = sgsize; if (p != NULL) pmap = p->p_vmspace->vm_map.pmap; else pmap = pmap_kernel(); for (; buflen > 0; ) { paddr_t pa; /* * Get the physical address for this page. */ (void) pmap_extract(pmap, va, &pa); /* * Compute the segment size, and adjust counts. */ sgsize = pagesz - (va & (pagesz - 1)); if (buflen < sgsize) sgsize = buflen; #ifdef notyet #if defined(SUN4) if (have_iocache) pa |= PG_IOC; #endif #endif pmap_kenter_pa(dva, (pa & -pagesz) | PMAP_NC, VM_PROT_READ | VM_PROT_WRITE, 0); dva += pagesz; va += sgsize; buflen -= sgsize; } pmap_update(pmap_kernel()); map->dm_nsegs = 1; return (0); } /* * Like _bus_dmamap_load(), but for raw memory allocated with * bus_dmamem_alloc(). */ int sun4_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) { struct vm_page *m; paddr_t pa; vaddr_t dva; bus_size_t sgsize; struct pglist *mlist; int pagesz = PAGE_SIZE; int error; map->dm_nsegs = 0; sgsize = (size + pagesz - 1) & -pagesz; /* Allocate DVMA addresses */ if ((map->_dm_flags & BUS_DMA_24BIT) != 0) { error = extent_alloc(dvmamap24, sgsize, pagesz, map->_dm_boundary, (flags & BUS_DMA_NOWAIT) == 0 ? EX_WAITOK : EX_NOWAIT, &dva); if (error) return (error); } else { /* Any properly aligned virtual address will do */ dva = _bus_dma_valloc_skewed(sgsize, map->_dm_boundary, pagesz, 0); if (dva == 0) return (ENOMEM); } map->dm_segs[0].ds_addr = dva; map->dm_segs[0].ds_len = size; map->dm_segs[0]._ds_sgsize = sgsize; /* Map physical pages into IOMMU */ mlist = segs[0]._ds_mlist; for (m = TAILQ_FIRST(mlist); m != NULL; m = TAILQ_NEXT(m,pageq.queue)) { if (sgsize == 0) panic("sun4_dmamap_load_raw: size botch"); pa = VM_PAGE_TO_PHYS(m); #ifdef notyet #if defined(SUN4) if (have_iocache) pa |= PG_IOC; #endif #endif pmap_kenter_pa(dva, (pa & -pagesz) | PMAP_NC, VM_PROT_READ | VM_PROT_WRITE, 0); dva += pagesz; sgsize -= pagesz; } pmap_update(pmap_kernel()); map->dm_nsegs = 1; map->dm_mapsize = size; return (0); } /* * sun4/sun4c function for unloading a DMA map. */ void sun4_dmamap_unload(bus_dma_tag_t t, bus_dmamap_t map) { bus_dma_segment_t *segs = map->dm_segs; int nsegs = map->dm_nsegs; int flags = map->_dm_flags; vaddr_t dva; bus_size_t len; int i, s, error; map->dm_maxsegsz = map->_dm_maxmaxsegsz; if ((flags & _BUS_DMA_DIRECTMAP) != 0) { /* Nothing to release */ map->dm_mapsize = 0; map->dm_nsegs = 0; map->_dm_flags &= ~_BUS_DMA_DIRECTMAP; return; } for (i = 0; i < nsegs; i++) { dva = segs[i].ds_addr & -PAGE_SIZE; len = segs[i]._ds_sgsize; pmap_kremove(dva, len); if ((flags & BUS_DMA_24BIT) != 0) { s = splhigh(); error = extent_free(dvmamap24, dva, len, EX_NOWAIT); splx(s); if (error != 0) printf("warning: %ld of DVMA space lost\n", len); } else { uvm_unmap(kernel_map, dva, dva + len); } } pmap_update(pmap_kernel()); /* Mark the mappings as invalid. */ map->dm_mapsize = 0; map->dm_nsegs = 0; } /* * Common function for mapping DMA-safe memory. May be called by * bus-specific DMA memory map functions. */ int sun4_dmamem_map(bus_dma_tag_t t, bus_dma_segment_t *segs, int nsegs, size_t size, void **kvap, int flags) { struct vm_page *m; vaddr_t va; struct pglist *mlist; const uvm_flag_t kmflags = (flags & BUS_DMA_NOWAIT) != 0 ? UVM_KMF_NOWAIT : 0; if (nsegs != 1) panic("sun4_dmamem_map: nsegs = %d", nsegs); size = round_page(size); va = uvm_km_alloc(kernel_map, size, 0, UVM_KMF_VAONLY | kmflags); if (va == 0) return (ENOMEM); segs[0]._ds_va = va; *kvap = (void *)va; mlist = segs[0]._ds_mlist; TAILQ_FOREACH(m, mlist, pageq.queue) { paddr_t pa; if (size == 0) panic("sun4_dmamem_map: size botch"); pa = VM_PAGE_TO_PHYS(m); pmap_kenter_pa(va, pa | PMAP_NC, VM_PROT_READ | VM_PROT_WRITE, 0); va += PAGE_SIZE; size -= PAGE_SIZE; } pmap_update(pmap_kernel()); return (0); } struct sparc_bus_dma_tag mainbus_dma_tag = { NULL, _bus_dmamap_create, _bus_dmamap_destroy, sun4_dmamap_load, _bus_dmamap_load_mbuf, _bus_dmamap_load_uio, sun4_dmamap_load_raw, sun4_dmamap_unload, _bus_dmamap_sync, _bus_dmamem_alloc, _bus_dmamem_free, sun4_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 void sparc_bus_barrier(bus_space_tag_t, bus_space_handle_t, bus_size_t, bus_size_t, int); int bus_space_map( bus_space_tag_t t, bus_addr_t a, bus_size_t s, int f, bus_space_handle_t *hp) { return (*t->sparc_bus_map)(t, a, s, f, (vaddr_t)0, hp); } int bus_space_map2( bus_space_tag_t t, bus_addr_t a, bus_size_t s, int f, vaddr_t v, bus_space_handle_t *hp) { return (*t->sparc_bus_map)(t, a, s, f, v, hp); } void bus_space_unmap( bus_space_tag_t t, bus_space_handle_t h, bus_size_t s) { (*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) { return (*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) { return (*t->sparc_bus_mmap)(t, a, o, p, f); } void * bus_intr_establish( bus_space_tag_t t, int p, int l, int (*h)(void *), void *a) { return (*t->sparc_intr_establish)(t, p, l, h, a, NULL); } void * bus_intr_establish2( bus_space_tag_t t, int p, int l, int (*h)(void *), void *a, void (*v)(void)) { return (*t->sparc_intr_establish)(t, p, l, h, a, v); } void bus_space_barrier( bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, bus_size_t s, int f) { (*t->sparc_bus_barrier)(t, h, o, s, f); } 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_2_real(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_4_real(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_8_real(t, h, o, *a++); } /* * void bus_space_set_multi_N(bus_space_tag_t tag, * bus_space_handle_t bsh, bus_size_t offset, u_intN_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, const 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, const 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, const 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, const uint64_t v, bus_size_t c) { while (c-- > 0) bus_space_write_8(t, h, o, v); } /* * void bus_space_read_region_N(bus_space_tag_t tag, * bus_space_handle_t bsh, bus_size_t off, * u_intN_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_N(bus_space_tag_t tag, * bus_space_handle_t bsh, bus_size_t off, * u_intN_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_set_region_N(bus_space_tag_t tag, * bus_space_handle_t bsh, bus_size_t off, * u_intN_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_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_read_region_stream_N(bus_space_tag_t tag, * bus_space_handle_t bsh, bus_size_t off, * u_intN_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_region_stream_N(bus_space_tag_t tag, * bus_space_handle_t bsh, bus_size_t off, * u_intN_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); } /* * void bus_space_set_region_stream_N(bus_space_tag_t tag, * bus_space_handle_t bsh, bus_size_t off, * u_intN_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_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_write_1( bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint8_t v) { (*t->sparc_write_1)(t, h, o, v); } void bus_space_write_2( bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint16_t v) { (*t->sparc_write_2)(t, h, o, v); } void bus_space_write_4( bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint32_t v) { (*t->sparc_write_4)(t, h, o, v); } void bus_space_write_8( bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint64_t v) { (*t->sparc_write_8)(t, h, o, v); } #if __SLIM_SPARC_BUS_SPACE void bus_space_write_1( bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint8_t v) { __insn_barrier(); bus_space_write_1_real(t, h, o, v); } void bus_space_write_2( bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint16_t v) { __insn_barrier(); bus_space_write_2_real(t, h, o, v); } void bus_space_write_4( bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint32_t v) { __insn_barrier(); bus_space_write_4_real(t, h, o, v); } void bus_space_write_8( bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint64_t v) { __insn_barrier(); bus_space_write_8_real(t, h, o, v); } #endif /* __SLIM_SPARC_BUS_SPACE */ uint8_t bus_space_read_1( bus_space_tag_t t, bus_space_handle_t h, bus_size_t o) { return (*t->sparc_read_1)(t, h, o); } uint16_t bus_space_read_2( bus_space_tag_t t, bus_space_handle_t h, bus_size_t o) { return (*t->sparc_read_2)(t, h, o); } uint32_t bus_space_read_4( bus_space_tag_t t, bus_space_handle_t h, bus_size_t o) { return (*t->sparc_read_4)(t, h, o); } uint64_t bus_space_read_8( bus_space_tag_t t, bus_space_handle_t h, bus_size_t o) { return (*t->sparc_read_8)(t, h, o); } #if __SLIM_SPARC_BUS_SPACE uint8_t bus_space_read_1( bus_space_tag_t t, bus_space_handle_t h, bus_size_t o) { __insn_barrier(); return bus_space_read_1_real(t, h, o); } uint16_t bus_space_read_2( bus_space_tag_t t, bus_space_handle_t h, bus_size_t o) { __insn_barrier(); return bus_space_read_2_real(t, h, o); } uint32_t bus_space_read_4( bus_space_tag_t t, bus_space_handle_t h, bus_size_t o) { __insn_barrier(); return bus_space_read_4_real(t, h, o); } uint64_t bus_space_read_8( bus_space_tag_t t, bus_space_handle_t h, bus_size_t o) { __insn_barrier(); return bus_space_read_8_real(t, h, o); } #endif /* __SLIM_SPARC_BUS_SPACE */ 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_read_multi_N(bus_space_tag_t tag, * bus_space_handle_t bsh, bus_size_t offset, * u_intN_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_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_real(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_4_real(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_8_real(t, h, o); } /* * void bus_space_write_multi_N(bus_space_tag_t tag, * bus_space_handle_t bsh, bus_size_t offset, * const u_intN_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++); } /* * 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 = malloc(sizeof(struct sparc_bus_space_tag), M_DEVBUF, M_NOWAIT|M_ZERO); if (sbt == NULL) return (NULL); 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); } static int sparc_bus_map_iodev(bus_space_tag_t t, bus_addr_t ba, bus_size_t size, int flags, vaddr_t va, bus_space_handle_t *hp) { vaddr_t v; paddr_t pa; unsigned int pmtype; bus_space_tag_t pt; static vaddr_t iobase; /* * This base class bus map function knows about address range * translation so bus drivers that need no other special * handling can just keep this method in their tags. * * We expect to resolve range translations iteratively, but allow * for recursion just in case. */ while ((pt = t->parent) != NULL) { if (t->ranges != NULL) { int error; if ((error = bus_space_translate_address_generic( t->ranges, t->nranges, &ba)) != 0) return (error); } if (pt->sparc_bus_map != sparc_bus_map) return (bus_space_map2(pt, ba, size, flags, va, hp)); t = pt; } if (iobase == 0) iobase = IODEV_BASE; size = round_page(size); if (size == 0) { printf("sparc_bus_map: zero size\n"); return (EINVAL); } if (va) v = trunc_page(va); else { v = iobase; iobase += size; if (iobase > IODEV_END) /* unlikely */ panic("sparc_bus_map: iobase=0x%lx", iobase); } pmtype = PMAP_IOENC(BUS_ADDR_IOSPACE(ba)); pa = BUS_ADDR_PADDR(ba); /* note: preserve page offset */ *hp = (bus_space_handle_t)(v | ((u_long)pa & PGOFSET)); pa = trunc_page(pa); do { pmap_kenter_pa(v, pa | pmtype | PMAP_NC, VM_PROT_READ | VM_PROT_WRITE, 0); v += PAGE_SIZE; pa += PAGE_SIZE; } while ((size -= PAGE_SIZE) > 0); pmap_update(pmap_kernel()); return (0); } static int sparc_bus_map_large(bus_space_tag_t t, bus_addr_t ba, bus_size_t size, int flags, bus_space_handle_t *hp) { vaddr_t v = 0; if (uvm_map(kernel_map, &v, size, NULL, 0, PAGE_SIZE, UVM_MAPFLAG(UVM_PROT_RW, UVM_PROT_RW, UVM_INH_SHARE, UVM_ADV_NORMAL, 0)) == 0) { return sparc_bus_map_iodev(t, ba, size, flags, v, hp); } return -1; } int sparc_bus_map(bus_space_tag_t t, bus_addr_t ba, bus_size_t size, int flags, vaddr_t va, bus_space_handle_t *hp) { if (flags & BUS_SPACE_MAP_LARGE) { return sparc_bus_map_large(t, ba, size, flags, hp); } else return sparc_bus_map_iodev(t, ba, size, flags, va, hp); } 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); /* * XXX * mappings with BUS_SPACE_MAP_LARGE need additional care here * we can just check if the VA is in the IODEV range */ pmap_kremove(va, round_page(size)); pmap_update(pmap_kernel()); 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 = handle + offset; return (0); } paddr_t sparc_bus_mmap(bus_space_tag_t t, bus_addr_t ba, off_t off, int prot, int flags) { u_int pmtype; paddr_t pa; bus_space_tag_t pt; /* * Base class bus mmap function; see also sparc_bus_map */ while ((pt = t->parent) != NULL) { if (t->ranges != NULL) { int error; if ((error = bus_space_translate_address_generic( t->ranges, t->nranges, &ba)) != 0) return (-1); } if (pt->sparc_bus_mmap != sparc_bus_mmap) return (bus_space_mmap(pt, ba, off, prot, flags)); t = pt; } pmtype = PMAP_IOENC(BUS_ADDR_IOSPACE(ba)); pa = trunc_page(BUS_ADDR_PADDR(ba) + off); return (paddr_t)(pa | pmtype | PMAP_NC); } /* * Establish a temporary bus mapping for device probing. */ int bus_space_probe(bus_space_tag_t tag, bus_addr_t paddr, bus_size_t size, size_t offset, int flags, int (*callback)(void *, void *), void *arg) { bus_space_handle_t bh; void *tmp; int result; if (bus_space_map2(tag, paddr, size, flags, TMPMAP_VA, &bh) != 0) return (0); tmp = (void *)bh; result = (probeget((char *)tmp + offset, size) != -1); if (result && callback != NULL) result = (*callback)(tmp, arg); bus_space_unmap(tag, bh, size); return (result); } void * sparc_mainbus_intr_establish(bus_space_tag_t t, int pil, int level, int (*handler)(void *), void *arg, void (*fastvec)(void)) { struct intrhand *ih; ih = (struct intrhand *) malloc(sizeof(struct intrhand), M_DEVBUF, M_NOWAIT); if (ih == NULL) return (NULL); ih->ih_fun = handler; ih->ih_arg = arg; intr_establish(pil, level, ih, fastvec, false); return (ih); } void sparc_bus_barrier (bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset, bus_size_t size, int flags) { /* No default barrier action defined */ return; } static uint8_t sparc_bus_space_read_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o) { return bus_space_read_1_real(t, h, o); } static uint16_t sparc_bus_space_read_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o) { return bus_space_read_2_real(t, h, o); } static uint32_t sparc_bus_space_read_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o) { return bus_space_read_4_real(t, h, o); } static uint64_t sparc_bus_space_read_8(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o) { return bus_space_read_8_real(t, h, o); } static void sparc_bus_space_write_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint8_t v) { bus_space_write_1_real(t, h, o, v); } static void sparc_bus_space_write_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint16_t v) { bus_space_write_2_real(t, h, o, v); } static void sparc_bus_space_write_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint32_t v) { bus_space_write_4_real(t, h, o, v); } static void sparc_bus_space_write_8(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint64_t v) { bus_space_write_8_real(t, h, o, v); } struct sparc_bus_space_tag mainbus_space_tag = { NULL, /* cookie */ NULL, /* parent bus tag */ NULL, /* ranges */ 0, /* nranges */ sparc_bus_map, /* bus_space_map */ sparc_bus_unmap, /* bus_space_unmap */ sparc_bus_subregion, /* bus_space_subregion */ sparc_bus_barrier, /* bus_space_barrier */ sparc_bus_mmap, /* bus_space_mmap */ sparc_mainbus_intr_establish, /* bus_intr_establish */ sparc_bus_space_read_1, /* bus_space_read_1 */ sparc_bus_space_read_2, /* bus_space_read_2 */ sparc_bus_space_read_4, /* bus_space_read_4 */ sparc_bus_space_read_8, /* bus_space_read_8 */ sparc_bus_space_write_1, /* bus_space_write_1 */ sparc_bus_space_write_2, /* bus_space_write_2 */ sparc_bus_space_write_4, /* bus_space_write_4 */ sparc_bus_space_write_8 /* bus_space_write_8 */ }; 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) { extern vaddr_t prom_vstart; extern vaddr_t prom_vend; const vaddr_t v = (vaddr_t)ptr; *handled = (v >= MSGBUF_VA && v < MSGBUF_VA + PAGE_SIZE) || (v >= prom_vstart && v < prom_vend && (prot & VM_PROT_WRITE) == 0); return 0; } int mm_md_readwrite(dev_t dev, struct uio *uio) { switch (minor(dev)) { #if defined(SUN4) case DEV_EEPROM: if (cputyp == CPU_SUN4) return eeprom_uio(uio); else #endif return ENXIO; default: return ENXIO; } }