Index: head/sys/amd64/amd64/machdep.c =================================================================== --- head/sys/amd64/amd64/machdep.c (revision 78134) +++ head/sys/amd64/amd64/machdep.c (revision 78135) @@ -1,2518 +1,2518 @@ /*- * Copyright (c) 1992 Terrence R. Lambert. * Copyright (c) 1982, 1987, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * 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. * 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. * * from: @(#)machdep.c 7.4 (Berkeley) 6/3/91 * $FreeBSD$ */ #include "opt_atalk.h" #include "opt_compat.h" #include "opt_cpu.h" #include "opt_ddb.h" #include "opt_inet.h" #include "opt_ipx.h" #include "opt_isa.h" #include "opt_maxmem.h" #include "opt_msgbuf.h" #include "opt_npx.h" #include "opt_perfmon.h" -#include "opt_userconfig.h" +/* #include "opt_userconfig.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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* pcb.h included via sys/user.h */ #include #ifdef PERFMON #include #endif #ifdef OLD_BUS_ARCH #include #endif #include #include #include #include #include #include extern void init386 __P((int first)); extern void dblfault_handler __P((void)); extern void printcpuinfo(void); /* XXX header file */ extern void earlysetcpuclass(void); /* same header file */ extern void finishidentcpu(void); extern void panicifcpuunsupported(void); extern void initializecpu(void); #define CS_SECURE(cs) (ISPL(cs) == SEL_UPL) #define EFL_SECURE(ef, oef) ((((ef) ^ (oef)) & ~PSL_USERCHANGE) == 0) static void cpu_startup __P((void *)); SYSINIT(cpu, SI_SUB_CPU, SI_ORDER_FIRST, cpu_startup, NULL) int _udatasel, _ucodesel; u_int atdevbase; #if defined(SWTCH_OPTIM_STATS) extern int swtch_optim_stats; SYSCTL_INT(_debug, OID_AUTO, swtch_optim_stats, CTLFLAG_RD, &swtch_optim_stats, 0, ""); SYSCTL_INT(_debug, OID_AUTO, tlb_flush_count, CTLFLAG_RD, &tlb_flush_count, 0, ""); #endif #ifdef PC98 static int ispc98 = 1; #else static int ispc98 = 0; #endif SYSCTL_INT(_machdep, OID_AUTO, ispc98, CTLFLAG_RD, &ispc98, 0, ""); int physmem = 0; int cold = 1; static void osendsig __P((sig_t catcher, int sig, sigset_t *mask, u_long code)); static int sysctl_hw_physmem(SYSCTL_HANDLER_ARGS) { int error = sysctl_handle_int(oidp, 0, ctob(physmem), req); return (error); } SYSCTL_PROC(_hw, HW_PHYSMEM, physmem, CTLTYPE_INT|CTLFLAG_RD, 0, 0, sysctl_hw_physmem, "I", ""); static int sysctl_hw_usermem(SYSCTL_HANDLER_ARGS) { int error = sysctl_handle_int(oidp, 0, ctob(physmem - cnt.v_wire_count), req); return (error); } SYSCTL_PROC(_hw, HW_USERMEM, usermem, CTLTYPE_INT|CTLFLAG_RD, 0, 0, sysctl_hw_usermem, "I", ""); static int sysctl_hw_availpages(SYSCTL_HANDLER_ARGS) { int error = sysctl_handle_int(oidp, 0, i386_btop(avail_end - avail_start), req); return (error); } SYSCTL_PROC(_hw, OID_AUTO, availpages, CTLTYPE_INT|CTLFLAG_RD, 0, 0, sysctl_hw_availpages, "I", ""); static int sysctl_machdep_msgbuf(SYSCTL_HANDLER_ARGS) { int error; /* Unwind the buffer, so that it's linear (possibly starting with * some initial nulls). */ error=sysctl_handle_opaque(oidp,msgbufp->msg_ptr+msgbufp->msg_bufr, msgbufp->msg_size-msgbufp->msg_bufr,req); if(error) return(error); if(msgbufp->msg_bufr>0) { error=sysctl_handle_opaque(oidp,msgbufp->msg_ptr, msgbufp->msg_bufr,req); } return(error); } SYSCTL_PROC(_machdep, OID_AUTO, msgbuf, CTLTYPE_STRING|CTLFLAG_RD, 0, 0, sysctl_machdep_msgbuf, "A","Contents of kernel message buffer"); static int msgbuf_clear; static int sysctl_machdep_msgbuf_clear(SYSCTL_HANDLER_ARGS) { int error; error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); if (!error && req->newptr) { /* Clear the buffer and reset write pointer */ bzero(msgbufp->msg_ptr,msgbufp->msg_size); msgbufp->msg_bufr=msgbufp->msg_bufx=0; msgbuf_clear=0; } return (error); } SYSCTL_PROC(_machdep, OID_AUTO, msgbuf_clear, CTLTYPE_INT|CTLFLAG_RW, &msgbuf_clear, 0, sysctl_machdep_msgbuf_clear, "I", "Clear kernel message buffer"); int Maxmem = 0; long dumplo; vm_offset_t phys_avail[10]; /* must be 2 less so 0 0 can signal end of chunks */ #define PHYS_AVAIL_ARRAY_END ((sizeof(phys_avail) / sizeof(vm_offset_t)) - 2) static vm_offset_t buffer_sva, buffer_eva; vm_offset_t clean_sva, clean_eva; static vm_offset_t pager_sva, pager_eva; static struct trapframe proc0_tf; #ifndef SMP static struct globaldata __globaldata; #endif struct mtx sched_lock; struct mtx Giant; static void cpu_startup(dummy) void *dummy; { register unsigned i; register caddr_t v; vm_offset_t maxaddr; vm_size_t size = 0; int firstaddr; vm_offset_t minaddr; int physmem_est; /* * Good {morning,afternoon,evening,night}. */ mtx_lock(&vm_mtx); earlysetcpuclass(); startrtclock(); printcpuinfo(); panicifcpuunsupported(); #ifdef PERFMON perfmon_init(); #endif printf("real memory = %u (%uK bytes)\n", ptoa(Maxmem), ptoa(Maxmem) / 1024); /* * Display any holes after the first chunk of extended memory. */ if (bootverbose) { int indx; printf("Physical memory chunk(s):\n"); for (indx = 0; phys_avail[indx + 1] != 0; indx += 2) { unsigned int size1 = phys_avail[indx + 1] - phys_avail[indx]; printf("0x%08x - 0x%08x, %u bytes (%u pages)\n", phys_avail[indx], phys_avail[indx + 1] - 1, size1, size1 / PAGE_SIZE); } } /* * Calculate callout wheel size */ for (callwheelsize = 1, callwheelbits = 0; callwheelsize < ncallout; callwheelsize <<= 1, ++callwheelbits) ; callwheelmask = callwheelsize - 1; /* * Allocate space for system data structures. * The first available kernel virtual address is in "v". * As pages of kernel virtual memory are allocated, "v" is incremented. * As pages of memory are allocated and cleared, * "firstaddr" is incremented. * An index into the kernel page table corresponding to the * virtual memory address maintained in "v" is kept in "mapaddr". */ /* * Make two passes. The first pass calculates how much memory is * needed and allocates it. The second pass assigns virtual * addresses to the various data structures. */ firstaddr = 0; again: v = (caddr_t)firstaddr; #define valloc(name, type, num) \ (name) = (type *)v; v = (caddr_t)((name)+(num)) #define valloclim(name, type, num, lim) \ (name) = (type *)v; v = (caddr_t)((lim) = ((name)+(num))) valloc(callout, struct callout, ncallout); valloc(callwheel, struct callout_tailq, callwheelsize); /* * Discount the physical memory larger than the size of kernel_map * to avoid eating up all of KVA space. */ if (kernel_map->first_free == NULL) { printf("Warning: no free entries in kernel_map.\n"); physmem_est = physmem; } else physmem_est = min(physmem, kernel_map->max_offset - kernel_map->min_offset); /* * The nominal buffer size (and minimum KVA allocation) is BKVASIZE. * For the first 64MB of ram nominally allocate sufficient buffers to * cover 1/4 of our ram. Beyond the first 64MB allocate additional * buffers to cover 1/20 of our ram over 64MB. * * factor represents the 1/4 x ram conversion. */ if (nbuf == 0) { int factor = 4 * BKVASIZE / PAGE_SIZE; nbuf = 50; if (physmem_est > 1024) nbuf += min((physmem_est - 1024) / factor, 16384 / factor); if (physmem_est > 16384) nbuf += (physmem_est - 16384) * 2 / (factor * 5); } /* * Do not allow the buffer_map to be more then 1/2 the size of the * kernel_map. */ if (nbuf > (kernel_map->max_offset - kernel_map->min_offset) / (BKVASIZE * 2)) { nbuf = (kernel_map->max_offset - kernel_map->min_offset) / (BKVASIZE * 2); printf("Warning: nbufs capped at %d\n", nbuf); } nswbuf = max(min(nbuf/4, 256), 16); valloc(swbuf, struct buf, nswbuf); valloc(buf, struct buf, nbuf); v = bufhashinit(v); /* * End of first pass, size has been calculated so allocate memory */ if (firstaddr == 0) { size = (vm_size_t)(v - firstaddr); firstaddr = (int)kmem_alloc(kernel_map, round_page(size)); if (firstaddr == 0) panic("startup: no room for tables"); goto again; } /* * End of second pass, addresses have been assigned */ if ((vm_size_t)(v - firstaddr) != size) panic("startup: table size inconsistency"); clean_map = kmem_suballoc(kernel_map, &clean_sva, &clean_eva, (nbuf*BKVASIZE) + (nswbuf*MAXPHYS) + pager_map_size); buffer_map = kmem_suballoc(clean_map, &buffer_sva, &buffer_eva, (nbuf*BKVASIZE)); buffer_map->system_map = 1; pager_map = kmem_suballoc(clean_map, &pager_sva, &pager_eva, (nswbuf*MAXPHYS) + pager_map_size); pager_map->system_map = 1; exec_map = kmem_suballoc(kernel_map, &minaddr, &maxaddr, (16*(ARG_MAX+(PAGE_SIZE*3)))); mtx_unlock(&vm_mtx); /* * XXX: Mbuf system machine-specific initializations should * go here, if anywhere. */ /* * Initialize callouts */ SLIST_INIT(&callfree); for (i = 0; i < ncallout; i++) { callout_init(&callout[i], 0); callout[i].c_flags = CALLOUT_LOCAL_ALLOC; SLIST_INSERT_HEAD(&callfree, &callout[i], c_links.sle); } for (i = 0; i < callwheelsize; i++) { TAILQ_INIT(&callwheel[i]); } mtx_init(&callout_lock, "callout", MTX_SPIN | MTX_RECURSE); #if defined(USERCONFIG) userconfig(); cninit(); /* the preferred console may have changed */ #endif printf("avail memory = %u (%uK bytes)\n", ptoa(cnt.v_free_count), ptoa(cnt.v_free_count) / 1024); /* * Set up buffers, so they can be used to read disk labels. */ bufinit(); vm_pager_bufferinit(); globaldata_register(GLOBALDATA); #ifndef SMP /* For SMP, we delay the cpu_setregs() until after SMP startup. */ cpu_setregs(); #endif } /* * Send an interrupt to process. * * Stack is set up to allow sigcode stored * at top to call routine, followed by kcall * to sigreturn routine below. After sigreturn * resets the signal mask, the stack, and the * frame pointer, it returns to the user * specified pc, psl. */ static void osendsig(catcher, sig, mask, code) sig_t catcher; int sig; sigset_t *mask; u_long code; { struct osigframe sf; struct osigframe *fp; struct proc *p; struct sigacts *psp; struct trapframe *regs; int oonstack; p = curproc; PROC_LOCK(p); psp = p->p_sigacts; regs = p->p_md.md_regs; oonstack = sigonstack(regs->tf_esp); /* Allocate and validate space for the signal handler context. */ if ((p->p_flag & P_ALTSTACK) && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { fp = (struct osigframe *)(p->p_sigstk.ss_sp + p->p_sigstk.ss_size - sizeof(struct osigframe)); #if defined(COMPAT_43) || defined(COMPAT_SUNOS) p->p_sigstk.ss_flags |= SS_ONSTACK; #endif } else fp = (struct osigframe *)regs->tf_esp - 1; PROC_UNLOCK(p); /* * grow_stack() will return 0 if *fp does not fit inside the stack * and the stack can not be grown. * useracc() will return FALSE if access is denied. */ if (grow_stack(p, (int)fp) == 0 || !useracc((caddr_t)fp, sizeof(*fp), VM_PROT_WRITE)) { /* * Process has trashed its stack; give it an illegal * instruction to halt it in its tracks. */ PROC_LOCK(p); SIGACTION(p, SIGILL) = SIG_DFL; SIGDELSET(p->p_sigignore, SIGILL); SIGDELSET(p->p_sigcatch, SIGILL); SIGDELSET(p->p_sigmask, SIGILL); psignal(p, SIGILL); PROC_UNLOCK(p); return; } /* Translate the signal if appropriate. */ if (p->p_sysent->sv_sigtbl && sig <= p->p_sysent->sv_sigsize) sig = p->p_sysent->sv_sigtbl[_SIG_IDX(sig)]; /* Build the argument list for the signal handler. */ sf.sf_signum = sig; sf.sf_scp = (register_t)&fp->sf_siginfo.si_sc; PROC_LOCK(p); if (SIGISMEMBER(p->p_sigacts->ps_siginfo, sig)) { /* Signal handler installed with SA_SIGINFO. */ sf.sf_arg2 = (register_t)&fp->sf_siginfo; sf.sf_siginfo.si_signo = sig; sf.sf_siginfo.si_code = code; sf.sf_ahu.sf_action = (__osiginfohandler_t *)catcher; } else { /* Old FreeBSD-style arguments. */ sf.sf_arg2 = code; sf.sf_addr = regs->tf_err; sf.sf_ahu.sf_handler = catcher; } PROC_UNLOCK(p); /* Save most if not all of trap frame. */ sf.sf_siginfo.si_sc.sc_eax = regs->tf_eax; sf.sf_siginfo.si_sc.sc_ebx = regs->tf_ebx; sf.sf_siginfo.si_sc.sc_ecx = regs->tf_ecx; sf.sf_siginfo.si_sc.sc_edx = regs->tf_edx; sf.sf_siginfo.si_sc.sc_esi = regs->tf_esi; sf.sf_siginfo.si_sc.sc_edi = regs->tf_edi; sf.sf_siginfo.si_sc.sc_cs = regs->tf_cs; sf.sf_siginfo.si_sc.sc_ds = regs->tf_ds; sf.sf_siginfo.si_sc.sc_ss = regs->tf_ss; sf.sf_siginfo.si_sc.sc_es = regs->tf_es; sf.sf_siginfo.si_sc.sc_fs = regs->tf_fs; sf.sf_siginfo.si_sc.sc_gs = rgs(); sf.sf_siginfo.si_sc.sc_isp = regs->tf_isp; /* Build the signal context to be used by osigreturn(). */ sf.sf_siginfo.si_sc.sc_onstack = (oonstack) ? 1 : 0; SIG2OSIG(*mask, sf.sf_siginfo.si_sc.sc_mask); sf.sf_siginfo.si_sc.sc_sp = regs->tf_esp; sf.sf_siginfo.si_sc.sc_fp = regs->tf_ebp; sf.sf_siginfo.si_sc.sc_pc = regs->tf_eip; sf.sf_siginfo.si_sc.sc_ps = regs->tf_eflags; sf.sf_siginfo.si_sc.sc_trapno = regs->tf_trapno; sf.sf_siginfo.si_sc.sc_err = regs->tf_err; /* * If we're a vm86 process, we want to save the segment registers. * We also change eflags to be our emulated eflags, not the actual * eflags. */ if (regs->tf_eflags & PSL_VM) { /* XXX confusing names: `tf' isn't a trapframe; `regs' is. */ struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86 = &p->p_addr->u_pcb.pcb_ext->ext_vm86; sf.sf_siginfo.si_sc.sc_gs = tf->tf_vm86_gs; sf.sf_siginfo.si_sc.sc_fs = tf->tf_vm86_fs; sf.sf_siginfo.si_sc.sc_es = tf->tf_vm86_es; sf.sf_siginfo.si_sc.sc_ds = tf->tf_vm86_ds; if (vm86->vm86_has_vme == 0) sf.sf_siginfo.si_sc.sc_ps = (tf->tf_eflags & ~(PSL_VIF | PSL_VIP)) | (vm86->vm86_eflags & (PSL_VIF | PSL_VIP)); /* See sendsig() for comments. */ tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_T | PSL_VIF | PSL_VIP); } /* Copy the sigframe out to the user's stack. */ if (copyout(&sf, fp, sizeof(*fp)) != 0) { /* * Something is wrong with the stack pointer. * ...Kill the process. */ PROC_LOCK(p); sigexit(p, SIGILL); /* NOTREACHED */ } regs->tf_esp = (int)fp; regs->tf_eip = PS_STRINGS - szosigcode; regs->tf_cs = _ucodesel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_fs = _udatasel; load_gs(_udatasel); regs->tf_ss = _udatasel; } void sendsig(catcher, sig, mask, code) sig_t catcher; int sig; sigset_t *mask; u_long code; { struct sigframe sf; struct proc *p; struct sigacts *psp; struct trapframe *regs; struct sigframe *sfp; int oonstack; p = curproc; PROC_LOCK(p); psp = p->p_sigacts; if (SIGISMEMBER(psp->ps_osigset, sig)) { PROC_UNLOCK(p); osendsig(catcher, sig, mask, code); return; } regs = p->p_md.md_regs; oonstack = sigonstack(regs->tf_esp); /* Save user context. */ bzero(&sf, sizeof(sf)); sf.sf_uc.uc_sigmask = *mask; sf.sf_uc.uc_stack = p->p_sigstk; sf.sf_uc.uc_stack.ss_flags = (p->p_flag & P_ALTSTACK) ? ((oonstack) ? SS_ONSTACK : 0) : SS_DISABLE; sf.sf_uc.uc_mcontext.mc_onstack = (oonstack) ? 1 : 0; sf.sf_uc.uc_mcontext.mc_gs = rgs(); bcopy(regs, &sf.sf_uc.uc_mcontext.mc_fs, sizeof(*regs)); /* Allocate and validate space for the signal handler context. */ if ((p->p_flag & P_ALTSTACK) != 0 && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { sfp = (struct sigframe *)(p->p_sigstk.ss_sp + p->p_sigstk.ss_size - sizeof(struct sigframe)); #if defined(COMPAT_43) || defined(COMPAT_SUNOS) p->p_sigstk.ss_flags |= SS_ONSTACK; #endif } else sfp = (struct sigframe *)regs->tf_esp - 1; PROC_UNLOCK(p); /* * grow_stack() will return 0 if *sfp does not fit inside the stack * and the stack can not be grown. * useracc() will return FALSE if access is denied. */ if (grow_stack(p, (int)sfp) == 0 || !useracc((caddr_t)sfp, sizeof(*sfp), VM_PROT_WRITE)) { /* * Process has trashed its stack; give it an illegal * instruction to halt it in its tracks. */ #ifdef DEBUG printf("process %d has trashed its stack\n", p->p_pid); #endif PROC_LOCK(p); SIGACTION(p, SIGILL) = SIG_DFL; SIGDELSET(p->p_sigignore, SIGILL); SIGDELSET(p->p_sigcatch, SIGILL); SIGDELSET(p->p_sigmask, SIGILL); psignal(p, SIGILL); PROC_UNLOCK(p); return; } /* Translate the signal if appropriate. */ if (p->p_sysent->sv_sigtbl && sig <= p->p_sysent->sv_sigsize) sig = p->p_sysent->sv_sigtbl[_SIG_IDX(sig)]; /* Build the argument list for the signal handler. */ sf.sf_signum = sig; sf.sf_ucontext = (register_t)&sfp->sf_uc; PROC_LOCK(p); if (SIGISMEMBER(p->p_sigacts->ps_siginfo, sig)) { /* Signal handler installed with SA_SIGINFO. */ sf.sf_siginfo = (register_t)&sfp->sf_si; sf.sf_ahu.sf_action = (__siginfohandler_t *)catcher; /* Fill siginfo structure. */ sf.sf_si.si_signo = sig; sf.sf_si.si_code = code; sf.sf_si.si_addr = (void *)regs->tf_err; } else { /* Old FreeBSD-style arguments. */ sf.sf_siginfo = code; sf.sf_addr = regs->tf_err; sf.sf_ahu.sf_handler = catcher; } PROC_UNLOCK(p); /* * If we're a vm86 process, we want to save the segment registers. * We also change eflags to be our emulated eflags, not the actual * eflags. */ if (regs->tf_eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86 = &p->p_addr->u_pcb.pcb_ext->ext_vm86; sf.sf_uc.uc_mcontext.mc_gs = tf->tf_vm86_gs; sf.sf_uc.uc_mcontext.mc_fs = tf->tf_vm86_fs; sf.sf_uc.uc_mcontext.mc_es = tf->tf_vm86_es; sf.sf_uc.uc_mcontext.mc_ds = tf->tf_vm86_ds; if (vm86->vm86_has_vme == 0) sf.sf_uc.uc_mcontext.mc_eflags = (tf->tf_eflags & ~(PSL_VIF | PSL_VIP)) | (vm86->vm86_eflags & (PSL_VIF | PSL_VIP)); /* * We should never have PSL_T set when returning from vm86 * mode. It may be set here if we deliver a signal before * getting to vm86 mode, so turn it off. * * Clear PSL_NT to inhibit T_TSSFLT faults on return from * syscalls made by the signal handler. This just avoids * wasting time for our lazy fixup of such faults. PSL_NT * does nothing in vm86 mode, but vm86 programs can set it * almost legitimately in probes for old cpu types. */ tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_T | PSL_VIF | PSL_VIP); } /* Copy the sigframe out to the user's stack. */ if (copyout(&sf, sfp, sizeof(*sfp)) != 0) { /* * Something is wrong with the stack pointer. * ...Kill the process. */ PROC_LOCK(p); sigexit(p, SIGILL); /* NOTREACHED */ } regs->tf_esp = (int)sfp; regs->tf_eip = PS_STRINGS - *(p->p_sysent->sv_szsigcode); regs->tf_cs = _ucodesel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_fs = _udatasel; regs->tf_ss = _udatasel; } /* * System call to cleanup state after a signal * has been taken. Reset signal mask and * stack state from context left by sendsig (above). * 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 * state to gain improper privileges. */ int osigreturn(p, uap) struct proc *p; struct osigreturn_args /* { struct osigcontext *sigcntxp; } */ *uap; { struct trapframe *regs; struct osigcontext *scp; int eflags; regs = p->p_md.md_regs; scp = uap->sigcntxp; if (!useracc((caddr_t)scp, sizeof(*scp), VM_PROT_READ)) return (EFAULT); eflags = scp->sc_ps; if (eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86; /* * if pcb_ext == 0 or vm86_inited == 0, the user hasn't * set up the vm86 area, and we can't enter vm86 mode. */ if (p->p_addr->u_pcb.pcb_ext == 0) return (EINVAL); vm86 = &p->p_addr->u_pcb.pcb_ext->ext_vm86; if (vm86->vm86_inited == 0) return (EINVAL); /* Go back to user mode if both flags are set. */ if ((eflags & PSL_VIP) && (eflags & PSL_VIF)) trapsignal(p, SIGBUS, 0); if (vm86->vm86_has_vme) { eflags = (tf->tf_eflags & ~VME_USERCHANGE) | (eflags & VME_USERCHANGE) | PSL_VM; } else { vm86->vm86_eflags = eflags; /* save VIF, VIP */ eflags = (tf->tf_eflags & ~VM_USERCHANGE) | (eflags & VM_USERCHANGE) | PSL_VM; } tf->tf_vm86_ds = scp->sc_ds; tf->tf_vm86_es = scp->sc_es; tf->tf_vm86_fs = scp->sc_fs; tf->tf_vm86_gs = scp->sc_gs; tf->tf_ds = _udatasel; tf->tf_es = _udatasel; tf->tf_fs = _udatasel; } else { /* * Don't allow users to change privileged or reserved flags. */ /* * XXX do allow users to change the privileged flag PSL_RF. * The cpu sets PSL_RF in tf_eflags for faults. Debuggers * should sometimes set it there too. tf_eflags is kept in * the signal context during signal handling and there is no * other place to remember it, so the PSL_RF bit may be * corrupted by the signal handler without us knowing. * Corruption of the PSL_RF bit at worst causes one more or * one less debugger trap, so allowing it is fairly harmless. */ if (!EFL_SECURE(eflags & ~PSL_RF, regs->tf_eflags & ~PSL_RF)) { return (EINVAL); } /* * Don't allow users to load a valid privileged %cs. Let the * hardware check for invalid selectors, excess privilege in * other selectors, invalid %eip's and invalid %esp's. */ if (!CS_SECURE(scp->sc_cs)) { trapsignal(p, SIGBUS, T_PROTFLT); return (EINVAL); } regs->tf_ds = scp->sc_ds; regs->tf_es = scp->sc_es; regs->tf_fs = scp->sc_fs; } /* Restore remaining registers. */ regs->tf_eax = scp->sc_eax; regs->tf_ebx = scp->sc_ebx; regs->tf_ecx = scp->sc_ecx; regs->tf_edx = scp->sc_edx; regs->tf_esi = scp->sc_esi; regs->tf_edi = scp->sc_edi; regs->tf_cs = scp->sc_cs; regs->tf_ss = scp->sc_ss; regs->tf_isp = scp->sc_isp; PROC_LOCK(p); #if defined(COMPAT_43) || defined(COMPAT_SUNOS) if (scp->sc_onstack & 1) p->p_sigstk.ss_flags |= SS_ONSTACK; else p->p_sigstk.ss_flags &= ~SS_ONSTACK; #endif SIGSETOLD(p->p_sigmask, scp->sc_mask); SIG_CANTMASK(p->p_sigmask); PROC_UNLOCK(p); regs->tf_ebp = scp->sc_fp; regs->tf_esp = scp->sc_sp; regs->tf_eip = scp->sc_pc; regs->tf_eflags = eflags; return (EJUSTRETURN); } int sigreturn(p, uap) struct proc *p; struct sigreturn_args /* { ucontext_t *sigcntxp; } */ *uap; { struct trapframe *regs; ucontext_t *ucp; int cs, eflags; ucp = uap->sigcntxp; if (!useracc((caddr_t)ucp, sizeof(struct osigcontext), VM_PROT_READ)) return (EFAULT); if (((struct osigcontext *)ucp)->sc_trapno == 0x01d516) return (osigreturn(p, (struct osigreturn_args *)uap)); /* * Since ucp is not an osigcontext but a ucontext_t, we have to * check again if all of it is accessible. A ucontext_t is * much larger, so instead of just checking for the pointer * being valid for the size of an osigcontext, now check for * it being valid for a whole, new-style ucontext_t. */ if (!useracc((caddr_t)ucp, sizeof(*ucp), VM_PROT_READ)) return (EFAULT); regs = p->p_md.md_regs; eflags = ucp->uc_mcontext.mc_eflags; if (eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86; /* * if pcb_ext == 0 or vm86_inited == 0, the user hasn't * set up the vm86 area, and we can't enter vm86 mode. */ if (p->p_addr->u_pcb.pcb_ext == 0) return (EINVAL); vm86 = &p->p_addr->u_pcb.pcb_ext->ext_vm86; if (vm86->vm86_inited == 0) return (EINVAL); /* Go back to user mode if both flags are set. */ if ((eflags & PSL_VIP) && (eflags & PSL_VIF)) trapsignal(p, SIGBUS, 0); if (vm86->vm86_has_vme) { eflags = (tf->tf_eflags & ~VME_USERCHANGE) | (eflags & VME_USERCHANGE) | PSL_VM; } else { vm86->vm86_eflags = eflags; /* save VIF, VIP */ eflags = (tf->tf_eflags & ~VM_USERCHANGE) | (eflags & VM_USERCHANGE) | PSL_VM; } bcopy(&ucp->uc_mcontext.mc_fs, tf, sizeof(struct trapframe)); tf->tf_eflags = eflags; tf->tf_vm86_ds = tf->tf_ds; tf->tf_vm86_es = tf->tf_es; tf->tf_vm86_fs = tf->tf_fs; tf->tf_vm86_gs = ucp->uc_mcontext.mc_gs; tf->tf_ds = _udatasel; tf->tf_es = _udatasel; tf->tf_fs = _udatasel; } else { /* * Don't allow users to change privileged or reserved flags. */ /* * XXX do allow users to change the privileged flag PSL_RF. * The cpu sets PSL_RF in tf_eflags for faults. Debuggers * should sometimes set it there too. tf_eflags is kept in * the signal context during signal handling and there is no * other place to remember it, so the PSL_RF bit may be * corrupted by the signal handler without us knowing. * Corruption of the PSL_RF bit at worst causes one more or * one less debugger trap, so allowing it is fairly harmless. */ if (!EFL_SECURE(eflags & ~PSL_RF, regs->tf_eflags & ~PSL_RF)) { printf("sigreturn: eflags = 0x%x\n", eflags); return (EINVAL); } /* * Don't allow users to load a valid privileged %cs. Let the * hardware check for invalid selectors, excess privilege in * other selectors, invalid %eip's and invalid %esp's. */ cs = ucp->uc_mcontext.mc_cs; if (!CS_SECURE(cs)) { printf("sigreturn: cs = 0x%x\n", cs); trapsignal(p, SIGBUS, T_PROTFLT); return (EINVAL); } bcopy(&ucp->uc_mcontext.mc_fs, regs, sizeof(*regs)); } PROC_LOCK(p); #if defined(COMPAT_43) || defined(COMPAT_SUNOS) if (ucp->uc_mcontext.mc_onstack & 1) p->p_sigstk.ss_flags |= SS_ONSTACK; else p->p_sigstk.ss_flags &= ~SS_ONSTACK; #endif p->p_sigmask = ucp->uc_sigmask; SIG_CANTMASK(p->p_sigmask); PROC_UNLOCK(p); return (EJUSTRETURN); } /* * Machine dependent boot() routine * * I haven't seen anything to put here yet * Possibly some stuff might be grafted back here from boot() */ void cpu_boot(int howto) { } /* * Shutdown the CPU as much as possible */ void cpu_halt(void) { for (;;) __asm__ ("hlt"); } /* * Hook to idle the CPU when possible. This currently only works in * the !SMP case, as there is no clean way to ensure that a CPU will be * woken when there is work available for it. */ static int cpu_idle_hlt = 1; SYSCTL_INT(_machdep, OID_AUTO, cpu_idle_hlt, CTLFLAG_RW, &cpu_idle_hlt, 0, "Idle loop HLT enable"); /* * Note that we have to be careful here to avoid a race between checking * procrunnable() and actually halting. If we don't do this, we may waste * the time between calling hlt and the next interrupt even though there * is a runnable process. */ void cpu_idle(void) { #ifndef SMP if (cpu_idle_hlt) { disable_intr(); if (procrunnable()) enable_intr(); else { enable_intr(); __asm __volatile("hlt"); } } #endif } /* * Clear registers on exec */ void setregs(p, entry, stack, ps_strings) struct proc *p; u_long entry; u_long stack; u_long ps_strings; { struct trapframe *regs = p->p_md.md_regs; struct pcb *pcb = &p->p_addr->u_pcb; if (pcb->pcb_ldt) user_ldt_free(pcb); bzero((char *)regs, sizeof(struct trapframe)); regs->tf_eip = entry; regs->tf_esp = stack; regs->tf_eflags = PSL_USER | (regs->tf_eflags & PSL_T); regs->tf_ss = _udatasel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_fs = _udatasel; regs->tf_cs = _ucodesel; /* PS_STRINGS value for BSD/OS binaries. It is 0 for non-BSD/OS. */ regs->tf_ebx = ps_strings; /* reset %gs as well */ if (pcb == PCPU_GET(curpcb)) load_gs(_udatasel); else pcb->pcb_gs = _udatasel; /* * Reset the hardware debug registers if they were in use. * They won't have any meaning for the newly exec'd process. */ if (pcb->pcb_flags & PCB_DBREGS) { pcb->pcb_dr0 = 0; pcb->pcb_dr1 = 0; pcb->pcb_dr2 = 0; pcb->pcb_dr3 = 0; pcb->pcb_dr6 = 0; pcb->pcb_dr7 = 0; if (pcb == PCPU_GET(curpcb)) { /* * Clear the debug registers on the running * CPU, otherwise they will end up affecting * the next process we switch to. */ reset_dbregs(); } pcb->pcb_flags &= ~PCB_DBREGS; } /* * Initialize the math emulator (if any) for the current process. * Actually, just clear the bit that says that the emulator has * been initialized. Initialization is delayed until the process * traps to the emulator (if it is done at all) mainly because * emulators don't provide an entry point for initialization. */ p->p_addr->u_pcb.pcb_flags &= ~FP_SOFTFP; /* * Arrange to trap the next npx or `fwait' instruction (see npx.c * for why fwait must be trapped at least if there is an npx or an * emulator). This is mainly to handle the case where npx0 is not * configured, since the npx routines normally set up the trap * otherwise. It should be done only at boot time, but doing it * here allows modifying `npx_exists' for testing the emulator on * systems with an npx. */ load_cr0(rcr0() | CR0_MP | CR0_TS); #ifdef DEV_NPX /* Initialize the npx (if any) for the current process. */ npxinit(__INITIAL_NPXCW__); #endif /* * XXX - Linux emulator * Make sure sure edx is 0x0 on entry. Linux binaries depend * on it. */ p->p_retval[1] = 0; } void cpu_setregs(void) { unsigned int cr0; cr0 = rcr0(); cr0 |= CR0_NE; /* Done by npxinit() */ cr0 |= CR0_MP | CR0_TS; /* Done at every execve() too. */ #ifndef I386_CPU cr0 |= CR0_WP | CR0_AM; #endif load_cr0(cr0); load_gs(_udatasel); } static int sysctl_machdep_adjkerntz(SYSCTL_HANDLER_ARGS) { int error; error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); if (!error && req->newptr) resettodr(); return (error); } SYSCTL_PROC(_machdep, CPU_ADJKERNTZ, adjkerntz, CTLTYPE_INT|CTLFLAG_RW, &adjkerntz, 0, sysctl_machdep_adjkerntz, "I", ""); SYSCTL_INT(_machdep, CPU_DISRTCSET, disable_rtc_set, CTLFLAG_RW, &disable_rtc_set, 0, ""); SYSCTL_STRUCT(_machdep, CPU_BOOTINFO, bootinfo, CTLFLAG_RD, &bootinfo, bootinfo, ""); SYSCTL_INT(_machdep, CPU_WALLCLOCK, wall_cmos_clock, CTLFLAG_RW, &wall_cmos_clock, 0, ""); /* * Initialize 386 and configure to run kernel */ /* * Initialize segments & interrupt table */ int _default_ldt; union descriptor gdt[NGDT * MAXCPU]; /* global descriptor table */ static struct gate_descriptor idt0[NIDT]; struct gate_descriptor *idt = &idt0[0]; /* interrupt descriptor table */ union descriptor ldt[NLDT]; /* local descriptor table */ #ifdef SMP /* table descriptors - used to load tables by microp */ struct region_descriptor r_gdt, r_idt; #endif int private_tss; /* flag indicating private tss */ #if defined(I586_CPU) && !defined(NO_F00F_HACK) extern int has_f00f_bug; #endif static struct i386tss dblfault_tss; static char dblfault_stack[PAGE_SIZE]; extern struct user *proc0paddr; /* software prototypes -- in more palatable form */ struct soft_segment_descriptor gdt_segs[] = { /* GNULL_SEL 0 Null Descriptor */ { 0x0, /* segment base address */ 0x0, /* length */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GCODE_SEL 1 Code Descriptor for kernel */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GDATA_SEL 2 Data Descriptor for kernel */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GPRIV_SEL 3 SMP Per-Processor Private Data Descriptor */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GPROC0_SEL 4 Proc 0 Tss Descriptor */ { 0x0, /* segment base address */ sizeof(struct i386tss)-1,/* length - all address space */ SDT_SYS386TSS, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GLDT_SEL 5 LDT Descriptor */ { (int) ldt, /* segment base address */ sizeof(ldt)-1, /* length - all address space */ SDT_SYSLDT, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GUSERLDT_SEL 6 User LDT Descriptor per process */ { (int) ldt, /* segment base address */ (512 * sizeof(union descriptor)-1), /* length */ SDT_SYSLDT, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GTGATE_SEL 7 Null Descriptor - Placeholder */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GBIOSLOWMEM_SEL 8 BIOS access to realmode segment 0x40, must be #8 in GDT */ { 0x400, /* segment base address */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GPANIC_SEL 9 Panic Tss Descriptor */ { (int) &dblfault_tss, /* segment base address */ sizeof(struct i386tss)-1,/* length - all address space */ SDT_SYS386TSS, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GBIOSCODE32_SEL 10 BIOS 32-bit interface (32bit Code) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSCODE16_SEL 11 BIOS 32-bit interface (16bit Code) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSDATA_SEL 12 BIOS 32-bit interface (Data) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSUTIL_SEL 13 BIOS 16-bit interface (Utility) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSARGS_SEL 14 BIOS 16-bit interface (Arguments) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, }; static struct soft_segment_descriptor ldt_segs[] = { /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Code Descriptor for user */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMERA, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Data Descriptor for user */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, }; void setidt(idx, func, typ, dpl, selec) int idx; inthand_t *func; int typ; int dpl; int selec; { struct gate_descriptor *ip; ip = idt + idx; ip->gd_looffset = (int)func; ip->gd_selector = selec; ip->gd_stkcpy = 0; ip->gd_xx = 0; ip->gd_type = typ; ip->gd_dpl = dpl; ip->gd_p = 1; ip->gd_hioffset = ((int)func)>>16 ; } #define IDTVEC(name) __CONCAT(X,name) extern inthand_t IDTVEC(div), IDTVEC(dbg), IDTVEC(nmi), IDTVEC(bpt), IDTVEC(ofl), IDTVEC(bnd), IDTVEC(ill), IDTVEC(dna), IDTVEC(fpusegm), IDTVEC(tss), IDTVEC(missing), IDTVEC(stk), IDTVEC(prot), IDTVEC(page), IDTVEC(mchk), IDTVEC(rsvd), IDTVEC(fpu), IDTVEC(align), IDTVEC(lcall_syscall), IDTVEC(int0x80_syscall); void sdtossd(sd, ssd) struct segment_descriptor *sd; struct soft_segment_descriptor *ssd; { ssd->ssd_base = (sd->sd_hibase << 24) | sd->sd_lobase; ssd->ssd_limit = (sd->sd_hilimit << 16) | sd->sd_lolimit; ssd->ssd_type = sd->sd_type; ssd->ssd_dpl = sd->sd_dpl; ssd->ssd_p = sd->sd_p; ssd->ssd_def32 = sd->sd_def32; ssd->ssd_gran = sd->sd_gran; } #define PHYSMAP_SIZE (2 * 8) /* * Populate the (physmap) array with base/bound pairs describing the * available physical memory in the system, then test this memory and * build the phys_avail array describing the actually-available memory. * * If we cannot accurately determine the physical memory map, then use * value from the 0xE801 call, and failing that, the RTC. * * Total memory size may be set by the kernel environment variable * hw.physmem or the compile-time define MAXMEM. */ static void getmemsize(int first) { int i, physmap_idx, pa_indx; u_int basemem, extmem; struct vm86frame vmf; struct vm86context vmc; vm_offset_t pa, physmap[PHYSMAP_SIZE]; pt_entry_t pte; const char *cp; struct bios_smap *smap; bzero(&vmf, sizeof(struct vm86frame)); bzero(physmap, sizeof(physmap)); /* * Perform "base memory" related probes & setup */ vm86_intcall(0x12, &vmf); basemem = vmf.vmf_ax; if (basemem > 640) { printf("Preposterous BIOS basemem of %uK, truncating to 640K\n", basemem); basemem = 640; } /* * XXX if biosbasemem is now < 640, there is a `hole' * between the end of base memory and the start of * ISA memory. The hole may be empty or it may * contain BIOS code or data. Map it read/write so * that the BIOS can write to it. (Memory from 0 to * the physical end of the kernel is mapped read-only * to begin with and then parts of it are remapped. * The parts that aren't remapped form holes that * remain read-only and are unused by the kernel. * The base memory area is below the physical end of * the kernel and right now forms a read-only hole. * The part of it from PAGE_SIZE to * (trunc_page(biosbasemem * 1024) - 1) will be * remapped and used by the kernel later.) * * This code is similar to the code used in * pmap_mapdev, but since no memory needs to be * allocated we simply change the mapping. */ for (pa = trunc_page(basemem * 1024); pa < ISA_HOLE_START; pa += PAGE_SIZE) { pte = (pt_entry_t)vtopte(pa + KERNBASE); *pte = pa | PG_RW | PG_V; } /* * if basemem != 640, map pages r/w into vm86 page table so * that the bios can scribble on it. */ pte = (pt_entry_t)vm86paddr; for (i = basemem / 4; i < 160; i++) pte[i] = (i << PAGE_SHIFT) | PG_V | PG_RW | PG_U; /* * map page 1 R/W into the kernel page table so we can use it * as a buffer. The kernel will unmap this page later. */ pte = (pt_entry_t)vtopte(KERNBASE + (1 << PAGE_SHIFT)); *pte = (1 << PAGE_SHIFT) | PG_RW | PG_V; /* * get memory map with INT 15:E820 */ vmc.npages = 0; smap = (void *)vm86_addpage(&vmc, 1, KERNBASE + (1 << PAGE_SHIFT)); vm86_getptr(&vmc, (vm_offset_t)smap, &vmf.vmf_es, &vmf.vmf_di); physmap_idx = 0; vmf.vmf_ebx = 0; do { vmf.vmf_eax = 0xE820; vmf.vmf_edx = SMAP_SIG; vmf.vmf_ecx = sizeof(struct bios_smap); i = vm86_datacall(0x15, &vmf, &vmc); if (i || vmf.vmf_eax != SMAP_SIG) break; if (boothowto & RB_VERBOSE) printf("SMAP type=%02x base=%08x %08x len=%08x %08x\n", smap->type, *(u_int32_t *)((char *)&smap->base + 4), (u_int32_t)smap->base, *(u_int32_t *)((char *)&smap->length + 4), (u_int32_t)smap->length); if (smap->type != 0x01) goto next_run; if (smap->length == 0) goto next_run; if (smap->base >= 0xffffffff) { printf("%uK of memory above 4GB ignored\n", (u_int)(smap->length / 1024)); goto next_run; } for (i = 0; i <= physmap_idx; i += 2) { if (smap->base < physmap[i + 1]) { if (boothowto & RB_VERBOSE) printf( "Overlapping or non-montonic memory region, ignoring second region\n"); goto next_run; } } if (smap->base == physmap[physmap_idx + 1]) { physmap[physmap_idx + 1] += smap->length; goto next_run; } physmap_idx += 2; if (physmap_idx == PHYSMAP_SIZE) { printf( "Too many segments in the physical address map, giving up\n"); break; } physmap[physmap_idx] = smap->base; physmap[physmap_idx + 1] = smap->base + smap->length; next_run: } while (vmf.vmf_ebx != 0); if (physmap[1] != 0) goto physmap_done; /* * If we failed above, try memory map with INT 15:E801 */ vmf.vmf_ax = 0xE801; if (vm86_intcall(0x15, &vmf) == 0) { extmem = vmf.vmf_cx + vmf.vmf_dx * 64; } else { #if 0 vmf.vmf_ah = 0x88; vm86_intcall(0x15, &vmf); extmem = vmf.vmf_ax; #else /* * Prefer the RTC value for extended memory. */ extmem = rtcin(RTC_EXTLO) + (rtcin(RTC_EXTHI) << 8); #endif } /* * Special hack for chipsets that still remap the 384k hole when * there's 16MB of memory - this really confuses people that * are trying to use bus mastering ISA controllers with the * "16MB limit"; they only have 16MB, but the remapping puts * them beyond the limit. * * If extended memory is between 15-16MB (16-17MB phys address range), * chop it to 15MB. */ if ((extmem > 15 * 1024) && (extmem < 16 * 1024)) extmem = 15 * 1024; physmap[0] = 0; physmap[1] = basemem * 1024; physmap_idx = 2; physmap[physmap_idx] = 0x100000; physmap[physmap_idx + 1] = physmap[physmap_idx] + extmem * 1024; physmap_done: /* * Now, physmap contains a map of physical memory. */ #ifdef SMP /* make hole for AP bootstrap code */ physmap[1] = mp_bootaddress(physmap[1] / 1024); /* look for the MP hardware - needed for apic addresses */ i386_mp_probe(); #endif /* * Maxmem isn't the "maximum memory", it's one larger than the * highest page of the physical address space. It should be * called something like "Maxphyspage". We may adjust this * based on ``hw.physmem'' and the results of the memory test. */ Maxmem = atop(physmap[physmap_idx + 1]); #ifdef MAXMEM Maxmem = MAXMEM / 4; #endif /* * hw.maxmem is a size in bytes; we also allow k, m, and g suffixes * for the appropriate modifiers. This overrides MAXMEM. */ if ((cp = getenv("hw.physmem")) != NULL) { u_int64_t AllowMem, sanity; char *ep; sanity = AllowMem = strtouq(cp, &ep, 0); if ((ep != cp) && (*ep != 0)) { switch(*ep) { case 'g': case 'G': AllowMem <<= 10; case 'm': case 'M': AllowMem <<= 10; case 'k': case 'K': AllowMem <<= 10; break; default: AllowMem = sanity = 0; } if (AllowMem < sanity) AllowMem = 0; } if (AllowMem == 0) printf("Ignoring invalid memory size of '%s'\n", cp); else Maxmem = atop(AllowMem); } if (atop(physmap[physmap_idx + 1]) != Maxmem && (boothowto & RB_VERBOSE)) printf("Physical memory use set to %uK\n", Maxmem * 4); /* * If Maxmem has been increased beyond what the system has detected, * extend the last memory segment to the new limit. */ if (atop(physmap[physmap_idx + 1]) < Maxmem) physmap[physmap_idx + 1] = ptoa(Maxmem); /* call pmap initialization to make new kernel address space */ pmap_bootstrap(first, 0); /* * Size up each available chunk of physical memory. */ physmap[0] = PAGE_SIZE; /* mask off page 0 */ pa_indx = 0; phys_avail[pa_indx++] = physmap[0]; phys_avail[pa_indx] = physmap[0]; #if 0 pte = (pt_entry_t)vtopte(KERNBASE); #else pte = (pt_entry_t)CMAP1; #endif /* * physmap is in bytes, so when converting to page boundaries, * round up the start address and round down the end address. */ for (i = 0; i <= physmap_idx; i += 2) { vm_offset_t end; end = ptoa(Maxmem); if (physmap[i + 1] < end) end = trunc_page(physmap[i + 1]); for (pa = round_page(physmap[i]); pa < end; pa += PAGE_SIZE) { int tmp, page_bad; #if 0 int *ptr = 0; #else int *ptr = (int *)CADDR1; #endif /* * block out kernel memory as not available. */ if (pa >= 0x100000 && pa < first) continue; page_bad = FALSE; /* * map page into kernel: valid, read/write,non-cacheable */ *pte = pa | PG_V | PG_RW | PG_N; invltlb(); tmp = *(int *)ptr; /* * Test for alternating 1's and 0's */ *(volatile int *)ptr = 0xaaaaaaaa; if (*(volatile int *)ptr != 0xaaaaaaaa) { page_bad = TRUE; } /* * Test for alternating 0's and 1's */ *(volatile int *)ptr = 0x55555555; if (*(volatile int *)ptr != 0x55555555) { page_bad = TRUE; } /* * Test for all 1's */ *(volatile int *)ptr = 0xffffffff; if (*(volatile int *)ptr != 0xffffffff) { page_bad = TRUE; } /* * Test for all 0's */ *(volatile int *)ptr = 0x0; if (*(volatile int *)ptr != 0x0) { page_bad = TRUE; } /* * Restore original value. */ *(int *)ptr = tmp; /* * Adjust array of valid/good pages. */ if (page_bad == TRUE) { continue; } /* * If this good page is a continuation of the * previous set of good pages, then just increase * the end pointer. Otherwise start a new chunk. * Note that "end" points one higher than end, * making the range >= start and < end. * If we're also doing a speculative memory * test and we at or past the end, bump up Maxmem * so that we keep going. The first bad page * will terminate the loop. */ if (phys_avail[pa_indx] == pa) { phys_avail[pa_indx] += PAGE_SIZE; } else { pa_indx++; if (pa_indx == PHYS_AVAIL_ARRAY_END) { printf("Too many holes in the physical address space, giving up\n"); pa_indx--; break; } phys_avail[pa_indx++] = pa; /* start */ phys_avail[pa_indx] = pa + PAGE_SIZE; /* end */ } physmem++; } } *pte = 0; invltlb(); /* * XXX * The last chunk must contain at least one page plus the message * buffer to avoid complicating other code (message buffer address * calculation, etc.). */ while (phys_avail[pa_indx - 1] + PAGE_SIZE + round_page(MSGBUF_SIZE) >= phys_avail[pa_indx]) { physmem -= atop(phys_avail[pa_indx] - phys_avail[pa_indx - 1]); phys_avail[pa_indx--] = 0; phys_avail[pa_indx--] = 0; } Maxmem = atop(phys_avail[pa_indx]); /* Trim off space for the message buffer. */ phys_avail[pa_indx] -= round_page(MSGBUF_SIZE); avail_end = phys_avail[pa_indx]; } void init386(first) int first; { int x; struct gate_descriptor *gdp; int gsel_tss; #ifndef SMP /* table descriptors - used to load tables by microp */ struct region_descriptor r_gdt, r_idt; #endif int off; proc0.p_addr = proc0paddr; atdevbase = ISA_HOLE_START + KERNBASE; if (bootinfo.bi_modulep) { preload_metadata = (caddr_t)bootinfo.bi_modulep + KERNBASE; preload_bootstrap_relocate(KERNBASE); } else { printf("WARNING: loader(8) metadata is missing!\n"); } if (bootinfo.bi_envp) kern_envp = (caddr_t)bootinfo.bi_envp + KERNBASE; /* * make gdt memory segments, the code segment goes up to end of the * page with etext in it, the data segment goes to the end of * the address space */ /* * XXX text protection is temporarily (?) disabled. The limit was * i386_btop(round_page(etext)) - 1. */ gdt_segs[GCODE_SEL].ssd_limit = i386_btop(0) - 1; gdt_segs[GDATA_SEL].ssd_limit = i386_btop(0) - 1; #ifdef SMP gdt_segs[GPRIV_SEL].ssd_limit = i386_btop(sizeof(struct privatespace)) - 1; gdt_segs[GPRIV_SEL].ssd_base = (int) &SMP_prvspace[0]; gdt_segs[GPROC0_SEL].ssd_base = (int) &SMP_prvspace[0].globaldata.gd_common_tss; SMP_prvspace[0].globaldata.gd_prvspace = &SMP_prvspace[0].globaldata; #else gdt_segs[GPRIV_SEL].ssd_limit = i386_btop(sizeof(struct globaldata)) - 1; gdt_segs[GPRIV_SEL].ssd_base = (int) &__globaldata; gdt_segs[GPROC0_SEL].ssd_base = (int) &__globaldata.gd_common_tss; __globaldata.gd_prvspace = &__globaldata; #endif for (x = 0; x < NGDT; x++) { #ifdef BDE_DEBUGGER /* avoid overwriting db entries with APM ones */ if (x >= GAPMCODE32_SEL && x <= GAPMDATA_SEL) continue; #endif ssdtosd(&gdt_segs[x], &gdt[x].sd); } r_gdt.rd_limit = NGDT * sizeof(gdt[0]) - 1; r_gdt.rd_base = (int) gdt; lgdt(&r_gdt); /* setup curproc so that mutexes work */ PCPU_SET(curproc, &proc0); PCPU_SET(spinlocks, NULL); LIST_INIT(&proc0.p_contested); mtx_init(&sched_lock, "sched lock", MTX_SPIN | MTX_RECURSE); #ifdef SMP /* * Interrupts can happen very early, so initialize imen_mtx here, rather * than in init_locks(). */ mtx_init(&imen_mtx, "imen", MTX_SPIN); #endif /* * Giant is used early for at least debugger traps and unexpected traps. */ mtx_init(&Giant, "Giant", MTX_DEF | MTX_RECURSE); mtx_init(&proc0.p_mtx, "process lock", MTX_DEF); mtx_lock(&Giant); /* make ldt memory segments */ /* * The data segment limit must not cover the user area because we * don't want the user area to be writable in copyout() etc. (page * level protection is lost in kernel mode on 386's). Also, we * don't want the user area to be writable directly (page level * protection of the user area is not available on 486's with * CR0_WP set, because there is no user-read/kernel-write mode). * * XXX - VM_MAXUSER_ADDRESS is an end address, not a max. And it * should be spelled ...MAX_USER... */ #define VM_END_USER_RW_ADDRESS VM_MAXUSER_ADDRESS /* * The code segment limit has to cover the user area until we move * the signal trampoline out of the user area. This is safe because * the code segment cannot be written to directly. */ #define VM_END_USER_R_ADDRESS (VM_END_USER_RW_ADDRESS + UPAGES * PAGE_SIZE) ldt_segs[LUCODE_SEL].ssd_limit = i386_btop(VM_END_USER_R_ADDRESS) - 1; ldt_segs[LUDATA_SEL].ssd_limit = i386_btop(VM_END_USER_RW_ADDRESS) - 1; for (x = 0; x < sizeof ldt_segs / sizeof ldt_segs[0]; x++) ssdtosd(&ldt_segs[x], &ldt[x].sd); _default_ldt = GSEL(GLDT_SEL, SEL_KPL); lldt(_default_ldt); PCPU_SET(currentldt, _default_ldt); /* exceptions */ for (x = 0; x < NIDT; x++) setidt(x, &IDTVEC(rsvd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(0, &IDTVEC(div), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(1, &IDTVEC(dbg), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(2, &IDTVEC(nmi), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(3, &IDTVEC(bpt), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(4, &IDTVEC(ofl), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(5, &IDTVEC(bnd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(6, &IDTVEC(ill), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(7, &IDTVEC(dna), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(8, 0, SDT_SYSTASKGT, SEL_KPL, GSEL(GPANIC_SEL, SEL_KPL)); setidt(9, &IDTVEC(fpusegm), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(10, &IDTVEC(tss), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(11, &IDTVEC(missing), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(12, &IDTVEC(stk), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(13, &IDTVEC(prot), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(14, &IDTVEC(page), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(15, &IDTVEC(rsvd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(16, &IDTVEC(fpu), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(17, &IDTVEC(align), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(18, &IDTVEC(mchk), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(0x80, &IDTVEC(int0x80_syscall), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); r_idt.rd_limit = sizeof(idt0) - 1; r_idt.rd_base = (int) idt; lidt(&r_idt); /* * We need this mutex before the console probe. */ mtx_init(&clock_lock, "clk", MTX_SPIN | MTX_RECURSE); /* * Initialize the console before we print anything out. */ cninit(); #ifdef DEV_ISA isa_defaultirq(); #endif #ifdef DDB kdb_init(); if (boothowto & RB_KDB) Debugger("Boot flags requested debugger"); #endif finishidentcpu(); /* Final stage of CPU initialization */ setidt(6, &IDTVEC(ill), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(13, &IDTVEC(prot), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); initializecpu(); /* Initialize CPU registers */ /* make an initial tss so cpu can get interrupt stack on syscall! */ PCPU_SET(common_tss.tss_esp0, (int) proc0.p_addr + UPAGES*PAGE_SIZE - 16); PCPU_SET(common_tss.tss_ss0, GSEL(GDATA_SEL, SEL_KPL)); gsel_tss = GSEL(GPROC0_SEL, SEL_KPL); private_tss = 0; PCPU_SET(tss_gdt, &gdt[GPROC0_SEL].sd); PCPU_SET(common_tssd, *PCPU_GET(tss_gdt)); PCPU_SET(common_tss.tss_ioopt, (sizeof (struct i386tss)) << 16); ltr(gsel_tss); dblfault_tss.tss_esp = dblfault_tss.tss_esp0 = dblfault_tss.tss_esp1 = dblfault_tss.tss_esp2 = (int) &dblfault_stack[sizeof(dblfault_stack)]; dblfault_tss.tss_ss = dblfault_tss.tss_ss0 = dblfault_tss.tss_ss1 = dblfault_tss.tss_ss2 = GSEL(GDATA_SEL, SEL_KPL); dblfault_tss.tss_cr3 = (int)IdlePTD; dblfault_tss.tss_eip = (int) dblfault_handler; dblfault_tss.tss_eflags = PSL_KERNEL; dblfault_tss.tss_ds = dblfault_tss.tss_es = dblfault_tss.tss_gs = GSEL(GDATA_SEL, SEL_KPL); dblfault_tss.tss_fs = GSEL(GPRIV_SEL, SEL_KPL); dblfault_tss.tss_cs = GSEL(GCODE_SEL, SEL_KPL); dblfault_tss.tss_ldt = GSEL(GLDT_SEL, SEL_KPL); vm86_initialize(); getmemsize(first); /* now running on new page tables, configured,and u/iom is accessible */ /* Map the message buffer. */ for (off = 0; off < round_page(MSGBUF_SIZE); off += PAGE_SIZE) pmap_kenter((vm_offset_t)msgbufp + off, avail_end + off); msgbufinit(msgbufp, MSGBUF_SIZE); /* make a call gate to reenter kernel with */ gdp = &ldt[LSYS5CALLS_SEL].gd; x = (int) &IDTVEC(lcall_syscall); gdp->gd_looffset = x; gdp->gd_selector = GSEL(GCODE_SEL,SEL_KPL); gdp->gd_stkcpy = 1; gdp->gd_type = SDT_SYS386CGT; gdp->gd_dpl = SEL_UPL; gdp->gd_p = 1; gdp->gd_hioffset = x >> 16; /* XXX does this work? */ ldt[LBSDICALLS_SEL] = ldt[LSYS5CALLS_SEL]; ldt[LSOL26CALLS_SEL] = ldt[LSYS5CALLS_SEL]; /* transfer to user mode */ _ucodesel = LSEL(LUCODE_SEL, SEL_UPL); _udatasel = LSEL(LUDATA_SEL, SEL_UPL); /* setup proc 0's pcb */ proc0.p_addr->u_pcb.pcb_flags = 0; proc0.p_addr->u_pcb.pcb_cr3 = (int)IdlePTD; proc0.p_addr->u_pcb.pcb_ext = 0; proc0.p_md.md_regs = &proc0_tf; } #if defined(I586_CPU) && !defined(NO_F00F_HACK) static void f00f_hack(void *unused); SYSINIT(f00f_hack, SI_SUB_INTRINSIC, SI_ORDER_FIRST, f00f_hack, NULL); static void f00f_hack(void *unused) { struct gate_descriptor *new_idt; #ifndef SMP struct region_descriptor r_idt; #endif vm_offset_t tmp; if (!has_f00f_bug) return; printf("Intel Pentium detected, installing workaround for F00F bug\n"); r_idt.rd_limit = sizeof(idt0) - 1; tmp = kmem_alloc(kernel_map, PAGE_SIZE * 2); if (tmp == 0) panic("kmem_alloc returned 0"); if (((unsigned int)tmp & (PAGE_SIZE-1)) != 0) panic("kmem_alloc returned non-page-aligned memory"); /* Put the first seven entries in the lower page */ new_idt = (struct gate_descriptor*)(tmp + PAGE_SIZE - (7*8)); bcopy(idt, new_idt, sizeof(idt0)); r_idt.rd_base = (int)new_idt; lidt(&r_idt); idt = new_idt; mtx_lock(&vm_mtx); if (vm_map_protect(kernel_map, tmp, tmp + PAGE_SIZE, VM_PROT_READ, FALSE) != KERN_SUCCESS) panic("vm_map_protect failed"); mtx_unlock(&vm_mtx); return; } #endif /* defined(I586_CPU) && !NO_F00F_HACK */ int ptrace_set_pc(p, addr) struct proc *p; unsigned long addr; { p->p_md.md_regs->tf_eip = addr; return (0); } int ptrace_single_step(p) struct proc *p; { p->p_md.md_regs->tf_eflags |= PSL_T; return (0); } int ptrace_read_u_check(p, addr, len) struct proc *p; vm_offset_t addr; size_t len; { vm_offset_t gap; if ((vm_offset_t) (addr + len) < addr) return EPERM; if ((vm_offset_t) (addr + len) <= sizeof(struct user)) return 0; gap = (char *) p->p_md.md_regs - (char *) p->p_addr; if ((vm_offset_t) addr < gap) return EPERM; if ((vm_offset_t) (addr + len) <= (vm_offset_t) (gap + sizeof(struct trapframe))) return 0; return EPERM; } int ptrace_write_u(p, off, data) struct proc *p; vm_offset_t off; long data; { struct trapframe frame_copy; vm_offset_t min; struct trapframe *tp; /* * Privileged kernel state is scattered all over the user area. * Only allow write access to parts of regs and to fpregs. */ min = (char *)p->p_md.md_regs - (char *)p->p_addr; if (off >= min && off <= min + sizeof(struct trapframe) - sizeof(int)) { tp = p->p_md.md_regs; frame_copy = *tp; *(int *)((char *)&frame_copy + (off - min)) = data; if (!EFL_SECURE(frame_copy.tf_eflags, tp->tf_eflags) || !CS_SECURE(frame_copy.tf_cs)) return (EINVAL); *(int*)((char *)p->p_addr + off) = data; return (0); } min = offsetof(struct user, u_pcb) + offsetof(struct pcb, pcb_savefpu); if (off >= min && off <= min + sizeof(struct save87) - sizeof(int)) { *(int*)((char *)p->p_addr + off) = data; return (0); } return (EFAULT); } int fill_regs(p, regs) struct proc *p; struct reg *regs; { struct pcb *pcb; struct trapframe *tp; tp = p->p_md.md_regs; regs->r_fs = tp->tf_fs; regs->r_es = tp->tf_es; regs->r_ds = tp->tf_ds; regs->r_edi = tp->tf_edi; regs->r_esi = tp->tf_esi; regs->r_ebp = tp->tf_ebp; regs->r_ebx = tp->tf_ebx; regs->r_edx = tp->tf_edx; regs->r_ecx = tp->tf_ecx; regs->r_eax = tp->tf_eax; regs->r_eip = tp->tf_eip; regs->r_cs = tp->tf_cs; regs->r_eflags = tp->tf_eflags; regs->r_esp = tp->tf_esp; regs->r_ss = tp->tf_ss; pcb = &p->p_addr->u_pcb; regs->r_gs = pcb->pcb_gs; return (0); } int set_regs(p, regs) struct proc *p; struct reg *regs; { struct pcb *pcb; struct trapframe *tp; tp = p->p_md.md_regs; if (!EFL_SECURE(regs->r_eflags, tp->tf_eflags) || !CS_SECURE(regs->r_cs)) return (EINVAL); tp->tf_fs = regs->r_fs; tp->tf_es = regs->r_es; tp->tf_ds = regs->r_ds; tp->tf_edi = regs->r_edi; tp->tf_esi = regs->r_esi; tp->tf_ebp = regs->r_ebp; tp->tf_ebx = regs->r_ebx; tp->tf_edx = regs->r_edx; tp->tf_ecx = regs->r_ecx; tp->tf_eax = regs->r_eax; tp->tf_eip = regs->r_eip; tp->tf_cs = regs->r_cs; tp->tf_eflags = regs->r_eflags; tp->tf_esp = regs->r_esp; tp->tf_ss = regs->r_ss; pcb = &p->p_addr->u_pcb; pcb->pcb_gs = regs->r_gs; return (0); } int fill_fpregs(p, fpregs) struct proc *p; struct fpreg *fpregs; { bcopy(&p->p_addr->u_pcb.pcb_savefpu, fpregs, sizeof *fpregs); return (0); } int set_fpregs(p, fpregs) struct proc *p; struct fpreg *fpregs; { bcopy(fpregs, &p->p_addr->u_pcb.pcb_savefpu, sizeof *fpregs); return (0); } int fill_dbregs(p, dbregs) struct proc *p; struct dbreg *dbregs; { struct pcb *pcb; pcb = &p->p_addr->u_pcb; dbregs->dr0 = pcb->pcb_dr0; dbregs->dr1 = pcb->pcb_dr1; dbregs->dr2 = pcb->pcb_dr2; dbregs->dr3 = pcb->pcb_dr3; dbregs->dr4 = 0; dbregs->dr5 = 0; dbregs->dr6 = pcb->pcb_dr6; dbregs->dr7 = pcb->pcb_dr7; return (0); } int set_dbregs(p, dbregs) struct proc *p; struct dbreg *dbregs; { struct pcb *pcb; int i; u_int32_t mask1, mask2; /* * Don't let an illegal value for dr7 get set. Specifically, * check for undefined settings. Setting these bit patterns * result in undefined behaviour and can lead to an unexpected * TRCTRAP. */ for (i = 0, mask1 = 0x3<<16, mask2 = 0x2<<16; i < 8; i++, mask1 <<= 2, mask2 <<= 2) if ((dbregs->dr7 & mask1) == mask2) return (EINVAL); if (dbregs->dr7 & 0x0000fc00) return (EINVAL); pcb = &p->p_addr->u_pcb; /* * Don't let a process set a breakpoint that is not within the * process's address space. If a process could do this, it * could halt the system by setting a breakpoint in the kernel * (if ddb was enabled). Thus, we need to check to make sure * that no breakpoints are being enabled for addresses outside * process's address space, unless, perhaps, we were called by * uid 0. * * XXX - what about when the watched area of the user's * address space is written into from within the kernel * ... wouldn't that still cause a breakpoint to be generated * from within kernel mode? */ if (suser(p) != 0) { if (dbregs->dr7 & 0x3) { /* dr0 is enabled */ if (dbregs->dr0 >= VM_MAXUSER_ADDRESS) return (EINVAL); } if (dbregs->dr7 & (0x3<<2)) { /* dr1 is enabled */ if (dbregs->dr1 >= VM_MAXUSER_ADDRESS) return (EINVAL); } if (dbregs->dr7 & (0x3<<4)) { /* dr2 is enabled */ if (dbregs->dr2 >= VM_MAXUSER_ADDRESS) return (EINVAL); } if (dbregs->dr7 & (0x3<<6)) { /* dr3 is enabled */ if (dbregs->dr3 >= VM_MAXUSER_ADDRESS) return (EINVAL); } } pcb->pcb_dr0 = dbregs->dr0; pcb->pcb_dr1 = dbregs->dr1; pcb->pcb_dr2 = dbregs->dr2; pcb->pcb_dr3 = dbregs->dr3; pcb->pcb_dr6 = dbregs->dr6; pcb->pcb_dr7 = dbregs->dr7; pcb->pcb_flags |= PCB_DBREGS; return (0); } /* * Return > 0 if a hardware breakpoint has been hit, and the * breakpoint was in user space. Return 0, otherwise. */ int user_dbreg_trap(void) { u_int32_t dr7, dr6; /* debug registers dr6 and dr7 */ u_int32_t bp; /* breakpoint bits extracted from dr6 */ int nbp; /* number of breakpoints that triggered */ caddr_t addr[4]; /* breakpoint addresses */ int i; dr7 = rdr7(); if ((dr7 & 0x000000ff) == 0) { /* * all GE and LE bits in the dr7 register are zero, * thus the trap couldn't have been caused by the * hardware debug registers */ return 0; } nbp = 0; dr6 = rdr6(); bp = dr6 & 0x0000000f; if (!bp) { /* * None of the breakpoint bits are set meaning this * trap was not caused by any of the debug registers */ return 0; } /* * at least one of the breakpoints were hit, check to see * which ones and if any of them are user space addresses */ if (bp & 0x01) { addr[nbp++] = (caddr_t)rdr0(); } if (bp & 0x02) { addr[nbp++] = (caddr_t)rdr1(); } if (bp & 0x04) { addr[nbp++] = (caddr_t)rdr2(); } if (bp & 0x08) { addr[nbp++] = (caddr_t)rdr3(); } for (i=0; i /* * Determine the size of the transfer, and make sure it is * within the boundaries of the partition. Adjust transfer * if needed, and signal errors or early completion. */ int bounds_check_with_label(struct bio *bp, struct disklabel *lp, int wlabel) { struct partition *p = lp->d_partitions + dkpart(bp->bio_dev); int labelsect = lp->d_partitions[0].p_offset; int maxsz = p->p_size, sz = (bp->bio_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT; /* overwriting disk label ? */ /* XXX should also protect bootstrap in first 8K */ if (bp->bio_blkno + p->p_offset <= LABELSECTOR + labelsect && #if LABELSECTOR != 0 bp->bio_blkno + p->p_offset + sz > LABELSECTOR + labelsect && #endif (bp->bio_cmd == BIO_WRITE) && wlabel == 0) { bp->bio_error = EROFS; goto bad; } #if defined(DOSBBSECTOR) && defined(notyet) /* overwriting master boot record? */ if (bp->bio_blkno + p->p_offset <= DOSBBSECTOR && (bp->bio_cmd == BIO_WRITE) && wlabel == 0) { bp->bio_error = EROFS; goto bad; } #endif /* beyond partition? */ if (bp->bio_blkno < 0 || bp->bio_blkno + sz > maxsz) { /* if exactly at end of disk, return an EOF */ if (bp->bio_blkno == maxsz) { bp->bio_resid = bp->bio_bcount; return(0); } /* or truncate if part of it fits */ sz = maxsz - bp->bio_blkno; if (sz <= 0) { bp->bio_error = EINVAL; goto bad; } bp->bio_bcount = sz << DEV_BSHIFT; } bp->bio_pblkno = bp->bio_blkno + p->p_offset; return(1); bad: bp->bio_flags |= BIO_ERROR; return(-1); } #ifdef DDB /* * Provide inb() and outb() as functions. They are normally only * available as macros calling inlined functions, thus cannot be * called inside DDB. * * The actual code is stolen from , and de-inlined. */ #undef inb #undef outb /* silence compiler warnings */ u_char inb(u_int); void outb(u_int, u_char); u_char inb(u_int port) { u_char data; /* * We use %%dx and not %1 here because i/o is done at %dx and not at * %edx, while gcc generates inferior code (movw instead of movl) * if we tell it to load (u_short) port. */ __asm __volatile("inb %%dx,%0" : "=a" (data) : "d" (port)); return (data); } void outb(u_int port, u_char data) { u_char al; /* * Use an unnecessary assignment to help gcc's register allocator. * This make a large difference for gcc-1.40 and a tiny difference * for gcc-2.6.0. For gcc-1.40, al had to be ``asm("ax")'' for * best results. gcc-2.6.0 can't handle this. */ al = data; __asm __volatile("outb %0,%%dx" : : "a" (al), "d" (port)); } #endif /* DDB */ Index: head/sys/amd64/conf/GENERIC =================================================================== --- head/sys/amd64/conf/GENERIC (revision 78134) +++ head/sys/amd64/conf/GENERIC (revision 78135) @@ -1,236 +1,236 @@ # # GENERIC -- Generic kernel configuration file for FreeBSD/i386 # # For more information on this file, please read the handbook section on # Kernel Configuration Files: # # http://www.FreeBSD.org/handbook/kernelconfig-config.html # # The handbook is also available locally in /usr/share/doc/handbook # if you've installed the doc distribution, otherwise always see the # FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the # latest information. # # An exhaustive list of options and more detailed explanations of the # device lines is also present in the NOTES configuration file. If you are # in doubt as to the purpose or necessity of a line, check first in NOTES. # # $FreeBSD$ machine i386 cpu I486_CPU cpu I586_CPU cpu I686_CPU ident GENERIC maxusers 32 #To statically compile in device wiring instead of /boot/device.hints #hints "GENERIC.hints" #Default places to look for devices. makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols options MATH_EMULATE #Support for x87 emulation options INET #InterNETworking options INET6 #IPv6 communications protocols options FFS #Berkeley Fast Filesystem options SOFTUPDATES #Enable FFS soft updates support options MD_ROOT #MD is a potential root device options NFS #Network Filesystem options NFS_ROOT #NFS usable as root device, NFS required options MSDOSFS #MSDOS Filesystem options CD9660 #ISO 9660 Filesystem options PROCFS #Process filesystem options COMPAT_43 #Compatible with BSD 4.3 [KEEP THIS!] options SCSI_DELAY=15000 #Delay (in ms) before probing SCSI options UCONSOLE #Allow users to grab the console -options USERCONFIG #boot -c editor -options VISUAL_USERCONFIG #visual boot -c editor +#options USERCONFIG #boot -c editor +#options VISUAL_USERCONFIG #visual boot -c editor options KTRACE #ktrace(1) support options SYSVSHM #SYSV-style shared memory options SYSVMSG #SYSV-style message queues options SYSVSEM #SYSV-style semaphores options P1003_1B #Posix P1003_1B real-time extensions options _KPOSIX_PRIORITY_SCHEDULING options KBD_INSTALL_CDEV # install a CDEV entry in /dev # Debugging for use in -current options DDB options INVARIANTS options INVARIANT_SUPPORT options WITNESS # To make an SMP kernel, the next two are needed #options SMP # Symmetric MultiProcessor Kernel #options APIC_IO # Symmetric (APIC) I/O device isa device eisa device pci # Floppy drives device fdc # ATA and ATAPI devices device ata device atadisk # ATA disk drives device atapicd # ATAPI CDROM drives device atapifd # ATAPI floppy drives device atapist # ATAPI tape drives options ATA_STATIC_ID #Static device numbering # SCSI Controllers device ahb # EISA AHA1742 family device ahc # AHA2940 and onboard AIC7xxx devices device amd # AMD 53C974 (Tekram DC-390(T)) device isp # Qlogic family #device ncr # NCR/Symbios Logic device sym # NCR/Symbios Logic (newer chipsets + those of `ncr') device adv # Advansys SCSI adapters device adw # Advansys wide SCSI adapters device aha # Adaptec 154x SCSI adapters device aic # Adaptec 15[012]x SCSI adapters, AIC-6[23]60. device bt # Buslogic/Mylex MultiMaster SCSI adapters device ncv # NCR 53C500 device nsp # Workbit Ninja SCSI-3 device stg # TMC 18C30/18C50 # RAID controllers interfaced to the SCSI subsystem device asr # DPT SmartRAID V, VI and Adaptec SCSI RAID device dpt # DPT Smartcache III, IV - See NOTES for options! device mly # Mylex AcceleRAID/eXtremeRAID # SCSI peripherals device scbus # SCSI bus (required) device da # Direct Access (disks) device sa # Sequential Access (tape etc) device cd # CD device pass # Passthrough device (direct SCSI access) # RAID controllers device aac # Adaptec FSA RAID device amr # AMI MegaRAID device ida # Compaq Smart RAID device mlx # Mylex DAC960 family device twe # 3ware ATA RAID # atkbdc0 controls both the keyboard and the PS/2 mouse device atkbdc 1 # At keyboard controller device atkbd # at keyboard device psm # psm mouse device vga # VGA screen # splash screen/screen saver device splash # syscons is the default console driver, resembling an SCO console device sc 1 # Enable this for the pcvt (VT220 compatible) console driver #device vt #options XSERVER # support for X server on a vt console #options FAT_CURSOR # start with block cursor # If you have a ThinkPAD, uncomment this along with the rest of the PCVT lines #options PCVT_SCANSET=2 # IBM keyboards are non-std # Floating point support - do not disable. device npx # Power management support (see NOTES for more options) device apm # Add suspend/resume support for the i8254. device pmtimer # PCCARD (PCMCIA) support device card # pccard bus device pcic # PCMCIA bridge # Serial (COM) ports device sio # 8250, 16[45]50 based serial ports # Parallel port device ppc device ppbus # Parallel port bus (required) device lpt # Printer device plip # TCP/IP over parallel device ppi # Parallel port interface device #device vpo # Requires scbus and da # PCI Ethernet NICs. device de # DEC/Intel DC21x4x (``Tulip'') device vx # 3Com 3c590, 3c595 (``Vortex'') # PCI Ethernet NICs that use the common MII bus controller code. # NOTE: Be sure to keep the 'device miibus' line in order to use these NICs! device miibus # MII bus support device dc # DEC/Intel 21143 and various workalikes device fxp # Intel EtherExpress PRO/100B (82557, 82558) device pcn # AMD Am79C79x PCI 10/100 NICs device rl # RealTek 8129/8139 device sf # Adaptec AIC-6915 (``Starfire'') device sis # Silicon Integrated Systems SiS 900/SiS 7016 device ste # Sundance ST201 (D-Link DFE-550TX) device tl # Texas Instruments ThunderLAN device tx # SMC EtherPower II (83c170 ``EPIC'') device vr # VIA Rhine, Rhine II device wb # Winbond W89C840F device wx # Intel Gigabit Ethernet Card (``Wiseman'') device xl # 3Com 3c90x (``Boomerang'', ``Cyclone'') # ISA Ethernet NICs. pccard nics included. device cs # Crystal Semiconductor CS89x0 NIC # 'device ed' requires 'device miibus' device ed # NE[12]000, SMC Ultra, 3c503, DS8390 cards device ex # Intel EtherExpress Pro/10 and Pro/10+ device ep # Etherlink III based cards device fe # Fujitsu MB8696x based cards device sn # SMC's 9000 series of ethernet chips device xe # Xircom pccard ethernet # The probe order of these is presently determined by i386/isa/isa_compat.c. #device ie #device le device lnc # Wireless NIC cards device an # Aironet 4500/4800 802.11 wireless NICs. device awi # BayStack 660 and others device wi # WaveLAN/IEEE 802.11 wireless NICs. #device wl # Older non 802.11 Wavelan wireless NIC. # Pseudo devices - the number indicates how many units to allocate. device random # Entropy device device loop # Network loopback device ether # Ethernet support device sl # Kernel SLIP device ppp 1 # Kernel PPP device tun # Packet tunnel. device pty # Pseudo-ttys (telnet etc) device md # Memory "disks" device gif 4 # IPv6 and IPv4 tunneling device faith 1 # IPv6-to-IPv4 relaying (translation) # The `bpf' device enables the Berkeley Packet Filter. # Be aware of the administrative consequences of enabling this! device bpf # Berkeley packet filter # USB support device uhci # UHCI PCI->USB interface device ohci # OHCI PCI->USB interface device usb # USB Bus (required) #device udbp # USB Double Bulk Pipe devices device ugen # Generic device uhid # "Human Interface Devices" device ukbd # Keyboard device ulpt # Printer device umass # Disks/Mass storage - Requires scbus and da device ums # Mouse device urio # Diamond Rio 500 MP3 player device uscanner # Scanners # USB Ethernet, requires mii device aue # ADMtek USB ethernet device cue # CATC USB ethernet device kue # Kawasaki LSI USB ethernet Index: head/sys/cam/cam_periph.c =================================================================== --- head/sys/cam/cam_periph.c (revision 78134) +++ head/sys/cam/cam_periph.c (revision 78135) @@ -1,1664 +1,1665 @@ /* * Common functions for CAM "type" (peripheral) drivers. * * Copyright (c) 1997, 1998 Justin T. Gibbs. * Copyright (c) 1997, 1998, 1999, 2000 Kenneth D. Merry. * All rights reserved. * * 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, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static u_int camperiphnextunit(struct periph_driver *p_drv, u_int newunit, int wired, path_id_t pathid, target_id_t target, lun_id_t lun); static u_int camperiphunit(struct periph_driver *p_drv, path_id_t pathid, target_id_t target, lun_id_t lun); static void camperiphdone(struct cam_periph *periph, union ccb *done_ccb); static void camperiphfree(struct cam_periph *periph); static int camperiphscsistatuserror(union ccb *ccb, cam_flags camflags, u_int32_t sense_flags, union ccb *save_ccb, int *openings, u_int32_t *relsim_flags, u_int32_t *timeout); static int camperiphscsisenseerror(union ccb *ccb, cam_flags camflags, u_int32_t sense_flags, union ccb *save_ccb, int *openings, u_int32_t *relsim_flags, u_int32_t *timeout); static int nperiph_drivers; struct periph_driver **periph_drivers; void periphdriver_register(void *data) { struct periph_driver **newdrivers, **old; int ndrivers; ndrivers = nperiph_drivers + 2; newdrivers = malloc(sizeof(*newdrivers) * ndrivers, M_TEMP, M_WAITOK); if (periph_drivers) bcopy(periph_drivers, newdrivers, sizeof(*newdrivers) * nperiph_drivers); newdrivers[nperiph_drivers] = (struct periph_driver *)data; newdrivers[nperiph_drivers + 1] = NULL; old = periph_drivers; periph_drivers = newdrivers; if (old) free(old, M_TEMP); nperiph_drivers++; } cam_status cam_periph_alloc(periph_ctor_t *periph_ctor, periph_oninv_t *periph_oninvalidate, periph_dtor_t *periph_dtor, periph_start_t *periph_start, char *name, cam_periph_type type, struct cam_path *path, ac_callback_t *ac_callback, ac_code code, void *arg) { struct periph_driver **p_drv; struct cam_periph *periph; struct cam_periph *cur_periph; path_id_t path_id; target_id_t target_id; lun_id_t lun_id; cam_status status; u_int init_level; int s; init_level = 0; /* * Handle Hot-Plug scenarios. If there is already a peripheral * of our type assigned to this path, we are likely waiting for * final close on an old, invalidated, peripheral. If this is * the case, queue up a deferred call to the peripheral's async * handler. If it looks like a mistaken re-alloation, complain. */ if ((periph = cam_periph_find(path, name)) != NULL) { if ((periph->flags & CAM_PERIPH_INVALID) != 0 && (periph->flags & CAM_PERIPH_NEW_DEV_FOUND) == 0) { periph->flags |= CAM_PERIPH_NEW_DEV_FOUND; periph->deferred_callback = ac_callback; periph->deferred_ac = code; return (CAM_REQ_INPROG); } else { printf("cam_periph_alloc: attempt to re-allocate " "valid device %s%d rejected\n", periph->periph_name, periph->unit_number); } return (CAM_REQ_INVALID); } periph = (struct cam_periph *)malloc(sizeof(*periph), M_DEVBUF, M_NOWAIT); if (periph == NULL) return (CAM_RESRC_UNAVAIL); init_level++; for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { if (strcmp((*p_drv)->driver_name, name) == 0) break; } path_id = xpt_path_path_id(path); target_id = xpt_path_target_id(path); lun_id = xpt_path_lun_id(path); bzero(periph, sizeof(*periph)); cam_init_pinfo(&periph->pinfo); periph->periph_start = periph_start; periph->periph_dtor = periph_dtor; periph->periph_oninval = periph_oninvalidate; periph->type = type; periph->periph_name = name; periph->unit_number = camperiphunit(*p_drv, path_id, target_id, lun_id); periph->immediate_priority = CAM_PRIORITY_NONE; periph->refcount = 0; SLIST_INIT(&periph->ccb_list); status = xpt_create_path(&path, periph, path_id, target_id, lun_id); if (status != CAM_REQ_CMP) goto failure; periph->path = path; init_level++; status = xpt_add_periph(periph); if (status != CAM_REQ_CMP) goto failure; s = splsoftcam(); cur_periph = TAILQ_FIRST(&(*p_drv)->units); while (cur_periph != NULL && cur_periph->unit_number < periph->unit_number) cur_periph = TAILQ_NEXT(cur_periph, unit_links); if (cur_periph != NULL) TAILQ_INSERT_BEFORE(cur_periph, periph, unit_links); else { TAILQ_INSERT_TAIL(&(*p_drv)->units, periph, unit_links); (*p_drv)->generation++; } splx(s); init_level++; status = periph_ctor(periph, arg); if (status == CAM_REQ_CMP) init_level++; failure: switch (init_level) { case 4: /* Initialized successfully */ break; case 3: s = splsoftcam(); TAILQ_REMOVE(&(*p_drv)->units, periph, unit_links); splx(s); xpt_remove_periph(periph); case 2: xpt_free_path(periph->path); case 1: free(periph, M_DEVBUF); case 0: /* No cleanup to perform. */ break; default: panic("cam_periph_alloc: Unkown init level"); } return(status); } /* * Find a peripheral structure with the specified path, target, lun, * and (optionally) type. If the name is NULL, this function will return * the first peripheral driver that matches the specified path. */ struct cam_periph * cam_periph_find(struct cam_path *path, char *name) { struct periph_driver **p_drv; struct cam_periph *periph; int s; for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { if (name != NULL && (strcmp((*p_drv)->driver_name, name) != 0)) continue; s = splsoftcam(); TAILQ_FOREACH(periph, &(*p_drv)->units, unit_links) { if (xpt_path_comp(periph->path, path) == 0) { splx(s); return(periph); } } splx(s); if (name != NULL) return(NULL); } return(NULL); } cam_status cam_periph_acquire(struct cam_periph *periph) { int s; if (periph == NULL) return(CAM_REQ_CMP_ERR); s = splsoftcam(); periph->refcount++; splx(s); return(CAM_REQ_CMP); } void cam_periph_release(struct cam_periph *periph) { int s; if (periph == NULL) return; s = splsoftcam(); if ((--periph->refcount == 0) && (periph->flags & CAM_PERIPH_INVALID)) { camperiphfree(periph); } splx(s); } /* * Look for the next unit number that is not currently in use for this * peripheral type starting at "newunit". Also exclude unit numbers that * are reserved by for future "hardwiring" unless we already know that this * is a potential wired device. Only assume that the device is "wired" the * first time through the loop since after that we'll be looking at unit * numbers that did not match a wiring entry. */ static u_int camperiphnextunit(struct periph_driver *p_drv, u_int newunit, int wired, path_id_t pathid, target_id_t target, lun_id_t lun) { struct cam_periph *periph; - char *periph_name, *strval; + char *periph_name; int s; - int i, val, dunit; - const char *dname; + int i, val, dunit, r; + const char *dname, *strval; s = splsoftcam(); periph_name = p_drv->driver_name; for (;;newunit++) { for (periph = TAILQ_FIRST(&p_drv->units); periph != NULL && periph->unit_number != newunit; periph = TAILQ_NEXT(periph, unit_links)) ; if (periph != NULL && periph->unit_number == newunit) { if (wired != 0) { xpt_print_path(periph->path); printf("Duplicate Wired Device entry!\n"); xpt_print_path(periph->path); printf("Second device (%s device at scbus%d " "target %d lun %d) will not be wired\n", periph_name, pathid, target, lun); wired = 0; } continue; } if (wired) break; /* * Don't match entries like "da 4" as a wired down * device, but do match entries like "da 4 target 5" * or even "da 4 scbus 1". */ - i = -1; - while ((i = resource_locate(i, periph_name)) != -1) { - dname = resource_query_name(i); - dunit = resource_query_unit(i); + i = 0; + dname = periph_name; + for (;;) { + r = resource_find_dev(&i, dname, &dunit, NULL, NULL); + if (r != 0) + break; /* if no "target" and no specific scbus, skip */ if (resource_int_value(dname, dunit, "target", &val) && (resource_string_value(dname, dunit, "at",&strval)|| strcmp(strval, "scbus") == 0)) continue; if (newunit == dunit) break; } - if (i == -1) + if (r != 0) break; } splx(s); return (newunit); } static u_int camperiphunit(struct periph_driver *p_drv, path_id_t pathid, target_id_t target, lun_id_t lun) { u_int unit; int hit, i, val, dunit; - const char *dname; - char pathbuf[32], *strval, *periph_name; + const char *dname, *strval; + char pathbuf[32], *periph_name; unit = 0; hit = 0; periph_name = p_drv->driver_name; snprintf(pathbuf, sizeof(pathbuf), "scbus%d", pathid); - i = -1; - while ((i = resource_locate(i, periph_name)) != -1) { - dname = resource_query_name(i); - dunit = resource_query_unit(i); + i = 0; + dname = periph_name; + while ((resource_find_dev(&i, dname, &dunit, NULL, NULL)) == 0) { if (resource_string_value(dname, dunit, "at", &strval) == 0) { if (strcmp(strval, pathbuf) != 0) continue; hit++; } if (resource_int_value(dname, dunit, "target", &val) == 0) { if (val != target) continue; hit++; } if (resource_int_value(dname, dunit, "lun", &val) == 0) { if (val != lun) continue; hit++; } if (hit != 0) { unit = dunit; break; } } /* * Either start from 0 looking for the next unit or from * the unit number given in the resource config. This way, * if we have wildcard matches, we don't return the same * unit number twice. */ unit = camperiphnextunit(p_drv, unit, /*wired*/hit, pathid, target, lun); return (unit); } void cam_periph_invalidate(struct cam_periph *periph) { int s; s = splsoftcam(); /* * We only call this routine the first time a peripheral is * invalidated. The oninvalidate() routine is always called at * splsoftcam(). */ if (((periph->flags & CAM_PERIPH_INVALID) == 0) && (periph->periph_oninval != NULL)) periph->periph_oninval(periph); periph->flags |= CAM_PERIPH_INVALID; periph->flags &= ~CAM_PERIPH_NEW_DEV_FOUND; if (periph->refcount == 0) camperiphfree(periph); else if (periph->refcount < 0) printf("cam_invalidate_periph: refcount < 0!!\n"); splx(s); } static void camperiphfree(struct cam_periph *periph) { int s; struct periph_driver **p_drv; for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) { if (strcmp((*p_drv)->driver_name, periph->periph_name) == 0) break; } if (periph->periph_dtor != NULL) periph->periph_dtor(periph); s = splsoftcam(); TAILQ_REMOVE(&(*p_drv)->units, periph, unit_links); (*p_drv)->generation++; splx(s); xpt_remove_periph(periph); if (periph->flags & CAM_PERIPH_NEW_DEV_FOUND) { union ccb ccb; void *arg; switch (periph->deferred_ac) { case AC_FOUND_DEVICE: ccb.ccb_h.func_code = XPT_GDEV_TYPE; xpt_setup_ccb(&ccb.ccb_h, periph->path, /*priority*/ 1); xpt_action(&ccb); arg = &ccb; break; case AC_PATH_REGISTERED: ccb.ccb_h.func_code = XPT_PATH_INQ; xpt_setup_ccb(&ccb.ccb_h, periph->path, /*priority*/ 1); xpt_action(&ccb); arg = &ccb; break; default: arg = NULL; break; } periph->deferred_callback(NULL, periph->deferred_ac, periph->path, arg); } xpt_free_path(periph->path); free(periph, M_DEVBUF); } /* * Wait interruptibly for an exclusive lock. */ int cam_periph_lock(struct cam_periph *periph, int priority) { int error; /* * Increment the reference count on the peripheral * while we wait for our lock attempt to succeed * to ensure the peripheral doesn't dissappear * out from under us while we sleep. */ if (cam_periph_acquire(periph) != CAM_REQ_CMP) return(ENXIO); while ((periph->flags & CAM_PERIPH_LOCKED) != 0) { periph->flags |= CAM_PERIPH_LOCK_WANTED; if ((error = tsleep(periph, priority, "caplck", 0)) != 0) { cam_periph_release(periph); return error; } } periph->flags |= CAM_PERIPH_LOCKED; return 0; } /* * Unlock and wake up any waiters. */ void cam_periph_unlock(struct cam_periph *periph) { periph->flags &= ~CAM_PERIPH_LOCKED; if ((periph->flags & CAM_PERIPH_LOCK_WANTED) != 0) { periph->flags &= ~CAM_PERIPH_LOCK_WANTED; wakeup(periph); } cam_periph_release(periph); } /* * Map user virtual pointers into kernel virtual address space, so we can * access the memory. This won't work on physical pointers, for now it's * up to the caller to check for that. (XXX KDM -- should we do that here * instead?) This also only works for up to MAXPHYS memory. Since we use * buffers to map stuff in and out, we're limited to the buffer size. */ int cam_periph_mapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo) { int numbufs, i; int flags[CAM_PERIPH_MAXMAPS]; u_int8_t **data_ptrs[CAM_PERIPH_MAXMAPS]; u_int32_t lengths[CAM_PERIPH_MAXMAPS]; u_int32_t dirs[CAM_PERIPH_MAXMAPS]; switch(ccb->ccb_h.func_code) { case XPT_DEV_MATCH: if (ccb->cdm.match_buf_len == 0) { printf("cam_periph_mapmem: invalid match buffer " "length 0\n"); return(EINVAL); } if (ccb->cdm.pattern_buf_len > 0) { data_ptrs[0] = (u_int8_t **)&ccb->cdm.patterns; lengths[0] = ccb->cdm.pattern_buf_len; dirs[0] = CAM_DIR_OUT; data_ptrs[1] = (u_int8_t **)&ccb->cdm.matches; lengths[1] = ccb->cdm.match_buf_len; dirs[1] = CAM_DIR_IN; numbufs = 2; } else { data_ptrs[0] = (u_int8_t **)&ccb->cdm.matches; lengths[0] = ccb->cdm.match_buf_len; dirs[0] = CAM_DIR_IN; numbufs = 1; } break; case XPT_SCSI_IO: case XPT_CONT_TARGET_IO: if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE) return(0); data_ptrs[0] = &ccb->csio.data_ptr; lengths[0] = ccb->csio.dxfer_len; dirs[0] = ccb->ccb_h.flags & CAM_DIR_MASK; numbufs = 1; break; default: return(EINVAL); break; /* NOTREACHED */ } /* * Check the transfer length and permissions first, so we don't * have to unmap any previously mapped buffers. */ for (i = 0; i < numbufs; i++) { flags[i] = 0; /* * The userland data pointer passed in may not be page * aligned. vmapbuf() truncates the address to a page * boundary, so if the address isn't page aligned, we'll * need enough space for the given transfer length, plus * whatever extra space is necessary to make it to the page * boundary. */ if ((lengths[i] + (((vm_offset_t)(*data_ptrs[i])) & PAGE_MASK)) > DFLTPHYS){ printf("cam_periph_mapmem: attempt to map %lu bytes, " "which is greater than DFLTPHYS(%d)\n", (long)(lengths[i] + (((vm_offset_t)(*data_ptrs[i])) & PAGE_MASK)), DFLTPHYS); return(E2BIG); } if (dirs[i] & CAM_DIR_OUT) { flags[i] = BIO_WRITE; if (!useracc(*data_ptrs[i], lengths[i], VM_PROT_READ)) { printf("cam_periph_mapmem: error, " "address %p, length %lu isn't " "user accessible for READ\n", (void *)*data_ptrs[i], (u_long)lengths[i]); return(EACCES); } } if (dirs[i] & CAM_DIR_IN) { flags[i] = BIO_READ; if (!useracc(*data_ptrs[i], lengths[i], VM_PROT_WRITE)) { printf("cam_periph_mapmem: error, " "address %p, length %lu isn't " "user accessible for WRITE\n", (void *)*data_ptrs[i], (u_long)lengths[i]); return(EACCES); } } } /* this keeps the current process from getting swapped */ /* * XXX KDM should I use P_NOSWAP instead? */ PHOLD(curproc); for (i = 0; i < numbufs; i++) { /* * Get the buffer. */ mapinfo->bp[i] = getpbuf(NULL); /* save the buffer's data address */ mapinfo->bp[i]->b_saveaddr = mapinfo->bp[i]->b_data; /* put our pointer in the data slot */ mapinfo->bp[i]->b_data = *data_ptrs[i]; /* set the transfer length, we know it's < DFLTPHYS */ mapinfo->bp[i]->b_bufsize = lengths[i]; /* set the flags */ mapinfo->bp[i]->b_flags = B_PHYS; /* set the direction */ mapinfo->bp[i]->b_iocmd = flags[i]; /* map the buffer into kernel memory */ vmapbuf(mapinfo->bp[i]); /* set our pointer to the new mapped area */ *data_ptrs[i] = mapinfo->bp[i]->b_data; mapinfo->num_bufs_used++; } return(0); } /* * Unmap memory segments mapped into kernel virtual address space by * cam_periph_mapmem(). */ void cam_periph_unmapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo) { int numbufs, i; u_int8_t **data_ptrs[CAM_PERIPH_MAXMAPS]; if (mapinfo->num_bufs_used <= 0) { /* allow ourselves to be swapped once again */ PRELE(curproc); return; } switch (ccb->ccb_h.func_code) { case XPT_DEV_MATCH: numbufs = min(mapinfo->num_bufs_used, 2); if (numbufs == 1) { data_ptrs[0] = (u_int8_t **)&ccb->cdm.matches; } else { data_ptrs[0] = (u_int8_t **)&ccb->cdm.patterns; data_ptrs[1] = (u_int8_t **)&ccb->cdm.matches; } break; case XPT_SCSI_IO: case XPT_CONT_TARGET_IO: data_ptrs[0] = &ccb->csio.data_ptr; numbufs = min(mapinfo->num_bufs_used, 1); break; default: /* allow ourselves to be swapped once again */ PRELE(curproc); return; break; /* NOTREACHED */ } for (i = 0; i < numbufs; i++) { /* Set the user's pointer back to the original value */ *data_ptrs[i] = mapinfo->bp[i]->b_saveaddr; /* unmap the buffer */ vunmapbuf(mapinfo->bp[i]); /* clear the flags we set above */ mapinfo->bp[i]->b_flags &= ~B_PHYS; /* release the buffer */ relpbuf(mapinfo->bp[i], NULL); } /* allow ourselves to be swapped once again */ PRELE(curproc); } union ccb * cam_periph_getccb(struct cam_periph *periph, u_int32_t priority) { struct ccb_hdr *ccb_h; int s; CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdgetccb\n")); s = splsoftcam(); while (SLIST_FIRST(&periph->ccb_list) == NULL) { if (periph->immediate_priority > priority) periph->immediate_priority = priority; xpt_schedule(periph, priority); if ((SLIST_FIRST(&periph->ccb_list) != NULL) && (SLIST_FIRST(&periph->ccb_list)->pinfo.priority == priority)) break; tsleep(&periph->ccb_list, PRIBIO, "cgticb", 0); } ccb_h = SLIST_FIRST(&periph->ccb_list); SLIST_REMOVE_HEAD(&periph->ccb_list, periph_links.sle); splx(s); return ((union ccb *)ccb_h); } void cam_periph_ccbwait(union ccb *ccb) { int s; s = splsoftcam(); if ((ccb->ccb_h.pinfo.index != CAM_UNQUEUED_INDEX) || ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG)) tsleep(&ccb->ccb_h.cbfcnp, PRIBIO, "cbwait", 0); splx(s); } int cam_periph_ioctl(struct cam_periph *periph, int cmd, caddr_t addr, int (*error_routine)(union ccb *ccb, cam_flags camflags, u_int32_t sense_flags)) { union ccb *ccb; int error; int found; error = found = 0; switch(cmd){ case CAMGETPASSTHRU: ccb = cam_periph_getccb(periph, /* priority */ 1); xpt_setup_ccb(&ccb->ccb_h, ccb->ccb_h.path, /*priority*/1); ccb->ccb_h.func_code = XPT_GDEVLIST; /* * Basically, the point of this is that we go through * getting the list of devices, until we find a passthrough * device. In the current version of the CAM code, the * only way to determine what type of device we're dealing * with is by its name. */ while (found == 0) { ccb->cgdl.index = 0; ccb->cgdl.status = CAM_GDEVLIST_MORE_DEVS; while (ccb->cgdl.status == CAM_GDEVLIST_MORE_DEVS) { /* we want the next device in the list */ xpt_action(ccb); if (strncmp(ccb->cgdl.periph_name, "pass", 4) == 0){ found = 1; break; } } if ((ccb->cgdl.status == CAM_GDEVLIST_LAST_DEVICE) && (found == 0)) { ccb->cgdl.periph_name[0] = '\0'; ccb->cgdl.unit_number = 0; break; } } /* copy the result back out */ bcopy(ccb, addr, sizeof(union ccb)); /* and release the ccb */ xpt_release_ccb(ccb); break; default: error = ENOTTY; break; } return(error); } int cam_periph_runccb(union ccb *ccb, int (*error_routine)(union ccb *ccb, cam_flags camflags, u_int32_t sense_flags), cam_flags camflags, u_int32_t sense_flags, struct devstat *ds) { int error; error = 0; /* * If the user has supplied a stats structure, and if we understand * this particular type of ccb, record the transaction start. */ if ((ds != NULL) && (ccb->ccb_h.func_code == XPT_SCSI_IO)) devstat_start_transaction(ds); xpt_action(ccb); do { cam_periph_ccbwait(ccb); if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) error = 0; else if (error_routine != NULL) error = (*error_routine)(ccb, camflags, sense_flags); else error = 0; } while (error == ERESTART); if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) cam_release_devq(ccb->ccb_h.path, /* relsim_flags */0, /* openings */0, /* timeout */0, /* getcount_only */ FALSE); if ((ds != NULL) && (ccb->ccb_h.func_code == XPT_SCSI_IO)) devstat_end_transaction(ds, ccb->csio.dxfer_len, ccb->csio.tag_action & 0xf, ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE) ? DEVSTAT_NO_DATA : (ccb->ccb_h.flags & CAM_DIR_OUT) ? DEVSTAT_WRITE : DEVSTAT_READ); return(error); } void cam_freeze_devq(struct cam_path *path) { struct ccb_hdr ccb_h; xpt_setup_ccb(&ccb_h, path, /*priority*/1); ccb_h.func_code = XPT_NOOP; ccb_h.flags = CAM_DEV_QFREEZE; xpt_action((union ccb *)&ccb_h); } u_int32_t cam_release_devq(struct cam_path *path, u_int32_t relsim_flags, u_int32_t openings, u_int32_t timeout, int getcount_only) { struct ccb_relsim crs; xpt_setup_ccb(&crs.ccb_h, path, /*priority*/1); crs.ccb_h.func_code = XPT_REL_SIMQ; crs.ccb_h.flags = getcount_only ? CAM_DEV_QFREEZE : 0; crs.release_flags = relsim_flags; crs.openings = openings; crs.release_timeout = timeout; xpt_action((union ccb *)&crs); return (crs.qfrozen_cnt); } #define saved_ccb_ptr ppriv_ptr0 static void camperiphdone(struct cam_periph *periph, union ccb *done_ccb) { union ccb *saved_ccb; cam_status status; int frozen; int sense; struct scsi_start_stop_unit *scsi_cmd; u_int32_t relsim_flags, timeout; u_int32_t qfrozen_cnt; int xpt_done_ccb; xpt_done_ccb = FALSE; status = done_ccb->ccb_h.status; frozen = (status & CAM_DEV_QFRZN) != 0; sense = (status & CAM_AUTOSNS_VALID) != 0; status &= CAM_STATUS_MASK; timeout = 0; relsim_flags = 0; saved_ccb = (union ccb *)done_ccb->ccb_h.saved_ccb_ptr; /* * Unfreeze the queue once if it is already frozen.. */ if (frozen != 0) { qfrozen_cnt = cam_release_devq(done_ccb->ccb_h.path, /*relsim_flags*/0, /*openings*/0, /*timeout*/0, /*getcount_only*/0); } switch (status) { case CAM_REQ_CMP: { /* * If we have successfully taken a device from the not * ready to ready state, re-scan the device and re-get * the inquiry information. Many devices (mostly disks) * don't properly report their inquiry information unless * they are spun up. * * If we manually retrieved sense into a CCB and got * something other than "NO SENSE" send the updated CCB * back to the client via xpt_done() to be processed via * the error recovery code again. */ if (done_ccb->ccb_h.func_code == XPT_SCSI_IO) { scsi_cmd = (struct scsi_start_stop_unit *) &done_ccb->csio.cdb_io.cdb_bytes; if (scsi_cmd->opcode == START_STOP_UNIT) xpt_async(AC_INQ_CHANGED, done_ccb->ccb_h.path, NULL); if (scsi_cmd->opcode == REQUEST_SENSE) { u_int sense_key; sense_key = saved_ccb->csio.sense_data.flags; sense_key &= SSD_KEY; if (sense_key != SSD_KEY_NO_SENSE) { saved_ccb->ccb_h.flags |= CAM_AUTOSNS_VALID; xpt_print_path(saved_ccb->ccb_h.path); printf("Recovered Sense\n"); #if 0 scsi_sense_print(&saved_ccb->csio); #endif cam_error_print(saved_ccb, CAM_ESF_ALL, CAM_EPF_ALL); xpt_done_ccb = TRUE; } } } bcopy(done_ccb->ccb_h.saved_ccb_ptr, done_ccb, sizeof(union ccb)); periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; if (xpt_done_ccb == FALSE) xpt_action(done_ccb); break; } case CAM_SCSI_STATUS_ERROR: scsi_cmd = (struct scsi_start_stop_unit *) &done_ccb->csio.cdb_io.cdb_bytes; if (sense != 0) { struct scsi_sense_data *sense; int error_code, sense_key, asc, ascq; sense = &done_ccb->csio.sense_data; scsi_extract_sense(sense, &error_code, &sense_key, &asc, &ascq); /* * If the error is "invalid field in CDB", * and the load/eject flag is set, turn the * flag off and try again. This is just in * case the drive in question barfs on the * load eject flag. The CAM code should set * the load/eject flag by default for * removable media. */ /* XXX KDM * Should we check to see what the specific * scsi status is?? Or does it not matter * since we already know that there was an * error, and we know what the specific * error code was, and we know what the * opcode is.. */ if ((scsi_cmd->opcode == START_STOP_UNIT) && ((scsi_cmd->how & SSS_LOEJ) != 0) && (asc == 0x24) && (ascq == 0x00) && (done_ccb->ccb_h.retry_count > 0)) { scsi_cmd->how &= ~SSS_LOEJ; xpt_action(done_ccb); } else if (done_ccb->ccb_h.retry_count > 1) { /* * In this case, the error recovery * command failed, but we've got * some retries left on it. Give * it another try. */ /* set the timeout to .5 sec */ relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT; timeout = 500; xpt_action(done_ccb); break; } else { /* * Perform the final retry with the original * CCB so that final error processing is * performed by the owner of the CCB. */ bcopy(done_ccb->ccb_h.saved_ccb_ptr, done_ccb, sizeof(union ccb)); periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; xpt_action(done_ccb); } } else { /* * Eh?? The command failed, but we don't * have any sense. What's up with that? * Fire the CCB again to return it to the * caller. */ bcopy(done_ccb->ccb_h.saved_ccb_ptr, done_ccb, sizeof(union ccb)); periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; xpt_action(done_ccb); } break; default: bcopy(done_ccb->ccb_h.saved_ccb_ptr, done_ccb, sizeof(union ccb)); periph->flags &= ~CAM_PERIPH_RECOVERY_INPROG; xpt_action(done_ccb); break; } /* decrement the retry count */ /* * XXX This isn't appropriate in all cases. Restructure, * so that the retry count is only decremented on an * actual retry. Remeber that the orignal ccb had its * retry count dropped before entering recovery, so * doing it again is a bug. */ if (done_ccb->ccb_h.retry_count > 0) done_ccb->ccb_h.retry_count--; qfrozen_cnt = cam_release_devq(done_ccb->ccb_h.path, /*relsim_flags*/relsim_flags, /*openings*/0, /*timeout*/timeout, /*getcount_only*/0); if (xpt_done_ccb == TRUE) (*done_ccb->ccb_h.cbfcnp)(periph, done_ccb); } /* * Generic Async Event handler. Peripheral drivers usually * filter out the events that require personal attention, * and leave the rest to this function. */ void cam_periph_async(struct cam_periph *periph, u_int32_t code, struct cam_path *path, void *arg) { switch (code) { case AC_LOST_DEVICE: cam_periph_invalidate(periph); break; case AC_SENT_BDR: case AC_BUS_RESET: { cam_periph_bus_settle(periph, SCSI_DELAY); break; } default: break; } } void cam_periph_bus_settle(struct cam_periph *periph, u_int bus_settle) { struct ccb_getdevstats cgds; xpt_setup_ccb(&cgds.ccb_h, periph->path, /*priority*/1); cgds.ccb_h.func_code = XPT_GDEV_STATS; xpt_action((union ccb *)&cgds); cam_periph_freeze_after_event(periph, &cgds.last_reset, bus_settle); } void cam_periph_freeze_after_event(struct cam_periph *periph, struct timeval* event_time, u_int duration_ms) { struct timeval delta; struct timeval duration_tv; int s; s = splclock(); microtime(&delta); splx(s); timevalsub(&delta, event_time); duration_tv.tv_sec = duration_ms / 1000; duration_tv.tv_usec = (duration_ms % 1000) * 1000; if (timevalcmp(&delta, &duration_tv, <)) { timevalsub(&duration_tv, &delta); duration_ms = duration_tv.tv_sec * 1000; duration_ms += duration_tv.tv_usec / 1000; cam_freeze_devq(periph->path); cam_release_devq(periph->path, RELSIM_RELEASE_AFTER_TIMEOUT, /*reduction*/0, /*timeout*/duration_ms, /*getcount_only*/0); } } static int camperiphscsistatuserror(union ccb *ccb, cam_flags camflags, u_int32_t sense_flags, union ccb *save_ccb, int *openings, u_int32_t *relsim_flags, u_int32_t *timeout) { int error; switch (ccb->csio.scsi_status) { case SCSI_STATUS_OK: case SCSI_STATUS_COND_MET: case SCSI_STATUS_INTERMED: case SCSI_STATUS_INTERMED_COND_MET: error = 0; break; case SCSI_STATUS_CMD_TERMINATED: case SCSI_STATUS_CHECK_COND: error = camperiphscsisenseerror(ccb, camflags, sense_flags, save_ccb, openings, relsim_flags, timeout); break; case SCSI_STATUS_QUEUE_FULL: { /* no decrement */ struct ccb_getdevstats cgds; /* * First off, find out what the current * transaction counts are. */ xpt_setup_ccb(&cgds.ccb_h, ccb->ccb_h.path, /*priority*/1); cgds.ccb_h.func_code = XPT_GDEV_STATS; xpt_action((union ccb *)&cgds); /* * If we were the only transaction active, treat * the QUEUE FULL as if it were a BUSY condition. */ if (cgds.dev_active != 0) { int total_openings; /* * Reduce the number of openings to * be 1 less than the amount it took * to get a queue full bounded by the * minimum allowed tag count for this * device. */ total_openings = cgds.dev_active + cgds.dev_openings; *openings = cgds.dev_active; if (*openings < cgds.mintags) *openings = cgds.mintags; if (*openings < total_openings) *relsim_flags = RELSIM_ADJUST_OPENINGS; else { /* * Some devices report queue full for * temporary resource shortages. For * this reason, we allow a minimum * tag count to be entered via a * quirk entry to prevent the queue * count on these devices from falling * to a pessimisticly low value. We * still wait for the next successful * completion, however, before queueing * more transactions to the device. */ *relsim_flags = RELSIM_RELEASE_AFTER_CMDCMPLT; } *timeout = 0; error = ERESTART; break; } /* FALLTHROUGH */ } case SCSI_STATUS_BUSY: /* * Restart the queue after either another * command completes or a 1 second timeout. */ if (ccb->ccb_h.retry_count > 0) { ccb->ccb_h.retry_count--; error = ERESTART; *relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT | RELSIM_RELEASE_AFTER_CMDCMPLT; *timeout = 1000; } else { error = EIO; } break; case SCSI_STATUS_RESERV_CONFLICT: error = EIO; break; default: error = EIO; break; } return (error); } static int camperiphscsisenseerror(union ccb *ccb, cam_flags camflags, u_int32_t sense_flags, union ccb *save_ccb, int *openings, u_int32_t *relsim_flags, u_int32_t *timeout) { struct cam_periph *periph; int error; periph = xpt_path_periph(ccb->ccb_h.path); if (periph->flags & CAM_PERIPH_RECOVERY_INPROG) { /* * If error recovery is already in progress, don't attempt * to process this error, but requeue it unconditionally * and attempt to process it once error recovery has * completed. This failed command is probably related to * the error that caused the currently active error recovery * action so our current recovery efforts should also * address this command. Be aware that the error recovery * code assumes that only one recovery action is in progress * on a particular peripheral instance at any given time * (e.g. only one saved CCB for error recovery) so it is * imperitive that we don't violate this assumption. */ error = ERESTART; } else { scsi_sense_action err_action; struct ccb_getdev cgd; const char *action_string; union ccb* print_ccb; /* A description of the error recovery action performed */ action_string = NULL; /* * The location of the orignal ccb * for sense printing purposes. */ print_ccb = ccb; /* * Grab the inquiry data for this device. */ xpt_setup_ccb(&cgd.ccb_h, ccb->ccb_h.path, /*priority*/ 1); cgd.ccb_h.func_code = XPT_GDEV_TYPE; xpt_action((union ccb *)&cgd); if ((ccb->ccb_h.status & CAM_AUTOSNS_VALID) != 0) err_action = scsi_error_action(&ccb->csio, &cgd.inq_data, sense_flags); else if ((ccb->ccb_h.flags & CAM_DIS_AUTOSENSE) == 0) err_action = SS_REQSENSE; else err_action = SS_RETRY|SSQ_DECREMENT_COUNT|EIO; error = err_action & SS_ERRMASK; /* * If the recovery action will consume a retry, * make sure we actually have retries available. */ if ((err_action & SSQ_DECREMENT_COUNT) != 0) { if (ccb->ccb_h.retry_count > 0) ccb->ccb_h.retry_count--; else { action_string = "Retries Exhausted"; goto sense_error_done; } } if ((err_action & SS_MASK) >= SS_START) { /* * Do common portions of commands that * use recovery CCBs. */ if (save_ccb == NULL) { action_string = "No recovery CCB supplied"; goto sense_error_done; } bcopy(ccb, save_ccb, sizeof(*save_ccb)); print_ccb = save_ccb; periph->flags |= CAM_PERIPH_RECOVERY_INPROG; } switch (err_action & SS_MASK) { case SS_NOP: action_string = "No Recovery Action Needed"; error = 0; break; case SS_RETRY: action_string = "Retrying Command"; error = ERESTART; break; case SS_FAIL: action_string = "Unretryable error"; break; case SS_START: { int le; /* * Send a start unit command to the device, and * then retry the command. */ action_string = "Attempting to Start Unit"; /* * Check for removable media and set * load/eject flag appropriately. */ if (SID_IS_REMOVABLE(&cgd.inq_data)) le = TRUE; else le = FALSE; scsi_start_stop(&ccb->csio, /*retries*/1, camperiphdone, MSG_SIMPLE_Q_TAG, /*start*/TRUE, /*load/eject*/le, /*immediate*/FALSE, SSD_FULL_SIZE, /*timeout*/50000); break; } case SS_TUR: { /* * Send a Test Unit Ready to the device. * If the 'many' flag is set, we send 120 * test unit ready commands, one every half * second. Otherwise, we just send one TUR. * We only want to do this if the retry * count has not been exhausted. */ int retries; if ((err_action & SSQ_MANY) != 0) { action_string = "Polling device for readiness"; retries = 120; } else { action_string = "Testing device for readiness"; retries = 1; } scsi_test_unit_ready(&ccb->csio, retries, camperiphdone, MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, /*timeout*/5000); /* * Accomplish our 500ms delay by deferring * the release of our device queue appropriately. */ *relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT; *timeout = 500; break; } case SS_REQSENSE: { /* * Send a Request Sense to the device. We * assume that we are in a contingent allegiance * condition so we do not tag this request. */ scsi_request_sense(&ccb->csio, /*retries*/1, camperiphdone, &save_ccb->csio.sense_data, sizeof(save_ccb->csio.sense_data), CAM_TAG_ACTION_NONE, /*sense_len*/SSD_FULL_SIZE, /*timeout*/5000); break; } default: panic("Unhandled error action %x\n", err_action); } if ((err_action & SS_MASK) >= SS_START) { /* * Drop the priority to 0 so that the recovery * CCB is the first to execute. Freeze the queue * after this command is sent so that we can * restore the old csio and have it queued in * the proper order before we release normal * transactions to the device. */ ccb->ccb_h.pinfo.priority = 0; ccb->ccb_h.flags |= CAM_DEV_QFREEZE; ccb->ccb_h.saved_ccb_ptr = save_ccb; error = ERESTART; } sense_error_done: if ((err_action & SSQ_PRINT_SENSE) != 0 && (ccb->ccb_h.status & CAM_AUTOSNS_VALID) != 0) { #if 0 scsi_sense_print(&print_ccb->csio); #endif cam_error_print(print_ccb, CAM_ESF_ALL, CAM_EPF_ALL); xpt_print_path(ccb->ccb_h.path); printf("%s\n", action_string); } } return (error); } /* * Generic error handler. Peripheral drivers usually filter * out the errors that they handle in a unique mannor, then * call this function. */ int cam_periph_error(union ccb *ccb, cam_flags camflags, u_int32_t sense_flags, union ccb *save_ccb) { const char *action_string; cam_status status; int frozen; int error; int openings; u_int32_t relsim_flags; u_int32_t timeout; action_string = NULL; status = ccb->ccb_h.status; frozen = (status & CAM_DEV_QFRZN) != 0; status &= CAM_STATUS_MASK; relsim_flags = 0; switch (status) { case CAM_REQ_CMP: error = 0; break; case CAM_SCSI_STATUS_ERROR: error = camperiphscsistatuserror(ccb, camflags, sense_flags, save_ccb, &openings, &relsim_flags, &timeout); break; case CAM_AUTOSENSE_FAIL: xpt_print_path(ccb->ccb_h.path); printf("AutoSense Failed\n"); case CAM_REQ_CMP_ERR: case CAM_CMD_TIMEOUT: case CAM_UNEXP_BUSFREE: case CAM_UNCOR_PARITY: case CAM_DATA_RUN_ERR: /* decrement the number of retries */ if (ccb->ccb_h.retry_count > 0) { ccb->ccb_h.retry_count--; error = ERESTART; } else { action_string = "Retries Exausted"; error = EIO; } break; case CAM_UA_ABORT: case CAM_UA_TERMIO: case CAM_MSG_REJECT_REC: /* XXX Don't know that these are correct */ error = EIO; break; case CAM_SEL_TIMEOUT: { struct cam_path *newpath; if ((camflags & CAM_RETRY_SELTO) != 0) { if (ccb->ccb_h.retry_count > 0) { ccb->ccb_h.retry_count--; error = ERESTART; /* * Wait a second to give the device * time to recover before we try again. */ relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT; timeout = 1000; break; } } error = ENXIO; /* Should we do more if we can't create the path?? */ if (xpt_create_path(&newpath, xpt_path_periph(ccb->ccb_h.path), xpt_path_path_id(ccb->ccb_h.path), xpt_path_target_id(ccb->ccb_h.path), CAM_LUN_WILDCARD) != CAM_REQ_CMP) break; /* * Let peripheral drivers know that this device has gone * away. */ xpt_async(AC_LOST_DEVICE, newpath, NULL); xpt_free_path(newpath); break; } case CAM_REQ_INVALID: case CAM_PATH_INVALID: case CAM_DEV_NOT_THERE: case CAM_NO_HBA: case CAM_PROVIDE_FAIL: case CAM_REQ_TOO_BIG: error = EINVAL; break; case CAM_SCSI_BUS_RESET: case CAM_BDR_SENT: /* * Commands that repeatedly timeout and cause these * kinds of error recovery actions, should return * CAM_CMD_TIMEOUT, which allows us to safely assume * that this command was an innocent bystander to * these events and should be unconditionally * retried. */ /* FALLTHROUGH */ case CAM_REQUEUE_REQ: /* Unconditional requeue */ error = ERESTART; break; case CAM_RESRC_UNAVAIL: case CAM_BUSY: /* timeout??? */ default: /* decrement the number of retries */ if (ccb->ccb_h.retry_count > 0) { ccb->ccb_h.retry_count--; error = ERESTART; } else { error = EIO; action_string = "Retries Exhausted"; } break; } /* Attempt a retry */ if (error == ERESTART || error == 0) { if (frozen != 0) ccb->ccb_h.status &= ~CAM_DEV_QFRZN; if (error == ERESTART) { action_string = "Retrying Command"; xpt_action(ccb); } if (frozen != 0) cam_release_devq(ccb->ccb_h.path, relsim_flags, openings, timeout, /*getcount_only*/0); } /* * If we have and error and are booting verbosely, whine * *unless* this was a non-retryable selection timeout. */ if (error != 0 && bootverbose && !(status == CAM_SEL_TIMEOUT && (camflags & CAM_RETRY_SELTO) == 0)) { if (action_string == NULL) action_string = "Unretryable Error"; if (error != ERESTART) { xpt_print_path(ccb->ccb_h.path); printf("error %d\n", error); } xpt_print_path(ccb->ccb_h.path); printf("%s\n", action_string); } return (error); } Index: head/sys/cam/cam_xpt.c =================================================================== --- head/sys/cam/cam_xpt.c (revision 78134) +++ head/sys/cam/cam_xpt.c (revision 78135) @@ -1,6985 +1,6985 @@ /* * Implementation of the Common Access Method Transport (XPT) layer. * * Copyright (c) 1997, 1998, 1999 Justin T. Gibbs. * Copyright (c) 1997, 1998, 1999 Kenneth D. Merry. * All rights reserved. * * 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, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PC98 #include /* geometry translation */ #endif #include #include #include #include #include #include #include #include #include #include #include #include "opt_cam.h" /* Datastructures internal to the xpt layer */ /* * Definition of an async handler callback block. These are used to add * SIMs and peripherals to the async callback lists. */ struct async_node { SLIST_ENTRY(async_node) links; u_int32_t event_enable; /* Async Event enables */ void (*callback)(void *arg, u_int32_t code, struct cam_path *path, void *args); void *callback_arg; }; SLIST_HEAD(async_list, async_node); SLIST_HEAD(periph_list, cam_periph); static STAILQ_HEAD(highpowerlist, ccb_hdr) highpowerq; /* * This is the maximum number of high powered commands (e.g. start unit) * that can be outstanding at a particular time. */ #ifndef CAM_MAX_HIGHPOWER #define CAM_MAX_HIGHPOWER 4 #endif /* number of high powered commands that can go through right now */ static int num_highpower = CAM_MAX_HIGHPOWER; /* * Structure for queueing a device in a run queue. * There is one run queue for allocating new ccbs, * and another for sending ccbs to the controller. */ struct cam_ed_qinfo { cam_pinfo pinfo; struct cam_ed *device; }; /* * The CAM EDT (Existing Device Table) contains the device information for * all devices for all busses in the system. The table contains a * cam_ed structure for each device on the bus. */ struct cam_ed { TAILQ_ENTRY(cam_ed) links; struct cam_ed_qinfo alloc_ccb_entry; struct cam_ed_qinfo send_ccb_entry; struct cam_et *target; lun_id_t lun_id; struct camq drvq; /* * Queue of type drivers wanting to do * work on this device. */ struct cam_ccbq ccbq; /* Queue of pending ccbs */ struct async_list asyncs; /* Async callback info for this B/T/L */ struct periph_list periphs; /* All attached devices */ u_int generation; /* Generation number */ struct cam_periph *owner; /* Peripheral driver's ownership tag */ struct xpt_quirk_entry *quirk; /* Oddities about this device */ /* Storage for the inquiry data */ #ifdef CAM_NEW_TRAN_CODE cam_proto protocol; u_int protocol_version; cam_xport transport; u_int transport_version; #endif /* CAM_NEW_TRAN_CODE */ struct scsi_inquiry_data inq_data; u_int8_t inq_flags; /* * Current settings for inquiry flags. * This allows us to override settings * like disconnection and tagged * queuing for a device. */ u_int8_t queue_flags; /* Queue flags from the control page */ u_int8_t serial_num_len; u_int8_t *serial_num; u_int32_t qfrozen_cnt; u_int32_t flags; #define CAM_DEV_UNCONFIGURED 0x01 #define CAM_DEV_REL_TIMEOUT_PENDING 0x02 #define CAM_DEV_REL_ON_COMPLETE 0x04 #define CAM_DEV_REL_ON_QUEUE_EMPTY 0x08 #define CAM_DEV_RESIZE_QUEUE_NEEDED 0x10 #define CAM_DEV_TAG_AFTER_COUNT 0x20 #define CAM_DEV_INQUIRY_DATA_VALID 0x40 u_int32_t tag_delay_count; #define CAM_TAG_DELAY_COUNT 5 u_int32_t refcount; struct callout_handle c_handle; }; /* * Each target is represented by an ET (Existing Target). These * entries are created when a target is successfully probed with an * identify, and removed when a device fails to respond after a number * of retries, or a bus rescan finds the device missing. */ struct cam_et { TAILQ_HEAD(, cam_ed) ed_entries; TAILQ_ENTRY(cam_et) links; struct cam_eb *bus; target_id_t target_id; u_int32_t refcount; u_int generation; struct timeval last_reset; }; /* * Each bus is represented by an EB (Existing Bus). These entries * are created by calls to xpt_bus_register and deleted by calls to * xpt_bus_deregister. */ struct cam_eb { TAILQ_HEAD(, cam_et) et_entries; TAILQ_ENTRY(cam_eb) links; path_id_t path_id; struct cam_sim *sim; struct timeval last_reset; u_int32_t flags; #define CAM_EB_RUNQ_SCHEDULED 0x01 u_int32_t refcount; u_int generation; }; struct cam_path { struct cam_periph *periph; struct cam_eb *bus; struct cam_et *target; struct cam_ed *device; }; struct xpt_quirk_entry { struct scsi_inquiry_pattern inq_pat; u_int8_t quirks; #define CAM_QUIRK_NOLUNS 0x01 #define CAM_QUIRK_NOSERIAL 0x02 #define CAM_QUIRK_HILUNS 0x04 u_int mintags; u_int maxtags; }; #define CAM_SCSI2_MAXLUN 8 typedef enum { XPT_FLAG_OPEN = 0x01 } xpt_flags; struct xpt_softc { xpt_flags flags; u_int32_t generation; }; static const char quantum[] = "QUANTUM"; static const char sony[] = "SONY"; static const char west_digital[] = "WDIGTL"; static const char samsung[] = "SAMSUNG"; static const char seagate[] = "SEAGATE"; static const char microp[] = "MICROP"; static struct xpt_quirk_entry xpt_quirk_table[] = { { /* Reports QUEUE FULL for temporary resource shortages */ { T_DIRECT, SIP_MEDIA_FIXED, quantum, "XP39100*", "*" }, /*quirks*/0, /*mintags*/24, /*maxtags*/32 }, { /* Reports QUEUE FULL for temporary resource shortages */ { T_DIRECT, SIP_MEDIA_FIXED, quantum, "XP34550*", "*" }, /*quirks*/0, /*mintags*/24, /*maxtags*/32 }, { /* Reports QUEUE FULL for temporary resource shortages */ { T_DIRECT, SIP_MEDIA_FIXED, quantum, "XP32275*", "*" }, /*quirks*/0, /*mintags*/24, /*maxtags*/32 }, { /* Broken tagged queuing drive */ { T_DIRECT, SIP_MEDIA_FIXED, microp, "4421-07*", "*" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, { /* Broken tagged queuing drive */ { T_DIRECT, SIP_MEDIA_FIXED, "HP", "C372*", "*" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, { /* Broken tagged queuing drive */ { T_DIRECT, SIP_MEDIA_FIXED, microp, "3391*", "x43h" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, { /* * Unfortunately, the Quantum Atlas III has the same * problem as the Atlas II drives above. * Reported by: "Johan Granlund" * * For future reference, the drive with the problem was: * QUANTUM QM39100TD-SW N1B0 * * It's possible that Quantum will fix the problem in later * firmware revisions. If that happens, the quirk entry * will need to be made specific to the firmware revisions * with the problem. * */ /* Reports QUEUE FULL for temporary resource shortages */ { T_DIRECT, SIP_MEDIA_FIXED, quantum, "QM39100*", "*" }, /*quirks*/0, /*mintags*/24, /*maxtags*/32 }, { /* * 18 Gig Atlas III, same problem as the 9G version. * Reported by: Andre Albsmeier * * * For future reference, the drive with the problem was: * QUANTUM QM318000TD-S N491 */ /* Reports QUEUE FULL for temporary resource shortages */ { T_DIRECT, SIP_MEDIA_FIXED, quantum, "QM318000*", "*" }, /*quirks*/0, /*mintags*/24, /*maxtags*/32 }, { /* * Broken tagged queuing drive * Reported by: Bret Ford * and: Martin Renters */ { T_DIRECT, SIP_MEDIA_FIXED, seagate, "ST410800*", "71*" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, /* * The Seagate Medalist Pro drives have very poor write * performance with anything more than 2 tags. * * Reported by: Paul van der Zwan * Drive: * * Reported by: Jeremy Lea * Drive: * * No one has actually reported that the 9G version * (ST39140*) of the Medalist Pro has the same problem, but * we're assuming that it does because the 4G and 6.5G * versions of the drive are broken. */ { { T_DIRECT, SIP_MEDIA_FIXED, seagate, "ST34520*", "*"}, /*quirks*/0, /*mintags*/2, /*maxtags*/2 }, { { T_DIRECT, SIP_MEDIA_FIXED, seagate, "ST36530*", "*"}, /*quirks*/0, /*mintags*/2, /*maxtags*/2 }, { { T_DIRECT, SIP_MEDIA_FIXED, seagate, "ST39140*", "*"}, /*quirks*/0, /*mintags*/2, /*maxtags*/2 }, { /* * Slow when tagged queueing is enabled. Write performance * steadily drops off with more and more concurrent * transactions. Best sequential write performance with * tagged queueing turned off and write caching turned on. * * PR: kern/10398 * Submitted by: Hideaki Okada * Drive: DCAS-34330 w/ "S65A" firmware. * * The drive with the problem had the "S65A" firmware * revision, and has also been reported (by Stephen J. * Roznowski ) for a drive with the "S61A" * firmware revision. * * Although no one has reported problems with the 2 gig * version of the DCAS drive, the assumption is that it * has the same problems as the 4 gig version. Therefore * this quirk entries disables tagged queueing for all * DCAS drives. */ { T_DIRECT, SIP_MEDIA_FIXED, "IBM", "DCAS*", "*" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, { /* Broken tagged queuing drive */ { T_DIRECT, SIP_MEDIA_REMOVABLE, "iomega", "jaz*", "*" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, { /* Broken tagged queuing drive */ { T_DIRECT, SIP_MEDIA_FIXED, "CONNER", "CFP2107*", "*" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, { /* * Broken tagged queuing drive. * Submitted by: * NAKAJI Hiroyuki * in PR kern/9535 */ { T_DIRECT, SIP_MEDIA_FIXED, samsung, "WN34324U*", "*" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, { /* * Slow when tagged queueing is enabled. (1.5MB/sec versus * 8MB/sec.) * Submitted by: Andrew Gallatin * Best performance with these drives is achieved with * tagged queueing turned off, and write caching turned on. */ { T_DIRECT, SIP_MEDIA_FIXED, west_digital, "WDE*", "*" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, { /* * Slow when tagged queueing is enabled. (1.5MB/sec versus * 8MB/sec.) * Submitted by: Andrew Gallatin * Best performance with these drives is achieved with * tagged queueing turned off, and write caching turned on. */ { T_DIRECT, SIP_MEDIA_FIXED, west_digital, "ENTERPRISE", "*" }, /*quirks*/0, /*mintags*/0, /*maxtags*/0 }, { /* * Doesn't handle queue full condition correctly, * so we need to limit maxtags to what the device * can handle instead of determining this automatically. */ { T_DIRECT, SIP_MEDIA_FIXED, samsung, "WN321010S*", "*" }, /*quirks*/0, /*mintags*/2, /*maxtags*/32 }, { /* Really only one LUN */ { T_ENCLOSURE, SIP_MEDIA_FIXED, "SUN", "SENA", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* I can't believe we need a quirk for DPT volumes. */ { T_ANY, SIP_MEDIA_FIXED|SIP_MEDIA_REMOVABLE, "DPT", "*", "*" }, CAM_QUIRK_NOSERIAL|CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/255 }, { /* * Many Sony CDROM drives don't like multi-LUN probing. */ { T_CDROM, SIP_MEDIA_REMOVABLE, sony, "CD-ROM CDU*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* * This drive doesn't like multiple LUN probing. * Submitted by: Parag Patel */ { T_WORM, SIP_MEDIA_REMOVABLE, sony, "CD-R CDU9*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { { T_WORM, SIP_MEDIA_REMOVABLE, "YAMAHA", "CDR100*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* * The 8200 doesn't like multi-lun probing, and probably * don't like serial number requests either. */ { T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "EXABYTE", "EXB-8200*", "*" }, CAM_QUIRK_NOSERIAL|CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* * Let's try the same as above, but for a drive that says * it's an IPL-6860 but is actually an EXB 8200. */ { T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "EXABYTE", "IPL-6860*", "*" }, CAM_QUIRK_NOSERIAL|CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* * These Hitachi drives don't like multi-lun probing. * The PR submitter has a DK319H, but says that the Linux * kernel has a similar work-around for the DK312 and DK314, * so all DK31* drives are quirked here. * PR: misc/18793 * Submitted by: Paul Haddad */ { T_DIRECT, SIP_MEDIA_FIXED, "HITACHI", "DK31*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/2, /*maxtags*/255 }, { /* * This old revision of the TDC3600 is also SCSI-1, and * hangs upon serial number probing. */ { T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG", " TDC 3600", "U07:" }, CAM_QUIRK_NOSERIAL, /*mintags*/0, /*maxtags*/0 }, { /* * Would repond to all LUNs if asked for. */ { T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "CALIPER", "CP150", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* * Would repond to all LUNs if asked for. */ { T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "KENNEDY", "96X2*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* Submitted by: Matthew Dodd */ { T_PROCESSOR, SIP_MEDIA_FIXED, "Cabletrn", "EA41*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* Submitted by: Matthew Dodd */ { T_PROCESSOR, SIP_MEDIA_FIXED, "CABLETRN", "EA41*", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* TeraSolutions special settings for TRC-22 RAID */ { T_DIRECT, SIP_MEDIA_FIXED, "TERASOLU", "TRC-22", "*" }, /*quirks*/0, /*mintags*/55, /*maxtags*/255 }, { /* * Would respond to all LUNs. Device type and removable * flag are jumper-selectable. */ { T_ANY, SIP_MEDIA_REMOVABLE|SIP_MEDIA_FIXED, "MaxOptix", "Tahiti 1", "*" }, CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 }, { /* Default tagged queuing parameters for all devices */ { T_ANY, SIP_MEDIA_REMOVABLE|SIP_MEDIA_FIXED, /*vendor*/"*", /*product*/"*", /*revision*/"*" }, /*quirks*/0, /*mintags*/2, /*maxtags*/255 }, }; static const int xpt_quirk_table_size = sizeof(xpt_quirk_table) / sizeof(*xpt_quirk_table); typedef enum { DM_RET_COPY = 0x01, DM_RET_FLAG_MASK = 0x0f, DM_RET_NONE = 0x00, DM_RET_STOP = 0x10, DM_RET_DESCEND = 0x20, DM_RET_ERROR = 0x30, DM_RET_ACTION_MASK = 0xf0 } dev_match_ret; typedef enum { XPT_DEPTH_BUS, XPT_DEPTH_TARGET, XPT_DEPTH_DEVICE, XPT_DEPTH_PERIPH } xpt_traverse_depth; struct xpt_traverse_config { xpt_traverse_depth depth; void *tr_func; void *tr_arg; }; typedef int xpt_busfunc_t (struct cam_eb *bus, void *arg); typedef int xpt_targetfunc_t (struct cam_et *target, void *arg); typedef int xpt_devicefunc_t (struct cam_ed *device, void *arg); typedef int xpt_periphfunc_t (struct cam_periph *periph, void *arg); typedef int xpt_pdrvfunc_t (struct periph_driver **pdrv, void *arg); /* Transport layer configuration information */ static struct xpt_softc xsoftc; /* Queues for our software interrupt handler */ typedef TAILQ_HEAD(cam_isrq, ccb_hdr) cam_isrq_t; static cam_isrq_t cam_bioq; static cam_isrq_t cam_netq; /* "Pool" of inactive ccbs managed by xpt_alloc_ccb and xpt_free_ccb */ static SLIST_HEAD(,ccb_hdr) ccb_freeq; static u_int xpt_max_ccbs; /* * Maximum size of ccb pool. Modified as * devices are added/removed or have their * opening counts changed. */ static u_int xpt_ccb_count; /* Current count of allocated ccbs */ struct cam_periph *xpt_periph; static periph_init_t xpt_periph_init; static periph_init_t probe_periph_init; static struct periph_driver xpt_driver = { xpt_periph_init, "xpt", TAILQ_HEAD_INITIALIZER(xpt_driver.units) }; static struct periph_driver probe_driver = { probe_periph_init, "probe", TAILQ_HEAD_INITIALIZER(probe_driver.units) }; PERIPHDRIVER_DECLARE(xpt, xpt_driver); PERIPHDRIVER_DECLARE(probe, probe_driver); #define XPT_CDEV_MAJOR 104 static d_open_t xptopen; static d_close_t xptclose; static d_ioctl_t xptioctl; static struct cdevsw xpt_cdevsw = { /* open */ xptopen, /* close */ xptclose, /* read */ noread, /* write */ nowrite, /* ioctl */ xptioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ nostrategy, /* name */ "xpt", /* maj */ XPT_CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, }; static struct intr_config_hook *xpt_config_hook; /* Registered busses */ static TAILQ_HEAD(,cam_eb) xpt_busses; static u_int bus_generation; /* Storage for debugging datastructures */ #ifdef CAMDEBUG struct cam_path *cam_dpath; u_int32_t cam_dflags; u_int32_t cam_debug_delay; #endif /* Pointers to software interrupt handlers */ void *camnet_ih; void *cambio_ih; #if defined(CAM_DEBUG_FLAGS) && !defined(CAMDEBUG) #error "You must have options CAMDEBUG to use options CAM_DEBUG_FLAGS" #endif /* * In order to enable the CAM_DEBUG_* options, the user must have CAMDEBUG * enabled. Also, the user must have either none, or all of CAM_DEBUG_BUS, * CAM_DEBUG_TARGET, and CAM_DEBUG_LUN specified. */ #if defined(CAM_DEBUG_BUS) || defined(CAM_DEBUG_TARGET) \ || defined(CAM_DEBUG_LUN) #ifdef CAMDEBUG #if !defined(CAM_DEBUG_BUS) || !defined(CAM_DEBUG_TARGET) \ || !defined(CAM_DEBUG_LUN) #error "You must define all or none of CAM_DEBUG_BUS, CAM_DEBUG_TARGET \ and CAM_DEBUG_LUN" #endif /* !CAM_DEBUG_BUS || !CAM_DEBUG_TARGET || !CAM_DEBUG_LUN */ #else /* !CAMDEBUG */ #error "You must use options CAMDEBUG if you use the CAM_DEBUG_* options" #endif /* CAMDEBUG */ #endif /* CAM_DEBUG_BUS || CAM_DEBUG_TARGET || CAM_DEBUG_LUN */ /* Our boot-time initialization hook */ static int cam_module_event_handler(module_t, int /*modeventtype_t*/, void *); static moduledata_t cam_moduledata = { "cam", cam_module_event_handler, NULL }; static void xpt_init(void *); DECLARE_MODULE(cam, cam_moduledata, SI_SUB_CONFIGURE, SI_ORDER_SECOND); MODULE_VERSION(cam, 1); static cam_status xpt_compile_path(struct cam_path *new_path, struct cam_periph *perph, path_id_t path_id, target_id_t target_id, lun_id_t lun_id); static void xpt_release_path(struct cam_path *path); static void xpt_async_bcast(struct async_list *async_head, u_int32_t async_code, struct cam_path *path, void *async_arg); static void xpt_dev_async(u_int32_t async_code, struct cam_eb *bus, struct cam_et *target, struct cam_ed *device, void *async_arg); static path_id_t xptnextfreepathid(void); static path_id_t xptpathid(const char *sim_name, int sim_unit, int sim_bus); static union ccb *xpt_get_ccb(struct cam_ed *device); static int xpt_schedule_dev(struct camq *queue, cam_pinfo *dev_pinfo, u_int32_t new_priority); static void xpt_run_dev_allocq(struct cam_eb *bus); static void xpt_run_dev_sendq(struct cam_eb *bus); static timeout_t xpt_release_devq_timeout; static timeout_t xpt_release_simq_timeout; static void xpt_release_bus(struct cam_eb *bus); static void xpt_release_devq_device(struct cam_ed *dev, u_int count, int run_queue); static struct cam_et* xpt_alloc_target(struct cam_eb *bus, target_id_t target_id); static void xpt_release_target(struct cam_eb *bus, struct cam_et *target); static struct cam_ed* xpt_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id); static void xpt_release_device(struct cam_eb *bus, struct cam_et *target, struct cam_ed *device); static u_int32_t xpt_dev_ccbq_resize(struct cam_path *path, int newopenings); static struct cam_eb* xpt_find_bus(path_id_t path_id); static struct cam_et* xpt_find_target(struct cam_eb *bus, target_id_t target_id); static struct cam_ed* xpt_find_device(struct cam_et *target, lun_id_t lun_id); static void xpt_scan_bus(struct cam_periph *periph, union ccb *ccb); static void xpt_scan_lun(struct cam_periph *periph, struct cam_path *path, cam_flags flags, union ccb *ccb); static void xptscandone(struct cam_periph *periph, union ccb *done_ccb); static xpt_busfunc_t xptconfigbuscountfunc; static xpt_busfunc_t xptconfigfunc; static void xpt_config(void *arg); static xpt_devicefunc_t xptpassannouncefunc; static void xpt_finishconfig(struct cam_periph *periph, union ccb *ccb); static void xptaction(struct cam_sim *sim, union ccb *work_ccb); static void xptpoll(struct cam_sim *sim); static void camisr(void *); #if 0 static void xptstart(struct cam_periph *periph, union ccb *work_ccb); static void xptasync(struct cam_periph *periph, u_int32_t code, cam_path *path); #endif static dev_match_ret xptbusmatch(struct dev_match_pattern *patterns, u_int num_patterns, struct cam_eb *bus); static dev_match_ret xptdevicematch(struct dev_match_pattern *patterns, u_int num_patterns, struct cam_ed *device); static dev_match_ret xptperiphmatch(struct dev_match_pattern *patterns, u_int num_patterns, struct cam_periph *periph); static xpt_busfunc_t xptedtbusfunc; static xpt_targetfunc_t xptedttargetfunc; static xpt_devicefunc_t xptedtdevicefunc; static xpt_periphfunc_t xptedtperiphfunc; static xpt_pdrvfunc_t xptplistpdrvfunc; static xpt_periphfunc_t xptplistperiphfunc; static int xptedtmatch(struct ccb_dev_match *cdm); static int xptperiphlistmatch(struct ccb_dev_match *cdm); static int xptbustraverse(struct cam_eb *start_bus, xpt_busfunc_t *tr_func, void *arg); static int xpttargettraverse(struct cam_eb *bus, struct cam_et *start_target, xpt_targetfunc_t *tr_func, void *arg); static int xptdevicetraverse(struct cam_et *target, struct cam_ed *start_device, xpt_devicefunc_t *tr_func, void *arg); static int xptperiphtraverse(struct cam_ed *device, struct cam_periph *start_periph, xpt_periphfunc_t *tr_func, void *arg); static int xptpdrvtraverse(struct periph_driver **start_pdrv, xpt_pdrvfunc_t *tr_func, void *arg); static int xptpdperiphtraverse(struct periph_driver **pdrv, struct cam_periph *start_periph, xpt_periphfunc_t *tr_func, void *arg); static xpt_busfunc_t xptdefbusfunc; static xpt_targetfunc_t xptdeftargetfunc; static xpt_devicefunc_t xptdefdevicefunc; static xpt_periphfunc_t xptdefperiphfunc; static int xpt_for_all_busses(xpt_busfunc_t *tr_func, void *arg); #ifdef notusedyet static int xpt_for_all_targets(xpt_targetfunc_t *tr_func, void *arg); #endif static int xpt_for_all_devices(xpt_devicefunc_t *tr_func, void *arg); #ifdef notusedyet static int xpt_for_all_periphs(xpt_periphfunc_t *tr_func, void *arg); #endif static xpt_devicefunc_t xptsetasyncfunc; static xpt_busfunc_t xptsetasyncbusfunc; static cam_status xptregister(struct cam_periph *periph, void *arg); static cam_status proberegister(struct cam_periph *periph, void *arg); static void probeschedule(struct cam_periph *probe_periph); static void probestart(struct cam_periph *periph, union ccb *start_ccb); static void proberequestdefaultnegotiation(struct cam_periph *periph); static void probedone(struct cam_periph *periph, union ccb *done_ccb); static void probecleanup(struct cam_periph *periph); static void xpt_find_quirk(struct cam_ed *device); #ifdef CAM_NEW_TRAN_CODE static void xpt_devise_transport(struct cam_path *path); #endif /* CAM_NEW_TRAN_CODE */ static void xpt_set_transfer_settings(struct ccb_trans_settings *cts, struct cam_ed *device, int async_update); static void xpt_toggle_tags(struct cam_path *path); static void xpt_start_tags(struct cam_path *path); static __inline int xpt_schedule_dev_allocq(struct cam_eb *bus, struct cam_ed *dev); static __inline int xpt_schedule_dev_sendq(struct cam_eb *bus, struct cam_ed *dev); static __inline int periph_is_queued(struct cam_periph *periph); static __inline int device_is_alloc_queued(struct cam_ed *device); static __inline int device_is_send_queued(struct cam_ed *device); static __inline int dev_allocq_is_runnable(struct cam_devq *devq); static __inline int xpt_schedule_dev_allocq(struct cam_eb *bus, struct cam_ed *dev) { int retval; if (dev->ccbq.devq_openings > 0) { if ((dev->flags & CAM_DEV_RESIZE_QUEUE_NEEDED) != 0) { cam_ccbq_resize(&dev->ccbq, dev->ccbq.dev_openings + dev->ccbq.dev_active); dev->flags &= ~CAM_DEV_RESIZE_QUEUE_NEEDED; } /* * The priority of a device waiting for CCB resources * is that of the the highest priority peripheral driver * enqueued. */ retval = xpt_schedule_dev(&bus->sim->devq->alloc_queue, &dev->alloc_ccb_entry.pinfo, CAMQ_GET_HEAD(&dev->drvq)->priority); } else { retval = 0; } return (retval); } static __inline int xpt_schedule_dev_sendq(struct cam_eb *bus, struct cam_ed *dev) { int retval; if (dev->ccbq.dev_openings > 0) { /* * The priority of a device waiting for controller * resources is that of the the highest priority CCB * enqueued. */ retval = xpt_schedule_dev(&bus->sim->devq->send_queue, &dev->send_ccb_entry.pinfo, CAMQ_GET_HEAD(&dev->ccbq.queue)->priority); } else { retval = 0; } return (retval); } static __inline int periph_is_queued(struct cam_periph *periph) { return (periph->pinfo.index != CAM_UNQUEUED_INDEX); } static __inline int device_is_alloc_queued(struct cam_ed *device) { return (device->alloc_ccb_entry.pinfo.index != CAM_UNQUEUED_INDEX); } static __inline int device_is_send_queued(struct cam_ed *device) { return (device->send_ccb_entry.pinfo.index != CAM_UNQUEUED_INDEX); } static __inline int dev_allocq_is_runnable(struct cam_devq *devq) { /* * Have work to do. * Have space to do more work. * Allowed to do work. */ return ((devq->alloc_queue.qfrozen_cnt == 0) && (devq->alloc_queue.entries > 0) && (devq->alloc_openings > 0)); } static void xpt_periph_init() { make_dev(&xpt_cdevsw, 0, UID_ROOT, GID_OPERATOR, 0600, "xpt0"); } static void probe_periph_init() { } static void xptdone(struct cam_periph *periph, union ccb *done_ccb) { /* Caller will release the CCB */ wakeup(&done_ccb->ccb_h.cbfcnp); } static int xptopen(dev_t dev, int flags, int fmt, struct proc *p) { int unit; unit = minor(dev) & 0xff; /* * Only allow read-write access. */ if (((flags & FWRITE) == 0) || ((flags & FREAD) == 0)) return(EPERM); /* * We don't allow nonblocking access. */ if ((flags & O_NONBLOCK) != 0) { printf("xpt%d: can't do nonblocking accesss\n", unit); return(ENODEV); } /* * We only have one transport layer right now. If someone accesses * us via something other than minor number 1, point out their * mistake. */ if (unit != 0) { printf("xptopen: got invalid xpt unit %d\n", unit); return(ENXIO); } /* Mark ourselves open */ xsoftc.flags |= XPT_FLAG_OPEN; return(0); } static int xptclose(dev_t dev, int flag, int fmt, struct proc *p) { int unit; unit = minor(dev) & 0xff; /* * We only have one transport layer right now. If someone accesses * us via something other than minor number 1, point out their * mistake. */ if (unit != 0) { printf("xptclose: got invalid xpt unit %d\n", unit); return(ENXIO); } /* Mark ourselves closed */ xsoftc.flags &= ~XPT_FLAG_OPEN; return(0); } static int xptioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) { int unit, error; error = 0; unit = minor(dev) & 0xff; /* * We only have one transport layer right now. If someone accesses * us via something other than minor number 1, point out their * mistake. */ if (unit != 0) { printf("xptioctl: got invalid xpt unit %d\n", unit); return(ENXIO); } switch(cmd) { /* * For the transport layer CAMIOCOMMAND ioctl, we really only want * to accept CCB types that don't quite make sense to send through a * passthrough driver. XPT_PATH_INQ is an exception to this, as stated * in the CAM spec. */ case CAMIOCOMMAND: { union ccb *ccb; union ccb *inccb; inccb = (union ccb *)addr; switch(inccb->ccb_h.func_code) { case XPT_SCAN_BUS: case XPT_RESET_BUS: if ((inccb->ccb_h.target_id != CAM_TARGET_WILDCARD) || (inccb->ccb_h.target_lun != CAM_LUN_WILDCARD)) { error = EINVAL; break; } /* FALLTHROUGH */ case XPT_PATH_INQ: case XPT_ENG_INQ: case XPT_SCAN_LUN: ccb = xpt_alloc_ccb(); /* * Create a path using the bus, target, and lun the * user passed in. */ if (xpt_create_path(&ccb->ccb_h.path, xpt_periph, inccb->ccb_h.path_id, inccb->ccb_h.target_id, inccb->ccb_h.target_lun) != CAM_REQ_CMP){ error = EINVAL; xpt_free_ccb(ccb); break; } /* Ensure all of our fields are correct */ xpt_setup_ccb(&ccb->ccb_h, ccb->ccb_h.path, inccb->ccb_h.pinfo.priority); xpt_merge_ccb(ccb, inccb); ccb->ccb_h.cbfcnp = xptdone; cam_periph_runccb(ccb, NULL, 0, 0, NULL); bcopy(ccb, inccb, sizeof(union ccb)); xpt_free_path(ccb->ccb_h.path); xpt_free_ccb(ccb); break; case XPT_DEBUG: { union ccb ccb; /* * This is an immediate CCB, so it's okay to * allocate it on the stack. */ /* * Create a path using the bus, target, and lun the * user passed in. */ if (xpt_create_path(&ccb.ccb_h.path, xpt_periph, inccb->ccb_h.path_id, inccb->ccb_h.target_id, inccb->ccb_h.target_lun) != CAM_REQ_CMP){ error = EINVAL; break; } /* Ensure all of our fields are correct */ xpt_setup_ccb(&ccb.ccb_h, ccb.ccb_h.path, inccb->ccb_h.pinfo.priority); xpt_merge_ccb(&ccb, inccb); ccb.ccb_h.cbfcnp = xptdone; xpt_action(&ccb); bcopy(&ccb, inccb, sizeof(union ccb)); xpt_free_path(ccb.ccb_h.path); break; } case XPT_DEV_MATCH: { struct cam_periph_map_info mapinfo; struct cam_path *old_path; /* * We can't deal with physical addresses for this * type of transaction. */ if (inccb->ccb_h.flags & CAM_DATA_PHYS) { error = EINVAL; break; } /* * Save this in case the caller had it set to * something in particular. */ old_path = inccb->ccb_h.path; /* * We really don't need a path for the matching * code. The path is needed because of the * debugging statements in xpt_action(). They * assume that the CCB has a valid path. */ inccb->ccb_h.path = xpt_periph->path; bzero(&mapinfo, sizeof(mapinfo)); /* * Map the pattern and match buffers into kernel * virtual address space. */ error = cam_periph_mapmem(inccb, &mapinfo); if (error) { inccb->ccb_h.path = old_path; break; } /* * This is an immediate CCB, we can send it on directly. */ xpt_action(inccb); /* * Map the buffers back into user space. */ cam_periph_unmapmem(inccb, &mapinfo); inccb->ccb_h.path = old_path; error = 0; break; } default: error = ENOTSUP; break; } break; } /* * This is the getpassthru ioctl. It takes a XPT_GDEVLIST ccb as input, * with the periphal driver name and unit name filled in. The other * fields don't really matter as input. The passthrough driver name * ("pass"), and unit number are passed back in the ccb. The current * device generation number, and the index into the device peripheral * driver list, and the status are also passed back. Note that * since we do everything in one pass, unlike the XPT_GDEVLIST ccb, * we never return a status of CAM_GDEVLIST_LIST_CHANGED. It is * (or rather should be) impossible for the device peripheral driver * list to change since we look at the whole thing in one pass, and * we do it with splcam protection. * */ case CAMGETPASSTHRU: { union ccb *ccb; struct cam_periph *periph; struct periph_driver **p_drv; char *name; u_int unit; u_int cur_generation; int base_periph_found; int splbreaknum; int s; ccb = (union ccb *)addr; unit = ccb->cgdl.unit_number; name = ccb->cgdl.periph_name; /* * Every 100 devices, we want to drop our spl protection to * give the software interrupt handler a chance to run. * Most systems won't run into this check, but this should * avoid starvation in the software interrupt handler in * large systems. */ splbreaknum = 100; ccb = (union ccb *)addr; base_periph_found = 0; /* * Sanity check -- make sure we don't get a null peripheral * driver name. */ if (*ccb->cgdl.periph_name == '\0') { error = EINVAL; break; } /* Keep the list from changing while we traverse it */ s = splcam(); ptstartover: cur_generation = xsoftc.generation; /* first find our driver in the list of drivers */ for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) if (strcmp((*p_drv)->driver_name, name) == 0) break; if (*p_drv == NULL) { splx(s); ccb->ccb_h.status = CAM_REQ_CMP_ERR; ccb->cgdl.status = CAM_GDEVLIST_ERROR; *ccb->cgdl.periph_name = '\0'; ccb->cgdl.unit_number = 0; error = ENOENT; break; } /* * Run through every peripheral instance of this driver * and check to see whether it matches the unit passed * in by the user. If it does, get out of the loops and * find the passthrough driver associated with that * peripheral driver. */ for (periph = TAILQ_FIRST(&(*p_drv)->units); periph != NULL; periph = TAILQ_NEXT(periph, unit_links)) { if (periph->unit_number == unit) { break; } else if (--splbreaknum == 0) { splx(s); s = splcam(); splbreaknum = 100; if (cur_generation != xsoftc.generation) goto ptstartover; } } /* * If we found the peripheral driver that the user passed * in, go through all of the peripheral drivers for that * particular device and look for a passthrough driver. */ if (periph != NULL) { struct cam_ed *device; int i; base_periph_found = 1; device = periph->path->device; for (i = 0, periph = SLIST_FIRST(&device->periphs); periph != NULL; periph = SLIST_NEXT(periph, periph_links), i++) { /* * Check to see whether we have a * passthrough device or not. */ if (strcmp(periph->periph_name, "pass") == 0) { /* * Fill in the getdevlist fields. */ strcpy(ccb->cgdl.periph_name, periph->periph_name); ccb->cgdl.unit_number = periph->unit_number; if (SLIST_NEXT(periph, periph_links)) ccb->cgdl.status = CAM_GDEVLIST_MORE_DEVS; else ccb->cgdl.status = CAM_GDEVLIST_LAST_DEVICE; ccb->cgdl.generation = device->generation; ccb->cgdl.index = i; /* * Fill in some CCB header fields * that the user may want. */ ccb->ccb_h.path_id = periph->path->bus->path_id; ccb->ccb_h.target_id = periph->path->target->target_id; ccb->ccb_h.target_lun = periph->path->device->lun_id; ccb->ccb_h.status = CAM_REQ_CMP; break; } } } /* * If the periph is null here, one of two things has * happened. The first possibility is that we couldn't * find the unit number of the particular peripheral driver * that the user is asking about. e.g. the user asks for * the passthrough driver for "da11". We find the list of * "da" peripherals all right, but there is no unit 11. * The other possibility is that we went through the list * of peripheral drivers attached to the device structure, * but didn't find one with the name "pass". Either way, * we return ENOENT, since we couldn't find something. */ if (periph == NULL) { ccb->ccb_h.status = CAM_REQ_CMP_ERR; ccb->cgdl.status = CAM_GDEVLIST_ERROR; *ccb->cgdl.periph_name = '\0'; ccb->cgdl.unit_number = 0; error = ENOENT; /* * It is unfortunate that this is even necessary, * but there are many, many clueless users out there. * If this is true, the user is looking for the * passthrough driver, but doesn't have one in his * kernel. */ if (base_periph_found == 1) { printf("xptioctl: pass driver is not in the " "kernel\n"); printf("xptioctl: put \"device pass0\" in " "your kernel config file\n"); } } splx(s); break; } default: error = ENOTTY; break; } return(error); } static int cam_module_event_handler(module_t mod, int what, void *arg) { if (what == MOD_LOAD) { xpt_init(NULL); } else if (what == MOD_UNLOAD) { return EBUSY; } return 0; } /* Functions accessed by the peripheral drivers */ static void xpt_init(dummy) void *dummy; { struct cam_sim *xpt_sim; struct cam_path *path; struct cam_devq *devq; cam_status status; TAILQ_INIT(&xpt_busses); TAILQ_INIT(&cam_bioq); TAILQ_INIT(&cam_netq); SLIST_INIT(&ccb_freeq); STAILQ_INIT(&highpowerq); /* * The xpt layer is, itself, the equivelent of a SIM. * Allow 16 ccbs in the ccb pool for it. This should * give decent parallelism when we probe busses and * perform other XPT functions. */ devq = cam_simq_alloc(16); xpt_sim = cam_sim_alloc(xptaction, xptpoll, "xpt", /*softc*/NULL, /*unit*/0, /*max_dev_transactions*/0, /*max_tagged_dev_transactions*/0, devq); xpt_max_ccbs = 16; xpt_bus_register(xpt_sim, /*bus #*/0); /* * Looking at the XPT from the SIM layer, the XPT is * the equivelent of a peripheral driver. Allocate * a peripheral driver entry for us. */ if ((status = xpt_create_path(&path, NULL, CAM_XPT_PATH_ID, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD)) != CAM_REQ_CMP) { printf("xpt_init: xpt_create_path failed with status %#x," " failing attach\n", status); return; } cam_periph_alloc(xptregister, NULL, NULL, NULL, "xpt", CAM_PERIPH_BIO, path, NULL, 0, NULL); xpt_free_path(path); xpt_sim->softc = xpt_periph; /* * Register a callback for when interrupts are enabled. */ xpt_config_hook = (struct intr_config_hook *)malloc(sizeof(struct intr_config_hook), M_TEMP, M_NOWAIT | M_ZERO); if (xpt_config_hook == NULL) { printf("xpt_init: Cannot malloc config hook " "- failing attach\n"); return; } xpt_config_hook->ich_func = xpt_config; if (config_intrhook_establish(xpt_config_hook) != 0) { free (xpt_config_hook, M_TEMP); printf("xpt_init: config_intrhook_establish failed " "- failing attach\n"); } /* Install our software interrupt handlers */ swi_add(NULL, "camnet", camisr, &cam_netq, SWI_CAMNET, 0, &camnet_ih); swi_add(NULL, "cambio", camisr, &cam_bioq, SWI_CAMBIO, 0, &cambio_ih); } static cam_status xptregister(struct cam_periph *periph, void *arg) { if (periph == NULL) { printf("xptregister: periph was NULL!!\n"); return(CAM_REQ_CMP_ERR); } periph->softc = NULL; xpt_periph = periph; return(CAM_REQ_CMP); } int32_t xpt_add_periph(struct cam_periph *periph) { struct cam_ed *device; int32_t status; struct periph_list *periph_head; device = periph->path->device; periph_head = &device->periphs; status = CAM_REQ_CMP; if (device != NULL) { int s; /* * Make room for this peripheral * so it will fit in the queue * when it's scheduled to run */ s = splsoftcam(); status = camq_resize(&device->drvq, device->drvq.array_size + 1); device->generation++; SLIST_INSERT_HEAD(periph_head, periph, periph_links); splx(s); } xsoftc.generation++; return (status); } void xpt_remove_periph(struct cam_periph *periph) { struct cam_ed *device; device = periph->path->device; if (device != NULL) { int s; struct periph_list *periph_head; periph_head = &device->periphs; /* Release the slot for this peripheral */ s = splsoftcam(); camq_resize(&device->drvq, device->drvq.array_size - 1); device->generation++; SLIST_REMOVE(periph_head, periph, cam_periph, periph_links); splx(s); } xsoftc.generation++; } #ifdef CAM_NEW_TRAN_CODE void xpt_announce_periph(struct cam_periph *periph, char *announce_string) { struct ccb_pathinq cpi; struct ccb_trans_settings cts; struct cam_path *path; u_int speed; u_int freq; u_int mb; int s; path = periph->path; /* * To ensure that this is printed in one piece, * mask out CAM interrupts. */ s = splsoftcam(); printf("%s%d at %s%d bus %d target %d lun %d\n", periph->periph_name, periph->unit_number, path->bus->sim->sim_name, path->bus->sim->unit_number, path->bus->sim->bus_id, path->target->target_id, path->device->lun_id); printf("%s%d: ", periph->periph_name, periph->unit_number); scsi_print_inquiry(&path->device->inq_data); if ((bootverbose) && (path->device->serial_num_len > 0)) { /* Don't wrap the screen - print only the first 60 chars */ printf("%s%d: Serial Number %.60s\n", periph->periph_name, periph->unit_number, path->device->serial_num); } xpt_setup_ccb(&cts.ccb_h, path, /*priority*/1); cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; cts.type = CTS_TYPE_CURRENT_SETTINGS; xpt_action((union ccb*)&cts); /* Ask the SIM for its base transfer speed */ xpt_setup_ccb(&cpi.ccb_h, path, /*priority*/1); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); speed = cpi.base_transfer_speed; freq = 0; if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_SPI) { struct ccb_trans_settings_spi *spi; spi = &cts.xport_specific.spi; if ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) != 0 && spi->sync_offset != 0) { freq = scsi_calc_syncsrate(spi->sync_period); speed = freq; } if ((spi->valid & CTS_SPI_VALID_BUS_WIDTH) != 0) speed *= (0x01 << spi->bus_width); } if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_FC) { struct ccb_trans_settings_fc *fc; fc = &cts.xport_specific.fc; speed = fc->bitrate; } mb = speed / 1000; if (mb > 0) printf("%s%d: %d.%03dMB/s transfers", periph->periph_name, periph->unit_number, mb, speed % 1000); else printf("%s%d: %dKB/s transfers", periph->periph_name, periph->unit_number, speed); /* Report additional information about SPI connections */ if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_SPI) { struct ccb_trans_settings_spi *spi; spi = &cts.xport_specific.spi; if (freq != 0) { printf(" (%d.%03dMHz%s, offset %d", freq / 1000, freq % 1000, (spi->ppr_options & MSG_EXT_PPR_DT_REQ) != 0 ? " DT" : "", spi->sync_offset); } if ((spi->valid & CTS_SPI_VALID_BUS_WIDTH) != 0 && spi->bus_width > 0) { if (freq != 0) { printf(", "); } else { printf(" ("); } printf("%dbit)", 8 * (0x01 << spi->bus_width)); } else if (freq != 0) { printf(")"); } } if (cts.ccb_h.status == CAM_REQ_CMP && cts.transport == XPORT_FC) { struct ccb_trans_settings_fc *fc; fc = &cts.xport_specific.fc; if (fc->wwnn) printf(" WWNN %q", (quad_t) fc->wwnn); if (fc->wwpn) printf(" WWPN %q", (quad_t) fc->wwpn); if (fc->port) printf(" PortID %u", fc->port); } if (path->device->inq_flags & SID_CmdQue || path->device->flags & CAM_DEV_TAG_AFTER_COUNT) { printf("\n%s%d: Tagged Queueing Enabled", periph->periph_name, periph->unit_number); } printf("\n"); /* * We only want to print the caller's announce string if they've * passed one in.. */ if (announce_string != NULL) printf("%s%d: %s\n", periph->periph_name, periph->unit_number, announce_string); splx(s); } #else /* CAM_NEW_TRAN_CODE */ void xpt_announce_periph(struct cam_periph *periph, char *announce_string) { int s; u_int mb; struct cam_path *path; struct ccb_trans_settings cts; path = periph->path; /* * To ensure that this is printed in one piece, * mask out CAM interrupts. */ s = splsoftcam(); printf("%s%d at %s%d bus %d target %d lun %d\n", periph->periph_name, periph->unit_number, path->bus->sim->sim_name, path->bus->sim->unit_number, path->bus->sim->bus_id, path->target->target_id, path->device->lun_id); printf("%s%d: ", periph->periph_name, periph->unit_number); scsi_print_inquiry(&path->device->inq_data); if ((bootverbose) && (path->device->serial_num_len > 0)) { /* Don't wrap the screen - print only the first 60 chars */ printf("%s%d: Serial Number %.60s\n", periph->periph_name, periph->unit_number, path->device->serial_num); } xpt_setup_ccb(&cts.ccb_h, path, /*priority*/1); cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; cts.flags = CCB_TRANS_CURRENT_SETTINGS; xpt_action((union ccb*)&cts); if (cts.ccb_h.status == CAM_REQ_CMP) { u_int speed; u_int freq; if ((cts.valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0 && cts.sync_offset != 0) { freq = scsi_calc_syncsrate(cts.sync_period); speed = freq; } else { struct ccb_pathinq cpi; /* Ask the SIM for its base transfer speed */ xpt_setup_ccb(&cpi.ccb_h, path, /*priority*/1); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); speed = cpi.base_transfer_speed; freq = 0; } if ((cts.valid & CCB_TRANS_BUS_WIDTH_VALID) != 0) speed *= (0x01 << cts.bus_width); mb = speed / 1000; if (mb > 0) printf("%s%d: %d.%03dMB/s transfers", periph->periph_name, periph->unit_number, mb, speed % 1000); else printf("%s%d: %dKB/s transfers", periph->periph_name, periph->unit_number, speed); if ((cts.valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0 && cts.sync_offset != 0) { printf(" (%d.%03dMHz, offset %d", freq / 1000, freq % 1000, cts.sync_offset); } if ((cts.valid & CCB_TRANS_BUS_WIDTH_VALID) != 0 && cts.bus_width > 0) { if ((cts.valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0 && cts.sync_offset != 0) { printf(", "); } else { printf(" ("); } printf("%dbit)", 8 * (0x01 << cts.bus_width)); } else if ((cts.valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0 && cts.sync_offset != 0) { printf(")"); } if (path->device->inq_flags & SID_CmdQue || path->device->flags & CAM_DEV_TAG_AFTER_COUNT) { printf(", Tagged Queueing Enabled"); } printf("\n"); } else if (path->device->inq_flags & SID_CmdQue || path->device->flags & CAM_DEV_TAG_AFTER_COUNT) { printf("%s%d: Tagged Queueing Enabled\n", periph->periph_name, periph->unit_number); } /* * We only want to print the caller's announce string if they've * passed one in.. */ if (announce_string != NULL) printf("%s%d: %s\n", periph->periph_name, periph->unit_number, announce_string); splx(s); } #endif /* CAM_NEW_TRAN_CODE */ static dev_match_ret xptbusmatch(struct dev_match_pattern *patterns, u_int num_patterns, struct cam_eb *bus) { dev_match_ret retval; int i; retval = DM_RET_NONE; /* * If we aren't given something to match against, that's an error. */ if (bus == NULL) return(DM_RET_ERROR); /* * If there are no match entries, then this bus matches no * matter what. */ if ((patterns == NULL) || (num_patterns == 0)) return(DM_RET_DESCEND | DM_RET_COPY); for (i = 0; i < num_patterns; i++) { struct bus_match_pattern *cur_pattern; /* * If the pattern in question isn't for a bus node, we * aren't interested. However, we do indicate to the * calling routine that we should continue descending the * tree, since the user wants to match against lower-level * EDT elements. */ if (patterns[i].type != DEV_MATCH_BUS) { if ((retval & DM_RET_ACTION_MASK) == DM_RET_NONE) retval |= DM_RET_DESCEND; continue; } cur_pattern = &patterns[i].pattern.bus_pattern; /* * If they want to match any bus node, we give them any * device node. */ if (cur_pattern->flags == BUS_MATCH_ANY) { /* set the copy flag */ retval |= DM_RET_COPY; /* * If we've already decided on an action, go ahead * and return. */ if ((retval & DM_RET_ACTION_MASK) != DM_RET_NONE) return(retval); } /* * Not sure why someone would do this... */ if (cur_pattern->flags == BUS_MATCH_NONE) continue; if (((cur_pattern->flags & BUS_MATCH_PATH) != 0) && (cur_pattern->path_id != bus->path_id)) continue; if (((cur_pattern->flags & BUS_MATCH_BUS_ID) != 0) && (cur_pattern->bus_id != bus->sim->bus_id)) continue; if (((cur_pattern->flags & BUS_MATCH_UNIT) != 0) && (cur_pattern->unit_number != bus->sim->unit_number)) continue; if (((cur_pattern->flags & BUS_MATCH_NAME) != 0) && (strncmp(cur_pattern->dev_name, bus->sim->sim_name, DEV_IDLEN) != 0)) continue; /* * If we get to this point, the user definitely wants * information on this bus. So tell the caller to copy the * data out. */ retval |= DM_RET_COPY; /* * If the return action has been set to descend, then we * know that we've already seen a non-bus matching * expression, therefore we need to further descend the tree. * This won't change by continuing around the loop, so we * go ahead and return. If we haven't seen a non-bus * matching expression, we keep going around the loop until * we exhaust the matching expressions. We'll set the stop * flag once we fall out of the loop. */ if ((retval & DM_RET_ACTION_MASK) == DM_RET_DESCEND) return(retval); } /* * If the return action hasn't been set to descend yet, that means * we haven't seen anything other than bus matching patterns. So * tell the caller to stop descending the tree -- the user doesn't * want to match against lower level tree elements. */ if ((retval & DM_RET_ACTION_MASK) == DM_RET_NONE) retval |= DM_RET_STOP; return(retval); } static dev_match_ret xptdevicematch(struct dev_match_pattern *patterns, u_int num_patterns, struct cam_ed *device) { dev_match_ret retval; int i; retval = DM_RET_NONE; /* * If we aren't given something to match against, that's an error. */ if (device == NULL) return(DM_RET_ERROR); /* * If there are no match entries, then this device matches no * matter what. */ if ((patterns == NULL) || (patterns == 0)) return(DM_RET_DESCEND | DM_RET_COPY); for (i = 0; i < num_patterns; i++) { struct device_match_pattern *cur_pattern; /* * If the pattern in question isn't for a device node, we * aren't interested. */ if (patterns[i].type != DEV_MATCH_DEVICE) { if ((patterns[i].type == DEV_MATCH_PERIPH) && ((retval & DM_RET_ACTION_MASK) == DM_RET_NONE)) retval |= DM_RET_DESCEND; continue; } cur_pattern = &patterns[i].pattern.device_pattern; /* * If they want to match any device node, we give them any * device node. */ if (cur_pattern->flags == DEV_MATCH_ANY) { /* set the copy flag */ retval |= DM_RET_COPY; /* * If we've already decided on an action, go ahead * and return. */ if ((retval & DM_RET_ACTION_MASK) != DM_RET_NONE) return(retval); } /* * Not sure why someone would do this... */ if (cur_pattern->flags == DEV_MATCH_NONE) continue; if (((cur_pattern->flags & DEV_MATCH_PATH) != 0) && (cur_pattern->path_id != device->target->bus->path_id)) continue; if (((cur_pattern->flags & DEV_MATCH_TARGET) != 0) && (cur_pattern->target_id != device->target->target_id)) continue; if (((cur_pattern->flags & DEV_MATCH_LUN) != 0) && (cur_pattern->target_lun != device->lun_id)) continue; if (((cur_pattern->flags & DEV_MATCH_INQUIRY) != 0) && (cam_quirkmatch((caddr_t)&device->inq_data, (caddr_t)&cur_pattern->inq_pat, 1, sizeof(cur_pattern->inq_pat), scsi_static_inquiry_match) == NULL)) continue; /* * If we get to this point, the user definitely wants * information on this device. So tell the caller to copy * the data out. */ retval |= DM_RET_COPY; /* * If the return action has been set to descend, then we * know that we've already seen a peripheral matching * expression, therefore we need to further descend the tree. * This won't change by continuing around the loop, so we * go ahead and return. If we haven't seen a peripheral * matching expression, we keep going around the loop until * we exhaust the matching expressions. We'll set the stop * flag once we fall out of the loop. */ if ((retval & DM_RET_ACTION_MASK) == DM_RET_DESCEND) return(retval); } /* * If the return action hasn't been set to descend yet, that means * we haven't seen any peripheral matching patterns. So tell the * caller to stop descending the tree -- the user doesn't want to * match against lower level tree elements. */ if ((retval & DM_RET_ACTION_MASK) == DM_RET_NONE) retval |= DM_RET_STOP; return(retval); } /* * Match a single peripheral against any number of match patterns. */ static dev_match_ret xptperiphmatch(struct dev_match_pattern *patterns, u_int num_patterns, struct cam_periph *periph) { dev_match_ret retval; int i; /* * If we aren't given something to match against, that's an error. */ if (periph == NULL) return(DM_RET_ERROR); /* * If there are no match entries, then this peripheral matches no * matter what. */ if ((patterns == NULL) || (num_patterns == 0)) return(DM_RET_STOP | DM_RET_COPY); /* * There aren't any nodes below a peripheral node, so there's no * reason to descend the tree any further. */ retval = DM_RET_STOP; for (i = 0; i < num_patterns; i++) { struct periph_match_pattern *cur_pattern; /* * If the pattern in question isn't for a peripheral, we * aren't interested. */ if (patterns[i].type != DEV_MATCH_PERIPH) continue; cur_pattern = &patterns[i].pattern.periph_pattern; /* * If they want to match on anything, then we will do so. */ if (cur_pattern->flags == PERIPH_MATCH_ANY) { /* set the copy flag */ retval |= DM_RET_COPY; /* * We've already set the return action to stop, * since there are no nodes below peripherals in * the tree. */ return(retval); } /* * Not sure why someone would do this... */ if (cur_pattern->flags == PERIPH_MATCH_NONE) continue; if (((cur_pattern->flags & PERIPH_MATCH_PATH) != 0) && (cur_pattern->path_id != periph->path->bus->path_id)) continue; /* * For the target and lun id's, we have to make sure the * target and lun pointers aren't NULL. The xpt peripheral * has a wildcard target and device. */ if (((cur_pattern->flags & PERIPH_MATCH_TARGET) != 0) && ((periph->path->target == NULL) ||(cur_pattern->target_id != periph->path->target->target_id))) continue; if (((cur_pattern->flags & PERIPH_MATCH_LUN) != 0) && ((periph->path->device == NULL) || (cur_pattern->target_lun != periph->path->device->lun_id))) continue; if (((cur_pattern->flags & PERIPH_MATCH_UNIT) != 0) && (cur_pattern->unit_number != periph->unit_number)) continue; if (((cur_pattern->flags & PERIPH_MATCH_NAME) != 0) && (strncmp(cur_pattern->periph_name, periph->periph_name, DEV_IDLEN) != 0)) continue; /* * If we get to this point, the user definitely wants * information on this peripheral. So tell the caller to * copy the data out. */ retval |= DM_RET_COPY; /* * The return action has already been set to stop, since * peripherals don't have any nodes below them in the EDT. */ return(retval); } /* * If we get to this point, the peripheral that was passed in * doesn't match any of the patterns. */ return(retval); } static int xptedtbusfunc(struct cam_eb *bus, void *arg) { struct ccb_dev_match *cdm; dev_match_ret retval; cdm = (struct ccb_dev_match *)arg; /* * If our position is for something deeper in the tree, that means * that we've already seen this node. So, we keep going down. */ if ((cdm->pos.position_type & CAM_DEV_POS_BUS) && (cdm->pos.cookie.bus == bus) && (cdm->pos.position_type & CAM_DEV_POS_TARGET) && (cdm->pos.cookie.target != NULL)) retval = DM_RET_DESCEND; else retval = xptbusmatch(cdm->patterns, cdm->num_patterns, bus); /* * If we got an error, bail out of the search. */ if ((retval & DM_RET_ACTION_MASK) == DM_RET_ERROR) { cdm->status = CAM_DEV_MATCH_ERROR; return(0); } /* * If the copy flag is set, copy this bus out. */ if (retval & DM_RET_COPY) { int spaceleft, j; spaceleft = cdm->match_buf_len - (cdm->num_matches * sizeof(struct dev_match_result)); /* * If we don't have enough space to put in another * match result, save our position and tell the * user there are more devices to check. */ if (spaceleft < sizeof(struct dev_match_result)) { bzero(&cdm->pos, sizeof(cdm->pos)); cdm->pos.position_type = CAM_DEV_POS_EDT | CAM_DEV_POS_BUS; cdm->pos.cookie.bus = bus; cdm->pos.generations[CAM_BUS_GENERATION]= bus_generation; cdm->status = CAM_DEV_MATCH_MORE; return(0); } j = cdm->num_matches; cdm->num_matches++; cdm->matches[j].type = DEV_MATCH_BUS; cdm->matches[j].result.bus_result.path_id = bus->path_id; cdm->matches[j].result.bus_result.bus_id = bus->sim->bus_id; cdm->matches[j].result.bus_result.unit_number = bus->sim->unit_number; strncpy(cdm->matches[j].result.bus_result.dev_name, bus->sim->sim_name, DEV_IDLEN); } /* * If the user is only interested in busses, there's no * reason to descend to the next level in the tree. */ if ((retval & DM_RET_ACTION_MASK) == DM_RET_STOP) return(1); /* * If there is a target generation recorded, check it to * make sure the target list hasn't changed. */ if ((cdm->pos.position_type & CAM_DEV_POS_BUS) && (bus == cdm->pos.cookie.bus) && (cdm->pos.position_type & CAM_DEV_POS_TARGET) && (cdm->pos.generations[CAM_TARGET_GENERATION] != 0) && (cdm->pos.generations[CAM_TARGET_GENERATION] != bus->generation)) { cdm->status = CAM_DEV_MATCH_LIST_CHANGED; return(0); } if ((cdm->pos.position_type & CAM_DEV_POS_BUS) && (cdm->pos.cookie.bus == bus) && (cdm->pos.position_type & CAM_DEV_POS_TARGET) && (cdm->pos.cookie.target != NULL)) return(xpttargettraverse(bus, (struct cam_et *)cdm->pos.cookie.target, xptedttargetfunc, arg)); else return(xpttargettraverse(bus, NULL, xptedttargetfunc, arg)); } static int xptedttargetfunc(struct cam_et *target, void *arg) { struct ccb_dev_match *cdm; cdm = (struct ccb_dev_match *)arg; /* * If there is a device list generation recorded, check it to * make sure the device list hasn't changed. */ if ((cdm->pos.position_type & CAM_DEV_POS_BUS) && (cdm->pos.cookie.bus == target->bus) && (cdm->pos.position_type & CAM_DEV_POS_TARGET) && (cdm->pos.cookie.target == target) && (cdm->pos.position_type & CAM_DEV_POS_DEVICE) && (cdm->pos.generations[CAM_DEV_GENERATION] != 0) && (cdm->pos.generations[CAM_DEV_GENERATION] != target->generation)) { cdm->status = CAM_DEV_MATCH_LIST_CHANGED; return(0); } if ((cdm->pos.position_type & CAM_DEV_POS_BUS) && (cdm->pos.cookie.bus == target->bus) && (cdm->pos.position_type & CAM_DEV_POS_TARGET) && (cdm->pos.cookie.target == target) && (cdm->pos.position_type & CAM_DEV_POS_DEVICE) && (cdm->pos.cookie.device != NULL)) return(xptdevicetraverse(target, (struct cam_ed *)cdm->pos.cookie.device, xptedtdevicefunc, arg)); else return(xptdevicetraverse(target, NULL, xptedtdevicefunc, arg)); } static int xptedtdevicefunc(struct cam_ed *device, void *arg) { struct ccb_dev_match *cdm; dev_match_ret retval; cdm = (struct ccb_dev_match *)arg; /* * If our position is for something deeper in the tree, that means * that we've already seen this node. So, we keep going down. */ if ((cdm->pos.position_type & CAM_DEV_POS_DEVICE) && (cdm->pos.cookie.device == device) && (cdm->pos.position_type & CAM_DEV_POS_PERIPH) && (cdm->pos.cookie.periph != NULL)) retval = DM_RET_DESCEND; else retval = xptdevicematch(cdm->patterns, cdm->num_patterns, device); if ((retval & DM_RET_ACTION_MASK) == DM_RET_ERROR) { cdm->status = CAM_DEV_MATCH_ERROR; return(0); } /* * If the copy flag is set, copy this device out. */ if (retval & DM_RET_COPY) { int spaceleft, j; spaceleft = cdm->match_buf_len - (cdm->num_matches * sizeof(struct dev_match_result)); /* * If we don't have enough space to put in another * match result, save our position and tell the * user there are more devices to check. */ if (spaceleft < sizeof(struct dev_match_result)) { bzero(&cdm->pos, sizeof(cdm->pos)); cdm->pos.position_type = CAM_DEV_POS_EDT | CAM_DEV_POS_BUS | CAM_DEV_POS_TARGET | CAM_DEV_POS_DEVICE; cdm->pos.cookie.bus = device->target->bus; cdm->pos.generations[CAM_BUS_GENERATION]= bus_generation; cdm->pos.cookie.target = device->target; cdm->pos.generations[CAM_TARGET_GENERATION] = device->target->bus->generation; cdm->pos.cookie.device = device; cdm->pos.generations[CAM_DEV_GENERATION] = device->target->generation; cdm->status = CAM_DEV_MATCH_MORE; return(0); } j = cdm->num_matches; cdm->num_matches++; cdm->matches[j].type = DEV_MATCH_DEVICE; cdm->matches[j].result.device_result.path_id = device->target->bus->path_id; cdm->matches[j].result.device_result.target_id = device->target->target_id; cdm->matches[j].result.device_result.target_lun = device->lun_id; bcopy(&device->inq_data, &cdm->matches[j].result.device_result.inq_data, sizeof(struct scsi_inquiry_data)); /* Let the user know whether this device is unconfigured */ if (device->flags & CAM_DEV_UNCONFIGURED) cdm->matches[j].result.device_result.flags = DEV_RESULT_UNCONFIGURED; else cdm->matches[j].result.device_result.flags = DEV_RESULT_NOFLAG; } /* * If the user isn't interested in peripherals, don't descend * the tree any further. */ if ((retval & DM_RET_ACTION_MASK) == DM_RET_STOP) return(1); /* * If there is a peripheral list generation recorded, make sure * it hasn't changed. */ if ((cdm->pos.position_type & CAM_DEV_POS_BUS) && (device->target->bus == cdm->pos.cookie.bus) && (cdm->pos.position_type & CAM_DEV_POS_TARGET) && (device->target == cdm->pos.cookie.target) && (cdm->pos.position_type & CAM_DEV_POS_DEVICE) && (device == cdm->pos.cookie.device) && (cdm->pos.position_type & CAM_DEV_POS_PERIPH) && (cdm->pos.generations[CAM_PERIPH_GENERATION] != 0) && (cdm->pos.generations[CAM_PERIPH_GENERATION] != device->generation)){ cdm->status = CAM_DEV_MATCH_LIST_CHANGED; return(0); } if ((cdm->pos.position_type & CAM_DEV_POS_BUS) && (cdm->pos.cookie.bus == device->target->bus) && (cdm->pos.position_type & CAM_DEV_POS_TARGET) && (cdm->pos.cookie.target == device->target) && (cdm->pos.position_type & CAM_DEV_POS_DEVICE) && (cdm->pos.cookie.device == device) && (cdm->pos.position_type & CAM_DEV_POS_PERIPH) && (cdm->pos.cookie.periph != NULL)) return(xptperiphtraverse(device, (struct cam_periph *)cdm->pos.cookie.periph, xptedtperiphfunc, arg)); else return(xptperiphtraverse(device, NULL, xptedtperiphfunc, arg)); } static int xptedtperiphfunc(struct cam_periph *periph, void *arg) { struct ccb_dev_match *cdm; dev_match_ret retval; cdm = (struct ccb_dev_match *)arg; retval = xptperiphmatch(cdm->patterns, cdm->num_patterns, periph); if ((retval & DM_RET_ACTION_MASK) == DM_RET_ERROR) { cdm->status = CAM_DEV_MATCH_ERROR; return(0); } /* * If the copy flag is set, copy this peripheral out. */ if (retval & DM_RET_COPY) { int spaceleft, j; spaceleft = cdm->match_buf_len - (cdm->num_matches * sizeof(struct dev_match_result)); /* * If we don't have enough space to put in another * match result, save our position and tell the * user there are more devices to check. */ if (spaceleft < sizeof(struct dev_match_result)) { bzero(&cdm->pos, sizeof(cdm->pos)); cdm->pos.position_type = CAM_DEV_POS_EDT | CAM_DEV_POS_BUS | CAM_DEV_POS_TARGET | CAM_DEV_POS_DEVICE | CAM_DEV_POS_PERIPH; cdm->pos.cookie.bus = periph->path->bus; cdm->pos.generations[CAM_BUS_GENERATION]= bus_generation; cdm->pos.cookie.target = periph->path->target; cdm->pos.generations[CAM_TARGET_GENERATION] = periph->path->bus->generation; cdm->pos.cookie.device = periph->path->device; cdm->pos.generations[CAM_DEV_GENERATION] = periph->path->target->generation; cdm->pos.cookie.periph = periph; cdm->pos.generations[CAM_PERIPH_GENERATION] = periph->path->device->generation; cdm->status = CAM_DEV_MATCH_MORE; return(0); } j = cdm->num_matches; cdm->num_matches++; cdm->matches[j].type = DEV_MATCH_PERIPH; cdm->matches[j].result.periph_result.path_id = periph->path->bus->path_id; cdm->matches[j].result.periph_result.target_id = periph->path->target->target_id; cdm->matches[j].result.periph_result.target_lun = periph->path->device->lun_id; cdm->matches[j].result.periph_result.unit_number = periph->unit_number; strncpy(cdm->matches[j].result.periph_result.periph_name, periph->periph_name, DEV_IDLEN); } return(1); } static int xptedtmatch(struct ccb_dev_match *cdm) { int ret; cdm->num_matches = 0; /* * Check the bus list generation. If it has changed, the user * needs to reset everything and start over. */ if ((cdm->pos.position_type & CAM_DEV_POS_BUS) && (cdm->pos.generations[CAM_BUS_GENERATION] != 0) && (cdm->pos.generations[CAM_BUS_GENERATION] != bus_generation)) { cdm->status = CAM_DEV_MATCH_LIST_CHANGED; return(0); } if ((cdm->pos.position_type & CAM_DEV_POS_BUS) && (cdm->pos.cookie.bus != NULL)) ret = xptbustraverse((struct cam_eb *)cdm->pos.cookie.bus, xptedtbusfunc, cdm); else ret = xptbustraverse(NULL, xptedtbusfunc, cdm); /* * If we get back 0, that means that we had to stop before fully * traversing the EDT. It also means that one of the subroutines * has set the status field to the proper value. If we get back 1, * we've fully traversed the EDT and copied out any matching entries. */ if (ret == 1) cdm->status = CAM_DEV_MATCH_LAST; return(ret); } static int xptplistpdrvfunc(struct periph_driver **pdrv, void *arg) { struct ccb_dev_match *cdm; cdm = (struct ccb_dev_match *)arg; if ((cdm->pos.position_type & CAM_DEV_POS_PDPTR) && (cdm->pos.cookie.pdrv == pdrv) && (cdm->pos.position_type & CAM_DEV_POS_PERIPH) && (cdm->pos.generations[CAM_PERIPH_GENERATION] != 0) && (cdm->pos.generations[CAM_PERIPH_GENERATION] != (*pdrv)->generation)) { cdm->status = CAM_DEV_MATCH_LIST_CHANGED; return(0); } if ((cdm->pos.position_type & CAM_DEV_POS_PDPTR) && (cdm->pos.cookie.pdrv == pdrv) && (cdm->pos.position_type & CAM_DEV_POS_PERIPH) && (cdm->pos.cookie.periph != NULL)) return(xptpdperiphtraverse(pdrv, (struct cam_periph *)cdm->pos.cookie.periph, xptplistperiphfunc, arg)); else return(xptpdperiphtraverse(pdrv, NULL,xptplistperiphfunc, arg)); } static int xptplistperiphfunc(struct cam_periph *periph, void *arg) { struct ccb_dev_match *cdm; dev_match_ret retval; cdm = (struct ccb_dev_match *)arg; retval = xptperiphmatch(cdm->patterns, cdm->num_patterns, periph); if ((retval & DM_RET_ACTION_MASK) == DM_RET_ERROR) { cdm->status = CAM_DEV_MATCH_ERROR; return(0); } /* * If the copy flag is set, copy this peripheral out. */ if (retval & DM_RET_COPY) { int spaceleft, j; spaceleft = cdm->match_buf_len - (cdm->num_matches * sizeof(struct dev_match_result)); /* * If we don't have enough space to put in another * match result, save our position and tell the * user there are more devices to check. */ if (spaceleft < sizeof(struct dev_match_result)) { struct periph_driver **pdrv; pdrv = NULL; bzero(&cdm->pos, sizeof(cdm->pos)); cdm->pos.position_type = CAM_DEV_POS_PDRV | CAM_DEV_POS_PDPTR | CAM_DEV_POS_PERIPH; /* * This may look a bit non-sensical, but it is * actually quite logical. There are very few * peripheral drivers, and bloating every peripheral * structure with a pointer back to its parent * peripheral driver linker set entry would cost * more in the long run than doing this quick lookup. */ for (pdrv = periph_drivers; *pdrv != NULL; pdrv++) { if (strcmp((*pdrv)->driver_name, periph->periph_name) == 0) break; } if (pdrv == NULL) { cdm->status = CAM_DEV_MATCH_ERROR; return(0); } cdm->pos.cookie.pdrv = pdrv; /* * The periph generation slot does double duty, as * does the periph pointer slot. They are used for * both edt and pdrv lookups and positioning. */ cdm->pos.cookie.periph = periph; cdm->pos.generations[CAM_PERIPH_GENERATION] = (*pdrv)->generation; cdm->status = CAM_DEV_MATCH_MORE; return(0); } j = cdm->num_matches; cdm->num_matches++; cdm->matches[j].type = DEV_MATCH_PERIPH; cdm->matches[j].result.periph_result.path_id = periph->path->bus->path_id; /* * The transport layer peripheral doesn't have a target or * lun. */ if (periph->path->target) cdm->matches[j].result.periph_result.target_id = periph->path->target->target_id; else cdm->matches[j].result.periph_result.target_id = -1; if (periph->path->device) cdm->matches[j].result.periph_result.target_lun = periph->path->device->lun_id; else cdm->matches[j].result.periph_result.target_lun = -1; cdm->matches[j].result.periph_result.unit_number = periph->unit_number; strncpy(cdm->matches[j].result.periph_result.periph_name, periph->periph_name, DEV_IDLEN); } return(1); } static int xptperiphlistmatch(struct ccb_dev_match *cdm) { int ret; cdm->num_matches = 0; /* * At this point in the edt traversal function, we check the bus * list generation to make sure that no busses have been added or * removed since the user last sent a XPT_DEV_MATCH ccb through. * For the peripheral driver list traversal function, however, we * don't have to worry about new peripheral driver types coming or * going; they're in a linker set, and therefore can't change * without a recompile. */ if ((cdm->pos.position_type & CAM_DEV_POS_PDPTR) && (cdm->pos.cookie.pdrv != NULL)) ret = xptpdrvtraverse( (struct periph_driver **)cdm->pos.cookie.pdrv, xptplistpdrvfunc, cdm); else ret = xptpdrvtraverse(NULL, xptplistpdrvfunc, cdm); /* * If we get back 0, that means that we had to stop before fully * traversing the peripheral driver tree. It also means that one of * the subroutines has set the status field to the proper value. If * we get back 1, we've fully traversed the EDT and copied out any * matching entries. */ if (ret == 1) cdm->status = CAM_DEV_MATCH_LAST; return(ret); } static int xptbustraverse(struct cam_eb *start_bus, xpt_busfunc_t *tr_func, void *arg) { struct cam_eb *bus, *next_bus; int retval; retval = 1; for (bus = (start_bus ? start_bus : TAILQ_FIRST(&xpt_busses)); bus != NULL; bus = next_bus) { next_bus = TAILQ_NEXT(bus, links); retval = tr_func(bus, arg); if (retval == 0) return(retval); } return(retval); } static int xpttargettraverse(struct cam_eb *bus, struct cam_et *start_target, xpt_targetfunc_t *tr_func, void *arg) { struct cam_et *target, *next_target; int retval; retval = 1; for (target = (start_target ? start_target : TAILQ_FIRST(&bus->et_entries)); target != NULL; target = next_target) { next_target = TAILQ_NEXT(target, links); retval = tr_func(target, arg); if (retval == 0) return(retval); } return(retval); } static int xptdevicetraverse(struct cam_et *target, struct cam_ed *start_device, xpt_devicefunc_t *tr_func, void *arg) { struct cam_ed *device, *next_device; int retval; retval = 1; for (device = (start_device ? start_device : TAILQ_FIRST(&target->ed_entries)); device != NULL; device = next_device) { next_device = TAILQ_NEXT(device, links); retval = tr_func(device, arg); if (retval == 0) return(retval); } return(retval); } static int xptperiphtraverse(struct cam_ed *device, struct cam_periph *start_periph, xpt_periphfunc_t *tr_func, void *arg) { struct cam_periph *periph, *next_periph; int retval; retval = 1; for (periph = (start_periph ? start_periph : SLIST_FIRST(&device->periphs)); periph != NULL; periph = next_periph) { next_periph = SLIST_NEXT(periph, periph_links); retval = tr_func(periph, arg); if (retval == 0) return(retval); } return(retval); } static int xptpdrvtraverse(struct periph_driver **start_pdrv, xpt_pdrvfunc_t *tr_func, void *arg) { struct periph_driver **pdrv; int retval; retval = 1; /* * We don't traverse the peripheral driver list like we do the * other lists, because it is a linker set, and therefore cannot be * changed during runtime. If the peripheral driver list is ever * re-done to be something other than a linker set (i.e. it can * change while the system is running), the list traversal should * be modified to work like the other traversal functions. */ for (pdrv = (start_pdrv ? start_pdrv : periph_drivers); *pdrv != NULL; pdrv++) { retval = tr_func(pdrv, arg); if (retval == 0) return(retval); } return(retval); } static int xptpdperiphtraverse(struct periph_driver **pdrv, struct cam_periph *start_periph, xpt_periphfunc_t *tr_func, void *arg) { struct cam_periph *periph, *next_periph; int retval; retval = 1; for (periph = (start_periph ? start_periph : TAILQ_FIRST(&(*pdrv)->units)); periph != NULL; periph = next_periph) { next_periph = TAILQ_NEXT(periph, unit_links); retval = tr_func(periph, arg); if (retval == 0) return(retval); } return(retval); } static int xptdefbusfunc(struct cam_eb *bus, void *arg) { struct xpt_traverse_config *tr_config; tr_config = (struct xpt_traverse_config *)arg; if (tr_config->depth == XPT_DEPTH_BUS) { xpt_busfunc_t *tr_func; tr_func = (xpt_busfunc_t *)tr_config->tr_func; return(tr_func(bus, tr_config->tr_arg)); } else return(xpttargettraverse(bus, NULL, xptdeftargetfunc, arg)); } static int xptdeftargetfunc(struct cam_et *target, void *arg) { struct xpt_traverse_config *tr_config; tr_config = (struct xpt_traverse_config *)arg; if (tr_config->depth == XPT_DEPTH_TARGET) { xpt_targetfunc_t *tr_func; tr_func = (xpt_targetfunc_t *)tr_config->tr_func; return(tr_func(target, tr_config->tr_arg)); } else return(xptdevicetraverse(target, NULL, xptdefdevicefunc, arg)); } static int xptdefdevicefunc(struct cam_ed *device, void *arg) { struct xpt_traverse_config *tr_config; tr_config = (struct xpt_traverse_config *)arg; if (tr_config->depth == XPT_DEPTH_DEVICE) { xpt_devicefunc_t *tr_func; tr_func = (xpt_devicefunc_t *)tr_config->tr_func; return(tr_func(device, tr_config->tr_arg)); } else return(xptperiphtraverse(device, NULL, xptdefperiphfunc, arg)); } static int xptdefperiphfunc(struct cam_periph *periph, void *arg) { struct xpt_traverse_config *tr_config; xpt_periphfunc_t *tr_func; tr_config = (struct xpt_traverse_config *)arg; tr_func = (xpt_periphfunc_t *)tr_config->tr_func; /* * Unlike the other default functions, we don't check for depth * here. The peripheral driver level is the last level in the EDT, * so if we're here, we should execute the function in question. */ return(tr_func(periph, tr_config->tr_arg)); } /* * Execute the given function for every bus in the EDT. */ static int xpt_for_all_busses(xpt_busfunc_t *tr_func, void *arg) { struct xpt_traverse_config tr_config; tr_config.depth = XPT_DEPTH_BUS; tr_config.tr_func = tr_func; tr_config.tr_arg = arg; return(xptbustraverse(NULL, xptdefbusfunc, &tr_config)); } #ifdef notusedyet /* * Execute the given function for every target in the EDT. */ static int xpt_for_all_targets(xpt_targetfunc_t *tr_func, void *arg) { struct xpt_traverse_config tr_config; tr_config.depth = XPT_DEPTH_TARGET; tr_config.tr_func = tr_func; tr_config.tr_arg = arg; return(xptbustraverse(NULL, xptdefbusfunc, &tr_config)); } #endif /* notusedyet */ /* * Execute the given function for every device in the EDT. */ static int xpt_for_all_devices(xpt_devicefunc_t *tr_func, void *arg) { struct xpt_traverse_config tr_config; tr_config.depth = XPT_DEPTH_DEVICE; tr_config.tr_func = tr_func; tr_config.tr_arg = arg; return(xptbustraverse(NULL, xptdefbusfunc, &tr_config)); } #ifdef notusedyet /* * Execute the given function for every peripheral in the EDT. */ static int xpt_for_all_periphs(xpt_periphfunc_t *tr_func, void *arg) { struct xpt_traverse_config tr_config; tr_config.depth = XPT_DEPTH_PERIPH; tr_config.tr_func = tr_func; tr_config.tr_arg = arg; return(xptbustraverse(NULL, xptdefbusfunc, &tr_config)); } #endif /* notusedyet */ static int xptsetasyncfunc(struct cam_ed *device, void *arg) { struct cam_path path; struct ccb_getdev cgd; struct async_node *cur_entry; cur_entry = (struct async_node *)arg; /* * Don't report unconfigured devices (Wildcard devs, * devices only for target mode, device instances * that have been invalidated but are waiting for * their last reference count to be released). */ if ((device->flags & CAM_DEV_UNCONFIGURED) != 0) return (1); xpt_compile_path(&path, NULL, device->target->bus->path_id, device->target->target_id, device->lun_id); xpt_setup_ccb(&cgd.ccb_h, &path, /*priority*/1); cgd.ccb_h.func_code = XPT_GDEV_TYPE; xpt_action((union ccb *)&cgd); cur_entry->callback(cur_entry->callback_arg, AC_FOUND_DEVICE, &path, &cgd); xpt_release_path(&path); return(1); } static int xptsetasyncbusfunc(struct cam_eb *bus, void *arg) { struct cam_path path; struct ccb_pathinq cpi; struct async_node *cur_entry; cur_entry = (struct async_node *)arg; xpt_compile_path(&path, /*periph*/NULL, bus->sim->path_id, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); xpt_setup_ccb(&cpi.ccb_h, &path, /*priority*/1); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); cur_entry->callback(cur_entry->callback_arg, AC_PATH_REGISTERED, &path, &cpi); xpt_release_path(&path); return(1); } void xpt_action(union ccb *start_ccb) { int iopl; CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("xpt_action\n")); start_ccb->ccb_h.status = CAM_REQ_INPROG; iopl = splsoftcam(); switch (start_ccb->ccb_h.func_code) { case XPT_SCSI_IO: { #ifdef CAM_NEW_TRAN_CODE struct cam_ed *device; #endif /* CAM_NEW_TRAN_CODE */ #ifdef CAMDEBUG char cdb_str[(SCSI_MAX_CDBLEN * 3) + 1]; struct cam_path *path; path = start_ccb->ccb_h.path; #endif /* * For the sake of compatibility with SCSI-1 * devices that may not understand the identify * message, we include lun information in the * second byte of all commands. SCSI-1 specifies * that luns are a 3 bit value and reserves only 3 * bits for lun information in the CDB. Later * revisions of the SCSI spec allow for more than 8 * luns, but have deprecated lun information in the * CDB. So, if the lun won't fit, we must omit. * * Also be aware that during initial probing for devices, * the inquiry information is unknown but initialized to 0. * This means that this code will be exercised while probing * devices with an ANSI revision greater than 2. */ #ifdef CAM_NEW_TRAN_CODE device = start_ccb->ccb_h.path->device; if (device->protocol_version <= SCSI_REV_2 #else /* CAM_NEW_TRAN_CODE */ if (SID_ANSI_REV(&start_ccb->ccb_h.path->device->inq_data) <= 2 #endif /* CAM_NEW_TRAN_CODE */ && start_ccb->ccb_h.target_lun < 8 && (start_ccb->ccb_h.flags & CAM_CDB_POINTER) == 0) { start_ccb->csio.cdb_io.cdb_bytes[1] |= start_ccb->ccb_h.target_lun << 5; } start_ccb->csio.scsi_status = SCSI_STATUS_OK; CAM_DEBUG(path, CAM_DEBUG_CDB,("%s. CDB: %s\n", scsi_op_desc(start_ccb->csio.cdb_io.cdb_bytes[0], &path->device->inq_data), scsi_cdb_string(start_ccb->csio.cdb_io.cdb_bytes, cdb_str, sizeof(cdb_str)))); /* FALLTHROUGH */ } case XPT_TARGET_IO: case XPT_CONT_TARGET_IO: start_ccb->csio.sense_resid = 0; start_ccb->csio.resid = 0; /* FALLTHROUGH */ case XPT_RESET_DEV: case XPT_ENG_EXEC: { struct cam_path *path; int s; int runq; path = start_ccb->ccb_h.path; s = splsoftcam(); cam_ccbq_insert_ccb(&path->device->ccbq, start_ccb); if (path->device->qfrozen_cnt == 0) runq = xpt_schedule_dev_sendq(path->bus, path->device); else runq = 0; splx(s); if (runq != 0) xpt_run_dev_sendq(path->bus); break; } case XPT_SET_TRAN_SETTINGS: { xpt_set_transfer_settings(&start_ccb->cts, start_ccb->ccb_h.path->device, /*async_update*/FALSE); break; } case XPT_CALC_GEOMETRY: { struct cam_sim *sim; /* Filter out garbage */ if (start_ccb->ccg.block_size == 0 || start_ccb->ccg.volume_size == 0) { start_ccb->ccg.cylinders = 0; start_ccb->ccg.heads = 0; start_ccb->ccg.secs_per_track = 0; start_ccb->ccb_h.status = CAM_REQ_CMP; break; } #ifdef PC98 /* * In a PC-98 system, geometry translation depens on * the "real" device geometry obtained from mode page 4. * SCSI geometry translation is performed in the * initialization routine of the SCSI BIOS and the result * stored in host memory. If the translation is available * in host memory, use it. If not, rely on the default * translation the device driver performs. */ if (scsi_da_bios_params(&start_ccb->ccg) != 0) { start_ccb->ccb_h.status = CAM_REQ_CMP; break; } #endif sim = start_ccb->ccb_h.path->bus->sim; (*(sim->sim_action))(sim, start_ccb); break; } case XPT_ABORT: { union ccb* abort_ccb; int s; abort_ccb = start_ccb->cab.abort_ccb; if (XPT_FC_IS_DEV_QUEUED(abort_ccb)) { if (abort_ccb->ccb_h.pinfo.index >= 0) { struct cam_ccbq *ccbq; ccbq = &abort_ccb->ccb_h.path->device->ccbq; cam_ccbq_remove_ccb(ccbq, abort_ccb); abort_ccb->ccb_h.status = CAM_REQ_ABORTED|CAM_DEV_QFRZN; xpt_freeze_devq(abort_ccb->ccb_h.path, 1); s = splcam(); xpt_done(abort_ccb); splx(s); start_ccb->ccb_h.status = CAM_REQ_CMP; break; } if (abort_ccb->ccb_h.pinfo.index == CAM_UNQUEUED_INDEX && (abort_ccb->ccb_h.status & CAM_SIM_QUEUED) == 0) { /* * We've caught this ccb en route to * the SIM. Flag it for abort and the * SIM will do so just before starting * real work on the CCB. */ abort_ccb->ccb_h.status = CAM_REQ_ABORTED|CAM_DEV_QFRZN; xpt_freeze_devq(abort_ccb->ccb_h.path, 1); start_ccb->ccb_h.status = CAM_REQ_CMP; break; } } if (XPT_FC_IS_QUEUED(abort_ccb) && (abort_ccb->ccb_h.pinfo.index == CAM_DONEQ_INDEX)) { /* * It's already completed but waiting * for our SWI to get to it. */ start_ccb->ccb_h.status = CAM_UA_ABORT; break; } /* * If we weren't able to take care of the abort request * in the XPT, pass the request down to the SIM for processing. */ /* FALLTHROUGH */ } case XPT_ACCEPT_TARGET_IO: case XPT_EN_LUN: case XPT_IMMED_NOTIFY: case XPT_NOTIFY_ACK: case XPT_GET_TRAN_SETTINGS: case XPT_RESET_BUS: { struct cam_sim *sim; sim = start_ccb->ccb_h.path->bus->sim; (*(sim->sim_action))(sim, start_ccb); break; } case XPT_PATH_INQ: { struct cam_sim *sim; sim = start_ccb->ccb_h.path->bus->sim; (*(sim->sim_action))(sim, start_ccb); break; } case XPT_PATH_STATS: start_ccb->cpis.last_reset = start_ccb->ccb_h.path->bus->last_reset; start_ccb->ccb_h.status = CAM_REQ_CMP; break; case XPT_GDEV_TYPE: { struct cam_ed *dev; int s; dev = start_ccb->ccb_h.path->device; s = splcam(); if ((dev->flags & CAM_DEV_UNCONFIGURED) != 0) { start_ccb->ccb_h.status = CAM_DEV_NOT_THERE; } else { struct ccb_getdev *cgd; struct cam_eb *bus; struct cam_et *tar; cgd = &start_ccb->cgd; bus = cgd->ccb_h.path->bus; tar = cgd->ccb_h.path->target; cgd->inq_data = dev->inq_data; cgd->ccb_h.status = CAM_REQ_CMP; cgd->serial_num_len = dev->serial_num_len; if ((dev->serial_num_len > 0) && (dev->serial_num != NULL)) bcopy(dev->serial_num, cgd->serial_num, dev->serial_num_len); } splx(s); break; } case XPT_GDEV_STATS: { struct cam_ed *dev; int s; dev = start_ccb->ccb_h.path->device; s = splcam(); if ((dev->flags & CAM_DEV_UNCONFIGURED) != 0) { start_ccb->ccb_h.status = CAM_DEV_NOT_THERE; } else { struct ccb_getdevstats *cgds; struct cam_eb *bus; struct cam_et *tar; cgds = &start_ccb->cgds; bus = cgds->ccb_h.path->bus; tar = cgds->ccb_h.path->target; cgds->dev_openings = dev->ccbq.dev_openings; cgds->dev_active = dev->ccbq.dev_active; cgds->devq_openings = dev->ccbq.devq_openings; cgds->devq_queued = dev->ccbq.queue.entries; cgds->held = dev->ccbq.held; cgds->last_reset = tar->last_reset; cgds->maxtags = dev->quirk->maxtags; cgds->mintags = dev->quirk->mintags; if (timevalcmp(&tar->last_reset, &bus->last_reset, <)) cgds->last_reset = bus->last_reset; cgds->ccb_h.status = CAM_REQ_CMP; } splx(s); break; } case XPT_GDEVLIST: { struct cam_periph *nperiph; struct periph_list *periph_head; struct ccb_getdevlist *cgdl; u_int i; int s; struct cam_ed *device; int found; found = 0; /* * Don't want anyone mucking with our data. */ s = splcam(); device = start_ccb->ccb_h.path->device; periph_head = &device->periphs; cgdl = &start_ccb->cgdl; /* * Check and see if the list has changed since the user * last requested a list member. If so, tell them that the * list has changed, and therefore they need to start over * from the beginning. */ if ((cgdl->index != 0) && (cgdl->generation != device->generation)) { cgdl->status = CAM_GDEVLIST_LIST_CHANGED; splx(s); break; } /* * Traverse the list of peripherals and attempt to find * the requested peripheral. */ for (nperiph = SLIST_FIRST(periph_head), i = 0; (nperiph != NULL) && (i <= cgdl->index); nperiph = SLIST_NEXT(nperiph, periph_links), i++) { if (i == cgdl->index) { strncpy(cgdl->periph_name, nperiph->periph_name, DEV_IDLEN); cgdl->unit_number = nperiph->unit_number; found = 1; } } if (found == 0) { cgdl->status = CAM_GDEVLIST_ERROR; splx(s); break; } if (nperiph == NULL) cgdl->status = CAM_GDEVLIST_LAST_DEVICE; else cgdl->status = CAM_GDEVLIST_MORE_DEVS; cgdl->index++; cgdl->generation = device->generation; splx(s); cgdl->ccb_h.status = CAM_REQ_CMP; break; } case XPT_DEV_MATCH: { int s; dev_pos_type position_type; struct ccb_dev_match *cdm; int ret; cdm = &start_ccb->cdm; /* * Prevent EDT changes while we traverse it. */ s = splcam(); /* * There are two ways of getting at information in the EDT. * The first way is via the primary EDT tree. It starts * with a list of busses, then a list of targets on a bus, * then devices/luns on a target, and then peripherals on a * device/lun. The "other" way is by the peripheral driver * lists. The peripheral driver lists are organized by * peripheral driver. (obviously) So it makes sense to * use the peripheral driver list if the user is looking * for something like "da1", or all "da" devices. If the * user is looking for something on a particular bus/target * or lun, it's generally better to go through the EDT tree. */ if (cdm->pos.position_type != CAM_DEV_POS_NONE) position_type = cdm->pos.position_type; else { u_int i; position_type = CAM_DEV_POS_NONE; for (i = 0; i < cdm->num_patterns; i++) { if ((cdm->patterns[i].type == DEV_MATCH_BUS) ||(cdm->patterns[i].type == DEV_MATCH_DEVICE)){ position_type = CAM_DEV_POS_EDT; break; } } if (cdm->num_patterns == 0) position_type = CAM_DEV_POS_EDT; else if (position_type == CAM_DEV_POS_NONE) position_type = CAM_DEV_POS_PDRV; } switch(position_type & CAM_DEV_POS_TYPEMASK) { case CAM_DEV_POS_EDT: ret = xptedtmatch(cdm); break; case CAM_DEV_POS_PDRV: ret = xptperiphlistmatch(cdm); break; default: cdm->status = CAM_DEV_MATCH_ERROR; break; } splx(s); if (cdm->status == CAM_DEV_MATCH_ERROR) start_ccb->ccb_h.status = CAM_REQ_CMP_ERR; else start_ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_SASYNC_CB: { struct ccb_setasync *csa; struct async_node *cur_entry; struct async_list *async_head; u_int32_t added; int s; csa = &start_ccb->csa; added = csa->event_enable; async_head = &csa->ccb_h.path->device->asyncs; /* * If there is already an entry for us, simply * update it. */ s = splcam(); cur_entry = SLIST_FIRST(async_head); while (cur_entry != NULL) { if ((cur_entry->callback_arg == csa->callback_arg) && (cur_entry->callback == csa->callback)) break; cur_entry = SLIST_NEXT(cur_entry, links); } if (cur_entry != NULL) { /* * If the request has no flags set, * remove the entry. */ added &= ~cur_entry->event_enable; if (csa->event_enable == 0) { SLIST_REMOVE(async_head, cur_entry, async_node, links); csa->ccb_h.path->device->refcount--; free(cur_entry, M_DEVBUF); } else { cur_entry->event_enable = csa->event_enable; } } else { cur_entry = malloc(sizeof(*cur_entry), M_DEVBUF, M_NOWAIT); if (cur_entry == NULL) { splx(s); csa->ccb_h.status = CAM_RESRC_UNAVAIL; break; } cur_entry->event_enable = csa->event_enable; cur_entry->callback_arg = csa->callback_arg; cur_entry->callback = csa->callback; SLIST_INSERT_HEAD(async_head, cur_entry, links); csa->ccb_h.path->device->refcount++; } if ((added & AC_FOUND_DEVICE) != 0) { /* * Get this peripheral up to date with all * the currently existing devices. */ xpt_for_all_devices(xptsetasyncfunc, cur_entry); } if ((added & AC_PATH_REGISTERED) != 0) { /* * Get this peripheral up to date with all * the currently existing busses. */ xpt_for_all_busses(xptsetasyncbusfunc, cur_entry); } splx(s); start_ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_REL_SIMQ: { struct ccb_relsim *crs; struct cam_ed *dev; int s; crs = &start_ccb->crs; dev = crs->ccb_h.path->device; if (dev == NULL) { crs->ccb_h.status = CAM_DEV_NOT_THERE; break; } s = splcam(); if ((crs->release_flags & RELSIM_ADJUST_OPENINGS) != 0) { if ((dev->inq_data.flags & SID_CmdQue) != 0) { /* Don't ever go below one opening */ if (crs->openings > 0) { xpt_dev_ccbq_resize(crs->ccb_h.path, crs->openings); if (bootverbose) { xpt_print_path(crs->ccb_h.path); printf("tagged openings " "now %d\n", crs->openings); } } } } if ((crs->release_flags & RELSIM_RELEASE_AFTER_TIMEOUT) != 0) { if ((dev->flags & CAM_DEV_REL_TIMEOUT_PENDING) != 0) { /* * Just extend the old timeout and decrement * the freeze count so that a single timeout * is sufficient for releasing the queue. */ start_ccb->ccb_h.flags &= ~CAM_DEV_QFREEZE; untimeout(xpt_release_devq_timeout, dev, dev->c_handle); } else { start_ccb->ccb_h.flags |= CAM_DEV_QFREEZE; } dev->c_handle = timeout(xpt_release_devq_timeout, dev, (crs->release_timeout * hz) / 1000); dev->flags |= CAM_DEV_REL_TIMEOUT_PENDING; } if ((crs->release_flags & RELSIM_RELEASE_AFTER_CMDCMPLT) != 0) { if ((dev->flags & CAM_DEV_REL_ON_COMPLETE) != 0) { /* * Decrement the freeze count so that a single * completion is still sufficient to unfreeze * the queue. */ start_ccb->ccb_h.flags &= ~CAM_DEV_QFREEZE; } else { dev->flags |= CAM_DEV_REL_ON_COMPLETE; start_ccb->ccb_h.flags |= CAM_DEV_QFREEZE; } } if ((crs->release_flags & RELSIM_RELEASE_AFTER_QEMPTY) != 0) { if ((dev->flags & CAM_DEV_REL_ON_QUEUE_EMPTY) != 0 || (dev->ccbq.dev_active == 0)) { start_ccb->ccb_h.flags &= ~CAM_DEV_QFREEZE; } else { dev->flags |= CAM_DEV_REL_ON_QUEUE_EMPTY; start_ccb->ccb_h.flags |= CAM_DEV_QFREEZE; } } splx(s); if ((start_ccb->ccb_h.flags & CAM_DEV_QFREEZE) == 0) { xpt_release_devq(crs->ccb_h.path, /*count*/1, /*run_queue*/TRUE); } start_ccb->crs.qfrozen_cnt = dev->qfrozen_cnt; start_ccb->ccb_h.status = CAM_REQ_CMP; break; } case XPT_SCAN_BUS: xpt_scan_bus(start_ccb->ccb_h.path->periph, start_ccb); break; case XPT_SCAN_LUN: xpt_scan_lun(start_ccb->ccb_h.path->periph, start_ccb->ccb_h.path, start_ccb->crcn.flags, start_ccb); break; case XPT_DEBUG: { #ifdef CAMDEBUG int s; s = splcam(); #ifdef CAM_DEBUG_DELAY cam_debug_delay = CAM_DEBUG_DELAY; #endif cam_dflags = start_ccb->cdbg.flags; if (cam_dpath != NULL) { xpt_free_path(cam_dpath); cam_dpath = NULL; } if (cam_dflags != CAM_DEBUG_NONE) { if (xpt_create_path(&cam_dpath, xpt_periph, start_ccb->ccb_h.path_id, start_ccb->ccb_h.target_id, start_ccb->ccb_h.target_lun) != CAM_REQ_CMP) { start_ccb->ccb_h.status = CAM_RESRC_UNAVAIL; cam_dflags = CAM_DEBUG_NONE; } else { start_ccb->ccb_h.status = CAM_REQ_CMP; xpt_print_path(cam_dpath); printf("debugging flags now %x\n", cam_dflags); } } else { cam_dpath = NULL; start_ccb->ccb_h.status = CAM_REQ_CMP; } splx(s); #else /* !CAMDEBUG */ start_ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; #endif /* CAMDEBUG */ break; } case XPT_NOOP: if ((start_ccb->ccb_h.flags & CAM_DEV_QFREEZE) != 0) xpt_freeze_devq(start_ccb->ccb_h.path, 1); start_ccb->ccb_h.status = CAM_REQ_CMP; break; default: case XPT_SDEV_TYPE: case XPT_TERM_IO: case XPT_ENG_INQ: /* XXX Implement */ start_ccb->ccb_h.status = CAM_PROVIDE_FAIL; break; } splx(iopl); } void xpt_polled_action(union ccb *start_ccb) { int s; u_int32_t timeout; struct cam_sim *sim; struct cam_devq *devq; struct cam_ed *dev; timeout = start_ccb->ccb_h.timeout; sim = start_ccb->ccb_h.path->bus->sim; devq = sim->devq; dev = start_ccb->ccb_h.path->device; s = splcam(); /* * Steal an opening so that no other queued requests * can get it before us while we simulate interrupts. */ dev->ccbq.devq_openings--; dev->ccbq.dev_openings--; while((devq->send_openings <= 0 || dev->ccbq.dev_openings < 0) && (--timeout > 0)) { DELAY(1000); (*(sim->sim_poll))(sim); camisr(&cam_netq); camisr(&cam_bioq); } dev->ccbq.devq_openings++; dev->ccbq.dev_openings++; if (timeout != 0) { xpt_action(start_ccb); while(--timeout > 0) { (*(sim->sim_poll))(sim); camisr(&cam_netq); camisr(&cam_bioq); if ((start_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_INPROG) break; DELAY(1000); } if (timeout == 0) { /* * XXX Is it worth adding a sim_timeout entry * point so we can attempt recovery? If * this is only used for dumps, I don't think * it is. */ start_ccb->ccb_h.status = CAM_CMD_TIMEOUT; } } else { start_ccb->ccb_h.status = CAM_RESRC_UNAVAIL; } splx(s); } /* * Schedule a peripheral driver to receive a ccb when it's * target device has space for more transactions. */ void xpt_schedule(struct cam_periph *perph, u_int32_t new_priority) { struct cam_ed *device; int s; int runq; CAM_DEBUG(perph->path, CAM_DEBUG_TRACE, ("xpt_schedule\n")); device = perph->path->device; s = splsoftcam(); if (periph_is_queued(perph)) { /* Simply reorder based on new priority */ CAM_DEBUG(perph->path, CAM_DEBUG_SUBTRACE, (" change priority to %d\n", new_priority)); if (new_priority < perph->pinfo.priority) { camq_change_priority(&device->drvq, perph->pinfo.index, new_priority); } runq = 0; } else { /* New entry on the queue */ CAM_DEBUG(perph->path, CAM_DEBUG_SUBTRACE, (" added periph to queue\n")); perph->pinfo.priority = new_priority; perph->pinfo.generation = ++device->drvq.generation; camq_insert(&device->drvq, &perph->pinfo); runq = xpt_schedule_dev_allocq(perph->path->bus, device); } splx(s); if (runq != 0) { CAM_DEBUG(perph->path, CAM_DEBUG_SUBTRACE, (" calling xpt_run_devq\n")); xpt_run_dev_allocq(perph->path->bus); } } /* * Schedule a device to run on a given queue. * If the device was inserted as a new entry on the queue, * return 1 meaning the device queue should be run. If we * were already queued, implying someone else has already * started the queue, return 0 so the caller doesn't attempt * to run the queue. Must be run at either splsoftcam * (or splcam since that encompases splsoftcam). */ static int xpt_schedule_dev(struct camq *queue, cam_pinfo *pinfo, u_int32_t new_priority) { int retval; u_int32_t old_priority; CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("xpt_schedule_dev\n")); old_priority = pinfo->priority; /* * Are we already queued? */ if (pinfo->index != CAM_UNQUEUED_INDEX) { /* Simply reorder based on new priority */ if (new_priority < old_priority) { camq_change_priority(queue, pinfo->index, new_priority); CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("changed priority to %d\n", new_priority)); } retval = 0; } else { /* New entry on the queue */ if (new_priority < old_priority) pinfo->priority = new_priority; CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("Inserting onto queue\n")); pinfo->generation = ++queue->generation; camq_insert(queue, pinfo); retval = 1; } return (retval); } static void xpt_run_dev_allocq(struct cam_eb *bus) { struct cam_devq *devq; int s; CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("xpt_run_dev_allocq\n")); devq = bus->sim->devq; CAM_DEBUG_PRINT(CAM_DEBUG_XPT, (" qfrozen_cnt == 0x%x, entries == %d, " "openings == %d, active == %d\n", devq->alloc_queue.qfrozen_cnt, devq->alloc_queue.entries, devq->alloc_openings, devq->alloc_active)); s = splsoftcam(); devq->alloc_queue.qfrozen_cnt++; while ((devq->alloc_queue.entries > 0) && (devq->alloc_openings > 0) && (devq->alloc_queue.qfrozen_cnt <= 1)) { struct cam_ed_qinfo *qinfo; struct cam_ed *device; union ccb *work_ccb; struct cam_periph *drv; struct camq *drvq; qinfo = (struct cam_ed_qinfo *)camq_remove(&devq->alloc_queue, CAMQ_HEAD); device = qinfo->device; CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("running device %p\n", device)); drvq = &device->drvq; #ifdef CAMDEBUG if (drvq->entries <= 0) { panic("xpt_run_dev_allocq: " "Device on queue without any work to do"); } #endif if ((work_ccb = xpt_get_ccb(device)) != NULL) { devq->alloc_openings--; devq->alloc_active++; drv = (struct cam_periph*)camq_remove(drvq, CAMQ_HEAD); splx(s); xpt_setup_ccb(&work_ccb->ccb_h, drv->path, drv->pinfo.priority); CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("calling periph start\n")); drv->periph_start(drv, work_ccb); } else { /* * Malloc failure in alloc_ccb */ /* * XXX add us to a list to be run from free_ccb * if we don't have any ccbs active on this * device queue otherwise we may never get run * again. */ break; } /* Raise IPL for possible insertion and test at top of loop */ s = splsoftcam(); if (drvq->entries > 0) { /* We have more work. Attempt to reschedule */ xpt_schedule_dev_allocq(bus, device); } } devq->alloc_queue.qfrozen_cnt--; splx(s); } static void xpt_run_dev_sendq(struct cam_eb *bus) { struct cam_devq *devq; int s; CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("xpt_run_dev_sendq\n")); devq = bus->sim->devq; s = splcam(); devq->send_queue.qfrozen_cnt++; splx(s); s = splsoftcam(); while ((devq->send_queue.entries > 0) && (devq->send_openings > 0)) { struct cam_ed_qinfo *qinfo; struct cam_ed *device; union ccb *work_ccb; struct cam_sim *sim; int ospl; ospl = splcam(); if (devq->send_queue.qfrozen_cnt > 1) { splx(ospl); break; } qinfo = (struct cam_ed_qinfo *)camq_remove(&devq->send_queue, CAMQ_HEAD); device = qinfo->device; /* * If the device has been "frozen", don't attempt * to run it. */ if (device->qfrozen_cnt > 0) { splx(ospl); continue; } CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("running device %p\n", device)); work_ccb = cam_ccbq_peek_ccb(&device->ccbq, CAMQ_HEAD); if (work_ccb == NULL) { printf("device on run queue with no ccbs???"); splx(ospl); continue; } if ((work_ccb->ccb_h.flags & CAM_HIGH_POWER) != 0) { if (num_highpower <= 0) { /* * We got a high power command, but we * don't have any available slots. Freeze * the device queue until we have a slot * available. */ device->qfrozen_cnt++; STAILQ_INSERT_TAIL(&highpowerq, &work_ccb->ccb_h, xpt_links.stqe); splx(ospl); continue; } else { /* * Consume a high power slot while * this ccb runs. */ num_highpower--; } } devq->active_dev = device; cam_ccbq_remove_ccb(&device->ccbq, work_ccb); cam_ccbq_send_ccb(&device->ccbq, work_ccb); splx(ospl); devq->send_openings--; devq->send_active++; if (device->ccbq.queue.entries > 0) xpt_schedule_dev_sendq(bus, device); if (work_ccb && (work_ccb->ccb_h.flags & CAM_DEV_QFREEZE) != 0){ /* * The client wants to freeze the queue * after this CCB is sent. */ ospl = splcam(); device->qfrozen_cnt++; splx(ospl); } splx(s); /* In Target mode, the peripheral driver knows best... */ if (work_ccb->ccb_h.func_code == XPT_SCSI_IO) { if ((device->inq_flags & SID_CmdQue) != 0 && work_ccb->csio.tag_action != CAM_TAG_ACTION_NONE) work_ccb->ccb_h.flags |= CAM_TAG_ACTION_VALID; else /* * Clear this in case of a retried CCB that * failed due to a rejected tag. */ work_ccb->ccb_h.flags &= ~CAM_TAG_ACTION_VALID; } /* * Device queues can be shared among multiple sim instances * that reside on different busses. Use the SIM in the queue * CCB's path, rather than the one in the bus that was passed * into this function. */ sim = work_ccb->ccb_h.path->bus->sim; (*(sim->sim_action))(sim, work_ccb); ospl = splcam(); devq->active_dev = NULL; splx(ospl); /* Raise IPL for possible insertion and test at top of loop */ s = splsoftcam(); } splx(s); s = splcam(); devq->send_queue.qfrozen_cnt--; splx(s); } /* * This function merges stuff from the slave ccb into the master ccb, while * keeping important fields in the master ccb constant. */ void xpt_merge_ccb(union ccb *master_ccb, union ccb *slave_ccb) { /* * Pull fields that are valid for peripheral drivers to set * into the master CCB along with the CCB "payload". */ master_ccb->ccb_h.retry_count = slave_ccb->ccb_h.retry_count; master_ccb->ccb_h.func_code = slave_ccb->ccb_h.func_code; master_ccb->ccb_h.timeout = slave_ccb->ccb_h.timeout; master_ccb->ccb_h.flags = slave_ccb->ccb_h.flags; bcopy(&(&slave_ccb->ccb_h)[1], &(&master_ccb->ccb_h)[1], sizeof(union ccb) - sizeof(struct ccb_hdr)); } void xpt_setup_ccb(struct ccb_hdr *ccb_h, struct cam_path *path, u_int32_t priority) { CAM_DEBUG(path, CAM_DEBUG_TRACE, ("xpt_setup_ccb\n")); ccb_h->pinfo.priority = priority; ccb_h->path = path; ccb_h->path_id = path->bus->path_id; if (path->target) ccb_h->target_id = path->target->target_id; else ccb_h->target_id = CAM_TARGET_WILDCARD; if (path->device) { ccb_h->target_lun = path->device->lun_id; ccb_h->pinfo.generation = ++path->device->ccbq.queue.generation; } else { ccb_h->target_lun = CAM_TARGET_WILDCARD; } ccb_h->pinfo.index = CAM_UNQUEUED_INDEX; ccb_h->flags = 0; } /* Path manipulation functions */ cam_status xpt_create_path(struct cam_path **new_path_ptr, struct cam_periph *perph, path_id_t path_id, target_id_t target_id, lun_id_t lun_id) { struct cam_path *path; cam_status status; path = (struct cam_path *)malloc(sizeof(*path), M_DEVBUF, M_NOWAIT); if (path == NULL) { status = CAM_RESRC_UNAVAIL; return(status); } status = xpt_compile_path(path, perph, path_id, target_id, lun_id); if (status != CAM_REQ_CMP) { free(path, M_DEVBUF); path = NULL; } *new_path_ptr = path; return (status); } static cam_status xpt_compile_path(struct cam_path *new_path, struct cam_periph *perph, path_id_t path_id, target_id_t target_id, lun_id_t lun_id) { struct cam_eb *bus; struct cam_et *target; struct cam_ed *device; cam_status status; int s; status = CAM_REQ_CMP; /* Completed without error */ target = NULL; /* Wildcarded */ device = NULL; /* Wildcarded */ /* * We will potentially modify the EDT, so block interrupts * that may attempt to create cam paths. */ s = splcam(); bus = xpt_find_bus(path_id); if (bus == NULL) { status = CAM_PATH_INVALID; } else { target = xpt_find_target(bus, target_id); if (target == NULL) { /* Create one */ struct cam_et *new_target; new_target = xpt_alloc_target(bus, target_id); if (new_target == NULL) { status = CAM_RESRC_UNAVAIL; } else { target = new_target; } } if (target != NULL) { device = xpt_find_device(target, lun_id); if (device == NULL) { /* Create one */ struct cam_ed *new_device; new_device = xpt_alloc_device(bus, target, lun_id); if (new_device == NULL) { status = CAM_RESRC_UNAVAIL; } else { device = new_device; } } } } splx(s); /* * Only touch the user's data if we are successful. */ if (status == CAM_REQ_CMP) { new_path->periph = perph; new_path->bus = bus; new_path->target = target; new_path->device = device; CAM_DEBUG(new_path, CAM_DEBUG_TRACE, ("xpt_compile_path\n")); } else { if (device != NULL) xpt_release_device(bus, target, device); if (target != NULL) xpt_release_target(bus, target); if (bus != NULL) xpt_release_bus(bus); } return (status); } static void xpt_release_path(struct cam_path *path) { CAM_DEBUG(path, CAM_DEBUG_TRACE, ("xpt_release_path\n")); if (path->device != NULL) { xpt_release_device(path->bus, path->target, path->device); path->device = NULL; } if (path->target != NULL) { xpt_release_target(path->bus, path->target); path->target = NULL; } if (path->bus != NULL) { xpt_release_bus(path->bus); path->bus = NULL; } } void xpt_free_path(struct cam_path *path) { CAM_DEBUG(path, CAM_DEBUG_TRACE, ("xpt_free_path\n")); xpt_release_path(path); free(path, M_DEVBUF); } /* * Return -1 for failure, 0 for exact match, 1 for match with wildcards * in path1, 2 for match with wildcards in path2. */ int xpt_path_comp(struct cam_path *path1, struct cam_path *path2) { int retval = 0; if (path1->bus != path2->bus) { if (path1->bus->path_id == CAM_BUS_WILDCARD) retval = 1; else if (path2->bus->path_id == CAM_BUS_WILDCARD) retval = 2; else return (-1); } if (path1->target != path2->target) { if (path1->target->target_id == CAM_TARGET_WILDCARD) { if (retval == 0) retval = 1; } else if (path2->target->target_id == CAM_TARGET_WILDCARD) retval = 2; else return (-1); } if (path1->device != path2->device) { if (path1->device->lun_id == CAM_LUN_WILDCARD) { if (retval == 0) retval = 1; } else if (path2->device->lun_id == CAM_LUN_WILDCARD) retval = 2; else return (-1); } return (retval); } void xpt_print_path(struct cam_path *path) { if (path == NULL) printf("(nopath): "); else { if (path->periph != NULL) printf("(%s%d:", path->periph->periph_name, path->periph->unit_number); else printf("(noperiph:"); if (path->bus != NULL) printf("%s%d:%d:", path->bus->sim->sim_name, path->bus->sim->unit_number, path->bus->sim->bus_id); else printf("nobus:"); if (path->target != NULL) printf("%d:", path->target->target_id); else printf("X:"); if (path->device != NULL) printf("%d): ", path->device->lun_id); else printf("X): "); } } int xpt_path_string(struct cam_path *path, char *str, size_t str_len) { struct sbuf sb; sbuf_new(&sb, str, str_len, 0); if (path == NULL) sbuf_printf(&sb, "(nopath): "); else { if (path->periph != NULL) sbuf_printf(&sb, "(%s%d:", path->periph->periph_name, path->periph->unit_number); else sbuf_printf(&sb, "(noperiph:"); if (path->bus != NULL) sbuf_printf(&sb, "%s%d:%d:", path->bus->sim->sim_name, path->bus->sim->unit_number, path->bus->sim->bus_id); else sbuf_printf(&sb, "nobus:"); if (path->target != NULL) sbuf_printf(&sb, "%d:", path->target->target_id); else sbuf_printf(&sb, "X:"); if (path->device != NULL) sbuf_printf(&sb, "%d): ", path->device->lun_id); else sbuf_printf(&sb, "X): "); } sbuf_finish(&sb); return(sbuf_len(&sb)); } path_id_t xpt_path_path_id(struct cam_path *path) { return(path->bus->path_id); } target_id_t xpt_path_target_id(struct cam_path *path) { if (path->target != NULL) return (path->target->target_id); else return (CAM_TARGET_WILDCARD); } lun_id_t xpt_path_lun_id(struct cam_path *path) { if (path->device != NULL) return (path->device->lun_id); else return (CAM_LUN_WILDCARD); } struct cam_sim * xpt_path_sim(struct cam_path *path) { return (path->bus->sim); } struct cam_periph* xpt_path_periph(struct cam_path *path) { return (path->periph); } /* * Release a CAM control block for the caller. Remit the cost of the structure * to the device referenced by the path. If the this device had no 'credits' * and peripheral drivers have registered async callbacks for this notification * call them now. */ void xpt_release_ccb(union ccb *free_ccb) { int s; struct cam_path *path; struct cam_ed *device; struct cam_eb *bus; CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("xpt_release_ccb\n")); path = free_ccb->ccb_h.path; device = path->device; bus = path->bus; s = splsoftcam(); cam_ccbq_release_opening(&device->ccbq); if (xpt_ccb_count > xpt_max_ccbs) { xpt_free_ccb(free_ccb); xpt_ccb_count--; } else { SLIST_INSERT_HEAD(&ccb_freeq, &free_ccb->ccb_h, xpt_links.sle); } bus->sim->devq->alloc_openings++; bus->sim->devq->alloc_active--; /* XXX Turn this into an inline function - xpt_run_device?? */ if ((device_is_alloc_queued(device) == 0) && (device->drvq.entries > 0)) { xpt_schedule_dev_allocq(bus, device); } splx(s); if (dev_allocq_is_runnable(bus->sim->devq)) xpt_run_dev_allocq(bus); } /* Functions accessed by SIM drivers */ /* * A sim structure, listing the SIM entry points and instance * identification info is passed to xpt_bus_register to hook the SIM * into the CAM framework. xpt_bus_register creates a cam_eb entry * for this new bus and places it in the array of busses and assigns * it a path_id. The path_id may be influenced by "hard wiring" * information specified by the user. Once interrupt services are * availible, the bus will be probed. */ int32_t xpt_bus_register(struct cam_sim *sim, u_int32_t bus) { struct cam_eb *new_bus; struct cam_eb *old_bus; struct ccb_pathinq cpi; int s; sim->bus_id = bus; new_bus = (struct cam_eb *)malloc(sizeof(*new_bus), M_DEVBUF, M_NOWAIT); if (new_bus == NULL) { /* Couldn't satisfy request */ return (CAM_RESRC_UNAVAIL); } if (strcmp(sim->sim_name, "xpt") != 0) { sim->path_id = xptpathid(sim->sim_name, sim->unit_number, sim->bus_id); } TAILQ_INIT(&new_bus->et_entries); new_bus->path_id = sim->path_id; new_bus->sim = sim; timevalclear(&new_bus->last_reset); new_bus->flags = 0; new_bus->refcount = 1; /* Held until a bus_deregister event */ new_bus->generation = 0; s = splcam(); old_bus = TAILQ_FIRST(&xpt_busses); while (old_bus != NULL && old_bus->path_id < new_bus->path_id) old_bus = TAILQ_NEXT(old_bus, links); if (old_bus != NULL) TAILQ_INSERT_BEFORE(old_bus, new_bus, links); else TAILQ_INSERT_TAIL(&xpt_busses, new_bus, links); bus_generation++; splx(s); /* Notify interested parties */ if (sim->path_id != CAM_XPT_PATH_ID) { struct cam_path path; xpt_compile_path(&path, /*periph*/NULL, sim->path_id, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); xpt_setup_ccb(&cpi.ccb_h, &path, /*priority*/1); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); xpt_async(AC_PATH_REGISTERED, &path, &cpi); xpt_release_path(&path); } return (CAM_SUCCESS); } int32_t xpt_bus_deregister(path_id_t pathid) { struct cam_path bus_path; cam_status status; status = xpt_compile_path(&bus_path, NULL, pathid, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); if (status != CAM_REQ_CMP) return (status); xpt_async(AC_LOST_DEVICE, &bus_path, NULL); xpt_async(AC_PATH_DEREGISTERED, &bus_path, NULL); /* Release the reference count held while registered. */ xpt_release_bus(bus_path.bus); xpt_release_path(&bus_path); return (CAM_REQ_CMP); } static path_id_t xptnextfreepathid(void) { struct cam_eb *bus; path_id_t pathid; - char *strval; + const char *strval; pathid = 0; bus = TAILQ_FIRST(&xpt_busses); retry: /* Find an unoccupied pathid */ while (bus != NULL && bus->path_id <= pathid) { if (bus->path_id == pathid) pathid++; bus = TAILQ_NEXT(bus, links); } /* * Ensure that this pathid is not reserved for * a bus that may be registered in the future. */ if (resource_string_value("scbus", pathid, "at", &strval) == 0) { ++pathid; /* Start the search over */ goto retry; } return (pathid); } static path_id_t xptpathid(const char *sim_name, int sim_unit, int sim_bus) { path_id_t pathid; int i, dunit, val; char buf[32]; + const char *dname; pathid = CAM_XPT_PATH_ID; snprintf(buf, sizeof(buf), "%s%d", sim_name, sim_unit); - i = -1; - while ((i = resource_query_string(i, "at", buf)) != -1) { - if (strcmp(resource_query_name(i), "scbus")) { + i = 0; + while ((resource_find_match(&i, &dname, &dunit, "at", buf)) == 0) { + if (strcmp(dname, "scbus")) { /* Avoid a bit of foot shooting. */ continue; } - dunit = resource_query_unit(i); if (dunit < 0) /* unwired?! */ continue; if (resource_int_value("scbus", dunit, "bus", &val) == 0) { if (sim_bus == val) { pathid = dunit; break; } } else if (sim_bus == 0) { /* Unspecified matches bus 0 */ pathid = dunit; break; } else { printf("Ambiguous scbus configuration for %s%d " "bus %d, cannot wire down. The kernel " "config entry for scbus%d should " "specify a controller bus.\n" "Scbus will be assigned dynamically.\n", sim_name, sim_unit, sim_bus, dunit); break; } } if (pathid == CAM_XPT_PATH_ID) pathid = xptnextfreepathid(); return (pathid); } void xpt_async(u_int32_t async_code, struct cam_path *path, void *async_arg) { struct cam_eb *bus; struct cam_et *target, *next_target; struct cam_ed *device, *next_device; int s; CAM_DEBUG(path, CAM_DEBUG_TRACE, ("xpt_async\n")); /* * Most async events come from a CAM interrupt context. In * a few cases, the error recovery code at the peripheral layer, * which may run from our SWI or a process context, may signal * deferred events with a call to xpt_async. Ensure async * notifications are serialized by blocking cam interrupts. */ s = splcam(); bus = path->bus; if (async_code == AC_BUS_RESET) { int s; s = splclock(); /* Update our notion of when the last reset occurred */ microtime(&bus->last_reset); splx(s); } for (target = TAILQ_FIRST(&bus->et_entries); target != NULL; target = next_target) { next_target = TAILQ_NEXT(target, links); if (path->target != target && path->target->target_id != CAM_TARGET_WILDCARD && target->target_id != CAM_TARGET_WILDCARD) continue; if (async_code == AC_SENT_BDR) { int s; /* Update our notion of when the last reset occurred */ s = splclock(); microtime(&path->target->last_reset); splx(s); } for (device = TAILQ_FIRST(&target->ed_entries); device != NULL; device = next_device) { next_device = TAILQ_NEXT(device, links); if (path->device != device && path->device->lun_id != CAM_LUN_WILDCARD && device->lun_id != CAM_LUN_WILDCARD) continue; xpt_dev_async(async_code, bus, target, device, async_arg); xpt_async_bcast(&device->asyncs, async_code, path, async_arg); } } /* * If this wasn't a fully wildcarded async, tell all * clients that want all async events. */ if (bus != xpt_periph->path->bus) xpt_async_bcast(&xpt_periph->path->device->asyncs, async_code, path, async_arg); splx(s); } static void xpt_async_bcast(struct async_list *async_head, u_int32_t async_code, struct cam_path *path, void *async_arg) { struct async_node *cur_entry; cur_entry = SLIST_FIRST(async_head); while (cur_entry != NULL) { struct async_node *next_entry; /* * Grab the next list entry before we call the current * entry's callback. This is because the callback function * can delete its async callback entry. */ next_entry = SLIST_NEXT(cur_entry, links); if ((cur_entry->event_enable & async_code) != 0) cur_entry->callback(cur_entry->callback_arg, async_code, path, async_arg); cur_entry = next_entry; } } /* * Handle any per-device event notifications that require action by the XPT. */ static void xpt_dev_async(u_int32_t async_code, struct cam_eb *bus, struct cam_et *target, struct cam_ed *device, void *async_arg) { cam_status status; struct cam_path newpath; /* * We only need to handle events for real devices. */ if (target->target_id == CAM_TARGET_WILDCARD || device->lun_id == CAM_LUN_WILDCARD) return; /* * We need our own path with wildcards expanded to * handle certain types of events. */ if ((async_code == AC_SENT_BDR) || (async_code == AC_BUS_RESET) || (async_code == AC_INQ_CHANGED)) status = xpt_compile_path(&newpath, NULL, bus->path_id, target->target_id, device->lun_id); else status = CAM_REQ_CMP_ERR; if (status == CAM_REQ_CMP) { /* * Allow transfer negotiation to occur in a * tag free environment. */ if (async_code == AC_SENT_BDR || async_code == AC_BUS_RESET) xpt_toggle_tags(&newpath); if (async_code == AC_INQ_CHANGED) { /* * We've sent a start unit command, or * something similar to a device that * may have caused its inquiry data to * change. So we re-scan the device to * refresh the inquiry data for it. */ xpt_scan_lun(newpath.periph, &newpath, CAM_EXPECT_INQ_CHANGE, NULL); } xpt_release_path(&newpath); } else if (async_code == AC_LOST_DEVICE) { device->flags |= CAM_DEV_UNCONFIGURED; } else if (async_code == AC_TRANSFER_NEG) { struct ccb_trans_settings *settings; settings = (struct ccb_trans_settings *)async_arg; xpt_set_transfer_settings(settings, device, /*async_update*/TRUE); } } u_int32_t xpt_freeze_devq(struct cam_path *path, u_int count) { int s; struct ccb_hdr *ccbh; s = splcam(); path->device->qfrozen_cnt += count; /* * Mark the last CCB in the queue as needing * to be requeued if the driver hasn't * changed it's state yet. This fixes a race * where a ccb is just about to be queued to * a controller driver when it's interrupt routine * freezes the queue. To completly close the * hole, controller drives must check to see * if a ccb's status is still CAM_REQ_INPROG * under spl protection just before they queue * the CCB. See ahc_action/ahc_freeze_devq for * an example. */ ccbh = TAILQ_LAST(&path->device->ccbq.active_ccbs, ccb_hdr_tailq); if (ccbh && ccbh->status == CAM_REQ_INPROG) ccbh->status = CAM_REQUEUE_REQ; splx(s); return (path->device->qfrozen_cnt); } u_int32_t xpt_freeze_simq(struct cam_sim *sim, u_int count) { sim->devq->send_queue.qfrozen_cnt += count; if (sim->devq->active_dev != NULL) { struct ccb_hdr *ccbh; ccbh = TAILQ_LAST(&sim->devq->active_dev->ccbq.active_ccbs, ccb_hdr_tailq); if (ccbh && ccbh->status == CAM_REQ_INPROG) ccbh->status = CAM_REQUEUE_REQ; } return (sim->devq->send_queue.qfrozen_cnt); } static void xpt_release_devq_timeout(void *arg) { struct cam_ed *device; device = (struct cam_ed *)arg; xpt_release_devq_device(device, /*count*/1, /*run_queue*/TRUE); } void xpt_release_devq(struct cam_path *path, u_int count, int run_queue) { xpt_release_devq_device(path->device, count, run_queue); } static void xpt_release_devq_device(struct cam_ed *dev, u_int count, int run_queue) { int rundevq; int s0, s1; rundevq = 0; s0 = splsoftcam(); s1 = splcam(); if (dev->qfrozen_cnt > 0) { count = (count > dev->qfrozen_cnt) ? dev->qfrozen_cnt : count; dev->qfrozen_cnt -= count; if (dev->qfrozen_cnt == 0) { /* * No longer need to wait for a successful * command completion. */ dev->flags &= ~CAM_DEV_REL_ON_COMPLETE; /* * Remove any timeouts that might be scheduled * to release this queue. */ if ((dev->flags & CAM_DEV_REL_TIMEOUT_PENDING) != 0) { untimeout(xpt_release_devq_timeout, dev, dev->c_handle); dev->flags &= ~CAM_DEV_REL_TIMEOUT_PENDING; } /* * Now that we are unfrozen schedule the * device so any pending transactions are * run. */ if ((dev->ccbq.queue.entries > 0) && (xpt_schedule_dev_sendq(dev->target->bus, dev)) && (run_queue != 0)) { rundevq = 1; } } } splx(s1); if (rundevq != 0) xpt_run_dev_sendq(dev->target->bus); splx(s0); } void xpt_release_simq(struct cam_sim *sim, int run_queue) { int s; struct camq *sendq; sendq = &(sim->devq->send_queue); s = splcam(); if (sendq->qfrozen_cnt > 0) { sendq->qfrozen_cnt--; if (sendq->qfrozen_cnt == 0) { struct cam_eb *bus; /* * If there is a timeout scheduled to release this * sim queue, remove it. The queue frozen count is * already at 0. */ if ((sim->flags & CAM_SIM_REL_TIMEOUT_PENDING) != 0){ untimeout(xpt_release_simq_timeout, sim, sim->c_handle); sim->flags &= ~CAM_SIM_REL_TIMEOUT_PENDING; } bus = xpt_find_bus(sim->path_id); splx(s); if (run_queue) { /* * Now that we are unfrozen run the send queue. */ xpt_run_dev_sendq(bus); } xpt_release_bus(bus); } else splx(s); } else splx(s); } static void xpt_release_simq_timeout(void *arg) { struct cam_sim *sim; sim = (struct cam_sim *)arg; xpt_release_simq(sim, /* run_queue */ TRUE); } void xpt_done(union ccb *done_ccb) { int s; s = splcam(); CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("xpt_done\n")); if ((done_ccb->ccb_h.func_code & XPT_FC_QUEUED) != 0) { /* * Queue up the request for handling by our SWI handler * any of the "non-immediate" type of ccbs. */ switch (done_ccb->ccb_h.path->periph->type) { case CAM_PERIPH_BIO: TAILQ_INSERT_TAIL(&cam_bioq, &done_ccb->ccb_h, sim_links.tqe); done_ccb->ccb_h.pinfo.index = CAM_DONEQ_INDEX; swi_sched(cambio_ih, SWI_NOSWITCH); break; case CAM_PERIPH_NET: TAILQ_INSERT_TAIL(&cam_netq, &done_ccb->ccb_h, sim_links.tqe); done_ccb->ccb_h.pinfo.index = CAM_DONEQ_INDEX; swi_sched(camnet_ih, SWI_NOSWITCH); break; } } splx(s); } union ccb * xpt_alloc_ccb() { union ccb *new_ccb; new_ccb = malloc(sizeof(*new_ccb), M_DEVBUF, M_WAITOK); return (new_ccb); } void xpt_free_ccb(union ccb *free_ccb) { free(free_ccb, M_DEVBUF); } /* Private XPT functions */ /* * Get a CAM control block for the caller. Charge the structure to the device * referenced by the path. If the this device has no 'credits' then the * device already has the maximum number of outstanding operations under way * and we return NULL. If we don't have sufficient resources to allocate more * ccbs, we also return NULL. */ static union ccb * xpt_get_ccb(struct cam_ed *device) { union ccb *new_ccb; int s; s = splsoftcam(); if ((new_ccb = (union ccb *)SLIST_FIRST(&ccb_freeq)) == NULL) { new_ccb = malloc(sizeof(*new_ccb), M_DEVBUF, M_NOWAIT); if (new_ccb == NULL) { splx(s); return (NULL); } callout_handle_init(&new_ccb->ccb_h.timeout_ch); SLIST_INSERT_HEAD(&ccb_freeq, &new_ccb->ccb_h, xpt_links.sle); xpt_ccb_count++; } cam_ccbq_take_opening(&device->ccbq); SLIST_REMOVE_HEAD(&ccb_freeq, xpt_links.sle); splx(s); return (new_ccb); } static void xpt_release_bus(struct cam_eb *bus) { int s; s = splcam(); if ((--bus->refcount == 0) && (TAILQ_FIRST(&bus->et_entries) == NULL)) { TAILQ_REMOVE(&xpt_busses, bus, links); bus_generation++; splx(s); free(bus, M_DEVBUF); } else splx(s); } static struct cam_et * xpt_alloc_target(struct cam_eb *bus, target_id_t target_id) { struct cam_et *target; target = (struct cam_et *)malloc(sizeof(*target), M_DEVBUF, M_NOWAIT); if (target != NULL) { struct cam_et *cur_target; TAILQ_INIT(&target->ed_entries); target->bus = bus; target->target_id = target_id; target->refcount = 1; target->generation = 0; timevalclear(&target->last_reset); /* * Hold a reference to our parent bus so it * will not go away before we do. */ bus->refcount++; /* Insertion sort into our bus's target list */ cur_target = TAILQ_FIRST(&bus->et_entries); while (cur_target != NULL && cur_target->target_id < target_id) cur_target = TAILQ_NEXT(cur_target, links); if (cur_target != NULL) { TAILQ_INSERT_BEFORE(cur_target, target, links); } else { TAILQ_INSERT_TAIL(&bus->et_entries, target, links); } bus->generation++; } return (target); } static void xpt_release_target(struct cam_eb *bus, struct cam_et *target) { int s; s = splcam(); if ((--target->refcount == 0) && (TAILQ_FIRST(&target->ed_entries) == NULL)) { TAILQ_REMOVE(&bus->et_entries, target, links); bus->generation++; splx(s); free(target, M_DEVBUF); xpt_release_bus(bus); } else splx(s); } static struct cam_ed * xpt_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id) { #ifdef CAM_NEW_TRAN_CODE struct cam_path path; #endif /* CAM_NEW_TRAN_CODE */ struct cam_ed *device; struct cam_devq *devq; cam_status status; /* Make space for us in the device queue on our bus */ devq = bus->sim->devq; status = cam_devq_resize(devq, devq->alloc_queue.array_size + 1); if (status != CAM_REQ_CMP) { device = NULL; } else { device = (struct cam_ed *)malloc(sizeof(*device), M_DEVBUF, M_NOWAIT); } if (device != NULL) { struct cam_ed *cur_device; cam_init_pinfo(&device->alloc_ccb_entry.pinfo); device->alloc_ccb_entry.device = device; cam_init_pinfo(&device->send_ccb_entry.pinfo); device->send_ccb_entry.device = device; device->target = target; device->lun_id = lun_id; /* Initialize our queues */ if (camq_init(&device->drvq, 0) != 0) { free(device, M_DEVBUF); return (NULL); } if (cam_ccbq_init(&device->ccbq, bus->sim->max_dev_openings) != 0) { camq_fini(&device->drvq); free(device, M_DEVBUF); return (NULL); } SLIST_INIT(&device->asyncs); SLIST_INIT(&device->periphs); device->generation = 0; device->owner = NULL; /* * Take the default quirk entry until we have inquiry * data and can determine a better quirk to use. */ device->quirk = &xpt_quirk_table[xpt_quirk_table_size - 1]; bzero(&device->inq_data, sizeof(device->inq_data)); device->inq_flags = 0; device->queue_flags = 0; device->serial_num = NULL; device->serial_num_len = 0; device->qfrozen_cnt = 0; device->flags = CAM_DEV_UNCONFIGURED; device->tag_delay_count = 0; device->refcount = 1; callout_handle_init(&device->c_handle); /* * Hold a reference to our parent target so it * will not go away before we do. */ target->refcount++; /* * XXX should be limited by number of CCBs this bus can * do. */ xpt_max_ccbs += device->ccbq.devq_openings; /* Insertion sort into our target's device list */ cur_device = TAILQ_FIRST(&target->ed_entries); while (cur_device != NULL && cur_device->lun_id < lun_id) cur_device = TAILQ_NEXT(cur_device, links); if (cur_device != NULL) { TAILQ_INSERT_BEFORE(cur_device, device, links); } else { TAILQ_INSERT_TAIL(&target->ed_entries, device, links); } target->generation++; #ifdef CAM_NEW_TRAN_CODE if (lun_id != CAM_LUN_WILDCARD) { xpt_compile_path(&path, NULL, bus->path_id, target->target_id, lun_id); xpt_devise_transport(&path); xpt_release_path(&path); } #endif /* CAM_NEW_TRAN_CODE */ } return (device); } static void xpt_release_device(struct cam_eb *bus, struct cam_et *target, struct cam_ed *device) { int s; s = splcam(); if ((--device->refcount == 0) && ((device->flags & CAM_DEV_UNCONFIGURED) != 0)) { struct cam_devq *devq; if (device->alloc_ccb_entry.pinfo.index != CAM_UNQUEUED_INDEX || device->send_ccb_entry.pinfo.index != CAM_UNQUEUED_INDEX) panic("Removing device while still queued for ccbs"); if ((device->flags & CAM_DEV_REL_TIMEOUT_PENDING) != 0) untimeout(xpt_release_devq_timeout, device, device->c_handle); TAILQ_REMOVE(&target->ed_entries, device,links); target->generation++; xpt_max_ccbs -= device->ccbq.devq_openings; /* Release our slot in the devq */ devq = bus->sim->devq; cam_devq_resize(devq, devq->alloc_queue.array_size - 1); splx(s); free(device, M_DEVBUF); xpt_release_target(bus, target); } else splx(s); } static u_int32_t xpt_dev_ccbq_resize(struct cam_path *path, int newopenings) { int s; int diff; int result; struct cam_ed *dev; dev = path->device; s = splsoftcam(); diff = newopenings - (dev->ccbq.dev_active + dev->ccbq.dev_openings); result = cam_ccbq_resize(&dev->ccbq, newopenings); if (result == CAM_REQ_CMP && (diff < 0)) { dev->flags |= CAM_DEV_RESIZE_QUEUE_NEEDED; } /* Adjust the global limit */ xpt_max_ccbs += diff; splx(s); return (result); } static struct cam_eb * xpt_find_bus(path_id_t path_id) { struct cam_eb *bus; for (bus = TAILQ_FIRST(&xpt_busses); bus != NULL; bus = TAILQ_NEXT(bus, links)) { if (bus->path_id == path_id) { bus->refcount++; break; } } return (bus); } static struct cam_et * xpt_find_target(struct cam_eb *bus, target_id_t target_id) { struct cam_et *target; for (target = TAILQ_FIRST(&bus->et_entries); target != NULL; target = TAILQ_NEXT(target, links)) { if (target->target_id == target_id) { target->refcount++; break; } } return (target); } static struct cam_ed * xpt_find_device(struct cam_et *target, lun_id_t lun_id) { struct cam_ed *device; for (device = TAILQ_FIRST(&target->ed_entries); device != NULL; device = TAILQ_NEXT(device, links)) { if (device->lun_id == lun_id) { device->refcount++; break; } } return (device); } typedef struct { union ccb *request_ccb; struct ccb_pathinq *cpi; int pending_count; } xpt_scan_bus_info; /* * To start a scan, request_ccb is an XPT_SCAN_BUS ccb. * As the scan progresses, xpt_scan_bus is used as the * callback on completion function. */ static void xpt_scan_bus(struct cam_periph *periph, union ccb *request_ccb) { CAM_DEBUG(request_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("xpt_scan_bus\n")); switch (request_ccb->ccb_h.func_code) { case XPT_SCAN_BUS: { xpt_scan_bus_info *scan_info; union ccb *work_ccb; struct cam_path *path; u_int i; u_int max_target; u_int initiator_id; /* Find out the characteristics of the bus */ work_ccb = xpt_alloc_ccb(); xpt_setup_ccb(&work_ccb->ccb_h, request_ccb->ccb_h.path, request_ccb->ccb_h.pinfo.priority); work_ccb->ccb_h.func_code = XPT_PATH_INQ; xpt_action(work_ccb); if (work_ccb->ccb_h.status != CAM_REQ_CMP) { request_ccb->ccb_h.status = work_ccb->ccb_h.status; xpt_free_ccb(work_ccb); xpt_done(request_ccb); return; } if ((work_ccb->cpi.hba_misc & PIM_NOINITIATOR) != 0) { /* * Can't scan the bus on an adapter that * cannot perform the initiator role. */ request_ccb->ccb_h.status = CAM_REQ_CMP; xpt_free_ccb(work_ccb); xpt_done(request_ccb); return; } /* Save some state for use while we probe for devices */ scan_info = (xpt_scan_bus_info *) malloc(sizeof(xpt_scan_bus_info), M_TEMP, M_WAITOK); scan_info->request_ccb = request_ccb; scan_info->cpi = &work_ccb->cpi; /* Cache on our stack so we can work asynchronously */ max_target = scan_info->cpi->max_target; initiator_id = scan_info->cpi->initiator_id; /* * Don't count the initiator if the * initiator is addressable. */ scan_info->pending_count = max_target + 1; if (initiator_id <= max_target) scan_info->pending_count--; for (i = 0; i <= max_target; i++) { cam_status status; if (i == initiator_id) continue; status = xpt_create_path(&path, xpt_periph, request_ccb->ccb_h.path_id, i, 0); if (status != CAM_REQ_CMP) { printf("xpt_scan_bus: xpt_create_path failed" " with status %#x, bus scan halted\n", status); break; } work_ccb = xpt_alloc_ccb(); xpt_setup_ccb(&work_ccb->ccb_h, path, request_ccb->ccb_h.pinfo.priority); work_ccb->ccb_h.func_code = XPT_SCAN_LUN; work_ccb->ccb_h.cbfcnp = xpt_scan_bus; work_ccb->ccb_h.ppriv_ptr0 = scan_info; work_ccb->crcn.flags = request_ccb->crcn.flags; xpt_action(work_ccb); } break; } case XPT_SCAN_LUN: { xpt_scan_bus_info *scan_info; path_id_t path_id; target_id_t target_id; lun_id_t lun_id; /* Reuse the same CCB to query if a device was really found */ scan_info = (xpt_scan_bus_info *)request_ccb->ccb_h.ppriv_ptr0; xpt_setup_ccb(&request_ccb->ccb_h, request_ccb->ccb_h.path, request_ccb->ccb_h.pinfo.priority); request_ccb->ccb_h.func_code = XPT_GDEV_TYPE; path_id = request_ccb->ccb_h.path_id; target_id = request_ccb->ccb_h.target_id; lun_id = request_ccb->ccb_h.target_lun; xpt_action(request_ccb); if (request_ccb->ccb_h.status != CAM_REQ_CMP) { struct cam_ed *device; struct cam_et *target; int s, phl; /* * If we already probed lun 0 successfully, or * we have additional configured luns on this * target that might have "gone away", go onto * the next lun. */ target = request_ccb->ccb_h.path->target; /* * We may touch devices that we don't * hold references too, so ensure they * don't disappear out from under us. * The target above is referenced by the * path in the request ccb. */ phl = 0; s = splcam(); device = TAILQ_FIRST(&target->ed_entries); if (device != NULL) { phl = device->quirk->quirks & CAM_QUIRK_HILUNS; if (device->lun_id == 0) device = TAILQ_NEXT(device, links); } splx(s); if ((lun_id != 0) || (device != NULL)) { if (lun_id < (CAM_SCSI2_MAXLUN-1) || phl) lun_id++; } } else { struct cam_ed *device; device = request_ccb->ccb_h.path->device; if ((device->quirk->quirks & CAM_QUIRK_NOLUNS) == 0) { /* Try the next lun */ if (lun_id < (CAM_SCSI2_MAXLUN-1) || (device->quirk->quirks & CAM_QUIRK_HILUNS)) lun_id++; } } xpt_free_path(request_ccb->ccb_h.path); /* Check Bounds */ if ((lun_id == request_ccb->ccb_h.target_lun) || lun_id > scan_info->cpi->max_lun) { /* We're done */ xpt_free_ccb(request_ccb); scan_info->pending_count--; if (scan_info->pending_count == 0) { xpt_free_ccb((union ccb *)scan_info->cpi); request_ccb = scan_info->request_ccb; free(scan_info, M_TEMP); request_ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(request_ccb); } } else { /* Try the next device */ struct cam_path *path; cam_status status; path = request_ccb->ccb_h.path; status = xpt_create_path(&path, xpt_periph, path_id, target_id, lun_id); if (status != CAM_REQ_CMP) { printf("xpt_scan_bus: xpt_create_path failed " "with status %#x, halting LUN scan\n", status); xpt_free_ccb(request_ccb); scan_info->pending_count--; if (scan_info->pending_count == 0) { xpt_free_ccb( (union ccb *)scan_info->cpi); request_ccb = scan_info->request_ccb; free(scan_info, M_TEMP); request_ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(request_ccb); break; } } xpt_setup_ccb(&request_ccb->ccb_h, path, request_ccb->ccb_h.pinfo.priority); request_ccb->ccb_h.func_code = XPT_SCAN_LUN; request_ccb->ccb_h.cbfcnp = xpt_scan_bus; request_ccb->ccb_h.ppriv_ptr0 = scan_info; request_ccb->crcn.flags = scan_info->request_ccb->crcn.flags; xpt_action(request_ccb); } break; } default: break; } } typedef enum { PROBE_TUR, PROBE_INQUIRY, PROBE_FULL_INQUIRY, PROBE_MODE_SENSE, PROBE_SERIAL_NUM, PROBE_TUR_FOR_NEGOTIATION } probe_action; typedef enum { PROBE_INQUIRY_CKSUM = 0x01, PROBE_SERIAL_CKSUM = 0x02, PROBE_NO_ANNOUNCE = 0x04 } probe_flags; typedef struct { TAILQ_HEAD(, ccb_hdr) request_ccbs; probe_action action; union ccb saved_ccb; probe_flags flags; MD5_CTX context; u_int8_t digest[16]; } probe_softc; static void xpt_scan_lun(struct cam_periph *periph, struct cam_path *path, cam_flags flags, union ccb *request_ccb) { struct ccb_pathinq cpi; cam_status status; struct cam_path *new_path; struct cam_periph *old_periph; int s; CAM_DEBUG(request_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("xpt_scan_lun\n")); xpt_setup_ccb(&cpi.ccb_h, path, /*priority*/1); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); if (cpi.ccb_h.status != CAM_REQ_CMP) { if (request_ccb != NULL) { request_ccb->ccb_h.status = cpi.ccb_h.status; xpt_done(request_ccb); } return; } if ((cpi.hba_misc & PIM_NOINITIATOR) != 0) { /* * Can't scan the bus on an adapter that * cannot perform the initiator role. */ if (request_ccb != NULL) { request_ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(request_ccb); } return; } if (request_ccb == NULL) { request_ccb = malloc(sizeof(union ccb), M_TEMP, M_NOWAIT); if (request_ccb == NULL) { xpt_print_path(path); printf("xpt_scan_lun: can't allocate CCB, can't " "continue\n"); return; } new_path = malloc(sizeof(*new_path), M_TEMP, M_NOWAIT); if (new_path == NULL) { xpt_print_path(path); printf("xpt_scan_lun: can't allocate path, can't " "continue\n"); free(request_ccb, M_TEMP); return; } status = xpt_compile_path(new_path, xpt_periph, path->bus->path_id, path->target->target_id, path->device->lun_id); if (status != CAM_REQ_CMP) { xpt_print_path(path); printf("xpt_scan_lun: can't compile path, can't " "continue\n"); free(request_ccb, M_TEMP); free(new_path, M_TEMP); return; } xpt_setup_ccb(&request_ccb->ccb_h, new_path, /*priority*/ 1); request_ccb->ccb_h.cbfcnp = xptscandone; request_ccb->ccb_h.func_code = XPT_SCAN_LUN; request_ccb->crcn.flags = flags; } s = splsoftcam(); if ((old_periph = cam_periph_find(path, "probe")) != NULL) { probe_softc *softc; softc = (probe_softc *)old_periph->softc; TAILQ_INSERT_TAIL(&softc->request_ccbs, &request_ccb->ccb_h, periph_links.tqe); } else { status = cam_periph_alloc(proberegister, NULL, probecleanup, probestart, "probe", CAM_PERIPH_BIO, request_ccb->ccb_h.path, NULL, 0, request_ccb); if (status != CAM_REQ_CMP) { xpt_print_path(path); printf("xpt_scan_lun: cam_alloc_periph returned an " "error, can't continue probe\n"); request_ccb->ccb_h.status = status; xpt_done(request_ccb); } } splx(s); } static void xptscandone(struct cam_periph *periph, union ccb *done_ccb) { xpt_release_path(done_ccb->ccb_h.path); free(done_ccb->ccb_h.path, M_TEMP); free(done_ccb, M_TEMP); } static cam_status proberegister(struct cam_periph *periph, void *arg) { union ccb *request_ccb; /* CCB representing the probe request */ probe_softc *softc; request_ccb = (union ccb *)arg; if (periph == NULL) { printf("proberegister: periph was NULL!!\n"); return(CAM_REQ_CMP_ERR); } if (request_ccb == NULL) { printf("proberegister: no probe CCB, " "can't register device\n"); return(CAM_REQ_CMP_ERR); } softc = (probe_softc *)malloc(sizeof(*softc), M_TEMP, M_NOWAIT); if (softc == NULL) { printf("proberegister: Unable to probe new device. " "Unable to allocate softc\n"); return(CAM_REQ_CMP_ERR); } TAILQ_INIT(&softc->request_ccbs); TAILQ_INSERT_TAIL(&softc->request_ccbs, &request_ccb->ccb_h, periph_links.tqe); softc->flags = 0; periph->softc = softc; cam_periph_acquire(periph); /* * Ensure we've waited at least a bus settle * delay before attempting to probe the device. * For HBAs that don't do bus resets, this won't make a difference. */ cam_periph_freeze_after_event(periph, &periph->path->bus->last_reset, SCSI_DELAY); probeschedule(periph); return(CAM_REQ_CMP); } static void probeschedule(struct cam_periph *periph) { struct ccb_pathinq cpi; union ccb *ccb; probe_softc *softc; softc = (probe_softc *)periph->softc; ccb = (union ccb *)TAILQ_FIRST(&softc->request_ccbs); xpt_setup_ccb(&cpi.ccb_h, periph->path, /*priority*/1); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); /* * If a device has gone away and another device, or the same one, * is back in the same place, it should have a unit attention * condition pending. It will not report the unit attention in * response to an inquiry, which may leave invalid transfer * negotiations in effect. The TUR will reveal the unit attention * condition. Only send the TUR for lun 0, since some devices * will get confused by commands other than inquiry to non-existent * luns. If you think a device has gone away start your scan from * lun 0. This will insure that any bogus transfer settings are * invalidated. * * If we haven't seen the device before and the controller supports * some kind of transfer negotiation, negotiate with the first * sent command if no bus reset was performed at startup. This * ensures that the device is not confused by transfer negotiation * settings left over by loader or BIOS action. */ if (((ccb->ccb_h.path->device->flags & CAM_DEV_UNCONFIGURED) == 0) && (ccb->ccb_h.target_lun == 0)) { softc->action = PROBE_TUR; } else if ((cpi.hba_inquiry & (PI_WIDE_32|PI_WIDE_16|PI_SDTR_ABLE)) != 0 && (cpi.hba_misc & PIM_NOBUSRESET) != 0) { proberequestdefaultnegotiation(periph); softc->action = PROBE_INQUIRY; } else { softc->action = PROBE_INQUIRY; } if (ccb->crcn.flags & CAM_EXPECT_INQ_CHANGE) softc->flags |= PROBE_NO_ANNOUNCE; else softc->flags &= ~PROBE_NO_ANNOUNCE; xpt_schedule(periph, ccb->ccb_h.pinfo.priority); } static void probestart(struct cam_periph *periph, union ccb *start_ccb) { /* Probe the device that our peripheral driver points to */ struct ccb_scsiio *csio; probe_softc *softc; CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("probestart\n")); softc = (probe_softc *)periph->softc; csio = &start_ccb->csio; switch (softc->action) { case PROBE_TUR: case PROBE_TUR_FOR_NEGOTIATION: { scsi_test_unit_ready(csio, /*retries*/4, probedone, MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, /*timeout*/60000); break; } case PROBE_INQUIRY: case PROBE_FULL_INQUIRY: { u_int inquiry_len; struct scsi_inquiry_data *inq_buf; inq_buf = &periph->path->device->inq_data; /* * If the device is currently configured, we calculate an * MD5 checksum of the inquiry data, and if the serial number * length is greater than 0, add the serial number data * into the checksum as well. Once the inquiry and the * serial number check finish, we attempt to figure out * whether we still have the same device. */ if ((periph->path->device->flags & CAM_DEV_UNCONFIGURED) == 0) { MD5Init(&softc->context); MD5Update(&softc->context, (unsigned char *)inq_buf, sizeof(struct scsi_inquiry_data)); softc->flags |= PROBE_INQUIRY_CKSUM; if (periph->path->device->serial_num_len > 0) { MD5Update(&softc->context, periph->path->device->serial_num, periph->path->device->serial_num_len); softc->flags |= PROBE_SERIAL_CKSUM; } MD5Final(softc->digest, &softc->context); } if (softc->action == PROBE_INQUIRY) inquiry_len = SHORT_INQUIRY_LENGTH; else inquiry_len = inq_buf->additional_length + 4; scsi_inquiry(csio, /*retries*/4, probedone, MSG_SIMPLE_Q_TAG, (u_int8_t *)inq_buf, inquiry_len, /*evpd*/FALSE, /*page_code*/0, SSD_MIN_SIZE, /*timeout*/60 * 1000); break; } case PROBE_MODE_SENSE: { void *mode_buf; int mode_buf_len; mode_buf_len = sizeof(struct scsi_mode_header_6) + sizeof(struct scsi_mode_blk_desc) + sizeof(struct scsi_control_page); mode_buf = malloc(mode_buf_len, M_TEMP, M_NOWAIT); if (mode_buf != NULL) { scsi_mode_sense(csio, /*retries*/4, probedone, MSG_SIMPLE_Q_TAG, /*dbd*/FALSE, SMS_PAGE_CTRL_CURRENT, SMS_CONTROL_MODE_PAGE, mode_buf, mode_buf_len, SSD_FULL_SIZE, /*timeout*/60000); break; } xpt_print_path(periph->path); printf("Unable to mode sense control page - malloc failure\n"); softc->action = PROBE_SERIAL_NUM; /* FALLTHROUGH */ } case PROBE_SERIAL_NUM: { struct scsi_vpd_unit_serial_number *serial_buf; struct cam_ed* device; serial_buf = NULL; device = periph->path->device; device->serial_num = NULL; device->serial_num_len = 0; if ((device->quirk->quirks & CAM_QUIRK_NOSERIAL) == 0) serial_buf = (struct scsi_vpd_unit_serial_number *) malloc(sizeof(*serial_buf), M_TEMP, M_NOWAIT | M_ZERO); if (serial_buf != NULL) { scsi_inquiry(csio, /*retries*/4, probedone, MSG_SIMPLE_Q_TAG, (u_int8_t *)serial_buf, sizeof(*serial_buf), /*evpd*/TRUE, SVPD_UNIT_SERIAL_NUMBER, SSD_MIN_SIZE, /*timeout*/60 * 1000); break; } /* * We'll have to do without, let our probedone * routine finish up for us. */ start_ccb->csio.data_ptr = NULL; probedone(periph, start_ccb); return; } } xpt_action(start_ccb); } static void proberequestdefaultnegotiation(struct cam_periph *periph) { struct ccb_trans_settings cts; xpt_setup_ccb(&cts.ccb_h, periph->path, /*priority*/1); cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; #ifdef CAM_NEW_TRAN_CODE cts.type = CTS_TYPE_USER_SETTINGS; #else /* CAM_NEW_TRAN_CODE */ cts.flags = CCB_TRANS_USER_SETTINGS; #endif /* CAM_NEW_TRAN_CODE */ xpt_action((union ccb *)&cts); cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS; #ifdef CAM_NEW_TRAN_CODE cts.type = CTS_TYPE_CURRENT_SETTINGS; #else /* CAM_NEW_TRAN_CODE */ cts.flags &= ~CCB_TRANS_USER_SETTINGS; cts.flags |= CCB_TRANS_CURRENT_SETTINGS; #endif /* CAM_NEW_TRAN_CODE */ xpt_action((union ccb *)&cts); } static void probedone(struct cam_periph *periph, union ccb *done_ccb) { probe_softc *softc; struct cam_path *path; u_int32_t priority; CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("probedone\n")); softc = (probe_softc *)periph->softc; path = done_ccb->ccb_h.path; priority = done_ccb->ccb_h.pinfo.priority; switch (softc->action) { case PROBE_TUR: { if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { if (cam_periph_error(done_ccb, 0, SF_NO_PRINT, NULL) == ERESTART) return; else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) /* Don't wedge the queue */ xpt_release_devq(done_ccb->ccb_h.path, /*count*/1, /*run_queue*/TRUE); } softc->action = PROBE_INQUIRY; xpt_release_ccb(done_ccb); xpt_schedule(periph, priority); return; } case PROBE_INQUIRY: case PROBE_FULL_INQUIRY: { if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { struct scsi_inquiry_data *inq_buf; u_int8_t periph_qual; u_int8_t periph_dtype; path->device->flags |= CAM_DEV_INQUIRY_DATA_VALID; inq_buf = &path->device->inq_data; periph_qual = SID_QUAL(inq_buf); periph_dtype = SID_TYPE(inq_buf); if (periph_dtype != T_NODEVICE) { switch(periph_qual) { case SID_QUAL_LU_CONNECTED: { u_int8_t alen; /* * We conservatively request only * SHORT_INQUIRY_LEN bytes of inquiry * information during our first try * at sending an INQUIRY. If the device * has more information to give, * perform a second request specifying * the amount of information the device * is willing to give. */ alen = inq_buf->additional_length; if (softc->action == PROBE_INQUIRY && alen > (SHORT_INQUIRY_LENGTH - 4)) { softc->action = PROBE_FULL_INQUIRY; xpt_release_ccb(done_ccb); xpt_schedule(periph, priority); return; } xpt_find_quirk(path->device); #ifdef CAM_NEW_TRAN_CODE xpt_devise_transport(path); #endif /* CAM_NEW_TRAN_CODE */ if ((inq_buf->flags & SID_CmdQue) != 0) softc->action = PROBE_MODE_SENSE; else softc->action = PROBE_SERIAL_NUM; path->device->flags &= ~CAM_DEV_UNCONFIGURED; xpt_release_ccb(done_ccb); xpt_schedule(periph, priority); return; } default: break; } } } else if (cam_periph_error(done_ccb, 0, done_ccb->ccb_h.target_lun > 0 ? SF_RETRY_UA|SF_QUIET_IR : SF_RETRY_UA, &softc->saved_ccb) == ERESTART) { return; } else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { /* Don't wedge the queue */ xpt_release_devq(done_ccb->ccb_h.path, /*count*/1, /*run_queue*/TRUE); } /* * If we get to this point, we got an error status back * from the inquiry and the error status doesn't require * automatically retrying the command. Therefore, the * inquiry failed. If we had inquiry information before * for this device, but this latest inquiry command failed, * the device has probably gone away. If this device isn't * already marked unconfigured, notify the peripheral * drivers that this device is no more. */ if ((path->device->flags & CAM_DEV_UNCONFIGURED) == 0) /* Send the async notification. */ xpt_async(AC_LOST_DEVICE, path, NULL); xpt_release_ccb(done_ccb); break; } case PROBE_MODE_SENSE: { struct ccb_scsiio *csio; struct scsi_mode_header_6 *mode_hdr; csio = &done_ccb->csio; mode_hdr = (struct scsi_mode_header_6 *)csio->data_ptr; if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { struct scsi_control_page *page; u_int8_t *offset; offset = ((u_int8_t *)&mode_hdr[1]) + mode_hdr->blk_desc_len; page = (struct scsi_control_page *)offset; path->device->queue_flags = page->queue_flags; } else if (cam_periph_error(done_ccb, 0, SF_RETRY_UA|SF_NO_PRINT, &softc->saved_ccb) == ERESTART) { return; } else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { /* Don't wedge the queue */ xpt_release_devq(done_ccb->ccb_h.path, /*count*/1, /*run_queue*/TRUE); } xpt_release_ccb(done_ccb); free(mode_hdr, M_TEMP); softc->action = PROBE_SERIAL_NUM; xpt_schedule(periph, priority); return; } case PROBE_SERIAL_NUM: { struct ccb_scsiio *csio; struct scsi_vpd_unit_serial_number *serial_buf; u_int32_t priority; int changed; int have_serialnum; changed = 1; have_serialnum = 0; csio = &done_ccb->csio; priority = done_ccb->ccb_h.pinfo.priority; serial_buf = (struct scsi_vpd_unit_serial_number *)csio->data_ptr; /* Clean up from previous instance of this device */ if (path->device->serial_num != NULL) { free(path->device->serial_num, M_DEVBUF); path->device->serial_num = NULL; path->device->serial_num_len = 0; } if (serial_buf == NULL) { /* * Don't process the command as it was never sent */ } else if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP && (serial_buf->length > 0)) { have_serialnum = 1; path->device->serial_num = (u_int8_t *)malloc((serial_buf->length + 1), M_DEVBUF, M_NOWAIT); if (path->device->serial_num != NULL) { bcopy(serial_buf->serial_num, path->device->serial_num, serial_buf->length); path->device->serial_num_len = serial_buf->length; path->device->serial_num[serial_buf->length] = '\0'; } } else if (cam_periph_error(done_ccb, 0, SF_RETRY_UA|SF_NO_PRINT, &softc->saved_ccb) == ERESTART) { return; } else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { /* Don't wedge the queue */ xpt_release_devq(done_ccb->ccb_h.path, /*count*/1, /*run_queue*/TRUE); } /* * Let's see if we have seen this device before. */ if ((softc->flags & PROBE_INQUIRY_CKSUM) != 0) { MD5_CTX context; u_int8_t digest[16]; MD5Init(&context); MD5Update(&context, (unsigned char *)&path->device->inq_data, sizeof(struct scsi_inquiry_data)); if (have_serialnum) MD5Update(&context, serial_buf->serial_num, serial_buf->length); MD5Final(digest, &context); if (bcmp(softc->digest, digest, 16) == 0) changed = 0; /* * XXX Do we need to do a TUR in order to ensure * that the device really hasn't changed??? */ if ((changed != 0) && ((softc->flags & PROBE_NO_ANNOUNCE) == 0)) xpt_async(AC_LOST_DEVICE, path, NULL); } if (serial_buf != NULL) free(serial_buf, M_TEMP); if (changed != 0) { /* * Now that we have all the necessary * information to safely perform transfer * negotiations... Controllers don't perform * any negotiation or tagged queuing until * after the first XPT_SET_TRAN_SETTINGS ccb is * received. So, on a new device, just retreive * the user settings, and set them as the current * settings to set the device up. */ proberequestdefaultnegotiation(periph); xpt_release_ccb(done_ccb); /* * Perform a TUR to allow the controller to * perform any necessary transfer negotiation. */ softc->action = PROBE_TUR_FOR_NEGOTIATION; xpt_schedule(periph, priority); return; } xpt_release_ccb(done_ccb); break; } case PROBE_TUR_FOR_NEGOTIATION: if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { /* Don't wedge the queue */ xpt_release_devq(done_ccb->ccb_h.path, /*count*/1, /*run_queue*/TRUE); } path->device->flags &= ~CAM_DEV_UNCONFIGURED; if ((softc->flags & PROBE_NO_ANNOUNCE) == 0) { /* Inform the XPT that a new device has been found */ done_ccb->ccb_h.func_code = XPT_GDEV_TYPE; xpt_action(done_ccb); xpt_async(AC_FOUND_DEVICE, xpt_periph->path, done_ccb); } xpt_release_ccb(done_ccb); break; } done_ccb = (union ccb *)TAILQ_FIRST(&softc->request_ccbs); TAILQ_REMOVE(&softc->request_ccbs, &done_ccb->ccb_h, periph_links.tqe); done_ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(done_ccb); if (TAILQ_FIRST(&softc->request_ccbs) == NULL) { cam_periph_invalidate(periph); cam_periph_release(periph); } else { probeschedule(periph); } } static void probecleanup(struct cam_periph *periph) { free(periph->softc, M_TEMP); } static void xpt_find_quirk(struct cam_ed *device) { caddr_t match; match = cam_quirkmatch((caddr_t)&device->inq_data, (caddr_t)xpt_quirk_table, sizeof(xpt_quirk_table)/sizeof(*xpt_quirk_table), sizeof(*xpt_quirk_table), scsi_inquiry_match); if (match == NULL) panic("xpt_find_quirk: device didn't match wildcard entry!!"); device->quirk = (struct xpt_quirk_entry *)match; } #ifdef CAM_NEW_TRAN_CODE static void xpt_devise_transport(struct cam_path *path) { struct ccb_pathinq cpi; struct ccb_trans_settings cts; struct scsi_inquiry_data *inq_buf; /* Get transport information from the SIM */ xpt_setup_ccb(&cpi.ccb_h, path, /*priority*/1); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); inq_buf = NULL; if ((path->device->flags & CAM_DEV_INQUIRY_DATA_VALID) != 0) inq_buf = &path->device->inq_data; path->device->protocol = PROTO_SCSI; path->device->protocol_version = inq_buf != NULL ? SID_ANSI_REV(inq_buf) : cpi.protocol_version; path->device->transport = cpi.transport; path->device->transport_version = cpi.transport_version; /* * Any device not using SPI3 features should * be considered SPI2 or lower. */ if (inq_buf != NULL) { if (path->device->transport == XPORT_SPI && (inq_buf->spi3data & SID_SPI_MASK) == 0 && path->device->transport_version > 2) path->device->transport_version = 2; } else { struct cam_ed* otherdev; for (otherdev = TAILQ_FIRST(&path->target->ed_entries); otherdev != NULL; otherdev = TAILQ_NEXT(otherdev, links)) { if (otherdev != path->device) break; } if (otherdev != NULL) { /* * Initially assume the same versioning as * prior luns for this target. */ path->device->protocol_version = otherdev->protocol_version; path->device->transport_version = otherdev->transport_version; } else { /* Until we know better, opt for safty */ path->device->protocol_version = 2; if (path->device->transport == XPORT_SPI) path->device->transport_version = 2; else path->device->transport_version = 0; } } /* * XXX * For a device compliant with SPC-2 we should be able * to determine the transport version supported by * scrutinizing the version descriptors in the * inquiry buffer. */ /* Tell the controller what we think */ xpt_setup_ccb(&cts.ccb_h, path, /*priority*/1); cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS; cts.type = CTS_TYPE_CURRENT_SETTINGS; cts.transport = path->device->transport; cts.transport_version = path->device->transport_version; cts.protocol = path->device->protocol; cts.protocol_version = path->device->protocol_version; cts.proto_specific.valid = 0; cts.xport_specific.valid = 0; xpt_action((union ccb *)&cts); } static void xpt_set_transfer_settings(struct ccb_trans_settings *cts, struct cam_ed *device, int async_update) { struct ccb_pathinq cpi; struct ccb_trans_settings cur_cts; struct ccb_trans_settings_scsi *scsi; struct ccb_trans_settings_scsi *cur_scsi; struct cam_sim *sim; struct scsi_inquiry_data *inq_data; if (device == NULL) { cts->ccb_h.status = CAM_PATH_INVALID; xpt_done((union ccb *)cts); return; } if (cts->protocol == PROTO_UNKNOWN || cts->protocol == PROTO_UNSPECIFIED) { cts->protocol = device->protocol; cts->protocol_version = device->protocol_version; } if (cts->protocol_version == PROTO_VERSION_UNKNOWN || cts->protocol_version == PROTO_VERSION_UNSPECIFIED) cts->protocol_version = device->protocol_version; if (cts->protocol != device->protocol) { xpt_print_path(cts->ccb_h.path); printf("Uninitialized Protocol %x:%x?\n", cts->protocol, device->protocol); cts->protocol = device->protocol; } if (cts->protocol_version > device->protocol_version) { if (bootverbose) { xpt_print_path(cts->ccb_h.path); printf("Down reving Protocol Version from %d to %d?\n", cts->protocol_version, device->protocol_version); } cts->protocol_version = device->protocol_version; } if (cts->transport == XPORT_UNKNOWN || cts->transport == XPORT_UNSPECIFIED) { cts->transport = device->transport; cts->transport_version = device->transport_version; } if (cts->transport_version == XPORT_VERSION_UNKNOWN || cts->transport_version == XPORT_VERSION_UNSPECIFIED) cts->transport_version = device->transport_version; if (cts->transport != device->transport) { xpt_print_path(cts->ccb_h.path); printf("Uninitialized Transport %x:%x?\n", cts->transport, device->transport); cts->transport = device->transport; } if (cts->transport_version > device->transport_version) { if (bootverbose) { xpt_print_path(cts->ccb_h.path); printf("Down reving Transport Version from %d to %d?\n", cts->transport_version, device->transport_version); } cts->transport_version = device->transport_version; } sim = cts->ccb_h.path->bus->sim; /* * Nothing more of interest to do unless * this is a device connected via the * SCSI protocol. */ if (cts->protocol != PROTO_SCSI) { if (async_update == FALSE) (*(sim->sim_action))(sim, (union ccb *)cts); return; } inq_data = &device->inq_data; scsi = &cts->proto_specific.scsi; xpt_setup_ccb(&cpi.ccb_h, cts->ccb_h.path, /*priority*/1); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); /* SCSI specific sanity checking */ if ((cpi.hba_inquiry & PI_TAG_ABLE) == 0 || (inq_data->flags & SID_CmdQue) == 0 || (device->queue_flags & SCP_QUEUE_DQUE) != 0 || (device->quirk->mintags == 0)) { /* * Can't tag on hardware that doesn't support tags, * doesn't have it enabled, or has broken tag support. */ scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB; } if (async_update == FALSE) { /* * Perform sanity checking against what the * controller and device can do. */ xpt_setup_ccb(&cur_cts.ccb_h, cts->ccb_h.path, /*priority*/1); cur_cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; cur_cts.type = cts->type; xpt_action((union ccb *)&cur_cts); cur_scsi = &cur_cts.proto_specific.scsi; if ((scsi->valid & CTS_SCSI_VALID_TQ) == 0) { scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB; scsi->flags |= cur_scsi->flags & CTS_SCSI_FLAGS_TAG_ENB; } if ((cur_scsi->valid & CTS_SCSI_VALID_TQ) == 0) scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB; } /* SPI specific sanity checking */ if (cts->transport == XPORT_SPI && async_update == FALSE) { u_int spi3caps; struct ccb_trans_settings_spi *spi; struct ccb_trans_settings_spi *cur_spi; spi = &cts->xport_specific.spi; cur_spi = &cur_cts.xport_specific.spi; /* Fill in any gaps in what the user gave us */ if ((spi->valid & CTS_SPI_VALID_SYNC_RATE) == 0) spi->sync_period = cur_spi->sync_period; if ((cur_spi->valid & CTS_SPI_VALID_SYNC_RATE) == 0) spi->sync_period = 0; if ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) == 0) spi->sync_offset = cur_spi->sync_offset; if ((cur_spi->valid & CTS_SPI_VALID_SYNC_OFFSET) == 0) spi->sync_offset = 0; if ((spi->valid & CTS_SPI_VALID_PPR_OPTIONS) == 0) spi->ppr_options = cur_spi->ppr_options; if ((cur_spi->valid & CTS_SPI_VALID_PPR_OPTIONS) == 0) spi->ppr_options = 0; if ((spi->valid & CTS_SPI_VALID_BUS_WIDTH) == 0) spi->bus_width = cur_spi->bus_width; if ((cur_spi->valid & CTS_SPI_VALID_BUS_WIDTH) == 0) spi->bus_width = 0; if ((spi->valid & CTS_SPI_VALID_DISC) == 0) { spi->flags &= ~CTS_SPI_FLAGS_DISC_ENB; spi->flags |= cur_spi->flags & CTS_SPI_FLAGS_DISC_ENB; } if ((cur_spi->valid & CTS_SPI_VALID_DISC) == 0) spi->flags &= ~CTS_SPI_FLAGS_DISC_ENB; if (((device->flags & CAM_DEV_INQUIRY_DATA_VALID) != 0 && (inq_data->flags & SID_Sync) == 0 && cts->type == CTS_TYPE_CURRENT_SETTINGS) || ((cpi.hba_inquiry & PI_SDTR_ABLE) == 0) || (cts->sync_offset == 0) || (cts->sync_period == 0)) { /* Force async */ spi->sync_period = 0; spi->sync_offset = 0; } switch (spi->bus_width) { case MSG_EXT_WDTR_BUS_32_BIT: if (((device->flags & CAM_DEV_INQUIRY_DATA_VALID) == 0 || (inq_data->flags & SID_WBus32) != 0 || cts->type == CTS_TYPE_USER_SETTINGS) && (cpi.hba_inquiry & PI_WIDE_32) != 0) break; /* Fall Through to 16-bit */ case MSG_EXT_WDTR_BUS_16_BIT: if (((device->flags & CAM_DEV_INQUIRY_DATA_VALID) == 0 || (inq_data->flags & SID_WBus16) != 0 || cts->type == CTS_TYPE_USER_SETTINGS) && (cpi.hba_inquiry & PI_WIDE_16) != 0) { spi->bus_width = MSG_EXT_WDTR_BUS_16_BIT; break; } /* Fall Through to 8-bit */ default: /* New bus width?? */ case MSG_EXT_WDTR_BUS_8_BIT: /* All targets can do this */ spi->bus_width = MSG_EXT_WDTR_BUS_8_BIT; break; } spi3caps = cpi.xport_specific.spi.ppr_options; if ((device->flags & CAM_DEV_INQUIRY_DATA_VALID) != 0 && cts->type == CTS_TYPE_CURRENT_SETTINGS) spi3caps &= inq_data->spi3data; if ((spi3caps & SID_SPI_CLOCK_DT) == 0) spi->ppr_options &= ~MSG_EXT_PPR_DT_REQ; if ((spi3caps & SID_SPI_IUS) == 0) spi->ppr_options &= ~MSG_EXT_PPR_IU_REQ; if ((spi3caps & SID_SPI_QAS) == 0) spi->ppr_options &= ~MSG_EXT_PPR_QAS_REQ; /* No SPI Transfer settings are allowed unless we are wide */ if (spi->bus_width == 0) spi->ppr_options = 0; if ((spi->flags & CTS_SPI_FLAGS_DISC_ENB) == 0) { /* * Can't tag queue without disconnection. */ scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB; scsi->valid |= CTS_SCSI_VALID_TQ; } /* * If we are currently performing tagged transactions to * this device and want to change its negotiation parameters, * go non-tagged for a bit to give the controller a chance to * negotiate unhampered by tag messages. */ if (cts->type == CTS_TYPE_CURRENT_SETTINGS && (device->inq_flags & SID_CmdQue) != 0 && (scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) != 0 && (spi->flags & (CTS_SPI_VALID_SYNC_RATE| CTS_SPI_VALID_SYNC_OFFSET| CTS_SPI_VALID_BUS_WIDTH)) != 0) xpt_toggle_tags(cts->ccb_h.path); } if (cts->type == CTS_TYPE_CURRENT_SETTINGS && (scsi->valid & CTS_SCSI_VALID_TQ) != 0) { int device_tagenb; /* * If we are transitioning from tags to no-tags or * vice-versa, we need to carefully freeze and restart * the queue so that we don't overlap tagged and non-tagged * commands. We also temporarily stop tags if there is * a change in transfer negotiation settings to allow * "tag-less" negotiation. */ if ((device->flags & CAM_DEV_TAG_AFTER_COUNT) != 0 || (device->inq_flags & SID_CmdQue) != 0) device_tagenb = TRUE; else device_tagenb = FALSE; if (((scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) != 0 && device_tagenb == FALSE) || ((scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) == 0 && device_tagenb == TRUE)) { if ((scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) != 0) { /* * Delay change to use tags until after a * few commands have gone to this device so * the controller has time to perform transfer * negotiations without tagged messages getting * in the way. */ device->tag_delay_count = CAM_TAG_DELAY_COUNT; device->flags |= CAM_DEV_TAG_AFTER_COUNT; } else { struct ccb_relsim crs; xpt_freeze_devq(cts->ccb_h.path, /*count*/1); device->inq_flags &= ~SID_CmdQue; xpt_dev_ccbq_resize(cts->ccb_h.path, sim->max_dev_openings); device->flags &= ~CAM_DEV_TAG_AFTER_COUNT; device->tag_delay_count = 0; xpt_setup_ccb(&crs.ccb_h, cts->ccb_h.path, /*priority*/1); crs.ccb_h.func_code = XPT_REL_SIMQ; crs.release_flags = RELSIM_RELEASE_AFTER_QEMPTY; crs.openings = crs.release_timeout = crs.qfrozen_cnt = 0; xpt_action((union ccb *)&crs); } } } if (async_update == FALSE) (*(sim->sim_action))(sim, (union ccb *)cts); } #else /* CAM_NEW_TRAN_CODE */ static void xpt_set_transfer_settings(struct ccb_trans_settings *cts, struct cam_ed *device, int async_update) { struct cam_sim *sim; int qfrozen; sim = cts->ccb_h.path->bus->sim; if (async_update == FALSE) { struct scsi_inquiry_data *inq_data; struct ccb_pathinq cpi; struct ccb_trans_settings cur_cts; if (device == NULL) { cts->ccb_h.status = CAM_PATH_INVALID; xpt_done((union ccb *)cts); return; } /* * Perform sanity checking against what the * controller and device can do. */ xpt_setup_ccb(&cpi.ccb_h, cts->ccb_h.path, /*priority*/1); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); xpt_setup_ccb(&cur_cts.ccb_h, cts->ccb_h.path, /*priority*/1); cur_cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; cur_cts.flags = CCB_TRANS_CURRENT_SETTINGS; xpt_action((union ccb *)&cur_cts); inq_data = &device->inq_data; /* Fill in any gaps in what the user gave us */ if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) == 0) cts->sync_period = cur_cts.sync_period; if ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) == 0) cts->sync_offset = cur_cts.sync_offset; if ((cts->valid & CCB_TRANS_BUS_WIDTH_VALID) == 0) cts->bus_width = cur_cts.bus_width; if ((cts->valid & CCB_TRANS_DISC_VALID) == 0) { cts->flags &= ~CCB_TRANS_DISC_ENB; cts->flags |= cur_cts.flags & CCB_TRANS_DISC_ENB; } if ((cts->valid & CCB_TRANS_TQ_VALID) == 0) { cts->flags &= ~CCB_TRANS_TAG_ENB; cts->flags |= cur_cts.flags & CCB_TRANS_TAG_ENB; } if (((device->flags & CAM_DEV_INQUIRY_DATA_VALID) != 0 && (inq_data->flags & SID_Sync) == 0) || ((cpi.hba_inquiry & PI_SDTR_ABLE) == 0) || (cts->sync_offset == 0) || (cts->sync_period == 0)) { /* Force async */ cts->sync_period = 0; cts->sync_offset = 0; } else if ((device->flags & CAM_DEV_INQUIRY_DATA_VALID) != 0 && (inq_data->spi3data & SID_SPI_CLOCK_DT) == 0 && cts->sync_period <= 0x9) { /* * Don't allow DT transmission rates if the * device does not support it. */ cts->sync_period = 0xa; } switch (cts->bus_width) { case MSG_EXT_WDTR_BUS_32_BIT: if (((device->flags & CAM_DEV_INQUIRY_DATA_VALID) == 0 || (inq_data->flags & SID_WBus32) != 0) && (cpi.hba_inquiry & PI_WIDE_32) != 0) break; /* Fall Through to 16-bit */ case MSG_EXT_WDTR_BUS_16_BIT: if (((device->flags & CAM_DEV_INQUIRY_DATA_VALID) == 0 || (inq_data->flags & SID_WBus16) != 0) && (cpi.hba_inquiry & PI_WIDE_16) != 0) { cts->bus_width = MSG_EXT_WDTR_BUS_16_BIT; break; } /* Fall Through to 8-bit */ default: /* New bus width?? */ case MSG_EXT_WDTR_BUS_8_BIT: /* All targets can do this */ cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT; break; } if ((cts->flags & CCB_TRANS_DISC_ENB) == 0) { /* * Can't tag queue without disconnection. */ cts->flags &= ~CCB_TRANS_TAG_ENB; cts->valid |= CCB_TRANS_TQ_VALID; } if ((cpi.hba_inquiry & PI_TAG_ABLE) == 0 || (inq_data->flags & SID_CmdQue) == 0 || (device->queue_flags & SCP_QUEUE_DQUE) != 0 || (device->quirk->mintags == 0)) { /* * Can't tag on hardware that doesn't support, * doesn't have it enabled, or has broken tag support. */ cts->flags &= ~CCB_TRANS_TAG_ENB; } } qfrozen = FALSE; if ((cts->valid & CCB_TRANS_TQ_VALID) != 0) { int device_tagenb; /* * If we are transitioning from tags to no-tags or * vice-versa, we need to carefully freeze and restart * the queue so that we don't overlap tagged and non-tagged * commands. We also temporarily stop tags if there is * a change in transfer negotiation settings to allow * "tag-less" negotiation. */ if ((device->flags & CAM_DEV_TAG_AFTER_COUNT) != 0 || (device->inq_flags & SID_CmdQue) != 0) device_tagenb = TRUE; else device_tagenb = FALSE; if (((cts->flags & CCB_TRANS_TAG_ENB) != 0 && device_tagenb == FALSE) || ((cts->flags & CCB_TRANS_TAG_ENB) == 0 && device_tagenb == TRUE)) { if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) { /* * Delay change to use tags until after a * few commands have gone to this device so * the controller has time to perform transfer * negotiations without tagged messages getting * in the way. */ device->tag_delay_count = CAM_TAG_DELAY_COUNT; device->flags |= CAM_DEV_TAG_AFTER_COUNT; } else { xpt_freeze_devq(cts->ccb_h.path, /*count*/1); qfrozen = TRUE; device->inq_flags &= ~SID_CmdQue; xpt_dev_ccbq_resize(cts->ccb_h.path, sim->max_dev_openings); device->flags &= ~CAM_DEV_TAG_AFTER_COUNT; device->tag_delay_count = 0; } } } if (async_update == FALSE) { /* * If we are currently performing tagged transactions to * this device and want to change its negotiation parameters, * go non-tagged for a bit to give the controller a chance to * negotiate unhampered by tag messages. */ if ((device->inq_flags & SID_CmdQue) != 0 && (cts->flags & (CCB_TRANS_SYNC_RATE_VALID| CCB_TRANS_SYNC_OFFSET_VALID| CCB_TRANS_BUS_WIDTH_VALID)) != 0) xpt_toggle_tags(cts->ccb_h.path); (*(sim->sim_action))(sim, (union ccb *)cts); } if (qfrozen) { struct ccb_relsim crs; xpt_setup_ccb(&crs.ccb_h, cts->ccb_h.path, /*priority*/1); crs.ccb_h.func_code = XPT_REL_SIMQ; crs.release_flags = RELSIM_RELEASE_AFTER_QEMPTY; crs.openings = crs.release_timeout = crs.qfrozen_cnt = 0; xpt_action((union ccb *)&crs); } } #endif /* CAM_NEW_TRAN_CODE */ static void xpt_toggle_tags(struct cam_path *path) { struct cam_ed *dev; /* * Give controllers a chance to renegotiate * before starting tag operations. We * "toggle" tagged queuing off then on * which causes the tag enable command delay * counter to come into effect. */ dev = path->device; if ((dev->flags & CAM_DEV_TAG_AFTER_COUNT) != 0 || ((dev->inq_flags & SID_CmdQue) != 0 && (dev->inq_flags & (SID_Sync|SID_WBus16|SID_WBus32)) != 0)) { struct ccb_trans_settings cts; xpt_setup_ccb(&cts.ccb_h, path, 1); #ifdef CAM_NEW_TRAN_CODE cts.protocol = PROTO_SCSI; cts.protocol_version = PROTO_VERSION_UNSPECIFIED; cts.transport = XPORT_UNSPECIFIED; cts.transport_version = XPORT_VERSION_UNSPECIFIED; cts.proto_specific.scsi.flags = 0; cts.proto_specific.scsi.valid = CTS_SCSI_VALID_TQ; #else /* CAM_NEW_TRAN_CODE */ cts.flags = 0; cts.valid = CCB_TRANS_TQ_VALID; #endif /* CAM_NEW_TRAN_CODE */ xpt_set_transfer_settings(&cts, path->device, /*async_update*/TRUE); #ifdef CAM_NEW_TRAN_CODE cts.proto_specific.scsi.flags = CTS_SCSI_FLAGS_TAG_ENB; #else /* CAM_NEW_TRAN_CODE */ cts.flags = CCB_TRANS_TAG_ENB; #endif /* CAM_NEW_TRAN_CODE */ xpt_set_transfer_settings(&cts, path->device, /*async_update*/TRUE); } } static void xpt_start_tags(struct cam_path *path) { struct ccb_relsim crs; struct cam_ed *device; struct cam_sim *sim; int newopenings; device = path->device; sim = path->bus->sim; device->flags &= ~CAM_DEV_TAG_AFTER_COUNT; xpt_freeze_devq(path, /*count*/1); device->inq_flags |= SID_CmdQue; newopenings = min(device->quirk->maxtags, sim->max_tagged_dev_openings); xpt_dev_ccbq_resize(path, newopenings); xpt_setup_ccb(&crs.ccb_h, path, /*priority*/1); crs.ccb_h.func_code = XPT_REL_SIMQ; crs.release_flags = RELSIM_RELEASE_AFTER_QEMPTY; crs.openings = crs.release_timeout = crs.qfrozen_cnt = 0; xpt_action((union ccb *)&crs); } static int busses_to_config; static int busses_to_reset; static int xptconfigbuscountfunc(struct cam_eb *bus, void *arg) { if (bus->path_id != CAM_XPT_PATH_ID) { struct cam_path path; struct ccb_pathinq cpi; int can_negotiate; busses_to_config++; xpt_compile_path(&path, NULL, bus->path_id, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); xpt_setup_ccb(&cpi.ccb_h, &path, /*priority*/1); cpi.ccb_h.func_code = XPT_PATH_INQ; xpt_action((union ccb *)&cpi); can_negotiate = cpi.hba_inquiry; can_negotiate &= (PI_WIDE_32|PI_WIDE_16|PI_SDTR_ABLE); if ((cpi.hba_misc & PIM_NOBUSRESET) == 0 && can_negotiate) busses_to_reset++; xpt_release_path(&path); } return(1); } static int xptconfigfunc(struct cam_eb *bus, void *arg) { struct cam_path *path; union ccb *work_ccb; if (bus->path_id != CAM_XPT_PATH_ID) { cam_status status; int can_negotiate; work_ccb = xpt_alloc_ccb(); if ((status = xpt_create_path(&path, xpt_periph, bus->path_id, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD)) !=CAM_REQ_CMP){ printf("xptconfigfunc: xpt_create_path failed with " "status %#x for bus %d\n", status, bus->path_id); printf("xptconfigfunc: halting bus configuration\n"); xpt_free_ccb(work_ccb); busses_to_config--; xpt_finishconfig(xpt_periph, NULL); return(0); } xpt_setup_ccb(&work_ccb->ccb_h, path, /*priority*/1); work_ccb->ccb_h.func_code = XPT_PATH_INQ; xpt_action(work_ccb); if (work_ccb->ccb_h.status != CAM_REQ_CMP) { printf("xptconfigfunc: CPI failed on bus %d " "with status %d\n", bus->path_id, work_ccb->ccb_h.status); xpt_finishconfig(xpt_periph, work_ccb); return(1); } can_negotiate = work_ccb->cpi.hba_inquiry; can_negotiate &= (PI_WIDE_32|PI_WIDE_16|PI_SDTR_ABLE); if ((work_ccb->cpi.hba_misc & PIM_NOBUSRESET) == 0 && (can_negotiate != 0)) { xpt_setup_ccb(&work_ccb->ccb_h, path, /*priority*/1); work_ccb->ccb_h.func_code = XPT_RESET_BUS; work_ccb->ccb_h.cbfcnp = NULL; CAM_DEBUG(path, CAM_DEBUG_SUBTRACE, ("Resetting Bus\n")); xpt_action(work_ccb); xpt_finishconfig(xpt_periph, work_ccb); } else { /* Act as though we performed a successful BUS RESET */ work_ccb->ccb_h.func_code = XPT_RESET_BUS; xpt_finishconfig(xpt_periph, work_ccb); } } return(1); } static void xpt_config(void *arg) { /* * Now that interrupts are enabled, go find our devices */ #ifdef CAMDEBUG /* Setup debugging flags and path */ #ifdef CAM_DEBUG_FLAGS cam_dflags = CAM_DEBUG_FLAGS; #else /* !CAM_DEBUG_FLAGS */ cam_dflags = CAM_DEBUG_NONE; #endif /* CAM_DEBUG_FLAGS */ #ifdef CAM_DEBUG_BUS if (cam_dflags != CAM_DEBUG_NONE) { if (xpt_create_path(&cam_dpath, xpt_periph, CAM_DEBUG_BUS, CAM_DEBUG_TARGET, CAM_DEBUG_LUN) != CAM_REQ_CMP) { printf("xpt_config: xpt_create_path() failed for debug" " target %d:%d:%d, debugging disabled\n", CAM_DEBUG_BUS, CAM_DEBUG_TARGET, CAM_DEBUG_LUN); cam_dflags = CAM_DEBUG_NONE; } } else cam_dpath = NULL; #else /* !CAM_DEBUG_BUS */ cam_dpath = NULL; #endif /* CAM_DEBUG_BUS */ #endif /* CAMDEBUG */ /* * Scan all installed busses. */ xpt_for_all_busses(xptconfigbuscountfunc, NULL); if (busses_to_config == 0) { /* Call manually because we don't have any busses */ xpt_finishconfig(xpt_periph, NULL); } else { if (busses_to_reset > 0 && SCSI_DELAY >= 2000) { printf("Waiting %d seconds for SCSI " "devices to settle\n", SCSI_DELAY/1000); } xpt_for_all_busses(xptconfigfunc, NULL); } } /* * If the given device only has one peripheral attached to it, and if that * peripheral is the passthrough driver, announce it. This insures that the * user sees some sort of announcement for every peripheral in their system. */ static int xptpassannouncefunc(struct cam_ed *device, void *arg) { struct cam_periph *periph; int i; for (periph = SLIST_FIRST(&device->periphs), i = 0; periph != NULL; periph = SLIST_NEXT(periph, periph_links), i++); periph = SLIST_FIRST(&device->periphs); if ((i == 1) && (strncmp(periph->periph_name, "pass", 4) == 0)) xpt_announce_periph(periph, NULL); return(1); } static void xpt_finishconfig(struct cam_periph *periph, union ccb *done_ccb) { struct periph_driver **p_drv; int i; if (done_ccb != NULL) { CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("xpt_finishconfig\n")); switch(done_ccb->ccb_h.func_code) { case XPT_RESET_BUS: if (done_ccb->ccb_h.status == CAM_REQ_CMP) { done_ccb->ccb_h.func_code = XPT_SCAN_BUS; done_ccb->ccb_h.cbfcnp = xpt_finishconfig; xpt_action(done_ccb); return; } /* FALLTHROUGH */ case XPT_SCAN_BUS: default: xpt_free_path(done_ccb->ccb_h.path); busses_to_config--; break; } } if (busses_to_config == 0) { /* Register all the peripheral drivers */ /* XXX This will have to change when we have loadable modules */ p_drv = periph_drivers; for (i = 0; p_drv[i] != NULL; i++) { (*p_drv[i]->init)(); } /* * Check for devices with no "standard" peripheral driver * attached. For any devices like that, announce the * passthrough driver so the user will see something. */ xpt_for_all_devices(xptpassannouncefunc, NULL); /* Release our hook so that the boot can continue. */ config_intrhook_disestablish(xpt_config_hook); free(xpt_config_hook, M_TEMP); xpt_config_hook = NULL; } if (done_ccb != NULL) xpt_free_ccb(done_ccb); } static void xptaction(struct cam_sim *sim, union ccb *work_ccb) { CAM_DEBUG(work_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("xptaction\n")); switch (work_ccb->ccb_h.func_code) { /* Common cases first */ case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi; cpi = &work_ccb->cpi; cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = 0; cpi->target_sprt = 0; cpi->hba_misc = 0; cpi->hba_eng_cnt = 0; cpi->max_target = 0; cpi->max_lun = 0; cpi->initiator_id = 0; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "", HBA_IDLEN); strncpy(cpi->dev_name, sim->sim_name, DEV_IDLEN); cpi->unit_number = sim->unit_number; cpi->bus_id = sim->bus_id; cpi->base_transfer_speed = 0; #ifdef CAM_NEW_TRAN_CODE cpi->protocol = PROTO_UNSPECIFIED; cpi->protocol_version = PROTO_VERSION_UNSPECIFIED; cpi->transport = XPORT_UNSPECIFIED; cpi->transport_version = XPORT_VERSION_UNSPECIFIED; #endif /* CAM_NEW_TRAN_CODE */ cpi->ccb_h.status = CAM_REQ_CMP; xpt_done(work_ccb); break; } default: work_ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(work_ccb); break; } } /* * The xpt as a "controller" has no interrupt sources, so polling * is a no-op. */ static void xptpoll(struct cam_sim *sim) { } static void camisr(void *V_queue) { cam_isrq_t *queue = V_queue; int s; struct ccb_hdr *ccb_h; s = splcam(); while ((ccb_h = TAILQ_FIRST(queue)) != NULL) { int runq; TAILQ_REMOVE(queue, ccb_h, sim_links.tqe); ccb_h->pinfo.index = CAM_UNQUEUED_INDEX; splx(s); CAM_DEBUG(ccb_h->path, CAM_DEBUG_TRACE, ("camisr")); runq = FALSE; if (ccb_h->flags & CAM_HIGH_POWER) { struct highpowerlist *hphead; struct cam_ed *device; union ccb *send_ccb; hphead = &highpowerq; send_ccb = (union ccb *)STAILQ_FIRST(hphead); /* * Increment the count since this command is done. */ num_highpower++; /* * Any high powered commands queued up? */ if (send_ccb != NULL) { device = send_ccb->ccb_h.path->device; STAILQ_REMOVE_HEAD(hphead, xpt_links.stqe); xpt_release_devq(send_ccb->ccb_h.path, /*count*/1, /*runqueue*/TRUE); } } if ((ccb_h->func_code & XPT_FC_USER_CCB) == 0) { struct cam_ed *dev; dev = ccb_h->path->device; s = splcam(); cam_ccbq_ccb_done(&dev->ccbq, (union ccb *)ccb_h); ccb_h->path->bus->sim->devq->send_active--; ccb_h->path->bus->sim->devq->send_openings++; splx(s); if (((dev->flags & CAM_DEV_REL_ON_COMPLETE) != 0 && (ccb_h->status&CAM_STATUS_MASK) != CAM_REQUEUE_REQ) || ((dev->flags & CAM_DEV_REL_ON_QUEUE_EMPTY) != 0 && (dev->ccbq.dev_active == 0))) { xpt_release_devq(ccb_h->path, /*count*/1, /*run_queue*/TRUE); } if ((dev->flags & CAM_DEV_TAG_AFTER_COUNT) != 0 && (--dev->tag_delay_count == 0)) xpt_start_tags(ccb_h->path); if ((dev->ccbq.queue.entries > 0) && (dev->qfrozen_cnt == 0) && (device_is_send_queued(dev) == 0)) { runq = xpt_schedule_dev_sendq(ccb_h->path->bus, dev); } } if (ccb_h->status & CAM_RELEASE_SIMQ) { xpt_release_simq(ccb_h->path->bus->sim, /*run_queue*/TRUE); ccb_h->status &= ~CAM_RELEASE_SIMQ; runq = FALSE; } if ((ccb_h->flags & CAM_DEV_QFRZDIS) && (ccb_h->status & CAM_DEV_QFRZN)) { xpt_release_devq(ccb_h->path, /*count*/1, /*run_queue*/TRUE); ccb_h->status &= ~CAM_DEV_QFRZN; } else if (runq) { xpt_run_dev_sendq(ccb_h->path->bus); } /* Call the peripheral driver's callback */ (*ccb_h->cbfcnp)(ccb_h->path->periph, (union ccb *)ccb_h); /* Raise IPL for while test */ s = splcam(); } splx(s); } Index: head/sys/conf/NOTES =================================================================== --- head/sys/conf/NOTES (revision 78134) +++ head/sys/conf/NOTES (revision 78135) @@ -1,2873 +1,2873 @@ # # NOTES -- Lines that can be cut/pasted into kernel and hints configs. # # Lines that begin with 'device', 'options', 'machine', 'ident', 'maxusers', # 'makeoptions', 'hints' etc go into the kernel configuration that you # run config(8) with. # # Lines that begin with 'hints.' are NOT for config(8), they go into your # hints file. See /boot/device.hints and/or the 'hints' config(8) directive. # # Please use ``make LINT'' to create an old-style LINT file if you want to # do kernel test-builds. # # $FreeBSD$ # # # This directive is mandatory; it defines the architecture to be # configured for; in this case, the 386 family based IBM-PC and # compatibles. # machine i386 # # This is the ``identification'' of the kernel. Usually this should # be the same as the name of your kernel. # ident LINT # # The `maxusers' parameter controls the static sizing of a number of # internal system tables by a complicated formula defined in param.c. # maxusers 10 # # We want LINT to cover profiling as well profile 2 # # The `makeoptions' parameter allows variables to be passed to the # generated Makefile in the build area. # # CONF_CFLAGS gives some extra compiler flags that are added to ${CFLAGS} # after most other flags. Here we use it to inhibit use of non-optimal # gcc builtin functions (e.g., memcmp). # # DEBUG happens to be magic. # The following is equivalent to 'config -g KERNELNAME' and creates # 'kernel.debug' compiled with -g debugging as well as a normal # 'kernel'. Use 'make install.debug' to install the debug kernel # but that isn't normally necessary as the debug symbols are not loaded # by the kernel and are not useful there anyway. # # KERNEL can be overridden so that you can change the default name of your # kernel. # makeoptions CONF_CFLAGS=-fno-builtin #Don't allow use of memcmp, etc. #makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols #makeoptions KERNEL=foo #Build kernel "foo" and install "/foo" # # Certain applications can grow to be larger than the 512M limit # that FreeBSD initially imposes. Below are some options to # allow that limit to grow to 1GB, and can be increased further # with changing the parameters. MAXDSIZ is the maximum that the # limit can be set to, and the DFLDSIZ is the default value for # the limit. You might want to set the default lower than the # max, and explicitly set the maximum with a shell command for processes # that regularly exceed the limit like INND. # options MAXDSIZ="(1024UL*1024*1024)" options DFLDSIZ="(1024UL*1024*1024)" # # BLKDEV_IOSIZE sets the default block size used in user block # device I/O. Note that this value will be overriden by the label # when specifying a block device from a label with a non-0 # partition blocksize. The default is PAGE_SIZE. # options BLKDEV_IOSIZE=8192 # Options for the VM subsystem options PQ_CACHESIZE=512 # color for 512k/16k cache # Deprecated options supported for backwards compatibility #options PQ_NOOPT # No coloring #options PQ_LARGECACHE # color for 512k/16k cache #options PQ_HUGECACHE # color for 1024k/16k cache #options PQ_MEDIUMCACHE # color for 256k/16k cache #options PQ_NORMALCACHE # color for 64k/16k cache # This allows you to actually store this configuration file into # the kernel binary itself, where it may be later read by saying: # strings -n 3 /boot/kernel/kernel | sed -n 's/^___//p' > MYKERNEL # options INCLUDE_CONFIG_FILE # Include this file in kernel # # The root device and filesystem type can be compiled in; # this provides a fallback option if the root device cannot # be correctly guesst by the bootstrap code, or an override if # the RB_DFLTROOT flag (-r) is specified when booting the kernel. # options ROOTDEVNAME=\"ufs:da0s2e\" ##################################################################### # SMP OPTIONS: # # SMP enables building of a Symmetric MultiProcessor Kernel. # APIC_IO enables the use of the IO APIC for Symmetric I/O. # # Notes: # # An SMP kernel will ONLY run on an Intel MP spec. qualified motherboard. # # Be sure to disable 'cpu I386_CPU' && 'cpu I486_CPU' for SMP kernels. # # Check the 'Rogue SMP hardware' section to see if additional options # are required by your hardware. # # Mandatory: options SMP # Symmetric MultiProcessor Kernel options APIC_IO # Symmetric (APIC) I/O # # Rogue SMP hardware: # # Bridged PCI cards: # # The MP tables of most of the current generation MP motherboards # do NOT properly support bridged PCI cards. To use one of these # cards you should refer to ??? # SMP Debugging Options: # # MUTEX_DEBUG enables various extra assertions in the mutex code. # WITNESS enables the mutex witness code which detects deadlocks and cycles # during locking operations. # WITNESS_DDB causes the witness code to drop into the kernel debugger if # a lock heirarchy violation occurs or if locks are held when going to # sleep. # WITNESS_SKIPSPIN disables the witness checks on spin mutexes. options MUTEX_DEBUG options WITNESS options WITNESS_DDB options WITNESS_SKIPSPIN ##################################################################### # CPU OPTIONS # # You must specify at least one CPU (the one you intend to run on); # deleting the specification for CPUs you don't need to use may make # parts of the system run faster. # I386_CPU is mutually exclusive with the other CPU types. # #cpu I386_CPU cpu I486_CPU cpu I586_CPU # aka Pentium(tm) cpu I686_CPU # aka Pentium Pro(tm) # # Options for CPU features. # # CPU_BLUELIGHTNING_FPU_OP_CACHE enables FPU operand cache on IBM # BlueLightning CPU. It works only with Cyrix FPU, and this option # should not be used with Intel FPU. # # CPU_BLUELIGHTNING_3X enables triple-clock mode on IBM Blue Lightning # CPU if CPU supports it. The default is double-clock mode on # BlueLightning CPU box. # # CPU_BTB_EN enables branch target buffer on Cyrix 5x86 (NOTE 1). # # CPU_DIRECT_MAPPED_CACHE sets L1 cache of Cyrix 486DLC CPU in direct # mapped mode. Default is 2-way set associative mode. # # CPU_CYRIX_NO_LOCK enables weak locking for the entire address space # of Cyrix 6x86 and 6x86MX CPUs by setting the NO_LOCK bit of CCR1. # Otherwise, the NO_LOCK bit of CCR1 is cleared. (NOTE 3) # # CPU_DISABLE_5X86_LSSER disables load store serialize (i.e. enables # reorder). This option should not be used if you use memory mapped # I/O device(s). # # CPU_FASTER_5X86_FPU enables faster FPU exception handler. # # CPU_I486_ON_386 enables CPU cache on i486 based CPU upgrade products # for i386 machines. # # CPU_IORT defines I/O clock delay time (NOTE 1). Default values of # I/O clock delay time on Cyrix 5x86 and 6x86 are 0 and 7,respectively # (no clock delay). # # CPU_L2_LATENCY specifed the L2 cache latency value. This option is used # only when CPU_PPRO2CELERON is defined and Mendocino Celeron is detected. # The default value is 5. # # CPU_LOOP_EN prevents flushing the prefetch buffer if the destination # of a jump is already present in the prefetch buffer on Cyrix 5x86(NOTE # 1). # # CPU_PPRO2CELERON enables L2 cache of Mendocino Celeron CPUs. This option # is useful when you use Socket 8 to Socket 370 converter, because most Pentium # Pro BIOSs do not enable L2 cache of Mendocino Celeron CPUs. # # CPU_RSTK_EN enables return stack on Cyrix 5x86 (NOTE 1). # # CPU_SUSP_HLT enables suspend on HALT. If this option is set, CPU # enters suspend mode following execution of HALT instruction. # # CPU_WT_ALLOC enables write allocation on Cyrix 6x86/6x86MX and AMD # K5/K6/K6-2 cpus. # # CYRIX_CACHE_WORKS enables CPU cache on Cyrix 486 CPUs with cache # flush at hold state. # # CYRIX_CACHE_REALLY_WORKS enables (1) CPU cache on Cyrix 486 CPUs # without cache flush at hold state, and (2) write-back CPU cache on # Cyrix 6x86 whose revision < 2.7 (NOTE 2). # # NO_F00F_HACK disables the hack that prevents Pentiums (and ONLY # Pentiums) from locking up when a LOCK CMPXCHG8B instruction is # executed. This option is only needed if I586_CPU is also defined, # and should be included for any non-Pentium CPU that defines it. # # NO_MEMORY_HOLE is an optimisation for systems with AMD K6 processors # which indicates that the 15-16MB range is *definitely* not being # occupied by an ISA memory hole. # # NOTE 1: The options, CPU_BTB_EN, CPU_LOOP_EN, CPU_IORT, # CPU_LOOP_EN and CPU_RSTK_EN should not be used because of CPU bugs. # These options may crash your system. # # NOTE 2: If CYRIX_CACHE_REALLY_WORKS is not set, CPU cache is enabled # in write-through mode when revision < 2.7. If revision of Cyrix # 6x86 >= 2.7, CPU cache is always enabled in write-back mode. # # NOTE 3: This option may cause failures for software that requires # locked cycles in order to operate correctly. # options CPU_BLUELIGHTNING_FPU_OP_CACHE options CPU_BLUELIGHTNING_3X options CPU_BTB_EN options CPU_DIRECT_MAPPED_CACHE options CPU_DISABLE_5X86_LSSER options CPU_FASTER_5X86_FPU options CPU_I486_ON_386 options CPU_IORT options CPU_L2_LATENCY=5 options CPU_LOOP_EN options CPU_PPRO2CELERON options CPU_RSTK_EN options CPU_SUSP_HLT options CPU_WT_ALLOC options CYRIX_CACHE_WORKS options CYRIX_CACHE_REALLY_WORKS #options NO_F00F_HACK # # A math emulator is mandatory if you wish to run on hardware which # does not have a floating-point processor. Pick either the original, # bogus (but freely-distributable) math emulator, or a much more # fully-featured but GPL-licensed emulator taken from Linux. # options MATH_EMULATE #Support for x87 emulation # Don't enable both of these in a real config. options GPL_MATH_EMULATE #Support for x87 emulation via #new math emulator ##################################################################### # COMPATIBILITY OPTIONS # # Implement system calls compatible with 4.3BSD and older versions of # FreeBSD. You probably do NOT want to remove this as much current code # still relies on the 4.3 emulation. # options COMPAT_43 # # These three options provide support for System V Interface # Definition-style interprocess communication, in the form of shared # memory, semaphores, and message queues, respectively. # options SYSVSHM options SYSVSEM options SYSVMSG ##################################################################### # DEBUGGING OPTIONS # # Enable the kernel debugger. # options DDB # # Don't drop into DDB for a panic. Intended for unattended operation # where you may want to drop to DDB from the console, but still want # the machine to recover from a panic # options DDB_UNATTENDED # # If using GDB remote mode to debug the kernel, there's a non-standard # extension to the remote protocol that can be used to use the serial # port as both the debugging port and the system console. It's non- # standard and you're on your own if you enable it. See also the # "remotechat" variables in the FreeBSD specific version of gdb. # options GDB_REMOTE_CHAT # # KTRACE enables the system-call tracing facility ktrace(2). # options KTRACE #kernel tracing # # KTR is a kernel tracing mechanism imported from BSD/OS. Currently it # has no userland interface aside from a few sysctl's. It is enabled with # the KTR option. The KTR_EXTEND option causes trace events to be generated # as a string from snprintf rather than as a string and up to 5 argument # pointers. KTR_ENTRIES defines the number of entries in the circular trace # buffer. KTR_COMPILE defines the mask of events to compile into the kernel # as defined by the KTR_* constants in . KTR_MASK defines the # initial value of the ktr_mask variable which determines at runtime what # events to trace. KTR_CPUMASK determines which CPU's log events, with # bit X corresponding to cpu X. KTR_VERBOSE enables dumping of KTR events # to the console by default. This functionality can be toggled via the # debug.ktr_verbose sysctl and defaults to off if KTR_VERBOSE is not defined. # options KTR options KTR_EXTEND options KTR_ENTRIES=1024 options KTR_COMPILE="(KTR_INTR|KTR_PROC)" options KTR_MASK=KTR_INTR options KTR_CPUMASK=0x3 options KTR_VERBOSE # # The INVARIANTS option is used in a number of source files to enable # extra sanity checking of internal structures. This support is not # enabled by default because of the extra time it would take to check # for these conditions, which can only occur as a result of # programming errors. # options INVARIANTS # # The INVARIANT_SUPPORT option makes us compile in support for # verifying some of the internal structures. It is a prerequisite for # 'INVARIANTS', as enabling 'INVARIANTS' will make these functions be # called. The intent is that you can set 'INVARIANTS' for single # source files (by changing the source file or specifying it on the # command line) if you have 'INVARIANT_SUPPORT' enabled. Also, if you # wish to build a kernel module with 'INVARIANTS', then adding # 'INVARIANT_SUPPORT' to your kernel will provide all the necessary # infrastructure without the added overhead. # options INVARIANT_SUPPORT # # The DIAGNOSTIC option is used to enable extra debugging information # from some parts of the kernel. As this makes everything more noisy, # it is disabled by default. # options DIAGNOSTIC # # REGRESSION causes optional kernel interfaces necessary only for regression # testing to be enabled. These interfaces may consitute security risks # when enabled, as they permit processes to easily modify aspects of the # run-time environment to reproduce unlikely or unusual (possibly normally # impossible) scenarios. # options REGRESSION # # PERFMON causes the driver for Pentium/Pentium Pro performance counters # to be compiled. See perfmon(4) for more information. # options PERFMON # # This option let some drivers co-exist that can't co-exist in a running # system. This is used to be able to compile all kernel code in one go for # quality assurance purposes (like this file, which the option takes it name # from.) # options COMPILING_LINT # XXX - this doesn't belong here. # Allow ordinary users to take the console - this is useful for X. options UCONSOLE # XXX - this doesn't belong here either -options USERCONFIG #boot -c editor -options INTRO_USERCONFIG #imply -c and show intro screen -options VISUAL_USERCONFIG #visual boot -c editor +#options USERCONFIG #boot -c editor +#options INTRO_USERCONFIG #imply -c and show intro screen +#options VISUAL_USERCONFIG #visual boot -c editor ##################################################################### # NETWORKING OPTIONS # # Protocol families: # Only the INET (Internet) family is officially supported in FreeBSD. # Source code for the NS (Xerox Network Service) is provided for amusement # value. # options INET #Internet communications protocols options INET6 #IPv6 communications protocols options IPSEC #IP security options IPSEC_ESP #IP security (crypto; define w/ IPSEC) options IPSEC_DEBUG #debug for IP security options IPX #IPX/SPX communications protocols options IPXIP #IPX in IP encapsulation (not available) options IPTUNNEL #IP in IPX encapsulation (not available) options NCP #NetWare Core protocol options NETATALK #Appletalk communications protocols options NETATALKDEBUG #Appletalk debugging # These are currently broken but are shipped due to interest. #options NS #Xerox NS protocols #options NSIP #XNS over IP # mchain library. It can be either loaded as KLD or compiled into kernel options LIBMCHAIN # netgraph(4). Enable the base netgraph code with the NETGRAPH option. # Individual node types can be enabled with the corresponding option # listed below; however, this is not strictly necessary as netgraph # will automatically load the corresponding KLD module if the node type # is not already compiled into the kernel. Each type below has a # corresponding man page, e.g., ng_async(8). options NETGRAPH #netgraph(4) system options NETGRAPH_ASYNC options NETGRAPH_BPF options NETGRAPH_CISCO options NETGRAPH_ECHO options NETGRAPH_ETHER options NETGRAPH_FRAME_RELAY options NETGRAPH_HOLE options NETGRAPH_IFACE options NETGRAPH_KSOCKET options NETGRAPH_LMI # MPPC compression requires proprietary files (not included) #options NETGRAPH_MPPC_COMPRESSION options NETGRAPH_MPPC_ENCRYPTION options NETGRAPH_ONE2MANY options NETGRAPH_PPP options NETGRAPH_PPPOE options NETGRAPH_PPTPGRE options NETGRAPH_RFC1490 options NETGRAPH_SOCKET options NETGRAPH_TEE options NETGRAPH_TTY options NETGRAPH_UI options NETGRAPH_VJC device mn # Munich32x/Falc54 Nx64kbit/sec cards. device lmc # tulip based LanMedia WAN cards device musycc # LMC/SBE LMC1504 quad T1/E1 # # Network interfaces: # The `loop' device is MANDATORY when networking is enabled. # The `ether' device provides generic code to handle # Ethernets; it is MANDATORY when a Ethernet device driver is # configured or token-ring is enabled. # The 'fddi' device provides generic code to support FDDI. # The `sppp' device serves a similar role for certain types # of synchronous PPP links (like `cx', `ar'). # The `sl' device implements the Serial Line IP (SLIP) service. # The `ppp' device implements the Point-to-Point Protocol. # The `bpf' device enables the Berkeley Packet Filter. Be # aware of the legal and administrative consequences of enabling this # option. The number of devices determines the maximum number of # simultaneous BPF clients programs runnable. # The `disc' device implements a minimal network interface, # which throws away all packets sent and never receives any. It is # included for testing purposes. This shows up as the 'ds' interface. # The `tap' device is a pty-like virtual Ethernet interface # The `tun' device implements (user-)ppp and nos-tun # The `gif' device implements IPv6 over IP4 tunneling, # IPv4 over IPv6 tunneling, IPv4 over IPv4 tunneling and # IPv6 over IPv6 tunneling. # The XBONEHACK option allows the same pair of addresses to be configured on # multiple gif interfaces. # The `faith' device captures packets sent to it and diverts them # to the IPv4/IPv6 translation daemon. # The `stf' device implements 6to4 encapsulation. # The `ef' device provides support for multiple ethernet frame types # specified via ETHER_* options. See ef(4) for details. # # The PPP_BSDCOMP option enables support for compress(1) style entire # packet compression, the PPP_DEFLATE is for zlib/gzip style compression. # PPP_FILTER enables code for filtering the ppp data stream and selecting # events for resetting the demand dial activity timer - requires bpf. # See pppd(8) for more details. # device ether #Generic Ethernet device vlan 1 #VLAN support device token #Generic TokenRing device fddi #Generic FDDI device sppp #Generic Synchronous PPP device loop 1 #Network loopback device device bpf #Berkeley packet filter device disc #Discard device (ds0, ds1, etc) device tap #Virtual Ethernet driver device tun #Tunnel driver (ppp(8), nos-tun(8)) device sl #Serial Line IP device ppp 2 #Point-to-point protocol options PPP_BSDCOMP #PPP BSD-compress support options PPP_DEFLATE #PPP zlib/deflate/gzip support options PPP_FILTER #enable bpf filtering (needs bpf) device ef # Multiple ethernet frames support options ETHER_II # enable Ethernet_II frame options ETHER_8023 # enable Ethernet_802.3 (Novell) frame options ETHER_8022 # enable Ethernet_802.2 frame options ETHER_SNAP # enable Ethernet_802.2/SNAP frame # for IPv6 device gif 4 #IPv6 and IPv4 tunneling options XBONEHACK device faith 1 #for IPv6 and IPv4 translation device stf #6to4 IPv6 over IPv4 encapsulation # # Internet family options: # # MROUTING enables the kernel multicast packet forwarder, which works # with mrouted(8). # # IPFIREWALL enables support for IP firewall construction, in # conjunction with the `ipfw' program. IPFIREWALL_VERBOSE sends # logged packets to the system logger. IPFIREWALL_VERBOSE_LIMIT # limits the number of times a matching entry can be logged. # # WARNING: IPFIREWALL defaults to a policy of "deny ip from any to any" # and if you do not add other rules during startup to allow access, # YOU WILL LOCK YOURSELF OUT. It is suggested that you set firewall_type=open # in /etc/rc.conf when first enabling this feature, then refining the # firewall rules in /etc/rc.firewall after you've tested that the new kernel # feature works properly. # # IPFIREWALL_DEFAULT_TO_ACCEPT causes the default rule (at boot) to # allow everything. Use with care, if a cracker can crash your # firewall machine, they can get to your protected machines. However, # if you are using it as an as-needed filter for specific problems as # they arise, then this may be for you. Changing the default to 'allow' # means that you won't get stuck if the kernel and /sbin/ipfw binary get # out of sync. # # IPDIVERT enables the divert IP sockets, used by ``ipfw divert'' # # IPSTEALTH enables code to support stealth forwarding (i.e., forwarding # packets without touching the ttl). This can be useful to hide firewalls # from traceroute and similar tools. # # TCPDEBUG is undocumented. # options MROUTING # Multicast routing options IPFIREWALL #firewall options IPFIREWALL_VERBOSE #print information about # dropped packets options IPFIREWALL_FORWARD #enable transparent proxy support options IPFIREWALL_VERBOSE_LIMIT=100 #limit verbosity options IPFIREWALL_DEFAULT_TO_ACCEPT #allow everything by default options IPV6FIREWALL #firewall for IPv6 options IPV6FIREWALL_VERBOSE options IPV6FIREWALL_VERBOSE_LIMIT=100 options IPV6FIREWALL_DEFAULT_TO_ACCEPT options IPDIVERT #divert sockets options IPFILTER #ipfilter support options IPFILTER_LOG #ipfilter logging options IPFILTER_DEFAULT_BLOCK #block all packets by default options IPSTEALTH #support for stealth forwarding options TCPDEBUG # RANDOM_IP_ID causes the ID field in IP packets to be randomized # instead of incremented by 1 with each packet generated. This # option closes a minor information leak which allows remote # observers to determine the rate of packet generation on the # machine by watching the counter. options RANDOM_IP_ID # Statically Link in accept filters options ACCEPT_FILTER_DATA options ACCEPT_FILTER_HTTP # TCP_DROP_SYNFIN adds support for ignoring TCP packets with SYN+FIN. This # prevents nmap et al. from identifying the TCP/IP stack, but breaks support # for RFC1644 extensions and is not recommended for web servers. # options TCP_DROP_SYNFIN #drop TCP packets with SYN+FIN # DUMMYNET enables the "dummynet" bandwidth limiter. You need # IPFIREWALL as well. See the dummynet(4) manpage for more info. # BRIDGE enables bridging between ethernet cards -- see bridge(4). # You can use IPFIREWALL and dummynet together with bridging. options DUMMYNET options BRIDGE # # ATM (HARP version) options # # ATM_CORE includes the base ATM functionality code. This must be included # for ATM support. # # ATM_IP includes support for running IP over ATM. # # At least one (and usually only one) of the following signalling managers # must be included (note that all signalling managers include PVC support): # ATM_SIGPVC includes support for the PVC-only signalling manager `sigpvc'. # ATM_SPANS includes support for the `spans' signalling manager, which runs # the FORE Systems's proprietary SPANS signalling protocol. # ATM_UNI includes support for the `uni30' and `uni31' signalling managers, # which run the ATM Forum UNI 3.x signalling protocols. # # The `hea' driver provides support for the Efficient Networks, Inc. # ENI-155p ATM PCI Adapter. # # The `hfa' driver provides support for the FORE Systems, Inc. # PCA-200E ATM PCI Adapter. # options ATM_CORE #core ATM protocol family options ATM_IP #IP over ATM support options ATM_SIGPVC #SIGPVC signalling manager options ATM_SPANS #SPANS signalling manager options ATM_UNI #UNI signalling manager device hea #Efficient ENI-155p ATM PCI device hfa #FORE PCA-200E ATM PCI ##################################################################### # FILESYSTEM OPTIONS # # Only the root, /usr, and /tmp filesystems need be statically # compiled; everything else will be automatically loaded at mount # time. (Exception: the UFS family--- FFS --- cannot # currently be demand-loaded.) Some people still prefer to statically # compile other filesystems as well. # # NB: The NULL, PORTAL, UMAP and UNION filesystems are known to be # buggy, and WILL panic your system if you attempt to do anything with # them. They are included here as an incentive for some enterprising # soul to sit down and fix them. # # One of these is mandatory: options FFS #Fast filesystem options NFS #Network File System # The rest are optional: #options NFS_NOSERVER #Disable the NFS-server code. options CD9660 #ISO 9660 filesystem options FDESCFS #File descriptor filesystem options HPFS #OS/2 File system options MSDOSFS #MS DOS File System (FAT, FAT32) options NTFS #NT File System options NULLFS #NULL filesystem options NWFS #NetWare filesystem options PORTALFS #Portal filesystem options PROCFS #Process filesystem options PSEUDOFS #Pseudo-filesystem framework options UMAPFS #UID map filesystem options UNIONFS #Union filesystem # options NODEVFS #disable devices filesystem # The xFS_ROOT options REQUIRE the associated ``options xFS'' options NFS_ROOT #NFS usable as root device # This code enables IFS, an FFS which exports inodes as the namespace. # You can find details in src/sys/ufs/ifs/README . options IFS # Soft updates is a technique for improving file system speed and # making abrupt shutdown less risky. # options SOFTUPDATES # Extended attributes allow additional data to be associated with files, # and is used for ACLs, Capabilities, and MAC labels. # See src/sys/ufs/ufs/README.extattr for more information. options UFS_EXTATTR options UFS_EXTATTR_AUTOSTART # Access Control List support for UFS filesystems. The current ACL # implementation requires extended attribute support, UFS_EXTATTR, # for the underlying filesystem. # See src/sys/ufs/ufs/README.acls for more information. options UFS_ACL # Make space in the kernel for a root filesystem on a md device. # Define to the number of kilobytes to reserve for the filesystem. options MD_ROOT_SIZE=10 # Make the md device a potential root device, either with preloaded # images of type mfs_root or md_root. options MD_ROOT # Allow this many swap-devices. # # In order to manage swap, the system must reserve bitmap space that # scales with the largest mounted swap device multiplied by NSWAPDEV, # irregardless of whether other swap devices exist or not. So it # is not a good idea to make this value too large. options NSWAPDEV=5 # Disk quotas are supported when this option is enabled. options QUOTA #enable disk quotas # If you are running a machine just as a fileserver for PC and MAC # users, using SAMBA or Netatalk, you may consider setting this option # and keeping all those users' directories on a filesystem that is # mounted with the suiddir option. This gives new files the same # ownership as the directory (similar to group). It's a security hole # if you let these users run programs, so confine it to file-servers # (but it'll save you lots of headaches in those cases). Root owned # directories are exempt and X bits are cleared. The suid bit must be # set on the directory as well; see chmod(1) PC owners can't see/set # ownerships so they keep getting their toes trodden on. This saves # you all the support calls as the filesystem it's used on will act as # they expect: "It's my dir so it must be my file". # options SUIDDIR # NFS options: options NFS_MINATTRTIMO=3 # VREG attrib cache timeout in sec options NFS_MAXATTRTIMO=60 options NFS_MINDIRATTRTIMO=30 # VDIR attrib cache timeout in sec options NFS_MAXDIRATTRTIMO=60 options NFS_GATHERDELAY=10 # Default write gather delay (msec) options NFS_UIDHASHSIZ=29 # Tune the size of nfssvc_sock with this options NFS_WDELAYHASHSIZ=16 # and with this options NFS_MUIDHASHSIZ=63 # Tune the size of nfsmount with this options NFS_DEBUG # Enable NFS Debugging # Coda stuff: options CODA #CODA filesystem. device vcoda 4 #coda minicache <-> venus comm. # # Add support for the EXT2FS filesystem of Linux fame. Be a bit # careful with this - the ext2fs code has a tendency to lag behind # changes and not be exercised very much, so mounting read/write could # be dangerous (and even mounting read only could result in panics.) # options EXT2FS # Use real implementations of the aio_* system calls. There are numerous # stability issues in the current aio code that make it unsuitable for # inclusion on shell boxes. options VFS_AIO # Enable the code UFS IO optimization through the VM system. This allows # use VM operations instead of copying operations when possible. # # Even with this enabled, actual use of the code is still controlled by the # sysctl vfs.ioopt. 0 gives no optimization, 1 gives normal (use VM # operations if a request happens to fit), 2 gives agressive optimization # (the operations are split to do as much as possible through the VM system.) # # Enabling this will probably not give an overall speedup except for # special workloads. options ENABLE_VFS_IOOPT # Cryptographically secure random number generator; /dev/[u]random device random ##################################################################### # POSIX P1003.1B # Real time extensions added in the 1993 Posix # P1003_1B: Infrastructure # _KPOSIX_PRIORITY_SCHEDULING: Build in _POSIX_PRIORITY_SCHEDULING # _KPOSIX_VERSION: Version kernel is built for options P1003_1B options _KPOSIX_PRIORITY_SCHEDULING options _KPOSIX_VERSION=199309L ##################################################################### # CLOCK OPTIONS # The granularity of operation is controlled by the kernel option HZ whose # default value (100) means a granularity of 10ms. For an accurate simulation # of high data rates it might be necessary to reduce the timer granularity to # 1ms or less. Consider, however, that some interfaces using programmed I/O # may require a considerable time to output packets. So, reducing the # granularity too much might actually cause ticks to be missed thus reducing # the accuracy of operation. options HZ=100 # Other clock options options CLK_CALIBRATION_LOOP options CLK_USE_I8254_CALIBRATION options CLK_USE_TSC_CALIBRATION ##################################################################### # SCSI DEVICES # SCSI DEVICE CONFIGURATION # The SCSI subsystem consists of the `base' SCSI code, a number of # high-level SCSI device `type' drivers, and the low-level host-adapter # device drivers. The host adapters are listed in the ISA and PCI # device configuration sections below. # # Beginning with FreeBSD 2.0.5 you can wire down your SCSI devices so # that a given bus, target, and LUN always come on line as the same # device unit. In earlier versions the unit numbers were assigned # in the order that the devices were probed on the SCSI bus. This # means that if you removed a disk drive, you may have had to rewrite # your /etc/fstab file, and also that you had to be careful when adding # a new disk as it may have been probed earlier and moved your device # configuration around. # This old behavior is maintained as the default behavior. The unit # assignment begins with the first non-wired down unit for a device # type. For example, if you wire a disk as "da3" then the first # non-wired disk will be assigned da4. # The syntax for wiring down devices is: hint.scbus.0.at="ahc0" hint.scbus.1.at="ahc1" hint.scbus.1.bus="0" hint.scbus.3.at="ahc2" hint.scbus.3.bus="0" hint.scbus.2.at="ahc2" hint.scbus.2.bus="1" hint.da.0.at="scbus0" hint.da.0.target="0" hint.da.0.unit="0" hint.da.1.at="scbus3" hint.da.1.target="1" hint.da.2.at="scbus2" hint.da.2.target="3" hint.sa.1.at="scbus1" hint.sa.1.target="6" # "units" (SCSI logical unit number) that are not specified are # treated as if specified as LUN 0. # All SCSI devices allocate as many units as are required. # The ch driver drives SCSI Media Changer ("jukebox") devices. # # The da driver drives SCSI Direct Access ("disk") and Optical Media # ("WORM") devices. # # The sa driver drives SCSI Sequential Access ("tape") devices. # # The cd driver drives SCSI Read Only Direct Access ("cd") devices. # # The ses driver drives SCSI Envinronment Services ("ses") and # SAF-TE ("SCSI Accessable Fault-Tolerant Enclosure") devices. # # The pt driver drives SCSI Processor devices. # # # Target Mode support is provided here but also requires that a SIM # (SCSI Host Adapter Driver) provide support as well. # # The targ driver provides target mode support as a Processor type device. # It exists to give the minimal context necessary to respond to Inquiry # commands. There is a sample user application that shows how the rest # of the command support might be done in /usr/share/examples/scsi_target. # # The targbh driver provides target mode support and exists to respond # to incoming commands that do not otherwise have a logical unit assigned # to them. # # The "unknown" device (uk? in pre-2.0.5) is now part of the base SCSI # configuration as the "pass" driver. device scbus #base SCSI code device ch #SCSI media changers device da #SCSI direct access devices (aka disks) device sa #SCSI tapes device cd #SCSI CD-ROMs device ses #SCSI Environmental Services (and SAF-TE) device pt #SCSI processor device targ #SCSI Target Mode Code device targbh #SCSI Target Mode Blackhole Device device pass #CAM passthrough driver # CAM OPTIONS: # debugging options: # -- NOTE -- If you specify one of the bus/target/lun options, you must # specify them all! # CAMDEBUG: When defined enables debugging macros # CAM_DEBUG_BUS: Debug the given bus. Use -1 to debug all busses. # CAM_DEBUG_TARGET: Debug the given target. Use -1 to debug all targets. # CAM_DEBUG_LUN: Debug the given lun. Use -1 to debug all luns. # CAM_DEBUG_FLAGS: OR together CAM_DEBUG_INFO, CAM_DEBUG_TRACE, # CAM_DEBUG_SUBTRACE, and CAM_DEBUG_CDB # # CAM_MAX_HIGHPOWER: Maximum number of concurrent high power (start unit) cmds # SCSI_NO_SENSE_STRINGS: When defined disables sense descriptions # SCSI_NO_OP_STRINGS: When defined disables opcode descriptions # SCSI_DELAY: The number of MILLISECONDS to freeze the SIM (scsi adapter) # queue after a bus reset, and the number of milliseconds to # freeze the device queue after a bus device reset. options CAMDEBUG options CAM_DEBUG_BUS=-1 options CAM_DEBUG_TARGET=-1 options CAM_DEBUG_LUN=-1 options CAM_DEBUG_FLAGS="CAM_DEBUG_INFO|CAM_DEBUG_TRACE|CAM_DEBUG_CDB" options CAM_MAX_HIGHPOWER=4 options SCSI_NO_SENSE_STRINGS options SCSI_NO_OP_STRINGS options SCSI_DELAY=8000 # Be pessimistic about Joe SCSI device # Options for the CAM CDROM driver: # CHANGER_MIN_BUSY_SECONDS: Guaranteed minimum time quantum for a changer LUN # CHANGER_MAX_BUSY_SECONDS: Maximum time quantum per changer LUN, only # enforced if there is I/O waiting for another LUN # The compiled in defaults for these variables are 2 and 10 seconds, # respectively. # # These can also be changed on the fly with the following sysctl variables: # kern.cam.cd.changer.min_busy_seconds # kern.cam.cd.changer.max_busy_seconds # options CHANGER_MIN_BUSY_SECONDS=2 options CHANGER_MAX_BUSY_SECONDS=10 # Options for the CAM sequential access driver: # SA_SPACE_TIMEOUT: Timeout for space operations, in minutes # SA_REWIND_TIMEOUT: Timeout for rewind operations, in minutes # SA_ERASE_TIMEOUT: Timeout for erase operations, in minutes # SA_1FM_AT_EOD: Default to model which only has a default one filemark at EOT. options SA_SPACE_TIMEOUT="(60)" options SA_REWIND_TIMEOUT="(2*60)" options SA_ERASE_TIMEOUT="(4*60)" options SA_1FM_AT_EOD # Optional timeout for the CAM processor target (pt) device # This is specified in seconds. The default is 60 seconds. options SCSI_PT_DEFAULT_TIMEOUT="60" # Optional enable of doing SES passthrough on other devices (e.g., disks) # # Normally disabled because a lot of newer SCSI disks report themselves # as having SES capabilities, but this can then clot up attempts to build # build a topology with the SES device that's on the box these drives # are in.... options SES_ENABLE_PASSTHROUGH ##################################################################### # MISCELLANEOUS DEVICES AND OPTIONS # The `pty' device usually turns out to be ``effectively mandatory'', # as it is required for `telnetd', `rlogind', `screen', `emacs', and # `xterm', among others. device pty #Pseudo ttys device speaker #Play IBM BASIC-style noises out your speaker device gzip #Exec gzipped a.out's device md #Memory/malloc disk device snp #Snoop device - to look at pty/vty/etc.. device ccd 4 #Concatenated disk driver # Configuring Vinum into the kernel is not necessary, since the kld # module gets started automatically when vinum(8) starts. This # device is also untested. Use at your own risk. # # The option VINUMDEBUG must match the value set in CFLAGS # in src/sbin/vinum/Makefile. Failure to do so will result in # the following message from vinum(8): # # Can't get vinum config: Invalid argument # # see vinum(4) for more reasons not to use these options. device vinum #Vinum concat/mirror/raid driver options VINUMDEBUG #enable Vinum debugging hooks # Kernel side iconv library options LIBICONV # Size of the kernel message buffer. Should be N * pagesize. options MSGBUF_SIZE=40960 ##################################################################### # HARDWARE BUS CONFIGURATION # ISA, EISA, MCA and PCI bus: # # Mandatory ISA devices: isa, npx # device isa # # Options for `isa': # # AUTO_EOI_1 enables the `automatic EOI' feature for the master 8259A # interrupt controller. This saves about 0.7-1.25 usec for each interrupt. # This option breaks suspend/resume on some portables. # # AUTO_EOI_2 enables the `automatic EOI' feature for the slave 8259A # interrupt controller. This saves about 0.7-1.25 usec for each interrupt. # Automatic EOI is documented not to work for for the slave with the # original i8259A, but it works for some clones and some integrated # versions. # # MAXMEM specifies the amount of RAM on the machine; if this is not # specified, FreeBSD will first read the amount of memory from the CMOS # RAM, so the amount of memory will initially be limited to 64MB or 16MB # depending on the BIOS. If the BIOS reports 64MB, a memory probe will # then attempt to detect the installed amount of RAM. If this probe # fails to detect >64MB RAM you will have to use the MAXMEM option. # The amount is in kilobytes, so for a machine with 128MB of RAM, it would # be 131072 (128 * 1024). # # BROKEN_KEYBOARD_RESET disables the use of the keyboard controller to # reset the CPU for reboot. This is needed on some systems with broken # keyboard controllers. options COMPAT_OLDISA #Use ISA shims and glue for old drivers options AUTO_EOI_1 #options AUTO_EOI_2 options MAXMEM="(128*1024)" #options BROKEN_KEYBOARD_RESET # Enable support for the kernel PLL to use an external PPS signal, # under supervision of [x]ntpd(8) # More info in ntpd documentation: http://www.eecis.udel.edu/~ntp options PPS_SYNC # If you see the "calcru: negative time of %ld usec for pid %d (%s)\n" # message you probably have some broken sw/hw which disables interrupts # for too long. You can make the system more resistant to this by # choosing a high value for NTIMECOUNTER. The default is 5, there # is no upper limit but more than a couple of hundred are not productive. # A better strategy may be to sysctl -w kern.timecounter.method=1 options NTIMECOUNTER=20 # # EISA bus # # The EISA bus device is `eisa'. It provides auto-detection and # configuration support for all devices on the EISA bus. device eisa # By default, only 10 EISA slots are probed, since the slot numbers # above clash with the configuration address space of the PCI subsystem, # and the EISA probe is not very smart about this. This is sufficient # for most machines, but in particular the HP NetServer LC series comes # with an onboard AIC7770 dual-channel SCSI controller on EISA slot #11, # thus you need to bump this figure to 12 for them. options EISA_SLOTS=12 # # MCA bus: # # The MCA bus device is `mca'. It provides auto-detection and # configuration support for all devices on the MCA bus. # No hints are required for MCA. device mca # # PCI bus & PCI options: # # The main PCI bus device is `pci'. It provides auto-detection and # configuration support for all devices on the PCI bus, using either # configuration mode defined in the PCI specification. device pci # # AGP GART support device agp # PCI options # #options PCI_QUIET #quiets PCI code on chipset settings ##################################################################### # HARDWARE DEVICE CONFIGURATION # EISA support is available for some device, so they can be auto-probed. # MicroChannel (MCA) support is available for some devices. # For ISA the required hints are listed. # EISA, MCA, PCI and pccard are self identifying buses, so no hints # are needed. # # Mandatory devices: # # The keyboard controller; it controls the keyboard and the PS/2 mouse. device atkbdc 1 hint.atkbdc.0.at="isa" hint.atkbdc.0.port="0x060" # The AT keyboard device atkbd hint.atkbd.0.at="atkbdc" hint.atkbd.0.irq="1" # Options for atkbd: options ATKBD_DFLT_KEYMAP # specify the built-in keymap makeoptions ATKBD_DFLT_KEYMAP="jp.106" # These options are valid for other keyboard drivers as well. options KBD_DISABLE_KEYMAP_LOAD # refuse to load a keymap options KBD_INSTALL_CDEV # install a CDEV entry in /dev # `flags' for atkbd: # 0x01 Force detection of keyboard, else we always assume a keyboard # 0x02 Don't reset keyboard, useful for some newer ThinkPads # 0x04 Old-style (XT) keyboard support, useful for older ThinkPads # PS/2 mouse device psm hint.psm.0.at="atkbdc" hint.psm.0.irq="12" # Options for psm: options PSM_HOOKRESUME #hook the system resume event, useful #for some laptops options PSM_RESETAFTERSUSPEND #reset the device at the resume event # The video card driver. device vga hint.vga.0.at="isa" # Options for vga: # Try the following option if the mouse pointer is not drawn correctly # or font does not seem to be loaded properly. May cause flicker on # some systems. options VGA_ALT_SEQACCESS # If you can dispense with some vga driver features, you may want to # use the following options to save some memory. #options VGA_NO_FONT_LOADING # don't save/load font #options VGA_NO_MODE_CHANGE # don't change video modes # Older video cards may require this option for proper operation. options VGA_SLOW_IOACCESS # do byte-wide i/o's to TS and GDC regs # The following option probably won't work with the LCD displays. options VGA_WIDTH90 # support 90 column modes # To include support for VESA video modes options VESA options FB_DEBUG # Frame buffer debugging options FB_INSTALL_CDEV # install a CDEV entry in /dev # Splash screen at start up! Screen savers require this too. device splash # The pcvt console driver (vt220 compatible). device vt hint.vt.0.at="isa" options XSERVER # support for running an X server on vt options FAT_CURSOR # start with block cursor # This PCVT option is for keyboards such as those used on IBM ThinkPad laptops options PCVT_SCANSET=2 # IBM keyboards are non-std # Other PCVT options are documented in pcvt(4). options PCVT_24LINESDEF options PCVT_CTRL_ALT_DEL options PCVT_META_ESC options PCVT_NSCREENS=9 options PCVT_PRETTYSCRNS options PCVT_SCREENSAVER options PCVT_USEKBDSEC options PCVT_VT220KEYB options PCVT_GREENSAVER # The syscons console driver (sco color console compatible). device sc 1 hint.sc.0.at="isa" options MAXCONS=16 # number of virtual consoles options SC_ALT_MOUSE_IMAGE # simplified mouse cursor in text mode options SC_DFLT_FONT # compile font in makeoptions SC_DFLT_FONT=cp850 options SC_DISABLE_DDBKEY # disable `debug' key options SC_DISABLE_REBOOT # disable reboot key sequence options SC_HISTORY_SIZE=200 # number of history buffer lines options SC_MOUSE_CHAR=0x3 # char code for text mode mouse cursor options SC_PIXEL_MODE # add support for the raster text mode # The following options will let you change the default colors of syscons. options SC_NORM_ATTR="(FG_GREEN|BG_BLACK)" options SC_NORM_REV_ATTR="(FG_YELLOW|BG_GREEN)" options SC_KERNEL_CONS_ATTR="(FG_RED|BG_BLACK)" options SC_KERNEL_CONS_REV_ATTR="(FG_BLACK|BG_RED)" # If you have a two button mouse, you may want to add the following option # to use the right button of the mouse to paste text. options SC_TWOBUTTON_MOUSE # You can selectively disable features in syscons. options SC_NO_CUTPASTE options SC_NO_FONT_LOADING options SC_NO_HISTORY options SC_NO_SYSMOUSE # `flags' for sc # 0x80 Put the video card in the VESA 800x600 dots, 16 color mode # 0x100 Probe for a keyboard device periodically if one is not present # 3Dfx Voodoo Graphics, Voodoo II /dev/3dfx CDEV support. This will create # the /dev/3dfx0 device to work with glide implementations. This should get # linked to /dev/3dfx and /dev/voodoo. Note that this is not the same as # the tdfx DRI module from XFree86 and is completely unrelated. # # To enable Linuxulator support, one must also include COMPAT_LINUX in the # config as well, or you will not have the dependencies. The other option # is to load both as modules. device tdfx # Enable 3Dfx Voodoo support options TDFX_LINUX # Enable Linuxulator support # # The Numeric Processing eXtension driver. In addition to this, you # may configure a math emulator (see above). If your machine has a # hardware FPU and the kernel configuration includes the npx device # *and* a math emulator compiled into the kernel, the hardware FPU # will be used, unless it is found to be broken or unless "flags" to # npx0 includes "0x08", which requests preference for the emulator. device npx hint.npx.0.at="nexus" hint.npx.0.port="0x0F0" hint.npx.0.flags="0x0" hint.npx.0.irq="13" # # `flags' for npx0: # 0x01 don't use the npx registers to optimize bcopy. # 0x02 don't use the npx registers to optimize bzero. # 0x04 don't use the npx registers to optimize copyin or copyout. # 0x08 use emulator even if hardware FPU is available. # The npx registers are normally used to optimize copying and zeroing when # all of the following conditions are satisfied: # I586_CPU is an option # the cpu is an i586 (perhaps not a Pentium) # the probe for npx0 succeeds # INT 16 exception handling works. # Then copying and zeroing using the npx registers is normally 30-100% faster. # The flags can be used to control cases where it doesn't work or is slower. # Setting them at boot time using userconfig works right (the optimizations # are not used until later in the bootstrap when npx0 is attached). # Flag 0x08 automatically disables the i586 optimized routines. # # # ACPI support using the Intel ACPI Component Architecture reference # implementation. # # ACPI_DEBUG enables the use of the debug.acpi.level and debug.acpi.layer # kernel environment variables to select initial debugging levels for the # Intel ACPICA code. (Note that the Intel code must also have USE_DEBUGGER # defined when it is built). # device acpica options ACPI_DEBUG # # Optional devices: # # # SCSI host adapters: # # adv: All Narrow SCSI bus AdvanSys controllers. # adw: Second Generation AdvanSys controllers including the ADV940UW. # aha: Adaptec 154x/1535/1640 # ahb: Adaptec 174x EISA controllers # ahc: Adaptec 274x/284x/2910/293x/294x/394x/3950x/3960x/398X/4944/ # 19160x/29160x, aic7770/aic78xx # aic: Adaptec 6260/6360, APA-1460 (PC Card), NEC PC9801-100 (C-BUS) # amd: Support for the AMD 53C974 SCSI host adapter chip as found on devices # such as the Tekram DC-390(T). # bt: Most Buslogic controllers: including BT-445, BT-54x, BT-64x, BT-74x, # BT-75x, BT-946, BT-948, BT-956, BT-958, SDC3211B, SDC3211F, SDC3222F # isp: Qlogic ISP 1020, 1040 and 1040B PCI SCSI host adapters, # ISP 1240 Dual Ultra SCSI, ISP 1080 and 1280 (Dual) Ultra2, # ISP 12160 Ultra3 SCSI, # Qlogic ISP 2100 and ISP 2200 Fibre Channel host adapters. # ispfw: Firmware module for Qlogic host adapters # ncr: NCR 53C810, 53C825 self-contained SCSI host adapters. # ncv: NCR 53C500 based SCSI host adapters. # nsp: Workbit Ninja SCSI-3 based PC Card SCSI host adapters. # sym: Symbios/Logic 53C8XX family of PCI-SCSI I/O processors: # 53C810, 53C810A, 53C815, 53C825, 53C825A, 53C860, 53C875, # 53C876, 53C885, 53C895, 53C895A, 53C896, 53C897, 53C1510D, # 53C1010-33, 53C1010-66. # stg: TMC 18C30, 18C50 based SCSI host adapters. # wds: WD7000 # # Note that the order is important in order for Buslogic ISA/EISA cards to be # probed correctly. # device bt hint.bt.0.at="isa" hint.bt.0.port="0x330" device adv hint.adv.0.at="isa" device adw device aha hint.aha.0.at="isa" device aic hint.aic.0.at="isa" device ahb device ahc device amd device isp hint.isp.0.disable="1" hint.isp.0.role="3" hint.isp.0.prefer_iomap="1" hint.isp.0.prefer_memmap="1" hint.isp.0.fwload_disable="1" hint.isp.0.ignore_nvram="1" hint.isp.0.fullduplex="1" hint.isp.0.topology="lport" hint.isp.0.topology="nport" hint.isp.0.topology="lport-only" hint.isp.0.topology="nport-only" # we can't get u_int64_t types, nor can we get strings if it's got # a leading 0x, hence this silly dodge. hint.isp.0.portwnn="w50000000aaaa0000" hint.isp.0.nodewnn="w50000000aaaa0001" device ispfw device ncr device ncv device nsp device sym device stg hint.stg.0.at="isa" hint.stg.0.port="0x140" hint.stg.0.port="11" device wds hint.wds.0.at="isa" hint.wds.0.port="0x350" hint.wds.0.irq="11" hint.wds.0.drq="6" # The aic7xxx driver will attempt to use memory mapped I/O for all PCI # controllers that have it configured only if this option is set. Unfortunately, # this doesn't work on some motherboards, which prevents it from being the # default. options AHC_ALLOW_MEMIO # Enable diagnostic sequencer code. options AHC_DEBUG_SEQUENCER # Dump the contents of the ahc controller configuration PROM. options AHC_DUMP_EEPROM # Bitmap of units to enable targetmode operations. options AHC_TMODE_ENABLE # The adw driver will attempt to use memory mapped I/O for all PCI # controllers that have it configured only if this option is set. options ADW_ALLOW_MEMIO # Options used in dev/isp/ (Qlogic SCSI/FC driver). # # ISP_TARGET_MODE - enable target mode operation # #options ISP_TARGET_MODE=1 # Options used in dev/sym/ (Symbios SCSI driver). #options SYM_SETUP_LP_PROBE_MAP #-Low Priority Probe Map (bits) # Allows the ncr to take precedence # 1 (1<<0) -> 810a, 860 # 2 (1<<1) -> 825a, 875, 885, 895 # 4 (1<<2) -> 895a, 896, 1510d #options SYM_SETUP_SCSI_DIFF #-HVD support for 825a, 875, 885 # disabled:0 (default), enabled:1 #options SYM_SETUP_PCI_PARITY #-PCI parity checking # disabled:0, enabled:1 (default) #options SYM_SETUP_MAX_LUN #-Number of LUNs supported # default:8, range:[1..64] # The 'asr' driver provides support for current DPT/Adaptec SCSI RAID # controllers (SmartRAID V and VI and later). # These controllers require the CAM infrastructure. # device asr # The 'dpt' driver provides support for old DPT controllers (http://www.dpt.com/). # These have hardware RAID-{0,1,5} support, and do multi-initiator I/O. # The DPT controllers are commonly re-licensed under other brand-names - # some controllers by Olivetti, Dec, HP, AT&T, SNI, AST, Alphatronic, NEC and # Compaq are actually DPT controllers. # # See src/sys/dev/dpt for debugging and other subtle options. # DPT_MEASURE_PERFORMANCE Enables a set of (semi)invasive metrics. Various # instruments are enabled. The tools in # /usr/sbin/dpt_* assume these to be enabled. # DPT_HANDLE_TIMEOUTS Normally device timeouts are handled by the DPT. # If you ant the driver to handle timeouts, enable # this option. If your system is very busy, this # option will create more trouble than solve. # DPT_TIMEOUT_FACTOR Used to compute the excessive amount of time to # wait when timing out with the above option. # DPT_DEBUG_xxxx These are controllable from sys/dev/dpt/dpt.h # DPT_LOST_IRQ When enabled, will try, once per second, to catch # any interrupt that got lost. Seems to help in some # DPT-firmware/Motherboard combinations. Minimal # cost, great benefit. # DPT_RESET_HBA Make "reset" actually reset the controller # instead of fudging it. Only enable this if you # are 100% certain you need it. device dpt # DPT options #!CAM# options DPT_MEASURE_PERFORMANCE #!CAM# options DPT_HANDLE_TIMEOUTS options DPT_TIMEOUT_FACTOR=4 options DPT_LOST_IRQ options DPT_RESET_HBA options DPT_ALLOW_MEMIO # # Mylex AcceleRAID and eXtremeRAID controllers with v6 and later # firmware. These controllers have a SCSI-like interface, and require # the CAM infrastructure. # device mly # # Adaptec FSA RAID controllers, including integrated DELL controllers, # the Dell PERC 2/QC and the HP NetRAID-4M # # AAC_COMPAT_LINUX Include code to support Linux-binary management # utilities (requires Linux compatibility # support). # device aac # # Compaq Smart RAID, Mylex DAC960 and AMI MegaRAID controllers. Only # one entry is needed; the code will find and configure all supported # controllers. # device ida # Compaq Smart RAID device mlx # Mylex DAC960 device amr # AMI MegaRAID # # 3ware ATA RAID # device twe # 3ware ATA RAID # # The 'ATA' driver supports all ATA and ATAPI devices, including PC Card # devices. You only need one "device ata" for it to find all # PCI and PC Card ATA/ATAPI devices on modern machines. device ata device atadisk # ATA disk drives device atapicd # ATAPI CDROM drives device atapifd # ATAPI floppy drives device atapist # ATAPI tape drives # # For older non-PCI, non-PnPBIOS systems, these are the hints lines to add: hint.ata.0.at="isa" hint.ata.0.port="0x1f0" hint.ata.0.irq="14" hint.ata.1.at="isa" hint.ata.1.port="0x170" hint.ata.1.irq="15" # # The following options are valid on the ATA driver: # # ATA_STATIC_ID: controller numbering is static ie depends on location # else the device numbers are dynamically allocated. options ATA_STATIC_ID # # Standard floppy disk controllers and floppy tapes, supports # the Y-E DATA External FDD (PC Card) # device fdc hint.fdc.0.at="isa" hint.fdc.0.port="0x3F0" hint.fdc.0.irq="6" hint.fdc.0.drq="2" # # FDC_DEBUG enables floppy debugging. Since the debug output is huge, you # gotta turn it actually on by setting the variable fd_debug with DDB, # however. options FDC_DEBUG # # Activate this line if you happen to have an Insight floppy tape. # Probing them proved to be dangerous for people with floppy disks only, # so it's "hidden" behind a flag: #hint.fdc.0.flags="1" # Specify floppy devices hint.fd.0.at="fdc0" hint.fd.0.drive="0" hint.fd.1.at="fdc0" hint.fd.1.drive="1" # M-systems DiskOnchip products see src/sys/contrib/dev/fla/README device fla hint.fla.0.at="isa" # # Other standard PC hardware: # # mse: Logitech and ATI InPort bus mouse ports # sio: serial ports (see sio(4)), including support for various # PC Card devices, such as Modem and NICs (see etc/defaults/pccard.conf) device mse hint.mse.0.at="isa" hint.mse.0.port="0x23c" hint.mse.0.irq="5" device sio hint.sio.0.at="isa" hint.sio.0.port="0x3F8" hint.sio.0.flags="0x10" hint.sio.0.irq="4" # # `flags' for serial drivers that support consoles (only for sio now): # 0x10 enable console support for this unit. The other console flags # are ignored unless this is set. Enabling console support does # not make the unit the preferred console - boot with -h or set # the 0x20 flag for that. Currently, at most one unit can have # console support; the first one (in config file order) with # this flag set is preferred. Setting this flag for sio0 gives # the old behaviour. # 0x20 force this unit to be the console (unless there is another # higher priority console). This replaces the COMCONSOLE option. # 0x40 reserve this unit for low level console operations. Do not # access the device in any normal way. # 0x80 use this port for serial line gdb support in ddb. # # PnP `flags' (set via userconfig using pnp x flags y) # 0x1 disable probing of this device. Used to prevent your modem # from being attached as a PnP modem. # # Options for serial drivers that support consoles (only for sio now): options BREAK_TO_DEBUGGER #a BREAK on a comconsole goes to #DDB, if available. options CONSPEED=9600 #default speed for serial console (default 9600) # Solaris implements a new BREAK which is initiated by a character # sequence CR ~ ^b which is similar to a familiar pattern used on # Sun servers by the Remote Console. options ALT_BREAK_TO_DEBUGGER # Options for sio: options COM_ESP #code for Hayes ESP options COM_MULTIPORT #code for some cards with shared IRQs # Other flags for sio that aren't documented in the man page. # 0x20000 enable hardware RTS/CTS and larger FIFOs. Only works for # ST16650A-compatible UARTs. # # Network interfaces: # # MII bus support is required for some PCI 10/100 ethernet NICs, # namely those which use MII-compliant transceivers or implement # tranceiver control interfaces that operate like an MII. Adding # "device miibus0" to the kernel config pulls in support for # the generic miibus API and all of the PHY drivers, including a # generic one for PHYs that aren't specifically handled by an # individual driver. device miibus # an: Aironet 4500/4800 802.11 wireless adapters. Supports the PCMCIA, # PCI and ISA varieties. # ar: Arnet SYNC/570i hdlc sync 2/4 port V.35/X.21 serial driver # (requires sppp) # awi: Support for IEEE 802.11 PC Card devices using the AMD Am79C930 and # Harris (Intersil) Chipset with PCnetMobile firmware by AMD. # cnw: Xircom CNW/Netware Airsurfer PC Card adapter # cs: IBM Etherjet and other Crystal Semi CS89x0-based adapters # cx: Cronyx/Sigma multiport sync/async (with Cisco or PPP framing) # dc: Support for PCI fast ethernet adapters based on the DEC/Intel 21143 # and various workalikes including: # the ADMtek AL981 Comet and AN985 Centaur, the ASIX Electronics # AX88140A and AX88141, the Davicom DM9100 and DM9102, the Lite-On # 82c168 and 82c169 PNIC, the Lite-On/Macronix LC82C115 PNIC II # and the Macronix 98713/98713A/98715/98715A/98725 PMAC. This driver # replaces the old al, ax, dm, pn and mx drivers. List of brands: # Digital DE500-BA, Kingston KNE100TX, D-Link DFE-570TX, SOHOware SFA110, # SVEC PN102-TX, CNet Pro110B, 120A, and 120B, Compex RL100-TX, # LinkSys LNE100TX, LNE100TX V2.0, Jaton XpressNet, Alfa Inc GFC2204, # KNE110TX. # de: Digital Equipment DC21040 # ed: Western Digital and SMC 80xx; Novell NE1000 and NE2000; 3Com 3C503 # HP PC Lan+, various PC Card devices (refer to etc/defauls/pccard.conf) # el: 3Com 3C501 (slow!) # ep: 3Com 3C509, 3C529, 3C556, 3C562D, 3C563D, 3C572, 3C574X, 3C579, 3C589 # and PC Card devices using these chipsets. # ex: Intel EtherExpress Pro/10 and other i82595-based adapters, # Olicom Ethernet PC Card devices. # fe: Fujitsu MB86960A/MB86965A Ethernet # fea: DEC DEFEA EISA FDDI adapter # fpa: Support for the Digital DEFPA PCI FDDI. `device fddi' is also needed. # fxp: Intel EtherExpress Pro/100B # (hint of prefer_iomap can be done to prefer I/O instead of Mem mapping) # ie: AT&T StarLAN 10 and EN100; 3Com 3C507; unknown NI5210; # Intel EtherExpress # le: Digital Equipment EtherWorks 2 and EtherWorks 3 (DEPCA, DE100, # DE101, DE200, DE201, DE202, DE203, DE204, DE205, DE422) # lnc: Lance/PCnet cards (Isolan, Novell NE2100, NE32-VL, AMD Am7990 and # Am79C960) # lge: Support for PCI gigabit ethernet adapters based on the Level 1 # LXT1001 NetCellerator chipset. This includes the D-Link DGE-500SX, # SMC TigerCard 1000 (SMC9462SX), and some Addtron cards. # nge: Support for PCI gigabit ethernet adapters based on the National # Semiconductor DP83820 and DP83821 chipset. This includes the # SMC EZ Card 1000 (SMC9462TX), D-Link DGE-500T, Asante FriendlyNet # GigaNIX 1000TA and 1000TPC, and the Addtron AEG320T. # oltr: Olicom ISA token-ring adapters OC-3115, OC-3117, OC-3118 and OC-3133 # (no hints needed). # Olicom PCI token-ring adapters OC-3136, OC-3137, OC-3139, OC-3140, # OC-3141, OC-3540, OC-3250 # rdp: RealTek RTL 8002-based pocket ethernet adapters # pcn: Support for PCI fast ethernet adapters based on the AMD Am79c97x # chipsets, including the PCnet/FAST, PCnet/FAST+, PCnet/PRO and # PCnet/Home. These were previously handled by the lnc driver (and # still will be if you leave this driver out of the kernel). # rl: Support for PCI fast ethernet adapters based on the RealTek 8129/8139 # chipset. Note that the RealTek driver defaults to using programmed # I/O to do register accesses because memory mapped mode seems to cause # severe lockups on SMP hardware. This driver also supports the # Accton EN1207D `Cheetah' adapter, which uses a chip called # the MPX 5030/5038, which is either a RealTek in disguise or a # RealTek workalike. Note that the D-Link DFE-530TX+ uses the RealTek # chipset and is supported by this driver, not the 'vr' driver. # sf: Support for Adaptec Duralink PCI fast ethernet adapters based on the # Adaptec AIC-6915 "starfire" controller. # This includes dual and quad port cards, as well as one 100baseFX card. # Most of these are 64-bit PCI devices, except for one single port # card which is 32-bit. # sis: Support for NICs based on the Silicon Integrated Systems SiS 900, # SiS 7016 and NS DP83815 PCI fast ethernet controller chips. # sk: Support for the SysKonnect SK-984x series PCI gigabit ethernet NICs. # This includes the SK-9841 and SK-9842 single port cards (single mode # and multimode fiber) and the SK-9843 and SK-9844 dual port cards # (also single mode and multimode). # The driver will autodetect the number of ports on the card and # attach each one as a separate network interface. # sn: Support for ISA and PC Card Ethernet devices using the # SMC91C90/92/94/95 chips. # sr: RISCom/N2 hdlc sync 1/2 port V.35/X.21 serial driver (requires sppp) # ste: Sundance Technologies ST201 PCI fast ethernet controller, includes # the D-Link DFE-550TX. # ti: Support for PCI gigabit ethernet NICs based on the Alteon Networks # Tigon 1 and Tigon 2 chipsets. This includes the Alteon AceNIC, the # 3Com 3c985, the Netgear GA620 and various others. Note that you will # probably want to bump up NMBCLUSTERS a lot to use this driver. # tl: Support for the Texas Instruments TNETE100 series 'ThunderLAN' # cards and integrated ethernet controllers. This includes several # Compaq Netelligent 10/100 cards and the built-in ethernet controllers # in several Compaq Prosignia, Proliant and Deskpro systems. It also # supports several Olicom 10Mbps and 10/100 boards. # tx: SMC 9432 TX, BTX and FTX cards. (SMC EtherPower II serie) # vr: Support for various fast ethernet adapters based on the VIA # Technologies VT3043 `Rhine I' and VT86C100A `Rhine II' chips, # including the D-Link DFE530TX (see 'rl' for DFE530TX+), the Hawking # Technologies PN102TX, and the AOpen/Acer ALN-320. # vx: 3Com 3C590 and 3C595 # wb: Support for fast ethernet adapters based on the Winbond W89C840F chip. # Note: this is not the same as the Winbond W89C940F, which is a # NE2000 clone. # wl: Lucent Wavelan (ISA card only). # wi: Lucent WaveLAN/IEEE 802.11 PCMCIA adapters. Note: this supports both # the PCMCIA and ISA cards: the ISA card is really a PCMCIA to ISA # bridge with a PCMCIA adapter plugged into it. # wx: Intel Gigabit Ethernet PCI card (`Wiseman') # xe: Xircom/Intel EtherExpress Pro100/16 PC Card ethernet controller, # Accton Fast EtherCard-16, Compaq Netelligent 10/100 PC Card, # Toshiba 10/100 Ethernet PC Card, Xircom 16-bit Ethernet + Modem 56 # xl: Support for the 3Com 3c900, 3c905, 3c905B and 3c905C (Fast) # Etherlink XL cards and integrated controllers. This includes the # integrated 3c905B-TX chips in certain Dell Optiplex and Dell # Precision desktop machines and the integrated 3c905-TX chips # in Dell Latitude laptop docking stations. # Also supported: 3Com 3c980(C)-TX, 3Com 3cSOHO100-TX, 3Com 3c450-TX # Order for ISA/EISA devices is important here device ar 1 hint.ar.0.at="isa" hint.ar.0.port="0x300" hint.ar.0.irq="10" hint.ar.0.maddr="0xd0000" device cs hint.cs.0.at="isa" hint.cs.0.port="0x300" device cx 1 hint.cx.0.at="isa" hint.cx.0.port="0x240" hint.cx.0.irq="15" hint.cx.0.drq="7" device ed hint.ed.0.at="isa" hint.ed.0.port="0x280" hint.ed.0.irq="5" hint.ed.0.maddr="0xd8000" device el 1 hint.el.0.at="isa" hint.el.0.port="0x300" hint.el.0.irq="9" device ep device ex device fe 1 options FE_8BIT_SUPPORT # LAC-98 support hint.fe.0.at="isa" hint.fe.0.port="0x300" device fea device ie 2 hint.ie.0.at="isa" hint.ie.0.port="0x300" hint.ie.0.irq="5" hint.ie.0.maddr="0xd0000" hint.ie.1.at="isa" hint.ie.1.port="0x360" hint.ie.1.irq="7" hint.ie.1.maddr="0xd0000" device le 1 hint.le.0.at="isa" hint.le.0.port="0x300" hint.le.0.irq="5" hint.le.0.maddr="0xd0000" device lnc 1 hint.lnc.0.at="isa" hint.lnc.0.port="0x280" hint.lnc.0.irq="10" hint.lnc.0.drq="0" device rdp 1 hint.rdp.0.at="isa" hint.rdp.0.port="0x378" hint.rdp.0.irq="7" hint.rdp.0.flags="2" device sr 1 hint.sr.0.at="isa" hint.sr.0.port="0x300" hint.sr.0.irq="5" hint.sr.0.maddr="0xd0000" device sn hint.sn.0.at="isa" hint.sn.0.port="0x300" hint.sn.0.irq="10" device an device awi device cnw device wi options WLCACHE # enables the signal-strength cache options WLDEBUG # enables verbose debugging output device wl 1 hint.wl.0.at="isa" hint.wl.0.port="0x300" device xe device oltr options OLTR_NO_BULLSEYE_MAC options OLTR_NO_HAWKEYE_MAC options OLTR_NO_TMS_MAC hint.oltr.0.at="isa" # PCI Ethernet NICs that use the common MII bus controller code. device dc # DEC/Intel 21143 and various workalikes device fxp # Intel EtherExpress PRO/100B (82557, 82558) hint.fxp.0.prefer_iomap="0" device rl # RealTek 8129/8139 device pcn # AMD Am79C79x PCI 10/100 NICs device sf # Adaptec AIC-6915 (``Starfire'') device sis # Silicon Integrated Systems SiS 900/SiS 7016 device ste # Sundance ST201 (D-Link DFE-550TX) device tl # Texas Instruments ThunderLAN device tx # SMC EtherPower II (83c170 ``EPIC'') device vr # VIA Rhine, Rhine II device wb # Winbond W89C840F device xl # 3Com 3c90x (``Boomerang'', ``Cyclone'') # PCI Ethernet NICs. device de # DEC/Intel DC21x4x (``Tulip'') device vx # 3Com 3c590, 3c595 (``Vortex'') # PCI Gigabit & FDDI NICs. device lge device nge device sk device ti device wx device fpa 1 # # ATM related options (Cranor version) # (note: this driver cannot be used with the HARP ATM stack) # # The `en' device provides support for Efficient Networks (ENI) # ENI-155 PCI midway cards, and the Adaptec 155Mbps PCI ATM cards (ANA-59x0). # # atm device provides generic atm functions and is required for # atm devices. # NATM enables the netnatm protocol family that can be used to # bypass TCP/IP. # # the current driver supports only PVC operations (no atm-arp, no multicast). # for more details, please read the original documents at # http://www.ccrc.wustl.edu/pub/chuck/tech/bsdatm/bsdatm.html # device atm device en options NATM #native ATM # # Audio drivers: `pcm', `sbc', `gusc', `pca' # # pcm: PCM audio through various sound cards. # # This has support for a large number of new audio cards, based on # CS423x, OPTi931, Yamaha OPL-SAx, and also for SB16, GusPnP. # For more information about this driver and supported cards, # see the pcm.4 man page. # # The flags of the device tells the device a bit more info about the # device that normally is obtained through the PnP interface. # bit 2..0 secondary DMA channel; # bit 4 set if the board uses two dma channels; # bit 15..8 board type, overrides autodetection; leave it # zero if don't know what to put in (and you don't, # since this is unsupported at the moment...). # # This driver will use the new PnP code if it's available. # # pca: PCM audio through your PC speaker # # Supported cards include: # Creative SoundBlaster ISA PnP/non-PnP # Supports ESS and Avance ISA chips as well. # Gravis UltraSound ISA PnP/non-PnP # Crystal Semiconductor CS461x/428x PCI # Neomagic 256AV (ac97) # Most of the more common ISA/PnP sb/mss/ess compatable cards. device pcm # For non-pnp sound cards with no bridge drivers only: hint.pcm.0.at="isa" hint.pcm.0.irq="10" hint.pcm.0.drq="1" hint.pcm.0.flags="0x0" # For PnP/PCI sound cards, no hints are required. # # midi: MIDI interfaces and synthesizers # device midi # For non-pnp sound cards with no bridge drivers: hint.midi.0.at="isa" hint.midi.0.irq="5" hint.midi.0.flags="0x0" # For serial ports (this example configures port 2): # TODO: implement generic tty-midi interface so that we can use # other uarts. hint.midi.0.at="isa" hint.midi.0.port="0x2F8" hint.midi.0.irq="3" # # seq: MIDI sequencer # device seq # The bridge drivers for sound cards. These can be separately configured # for providing services to the likes of new-midi. # When used with 'device pcm' they also provide pcm sound services. # # sbc: Creative SoundBlaster ISA PnP/non-PnP # Supports ESS and Avance ISA chips as well. # gusc: Gravis UltraSound ISA PnP/non-PnP # csa: Crystal Semiconductor CS461x/428x PCI # For non-PnP cards: device sbc hint.sbc.0.at="isa" hint.sbc.0.port="0x220" hint.sbc.0.irq="5" hint.sbc.0.drq="1" hint.sbc.0.flags="0x15" device gusc hint.gusc.0.at="isa" hint.gusc.0.port="0x220" hint.gusc.0.irq="5" hint.gusc.0.drq="1" hint.gusc.0.flags="0x13" device pca hint.pca.0.at="isa" hint.pca.0.port="0x040" # # Miscellaneous hardware: # # mcd: Mitsumi CD-ROM using proprietary (non-ATAPI) interface # scd: Sony CD-ROM using proprietary (non-ATAPI) interface # matcd: Matsushita/Panasonic CD-ROM using proprietary (non-ATAPI) interface # wt: Wangtek and Archive QIC-02/QIC-36 tape drives # ctx: Cortex-I frame grabber # apm: Laptop Advanced Power Management (experimental) # pmtimer: Timer device driver for power management events (APM or ACPI) # spigot: The Creative Labs Video Spigot video-acquisition board # meteor: Matrox Meteor video capture board # bktr: Brooktree bt848/848a/849a/878/879 video capture and TV Tuner board # cy: Cyclades serial driver # dgb: Digiboard PC/Xi and PC/Xe series driver (ALPHA QUALITY!) # dgm: Digiboard PC/Xem driver (obsolete) # digi: Digiboard driver # gp: National Instruments AT-GPIB and AT-GPIB/TNT board, PCMCIA-GPIB # asc: GI1904-based hand scanners, e.g. the Trust Amiscan Grey # gsc: Genius GS-4500 hand scanner. # joy: joystick (including IO DATA PCJOY PC Card joystick) # The LOUTB option specifies a slower outb() for debugging purposes. # rc: RISCom/8 multiport card # rp: Comtrol Rocketport(ISA) - single card # tw: TW-523 power line interface for use with X-10 home control products # si: Specialix SI/XIO 4-32 port terminal multiplexor # spic: Sony Programmable I/O controller (VAIO notebooks) # stl: Stallion EasyIO and EasyConnection 8/32 (cd1400 based) # stli: Stallion EasyConnection 8/64, ONboard, Brumby (intelligent) # Notes on APM # The flags takes the following meaning for apm0: # 0x0020 Statclock is broken. # If apm is omitted, some systems require sysctl -w kern.timecounter.method=1 # for correct timekeeping. # Notes on the spigot: # The video spigot is at 0xad6. This port address can not be changed. # The irq values may only be 10, 11, or 15 # I/O memory is an 8kb region. Possible values are: # 0a0000, 0a2000, ..., 0fffff, f00000, f02000, ..., ffffff # The start address must be on an even boundary. # Add the following option if you want to allow non-root users to be able # to access the spigot. This option is not secure because it allows users # direct access to the I/O page. # options SPIGOT_UNSECURE # Notes on the Comtrol Rocketport driver: # # The exact values used for rp0 depend on how many boards you have # in the system. The manufacturer's sample configs are listed as: # # device rp # core driver support # # Comtrol Rocketport ISA single card # hints.rp.0.at="isa" # hints.rp.0.port="0x280" # # If instead you have two ISA cards, one installed at 0x100 and the # second installed at 0x180, then you should add the following to # your kernel probe hints: # hints.rp.0.at="isa" # hints.rp.0.port="0x100" # hints.rp.1.at="isa" # hints.rp.1.port="0x180" # # For 4 ISA cards, it might be something like this: # hints.rp.0.at="isa" # hints.rp.0.port="0x180" # hints.rp.1.at="isa" # hints.rp.1.port="0x100" # hints.rp.2.at="isa" # hints.rp.2.port="0x340" # hints.rp.3.at="isa" # hints.rp.3.port="0x240" # # And for PCI cards, you need no hints. # Notes on the Digiboard driver: # # The following flag values have special meanings: # 0x01 - alternate layout of pins (dgb & dgm) # 0x02 - use the windowed PC/Xe in 64K mode (dgb only) # Notes on the Specialix SI/XIO driver: # The host card is memory, not IO mapped. # The Rev 1 host cards use a 64K chunk, on a 32K boundary. # The Rev 2 host cards use a 32K chunk, on a 32K boundary. # The cards can use an IRQ of 11, 12 or 15. # Notes on the Sony Programmable I/O controller # This is a temporary driver that should someday be replaced by something # that hooks into the ACPI layer. The device is hooked to the PIIX4's # General Device 10 decoder, which means you have to fiddle with PCI # registers to map it in, even though it is otherwise treated here as # an ISA device. At the moment, the driver polls, although the device # is capable of generating interrupts. It largely undocumented. # The port location in the hint is where you WANT the device to be # mapped. 0x10a0 seems to be traditional. At the moment the jogdial # is the only thing truly supported, but aparently a fair percentage # of the Vaio extra features are controlled by this device. # Notes on the Stallion stl and stli drivers: # See src/i386/isa/README.stl for complete instructions. # This is version 0.0.5alpha, unsupported by Stallion. # The stl driver has a secondary IO port hard coded at 0x280. You need # to change src/i386/isa/stallion.c if you reconfigure this on the boards. # The "flags" and "msize" settings on the stli driver depend on the board: # EasyConnection 8/64 ISA: flags 23 msize 0x1000 # EasyConnection 8/64 EISA: flags 24 msize 0x10000 # EasyConnection 8/64 MCA: flags 25 msize 0x1000 # ONboard ISA: flags 4 msize 0x10000 # ONboard EISA: flags 7 msize 0x10000 # ONboard MCA: flags 3 msize 0x10000 # Brumby: flags 2 msize 0x4000 # Stallion: flags 1 msize 0x10000 device mcd 1 hint.mcd.0.at="isa" hint.mcd.0.port="0x300" hint.mcd.0.irq="10" # for the Sony CDU31/33A CDROM device scd 1 hint.scd.0.at="isa" hint.scd.0.port="0x230" # for the SoundBlaster 16 multicd - up to 4 devices device matcd 1 hint.matcd.0.at="isa" hint.matcd.0.port="0x230" device wt 1 hint.wt.0.at="isa" hint.wt.0.port="0x300" hint.wt.0.irq="5" hint.wt.0.drq="1" device ctx 1 hint.ctx.0.at="isa" hint.ctx.0.port="0x230" hint.ctx.0.maddr="0xd0000" device spigot 1 hint.spigot.0.at="isa" hint.spigot.0.port="0xad6" hint.spigot.0.irq="15" hint.spigot.0.maddr="0xee000" device apm hint.apm.0.flags="0x20" device pmtimer # Adjust system timer at wakeup time hint.pmtimer.0.at="isa" device gp hint.gp.0.at="isa" hint.gp.0.port="0x2c0" device gsc 1 hint.gsc.0.at="isa" hint.gsc.0.port="0x270" hint.gsc.0.drq="3" device joy # PnP aware, hints for nonpnp only hint.joy.0.at="isa" hint.joy.0.port="0x201" device cy 1 options CY_PCI_FASTINTR # Use with cy_pci unless irq is shared hint.cy.0.at="isa" hint.cy.0.irq="10" hint.cy.0.maddr="0xd4000" hint.cy.0.msize="0x2000" device dgb 1 options NDGBPORTS=16 # Defaults to 16*NDGB hint.dgb.0.at="isa" hint.dgb.0.port="0x220" hint.dgb.0.maddr="0xfc000" device dgm hint.dgm.0.at="isa" hint.dgm.0.port="0x104" hint.dgm.0.maddr="0xd0000" device digi hint.dgm.0.at="isa" hint.dgm.0.port="0x104" hint.dgm.0.maddr="0xd0000" # BIOS & FEP/OS components of device digi. Normally left as modules device digi_CX device digi_CX_PCI device digi_EPCX device digi_EPCX_PCI device digi_Xe device digi_Xem device digi_Xr device rc 1 hint.rc.0.at="isa" hint.rc.0.port="0x220" hint.rc.0.irq="12" device rp hint.rp.0.at="isa" hint.rp.0.port="0x280" # the port and irq for tw0 are fictitious device tw 1 hint.tw.0.at="isa" hint.tw.0.port="0x380" hint.tw.0.irq="11" device si options SI_DEBUG hint.si.0.at="isa" hint.si.0.maddr="0xd0000" hint.si.0.irq="12" device asc 1 hint.asc.0.at="isa" hint.asc.0.port="0x3EB" hint.asc.0.drq="3" hint.asc.0.irq="10" device spic hint.spic.0.at="isa" hint.spic.0.port="0x10a0" device stl hint.stl.0.at="isa" hint.stl.0.port="0x2a0" hint.stl.0.irq="10" device stli hint.stli.0.at="isa" hint.stli.0.port="0x2a0" hint.stli.0.maddr="0xcc000" hint.stli.0.flags="23" hint.stli.0.msize="0x1000" # You are unlikely to have the hardware for loran device loran hint.loran.0.at="isa" hint.loran.0.irq="5" # HOT1 Xilinx 6200 card (http://www.vcc.com/) device xrpu # # The `meteor' device is a PCI video capture board. It can also have the # following options: # options METEOR_ALLOC_PAGES=xxx preallocate kernel pages for data entry # figure (ROWS*COLUMN*BYTES_PER_PIXEL*FRAME+PAGE_SIZE-1)/PAGE_SIZE # options METEOR_DEALLOC_PAGES remove all allocated pages on close(2) # options METEOR_DEALLOC_ABOVE=xxx remove all allocated pages above the # specified amount. If this value is below the allocated amount no action # taken # options METEOR_SYSTEM_DEFAULT={METEOR_PAL|METEOR_NTSC|METEOR_SECAM}, used # for initialization of fps routine when a signal is not present. # # The 'bktr' device is a PCI video capture device using the Brooktree # bt848/bt848a/bt849a/bt878/bt879 chipset. When used with a TV Tuner it forms a # TV card, eg Miro PC/TV, Hauppauge WinCast/TV WinTV, VideoLogic Captivator, # Intel Smart Video III, AverMedia, IMS Turbo, FlyVideo. # # options OVERRIDE_CARD=xxx # options OVERRIDE_TUNER=xxx # options OVERRIDE_MSP=1 # options OVERRIDE_DBX=1 # These options can be used to override the auto detection # The current values for xxx are found in src/sys/dev/bktr/bktr_card.h # Using sysctl(8) run-time overrides on a per-card basis can be made # # options BROOKTREE_SYSTEM_DEFAULT=BROOKTREE_PAL # or # options BROOKTREE_SYSTEM_DEFAULT=BROOKTREE_NTSC # Specifes the default video capture mode. # This is required for Dual Crystal (28&35Mhz) boards where PAL is used # to prevent hangs during initialisation. eg VideoLogic Captivator PCI. # # options BKTR_USE_PLL # PAL or SECAM users who have a 28Mhz crystal (and no 35Mhz crystal) # must enable PLL mode with this option. eg some new Bt878 cards. # # options BKTR_GPIO_ACCESS # This enable IOCTLs which give user level access to the GPIO port. # # options BKTR_NO_MSP_RESET # Prevents the MSP34xx reset. Good if you initialise the MSP in another OS first # # options BKTR_430_FX_MODE # Switch Bt878/879 cards into Intel 430FX chipset compatibility mode. # # options BKTR_SIS_VIA_MODE # Switch Bt878/879 cards into SIS/VIA chipset compatibility mode which is # needed for some old SiS and VIA chipset motherboards. # This also allows Bt878/879 chips to work on old OPTi (<1997) chipset # motherboards and motherboards with bad or incomplete PCI 2.1 support. # As a rough guess, old = before 1998 # device meteor 1 # Brooktree driver has been ported to the new I2C framework. Thus, # you'll need to have the following 3 lines in the kernel config. # device smbus # device iicbus # device iicbb # The iic and smb devices are only needed if you want to control other # I2C slaves connected to the external connector of some cards. # device bktr 1 # # PC Card/PCMCIA # (OLDCARD) # # card: pccard slots # pcic: isa/pccard bridge device pcic hint.pcic.0.at="isa" hint.pcic.1.at="isa" device card # # PC Card/PCMCIA and Cardbus # (NEWCARD) # # Note that NEWCARD and OLDCARD are incompatible. Do not use both at the same # time. # # pccbb: isa/pccard and pci/cardbus bridge # pccard: pccard slots # cardbus: cardbus slots #device pccbb #device pccard #device cardbus # You may need to reset all pccards after resuming options PCIC_RESUME_RESET # reset after resume # # Laptop/Notebook options: # # See also: # apm under `Miscellaneous hardware' # above. # For older notebooks that signal a powerfail condition (external # power supply dropped, or battery state low) by issuing an NMI: options POWERFAIL_NMI # make it beep instead of panicing # # SMB bus # # System Management Bus support is provided by the 'smbus' device. # Access to the SMBus device is via the 'smb' device (/dev/smb*), # which is a child of the 'smbus' device. # # Supported devices: # smb standard io through /dev/smb* # # Supported SMB interfaces: # iicsmb I2C to SMB bridge with any iicbus interface # bktr brooktree848 I2C hardware interface # intpm Intel PIIX4 Power Management Unit # alpm Acer Aladdin-IV/V/Pro2 Power Management Unit # ichsmb Intel ICH SMBus controller chips (82801AA, 82801AB, 82801BA) # device smbus # Bus support, required for smb below. device intpm device alpm device ichsmb device smb # # I2C Bus # # Philips i2c bus support is provided by the `iicbus' device. # # Supported devices: # ic i2c network interface # iic i2c standard io # iicsmb i2c to smb bridge. Allow i2c i/o with smb commands. # # Supported interfaces: # pcf Philips PCF8584 ISA-bus controller # bktr brooktree848 I2C software interface # # Other: # iicbb generic I2C bit-banging code (needed by lpbb, bktr) # device iicbus # Bus support, required for ic/iic/iicsmb below. device iicbb device ic device iic device iicsmb # smb over i2c bridge device pcf hint.pcf.0.at="isa" hint.pcf.0.port="0x320" hint.pcf.0.irq="5" #--------------------------------------------------------------------------- # ISDN4BSD # # See /usr/share/examples/isdn/ROADMAP for an introduction to isdn4bsd. # # i4b passive ISDN cards support contains the following hardware drivers: # # isic - Siemens/Infineon ISDN ISAC/HSCX/IPAC chipset driver # iwic - Winbond W6692 PCI bus ISDN S/T interface controller # ifpi - AVM Fritz!Card PCI driver # ihfc - Cologne Chip HFC ISA/ISA-PnP chipset driver # ifpnp - AVM Fritz!Card PnP driver # itjc - Siemens ISAC / TJNet Tiger300/320 chipset # # i4b active ISDN cards support contains the following hardware drivers: # # iavc - AVM B1 PCI, AVM B1 ISA, AVM T1 # # Note that the ``options'' (if given) and ``device'' lines must BOTH # be uncommented to enable support for a given card ! # # In addition to a hardware driver (and probably an option) the mandatory # ISDN protocol stack devices and the mandatory support device must be # enabled as well as one or more devices from the optional devices section. # #--------------------------------------------------------------------------- # isic driver (Siemens/Infineon chipsets) # device isic # # ISA bus non-PnP Cards: # ---------------------- # # Teles S0/8 or Niccy 1008 options TEL_S0_8 hint.isic.0.at="isa" hint.isic.0.maddr="0xd0000" hint.isic.0.irq="5" hint.isic.0.flags="1" # # Teles S0/16 or Creatix ISDN-S0 or Niccy 1016 options TEL_S0_16 hint.isic.0.at="isa" hint.isic.0.port="0xd80" hint.isic.0.maddr="0xd0000" hint.isic.0.irq="5" hint.isic.0.flags="2" # # Teles S0/16.3 options TEL_S0_16_3 hint.isic.0.at="isa" hint.isic.0.port="0xd80" hint.isic.0.irq="5" hint.isic.0.flags="3" # # AVM A1 or AVM Fritz!Card options AVM_A1 hint.isic.0.at="isa" hint.isic.0.port="0x340" hint.isic.0.irq="5" hint.isic.0.flags="4" # # USRobotics Sportster ISDN TA intern options USR_STI hint.isic.0.at="isa" hint.isic.0.port="0x268" hint.isic.0.irq="5" hint.isic.0.flags="7" # # ITK ix1 Micro ( < V.3, non-PnP version ) options ITKIX1 hint.isic.0.at="isa" hint.isic.0.port="0x398" hint.isic.0.irq="10" hint.isic.0.flags="18" # # ELSA PCC-16 options ELSA_PCC16 hint.isic.0.at="isa" hint.isic.0.port="0x360" hint.isic.0.irq="10" hint.isic.0.flags="20" # # ISA bus PnP Cards: # ------------------ # # Teles S0/16.3 PnP options TEL_S0_16_3_P # # Creatix ISDN-S0 P&P options CRTX_S0_P # # Dr. Neuhaus Niccy Go@ options DRN_NGO # # Sedlbauer Win Speed options SEDLBAUER # # Dynalink IS64PH options DYNALINK # # ELSA QuickStep 1000pro ISA options ELSA_QS1ISA # # Siemens I-Surf 2.0 options SIEMENS_ISURF2 # # Asuscom ISDNlink 128K ISA options ASUSCOM_IPAC # # Eicon Diehl DIVA 2.0 and 2.02 options EICON_DIVA # # PCI bus Cards: # -------------- # # ELSA MicroLink ISDN/PCI (same as ELSA QuickStep 1000pro PCI) options ELSA_QS1PCI # # #--------------------------------------------------------------------------- # ifpnp driver for AVM Fritz!Card PnP # # AVM Fritz!Card PnP device ifpnp # #--------------------------------------------------------------------------- # ihfc driver for Cologne Chip ISA chipsets (experimental!) # # Teles 16.3c ISA PnP # AcerISDN P10 ISA PnP # TELEINT ISDN SPEED No.1 device ihfc # #--------------------------------------------------------------------------- # ifpi driver for AVM Fritz!Card PCI # # AVM Fritz!Card PCI device ifpi # #--------------------------------------------------------------------------- # iwic driver for Winbond W6692 chipset # # ASUSCOM P-IN100-ST-D (and other Winbond W6692 based cards) device iwic # #--------------------------------------------------------------------------- # itjc driver for Simens ISAC / TJNet Tiger300/320 chipset # # Traverse Technologies NETjet-S # Teles PCI-TJ device itjc # #--------------------------------------------------------------------------- # iavc driver (AVM active cards, needs i4bcapi driver!) # device iavc # # AVM B1 ISA bus (PnP mode not supported!) # ---------------------------------------- hint.iavc.0.at="isa" hint.iavc.0.port="0x150" hint.iavc.0.irq="5" # #--------------------------------------------------------------------------- # ISDN Protocol Stack - mandatory for all hardware drivers # # Q.921 / layer 2 - i4b passive cards D channel handling device "i4bq921" # # Q.931 / layer 3 - i4b passive cards D channel handling device "i4bq931" # # layer 4 - i4b common passive and active card handling device "i4b" # #--------------------------------------------------------------------------- # ISDN devices - mandatory for all hardware drivers # # userland driver to do ISDN tracing (for passive cards only) device "i4btrc" 4 # # userland driver to control the whole thing device "i4bctl" # #--------------------------------------------------------------------------- # ISDN devices - optional # # userland driver for access to raw B channel device "i4brbch" 4 # # userland driver for telephony device "i4btel" 2 # # network driver for IP over raw HDLC ISDN device "i4bipr" 4 # enable VJ header compression detection for ipr i/f options IPR_VJ # enable logging of the first n IP packets to isdnd (n=32 here) options IPR_LOG=32 # # network driver for sync PPP over ISDN; requires an equivalent # number of sppp device to be configured device "i4bisppp" 4 # # B-channel interface to the netgraph subsystem device "i4bing" 2 # # CAPI driver needed for active ISDN cards (see iavc driver above) device "i4bcapi" # #--------------------------------------------------------------------------- # Parallel-Port Bus # # Parallel port bus support is provided by the `ppbus' device. # Multiple devices may be attached to the parallel port, devices # are automatically probed and attached when found. # # Supported devices: # vpo Iomega Zip Drive # Requires SCSI disk support ('scbus' and 'da'), best # performance is achieved with ports in EPP 1.9 mode. # lpt Parallel Printer # plip Parallel network interface # ppi General-purpose I/O ("Geek Port") + IEEE1284 I/O # pps Pulse per second Timing Interface # lpbb Philips official parallel port I2C bit-banging interface # # Supported interfaces: # ppc ISA-bus parallel port interfaces. # options PPC_PROBE_CHIPSET # Enable chipset specific detection # (see flags in ppc(4)) options DEBUG_1284 # IEEE1284 signaling protocol debug options PERIPH_1284 # Makes your computer act as a IEEE1284 # compliant peripheral options DONTPROBE_1284 # Avoid boot detection of PnP parallel devices options VP0_DEBUG # ZIP/ZIP+ debug options LPT_DEBUG # Printer driver debug options PPC_DEBUG # Parallel chipset level debug options PLIP_DEBUG # Parallel network IP interface debug options PCFCLOCK_VERBOSE # Verbose pcfclock driver options PCFCLOCK_MAX_RETRIES=5 # Maximum read tries (default 10) device ppc hint.ppc.0.at="isa" hint.ppc.0.irq="7" device ppbus device vpo device lpt device plip device ppi device pps device lpbb device pcfclock # Kernel BOOTP support options BOOTP # Use BOOTP to obtain IP address/hostname options BOOTP_NFSROOT # NFS mount root filesystem using BOOTP info options BOOTP_NFSV3 # Use NFS v3 to NFS mount root options BOOTP_COMPAT # Workaround for broken bootp daemons. options BOOTP_WIRED_TO=fxp0 # Use interface fxp0 for BOOTP # # Add tie-ins for a hardware watchdog. This only enable the hooks; # the user must still supply the actual driver. # options HW_WDOG # # Set the number of PV entries per process. Increasing this can # stop panics related to heavy use of shared memory. However, that can # (combined with large amounts of physical memory) cause panics at # boot time due the kernel running out of VM space. # # If you're tweaking this, you might also want to increase the sysctls # "vm.v_free_min", "vm.v_free_reserved", and "vm.v_free_target". # # The value below is the one more than the default. # options PMAP_SHPGPERPROC=201 # # Disable swapping. This option removes all code which actually performs # swapping, so it's not possible to turn it back on at run-time. # # This is sometimes usable for systems which don't have any swap space # (see also sysctls "vm.defer_swapspace_pageouts" and # "vm.disable_swapspace_pageouts") # #options NO_SWAPPING # Set the number of sf_bufs to allocate. sf_bufs are virtual buffers # for sendfile(2) that are used to map file VM pages, and normally # default to a quantity that is roughly 16*MAXUSERS+512. You would # typically want about 4 of these for each simultaneous file send. # options NSFBUFS=1024 # # Enable extra debugging code for locks. This stores the filename and # line of whatever acquired the lock in the lock itself, and change a # number of function calls to pass around the relevant data. This is # not at all useful unless you are debugging lock code. Also note # that it is likely to break e.g. fstat(1) unless you recompile your # userland with -DDEBUG_LOCKS as well. # options DEBUG_LOCKS ##################################################################### # ABI Emulation # Enable iBCS2 runtime support for SCO and ISC binaries options IBCS2 # Emulate spx device for client side of SVR3 local X interface options SPX_HACK # Enable Linux ABI emulation options COMPAT_LINUX # Enable the linux-like proc filesystem support (requires COMPAT_LINUX # and PSEUDOFS) options LINPROCFS # Linux debugging options DEBUG_LINUX # # SysVR4 ABI emulation # # The svr4 ABI emulator can be statically compiled into the kernel or loaded as # a KLD module. # The STREAMS network emulation code can also be compiled statically or as a # module. If loaded as a module, it must be loaded before the svr4 module # (the /usr/sbin/svr4 script does this for you). If compiling statically, # the `streams' device must be configured into any kernel which also # specifies COMPAT_SVR4. It is possible to have a statically-configured # STREAMS device and a dynamically loadable svr4 emulator; the /usr/sbin/svr4 # script understands that it doesn't need to load the `streams' module under # those circumstances. # Caveat: At this time, `options KTRACE' is required for the svr4 emulator # (whether static or dynamic). # options COMPAT_SVR4 # build emulator statically options DEBUG_SVR4 # enable verbose debugging device streams # STREAMS network driver (required for svr4). ##################################################################### # USB support # UHCI controller device uhci # OHCI controller device ohci # General USB code (mandatory for USB) device usb # # USB Double Bulk Pipe devices device udbp # Generic USB device driver device ugen # Human Interface Device (anything with buttons and dials) device uhid # USB keyboard device ukbd # USB printer device ulpt # USB Iomega Zip 100 Drive device umass # USB modem support device umodem # USB mouse device ums # Diamond Rio 500 Mp3 player device urio # USB scanners device uscanner # # ADMtek USB ethernet. Supports the LinkSys USB100TX, # the Billionton USB100, the Melco LU-ATX, the D-Link DSB-650TX # and the SMC 2202USB. Also works with the ADMtek AN986 Pegasus # eval board. device aue # # CATC USB-EL1201A USB ethernet. Supports the CATC Netmate # and Netmate II, and the Belkin F5U111. device cue # # Kawasaki LSI ethernet. Supports the LinkSys USB10T, # Entrega USB-NET-E45, Peracom Ethernet Adapter, the # 3Com 3c19250, the ADS Technologies USB-10BT, the ATen UC10T, # the Netgear EA101, the D-Link DSB-650, the SMC 2102USB # and 2104USB, and the Corega USB-T. device kue # debugging options for the USB subsystem # options UHCI_DEBUG options OHCI_DEBUG options USB_DEBUG options UGEN_DEBUG options UHID_DEBUG options UHUB_DEBUG options UKBD_DEBUG options ULPT_DEBUG options UMASS_DEBUG options UMS_DEBUG options URIO_DEBUG # options for ukbd: options UKBD_DFLT_KEYMAP # specify the built-in keymap makeoptions UKBD_DFLT_KEYMAP=it.iso # # Embedded system options: # # An embedded system might want to run something other than init. options INIT_PATH="/sbin/init:/stand/sysinstall" # Debug options options BUS_DEBUG # enable newbus debugging options DEBUG_VFS_LOCKS # enable vfs lock debugging options NPX_DEBUG # enable npx debugging (FPU/math emu) ##################################################################### # SYSV IPC KERNEL PARAMETERS # # Maximum number of entries in a semaphore map. options SEMMAP=31 # Maximum number of System V semaphores that can be used on the system at # one time. options SEMMNI=11 # Total number of semaphores system wide options SEMMNS=61 # Total number of undo structures in system options SEMMNU=31 # Maximum number of System V semaphores that can be used by a single process # at one time. options SEMMSL=61 # Maximum number of operations that can be outstanding on a single System V # semaphore at one time. options SEMOPM=101 # Maximum number of undo operations that can be outstanding on a single # System V semaphore at one time. options SEMUME=11 # Maximum number of shared memory pages system wide. options SHMALL=1025 # Maximum size, in bytes, of a single System V shared memory region. options SHMMAX="(SHMMAXPGS*PAGE_SIZE+1)" options SHMMAXPGS=1025 # Minimum size, in bytes, of a single System V shared memory region. options SHMMIN=2 # Maximum number of shared memory regions that can be used on the system # at one time. options SHMMNI=33 # Maximum number of System V shared memory regions that can be attached to # a single process at one time. options SHMSEG=9 # Set the amount of time (in seconds) the system will wait before # rebooting automatically when a kernel panic occurs. If set to (-1), # the system will wait indefinitely until a key is pressed on the # console. options PANIC_REBOOT_WAIT_TIME=16 ##################################################################### # More undocumented options for linting. # Note that documenting these are not considered an affront. options CAM_DEBUG_DELAY # VFS cluster debugging. options CLUSTERDEBUG # Eliminate unneeded cache flush instruction(s). options CPU_UPGRADE_HW_CACHE options DEBUG # PECOFF module (Win32 Execution Format) options PECOFF_SUPPORT options PECOFF_DEBUG # Disable the 4 MByte PSE CPU feature. #options DISABLE_PSE options ENABLE_ALART options I4B_SMP_WORKAROUND options I586_PMC_GUPROF=0x70000 options KBDIO_DEBUG=2 options KBD_MAXRETRY=4 options KBD_MAXWAIT=6 options KBD_RESETDELAY=201 # Enable the PF_KEY Key Management API. options KEY # Kernel filelock debugging. options LOCKF_DEBUG # System V compatible message queues # Please note that the values provided here are used to test kernel # building. The defaults in the sources provide almost the same numbers. # MSGSSZ must be a power of 2 between 8 and 1024. options MSGMNB=2049 # Max number of chars in queue options MSGMNI=41 # Max number of message queue identifiers options MSGSEG=2049 # Max number of message segments options MSGSSZ=16 # Size of a message segment options MSGTQL=41 # Max number of messages in system options NBUF=512 # Number of buffer headers options NMBCLUSTERS=1024 # Number of mbuf clusters options PSM_DEBUG=1 options SCSI_NCR_DEBUG options SCSI_NCR_MAX_SYNC=10000 options SCSI_NCR_MAX_WIDE=1 options SCSI_NCR_MYADDR=7 options SC_DEBUG_LEVEL=5 # Syscons debug level options SC_RENDER_DEBUG # syscons rendering debugging options SHOW_BUSYBUFS # List buffers that prevent root unmount options SIMPLELOCK_DEBUG options SLIP_IFF_OPTS options TIMER_FREQ="((14318182+6)/12)" options VFS_BIO_DEBUG # VFS buffer I/O debugging options VM_KMEM_SIZE options VM_KMEM_SIZE_MAX options VM_KMEM_SIZE_SCALE Index: head/sys/conf/files.alpha =================================================================== --- head/sys/conf/files.alpha (revision 78134) +++ head/sys/conf/files.alpha (revision 78135) @@ -1,224 +1,224 @@ # This file tells config what files go into building a kernel, # files marked standard are always included. # # $FreeBSD$ # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and # dependency lines other than the first are silently ignored. # # linux_genassym.o optional compat_linux \ dependency "$S/alpha/linux/linux_genassym.c" \ compile-with "${CC} ${CFLAGS} -c ${.IMPSRC}" \ no-obj no-implicit-rule \ clean "linux_genassym.o" # linux_assym.h optional compat_linux \ dependency "$S/kern/genassym.sh linux_genassym.o" \ compile-with "sh $S/kern/genassym.sh linux_genassym.o > ${.TARGET}" \ no-obj no-implicit-rule before-depend \ clean "linux_assym.h" # font8x16.o optional std8x16font \ compile-with "uudecode < /usr/share/syscons/fonts/${STD8X16FONT}-8x16.fnt && file2c 'unsigned char font_16[16*256] = {' '};' < ${STD8X16FONT}-8x16 > font8x16.c && ${CC} -c ${CFLAGS} font8x16.c" \ no-implicit-rule before-depend \ clean "${STD8X16FONT}-8x16 font8x16.c" # atkbdmap.h optional atkbd_dflt_keymap \ compile-with "/usr/sbin/kbdcontrol -L ${ATKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > atkbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "atkbdmap.h" # alpha/alpha/alpha-gdbstub.c optional ddb alpha/alpha/api_up1000.c optional api_up1000 alpha/alpha/atomic.s standard alpha/alpha/autoconf.c standard alpha/alpha/busdma_machdep.c standard alpha/alpha/busspace.c standard alpha/alpha/clock.c standard alpha/alpha/clock_if.m standard alpha/alpha/cpuconf.c standard alpha/alpha/db_disasm.c optional ddb alpha/alpha/db_interface.c optional ddb alpha/alpha/db_trace.c optional ddb alpha/alpha/dec_1000a.c optional dec_1000a alpha/alpha/dec_1000a.c optional dec_1000a alpha/alpha/dec_2100_a50.c optional dec_2100_a50 alpha/alpha/dec_2100_a500.c optional dec_2100_a500 alpha/alpha/dec_axppci_33.c optional dec_axppci_33 alpha/alpha/dec_eb164.c optional dec_eb164 alpha/alpha/dec_eb64plus.c optional dec_eb64plus alpha/alpha/dec_kn20aa.c optional dec_kn20aa alpha/alpha/dec_kn300.c optional dec_kn300 alpha/alpha/dec_kn8ae.c optional dec_kn8ae alpha/alpha/dec_st550.c optional dec_st550 alpha/alpha/dec_st6600.c optional dec_st6600 alpha/alpha/dec_3000_300.c optional dec_3000_300 alpha/alpha/dec_3000_500.c optional dec_3000_500 alpha/alpha/elf_machdep.c standard alpha/alpha/exception.s standard alpha/alpha/fp_emulate.c standard alpha/alpha/ieee_float.c standard alpha/alpha/in_cksum.c optional inet alpha/alpha/interrupt.c standard # locore.s needs to be handled in Makefile to put it first. Otherwise it's # now normal. # alpha/alpha/locore.s standard alpha/alpha/machdep.c standard alpha/alpha/mem.c standard alpha/alpha/mountroot.c optional slice alpha/alpha/pal.s standard alpha/alpha/perfmon.c optional perfmon profiling-routine alpha/alpha/perfmon.c optional perfmon alpha/alpha/pmap.c standard alpha/alpha/procfs_machdep.c standard alpha/alpha/mp_machdep.c optional smp alpha/alpha/prom.c standard alpha/alpha/promcons.c standard alpha/alpha/prom_disp.s standard alpha/alpha/sgmap.c standard alpha/alpha/support.s standard alpha/alpha/swtch.s standard alpha/alpha/sys_machdep.c standard alpha/alpha/trap.c standard -alpha/alpha/userconfig.c optional userconfig +#alpha/alpha/userconfig.c optional userconfig alpha/alpha/vm_machdep.c standard alpha/isa/isa.c optional isa alpha/isa/isa_dma.c optional isa alpha/isa/mcclock_isa.c optional isa alpha/linux/linux_dummy.c optional compat_linux alpha/linux/linux_locore.s optional compat_linux \ dependency "linux_assym.h" alpha/linux/linux_machdep.c optional compat_linux alpha/linux/linux_sysent.c optional compat_linux alpha/linux/linux_sysvec.c optional compat_linux alpha/mcbus/mcbus.c optional dec_kn300 alpha/mcbus/mcmem.c optional dec_kn300 alpha/mcbus/mcpcia.c optional dec_kn300 alpha/osf1/imgact_osf1.c optional compat_linux alpha/osf1/imgact_osf1.c optional compat_osf1 alpha/osf1/osf1_ioctl.c optional compat_linux alpha/osf1/osf1_ioctl.c optional compat_osf1 alpha/osf1/osf1_misc.c optional compat_linux alpha/osf1/osf1_misc.c optional compat_osf1 alpha/osf1/osf1_mount.c optional compat_linux alpha/osf1/osf1_mount.c optional compat_osf1 alpha/osf1/osf1_signal.c optional compat_linux alpha/osf1/osf1_signal.c optional compat_osf1 alpha/osf1/osf1_sysent.c optional compat_linux alpha/osf1/osf1_sysent.c optional compat_osf1 alpha/osf1/osf1_sysvec.c optional compat_linux alpha/osf1/osf1_sysvec.c optional compat_osf1 alpha/pci/alphapci_if.m optional pci alpha/pci/apecs.c optional dec_2100_a50 alpha/pci/apecs.c optional dec_eb64plus alpha/pci/apecs.c optional dec_1000a alpha/pci/apecs_pci.c optional dec_2100_a50 alpha/pci/apecs_pci.c optional dec_eb64plus alpha/pci/apecs_pci.c optional dec_1000a alpha/pci/bwx.c optional pci alpha/pci/cia.c optional dec_eb164 alpha/pci/cia.c optional dec_kn20aa alpha/pci/cia.c optional dec_st550 alpha/pci/cia.c optional dec_1000a alpha/pci/cia_pci.c optional dec_eb164 alpha/pci/cia_pci.c optional dec_kn20aa alpha/pci/cia_pci.c optional dec_st550 alpha/pci/cia_pci.c optional dec_1000a alpha/pci/irongate.c optional api_up1000 alpha/pci/irongate_pci.c optional api_up1000 alpha/pci/lca.c optional dec_axppci_33 alpha/pci/lca_pci.c optional dec_axppci_33 alpha/pci/pci_eb164_intr.s optional dec_eb164 alpha/pci/pci_eb164_intr.s optional dec_kn20aa alpha/pci/pci_eb164_intr.s optional dec_st550 alpha/pci/pci_eb64plus_intr.s optional dec_2100_a50 alpha/pci/pci_eb64plus_intr.s optional dec_eb64plus alpha/pci/pcibus.c optional pci alpha/pci/swiz.c optional pci alpha/pci/t2.c optional dec_2100_a500 alpha/pci/t2_pci.c optional dec_2100_a500 alpha/pci/tsunami.c optional dec_st6600 alpha/pci/tsunami_pci.c optional dec_st6600 alpha/tc/am7990.c optional le dec_3000_300 alpha/tc/am7990.c optional le dec_3000_500 alpha/tc/esp.c optional esp dec_3000_300 alpha/tc/esp.c optional esp dec_3000_500 alpha/tc/if_le_dec.c optional le dec_3000_300 alpha/tc/if_le_dec.c optional le dec_3000_500 alpha/tc/if_le_ioasic.c optional le dec_3000_300 alpha/tc/if_le_ioasic.c optional le dec_3000_500 alpha/tc/ioasic.c optional dec_3000_300 alpha/tc/ioasic.c optional dec_3000_500 alpha/tc/mcclock_ioasic.c optional dec_3000_300 alpha/tc/mcclock_ioasic.c optional dec_3000_500 alpha/tc/tc.c optional dec_3000_300 alpha/tc/tc.c optional dec_3000_500 alpha/tc/tcasic.c optional dec_3000_300 alpha/tc/tcasic.c optional dec_3000_500 alpha/tc/tcds.c optional esp dec_3000_300 alpha/tc/tcds.c optional esp dec_3000_500 alpha/tc/tcds_dma.c optional tcds dec_3000_300 alpha/tc/tcds_dma.c optional tcds dec_3000_500 alpha/tlsb/dwlpx.c optional dec_kn8ae alpha/tlsb/gbus.c optional dec_kn8ae alpha/tlsb/kftxx.c optional dec_kn8ae alpha/tlsb/mcclock_tlsb.c optional dec_kn8ae alpha/tlsb/tlsb.c optional dec_kn8ae alpha/tlsb/tlsbcpu.c optional dec_kn8ae alpha/tlsb/tlsbmem.c optional dec_kn8ae alpha/tlsb/zs_tlsb.c optional dec_kn8ae compat/linux/linux_file.c optional compat_linux compat/linux/linux_ioctl.c optional compat_linux compat/linux/linux_ipc.c optional compat_linux compat/linux/linux_mib.c optional compat_linux compat/linux/linux_misc.c optional compat_linux compat/linux/linux_signal.c optional compat_linux compat/linux/linux_socket.c optional compat_linux compat/linux/linux_stats.c optional compat_linux compat/linux/linux_util.c optional compat_linux compat/linprocfs/linprocfs_misc.c optional linprocfs compat/linprocfs/linprocfs_subr.c optional linprocfs compat/linprocfs/linprocfs_vfsops.c optional linprocfs compat/linprocfs/linprocfs_vnops.c optional linprocfs dev/advansys/adv_isa.c optional adv isa dev/aic/aic_isa.c optional aic isa dev/dec/mcclock.c optional mcclock dev/dec/mcclock_if.m optional mcclock dev/ed/if_ed_isa.c optional ed isa dev/fb/fb.c optional fb dev/fb/fb.c optional vga dev/fb/splash.c count splash dev/fb/vga.c optional vga dev/kbd/atkbd.c optional atkbd dev/kbd/atkbdc.c count atkbdc dev/kbd/kbd.c optional atkbd dev/kbd/kbd.c optional kbd dev/kbd/kbd.c optional sc dev/kbd/kbd.c optional ukbd dev/syscons/schistory.c count sc dev/syscons/scmouse.c optional sc dev/syscons/scterm.c optional sc dev/syscons/scterm-dumb.c optional sc dev/syscons/scterm-sc.c optional sc dev/syscons/scvgarndr.c optional sc vga dev/syscons/scvidctl.c optional sc dev/syscons/scvtb.c optional sc dev/syscons/syscons.c optional sc dev/syscons/sysmouse.c optional sc isa/atkbd_isa.c optional atkbd isa/atkbdc_isa.c optional atkbdc isa/fd.c optional fdc isa/ppc.c optional ppc isa/psm.c optional psm isa/sio.c optional sio isa/syscons_isa.c optional sc isa/vga_isa.c optional vga kern/subr_diskmbr.c standard libkern/alpha/htonl.S standard libkern/alpha/htons.S standard libkern/alpha/ntohl.S standard libkern/alpha/ntohs.S standard libkern/bcmp.c standard libkern/ffs.c standard Index: head/sys/conf/files.i386 =================================================================== --- head/sys/conf/files.i386 (revision 78134) +++ head/sys/conf/files.i386 (revision 78135) @@ -1,386 +1,386 @@ # This file tells config what files go into building a kernel, # files marked standard are always included. # # $FreeBSD$ # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and # dependency lines other than the first are silently ignored. # linux_genassym.o optional compat_linux \ dependency "$S/i386/linux/linux_genassym.c" \ compile-with "${CC} ${CFLAGS} -c ${.IMPSRC}" \ no-obj no-implicit-rule \ clean "linux_genassym.o" # linux_assym.h optional compat_linux \ dependency "$S/kern/genassym.sh linux_genassym.o" \ compile-with "sh $S/kern/genassym.sh linux_genassym.o > ${.TARGET}" \ no-obj no-implicit-rule before-depend \ clean "linux_assym.h" # svr4_genassym.o optional compat_svr4 \ dependency "$S/i386/svr4/svr4_genassym.c" \ compile-with "${CC} ${CFLAGS} -c ${.IMPSRC}" \ no-obj no-implicit-rule \ clean "svr4_genassym.o" # svr4_assym.h optional compat_svr4 \ dependency "$S/kern/genassym.sh svr4_genassym.o" \ compile-with "sh $S/kern/genassym.sh svr4_genassym.o > ${.TARGET}" \ no-obj no-implicit-rule before-depend \ clean "svr4_assym.h" # font.h optional sc_dflt_font \ compile-with "uudecode < /usr/share/syscons/fonts/${SC_DFLT_FONT}-8x16.fnt && file2c 'static u_char dflt_font_16[16*256] = {' '};' < ${SC_DFLT_FONT}-8x16 > font.h && uudecode < /usr/share/syscons/fonts/${SC_DFLT_FONT}-8x14.fnt && file2c 'static u_char dflt_font_14[14*256] = {' '};' < ${SC_DFLT_FONT}-8x14 >> font.h && uudecode < /usr/share/syscons/fonts/${SC_DFLT_FONT}-8x8.fnt && file2c 'static u_char dflt_font_8[8*256] = {' '};' < ${SC_DFLT_FONT}-8x8 >> font.h" \ no-obj no-implicit-rule before-depend \ clean "font.h ${SC_DFLT_FONT}-8x14 ${SC_DFLT_FONT}-8x16 ${SC_DFLT_FONT}-8x8" # atkbdmap.h optional atkbd_dflt_keymap \ compile-with "/usr/sbin/kbdcontrol -L ${ATKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > atkbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "atkbdmap.h" # ukbdmap.h optional ukbd_dflt_keymap \ compile-with "/usr/sbin/kbdcontrol -L ${UKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > ukbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "ukbdmap.h" # msysosak.o optional fla \ dependency "$S/contrib/dev/fla/i386/msysosak.o.uu" \ compile-with "uudecode < $S/contrib/dev/fla/i386/msysosak.o.uu" \ no-implicit-rule # trlld.o optional oltr \ dependency "$S/contrib/dev/oltr/i386${FMT}.trlld.o.uu" \ compile-with "uudecode < $S/contrib/dev/oltr/i386${FMT}.trlld.o.uu" \ no-implicit-rule # compat/linux/linux_file.c optional compat_linux compat/linux/linux_ioctl.c optional compat_linux compat/linux/linux_ipc.c optional compat_linux compat/linux/linux_mib.c optional compat_linux compat/linux/linux_misc.c optional compat_linux compat/linux/linux_signal.c optional compat_linux compat/linux/linux_socket.c optional compat_linux compat/linux/linux_stats.c optional compat_linux compat/linux/linux_util.c optional compat_linux compat/pecoff/imgact_pecoff.c optional pecoff_support compat/svr4/imgact_svr4.c optional compat_svr4 compat/svr4/svr4_fcntl.c optional compat_svr4 compat/svr4/svr4_filio.c optional compat_svr4 compat/svr4/svr4_ioctl.c optional compat_svr4 compat/svr4/svr4_ipc.c optional compat_svr4 compat/svr4/svr4_misc.c optional compat_svr4 compat/svr4/svr4_resource.c optional compat_svr4 compat/svr4/svr4_signal.c optional compat_svr4 compat/svr4/svr4_socket.c optional compat_svr4 compat/svr4/svr4_sockio.c optional compat_svr4 compat/svr4/svr4_stat.c optional compat_svr4 compat/svr4/svr4_stream.c optional compat_svr4 compat/svr4/svr4_syscallnames.c optional compat_svr4 compat/svr4/svr4_sysent.c optional compat_svr4 compat/svr4/svr4_sysvec.c optional compat_svr4 compat/svr4/svr4_termios.c optional compat_svr4 compat/svr4/svr4_ttold.c optional compat_svr4 contrib/dev/fla/fla.c optional fla contrib/dev/oltr/if_oltr.c optional oltr contrib/dev/oltr/trlldbm.c optional oltr contrib/dev/oltr/trlldhm.c optional oltr contrib/dev/oltr/trlldmac.c optional oltr crypto/des/des_ecb.c optional netsmbcrypto crypto/des/des_setkey.c optional netsmbcrypto dev/advansys/adv_isa.c optional adv isa dev/aic/aic_isa.c optional aic isa dev/ar/if_ar_isa.c optional ar isa dev/ed/if_ed_isa.c optional ed isa dev/eisa/eisaconf.c optional eisa dev/fb/fb.c optional fb dev/fb/fb.c optional vga dev/fb/splash.c count splash dev/fb/vga.c optional vga dev/fe/if_fe_isa.c optional fe isa dev/kbd/atkbd.c optional atkbd dev/kbd/atkbdc.c count atkbdc dev/kbd/kbd.c optional atkbd dev/kbd/kbd.c optional kbd dev/kbd/kbd.c optional sc dev/kbd/kbd.c optional ukbd dev/kbd/kbd.c optional vt dev/sr/if_sr_isa.c optional sr isa dev/syscons/schistory.c count sc dev/syscons/scmouse.c optional sc dev/syscons/scterm.c optional sc dev/syscons/scterm-dumb.c optional sc dev/syscons/scterm-sc.c optional sc dev/syscons/scvesactl.c optional sc vga vesa dev/syscons/scvgarndr.c optional sc vga dev/syscons/scvidctl.c optional sc dev/syscons/scvtb.c optional sc dev/syscons/syscons.c optional sc dev/syscons/sysmouse.c optional sc fs/smbfs/smbfs_io.c optional smbfs fs/smbfs/smbfs_node.c optional smbfs fs/smbfs/smbfs_smb.c optional smbfs fs/smbfs/smbfs_subr.c optional smbfs fs/smbfs/smbfs_vfsops.c optional smbfs fs/smbfs/smbfs_vnops.c optional smbfs gnu/i386/fpemul/div_small.s optional gpl_math_emulate \ warning "kernel contains GPL contaminated math emulator" gnu/i386/fpemul/errors.c optional gpl_math_emulate gnu/i386/fpemul/fpu_arith.c optional gpl_math_emulate gnu/i386/fpemul/fpu_aux.c optional gpl_math_emulate gnu/i386/fpemul/fpu_entry.c optional gpl_math_emulate gnu/i386/fpemul/fpu_etc.c optional gpl_math_emulate gnu/i386/fpemul/fpu_trig.c optional gpl_math_emulate gnu/i386/fpemul/get_address.c optional gpl_math_emulate gnu/i386/fpemul/load_store.c optional gpl_math_emulate gnu/i386/fpemul/poly_2xm1.c optional gpl_math_emulate gnu/i386/fpemul/poly_atan.c optional gpl_math_emulate gnu/i386/fpemul/poly_div.s optional gpl_math_emulate gnu/i386/fpemul/poly_l2.c optional gpl_math_emulate gnu/i386/fpemul/poly_mul64.s optional gpl_math_emulate gnu/i386/fpemul/poly_sin.c optional gpl_math_emulate gnu/i386/fpemul/poly_tan.c optional gpl_math_emulate gnu/i386/fpemul/polynomial.s optional gpl_math_emulate gnu/i386/fpemul/reg_add_sub.c optional gpl_math_emulate gnu/i386/fpemul/reg_compare.c optional gpl_math_emulate gnu/i386/fpemul/reg_constant.c optional gpl_math_emulate gnu/i386/fpemul/reg_div.s optional gpl_math_emulate gnu/i386/fpemul/reg_ld_str.c optional gpl_math_emulate gnu/i386/fpemul/reg_mul.c optional gpl_math_emulate gnu/i386/fpemul/reg_norm.s optional gpl_math_emulate gnu/i386/fpemul/reg_round.s optional gpl_math_emulate gnu/i386/fpemul/reg_u_add.s optional gpl_math_emulate gnu/i386/fpemul/reg_u_div.s optional gpl_math_emulate gnu/i386/fpemul/reg_u_mul.s optional gpl_math_emulate gnu/i386/fpemul/reg_u_sub.s optional gpl_math_emulate gnu/i386/fpemul/wm_shrx.s optional gpl_math_emulate gnu/i386/fpemul/wm_sqrt.s optional gpl_math_emulate i386/apm/apm.c optional apm i386/i386/atomic.c standard \ compile-with "${CC} -c ${CFLAGS} ${DEFINED_PROF:S/^$/-fomit-frame-pointer/} ${.IMPSRC}" i386/i386/autoconf.c standard i386/i386/bios.c standard i386/i386/bioscall.s standard i386/i386/busdma_machdep.c standard i386/i386/db_disasm.c optional ddb i386/i386/db_interface.c optional ddb i386/i386/db_trace.c optional ddb i386/i386/elf_machdep.c standard i386/i386/exception.s standard i386/i386/i386-gdbstub.c optional ddb i386/i386/i686_mem.c standard i386/i386/identcpu.c standard i386/i386/in_cksum.c optional inet i386/i386/initcpu.c standard i386/i386/k6_mem.c standard # locore.s needs to be handled in Makefile to put it first. Otherwise it's # now normal. # i386/i386/locore.s standard i386/i386/machdep.c standard i386/i386/math_emulate.c optional math_emulate i386/i386/mem.c standard i386/i386/mp_clock.c optional smp i386/i386/mp_machdep.c optional smp i386/i386/mpapic.c optional smp i386/i386/mpboot.s optional smp i386/i386/nexus.c standard i386/i386/perfmon.c optional perfmon i386/i386/perfmon.c optional perfmon profiling-routine i386/i386/pmap.c standard i386/i386/procfs_machdep.c standard i386/i386/support.s standard i386/i386/swtch.s standard i386/i386/sys_machdep.c standard i386/i386/trap.c standard -i386/i386/userconfig.c optional userconfig +#i386/i386/userconfig.c optional userconfig i386/i386/vm86.c standard i386/i386/vm_machdep.c standard i386/ibcs2/ibcs2_errno.c optional ibcs2 i386/ibcs2/ibcs2_fcntl.c optional ibcs2 i386/ibcs2/ibcs2_ioctl.c optional ibcs2 i386/ibcs2/ibcs2_ipc.c optional ibcs2 i386/ibcs2/ibcs2_isc.c optional ibcs2 i386/ibcs2/ibcs2_isc_sysent.c optional ibcs2 i386/ibcs2/ibcs2_misc.c optional ibcs2 i386/ibcs2/ibcs2_msg.c optional ibcs2 i386/ibcs2/ibcs2_other.c optional ibcs2 i386/ibcs2/ibcs2_signal.c optional ibcs2 i386/ibcs2/ibcs2_socksys.c optional ibcs2 i386/ibcs2/ibcs2_stat.c optional ibcs2 i386/ibcs2/ibcs2_sysent.c optional ibcs2 i386/ibcs2/ibcs2_sysi86.c optional ibcs2 i386/ibcs2/ibcs2_sysvec.c optional ibcs2 i386/ibcs2/ibcs2_util.c optional ibcs2 i386/ibcs2/ibcs2_xenix.c optional ibcs2 i386/ibcs2/ibcs2_xenix_sysent.c optional ibcs2 i386/ibcs2/imgact_coff.c optional ibcs2 i386/isa/asc.c count asc i386/isa/clock.c standard i386/isa/cronyx.c optional cx i386/isa/ctx.c count ctx i386/isa/cx.c count cx i386/isa/cy.c count cy i386/isa/elink.c optional ep i386/isa/elink.c optional ie i386/isa/gpib.c optional gp i386/isa/gsc.c count gsc i386/isa/if_cx.c optional cx i386/isa/if_el.c count el i386/isa/if_le.c count le i386/isa/if_rdp.c count rdp i386/isa/if_wl.c count wl i386/isa/if_wlp.c optional wlp i386/isa/intr_machdep.c standard i386/isa/ithread.c standard i386/isa/isa.c optional isa i386/isa/isa_compat.c optional isa compat_oldisa \ warning "Old ISA driver compatibility shims present." i386/isa/isa_dma.c optional isa i386/isa/istallion.c optional stli i386/isa/loran.c optional loran i386/isa/matcd/matcd.c count matcd i386/isa/mca_machdep.c optional mca i386/isa/mcd.c count mcd i386/isa/mse.c optional mse i386/isa/npx.c mandatory npx i386/isa/pcaudio.c optional pca i386/isa/pcf.c optional pcf i386/isa/pcvt/pcvt_drv.c optional vt i386/isa/pcvt/pcvt_ext.c optional vt i386/isa/pcvt/pcvt_kbd.c optional vt i386/isa/pcvt/pcvt_out.c optional vt i386/isa/pcvt/pcvt_sup.c optional vt i386/isa/pcvt/pcvt_vtf.c optional vt i386/isa/pmtimer.c optional pmtimer i386/isa/prof_machdep.c optional profiling-routine i386/isa/rc.c count rc #i386/isa/rp.c optional rp i386/isa/scd.c count scd i386/isa/spic.c optional spic i386/isa/spigot.c count spigot i386/isa/spkr.c optional speaker i386/isa/stallion.c optional stl i386/isa/tw.c count tw i386/isa/vesa.c optional vga vesa i386/isa/wt.c count wt i386/linux/imgact_linux.c optional compat_linux i386/linux/linux_dummy.c optional compat_linux i386/linux/linux_locore.s optional compat_linux \ dependency "linux_assym.h" i386/linux/linux_machdep.c optional compat_linux i386/linux/linux_sysent.c optional compat_linux i386/linux/linux_sysvec.c optional compat_linux i386/pci/pci_cfgreg.c optional pci i386/pci/pci_bus.c optional pci i386/svr4/svr4_locore.s optional compat_svr4 \ dependency "svr4_assym.h" \ warning "COMPAT_SVR4 is broken and usage is, until fixed, not recommended" i386/svr4/svr4_machdep.c optional compat_svr4 # # isdn4bsd, needed for isic | iwic | ifpi | ihfc | ifpnp | itjc # i4b/layer1/i4b_l1dmux.c optional isic i4b/layer1/i4b_l1lib.c optional isic i4b/layer1/i4b_l1dmux.c optional iwic i4b/layer1/i4b_l1lib.c optional iwic i4b/layer1/i4b_l1dmux.c optional ifpi i4b/layer1/i4b_l1lib.c optional ifpi i4b/layer1/i4b_l1dmux.c optional ihfc i4b/layer1/i4b_l1lib.c optional ihfc i4b/layer1/i4b_l1dmux.c optional ifpnp i4b/layer1/i4b_l1lib.c optional ifpnp i4b/layer1/i4b_l1dmux.c optional itjc i4b/layer1/i4b_l1lib.c optional itjc # # isdn4bsd, isic # i4b/layer1/isic/i4b_asuscom_ipac.c optional isic i4b/layer1/isic/i4b_avm_a1.c optional isic i4b/layer1/isic/i4b_bchan.c optional isic i4b/layer1/isic/i4b_ctx_s0P.c optional isic i4b/layer1/isic/i4b_drn_ngo.c optional isic i4b/layer1/isic/i4b_dynalink.c optional isic i4b/layer1/isic/i4b_elsa_qs1i.c optional isic i4b/layer1/isic/i4b_elsa_qs1p.c optional isic i4b/layer1/isic/i4b_elsa_pcc16.c optional isic i4b/layer1/isic/i4b_hscx.c optional isic i4b/layer1/isic/i4b_isac.c optional isic i4b/layer1/isic/i4b_isic.c count isic i4b/layer1/isic/i4b_isic_isa.c optional isic i4b/layer1/isic/i4b_isic_pnp.c optional isic i4b/layer1/isic/i4b_itk_ix1.c optional isic i4b/layer1/isic/i4b_l1.c optional isic i4b/layer1/isic/i4b_l1fsm.c optional isic i4b/layer1/isic/i4b_siemens_isurf.c optional isic i4b/layer1/isic/i4b_sws.c optional isic i4b/layer1/isic/i4b_tel_s016.c optional isic i4b/layer1/isic/i4b_tel_s0163.c optional isic i4b/layer1/isic/i4b_tel_s08.c optional isic i4b/layer1/isic/i4b_usr_sti.c optional isic i4b/layer1/isic/i4b_diva.c optional isic # # isdn4bsd, iwic # i4b/layer1/iwic/i4b_iwic_pci.c count iwic i4b/layer1/iwic/i4b_iwic_dchan.c optional iwic i4b/layer1/iwic/i4b_iwic_bchan.c optional iwic i4b/layer1/iwic/i4b_iwic_fsm.c optional iwic i4b/layer1/iwic/i4b_iwic_l1if.c optional iwic # # isdn4bsd, ifpi # i4b/layer1/ifpi/i4b_ifpi_pci.c count ifpi i4b/layer1/ifpi/i4b_ifpi_isac.c optional ifpi i4b/layer1/ifpi/i4b_ifpi_l1.c optional ifpi i4b/layer1/ifpi/i4b_ifpi_l1fsm.c optional ifpi # # isdn4bsd, ifpnp # i4b/layer1/ifpnp/i4b_ifpnp_avm.c count ifpnp i4b/layer1/ifpnp/i4b_ifpnp_isac.c optional ifpnp i4b/layer1/ifpnp/i4b_ifpnp_l1.c optional ifpnp i4b/layer1/ifpnp/i4b_ifpnp_l1fsm.c optional ifpnp # # isdn4bsd, ihfc # i4b/layer1/ihfc/i4b_ihfc_l1if.c count ihfc i4b/layer1/ihfc/i4b_ihfc_pnp.c optional ihfc i4b/layer1/ihfc/i4b_ihfc_drv.c optional ihfc # # isdn4bsd, itjc # i4b/layer1/itjc/i4b_itjc_pci.c count itjc i4b/layer1/itjc/i4b_itjc_isac.c optional itjc i4b/layer1/itjc/i4b_itjc_l1.c optional itjc i4b/layer1/itjc/i4b_itjc_l1fsm.c optional itjc # isa/atkbd_isa.c optional atkbd isa/atkbdc_isa.c optional atkbdc isa/fd.c optional fdc isa/ppc.c optional ppc isa/psm.c optional psm isa/sio.c optional sio isa/syscons_isa.c optional sc isa/vga_isa.c optional vga kern/imgact_aout.c standard kern/imgact_gzip.c optional gzip kern/link_aout.c standard kern/md4c.c optional netsmb kern/subr_diskmbr.c standard libkern/divdi3.c standard libkern/moddi3.c standard libkern/qdivrem.c standard libkern/ucmpdi2.c standard libkern/udivdi3.c standard libkern/umoddi3.c standard netsmb/smb_conn.c optional netsmb netsmb/smb_crypt.c optional netsmb netsmb/smb_dev.c optional netsmb netsmb/smb_iod.c optional netsmb netsmb/smb_rq.c optional netsmb netsmb/smb_smb.c optional netsmb netsmb/smb_subr.c optional netsmb netsmb/smb_trantcp.c optional netsmb netsmb/smb_usr.c optional netsmb Index: head/sys/conf/files.ia64 =================================================================== --- head/sys/conf/files.ia64 (revision 78134) +++ head/sys/conf/files.ia64 (revision 78135) @@ -1,101 +1,101 @@ # This file tells config what files go into building a kernel, # files marked standard are always included. # # $FreeBSD$ # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and # dependency lines other than the first are silently ignored. # # font8x16.o optional std8x16font \ compile-with "uudecode < /usr/share/syscons/fonts/${STD8X16FONT}-8x16.fnt && file2c 'unsigned char font_16[16*256] = {' '};' < ${STD8X16FONT}-8x16 > font8x16.c && ${CC} -c ${CFLAGS} font8x16.c" \ no-implicit-rule before-depend \ clean "${STD8X16FONT}-8x16 font8x16.c" # atkbdmap.h optional atkbd_dflt_keymap \ compile-with "/usr/sbin/kbdcontrol -L ${ATKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > atkbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "atkbdmap.h" # ia64/ia64/ia64-gdbstub.c optional ddb ia64/ia64/autoconf.c standard ia64/ia64/busdma_machdep.c standard ia64/ia64/clock.c standard ia64/ia64/clock_if.m standard ia64/ia64/db_disasm.c optional ddb ia64/ia64/db_interface.c optional ddb ia64/ia64/db_trace.c optional ddb ia64/ia64/elf_machdep.c standard ia64/ia64/exception.s standard ia64/ia64/in_cksum.c optional inet ia64/ia64/interrupt.c standard # locore.s needs to be handled in Makefile to put it first. Otherwise it's # now normal. # ia64/ia64/locore.s standard ia64/ia64/machdep.c standard ia64/ia64/mem.c standard ia64/ia64/mountroot.c optional slice ia64/ia64/mp_machdep.c optional smp ia64/ia64/pal.s standard ia64/ia64/perfmon.c optional perfmon profiling-routine ia64/ia64/perfmon.c optional perfmon ia64/ia64/pmap.c standard ia64/ia64/procfs_machdep.c standard ia64/ia64/support.s standard ia64/ia64/ssc.c standard ia64/ia64/sscclock.c standard ia64/ia64/sscdisk.c standard ia64/ia64/swtch.s standard ia64/ia64/sys_machdep.c standard ia64/ia64/trap.c standard -ia64/ia64/userconfig.c optional userconfig +#ia64/ia64/userconfig.c optional userconfig ia64/ia64/vm_machdep.c standard ia64/isa/isa.c optional isa ia64/isa/isa_dma.c optional isa contrib/dev/acpica/Subsystem/Hardware/hwcpu64.c optional acpica dev/advansys/adv_isa.c optional adv isa dev/aic/aic_isa.c optional aic isa dev/fb/fb.c optional fb dev/fb/fb.c optional vga dev/fb/splash.c count splash dev/fb/vga.c optional vga dev/kbd/atkbd.c optional atkbd dev/kbd/atkbdc.c count atkbdc dev/kbd/kbd.c optional atkbd dev/kbd/kbd.c optional kbd dev/kbd/kbd.c optional sc dev/kbd/kbd.c optional ukbd dev/syscons/schistory.c count sc dev/syscons/scmouse.c optional sc dev/syscons/scterm.c optional sc dev/syscons/scterm-dumb.c optional sc dev/syscons/scterm-sc.c optional sc dev/syscons/scvgarndr.c optional sc vga dev/syscons/scvidctl.c optional sc dev/syscons/scvtb.c optional sc dev/syscons/syscons.c optional sc dev/syscons/sysmouse.c optional sc isa/atkbd_isa.c optional atkbd isa/atkbdc_isa.c optional atkbdc isa/fd.c optional fdc isa/ppc.c optional ppc isa/psm.c optional psm isa/sio.c optional sio isa/syscons_isa.c optional sc isa/vga_isa.c optional vga kern/subr_diskmbr.c standard libkern/ia64/htonl.S standard libkern/ia64/htons.S standard libkern/ia64/ntohl.S standard libkern/ia64/ntohs.S standard libkern/ia64/__divsi3.S standard libkern/ia64/__modsi3.S standard libkern/ia64/__udivsi3.S standard libkern/ia64/__umodsi3.S standard libkern/ia64/__divdi3.S standard libkern/ia64/__moddi3.S standard libkern/ia64/__udivdi3.S standard libkern/ia64/__umoddi3.S standard libkern/bcmp.c standard libkern/ffs.c standard Index: head/sys/conf/files.pc98 =================================================================== --- head/sys/conf/files.pc98 (revision 78134) +++ head/sys/conf/files.pc98 (revision 78135) @@ -1,401 +1,401 @@ # This file tells config what files go into building a kernel, # files marked standard are always included. # # modified for PC-9801 # # $FreeBSD$ # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and # dependency lines other than the first are silently ignored. # linux_genassym.o optional compat_linux \ dependency "$S/i386/linux/linux_genassym.c" \ compile-with "${CC} ${CFLAGS} -c ${.IMPSRC}" \ no-obj no-implicit-rule \ clean "linux_genassym.o" # linux_assym.h optional compat_linux \ dependency "$S/kern/genassym.sh linux_genassym.o" \ compile-with "sh $S/kern/genassym.sh linux_genassym.o > ${.TARGET}" \ no-obj no-implicit-rule before-depend \ clean "linux_assym.h" # svr4_genassym.o optional compat_svr4 \ dependency "$S/i386/svr4/svr4_genassym.c" \ compile-with "${CC} ${CFLAGS} -c ${.IMPSRC}" \ no-obj no-implicit-rule \ clean "svr4_genassym.o" # svr4_assym.h optional compat_svr4 \ dependency "$S/kern/genassym.sh svr4_genassym.o" \ compile-with "sh $S/kern/genassym.sh svr4_genassym.o > ${.TARGET}" \ no-obj no-implicit-rule before-depend \ clean "svr4_assym.h" # atkbdmap.h optional atkbd_dflt_keymap \ compile-with "/usr/sbin/kbdcontrol -L ${ATKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > atkbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "atkbdmap.h" # ukbdmap.h optional ukbd_dflt_keymap \ compile-with "/usr/sbin/kbdcontrol -L ${UKBD_DFLT_KEYMAP} | sed -e 's/^static keymap_t.* = /static keymap_t key_map = /' -e 's/^static accentmap_t.* = /static accentmap_t accent_map = /' > ukbdmap.h" \ no-obj no-implicit-rule before-depend \ clean "ukbdmap.h" # msysosak.o optional fla \ dependency "$S/contrib/dev/fla/i386/msysosak.o.uu" \ compile-with "uudecode < $S/contrib/dev/fla/i386/msysosak.o.uu" \ no-implicit-rule # trlld.o optional oltr \ dependency "$S/contrib/dev/oltr/i386${FMT}.trlld.o.uu" \ compile-with "uudecode < $S/contrib/dev/oltr/i386${FMT}.trlld.o.uu" \ no-implicit-rule # compat/linux/linux_file.c optional compat_linux compat/linux/linux_ioctl.c optional compat_linux compat/linux/linux_ipc.c optional compat_linux compat/linux/linux_mib.c optional compat_linux compat/linux/linux_misc.c optional compat_linux compat/linux/linux_signal.c optional compat_linux compat/linux/linux_socket.c optional compat_linux compat/linux/linux_stats.c optional compat_linux compat/linux/linux_util.c optional compat_linux compat/linprocfs/linprocfs_misc.c optional linprocfs compat/linprocfs/linprocfs_subr.c optional linprocfs compat/linprocfs/linprocfs_vfsops.c optional linprocfs compat/linprocfs/linprocfs_vnops.c optional linprocfs compat/pecoff/imgact_pecoff.c optional pecoff_support compat/svr4/imgact_svr4.c optional compat_svr4 compat/svr4/svr4_fcntl.c optional compat_svr4 compat/svr4/svr4_filio.c optional compat_svr4 compat/svr4/svr4_ioctl.c optional compat_svr4 compat/svr4/svr4_ipc.c optional compat_svr4 compat/svr4/svr4_misc.c optional compat_svr4 compat/svr4/svr4_resource.c optional compat_svr4 compat/svr4/svr4_signal.c optional compat_svr4 compat/svr4/svr4_socket.c optional compat_svr4 compat/svr4/svr4_sockio.c optional compat_svr4 compat/svr4/svr4_stat.c optional compat_svr4 compat/svr4/svr4_stream.c optional compat_svr4 compat/svr4/svr4_syscallnames.c optional compat_svr4 compat/svr4/svr4_sysent.c optional compat_svr4 compat/svr4/svr4_sysvec.c optional compat_svr4 compat/svr4/svr4_termios.c optional compat_svr4 compat/svr4/svr4_ttold.c optional compat_svr4 contrib/dev/fla/fla.c optional fla contrib/dev/oltr/if_oltr.c optional oltr contrib/dev/oltr/trlldbm.c optional oltr contrib/dev/oltr/trlldhm.c optional oltr contrib/dev/oltr/trlldmac.c optional oltr crypto/des/des_ecb.c optional netsmbcrypto crypto/des/des_setkey.c optional netsmbcrypto #dev/advansys/adv_isa.c optional adv isa dev/aic/aic_cbus.c optional aic isa dev/ar/if_ar_isa.c optional ar isa dev/ct/bshw_machdep.c optional ct dev/ct/ct.c optional ct dev/ct/ct_isa.c optional ct isa dev/ed/if_ed_cbus.c optional ed isa dev/eisa/eisaconf.c optional eisa dev/fb/fb.c optional fb dev/fb/fb.c optional gdc dev/fb/splash.c count splash dev/fe/if_fe_cbus.c optional fe isa dev/kbd/kbd.c optional kbd dev/kbd/kbd.c optional sc dev/kbd/kbd.c optional ukbd dev/kbd/kbd.c count pckbd dev/snc/dp83932.c optional snc dev/snc/dp83932subr.c optional snc dev/snc/if_snc.c optional snc dev/snc/if_snc_cbus.c optional snc isa dev/snc/if_snc_pccard.c optional snc card dev/sr/if_sr_isa.c optional sr isa dev/syscons/schistory.c count sc dev/syscons/scmouse.c optional sc dev/syscons/scterm.c optional sc dev/syscons/scterm-dumb.c optional sc dev/syscons/scvidctl.c optional sc dev/syscons/sysmouse.c optional sc fs/smbfs/smbfs_io.c optional smbfs fs/smbfs/smbfs_node.c optional smbfs fs/smbfs/smbfs_smb.c optional smbfs fs/smbfs/smbfs_subr.c optional smbfs fs/smbfs/smbfs_vfsops.c optional smbfs fs/smbfs/smbfs_vnops.c optional smbfs gnu/i386/fpemul/div_small.s optional gpl_math_emulate gnu/i386/fpemul/errors.c optional gpl_math_emulate gnu/i386/fpemul/fpu_arith.c optional gpl_math_emulate gnu/i386/fpemul/fpu_aux.c optional gpl_math_emulate gnu/i386/fpemul/fpu_entry.c optional gpl_math_emulate gnu/i386/fpemul/fpu_etc.c optional gpl_math_emulate gnu/i386/fpemul/fpu_trig.c optional gpl_math_emulate gnu/i386/fpemul/get_address.c optional gpl_math_emulate gnu/i386/fpemul/load_store.c optional gpl_math_emulate gnu/i386/fpemul/poly_2xm1.c optional gpl_math_emulate gnu/i386/fpemul/poly_atan.c optional gpl_math_emulate gnu/i386/fpemul/poly_div.s optional gpl_math_emulate gnu/i386/fpemul/poly_l2.c optional gpl_math_emulate gnu/i386/fpemul/poly_mul64.s optional gpl_math_emulate gnu/i386/fpemul/poly_sin.c optional gpl_math_emulate gnu/i386/fpemul/poly_tan.c optional gpl_math_emulate gnu/i386/fpemul/polynomial.s optional gpl_math_emulate gnu/i386/fpemul/reg_add_sub.c optional gpl_math_emulate gnu/i386/fpemul/reg_compare.c optional gpl_math_emulate gnu/i386/fpemul/reg_constant.c optional gpl_math_emulate gnu/i386/fpemul/reg_div.s optional gpl_math_emulate gnu/i386/fpemul/reg_ld_str.c optional gpl_math_emulate gnu/i386/fpemul/reg_mul.c optional gpl_math_emulate gnu/i386/fpemul/reg_norm.s optional gpl_math_emulate gnu/i386/fpemul/reg_round.s optional gpl_math_emulate gnu/i386/fpemul/reg_u_add.s optional gpl_math_emulate gnu/i386/fpemul/reg_u_div.s optional gpl_math_emulate gnu/i386/fpemul/reg_u_mul.s optional gpl_math_emulate gnu/i386/fpemul/reg_u_sub.s optional gpl_math_emulate gnu/i386/fpemul/wm_shrx.s optional gpl_math_emulate gnu/i386/fpemul/wm_sqrt.s optional gpl_math_emulate i386/i386/atomic.c standard \ compile-with "${CC} -c ${CFLAGS} ${DEFINED_PROF:S/^$/-fomit-frame-pointer/} ${.IMPSRC}" i386/i386/autoconf.c standard i386/i386/bios.c standard i386/i386/bioscall.s standard i386/i386/busdma_machdep.c standard i386/i386/db_disasm.c optional ddb i386/i386/db_interface.c optional ddb i386/i386/db_trace.c optional ddb i386/i386/elf_machdep.c standard i386/i386/exception.s standard i386/i386/i386-gdbstub.c optional ddb i386/i386/i686_mem.c standard i386/i386/identcpu.c standard i386/i386/in_cksum.c optional inet i386/i386/initcpu.c standard i386/i386/k6_mem.c standard # locore.s needs to be handled in Makefile to put it first. Otherwise it's # now normal. # i386/i386/locore.s standard i386/i386/math_emulate.c optional math_emulate i386/i386/mem.c standard i386/i386/mp_clock.c optional smp i386/i386/mp_machdep.c optional smp i386/i386/mpapic.c optional smp i386/i386/mpboot.s optional smp i386/i386/nexus.c standard i386/i386/perfmon.c optional perfmon i386/i386/perfmon.c optional perfmon profiling-routine i386/i386/pmap.c standard i386/i386/procfs_machdep.c standard i386/i386/support.s standard i386/i386/swtch.s standard i386/i386/sys_machdep.c standard i386/i386/trap.c standard i386/i386/vm86.c standard i386/i386/vm_machdep.c standard i386/ibcs2/ibcs2_errno.c optional ibcs2 i386/ibcs2/ibcs2_fcntl.c optional ibcs2 i386/ibcs2/ibcs2_ioctl.c optional ibcs2 i386/ibcs2/ibcs2_ipc.c optional ibcs2 i386/ibcs2/ibcs2_isc.c optional ibcs2 i386/ibcs2/ibcs2_isc_sysent.c optional ibcs2 i386/ibcs2/ibcs2_misc.c optional ibcs2 i386/ibcs2/ibcs2_msg.c optional ibcs2 i386/ibcs2/ibcs2_other.c optional ibcs2 i386/ibcs2/ibcs2_signal.c optional ibcs2 i386/ibcs2/ibcs2_socksys.c optional ibcs2 i386/ibcs2/ibcs2_stat.c optional ibcs2 i386/ibcs2/ibcs2_sysent.c optional ibcs2 i386/ibcs2/ibcs2_sysi86.c optional ibcs2 i386/ibcs2/ibcs2_sysvec.c optional ibcs2 i386/ibcs2/ibcs2_util.c optional ibcs2 i386/ibcs2/ibcs2_xenix.c optional ibcs2 i386/ibcs2/ibcs2_xenix_sysent.c optional ibcs2 i386/ibcs2/imgact_coff.c optional ibcs2 i386/isa/bs/bs.c optional bs i386/isa/bs/bsfunc.c optional bs i386/isa/bs/bshw.c optional bs i386/isa/bs/bsif.c count bs i386/isa/asc.c count asc i386/isa/cronyx.c optional cx i386/isa/ctx.c count ctx i386/isa/cx.c count cx i386/isa/cy.c count cy i386/isa/elink.c optional ep i386/isa/elink.c optional ie i386/isa/gpib.c optional gp i386/isa/gsc.c count gsc i386/isa/if_cx.c optional cx i386/isa/if_el.c count el i386/isa/if_le.c count le i386/isa/if_rdp.c count rdp i386/isa/if_wl.c count wl i386/isa/intr_machdep.c standard i386/isa/ithread.c standard i386/isa/isa.c optional isa i386/isa/isa_compat.c optional isa compat_oldisa \ warning "Old ISA driver compatibility shims present." i386/isa/istallion.c optional stli i386/isa/loran.c optional loran i386/isa/matcd/matcd.c count matcd i386/isa/mca_machdep.c optional mca i386/isa/mcd.c count mcd i386/isa/pcf.c optional pcf i386/isa/pcvt/pcvt_drv.c optional vt i386/isa/pcvt/pcvt_ext.c optional vt i386/isa/pcvt/pcvt_kbd.c optional vt i386/isa/pcvt/pcvt_out.c optional vt i386/isa/pcvt/pcvt_sup.c optional vt i386/isa/pcvt/pcvt_vtf.c optional vt i386/isa/pmtimer.c optional pmtimer i386/isa/prof_machdep.c optional profiling-routine i386/isa/rc.c count rc #i386/isa/rp.c optional rp i386/isa/scd.c count scd i386/isa/spic.c optional spic i386/isa/spigot.c count spigot i386/isa/stallion.c optional stl i386/isa/tw.c count tw i386/isa/wt.c count wt i386/linux/imgact_linux.c optional compat_linux i386/linux/linux_dummy.c optional compat_linux i386/linux/linux_locore.s optional compat_linux \ dependency "linux_assym.h" i386/linux/linux_machdep.c optional compat_linux i386/linux/linux_sysent.c optional compat_linux i386/linux/linux_sysvec.c optional compat_linux i386/pci/pci_cfgreg.c optional pci i386/pci/pci_bus.c optional pci i386/svr4/svr4_locore.s optional compat_svr4 \ dependency "svr4_assym.h" \ warning "COMPAT_SVR4 is broken and usage is, until fixed, not recommended" i386/svr4/svr4_machdep.c optional compat_svr4 # # isdn4bsd, needed for isic | iwic | ifpi | ihfc | ifpnp | itjc # i4b/layer1/i4b_l1dmux.c optional isic i4b/layer1/i4b_l1lib.c optional isic i4b/layer1/i4b_l1dmux.c optional iwic i4b/layer1/i4b_l1lib.c optional iwic i4b/layer1/i4b_l1dmux.c optional ifpi i4b/layer1/i4b_l1lib.c optional ifpi i4b/layer1/i4b_l1dmux.c optional ihfc i4b/layer1/i4b_l1lib.c optional ihfc i4b/layer1/i4b_l1dmux.c optional ifpnp i4b/layer1/i4b_l1lib.c optional ifpnp i4b/layer1/i4b_l1dmux.c optional itjc i4b/layer1/i4b_l1lib.c optional itjc # # isdn4bsd, isic # i4b/layer1/isic/i4b_asuscom_ipac.c optional isic i4b/layer1/isic/i4b_avm_a1.c optional isic i4b/layer1/isic/i4b_bchan.c optional isic i4b/layer1/isic/i4b_ctx_s0P.c optional isic i4b/layer1/isic/i4b_drn_ngo.c optional isic i4b/layer1/isic/i4b_dynalink.c optional isic i4b/layer1/isic/i4b_elsa_qs1i.c optional isic i4b/layer1/isic/i4b_elsa_qs1p.c optional isic i4b/layer1/isic/i4b_elsa_pcc16.c optional isic i4b/layer1/isic/i4b_hscx.c optional isic i4b/layer1/isic/i4b_isac.c optional isic i4b/layer1/isic/i4b_isic.c count isic i4b/layer1/isic/i4b_isic_isa.c optional isic i4b/layer1/isic/i4b_isic_pnp.c optional isic i4b/layer1/isic/i4b_itk_ix1.c optional isic i4b/layer1/isic/i4b_l1.c optional isic i4b/layer1/isic/i4b_l1fsm.c optional isic i4b/layer1/isic/i4b_siemens_isurf.c optional isic i4b/layer1/isic/i4b_sws.c optional isic i4b/layer1/isic/i4b_tel_s016.c optional isic i4b/layer1/isic/i4b_tel_s0163.c optional isic i4b/layer1/isic/i4b_tel_s08.c optional isic i4b/layer1/isic/i4b_usr_sti.c optional isic i4b/layer1/isic/i4b_diva.c optional isic # # isdn4bsd, iwic # i4b/layer1/iwic/i4b_iwic_pci.c count iwic i4b/layer1/iwic/i4b_iwic_dchan.c optional iwic i4b/layer1/iwic/i4b_iwic_bchan.c optional iwic i4b/layer1/iwic/i4b_iwic_fsm.c optional iwic i4b/layer1/iwic/i4b_iwic_l1if.c optional iwic # # isdn4bsd, ifpi # i4b/layer1/ifpi/i4b_ifpi_pci.c count ifpi i4b/layer1/ifpi/i4b_ifpi_isac.c optional ifpi i4b/layer1/ifpi/i4b_ifpi_l1.c optional ifpi i4b/layer1/ifpi/i4b_ifpi_l1fsm.c optional ifpi # # isdn4bsd, ifpnp # i4b/layer1/ifpnp/i4b_ifpnp_avm.c count ifpnp i4b/layer1/ifpnp/i4b_ifpnp_isac.c optional ifpnp i4b/layer1/ifpnp/i4b_ifpnp_l1.c optional ifpnp i4b/layer1/ifpnp/i4b_ifpnp_l1fsm.c optional ifpnp # # isdn4bsd, ihfc # i4b/layer1/ihfc/i4b_ihfc_l1if.c count ihfc i4b/layer1/ihfc/i4b_ihfc_pnp.c optional ihfc i4b/layer1/ihfc/i4b_ihfc_drv.c optional ihfc # # isdn4bsd, itjc # i4b/layer1/itjc/i4b_itjc_pci.c count itjc i4b/layer1/itjc/i4b_itjc_isac.c optional itjc i4b/layer1/itjc/i4b_itjc_l1.c optional itjc i4b/layer1/itjc/i4b_itjc_l1fsm.c optional itjc # isa/psm.c optional psm kern/imgact_aout.c standard kern/imgact_gzip.c optional gzip kern/link_aout.c standard kern/md4c.c optional netsmb kern/subr_diskmbr.c count compat_atdisk libkern/divdi3.c standard libkern/moddi3.c standard libkern/qdivrem.c standard libkern/ucmpdi2.c standard libkern/udivdi3.c standard libkern/umoddi3.c standard netsmb/smb_conn.c optional netsmb netsmb/smb_crypt.c optional netsmb netsmb/smb_dev.c optional netsmb netsmb/smb_iod.c optional netsmb netsmb/smb_rq.c optional netsmb netsmb/smb_smb.c optional netsmb netsmb/smb_subr.c optional netsmb netsmb/smb_trantcp.c optional netsmb netsmb/smb_usr.c optional netsmb pc98/apm/apm.c optional apm pc98/apm/apm_bioscall.s optional apm pc98/i386/machdep.c standard -pc98/i386/userconfig.c optional userconfig +#pc98/i386/userconfig.c optional userconfig pc98/pc98/atapi.c optional wdc pc98/pc98/clock.c standard pc98/pc98/diskslice_machdep.c standard pc98/pc98/fd.c optional fdc pc98/pc98/isa_dma.c optional isa pc98/pc98/mse.c optional mse pc98/pc98/npx.c mandatory npx pc98/pc98/olpt.c count olpt pc98/pc98/pc98gdc.c optional gdc pc98/pc98/pc98kbd.c optional pckbd pc98/pc98/pc98_machdep.c standard pc98/pc98/pcaudio.c optional pca pc98/pc98/ppc.c optional ppc pc98/pc98/scgdcrndr.c optional sc gdc pc98/pc98/scterm-sck.c optional sc pc98/pc98/scvtbpc98.c optional sc pc98/pc98/sio.c optional sio pc98/pc98/spkr.c optional speaker pc98/pc98/syscons.c optional sc pc98/pc98/syscons_pc98.c optional sc pc98/pc98/wd.c count wdc pc98/pc98/wd_cd.c count wcd wdc pc98/pc98/wfd.c count wfd wdc pc98/pc98/wst.c count wst wdc pccard/mecia.c optional mecia card #pci/ide_pci.c optional wdc pci Index: head/sys/conf/options.i386 =================================================================== --- head/sys/conf/options.i386 (revision 78134) +++ head/sys/conf/options.i386 (revision 78135) @@ -1,210 +1,210 @@ # $FreeBSD$ DISABLE_PSE IDE_DELAY MATH_EMULATE opt_math_emulate.h GPL_MATH_EMULATE opt_math_emulate.h PMAP_SHPGPERPROC opt_pmap.h PPC_PROBE_CHIPSET opt_ppc.h PPC_DEBUG opt_ppc.h SHOW_BUSYBUFS PANIC_REBOOT_WAIT_TIME opt_panic.h MAXMEM PERFMON opt_perfmon.h POWERFAIL_NMI opt_trap.h AUTO_EOI_1 opt_auto_eoi.h AUTO_EOI_2 opt_auto_eoi.h CONSPEED opt_comconsole.h I586_PMC_GUPROF opt_i586_guprof.h WLCACHE opt_wavelan.h WLDEBUG opt_wavelan.h COMPAT_OLDISA BROKEN_KEYBOARD_RESET opt_reset.h # Options for emulators. These should only be used at config time, so # they are handled like options for static file systems # (see src/sys/conf/options), except for broken debugging options. IBCS2 opt_dontuse.h COMPAT_LINUX opt_dontuse.h DEBUG_LINUX opt_linux.h COMPAT_SVR4 opt_dontuse.h DEBUG_SVR4 opt_svr4.h PECOFF_SUPPORT opt_dontuse.h PECOFF_DEBUG opt_pecoff.h # i386 SMP options APIC_IO opt_global.h CLK_CALIBRATION_LOOP opt_clock.h CLK_USE_I8254_CALIBRATION opt_clock.h CLK_USE_TSC_CALIBRATION opt_clock.h TIMER_FREQ opt_clock.h NO_F00F_HACK opt_cpu.h CPU_BLUELIGHTNING_FPU_OP_CACHE opt_cpu.h CPU_BLUELIGHTNING_3X opt_cpu.h CPU_BTB_EN opt_cpu.h CPU_CYRIX_NO_LOCK opt_cpu.h CPU_DIRECT_MAPPED_CACHE opt_cpu.h CPU_DISABLE_5X86_LSSER opt_cpu.h CPU_FASTER_5X86_FPU opt_cpu.h CPU_I486_ON_386 opt_cpu.h CPU_IORT opt_cpu.h CPU_L2_LATENCY opt_cpu.h CPU_LOOP_EN opt_cpu.h CPU_PPRO2CELERON opt_cpu.h CPU_RSTK_EN opt_cpu.h CPU_SUSP_HLT opt_cpu.h CPU_UPGRADE_HW_CACHE opt_cpu.h CPU_WT_ALLOC opt_cpu.h CYRIX_CACHE_WORKS opt_cpu.h CYRIX_CACHE_REALLY_WORKS opt_cpu.h NO_MEMORY_HOLE opt_cpu.h # The CPU type affects the endian conversion functions all over the kernel. I386_CPU opt_global.h I486_CPU opt_global.h I586_CPU opt_global.h I686_CPU opt_global.h MAXCONS opt_syscons.h SC_ALT_MOUSE_IMAGE opt_syscons.h SC_DEBUG_LEVEL opt_syscons.h SC_DFLT_FONT opt_syscons.h SC_DISABLE_DDBKEY opt_syscons.h SC_DISABLE_REBOOT opt_syscons.h SC_HISTORY_SIZE opt_syscons.h SC_KERNEL_CONS_ATTR opt_syscons.h SC_KERNEL_CONS_REV_ATTR opt_syscons.h SC_MOUSE_CHAR opt_syscons.h SC_NO_CUTPASTE opt_syscons.h SC_NO_FONT_LOADING opt_syscons.h SC_NO_HISTORY opt_syscons.h SC_NO_SYSMOUSE opt_syscons.h SC_NORM_ATTR opt_syscons.h SC_NORM_REV_ATTR opt_syscons.h SC_PIXEL_MODE opt_syscons.h SC_RENDER_DEBUG opt_syscons.h SC_TWOBUTTON_MOUSE opt_syscons.h VGA_ALT_SEQACCESS opt_vga.h VGA_DEBUG opt_vga.h VGA_NO_FONT_LOADING opt_vga.h VGA_NO_MODE_CHANGE opt_vga.h VGA_SLOW_IOACCESS opt_vga.h VGA_WIDTH90 opt_vga.h VESA opt_vesa.h VESA_DEBUG opt_vesa.h PSM_HOOKRESUME opt_psm.h PSM_RESETAFTERSUSPEND opt_psm.h PSM_DEBUG opt_psm.h PCIC_RESUME_RESET opt_pcic.h ATKBD_DFLT_KEYMAP opt_atkbd.h KBD_DISABLE_KEYMAP_LOAD opt_kbd.h KBD_INSTALL_CDEV opt_kbd.h KBD_MAXRETRY opt_kbd.h KBD_MAXWAIT opt_kbd.h KBD_RESETDELAY opt_kbd.h KBDIO_DEBUG opt_kbd.h -USERCONFIG opt_userconfig.h -VISUAL_USERCONFIG opt_userconfig.h -INTRO_USERCONFIG opt_userconfig.h -DEV_EISA opt_userconfig.h +#USERCONFIG opt_userconfig.h +#VISUAL_USERCONFIG opt_userconfig.h +#INTRO_USERCONFIG opt_userconfig.h +#DEV_EISA opt_userconfig.h EISA_SLOTS opt_eisa.h FE_8BIT_SUPPORT opt_fe.h # pcvt(4) has a bunch of options FAT_CURSOR opt_pcvt.h XSERVER opt_pcvt.h PCVT_24LINESDEF opt_pcvt.h PCVT_CTRL_ALT_DEL opt_pcvt.h PCVT_META_ESC opt_pcvt.h PCVT_NSCREENS opt_pcvt.h PCVT_PRETTYSCRNS opt_pcvt.h PCVT_SCANSET opt_pcvt.h PCVT_SCREENSAVER opt_pcvt.h PCVT_USEKBDSEC opt_pcvt.h PCVT_VT220KEYB opt_pcvt.h PCVT_GREENSAVER opt_pcvt.h # voxware options GUS_DMA2 opt_sound.h GUS_DMA opt_sound.h GUS_IRQ opt_sound.h # Video spigot SPIGOT_UNSECURE opt_spigot.h # ------------------------------- # isdn4bsd: passive ISA cards # ------------------------------- TEL_S0_8 opt_i4b.h TEL_S0_16 opt_i4b.h TEL_S0_16_3 opt_i4b.h AVM_A1 opt_i4b.h USR_STI opt_i4b.h ITKIX1 opt_i4b.h ELSA_PCC16 opt_i4b.h # ------------------------------- # isdn4bsd: passive ISA PnP cards # ------------------------------- CRTX_S0_P opt_i4b.h DRN_NGO opt_i4b.h TEL_S0_16_3_P opt_i4b.h SEDLBAUER opt_i4b.h DYNALINK opt_i4b.h ASUSCOM_IPAC opt_i4b.h ELSA_QS1ISA opt_i4b.h SIEMENS_ISURF2 opt_i4b.h EICON_DIVA opt_i4b.h # ------------------------------- # isdn4bsd: passive PCI cards # ------------------------------- ELSA_QS1PCI opt_i4b.h AVM_A1_PCI opt_i4b.h # ------------------------------- # isdn4bsd: passive PCMCIA cards # ------------------------------- #AVM_A1_PCMCIA opt_i4b.h # ------------------------------- # isdn4bsd: misc options # ------------------------------- # temporary workaround for SMP machines I4B_SMP_WORKAROUND opt_i4b.h # enable VJ compression code for ipr i/f IPR_VJ opt_i4b.h IPR_LOG opt_i4b.h # ------------------------------- # oltr: build options # ------------------------------- # Exclude microcode options OLTR_NO_TMS_MAC opt_oltr.h OLTR_NO_HAWKEYE_MAC opt_oltr.h OLTR_NO_BULLSEYE_MAC opt_oltr.h # Total number of ports controlled by the dgb(4) driver. # Defaults to NDGB*16. NDGBPORTS opt_dgb.h # Device options DEV_NPX opt_npx.h DEV_APM opt_apm.h # SMB/CIFS requester NETSMB opt_netsmb.h NETSMBCRYPTO opt_netsmb.h # SMB/CIFS filesystem SMBFS # ------------------------------- # EOF # ------------------------------- Index: head/sys/conf/options.pc98 =================================================================== --- head/sys/conf/options.pc98 (revision 78134) +++ head/sys/conf/options.pc98 (revision 78135) @@ -1,213 +1,213 @@ # $FreeBSD$ DISABLE_PSE IDE_DELAY MATH_EMULATE opt_math_emulate.h GPL_MATH_EMULATE opt_math_emulate.h PMAP_SHPGPERPROC opt_pmap.h PPC_PROBE_CHIPSET opt_ppc.h PPC_DEBUG opt_ppc.h SHOW_BUSYBUFS PANIC_REBOOT_WAIT_TIME opt_panic.h MAXMEM PERFMON opt_perfmon.h POWERFAIL_NMI opt_trap.h AUTO_EOI_1 opt_auto_eoi.h AUTO_EOI_2 opt_auto_eoi.h CONSPEED opt_comconsole.h I586_PMC_GUPROF opt_i586_guprof.h WLCACHE opt_wavelan.h WLDEBUG opt_wavelan.h COMPAT_OLDISA BROKEN_KEYBOARD_RESET opt_reset.h # Options for emulators. These should only be used at config time, so # they are handled like options for static file systems # (see src/sys/conf/options), except for broken debugging options. IBCS2 opt_dontuse.h COMPAT_LINUX opt_dontuse.h DEBUG_LINUX opt_linux.h COMPAT_SVR4 opt_dontuse.h DEBUG_SVR4 opt_svr4.h PECOFF_SUPPORT opt_dontuse.h PECOFF_DEBUG opt_pecoff.h # i386 SMP options APIC_IO opt_global.h CLK_CALIBRATION_LOOP opt_clock.h CLK_USE_I8254_CALIBRATION opt_clock.h CLK_USE_TSC_CALIBRATION opt_clock.h TIMER_FREQ opt_clock.h NO_F00F_HACK opt_cpu.h CPU_BLUELIGHTNING_FPU_OP_CACHE opt_cpu.h CPU_BLUELIGHTNING_3X opt_cpu.h CPU_BTB_EN opt_cpu.h CPU_CYRIX_NO_LOCK opt_cpu.h CPU_DIRECT_MAPPED_CACHE opt_cpu.h CPU_DISABLE_5X86_LSSER opt_cpu.h CPU_FASTER_5X86_FPU opt_cpu.h CPU_I486_ON_386 opt_cpu.h CPU_IORT opt_cpu.h CPU_L2_LATENCY opt_cpu.h CPU_LOOP_EN opt_cpu.h CPU_PPRO2CELERON opt_cpu.h CPU_RSTK_EN opt_cpu.h CPU_SUSP_HLT opt_cpu.h CPU_UPGRADE_HW_CACHE opt_cpu.h CPU_WT_ALLOC opt_cpu.h CYRIX_CACHE_WORKS opt_cpu.h CYRIX_CACHE_REALLY_WORKS opt_cpu.h NO_MEMORY_HOLE opt_cpu.h # The CPU type affects the endian conversion functions all over the kernel. I386_CPU opt_global.h I486_CPU opt_global.h I586_CPU opt_global.h I686_CPU opt_global.h MAXCONS opt_syscons.h SC_ALT_MOUSE_IMAGE opt_syscons.h SC_DEBUG_LEVEL opt_syscons.h SC_DFLT_FONT opt_syscons.h SC_DISABLE_DDBKEY opt_syscons.h SC_DISABLE_REBOOT opt_syscons.h SC_HISTORY_SIZE opt_syscons.h SC_KERNEL_CONS_ATTR opt_syscons.h SC_KERNEL_CONS_REV_ATTR opt_syscons.h SC_MOUSE_CHAR opt_syscons.h SC_NO_CUTPASTE opt_syscons.h SC_NO_FONT_LOADING opt_syscons.h SC_NO_HISTORY opt_syscons.h SC_NO_SYSMOUSE opt_syscons.h SC_NORM_ATTR opt_syscons.h SC_NORM_REV_ATTR opt_syscons.h SC_PIXEL_MODE opt_syscons.h SC_RENDER_DEBUG opt_syscons.h SC_TWOBUTTON_MOUSE opt_syscons.h GDC opt_gdc.h PSM_HOOKRESUME opt_psm.h PSM_RESETAFTERSUSPEND opt_psm.h PSM_DEBUG opt_psm.h PCIC_RESUME_RESET opt_pcic.h KBD_DISABLE_KEYMAP_LOAD opt_kbd.h KBD_INSTALL_CDEV opt_kbd.h KBD_MAXRETRY opt_kbd.h KBD_MAXWAIT opt_kbd.h KBD_RESETDELAY opt_kbd.h KBDIO_DEBUG opt_kbd.h -USERCONFIG opt_userconfig.h -VISUAL_USERCONFIG opt_userconfig.h -INTRO_USERCONFIG opt_userconfig.h -DEV_EISA opt_userconfig.h +#USERCONFIG opt_userconfig.h +#VISUAL_USERCONFIG opt_userconfig.h +#INTRO_USERCONFIG opt_userconfig.h +#DEV_EISA opt_userconfig.h EISA_SLOTS opt_eisa.h FE_8BIT_SUPPORT opt_fe.h # pcvt(4) has a bunch of options FAT_CURSOR opt_pcvt.h XSERVER opt_pcvt.h PCVT_24LINESDEF opt_pcvt.h PCVT_CTRL_ALT_DEL opt_pcvt.h PCVT_META_ESC opt_pcvt.h PCVT_NSCREENS opt_pcvt.h PCVT_PRETTYSCRNS opt_pcvt.h PCVT_SCANSET opt_pcvt.h PCVT_SCREENSAVER opt_pcvt.h PCVT_USEKBDSEC opt_pcvt.h PCVT_VT220KEYB opt_pcvt.h PCVT_GREENSAVER opt_pcvt.h # voxware options GUS_DMA2 opt_sound.h GUS_DMA opt_sound.h GUS_IRQ opt_sound.h # Video spigot SPIGOT_UNSECURE opt_spigot.h # ------------------------------- # isdn4bsd: passive ISA cards # ------------------------------- TEL_S0_8 opt_i4b.h TEL_S0_16 opt_i4b.h TEL_S0_16_3 opt_i4b.h AVM_A1 opt_i4b.h USR_STI opt_i4b.h ITKIX1 opt_i4b.h ELSA_PCC16 opt_i4b.h # ------------------------------- # isdn4bsd: passive ISA PnP cards # ------------------------------- CRTX_S0_P opt_i4b.h DRN_NGO opt_i4b.h TEL_S0_16_3_P opt_i4b.h SEDLBAUER opt_i4b.h DYNALINK opt_i4b.h ASUSCOM_IPAC opt_i4b.h ELSA_QS1ISA opt_i4b.h SIEMENS_ISURF2 opt_i4b.h EICON_DIVA opt_i4b.h # ------------------------------- # isdn4bsd: passive PCI cards # ------------------------------- ELSA_QS1PCI opt_i4b.h AVM_A1_PCI opt_i4b.h # ------------------------------- # isdn4bsd: passive PCMCIA cards # ------------------------------- #AVM_A1_PCMCIA opt_i4b.h # ------------------------------- # isdn4bsd: misc options # ------------------------------- # temporary workaround for SMP machines I4B_SMP_WORKAROUND opt_i4b.h # enable VJ compression code for ipr i/f IPR_VJ opt_i4b.h IPR_LOG opt_i4b.h # ------------------------------- # oltr: build options # ------------------------------- # Exclude microcode options OLTR_NO_TMS_MAC opt_oltr.h OLTR_NO_HAWKEYE_MAC opt_oltr.h OLTR_NO_BULLSEYE_MAC opt_oltr.h # Total number of ports controlled by the dgb(4) driver. # Defaults to NDGB*16. NDGBPORTS opt_dgb.h # bs driver options SCSI_BOUNCE_SIZE opt_bs.h BS_TARG_SAFEMODE opt_bs.h # npx options FPU_ERROR_BROKEN opt_npx.h # PC98 options PC98 opt_global.h EPSON_BOUNCEDMA opt_pc98.h EPSON_MEMWIN opt_pc98.h LINE30 opt_syscons.h # Device options DEV_NPX opt_npx.h DEV_APM opt_apm.h # SMB/CIFS requester NETSMB opt_netsmb.h NETSMBCRYPTO opt_netsmb.h # SMB/CIFS filesystem SMBFS # ------------------------------- # EOF # ------------------------------- Index: head/sys/dev/atkbdc/atkbdc_isa.c =================================================================== --- head/sys/dev/atkbdc/atkbdc_isa.c (revision 78134) +++ head/sys/dev/atkbdc/atkbdc_isa.c (revision 78135) @@ -1,317 +1,315 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * All rights reserved. * * 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 as * the first lines of this file unmodified. * 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 AUTHORS ``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 AUTHORS 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. * * $FreeBSD$ */ #include "opt_kbd.h" #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_ATKBDDEV, "atkbddev", "AT Keyboard device"); /* children */ typedef struct atkbdc_device { int flags; /* configuration flags */ int irq; /* ISA IRQ mask */ u_int32_t vendorid; u_int32_t serial; u_int32_t logicalid; u_int32_t compatid; } atkbdc_device_t; /* kbdc */ devclass_t atkbdc_devclass; static int atkbdc_probe(device_t dev); static int atkbdc_attach(device_t dev); static int atkbdc_print_child(device_t bus, device_t dev); static int atkbdc_read_ivar(device_t bus, device_t dev, int index, uintptr_t *val); static int atkbdc_write_ivar(device_t bus, device_t dev, int index, uintptr_t val); static device_method_t atkbdc_methods[] = { DEVMETHOD(device_probe, atkbdc_probe), DEVMETHOD(device_attach, atkbdc_attach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(bus_print_child, atkbdc_print_child), DEVMETHOD(bus_read_ivar, atkbdc_read_ivar), DEVMETHOD(bus_write_ivar, atkbdc_write_ivar), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t atkbdc_driver = { ATKBDC_DRIVER_NAME, atkbdc_methods, sizeof(atkbdc_softc_t *), }; static struct isa_pnp_id atkbdc_ids[] = { { 0x0303d041, "Keyboard controller (i8042)" }, /* PNP0303 */ { 0 } }; static int atkbdc_probe(device_t dev) { struct resource *port0; struct resource *port1; int error; int rid; /* check PnP IDs */ if (ISA_PNP_PROBE(device_get_parent(dev), dev, atkbdc_ids) == ENXIO) return ENXIO; device_set_desc(dev, "Keyboard controller (i8042)"); rid = 0; port0 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); if (port0 == NULL) return ENXIO; /* XXX */ if (bus_get_resource_start(dev, SYS_RES_IOPORT, 1) <= 0) { bus_set_resource(dev, SYS_RES_IOPORT, 1, rman_get_start(port0) + KBD_STATUS_PORT, 1); } rid = 1; port1 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); if (port1 == NULL) { bus_release_resource(dev, SYS_RES_IOPORT, 0, port0); return ENXIO; } error = atkbdc_probe_unit(device_get_unit(dev), port0, port1); bus_release_resource(dev, SYS_RES_IOPORT, 0, port0); bus_release_resource(dev, SYS_RES_IOPORT, 1, port1); return error; } static void atkbdc_add_device(device_t dev, const char *name, int unit) { atkbdc_device_t *kdev; device_t child; int t; if (resource_int_value(name, unit, "disabled", &t) == 0 && t != 0) return; kdev = malloc(sizeof(struct atkbdc_device), M_ATKBDDEV, M_NOWAIT | M_ZERO); if (!kdev) return; if (resource_int_value(name, unit, "irq", &t) == 0) kdev->irq = t; else kdev->irq = -1; if (resource_int_value(name, unit, "flags", &t) == 0) kdev->flags = t; else kdev->flags = 0; child = device_add_child(dev, name, unit); device_set_ivars(child, kdev); } static int atkbdc_attach(device_t dev) { atkbdc_softc_t *sc; - int unit; + int unit, dunit; int error; int rid; int i; - const char *name; + const char *name, *dname; unit = device_get_unit(dev); sc = *(atkbdc_softc_t **)device_get_softc(dev); if (sc == NULL) { /* * We have to maintain two copies of the kbdc_softc struct, * as the low-level console needs to have access to the * keyboard controller before kbdc is probed and attached. * kbdc_soft[] contains the default entry for that purpose. * See atkbdc.c. XXX */ sc = atkbdc_get_softc(unit); if (sc == NULL) return ENOMEM; } rid = 0; sc->port0 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); if (sc->port0 == NULL) return ENXIO; rid = 1; sc->port1 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); if (sc->port1 == NULL) { bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->port0); return ENXIO; } error = atkbdc_attach_unit(unit, sc, sc->port0, sc->port1); if (error) { bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->port0); bus_release_resource(dev, SYS_RES_IOPORT, 1, sc->port1); return error; } *(atkbdc_softc_t **)device_get_softc(dev) = sc; /* * Add all devices configured to be attached to atkbdc0. */ name = device_get_nameunit(dev); - i = -1; - while ((i = resource_query_string(i, "at", name)) != -1) - atkbdc_add_device(dev, resource_query_name(i), - resource_query_unit(i)); + i = 0; + while ((resource_find_match(&i, &dname, &dunit, "at", name)) == 0) + atkbdc_add_device(dev, dname, dunit); /* * and atkbdc? */ name = device_get_name(dev); - i = -1; - while ((i = resource_query_string(i, "at", name)) != -1) - atkbdc_add_device(dev, resource_query_name(i), - resource_query_unit(i)); + i = 0; + while ((resource_find_match(&i, &dname, &dunit, "at", name)) == 0) + atkbdc_add_device(dev, dname, dunit); bus_generic_attach(dev); return 0; } static int atkbdc_print_child(device_t bus, device_t dev) { atkbdc_device_t *kbdcdev; int retval = 0; kbdcdev = (atkbdc_device_t *)device_get_ivars(dev); retval += bus_print_child_header(bus, dev); if (kbdcdev->flags != 0) retval += printf(" flags 0x%x", kbdcdev->flags); if (kbdcdev->irq != -1) retval += printf(" irq %d", kbdcdev->irq); retval += bus_print_child_footer(bus, dev); return (retval); } static int atkbdc_read_ivar(device_t bus, device_t dev, int index, uintptr_t *val) { atkbdc_device_t *ivar; ivar = (atkbdc_device_t *)device_get_ivars(dev); switch (index) { case KBDC_IVAR_IRQ: *val = (u_long)ivar->irq; break; case KBDC_IVAR_FLAGS: *val = (u_long)ivar->flags; break; case KBDC_IVAR_VENDORID: *val = (u_long)ivar->vendorid; break; case KBDC_IVAR_SERIAL: *val = (u_long)ivar->serial; break; case KBDC_IVAR_LOGICALID: *val = (u_long)ivar->logicalid; break; case KBDC_IVAR_COMPATID: *val = (u_long)ivar->compatid; break; default: return ENOENT; } return 0; } static int atkbdc_write_ivar(device_t bus, device_t dev, int index, uintptr_t val) { atkbdc_device_t *ivar; ivar = (atkbdc_device_t *)device_get_ivars(dev); switch (index) { case KBDC_IVAR_IRQ: ivar->irq = (int)val; break; case KBDC_IVAR_FLAGS: ivar->flags = (int)val; break; case KBDC_IVAR_VENDORID: ivar->vendorid = (u_int32_t)val; break; case KBDC_IVAR_SERIAL: ivar->serial = (u_int32_t)val; break; case KBDC_IVAR_LOGICALID: ivar->logicalid = (u_int32_t)val; break; case KBDC_IVAR_COMPATID: ivar->compatid = (u_int32_t)val; break; default: return ENOENT; } return 0; } DRIVER_MODULE(atkbdc, isa, atkbdc_driver, atkbdc_devclass, 0, 0); Index: head/sys/dev/atkbdc/atkbdc_subr.c =================================================================== --- head/sys/dev/atkbdc/atkbdc_subr.c (revision 78134) +++ head/sys/dev/atkbdc/atkbdc_subr.c (revision 78135) @@ -1,317 +1,315 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * All rights reserved. * * 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 as * the first lines of this file unmodified. * 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 AUTHORS ``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 AUTHORS 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. * * $FreeBSD$ */ #include "opt_kbd.h" #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_ATKBDDEV, "atkbddev", "AT Keyboard device"); /* children */ typedef struct atkbdc_device { int flags; /* configuration flags */ int irq; /* ISA IRQ mask */ u_int32_t vendorid; u_int32_t serial; u_int32_t logicalid; u_int32_t compatid; } atkbdc_device_t; /* kbdc */ devclass_t atkbdc_devclass; static int atkbdc_probe(device_t dev); static int atkbdc_attach(device_t dev); static int atkbdc_print_child(device_t bus, device_t dev); static int atkbdc_read_ivar(device_t bus, device_t dev, int index, uintptr_t *val); static int atkbdc_write_ivar(device_t bus, device_t dev, int index, uintptr_t val); static device_method_t atkbdc_methods[] = { DEVMETHOD(device_probe, atkbdc_probe), DEVMETHOD(device_attach, atkbdc_attach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(bus_print_child, atkbdc_print_child), DEVMETHOD(bus_read_ivar, atkbdc_read_ivar), DEVMETHOD(bus_write_ivar, atkbdc_write_ivar), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t atkbdc_driver = { ATKBDC_DRIVER_NAME, atkbdc_methods, sizeof(atkbdc_softc_t *), }; static struct isa_pnp_id atkbdc_ids[] = { { 0x0303d041, "Keyboard controller (i8042)" }, /* PNP0303 */ { 0 } }; static int atkbdc_probe(device_t dev) { struct resource *port0; struct resource *port1; int error; int rid; /* check PnP IDs */ if (ISA_PNP_PROBE(device_get_parent(dev), dev, atkbdc_ids) == ENXIO) return ENXIO; device_set_desc(dev, "Keyboard controller (i8042)"); rid = 0; port0 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); if (port0 == NULL) return ENXIO; /* XXX */ if (bus_get_resource_start(dev, SYS_RES_IOPORT, 1) <= 0) { bus_set_resource(dev, SYS_RES_IOPORT, 1, rman_get_start(port0) + KBD_STATUS_PORT, 1); } rid = 1; port1 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); if (port1 == NULL) { bus_release_resource(dev, SYS_RES_IOPORT, 0, port0); return ENXIO; } error = atkbdc_probe_unit(device_get_unit(dev), port0, port1); bus_release_resource(dev, SYS_RES_IOPORT, 0, port0); bus_release_resource(dev, SYS_RES_IOPORT, 1, port1); return error; } static void atkbdc_add_device(device_t dev, const char *name, int unit) { atkbdc_device_t *kdev; device_t child; int t; if (resource_int_value(name, unit, "disabled", &t) == 0 && t != 0) return; kdev = malloc(sizeof(struct atkbdc_device), M_ATKBDDEV, M_NOWAIT | M_ZERO); if (!kdev) return; if (resource_int_value(name, unit, "irq", &t) == 0) kdev->irq = t; else kdev->irq = -1; if (resource_int_value(name, unit, "flags", &t) == 0) kdev->flags = t; else kdev->flags = 0; child = device_add_child(dev, name, unit); device_set_ivars(child, kdev); } static int atkbdc_attach(device_t dev) { atkbdc_softc_t *sc; - int unit; + int unit, dunit; int error; int rid; int i; - const char *name; + const char *name, *dname; unit = device_get_unit(dev); sc = *(atkbdc_softc_t **)device_get_softc(dev); if (sc == NULL) { /* * We have to maintain two copies of the kbdc_softc struct, * as the low-level console needs to have access to the * keyboard controller before kbdc is probed and attached. * kbdc_soft[] contains the default entry for that purpose. * See atkbdc.c. XXX */ sc = atkbdc_get_softc(unit); if (sc == NULL) return ENOMEM; } rid = 0; sc->port0 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); if (sc->port0 == NULL) return ENXIO; rid = 1; sc->port1 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); if (sc->port1 == NULL) { bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->port0); return ENXIO; } error = atkbdc_attach_unit(unit, sc, sc->port0, sc->port1); if (error) { bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->port0); bus_release_resource(dev, SYS_RES_IOPORT, 1, sc->port1); return error; } *(atkbdc_softc_t **)device_get_softc(dev) = sc; /* * Add all devices configured to be attached to atkbdc0. */ name = device_get_nameunit(dev); - i = -1; - while ((i = resource_query_string(i, "at", name)) != -1) - atkbdc_add_device(dev, resource_query_name(i), - resource_query_unit(i)); + i = 0; + while ((resource_find_match(&i, &dname, &dunit, "at", name)) == 0) + atkbdc_add_device(dev, dname, dunit); /* * and atkbdc? */ name = device_get_name(dev); - i = -1; - while ((i = resource_query_string(i, "at", name)) != -1) - atkbdc_add_device(dev, resource_query_name(i), - resource_query_unit(i)); + i = 0; + while ((resource_find_match(&i, &dname, &dunit, "at", name)) == 0) + atkbdc_add_device(dev, dname, dunit); bus_generic_attach(dev); return 0; } static int atkbdc_print_child(device_t bus, device_t dev) { atkbdc_device_t *kbdcdev; int retval = 0; kbdcdev = (atkbdc_device_t *)device_get_ivars(dev); retval += bus_print_child_header(bus, dev); if (kbdcdev->flags != 0) retval += printf(" flags 0x%x", kbdcdev->flags); if (kbdcdev->irq != -1) retval += printf(" irq %d", kbdcdev->irq); retval += bus_print_child_footer(bus, dev); return (retval); } static int atkbdc_read_ivar(device_t bus, device_t dev, int index, uintptr_t *val) { atkbdc_device_t *ivar; ivar = (atkbdc_device_t *)device_get_ivars(dev); switch (index) { case KBDC_IVAR_IRQ: *val = (u_long)ivar->irq; break; case KBDC_IVAR_FLAGS: *val = (u_long)ivar->flags; break; case KBDC_IVAR_VENDORID: *val = (u_long)ivar->vendorid; break; case KBDC_IVAR_SERIAL: *val = (u_long)ivar->serial; break; case KBDC_IVAR_LOGICALID: *val = (u_long)ivar->logicalid; break; case KBDC_IVAR_COMPATID: *val = (u_long)ivar->compatid; break; default: return ENOENT; } return 0; } static int atkbdc_write_ivar(device_t bus, device_t dev, int index, uintptr_t val) { atkbdc_device_t *ivar; ivar = (atkbdc_device_t *)device_get_ivars(dev); switch (index) { case KBDC_IVAR_IRQ: ivar->irq = (int)val; break; case KBDC_IVAR_FLAGS: ivar->flags = (int)val; break; case KBDC_IVAR_VENDORID: ivar->vendorid = (u_int32_t)val; break; case KBDC_IVAR_SERIAL: ivar->serial = (u_int32_t)val; break; case KBDC_IVAR_LOGICALID: ivar->logicalid = (u_int32_t)val; break; case KBDC_IVAR_COMPATID: ivar->compatid = (u_int32_t)val; break; default: return ENOENT; } return 0; } DRIVER_MODULE(atkbdc, isa, atkbdc_driver, atkbdc_devclass, 0, 0); Index: head/sys/dev/fdc/fdc.c =================================================================== --- head/sys/dev/fdc/fdc.c (revision 78134) +++ head/sys/dev/fdc/fdc.c (revision 78135) @@ -1,2450 +1,2449 @@ /* * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Don Ahn. * * Libretto PCMCIA floppy support by David Horwitt (dhorwitt@ucsd.edu) * aided by the Linux floppy driver modifications from David Bateman * (dbateman@eng.uts.edu.au). * * Copyright (c) 1993, 1994 by * jc@irbs.UUCP (John Capo) * vak@zebub.msk.su (Serge Vakulenko) * ache@astral.msk.su (Andrew A. Chernov) * * Copyright (c) 1993, 1994, 1995 by * joerg_wunsch@uriah.sax.de (Joerg Wunsch) * dufault@hda.com (Peter Dufault) * * Copyright (c) 2001 Joerg Wunsch, * joerg_wunsch@uriah.heep.sax.de (Joerg Wunsch) * * 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 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. * * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 * $FreeBSD$ * */ #include "opt_fdc.h" #include "card.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 #include /* configuration flags */ #define FDC_PRETEND_D0 (1 << 0) /* pretend drive 0 to be there */ #define FDC_NO_FIFO (1 << 2) /* do not enable FIFO */ /* internally used only, not really from CMOS: */ #define RTCFDT_144M_PRETENDED 0x1000 /* error returns for fd_cmd() */ #define FD_FAILED -1 #define FD_NOT_VALID -2 #define FDC_ERRMAX 100 /* do not log more */ #define NUMTYPES 17 #define NUMDENS (NUMTYPES - 7) #define NO_TYPE 0 /* must match NO_TYPE in ft.c */ #define FD_1720 1 #define FD_1480 2 #define FD_1440 3 #define FD_1200 4 #define FD_820 5 #define FD_800 6 #define FD_720 7 #define FD_360 8 #define FD_640 9 #define FD_1232 10 #define FD_1480in5_25 11 #define FD_1440in5_25 12 #define FD_820in5_25 13 #define FD_800in5_25 14 #define FD_720in5_25 15 #define FD_360in5_25 16 #define FD_640in5_25 17 static struct fd_type fd_types[NUMTYPES] = { { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */ { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */ { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */ { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */ { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /* 820K in HD 3.5in */ { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /* 800K in HD 3.5in */ { 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /* 720K in HD 3.5in */ { 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /* 360K in DD 5.25in */ { 8,2,0xFF,0x2A,80,1280,1,FDC_250KBPS,2,0x50,1 }, /* 640K in DD 5.25in */ { 8,3,0xFF,0x35,77,1232,1,FDC_500KBPS,2,0x74,1 }, /* 1.23M in HD 5.25in */ { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */ { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */ { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /* 820K in HD 5.25in */ { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /* 800K in HD 5.25in */ { 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /* 720K in HD 5.25in */ { 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /* 360K in HD 5.25in */ { 8,2,0xFF,0x2A,80,1280,1,FDC_300KBPS,2,0x50,1 }, /* 640K in HD 5.25in */ }; #define DRVS_PER_CTLR 2 /* 2 floppies */ #define MAX_SEC_SIZE (128 << 3) /***********************************************************************\ * Per controller structure. * \***********************************************************************/ static devclass_t fdc_devclass; /***********************************************************************\ * Per drive structure. * * N per controller (DRVS_PER_CTLR) * \***********************************************************************/ struct fd_data { struct fdc_data *fdc; /* pointer to controller structure */ int fdsu; /* this units number on this controller */ int type; /* Drive type (FD_1440...) */ struct fd_type *ft; /* pointer to the type descriptor */ int flags; #define FD_OPEN 0x01 /* it's open */ #define FD_ACTIVE 0x02 /* it's active */ #define FD_MOTOR 0x04 /* motor should be on */ #define FD_MOTOR_WAIT 0x08 /* motor coming up */ int skip; int hddrv; #define FD_NO_TRACK -2 int track; /* where we think the head is */ int options; /* user configurable options, see ioctl_fd.h */ struct callout_handle toffhandle; struct callout_handle tohandle; struct devstat device_stats; device_t dev; fdu_t fdu; }; struct fdc_ivars { int fdunit; }; static devclass_t fd_devclass; /***********************************************************************\ * Throughout this file the following conventions will be used: * * fd is a pointer to the fd_data struct for the drive in question * * fdc is a pointer to the fdc_data struct for the controller * * fdu is the floppy drive unit number * * fdcu is the floppy controller unit number * * fdsu is the floppy drive unit number on that controller. (sub-unit) * \***********************************************************************/ /* needed for ft driver, thus exported */ int in_fdc(struct fdc_data *); int out_fdc(struct fdc_data *, int); /* internal functions */ static void fdc_intr(void *); static void set_motor(struct fdc_data *, int, int); # define TURNON 1 # define TURNOFF 0 static timeout_t fd_turnoff; static timeout_t fd_motor_on; static void fd_turnon(struct fd_data *); static void fdc_reset(fdc_p); static int fd_in(struct fdc_data *, int *); static void fdstart(struct fdc_data *); static timeout_t fd_iotimeout; static timeout_t fd_pseudointr; static int fdstate(struct fdc_data *); static int retrier(struct fdc_data *); static int fdformat(dev_t, struct fd_formb *, struct proc *); static int enable_fifo(fdc_p fdc); static int fifo_threshold = 8; /* XXX: should be accessible via sysctl */ #ifdef FDC_DEBUG static char const * const fdstates[] = { "DEVIDLE", "FINDWORK", "DOSEEK", "SEEKCOMPLETE", "IOCOMPLETE", "RECALCOMPLETE", "STARTRECAL", "RESETCTLR", "SEEKWAIT", "RECALWAIT", "MOTORWAIT", "IOTIMEDOUT", "RESETCOMPLETE", "PIOREAD", }; /* CAUTION: fd_debug causes huge amounts of logging output */ static int volatile fd_debug = 0; #define TRACE0(arg) if(fd_debug) printf(arg) #define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2) #else /* FDC_DEBUG */ #define TRACE0(arg) #define TRACE1(arg1, arg2) #endif /* FDC_DEBUG */ static void fdout_wr(fdc_p fdc, u_int8_t v) { bus_space_write_1(fdc->portt, fdc->porth, FDOUT+fdc->port_off, v); } static u_int8_t fdsts_rd(fdc_p fdc) { return bus_space_read_1(fdc->portt, fdc->porth, FDSTS+fdc->port_off); } static void fddata_wr(fdc_p fdc, u_int8_t v) { bus_space_write_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off, v); } static u_int8_t fddata_rd(fdc_p fdc) { return bus_space_read_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off); } static void fdctl_wr_isa(fdc_p fdc, u_int8_t v) { bus_space_write_1(fdc->ctlt, fdc->ctlh, 0, v); } #if NCARD > 0 static void fdctl_wr_pcmcia(fdc_p fdc, u_int8_t v) { bus_space_write_1(fdc->portt, fdc->porth, FDCTL+fdc->port_off, v); } #endif #if 0 static u_int8_t fdin_rd(fdc_p fdc) { return bus_space_read_1(fdc->portt, fdc->porth, FDIN); } #endif /* * named Fdopen() to avoid confusion with fdopen() in fd(4); the * difference is now only meaningful for debuggers */ static d_open_t Fdopen; static d_close_t fdclose; static d_ioctl_t fdioctl; static d_strategy_t fdstrategy; #define CDEV_MAJOR 9 static struct cdevsw fd_cdevsw = { /* open */ Fdopen, /* close */ fdclose, /* read */ physread, /* write */ physwrite, /* ioctl */ fdioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ fdstrategy, /* name */ "fd", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ D_DISK, }; static int fdc_err(struct fdc_data *fdc, const char *s) { fdc->fdc_errs++; if (s) { if (fdc->fdc_errs < FDC_ERRMAX) device_printf(fdc->fdc_dev, "%s", s); else if (fdc->fdc_errs == FDC_ERRMAX) device_printf(fdc->fdc_dev, "too many errors, not " "logging any more\n"); } return FD_FAILED; } /* * fd_cmd: Send a command to the chip. Takes a varargs with this structure: * Unit number, * # of output bytes, output bytes as ints ..., * # of input bytes, input bytes as ints ... */ static int fd_cmd(struct fdc_data *fdc, int n_out, ...) { u_char cmd; int n_in; int n; va_list ap; va_start(ap, n_out); cmd = (u_char)(va_arg(ap, int)); va_end(ap); va_start(ap, n_out); for (n = 0; n < n_out; n++) { if (out_fdc(fdc, va_arg(ap, int)) < 0) { char msg[50]; snprintf(msg, sizeof(msg), "cmd %x failed at out byte %d of %d\n", cmd, n + 1, n_out); return fdc_err(fdc, msg); } } n_in = va_arg(ap, int); for (n = 0; n < n_in; n++) { int *ptr = va_arg(ap, int *); if (fd_in(fdc, ptr) < 0) { char msg[50]; snprintf(msg, sizeof(msg), "cmd %02x failed at in byte %d of %d\n", cmd, n + 1, n_in); return fdc_err(fdc, msg); } } return 0; } static int enable_fifo(fdc_p fdc) { int i, j; if ((fdc->flags & FDC_HAS_FIFO) == 0) { /* * Cannot use fd_cmd the normal way here, since * this might be an invalid command. Thus we send the * first byte, and check for an early turn of data directon. */ if (out_fdc(fdc, I8207X_CONFIGURE) < 0) return fdc_err(fdc, "Enable FIFO failed\n"); /* If command is invalid, return */ j = 100000; while ((i = fdsts_rd(fdc) & (NE7_DIO | NE7_RQM)) != NE7_RQM && j-- > 0) if (i == (NE7_DIO | NE7_RQM)) { fdc_reset(fdc); return FD_FAILED; } if (j<0 || fd_cmd(fdc, 3, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) { fdc_reset(fdc); return fdc_err(fdc, "Enable FIFO failed\n"); } fdc->flags |= FDC_HAS_FIFO; return 0; } if (fd_cmd(fdc, 4, I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) return fdc_err(fdc, "Re-enable FIFO failed\n"); return 0; } static int fd_sense_drive_status(fdc_p fdc, int *st3p) { int st3; if (fd_cmd(fdc, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3)) { return fdc_err(fdc, "Sense Drive Status failed\n"); } if (st3p) *st3p = st3; return 0; } static int fd_sense_int(fdc_p fdc, int *st0p, int *cylp) { int cyl, st0, ret; ret = fd_cmd(fdc, 1, NE7CMD_SENSEI, 1, &st0); if (ret) { (void)fdc_err(fdc, "sense intr err reading stat reg 0\n"); return ret; } if (st0p) *st0p = st0; if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) { /* * There doesn't seem to have been an interrupt. */ return FD_NOT_VALID; } if (fd_in(fdc, &cyl) < 0) { return fdc_err(fdc, "can't get cyl num\n"); } if (cylp) *cylp = cyl; return 0; } static int fd_read_status(fdc_p fdc, int fdsu) { int i, ret; for (i = 0; i < 7; i++) { /* * XXX types are poorly chosen. Only bytes can by read * from the hardware, but fdc->status[] wants u_ints and * fd_in() gives ints. */ int status; ret = fd_in(fdc, &status); fdc->status[i] = status; if (ret != 0) break; } if (ret == 0) fdc->flags |= FDC_STAT_VALID; else fdc->flags &= ~FDC_STAT_VALID; return ret; } /****************************************************************************/ /* autoconfiguration stuff */ /****************************************************************************/ static int fdc_alloc_resources(struct fdc_data *fdc) { device_t dev; int ispnp, ispcmcia; dev = fdc->fdc_dev; ispnp = (fdc->flags & FDC_ISPNP) != 0; ispcmcia = (fdc->flags & FDC_ISPCMCIA) != 0; fdc->rid_ioport = fdc->rid_irq = fdc->rid_drq = 0; fdc->res_ioport = fdc->res_irq = fdc->res_drq = 0; /* * On standard ISA, we don't just use an 8 port range * (e.g. 0x3f0-0x3f7) since that covers an IDE control * register at 0x3f6. * * Isn't PC hardware wonderful. * * The Y-E Data PCMCIA FDC doesn't have this problem, it * uses the register with offset 6 for pseudo-DMA, and the * one with offset 7 as control register. */ fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, &fdc->rid_ioport, 0ul, ~0ul, ispcmcia ? 8 : (ispnp ? 1 : 6), RF_ACTIVE); if (fdc->res_ioport == 0) { device_printf(dev, "cannot reserve I/O port range\n"); return ENXIO; } fdc->portt = rman_get_bustag(fdc->res_ioport); fdc->porth = rman_get_bushandle(fdc->res_ioport); if (!ispcmcia) { /* * Some BIOSen report the device at 0x3f2-0x3f5,0x3f7 * and some at 0x3f0-0x3f5,0x3f7. We detect the former * by checking the size and adjust the port address * accordingly. */ if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 4) fdc->port_off = -2; /* * Register the control port range as rid 1 if it * isn't there already. Most PnP BIOSen will have * already done this but non-PnP configurations don't. * * And some (!!) report 0x3f2-0x3f5 and completely * leave out the control register! It seems that some * non-antique controller chips have a different * method of programming the transfer speed which * doesn't require the control register, but it's * mighty bogus as the chip still responds to the * address for the control register. */ if (bus_get_resource_count(dev, SYS_RES_IOPORT, 1) == 0) { u_long ctlstart; /* Find the control port, usually 0x3f7 */ ctlstart = rman_get_start(fdc->res_ioport) + fdc->port_off + 7; bus_set_resource(dev, SYS_RES_IOPORT, 1, ctlstart, 1); } /* * Now (finally!) allocate the control port. */ fdc->rid_ctl = 1; fdc->res_ctl = bus_alloc_resource(dev, SYS_RES_IOPORT, &fdc->rid_ctl, 0ul, ~0ul, 1, RF_ACTIVE); if (fdc->res_ctl == 0) { device_printf(dev, "cannot reserve control I/O port range\n"); return ENXIO; } fdc->ctlt = rman_get_bustag(fdc->res_ctl); fdc->ctlh = rman_get_bushandle(fdc->res_ctl); } fdc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &fdc->rid_irq, 0ul, ~0ul, 1, RF_ACTIVE); if (fdc->res_irq == 0) { device_printf(dev, "cannot reserve interrupt line\n"); return ENXIO; } if ((fdc->flags & FDC_NODMA) == 0) { fdc->res_drq = bus_alloc_resource(dev, SYS_RES_DRQ, &fdc->rid_drq, 0ul, ~0ul, 1, RF_ACTIVE); if (fdc->res_drq == 0) { device_printf(dev, "cannot reserve DMA request line\n"); return ENXIO; } fdc->dmachan = fdc->res_drq->r_start; } return 0; } static void fdc_release_resources(struct fdc_data *fdc) { device_t dev; dev = fdc->fdc_dev; if (fdc->res_irq != 0) { bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq, fdc->res_irq); bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq, fdc->res_irq); } if (fdc->res_ctl != 0) { bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, fdc->res_ctl); bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, fdc->res_ctl); } if (fdc->res_ioport != 0) { bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, fdc->res_ioport); bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, fdc->res_ioport); } if (fdc->res_drq != 0) { bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq, fdc->res_drq); bus_release_resource(dev, SYS_RES_DRQ, fdc->rid_drq, fdc->res_drq); } } /****************************************************************************/ /* autoconfiguration stuff */ /****************************************************************************/ static struct isa_pnp_id fdc_ids[] = { {0x0007d041, "PC standard floppy disk controller"}, /* PNP0700 */ {0x0107d041, "Standard floppy controller supporting MS Device Bay Spec"}, /* PNP0701 */ {0} }; static int fdc_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { struct fdc_ivars *ivars = device_get_ivars(child); switch (which) { case FDC_IVAR_FDUNIT: *result = ivars->fdunit; break; default: return ENOENT; } return 0; } /* * fdc controller section. */ static int fdc_probe(device_t dev) { int error, ic_type; struct fdc_data *fdc; fdc = device_get_softc(dev); bzero(fdc, sizeof *fdc); fdc->fdc_dev = dev; fdc->fdctl_wr = fdctl_wr_isa; /* Check pnp ids */ error = ISA_PNP_PROBE(device_get_parent(dev), dev, fdc_ids); if (error == ENXIO) return ENXIO; if (error == 0) fdc->flags |= FDC_ISPNP; /* Attempt to allocate our resources for the duration of the probe */ error = fdc_alloc_resources(fdc); if (error) goto out; /* First - lets reset the floppy controller */ fdout_wr(fdc, 0); DELAY(100); fdout_wr(fdc, FDO_FRST); /* see if it can handle a command */ if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0)) { error = ENXIO; goto out; } if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) { ic_type = (u_char)ic_type; switch (ic_type) { case 0x80: device_set_desc(dev, "NEC 765 or clone"); fdc->fdct = FDC_NE765; break; case 0x81: device_set_desc(dev, "Intel 82077 or clone"); fdc->fdct = FDC_I82077; break; case 0x90: device_set_desc(dev, "NEC 72065B or clone"); fdc->fdct = FDC_NE72065; break; default: device_set_desc(dev, "generic floppy controller"); fdc->fdct = FDC_UNKNOWN; break; } } out: fdc_release_resources(fdc); return (error); } #if NCARD > 0 static int fdc_pccard_probe(device_t dev) { int error; struct fdc_data *fdc; fdc = device_get_softc(dev); bzero(fdc, sizeof *fdc); fdc->fdc_dev = dev; fdc->fdctl_wr = fdctl_wr_pcmcia; fdc->flags |= FDC_ISPCMCIA | FDC_NODMA; /* Attempt to allocate our resources for the duration of the probe */ error = fdc_alloc_resources(fdc); if (error) goto out; /* First - lets reset the floppy controller */ fdout_wr(fdc, 0); DELAY(100); fdout_wr(fdc, FDO_FRST); /* see if it can handle a command */ if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0)) { error = ENXIO; goto out; } device_set_desc(dev, "Y-E Data PCMCIA floppy"); fdc->fdct = FDC_NE765; out: fdc_release_resources(fdc); return (error); } static int fdc_pccard_detach(device_t dev) { struct fdc_data *fdc; int error; fdc = device_get_softc(dev); /* have our children detached first */ if ((error = bus_generic_detach(dev))) return (error); if ((fdc->flags & FDC_ATTACHED) == 0) { device_printf(dev, "already unloaded\n"); return (0); } fdc->flags &= ~FDC_ATTACHED; BUS_TEARDOWN_INTR(device_get_parent(dev), dev, fdc->res_irq, fdc->fdc_intr); fdc_release_resources(fdc); device_printf(dev, "unload\n"); return (0); } #endif /* NCARD > 0 */ /* * Add a child device to the fdc controller. It will then be probed etc. */ static void fdc_add_child(device_t dev, const char *name, int unit) { int disabled; struct fdc_ivars *ivar; device_t child; ivar = malloc(sizeof *ivar, M_DEVBUF /* XXX */, M_NOWAIT | M_ZERO); if (ivar == NULL) return; if (resource_int_value(name, unit, "drive", &ivar->fdunit) != 0) ivar->fdunit = 0; child = device_add_child(dev, name, unit); if (child == NULL) return; device_set_ivars(child, ivar); if (resource_int_value(name, unit, "disabled", &disabled) == 0 && disabled != 0) device_disable(child); } static int fdc_attach(device_t dev) { struct fdc_data *fdc; - int i, error; - const char *name; + int i, error, dunit; + const char *name, *dname; fdc = device_get_softc(dev); error = fdc_alloc_resources(fdc); if (error) { device_printf(dev, "cannot re-acquire resources\n"); return error; } error = BUS_SETUP_INTR(device_get_parent(dev), dev, fdc->res_irq, INTR_TYPE_BIO | INTR_ENTROPY, fdc_intr, fdc, &fdc->fdc_intr); if (error) { device_printf(dev, "cannot setup interrupt\n"); return error; } fdc->fdcu = device_get_unit(dev); fdc->flags |= FDC_ATTACHED; if ((fdc->flags & FDC_NODMA) == 0) { /* * Acquire the DMA channel forever, the driver will do * the rest * XXX should integrate with rman */ isa_dma_acquire(fdc->dmachan); isa_dmainit(fdc->dmachan, MAX_SEC_SIZE); } fdc->state = DEVIDLE; /* reset controller, turn motor off, clear fdout mirror reg */ fdout_wr(fdc, ((fdc->fdout = 0))); bioq_init(&fdc->head); /* * Probe and attach any children. We should probably detect * devices from the BIOS unless overridden. */ name = device_get_nameunit(dev); - i = -1; - while ((i = resource_query_string(i, "at", name)) != -1) - fdc_add_child(dev, resource_query_name(i), - resource_query_unit(i)); + i = 0; + while ((resource_find_match(&i, &dname, &dunit, "at", name)) == 0) + fdc_add_child(dev, dname, dunit); return (bus_generic_attach(dev)); } static int fdc_print_child(device_t me, device_t child) { int retval = 0; retval += bus_print_child_header(me, child); retval += printf(" on %s drive %d\n", device_get_nameunit(me), fdc_get_fdunit(child)); return (retval); } static device_method_t fdc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fdc_probe), DEVMETHOD(device_attach, fdc_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, fdc_print_child), DEVMETHOD(bus_read_ivar, fdc_read_ivar), /* Our children never use any other bus interface methods. */ { 0, 0 } }; static driver_t fdc_driver = { "fdc", fdc_methods, sizeof(struct fdc_data) }; DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, 0, 0); #if NCARD > 0 static device_method_t fdc_pccard_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fdc_pccard_probe), DEVMETHOD(device_attach, fdc_attach), DEVMETHOD(device_detach, fdc_pccard_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, fdc_print_child), DEVMETHOD(bus_read_ivar, fdc_read_ivar), /* Our children never use any other bus interface methods. */ { 0, 0 } }; static driver_t fdc_pccard_driver = { "fdc", fdc_pccard_methods, sizeof(struct fdc_data) }; DRIVER_MODULE(fdc, pccard, fdc_pccard_driver, fdc_devclass, 0, 0); #endif /* NCARD > 0 */ static void fd_clone __P((void *arg, char *name, int namelen, dev_t *dev)); static struct { char *match; int minor; int link; } fd_suffix[] = { { "a", 0, 1 }, { "b", 0, 1 }, { "c", 0, 1 }, { "d", 0, 1 }, { "e", 0, 1 }, { "f", 0, 1 }, { "g", 0, 1 }, { "h", 0, 1 }, { ".1720", 1, 0 }, { ".1480", 2, 0 }, { ".1440", 3, 0 }, { ".1200", 4, 0 }, { ".820", 5, 0 }, { ".800", 6, 0 }, { ".720", 7, 0 }, { ".360", 8, 0 }, { ".640", 9, 0 }, { ".1232", 10, 0 }, { 0, 0 } }; static void fd_clone(arg, name, namelen, dev) void *arg; char *name; int namelen; dev_t *dev; { int u, d, i; char *n; dev_t pdev; if (*dev != NODEV) return; if (dev_stdclone(name, &n, "fd", &u) != 2) return; for (i = 0; ; i++) { if (fd_suffix[i].match == NULL) return; if (strcmp(n, fd_suffix[i].match)) continue; d = fd_suffix[i].minor; break; } if (fd_suffix[i].link == 0) { *dev = make_dev(&fd_cdevsw, (u << 6) + d, UID_ROOT, GID_OPERATOR, 0640, name); } else { pdev = makedev(fd_cdevsw.d_maj, (u << 6) + d); *dev = make_dev_alias(pdev, name); } } /******************************************************************/ /* * devices attached to the controller section. */ static int fd_probe(device_t dev) { int i; u_int fdt, st0, st3; struct fd_data *fd; struct fdc_data *fdc; fdsu_t fdsu; static int fd_fifo = 0; fdsu = *(int *)device_get_ivars(dev); /* xxx cheat a bit... */ fd = device_get_softc(dev); fdc = device_get_softc(device_get_parent(dev)); bzero(fd, sizeof *fd); fd->dev = dev; fd->fdc = fdc; fd->fdsu = fdsu; fd->fdu = device_get_unit(dev); #ifdef __i386__ /* look up what bios thinks we have */ switch (fd->fdu) { case 0: if ((fdc->flags & FDC_ISPCMCIA)) fdt = RTCFDT_144M; else if (device_get_flags(fdc->fdc_dev) & FDC_PRETEND_D0) fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED; else fdt = (rtcin(RTC_FDISKETTE) & 0xf0); break; case 1: fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0); break; default: fdt = RTCFDT_NONE; break; } #else fdt = RTCFDT_144M; /* XXX probably */ #endif /* is there a unit? */ if (fdt == RTCFDT_NONE) return (ENXIO); /* select it */ set_motor(fdc, fdsu, TURNON); DELAY(1000000); /* 1 sec */ /* XXX This doesn't work before the first set_motor() */ if (fd_fifo == 0 && fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN && (device_get_flags(fdc->fdc_dev) & FDC_NO_FIFO) == 0 && enable_fifo(fdc) == 0) { device_printf(device_get_parent(dev), "FIFO enabled, %d bytes threshold\n", fifo_threshold); } fd_fifo = 1; if ((fd_cmd(fdc, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* if at track 0, first seek inwards */ /* seek some steps: */ fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0); DELAY(300000); /* ...wait a moment... */ fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ } /* If we're at track 0 first seek inwards. */ if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* Seek some steps... */ if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { /* ...wait a moment... */ DELAY(300000); /* make ctrlr happy: */ fd_sense_int(fdc, 0, 0); } } for (i = 0; i < 2; i++) { /* * we must recalibrate twice, just in case the * heads have been beyond cylinder 76, since most * FDCs still barf when attempting to recalibrate * more than 77 steps */ /* go back to 0: */ if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) { /* a second being enough for full stroke seek*/ DELAY(i == 0 ? 1000000 : 300000); /* anything responding? */ if (fd_sense_int(fdc, &st0, 0) == 0 && (st0 & NE7_ST0_EC) == 0) break; /* already probed succesfully */ } } set_motor(fdc, fdsu, TURNOFF); if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ return (ENXIO); fd->track = FD_NO_TRACK; fd->fdc = fdc; fd->fdsu = fdsu; fd->options = 0; callout_handle_init(&fd->toffhandle); callout_handle_init(&fd->tohandle); switch (fdt) { case RTCFDT_12M: device_set_desc(dev, "1200-KB 5.25\" drive"); fd->type = FD_1200; break; case RTCFDT_144M | RTCFDT_144M_PRETENDED: device_set_desc(dev, "config-pretended 1440-MB 3.5\" drive"); fdt = RTCFDT_144M; fd->type = FD_1440; case RTCFDT_144M: device_set_desc(dev, "1440-KB 3.5\" drive"); fd->type = FD_1440; break; case RTCFDT_288M: case RTCFDT_288M_1: device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)"); fd->type = FD_1440; break; case RTCFDT_360K: device_set_desc(dev, "360-KB 5.25\" drive"); fd->type = FD_360; break; case RTCFDT_720K: printf("720-KB 3.5\" drive"); fd->type = FD_720; break; default: return (ENXIO); } return (0); } static int fd_attach(device_t dev) { struct fd_data *fd; static int cdevsw_add_done = 0; fd = device_get_softc(dev); if (!cdevsw_add_done) { cdevsw_add(&fd_cdevsw); /* XXX */ cdevsw_add_done++; } EVENTHANDLER_REGISTER(dev_clone, fd_clone, 0, 1000); make_dev(&fd_cdevsw, (fd->fdu << 6), UID_ROOT, GID_OPERATOR, 0640, "fd%d", fd->fdu); /* * Export the drive to the devstat interface. */ devstat_add_entry(&fd->device_stats, device_get_name(dev), device_get_unit(dev), 0, DEVSTAT_NO_ORDERED_TAGS, DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER, DEVSTAT_PRIORITY_FD); return (0); } static int fd_detach(device_t dev) { struct fd_data *fd; fd = device_get_softc(dev); untimeout(fd_turnoff, fd, fd->toffhandle); return (0); } static device_method_t fd_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fd_probe), DEVMETHOD(device_attach, fd_attach), DEVMETHOD(device_detach, fd_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), /* XXX */ DEVMETHOD(device_resume, bus_generic_resume), /* XXX */ { 0, 0 } }; static driver_t fd_driver = { "fd", fd_methods, sizeof(struct fd_data) }; DRIVER_MODULE(fd, fdc, fd_driver, fd_devclass, 0, 0); /****************************************************************************/ /* motor control stuff */ /* remember to not deselect the drive we're working on */ /****************************************************************************/ static void set_motor(struct fdc_data *fdc, int fdsu, int turnon) { int fdout = fdc->fdout; int needspecify = 0; if(turnon) { fdout &= ~FDO_FDSEL; fdout |= (FDO_MOEN0 << fdsu) + fdsu; } else fdout &= ~(FDO_MOEN0 << fdsu); if(!turnon && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0) /* gonna turn off the last drive, put FDC to bed */ fdout &= ~ (FDO_FRST|FDO_FDMAEN); else { /* make sure controller is selected and specified */ if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0) needspecify = 1; fdout |= (FDO_FRST|FDO_FDMAEN); } fdout_wr(fdc, fdout); fdc->fdout = fdout; TRACE1("[0x%x->FDOUT]", fdout); if (needspecify) { /* * we silently assume the command will be accepted * after an FDC reset * * Steinbach's Guideline for Systems Programming: * Never test for an error condition you don't know * how to handle. */ (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0); if (fdc->flags & FDC_HAS_FIFO) (void) enable_fifo(fdc); } } static void fd_turnoff(void *xfd) { int s; fd_p fd = xfd; TRACE1("[fd%d: turnoff]", fd->fdu); s = splbio(); /* * Don't turn off the motor yet if the drive is active. * * If we got here, this could only mean we missed an interrupt. * This can e. g. happen on the Y-E Date PCMCIA floppy controller * after a controller reset. Just schedule a pseudo-interrupt * so the state machine gets re-entered. */ if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fd->fdu) { fdc_intr(fd->fdc); splx(s); return; } fd->flags &= ~FD_MOTOR; set_motor(fd->fdc, fd->fdsu, TURNOFF); splx(s); } static void fd_motor_on(void *xfd) { int s; fd_p fd = xfd; s = splbio(); fd->flags &= ~FD_MOTOR_WAIT; if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT)) { fdc_intr(fd->fdc); } splx(s); } static void fd_turnon(fd_p fd) { if(!(fd->flags & FD_MOTOR)) { fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT); set_motor(fd->fdc, fd->fdsu, TURNON); timeout(fd_motor_on, fd, hz); /* in 1 sec its ok */ } } static void fdc_reset(fdc_p fdc) { /* Try a reset, keep motor on */ fdout_wr(fdc, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); DELAY(100); /* enable FDC, but defer interrupts a moment */ fdout_wr(fdc, fdc->fdout & ~FDO_FDMAEN); TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN); DELAY(100); fdout_wr(fdc, fdc->fdout); TRACE1("[0x%x->FDOUT]", fdc->fdout); /* after a reset, silently believe the FDC will accept commands */ (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0); if (fdc->flags & FDC_HAS_FIFO) (void) enable_fifo(fdc); } /****************************************************************************/ /* fdc in/out */ /****************************************************************************/ int in_fdc(struct fdc_data *fdc) { int i, j = 100000; while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) if (i == NE7_RQM) return fdc_err(fdc, "ready for output in input\n"); if (j <= 0) return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); #ifdef FDC_DEBUG i = fddata_rd(fdc); TRACE1("[FDDATA->0x%x]", (unsigned char)i); return(i); #else /* !FDC_DEBUG */ return fddata_rd(fdc); #endif /* FDC_DEBUG */ } /* * fd_in: Like in_fdc, but allows you to see if it worked. */ static int fd_in(struct fdc_data *fdc, int *ptr) { int i, j = 100000; while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) if (i == NE7_RQM) return fdc_err(fdc, "ready for output in input\n"); if (j <= 0) return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); #ifdef FDC_DEBUG i = fddata_rd(fdc); TRACE1("[FDDATA->0x%x]", (unsigned char)i); *ptr = i; return 0; #else /* !FDC_DEBUG */ i = fddata_rd(fdc); if (ptr) *ptr = i; return 0; #endif /* FDC_DEBUG */ } int out_fdc(struct fdc_data *fdc, int x) { int i; /* Check that the direction bit is set */ i = 100000; while ((fdsts_rd(fdc) & NE7_DIO) && i-- > 0); if (i <= 0) return fdc_err(fdc, "direction bit not set\n"); /* Check that the floppy controller is ready for a command */ i = 100000; while ((fdsts_rd(fdc) & NE7_RQM) == 0 && i-- > 0); if (i <= 0) return fdc_err(fdc, bootverbose? "output ready timeout\n": 0); /* Send the command and return */ fddata_wr(fdc, x); TRACE1("[0x%x->FDDATA]", x); return (0); } /****************************************************************************/ /* fdopen/fdclose */ /****************************************************************************/ int Fdopen(dev_t dev, int flags, int mode, struct proc *p) { fdu_t fdu = FDUNIT(minor(dev)); int type = FDTYPE(minor(dev)); fd_p fd; fdc_p fdc; /* check bounds */ if ((fd = devclass_get_softc(fd_devclass, fdu)) == 0) return (ENXIO); fdc = fd->fdc; if ((fdc == NULL) || (fd->type == NO_TYPE)) return (ENXIO); if (type > NUMDENS) return (ENXIO); if (type == 0) type = fd->type; else { /* * For each type of basic drive, make sure we are trying * to open a type it can do, */ if (type != fd->type) { switch (fd->type) { case FD_360: return (ENXIO); case FD_720: if ( type != FD_820 && type != FD_800 && type != FD_640 ) return (ENXIO); break; case FD_1200: switch (type) { case FD_1480: type = FD_1480in5_25; break; case FD_1440: type = FD_1440in5_25; break; case FD_1232: break; case FD_820: type = FD_820in5_25; break; case FD_800: type = FD_800in5_25; break; case FD_720: type = FD_720in5_25; break; case FD_640: type = FD_640in5_25; break; case FD_360: type = FD_360in5_25; break; default: return(ENXIO); } break; case FD_1440: if ( type != FD_1720 && type != FD_1480 && type != FD_1200 && type != FD_820 && type != FD_800 && type != FD_720 && type != FD_640 ) return(ENXIO); break; } } } fd->ft = fd_types + type - 1; fd->flags |= FD_OPEN; return 0; } int fdclose(dev_t dev, int flags, int mode, struct proc *p) { fdu_t fdu = FDUNIT(minor(dev)); struct fd_data *fd; fd = devclass_get_softc(fd_devclass, fdu); fd->flags &= ~FD_OPEN; fd->options &= ~(FDOPT_NORETRY | FDOPT_NOERRLOG | FDOPT_NOERROR); return (0); } /****************************************************************************/ /* fdstrategy */ /****************************************************************************/ void fdstrategy(struct bio *bp) { unsigned nblocks, blknum, cando; int s; fdu_t fdu; fdc_p fdc; fd_p fd; size_t fdblk; fdu = FDUNIT(minor(bp->bio_dev)); fd = devclass_get_softc(fd_devclass, fdu); if (fd == 0) panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)", (u_long)major(bp->bio_dev), (u_long)minor(bp->bio_dev)); fdc = fd->fdc; if (fd->type == NO_TYPE) { bp->bio_error = ENXIO; bp->bio_flags |= BIO_ERROR; goto bad; }; fdblk = 128 << (fd->ft->secsize); if (!(bp->bio_cmd & BIO_FORMAT)) { if (bp->bio_blkno < 0) { printf( "fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n", fdu, (u_long)bp->bio_blkno, bp->bio_bcount); bp->bio_error = EINVAL; bp->bio_flags |= BIO_ERROR; goto bad; } if ((bp->bio_bcount % fdblk) != 0) { bp->bio_error = EINVAL; bp->bio_flags |= BIO_ERROR; goto bad; } } /* * Set up block calculations. */ if (bp->bio_blkno > 20000000) { /* * Reject unreasonably high block number, prevent the * multiplication below from overflowing. */ bp->bio_error = EINVAL; bp->bio_flags |= BIO_ERROR; goto bad; } blknum = (unsigned) bp->bio_blkno * DEV_BSIZE/fdblk; nblocks = fd->ft->size; bp->bio_resid = 0; if (blknum + (bp->bio_bcount / fdblk) > nblocks) { if (blknum <= nblocks) { cando = (nblocks - blknum) * fdblk; bp->bio_resid = bp->bio_bcount - cando; if (cando == 0) goto bad; /* not actually bad but EOF */ } else { bp->bio_error = EINVAL; bp->bio_flags |= BIO_ERROR; goto bad; } } bp->bio_pblkno = bp->bio_blkno; s = splbio(); bioqdisksort(&fdc->head, bp); untimeout(fd_turnoff, fd, fd->toffhandle); /* a good idea */ /* Tell devstat we are starting on the transaction */ devstat_start_transaction(&fd->device_stats); device_busy(fd->dev); fdstart(fdc); splx(s); return; bad: biodone(bp); } /***************************************************************\ * fdstart * * We have just queued something.. if the controller is not busy * * then simulate the case where it has just finished a command * * So that it (the interrupt routine) looks on the queue for more* * work to do and picks up what we just added. * * If the controller is already busy, we need do nothing, as it * * will pick up our work when the present work completes * \***************************************************************/ static void fdstart(struct fdc_data *fdc) { int s; s = splbio(); if(fdc->state == DEVIDLE) { fdc_intr(fdc); } splx(s); } static void fd_iotimeout(void *xfdc) { fdc_p fdc; int s; fdc = xfdc; TRACE1("fd%d[fd_iotimeout()]", fdc->fdu); /* * Due to IBM's brain-dead design, the FDC has a faked ready * signal, hardwired to ready == true. Thus, any command * issued if there's no diskette in the drive will _never_ * complete, and must be aborted by resetting the FDC. * Many thanks, Big Blue! * The FDC must not be reset directly, since that would * interfere with the state machine. Instead, pretend that * the command completed but was invalid. The state machine * will reset the FDC and retry once. */ s = splbio(); fdc->status[0] = NE7_ST0_IC_IV; fdc->flags &= ~FDC_STAT_VALID; fdc->state = IOTIMEDOUT; fdc_intr(fdc); splx(s); } /* just ensure it has the right spl */ static void fd_pseudointr(void *xfdc) { int s; s = splbio(); fdc_intr(xfdc); splx(s); } /***********************************************************************\ * fdintr * * keep calling the state machine until it returns a 0 * * ALWAYS called at SPLBIO * \***********************************************************************/ static void fdc_intr(void *xfdc) { fdc_p fdc = xfdc; while(fdstate(fdc)) ; } /* * magic pseudo-DMA initialization for YE FDC. Sets count and * direction */ #define SET_BCDR(fdc,wr,cnt,port) \ bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port, \ ((cnt)-1) & 0xff); \ bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port + 1, \ ((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f))); /* * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy */ static int fdcpio(fdc_p fdc, long flags, caddr_t addr, u_int count) { u_char *cptr = (u_char *)addr; if (flags == BIO_READ) { if (fdc->state != PIOREAD) { fdc->state = PIOREAD; return(0); }; SET_BCDR(fdc, 0, count, 0); bus_space_read_multi_1(fdc->portt, fdc->porth, fdc->port_off + FDC_YE_DATAPORT, cptr, count); } else { bus_space_write_multi_1(fdc->portt, fdc->porth, fdc->port_off + FDC_YE_DATAPORT, cptr, count); SET_BCDR(fdc, 0, count, 0); }; return(1); } /***********************************************************************\ * The controller state machine. * * if it returns a non zero value, it should be called again immediatly * \***********************************************************************/ static int fdstate(fdc_p fdc) { int read, format, head, i, sec = 0, sectrac, st0, cyl, st3, idf; unsigned blknum = 0, b_cylinder = 0; fdu_t fdu = fdc->fdu; fd_p fd; register struct bio *bp; struct fd_formb *finfo = NULL; size_t fdblk; bp = fdc->bp; if (bp == NULL) { bp = bioq_first(&fdc->head); if (bp != NULL) { bioq_remove(&fdc->head, bp); fdc->bp = bp; } } if (bp == NULL) { /***********************************************\ * nothing left for this controller to do * * Force into the IDLE state, * \***********************************************/ fdc->state = DEVIDLE; if (fdc->fd) { device_printf(fdc->fdc_dev, "unexpected valid fd pointer\n"); fdc->fd = (fd_p) 0; fdc->fdu = -1; } TRACE1("[fdc%d IDLE]", fdc->fdcu); return (0); } fdu = FDUNIT(minor(bp->bio_dev)); fd = devclass_get_softc(fd_devclass, fdu); fdblk = 128 << fd->ft->secsize; if (fdc->fd && (fd != fdc->fd)) device_printf(fd->dev, "confused fd pointers\n"); read = bp->bio_cmd == BIO_READ; if (read) idf = ISADMA_READ; else idf = ISADMA_WRITE; format = bp->bio_cmd & BIO_FORMAT; if (format) { finfo = (struct fd_formb *)bp->bio_data; fd->skip = (char *)&(finfo->fd_formb_cylno(0)) - (char *)finfo; } if (fdc->state == DOSEEK || fdc->state == SEEKCOMPLETE) { blknum = (unsigned) bp->bio_pblkno * DEV_BSIZE/fdblk + fd->skip/fdblk; b_cylinder = blknum / (fd->ft->sectrac * fd->ft->heads); } TRACE1("fd%d", fdu); TRACE1("[%s]", fdstates[fdc->state]); TRACE1("(0x%x)", fd->flags); untimeout(fd_turnoff, fd, fd->toffhandle); fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); switch (fdc->state) { case DEVIDLE: case FINDWORK: /* we have found new work */ fdc->retry = 0; fd->skip = 0; fdc->fd = fd; fdc->fdu = fdu; fdc->fdctl_wr(fdc, fd->ft->trans); TRACE1("[0x%x->FDCTL]", fd->ft->trans); /*******************************************************\ * If the next drive has a motor startup pending, then * * it will start up in its own good time * \*******************************************************/ if(fd->flags & FD_MOTOR_WAIT) { fdc->state = MOTORWAIT; return (0); /* come back later */ } /*******************************************************\ * Maybe if it's not starting, it SHOULD be starting * \*******************************************************/ if (!(fd->flags & FD_MOTOR)) { fdc->state = MOTORWAIT; fd_turnon(fd); return (0); } else /* at least make sure we are selected */ { set_motor(fdc, fd->fdsu, TURNON); } if (fdc->flags & FDC_NEEDS_RESET) { fdc->state = RESETCTLR; fdc->flags &= ~FDC_NEEDS_RESET; } else fdc->state = DOSEEK; break; case DOSEEK: if (b_cylinder == (unsigned)fd->track) { fdc->state = SEEKCOMPLETE; break; } if (fd_cmd(fdc, 3, NE7CMD_SEEK, fd->fdsu, b_cylinder * fd->ft->steptrac, 0)) { /* * seek command not accepted, looks like * the FDC went off to the Saints... */ fdc->retry = 6; /* try a reset */ return(retrier(fdc)); } fd->track = FD_NO_TRACK; fdc->state = SEEKWAIT; return(0); /* will return later */ case SEEKWAIT: /* allow heads to settle */ timeout(fd_pseudointr, fdc, hz / 16); fdc->state = SEEKCOMPLETE; return(0); /* will return later */ case SEEKCOMPLETE : /* SEEK DONE, START DMA */ /* Make sure seek really happened*/ if(fd->track == FD_NO_TRACK) { int descyl = b_cylinder * fd->ft->steptrac; do { /* * This might be a "ready changed" interrupt, * which cannot really happen since the * RDY pin is hardwired to + 5 volts. This * generally indicates a "bouncing" intr * line, so do one of the following: * * When running on an enhanced FDC that is * known to not go stuck after responding * with INVALID, fetch all interrupt states * until seeing either an INVALID or a * real interrupt condition. * * When running on a dumb old NE765, give * up immediately. The controller will * provide up to four dummy RC interrupt * conditions right after reset (for the * corresponding four drives), so this is * our only chance to get notice that it * was not the FDC that caused the interrupt. */ if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) return 0; if(fdc->fdct == FDC_NE765 && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) return 0; /* hope for a real intr */ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); if (0 == descyl) { int failed = 0; /* * seek to cyl 0 requested; make sure we are * really there */ if (fd_sense_drive_status(fdc, &st3)) failed = 1; if ((st3 & NE7_ST3_T0) == 0) { printf( "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n", fdu, st3, NE7_ST3BITS); failed = 1; } if (failed) { if(fdc->retry < 3) fdc->retry = 3; return (retrier(fdc)); } } if (cyl != descyl) { printf( "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", fdu, descyl, cyl, st0); if (fdc->retry < 3) fdc->retry = 3; return (retrier(fdc)); } } fd->track = b_cylinder; if (!(fdc->flags & FDC_NODMA)) isa_dmastart(idf, bp->bio_data+fd->skip, format ? bp->bio_bcount : fdblk, fdc->dmachan); sectrac = fd->ft->sectrac; sec = blknum % (sectrac * fd->ft->heads); head = sec / sectrac; sec = sec % sectrac + 1; fd->hddrv = ((head&1)<<2)+fdu; if(format || !read) { /* make sure the drive is writable */ if(fd_sense_drive_status(fdc, &st3) != 0) { /* stuck controller? */ if (!(fdc->flags & FDC_NODMA)) isa_dmadone(idf, bp->bio_data + fd->skip, format ? bp->bio_bcount : fdblk, fdc->dmachan); fdc->retry = 6; /* reset the beast */ return (retrier(fdc)); } if(st3 & NE7_ST3_WP) { /* * XXX YES! this is ugly. * in order to force the current operation * to fail, we will have to fake an FDC * error - all error handling is done * by the retrier() */ fdc->status[0] = NE7_ST0_IC_AT; fdc->status[1] = NE7_ST1_NW; fdc->status[2] = 0; fdc->status[3] = fd->track; fdc->status[4] = head; fdc->status[5] = sec; fdc->retry = 8; /* break out immediately */ fdc->state = IOTIMEDOUT; /* not really... */ return (1); } } if (format) { if (fdc->flags & FDC_NODMA) { /* * This seems to be necessary for * whatever obscure reason; if we omit * it, we end up filling the sector ID * fields of the newly formatted track * entirely with garbage, causing * `wrong cylinder' errors all over * the place when trying to read them * back. * * Umpf. */ SET_BCDR(fdc, 1, bp->bio_bcount, 0); (void)fdcpio(fdc,bp->bio_cmd, bp->bio_data+fd->skip, bp->bio_bcount); } /* formatting */ if(fd_cmd(fdc, 6, NE7CMD_FORMAT, head << 2 | fdu, finfo->fd_formb_secshift, finfo->fd_formb_nsecs, finfo->fd_formb_gaplen, finfo->fd_formb_fillbyte, 0)) { /* controller fell over */ if (!(fdc->flags & FDC_NODMA)) isa_dmadone(idf, bp->bio_data + fd->skip, format ? bp->bio_bcount : fdblk, fdc->dmachan); fdc->retry = 6; return (retrier(fdc)); } } else { if (fdc->flags & FDC_NODMA) { /* * this seems to be necessary even when * reading data */ SET_BCDR(fdc, 1, fdblk, 0); /* * perform the write pseudo-DMA before * the WRITE command is sent */ if (!read) (void)fdcpio(fdc,bp->bio_cmd, bp->bio_data+fd->skip, fdblk); } if (fd_cmd(fdc, 9, (read ? NE7CMD_READ : NE7CMD_WRITE), head << 2 | fdu, /* head & unit */ fd->track, /* track */ head, sec, /* sector + 1 */ fd->ft->secsize, /* sector size */ sectrac, /* sectors/track */ fd->ft->gap, /* gap size */ fd->ft->datalen, /* data length */ 0)) { /* the beast is sleeping again */ if (!(fdc->flags & FDC_NODMA)) isa_dmadone(idf, bp->bio_data + fd->skip, format ? bp->bio_bcount : fdblk, fdc->dmachan); fdc->retry = 6; return (retrier(fdc)); } } if (fdc->flags & FDC_NODMA) /* * if this is a read, then simply await interrupt * before performing PIO */ if (read && !fdcpio(fdc,bp->bio_cmd, bp->bio_data+fd->skip,fdblk)) { fd->tohandle = timeout(fd_iotimeout, fdc, hz); return(0); /* will return later */ }; /* * write (or format) operation will fall through and * await completion interrupt */ fdc->state = IOCOMPLETE; fd->tohandle = timeout(fd_iotimeout, fdc, hz); return (0); /* will return later */ case PIOREAD: /* * actually perform the PIO read. The IOCOMPLETE case * removes the timeout for us. */ (void)fdcpio(fdc,bp->bio_cmd,bp->bio_data+fd->skip,fdblk); fdc->state = IOCOMPLETE; /* FALLTHROUGH */ case IOCOMPLETE: /* IO DONE, post-analyze */ untimeout(fd_iotimeout, fdc, fd->tohandle); if (fd_read_status(fdc, fd->fdsu)) { if (!(fdc->flags & FDC_NODMA)) isa_dmadone(idf, bp->bio_data + fd->skip, format ? bp->bio_bcount : fdblk, fdc->dmachan); if (fdc->retry < 6) fdc->retry = 6; /* force a reset */ return (retrier(fdc)); } fdc->state = IOTIMEDOUT; /* FALLTHROUGH */ case IOTIMEDOUT: if (!(fdc->flags & FDC_NODMA)) isa_dmadone(idf, bp->bio_data + fd->skip, format ? bp->bio_bcount : fdblk, fdc->dmachan); if (fdc->status[0] & NE7_ST0_IC) { if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT && fdc->status[1] & NE7_ST1_OR) { /* * DMA overrun. Someone hogged the bus * and didn't release it in time for the * next FDC transfer. * Just restart it, don't increment retry * count. (vak) */ fdc->state = SEEKCOMPLETE; return (1); } else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV && fdc->retry < 6) fdc->retry = 6; /* force a reset */ else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT && fdc->status[2] & NE7_ST2_WC && fdc->retry < 3) fdc->retry = 3; /* force recalibrate */ return (retrier(fdc)); } /* All OK */ fd->skip += fdblk; if (!format && fd->skip < bp->bio_bcount - bp->bio_resid) { /* set up next transfer */ fdc->state = DOSEEK; } else { /* ALL DONE */ fd->skip = 0; fdc->bp = NULL; device_unbusy(fd->dev); biofinish(bp, &fd->device_stats, 0); fdc->fd = (fd_p) 0; fdc->fdu = -1; fdc->state = FINDWORK; } return (1); case RESETCTLR: fdc_reset(fdc); fdc->retry++; fdc->state = RESETCOMPLETE; return (0); case RESETCOMPLETE: /* * Discard all the results from the reset so that they * can't cause an unexpected interrupt later. */ for (i = 0; i < 4; i++) (void)fd_sense_int(fdc, &st0, &cyl); fdc->state = STARTRECAL; /* Fall through. */ case STARTRECAL: if(fd_cmd(fdc, 2, NE7CMD_RECAL, fdu, 0)) { /* arrgl */ fdc->retry = 6; return (retrier(fdc)); } fdc->state = RECALWAIT; return (0); /* will return later */ case RECALWAIT: /* allow heads to settle */ timeout(fd_pseudointr, fdc, hz / 8); fdc->state = RECALCOMPLETE; return (0); /* will return later */ case RECALCOMPLETE: do { /* * See SEEKCOMPLETE for a comment on this: */ if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) return 0; if(fdc->fdct == FDC_NE765 && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) return 0; /* hope for a real intr */ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0) { if(fdc->retry > 3) /* * a recalibrate from beyond cylinder 77 * will "fail" due to the FDC limitations; * since people used to complain much about * the failure message, try not logging * this one if it seems to be the first * time in a line */ printf("fd%d: recal failed ST0 %b cyl %d\n", fdu, st0, NE7_ST0BITS, cyl); if(fdc->retry < 3) fdc->retry = 3; return (retrier(fdc)); } fd->track = 0; /* Seek (probably) necessary */ fdc->state = DOSEEK; return (1); /* will return immediatly */ case MOTORWAIT: if(fd->flags & FD_MOTOR_WAIT) { return (0); /* time's not up yet */ } if (fdc->flags & FDC_NEEDS_RESET) { fdc->state = RESETCTLR; fdc->flags &= ~FDC_NEEDS_RESET; } else { /* * If all motors were off, then the controller was * reset, so it has lost track of the current * cylinder. Recalibrate to handle this case. * But first, discard the results of the reset. */ fdc->state = RESETCOMPLETE; } return (1); /* will return immediatly */ default: device_printf(fdc->fdc_dev, "unexpected FD int->"); if (fd_read_status(fdc, fd->fdsu) == 0) printf("FDC status :%x %x %x %x %x %x %x ", fdc->status[0], fdc->status[1], fdc->status[2], fdc->status[3], fdc->status[4], fdc->status[5], fdc->status[6] ); else printf("No status available "); if (fd_sense_int(fdc, &st0, &cyl) != 0) { printf("[controller is dead now]\n"); return (0); } printf("ST0 = %x, PCN = %x\n", st0, cyl); return (0); } /*XXX confusing: some branches return immediately, others end up here*/ return (1); /* Come back immediatly to new state */ } static int retrier(struct fdc_data *fdc) { struct bio *bp; struct fd_data *fd; int fdu; bp = fdc->bp; /* XXX shouldn't this be cached somewhere? */ fdu = FDUNIT(minor(bp->bio_dev)); fd = devclass_get_softc(fd_devclass, fdu); if (fd->options & FDOPT_NORETRY) goto fail; switch (fdc->retry) { case 0: case 1: case 2: fdc->state = SEEKCOMPLETE; break; case 3: case 4: case 5: fdc->state = STARTRECAL; break; case 6: fdc->state = RESETCTLR; break; case 7: break; default: fail: { int printerror = (fd->options & FDOPT_NOERRLOG) == 0; if (printerror) diskerr(bp, "hard error", fdc->fd->skip / DEV_BSIZE, (struct disklabel *)NULL); if (printerror) { if (fdc->flags & FDC_STAT_VALID) { printf( " (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n", fdc->status[0], NE7_ST0BITS, fdc->status[1], NE7_ST1BITS, fdc->status[2], NE7_ST2BITS, fdc->status[3], fdc->status[4], fdc->status[5]); } else printf(" (No status)\n"); } } if ((fd->options & FDOPT_NOERROR) == 0) { bp->bio_flags |= BIO_ERROR; bp->bio_error = EIO; bp->bio_resid += bp->bio_bcount - fdc->fd->skip; } fdc->bp = NULL; fdc->fd->skip = 0; device_unbusy(fd->dev); biofinish(bp, &fdc->fd->device_stats, 0); fdc->state = FINDWORK; fdc->flags |= FDC_NEEDS_RESET; fdc->fd = (fd_p) 0; fdc->fdu = -1; return (1); } fdc->retry++; return (1); } static void fdbiodone(struct bio *bp) { wakeup(bp); } static int fdformat(dev, finfo, p) dev_t dev; struct fd_formb *finfo; struct proc *p; { fdu_t fdu; fd_p fd; struct bio *bp; int rv = 0, s; size_t fdblk; fdu = FDUNIT(minor(dev)); fd = devclass_get_softc(fd_devclass, fdu); fdblk = 128 << fd->ft->secsize; /* set up a buffer header for fdstrategy() */ bp = (struct bio *)malloc(sizeof(struct bio), M_TEMP, M_NOWAIT); if(bp == 0) return ENOMEM; /* * keep the process from being swapped */ PHOLD(p); bzero((void *)bp, sizeof(*bp)); bp->bio_cmd = BIO_FORMAT; /* * calculate a fake blkno, so fdstrategy() would initiate a * seek to the requested cylinder */ bp->bio_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) + finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE; bp->bio_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; bp->bio_data = (caddr_t)finfo; /* now do the format */ bp->bio_dev = dev; bp->bio_done = fdbiodone; fdstrategy(bp); /* ...and wait for it to complete */ s = splbio(); while(!(bp->bio_flags & BIO_DONE)) { rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz); if (rv == EWOULDBLOCK) break; } splx(s); if (rv == EWOULDBLOCK) { /* timed out */ rv = EIO; device_unbusy(fd->dev); } if (bp->bio_flags & BIO_ERROR) rv = bp->bio_error; /* * allow the process to be swapped */ PRELE(p); free(bp, M_TEMP); return rv; } /* * TODO: don't allocate buffer on stack. */ static int fdioctl(dev, cmd, addr, flag, p) dev_t dev; u_long cmd; caddr_t addr; int flag; struct proc *p; { fdu_t fdu = FDUNIT(minor(dev)); fd_p fd = devclass_get_softc(fd_devclass, fdu); size_t fdblk; struct fd_type *fdt; struct disklabel *dl; struct fdc_status *fsp; char buffer[DEV_BSIZE]; int error = 0; fdblk = 128 << fd->ft->secsize; switch (cmd) { case DIOCGDINFO: bzero(buffer, sizeof (buffer)); dl = (struct disklabel *)buffer; dl->d_secsize = fdblk; fdt = fd->ft; dl->d_secpercyl = fdt->size / fdt->tracks; dl->d_type = DTYPE_FLOPPY; if (readdisklabel(dkmodpart(dev, RAW_PART), dl) == NULL) error = 0; else error = EINVAL; *(struct disklabel *)addr = *dl; break; case DIOCSDINFO: if ((flag & FWRITE) == 0) error = EBADF; break; case DIOCWLABEL: if ((flag & FWRITE) == 0) error = EBADF; break; case DIOCWDINFO: if ((flag & FWRITE) == 0) { error = EBADF; break; } dl = (struct disklabel *)addr; if ((error = setdisklabel((struct disklabel *)buffer, dl, (u_long)0)) != 0) break; error = writedisklabel(dev, (struct disklabel *)buffer); break; case FD_FORM: if ((flag & FWRITE) == 0) error = EBADF; /* must be opened for writing */ else if (((struct fd_formb *)addr)->format_version != FD_FORMAT_VERSION) error = EINVAL; /* wrong version of formatting prog */ else error = fdformat(dev, (struct fd_formb *)addr, p); break; case FD_GTYPE: /* get drive type */ *(struct fd_type *)addr = *fd->ft; break; case FD_STYPE: /* set drive type */ /* this is considered harmful; only allow for superuser */ if (suser(p) != 0) return EPERM; *fd->ft = *(struct fd_type *)addr; break; case FD_GOPTS: /* get drive options */ *(int *)addr = fd->options; break; case FD_SOPTS: /* set drive options */ fd->options = *(int *)addr; break; case FD_CLRERR: if (suser(p) != 0) return EPERM; fd->fdc->fdc_errs = 0; break; case FD_GSTAT: fsp = (struct fdc_status *)addr; if ((fd->fdc->flags & FDC_STAT_VALID) == 0) return EINVAL; memcpy(fsp->status, fd->fdc->status, 7 * sizeof(u_int)); break; default: error = ENOTTY; break; } return (error); } /* * Hello emacs, these are the * Local Variables: * c-indent-level: 8 * c-continued-statement-offset: 8 * c-continued-brace-offset: 0 * c-brace-offset: -8 * c-brace-imaginary-offset: 0 * c-argdecl-indent: 8 * c-label-offset: -8 * c++-hanging-braces: 1 * c++-access-specifier-offset: -8 * c++-empty-arglist-indent: 8 * c++-friend-offset: 0 * End: */ Index: head/sys/i386/conf/GENERIC =================================================================== --- head/sys/i386/conf/GENERIC (revision 78134) +++ head/sys/i386/conf/GENERIC (revision 78135) @@ -1,236 +1,236 @@ # # GENERIC -- Generic kernel configuration file for FreeBSD/i386 # # For more information on this file, please read the handbook section on # Kernel Configuration Files: # # http://www.FreeBSD.org/handbook/kernelconfig-config.html # # The handbook is also available locally in /usr/share/doc/handbook # if you've installed the doc distribution, otherwise always see the # FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the # latest information. # # An exhaustive list of options and more detailed explanations of the # device lines is also present in the NOTES configuration file. If you are # in doubt as to the purpose or necessity of a line, check first in NOTES. # # $FreeBSD$ machine i386 cpu I486_CPU cpu I586_CPU cpu I686_CPU ident GENERIC maxusers 32 #To statically compile in device wiring instead of /boot/device.hints #hints "GENERIC.hints" #Default places to look for devices. makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols options MATH_EMULATE #Support for x87 emulation options INET #InterNETworking options INET6 #IPv6 communications protocols options FFS #Berkeley Fast Filesystem options SOFTUPDATES #Enable FFS soft updates support options MD_ROOT #MD is a potential root device options NFS #Network Filesystem options NFS_ROOT #NFS usable as root device, NFS required options MSDOSFS #MSDOS Filesystem options CD9660 #ISO 9660 Filesystem options PROCFS #Process filesystem options COMPAT_43 #Compatible with BSD 4.3 [KEEP THIS!] options SCSI_DELAY=15000 #Delay (in ms) before probing SCSI options UCONSOLE #Allow users to grab the console -options USERCONFIG #boot -c editor -options VISUAL_USERCONFIG #visual boot -c editor +#options USERCONFIG #boot -c editor +#options VISUAL_USERCONFIG #visual boot -c editor options KTRACE #ktrace(1) support options SYSVSHM #SYSV-style shared memory options SYSVMSG #SYSV-style message queues options SYSVSEM #SYSV-style semaphores options P1003_1B #Posix P1003_1B real-time extensions options _KPOSIX_PRIORITY_SCHEDULING options KBD_INSTALL_CDEV # install a CDEV entry in /dev # Debugging for use in -current options DDB options INVARIANTS options INVARIANT_SUPPORT options WITNESS # To make an SMP kernel, the next two are needed #options SMP # Symmetric MultiProcessor Kernel #options APIC_IO # Symmetric (APIC) I/O device isa device eisa device pci # Floppy drives device fdc # ATA and ATAPI devices device ata device atadisk # ATA disk drives device atapicd # ATAPI CDROM drives device atapifd # ATAPI floppy drives device atapist # ATAPI tape drives options ATA_STATIC_ID #Static device numbering # SCSI Controllers device ahb # EISA AHA1742 family device ahc # AHA2940 and onboard AIC7xxx devices device amd # AMD 53C974 (Tekram DC-390(T)) device isp # Qlogic family #device ncr # NCR/Symbios Logic device sym # NCR/Symbios Logic (newer chipsets + those of `ncr') device adv # Advansys SCSI adapters device adw # Advansys wide SCSI adapters device aha # Adaptec 154x SCSI adapters device aic # Adaptec 15[012]x SCSI adapters, AIC-6[23]60. device bt # Buslogic/Mylex MultiMaster SCSI adapters device ncv # NCR 53C500 device nsp # Workbit Ninja SCSI-3 device stg # TMC 18C30/18C50 # RAID controllers interfaced to the SCSI subsystem device asr # DPT SmartRAID V, VI and Adaptec SCSI RAID device dpt # DPT Smartcache III, IV - See NOTES for options! device mly # Mylex AcceleRAID/eXtremeRAID # SCSI peripherals device scbus # SCSI bus (required) device da # Direct Access (disks) device sa # Sequential Access (tape etc) device cd # CD device pass # Passthrough device (direct SCSI access) # RAID controllers device aac # Adaptec FSA RAID device amr # AMI MegaRAID device ida # Compaq Smart RAID device mlx # Mylex DAC960 family device twe # 3ware ATA RAID # atkbdc0 controls both the keyboard and the PS/2 mouse device atkbdc 1 # At keyboard controller device atkbd # at keyboard device psm # psm mouse device vga # VGA screen # splash screen/screen saver device splash # syscons is the default console driver, resembling an SCO console device sc 1 # Enable this for the pcvt (VT220 compatible) console driver #device vt #options XSERVER # support for X server on a vt console #options FAT_CURSOR # start with block cursor # If you have a ThinkPAD, uncomment this along with the rest of the PCVT lines #options PCVT_SCANSET=2 # IBM keyboards are non-std # Floating point support - do not disable. device npx # Power management support (see NOTES for more options) device apm # Add suspend/resume support for the i8254. device pmtimer # PCCARD (PCMCIA) support device card # pccard bus device pcic # PCMCIA bridge # Serial (COM) ports device sio # 8250, 16[45]50 based serial ports # Parallel port device ppc device ppbus # Parallel port bus (required) device lpt # Printer device plip # TCP/IP over parallel device ppi # Parallel port interface device #device vpo # Requires scbus and da # PCI Ethernet NICs. device de # DEC/Intel DC21x4x (``Tulip'') device vx # 3Com 3c590, 3c595 (``Vortex'') # PCI Ethernet NICs that use the common MII bus controller code. # NOTE: Be sure to keep the 'device miibus' line in order to use these NICs! device miibus # MII bus support device dc # DEC/Intel 21143 and various workalikes device fxp # Intel EtherExpress PRO/100B (82557, 82558) device pcn # AMD Am79C79x PCI 10/100 NICs device rl # RealTek 8129/8139 device sf # Adaptec AIC-6915 (``Starfire'') device sis # Silicon Integrated Systems SiS 900/SiS 7016 device ste # Sundance ST201 (D-Link DFE-550TX) device tl # Texas Instruments ThunderLAN device tx # SMC EtherPower II (83c170 ``EPIC'') device vr # VIA Rhine, Rhine II device wb # Winbond W89C840F device wx # Intel Gigabit Ethernet Card (``Wiseman'') device xl # 3Com 3c90x (``Boomerang'', ``Cyclone'') # ISA Ethernet NICs. pccard nics included. device cs # Crystal Semiconductor CS89x0 NIC # 'device ed' requires 'device miibus' device ed # NE[12]000, SMC Ultra, 3c503, DS8390 cards device ex # Intel EtherExpress Pro/10 and Pro/10+ device ep # Etherlink III based cards device fe # Fujitsu MB8696x based cards device sn # SMC's 9000 series of ethernet chips device xe # Xircom pccard ethernet # The probe order of these is presently determined by i386/isa/isa_compat.c. #device ie #device le device lnc # Wireless NIC cards device an # Aironet 4500/4800 802.11 wireless NICs. device awi # BayStack 660 and others device wi # WaveLAN/IEEE 802.11 wireless NICs. #device wl # Older non 802.11 Wavelan wireless NIC. # Pseudo devices - the number indicates how many units to allocate. device random # Entropy device device loop # Network loopback device ether # Ethernet support device sl # Kernel SLIP device ppp 1 # Kernel PPP device tun # Packet tunnel. device pty # Pseudo-ttys (telnet etc) device md # Memory "disks" device gif 4 # IPv6 and IPv4 tunneling device faith 1 # IPv6-to-IPv4 relaying (translation) # The `bpf' device enables the Berkeley Packet Filter. # Be aware of the administrative consequences of enabling this! device bpf # Berkeley packet filter # USB support device uhci # UHCI PCI->USB interface device ohci # OHCI PCI->USB interface device usb # USB Bus (required) #device udbp # USB Double Bulk Pipe devices device ugen # Generic device uhid # "Human Interface Devices" device ukbd # Keyboard device ulpt # Printer device umass # Disks/Mass storage - Requires scbus and da device ums # Mouse device urio # Diamond Rio 500 MP3 player device uscanner # Scanners # USB Ethernet, requires mii device aue # ADMtek USB ethernet device cue # CATC USB ethernet device kue # Kawasaki LSI USB ethernet Index: head/sys/i386/conf/NOTES =================================================================== --- head/sys/i386/conf/NOTES (revision 78134) +++ head/sys/i386/conf/NOTES (revision 78135) @@ -1,2873 +1,2873 @@ # # NOTES -- Lines that can be cut/pasted into kernel and hints configs. # # Lines that begin with 'device', 'options', 'machine', 'ident', 'maxusers', # 'makeoptions', 'hints' etc go into the kernel configuration that you # run config(8) with. # # Lines that begin with 'hints.' are NOT for config(8), they go into your # hints file. See /boot/device.hints and/or the 'hints' config(8) directive. # # Please use ``make LINT'' to create an old-style LINT file if you want to # do kernel test-builds. # # $FreeBSD$ # # # This directive is mandatory; it defines the architecture to be # configured for; in this case, the 386 family based IBM-PC and # compatibles. # machine i386 # # This is the ``identification'' of the kernel. Usually this should # be the same as the name of your kernel. # ident LINT # # The `maxusers' parameter controls the static sizing of a number of # internal system tables by a complicated formula defined in param.c. # maxusers 10 # # We want LINT to cover profiling as well profile 2 # # The `makeoptions' parameter allows variables to be passed to the # generated Makefile in the build area. # # CONF_CFLAGS gives some extra compiler flags that are added to ${CFLAGS} # after most other flags. Here we use it to inhibit use of non-optimal # gcc builtin functions (e.g., memcmp). # # DEBUG happens to be magic. # The following is equivalent to 'config -g KERNELNAME' and creates # 'kernel.debug' compiled with -g debugging as well as a normal # 'kernel'. Use 'make install.debug' to install the debug kernel # but that isn't normally necessary as the debug symbols are not loaded # by the kernel and are not useful there anyway. # # KERNEL can be overridden so that you can change the default name of your # kernel. # makeoptions CONF_CFLAGS=-fno-builtin #Don't allow use of memcmp, etc. #makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols #makeoptions KERNEL=foo #Build kernel "foo" and install "/foo" # # Certain applications can grow to be larger than the 512M limit # that FreeBSD initially imposes. Below are some options to # allow that limit to grow to 1GB, and can be increased further # with changing the parameters. MAXDSIZ is the maximum that the # limit can be set to, and the DFLDSIZ is the default value for # the limit. You might want to set the default lower than the # max, and explicitly set the maximum with a shell command for processes # that regularly exceed the limit like INND. # options MAXDSIZ="(1024UL*1024*1024)" options DFLDSIZ="(1024UL*1024*1024)" # # BLKDEV_IOSIZE sets the default block size used in user block # device I/O. Note that this value will be overriden by the label # when specifying a block device from a label with a non-0 # partition blocksize. The default is PAGE_SIZE. # options BLKDEV_IOSIZE=8192 # Options for the VM subsystem options PQ_CACHESIZE=512 # color for 512k/16k cache # Deprecated options supported for backwards compatibility #options PQ_NOOPT # No coloring #options PQ_LARGECACHE # color for 512k/16k cache #options PQ_HUGECACHE # color for 1024k/16k cache #options PQ_MEDIUMCACHE # color for 256k/16k cache #options PQ_NORMALCACHE # color for 64k/16k cache # This allows you to actually store this configuration file into # the kernel binary itself, where it may be later read by saying: # strings -n 3 /boot/kernel/kernel | sed -n 's/^___//p' > MYKERNEL # options INCLUDE_CONFIG_FILE # Include this file in kernel # # The root device and filesystem type can be compiled in; # this provides a fallback option if the root device cannot # be correctly guesst by the bootstrap code, or an override if # the RB_DFLTROOT flag (-r) is specified when booting the kernel. # options ROOTDEVNAME=\"ufs:da0s2e\" ##################################################################### # SMP OPTIONS: # # SMP enables building of a Symmetric MultiProcessor Kernel. # APIC_IO enables the use of the IO APIC for Symmetric I/O. # # Notes: # # An SMP kernel will ONLY run on an Intel MP spec. qualified motherboard. # # Be sure to disable 'cpu I386_CPU' && 'cpu I486_CPU' for SMP kernels. # # Check the 'Rogue SMP hardware' section to see if additional options # are required by your hardware. # # Mandatory: options SMP # Symmetric MultiProcessor Kernel options APIC_IO # Symmetric (APIC) I/O # # Rogue SMP hardware: # # Bridged PCI cards: # # The MP tables of most of the current generation MP motherboards # do NOT properly support bridged PCI cards. To use one of these # cards you should refer to ??? # SMP Debugging Options: # # MUTEX_DEBUG enables various extra assertions in the mutex code. # WITNESS enables the mutex witness code which detects deadlocks and cycles # during locking operations. # WITNESS_DDB causes the witness code to drop into the kernel debugger if # a lock heirarchy violation occurs or if locks are held when going to # sleep. # WITNESS_SKIPSPIN disables the witness checks on spin mutexes. options MUTEX_DEBUG options WITNESS options WITNESS_DDB options WITNESS_SKIPSPIN ##################################################################### # CPU OPTIONS # # You must specify at least one CPU (the one you intend to run on); # deleting the specification for CPUs you don't need to use may make # parts of the system run faster. # I386_CPU is mutually exclusive with the other CPU types. # #cpu I386_CPU cpu I486_CPU cpu I586_CPU # aka Pentium(tm) cpu I686_CPU # aka Pentium Pro(tm) # # Options for CPU features. # # CPU_BLUELIGHTNING_FPU_OP_CACHE enables FPU operand cache on IBM # BlueLightning CPU. It works only with Cyrix FPU, and this option # should not be used with Intel FPU. # # CPU_BLUELIGHTNING_3X enables triple-clock mode on IBM Blue Lightning # CPU if CPU supports it. The default is double-clock mode on # BlueLightning CPU box. # # CPU_BTB_EN enables branch target buffer on Cyrix 5x86 (NOTE 1). # # CPU_DIRECT_MAPPED_CACHE sets L1 cache of Cyrix 486DLC CPU in direct # mapped mode. Default is 2-way set associative mode. # # CPU_CYRIX_NO_LOCK enables weak locking for the entire address space # of Cyrix 6x86 and 6x86MX CPUs by setting the NO_LOCK bit of CCR1. # Otherwise, the NO_LOCK bit of CCR1 is cleared. (NOTE 3) # # CPU_DISABLE_5X86_LSSER disables load store serialize (i.e. enables # reorder). This option should not be used if you use memory mapped # I/O device(s). # # CPU_FASTER_5X86_FPU enables faster FPU exception handler. # # CPU_I486_ON_386 enables CPU cache on i486 based CPU upgrade products # for i386 machines. # # CPU_IORT defines I/O clock delay time (NOTE 1). Default values of # I/O clock delay time on Cyrix 5x86 and 6x86 are 0 and 7,respectively # (no clock delay). # # CPU_L2_LATENCY specifed the L2 cache latency value. This option is used # only when CPU_PPRO2CELERON is defined and Mendocino Celeron is detected. # The default value is 5. # # CPU_LOOP_EN prevents flushing the prefetch buffer if the destination # of a jump is already present in the prefetch buffer on Cyrix 5x86(NOTE # 1). # # CPU_PPRO2CELERON enables L2 cache of Mendocino Celeron CPUs. This option # is useful when you use Socket 8 to Socket 370 converter, because most Pentium # Pro BIOSs do not enable L2 cache of Mendocino Celeron CPUs. # # CPU_RSTK_EN enables return stack on Cyrix 5x86 (NOTE 1). # # CPU_SUSP_HLT enables suspend on HALT. If this option is set, CPU # enters suspend mode following execution of HALT instruction. # # CPU_WT_ALLOC enables write allocation on Cyrix 6x86/6x86MX and AMD # K5/K6/K6-2 cpus. # # CYRIX_CACHE_WORKS enables CPU cache on Cyrix 486 CPUs with cache # flush at hold state. # # CYRIX_CACHE_REALLY_WORKS enables (1) CPU cache on Cyrix 486 CPUs # without cache flush at hold state, and (2) write-back CPU cache on # Cyrix 6x86 whose revision < 2.7 (NOTE 2). # # NO_F00F_HACK disables the hack that prevents Pentiums (and ONLY # Pentiums) from locking up when a LOCK CMPXCHG8B instruction is # executed. This option is only needed if I586_CPU is also defined, # and should be included for any non-Pentium CPU that defines it. # # NO_MEMORY_HOLE is an optimisation for systems with AMD K6 processors # which indicates that the 15-16MB range is *definitely* not being # occupied by an ISA memory hole. # # NOTE 1: The options, CPU_BTB_EN, CPU_LOOP_EN, CPU_IORT, # CPU_LOOP_EN and CPU_RSTK_EN should not be used because of CPU bugs. # These options may crash your system. # # NOTE 2: If CYRIX_CACHE_REALLY_WORKS is not set, CPU cache is enabled # in write-through mode when revision < 2.7. If revision of Cyrix # 6x86 >= 2.7, CPU cache is always enabled in write-back mode. # # NOTE 3: This option may cause failures for software that requires # locked cycles in order to operate correctly. # options CPU_BLUELIGHTNING_FPU_OP_CACHE options CPU_BLUELIGHTNING_3X options CPU_BTB_EN options CPU_DIRECT_MAPPED_CACHE options CPU_DISABLE_5X86_LSSER options CPU_FASTER_5X86_FPU options CPU_I486_ON_386 options CPU_IORT options CPU_L2_LATENCY=5 options CPU_LOOP_EN options CPU_PPRO2CELERON options CPU_RSTK_EN options CPU_SUSP_HLT options CPU_WT_ALLOC options CYRIX_CACHE_WORKS options CYRIX_CACHE_REALLY_WORKS #options NO_F00F_HACK # # A math emulator is mandatory if you wish to run on hardware which # does not have a floating-point processor. Pick either the original, # bogus (but freely-distributable) math emulator, or a much more # fully-featured but GPL-licensed emulator taken from Linux. # options MATH_EMULATE #Support for x87 emulation # Don't enable both of these in a real config. options GPL_MATH_EMULATE #Support for x87 emulation via #new math emulator ##################################################################### # COMPATIBILITY OPTIONS # # Implement system calls compatible with 4.3BSD and older versions of # FreeBSD. You probably do NOT want to remove this as much current code # still relies on the 4.3 emulation. # options COMPAT_43 # # These three options provide support for System V Interface # Definition-style interprocess communication, in the form of shared # memory, semaphores, and message queues, respectively. # options SYSVSHM options SYSVSEM options SYSVMSG ##################################################################### # DEBUGGING OPTIONS # # Enable the kernel debugger. # options DDB # # Don't drop into DDB for a panic. Intended for unattended operation # where you may want to drop to DDB from the console, but still want # the machine to recover from a panic # options DDB_UNATTENDED # # If using GDB remote mode to debug the kernel, there's a non-standard # extension to the remote protocol that can be used to use the serial # port as both the debugging port and the system console. It's non- # standard and you're on your own if you enable it. See also the # "remotechat" variables in the FreeBSD specific version of gdb. # options GDB_REMOTE_CHAT # # KTRACE enables the system-call tracing facility ktrace(2). # options KTRACE #kernel tracing # # KTR is a kernel tracing mechanism imported from BSD/OS. Currently it # has no userland interface aside from a few sysctl's. It is enabled with # the KTR option. The KTR_EXTEND option causes trace events to be generated # as a string from snprintf rather than as a string and up to 5 argument # pointers. KTR_ENTRIES defines the number of entries in the circular trace # buffer. KTR_COMPILE defines the mask of events to compile into the kernel # as defined by the KTR_* constants in . KTR_MASK defines the # initial value of the ktr_mask variable which determines at runtime what # events to trace. KTR_CPUMASK determines which CPU's log events, with # bit X corresponding to cpu X. KTR_VERBOSE enables dumping of KTR events # to the console by default. This functionality can be toggled via the # debug.ktr_verbose sysctl and defaults to off if KTR_VERBOSE is not defined. # options KTR options KTR_EXTEND options KTR_ENTRIES=1024 options KTR_COMPILE="(KTR_INTR|KTR_PROC)" options KTR_MASK=KTR_INTR options KTR_CPUMASK=0x3 options KTR_VERBOSE # # The INVARIANTS option is used in a number of source files to enable # extra sanity checking of internal structures. This support is not # enabled by default because of the extra time it would take to check # for these conditions, which can only occur as a result of # programming errors. # options INVARIANTS # # The INVARIANT_SUPPORT option makes us compile in support for # verifying some of the internal structures. It is a prerequisite for # 'INVARIANTS', as enabling 'INVARIANTS' will make these functions be # called. The intent is that you can set 'INVARIANTS' for single # source files (by changing the source file or specifying it on the # command line) if you have 'INVARIANT_SUPPORT' enabled. Also, if you # wish to build a kernel module with 'INVARIANTS', then adding # 'INVARIANT_SUPPORT' to your kernel will provide all the necessary # infrastructure without the added overhead. # options INVARIANT_SUPPORT # # The DIAGNOSTIC option is used to enable extra debugging information # from some parts of the kernel. As this makes everything more noisy, # it is disabled by default. # options DIAGNOSTIC # # REGRESSION causes optional kernel interfaces necessary only for regression # testing to be enabled. These interfaces may consitute security risks # when enabled, as they permit processes to easily modify aspects of the # run-time environment to reproduce unlikely or unusual (possibly normally # impossible) scenarios. # options REGRESSION # # PERFMON causes the driver for Pentium/Pentium Pro performance counters # to be compiled. See perfmon(4) for more information. # options PERFMON # # This option let some drivers co-exist that can't co-exist in a running # system. This is used to be able to compile all kernel code in one go for # quality assurance purposes (like this file, which the option takes it name # from.) # options COMPILING_LINT # XXX - this doesn't belong here. # Allow ordinary users to take the console - this is useful for X. options UCONSOLE # XXX - this doesn't belong here either -options USERCONFIG #boot -c editor -options INTRO_USERCONFIG #imply -c and show intro screen -options VISUAL_USERCONFIG #visual boot -c editor +#options USERCONFIG #boot -c editor +#options INTRO_USERCONFIG #imply -c and show intro screen +#options VISUAL_USERCONFIG #visual boot -c editor ##################################################################### # NETWORKING OPTIONS # # Protocol families: # Only the INET (Internet) family is officially supported in FreeBSD. # Source code for the NS (Xerox Network Service) is provided for amusement # value. # options INET #Internet communications protocols options INET6 #IPv6 communications protocols options IPSEC #IP security options IPSEC_ESP #IP security (crypto; define w/ IPSEC) options IPSEC_DEBUG #debug for IP security options IPX #IPX/SPX communications protocols options IPXIP #IPX in IP encapsulation (not available) options IPTUNNEL #IP in IPX encapsulation (not available) options NCP #NetWare Core protocol options NETATALK #Appletalk communications protocols options NETATALKDEBUG #Appletalk debugging # These are currently broken but are shipped due to interest. #options NS #Xerox NS protocols #options NSIP #XNS over IP # mchain library. It can be either loaded as KLD or compiled into kernel options LIBMCHAIN # netgraph(4). Enable the base netgraph code with the NETGRAPH option. # Individual node types can be enabled with the corresponding option # listed below; however, this is not strictly necessary as netgraph # will automatically load the corresponding KLD module if the node type # is not already compiled into the kernel. Each type below has a # corresponding man page, e.g., ng_async(8). options NETGRAPH #netgraph(4) system options NETGRAPH_ASYNC options NETGRAPH_BPF options NETGRAPH_CISCO options NETGRAPH_ECHO options NETGRAPH_ETHER options NETGRAPH_FRAME_RELAY options NETGRAPH_HOLE options NETGRAPH_IFACE options NETGRAPH_KSOCKET options NETGRAPH_LMI # MPPC compression requires proprietary files (not included) #options NETGRAPH_MPPC_COMPRESSION options NETGRAPH_MPPC_ENCRYPTION options NETGRAPH_ONE2MANY options NETGRAPH_PPP options NETGRAPH_PPPOE options NETGRAPH_PPTPGRE options NETGRAPH_RFC1490 options NETGRAPH_SOCKET options NETGRAPH_TEE options NETGRAPH_TTY options NETGRAPH_UI options NETGRAPH_VJC device mn # Munich32x/Falc54 Nx64kbit/sec cards. device lmc # tulip based LanMedia WAN cards device musycc # LMC/SBE LMC1504 quad T1/E1 # # Network interfaces: # The `loop' device is MANDATORY when networking is enabled. # The `ether' device provides generic code to handle # Ethernets; it is MANDATORY when a Ethernet device driver is # configured or token-ring is enabled. # The 'fddi' device provides generic code to support FDDI. # The `sppp' device serves a similar role for certain types # of synchronous PPP links (like `cx', `ar'). # The `sl' device implements the Serial Line IP (SLIP) service. # The `ppp' device implements the Point-to-Point Protocol. # The `bpf' device enables the Berkeley Packet Filter. Be # aware of the legal and administrative consequences of enabling this # option. The number of devices determines the maximum number of # simultaneous BPF clients programs runnable. # The `disc' device implements a minimal network interface, # which throws away all packets sent and never receives any. It is # included for testing purposes. This shows up as the 'ds' interface. # The `tap' device is a pty-like virtual Ethernet interface # The `tun' device implements (user-)ppp and nos-tun # The `gif' device implements IPv6 over IP4 tunneling, # IPv4 over IPv6 tunneling, IPv4 over IPv4 tunneling and # IPv6 over IPv6 tunneling. # The XBONEHACK option allows the same pair of addresses to be configured on # multiple gif interfaces. # The `faith' device captures packets sent to it and diverts them # to the IPv4/IPv6 translation daemon. # The `stf' device implements 6to4 encapsulation. # The `ef' device provides support for multiple ethernet frame types # specified via ETHER_* options. See ef(4) for details. # # The PPP_BSDCOMP option enables support for compress(1) style entire # packet compression, the PPP_DEFLATE is for zlib/gzip style compression. # PPP_FILTER enables code for filtering the ppp data stream and selecting # events for resetting the demand dial activity timer - requires bpf. # See pppd(8) for more details. # device ether #Generic Ethernet device vlan 1 #VLAN support device token #Generic TokenRing device fddi #Generic FDDI device sppp #Generic Synchronous PPP device loop 1 #Network loopback device device bpf #Berkeley packet filter device disc #Discard device (ds0, ds1, etc) device tap #Virtual Ethernet driver device tun #Tunnel driver (ppp(8), nos-tun(8)) device sl #Serial Line IP device ppp 2 #Point-to-point protocol options PPP_BSDCOMP #PPP BSD-compress support options PPP_DEFLATE #PPP zlib/deflate/gzip support options PPP_FILTER #enable bpf filtering (needs bpf) device ef # Multiple ethernet frames support options ETHER_II # enable Ethernet_II frame options ETHER_8023 # enable Ethernet_802.3 (Novell) frame options ETHER_8022 # enable Ethernet_802.2 frame options ETHER_SNAP # enable Ethernet_802.2/SNAP frame # for IPv6 device gif 4 #IPv6 and IPv4 tunneling options XBONEHACK device faith 1 #for IPv6 and IPv4 translation device stf #6to4 IPv6 over IPv4 encapsulation # # Internet family options: # # MROUTING enables the kernel multicast packet forwarder, which works # with mrouted(8). # # IPFIREWALL enables support for IP firewall construction, in # conjunction with the `ipfw' program. IPFIREWALL_VERBOSE sends # logged packets to the system logger. IPFIREWALL_VERBOSE_LIMIT # limits the number of times a matching entry can be logged. # # WARNING: IPFIREWALL defaults to a policy of "deny ip from any to any" # and if you do not add other rules during startup to allow access, # YOU WILL LOCK YOURSELF OUT. It is suggested that you set firewall_type=open # in /etc/rc.conf when first enabling this feature, then refining the # firewall rules in /etc/rc.firewall after you've tested that the new kernel # feature works properly. # # IPFIREWALL_DEFAULT_TO_ACCEPT causes the default rule (at boot) to # allow everything. Use with care, if a cracker can crash your # firewall machine, they can get to your protected machines. However, # if you are using it as an as-needed filter for specific problems as # they arise, then this may be for you. Changing the default to 'allow' # means that you won't get stuck if the kernel and /sbin/ipfw binary get # out of sync. # # IPDIVERT enables the divert IP sockets, used by ``ipfw divert'' # # IPSTEALTH enables code to support stealth forwarding (i.e., forwarding # packets without touching the ttl). This can be useful to hide firewalls # from traceroute and similar tools. # # TCPDEBUG is undocumented. # options MROUTING # Multicast routing options IPFIREWALL #firewall options IPFIREWALL_VERBOSE #print information about # dropped packets options IPFIREWALL_FORWARD #enable transparent proxy support options IPFIREWALL_VERBOSE_LIMIT=100 #limit verbosity options IPFIREWALL_DEFAULT_TO_ACCEPT #allow everything by default options IPV6FIREWALL #firewall for IPv6 options IPV6FIREWALL_VERBOSE options IPV6FIREWALL_VERBOSE_LIMIT=100 options IPV6FIREWALL_DEFAULT_TO_ACCEPT options IPDIVERT #divert sockets options IPFILTER #ipfilter support options IPFILTER_LOG #ipfilter logging options IPFILTER_DEFAULT_BLOCK #block all packets by default options IPSTEALTH #support for stealth forwarding options TCPDEBUG # RANDOM_IP_ID causes the ID field in IP packets to be randomized # instead of incremented by 1 with each packet generated. This # option closes a minor information leak which allows remote # observers to determine the rate of packet generation on the # machine by watching the counter. options RANDOM_IP_ID # Statically Link in accept filters options ACCEPT_FILTER_DATA options ACCEPT_FILTER_HTTP # TCP_DROP_SYNFIN adds support for ignoring TCP packets with SYN+FIN. This # prevents nmap et al. from identifying the TCP/IP stack, but breaks support # for RFC1644 extensions and is not recommended for web servers. # options TCP_DROP_SYNFIN #drop TCP packets with SYN+FIN # DUMMYNET enables the "dummynet" bandwidth limiter. You need # IPFIREWALL as well. See the dummynet(4) manpage for more info. # BRIDGE enables bridging between ethernet cards -- see bridge(4). # You can use IPFIREWALL and dummynet together with bridging. options DUMMYNET options BRIDGE # # ATM (HARP version) options # # ATM_CORE includes the base ATM functionality code. This must be included # for ATM support. # # ATM_IP includes support for running IP over ATM. # # At least one (and usually only one) of the following signalling managers # must be included (note that all signalling managers include PVC support): # ATM_SIGPVC includes support for the PVC-only signalling manager `sigpvc'. # ATM_SPANS includes support for the `spans' signalling manager, which runs # the FORE Systems's proprietary SPANS signalling protocol. # ATM_UNI includes support for the `uni30' and `uni31' signalling managers, # which run the ATM Forum UNI 3.x signalling protocols. # # The `hea' driver provides support for the Efficient Networks, Inc. # ENI-155p ATM PCI Adapter. # # The `hfa' driver provides support for the FORE Systems, Inc. # PCA-200E ATM PCI Adapter. # options ATM_CORE #core ATM protocol family options ATM_IP #IP over ATM support options ATM_SIGPVC #SIGPVC signalling manager options ATM_SPANS #SPANS signalling manager options ATM_UNI #UNI signalling manager device hea #Efficient ENI-155p ATM PCI device hfa #FORE PCA-200E ATM PCI ##################################################################### # FILESYSTEM OPTIONS # # Only the root, /usr, and /tmp filesystems need be statically # compiled; everything else will be automatically loaded at mount # time. (Exception: the UFS family--- FFS --- cannot # currently be demand-loaded.) Some people still prefer to statically # compile other filesystems as well. # # NB: The NULL, PORTAL, UMAP and UNION filesystems are known to be # buggy, and WILL panic your system if you attempt to do anything with # them. They are included here as an incentive for some enterprising # soul to sit down and fix them. # # One of these is mandatory: options FFS #Fast filesystem options NFS #Network File System # The rest are optional: #options NFS_NOSERVER #Disable the NFS-server code. options CD9660 #ISO 9660 filesystem options FDESCFS #File descriptor filesystem options HPFS #OS/2 File system options MSDOSFS #MS DOS File System (FAT, FAT32) options NTFS #NT File System options NULLFS #NULL filesystem options NWFS #NetWare filesystem options PORTALFS #Portal filesystem options PROCFS #Process filesystem options PSEUDOFS #Pseudo-filesystem framework options UMAPFS #UID map filesystem options UNIONFS #Union filesystem # options NODEVFS #disable devices filesystem # The xFS_ROOT options REQUIRE the associated ``options xFS'' options NFS_ROOT #NFS usable as root device # This code enables IFS, an FFS which exports inodes as the namespace. # You can find details in src/sys/ufs/ifs/README . options IFS # Soft updates is a technique for improving file system speed and # making abrupt shutdown less risky. # options SOFTUPDATES # Extended attributes allow additional data to be associated with files, # and is used for ACLs, Capabilities, and MAC labels. # See src/sys/ufs/ufs/README.extattr for more information. options UFS_EXTATTR options UFS_EXTATTR_AUTOSTART # Access Control List support for UFS filesystems. The current ACL # implementation requires extended attribute support, UFS_EXTATTR, # for the underlying filesystem. # See src/sys/ufs/ufs/README.acls for more information. options UFS_ACL # Make space in the kernel for a root filesystem on a md device. # Define to the number of kilobytes to reserve for the filesystem. options MD_ROOT_SIZE=10 # Make the md device a potential root device, either with preloaded # images of type mfs_root or md_root. options MD_ROOT # Allow this many swap-devices. # # In order to manage swap, the system must reserve bitmap space that # scales with the largest mounted swap device multiplied by NSWAPDEV, # irregardless of whether other swap devices exist or not. So it # is not a good idea to make this value too large. options NSWAPDEV=5 # Disk quotas are supported when this option is enabled. options QUOTA #enable disk quotas # If you are running a machine just as a fileserver for PC and MAC # users, using SAMBA or Netatalk, you may consider setting this option # and keeping all those users' directories on a filesystem that is # mounted with the suiddir option. This gives new files the same # ownership as the directory (similar to group). It's a security hole # if you let these users run programs, so confine it to file-servers # (but it'll save you lots of headaches in those cases). Root owned # directories are exempt and X bits are cleared. The suid bit must be # set on the directory as well; see chmod(1) PC owners can't see/set # ownerships so they keep getting their toes trodden on. This saves # you all the support calls as the filesystem it's used on will act as # they expect: "It's my dir so it must be my file". # options SUIDDIR # NFS options: options NFS_MINATTRTIMO=3 # VREG attrib cache timeout in sec options NFS_MAXATTRTIMO=60 options NFS_MINDIRATTRTIMO=30 # VDIR attrib cache timeout in sec options NFS_MAXDIRATTRTIMO=60 options NFS_GATHERDELAY=10 # Default write gather delay (msec) options NFS_UIDHASHSIZ=29 # Tune the size of nfssvc_sock with this options NFS_WDELAYHASHSIZ=16 # and with this options NFS_MUIDHASHSIZ=63 # Tune the size of nfsmount with this options NFS_DEBUG # Enable NFS Debugging # Coda stuff: options CODA #CODA filesystem. device vcoda 4 #coda minicache <-> venus comm. # # Add support for the EXT2FS filesystem of Linux fame. Be a bit # careful with this - the ext2fs code has a tendency to lag behind # changes and not be exercised very much, so mounting read/write could # be dangerous (and even mounting read only could result in panics.) # options EXT2FS # Use real implementations of the aio_* system calls. There are numerous # stability issues in the current aio code that make it unsuitable for # inclusion on shell boxes. options VFS_AIO # Enable the code UFS IO optimization through the VM system. This allows # use VM operations instead of copying operations when possible. # # Even with this enabled, actual use of the code is still controlled by the # sysctl vfs.ioopt. 0 gives no optimization, 1 gives normal (use VM # operations if a request happens to fit), 2 gives agressive optimization # (the operations are split to do as much as possible through the VM system.) # # Enabling this will probably not give an overall speedup except for # special workloads. options ENABLE_VFS_IOOPT # Cryptographically secure random number generator; /dev/[u]random device random ##################################################################### # POSIX P1003.1B # Real time extensions added in the 1993 Posix # P1003_1B: Infrastructure # _KPOSIX_PRIORITY_SCHEDULING: Build in _POSIX_PRIORITY_SCHEDULING # _KPOSIX_VERSION: Version kernel is built for options P1003_1B options _KPOSIX_PRIORITY_SCHEDULING options _KPOSIX_VERSION=199309L ##################################################################### # CLOCK OPTIONS # The granularity of operation is controlled by the kernel option HZ whose # default value (100) means a granularity of 10ms. For an accurate simulation # of high data rates it might be necessary to reduce the timer granularity to # 1ms or less. Consider, however, that some interfaces using programmed I/O # may require a considerable time to output packets. So, reducing the # granularity too much might actually cause ticks to be missed thus reducing # the accuracy of operation. options HZ=100 # Other clock options options CLK_CALIBRATION_LOOP options CLK_USE_I8254_CALIBRATION options CLK_USE_TSC_CALIBRATION ##################################################################### # SCSI DEVICES # SCSI DEVICE CONFIGURATION # The SCSI subsystem consists of the `base' SCSI code, a number of # high-level SCSI device `type' drivers, and the low-level host-adapter # device drivers. The host adapters are listed in the ISA and PCI # device configuration sections below. # # Beginning with FreeBSD 2.0.5 you can wire down your SCSI devices so # that a given bus, target, and LUN always come on line as the same # device unit. In earlier versions the unit numbers were assigned # in the order that the devices were probed on the SCSI bus. This # means that if you removed a disk drive, you may have had to rewrite # your /etc/fstab file, and also that you had to be careful when adding # a new disk as it may have been probed earlier and moved your device # configuration around. # This old behavior is maintained as the default behavior. The unit # assignment begins with the first non-wired down unit for a device # type. For example, if you wire a disk as "da3" then the first # non-wired disk will be assigned da4. # The syntax for wiring down devices is: hint.scbus.0.at="ahc0" hint.scbus.1.at="ahc1" hint.scbus.1.bus="0" hint.scbus.3.at="ahc2" hint.scbus.3.bus="0" hint.scbus.2.at="ahc2" hint.scbus.2.bus="1" hint.da.0.at="scbus0" hint.da.0.target="0" hint.da.0.unit="0" hint.da.1.at="scbus3" hint.da.1.target="1" hint.da.2.at="scbus2" hint.da.2.target="3" hint.sa.1.at="scbus1" hint.sa.1.target="6" # "units" (SCSI logical unit number) that are not specified are # treated as if specified as LUN 0. # All SCSI devices allocate as many units as are required. # The ch driver drives SCSI Media Changer ("jukebox") devices. # # The da driver drives SCSI Direct Access ("disk") and Optical Media # ("WORM") devices. # # The sa driver drives SCSI Sequential Access ("tape") devices. # # The cd driver drives SCSI Read Only Direct Access ("cd") devices. # # The ses driver drives SCSI Envinronment Services ("ses") and # SAF-TE ("SCSI Accessable Fault-Tolerant Enclosure") devices. # # The pt driver drives SCSI Processor devices. # # # Target Mode support is provided here but also requires that a SIM # (SCSI Host Adapter Driver) provide support as well. # # The targ driver provides target mode support as a Processor type device. # It exists to give the minimal context necessary to respond to Inquiry # commands. There is a sample user application that shows how the rest # of the command support might be done in /usr/share/examples/scsi_target. # # The targbh driver provides target mode support and exists to respond # to incoming commands that do not otherwise have a logical unit assigned # to them. # # The "unknown" device (uk? in pre-2.0.5) is now part of the base SCSI # configuration as the "pass" driver. device scbus #base SCSI code device ch #SCSI media changers device da #SCSI direct access devices (aka disks) device sa #SCSI tapes device cd #SCSI CD-ROMs device ses #SCSI Environmental Services (and SAF-TE) device pt #SCSI processor device targ #SCSI Target Mode Code device targbh #SCSI Target Mode Blackhole Device device pass #CAM passthrough driver # CAM OPTIONS: # debugging options: # -- NOTE -- If you specify one of the bus/target/lun options, you must # specify them all! # CAMDEBUG: When defined enables debugging macros # CAM_DEBUG_BUS: Debug the given bus. Use -1 to debug all busses. # CAM_DEBUG_TARGET: Debug the given target. Use -1 to debug all targets. # CAM_DEBUG_LUN: Debug the given lun. Use -1 to debug all luns. # CAM_DEBUG_FLAGS: OR together CAM_DEBUG_INFO, CAM_DEBUG_TRACE, # CAM_DEBUG_SUBTRACE, and CAM_DEBUG_CDB # # CAM_MAX_HIGHPOWER: Maximum number of concurrent high power (start unit) cmds # SCSI_NO_SENSE_STRINGS: When defined disables sense descriptions # SCSI_NO_OP_STRINGS: When defined disables opcode descriptions # SCSI_DELAY: The number of MILLISECONDS to freeze the SIM (scsi adapter) # queue after a bus reset, and the number of milliseconds to # freeze the device queue after a bus device reset. options CAMDEBUG options CAM_DEBUG_BUS=-1 options CAM_DEBUG_TARGET=-1 options CAM_DEBUG_LUN=-1 options CAM_DEBUG_FLAGS="CAM_DEBUG_INFO|CAM_DEBUG_TRACE|CAM_DEBUG_CDB" options CAM_MAX_HIGHPOWER=4 options SCSI_NO_SENSE_STRINGS options SCSI_NO_OP_STRINGS options SCSI_DELAY=8000 # Be pessimistic about Joe SCSI device # Options for the CAM CDROM driver: # CHANGER_MIN_BUSY_SECONDS: Guaranteed minimum time quantum for a changer LUN # CHANGER_MAX_BUSY_SECONDS: Maximum time quantum per changer LUN, only # enforced if there is I/O waiting for another LUN # The compiled in defaults for these variables are 2 and 10 seconds, # respectively. # # These can also be changed on the fly with the following sysctl variables: # kern.cam.cd.changer.min_busy_seconds # kern.cam.cd.changer.max_busy_seconds # options CHANGER_MIN_BUSY_SECONDS=2 options CHANGER_MAX_BUSY_SECONDS=10 # Options for the CAM sequential access driver: # SA_SPACE_TIMEOUT: Timeout for space operations, in minutes # SA_REWIND_TIMEOUT: Timeout for rewind operations, in minutes # SA_ERASE_TIMEOUT: Timeout for erase operations, in minutes # SA_1FM_AT_EOD: Default to model which only has a default one filemark at EOT. options SA_SPACE_TIMEOUT="(60)" options SA_REWIND_TIMEOUT="(2*60)" options SA_ERASE_TIMEOUT="(4*60)" options SA_1FM_AT_EOD # Optional timeout for the CAM processor target (pt) device # This is specified in seconds. The default is 60 seconds. options SCSI_PT_DEFAULT_TIMEOUT="60" # Optional enable of doing SES passthrough on other devices (e.g., disks) # # Normally disabled because a lot of newer SCSI disks report themselves # as having SES capabilities, but this can then clot up attempts to build # build a topology with the SES device that's on the box these drives # are in.... options SES_ENABLE_PASSTHROUGH ##################################################################### # MISCELLANEOUS DEVICES AND OPTIONS # The `pty' device usually turns out to be ``effectively mandatory'', # as it is required for `telnetd', `rlogind', `screen', `emacs', and # `xterm', among others. device pty #Pseudo ttys device speaker #Play IBM BASIC-style noises out your speaker device gzip #Exec gzipped a.out's device md #Memory/malloc disk device snp #Snoop device - to look at pty/vty/etc.. device ccd 4 #Concatenated disk driver # Configuring Vinum into the kernel is not necessary, since the kld # module gets started automatically when vinum(8) starts. This # device is also untested. Use at your own risk. # # The option VINUMDEBUG must match the value set in CFLAGS # in src/sbin/vinum/Makefile. Failure to do so will result in # the following message from vinum(8): # # Can't get vinum config: Invalid argument # # see vinum(4) for more reasons not to use these options. device vinum #Vinum concat/mirror/raid driver options VINUMDEBUG #enable Vinum debugging hooks # Kernel side iconv library options LIBICONV # Size of the kernel message buffer. Should be N * pagesize. options MSGBUF_SIZE=40960 ##################################################################### # HARDWARE BUS CONFIGURATION # ISA, EISA, MCA and PCI bus: # # Mandatory ISA devices: isa, npx # device isa # # Options for `isa': # # AUTO_EOI_1 enables the `automatic EOI' feature for the master 8259A # interrupt controller. This saves about 0.7-1.25 usec for each interrupt. # This option breaks suspend/resume on some portables. # # AUTO_EOI_2 enables the `automatic EOI' feature for the slave 8259A # interrupt controller. This saves about 0.7-1.25 usec for each interrupt. # Automatic EOI is documented not to work for for the slave with the # original i8259A, but it works for some clones and some integrated # versions. # # MAXMEM specifies the amount of RAM on the machine; if this is not # specified, FreeBSD will first read the amount of memory from the CMOS # RAM, so the amount of memory will initially be limited to 64MB or 16MB # depending on the BIOS. If the BIOS reports 64MB, a memory probe will # then attempt to detect the installed amount of RAM. If this probe # fails to detect >64MB RAM you will have to use the MAXMEM option. # The amount is in kilobytes, so for a machine with 128MB of RAM, it would # be 131072 (128 * 1024). # # BROKEN_KEYBOARD_RESET disables the use of the keyboard controller to # reset the CPU for reboot. This is needed on some systems with broken # keyboard controllers. options COMPAT_OLDISA #Use ISA shims and glue for old drivers options AUTO_EOI_1 #options AUTO_EOI_2 options MAXMEM="(128*1024)" #options BROKEN_KEYBOARD_RESET # Enable support for the kernel PLL to use an external PPS signal, # under supervision of [x]ntpd(8) # More info in ntpd documentation: http://www.eecis.udel.edu/~ntp options PPS_SYNC # If you see the "calcru: negative time of %ld usec for pid %d (%s)\n" # message you probably have some broken sw/hw which disables interrupts # for too long. You can make the system more resistant to this by # choosing a high value for NTIMECOUNTER. The default is 5, there # is no upper limit but more than a couple of hundred are not productive. # A better strategy may be to sysctl -w kern.timecounter.method=1 options NTIMECOUNTER=20 # # EISA bus # # The EISA bus device is `eisa'. It provides auto-detection and # configuration support for all devices on the EISA bus. device eisa # By default, only 10 EISA slots are probed, since the slot numbers # above clash with the configuration address space of the PCI subsystem, # and the EISA probe is not very smart about this. This is sufficient # for most machines, but in particular the HP NetServer LC series comes # with an onboard AIC7770 dual-channel SCSI controller on EISA slot #11, # thus you need to bump this figure to 12 for them. options EISA_SLOTS=12 # # MCA bus: # # The MCA bus device is `mca'. It provides auto-detection and # configuration support for all devices on the MCA bus. # No hints are required for MCA. device mca # # PCI bus & PCI options: # # The main PCI bus device is `pci'. It provides auto-detection and # configuration support for all devices on the PCI bus, using either # configuration mode defined in the PCI specification. device pci # # AGP GART support device agp # PCI options # #options PCI_QUIET #quiets PCI code on chipset settings ##################################################################### # HARDWARE DEVICE CONFIGURATION # EISA support is available for some device, so they can be auto-probed. # MicroChannel (MCA) support is available for some devices. # For ISA the required hints are listed. # EISA, MCA, PCI and pccard are self identifying buses, so no hints # are needed. # # Mandatory devices: # # The keyboard controller; it controls the keyboard and the PS/2 mouse. device atkbdc 1 hint.atkbdc.0.at="isa" hint.atkbdc.0.port="0x060" # The AT keyboard device atkbd hint.atkbd.0.at="atkbdc" hint.atkbd.0.irq="1" # Options for atkbd: options ATKBD_DFLT_KEYMAP # specify the built-in keymap makeoptions ATKBD_DFLT_KEYMAP="jp.106" # These options are valid for other keyboard drivers as well. options KBD_DISABLE_KEYMAP_LOAD # refuse to load a keymap options KBD_INSTALL_CDEV # install a CDEV entry in /dev # `flags' for atkbd: # 0x01 Force detection of keyboard, else we always assume a keyboard # 0x02 Don't reset keyboard, useful for some newer ThinkPads # 0x04 Old-style (XT) keyboard support, useful for older ThinkPads # PS/2 mouse device psm hint.psm.0.at="atkbdc" hint.psm.0.irq="12" # Options for psm: options PSM_HOOKRESUME #hook the system resume event, useful #for some laptops options PSM_RESETAFTERSUSPEND #reset the device at the resume event # The video card driver. device vga hint.vga.0.at="isa" # Options for vga: # Try the following option if the mouse pointer is not drawn correctly # or font does not seem to be loaded properly. May cause flicker on # some systems. options VGA_ALT_SEQACCESS # If you can dispense with some vga driver features, you may want to # use the following options to save some memory. #options VGA_NO_FONT_LOADING # don't save/load font #options VGA_NO_MODE_CHANGE # don't change video modes # Older video cards may require this option for proper operation. options VGA_SLOW_IOACCESS # do byte-wide i/o's to TS and GDC regs # The following option probably won't work with the LCD displays. options VGA_WIDTH90 # support 90 column modes # To include support for VESA video modes options VESA options FB_DEBUG # Frame buffer debugging options FB_INSTALL_CDEV # install a CDEV entry in /dev # Splash screen at start up! Screen savers require this too. device splash # The pcvt console driver (vt220 compatible). device vt hint.vt.0.at="isa" options XSERVER # support for running an X server on vt options FAT_CURSOR # start with block cursor # This PCVT option is for keyboards such as those used on IBM ThinkPad laptops options PCVT_SCANSET=2 # IBM keyboards are non-std # Other PCVT options are documented in pcvt(4). options PCVT_24LINESDEF options PCVT_CTRL_ALT_DEL options PCVT_META_ESC options PCVT_NSCREENS=9 options PCVT_PRETTYSCRNS options PCVT_SCREENSAVER options PCVT_USEKBDSEC options PCVT_VT220KEYB options PCVT_GREENSAVER # The syscons console driver (sco color console compatible). device sc 1 hint.sc.0.at="isa" options MAXCONS=16 # number of virtual consoles options SC_ALT_MOUSE_IMAGE # simplified mouse cursor in text mode options SC_DFLT_FONT # compile font in makeoptions SC_DFLT_FONT=cp850 options SC_DISABLE_DDBKEY # disable `debug' key options SC_DISABLE_REBOOT # disable reboot key sequence options SC_HISTORY_SIZE=200 # number of history buffer lines options SC_MOUSE_CHAR=0x3 # char code for text mode mouse cursor options SC_PIXEL_MODE # add support for the raster text mode # The following options will let you change the default colors of syscons. options SC_NORM_ATTR="(FG_GREEN|BG_BLACK)" options SC_NORM_REV_ATTR="(FG_YELLOW|BG_GREEN)" options SC_KERNEL_CONS_ATTR="(FG_RED|BG_BLACK)" options SC_KERNEL_CONS_REV_ATTR="(FG_BLACK|BG_RED)" # If you have a two button mouse, you may want to add the following option # to use the right button of the mouse to paste text. options SC_TWOBUTTON_MOUSE # You can selectively disable features in syscons. options SC_NO_CUTPASTE options SC_NO_FONT_LOADING options SC_NO_HISTORY options SC_NO_SYSMOUSE # `flags' for sc # 0x80 Put the video card in the VESA 800x600 dots, 16 color mode # 0x100 Probe for a keyboard device periodically if one is not present # 3Dfx Voodoo Graphics, Voodoo II /dev/3dfx CDEV support. This will create # the /dev/3dfx0 device to work with glide implementations. This should get # linked to /dev/3dfx and /dev/voodoo. Note that this is not the same as # the tdfx DRI module from XFree86 and is completely unrelated. # # To enable Linuxulator support, one must also include COMPAT_LINUX in the # config as well, or you will not have the dependencies. The other option # is to load both as modules. device tdfx # Enable 3Dfx Voodoo support options TDFX_LINUX # Enable Linuxulator support # # The Numeric Processing eXtension driver. In addition to this, you # may configure a math emulator (see above). If your machine has a # hardware FPU and the kernel configuration includes the npx device # *and* a math emulator compiled into the kernel, the hardware FPU # will be used, unless it is found to be broken or unless "flags" to # npx0 includes "0x08", which requests preference for the emulator. device npx hint.npx.0.at="nexus" hint.npx.0.port="0x0F0" hint.npx.0.flags="0x0" hint.npx.0.irq="13" # # `flags' for npx0: # 0x01 don't use the npx registers to optimize bcopy. # 0x02 don't use the npx registers to optimize bzero. # 0x04 don't use the npx registers to optimize copyin or copyout. # 0x08 use emulator even if hardware FPU is available. # The npx registers are normally used to optimize copying and zeroing when # all of the following conditions are satisfied: # I586_CPU is an option # the cpu is an i586 (perhaps not a Pentium) # the probe for npx0 succeeds # INT 16 exception handling works. # Then copying and zeroing using the npx registers is normally 30-100% faster. # The flags can be used to control cases where it doesn't work or is slower. # Setting them at boot time using userconfig works right (the optimizations # are not used until later in the bootstrap when npx0 is attached). # Flag 0x08 automatically disables the i586 optimized routines. # # # ACPI support using the Intel ACPI Component Architecture reference # implementation. # # ACPI_DEBUG enables the use of the debug.acpi.level and debug.acpi.layer # kernel environment variables to select initial debugging levels for the # Intel ACPICA code. (Note that the Intel code must also have USE_DEBUGGER # defined when it is built). # device acpica options ACPI_DEBUG # # Optional devices: # # # SCSI host adapters: # # adv: All Narrow SCSI bus AdvanSys controllers. # adw: Second Generation AdvanSys controllers including the ADV940UW. # aha: Adaptec 154x/1535/1640 # ahb: Adaptec 174x EISA controllers # ahc: Adaptec 274x/284x/2910/293x/294x/394x/3950x/3960x/398X/4944/ # 19160x/29160x, aic7770/aic78xx # aic: Adaptec 6260/6360, APA-1460 (PC Card), NEC PC9801-100 (C-BUS) # amd: Support for the AMD 53C974 SCSI host adapter chip as found on devices # such as the Tekram DC-390(T). # bt: Most Buslogic controllers: including BT-445, BT-54x, BT-64x, BT-74x, # BT-75x, BT-946, BT-948, BT-956, BT-958, SDC3211B, SDC3211F, SDC3222F # isp: Qlogic ISP 1020, 1040 and 1040B PCI SCSI host adapters, # ISP 1240 Dual Ultra SCSI, ISP 1080 and 1280 (Dual) Ultra2, # ISP 12160 Ultra3 SCSI, # Qlogic ISP 2100 and ISP 2200 Fibre Channel host adapters. # ispfw: Firmware module for Qlogic host adapters # ncr: NCR 53C810, 53C825 self-contained SCSI host adapters. # ncv: NCR 53C500 based SCSI host adapters. # nsp: Workbit Ninja SCSI-3 based PC Card SCSI host adapters. # sym: Symbios/Logic 53C8XX family of PCI-SCSI I/O processors: # 53C810, 53C810A, 53C815, 53C825, 53C825A, 53C860, 53C875, # 53C876, 53C885, 53C895, 53C895A, 53C896, 53C897, 53C1510D, # 53C1010-33, 53C1010-66. # stg: TMC 18C30, 18C50 based SCSI host adapters. # wds: WD7000 # # Note that the order is important in order for Buslogic ISA/EISA cards to be # probed correctly. # device bt hint.bt.0.at="isa" hint.bt.0.port="0x330" device adv hint.adv.0.at="isa" device adw device aha hint.aha.0.at="isa" device aic hint.aic.0.at="isa" device ahb device ahc device amd device isp hint.isp.0.disable="1" hint.isp.0.role="3" hint.isp.0.prefer_iomap="1" hint.isp.0.prefer_memmap="1" hint.isp.0.fwload_disable="1" hint.isp.0.ignore_nvram="1" hint.isp.0.fullduplex="1" hint.isp.0.topology="lport" hint.isp.0.topology="nport" hint.isp.0.topology="lport-only" hint.isp.0.topology="nport-only" # we can't get u_int64_t types, nor can we get strings if it's got # a leading 0x, hence this silly dodge. hint.isp.0.portwnn="w50000000aaaa0000" hint.isp.0.nodewnn="w50000000aaaa0001" device ispfw device ncr device ncv device nsp device sym device stg hint.stg.0.at="isa" hint.stg.0.port="0x140" hint.stg.0.port="11" device wds hint.wds.0.at="isa" hint.wds.0.port="0x350" hint.wds.0.irq="11" hint.wds.0.drq="6" # The aic7xxx driver will attempt to use memory mapped I/O for all PCI # controllers that have it configured only if this option is set. Unfortunately, # this doesn't work on some motherboards, which prevents it from being the # default. options AHC_ALLOW_MEMIO # Enable diagnostic sequencer code. options AHC_DEBUG_SEQUENCER # Dump the contents of the ahc controller configuration PROM. options AHC_DUMP_EEPROM # Bitmap of units to enable targetmode operations. options AHC_TMODE_ENABLE # The adw driver will attempt to use memory mapped I/O for all PCI # controllers that have it configured only if this option is set. options ADW_ALLOW_MEMIO # Options used in dev/isp/ (Qlogic SCSI/FC driver). # # ISP_TARGET_MODE - enable target mode operation # #options ISP_TARGET_MODE=1 # Options used in dev/sym/ (Symbios SCSI driver). #options SYM_SETUP_LP_PROBE_MAP #-Low Priority Probe Map (bits) # Allows the ncr to take precedence # 1 (1<<0) -> 810a, 860 # 2 (1<<1) -> 825a, 875, 885, 895 # 4 (1<<2) -> 895a, 896, 1510d #options SYM_SETUP_SCSI_DIFF #-HVD support for 825a, 875, 885 # disabled:0 (default), enabled:1 #options SYM_SETUP_PCI_PARITY #-PCI parity checking # disabled:0, enabled:1 (default) #options SYM_SETUP_MAX_LUN #-Number of LUNs supported # default:8, range:[1..64] # The 'asr' driver provides support for current DPT/Adaptec SCSI RAID # controllers (SmartRAID V and VI and later). # These controllers require the CAM infrastructure. # device asr # The 'dpt' driver provides support for old DPT controllers (http://www.dpt.com/). # These have hardware RAID-{0,1,5} support, and do multi-initiator I/O. # The DPT controllers are commonly re-licensed under other brand-names - # some controllers by Olivetti, Dec, HP, AT&T, SNI, AST, Alphatronic, NEC and # Compaq are actually DPT controllers. # # See src/sys/dev/dpt for debugging and other subtle options. # DPT_MEASURE_PERFORMANCE Enables a set of (semi)invasive metrics. Various # instruments are enabled. The tools in # /usr/sbin/dpt_* assume these to be enabled. # DPT_HANDLE_TIMEOUTS Normally device timeouts are handled by the DPT. # If you ant the driver to handle timeouts, enable # this option. If your system is very busy, this # option will create more trouble than solve. # DPT_TIMEOUT_FACTOR Used to compute the excessive amount of time to # wait when timing out with the above option. # DPT_DEBUG_xxxx These are controllable from sys/dev/dpt/dpt.h # DPT_LOST_IRQ When enabled, will try, once per second, to catch # any interrupt that got lost. Seems to help in some # DPT-firmware/Motherboard combinations. Minimal # cost, great benefit. # DPT_RESET_HBA Make "reset" actually reset the controller # instead of fudging it. Only enable this if you # are 100% certain you need it. device dpt # DPT options #!CAM# options DPT_MEASURE_PERFORMANCE #!CAM# options DPT_HANDLE_TIMEOUTS options DPT_TIMEOUT_FACTOR=4 options DPT_LOST_IRQ options DPT_RESET_HBA options DPT_ALLOW_MEMIO # # Mylex AcceleRAID and eXtremeRAID controllers with v6 and later # firmware. These controllers have a SCSI-like interface, and require # the CAM infrastructure. # device mly # # Adaptec FSA RAID controllers, including integrated DELL controllers, # the Dell PERC 2/QC and the HP NetRAID-4M # # AAC_COMPAT_LINUX Include code to support Linux-binary management # utilities (requires Linux compatibility # support). # device aac # # Compaq Smart RAID, Mylex DAC960 and AMI MegaRAID controllers. Only # one entry is needed; the code will find and configure all supported # controllers. # device ida # Compaq Smart RAID device mlx # Mylex DAC960 device amr # AMI MegaRAID # # 3ware ATA RAID # device twe # 3ware ATA RAID # # The 'ATA' driver supports all ATA and ATAPI devices, including PC Card # devices. You only need one "device ata" for it to find all # PCI and PC Card ATA/ATAPI devices on modern machines. device ata device atadisk # ATA disk drives device atapicd # ATAPI CDROM drives device atapifd # ATAPI floppy drives device atapist # ATAPI tape drives # # For older non-PCI, non-PnPBIOS systems, these are the hints lines to add: hint.ata.0.at="isa" hint.ata.0.port="0x1f0" hint.ata.0.irq="14" hint.ata.1.at="isa" hint.ata.1.port="0x170" hint.ata.1.irq="15" # # The following options are valid on the ATA driver: # # ATA_STATIC_ID: controller numbering is static ie depends on location # else the device numbers are dynamically allocated. options ATA_STATIC_ID # # Standard floppy disk controllers and floppy tapes, supports # the Y-E DATA External FDD (PC Card) # device fdc hint.fdc.0.at="isa" hint.fdc.0.port="0x3F0" hint.fdc.0.irq="6" hint.fdc.0.drq="2" # # FDC_DEBUG enables floppy debugging. Since the debug output is huge, you # gotta turn it actually on by setting the variable fd_debug with DDB, # however. options FDC_DEBUG # # Activate this line if you happen to have an Insight floppy tape. # Probing them proved to be dangerous for people with floppy disks only, # so it's "hidden" behind a flag: #hint.fdc.0.flags="1" # Specify floppy devices hint.fd.0.at="fdc0" hint.fd.0.drive="0" hint.fd.1.at="fdc0" hint.fd.1.drive="1" # M-systems DiskOnchip products see src/sys/contrib/dev/fla/README device fla hint.fla.0.at="isa" # # Other standard PC hardware: # # mse: Logitech and ATI InPort bus mouse ports # sio: serial ports (see sio(4)), including support for various # PC Card devices, such as Modem and NICs (see etc/defaults/pccard.conf) device mse hint.mse.0.at="isa" hint.mse.0.port="0x23c" hint.mse.0.irq="5" device sio hint.sio.0.at="isa" hint.sio.0.port="0x3F8" hint.sio.0.flags="0x10" hint.sio.0.irq="4" # # `flags' for serial drivers that support consoles (only for sio now): # 0x10 enable console support for this unit. The other console flags # are ignored unless this is set. Enabling console support does # not make the unit the preferred console - boot with -h or set # the 0x20 flag for that. Currently, at most one unit can have # console support; the first one (in config file order) with # this flag set is preferred. Setting this flag for sio0 gives # the old behaviour. # 0x20 force this unit to be the console (unless there is another # higher priority console). This replaces the COMCONSOLE option. # 0x40 reserve this unit for low level console operations. Do not # access the device in any normal way. # 0x80 use this port for serial line gdb support in ddb. # # PnP `flags' (set via userconfig using pnp x flags y) # 0x1 disable probing of this device. Used to prevent your modem # from being attached as a PnP modem. # # Options for serial drivers that support consoles (only for sio now): options BREAK_TO_DEBUGGER #a BREAK on a comconsole goes to #DDB, if available. options CONSPEED=9600 #default speed for serial console (default 9600) # Solaris implements a new BREAK which is initiated by a character # sequence CR ~ ^b which is similar to a familiar pattern used on # Sun servers by the Remote Console. options ALT_BREAK_TO_DEBUGGER # Options for sio: options COM_ESP #code for Hayes ESP options COM_MULTIPORT #code for some cards with shared IRQs # Other flags for sio that aren't documented in the man page. # 0x20000 enable hardware RTS/CTS and larger FIFOs. Only works for # ST16650A-compatible UARTs. # # Network interfaces: # # MII bus support is required for some PCI 10/100 ethernet NICs, # namely those which use MII-compliant transceivers or implement # tranceiver control interfaces that operate like an MII. Adding # "device miibus0" to the kernel config pulls in support for # the generic miibus API and all of the PHY drivers, including a # generic one for PHYs that aren't specifically handled by an # individual driver. device miibus # an: Aironet 4500/4800 802.11 wireless adapters. Supports the PCMCIA, # PCI and ISA varieties. # ar: Arnet SYNC/570i hdlc sync 2/4 port V.35/X.21 serial driver # (requires sppp) # awi: Support for IEEE 802.11 PC Card devices using the AMD Am79C930 and # Harris (Intersil) Chipset with PCnetMobile firmware by AMD. # cnw: Xircom CNW/Netware Airsurfer PC Card adapter # cs: IBM Etherjet and other Crystal Semi CS89x0-based adapters # cx: Cronyx/Sigma multiport sync/async (with Cisco or PPP framing) # dc: Support for PCI fast ethernet adapters based on the DEC/Intel 21143 # and various workalikes including: # the ADMtek AL981 Comet and AN985 Centaur, the ASIX Electronics # AX88140A and AX88141, the Davicom DM9100 and DM9102, the Lite-On # 82c168 and 82c169 PNIC, the Lite-On/Macronix LC82C115 PNIC II # and the Macronix 98713/98713A/98715/98715A/98725 PMAC. This driver # replaces the old al, ax, dm, pn and mx drivers. List of brands: # Digital DE500-BA, Kingston KNE100TX, D-Link DFE-570TX, SOHOware SFA110, # SVEC PN102-TX, CNet Pro110B, 120A, and 120B, Compex RL100-TX, # LinkSys LNE100TX, LNE100TX V2.0, Jaton XpressNet, Alfa Inc GFC2204, # KNE110TX. # de: Digital Equipment DC21040 # ed: Western Digital and SMC 80xx; Novell NE1000 and NE2000; 3Com 3C503 # HP PC Lan+, various PC Card devices (refer to etc/defauls/pccard.conf) # el: 3Com 3C501 (slow!) # ep: 3Com 3C509, 3C529, 3C556, 3C562D, 3C563D, 3C572, 3C574X, 3C579, 3C589 # and PC Card devices using these chipsets. # ex: Intel EtherExpress Pro/10 and other i82595-based adapters, # Olicom Ethernet PC Card devices. # fe: Fujitsu MB86960A/MB86965A Ethernet # fea: DEC DEFEA EISA FDDI adapter # fpa: Support for the Digital DEFPA PCI FDDI. `device fddi' is also needed. # fxp: Intel EtherExpress Pro/100B # (hint of prefer_iomap can be done to prefer I/O instead of Mem mapping) # ie: AT&T StarLAN 10 and EN100; 3Com 3C507; unknown NI5210; # Intel EtherExpress # le: Digital Equipment EtherWorks 2 and EtherWorks 3 (DEPCA, DE100, # DE101, DE200, DE201, DE202, DE203, DE204, DE205, DE422) # lnc: Lance/PCnet cards (Isolan, Novell NE2100, NE32-VL, AMD Am7990 and # Am79C960) # lge: Support for PCI gigabit ethernet adapters based on the Level 1 # LXT1001 NetCellerator chipset. This includes the D-Link DGE-500SX, # SMC TigerCard 1000 (SMC9462SX), and some Addtron cards. # nge: Support for PCI gigabit ethernet adapters based on the National # Semiconductor DP83820 and DP83821 chipset. This includes the # SMC EZ Card 1000 (SMC9462TX), D-Link DGE-500T, Asante FriendlyNet # GigaNIX 1000TA and 1000TPC, and the Addtron AEG320T. # oltr: Olicom ISA token-ring adapters OC-3115, OC-3117, OC-3118 and OC-3133 # (no hints needed). # Olicom PCI token-ring adapters OC-3136, OC-3137, OC-3139, OC-3140, # OC-3141, OC-3540, OC-3250 # rdp: RealTek RTL 8002-based pocket ethernet adapters # pcn: Support for PCI fast ethernet adapters based on the AMD Am79c97x # chipsets, including the PCnet/FAST, PCnet/FAST+, PCnet/PRO and # PCnet/Home. These were previously handled by the lnc driver (and # still will be if you leave this driver out of the kernel). # rl: Support for PCI fast ethernet adapters based on the RealTek 8129/8139 # chipset. Note that the RealTek driver defaults to using programmed # I/O to do register accesses because memory mapped mode seems to cause # severe lockups on SMP hardware. This driver also supports the # Accton EN1207D `Cheetah' adapter, which uses a chip called # the MPX 5030/5038, which is either a RealTek in disguise or a # RealTek workalike. Note that the D-Link DFE-530TX+ uses the RealTek # chipset and is supported by this driver, not the 'vr' driver. # sf: Support for Adaptec Duralink PCI fast ethernet adapters based on the # Adaptec AIC-6915 "starfire" controller. # This includes dual and quad port cards, as well as one 100baseFX card. # Most of these are 64-bit PCI devices, except for one single port # card which is 32-bit. # sis: Support for NICs based on the Silicon Integrated Systems SiS 900, # SiS 7016 and NS DP83815 PCI fast ethernet controller chips. # sk: Support for the SysKonnect SK-984x series PCI gigabit ethernet NICs. # This includes the SK-9841 and SK-9842 single port cards (single mode # and multimode fiber) and the SK-9843 and SK-9844 dual port cards # (also single mode and multimode). # The driver will autodetect the number of ports on the card and # attach each one as a separate network interface. # sn: Support for ISA and PC Card Ethernet devices using the # SMC91C90/92/94/95 chips. # sr: RISCom/N2 hdlc sync 1/2 port V.35/X.21 serial driver (requires sppp) # ste: Sundance Technologies ST201 PCI fast ethernet controller, includes # the D-Link DFE-550TX. # ti: Support for PCI gigabit ethernet NICs based on the Alteon Networks # Tigon 1 and Tigon 2 chipsets. This includes the Alteon AceNIC, the # 3Com 3c985, the Netgear GA620 and various others. Note that you will # probably want to bump up NMBCLUSTERS a lot to use this driver. # tl: Support for the Texas Instruments TNETE100 series 'ThunderLAN' # cards and integrated ethernet controllers. This includes several # Compaq Netelligent 10/100 cards and the built-in ethernet controllers # in several Compaq Prosignia, Proliant and Deskpro systems. It also # supports several Olicom 10Mbps and 10/100 boards. # tx: SMC 9432 TX, BTX and FTX cards. (SMC EtherPower II serie) # vr: Support for various fast ethernet adapters based on the VIA # Technologies VT3043 `Rhine I' and VT86C100A `Rhine II' chips, # including the D-Link DFE530TX (see 'rl' for DFE530TX+), the Hawking # Technologies PN102TX, and the AOpen/Acer ALN-320. # vx: 3Com 3C590 and 3C595 # wb: Support for fast ethernet adapters based on the Winbond W89C840F chip. # Note: this is not the same as the Winbond W89C940F, which is a # NE2000 clone. # wl: Lucent Wavelan (ISA card only). # wi: Lucent WaveLAN/IEEE 802.11 PCMCIA adapters. Note: this supports both # the PCMCIA and ISA cards: the ISA card is really a PCMCIA to ISA # bridge with a PCMCIA adapter plugged into it. # wx: Intel Gigabit Ethernet PCI card (`Wiseman') # xe: Xircom/Intel EtherExpress Pro100/16 PC Card ethernet controller, # Accton Fast EtherCard-16, Compaq Netelligent 10/100 PC Card, # Toshiba 10/100 Ethernet PC Card, Xircom 16-bit Ethernet + Modem 56 # xl: Support for the 3Com 3c900, 3c905, 3c905B and 3c905C (Fast) # Etherlink XL cards and integrated controllers. This includes the # integrated 3c905B-TX chips in certain Dell Optiplex and Dell # Precision desktop machines and the integrated 3c905-TX chips # in Dell Latitude laptop docking stations. # Also supported: 3Com 3c980(C)-TX, 3Com 3cSOHO100-TX, 3Com 3c450-TX # Order for ISA/EISA devices is important here device ar 1 hint.ar.0.at="isa" hint.ar.0.port="0x300" hint.ar.0.irq="10" hint.ar.0.maddr="0xd0000" device cs hint.cs.0.at="isa" hint.cs.0.port="0x300" device cx 1 hint.cx.0.at="isa" hint.cx.0.port="0x240" hint.cx.0.irq="15" hint.cx.0.drq="7" device ed hint.ed.0.at="isa" hint.ed.0.port="0x280" hint.ed.0.irq="5" hint.ed.0.maddr="0xd8000" device el 1 hint.el.0.at="isa" hint.el.0.port="0x300" hint.el.0.irq="9" device ep device ex device fe 1 options FE_8BIT_SUPPORT # LAC-98 support hint.fe.0.at="isa" hint.fe.0.port="0x300" device fea device ie 2 hint.ie.0.at="isa" hint.ie.0.port="0x300" hint.ie.0.irq="5" hint.ie.0.maddr="0xd0000" hint.ie.1.at="isa" hint.ie.1.port="0x360" hint.ie.1.irq="7" hint.ie.1.maddr="0xd0000" device le 1 hint.le.0.at="isa" hint.le.0.port="0x300" hint.le.0.irq="5" hint.le.0.maddr="0xd0000" device lnc 1 hint.lnc.0.at="isa" hint.lnc.0.port="0x280" hint.lnc.0.irq="10" hint.lnc.0.drq="0" device rdp 1 hint.rdp.0.at="isa" hint.rdp.0.port="0x378" hint.rdp.0.irq="7" hint.rdp.0.flags="2" device sr 1 hint.sr.0.at="isa" hint.sr.0.port="0x300" hint.sr.0.irq="5" hint.sr.0.maddr="0xd0000" device sn hint.sn.0.at="isa" hint.sn.0.port="0x300" hint.sn.0.irq="10" device an device awi device cnw device wi options WLCACHE # enables the signal-strength cache options WLDEBUG # enables verbose debugging output device wl 1 hint.wl.0.at="isa" hint.wl.0.port="0x300" device xe device oltr options OLTR_NO_BULLSEYE_MAC options OLTR_NO_HAWKEYE_MAC options OLTR_NO_TMS_MAC hint.oltr.0.at="isa" # PCI Ethernet NICs that use the common MII bus controller code. device dc # DEC/Intel 21143 and various workalikes device fxp # Intel EtherExpress PRO/100B (82557, 82558) hint.fxp.0.prefer_iomap="0" device rl # RealTek 8129/8139 device pcn # AMD Am79C79x PCI 10/100 NICs device sf # Adaptec AIC-6915 (``Starfire'') device sis # Silicon Integrated Systems SiS 900/SiS 7016 device ste # Sundance ST201 (D-Link DFE-550TX) device tl # Texas Instruments ThunderLAN device tx # SMC EtherPower II (83c170 ``EPIC'') device vr # VIA Rhine, Rhine II device wb # Winbond W89C840F device xl # 3Com 3c90x (``Boomerang'', ``Cyclone'') # PCI Ethernet NICs. device de # DEC/Intel DC21x4x (``Tulip'') device vx # 3Com 3c590, 3c595 (``Vortex'') # PCI Gigabit & FDDI NICs. device lge device nge device sk device ti device wx device fpa 1 # # ATM related options (Cranor version) # (note: this driver cannot be used with the HARP ATM stack) # # The `en' device provides support for Efficient Networks (ENI) # ENI-155 PCI midway cards, and the Adaptec 155Mbps PCI ATM cards (ANA-59x0). # # atm device provides generic atm functions and is required for # atm devices. # NATM enables the netnatm protocol family that can be used to # bypass TCP/IP. # # the current driver supports only PVC operations (no atm-arp, no multicast). # for more details, please read the original documents at # http://www.ccrc.wustl.edu/pub/chuck/tech/bsdatm/bsdatm.html # device atm device en options NATM #native ATM # # Audio drivers: `pcm', `sbc', `gusc', `pca' # # pcm: PCM audio through various sound cards. # # This has support for a large number of new audio cards, based on # CS423x, OPTi931, Yamaha OPL-SAx, and also for SB16, GusPnP. # For more information about this driver and supported cards, # see the pcm.4 man page. # # The flags of the device tells the device a bit more info about the # device that normally is obtained through the PnP interface. # bit 2..0 secondary DMA channel; # bit 4 set if the board uses two dma channels; # bit 15..8 board type, overrides autodetection; leave it # zero if don't know what to put in (and you don't, # since this is unsupported at the moment...). # # This driver will use the new PnP code if it's available. # # pca: PCM audio through your PC speaker # # Supported cards include: # Creative SoundBlaster ISA PnP/non-PnP # Supports ESS and Avance ISA chips as well. # Gravis UltraSound ISA PnP/non-PnP # Crystal Semiconductor CS461x/428x PCI # Neomagic 256AV (ac97) # Most of the more common ISA/PnP sb/mss/ess compatable cards. device pcm # For non-pnp sound cards with no bridge drivers only: hint.pcm.0.at="isa" hint.pcm.0.irq="10" hint.pcm.0.drq="1" hint.pcm.0.flags="0x0" # For PnP/PCI sound cards, no hints are required. # # midi: MIDI interfaces and synthesizers # device midi # For non-pnp sound cards with no bridge drivers: hint.midi.0.at="isa" hint.midi.0.irq="5" hint.midi.0.flags="0x0" # For serial ports (this example configures port 2): # TODO: implement generic tty-midi interface so that we can use # other uarts. hint.midi.0.at="isa" hint.midi.0.port="0x2F8" hint.midi.0.irq="3" # # seq: MIDI sequencer # device seq # The bridge drivers for sound cards. These can be separately configured # for providing services to the likes of new-midi. # When used with 'device pcm' they also provide pcm sound services. # # sbc: Creative SoundBlaster ISA PnP/non-PnP # Supports ESS and Avance ISA chips as well. # gusc: Gravis UltraSound ISA PnP/non-PnP # csa: Crystal Semiconductor CS461x/428x PCI # For non-PnP cards: device sbc hint.sbc.0.at="isa" hint.sbc.0.port="0x220" hint.sbc.0.irq="5" hint.sbc.0.drq="1" hint.sbc.0.flags="0x15" device gusc hint.gusc.0.at="isa" hint.gusc.0.port="0x220" hint.gusc.0.irq="5" hint.gusc.0.drq="1" hint.gusc.0.flags="0x13" device pca hint.pca.0.at="isa" hint.pca.0.port="0x040" # # Miscellaneous hardware: # # mcd: Mitsumi CD-ROM using proprietary (non-ATAPI) interface # scd: Sony CD-ROM using proprietary (non-ATAPI) interface # matcd: Matsushita/Panasonic CD-ROM using proprietary (non-ATAPI) interface # wt: Wangtek and Archive QIC-02/QIC-36 tape drives # ctx: Cortex-I frame grabber # apm: Laptop Advanced Power Management (experimental) # pmtimer: Timer device driver for power management events (APM or ACPI) # spigot: The Creative Labs Video Spigot video-acquisition board # meteor: Matrox Meteor video capture board # bktr: Brooktree bt848/848a/849a/878/879 video capture and TV Tuner board # cy: Cyclades serial driver # dgb: Digiboard PC/Xi and PC/Xe series driver (ALPHA QUALITY!) # dgm: Digiboard PC/Xem driver (obsolete) # digi: Digiboard driver # gp: National Instruments AT-GPIB and AT-GPIB/TNT board, PCMCIA-GPIB # asc: GI1904-based hand scanners, e.g. the Trust Amiscan Grey # gsc: Genius GS-4500 hand scanner. # joy: joystick (including IO DATA PCJOY PC Card joystick) # The LOUTB option specifies a slower outb() for debugging purposes. # rc: RISCom/8 multiport card # rp: Comtrol Rocketport(ISA) - single card # tw: TW-523 power line interface for use with X-10 home control products # si: Specialix SI/XIO 4-32 port terminal multiplexor # spic: Sony Programmable I/O controller (VAIO notebooks) # stl: Stallion EasyIO and EasyConnection 8/32 (cd1400 based) # stli: Stallion EasyConnection 8/64, ONboard, Brumby (intelligent) # Notes on APM # The flags takes the following meaning for apm0: # 0x0020 Statclock is broken. # If apm is omitted, some systems require sysctl -w kern.timecounter.method=1 # for correct timekeeping. # Notes on the spigot: # The video spigot is at 0xad6. This port address can not be changed. # The irq values may only be 10, 11, or 15 # I/O memory is an 8kb region. Possible values are: # 0a0000, 0a2000, ..., 0fffff, f00000, f02000, ..., ffffff # The start address must be on an even boundary. # Add the following option if you want to allow non-root users to be able # to access the spigot. This option is not secure because it allows users # direct access to the I/O page. # options SPIGOT_UNSECURE # Notes on the Comtrol Rocketport driver: # # The exact values used for rp0 depend on how many boards you have # in the system. The manufacturer's sample configs are listed as: # # device rp # core driver support # # Comtrol Rocketport ISA single card # hints.rp.0.at="isa" # hints.rp.0.port="0x280" # # If instead you have two ISA cards, one installed at 0x100 and the # second installed at 0x180, then you should add the following to # your kernel probe hints: # hints.rp.0.at="isa" # hints.rp.0.port="0x100" # hints.rp.1.at="isa" # hints.rp.1.port="0x180" # # For 4 ISA cards, it might be something like this: # hints.rp.0.at="isa" # hints.rp.0.port="0x180" # hints.rp.1.at="isa" # hints.rp.1.port="0x100" # hints.rp.2.at="isa" # hints.rp.2.port="0x340" # hints.rp.3.at="isa" # hints.rp.3.port="0x240" # # And for PCI cards, you need no hints. # Notes on the Digiboard driver: # # The following flag values have special meanings: # 0x01 - alternate layout of pins (dgb & dgm) # 0x02 - use the windowed PC/Xe in 64K mode (dgb only) # Notes on the Specialix SI/XIO driver: # The host card is memory, not IO mapped. # The Rev 1 host cards use a 64K chunk, on a 32K boundary. # The Rev 2 host cards use a 32K chunk, on a 32K boundary. # The cards can use an IRQ of 11, 12 or 15. # Notes on the Sony Programmable I/O controller # This is a temporary driver that should someday be replaced by something # that hooks into the ACPI layer. The device is hooked to the PIIX4's # General Device 10 decoder, which means you have to fiddle with PCI # registers to map it in, even though it is otherwise treated here as # an ISA device. At the moment, the driver polls, although the device # is capable of generating interrupts. It largely undocumented. # The port location in the hint is where you WANT the device to be # mapped. 0x10a0 seems to be traditional. At the moment the jogdial # is the only thing truly supported, but aparently a fair percentage # of the Vaio extra features are controlled by this device. # Notes on the Stallion stl and stli drivers: # See src/i386/isa/README.stl for complete instructions. # This is version 0.0.5alpha, unsupported by Stallion. # The stl driver has a secondary IO port hard coded at 0x280. You need # to change src/i386/isa/stallion.c if you reconfigure this on the boards. # The "flags" and "msize" settings on the stli driver depend on the board: # EasyConnection 8/64 ISA: flags 23 msize 0x1000 # EasyConnection 8/64 EISA: flags 24 msize 0x10000 # EasyConnection 8/64 MCA: flags 25 msize 0x1000 # ONboard ISA: flags 4 msize 0x10000 # ONboard EISA: flags 7 msize 0x10000 # ONboard MCA: flags 3 msize 0x10000 # Brumby: flags 2 msize 0x4000 # Stallion: flags 1 msize 0x10000 device mcd 1 hint.mcd.0.at="isa" hint.mcd.0.port="0x300" hint.mcd.0.irq="10" # for the Sony CDU31/33A CDROM device scd 1 hint.scd.0.at="isa" hint.scd.0.port="0x230" # for the SoundBlaster 16 multicd - up to 4 devices device matcd 1 hint.matcd.0.at="isa" hint.matcd.0.port="0x230" device wt 1 hint.wt.0.at="isa" hint.wt.0.port="0x300" hint.wt.0.irq="5" hint.wt.0.drq="1" device ctx 1 hint.ctx.0.at="isa" hint.ctx.0.port="0x230" hint.ctx.0.maddr="0xd0000" device spigot 1 hint.spigot.0.at="isa" hint.spigot.0.port="0xad6" hint.spigot.0.irq="15" hint.spigot.0.maddr="0xee000" device apm hint.apm.0.flags="0x20" device pmtimer # Adjust system timer at wakeup time hint.pmtimer.0.at="isa" device gp hint.gp.0.at="isa" hint.gp.0.port="0x2c0" device gsc 1 hint.gsc.0.at="isa" hint.gsc.0.port="0x270" hint.gsc.0.drq="3" device joy # PnP aware, hints for nonpnp only hint.joy.0.at="isa" hint.joy.0.port="0x201" device cy 1 options CY_PCI_FASTINTR # Use with cy_pci unless irq is shared hint.cy.0.at="isa" hint.cy.0.irq="10" hint.cy.0.maddr="0xd4000" hint.cy.0.msize="0x2000" device dgb 1 options NDGBPORTS=16 # Defaults to 16*NDGB hint.dgb.0.at="isa" hint.dgb.0.port="0x220" hint.dgb.0.maddr="0xfc000" device dgm hint.dgm.0.at="isa" hint.dgm.0.port="0x104" hint.dgm.0.maddr="0xd0000" device digi hint.dgm.0.at="isa" hint.dgm.0.port="0x104" hint.dgm.0.maddr="0xd0000" # BIOS & FEP/OS components of device digi. Normally left as modules device digi_CX device digi_CX_PCI device digi_EPCX device digi_EPCX_PCI device digi_Xe device digi_Xem device digi_Xr device rc 1 hint.rc.0.at="isa" hint.rc.0.port="0x220" hint.rc.0.irq="12" device rp hint.rp.0.at="isa" hint.rp.0.port="0x280" # the port and irq for tw0 are fictitious device tw 1 hint.tw.0.at="isa" hint.tw.0.port="0x380" hint.tw.0.irq="11" device si options SI_DEBUG hint.si.0.at="isa" hint.si.0.maddr="0xd0000" hint.si.0.irq="12" device asc 1 hint.asc.0.at="isa" hint.asc.0.port="0x3EB" hint.asc.0.drq="3" hint.asc.0.irq="10" device spic hint.spic.0.at="isa" hint.spic.0.port="0x10a0" device stl hint.stl.0.at="isa" hint.stl.0.port="0x2a0" hint.stl.0.irq="10" device stli hint.stli.0.at="isa" hint.stli.0.port="0x2a0" hint.stli.0.maddr="0xcc000" hint.stli.0.flags="23" hint.stli.0.msize="0x1000" # You are unlikely to have the hardware for loran device loran hint.loran.0.at="isa" hint.loran.0.irq="5" # HOT1 Xilinx 6200 card (http://www.vcc.com/) device xrpu # # The `meteor' device is a PCI video capture board. It can also have the # following options: # options METEOR_ALLOC_PAGES=xxx preallocate kernel pages for data entry # figure (ROWS*COLUMN*BYTES_PER_PIXEL*FRAME+PAGE_SIZE-1)/PAGE_SIZE # options METEOR_DEALLOC_PAGES remove all allocated pages on close(2) # options METEOR_DEALLOC_ABOVE=xxx remove all allocated pages above the # specified amount. If this value is below the allocated amount no action # taken # options METEOR_SYSTEM_DEFAULT={METEOR_PAL|METEOR_NTSC|METEOR_SECAM}, used # for initialization of fps routine when a signal is not present. # # The 'bktr' device is a PCI video capture device using the Brooktree # bt848/bt848a/bt849a/bt878/bt879 chipset. When used with a TV Tuner it forms a # TV card, eg Miro PC/TV, Hauppauge WinCast/TV WinTV, VideoLogic Captivator, # Intel Smart Video III, AverMedia, IMS Turbo, FlyVideo. # # options OVERRIDE_CARD=xxx # options OVERRIDE_TUNER=xxx # options OVERRIDE_MSP=1 # options OVERRIDE_DBX=1 # These options can be used to override the auto detection # The current values for xxx are found in src/sys/dev/bktr/bktr_card.h # Using sysctl(8) run-time overrides on a per-card basis can be made # # options BROOKTREE_SYSTEM_DEFAULT=BROOKTREE_PAL # or # options BROOKTREE_SYSTEM_DEFAULT=BROOKTREE_NTSC # Specifes the default video capture mode. # This is required for Dual Crystal (28&35Mhz) boards where PAL is used # to prevent hangs during initialisation. eg VideoLogic Captivator PCI. # # options BKTR_USE_PLL # PAL or SECAM users who have a 28Mhz crystal (and no 35Mhz crystal) # must enable PLL mode with this option. eg some new Bt878 cards. # # options BKTR_GPIO_ACCESS # This enable IOCTLs which give user level access to the GPIO port. # # options BKTR_NO_MSP_RESET # Prevents the MSP34xx reset. Good if you initialise the MSP in another OS first # # options BKTR_430_FX_MODE # Switch Bt878/879 cards into Intel 430FX chipset compatibility mode. # # options BKTR_SIS_VIA_MODE # Switch Bt878/879 cards into SIS/VIA chipset compatibility mode which is # needed for some old SiS and VIA chipset motherboards. # This also allows Bt878/879 chips to work on old OPTi (<1997) chipset # motherboards and motherboards with bad or incomplete PCI 2.1 support. # As a rough guess, old = before 1998 # device meteor 1 # Brooktree driver has been ported to the new I2C framework. Thus, # you'll need to have the following 3 lines in the kernel config. # device smbus # device iicbus # device iicbb # The iic and smb devices are only needed if you want to control other # I2C slaves connected to the external connector of some cards. # device bktr 1 # # PC Card/PCMCIA # (OLDCARD) # # card: pccard slots # pcic: isa/pccard bridge device pcic hint.pcic.0.at="isa" hint.pcic.1.at="isa" device card # # PC Card/PCMCIA and Cardbus # (NEWCARD) # # Note that NEWCARD and OLDCARD are incompatible. Do not use both at the same # time. # # pccbb: isa/pccard and pci/cardbus bridge # pccard: pccard slots # cardbus: cardbus slots #device pccbb #device pccard #device cardbus # You may need to reset all pccards after resuming options PCIC_RESUME_RESET # reset after resume # # Laptop/Notebook options: # # See also: # apm under `Miscellaneous hardware' # above. # For older notebooks that signal a powerfail condition (external # power supply dropped, or battery state low) by issuing an NMI: options POWERFAIL_NMI # make it beep instead of panicing # # SMB bus # # System Management Bus support is provided by the 'smbus' device. # Access to the SMBus device is via the 'smb' device (/dev/smb*), # which is a child of the 'smbus' device. # # Supported devices: # smb standard io through /dev/smb* # # Supported SMB interfaces: # iicsmb I2C to SMB bridge with any iicbus interface # bktr brooktree848 I2C hardware interface # intpm Intel PIIX4 Power Management Unit # alpm Acer Aladdin-IV/V/Pro2 Power Management Unit # ichsmb Intel ICH SMBus controller chips (82801AA, 82801AB, 82801BA) # device smbus # Bus support, required for smb below. device intpm device alpm device ichsmb device smb # # I2C Bus # # Philips i2c bus support is provided by the `iicbus' device. # # Supported devices: # ic i2c network interface # iic i2c standard io # iicsmb i2c to smb bridge. Allow i2c i/o with smb commands. # # Supported interfaces: # pcf Philips PCF8584 ISA-bus controller # bktr brooktree848 I2C software interface # # Other: # iicbb generic I2C bit-banging code (needed by lpbb, bktr) # device iicbus # Bus support, required for ic/iic/iicsmb below. device iicbb device ic device iic device iicsmb # smb over i2c bridge device pcf hint.pcf.0.at="isa" hint.pcf.0.port="0x320" hint.pcf.0.irq="5" #--------------------------------------------------------------------------- # ISDN4BSD # # See /usr/share/examples/isdn/ROADMAP for an introduction to isdn4bsd. # # i4b passive ISDN cards support contains the following hardware drivers: # # isic - Siemens/Infineon ISDN ISAC/HSCX/IPAC chipset driver # iwic - Winbond W6692 PCI bus ISDN S/T interface controller # ifpi - AVM Fritz!Card PCI driver # ihfc - Cologne Chip HFC ISA/ISA-PnP chipset driver # ifpnp - AVM Fritz!Card PnP driver # itjc - Siemens ISAC / TJNet Tiger300/320 chipset # # i4b active ISDN cards support contains the following hardware drivers: # # iavc - AVM B1 PCI, AVM B1 ISA, AVM T1 # # Note that the ``options'' (if given) and ``device'' lines must BOTH # be uncommented to enable support for a given card ! # # In addition to a hardware driver (and probably an option) the mandatory # ISDN protocol stack devices and the mandatory support device must be # enabled as well as one or more devices from the optional devices section. # #--------------------------------------------------------------------------- # isic driver (Siemens/Infineon chipsets) # device isic # # ISA bus non-PnP Cards: # ---------------------- # # Teles S0/8 or Niccy 1008 options TEL_S0_8 hint.isic.0.at="isa" hint.isic.0.maddr="0xd0000" hint.isic.0.irq="5" hint.isic.0.flags="1" # # Teles S0/16 or Creatix ISDN-S0 or Niccy 1016 options TEL_S0_16 hint.isic.0.at="isa" hint.isic.0.port="0xd80" hint.isic.0.maddr="0xd0000" hint.isic.0.irq="5" hint.isic.0.flags="2" # # Teles S0/16.3 options TEL_S0_16_3 hint.isic.0.at="isa" hint.isic.0.port="0xd80" hint.isic.0.irq="5" hint.isic.0.flags="3" # # AVM A1 or AVM Fritz!Card options AVM_A1 hint.isic.0.at="isa" hint.isic.0.port="0x340" hint.isic.0.irq="5" hint.isic.0.flags="4" # # USRobotics Sportster ISDN TA intern options USR_STI hint.isic.0.at="isa" hint.isic.0.port="0x268" hint.isic.0.irq="5" hint.isic.0.flags="7" # # ITK ix1 Micro ( < V.3, non-PnP version ) options ITKIX1 hint.isic.0.at="isa" hint.isic.0.port="0x398" hint.isic.0.irq="10" hint.isic.0.flags="18" # # ELSA PCC-16 options ELSA_PCC16 hint.isic.0.at="isa" hint.isic.0.port="0x360" hint.isic.0.irq="10" hint.isic.0.flags="20" # # ISA bus PnP Cards: # ------------------ # # Teles S0/16.3 PnP options TEL_S0_16_3_P # # Creatix ISDN-S0 P&P options CRTX_S0_P # # Dr. Neuhaus Niccy Go@ options DRN_NGO # # Sedlbauer Win Speed options SEDLBAUER # # Dynalink IS64PH options DYNALINK # # ELSA QuickStep 1000pro ISA options ELSA_QS1ISA # # Siemens I-Surf 2.0 options SIEMENS_ISURF2 # # Asuscom ISDNlink 128K ISA options ASUSCOM_IPAC # # Eicon Diehl DIVA 2.0 and 2.02 options EICON_DIVA # # PCI bus Cards: # -------------- # # ELSA MicroLink ISDN/PCI (same as ELSA QuickStep 1000pro PCI) options ELSA_QS1PCI # # #--------------------------------------------------------------------------- # ifpnp driver for AVM Fritz!Card PnP # # AVM Fritz!Card PnP device ifpnp # #--------------------------------------------------------------------------- # ihfc driver for Cologne Chip ISA chipsets (experimental!) # # Teles 16.3c ISA PnP # AcerISDN P10 ISA PnP # TELEINT ISDN SPEED No.1 device ihfc # #--------------------------------------------------------------------------- # ifpi driver for AVM Fritz!Card PCI # # AVM Fritz!Card PCI device ifpi # #--------------------------------------------------------------------------- # iwic driver for Winbond W6692 chipset # # ASUSCOM P-IN100-ST-D (and other Winbond W6692 based cards) device iwic # #--------------------------------------------------------------------------- # itjc driver for Simens ISAC / TJNet Tiger300/320 chipset # # Traverse Technologies NETjet-S # Teles PCI-TJ device itjc # #--------------------------------------------------------------------------- # iavc driver (AVM active cards, needs i4bcapi driver!) # device iavc # # AVM B1 ISA bus (PnP mode not supported!) # ---------------------------------------- hint.iavc.0.at="isa" hint.iavc.0.port="0x150" hint.iavc.0.irq="5" # #--------------------------------------------------------------------------- # ISDN Protocol Stack - mandatory for all hardware drivers # # Q.921 / layer 2 - i4b passive cards D channel handling device "i4bq921" # # Q.931 / layer 3 - i4b passive cards D channel handling device "i4bq931" # # layer 4 - i4b common passive and active card handling device "i4b" # #--------------------------------------------------------------------------- # ISDN devices - mandatory for all hardware drivers # # userland driver to do ISDN tracing (for passive cards only) device "i4btrc" 4 # # userland driver to control the whole thing device "i4bctl" # #--------------------------------------------------------------------------- # ISDN devices - optional # # userland driver for access to raw B channel device "i4brbch" 4 # # userland driver for telephony device "i4btel" 2 # # network driver for IP over raw HDLC ISDN device "i4bipr" 4 # enable VJ header compression detection for ipr i/f options IPR_VJ # enable logging of the first n IP packets to isdnd (n=32 here) options IPR_LOG=32 # # network driver for sync PPP over ISDN; requires an equivalent # number of sppp device to be configured device "i4bisppp" 4 # # B-channel interface to the netgraph subsystem device "i4bing" 2 # # CAPI driver needed for active ISDN cards (see iavc driver above) device "i4bcapi" # #--------------------------------------------------------------------------- # Parallel-Port Bus # # Parallel port bus support is provided by the `ppbus' device. # Multiple devices may be attached to the parallel port, devices # are automatically probed and attached when found. # # Supported devices: # vpo Iomega Zip Drive # Requires SCSI disk support ('scbus' and 'da'), best # performance is achieved with ports in EPP 1.9 mode. # lpt Parallel Printer # plip Parallel network interface # ppi General-purpose I/O ("Geek Port") + IEEE1284 I/O # pps Pulse per second Timing Interface # lpbb Philips official parallel port I2C bit-banging interface # # Supported interfaces: # ppc ISA-bus parallel port interfaces. # options PPC_PROBE_CHIPSET # Enable chipset specific detection # (see flags in ppc(4)) options DEBUG_1284 # IEEE1284 signaling protocol debug options PERIPH_1284 # Makes your computer act as a IEEE1284 # compliant peripheral options DONTPROBE_1284 # Avoid boot detection of PnP parallel devices options VP0_DEBUG # ZIP/ZIP+ debug options LPT_DEBUG # Printer driver debug options PPC_DEBUG # Parallel chipset level debug options PLIP_DEBUG # Parallel network IP interface debug options PCFCLOCK_VERBOSE # Verbose pcfclock driver options PCFCLOCK_MAX_RETRIES=5 # Maximum read tries (default 10) device ppc hint.ppc.0.at="isa" hint.ppc.0.irq="7" device ppbus device vpo device lpt device plip device ppi device pps device lpbb device pcfclock # Kernel BOOTP support options BOOTP # Use BOOTP to obtain IP address/hostname options BOOTP_NFSROOT # NFS mount root filesystem using BOOTP info options BOOTP_NFSV3 # Use NFS v3 to NFS mount root options BOOTP_COMPAT # Workaround for broken bootp daemons. options BOOTP_WIRED_TO=fxp0 # Use interface fxp0 for BOOTP # # Add tie-ins for a hardware watchdog. This only enable the hooks; # the user must still supply the actual driver. # options HW_WDOG # # Set the number of PV entries per process. Increasing this can # stop panics related to heavy use of shared memory. However, that can # (combined with large amounts of physical memory) cause panics at # boot time due the kernel running out of VM space. # # If you're tweaking this, you might also want to increase the sysctls # "vm.v_free_min", "vm.v_free_reserved", and "vm.v_free_target". # # The value below is the one more than the default. # options PMAP_SHPGPERPROC=201 # # Disable swapping. This option removes all code which actually performs # swapping, so it's not possible to turn it back on at run-time. # # This is sometimes usable for systems which don't have any swap space # (see also sysctls "vm.defer_swapspace_pageouts" and # "vm.disable_swapspace_pageouts") # #options NO_SWAPPING # Set the number of sf_bufs to allocate. sf_bufs are virtual buffers # for sendfile(2) that are used to map file VM pages, and normally # default to a quantity that is roughly 16*MAXUSERS+512. You would # typically want about 4 of these for each simultaneous file send. # options NSFBUFS=1024 # # Enable extra debugging code for locks. This stores the filename and # line of whatever acquired the lock in the lock itself, and change a # number of function calls to pass around the relevant data. This is # not at all useful unless you are debugging lock code. Also note # that it is likely to break e.g. fstat(1) unless you recompile your # userland with -DDEBUG_LOCKS as well. # options DEBUG_LOCKS ##################################################################### # ABI Emulation # Enable iBCS2 runtime support for SCO and ISC binaries options IBCS2 # Emulate spx device for client side of SVR3 local X interface options SPX_HACK # Enable Linux ABI emulation options COMPAT_LINUX # Enable the linux-like proc filesystem support (requires COMPAT_LINUX # and PSEUDOFS) options LINPROCFS # Linux debugging options DEBUG_LINUX # # SysVR4 ABI emulation # # The svr4 ABI emulator can be statically compiled into the kernel or loaded as # a KLD module. # The STREAMS network emulation code can also be compiled statically or as a # module. If loaded as a module, it must be loaded before the svr4 module # (the /usr/sbin/svr4 script does this for you). If compiling statically, # the `streams' device must be configured into any kernel which also # specifies COMPAT_SVR4. It is possible to have a statically-configured # STREAMS device and a dynamically loadable svr4 emulator; the /usr/sbin/svr4 # script understands that it doesn't need to load the `streams' module under # those circumstances. # Caveat: At this time, `options KTRACE' is required for the svr4 emulator # (whether static or dynamic). # options COMPAT_SVR4 # build emulator statically options DEBUG_SVR4 # enable verbose debugging device streams # STREAMS network driver (required for svr4). ##################################################################### # USB support # UHCI controller device uhci # OHCI controller device ohci # General USB code (mandatory for USB) device usb # # USB Double Bulk Pipe devices device udbp # Generic USB device driver device ugen # Human Interface Device (anything with buttons and dials) device uhid # USB keyboard device ukbd # USB printer device ulpt # USB Iomega Zip 100 Drive device umass # USB modem support device umodem # USB mouse device ums # Diamond Rio 500 Mp3 player device urio # USB scanners device uscanner # # ADMtek USB ethernet. Supports the LinkSys USB100TX, # the Billionton USB100, the Melco LU-ATX, the D-Link DSB-650TX # and the SMC 2202USB. Also works with the ADMtek AN986 Pegasus # eval board. device aue # # CATC USB-EL1201A USB ethernet. Supports the CATC Netmate # and Netmate II, and the Belkin F5U111. device cue # # Kawasaki LSI ethernet. Supports the LinkSys USB10T, # Entrega USB-NET-E45, Peracom Ethernet Adapter, the # 3Com 3c19250, the ADS Technologies USB-10BT, the ATen UC10T, # the Netgear EA101, the D-Link DSB-650, the SMC 2102USB # and 2104USB, and the Corega USB-T. device kue # debugging options for the USB subsystem # options UHCI_DEBUG options OHCI_DEBUG options USB_DEBUG options UGEN_DEBUG options UHID_DEBUG options UHUB_DEBUG options UKBD_DEBUG options ULPT_DEBUG options UMASS_DEBUG options UMS_DEBUG options URIO_DEBUG # options for ukbd: options UKBD_DFLT_KEYMAP # specify the built-in keymap makeoptions UKBD_DFLT_KEYMAP=it.iso # # Embedded system options: # # An embedded system might want to run something other than init. options INIT_PATH="/sbin/init:/stand/sysinstall" # Debug options options BUS_DEBUG # enable newbus debugging options DEBUG_VFS_LOCKS # enable vfs lock debugging options NPX_DEBUG # enable npx debugging (FPU/math emu) ##################################################################### # SYSV IPC KERNEL PARAMETERS # # Maximum number of entries in a semaphore map. options SEMMAP=31 # Maximum number of System V semaphores that can be used on the system at # one time. options SEMMNI=11 # Total number of semaphores system wide options SEMMNS=61 # Total number of undo structures in system options SEMMNU=31 # Maximum number of System V semaphores that can be used by a single process # at one time. options SEMMSL=61 # Maximum number of operations that can be outstanding on a single System V # semaphore at one time. options SEMOPM=101 # Maximum number of undo operations that can be outstanding on a single # System V semaphore at one time. options SEMUME=11 # Maximum number of shared memory pages system wide. options SHMALL=1025 # Maximum size, in bytes, of a single System V shared memory region. options SHMMAX="(SHMMAXPGS*PAGE_SIZE+1)" options SHMMAXPGS=1025 # Minimum size, in bytes, of a single System V shared memory region. options SHMMIN=2 # Maximum number of shared memory regions that can be used on the system # at one time. options SHMMNI=33 # Maximum number of System V shared memory regions that can be attached to # a single process at one time. options SHMSEG=9 # Set the amount of time (in seconds) the system will wait before # rebooting automatically when a kernel panic occurs. If set to (-1), # the system will wait indefinitely until a key is pressed on the # console. options PANIC_REBOOT_WAIT_TIME=16 ##################################################################### # More undocumented options for linting. # Note that documenting these are not considered an affront. options CAM_DEBUG_DELAY # VFS cluster debugging. options CLUSTERDEBUG # Eliminate unneeded cache flush instruction(s). options CPU_UPGRADE_HW_CACHE options DEBUG # PECOFF module (Win32 Execution Format) options PECOFF_SUPPORT options PECOFF_DEBUG # Disable the 4 MByte PSE CPU feature. #options DISABLE_PSE options ENABLE_ALART options I4B_SMP_WORKAROUND options I586_PMC_GUPROF=0x70000 options KBDIO_DEBUG=2 options KBD_MAXRETRY=4 options KBD_MAXWAIT=6 options KBD_RESETDELAY=201 # Enable the PF_KEY Key Management API. options KEY # Kernel filelock debugging. options LOCKF_DEBUG # System V compatible message queues # Please note that the values provided here are used to test kernel # building. The defaults in the sources provide almost the same numbers. # MSGSSZ must be a power of 2 between 8 and 1024. options MSGMNB=2049 # Max number of chars in queue options MSGMNI=41 # Max number of message queue identifiers options MSGSEG=2049 # Max number of message segments options MSGSSZ=16 # Size of a message segment options MSGTQL=41 # Max number of messages in system options NBUF=512 # Number of buffer headers options NMBCLUSTERS=1024 # Number of mbuf clusters options PSM_DEBUG=1 options SCSI_NCR_DEBUG options SCSI_NCR_MAX_SYNC=10000 options SCSI_NCR_MAX_WIDE=1 options SCSI_NCR_MYADDR=7 options SC_DEBUG_LEVEL=5 # Syscons debug level options SC_RENDER_DEBUG # syscons rendering debugging options SHOW_BUSYBUFS # List buffers that prevent root unmount options SIMPLELOCK_DEBUG options SLIP_IFF_OPTS options TIMER_FREQ="((14318182+6)/12)" options VFS_BIO_DEBUG # VFS buffer I/O debugging options VM_KMEM_SIZE options VM_KMEM_SIZE_MAX options VM_KMEM_SIZE_SCALE Index: head/sys/i386/i386/machdep.c =================================================================== --- head/sys/i386/i386/machdep.c (revision 78134) +++ head/sys/i386/i386/machdep.c (revision 78135) @@ -1,2518 +1,2518 @@ /*- * Copyright (c) 1992 Terrence R. Lambert. * Copyright (c) 1982, 1987, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * 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. * 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. * * from: @(#)machdep.c 7.4 (Berkeley) 6/3/91 * $FreeBSD$ */ #include "opt_atalk.h" #include "opt_compat.h" #include "opt_cpu.h" #include "opt_ddb.h" #include "opt_inet.h" #include "opt_ipx.h" #include "opt_isa.h" #include "opt_maxmem.h" #include "opt_msgbuf.h" #include "opt_npx.h" #include "opt_perfmon.h" -#include "opt_userconfig.h" +/* #include "opt_userconfig.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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* pcb.h included via sys/user.h */ #include #ifdef PERFMON #include #endif #ifdef OLD_BUS_ARCH #include #endif #include #include #include #include #include #include extern void init386 __P((int first)); extern void dblfault_handler __P((void)); extern void printcpuinfo(void); /* XXX header file */ extern void earlysetcpuclass(void); /* same header file */ extern void finishidentcpu(void); extern void panicifcpuunsupported(void); extern void initializecpu(void); #define CS_SECURE(cs) (ISPL(cs) == SEL_UPL) #define EFL_SECURE(ef, oef) ((((ef) ^ (oef)) & ~PSL_USERCHANGE) == 0) static void cpu_startup __P((void *)); SYSINIT(cpu, SI_SUB_CPU, SI_ORDER_FIRST, cpu_startup, NULL) int _udatasel, _ucodesel; u_int atdevbase; #if defined(SWTCH_OPTIM_STATS) extern int swtch_optim_stats; SYSCTL_INT(_debug, OID_AUTO, swtch_optim_stats, CTLFLAG_RD, &swtch_optim_stats, 0, ""); SYSCTL_INT(_debug, OID_AUTO, tlb_flush_count, CTLFLAG_RD, &tlb_flush_count, 0, ""); #endif #ifdef PC98 static int ispc98 = 1; #else static int ispc98 = 0; #endif SYSCTL_INT(_machdep, OID_AUTO, ispc98, CTLFLAG_RD, &ispc98, 0, ""); int physmem = 0; int cold = 1; static void osendsig __P((sig_t catcher, int sig, sigset_t *mask, u_long code)); static int sysctl_hw_physmem(SYSCTL_HANDLER_ARGS) { int error = sysctl_handle_int(oidp, 0, ctob(physmem), req); return (error); } SYSCTL_PROC(_hw, HW_PHYSMEM, physmem, CTLTYPE_INT|CTLFLAG_RD, 0, 0, sysctl_hw_physmem, "I", ""); static int sysctl_hw_usermem(SYSCTL_HANDLER_ARGS) { int error = sysctl_handle_int(oidp, 0, ctob(physmem - cnt.v_wire_count), req); return (error); } SYSCTL_PROC(_hw, HW_USERMEM, usermem, CTLTYPE_INT|CTLFLAG_RD, 0, 0, sysctl_hw_usermem, "I", ""); static int sysctl_hw_availpages(SYSCTL_HANDLER_ARGS) { int error = sysctl_handle_int(oidp, 0, i386_btop(avail_end - avail_start), req); return (error); } SYSCTL_PROC(_hw, OID_AUTO, availpages, CTLTYPE_INT|CTLFLAG_RD, 0, 0, sysctl_hw_availpages, "I", ""); static int sysctl_machdep_msgbuf(SYSCTL_HANDLER_ARGS) { int error; /* Unwind the buffer, so that it's linear (possibly starting with * some initial nulls). */ error=sysctl_handle_opaque(oidp,msgbufp->msg_ptr+msgbufp->msg_bufr, msgbufp->msg_size-msgbufp->msg_bufr,req); if(error) return(error); if(msgbufp->msg_bufr>0) { error=sysctl_handle_opaque(oidp,msgbufp->msg_ptr, msgbufp->msg_bufr,req); } return(error); } SYSCTL_PROC(_machdep, OID_AUTO, msgbuf, CTLTYPE_STRING|CTLFLAG_RD, 0, 0, sysctl_machdep_msgbuf, "A","Contents of kernel message buffer"); static int msgbuf_clear; static int sysctl_machdep_msgbuf_clear(SYSCTL_HANDLER_ARGS) { int error; error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); if (!error && req->newptr) { /* Clear the buffer and reset write pointer */ bzero(msgbufp->msg_ptr,msgbufp->msg_size); msgbufp->msg_bufr=msgbufp->msg_bufx=0; msgbuf_clear=0; } return (error); } SYSCTL_PROC(_machdep, OID_AUTO, msgbuf_clear, CTLTYPE_INT|CTLFLAG_RW, &msgbuf_clear, 0, sysctl_machdep_msgbuf_clear, "I", "Clear kernel message buffer"); int Maxmem = 0; long dumplo; vm_offset_t phys_avail[10]; /* must be 2 less so 0 0 can signal end of chunks */ #define PHYS_AVAIL_ARRAY_END ((sizeof(phys_avail) / sizeof(vm_offset_t)) - 2) static vm_offset_t buffer_sva, buffer_eva; vm_offset_t clean_sva, clean_eva; static vm_offset_t pager_sva, pager_eva; static struct trapframe proc0_tf; #ifndef SMP static struct globaldata __globaldata; #endif struct mtx sched_lock; struct mtx Giant; static void cpu_startup(dummy) void *dummy; { register unsigned i; register caddr_t v; vm_offset_t maxaddr; vm_size_t size = 0; int firstaddr; vm_offset_t minaddr; int physmem_est; /* * Good {morning,afternoon,evening,night}. */ mtx_lock(&vm_mtx); earlysetcpuclass(); startrtclock(); printcpuinfo(); panicifcpuunsupported(); #ifdef PERFMON perfmon_init(); #endif printf("real memory = %u (%uK bytes)\n", ptoa(Maxmem), ptoa(Maxmem) / 1024); /* * Display any holes after the first chunk of extended memory. */ if (bootverbose) { int indx; printf("Physical memory chunk(s):\n"); for (indx = 0; phys_avail[indx + 1] != 0; indx += 2) { unsigned int size1 = phys_avail[indx + 1] - phys_avail[indx]; printf("0x%08x - 0x%08x, %u bytes (%u pages)\n", phys_avail[indx], phys_avail[indx + 1] - 1, size1, size1 / PAGE_SIZE); } } /* * Calculate callout wheel size */ for (callwheelsize = 1, callwheelbits = 0; callwheelsize < ncallout; callwheelsize <<= 1, ++callwheelbits) ; callwheelmask = callwheelsize - 1; /* * Allocate space for system data structures. * The first available kernel virtual address is in "v". * As pages of kernel virtual memory are allocated, "v" is incremented. * As pages of memory are allocated and cleared, * "firstaddr" is incremented. * An index into the kernel page table corresponding to the * virtual memory address maintained in "v" is kept in "mapaddr". */ /* * Make two passes. The first pass calculates how much memory is * needed and allocates it. The second pass assigns virtual * addresses to the various data structures. */ firstaddr = 0; again: v = (caddr_t)firstaddr; #define valloc(name, type, num) \ (name) = (type *)v; v = (caddr_t)((name)+(num)) #define valloclim(name, type, num, lim) \ (name) = (type *)v; v = (caddr_t)((lim) = ((name)+(num))) valloc(callout, struct callout, ncallout); valloc(callwheel, struct callout_tailq, callwheelsize); /* * Discount the physical memory larger than the size of kernel_map * to avoid eating up all of KVA space. */ if (kernel_map->first_free == NULL) { printf("Warning: no free entries in kernel_map.\n"); physmem_est = physmem; } else physmem_est = min(physmem, kernel_map->max_offset - kernel_map->min_offset); /* * The nominal buffer size (and minimum KVA allocation) is BKVASIZE. * For the first 64MB of ram nominally allocate sufficient buffers to * cover 1/4 of our ram. Beyond the first 64MB allocate additional * buffers to cover 1/20 of our ram over 64MB. * * factor represents the 1/4 x ram conversion. */ if (nbuf == 0) { int factor = 4 * BKVASIZE / PAGE_SIZE; nbuf = 50; if (physmem_est > 1024) nbuf += min((physmem_est - 1024) / factor, 16384 / factor); if (physmem_est > 16384) nbuf += (physmem_est - 16384) * 2 / (factor * 5); } /* * Do not allow the buffer_map to be more then 1/2 the size of the * kernel_map. */ if (nbuf > (kernel_map->max_offset - kernel_map->min_offset) / (BKVASIZE * 2)) { nbuf = (kernel_map->max_offset - kernel_map->min_offset) / (BKVASIZE * 2); printf("Warning: nbufs capped at %d\n", nbuf); } nswbuf = max(min(nbuf/4, 256), 16); valloc(swbuf, struct buf, nswbuf); valloc(buf, struct buf, nbuf); v = bufhashinit(v); /* * End of first pass, size has been calculated so allocate memory */ if (firstaddr == 0) { size = (vm_size_t)(v - firstaddr); firstaddr = (int)kmem_alloc(kernel_map, round_page(size)); if (firstaddr == 0) panic("startup: no room for tables"); goto again; } /* * End of second pass, addresses have been assigned */ if ((vm_size_t)(v - firstaddr) != size) panic("startup: table size inconsistency"); clean_map = kmem_suballoc(kernel_map, &clean_sva, &clean_eva, (nbuf*BKVASIZE) + (nswbuf*MAXPHYS) + pager_map_size); buffer_map = kmem_suballoc(clean_map, &buffer_sva, &buffer_eva, (nbuf*BKVASIZE)); buffer_map->system_map = 1; pager_map = kmem_suballoc(clean_map, &pager_sva, &pager_eva, (nswbuf*MAXPHYS) + pager_map_size); pager_map->system_map = 1; exec_map = kmem_suballoc(kernel_map, &minaddr, &maxaddr, (16*(ARG_MAX+(PAGE_SIZE*3)))); mtx_unlock(&vm_mtx); /* * XXX: Mbuf system machine-specific initializations should * go here, if anywhere. */ /* * Initialize callouts */ SLIST_INIT(&callfree); for (i = 0; i < ncallout; i++) { callout_init(&callout[i], 0); callout[i].c_flags = CALLOUT_LOCAL_ALLOC; SLIST_INSERT_HEAD(&callfree, &callout[i], c_links.sle); } for (i = 0; i < callwheelsize; i++) { TAILQ_INIT(&callwheel[i]); } mtx_init(&callout_lock, "callout", MTX_SPIN | MTX_RECURSE); #if defined(USERCONFIG) userconfig(); cninit(); /* the preferred console may have changed */ #endif printf("avail memory = %u (%uK bytes)\n", ptoa(cnt.v_free_count), ptoa(cnt.v_free_count) / 1024); /* * Set up buffers, so they can be used to read disk labels. */ bufinit(); vm_pager_bufferinit(); globaldata_register(GLOBALDATA); #ifndef SMP /* For SMP, we delay the cpu_setregs() until after SMP startup. */ cpu_setregs(); #endif } /* * Send an interrupt to process. * * Stack is set up to allow sigcode stored * at top to call routine, followed by kcall * to sigreturn routine below. After sigreturn * resets the signal mask, the stack, and the * frame pointer, it returns to the user * specified pc, psl. */ static void osendsig(catcher, sig, mask, code) sig_t catcher; int sig; sigset_t *mask; u_long code; { struct osigframe sf; struct osigframe *fp; struct proc *p; struct sigacts *psp; struct trapframe *regs; int oonstack; p = curproc; PROC_LOCK(p); psp = p->p_sigacts; regs = p->p_md.md_regs; oonstack = sigonstack(regs->tf_esp); /* Allocate and validate space for the signal handler context. */ if ((p->p_flag & P_ALTSTACK) && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { fp = (struct osigframe *)(p->p_sigstk.ss_sp + p->p_sigstk.ss_size - sizeof(struct osigframe)); #if defined(COMPAT_43) || defined(COMPAT_SUNOS) p->p_sigstk.ss_flags |= SS_ONSTACK; #endif } else fp = (struct osigframe *)regs->tf_esp - 1; PROC_UNLOCK(p); /* * grow_stack() will return 0 if *fp does not fit inside the stack * and the stack can not be grown. * useracc() will return FALSE if access is denied. */ if (grow_stack(p, (int)fp) == 0 || !useracc((caddr_t)fp, sizeof(*fp), VM_PROT_WRITE)) { /* * Process has trashed its stack; give it an illegal * instruction to halt it in its tracks. */ PROC_LOCK(p); SIGACTION(p, SIGILL) = SIG_DFL; SIGDELSET(p->p_sigignore, SIGILL); SIGDELSET(p->p_sigcatch, SIGILL); SIGDELSET(p->p_sigmask, SIGILL); psignal(p, SIGILL); PROC_UNLOCK(p); return; } /* Translate the signal if appropriate. */ if (p->p_sysent->sv_sigtbl && sig <= p->p_sysent->sv_sigsize) sig = p->p_sysent->sv_sigtbl[_SIG_IDX(sig)]; /* Build the argument list for the signal handler. */ sf.sf_signum = sig; sf.sf_scp = (register_t)&fp->sf_siginfo.si_sc; PROC_LOCK(p); if (SIGISMEMBER(p->p_sigacts->ps_siginfo, sig)) { /* Signal handler installed with SA_SIGINFO. */ sf.sf_arg2 = (register_t)&fp->sf_siginfo; sf.sf_siginfo.si_signo = sig; sf.sf_siginfo.si_code = code; sf.sf_ahu.sf_action = (__osiginfohandler_t *)catcher; } else { /* Old FreeBSD-style arguments. */ sf.sf_arg2 = code; sf.sf_addr = regs->tf_err; sf.sf_ahu.sf_handler = catcher; } PROC_UNLOCK(p); /* Save most if not all of trap frame. */ sf.sf_siginfo.si_sc.sc_eax = regs->tf_eax; sf.sf_siginfo.si_sc.sc_ebx = regs->tf_ebx; sf.sf_siginfo.si_sc.sc_ecx = regs->tf_ecx; sf.sf_siginfo.si_sc.sc_edx = regs->tf_edx; sf.sf_siginfo.si_sc.sc_esi = regs->tf_esi; sf.sf_siginfo.si_sc.sc_edi = regs->tf_edi; sf.sf_siginfo.si_sc.sc_cs = regs->tf_cs; sf.sf_siginfo.si_sc.sc_ds = regs->tf_ds; sf.sf_siginfo.si_sc.sc_ss = regs->tf_ss; sf.sf_siginfo.si_sc.sc_es = regs->tf_es; sf.sf_siginfo.si_sc.sc_fs = regs->tf_fs; sf.sf_siginfo.si_sc.sc_gs = rgs(); sf.sf_siginfo.si_sc.sc_isp = regs->tf_isp; /* Build the signal context to be used by osigreturn(). */ sf.sf_siginfo.si_sc.sc_onstack = (oonstack) ? 1 : 0; SIG2OSIG(*mask, sf.sf_siginfo.si_sc.sc_mask); sf.sf_siginfo.si_sc.sc_sp = regs->tf_esp; sf.sf_siginfo.si_sc.sc_fp = regs->tf_ebp; sf.sf_siginfo.si_sc.sc_pc = regs->tf_eip; sf.sf_siginfo.si_sc.sc_ps = regs->tf_eflags; sf.sf_siginfo.si_sc.sc_trapno = regs->tf_trapno; sf.sf_siginfo.si_sc.sc_err = regs->tf_err; /* * If we're a vm86 process, we want to save the segment registers. * We also change eflags to be our emulated eflags, not the actual * eflags. */ if (regs->tf_eflags & PSL_VM) { /* XXX confusing names: `tf' isn't a trapframe; `regs' is. */ struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86 = &p->p_addr->u_pcb.pcb_ext->ext_vm86; sf.sf_siginfo.si_sc.sc_gs = tf->tf_vm86_gs; sf.sf_siginfo.si_sc.sc_fs = tf->tf_vm86_fs; sf.sf_siginfo.si_sc.sc_es = tf->tf_vm86_es; sf.sf_siginfo.si_sc.sc_ds = tf->tf_vm86_ds; if (vm86->vm86_has_vme == 0) sf.sf_siginfo.si_sc.sc_ps = (tf->tf_eflags & ~(PSL_VIF | PSL_VIP)) | (vm86->vm86_eflags & (PSL_VIF | PSL_VIP)); /* See sendsig() for comments. */ tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_T | PSL_VIF | PSL_VIP); } /* Copy the sigframe out to the user's stack. */ if (copyout(&sf, fp, sizeof(*fp)) != 0) { /* * Something is wrong with the stack pointer. * ...Kill the process. */ PROC_LOCK(p); sigexit(p, SIGILL); /* NOTREACHED */ } regs->tf_esp = (int)fp; regs->tf_eip = PS_STRINGS - szosigcode; regs->tf_cs = _ucodesel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_fs = _udatasel; load_gs(_udatasel); regs->tf_ss = _udatasel; } void sendsig(catcher, sig, mask, code) sig_t catcher; int sig; sigset_t *mask; u_long code; { struct sigframe sf; struct proc *p; struct sigacts *psp; struct trapframe *regs; struct sigframe *sfp; int oonstack; p = curproc; PROC_LOCK(p); psp = p->p_sigacts; if (SIGISMEMBER(psp->ps_osigset, sig)) { PROC_UNLOCK(p); osendsig(catcher, sig, mask, code); return; } regs = p->p_md.md_regs; oonstack = sigonstack(regs->tf_esp); /* Save user context. */ bzero(&sf, sizeof(sf)); sf.sf_uc.uc_sigmask = *mask; sf.sf_uc.uc_stack = p->p_sigstk; sf.sf_uc.uc_stack.ss_flags = (p->p_flag & P_ALTSTACK) ? ((oonstack) ? SS_ONSTACK : 0) : SS_DISABLE; sf.sf_uc.uc_mcontext.mc_onstack = (oonstack) ? 1 : 0; sf.sf_uc.uc_mcontext.mc_gs = rgs(); bcopy(regs, &sf.sf_uc.uc_mcontext.mc_fs, sizeof(*regs)); /* Allocate and validate space for the signal handler context. */ if ((p->p_flag & P_ALTSTACK) != 0 && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { sfp = (struct sigframe *)(p->p_sigstk.ss_sp + p->p_sigstk.ss_size - sizeof(struct sigframe)); #if defined(COMPAT_43) || defined(COMPAT_SUNOS) p->p_sigstk.ss_flags |= SS_ONSTACK; #endif } else sfp = (struct sigframe *)regs->tf_esp - 1; PROC_UNLOCK(p); /* * grow_stack() will return 0 if *sfp does not fit inside the stack * and the stack can not be grown. * useracc() will return FALSE if access is denied. */ if (grow_stack(p, (int)sfp) == 0 || !useracc((caddr_t)sfp, sizeof(*sfp), VM_PROT_WRITE)) { /* * Process has trashed its stack; give it an illegal * instruction to halt it in its tracks. */ #ifdef DEBUG printf("process %d has trashed its stack\n", p->p_pid); #endif PROC_LOCK(p); SIGACTION(p, SIGILL) = SIG_DFL; SIGDELSET(p->p_sigignore, SIGILL); SIGDELSET(p->p_sigcatch, SIGILL); SIGDELSET(p->p_sigmask, SIGILL); psignal(p, SIGILL); PROC_UNLOCK(p); return; } /* Translate the signal if appropriate. */ if (p->p_sysent->sv_sigtbl && sig <= p->p_sysent->sv_sigsize) sig = p->p_sysent->sv_sigtbl[_SIG_IDX(sig)]; /* Build the argument list for the signal handler. */ sf.sf_signum = sig; sf.sf_ucontext = (register_t)&sfp->sf_uc; PROC_LOCK(p); if (SIGISMEMBER(p->p_sigacts->ps_siginfo, sig)) { /* Signal handler installed with SA_SIGINFO. */ sf.sf_siginfo = (register_t)&sfp->sf_si; sf.sf_ahu.sf_action = (__siginfohandler_t *)catcher; /* Fill siginfo structure. */ sf.sf_si.si_signo = sig; sf.sf_si.si_code = code; sf.sf_si.si_addr = (void *)regs->tf_err; } else { /* Old FreeBSD-style arguments. */ sf.sf_siginfo = code; sf.sf_addr = regs->tf_err; sf.sf_ahu.sf_handler = catcher; } PROC_UNLOCK(p); /* * If we're a vm86 process, we want to save the segment registers. * We also change eflags to be our emulated eflags, not the actual * eflags. */ if (regs->tf_eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86 = &p->p_addr->u_pcb.pcb_ext->ext_vm86; sf.sf_uc.uc_mcontext.mc_gs = tf->tf_vm86_gs; sf.sf_uc.uc_mcontext.mc_fs = tf->tf_vm86_fs; sf.sf_uc.uc_mcontext.mc_es = tf->tf_vm86_es; sf.sf_uc.uc_mcontext.mc_ds = tf->tf_vm86_ds; if (vm86->vm86_has_vme == 0) sf.sf_uc.uc_mcontext.mc_eflags = (tf->tf_eflags & ~(PSL_VIF | PSL_VIP)) | (vm86->vm86_eflags & (PSL_VIF | PSL_VIP)); /* * We should never have PSL_T set when returning from vm86 * mode. It may be set here if we deliver a signal before * getting to vm86 mode, so turn it off. * * Clear PSL_NT to inhibit T_TSSFLT faults on return from * syscalls made by the signal handler. This just avoids * wasting time for our lazy fixup of such faults. PSL_NT * does nothing in vm86 mode, but vm86 programs can set it * almost legitimately in probes for old cpu types. */ tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_T | PSL_VIF | PSL_VIP); } /* Copy the sigframe out to the user's stack. */ if (copyout(&sf, sfp, sizeof(*sfp)) != 0) { /* * Something is wrong with the stack pointer. * ...Kill the process. */ PROC_LOCK(p); sigexit(p, SIGILL); /* NOTREACHED */ } regs->tf_esp = (int)sfp; regs->tf_eip = PS_STRINGS - *(p->p_sysent->sv_szsigcode); regs->tf_cs = _ucodesel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_fs = _udatasel; regs->tf_ss = _udatasel; } /* * System call to cleanup state after a signal * has been taken. Reset signal mask and * stack state from context left by sendsig (above). * 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 * state to gain improper privileges. */ int osigreturn(p, uap) struct proc *p; struct osigreturn_args /* { struct osigcontext *sigcntxp; } */ *uap; { struct trapframe *regs; struct osigcontext *scp; int eflags; regs = p->p_md.md_regs; scp = uap->sigcntxp; if (!useracc((caddr_t)scp, sizeof(*scp), VM_PROT_READ)) return (EFAULT); eflags = scp->sc_ps; if (eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86; /* * if pcb_ext == 0 or vm86_inited == 0, the user hasn't * set up the vm86 area, and we can't enter vm86 mode. */ if (p->p_addr->u_pcb.pcb_ext == 0) return (EINVAL); vm86 = &p->p_addr->u_pcb.pcb_ext->ext_vm86; if (vm86->vm86_inited == 0) return (EINVAL); /* Go back to user mode if both flags are set. */ if ((eflags & PSL_VIP) && (eflags & PSL_VIF)) trapsignal(p, SIGBUS, 0); if (vm86->vm86_has_vme) { eflags = (tf->tf_eflags & ~VME_USERCHANGE) | (eflags & VME_USERCHANGE) | PSL_VM; } else { vm86->vm86_eflags = eflags; /* save VIF, VIP */ eflags = (tf->tf_eflags & ~VM_USERCHANGE) | (eflags & VM_USERCHANGE) | PSL_VM; } tf->tf_vm86_ds = scp->sc_ds; tf->tf_vm86_es = scp->sc_es; tf->tf_vm86_fs = scp->sc_fs; tf->tf_vm86_gs = scp->sc_gs; tf->tf_ds = _udatasel; tf->tf_es = _udatasel; tf->tf_fs = _udatasel; } else { /* * Don't allow users to change privileged or reserved flags. */ /* * XXX do allow users to change the privileged flag PSL_RF. * The cpu sets PSL_RF in tf_eflags for faults. Debuggers * should sometimes set it there too. tf_eflags is kept in * the signal context during signal handling and there is no * other place to remember it, so the PSL_RF bit may be * corrupted by the signal handler without us knowing. * Corruption of the PSL_RF bit at worst causes one more or * one less debugger trap, so allowing it is fairly harmless. */ if (!EFL_SECURE(eflags & ~PSL_RF, regs->tf_eflags & ~PSL_RF)) { return (EINVAL); } /* * Don't allow users to load a valid privileged %cs. Let the * hardware check for invalid selectors, excess privilege in * other selectors, invalid %eip's and invalid %esp's. */ if (!CS_SECURE(scp->sc_cs)) { trapsignal(p, SIGBUS, T_PROTFLT); return (EINVAL); } regs->tf_ds = scp->sc_ds; regs->tf_es = scp->sc_es; regs->tf_fs = scp->sc_fs; } /* Restore remaining registers. */ regs->tf_eax = scp->sc_eax; regs->tf_ebx = scp->sc_ebx; regs->tf_ecx = scp->sc_ecx; regs->tf_edx = scp->sc_edx; regs->tf_esi = scp->sc_esi; regs->tf_edi = scp->sc_edi; regs->tf_cs = scp->sc_cs; regs->tf_ss = scp->sc_ss; regs->tf_isp = scp->sc_isp; PROC_LOCK(p); #if defined(COMPAT_43) || defined(COMPAT_SUNOS) if (scp->sc_onstack & 1) p->p_sigstk.ss_flags |= SS_ONSTACK; else p->p_sigstk.ss_flags &= ~SS_ONSTACK; #endif SIGSETOLD(p->p_sigmask, scp->sc_mask); SIG_CANTMASK(p->p_sigmask); PROC_UNLOCK(p); regs->tf_ebp = scp->sc_fp; regs->tf_esp = scp->sc_sp; regs->tf_eip = scp->sc_pc; regs->tf_eflags = eflags; return (EJUSTRETURN); } int sigreturn(p, uap) struct proc *p; struct sigreturn_args /* { ucontext_t *sigcntxp; } */ *uap; { struct trapframe *regs; ucontext_t *ucp; int cs, eflags; ucp = uap->sigcntxp; if (!useracc((caddr_t)ucp, sizeof(struct osigcontext), VM_PROT_READ)) return (EFAULT); if (((struct osigcontext *)ucp)->sc_trapno == 0x01d516) return (osigreturn(p, (struct osigreturn_args *)uap)); /* * Since ucp is not an osigcontext but a ucontext_t, we have to * check again if all of it is accessible. A ucontext_t is * much larger, so instead of just checking for the pointer * being valid for the size of an osigcontext, now check for * it being valid for a whole, new-style ucontext_t. */ if (!useracc((caddr_t)ucp, sizeof(*ucp), VM_PROT_READ)) return (EFAULT); regs = p->p_md.md_regs; eflags = ucp->uc_mcontext.mc_eflags; if (eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86; /* * if pcb_ext == 0 or vm86_inited == 0, the user hasn't * set up the vm86 area, and we can't enter vm86 mode. */ if (p->p_addr->u_pcb.pcb_ext == 0) return (EINVAL); vm86 = &p->p_addr->u_pcb.pcb_ext->ext_vm86; if (vm86->vm86_inited == 0) return (EINVAL); /* Go back to user mode if both flags are set. */ if ((eflags & PSL_VIP) && (eflags & PSL_VIF)) trapsignal(p, SIGBUS, 0); if (vm86->vm86_has_vme) { eflags = (tf->tf_eflags & ~VME_USERCHANGE) | (eflags & VME_USERCHANGE) | PSL_VM; } else { vm86->vm86_eflags = eflags; /* save VIF, VIP */ eflags = (tf->tf_eflags & ~VM_USERCHANGE) | (eflags & VM_USERCHANGE) | PSL_VM; } bcopy(&ucp->uc_mcontext.mc_fs, tf, sizeof(struct trapframe)); tf->tf_eflags = eflags; tf->tf_vm86_ds = tf->tf_ds; tf->tf_vm86_es = tf->tf_es; tf->tf_vm86_fs = tf->tf_fs; tf->tf_vm86_gs = ucp->uc_mcontext.mc_gs; tf->tf_ds = _udatasel; tf->tf_es = _udatasel; tf->tf_fs = _udatasel; } else { /* * Don't allow users to change privileged or reserved flags. */ /* * XXX do allow users to change the privileged flag PSL_RF. * The cpu sets PSL_RF in tf_eflags for faults. Debuggers * should sometimes set it there too. tf_eflags is kept in * the signal context during signal handling and there is no * other place to remember it, so the PSL_RF bit may be * corrupted by the signal handler without us knowing. * Corruption of the PSL_RF bit at worst causes one more or * one less debugger trap, so allowing it is fairly harmless. */ if (!EFL_SECURE(eflags & ~PSL_RF, regs->tf_eflags & ~PSL_RF)) { printf("sigreturn: eflags = 0x%x\n", eflags); return (EINVAL); } /* * Don't allow users to load a valid privileged %cs. Let the * hardware check for invalid selectors, excess privilege in * other selectors, invalid %eip's and invalid %esp's. */ cs = ucp->uc_mcontext.mc_cs; if (!CS_SECURE(cs)) { printf("sigreturn: cs = 0x%x\n", cs); trapsignal(p, SIGBUS, T_PROTFLT); return (EINVAL); } bcopy(&ucp->uc_mcontext.mc_fs, regs, sizeof(*regs)); } PROC_LOCK(p); #if defined(COMPAT_43) || defined(COMPAT_SUNOS) if (ucp->uc_mcontext.mc_onstack & 1) p->p_sigstk.ss_flags |= SS_ONSTACK; else p->p_sigstk.ss_flags &= ~SS_ONSTACK; #endif p->p_sigmask = ucp->uc_sigmask; SIG_CANTMASK(p->p_sigmask); PROC_UNLOCK(p); return (EJUSTRETURN); } /* * Machine dependent boot() routine * * I haven't seen anything to put here yet * Possibly some stuff might be grafted back here from boot() */ void cpu_boot(int howto) { } /* * Shutdown the CPU as much as possible */ void cpu_halt(void) { for (;;) __asm__ ("hlt"); } /* * Hook to idle the CPU when possible. This currently only works in * the !SMP case, as there is no clean way to ensure that a CPU will be * woken when there is work available for it. */ static int cpu_idle_hlt = 1; SYSCTL_INT(_machdep, OID_AUTO, cpu_idle_hlt, CTLFLAG_RW, &cpu_idle_hlt, 0, "Idle loop HLT enable"); /* * Note that we have to be careful here to avoid a race between checking * procrunnable() and actually halting. If we don't do this, we may waste * the time between calling hlt and the next interrupt even though there * is a runnable process. */ void cpu_idle(void) { #ifndef SMP if (cpu_idle_hlt) { disable_intr(); if (procrunnable()) enable_intr(); else { enable_intr(); __asm __volatile("hlt"); } } #endif } /* * Clear registers on exec */ void setregs(p, entry, stack, ps_strings) struct proc *p; u_long entry; u_long stack; u_long ps_strings; { struct trapframe *regs = p->p_md.md_regs; struct pcb *pcb = &p->p_addr->u_pcb; if (pcb->pcb_ldt) user_ldt_free(pcb); bzero((char *)regs, sizeof(struct trapframe)); regs->tf_eip = entry; regs->tf_esp = stack; regs->tf_eflags = PSL_USER | (regs->tf_eflags & PSL_T); regs->tf_ss = _udatasel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_fs = _udatasel; regs->tf_cs = _ucodesel; /* PS_STRINGS value for BSD/OS binaries. It is 0 for non-BSD/OS. */ regs->tf_ebx = ps_strings; /* reset %gs as well */ if (pcb == PCPU_GET(curpcb)) load_gs(_udatasel); else pcb->pcb_gs = _udatasel; /* * Reset the hardware debug registers if they were in use. * They won't have any meaning for the newly exec'd process. */ if (pcb->pcb_flags & PCB_DBREGS) { pcb->pcb_dr0 = 0; pcb->pcb_dr1 = 0; pcb->pcb_dr2 = 0; pcb->pcb_dr3 = 0; pcb->pcb_dr6 = 0; pcb->pcb_dr7 = 0; if (pcb == PCPU_GET(curpcb)) { /* * Clear the debug registers on the running * CPU, otherwise they will end up affecting * the next process we switch to. */ reset_dbregs(); } pcb->pcb_flags &= ~PCB_DBREGS; } /* * Initialize the math emulator (if any) for the current process. * Actually, just clear the bit that says that the emulator has * been initialized. Initialization is delayed until the process * traps to the emulator (if it is done at all) mainly because * emulators don't provide an entry point for initialization. */ p->p_addr->u_pcb.pcb_flags &= ~FP_SOFTFP; /* * Arrange to trap the next npx or `fwait' instruction (see npx.c * for why fwait must be trapped at least if there is an npx or an * emulator). This is mainly to handle the case where npx0 is not * configured, since the npx routines normally set up the trap * otherwise. It should be done only at boot time, but doing it * here allows modifying `npx_exists' for testing the emulator on * systems with an npx. */ load_cr0(rcr0() | CR0_MP | CR0_TS); #ifdef DEV_NPX /* Initialize the npx (if any) for the current process. */ npxinit(__INITIAL_NPXCW__); #endif /* * XXX - Linux emulator * Make sure sure edx is 0x0 on entry. Linux binaries depend * on it. */ p->p_retval[1] = 0; } void cpu_setregs(void) { unsigned int cr0; cr0 = rcr0(); cr0 |= CR0_NE; /* Done by npxinit() */ cr0 |= CR0_MP | CR0_TS; /* Done at every execve() too. */ #ifndef I386_CPU cr0 |= CR0_WP | CR0_AM; #endif load_cr0(cr0); load_gs(_udatasel); } static int sysctl_machdep_adjkerntz(SYSCTL_HANDLER_ARGS) { int error; error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); if (!error && req->newptr) resettodr(); return (error); } SYSCTL_PROC(_machdep, CPU_ADJKERNTZ, adjkerntz, CTLTYPE_INT|CTLFLAG_RW, &adjkerntz, 0, sysctl_machdep_adjkerntz, "I", ""); SYSCTL_INT(_machdep, CPU_DISRTCSET, disable_rtc_set, CTLFLAG_RW, &disable_rtc_set, 0, ""); SYSCTL_STRUCT(_machdep, CPU_BOOTINFO, bootinfo, CTLFLAG_RD, &bootinfo, bootinfo, ""); SYSCTL_INT(_machdep, CPU_WALLCLOCK, wall_cmos_clock, CTLFLAG_RW, &wall_cmos_clock, 0, ""); /* * Initialize 386 and configure to run kernel */ /* * Initialize segments & interrupt table */ int _default_ldt; union descriptor gdt[NGDT * MAXCPU]; /* global descriptor table */ static struct gate_descriptor idt0[NIDT]; struct gate_descriptor *idt = &idt0[0]; /* interrupt descriptor table */ union descriptor ldt[NLDT]; /* local descriptor table */ #ifdef SMP /* table descriptors - used to load tables by microp */ struct region_descriptor r_gdt, r_idt; #endif int private_tss; /* flag indicating private tss */ #if defined(I586_CPU) && !defined(NO_F00F_HACK) extern int has_f00f_bug; #endif static struct i386tss dblfault_tss; static char dblfault_stack[PAGE_SIZE]; extern struct user *proc0paddr; /* software prototypes -- in more palatable form */ struct soft_segment_descriptor gdt_segs[] = { /* GNULL_SEL 0 Null Descriptor */ { 0x0, /* segment base address */ 0x0, /* length */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GCODE_SEL 1 Code Descriptor for kernel */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GDATA_SEL 2 Data Descriptor for kernel */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GPRIV_SEL 3 SMP Per-Processor Private Data Descriptor */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GPROC0_SEL 4 Proc 0 Tss Descriptor */ { 0x0, /* segment base address */ sizeof(struct i386tss)-1,/* length - all address space */ SDT_SYS386TSS, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GLDT_SEL 5 LDT Descriptor */ { (int) ldt, /* segment base address */ sizeof(ldt)-1, /* length - all address space */ SDT_SYSLDT, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GUSERLDT_SEL 6 User LDT Descriptor per process */ { (int) ldt, /* segment base address */ (512 * sizeof(union descriptor)-1), /* length */ SDT_SYSLDT, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GTGATE_SEL 7 Null Descriptor - Placeholder */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GBIOSLOWMEM_SEL 8 BIOS access to realmode segment 0x40, must be #8 in GDT */ { 0x400, /* segment base address */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GPANIC_SEL 9 Panic Tss Descriptor */ { (int) &dblfault_tss, /* segment base address */ sizeof(struct i386tss)-1,/* length - all address space */ SDT_SYS386TSS, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GBIOSCODE32_SEL 10 BIOS 32-bit interface (32bit Code) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSCODE16_SEL 11 BIOS 32-bit interface (16bit Code) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSDATA_SEL 12 BIOS 32-bit interface (Data) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSUTIL_SEL 13 BIOS 16-bit interface (Utility) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSARGS_SEL 14 BIOS 16-bit interface (Arguments) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, }; static struct soft_segment_descriptor ldt_segs[] = { /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Code Descriptor for user */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMERA, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Data Descriptor for user */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, }; void setidt(idx, func, typ, dpl, selec) int idx; inthand_t *func; int typ; int dpl; int selec; { struct gate_descriptor *ip; ip = idt + idx; ip->gd_looffset = (int)func; ip->gd_selector = selec; ip->gd_stkcpy = 0; ip->gd_xx = 0; ip->gd_type = typ; ip->gd_dpl = dpl; ip->gd_p = 1; ip->gd_hioffset = ((int)func)>>16 ; } #define IDTVEC(name) __CONCAT(X,name) extern inthand_t IDTVEC(div), IDTVEC(dbg), IDTVEC(nmi), IDTVEC(bpt), IDTVEC(ofl), IDTVEC(bnd), IDTVEC(ill), IDTVEC(dna), IDTVEC(fpusegm), IDTVEC(tss), IDTVEC(missing), IDTVEC(stk), IDTVEC(prot), IDTVEC(page), IDTVEC(mchk), IDTVEC(rsvd), IDTVEC(fpu), IDTVEC(align), IDTVEC(lcall_syscall), IDTVEC(int0x80_syscall); void sdtossd(sd, ssd) struct segment_descriptor *sd; struct soft_segment_descriptor *ssd; { ssd->ssd_base = (sd->sd_hibase << 24) | sd->sd_lobase; ssd->ssd_limit = (sd->sd_hilimit << 16) | sd->sd_lolimit; ssd->ssd_type = sd->sd_type; ssd->ssd_dpl = sd->sd_dpl; ssd->ssd_p = sd->sd_p; ssd->ssd_def32 = sd->sd_def32; ssd->ssd_gran = sd->sd_gran; } #define PHYSMAP_SIZE (2 * 8) /* * Populate the (physmap) array with base/bound pairs describing the * available physical memory in the system, then test this memory and * build the phys_avail array describing the actually-available memory. * * If we cannot accurately determine the physical memory map, then use * value from the 0xE801 call, and failing that, the RTC. * * Total memory size may be set by the kernel environment variable * hw.physmem or the compile-time define MAXMEM. */ static void getmemsize(int first) { int i, physmap_idx, pa_indx; u_int basemem, extmem; struct vm86frame vmf; struct vm86context vmc; vm_offset_t pa, physmap[PHYSMAP_SIZE]; pt_entry_t pte; const char *cp; struct bios_smap *smap; bzero(&vmf, sizeof(struct vm86frame)); bzero(physmap, sizeof(physmap)); /* * Perform "base memory" related probes & setup */ vm86_intcall(0x12, &vmf); basemem = vmf.vmf_ax; if (basemem > 640) { printf("Preposterous BIOS basemem of %uK, truncating to 640K\n", basemem); basemem = 640; } /* * XXX if biosbasemem is now < 640, there is a `hole' * between the end of base memory and the start of * ISA memory. The hole may be empty or it may * contain BIOS code or data. Map it read/write so * that the BIOS can write to it. (Memory from 0 to * the physical end of the kernel is mapped read-only * to begin with and then parts of it are remapped. * The parts that aren't remapped form holes that * remain read-only and are unused by the kernel. * The base memory area is below the physical end of * the kernel and right now forms a read-only hole. * The part of it from PAGE_SIZE to * (trunc_page(biosbasemem * 1024) - 1) will be * remapped and used by the kernel later.) * * This code is similar to the code used in * pmap_mapdev, but since no memory needs to be * allocated we simply change the mapping. */ for (pa = trunc_page(basemem * 1024); pa < ISA_HOLE_START; pa += PAGE_SIZE) { pte = (pt_entry_t)vtopte(pa + KERNBASE); *pte = pa | PG_RW | PG_V; } /* * if basemem != 640, map pages r/w into vm86 page table so * that the bios can scribble on it. */ pte = (pt_entry_t)vm86paddr; for (i = basemem / 4; i < 160; i++) pte[i] = (i << PAGE_SHIFT) | PG_V | PG_RW | PG_U; /* * map page 1 R/W into the kernel page table so we can use it * as a buffer. The kernel will unmap this page later. */ pte = (pt_entry_t)vtopte(KERNBASE + (1 << PAGE_SHIFT)); *pte = (1 << PAGE_SHIFT) | PG_RW | PG_V; /* * get memory map with INT 15:E820 */ vmc.npages = 0; smap = (void *)vm86_addpage(&vmc, 1, KERNBASE + (1 << PAGE_SHIFT)); vm86_getptr(&vmc, (vm_offset_t)smap, &vmf.vmf_es, &vmf.vmf_di); physmap_idx = 0; vmf.vmf_ebx = 0; do { vmf.vmf_eax = 0xE820; vmf.vmf_edx = SMAP_SIG; vmf.vmf_ecx = sizeof(struct bios_smap); i = vm86_datacall(0x15, &vmf, &vmc); if (i || vmf.vmf_eax != SMAP_SIG) break; if (boothowto & RB_VERBOSE) printf("SMAP type=%02x base=%08x %08x len=%08x %08x\n", smap->type, *(u_int32_t *)((char *)&smap->base + 4), (u_int32_t)smap->base, *(u_int32_t *)((char *)&smap->length + 4), (u_int32_t)smap->length); if (smap->type != 0x01) goto next_run; if (smap->length == 0) goto next_run; if (smap->base >= 0xffffffff) { printf("%uK of memory above 4GB ignored\n", (u_int)(smap->length / 1024)); goto next_run; } for (i = 0; i <= physmap_idx; i += 2) { if (smap->base < physmap[i + 1]) { if (boothowto & RB_VERBOSE) printf( "Overlapping or non-montonic memory region, ignoring second region\n"); goto next_run; } } if (smap->base == physmap[physmap_idx + 1]) { physmap[physmap_idx + 1] += smap->length; goto next_run; } physmap_idx += 2; if (physmap_idx == PHYSMAP_SIZE) { printf( "Too many segments in the physical address map, giving up\n"); break; } physmap[physmap_idx] = smap->base; physmap[physmap_idx + 1] = smap->base + smap->length; next_run: } while (vmf.vmf_ebx != 0); if (physmap[1] != 0) goto physmap_done; /* * If we failed above, try memory map with INT 15:E801 */ vmf.vmf_ax = 0xE801; if (vm86_intcall(0x15, &vmf) == 0) { extmem = vmf.vmf_cx + vmf.vmf_dx * 64; } else { #if 0 vmf.vmf_ah = 0x88; vm86_intcall(0x15, &vmf); extmem = vmf.vmf_ax; #else /* * Prefer the RTC value for extended memory. */ extmem = rtcin(RTC_EXTLO) + (rtcin(RTC_EXTHI) << 8); #endif } /* * Special hack for chipsets that still remap the 384k hole when * there's 16MB of memory - this really confuses people that * are trying to use bus mastering ISA controllers with the * "16MB limit"; they only have 16MB, but the remapping puts * them beyond the limit. * * If extended memory is between 15-16MB (16-17MB phys address range), * chop it to 15MB. */ if ((extmem > 15 * 1024) && (extmem < 16 * 1024)) extmem = 15 * 1024; physmap[0] = 0; physmap[1] = basemem * 1024; physmap_idx = 2; physmap[physmap_idx] = 0x100000; physmap[physmap_idx + 1] = physmap[physmap_idx] + extmem * 1024; physmap_done: /* * Now, physmap contains a map of physical memory. */ #ifdef SMP /* make hole for AP bootstrap code */ physmap[1] = mp_bootaddress(physmap[1] / 1024); /* look for the MP hardware - needed for apic addresses */ i386_mp_probe(); #endif /* * Maxmem isn't the "maximum memory", it's one larger than the * highest page of the physical address space. It should be * called something like "Maxphyspage". We may adjust this * based on ``hw.physmem'' and the results of the memory test. */ Maxmem = atop(physmap[physmap_idx + 1]); #ifdef MAXMEM Maxmem = MAXMEM / 4; #endif /* * hw.maxmem is a size in bytes; we also allow k, m, and g suffixes * for the appropriate modifiers. This overrides MAXMEM. */ if ((cp = getenv("hw.physmem")) != NULL) { u_int64_t AllowMem, sanity; char *ep; sanity = AllowMem = strtouq(cp, &ep, 0); if ((ep != cp) && (*ep != 0)) { switch(*ep) { case 'g': case 'G': AllowMem <<= 10; case 'm': case 'M': AllowMem <<= 10; case 'k': case 'K': AllowMem <<= 10; break; default: AllowMem = sanity = 0; } if (AllowMem < sanity) AllowMem = 0; } if (AllowMem == 0) printf("Ignoring invalid memory size of '%s'\n", cp); else Maxmem = atop(AllowMem); } if (atop(physmap[physmap_idx + 1]) != Maxmem && (boothowto & RB_VERBOSE)) printf("Physical memory use set to %uK\n", Maxmem * 4); /* * If Maxmem has been increased beyond what the system has detected, * extend the last memory segment to the new limit. */ if (atop(physmap[physmap_idx + 1]) < Maxmem) physmap[physmap_idx + 1] = ptoa(Maxmem); /* call pmap initialization to make new kernel address space */ pmap_bootstrap(first, 0); /* * Size up each available chunk of physical memory. */ physmap[0] = PAGE_SIZE; /* mask off page 0 */ pa_indx = 0; phys_avail[pa_indx++] = physmap[0]; phys_avail[pa_indx] = physmap[0]; #if 0 pte = (pt_entry_t)vtopte(KERNBASE); #else pte = (pt_entry_t)CMAP1; #endif /* * physmap is in bytes, so when converting to page boundaries, * round up the start address and round down the end address. */ for (i = 0; i <= physmap_idx; i += 2) { vm_offset_t end; end = ptoa(Maxmem); if (physmap[i + 1] < end) end = trunc_page(physmap[i + 1]); for (pa = round_page(physmap[i]); pa < end; pa += PAGE_SIZE) { int tmp, page_bad; #if 0 int *ptr = 0; #else int *ptr = (int *)CADDR1; #endif /* * block out kernel memory as not available. */ if (pa >= 0x100000 && pa < first) continue; page_bad = FALSE; /* * map page into kernel: valid, read/write,non-cacheable */ *pte = pa | PG_V | PG_RW | PG_N; invltlb(); tmp = *(int *)ptr; /* * Test for alternating 1's and 0's */ *(volatile int *)ptr = 0xaaaaaaaa; if (*(volatile int *)ptr != 0xaaaaaaaa) { page_bad = TRUE; } /* * Test for alternating 0's and 1's */ *(volatile int *)ptr = 0x55555555; if (*(volatile int *)ptr != 0x55555555) { page_bad = TRUE; } /* * Test for all 1's */ *(volatile int *)ptr = 0xffffffff; if (*(volatile int *)ptr != 0xffffffff) { page_bad = TRUE; } /* * Test for all 0's */ *(volatile int *)ptr = 0x0; if (*(volatile int *)ptr != 0x0) { page_bad = TRUE; } /* * Restore original value. */ *(int *)ptr = tmp; /* * Adjust array of valid/good pages. */ if (page_bad == TRUE) { continue; } /* * If this good page is a continuation of the * previous set of good pages, then just increase * the end pointer. Otherwise start a new chunk. * Note that "end" points one higher than end, * making the range >= start and < end. * If we're also doing a speculative memory * test and we at or past the end, bump up Maxmem * so that we keep going. The first bad page * will terminate the loop. */ if (phys_avail[pa_indx] == pa) { phys_avail[pa_indx] += PAGE_SIZE; } else { pa_indx++; if (pa_indx == PHYS_AVAIL_ARRAY_END) { printf("Too many holes in the physical address space, giving up\n"); pa_indx--; break; } phys_avail[pa_indx++] = pa; /* start */ phys_avail[pa_indx] = pa + PAGE_SIZE; /* end */ } physmem++; } } *pte = 0; invltlb(); /* * XXX * The last chunk must contain at least one page plus the message * buffer to avoid complicating other code (message buffer address * calculation, etc.). */ while (phys_avail[pa_indx - 1] + PAGE_SIZE + round_page(MSGBUF_SIZE) >= phys_avail[pa_indx]) { physmem -= atop(phys_avail[pa_indx] - phys_avail[pa_indx - 1]); phys_avail[pa_indx--] = 0; phys_avail[pa_indx--] = 0; } Maxmem = atop(phys_avail[pa_indx]); /* Trim off space for the message buffer. */ phys_avail[pa_indx] -= round_page(MSGBUF_SIZE); avail_end = phys_avail[pa_indx]; } void init386(first) int first; { int x; struct gate_descriptor *gdp; int gsel_tss; #ifndef SMP /* table descriptors - used to load tables by microp */ struct region_descriptor r_gdt, r_idt; #endif int off; proc0.p_addr = proc0paddr; atdevbase = ISA_HOLE_START + KERNBASE; if (bootinfo.bi_modulep) { preload_metadata = (caddr_t)bootinfo.bi_modulep + KERNBASE; preload_bootstrap_relocate(KERNBASE); } else { printf("WARNING: loader(8) metadata is missing!\n"); } if (bootinfo.bi_envp) kern_envp = (caddr_t)bootinfo.bi_envp + KERNBASE; /* * make gdt memory segments, the code segment goes up to end of the * page with etext in it, the data segment goes to the end of * the address space */ /* * XXX text protection is temporarily (?) disabled. The limit was * i386_btop(round_page(etext)) - 1. */ gdt_segs[GCODE_SEL].ssd_limit = i386_btop(0) - 1; gdt_segs[GDATA_SEL].ssd_limit = i386_btop(0) - 1; #ifdef SMP gdt_segs[GPRIV_SEL].ssd_limit = i386_btop(sizeof(struct privatespace)) - 1; gdt_segs[GPRIV_SEL].ssd_base = (int) &SMP_prvspace[0]; gdt_segs[GPROC0_SEL].ssd_base = (int) &SMP_prvspace[0].globaldata.gd_common_tss; SMP_prvspace[0].globaldata.gd_prvspace = &SMP_prvspace[0].globaldata; #else gdt_segs[GPRIV_SEL].ssd_limit = i386_btop(sizeof(struct globaldata)) - 1; gdt_segs[GPRIV_SEL].ssd_base = (int) &__globaldata; gdt_segs[GPROC0_SEL].ssd_base = (int) &__globaldata.gd_common_tss; __globaldata.gd_prvspace = &__globaldata; #endif for (x = 0; x < NGDT; x++) { #ifdef BDE_DEBUGGER /* avoid overwriting db entries with APM ones */ if (x >= GAPMCODE32_SEL && x <= GAPMDATA_SEL) continue; #endif ssdtosd(&gdt_segs[x], &gdt[x].sd); } r_gdt.rd_limit = NGDT * sizeof(gdt[0]) - 1; r_gdt.rd_base = (int) gdt; lgdt(&r_gdt); /* setup curproc so that mutexes work */ PCPU_SET(curproc, &proc0); PCPU_SET(spinlocks, NULL); LIST_INIT(&proc0.p_contested); mtx_init(&sched_lock, "sched lock", MTX_SPIN | MTX_RECURSE); #ifdef SMP /* * Interrupts can happen very early, so initialize imen_mtx here, rather * than in init_locks(). */ mtx_init(&imen_mtx, "imen", MTX_SPIN); #endif /* * Giant is used early for at least debugger traps and unexpected traps. */ mtx_init(&Giant, "Giant", MTX_DEF | MTX_RECURSE); mtx_init(&proc0.p_mtx, "process lock", MTX_DEF); mtx_lock(&Giant); /* make ldt memory segments */ /* * The data segment limit must not cover the user area because we * don't want the user area to be writable in copyout() etc. (page * level protection is lost in kernel mode on 386's). Also, we * don't want the user area to be writable directly (page level * protection of the user area is not available on 486's with * CR0_WP set, because there is no user-read/kernel-write mode). * * XXX - VM_MAXUSER_ADDRESS is an end address, not a max. And it * should be spelled ...MAX_USER... */ #define VM_END_USER_RW_ADDRESS VM_MAXUSER_ADDRESS /* * The code segment limit has to cover the user area until we move * the signal trampoline out of the user area. This is safe because * the code segment cannot be written to directly. */ #define VM_END_USER_R_ADDRESS (VM_END_USER_RW_ADDRESS + UPAGES * PAGE_SIZE) ldt_segs[LUCODE_SEL].ssd_limit = i386_btop(VM_END_USER_R_ADDRESS) - 1; ldt_segs[LUDATA_SEL].ssd_limit = i386_btop(VM_END_USER_RW_ADDRESS) - 1; for (x = 0; x < sizeof ldt_segs / sizeof ldt_segs[0]; x++) ssdtosd(&ldt_segs[x], &ldt[x].sd); _default_ldt = GSEL(GLDT_SEL, SEL_KPL); lldt(_default_ldt); PCPU_SET(currentldt, _default_ldt); /* exceptions */ for (x = 0; x < NIDT; x++) setidt(x, &IDTVEC(rsvd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(0, &IDTVEC(div), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(1, &IDTVEC(dbg), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(2, &IDTVEC(nmi), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(3, &IDTVEC(bpt), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(4, &IDTVEC(ofl), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(5, &IDTVEC(bnd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(6, &IDTVEC(ill), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(7, &IDTVEC(dna), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(8, 0, SDT_SYSTASKGT, SEL_KPL, GSEL(GPANIC_SEL, SEL_KPL)); setidt(9, &IDTVEC(fpusegm), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(10, &IDTVEC(tss), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(11, &IDTVEC(missing), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(12, &IDTVEC(stk), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(13, &IDTVEC(prot), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(14, &IDTVEC(page), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(15, &IDTVEC(rsvd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(16, &IDTVEC(fpu), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(17, &IDTVEC(align), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(18, &IDTVEC(mchk), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(0x80, &IDTVEC(int0x80_syscall), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); r_idt.rd_limit = sizeof(idt0) - 1; r_idt.rd_base = (int) idt; lidt(&r_idt); /* * We need this mutex before the console probe. */ mtx_init(&clock_lock, "clk", MTX_SPIN | MTX_RECURSE); /* * Initialize the console before we print anything out. */ cninit(); #ifdef DEV_ISA isa_defaultirq(); #endif #ifdef DDB kdb_init(); if (boothowto & RB_KDB) Debugger("Boot flags requested debugger"); #endif finishidentcpu(); /* Final stage of CPU initialization */ setidt(6, &IDTVEC(ill), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(13, &IDTVEC(prot), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); initializecpu(); /* Initialize CPU registers */ /* make an initial tss so cpu can get interrupt stack on syscall! */ PCPU_SET(common_tss.tss_esp0, (int) proc0.p_addr + UPAGES*PAGE_SIZE - 16); PCPU_SET(common_tss.tss_ss0, GSEL(GDATA_SEL, SEL_KPL)); gsel_tss = GSEL(GPROC0_SEL, SEL_KPL); private_tss = 0; PCPU_SET(tss_gdt, &gdt[GPROC0_SEL].sd); PCPU_SET(common_tssd, *PCPU_GET(tss_gdt)); PCPU_SET(common_tss.tss_ioopt, (sizeof (struct i386tss)) << 16); ltr(gsel_tss); dblfault_tss.tss_esp = dblfault_tss.tss_esp0 = dblfault_tss.tss_esp1 = dblfault_tss.tss_esp2 = (int) &dblfault_stack[sizeof(dblfault_stack)]; dblfault_tss.tss_ss = dblfault_tss.tss_ss0 = dblfault_tss.tss_ss1 = dblfault_tss.tss_ss2 = GSEL(GDATA_SEL, SEL_KPL); dblfault_tss.tss_cr3 = (int)IdlePTD; dblfault_tss.tss_eip = (int) dblfault_handler; dblfault_tss.tss_eflags = PSL_KERNEL; dblfault_tss.tss_ds = dblfault_tss.tss_es = dblfault_tss.tss_gs = GSEL(GDATA_SEL, SEL_KPL); dblfault_tss.tss_fs = GSEL(GPRIV_SEL, SEL_KPL); dblfault_tss.tss_cs = GSEL(GCODE_SEL, SEL_KPL); dblfault_tss.tss_ldt = GSEL(GLDT_SEL, SEL_KPL); vm86_initialize(); getmemsize(first); /* now running on new page tables, configured,and u/iom is accessible */ /* Map the message buffer. */ for (off = 0; off < round_page(MSGBUF_SIZE); off += PAGE_SIZE) pmap_kenter((vm_offset_t)msgbufp + off, avail_end + off); msgbufinit(msgbufp, MSGBUF_SIZE); /* make a call gate to reenter kernel with */ gdp = &ldt[LSYS5CALLS_SEL].gd; x = (int) &IDTVEC(lcall_syscall); gdp->gd_looffset = x; gdp->gd_selector = GSEL(GCODE_SEL,SEL_KPL); gdp->gd_stkcpy = 1; gdp->gd_type = SDT_SYS386CGT; gdp->gd_dpl = SEL_UPL; gdp->gd_p = 1; gdp->gd_hioffset = x >> 16; /* XXX does this work? */ ldt[LBSDICALLS_SEL] = ldt[LSYS5CALLS_SEL]; ldt[LSOL26CALLS_SEL] = ldt[LSYS5CALLS_SEL]; /* transfer to user mode */ _ucodesel = LSEL(LUCODE_SEL, SEL_UPL); _udatasel = LSEL(LUDATA_SEL, SEL_UPL); /* setup proc 0's pcb */ proc0.p_addr->u_pcb.pcb_flags = 0; proc0.p_addr->u_pcb.pcb_cr3 = (int)IdlePTD; proc0.p_addr->u_pcb.pcb_ext = 0; proc0.p_md.md_regs = &proc0_tf; } #if defined(I586_CPU) && !defined(NO_F00F_HACK) static void f00f_hack(void *unused); SYSINIT(f00f_hack, SI_SUB_INTRINSIC, SI_ORDER_FIRST, f00f_hack, NULL); static void f00f_hack(void *unused) { struct gate_descriptor *new_idt; #ifndef SMP struct region_descriptor r_idt; #endif vm_offset_t tmp; if (!has_f00f_bug) return; printf("Intel Pentium detected, installing workaround for F00F bug\n"); r_idt.rd_limit = sizeof(idt0) - 1; tmp = kmem_alloc(kernel_map, PAGE_SIZE * 2); if (tmp == 0) panic("kmem_alloc returned 0"); if (((unsigned int)tmp & (PAGE_SIZE-1)) != 0) panic("kmem_alloc returned non-page-aligned memory"); /* Put the first seven entries in the lower page */ new_idt = (struct gate_descriptor*)(tmp + PAGE_SIZE - (7*8)); bcopy(idt, new_idt, sizeof(idt0)); r_idt.rd_base = (int)new_idt; lidt(&r_idt); idt = new_idt; mtx_lock(&vm_mtx); if (vm_map_protect(kernel_map, tmp, tmp + PAGE_SIZE, VM_PROT_READ, FALSE) != KERN_SUCCESS) panic("vm_map_protect failed"); mtx_unlock(&vm_mtx); return; } #endif /* defined(I586_CPU) && !NO_F00F_HACK */ int ptrace_set_pc(p, addr) struct proc *p; unsigned long addr; { p->p_md.md_regs->tf_eip = addr; return (0); } int ptrace_single_step(p) struct proc *p; { p->p_md.md_regs->tf_eflags |= PSL_T; return (0); } int ptrace_read_u_check(p, addr, len) struct proc *p; vm_offset_t addr; size_t len; { vm_offset_t gap; if ((vm_offset_t) (addr + len) < addr) return EPERM; if ((vm_offset_t) (addr + len) <= sizeof(struct user)) return 0; gap = (char *) p->p_md.md_regs - (char *) p->p_addr; if ((vm_offset_t) addr < gap) return EPERM; if ((vm_offset_t) (addr + len) <= (vm_offset_t) (gap + sizeof(struct trapframe))) return 0; return EPERM; } int ptrace_write_u(p, off, data) struct proc *p; vm_offset_t off; long data; { struct trapframe frame_copy; vm_offset_t min; struct trapframe *tp; /* * Privileged kernel state is scattered all over the user area. * Only allow write access to parts of regs and to fpregs. */ min = (char *)p->p_md.md_regs - (char *)p->p_addr; if (off >= min && off <= min + sizeof(struct trapframe) - sizeof(int)) { tp = p->p_md.md_regs; frame_copy = *tp; *(int *)((char *)&frame_copy + (off - min)) = data; if (!EFL_SECURE(frame_copy.tf_eflags, tp->tf_eflags) || !CS_SECURE(frame_copy.tf_cs)) return (EINVAL); *(int*)((char *)p->p_addr + off) = data; return (0); } min = offsetof(struct user, u_pcb) + offsetof(struct pcb, pcb_savefpu); if (off >= min && off <= min + sizeof(struct save87) - sizeof(int)) { *(int*)((char *)p->p_addr + off) = data; return (0); } return (EFAULT); } int fill_regs(p, regs) struct proc *p; struct reg *regs; { struct pcb *pcb; struct trapframe *tp; tp = p->p_md.md_regs; regs->r_fs = tp->tf_fs; regs->r_es = tp->tf_es; regs->r_ds = tp->tf_ds; regs->r_edi = tp->tf_edi; regs->r_esi = tp->tf_esi; regs->r_ebp = tp->tf_ebp; regs->r_ebx = tp->tf_ebx; regs->r_edx = tp->tf_edx; regs->r_ecx = tp->tf_ecx; regs->r_eax = tp->tf_eax; regs->r_eip = tp->tf_eip; regs->r_cs = tp->tf_cs; regs->r_eflags = tp->tf_eflags; regs->r_esp = tp->tf_esp; regs->r_ss = tp->tf_ss; pcb = &p->p_addr->u_pcb; regs->r_gs = pcb->pcb_gs; return (0); } int set_regs(p, regs) struct proc *p; struct reg *regs; { struct pcb *pcb; struct trapframe *tp; tp = p->p_md.md_regs; if (!EFL_SECURE(regs->r_eflags, tp->tf_eflags) || !CS_SECURE(regs->r_cs)) return (EINVAL); tp->tf_fs = regs->r_fs; tp->tf_es = regs->r_es; tp->tf_ds = regs->r_ds; tp->tf_edi = regs->r_edi; tp->tf_esi = regs->r_esi; tp->tf_ebp = regs->r_ebp; tp->tf_ebx = regs->r_ebx; tp->tf_edx = regs->r_edx; tp->tf_ecx = regs->r_ecx; tp->tf_eax = regs->r_eax; tp->tf_eip = regs->r_eip; tp->tf_cs = regs->r_cs; tp->tf_eflags = regs->r_eflags; tp->tf_esp = regs->r_esp; tp->tf_ss = regs->r_ss; pcb = &p->p_addr->u_pcb; pcb->pcb_gs = regs->r_gs; return (0); } int fill_fpregs(p, fpregs) struct proc *p; struct fpreg *fpregs; { bcopy(&p->p_addr->u_pcb.pcb_savefpu, fpregs, sizeof *fpregs); return (0); } int set_fpregs(p, fpregs) struct proc *p; struct fpreg *fpregs; { bcopy(fpregs, &p->p_addr->u_pcb.pcb_savefpu, sizeof *fpregs); return (0); } int fill_dbregs(p, dbregs) struct proc *p; struct dbreg *dbregs; { struct pcb *pcb; pcb = &p->p_addr->u_pcb; dbregs->dr0 = pcb->pcb_dr0; dbregs->dr1 = pcb->pcb_dr1; dbregs->dr2 = pcb->pcb_dr2; dbregs->dr3 = pcb->pcb_dr3; dbregs->dr4 = 0; dbregs->dr5 = 0; dbregs->dr6 = pcb->pcb_dr6; dbregs->dr7 = pcb->pcb_dr7; return (0); } int set_dbregs(p, dbregs) struct proc *p; struct dbreg *dbregs; { struct pcb *pcb; int i; u_int32_t mask1, mask2; /* * Don't let an illegal value for dr7 get set. Specifically, * check for undefined settings. Setting these bit patterns * result in undefined behaviour and can lead to an unexpected * TRCTRAP. */ for (i = 0, mask1 = 0x3<<16, mask2 = 0x2<<16; i < 8; i++, mask1 <<= 2, mask2 <<= 2) if ((dbregs->dr7 & mask1) == mask2) return (EINVAL); if (dbregs->dr7 & 0x0000fc00) return (EINVAL); pcb = &p->p_addr->u_pcb; /* * Don't let a process set a breakpoint that is not within the * process's address space. If a process could do this, it * could halt the system by setting a breakpoint in the kernel * (if ddb was enabled). Thus, we need to check to make sure * that no breakpoints are being enabled for addresses outside * process's address space, unless, perhaps, we were called by * uid 0. * * XXX - what about when the watched area of the user's * address space is written into from within the kernel * ... wouldn't that still cause a breakpoint to be generated * from within kernel mode? */ if (suser(p) != 0) { if (dbregs->dr7 & 0x3) { /* dr0 is enabled */ if (dbregs->dr0 >= VM_MAXUSER_ADDRESS) return (EINVAL); } if (dbregs->dr7 & (0x3<<2)) { /* dr1 is enabled */ if (dbregs->dr1 >= VM_MAXUSER_ADDRESS) return (EINVAL); } if (dbregs->dr7 & (0x3<<4)) { /* dr2 is enabled */ if (dbregs->dr2 >= VM_MAXUSER_ADDRESS) return (EINVAL); } if (dbregs->dr7 & (0x3<<6)) { /* dr3 is enabled */ if (dbregs->dr3 >= VM_MAXUSER_ADDRESS) return (EINVAL); } } pcb->pcb_dr0 = dbregs->dr0; pcb->pcb_dr1 = dbregs->dr1; pcb->pcb_dr2 = dbregs->dr2; pcb->pcb_dr3 = dbregs->dr3; pcb->pcb_dr6 = dbregs->dr6; pcb->pcb_dr7 = dbregs->dr7; pcb->pcb_flags |= PCB_DBREGS; return (0); } /* * Return > 0 if a hardware breakpoint has been hit, and the * breakpoint was in user space. Return 0, otherwise. */ int user_dbreg_trap(void) { u_int32_t dr7, dr6; /* debug registers dr6 and dr7 */ u_int32_t bp; /* breakpoint bits extracted from dr6 */ int nbp; /* number of breakpoints that triggered */ caddr_t addr[4]; /* breakpoint addresses */ int i; dr7 = rdr7(); if ((dr7 & 0x000000ff) == 0) { /* * all GE and LE bits in the dr7 register are zero, * thus the trap couldn't have been caused by the * hardware debug registers */ return 0; } nbp = 0; dr6 = rdr6(); bp = dr6 & 0x0000000f; if (!bp) { /* * None of the breakpoint bits are set meaning this * trap was not caused by any of the debug registers */ return 0; } /* * at least one of the breakpoints were hit, check to see * which ones and if any of them are user space addresses */ if (bp & 0x01) { addr[nbp++] = (caddr_t)rdr0(); } if (bp & 0x02) { addr[nbp++] = (caddr_t)rdr1(); } if (bp & 0x04) { addr[nbp++] = (caddr_t)rdr2(); } if (bp & 0x08) { addr[nbp++] = (caddr_t)rdr3(); } for (i=0; i /* * Determine the size of the transfer, and make sure it is * within the boundaries of the partition. Adjust transfer * if needed, and signal errors or early completion. */ int bounds_check_with_label(struct bio *bp, struct disklabel *lp, int wlabel) { struct partition *p = lp->d_partitions + dkpart(bp->bio_dev); int labelsect = lp->d_partitions[0].p_offset; int maxsz = p->p_size, sz = (bp->bio_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT; /* overwriting disk label ? */ /* XXX should also protect bootstrap in first 8K */ if (bp->bio_blkno + p->p_offset <= LABELSECTOR + labelsect && #if LABELSECTOR != 0 bp->bio_blkno + p->p_offset + sz > LABELSECTOR + labelsect && #endif (bp->bio_cmd == BIO_WRITE) && wlabel == 0) { bp->bio_error = EROFS; goto bad; } #if defined(DOSBBSECTOR) && defined(notyet) /* overwriting master boot record? */ if (bp->bio_blkno + p->p_offset <= DOSBBSECTOR && (bp->bio_cmd == BIO_WRITE) && wlabel == 0) { bp->bio_error = EROFS; goto bad; } #endif /* beyond partition? */ if (bp->bio_blkno < 0 || bp->bio_blkno + sz > maxsz) { /* if exactly at end of disk, return an EOF */ if (bp->bio_blkno == maxsz) { bp->bio_resid = bp->bio_bcount; return(0); } /* or truncate if part of it fits */ sz = maxsz - bp->bio_blkno; if (sz <= 0) { bp->bio_error = EINVAL; goto bad; } bp->bio_bcount = sz << DEV_BSHIFT; } bp->bio_pblkno = bp->bio_blkno + p->p_offset; return(1); bad: bp->bio_flags |= BIO_ERROR; return(-1); } #ifdef DDB /* * Provide inb() and outb() as functions. They are normally only * available as macros calling inlined functions, thus cannot be * called inside DDB. * * The actual code is stolen from , and de-inlined. */ #undef inb #undef outb /* silence compiler warnings */ u_char inb(u_int); void outb(u_int, u_char); u_char inb(u_int port) { u_char data; /* * We use %%dx and not %1 here because i/o is done at %dx and not at * %edx, while gcc generates inferior code (movw instead of movl) * if we tell it to load (u_short) port. */ __asm __volatile("inb %%dx,%0" : "=a" (data) : "d" (port)); return (data); } void outb(u_int port, u_char data) { u_char al; /* * Use an unnecessary assignment to help gcc's register allocator. * This make a large difference for gcc-1.40 and a tiny difference * for gcc-2.6.0. For gcc-1.40, al had to be ``asm("ax")'' for * best results. gcc-2.6.0 can't handle this. */ al = data; __asm __volatile("outb %0,%%dx" : : "a" (al), "d" (port)); } #endif /* DDB */ Index: head/sys/i386/isa/isa_compat.c =================================================================== --- head/sys/i386/isa/isa_compat.c (revision 78134) +++ head/sys/i386/isa/isa_compat.c (revision 78135) @@ -1,290 +1,296 @@ /*- * Copyright (c) 1998 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct isa_compat_resources { struct resource *ports; struct resource *memory; struct resource *drq; struct resource *irq; }; static void isa_compat_alloc_resources(device_t dev, struct isa_compat_resources *res) { int rid; u_long start, count; if (bus_get_resource(dev, SYS_RES_IOPORT, 0, &start, &count) == 0) { rid = 0; res->ports = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0ul, ~0ul, 1, RF_ACTIVE); if (res->ports == NULL && bootverbose) printf("isa_compat: didn't get ports for %s\n", device_get_name(dev)); } else res->ports = 0; if (bus_get_resource(dev, SYS_RES_MEMORY, 0, &start, &count) == 0 && start != 0) { rid = 0; res->memory = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0ul, ~0ul, 1, RF_ACTIVE); if (res->memory == NULL && bootverbose) printf("isa_compat: didn't get memory for %s\n", device_get_name(dev)); } else res->memory = 0; if (bus_get_resource(dev, SYS_RES_DRQ, 0, &start, &count) == 0) { rid = 0; res->drq = bus_alloc_resource(dev, SYS_RES_DRQ, &rid, 0ul, ~0ul, 1, RF_ACTIVE); if (res->drq == NULL && bootverbose) printf("isa_compat: didn't get drq for %s\n", device_get_name(dev)); } else res->drq = 0; if (bus_get_resource(dev, SYS_RES_IRQ, 0, &start, &count) == 0) { rid = 0; res->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0ul, ~0ul, 1, RF_SHAREABLE | RF_ACTIVE); if (res->irq == NULL && bootverbose) printf("isa_compat: didn't get irq for %s\n", device_get_name(dev)); } else res->irq = 0; } static void isa_compat_release_resources(device_t dev, struct isa_compat_resources *res) { if (res->ports) { bus_release_resource(dev, SYS_RES_IOPORT, 0, res->ports); res->ports = 0; } if (res->memory) { bus_release_resource(dev, SYS_RES_MEMORY, 0, res->memory); res->memory = 0; } if (res->drq) { bus_release_resource(dev, SYS_RES_DRQ, 0, res->drq); res->drq = 0; } if (res->irq) { bus_release_resource(dev, SYS_RES_IRQ, 0, res->irq); res->irq = 0; } } #define irqmask(x) ((x) < 0 ? 0 : (1 << (x))) static int isa_compat_probe(device_t dev) { struct isa_device *dvp = device_get_softc(dev); struct isa_compat_resources res; u_long start, count; /* No pnp support */ if (isa_get_vendorid(dev)) return (ENXIO); bzero(&res, sizeof(res)); /* * Fill in the isa_device fields. */ dvp->id_driver = device_get_driver(dev)->priv; if (bus_get_resource(dev, SYS_RES_IOPORT, 0, &start, &count) == 0) dvp->id_iobase = start; else dvp->id_iobase = -1; if (bus_get_resource(dev, SYS_RES_IRQ, 0, &start, &count) == 0) dvp->id_irq = irqmask(start); else dvp->id_irq = 0; if (bus_get_resource(dev, SYS_RES_DRQ, 0, &start, &count) == 0) dvp->id_drq = start; else dvp->id_drq = -1; if (bus_get_resource(dev, SYS_RES_MEMORY, 0, &start, &count) == 0) { dvp->id_maddr = (void *)(uintptr_t)start; dvp->id_msize = count; } else { dvp->id_maddr = NULL; dvp->id_msize = 0; } dvp->id_unit = device_get_unit(dev); dvp->id_flags = device_get_flags(dev); dvp->id_enabled = device_is_enabled(dev); /* XXX unused */ dvp->id_device = dev; /* * Do the wrapped probe. */ if (dvp->id_driver->probe) { int portsize; void *maddr; struct isa_device old; isa_compat_alloc_resources(dev, &res); if (res.memory) maddr = rman_get_virtual(res.memory); else maddr = 0; dvp->id_maddr = maddr; old = *dvp; portsize = dvp->id_driver->probe(dvp); isa_compat_release_resources(dev, &res); if (portsize != 0) { if (portsize > 0 || dvp->id_iobase != old.id_iobase) bus_set_resource(dev, SYS_RES_IOPORT, 0, dvp->id_iobase, portsize); if (dvp->id_irq != old.id_irq) bus_set_resource(dev, SYS_RES_IRQ, 0, ffs(dvp->id_irq) - 1, 1); if (dvp->id_drq != old.id_drq) bus_set_resource(dev, SYS_RES_DRQ, 0, dvp->id_drq, 1); if (dvp->id_maddr != old.id_maddr || dvp->id_msize != old.id_msize) { maddr = dvp->id_maddr; if (maddr != NULL) bus_set_resource(dev, SYS_RES_MEMORY, 0, kvtop(maddr), dvp->id_msize); else bus_delete_resource(dev, SYS_RES_MEMORY, 0); } return 0; } } return ENXIO; } static int isa_compat_attach(device_t dev) { struct isa_device *dvp = device_get_softc(dev); struct isa_compat_resources res; int error; bzero(&res, sizeof(res)); isa_compat_alloc_resources(dev, &res); if (dvp->id_driver->attach) dvp->id_driver->attach(dvp); if (res.irq && dvp->id_irq && dvp->id_intr) { void *ih; error = BUS_SETUP_INTR(device_get_parent(dev), dev, res.irq, dvp->id_driver->intrflags, dvp->id_intr, (void *)(uintptr_t)dvp->id_unit, &ih); if (error) printf("isa_compat_attach: failed to setup intr: %d\n", error); } device_printf(dev, "driver is using old-style compatibility shims\n"); return 0; } static device_method_t isa_compat_methods[] = { /* Device interface */ DEVMETHOD(device_probe, isa_compat_probe), DEVMETHOD(device_attach, isa_compat_attach), { 0, 0 } }; /* * Create a new style driver around each old isa driver. */ int compat_isa_handler(module_t mod, int type, void *data) { struct isa_driver *id = (struct isa_driver *)data; driver_t *driver; devclass_t isa_devclass = devclass_find("isa"); switch (type) { case MOD_LOAD: driver = malloc(sizeof(driver_t), M_DEVBUF, M_NOWAIT | M_ZERO); if (!driver) return ENOMEM; driver->name = id->name; driver->methods = isa_compat_methods; driver->size = sizeof(struct isa_device); driver->priv = id; - if (id->sensitive_hw) + if (id->sensitive_hw) { +#if 0 resource_set_int(id->name, -1, "sensitive", 1); +#else + printf("WARNING: isa driver %s is sensitive, but cannot set it!\n", + driver->name); +#endif + } devclass_add_driver(isa_devclass, driver); break; case MOD_UNLOAD: printf("%s: module unload not supported!\n", id->name); return EOPNOTSUPP; default: break; } return 0; } Index: head/sys/isa/atkbdc_isa.c =================================================================== --- head/sys/isa/atkbdc_isa.c (revision 78134) +++ head/sys/isa/atkbdc_isa.c (revision 78135) @@ -1,317 +1,315 @@ /*- * Copyright (c) 1999 Kazutaka YOKOTA * All rights reserved. * * 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 as * the first lines of this file unmodified. * 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 AUTHORS ``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 AUTHORS 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. * * $FreeBSD$ */ #include "opt_kbd.h" #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_ATKBDDEV, "atkbddev", "AT Keyboard device"); /* children */ typedef struct atkbdc_device { int flags; /* configuration flags */ int irq; /* ISA IRQ mask */ u_int32_t vendorid; u_int32_t serial; u_int32_t logicalid; u_int32_t compatid; } atkbdc_device_t; /* kbdc */ devclass_t atkbdc_devclass; static int atkbdc_probe(device_t dev); static int atkbdc_attach(device_t dev); static int atkbdc_print_child(device_t bus, device_t dev); static int atkbdc_read_ivar(device_t bus, device_t dev, int index, uintptr_t *val); static int atkbdc_write_ivar(device_t bus, device_t dev, int index, uintptr_t val); static device_method_t atkbdc_methods[] = { DEVMETHOD(device_probe, atkbdc_probe), DEVMETHOD(device_attach, atkbdc_attach), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(bus_print_child, atkbdc_print_child), DEVMETHOD(bus_read_ivar, atkbdc_read_ivar), DEVMETHOD(bus_write_ivar, atkbdc_write_ivar), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t atkbdc_driver = { ATKBDC_DRIVER_NAME, atkbdc_methods, sizeof(atkbdc_softc_t *), }; static struct isa_pnp_id atkbdc_ids[] = { { 0x0303d041, "Keyboard controller (i8042)" }, /* PNP0303 */ { 0 } }; static int atkbdc_probe(device_t dev) { struct resource *port0; struct resource *port1; int error; int rid; /* check PnP IDs */ if (ISA_PNP_PROBE(device_get_parent(dev), dev, atkbdc_ids) == ENXIO) return ENXIO; device_set_desc(dev, "Keyboard controller (i8042)"); rid = 0; port0 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); if (port0 == NULL) return ENXIO; /* XXX */ if (bus_get_resource_start(dev, SYS_RES_IOPORT, 1) <= 0) { bus_set_resource(dev, SYS_RES_IOPORT, 1, rman_get_start(port0) + KBD_STATUS_PORT, 1); } rid = 1; port1 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); if (port1 == NULL) { bus_release_resource(dev, SYS_RES_IOPORT, 0, port0); return ENXIO; } error = atkbdc_probe_unit(device_get_unit(dev), port0, port1); bus_release_resource(dev, SYS_RES_IOPORT, 0, port0); bus_release_resource(dev, SYS_RES_IOPORT, 1, port1); return error; } static void atkbdc_add_device(device_t dev, const char *name, int unit) { atkbdc_device_t *kdev; device_t child; int t; if (resource_int_value(name, unit, "disabled", &t) == 0 && t != 0) return; kdev = malloc(sizeof(struct atkbdc_device), M_ATKBDDEV, M_NOWAIT | M_ZERO); if (!kdev) return; if (resource_int_value(name, unit, "irq", &t) == 0) kdev->irq = t; else kdev->irq = -1; if (resource_int_value(name, unit, "flags", &t) == 0) kdev->flags = t; else kdev->flags = 0; child = device_add_child(dev, name, unit); device_set_ivars(child, kdev); } static int atkbdc_attach(device_t dev) { atkbdc_softc_t *sc; - int unit; + int unit, dunit; int error; int rid; int i; - const char *name; + const char *name, *dname; unit = device_get_unit(dev); sc = *(atkbdc_softc_t **)device_get_softc(dev); if (sc == NULL) { /* * We have to maintain two copies of the kbdc_softc struct, * as the low-level console needs to have access to the * keyboard controller before kbdc is probed and attached. * kbdc_soft[] contains the default entry for that purpose. * See atkbdc.c. XXX */ sc = atkbdc_get_softc(unit); if (sc == NULL) return ENOMEM; } rid = 0; sc->port0 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); if (sc->port0 == NULL) return ENXIO; rid = 1; sc->port1 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); if (sc->port1 == NULL) { bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->port0); return ENXIO; } error = atkbdc_attach_unit(unit, sc, sc->port0, sc->port1); if (error) { bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->port0); bus_release_resource(dev, SYS_RES_IOPORT, 1, sc->port1); return error; } *(atkbdc_softc_t **)device_get_softc(dev) = sc; /* * Add all devices configured to be attached to atkbdc0. */ name = device_get_nameunit(dev); - i = -1; - while ((i = resource_query_string(i, "at", name)) != -1) - atkbdc_add_device(dev, resource_query_name(i), - resource_query_unit(i)); + i = 0; + while ((resource_find_match(&i, &dname, &dunit, "at", name)) == 0) + atkbdc_add_device(dev, dname, dunit); /* * and atkbdc? */ name = device_get_name(dev); - i = -1; - while ((i = resource_query_string(i, "at", name)) != -1) - atkbdc_add_device(dev, resource_query_name(i), - resource_query_unit(i)); + i = 0; + while ((resource_find_match(&i, &dname, &dunit, "at", name)) == 0) + atkbdc_add_device(dev, dname, dunit); bus_generic_attach(dev); return 0; } static int atkbdc_print_child(device_t bus, device_t dev) { atkbdc_device_t *kbdcdev; int retval = 0; kbdcdev = (atkbdc_device_t *)device_get_ivars(dev); retval += bus_print_child_header(bus, dev); if (kbdcdev->flags != 0) retval += printf(" flags 0x%x", kbdcdev->flags); if (kbdcdev->irq != -1) retval += printf(" irq %d", kbdcdev->irq); retval += bus_print_child_footer(bus, dev); return (retval); } static int atkbdc_read_ivar(device_t bus, device_t dev, int index, uintptr_t *val) { atkbdc_device_t *ivar; ivar = (atkbdc_device_t *)device_get_ivars(dev); switch (index) { case KBDC_IVAR_IRQ: *val = (u_long)ivar->irq; break; case KBDC_IVAR_FLAGS: *val = (u_long)ivar->flags; break; case KBDC_IVAR_VENDORID: *val = (u_long)ivar->vendorid; break; case KBDC_IVAR_SERIAL: *val = (u_long)ivar->serial; break; case KBDC_IVAR_LOGICALID: *val = (u_long)ivar->logicalid; break; case KBDC_IVAR_COMPATID: *val = (u_long)ivar->compatid; break; default: return ENOENT; } return 0; } static int atkbdc_write_ivar(device_t bus, device_t dev, int index, uintptr_t val) { atkbdc_device_t *ivar; ivar = (atkbdc_device_t *)device_get_ivars(dev); switch (index) { case KBDC_IVAR_IRQ: ivar->irq = (int)val; break; case KBDC_IVAR_FLAGS: ivar->flags = (int)val; break; case KBDC_IVAR_VENDORID: ivar->vendorid = (u_int32_t)val; break; case KBDC_IVAR_SERIAL: ivar->serial = (u_int32_t)val; break; case KBDC_IVAR_LOGICALID: ivar->logicalid = (u_int32_t)val; break; case KBDC_IVAR_COMPATID: ivar->compatid = (u_int32_t)val; break; default: return ENOENT; } return 0; } DRIVER_MODULE(atkbdc, isa, atkbdc_driver, atkbdc_devclass, 0, 0); Index: head/sys/isa/fd.c =================================================================== --- head/sys/isa/fd.c (revision 78134) +++ head/sys/isa/fd.c (revision 78135) @@ -1,2450 +1,2449 @@ /* * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Don Ahn. * * Libretto PCMCIA floppy support by David Horwitt (dhorwitt@ucsd.edu) * aided by the Linux floppy driver modifications from David Bateman * (dbateman@eng.uts.edu.au). * * Copyright (c) 1993, 1994 by * jc@irbs.UUCP (John Capo) * vak@zebub.msk.su (Serge Vakulenko) * ache@astral.msk.su (Andrew A. Chernov) * * Copyright (c) 1993, 1994, 1995 by * joerg_wunsch@uriah.sax.de (Joerg Wunsch) * dufault@hda.com (Peter Dufault) * * Copyright (c) 2001 Joerg Wunsch, * joerg_wunsch@uriah.heep.sax.de (Joerg Wunsch) * * 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 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. * * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 * $FreeBSD$ * */ #include "opt_fdc.h" #include "card.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 #include /* configuration flags */ #define FDC_PRETEND_D0 (1 << 0) /* pretend drive 0 to be there */ #define FDC_NO_FIFO (1 << 2) /* do not enable FIFO */ /* internally used only, not really from CMOS: */ #define RTCFDT_144M_PRETENDED 0x1000 /* error returns for fd_cmd() */ #define FD_FAILED -1 #define FD_NOT_VALID -2 #define FDC_ERRMAX 100 /* do not log more */ #define NUMTYPES 17 #define NUMDENS (NUMTYPES - 7) #define NO_TYPE 0 /* must match NO_TYPE in ft.c */ #define FD_1720 1 #define FD_1480 2 #define FD_1440 3 #define FD_1200 4 #define FD_820 5 #define FD_800 6 #define FD_720 7 #define FD_360 8 #define FD_640 9 #define FD_1232 10 #define FD_1480in5_25 11 #define FD_1440in5_25 12 #define FD_820in5_25 13 #define FD_800in5_25 14 #define FD_720in5_25 15 #define FD_360in5_25 16 #define FD_640in5_25 17 static struct fd_type fd_types[NUMTYPES] = { { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */ { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */ { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */ { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */ { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /* 820K in HD 3.5in */ { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /* 800K in HD 3.5in */ { 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /* 720K in HD 3.5in */ { 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /* 360K in DD 5.25in */ { 8,2,0xFF,0x2A,80,1280,1,FDC_250KBPS,2,0x50,1 }, /* 640K in DD 5.25in */ { 8,3,0xFF,0x35,77,1232,1,FDC_500KBPS,2,0x74,1 }, /* 1.23M in HD 5.25in */ { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */ { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */ { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /* 820K in HD 5.25in */ { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /* 800K in HD 5.25in */ { 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /* 720K in HD 5.25in */ { 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /* 360K in HD 5.25in */ { 8,2,0xFF,0x2A,80,1280,1,FDC_300KBPS,2,0x50,1 }, /* 640K in HD 5.25in */ }; #define DRVS_PER_CTLR 2 /* 2 floppies */ #define MAX_SEC_SIZE (128 << 3) /***********************************************************************\ * Per controller structure. * \***********************************************************************/ static devclass_t fdc_devclass; /***********************************************************************\ * Per drive structure. * * N per controller (DRVS_PER_CTLR) * \***********************************************************************/ struct fd_data { struct fdc_data *fdc; /* pointer to controller structure */ int fdsu; /* this units number on this controller */ int type; /* Drive type (FD_1440...) */ struct fd_type *ft; /* pointer to the type descriptor */ int flags; #define FD_OPEN 0x01 /* it's open */ #define FD_ACTIVE 0x02 /* it's active */ #define FD_MOTOR 0x04 /* motor should be on */ #define FD_MOTOR_WAIT 0x08 /* motor coming up */ int skip; int hddrv; #define FD_NO_TRACK -2 int track; /* where we think the head is */ int options; /* user configurable options, see ioctl_fd.h */ struct callout_handle toffhandle; struct callout_handle tohandle; struct devstat device_stats; device_t dev; fdu_t fdu; }; struct fdc_ivars { int fdunit; }; static devclass_t fd_devclass; /***********************************************************************\ * Throughout this file the following conventions will be used: * * fd is a pointer to the fd_data struct for the drive in question * * fdc is a pointer to the fdc_data struct for the controller * * fdu is the floppy drive unit number * * fdcu is the floppy controller unit number * * fdsu is the floppy drive unit number on that controller. (sub-unit) * \***********************************************************************/ /* needed for ft driver, thus exported */ int in_fdc(struct fdc_data *); int out_fdc(struct fdc_data *, int); /* internal functions */ static void fdc_intr(void *); static void set_motor(struct fdc_data *, int, int); # define TURNON 1 # define TURNOFF 0 static timeout_t fd_turnoff; static timeout_t fd_motor_on; static void fd_turnon(struct fd_data *); static void fdc_reset(fdc_p); static int fd_in(struct fdc_data *, int *); static void fdstart(struct fdc_data *); static timeout_t fd_iotimeout; static timeout_t fd_pseudointr; static int fdstate(struct fdc_data *); static int retrier(struct fdc_data *); static int fdformat(dev_t, struct fd_formb *, struct proc *); static int enable_fifo(fdc_p fdc); static int fifo_threshold = 8; /* XXX: should be accessible via sysctl */ #ifdef FDC_DEBUG static char const * const fdstates[] = { "DEVIDLE", "FINDWORK", "DOSEEK", "SEEKCOMPLETE", "IOCOMPLETE", "RECALCOMPLETE", "STARTRECAL", "RESETCTLR", "SEEKWAIT", "RECALWAIT", "MOTORWAIT", "IOTIMEDOUT", "RESETCOMPLETE", "PIOREAD", }; /* CAUTION: fd_debug causes huge amounts of logging output */ static int volatile fd_debug = 0; #define TRACE0(arg) if(fd_debug) printf(arg) #define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2) #else /* FDC_DEBUG */ #define TRACE0(arg) #define TRACE1(arg1, arg2) #endif /* FDC_DEBUG */ static void fdout_wr(fdc_p fdc, u_int8_t v) { bus_space_write_1(fdc->portt, fdc->porth, FDOUT+fdc->port_off, v); } static u_int8_t fdsts_rd(fdc_p fdc) { return bus_space_read_1(fdc->portt, fdc->porth, FDSTS+fdc->port_off); } static void fddata_wr(fdc_p fdc, u_int8_t v) { bus_space_write_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off, v); } static u_int8_t fddata_rd(fdc_p fdc) { return bus_space_read_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off); } static void fdctl_wr_isa(fdc_p fdc, u_int8_t v) { bus_space_write_1(fdc->ctlt, fdc->ctlh, 0, v); } #if NCARD > 0 static void fdctl_wr_pcmcia(fdc_p fdc, u_int8_t v) { bus_space_write_1(fdc->portt, fdc->porth, FDCTL+fdc->port_off, v); } #endif #if 0 static u_int8_t fdin_rd(fdc_p fdc) { return bus_space_read_1(fdc->portt, fdc->porth, FDIN); } #endif /* * named Fdopen() to avoid confusion with fdopen() in fd(4); the * difference is now only meaningful for debuggers */ static d_open_t Fdopen; static d_close_t fdclose; static d_ioctl_t fdioctl; static d_strategy_t fdstrategy; #define CDEV_MAJOR 9 static struct cdevsw fd_cdevsw = { /* open */ Fdopen, /* close */ fdclose, /* read */ physread, /* write */ physwrite, /* ioctl */ fdioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ fdstrategy, /* name */ "fd", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ D_DISK, }; static int fdc_err(struct fdc_data *fdc, const char *s) { fdc->fdc_errs++; if (s) { if (fdc->fdc_errs < FDC_ERRMAX) device_printf(fdc->fdc_dev, "%s", s); else if (fdc->fdc_errs == FDC_ERRMAX) device_printf(fdc->fdc_dev, "too many errors, not " "logging any more\n"); } return FD_FAILED; } /* * fd_cmd: Send a command to the chip. Takes a varargs with this structure: * Unit number, * # of output bytes, output bytes as ints ..., * # of input bytes, input bytes as ints ... */ static int fd_cmd(struct fdc_data *fdc, int n_out, ...) { u_char cmd; int n_in; int n; va_list ap; va_start(ap, n_out); cmd = (u_char)(va_arg(ap, int)); va_end(ap); va_start(ap, n_out); for (n = 0; n < n_out; n++) { if (out_fdc(fdc, va_arg(ap, int)) < 0) { char msg[50]; snprintf(msg, sizeof(msg), "cmd %x failed at out byte %d of %d\n", cmd, n + 1, n_out); return fdc_err(fdc, msg); } } n_in = va_arg(ap, int); for (n = 0; n < n_in; n++) { int *ptr = va_arg(ap, int *); if (fd_in(fdc, ptr) < 0) { char msg[50]; snprintf(msg, sizeof(msg), "cmd %02x failed at in byte %d of %d\n", cmd, n + 1, n_in); return fdc_err(fdc, msg); } } return 0; } static int enable_fifo(fdc_p fdc) { int i, j; if ((fdc->flags & FDC_HAS_FIFO) == 0) { /* * Cannot use fd_cmd the normal way here, since * this might be an invalid command. Thus we send the * first byte, and check for an early turn of data directon. */ if (out_fdc(fdc, I8207X_CONFIGURE) < 0) return fdc_err(fdc, "Enable FIFO failed\n"); /* If command is invalid, return */ j = 100000; while ((i = fdsts_rd(fdc) & (NE7_DIO | NE7_RQM)) != NE7_RQM && j-- > 0) if (i == (NE7_DIO | NE7_RQM)) { fdc_reset(fdc); return FD_FAILED; } if (j<0 || fd_cmd(fdc, 3, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) { fdc_reset(fdc); return fdc_err(fdc, "Enable FIFO failed\n"); } fdc->flags |= FDC_HAS_FIFO; return 0; } if (fd_cmd(fdc, 4, I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) return fdc_err(fdc, "Re-enable FIFO failed\n"); return 0; } static int fd_sense_drive_status(fdc_p fdc, int *st3p) { int st3; if (fd_cmd(fdc, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3)) { return fdc_err(fdc, "Sense Drive Status failed\n"); } if (st3p) *st3p = st3; return 0; } static int fd_sense_int(fdc_p fdc, int *st0p, int *cylp) { int cyl, st0, ret; ret = fd_cmd(fdc, 1, NE7CMD_SENSEI, 1, &st0); if (ret) { (void)fdc_err(fdc, "sense intr err reading stat reg 0\n"); return ret; } if (st0p) *st0p = st0; if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) { /* * There doesn't seem to have been an interrupt. */ return FD_NOT_VALID; } if (fd_in(fdc, &cyl) < 0) { return fdc_err(fdc, "can't get cyl num\n"); } if (cylp) *cylp = cyl; return 0; } static int fd_read_status(fdc_p fdc, int fdsu) { int i, ret; for (i = 0; i < 7; i++) { /* * XXX types are poorly chosen. Only bytes can by read * from the hardware, but fdc->status[] wants u_ints and * fd_in() gives ints. */ int status; ret = fd_in(fdc, &status); fdc->status[i] = status; if (ret != 0) break; } if (ret == 0) fdc->flags |= FDC_STAT_VALID; else fdc->flags &= ~FDC_STAT_VALID; return ret; } /****************************************************************************/ /* autoconfiguration stuff */ /****************************************************************************/ static int fdc_alloc_resources(struct fdc_data *fdc) { device_t dev; int ispnp, ispcmcia; dev = fdc->fdc_dev; ispnp = (fdc->flags & FDC_ISPNP) != 0; ispcmcia = (fdc->flags & FDC_ISPCMCIA) != 0; fdc->rid_ioport = fdc->rid_irq = fdc->rid_drq = 0; fdc->res_ioport = fdc->res_irq = fdc->res_drq = 0; /* * On standard ISA, we don't just use an 8 port range * (e.g. 0x3f0-0x3f7) since that covers an IDE control * register at 0x3f6. * * Isn't PC hardware wonderful. * * The Y-E Data PCMCIA FDC doesn't have this problem, it * uses the register with offset 6 for pseudo-DMA, and the * one with offset 7 as control register. */ fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, &fdc->rid_ioport, 0ul, ~0ul, ispcmcia ? 8 : (ispnp ? 1 : 6), RF_ACTIVE); if (fdc->res_ioport == 0) { device_printf(dev, "cannot reserve I/O port range\n"); return ENXIO; } fdc->portt = rman_get_bustag(fdc->res_ioport); fdc->porth = rman_get_bushandle(fdc->res_ioport); if (!ispcmcia) { /* * Some BIOSen report the device at 0x3f2-0x3f5,0x3f7 * and some at 0x3f0-0x3f5,0x3f7. We detect the former * by checking the size and adjust the port address * accordingly. */ if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 4) fdc->port_off = -2; /* * Register the control port range as rid 1 if it * isn't there already. Most PnP BIOSen will have * already done this but non-PnP configurations don't. * * And some (!!) report 0x3f2-0x3f5 and completely * leave out the control register! It seems that some * non-antique controller chips have a different * method of programming the transfer speed which * doesn't require the control register, but it's * mighty bogus as the chip still responds to the * address for the control register. */ if (bus_get_resource_count(dev, SYS_RES_IOPORT, 1) == 0) { u_long ctlstart; /* Find the control port, usually 0x3f7 */ ctlstart = rman_get_start(fdc->res_ioport) + fdc->port_off + 7; bus_set_resource(dev, SYS_RES_IOPORT, 1, ctlstart, 1); } /* * Now (finally!) allocate the control port. */ fdc->rid_ctl = 1; fdc->res_ctl = bus_alloc_resource(dev, SYS_RES_IOPORT, &fdc->rid_ctl, 0ul, ~0ul, 1, RF_ACTIVE); if (fdc->res_ctl == 0) { device_printf(dev, "cannot reserve control I/O port range\n"); return ENXIO; } fdc->ctlt = rman_get_bustag(fdc->res_ctl); fdc->ctlh = rman_get_bushandle(fdc->res_ctl); } fdc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &fdc->rid_irq, 0ul, ~0ul, 1, RF_ACTIVE); if (fdc->res_irq == 0) { device_printf(dev, "cannot reserve interrupt line\n"); return ENXIO; } if ((fdc->flags & FDC_NODMA) == 0) { fdc->res_drq = bus_alloc_resource(dev, SYS_RES_DRQ, &fdc->rid_drq, 0ul, ~0ul, 1, RF_ACTIVE); if (fdc->res_drq == 0) { device_printf(dev, "cannot reserve DMA request line\n"); return ENXIO; } fdc->dmachan = fdc->res_drq->r_start; } return 0; } static void fdc_release_resources(struct fdc_data *fdc) { device_t dev; dev = fdc->fdc_dev; if (fdc->res_irq != 0) { bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq, fdc->res_irq); bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq, fdc->res_irq); } if (fdc->res_ctl != 0) { bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, fdc->res_ctl); bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, fdc->res_ctl); } if (fdc->res_ioport != 0) { bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, fdc->res_ioport); bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, fdc->res_ioport); } if (fdc->res_drq != 0) { bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq, fdc->res_drq); bus_release_resource(dev, SYS_RES_DRQ, fdc->rid_drq, fdc->res_drq); } } /****************************************************************************/ /* autoconfiguration stuff */ /****************************************************************************/ static struct isa_pnp_id fdc_ids[] = { {0x0007d041, "PC standard floppy disk controller"}, /* PNP0700 */ {0x0107d041, "Standard floppy controller supporting MS Device Bay Spec"}, /* PNP0701 */ {0} }; static int fdc_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { struct fdc_ivars *ivars = device_get_ivars(child); switch (which) { case FDC_IVAR_FDUNIT: *result = ivars->fdunit; break; default: return ENOENT; } return 0; } /* * fdc controller section. */ static int fdc_probe(device_t dev) { int error, ic_type; struct fdc_data *fdc; fdc = device_get_softc(dev); bzero(fdc, sizeof *fdc); fdc->fdc_dev = dev; fdc->fdctl_wr = fdctl_wr_isa; /* Check pnp ids */ error = ISA_PNP_PROBE(device_get_parent(dev), dev, fdc_ids); if (error == ENXIO) return ENXIO; if (error == 0) fdc->flags |= FDC_ISPNP; /* Attempt to allocate our resources for the duration of the probe */ error = fdc_alloc_resources(fdc); if (error) goto out; /* First - lets reset the floppy controller */ fdout_wr(fdc, 0); DELAY(100); fdout_wr(fdc, FDO_FRST); /* see if it can handle a command */ if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0)) { error = ENXIO; goto out; } if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) { ic_type = (u_char)ic_type; switch (ic_type) { case 0x80: device_set_desc(dev, "NEC 765 or clone"); fdc->fdct = FDC_NE765; break; case 0x81: device_set_desc(dev, "Intel 82077 or clone"); fdc->fdct = FDC_I82077; break; case 0x90: device_set_desc(dev, "NEC 72065B or clone"); fdc->fdct = FDC_NE72065; break; default: device_set_desc(dev, "generic floppy controller"); fdc->fdct = FDC_UNKNOWN; break; } } out: fdc_release_resources(fdc); return (error); } #if NCARD > 0 static int fdc_pccard_probe(device_t dev) { int error; struct fdc_data *fdc; fdc = device_get_softc(dev); bzero(fdc, sizeof *fdc); fdc->fdc_dev = dev; fdc->fdctl_wr = fdctl_wr_pcmcia; fdc->flags |= FDC_ISPCMCIA | FDC_NODMA; /* Attempt to allocate our resources for the duration of the probe */ error = fdc_alloc_resources(fdc); if (error) goto out; /* First - lets reset the floppy controller */ fdout_wr(fdc, 0); DELAY(100); fdout_wr(fdc, FDO_FRST); /* see if it can handle a command */ if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0)) { error = ENXIO; goto out; } device_set_desc(dev, "Y-E Data PCMCIA floppy"); fdc->fdct = FDC_NE765; out: fdc_release_resources(fdc); return (error); } static int fdc_pccard_detach(device_t dev) { struct fdc_data *fdc; int error; fdc = device_get_softc(dev); /* have our children detached first */ if ((error = bus_generic_detach(dev))) return (error); if ((fdc->flags & FDC_ATTACHED) == 0) { device_printf(dev, "already unloaded\n"); return (0); } fdc->flags &= ~FDC_ATTACHED; BUS_TEARDOWN_INTR(device_get_parent(dev), dev, fdc->res_irq, fdc->fdc_intr); fdc_release_resources(fdc); device_printf(dev, "unload\n"); return (0); } #endif /* NCARD > 0 */ /* * Add a child device to the fdc controller. It will then be probed etc. */ static void fdc_add_child(device_t dev, const char *name, int unit) { int disabled; struct fdc_ivars *ivar; device_t child; ivar = malloc(sizeof *ivar, M_DEVBUF /* XXX */, M_NOWAIT | M_ZERO); if (ivar == NULL) return; if (resource_int_value(name, unit, "drive", &ivar->fdunit) != 0) ivar->fdunit = 0; child = device_add_child(dev, name, unit); if (child == NULL) return; device_set_ivars(child, ivar); if (resource_int_value(name, unit, "disabled", &disabled) == 0 && disabled != 0) device_disable(child); } static int fdc_attach(device_t dev) { struct fdc_data *fdc; - int i, error; - const char *name; + int i, error, dunit; + const char *name, *dname; fdc = device_get_softc(dev); error = fdc_alloc_resources(fdc); if (error) { device_printf(dev, "cannot re-acquire resources\n"); return error; } error = BUS_SETUP_INTR(device_get_parent(dev), dev, fdc->res_irq, INTR_TYPE_BIO | INTR_ENTROPY, fdc_intr, fdc, &fdc->fdc_intr); if (error) { device_printf(dev, "cannot setup interrupt\n"); return error; } fdc->fdcu = device_get_unit(dev); fdc->flags |= FDC_ATTACHED; if ((fdc->flags & FDC_NODMA) == 0) { /* * Acquire the DMA channel forever, the driver will do * the rest * XXX should integrate with rman */ isa_dma_acquire(fdc->dmachan); isa_dmainit(fdc->dmachan, MAX_SEC_SIZE); } fdc->state = DEVIDLE; /* reset controller, turn motor off, clear fdout mirror reg */ fdout_wr(fdc, ((fdc->fdout = 0))); bioq_init(&fdc->head); /* * Probe and attach any children. We should probably detect * devices from the BIOS unless overridden. */ name = device_get_nameunit(dev); - i = -1; - while ((i = resource_query_string(i, "at", name)) != -1) - fdc_add_child(dev, resource_query_name(i), - resource_query_unit(i)); + i = 0; + while ((resource_find_match(&i, &dname, &dunit, "at", name)) == 0) + fdc_add_child(dev, dname, dunit); return (bus_generic_attach(dev)); } static int fdc_print_child(device_t me, device_t child) { int retval = 0; retval += bus_print_child_header(me, child); retval += printf(" on %s drive %d\n", device_get_nameunit(me), fdc_get_fdunit(child)); return (retval); } static device_method_t fdc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fdc_probe), DEVMETHOD(device_attach, fdc_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, fdc_print_child), DEVMETHOD(bus_read_ivar, fdc_read_ivar), /* Our children never use any other bus interface methods. */ { 0, 0 } }; static driver_t fdc_driver = { "fdc", fdc_methods, sizeof(struct fdc_data) }; DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, 0, 0); #if NCARD > 0 static device_method_t fdc_pccard_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fdc_pccard_probe), DEVMETHOD(device_attach, fdc_attach), DEVMETHOD(device_detach, fdc_pccard_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, fdc_print_child), DEVMETHOD(bus_read_ivar, fdc_read_ivar), /* Our children never use any other bus interface methods. */ { 0, 0 } }; static driver_t fdc_pccard_driver = { "fdc", fdc_pccard_methods, sizeof(struct fdc_data) }; DRIVER_MODULE(fdc, pccard, fdc_pccard_driver, fdc_devclass, 0, 0); #endif /* NCARD > 0 */ static void fd_clone __P((void *arg, char *name, int namelen, dev_t *dev)); static struct { char *match; int minor; int link; } fd_suffix[] = { { "a", 0, 1 }, { "b", 0, 1 }, { "c", 0, 1 }, { "d", 0, 1 }, { "e", 0, 1 }, { "f", 0, 1 }, { "g", 0, 1 }, { "h", 0, 1 }, { ".1720", 1, 0 }, { ".1480", 2, 0 }, { ".1440", 3, 0 }, { ".1200", 4, 0 }, { ".820", 5, 0 }, { ".800", 6, 0 }, { ".720", 7, 0 }, { ".360", 8, 0 }, { ".640", 9, 0 }, { ".1232", 10, 0 }, { 0, 0 } }; static void fd_clone(arg, name, namelen, dev) void *arg; char *name; int namelen; dev_t *dev; { int u, d, i; char *n; dev_t pdev; if (*dev != NODEV) return; if (dev_stdclone(name, &n, "fd", &u) != 2) return; for (i = 0; ; i++) { if (fd_suffix[i].match == NULL) return; if (strcmp(n, fd_suffix[i].match)) continue; d = fd_suffix[i].minor; break; } if (fd_suffix[i].link == 0) { *dev = make_dev(&fd_cdevsw, (u << 6) + d, UID_ROOT, GID_OPERATOR, 0640, name); } else { pdev = makedev(fd_cdevsw.d_maj, (u << 6) + d); *dev = make_dev_alias(pdev, name); } } /******************************************************************/ /* * devices attached to the controller section. */ static int fd_probe(device_t dev) { int i; u_int fdt, st0, st3; struct fd_data *fd; struct fdc_data *fdc; fdsu_t fdsu; static int fd_fifo = 0; fdsu = *(int *)device_get_ivars(dev); /* xxx cheat a bit... */ fd = device_get_softc(dev); fdc = device_get_softc(device_get_parent(dev)); bzero(fd, sizeof *fd); fd->dev = dev; fd->fdc = fdc; fd->fdsu = fdsu; fd->fdu = device_get_unit(dev); #ifdef __i386__ /* look up what bios thinks we have */ switch (fd->fdu) { case 0: if ((fdc->flags & FDC_ISPCMCIA)) fdt = RTCFDT_144M; else if (device_get_flags(fdc->fdc_dev) & FDC_PRETEND_D0) fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED; else fdt = (rtcin(RTC_FDISKETTE) & 0xf0); break; case 1: fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0); break; default: fdt = RTCFDT_NONE; break; } #else fdt = RTCFDT_144M; /* XXX probably */ #endif /* is there a unit? */ if (fdt == RTCFDT_NONE) return (ENXIO); /* select it */ set_motor(fdc, fdsu, TURNON); DELAY(1000000); /* 1 sec */ /* XXX This doesn't work before the first set_motor() */ if (fd_fifo == 0 && fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN && (device_get_flags(fdc->fdc_dev) & FDC_NO_FIFO) == 0 && enable_fifo(fdc) == 0) { device_printf(device_get_parent(dev), "FIFO enabled, %d bytes threshold\n", fifo_threshold); } fd_fifo = 1; if ((fd_cmd(fdc, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* if at track 0, first seek inwards */ /* seek some steps: */ fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0); DELAY(300000); /* ...wait a moment... */ fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ } /* If we're at track 0 first seek inwards. */ if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* Seek some steps... */ if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { /* ...wait a moment... */ DELAY(300000); /* make ctrlr happy: */ fd_sense_int(fdc, 0, 0); } } for (i = 0; i < 2; i++) { /* * we must recalibrate twice, just in case the * heads have been beyond cylinder 76, since most * FDCs still barf when attempting to recalibrate * more than 77 steps */ /* go back to 0: */ if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) { /* a second being enough for full stroke seek*/ DELAY(i == 0 ? 1000000 : 300000); /* anything responding? */ if (fd_sense_int(fdc, &st0, 0) == 0 && (st0 & NE7_ST0_EC) == 0) break; /* already probed succesfully */ } } set_motor(fdc, fdsu, TURNOFF); if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ return (ENXIO); fd->track = FD_NO_TRACK; fd->fdc = fdc; fd->fdsu = fdsu; fd->options = 0; callout_handle_init(&fd->toffhandle); callout_handle_init(&fd->tohandle); switch (fdt) { case RTCFDT_12M: device_set_desc(dev, "1200-KB 5.25\" drive"); fd->type = FD_1200; break; case RTCFDT_144M | RTCFDT_144M_PRETENDED: device_set_desc(dev, "config-pretended 1440-MB 3.5\" drive"); fdt = RTCFDT_144M; fd->type = FD_1440; case RTCFDT_144M: device_set_desc(dev, "1440-KB 3.5\" drive"); fd->type = FD_1440; break; case RTCFDT_288M: case RTCFDT_288M_1: device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)"); fd->type = FD_1440; break; case RTCFDT_360K: device_set_desc(dev, "360-KB 5.25\" drive"); fd->type = FD_360; break; case RTCFDT_720K: printf("720-KB 3.5\" drive"); fd->type = FD_720; break; default: return (ENXIO); } return (0); } static int fd_attach(device_t dev) { struct fd_data *fd; static int cdevsw_add_done = 0; fd = device_get_softc(dev); if (!cdevsw_add_done) { cdevsw_add(&fd_cdevsw); /* XXX */ cdevsw_add_done++; } EVENTHANDLER_REGISTER(dev_clone, fd_clone, 0, 1000); make_dev(&fd_cdevsw, (fd->fdu << 6), UID_ROOT, GID_OPERATOR, 0640, "fd%d", fd->fdu); /* * Export the drive to the devstat interface. */ devstat_add_entry(&fd->device_stats, device_get_name(dev), device_get_unit(dev), 0, DEVSTAT_NO_ORDERED_TAGS, DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER, DEVSTAT_PRIORITY_FD); return (0); } static int fd_detach(device_t dev) { struct fd_data *fd; fd = device_get_softc(dev); untimeout(fd_turnoff, fd, fd->toffhandle); return (0); } static device_method_t fd_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fd_probe), DEVMETHOD(device_attach, fd_attach), DEVMETHOD(device_detach, fd_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), /* XXX */ DEVMETHOD(device_resume, bus_generic_resume), /* XXX */ { 0, 0 } }; static driver_t fd_driver = { "fd", fd_methods, sizeof(struct fd_data) }; DRIVER_MODULE(fd, fdc, fd_driver, fd_devclass, 0, 0); /****************************************************************************/ /* motor control stuff */ /* remember to not deselect the drive we're working on */ /****************************************************************************/ static void set_motor(struct fdc_data *fdc, int fdsu, int turnon) { int fdout = fdc->fdout; int needspecify = 0; if(turnon) { fdout &= ~FDO_FDSEL; fdout |= (FDO_MOEN0 << fdsu) + fdsu; } else fdout &= ~(FDO_MOEN0 << fdsu); if(!turnon && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0) /* gonna turn off the last drive, put FDC to bed */ fdout &= ~ (FDO_FRST|FDO_FDMAEN); else { /* make sure controller is selected and specified */ if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0) needspecify = 1; fdout |= (FDO_FRST|FDO_FDMAEN); } fdout_wr(fdc, fdout); fdc->fdout = fdout; TRACE1("[0x%x->FDOUT]", fdout); if (needspecify) { /* * we silently assume the command will be accepted * after an FDC reset * * Steinbach's Guideline for Systems Programming: * Never test for an error condition you don't know * how to handle. */ (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0); if (fdc->flags & FDC_HAS_FIFO) (void) enable_fifo(fdc); } } static void fd_turnoff(void *xfd) { int s; fd_p fd = xfd; TRACE1("[fd%d: turnoff]", fd->fdu); s = splbio(); /* * Don't turn off the motor yet if the drive is active. * * If we got here, this could only mean we missed an interrupt. * This can e. g. happen on the Y-E Date PCMCIA floppy controller * after a controller reset. Just schedule a pseudo-interrupt * so the state machine gets re-entered. */ if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fd->fdu) { fdc_intr(fd->fdc); splx(s); return; } fd->flags &= ~FD_MOTOR; set_motor(fd->fdc, fd->fdsu, TURNOFF); splx(s); } static void fd_motor_on(void *xfd) { int s; fd_p fd = xfd; s = splbio(); fd->flags &= ~FD_MOTOR_WAIT; if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT)) { fdc_intr(fd->fdc); } splx(s); } static void fd_turnon(fd_p fd) { if(!(fd->flags & FD_MOTOR)) { fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT); set_motor(fd->fdc, fd->fdsu, TURNON); timeout(fd_motor_on, fd, hz); /* in 1 sec its ok */ } } static void fdc_reset(fdc_p fdc) { /* Try a reset, keep motor on */ fdout_wr(fdc, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); DELAY(100); /* enable FDC, but defer interrupts a moment */ fdout_wr(fdc, fdc->fdout & ~FDO_FDMAEN); TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN); DELAY(100); fdout_wr(fdc, fdc->fdout); TRACE1("[0x%x->FDOUT]", fdc->fdout); /* after a reset, silently believe the FDC will accept commands */ (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0); if (fdc->flags & FDC_HAS_FIFO) (void) enable_fifo(fdc); } /****************************************************************************/ /* fdc in/out */ /****************************************************************************/ int in_fdc(struct fdc_data *fdc) { int i, j = 100000; while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) if (i == NE7_RQM) return fdc_err(fdc, "ready for output in input\n"); if (j <= 0) return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); #ifdef FDC_DEBUG i = fddata_rd(fdc); TRACE1("[FDDATA->0x%x]", (unsigned char)i); return(i); #else /* !FDC_DEBUG */ return fddata_rd(fdc); #endif /* FDC_DEBUG */ } /* * fd_in: Like in_fdc, but allows you to see if it worked. */ static int fd_in(struct fdc_data *fdc, int *ptr) { int i, j = 100000; while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) if (i == NE7_RQM) return fdc_err(fdc, "ready for output in input\n"); if (j <= 0) return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); #ifdef FDC_DEBUG i = fddata_rd(fdc); TRACE1("[FDDATA->0x%x]", (unsigned char)i); *ptr = i; return 0; #else /* !FDC_DEBUG */ i = fddata_rd(fdc); if (ptr) *ptr = i; return 0; #endif /* FDC_DEBUG */ } int out_fdc(struct fdc_data *fdc, int x) { int i; /* Check that the direction bit is set */ i = 100000; while ((fdsts_rd(fdc) & NE7_DIO) && i-- > 0); if (i <= 0) return fdc_err(fdc, "direction bit not set\n"); /* Check that the floppy controller is ready for a command */ i = 100000; while ((fdsts_rd(fdc) & NE7_RQM) == 0 && i-- > 0); if (i <= 0) return fdc_err(fdc, bootverbose? "output ready timeout\n": 0); /* Send the command and return */ fddata_wr(fdc, x); TRACE1("[0x%x->FDDATA]", x); return (0); } /****************************************************************************/ /* fdopen/fdclose */ /****************************************************************************/ int Fdopen(dev_t dev, int flags, int mode, struct proc *p) { fdu_t fdu = FDUNIT(minor(dev)); int type = FDTYPE(minor(dev)); fd_p fd; fdc_p fdc; /* check bounds */ if ((fd = devclass_get_softc(fd_devclass, fdu)) == 0) return (ENXIO); fdc = fd->fdc; if ((fdc == NULL) || (fd->type == NO_TYPE)) return (ENXIO); if (type > NUMDENS) return (ENXIO); if (type == 0) type = fd->type; else { /* * For each type of basic drive, make sure we are trying * to open a type it can do, */ if (type != fd->type) { switch (fd->type) { case FD_360: return (ENXIO); case FD_720: if ( type != FD_820 && type != FD_800 && type != FD_640 ) return (ENXIO); break; case FD_1200: switch (type) { case FD_1480: type = FD_1480in5_25; break; case FD_1440: type = FD_1440in5_25; break; case FD_1232: break; case FD_820: type = FD_820in5_25; break; case FD_800: type = FD_800in5_25; break; case FD_720: type = FD_720in5_25; break; case FD_640: type = FD_640in5_25; break; case FD_360: type = FD_360in5_25; break; default: return(ENXIO); } break; case FD_1440: if ( type != FD_1720 && type != FD_1480 && type != FD_1200 && type != FD_820 && type != FD_800 && type != FD_720 && type != FD_640 ) return(ENXIO); break; } } } fd->ft = fd_types + type - 1; fd->flags |= FD_OPEN; return 0; } int fdclose(dev_t dev, int flags, int mode, struct proc *p) { fdu_t fdu = FDUNIT(minor(dev)); struct fd_data *fd; fd = devclass_get_softc(fd_devclass, fdu); fd->flags &= ~FD_OPEN; fd->options &= ~(FDOPT_NORETRY | FDOPT_NOERRLOG | FDOPT_NOERROR); return (0); } /****************************************************************************/ /* fdstrategy */ /****************************************************************************/ void fdstrategy(struct bio *bp) { unsigned nblocks, blknum, cando; int s; fdu_t fdu; fdc_p fdc; fd_p fd; size_t fdblk; fdu = FDUNIT(minor(bp->bio_dev)); fd = devclass_get_softc(fd_devclass, fdu); if (fd == 0) panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)", (u_long)major(bp->bio_dev), (u_long)minor(bp->bio_dev)); fdc = fd->fdc; if (fd->type == NO_TYPE) { bp->bio_error = ENXIO; bp->bio_flags |= BIO_ERROR; goto bad; }; fdblk = 128 << (fd->ft->secsize); if (!(bp->bio_cmd & BIO_FORMAT)) { if (bp->bio_blkno < 0) { printf( "fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n", fdu, (u_long)bp->bio_blkno, bp->bio_bcount); bp->bio_error = EINVAL; bp->bio_flags |= BIO_ERROR; goto bad; } if ((bp->bio_bcount % fdblk) != 0) { bp->bio_error = EINVAL; bp->bio_flags |= BIO_ERROR; goto bad; } } /* * Set up block calculations. */ if (bp->bio_blkno > 20000000) { /* * Reject unreasonably high block number, prevent the * multiplication below from overflowing. */ bp->bio_error = EINVAL; bp->bio_flags |= BIO_ERROR; goto bad; } blknum = (unsigned) bp->bio_blkno * DEV_BSIZE/fdblk; nblocks = fd->ft->size; bp->bio_resid = 0; if (blknum + (bp->bio_bcount / fdblk) > nblocks) { if (blknum <= nblocks) { cando = (nblocks - blknum) * fdblk; bp->bio_resid = bp->bio_bcount - cando; if (cando == 0) goto bad; /* not actually bad but EOF */ } else { bp->bio_error = EINVAL; bp->bio_flags |= BIO_ERROR; goto bad; } } bp->bio_pblkno = bp->bio_blkno; s = splbio(); bioqdisksort(&fdc->head, bp); untimeout(fd_turnoff, fd, fd->toffhandle); /* a good idea */ /* Tell devstat we are starting on the transaction */ devstat_start_transaction(&fd->device_stats); device_busy(fd->dev); fdstart(fdc); splx(s); return; bad: biodone(bp); } /***************************************************************\ * fdstart * * We have just queued something.. if the controller is not busy * * then simulate the case where it has just finished a command * * So that it (the interrupt routine) looks on the queue for more* * work to do and picks up what we just added. * * If the controller is already busy, we need do nothing, as it * * will pick up our work when the present work completes * \***************************************************************/ static void fdstart(struct fdc_data *fdc) { int s; s = splbio(); if(fdc->state == DEVIDLE) { fdc_intr(fdc); } splx(s); } static void fd_iotimeout(void *xfdc) { fdc_p fdc; int s; fdc = xfdc; TRACE1("fd%d[fd_iotimeout()]", fdc->fdu); /* * Due to IBM's brain-dead design, the FDC has a faked ready * signal, hardwired to ready == true. Thus, any command * issued if there's no diskette in the drive will _never_ * complete, and must be aborted by resetting the FDC. * Many thanks, Big Blue! * The FDC must not be reset directly, since that would * interfere with the state machine. Instead, pretend that * the command completed but was invalid. The state machine * will reset the FDC and retry once. */ s = splbio(); fdc->status[0] = NE7_ST0_IC_IV; fdc->flags &= ~FDC_STAT_VALID; fdc->state = IOTIMEDOUT; fdc_intr(fdc); splx(s); } /* just ensure it has the right spl */ static void fd_pseudointr(void *xfdc) { int s; s = splbio(); fdc_intr(xfdc); splx(s); } /***********************************************************************\ * fdintr * * keep calling the state machine until it returns a 0 * * ALWAYS called at SPLBIO * \***********************************************************************/ static void fdc_intr(void *xfdc) { fdc_p fdc = xfdc; while(fdstate(fdc)) ; } /* * magic pseudo-DMA initialization for YE FDC. Sets count and * direction */ #define SET_BCDR(fdc,wr,cnt,port) \ bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port, \ ((cnt)-1) & 0xff); \ bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port + 1, \ ((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f))); /* * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy */ static int fdcpio(fdc_p fdc, long flags, caddr_t addr, u_int count) { u_char *cptr = (u_char *)addr; if (flags == BIO_READ) { if (fdc->state != PIOREAD) { fdc->state = PIOREAD; return(0); }; SET_BCDR(fdc, 0, count, 0); bus_space_read_multi_1(fdc->portt, fdc->porth, fdc->port_off + FDC_YE_DATAPORT, cptr, count); } else { bus_space_write_multi_1(fdc->portt, fdc->porth, fdc->port_off + FDC_YE_DATAPORT, cptr, count); SET_BCDR(fdc, 0, count, 0); }; return(1); } /***********************************************************************\ * The controller state machine. * * if it returns a non zero value, it should be called again immediatly * \***********************************************************************/ static int fdstate(fdc_p fdc) { int read, format, head, i, sec = 0, sectrac, st0, cyl, st3, idf; unsigned blknum = 0, b_cylinder = 0; fdu_t fdu = fdc->fdu; fd_p fd; register struct bio *bp; struct fd_formb *finfo = NULL; size_t fdblk; bp = fdc->bp; if (bp == NULL) { bp = bioq_first(&fdc->head); if (bp != NULL) { bioq_remove(&fdc->head, bp); fdc->bp = bp; } } if (bp == NULL) { /***********************************************\ * nothing left for this controller to do * * Force into the IDLE state, * \***********************************************/ fdc->state = DEVIDLE; if (fdc->fd) { device_printf(fdc->fdc_dev, "unexpected valid fd pointer\n"); fdc->fd = (fd_p) 0; fdc->fdu = -1; } TRACE1("[fdc%d IDLE]", fdc->fdcu); return (0); } fdu = FDUNIT(minor(bp->bio_dev)); fd = devclass_get_softc(fd_devclass, fdu); fdblk = 128 << fd->ft->secsize; if (fdc->fd && (fd != fdc->fd)) device_printf(fd->dev, "confused fd pointers\n"); read = bp->bio_cmd == BIO_READ; if (read) idf = ISADMA_READ; else idf = ISADMA_WRITE; format = bp->bio_cmd & BIO_FORMAT; if (format) { finfo = (struct fd_formb *)bp->bio_data; fd->skip = (char *)&(finfo->fd_formb_cylno(0)) - (char *)finfo; } if (fdc->state == DOSEEK || fdc->state == SEEKCOMPLETE) { blknum = (unsigned) bp->bio_pblkno * DEV_BSIZE/fdblk + fd->skip/fdblk; b_cylinder = blknum / (fd->ft->sectrac * fd->ft->heads); } TRACE1("fd%d", fdu); TRACE1("[%s]", fdstates[fdc->state]); TRACE1("(0x%x)", fd->flags); untimeout(fd_turnoff, fd, fd->toffhandle); fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); switch (fdc->state) { case DEVIDLE: case FINDWORK: /* we have found new work */ fdc->retry = 0; fd->skip = 0; fdc->fd = fd; fdc->fdu = fdu; fdc->fdctl_wr(fdc, fd->ft->trans); TRACE1("[0x%x->FDCTL]", fd->ft->trans); /*******************************************************\ * If the next drive has a motor startup pending, then * * it will start up in its own good time * \*******************************************************/ if(fd->flags & FD_MOTOR_WAIT) { fdc->state = MOTORWAIT; return (0); /* come back later */ } /*******************************************************\ * Maybe if it's not starting, it SHOULD be starting * \*******************************************************/ if (!(fd->flags & FD_MOTOR)) { fdc->state = MOTORWAIT; fd_turnon(fd); return (0); } else /* at least make sure we are selected */ { set_motor(fdc, fd->fdsu, TURNON); } if (fdc->flags & FDC_NEEDS_RESET) { fdc->state = RESETCTLR; fdc->flags &= ~FDC_NEEDS_RESET; } else fdc->state = DOSEEK; break; case DOSEEK: if (b_cylinder == (unsigned)fd->track) { fdc->state = SEEKCOMPLETE; break; } if (fd_cmd(fdc, 3, NE7CMD_SEEK, fd->fdsu, b_cylinder * fd->ft->steptrac, 0)) { /* * seek command not accepted, looks like * the FDC went off to the Saints... */ fdc->retry = 6; /* try a reset */ return(retrier(fdc)); } fd->track = FD_NO_TRACK; fdc->state = SEEKWAIT; return(0); /* will return later */ case SEEKWAIT: /* allow heads to settle */ timeout(fd_pseudointr, fdc, hz / 16); fdc->state = SEEKCOMPLETE; return(0); /* will return later */ case SEEKCOMPLETE : /* SEEK DONE, START DMA */ /* Make sure seek really happened*/ if(fd->track == FD_NO_TRACK) { int descyl = b_cylinder * fd->ft->steptrac; do { /* * This might be a "ready changed" interrupt, * which cannot really happen since the * RDY pin is hardwired to + 5 volts. This * generally indicates a "bouncing" intr * line, so do one of the following: * * When running on an enhanced FDC that is * known to not go stuck after responding * with INVALID, fetch all interrupt states * until seeing either an INVALID or a * real interrupt condition. * * When running on a dumb old NE765, give * up immediately. The controller will * provide up to four dummy RC interrupt * conditions right after reset (for the * corresponding four drives), so this is * our only chance to get notice that it * was not the FDC that caused the interrupt. */ if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) return 0; if(fdc->fdct == FDC_NE765 && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) return 0; /* hope for a real intr */ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); if (0 == descyl) { int failed = 0; /* * seek to cyl 0 requested; make sure we are * really there */ if (fd_sense_drive_status(fdc, &st3)) failed = 1; if ((st3 & NE7_ST3_T0) == 0) { printf( "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n", fdu, st3, NE7_ST3BITS); failed = 1; } if (failed) { if(fdc->retry < 3) fdc->retry = 3; return (retrier(fdc)); } } if (cyl != descyl) { printf( "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", fdu, descyl, cyl, st0); if (fdc->retry < 3) fdc->retry = 3; return (retrier(fdc)); } } fd->track = b_cylinder; if (!(fdc->flags & FDC_NODMA)) isa_dmastart(idf, bp->bio_data+fd->skip, format ? bp->bio_bcount : fdblk, fdc->dmachan); sectrac = fd->ft->sectrac; sec = blknum % (sectrac * fd->ft->heads); head = sec / sectrac; sec = sec % sectrac + 1; fd->hddrv = ((head&1)<<2)+fdu; if(format || !read) { /* make sure the drive is writable */ if(fd_sense_drive_status(fdc, &st3) != 0) { /* stuck controller? */ if (!(fdc->flags & FDC_NODMA)) isa_dmadone(idf, bp->bio_data + fd->skip, format ? bp->bio_bcount : fdblk, fdc->dmachan); fdc->retry = 6; /* reset the beast */ return (retrier(fdc)); } if(st3 & NE7_ST3_WP) { /* * XXX YES! this is ugly. * in order to force the current operation * to fail, we will have to fake an FDC * error - all error handling is done * by the retrier() */ fdc->status[0] = NE7_ST0_IC_AT; fdc->status[1] = NE7_ST1_NW; fdc->status[2] = 0; fdc->status[3] = fd->track; fdc->status[4] = head; fdc->status[5] = sec; fdc->retry = 8; /* break out immediately */ fdc->state = IOTIMEDOUT; /* not really... */ return (1); } } if (format) { if (fdc->flags & FDC_NODMA) { /* * This seems to be necessary for * whatever obscure reason; if we omit * it, we end up filling the sector ID * fields of the newly formatted track * entirely with garbage, causing * `wrong cylinder' errors all over * the place when trying to read them * back. * * Umpf. */ SET_BCDR(fdc, 1, bp->bio_bcount, 0); (void)fdcpio(fdc,bp->bio_cmd, bp->bio_data+fd->skip, bp->bio_bcount); } /* formatting */ if(fd_cmd(fdc, 6, NE7CMD_FORMAT, head << 2 | fdu, finfo->fd_formb_secshift, finfo->fd_formb_nsecs, finfo->fd_formb_gaplen, finfo->fd_formb_fillbyte, 0)) { /* controller fell over */ if (!(fdc->flags & FDC_NODMA)) isa_dmadone(idf, bp->bio_data + fd->skip, format ? bp->bio_bcount : fdblk, fdc->dmachan); fdc->retry = 6; return (retrier(fdc)); } } else { if (fdc->flags & FDC_NODMA) { /* * this seems to be necessary even when * reading data */ SET_BCDR(fdc, 1, fdblk, 0); /* * perform the write pseudo-DMA before * the WRITE command is sent */ if (!read) (void)fdcpio(fdc,bp->bio_cmd, bp->bio_data+fd->skip, fdblk); } if (fd_cmd(fdc, 9, (read ? NE7CMD_READ : NE7CMD_WRITE), head << 2 | fdu, /* head & unit */ fd->track, /* track */ head, sec, /* sector + 1 */ fd->ft->secsize, /* sector size */ sectrac, /* sectors/track */ fd->ft->gap, /* gap size */ fd->ft->datalen, /* data length */ 0)) { /* the beast is sleeping again */ if (!(fdc->flags & FDC_NODMA)) isa_dmadone(idf, bp->bio_data + fd->skip, format ? bp->bio_bcount : fdblk, fdc->dmachan); fdc->retry = 6; return (retrier(fdc)); } } if (fdc->flags & FDC_NODMA) /* * if this is a read, then simply await interrupt * before performing PIO */ if (read && !fdcpio(fdc,bp->bio_cmd, bp->bio_data+fd->skip,fdblk)) { fd->tohandle = timeout(fd_iotimeout, fdc, hz); return(0); /* will return later */ }; /* * write (or format) operation will fall through and * await completion interrupt */ fdc->state = IOCOMPLETE; fd->tohandle = timeout(fd_iotimeout, fdc, hz); return (0); /* will return later */ case PIOREAD: /* * actually perform the PIO read. The IOCOMPLETE case * removes the timeout for us. */ (void)fdcpio(fdc,bp->bio_cmd,bp->bio_data+fd->skip,fdblk); fdc->state = IOCOMPLETE; /* FALLTHROUGH */ case IOCOMPLETE: /* IO DONE, post-analyze */ untimeout(fd_iotimeout, fdc, fd->tohandle); if (fd_read_status(fdc, fd->fdsu)) { if (!(fdc->flags & FDC_NODMA)) isa_dmadone(idf, bp->bio_data + fd->skip, format ? bp->bio_bcount : fdblk, fdc->dmachan); if (fdc->retry < 6) fdc->retry = 6; /* force a reset */ return (retrier(fdc)); } fdc->state = IOTIMEDOUT; /* FALLTHROUGH */ case IOTIMEDOUT: if (!(fdc->flags & FDC_NODMA)) isa_dmadone(idf, bp->bio_data + fd->skip, format ? bp->bio_bcount : fdblk, fdc->dmachan); if (fdc->status[0] & NE7_ST0_IC) { if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT && fdc->status[1] & NE7_ST1_OR) { /* * DMA overrun. Someone hogged the bus * and didn't release it in time for the * next FDC transfer. * Just restart it, don't increment retry * count. (vak) */ fdc->state = SEEKCOMPLETE; return (1); } else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV && fdc->retry < 6) fdc->retry = 6; /* force a reset */ else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT && fdc->status[2] & NE7_ST2_WC && fdc->retry < 3) fdc->retry = 3; /* force recalibrate */ return (retrier(fdc)); } /* All OK */ fd->skip += fdblk; if (!format && fd->skip < bp->bio_bcount - bp->bio_resid) { /* set up next transfer */ fdc->state = DOSEEK; } else { /* ALL DONE */ fd->skip = 0; fdc->bp = NULL; device_unbusy(fd->dev); biofinish(bp, &fd->device_stats, 0); fdc->fd = (fd_p) 0; fdc->fdu = -1; fdc->state = FINDWORK; } return (1); case RESETCTLR: fdc_reset(fdc); fdc->retry++; fdc->state = RESETCOMPLETE; return (0); case RESETCOMPLETE: /* * Discard all the results from the reset so that they * can't cause an unexpected interrupt later. */ for (i = 0; i < 4; i++) (void)fd_sense_int(fdc, &st0, &cyl); fdc->state = STARTRECAL; /* Fall through. */ case STARTRECAL: if(fd_cmd(fdc, 2, NE7CMD_RECAL, fdu, 0)) { /* arrgl */ fdc->retry = 6; return (retrier(fdc)); } fdc->state = RECALWAIT; return (0); /* will return later */ case RECALWAIT: /* allow heads to settle */ timeout(fd_pseudointr, fdc, hz / 8); fdc->state = RECALCOMPLETE; return (0); /* will return later */ case RECALCOMPLETE: do { /* * See SEEKCOMPLETE for a comment on this: */ if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) return 0; if(fdc->fdct == FDC_NE765 && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) return 0; /* hope for a real intr */ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0) { if(fdc->retry > 3) /* * a recalibrate from beyond cylinder 77 * will "fail" due to the FDC limitations; * since people used to complain much about * the failure message, try not logging * this one if it seems to be the first * time in a line */ printf("fd%d: recal failed ST0 %b cyl %d\n", fdu, st0, NE7_ST0BITS, cyl); if(fdc->retry < 3) fdc->retry = 3; return (retrier(fdc)); } fd->track = 0; /* Seek (probably) necessary */ fdc->state = DOSEEK; return (1); /* will return immediatly */ case MOTORWAIT: if(fd->flags & FD_MOTOR_WAIT) { return (0); /* time's not up yet */ } if (fdc->flags & FDC_NEEDS_RESET) { fdc->state = RESETCTLR; fdc->flags &= ~FDC_NEEDS_RESET; } else { /* * If all motors were off, then the controller was * reset, so it has lost track of the current * cylinder. Recalibrate to handle this case. * But first, discard the results of the reset. */ fdc->state = RESETCOMPLETE; } return (1); /* will return immediatly */ default: device_printf(fdc->fdc_dev, "unexpected FD int->"); if (fd_read_status(fdc, fd->fdsu) == 0) printf("FDC status :%x %x %x %x %x %x %x ", fdc->status[0], fdc->status[1], fdc->status[2], fdc->status[3], fdc->status[4], fdc->status[5], fdc->status[6] ); else printf("No status available "); if (fd_sense_int(fdc, &st0, &cyl) != 0) { printf("[controller is dead now]\n"); return (0); } printf("ST0 = %x, PCN = %x\n", st0, cyl); return (0); } /*XXX confusing: some branches return immediately, others end up here*/ return (1); /* Come back immediatly to new state */ } static int retrier(struct fdc_data *fdc) { struct bio *bp; struct fd_data *fd; int fdu; bp = fdc->bp; /* XXX shouldn't this be cached somewhere? */ fdu = FDUNIT(minor(bp->bio_dev)); fd = devclass_get_softc(fd_devclass, fdu); if (fd->options & FDOPT_NORETRY) goto fail; switch (fdc->retry) { case 0: case 1: case 2: fdc->state = SEEKCOMPLETE; break; case 3: case 4: case 5: fdc->state = STARTRECAL; break; case 6: fdc->state = RESETCTLR; break; case 7: break; default: fail: { int printerror = (fd->options & FDOPT_NOERRLOG) == 0; if (printerror) diskerr(bp, "hard error", fdc->fd->skip / DEV_BSIZE, (struct disklabel *)NULL); if (printerror) { if (fdc->flags & FDC_STAT_VALID) { printf( " (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n", fdc->status[0], NE7_ST0BITS, fdc->status[1], NE7_ST1BITS, fdc->status[2], NE7_ST2BITS, fdc->status[3], fdc->status[4], fdc->status[5]); } else printf(" (No status)\n"); } } if ((fd->options & FDOPT_NOERROR) == 0) { bp->bio_flags |= BIO_ERROR; bp->bio_error = EIO; bp->bio_resid += bp->bio_bcount - fdc->fd->skip; } fdc->bp = NULL; fdc->fd->skip = 0; device_unbusy(fd->dev); biofinish(bp, &fdc->fd->device_stats, 0); fdc->state = FINDWORK; fdc->flags |= FDC_NEEDS_RESET; fdc->fd = (fd_p) 0; fdc->fdu = -1; return (1); } fdc->retry++; return (1); } static void fdbiodone(struct bio *bp) { wakeup(bp); } static int fdformat(dev, finfo, p) dev_t dev; struct fd_formb *finfo; struct proc *p; { fdu_t fdu; fd_p fd; struct bio *bp; int rv = 0, s; size_t fdblk; fdu = FDUNIT(minor(dev)); fd = devclass_get_softc(fd_devclass, fdu); fdblk = 128 << fd->ft->secsize; /* set up a buffer header for fdstrategy() */ bp = (struct bio *)malloc(sizeof(struct bio), M_TEMP, M_NOWAIT); if(bp == 0) return ENOMEM; /* * keep the process from being swapped */ PHOLD(p); bzero((void *)bp, sizeof(*bp)); bp->bio_cmd = BIO_FORMAT; /* * calculate a fake blkno, so fdstrategy() would initiate a * seek to the requested cylinder */ bp->bio_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) + finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE; bp->bio_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; bp->bio_data = (caddr_t)finfo; /* now do the format */ bp->bio_dev = dev; bp->bio_done = fdbiodone; fdstrategy(bp); /* ...and wait for it to complete */ s = splbio(); while(!(bp->bio_flags & BIO_DONE)) { rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz); if (rv == EWOULDBLOCK) break; } splx(s); if (rv == EWOULDBLOCK) { /* timed out */ rv = EIO; device_unbusy(fd->dev); } if (bp->bio_flags & BIO_ERROR) rv = bp->bio_error; /* * allow the process to be swapped */ PRELE(p); free(bp, M_TEMP); return rv; } /* * TODO: don't allocate buffer on stack. */ static int fdioctl(dev, cmd, addr, flag, p) dev_t dev; u_long cmd; caddr_t addr; int flag; struct proc *p; { fdu_t fdu = FDUNIT(minor(dev)); fd_p fd = devclass_get_softc(fd_devclass, fdu); size_t fdblk; struct fd_type *fdt; struct disklabel *dl; struct fdc_status *fsp; char buffer[DEV_BSIZE]; int error = 0; fdblk = 128 << fd->ft->secsize; switch (cmd) { case DIOCGDINFO: bzero(buffer, sizeof (buffer)); dl = (struct disklabel *)buffer; dl->d_secsize = fdblk; fdt = fd->ft; dl->d_secpercyl = fdt->size / fdt->tracks; dl->d_type = DTYPE_FLOPPY; if (readdisklabel(dkmodpart(dev, RAW_PART), dl) == NULL) error = 0; else error = EINVAL; *(struct disklabel *)addr = *dl; break; case DIOCSDINFO: if ((flag & FWRITE) == 0) error = EBADF; break; case DIOCWLABEL: if ((flag & FWRITE) == 0) error = EBADF; break; case DIOCWDINFO: if ((flag & FWRITE) == 0) { error = EBADF; break; } dl = (struct disklabel *)addr; if ((error = setdisklabel((struct disklabel *)buffer, dl, (u_long)0)) != 0) break; error = writedisklabel(dev, (struct disklabel *)buffer); break; case FD_FORM: if ((flag & FWRITE) == 0) error = EBADF; /* must be opened for writing */ else if (((struct fd_formb *)addr)->format_version != FD_FORMAT_VERSION) error = EINVAL; /* wrong version of formatting prog */ else error = fdformat(dev, (struct fd_formb *)addr, p); break; case FD_GTYPE: /* get drive type */ *(struct fd_type *)addr = *fd->ft; break; case FD_STYPE: /* set drive type */ /* this is considered harmful; only allow for superuser */ if (suser(p) != 0) return EPERM; *fd->ft = *(struct fd_type *)addr; break; case FD_GOPTS: /* get drive options */ *(int *)addr = fd->options; break; case FD_SOPTS: /* set drive options */ fd->options = *(int *)addr; break; case FD_CLRERR: if (suser(p) != 0) return EPERM; fd->fdc->fdc_errs = 0; break; case FD_GSTAT: fsp = (struct fdc_status *)addr; if ((fd->fdc->flags & FDC_STAT_VALID) == 0) return EINVAL; memcpy(fsp->status, fd->fdc->status, 7 * sizeof(u_int)); break; default: error = ENOTTY; break; } return (error); } /* * Hello emacs, these are the * Local Variables: * c-indent-level: 8 * c-continued-statement-offset: 8 * c-continued-brace-offset: 0 * c-brace-offset: -8 * c-brace-imaginary-offset: 0 * c-argdecl-indent: 8 * c-label-offset: -8 * c++-hanging-braces: 1 * c++-access-specifier-offset: -8 * c++-empty-arglist-indent: 8 * c++-friend-offset: 0 * End: */ Index: head/sys/isa/isahint.c =================================================================== --- head/sys/isa/isahint.c (revision 78134) +++ head/sys/isa/isahint.c (revision 78135) @@ -1,132 +1,130 @@ /*- * Copyright (c) 1999 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include static void isahint_add_device(device_t parent, const char *name, int unit) { device_t child; int sensitive, start, count, t; int order; /* device-specific flag overrides any wildcard */ sensitive = 0; if (resource_int_value(name, unit, "sensitive", &sensitive) != 0) resource_int_value(name, -1, "sensitive", &sensitive); if (sensitive) order = ISA_ORDER_SENSITIVE; else order = ISA_ORDER_SPECULATIVE; child = BUS_ADD_CHILD(parent, order, name, unit); if (child == 0) return; start = 0; count = 0; resource_int_value(name, unit, "port", &start); resource_int_value(name, unit, "portsize", &count); if (start > 0 || count > 0) bus_set_resource(child, SYS_RES_IOPORT, 0, start, count); start = 0; count = 0; resource_int_value(name, unit, "maddr", &start); resource_int_value(name, unit, "msize", &count); if (start > 0 || count > 0) bus_set_resource(child, SYS_RES_MEMORY, 0, start, count); if (resource_int_value(name, unit, "irq", &start) == 0 && start > 0) bus_set_resource(child, SYS_RES_IRQ, 0, start, 1); if (resource_int_value(name, unit, "drq", &start) == 0 && start >= 0) bus_set_resource(child, SYS_RES_DRQ, 0, start, 1); if (resource_int_value(name, unit, "flags", &t) == 0) device_set_flags(child, t); if (resource_int_value(name, unit, "disabled", &t) == 0 && t != 0) device_disable(child); } static void isahint_identify(driver_t *driver, device_t parent) { int i; static char buf[] = "isaXXX"; + const char *dname; + int dunit; /* * Add all devices configured to be attached to parent. */ sprintf(buf, "isa%d", device_get_unit(parent)); - i = -1; - while ((i = resource_query_string(i, "at", buf)) != -1) { - if (strcmp(resource_query_name(i), "atkbd") == 0) + i = 0; + while ((resource_find_match(&i, &dname, &dunit, "at", buf)) == 0) { + if (strcmp(dname, "atkbd") == 0) continue; /* old GENERIC kludge */ - isahint_add_device(parent, - resource_query_name(i), - resource_query_unit(i)); + isahint_add_device(parent, dname, dunit); } /* * and isa? */ - i = -1; - while ((i = resource_query_string(i, "at", "isa")) != -1) { - if (strcmp(resource_query_name(i), "atkbd") == 0) + i = 0; + while ((resource_find_match(&i, &dname, &dunit, "at", "isa")) == 0) { + if (strcmp(dname, "atkbd") == 0) continue; /* old GENERIC kludge */ - isahint_add_device(parent, - resource_query_name(i), - resource_query_unit(i)); + isahint_add_device(parent, dname, dunit); } } static device_method_t isahint_methods[] = { /* Device interface */ DEVMETHOD(device_identify, isahint_identify), { 0, 0 } }; static driver_t isahint_driver = { "hint", isahint_methods, 1, /* no softc */ }; static devclass_t hint_devclass; DRIVER_MODULE(isahint, isa, isahint_driver, hint_devclass, 0, 0); Index: head/sys/kern/subr_bus.c =================================================================== --- head/sys/kern/subr_bus.c (revision 78134) +++ head/sys/kern/subr_bus.c (revision 78135) @@ -1,2593 +1,2402 @@ /*- * Copyright (c) 1997,1998 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * * $FreeBSD$ */ #include "opt_bus.h" #include #include #include #include #include #include #include #include #include #include #include #include /* for device_printf() */ static MALLOC_DEFINE(M_BUS, "bus", "Bus data structures"); #ifdef BUS_DEBUG static int bus_debug = 1; SYSCTL_INT(_debug, OID_AUTO, bus_debug, CTLFLAG_RW, &bus_debug, 0, "Debug bus code"); #define PDEBUG(a) if (bus_debug) {printf(__FUNCTION__ ":%d: ", __LINE__), printf a, printf("\n");} #define DEVICENAME(d) ((d)? device_get_name(d): "no device") #define DRIVERNAME(d) ((d)? d->name : "no driver") #define DEVCLANAME(d) ((d)? d->name : "no devclass") /* Produce the indenting, indent*2 spaces plus a '.' ahead of that to * prevent syslog from deleting initial spaces */ #define indentprintf(p) do { int iJ; printf("."); for (iJ=0; iJname, classname)) return (dc); } PDEBUG(("%s not found%s", classname, (create? ", creating": ""))); if (create) { dc = malloc(sizeof(struct devclass) + strlen(classname) + 1, M_BUS, M_NOWAIT|M_ZERO); if (!dc) return (NULL); dc->name = (char*) (dc + 1); strcpy(dc->name, classname); dc->devices = NULL; dc->maxunit = 0; TAILQ_INIT(&dc->drivers); TAILQ_INSERT_TAIL(&devclasses, dc, link); bus_data_generation_update(); } return (dc); } devclass_t devclass_create(const char *classname) { return (devclass_find_internal(classname, TRUE)); } devclass_t devclass_find(const char *classname) { return (devclass_find_internal(classname, FALSE)); } int devclass_add_driver(devclass_t dc, driver_t *driver) { driverlink_t dl; int i; PDEBUG(("%s", DRIVERNAME(driver))); dl = malloc(sizeof *dl, M_BUS, M_NOWAIT|M_ZERO); if (!dl) return (ENOMEM); /* * Compile the driver's methods. Also increase the reference count * so that the class doesn't get freed when the last instance * goes. This means we can safely use static methods and avoids a * double-free in devclass_delete_driver. */ kobj_class_compile((kobj_class_t) driver); /* * Make sure the devclass which the driver is implementing exists. */ devclass_find_internal(driver->name, TRUE); dl->driver = driver; TAILQ_INSERT_TAIL(&dc->drivers, dl, link); driver->refs++; /* * Call BUS_DRIVER_ADDED for any existing busses in this class. */ for (i = 0; i < dc->maxunit; i++) if (dc->devices[i]) BUS_DRIVER_ADDED(dc->devices[i], driver); bus_data_generation_update(); return (0); } int devclass_delete_driver(devclass_t busclass, driver_t *driver) { devclass_t dc = devclass_find(driver->name); driverlink_t dl; device_t dev; int i; int error; PDEBUG(("%s from devclass %s", driver->name, DEVCLANAME(busclass))); if (!dc) return (0); /* * Find the link structure in the bus' list of drivers. */ TAILQ_FOREACH(dl, &busclass->drivers, link) { if (dl->driver == driver) break; } if (!dl) { PDEBUG(("%s not found in %s list", driver->name, busclass->name)); return (ENOENT); } /* * Disassociate from any devices. We iterate through all the * devices in the devclass of the driver and detach any which are * using the driver and which have a parent in the devclass which * we are deleting from. * * Note that since a driver can be in multiple devclasses, we * should not detach devices which are not children of devices in * the affected devclass. */ for (i = 0; i < dc->maxunit; i++) { if (dc->devices[i]) { dev = dc->devices[i]; if (dev->driver == driver && dev->parent && dev->parent->devclass == busclass) { if ((error = device_detach(dev)) != 0) return (error); device_set_driver(dev, NULL); } } } TAILQ_REMOVE(&busclass->drivers, dl, link); free(dl, M_BUS); driver->refs--; if (driver->refs == 0) kobj_class_free((kobj_class_t) driver); bus_data_generation_update(); return (0); } static driverlink_t devclass_find_driver_internal(devclass_t dc, const char *classname) { driverlink_t dl; PDEBUG(("%s in devclass %s", classname, DEVCLANAME(dc))); TAILQ_FOREACH(dl, &dc->drivers, link) { if (!strcmp(dl->driver->name, classname)) return (dl); } PDEBUG(("not found")); return (NULL); } driver_t * devclass_find_driver(devclass_t dc, const char *classname) { driverlink_t dl; dl = devclass_find_driver_internal(dc, classname); if (dl) return (dl->driver); return (NULL); } const char * devclass_get_name(devclass_t dc) { return (dc->name); } device_t devclass_get_device(devclass_t dc, int unit) { if (dc == NULL || unit < 0 || unit >= dc->maxunit) return (NULL); return (dc->devices[unit]); } void * devclass_get_softc(devclass_t dc, int unit) { device_t dev; dev = devclass_get_device(dc, unit); if (!dev) return (NULL); return (device_get_softc(dev)); } int devclass_get_devices(devclass_t dc, device_t **devlistp, int *devcountp) { int i; int count; device_t *list; count = 0; for (i = 0; i < dc->maxunit; i++) if (dc->devices[i]) count++; list = malloc(count * sizeof(device_t), M_TEMP, M_NOWAIT|M_ZERO); if (!list) return (ENOMEM); count = 0; for (i = 0; i < dc->maxunit; i++) { if (dc->devices[i]) { list[count] = dc->devices[i]; count++; } } *devlistp = list; *devcountp = count; return (0); } int devclass_get_maxunit(devclass_t dc) { return (dc->maxunit); } static int devclass_alloc_unit(devclass_t dc, int *unitp) { int unit = *unitp; PDEBUG(("unit %d in devclass %s", unit, DEVCLANAME(dc))); /* If we were given a wired unit number, check for existing device */ /* XXX imp XXX */ if (unit != -1) { if (unit >= 0 && unit < dc->maxunit && dc->devices[unit] != NULL) { /* find the next available slot */ while (++unit < dc->maxunit && dc->devices[unit] != NULL) continue; if (bootverbose) printf("%s-: %s%d already exists, using %s%d instead\n", dc->name, dc->name, *unitp, dc->name, unit); } } else { /* Unwired device, find the next available slot for it */ unit = 0; while (unit < dc->maxunit && dc->devices[unit] != NULL) unit++; } /* * We've selected a unit beyond the length of the table, so let's * extend the table to make room for all units up to and including * this one. */ if (unit >= dc->maxunit) { device_t *newlist; int newsize; newsize = roundup((unit + 1), MINALLOCSIZE / sizeof(device_t)); newlist = malloc(sizeof(device_t) * newsize, M_BUS, M_NOWAIT); if (!newlist) return (ENOMEM); bcopy(dc->devices, newlist, sizeof(device_t) * dc->maxunit); bzero(newlist + dc->maxunit, sizeof(device_t) * (newsize - dc->maxunit)); if (dc->devices) free(dc->devices, M_BUS); dc->devices = newlist; dc->maxunit = newsize; } PDEBUG(("now: unit %d in devclass %s", unit, DEVCLANAME(dc))); *unitp = unit; return (0); } static int devclass_add_device(devclass_t dc, device_t dev) { int buflen, error; PDEBUG(("%s in devclass %s", DEVICENAME(dev), DEVCLANAME(dc))); buflen = strlen(dc->name) + 5; dev->nameunit = malloc(buflen, M_BUS, M_NOWAIT|M_ZERO); if (!dev->nameunit) return (ENOMEM); if ((error = devclass_alloc_unit(dc, &dev->unit)) != 0) { free(dev->nameunit, M_BUS); dev->nameunit = NULL; return (error); } dc->devices[dev->unit] = dev; dev->devclass = dc; snprintf(dev->nameunit, buflen, "%s%d", dc->name, dev->unit); return (0); } static int devclass_delete_device(devclass_t dc, device_t dev) { if (!dc || !dev) return (0); PDEBUG(("%s in devclass %s", DEVICENAME(dev), DEVCLANAME(dc))); if (dev->devclass != dc || dc->devices[dev->unit] != dev) panic("devclass_delete_device: inconsistent device class"); dc->devices[dev->unit] = NULL; if (dev->flags & DF_WILDCARD) dev->unit = -1; dev->devclass = NULL; free(dev->nameunit, M_BUS); dev->nameunit = NULL; return (0); } static device_t make_device(device_t parent, const char *name, int unit) { device_t dev; devclass_t dc; PDEBUG(("%s at %s as unit %d", name, DEVICENAME(parent), unit)); if (name) { dc = devclass_find_internal(name, TRUE); if (!dc) { printf("make_device: can't find device class %s\n", name); return (NULL); } } else { dc = NULL; } dev = malloc(sizeof(struct device), M_BUS, M_NOWAIT|M_ZERO); if (!dev) return (0); dev->parent = parent; TAILQ_INIT(&dev->children); kobj_init((kobj_t) dev, &null_class); dev->driver = NULL; dev->devclass = NULL; dev->unit = unit; dev->nameunit = NULL; dev->desc = NULL; dev->busy = 0; dev->devflags = 0; dev->flags = DF_ENABLED; dev->order = 0; if (unit == -1) dev->flags |= DF_WILDCARD; if (name) { dev->flags |= DF_FIXEDCLASS; devclass_add_device(dc, dev); } dev->ivars = NULL; dev->softc = NULL; dev->state = DS_NOTPRESENT; TAILQ_INSERT_TAIL(&bus_data_devices, dev, devlink); bus_data_generation_update(); return (dev); } static int device_print_child(device_t dev, device_t child) { int retval = 0; if (device_is_alive(child)) retval += BUS_PRINT_CHILD(dev, child); else retval += device_printf(child, " not found\n"); return (retval); } device_t device_add_child(device_t dev, const char *name, int unit) { return (device_add_child_ordered(dev, 0, name, unit)); } device_t device_add_child_ordered(device_t dev, int order, const char *name, int unit) { device_t child; device_t place; PDEBUG(("%s at %s with order %d as unit %d", name, DEVICENAME(dev), order, unit)); child = make_device(dev, name, unit); if (child == NULL) return (child); child->order = order; TAILQ_FOREACH(place, &dev->children, link) { if (place->order > order) break; } if (place) { /* * The device 'place' is the first device whose order is * greater than the new child. */ TAILQ_INSERT_BEFORE(place, child, link); } else { /* * The new child's order is greater or equal to the order of * any existing device. Add the child to the tail of the list. */ TAILQ_INSERT_TAIL(&dev->children, child, link); } bus_data_generation_update(); return (child); } int device_delete_child(device_t dev, device_t child) { int error; device_t grandchild; PDEBUG(("%s from %s", DEVICENAME(child), DEVICENAME(dev))); /* remove children first */ while ( (grandchild = TAILQ_FIRST(&child->children)) ) { error = device_delete_child(child, grandchild); if (error) return (error); } if ((error = device_detach(child)) != 0) return (error); if (child->devclass) devclass_delete_device(child->devclass, child); TAILQ_REMOVE(&dev->children, child, link); TAILQ_REMOVE(&bus_data_devices, child, devlink); device_set_desc(child, NULL); free(child, M_BUS); bus_data_generation_update(); return (0); } /* * Find only devices attached to this bus. */ device_t device_find_child(device_t dev, const char *classname, int unit) { devclass_t dc; device_t child; dc = devclass_find(classname); if (!dc) return (NULL); child = devclass_get_device(dc, unit); if (child && child->parent == dev) return (child); return (NULL); } static driverlink_t first_matching_driver(devclass_t dc, device_t dev) { if (dev->devclass) return (devclass_find_driver_internal(dc, dev->devclass->name)); return (TAILQ_FIRST(&dc->drivers)); } static driverlink_t next_matching_driver(devclass_t dc, device_t dev, driverlink_t last) { if (dev->devclass) { driverlink_t dl; for (dl = TAILQ_NEXT(last, link); dl; dl = TAILQ_NEXT(dl, link)) if (!strcmp(dev->devclass->name, dl->driver->name)) return (dl); return (NULL); } return (TAILQ_NEXT(last, link)); } static int device_probe_child(device_t dev, device_t child) { devclass_t dc; driverlink_t best = 0; driverlink_t dl; int result, pri = 0; int hasclass = (child->devclass != 0); dc = dev->devclass; if (!dc) panic("device_probe_child: parent device has no devclass"); if (child->state == DS_ALIVE) return (0); for (dl = first_matching_driver(dc, child); dl; dl = next_matching_driver(dc, child, dl)) { PDEBUG(("Trying %s", DRIVERNAME(dl->driver))); device_set_driver(child, dl->driver); if (!hasclass) device_set_devclass(child, dl->driver->name); result = DEVICE_PROBE(child); if (!hasclass) device_set_devclass(child, 0); /* * If the driver returns SUCCESS, there can be no higher match * for this device. */ if (result == 0) { best = dl; pri = 0; break; } /* * The driver returned an error so it certainly doesn't match. */ if (result > 0) { device_set_driver(child, 0); continue; } /* * A priority lower than SUCCESS, remember the best matching * driver. Initialise the value of pri for the first match. */ if (best == 0 || result > pri) { best = dl; pri = result; continue; } } /* * If we found a driver, change state and initialise the devclass. */ if (best) { if (!child->devclass) device_set_devclass(child, best->driver->name); device_set_driver(child, best->driver); if (pri < 0) { /* * A bit bogus. Call the probe method again to make * sure that we have the right description. */ DEVICE_PROBE(child); } child->state = DS_ALIVE; bus_data_generation_update(); return (0); } return (ENXIO); } device_t device_get_parent(device_t dev) { return (dev->parent); } int device_get_children(device_t dev, device_t **devlistp, int *devcountp) { int count; device_t child; device_t *list; count = 0; TAILQ_FOREACH(child, &dev->children, link) { count++; } list = malloc(count * sizeof(device_t), M_TEMP, M_NOWAIT|M_ZERO); if (!list) return (ENOMEM); count = 0; TAILQ_FOREACH(child, &dev->children, link) { list[count] = child; count++; } *devlistp = list; *devcountp = count; return (0); } driver_t * device_get_driver(device_t dev) { return (dev->driver); } devclass_t device_get_devclass(device_t dev) { return (dev->devclass); } const char * device_get_name(device_t dev) { if (dev->devclass) return (devclass_get_name(dev->devclass)); return (NULL); } const char * device_get_nameunit(device_t dev) { return (dev->nameunit); } int device_get_unit(device_t dev) { return (dev->unit); } const char * device_get_desc(device_t dev) { return (dev->desc); } u_int32_t device_get_flags(device_t dev) { return (dev->devflags); } int device_print_prettyname(device_t dev) { const char *name = device_get_name(dev); if (name == 0) return (printf("unknown: ")); return (printf("%s%d: ", name, device_get_unit(dev))); } int device_printf(device_t dev, const char * fmt, ...) { va_list ap; int retval; retval = device_print_prettyname(dev); va_start(ap, fmt); retval += vprintf(fmt, ap); va_end(ap); return (retval); } static void device_set_desc_internal(device_t dev, const char* desc, int copy) { if (dev->desc && (dev->flags & DF_DESCMALLOCED)) { free(dev->desc, M_BUS); dev->flags &= ~DF_DESCMALLOCED; dev->desc = NULL; } if (copy && desc) { dev->desc = malloc(strlen(desc) + 1, M_BUS, M_NOWAIT); if (dev->desc) { strcpy(dev->desc, desc); dev->flags |= DF_DESCMALLOCED; } } else { /* Avoid a -Wcast-qual warning */ dev->desc = (char *)(uintptr_t) desc; } bus_data_generation_update(); } void device_set_desc(device_t dev, const char* desc) { device_set_desc_internal(dev, desc, FALSE); } void device_set_desc_copy(device_t dev, const char* desc) { device_set_desc_internal(dev, desc, TRUE); } void device_set_flags(device_t dev, u_int32_t flags) { dev->devflags = flags; } void * device_get_softc(device_t dev) { return (dev->softc); } void device_set_softc(device_t dev, void *softc) { if (dev->softc && !(dev->flags & DF_EXTERNALSOFTC)) free(dev->softc, M_BUS); dev->softc = softc; if (dev->softc) dev->flags |= DF_EXTERNALSOFTC; else dev->flags &= ~DF_EXTERNALSOFTC; } void * device_get_ivars(device_t dev) { return (dev->ivars); } void device_set_ivars(device_t dev, void * ivars) { if (!dev) return; dev->ivars = ivars; return; } device_state_t device_get_state(device_t dev) { return (dev->state); } void device_enable(device_t dev) { dev->flags |= DF_ENABLED; } void device_disable(device_t dev) { dev->flags &= ~DF_ENABLED; } void device_busy(device_t dev) { if (dev->state < DS_ATTACHED) panic("device_busy: called for unattached device"); if (dev->busy == 0 && dev->parent) device_busy(dev->parent); dev->busy++; dev->state = DS_BUSY; } void device_unbusy(device_t dev) { if (dev->state != DS_BUSY) panic("device_unbusy: called for non-busy device"); dev->busy--; if (dev->busy == 0) { if (dev->parent) device_unbusy(dev->parent); dev->state = DS_ATTACHED; } } void device_quiet(device_t dev) { dev->flags |= DF_QUIET; } void device_verbose(device_t dev) { dev->flags &= ~DF_QUIET; } int device_is_quiet(device_t dev) { return ((dev->flags & DF_QUIET) != 0); } int device_is_enabled(device_t dev) { return ((dev->flags & DF_ENABLED) != 0); } int device_is_alive(device_t dev) { return (dev->state >= DS_ALIVE); } int device_set_devclass(device_t dev, const char *classname) { devclass_t dc; int error; if (!classname) { if (dev->devclass) devclass_delete_device(dev->devclass, dev); return (0); } if (dev->devclass) { printf("device_set_devclass: device class already set\n"); return (EINVAL); } dc = devclass_find_internal(classname, TRUE); if (!dc) return (ENOMEM); error = devclass_add_device(dc, dev); bus_data_generation_update(); return (error); } int device_set_driver(device_t dev, driver_t *driver) { if (dev->state >= DS_ATTACHED) return (EBUSY); if (dev->driver == driver) return (0); if (dev->softc && !(dev->flags & DF_EXTERNALSOFTC)) { free(dev->softc, M_BUS); dev->softc = NULL; } kobj_delete((kobj_t) dev, 0); dev->driver = driver; if (driver) { kobj_init((kobj_t) dev, (kobj_class_t) driver); if (!(dev->flags & DF_EXTERNALSOFTC)) { dev->softc = malloc(driver->size, M_BUS, M_NOWAIT | M_ZERO); if (!dev->softc) { kobj_init((kobj_t) dev, &null_class); dev->driver = NULL; return (ENOMEM); } } } else { kobj_init((kobj_t) dev, &null_class); } bus_data_generation_update(); return (0); } int device_probe_and_attach(device_t dev) { device_t bus = dev->parent; int error = 0; int hasclass = (dev->devclass != 0); if (dev->state >= DS_ALIVE) return (0); if (dev->flags & DF_ENABLED) { error = device_probe_child(bus, dev); if (!error) { if (!device_is_quiet(dev)) device_print_child(bus, dev); error = DEVICE_ATTACH(dev); if (!error) dev->state = DS_ATTACHED; else { printf("device_probe_and_attach: %s%d attach returned %d\n", dev->driver->name, dev->unit, error); /* Unset the class; set in device_probe_child */ if (!hasclass) device_set_devclass(dev, 0); device_set_driver(dev, NULL); dev->state = DS_NOTPRESENT; } } else { if (!(dev->flags & DF_DONENOMATCH)) { BUS_PROBE_NOMATCH(bus, dev); dev->flags |= DF_DONENOMATCH; } } } else { if (bootverbose) { device_print_prettyname(dev); printf("not probed (disabled)\n"); } } return (error); } int device_detach(device_t dev) { int error; PDEBUG(("%s", DEVICENAME(dev))); if (dev->state == DS_BUSY) return (EBUSY); if (dev->state != DS_ATTACHED) return (0); if ((error = DEVICE_DETACH(dev)) != 0) return (error); device_printf(dev, "detached\n"); if (dev->parent) BUS_CHILD_DETACHED(dev->parent, dev); if (!(dev->flags & DF_FIXEDCLASS)) devclass_delete_device(dev->devclass, dev); dev->state = DS_NOTPRESENT; device_set_driver(dev, NULL); return (0); } int device_shutdown(device_t dev) { if (dev->state < DS_ATTACHED) return (0); return (DEVICE_SHUTDOWN(dev)); } int device_set_unit(device_t dev, int unit) { devclass_t dc; int err; dc = device_get_devclass(dev); if (unit < dc->maxunit && dc->devices[unit]) return (EBUSY); err = devclass_delete_device(dc, dev); if (err) return (err); dev->unit = unit; err = devclass_add_device(dc, dev); if (err) return (err); bus_data_generation_update(); return (0); } /*======================================*/ /* - * Access functions for device resources. - */ - -/* Runtime version */ -static struct config_device *devtab; -static int devtab_count = 0; - -static int -resource_new_name(const char *name, int unit) -{ - struct config_device *new; - - new = malloc((devtab_count + 1) * sizeof(*new), M_TEMP, M_NOWAIT); - if (new == NULL) - return (-1); - if (devtab && devtab_count > 0) - bcopy(devtab, new, devtab_count * sizeof(*new)); - bzero(&new[devtab_count], sizeof(*new)); - new[devtab_count].name = malloc(strlen(name) + 1, M_TEMP, M_NOWAIT); - if (new[devtab_count].name == NULL) { - free(new, M_TEMP); - return (-1); - } - strcpy(new[devtab_count].name, name); - new[devtab_count].unit = unit; - new[devtab_count].resource_count = 0; - new[devtab_count].resources = NULL; - devtab = new; - return (devtab_count++); -} - -static int -resource_new_resname(int j, const char *resname, resource_type type) -{ - struct config_resource *new; - int i; - - i = devtab[j].resource_count; - new = malloc((i + 1) * sizeof(*new), M_TEMP, M_NOWAIT); - if (new == NULL) - return (-1); - if (devtab[j].resources && i > 0) - bcopy(devtab[j].resources, new, i * sizeof(*new)); - bzero(&new[i], sizeof(*new)); - new[i].name = malloc(strlen(resname) + 1, M_TEMP, M_NOWAIT); - if (new[i].name == NULL) { - free(new, M_TEMP); - return (-1); - } - strcpy(new[i].name, resname); - new[i].type = type; - if (devtab[j].resources) - free(devtab[j].resources, M_TEMP); - devtab[j].resources = new; - devtab[j].resource_count = i + 1; - return (i); -} - -static int -resource_match_string(int i, const char *resname, const char *value) -{ - int j; - struct config_resource *res; - - for (j = 0, res = devtab[i].resources; - j < devtab[i].resource_count; j++, res++) - if (!strcmp(res->name, resname) - && res->type == RES_STRING - && !strcmp(res->u.stringval, value)) - return (j); - return (-1); -} - -static int -resource_find_hard(char *cp, const char *name, int unit, - const char *resname, struct config_resource **result) -{ - char match[256]; - int matchlen; - char *op; - long val; - - snprintf(match, sizeof(match), "hint.%s.%d.%s=", name, unit, resname); - matchlen = strlen(match); - while (cp) { - if (strncmp(match, cp, matchlen) == 0) - break; - while (*cp != '\0') - cp++; - cp++; - if (*cp == '\0') { - cp = NULL; - break; - } - } - if (cp) - cp += matchlen; /* skip over name and '=' */ - else - return (ENOENT); - val = strtoul(cp, &op, 0); - if (*cp != '\0' && *op == '\0') { - (*result)->type = RES_INT; - (*result)->u.intval = val; - } else { - (*result)->type = RES_STRING; - (*result)->u.stringval = cp; - } - return (0); -} - -static int -resource_find(const char *name, int unit, const char *resname, - struct config_resource **result) -{ - int i, j; - struct config_resource *res; - - if (!hints_loaded) { - /* First specific, then generic. Dynamic over static. */ - i = resource_find_hard(kern_envp, name, unit, resname, result); - if (i == 0) - return (0); - i = resource_find_hard(static_hints, name, unit, resname, - result); - if (i == 0) - return (0); - i = resource_find_hard(kern_envp, name, -1, resname, result); - if (i == 0) - return (0); - i = resource_find_hard(static_hints, name, -1, resname, result); - return (i); - } - - /* - * First check specific instances, then generic. - */ - for (i = 0; i < devtab_count; i++) { - if (devtab[i].unit < 0) - continue; - if (!strcmp(devtab[i].name, name) && devtab[i].unit == unit) { - res = devtab[i].resources; - for (j = 0; j < devtab[i].resource_count; j++, res++) - if (!strcmp(res->name, resname)) { - *result = res; - return (0); - } - } - } - for (i = 0; i < devtab_count; i++) { - if (devtab[i].unit >= 0) - continue; - if (!strcmp(devtab[i].name, name)) { - res = devtab[i].resources; - for (j = 0; j < devtab[i].resource_count; j++, res++) - if (!strcmp(res->name, resname)) { - *result = res; - return (0); - } - } - } - return (ENOENT); -} - -int -resource_int_value(const char *name, int unit, const char *resname, int *result) -{ - struct config_resource tmpres; - struct config_resource *res; - int error; - - res = &tmpres; - if ((error = resource_find(name, unit, resname, &res)) != 0) - return (error); - if (res->type != RES_INT) - return (EFTYPE); - *result = res->u.intval; - return (0); -} - -int -resource_long_value(const char *name, int unit, const char *resname, - long *result) -{ - struct config_resource tmpres; - struct config_resource *res; - int error; - - res = &tmpres; - if ((error = resource_find(name, unit, resname, &res)) != 0) - return (error); - if (res->type != RES_LONG) - return (EFTYPE); - *result = res->u.longval; - return (0); -} - -int -resource_string_value(const char *name, int unit, const char *resname, - char **result) -{ - struct config_resource tmpres; - struct config_resource *res; - int error; - - res = &tmpres; - if ((error = resource_find(name, unit, resname, &res)) != 0) - return (error); - if (res->type != RES_STRING) - return (EFTYPE); - *result = res->u.stringval; - return (0); -} - -int -resource_query_string(int i, const char *resname, const char *value) -{ - if (i < 0) - i = 0; - else - i = i + 1; - for (; i < devtab_count; i++) - if (resource_match_string(i, resname, value) >= 0) - return (i); - return (-1); -} - -int -resource_locate(int i, const char *resname) -{ - if (i < 0) - i = 0; - else - i = i + 1; - for (; i < devtab_count; i++) - if (!strcmp(devtab[i].name, resname)) - return (i); - return (-1); -} - -int -resource_count(void) -{ - return (devtab_count); -} - -char * -resource_query_name(int i) -{ - return (devtab[i].name); -} - -int -resource_query_unit(int i) -{ - return (devtab[i].unit); -} - -static int -resource_create(const char *name, int unit, const char *resname, - resource_type type, struct config_resource **result) -{ - int i, j; - struct config_resource *res = NULL; - - for (i = 0; i < devtab_count; i++) { - if (!strcmp(devtab[i].name, name) && devtab[i].unit == unit) { - res = devtab[i].resources; - break; - } - } - if (res == NULL) { - i = resource_new_name(name, unit); - if (i < 0) - return (ENOMEM); - res = devtab[i].resources; - } - for (j = 0; j < devtab[i].resource_count; j++, res++) { - if (!strcmp(res->name, resname)) { - *result = res; - return (0); - } - } - j = resource_new_resname(i, resname, type); - if (j < 0) - return (ENOMEM); - res = &devtab[i].resources[j]; - *result = res; - return (0); -} - -int -resource_set_int(const char *name, int unit, const char *resname, int value) -{ - int error; - struct config_resource *res; - - error = resource_create(name, unit, resname, RES_INT, &res); - if (error) - return (error); - if (res->type != RES_INT) - return (EFTYPE); - res->u.intval = value; - return (0); -} - -int -resource_set_long(const char *name, int unit, const char *resname, long value) -{ - int error; - struct config_resource *res; - - error = resource_create(name, unit, resname, RES_LONG, &res); - if (error) - return (error); - if (res->type != RES_LONG) - return (EFTYPE); - res->u.longval = value; - return (0); -} - -int -resource_set_string(const char *name, int unit, const char *resname, - const char *value) -{ - int error; - struct config_resource *res; - - error = resource_create(name, unit, resname, RES_STRING, &res); - if (error) - return (error); - if (res->type != RES_STRING) - return (EFTYPE); - if (res->u.stringval) - free(res->u.stringval, M_TEMP); - res->u.stringval = malloc(strlen(value) + 1, M_TEMP, M_NOWAIT); - if (res->u.stringval == NULL) - return (ENOMEM); - strcpy(res->u.stringval, value); - return (0); -} - -/* - * We use the identify routine to get the hints for all the other devices. - * Strings that are all digits or begin with 0x are integers. - * - * hint.aha.0.bus_speedup=1 - * hint.aha.1.irq=10 - * hint.wl.0.netid=PLUG - * hint.wl.1.netid=XYZZY - */ -static void -hint_load(char *cp) -{ - char *ep, *op, *walker; - int len; - int val; - char name[20]; - int unit; - char resname[255]; - - for (ep = cp; *ep != '=' && *ep != '\0'; ep++) - continue; - len = ep - cp; - if (*ep == '=') - ep++; - walker = cp; - walker += 5; - op = walker; - while (*walker && *walker != '.') - walker++; - if (*walker != '.') - return; - if (walker - op > sizeof(name)) - return; - strncpy(name, op, walker - op); - name[walker - op] = '\0'; - walker++; - op = walker; - while (*walker && *walker != '.') - walker++; - if (*walker != '.') - return; - unit = strtoul(op, &walker, 0); - if (*walker != '.') - return; - walker++; - op = walker; - while (*walker && *walker != '=') - walker++; - if (*walker != '=') - return; - if (walker - op > sizeof(resname)) - return; - strncpy(resname, op, walker - op); - resname[walker - op] = '\0'; - walker++; - if (walker != ep) - return; - if (bootverbose) - printf("Setting %s %d %s to ", name, unit, resname); - val = strtoul(ep, &op, 0); - if (*ep != '\0' && *op == '\0') { - resource_set_int(name, unit, resname, val); - if (bootverbose) - printf("%d (int)\n", val); - } else { - resource_set_string(name, unit, resname, ep); - if (bootverbose) - printf("%s (string)\n", ep); - } -} - - -static void -hints_load(void *dummy __unused) -{ - char *cp; - - if (hintmode == 2) { /* default hints only */ - cp = kern_envp; - while (cp) { - if (strncmp(cp, "hint.", 5) == 0) { - /* ok, we found a hint, ignore these defaults */ - hintmode = 0; - break; - } - while (*cp != '\0') - cp++; - cp++; - if (*cp == '\0') - break; - } - } - if (hintmode != 0) { - cp = static_hints; - while (cp) { - if (strncmp(cp, "hint.", 5) == 0) - hint_load(cp); - while (*cp != '\0') - cp++; - cp++; - if (*cp == '\0') - break; - } - } - cp = kern_envp; - while (cp) { - if (strncmp(cp, "hint.", 5) == 0) - hint_load(cp); - while (*cp != '\0') - cp++; - cp++; - if (*cp == '\0') - break; - } - hints_loaded++; -} -SYSINIT(cfghints, SI_SUB_KMEM, SI_ORDER_ANY + 60, hints_load, 0) - -/*======================================*/ -/* * Some useful method implementations to make life easier for bus drivers. */ - void +void resource_list_init(struct resource_list *rl) { SLIST_INIT(rl); } void resource_list_free(struct resource_list *rl) { struct resource_list_entry *rle; while ((rle = SLIST_FIRST(rl)) != NULL) { if (rle->res) panic("resource_list_free: resource entry is busy"); SLIST_REMOVE_HEAD(rl, link); free(rle, M_BUS); } } void resource_list_add(struct resource_list *rl, int type, int rid, u_long start, u_long end, u_long count) { struct resource_list_entry *rle; rle = resource_list_find(rl, type, rid); if (!rle) { rle = malloc(sizeof(struct resource_list_entry), M_BUS, M_NOWAIT); if (!rle) panic("resource_list_add: can't record entry"); SLIST_INSERT_HEAD(rl, rle, link); rle->type = type; rle->rid = rid; rle->res = NULL; } if (rle->res) panic("resource_list_add: resource entry is busy"); rle->start = start; rle->end = end; rle->count = count; } struct resource_list_entry * resource_list_find(struct resource_list *rl, int type, int rid) { struct resource_list_entry *rle; SLIST_FOREACH(rle, rl, link) { if (rle->type == type && rle->rid == rid) return (rle); } return (NULL); } void resource_list_delete(struct resource_list *rl, int type, int rid) { struct resource_list_entry *rle = resource_list_find(rl, type, rid); if (rle) { if (rle->res != NULL) panic("resource_list_delete: resource has not been released"); SLIST_REMOVE(rl, rle, resource_list_entry, link); free(rle, M_BUS); } } struct resource * resource_list_alloc(struct resource_list *rl, device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct resource_list_entry *rle = 0; int passthrough = (device_get_parent(child) != bus); int isdefault = (start == 0UL && end == ~0UL); if (passthrough) { return (BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type, rid, start, end, count, flags)); } rle = resource_list_find(rl, type, *rid); if (!rle) return (NULL); /* no resource of that type/rid */ if (rle->res) panic("resource_list_alloc: resource entry is busy"); if (isdefault) { start = rle->start; count = max(count, rle->count); end = max(rle->end, start + count - 1); } rle->res = BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type, rid, start, end, count, flags); /* * Record the new range. */ if (rle->res) { rle->start = rman_get_start(rle->res); rle->end = rman_get_end(rle->res); rle->count = count; } return (rle->res); } int resource_list_release(struct resource_list *rl, device_t bus, device_t child, int type, int rid, struct resource *res) { struct resource_list_entry *rle = 0; int passthrough = (device_get_parent(child) != bus); int error; if (passthrough) { return (BUS_RELEASE_RESOURCE(device_get_parent(bus), child, type, rid, res)); } rle = resource_list_find(rl, type, rid); if (!rle) panic("resource_list_release: can't find resource"); if (!rle->res) panic("resource_list_release: resource entry is not busy"); error = BUS_RELEASE_RESOURCE(device_get_parent(bus), child, type, rid, res); if (error) return (error); rle->res = NULL; return (0); } /* * Call DEVICE_IDENTIFY for each driver. */ int bus_generic_probe(device_t dev) { devclass_t dc = dev->devclass; driverlink_t dl; TAILQ_FOREACH(dl, &dc->drivers, link) { DEVICE_IDENTIFY(dl->driver, dev); } return (0); } int bus_generic_attach(device_t dev) { device_t child; TAILQ_FOREACH(child, &dev->children, link) { device_probe_and_attach(child); } return (0); } int bus_generic_detach(device_t dev) { device_t child; int error; if (dev->state != DS_ATTACHED) return (EBUSY); TAILQ_FOREACH(child, &dev->children, link) { if ((error = device_detach(child)) != 0) return (error); } return (0); } int bus_generic_shutdown(device_t dev) { device_t child; TAILQ_FOREACH(child, &dev->children, link) { device_shutdown(child); } return (0); } int bus_generic_suspend(device_t dev) { int error; device_t child, child2; TAILQ_FOREACH(child, &dev->children, link) { error = DEVICE_SUSPEND(child); if (error) { for (child2 = TAILQ_FIRST(&dev->children); child2 && child2 != child; child2 = TAILQ_NEXT(child2, link)) DEVICE_RESUME(child2); return (error); } } return (0); } int bus_generic_resume(device_t dev) { device_t child; TAILQ_FOREACH(child, &dev->children, link) { DEVICE_RESUME(child); /* if resume fails, there's nothing we can usefully do... */ } return (0); } int bus_print_child_header (device_t dev, device_t child) { int retval = 0; if (device_get_desc(child)) { retval += device_printf(child, "<%s>", device_get_desc(child)); } else { retval += printf("%s", device_get_nameunit(child)); } return (retval); } int bus_print_child_footer (device_t dev, device_t child) { return (printf(" on %s\n", device_get_nameunit(dev))); } int bus_generic_print_child(device_t dev, device_t child) { int retval = 0; retval += bus_print_child_header(dev, child); retval += bus_print_child_footer(dev, child); return (retval); } int bus_generic_read_ivar(device_t dev, device_t child, int index, uintptr_t * result) { return (ENOENT); } int bus_generic_write_ivar(device_t dev, device_t child, int index, uintptr_t value) { return (ENOENT); } struct resource_list * bus_generic_get_resource_list (device_t dev, device_t child) { return (NULL); } void bus_generic_driver_added(device_t dev, driver_t *driver) { device_t child; DEVICE_IDENTIFY(driver, dev); TAILQ_FOREACH(child, &dev->children, link) { if (child->state == DS_NOTPRESENT) device_probe_and_attach(child); } } int bus_generic_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_intr_t *intr, void *arg, void **cookiep) { /* Propagate up the bus hierarchy until someone handles it. */ if (dev->parent) return (BUS_SETUP_INTR(dev->parent, child, irq, flags, intr, arg, cookiep)); return (EINVAL); } int bus_generic_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie) { /* Propagate up the bus hierarchy until someone handles it. */ if (dev->parent) return (BUS_TEARDOWN_INTR(dev->parent, child, irq, cookie)); return (EINVAL); } struct resource * bus_generic_alloc_resource(device_t dev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { /* Propagate up the bus hierarchy until someone handles it. */ if (dev->parent) return (BUS_ALLOC_RESOURCE(dev->parent, child, type, rid, start, end, count, flags)); return (NULL); } int bus_generic_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { /* Propagate up the bus hierarchy until someone handles it. */ if (dev->parent) return (BUS_RELEASE_RESOURCE(dev->parent, child, type, rid, r)); return (EINVAL); } int bus_generic_activate_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { /* Propagate up the bus hierarchy until someone handles it. */ if (dev->parent) return (BUS_ACTIVATE_RESOURCE(dev->parent, child, type, rid, r)); return (EINVAL); } int bus_generic_deactivate_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { /* Propagate up the bus hierarchy until someone handles it. */ if (dev->parent) return (BUS_DEACTIVATE_RESOURCE(dev->parent, child, type, rid, r)); return (EINVAL); } int bus_generic_rl_get_resource (device_t dev, device_t child, int type, int rid, u_long *startp, u_long *countp) { struct resource_list * rl = NULL; struct resource_list_entry * rle = NULL; rl = BUS_GET_RESOURCE_LIST(dev, child); if (!rl) return (EINVAL); rle = resource_list_find(rl, type, rid); if (!rle) return (ENOENT); if (startp) *startp = rle->start; if (countp) *countp = rle->count; return (0); } int bus_generic_rl_set_resource (device_t dev, device_t child, int type, int rid, u_long start, u_long count) { struct resource_list * rl = NULL; rl = BUS_GET_RESOURCE_LIST(dev, child); if (!rl) return (EINVAL); resource_list_add(rl, type, rid, start, (start + count - 1), count); return (0); } void bus_generic_rl_delete_resource (device_t dev, device_t child, int type, int rid) { struct resource_list * rl = NULL; rl = BUS_GET_RESOURCE_LIST(dev, child); if (!rl) return; resource_list_delete(rl, type, rid); return; } int bus_generic_rl_release_resource (device_t dev, device_t child, int type, int rid, struct resource *r) { struct resource_list * rl = NULL; rl = BUS_GET_RESOURCE_LIST(dev, child); if (!rl) return (EINVAL); return (resource_list_release(rl, dev, child, type, rid, r)); } struct resource * bus_generic_rl_alloc_resource (device_t dev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct resource_list * rl = NULL; rl = BUS_GET_RESOURCE_LIST(dev, child); if (!rl) return (NULL); return (resource_list_alloc(rl, dev, child, type, rid, start, end, count, flags)); } /* * Some convenience functions to make it easier for drivers to use the * resource-management functions. All these really do is hide the * indirection through the parent's method table, making for slightly * less-wordy code. In the future, it might make sense for this code * to maintain some sort of a list of resources allocated by each device. */ struct resource * bus_alloc_resource(device_t dev, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { if (dev->parent == 0) return (0); return (BUS_ALLOC_RESOURCE(dev->parent, dev, type, rid, start, end, count, flags)); } int bus_activate_resource(device_t dev, int type, int rid, struct resource *r) { if (dev->parent == 0) return (EINVAL); return (BUS_ACTIVATE_RESOURCE(dev->parent, dev, type, rid, r)); } int bus_deactivate_resource(device_t dev, int type, int rid, struct resource *r) { if (dev->parent == 0) return (EINVAL); return (BUS_DEACTIVATE_RESOURCE(dev->parent, dev, type, rid, r)); } int bus_release_resource(device_t dev, int type, int rid, struct resource *r) { if (dev->parent == 0) return (EINVAL); return (BUS_RELEASE_RESOURCE(dev->parent, dev, type, rid, r)); } int bus_setup_intr(device_t dev, struct resource *r, int flags, driver_intr_t handler, void *arg, void **cookiep) { if (dev->parent == 0) return (EINVAL); return (BUS_SETUP_INTR(dev->parent, dev, r, flags, handler, arg, cookiep)); } int bus_teardown_intr(device_t dev, struct resource *r, void *cookie) { if (dev->parent == 0) return (EINVAL); return (BUS_TEARDOWN_INTR(dev->parent, dev, r, cookie)); } int bus_set_resource(device_t dev, int type, int rid, u_long start, u_long count) { return BUS_SET_RESOURCE(device_get_parent(dev), dev, type, rid, start, count); } int bus_get_resource(device_t dev, int type, int rid, u_long *startp, u_long *countp) { return (BUS_GET_RESOURCE(device_get_parent(dev), dev, type, rid, startp, countp)); } u_long bus_get_resource_start(device_t dev, int type, int rid) { u_long start, count; int error; error = BUS_GET_RESOURCE(device_get_parent(dev), dev, type, rid, &start, &count); if (error) return (0); return (start); } u_long bus_get_resource_count(device_t dev, int type, int rid) { u_long start, count; int error; error = BUS_GET_RESOURCE(device_get_parent(dev), dev, type, rid, &start, &count); if (error) return (0); return (count); } void bus_delete_resource(device_t dev, int type, int rid) { BUS_DELETE_RESOURCE(device_get_parent(dev), dev, type, rid); } static int root_print_child(device_t dev, device_t child) { int retval = 0; retval += bus_print_child_header(dev, child); retval += printf("\n"); return (retval); } static int root_setup_intr(device_t dev, device_t child, driver_intr_t *intr, void *arg, void **cookiep) { /* * If an interrupt mapping gets to here something bad has happened. */ panic("root_setup_intr"); } static kobj_method_t root_methods[] = { /* Device interface */ KOBJMETHOD(device_shutdown, bus_generic_shutdown), KOBJMETHOD(device_suspend, bus_generic_suspend), KOBJMETHOD(device_resume, bus_generic_resume), /* Bus interface */ KOBJMETHOD(bus_print_child, root_print_child), KOBJMETHOD(bus_read_ivar, bus_generic_read_ivar), KOBJMETHOD(bus_write_ivar, bus_generic_write_ivar), KOBJMETHOD(bus_setup_intr, root_setup_intr), { 0, 0 } }; static driver_t root_driver = { "root", root_methods, 1, /* no softc */ }; device_t root_bus; devclass_t root_devclass; static int root_bus_module_handler(module_t mod, int what, void* arg) { switch (what) { case MOD_LOAD: TAILQ_INIT(&bus_data_devices); kobj_class_compile((kobj_class_t) &root_driver); root_bus = make_device(NULL, "root", 0); root_bus->desc = "System root bus"; kobj_init((kobj_t) root_bus, (kobj_class_t) &root_driver); root_bus->driver = &root_driver; root_bus->state = DS_ATTACHED; root_devclass = devclass_find_internal("root", FALSE); return (0); case MOD_SHUTDOWN: device_shutdown(root_bus); return (0); } return (0); } static moduledata_t root_bus_mod = { "rootbus", root_bus_module_handler, 0 }; DECLARE_MODULE(rootbus, root_bus_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); void root_bus_configure(void) { device_t dev; PDEBUG((".")); TAILQ_FOREACH(dev, &root_bus->children, link) { device_probe_and_attach(dev); } } int driver_module_handler(module_t mod, int what, void *arg) { int error, i; struct driver_module_data *dmd; devclass_t bus_devclass; dmd = (struct driver_module_data *)arg; bus_devclass = devclass_find_internal(dmd->dmd_busname, TRUE); error = 0; switch (what) { case MOD_LOAD: if (dmd->dmd_chainevh) error = dmd->dmd_chainevh(mod,what,dmd->dmd_chainarg); for (i = 0; !error && i < dmd->dmd_ndrivers; i++) { PDEBUG(("Loading module: driver %s on bus %s", DRIVERNAME(dmd->dmd_drivers[i]), dmd->dmd_busname)); error = devclass_add_driver(bus_devclass, dmd->dmd_drivers[i]); } if (error) break; /* * The drivers loaded in this way are assumed to all * implement the same devclass. */ *dmd->dmd_devclass = devclass_find_internal(dmd->dmd_drivers[0]->name, TRUE); break; case MOD_UNLOAD: for (i = 0; !error && i < dmd->dmd_ndrivers; i++) { PDEBUG(("Unloading module: driver %s from bus %s", DRIVERNAME(dmd->dmd_drivers[i]), dmd->dmd_busname)); error = devclass_delete_driver(bus_devclass, dmd->dmd_drivers[i]); } if (!error && dmd->dmd_chainevh) error = dmd->dmd_chainevh(mod,what,dmd->dmd_chainarg); break; } return (error); } #ifdef BUS_DEBUG /* the _short versions avoid iteration by not calling anything that prints * more than oneliners. I love oneliners. */ static void print_device_short(device_t dev, int indent) { if (!dev) return; indentprintf(("device %d: <%s> %sparent,%schildren,%s%s%s%s,%sivars,%ssoftc,busy=%d\n", dev->unit, dev->desc, (dev->parent? "":"no "), (TAILQ_EMPTY(&dev->children)? "no ":""), (dev->flags&DF_ENABLED? "enabled,":"disabled,"), (dev->flags&DF_FIXEDCLASS? "fixed,":""), (dev->flags&DF_WILDCARD? "wildcard,":""), (dev->flags&DF_DESCMALLOCED? "descmalloced,":""), (dev->ivars? "":"no "), (dev->softc? "":"no "), dev->busy)); } static void print_device(device_t dev, int indent) { if (!dev) return; print_device_short(dev, indent); indentprintf(("Parent:\n")); print_device_short(dev->parent, indent+1); indentprintf(("Driver:\n")); print_driver_short(dev->driver, indent+1); indentprintf(("Devclass:\n")); print_devclass_short(dev->devclass, indent+1); } void print_device_tree_short(device_t dev, int indent) /* print the device and all its children (indented) */ { device_t child; if (!dev) return; print_device_short(dev, indent); TAILQ_FOREACH(child, &dev->children, link) { print_device_tree_short(child, indent+1); } } void print_device_tree(device_t dev, int indent) /* print the device and all its children (indented) */ { device_t child; if (!dev) return; print_device(dev, indent); TAILQ_FOREACH(child, &dev->children, link) { print_device_tree(child, indent+1); } } static void print_driver_short(driver_t *driver, int indent) { if (!driver) return; indentprintf(("driver %s: softc size = %d\n", driver->name, driver->size)); } static void print_driver(driver_t *driver, int indent) { if (!driver) return; print_driver_short(driver, indent); } static void print_driver_list(driver_list_t drivers, int indent) { driverlink_t driver; TAILQ_FOREACH(driver, &drivers, link) { print_driver(driver->driver, indent); } } static void print_devclass_short(devclass_t dc, int indent) { if ( !dc ) return; indentprintf(("devclass %s: max units = %d\n", dc->name, dc->maxunit)); } static void print_devclass(devclass_t dc, int indent) { int i; if ( !dc ) return; print_devclass_short(dc, indent); indentprintf(("Drivers:\n")); print_driver_list(dc->drivers, indent+1); indentprintf(("Devices:\n")); for (i = 0; i < dc->maxunit; i++) if (dc->devices[i]) print_device(dc->devices[i], indent+1); } void print_devclass_list_short(void) { devclass_t dc; printf("Short listing of devclasses, drivers & devices:\n"); TAILQ_FOREACH(dc, &devclasses, link) { print_devclass_short(dc, 0); } } void print_devclass_list(void) { devclass_t dc; printf("Full listing of devclasses, drivers & devices:\n"); TAILQ_FOREACH(dc, &devclasses, link) { print_devclass(dc, 0); } } #endif /* * User-space access to the device tree. * * We implement a small set of nodes: * * hw.bus Single integer read method to obtain the * current generation count. * hw.bus.devices Reads the entire device tree in flat space. * hw.bus.rman Resource manager interface * * We might like to add the ability to scan devclasses and/or drivers to * determine what else is currently loaded/available. */ SYSCTL_NODE(_hw, OID_AUTO, bus, CTLFLAG_RW, NULL, NULL); static int sysctl_bus(SYSCTL_HANDLER_ARGS) { struct u_businfo ubus; ubus.ub_version = BUS_USER_VERSION; ubus.ub_generation = bus_data_generation; return (SYSCTL_OUT(req, &ubus, sizeof(ubus))); } SYSCTL_NODE(_hw_bus, OID_AUTO, info, CTLFLAG_RW, sysctl_bus, "bus-related data"); static int sysctl_devices(SYSCTL_HANDLER_ARGS) { int *name = (int *)arg1; u_int namelen = arg2; int index; struct device *dev; struct u_device udev; /* XXX this is a bit big */ int error; if (namelen != 2) return (EINVAL); if (bus_data_generation_check(name[0])) return (EINVAL); index = name[1]; /* * Scan the list of devices, looking for the requested index. */ TAILQ_FOREACH(dev, &bus_data_devices, devlink) { if (index-- == 0) break; } if (dev == NULL) return (ENOENT); /* * Populate the return array. */ udev.dv_handle = (uintptr_t)dev; udev.dv_parent = (uintptr_t)dev->parent; if (dev->nameunit == NULL) { udev.dv_name[0] = 0; } else { snprintf(udev.dv_name, 32, "%s", dev->nameunit); } if (dev->desc == NULL) { udev.dv_desc[0] = 0; } else { snprintf(udev.dv_desc, 32, "%s", dev->desc); } if ((dev->driver == NULL) || (dev->driver->name == NULL)) { udev.dv_drivername[0] = 0; } else { snprintf(udev.dv_drivername, 32, "%s", dev->driver->name); } error = SYSCTL_OUT(req, &udev, sizeof(udev)); return (error); } SYSCTL_NODE(_hw_bus, OID_AUTO, devices, CTLFLAG_RD, sysctl_devices, "system device tree"); /* * Sysctl interface for scanning the resource lists. * * We take two input parameters; the index into the list of resource * managers, and the resource offset into the list. */ static int sysctl_rman(SYSCTL_HANDLER_ARGS) { int *name = (int *)arg1; u_int namelen = arg2; int rman_idx, res_idx; struct rman *rm; struct resource *res; struct u_rman urm; struct u_resource ures; int error; if (namelen != 3) return (EINVAL); if (bus_data_generation_check(name[0])) return (EINVAL); rman_idx = name[1]; res_idx = name[2]; /* * Find the indexed resource manager */ TAILQ_FOREACH(rm, &rman_head, rm_link) { if (rman_idx-- == 0) break; } if (rm == NULL) return (ENOENT); /* * If the resource index is -1, we want details on the * resource manager. */ if (res_idx == -1) { urm.rm_handle = (uintptr_t)rm; snprintf(urm.rm_descr, RM_TEXTLEN, "%s", rm->rm_descr); urm.rm_descr[RM_TEXTLEN - 1] = '\0'; urm.rm_start = rm->rm_start; urm.rm_size = rm->rm_end - rm->rm_start + 1; urm.rm_type = rm->rm_type; error = SYSCTL_OUT(req, &urm, sizeof(urm)); return (error); } /* * Find the indexed resource and return it. */ TAILQ_FOREACH(res, &rm->rm_list, r_link) { if (res_idx-- == 0) { ures.r_handle = (uintptr_t)res; ures.r_parent = (uintptr_t)res->r_rm; ures.r_device = (uintptr_t)res->r_dev; if (res->r_dev != NULL) { if (device_get_name(res->r_dev) != NULL) { snprintf(ures.r_devname, RM_TEXTLEN, "%s%d", device_get_name(res->r_dev), device_get_unit(res->r_dev)); } else { snprintf(ures.r_devname, RM_TEXTLEN, "nomatch"); } } else { ures.r_devname[0] = 0; } ures.r_start = res->r_start; ures.r_size = res->r_end - res->r_start + 1; ures.r_flags = res->r_flags; error = SYSCTL_OUT(req, &ures, sizeof(ures)); return (error); } } return (ENOENT); } SYSCTL_NODE(_hw_bus, OID_AUTO, rman, CTLFLAG_RD, sysctl_rman, "kernel resource manager"); int bus_data_generation_check(int generation) { if (generation != bus_data_generation) return (1); /* XXX generate optimised lists here? */ return (0); } void bus_data_generation_update(void) { bus_data_generation++; +} + +/*======================================*/ +/* + * Access functions for device resources. + */ + +/* + * Evil wildcarding resource string lookup. + * This walks the supplied env string table and returns a match. + * The start point can be remembered for incremental searches. + */ +static int +res_find(const char *cp, int *line, int *startln, + const char *name, int *unit, const char *resname, const char *value, + const char **ret_name, int *ret_namelen, int *ret_unit, + const char **ret_resname, int *ret_resnamelen, const char **ret_value) +{ + int n = 0, hit; + char r_name[32]; + int r_unit; + char r_resname[32]; + char r_value[128]; + const char *s; + char *p; + + while (cp) { + hit = 1; + (*line)++; + if (strncmp(cp, "hint.", 5) != 0) + hit = 0; + else + n = sscanf(cp, "hint.%32[^.].%d.%32[^=]=%128s", + r_name, &r_unit, r_resname, r_value); + if (hit && n != 4) { + printf("CONFIG: invalid hint '%s'\n", cp); + /* XXX: abuse bogus index() declaration */ + p = index(cp, 'h'); + *p = 'H'; + hit = 0; + } + if (hit && startln && *startln >= 0 && *line < *startln) + hit = 0; + if (hit && name && strcmp(name, r_name) != 0) + hit = 0; + if (hit && unit && *unit != r_unit) + hit = 0; + if (hit && resname && strcmp(resname, r_resname) != 0) + hit = 0; + if (hit && value && strcmp(value, r_value) != 0) + hit = 0; + if (hit) + break; + while (*cp != '\0') + cp++; + cp++; + if (*cp == '\0') { + cp = NULL; + break; + } + } + if (cp == NULL) + return ENOENT; + + s = cp; + /* This is a bit of a hack, but at least is reentrant */ + /* Note that it returns some !unterminated! strings. */ + s = index(s, '.') + 1; /* start of device */ + if (ret_name) + *ret_name = s; + s = index(s, '.') + 1; /* start of unit */ + if (ret_namelen) + *ret_namelen = s - *ret_name - 1; /* device length */ + if (ret_unit) + *ret_unit = r_unit; + s = index(s, '.') + 1; /* start of resname */ + if (ret_resname) + *ret_resname = s; + s = index(s, '=') + 1; /* start of value */ + if (ret_resnamelen) + *ret_resnamelen = s - *ret_resname - 1; /* value len */ + if (ret_value) + *ret_value = s; + if (startln) /* line number for anchor */ + *startln = *line + 1; + return 0; +} + +/* + * Search all the data sources for matches to our query. We look for + * dynamic hints first as overrides for static or fallback hints. + */ +static int +resource_find(int *line, int *startln, + const char *name, int *unit, const char *resname, const char *value, + const char **ret_name, int *ret_namelen, int *ret_unit, + const char **ret_resname, int *ret_resnamelen, const char **ret_value) +{ + int i; + int un; + + *line = 0; + + /* Search for exact unit matches first */ + i = res_find(kern_envp, line, startln, name, unit, resname, value, + ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen, + ret_value); + if (i == 0) + return 0; + i = res_find(static_hints, line, startln, name, unit, resname, value, + ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen, + ret_value); + if (i == 0) + return 0; + if (unit == NULL) + return ENOENT; + /* If we are still here, search for wildcard matches */ + un = -1; + i = res_find(kern_envp, line, startln, name, &un, resname, value, + ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen, + ret_value); + if (i == 0) + return 0; + un = -1; + i = res_find(static_hints, line, startln, name, &un, resname, value, + ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen, + ret_value); + if (i == 0) + return 0; + return ENOENT; +} + +int +resource_int_value(const char *name, int unit, const char *resname, int *result) +{ + int error; + const char *str; + char *op; + unsigned long val; + int line; + + line = 0; + error = resource_find(&line, NULL, name, &unit, resname, NULL, + NULL, NULL, NULL, NULL, NULL, &str); + if (error) + return error; + if (*str == '\0') + return EFTYPE; + val = strtoul(str, &op, 0); + if (*op != '\0') + return EFTYPE; + *result = val; + return 0; +} + +int +resource_long_value(const char *name, int unit, const char *resname, + long *result) +{ + int error; + const char *str; + char *op; + unsigned long val; + int line; + + line = 0; + error = resource_find(&line, NULL, name, &unit, resname, NULL, + NULL, NULL, NULL, NULL, NULL, &str); + if (error) + return error; + if (*str == '\0') + return EFTYPE; + val = strtoul(str, &op, 0); + if (*op != '\0') + return EFTYPE; + *result = val; + return 0; +} + +int +resource_string_value(const char *name, int unit, const char *resname, + const char **result) +{ + int error; + const char *str; + int line; + + line = 0; + error = resource_find(&line, NULL, name, &unit, resname, NULL, + NULL, NULL, NULL, NULL, NULL, &str); + if (error) + return error; + *result = str; + return 0; +} + +/* + * This is a bit nasty, but allows us to not modify the env strings. + */ +static const char * +resource_string_copy(const char *s, int len) +{ + static char stringbuf[256]; + static int offset = 0; + const char *ret; + + if (len == 0) + len = strlen(s); + if (len > 255) + return NULL; + if ((offset + len + 1) > 255) + offset = 0; + bcopy(s, &stringbuf[offset], len); + stringbuf[offset + len] = '\0'; + ret = &stringbuf[offset]; + offset += len + 1; + return ret; +} + +/* + * err = resource_find_at(&anchor, &name, &unit, resname, value) + * Iteratively fetch a list of devices wired "at" something + * res and value are restrictions. eg: "at", "scbus0". + * For practical purposes, res = required, value = optional. + * *name and *unit are set. + * set *anchor to zero before starting. + */ +int +resource_find_match(int *anchor, const char **name, int *unit, + const char *resname, const char *value) +{ + const char *found_name; + int found_namelen; + int found_unit; + int ret; + int newln; + + newln = *anchor; + ret = resource_find(anchor, &newln, NULL, NULL, resname, value, + &found_name, &found_namelen, &found_unit, NULL, NULL, NULL); + if (ret == 0) { + *name = resource_string_copy(found_name, found_namelen); + *unit = found_unit; + } + *anchor = newln; + return ret; +} + + +/* + * err = resource_find_dev(&anchor, name, &unit, res, value); + * Iterate through a list of devices, returning their unit numbers. + * res and value are optional restrictions. eg: "at", "scbus0". + * *unit is set to the value. + * set *anchor to zero before starting. + */ +int +resource_find_dev(int *anchor, const char *name, int *unit, + const char *resname, const char *value) +{ + int found_unit; + int newln; + int ret; + + newln = *anchor; + ret = resource_find(anchor, &newln, name, NULL, resname, value, + NULL, NULL, &found_unit, NULL, NULL, NULL); + if (ret == 0) { + *unit = found_unit; + } + *anchor = newln; + return ret; } Index: head/sys/kern/subr_hints.c =================================================================== --- head/sys/kern/subr_hints.c (nonexistent) +++ head/sys/kern/subr_hints.c (revision 78135) @@ -0,0 +1,304 @@ +/*- + * Copyright (c) 2000,2001 Peter Wemm + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +#include +#include + +/* + * Access functions for device resources. + */ + +extern char static_hints[]; /* by config for now */ + +/* + * Evil wildcarding resource string lookup. + * This walks the supplied env string table and returns a match. + * The start point can be remembered for incremental searches. + */ +static int +res_find(const char *cp, int *line, int *startln, + const char *name, int *unit, const char *resname, const char *value, + const char **ret_name, int *ret_namelen, int *ret_unit, + const char **ret_resname, int *ret_resnamelen, const char **ret_value) +{ + int n = 0, hit; + char r_name[32]; + int r_unit; + char r_resname[32]; + char r_value[128]; + const char *s; + char *p; + + while (cp) { + hit = 1; + (*line)++; + if (strncmp(cp, "hint.", 5) != 0) + hit = 0; + else + n = sscanf(cp, "hint.%32[^.].%d.%32[^=]=%128s", + r_name, &r_unit, r_resname, r_value); + if (hit && n != 4) { + printf("CONFIG: invalid hint '%s'\n", cp); + /* XXX: abuse bogus index() declaration */ + p = index(cp, 'h'); + *p = 'H'; + hit = 0; + } + if (hit && startln && *startln >= 0 && *line < *startln) + hit = 0; + if (hit && name && strcmp(name, r_name) != 0) + hit = 0; + if (hit && unit && *unit != r_unit) + hit = 0; + if (hit && resname && strcmp(resname, r_resname) != 0) + hit = 0; + if (hit && value && strcmp(value, r_value) != 0) + hit = 0; + if (hit) + break; + while (*cp != '\0') + cp++; + cp++; + if (*cp == '\0') { + cp = NULL; + break; + } + } + if (cp == NULL) + return ENOENT; + + s = cp; + /* This is a bit of a hack, but at least is reentrant */ + /* Note that it returns some !unterminated! strings. */ + s = index(s, '.') + 1; /* start of device */ + if (ret_name) + *ret_name = s; + s = index(s, '.') + 1; /* start of unit */ + if (ret_namelen) + *ret_namelen = s - *ret_name - 1; /* device length */ + if (ret_unit) + *ret_unit = r_unit; + s = index(s, '.') + 1; /* start of resname */ + if (ret_resname) + *ret_resname = s; + s = index(s, '=') + 1; /* start of value */ + if (ret_resnamelen) + *ret_resnamelen = s - *ret_resname - 1; /* value len */ + if (ret_value) + *ret_value = s; + if (startln) /* line number for anchor */ + *startln = *line + 1; + return 0; +} + +/* + * Search all the data sources for matches to our query. We look for + * dynamic hints first as overrides for static or fallback hints. + */ +static int +resource_find(int *line, int *startln, + const char *name, int *unit, const char *resname, const char *value, + const char **ret_name, int *ret_namelen, int *ret_unit, + const char **ret_resname, int *ret_resnamelen, const char **ret_value) +{ + int i; + int un; + + *line = 0; + + /* Search for exact unit matches first */ + i = res_find(kern_envp, line, startln, name, unit, resname, value, + ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen, + ret_value); + if (i == 0) + return 0; + i = res_find(static_hints, line, startln, name, unit, resname, value, + ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen, + ret_value); + if (i == 0) + return 0; + if (unit == NULL) + return ENOENT; + /* If we are still here, search for wildcard matches */ + un = -1; + i = res_find(kern_envp, line, startln, name, &un, resname, value, + ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen, + ret_value); + if (i == 0) + return 0; + un = -1; + i = res_find(static_hints, line, startln, name, &un, resname, value, + ret_name, ret_namelen, ret_unit, ret_resname, ret_resnamelen, + ret_value); + if (i == 0) + return 0; + return ENOENT; +} + +int +resource_int_value(const char *name, int unit, const char *resname, int *result) +{ + int error; + const char *str; + char *op; + unsigned long val; + int line; + + line = 0; + error = resource_find(&line, NULL, name, &unit, resname, NULL, + NULL, NULL, NULL, NULL, NULL, &str); + if (error) + return error; + if (*str == '\0') + return EFTYPE; + val = strtoul(str, &op, 0); + if (*op != '\0') + return EFTYPE; + *result = val; + return 0; +} + +int +resource_long_value(const char *name, int unit, const char *resname, + long *result) +{ + int error; + const char *str; + char *op; + unsigned long val; + int line; + + line = 0; + error = resource_find(&line, NULL, name, &unit, resname, NULL, + NULL, NULL, NULL, NULL, NULL, &str); + if (error) + return error; + if (*str == '\0') + return EFTYPE; + val = strtoul(str, &op, 0); + if (*op != '\0') + return EFTYPE; + *result = val; + return 0; +} + +int +resource_string_value(const char *name, int unit, const char *resname, + const char **result) +{ + int error; + const char *str; + int line; + + line = 0; + error = resource_find(&line, NULL, name, &unit, resname, NULL, + NULL, NULL, NULL, NULL, NULL, &str); + if (error) + return error; + *result = str; + return 0; +} + +/* + * This is a bit nasty, but allows us to not modify the env strings. + */ +static const char * +resource_string_copy(const char *s, int len) +{ + static char stringbuf[256]; + static int offset = 0; + const char *ret; + + if (len == 0) + len = strlen(s); + if (len > 255) + return NULL; + if ((offset + len + 1) > 255) + offset = 0; + bcopy(s, &stringbuf[offset], len); + stringbuf[offset + len] = '\0'; + ret = &stringbuf[offset]; + offset += len + 1; + return ret; +} + +/* + * err = resource_find_at(&anchor, &name, &unit, resname, value) + * Iteratively fetch a list of devices wired "at" something + * res and value are restrictions. eg: "at", "scbus0". + * For practical purposes, res = required, value = optional. + * *name and *unit are set. + * set *anchor to zero before starting. + */ +int +resource_find_match(int *anchor, const char **name, int *unit, + const char *resname, const char *value) +{ + const char *found_name; + int found_namelen; + int found_unit; + int ret; + int newln; + + newln = *anchor; + ret = resource_find(anchor, &newln, NULL, NULL, resname, value, + &found_name, &found_namelen, &found_unit, NULL, NULL, NULL); + if (ret == 0) { + *name = resource_string_copy(found_name, found_namelen); + *unit = found_unit; + } + *anchor = newln; + return ret; +} + + +/* + * err = resource_find_dev(&anchor, name, &unit, res, value); + * Iterate through a list of devices, returning their unit numbers. + * res and value are optional restrictions. eg: "at", "scbus0". + * *unit is set to the value. + * set *anchor to zero before starting. + */ +int +resource_find_dev(int *anchor, const char *name, int *unit, + const char *resname, const char *value) +{ + int found_unit; + int newln; + int ret; + + newln = *anchor; + ret = resource_find(anchor, &newln, name, NULL, resname, value, + NULL, NULL, &found_unit, NULL, NULL, NULL); + if (ret == 0) { + *unit = found_unit; + } + *anchor = newln; + return ret; +} Property changes on: head/sys/kern/subr_hints.c ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/sys/pc98/cbus/fdc.c =================================================================== --- head/sys/pc98/cbus/fdc.c (revision 78134) +++ head/sys/pc98/cbus/fdc.c (revision 78135) @@ -1,2909 +1,2908 @@ /* * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Don Ahn. * * Libretto PCMCIA floppy support by David Horwitt (dhorwitt@ucsd.edu) * aided by the Linux floppy driver modifications from David Bateman * (dbateman@eng.uts.edu.au). * * Copyright (c) 1993, 1994 by * jc@irbs.UUCP (John Capo) * vak@zebub.msk.su (Serge Vakulenko) * ache@astral.msk.su (Andrew A. Chernov) * * Copyright (c) 1993, 1994, 1995 by * joerg_wunsch@uriah.sax.de (Joerg Wunsch) * dufault@hda.com (Peter Dufault) * * Copyright (c) 2001 Joerg Wunsch, * joerg_wunsch@uriah.sax.de (Joerg Wunsch) * * 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 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. * * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 * $FreeBSD$ * */ #include "opt_fdc.h" #include "card.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PC98 #include #include #include #include #include #else #include #include #include #include #endif /* misuse a flag to identify format operation */ /* configuration flags */ #define FDC_PRETEND_D0 (1 << 0) /* pretend drive 0 to be there */ #define FDC_NO_FIFO (1 << 2) /* do not enable FIFO */ /* internally used only, not really from CMOS: */ #define RTCFDT_144M_PRETENDED 0x1000 /* error returns for fd_cmd() */ #define FD_FAILED -1 #define FD_NOT_VALID -2 #define FDC_ERRMAX 100 /* do not log more */ #ifdef PC98 #define NUMTYPES 12 #define NUMDENS NUMTYPES #else #define NUMTYPES 17 #define NUMDENS (NUMTYPES - 7) #endif /* These defines (-1) must match index for fd_types */ #define F_TAPE_TYPE 0x020 /* bit for fd_types to indicate tape */ #define NO_TYPE 0 /* must match NO_TYPE in ft.c */ #define FD_1720 1 #define FD_1480 2 #define FD_1440 3 #define FD_1200 4 #define FD_820 5 #define FD_800 6 #define FD_720 7 #define FD_360 8 #define FD_640 9 #define FD_1232 10 #ifdef PC98 #define FD_1280 11 #define FD_1476 12 #define FDT_NONE 0 /* none present */ #define FDT_12M 1 /* 1M/640K FDD */ #define FDT_144M 2 /* 1.44M/1M/640K FDD */ #else #define FD_1480in5_25 11 #define FD_1440in5_25 12 #define FD_820in5_25 13 #define FD_800in5_25 14 #define FD_720in5_25 15 #define FD_360in5_25 16 #define FD_640in5_25 17 #endif static struct fd_type fd_types[NUMTYPES] = { #ifdef PC98 { 21,2,0xFF,0x04,82,3444,1,2,2,0x0C,2 }, /* 1.72M in 3mode */ { 18,2,0xFF,0x1B,82,2952,1,2,2,0x54,1 }, /* 1.48M in 3mode */ { 18,2,0xFF,0x1B,80,2880,1,2,2,0x54,1 }, /* 1.44M in 3mode */ { 15,2,0xFF,0x1B,80,2400,1,0,2,0x54,1 }, /* 1.2M */ { 10,2,0xFF,0x10,82,1640,1,1,2,0x30,1 }, /* 820K */ { 10,2,0xFF,0x10,80,1600,1,1,2,0x30,1 }, /* 800K */ { 9,2,0xFF,0x20,80,1440,1,1,2,0x50,1 }, /* 720K */ { 9,2,0xFF,0x20,40, 720,2,1,2,0x50,1 }, /* 360K */ { 8,2,0xFF,0x2A,80,1280,1,1,2,0x50,1 }, /* 640K */ { 8,3,0xFF,0x35,77,1232,1,0,2,0x74,1 }, /* 1.23M 1024/sec */ { 8,3,0xFF,0x35,80,1280,1,0,2,0x74,1 }, /* 1.28M 1024/sec */ { 9,3,0xFF,0x35,82,1476,1,0,2,0x47,1 }, /* 1.48M 1024/sec 9sec */ #if 0 { 10,3,0xFF,0x1B,82,1640,1,2,2,0x54,1 }, /* 1.64M in 3mode - Reserve */ #endif #else { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */ { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */ { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */ { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */ { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /* 820K in HD 3.5in */ { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /* 800K in HD 3.5in */ { 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /* 720K in HD 3.5in */ { 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /* 360K in DD 5.25in */ { 8,2,0xFF,0x2A,80,1280,1,FDC_250KBPS,2,0x50,1 }, /* 640K in DD 5.25in */ { 8,3,0xFF,0x35,77,1232,1,FDC_500KBPS,2,0x74,1 }, /* 1.23M in HD 5.25in */ { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */ { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */ { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /* 820K in HD 5.25in */ { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /* 800K in HD 5.25in */ { 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /* 720K in HD 5.25in */ { 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /* 360K in HD 5.25in */ { 8,2,0xFF,0x2A,80,1280,1,FDC_300KBPS,2,0x50,1 }, /* 640K in HD 5.25in */ #endif }; #ifdef PC98 #define DRVS_PER_CTLR 4 /* 4 floppies */ #else #define DRVS_PER_CTLR 2 /* 2 floppies */ #endif /***********************************************************************\ * Per controller structure. * \***********************************************************************/ static devclass_t fdc_devclass; /***********************************************************************\ * Per drive structure. * * N per controller (DRVS_PER_CTLR) * \***********************************************************************/ struct fd_data { struct fdc_data *fdc; /* pointer to controller structure */ int fdsu; /* this units number on this controller */ int type; /* Drive type (FD_1440...) */ struct fd_type *ft; /* pointer to the type descriptor */ int flags; #define FD_OPEN 0x01 /* it's open */ #define FD_ACTIVE 0x02 /* it's active */ #define FD_MOTOR 0x04 /* motor should be on */ #define FD_MOTOR_WAIT 0x08 /* motor coming up */ int skip; int hddrv; #define FD_NO_TRACK -2 int track; /* where we think the head is */ int options; /* user configurable options, see ioctl_fd.h */ struct callout_handle toffhandle; struct callout_handle tohandle; struct devstat device_stats; #ifdef PC98 int pc98_trans; #endif device_t dev; fdu_t fdu; }; struct fdc_ivars { int fdunit; }; static devclass_t fd_devclass; #ifdef EPSON_NRDISK typedef unsigned int nrd_t; #define P_NRD_ADDRH 0xc24 #define P_NRD_ADDRM 0xc22 #define P_NRD_ADDRL 0xc20 #define P_NRD_CHECK 0xc20 #define P_NRD_DATA 0xc26 #define P_NRD_LED 0xc36 #define B_NRD_CHK 0x80 #define B_NRD_LED 0x40 #define A_NRD_INFO 0x2 #define A_NRD_BASE 0x400 #define NRD_STATUS 0x0 #define NRD_ST0_HD 0x04 static fdu_t nrdu=-1; static int nrdsec=0; static nrd_t nrdblkn=0; static nrd_t nrdaddr=0x0; #define nrd_check_ready() ({ \ (epson_inb(P_NRD_CHECK) & B_NRD_CHK) ? 0 : 1; \ }) #define nrd_LED_on() epson_outb(P_NRD_LED, B_NRD_LED) #define nrd_LED_off() epson_outb(P_NRD_LED, ~B_NRD_LED) #define nrd_trac() ((int)(nrd_info(nrdaddr) & 0xff)) #define nrd_head() ((int)((nrd_info(nrdaddr) >> 8) & 0xff)) #define nrd_sec() ((int)(nrd_info(nrdaddr + 2) & 0xff)) #define nrd_secsize() ((int)((nrd_info(A_NRD_INFO) >> 8) & 0xff)) #define nrd_addrset(p) nrd_addr((nrd_t)((nrd_t)p+A_NRD_BASE)) static inline void nrd_addr(addr) nrd_t addr; { epson_outb(P_NRD_ADDRH, (u_char)((addr >> 16) & 0x1f)); epson_outb(P_NRD_ADDRM, (u_char)((addr >> 8) & 0xff)); epson_outb(P_NRD_ADDRL, (u_char)(addr & 0xff)); } static inline u_short nrd_info(addr) nrd_t addr; { u_short tmp; nrd_addr(addr); outb(0x43f, 0x42); tmp = (short)inw(P_NRD_DATA); outb(0x43f, 0x40); return ((u_short)tmp); } #endif /* EPSON_NRDISK */ /***********************************************************************\ * Throughout this file the following conventions will be used: * * fd is a pointer to the fd_data struct for the drive in question * * fdc is a pointer to the fdc_data struct for the controller * * fdu is the floppy drive unit number * * fdcu is the floppy controller unit number * * fdsu is the floppy drive unit number on that controller. (sub-unit) * \***********************************************************************/ /* needed for ft driver, thus exported */ int in_fdc(struct fdc_data *); int out_fdc(struct fdc_data *, int); /* internal functions */ static void fdc_intr(void *); static void set_motor(struct fdc_data *, int, int); # define TURNON 1 # define TURNOFF 0 static timeout_t fd_turnoff; static timeout_t fd_motor_on; static void fd_turnon(struct fd_data *); static void fdc_reset(fdc_p); static int fd_in(struct fdc_data *, int *); static void fdstart(struct fdc_data *); static timeout_t fd_iotimeout; static timeout_t fd_pseudointr; static int fdstate(struct fdc_data *); static int retrier(struct fdc_data *); static int fdformat(dev_t, struct fd_formb *, struct proc *); static int enable_fifo(fdc_p fdc); static int fifo_threshold = 8; /* XXX: should be accessible via sysctl */ #define DEVIDLE 0 #define FINDWORK 1 #define DOSEEK 2 #define SEEKCOMPLETE 3 #define IOCOMPLETE 4 #define RECALCOMPLETE 5 #define STARTRECAL 6 #define RESETCTLR 7 #define SEEKWAIT 8 #define RECALWAIT 9 #define MOTORWAIT 10 #define IOTIMEDOUT 11 #define RESETCOMPLETE 12 #define PIOREAD 13 #ifdef FDC_DEBUG static char const * const fdstates[] = { "DEVIDLE", "FINDWORK", "DOSEEK", "SEEKCOMPLETE", "IOCOMPLETE", "RECALCOMPLETE", "STARTRECAL", "RESETCTLR", "SEEKWAIT", "RECALWAIT", "MOTORWAIT", "IOTIMEDOUT", "RESETCOMPLETE", "PIOREAD", }; /* CAUTION: fd_debug causes huge amounts of logging output */ static int volatile fd_debug = 0; #define TRACE0(arg) if(fd_debug) printf(arg) #define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2) #else /* FDC_DEBUG */ #define TRACE0(arg) #define TRACE1(arg1, arg2) #endif /* FDC_DEBUG */ static void fdout_wr(fdc_p fdc, u_int8_t v) { bus_space_write_1(fdc->portt, fdc->porth, FDOUT+fdc->port_off, v); } static u_int8_t fdsts_rd(fdc_p fdc) { return bus_space_read_1(fdc->portt, fdc->porth, FDSTS+fdc->port_off); } static void fddata_wr(fdc_p fdc, u_int8_t v) { bus_space_write_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off, v); } static u_int8_t fddata_rd(fdc_p fdc) { return bus_space_read_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off); } #ifndef PC98 static void fdctl_wr_isa(fdc_p fdc, u_int8_t v) { bus_space_write_1(fdc->ctlt, fdc->ctlh, 0, v); } #if NCARD > 0 static void fdctl_wr_pcmcia(fdc_p fdc, u_int8_t v) { bus_space_write_1(fdc->portt, fdc->porth, FDCTL+fdc->port_off, v); } #endif #endif /* PC98 */ #if 0 static u_int8_t fdin_rd(fdc_p fdc) { return bus_space_read_1(fdc->portt, fdc->porth, FDIN); } #endif static d_open_t Fdopen; /* NOTE, not fdopen */ static d_close_t fdclose; static d_ioctl_t fdioctl; static d_strategy_t fdstrategy; #define CDEV_MAJOR 9 static struct cdevsw fd_cdevsw = { /* open */ Fdopen, /* close */ fdclose, /* read */ physread, /* write */ physwrite, /* ioctl */ fdioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ fdstrategy, /* name */ "fd", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ D_DISK, }; static int fdc_err(struct fdc_data *fdc, const char *s) { fdc->fdc_errs++; if (s) { if (fdc->fdc_errs < FDC_ERRMAX) device_printf(fdc->fdc_dev, "%s", s); else if (fdc->fdc_errs == FDC_ERRMAX) device_printf(fdc->fdc_dev, "too many errors, not " "logging any more\n"); } return FD_FAILED; } /* * fd_cmd: Send a command to the chip. Takes a varargs with this structure: * Unit number, * # of output bytes, output bytes as ints ..., * # of input bytes, input bytes as ints ... */ static int fd_cmd(struct fdc_data *fdc, int n_out, ...) { u_char cmd; int n_in; int n; va_list ap; va_start(ap, n_out); cmd = (u_char)(va_arg(ap, int)); va_end(ap); va_start(ap, n_out); for (n = 0; n < n_out; n++) { if (out_fdc(fdc, va_arg(ap, int)) < 0) { char msg[50]; snprintf(msg, sizeof(msg), "cmd %x failed at out byte %d of %d\n", cmd, n + 1, n_out); return fdc_err(fdc, msg); } } n_in = va_arg(ap, int); for (n = 0; n < n_in; n++) { int *ptr = va_arg(ap, int *); if (fd_in(fdc, ptr) < 0) { char msg[50]; snprintf(msg, sizeof(msg), "cmd %02x failed at in byte %d of %d\n", cmd, n + 1, n_in); return fdc_err(fdc, msg); } } return 0; } static int enable_fifo(fdc_p fdc) { int i, j; if ((fdc->flags & FDC_HAS_FIFO) == 0) { /* * XXX: * Cannot use fd_cmd the normal way here, since * this might be an invalid command. Thus we send the * first byte, and check for an early turn of data directon. */ if (out_fdc(fdc, I8207X_CONFIGURE) < 0) return fdc_err(fdc, "Enable FIFO failed\n"); /* If command is invalid, return */ j = 100000; while ((i = fdsts_rd(fdc) & (NE7_DIO | NE7_RQM)) != NE7_RQM && j-- > 0) if (i == (NE7_DIO | NE7_RQM)) { fdc_reset(fdc); return FD_FAILED; } if (j<0 || fd_cmd(fdc, 3, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) { fdc_reset(fdc); return fdc_err(fdc, "Enable FIFO failed\n"); } fdc->flags |= FDC_HAS_FIFO; return 0; } if (fd_cmd(fdc, 4, I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) return fdc_err(fdc, "Re-enable FIFO failed\n"); return 0; } static int fd_sense_drive_status(fdc_p fdc, int *st3p) { int st3; if (fd_cmd(fdc, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3)) { return fdc_err(fdc, "Sense Drive Status failed\n"); } if (st3p) *st3p = st3; return 0; } static int fd_sense_int(fdc_p fdc, int *st0p, int *cylp) { int cyl, st0, ret; #ifdef EPSON_NRDISK if (fdc->fdu == nrdu) { if (fdc->fd->track >= 0) nrdaddr = (fdc->fd->track + 1) * 8; else nrdaddr = 0x0; *st0p = nrd_head() ? NRD_ST0_HD : NRD_STATUS; *cylp = nrd_trac(); } else { #endif /* EPSON_NRDISK */ ret = fd_cmd(fdc, 1, NE7CMD_SENSEI, 1, &st0); if (ret) { (void)fdc_err(fdc, "sense intr err reading stat reg 0\n"); return ret; } if (st0p) *st0p = st0; if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) { /* * There doesn't seem to have been an interrupt. */ return FD_NOT_VALID; } if (fd_in(fdc, &cyl) < 0) { return fdc_err(fdc, "can't get cyl num\n"); } if (cylp) *cylp = cyl; #ifdef EPSON_NRDISK } #endif /* EPSON_NRDISK */ return 0; } static int fd_read_status(fdc_p fdc, int fdsu) { int i, ret; for (i = 0; i < 7; i++) { /* * XXX types are poorly chosen. Only bytes can by read * from the hardware, but fdc->status[] wants u_ints and * fd_in() gives ints. */ int status; #ifdef EPSON_NRDISK if (fdc->fdu == nrdu) { switch (i) { case 0: fdc->status[i] = nrd_head() ? NRD_ST0_HD : NRD_STATUS; break; case 1: fdc->status[i] = NRD_STATUS; break; case 2: fdc->status[i] = NRD_STATUS; break; case 3: fdc->status[i] = nrd_trac(); break; case 4: fdc->status[i] = nrd_head(); break; case 5: fdc->status[i] = nrdsec; break; case 6: fdc->status[i] = nrd_secsize(); break; } ret = 0; } else { #endif /* EPSON_NRDISK */ ret = fd_in(fdc, &status); fdc->status[i] = status; if (ret != 0) break; #ifdef EPSON_NRDISK } #endif /* EPSON_NRDISK */ } if (ret == 0) fdc->flags |= FDC_STAT_VALID; else fdc->flags &= ~FDC_STAT_VALID; return ret; } /****************************************************************************/ /* autoconfiguration stuff */ /****************************************************************************/ #ifdef PC98 static int pc98_trans = 0; /* 0 : HD , 1 : DD , 2 : 1.44 */ static int pc98_trans_prev = 0; static void set_density(fdc_p fdc) { /* always motor on */ outb(IO_FDPORT, (pc98_trans != 1 ? FDP_FDDEXC : 0) | FDP_PORTEXC); DELAY(100); fdout_wr(fdc, FDO_RST | FDO_DMAE); /* in the case of note W, always inhibit 100ms timer */ } static int pc98_fd_check_ready(fdu_t fdu) { fd_p fd = devclass_get_softc(fd_devclass, fdu); struct fdc_data *fdc = fd->fdc; int retry = 0; #ifdef EPSON_NRDISK if (fdu == nrdu) { if (nrd_check_ready()) return 0; else return -1; } #endif while (retry++ < 30000) { set_motor(fdc, fd->fdsu, TURNON); out_fdc(fdc, NE7CMD_SENSED); /* Sense Drive Status */ DELAY(100); out_fdc(fdc, fdu); /* Drive number */ DELAY(100); if ((in_fdc(fdc) & NE7_ST3_RD)){ fdout_wr(fdc, FDO_DMAE | FDO_MTON); DELAY(10); return 0; } } return -1; } #endif static int fdc_alloc_resources(struct fdc_data *fdc) { device_t dev; int ispnp, ispcmcia; dev = fdc->fdc_dev; ispnp = (fdc->flags & FDC_ISPNP) != 0; ispcmcia = (fdc->flags & FDC_ISPCMCIA) != 0; fdc->rid_ioport = fdc->rid_irq = fdc->rid_drq = 0; fdc->res_ioport = fdc->res_irq = fdc->res_drq = 0; #ifdef PC98 fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, &fdc->rid_ioport, 0ul, ~0ul, ispnp ? 1 : IO_FDCSIZE, RF_ACTIVE); #else /* * On standard ISA, we don't just use an 8 port range * (e.g. 0x3f0-0x3f7) since that covers an IDE control * register at 0x3f6. * * Isn't PC hardware wonderful. * * The Y-E Data PCMCIA FDC doesn't have this problem, it * uses the register with offset 6 for pseudo-DMA, and the * one with offset 7 as control register. */ fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, &fdc->rid_ioport, 0ul, ~0ul, ispcmcia ? 8 : (ispnp ? 1 : 6), RF_ACTIVE); #endif if (fdc->res_ioport == 0) { device_printf(dev, "cannot reserve I/O port range\n"); return ENXIO; } fdc->portt = rman_get_bustag(fdc->res_ioport); fdc->porth = rman_get_bushandle(fdc->res_ioport); #ifndef PC98 if (!ispcmcia) { /* * Some BIOSen report the device at 0x3f2-0x3f5,0x3f7 * and some at 0x3f0-0x3f5,0x3f7. We detect the former * by checking the size and adjust the port address * accordingly. */ if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 4) fdc->port_off = -2; /* * Register the control port range as rid 1 if it * isn't there already. Most PnP BIOSen will have * already done this but non-PnP configurations don't. * * And some (!!) report 0x3f2-0x3f5 and completely * leave out the control register! It seems that some * non-antique controller chips have a different * method of programming the transfer speed which * doesn't require the control register, but it's * mighty bogus as the chip still responds to the * address for the control register. */ if (bus_get_resource_count(dev, SYS_RES_IOPORT, 1) == 0) { u_long ctlstart; /* Find the control port, usually 0x3f7 */ ctlstart = rman_get_start(fdc->res_ioport) + fdc->port_off + 7; bus_set_resource(dev, SYS_RES_IOPORT, 1, ctlstart, 1); } /* * Now (finally!) allocate the control port. */ fdc->rid_ctl = 1; fdc->res_ctl = bus_alloc_resource(dev, SYS_RES_IOPORT, &fdc->rid_ctl, 0ul, ~0ul, 1, RF_ACTIVE); if (fdc->res_ctl == 0) { device_printf(dev, "cannot reserve control I/O port range\n"); return ENXIO; } fdc->ctlt = rman_get_bustag(fdc->res_ctl); fdc->ctlh = rman_get_bushandle(fdc->res_ctl); } #endif fdc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &fdc->rid_irq, 0ul, ~0ul, 1, RF_ACTIVE); if (fdc->res_irq == 0) { device_printf(dev, "cannot reserve interrupt line\n"); return ENXIO; } if ((fdc->flags & FDC_NODMA) == 0) { fdc->res_drq = bus_alloc_resource(dev, SYS_RES_DRQ, &fdc->rid_drq, 0ul, ~0ul, 1, RF_ACTIVE); if (fdc->res_drq == 0) { device_printf(dev, "cannot reserve DMA request line\n"); return ENXIO; } fdc->dmachan = fdc->res_drq->r_start; } return 0; } static void fdc_release_resources(struct fdc_data *fdc) { device_t dev; dev = fdc->fdc_dev; if (fdc->res_irq != 0) { bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq, fdc->res_irq); bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq, fdc->res_irq); } #ifndef PC98 if (fdc->res_ctl != 0) { bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, fdc->res_ctl); bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, fdc->res_ctl); } #endif if (fdc->res_ioport != 0) { bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, fdc->res_ioport); bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, fdc->res_ioport); } if (fdc->res_drq != 0) { bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq, fdc->res_drq); bus_release_resource(dev, SYS_RES_DRQ, fdc->rid_drq, fdc->res_drq); } } /****************************************************************************/ /* autoconfiguration stuff */ /****************************************************************************/ static struct isa_pnp_id fdc_ids[] = { {0x0007d041, "PC standard floppy disk controller"}, /* PNP0700 */ {0x0107d041, "Standard floppy controller supporting MS Device Bay Spec"}, /* PNP0701 */ {0} }; static int fdc_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { struct fdc_ivars *ivars = device_get_ivars(child); switch (which) { case FDC_IVAR_FDUNIT: *result = ivars->fdunit; break; default: return ENOENT; } return 0; } /* * fdc controller section. */ static int fdc_probe(device_t dev) { #ifdef PC98 int error; #else int error, ic_type; #endif struct fdc_data *fdc; fdc = device_get_softc(dev); bzero(fdc, sizeof *fdc); fdc->fdc_dev = dev; #ifndef PC98 fdc->fdctl_wr = fdctl_wr_isa; #endif /* Check pnp ids */ error = ISA_PNP_PROBE(device_get_parent(dev), dev, fdc_ids); if (error == ENXIO) return ENXIO; if (error == 0) fdc->flags |= FDC_ISPNP; /* Attempt to allocate our resources for the duration of the probe */ error = fdc_alloc_resources(fdc); if (error) goto out; #ifndef PC98 /* First - lets reset the floppy controller */ fdout_wr(fdc, 0); DELAY(100); fdout_wr(fdc, FDO_FRST); #endif /* see if it can handle a command */ #ifdef PC98 if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(4, 240), NE7_SPEC_2(2, 0), 0)) { error = ENXIO; goto out; } #else if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0)) { error = ENXIO; goto out; } #endif #ifndef PC98 if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) { ic_type = (u_char)ic_type; switch (ic_type) { case 0x80: device_set_desc(dev, "NEC 765 or clone"); fdc->fdct = FDC_NE765; break; case 0x81: device_set_desc(dev, "Intel 82077 or clone"); fdc->fdct = FDC_I82077; break; case 0x90: device_set_desc(dev, "NEC 72065B or clone"); fdc->fdct = FDC_NE72065; break; default: device_set_desc(dev, "generic floppy controller"); fdc->fdct = FDC_UNKNOWN; break; } } #endif out: fdc_release_resources(fdc); return (error); } #if NCARD > 0 static int fdc_pccard_probe(device_t dev) { int error; struct fdc_data *fdc; fdc = device_get_softc(dev); bzero(fdc, sizeof *fdc); fdc->fdc_dev = dev; #ifndef PC98 fdc->fdctl_wr = fdctl_wr_pcmcia; #endif fdc->flags |= FDC_ISPCMCIA | FDC_NODMA; /* Attempt to allocate our resources for the duration of the probe */ error = fdc_alloc_resources(fdc); if (error) goto out; #ifndef PC98 /* First - lets reset the floppy controller */ fdout_wr(fdc, 0); DELAY(100); fdout_wr(fdc, FDO_FRST); #endif /* see if it can handle a command */ if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0)) { error = ENXIO; goto out; } device_set_desc(dev, "Y-E Data PCMCIA floppy"); fdc->fdct = FDC_NE765; out: fdc_release_resources(fdc); return (error); } static int fdc_pccard_detach(device_t dev) { struct fdc_data *fdc; int error; fdc = device_get_softc(dev); /* have our children detached first */ if ((error = bus_generic_detach(dev))) return (error); if ((fdc->flags & FDC_ATTACHED) == 0) { device_printf(dev, "already unloaded\n"); return (0); } fdc->flags &= ~FDC_ATTACHED; BUS_TEARDOWN_INTR(device_get_parent(dev), dev, fdc->res_irq, fdc->fdc_intr); fdc_release_resources(fdc); device_printf(dev, "unload\n"); return (0); } #endif /* NCARD > 0 */ /* * Add a child device to the fdc controller. It will then be probed etc. */ static void fdc_add_child(device_t dev, const char *name, int unit) { int disabled; struct fdc_ivars *ivar; device_t child; ivar = malloc(sizeof *ivar, M_DEVBUF /* XXX */, M_NOWAIT | M_ZERO); if (ivar == NULL) return; if (resource_int_value(name, unit, "drive", &ivar->fdunit) != 0) ivar->fdunit = 0; child = device_add_child(dev, name, unit); if (child == NULL) return; device_set_ivars(child, ivar); if (resource_int_value(name, unit, "disabled", &disabled) == 0 && disabled != 0) device_disable(child); } static int fdc_attach(device_t dev) { struct fdc_data *fdc; - int i, error; - const char *name; + int i, error, dunit; + const char *name, *dname; fdc = device_get_softc(dev); error = fdc_alloc_resources(fdc); if (error) { device_printf(dev, "cannot re-aquire resources\n"); return error; } error = BUS_SETUP_INTR(device_get_parent(dev), dev, fdc->res_irq, INTR_TYPE_BIO | INTR_ENTROPY, fdc_intr, fdc, &fdc->fdc_intr); if (error) { device_printf(dev, "cannot setup interrupt\n"); return error; } fdc->fdcu = device_get_unit(dev); fdc->flags |= FDC_ATTACHED; if ((fdc->flags & FDC_NODMA) == 0) { /* Acquire the DMA channel forever, The driver will do the rest */ /* XXX should integrate with rman */ isa_dma_acquire(fdc->dmachan); isa_dmainit(fdc->dmachan, 128 << 3 /* XXX max secsize */); } fdc->state = DEVIDLE; #ifdef PC98 /* reset controller, turn motor off, clear fdout mirror reg */ fdc_reset(fdc); #else /* reset controller, turn motor off, clear fdout mirror reg */ fdout_wr(fdc, ((fdc->fdout = 0))); #endif bioq_init(&fdc->head); /* * Probe and attach any children. We should probably detect * devices from the BIOS unless overridden. */ name = device_get_nameunit(dev); - i = -1; - while ((i = resource_query_string(i, "at", name)) != -1) - fdc_add_child(dev, resource_query_name(i), - resource_query_unit(i)); + i = 0; + while ((resource_find_match(&i, &dname, &dunit, "at", name)) == 0) + fdc_add_child(dev, dname, dunit); return (bus_generic_attach(dev)); } static int fdc_print_child(device_t me, device_t child) { int retval = 0; retval += bus_print_child_header(me, child); retval += printf(" on %s drive %d\n", device_get_nameunit(me), fdc_get_fdunit(child)); return (retval); } static device_method_t fdc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fdc_probe), DEVMETHOD(device_attach, fdc_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, fdc_print_child), DEVMETHOD(bus_read_ivar, fdc_read_ivar), /* Our children never use any other bus interface methods. */ { 0, 0 } }; static driver_t fdc_driver = { "fdc", fdc_methods, sizeof(struct fdc_data) }; DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, 0, 0); #if NCARD > 0 static device_method_t fdc_pccard_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fdc_pccard_probe), DEVMETHOD(device_attach, fdc_attach), DEVMETHOD(device_detach, fdc_pccard_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, fdc_print_child), DEVMETHOD(bus_read_ivar, fdc_read_ivar), /* Our children never use any other bus interface methods. */ { 0, 0 } }; static driver_t fdc_pccard_driver = { "fdc", fdc_pccard_methods, sizeof(struct fdc_data) }; DRIVER_MODULE(fdc, pccard, fdc_pccard_driver, fdc_devclass, 0, 0); #endif /* NCARD > 0 */ static void fd_clone __P((void *arg, char *name, int namelen, dev_t *dev)); static struct { char *match; int minor; int link; } fd_suffix[] = { { "a", 0, 1 }, { "b", 0, 1 }, { "c", 0, 1 }, { "d", 0, 1 }, { "e", 0, 1 }, { "f", 0, 1 }, { "g", 0, 1 }, { "h", 0, 1 }, { ".1720", 1, 0 }, { ".1480", 2, 0 }, { ".1440", 3, 0 }, { ".1200", 4, 0 }, { ".820", 5, 0 }, { ".800", 6, 0 }, { ".720", 7, 0 }, { ".360", 8, 0 }, { ".640", 9, 0 }, { ".1232", 10, 0 }, #ifdef PC98 { ".1280", 11, 0 }, { ".1476", 12, 0 }, #endif { 0, 0 } }; static void fd_clone(arg, name, namelen, dev) void *arg; char *name; int namelen; dev_t *dev; { int u, d, i; char *n; dev_t pdev; if (*dev != NODEV) return; if (dev_stdclone(name, &n, "fd", &u) != 2) return; for (i = 0; ; i++) { if (fd_suffix[i].match == NULL) return; if (strcmp(n, fd_suffix[i].match)) continue; d = fd_suffix[i].minor; break; } if (fd_suffix[i].link == 0) { *dev = make_dev(&fd_cdevsw, (u << 6) + d, UID_ROOT, GID_OPERATOR, 0640, name); } else { pdev = makedev(fd_cdevsw.d_maj, (u << 6) + d); *dev = make_dev_alias(pdev, name); } } /******************************************************************/ /* * devices attached to the controller section. */ static int fd_probe(device_t dev) { #ifdef PC98 u_int fdt; #else int i; u_int fdt, st0, st3; #endif struct fd_data *fd; struct fdc_data *fdc; fdsu_t fdsu; #ifndef PC98 static int fd_fifo = 0; #endif fdsu = *(int *)device_get_ivars(dev); /* xxx cheat a bit... */ fd = device_get_softc(dev); fdc = device_get_softc(device_get_parent(dev)); bzero(fd, sizeof *fd); fd->dev = dev; fd->fdc = fdc; fd->fdsu = fdsu; fd->fdu = device_get_unit(dev); #ifdef PC98 /* look up what bios thinks we have */ switch (fd->fdu) { case 0: case 1: case 2: case 3: if ((PC98_SYSTEM_PARAMETER(0x5ae) >> fd->fdu) & 0x01) fdt = FDT_144M; #ifdef EPSON_NRDISK else if ((PC98_SYSTEM_PARAMETER(0x55c) >> fd->fdu) & 0x01) { fdt = FDT_12M; switch (epson_machine_id) { case 0x20: case 0x27: if ((PC98_SYSTEM_PARAMETER(0x488) >> fd->fdu) & 0x01) { if (nrd_check_ready()) { nrd_LED_on(); nrdu = fd->fdu; } else { fdt = FDT_NONE; } } } } #else /* !EPSON_NRDISK */ else if ((PC98_SYSTEM_PARAMETER(0x55c) >> fd->fdu) & 0x01) { fdt = FDT_12M; switch (epson_machine_id) { case 0x20: case 0x27: if ((PC98_SYSTEM_PARAMETER(0x488) >> fd->fdu) & 0x01) fdt = FDT_NONE; } } #endif /* EPSON_NRDISK */ else fdt = FDT_NONE; break; default: fdt = FDT_NONE; break; } #else #ifdef __i386__ /* look up what bios thinks we have */ switch (fd->fdu) { case 0: if ((fdc->flags & FDC_ISPCMCIA)) fdt = RTCFDT_144M; else if (device_get_flags(fdc->fdc_dev) & FDC_PRETEND_D0) fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED; else fdt = (rtcin(RTC_FDISKETTE) & 0xf0); break; case 1: fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0); break; default: fdt = RTCFDT_NONE; break; } #else fdt = RTCFDT_144M; /* XXX probably */ #endif #endif /* is there a unit? */ #ifdef PC98 if (fdt == FDT_NONE) return (ENXIO); #else if (fdt == RTCFDT_NONE) return (ENXIO); #endif #ifndef PC98 /* select it */ set_motor(fdc, fdsu, TURNON); DELAY(1000000); /* 1 sec */ /* XXX This doesn't work before the first set_motor() */ if (fd_fifo == 0 && fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN && (device_get_flags(fdc->fdc_dev) & FDC_NO_FIFO) == 0 && enable_fifo(fdc) == 0) { device_printf(device_get_parent(dev), "FIFO enabled, %d bytes threshold\n", fifo_threshold); } fd_fifo = 1; if ((fd_cmd(fdc, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* if at track 0, first seek inwards */ /* seek some steps: */ fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0); DELAY(300000); /* ...wait a moment... */ fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ } /* If we're at track 0 first seek inwards. */ if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* Seek some steps... */ if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { /* ...wait a moment... */ DELAY(300000); /* make ctrlr happy: */ fd_sense_int(fdc, 0, 0); } } for (i = 0; i < 2; i++) { /* * we must recalibrate twice, just in case the * heads have been beyond cylinder 76, since most * FDCs still barf when attempting to recalibrate * more than 77 steps */ /* go back to 0: */ if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) { /* a second being enough for full stroke seek*/ DELAY(i == 0 ? 1000000 : 300000); /* anything responding? */ if (fd_sense_int(fdc, &st0, 0) == 0 && (st0 & NE7_ST0_EC) == 0) break; /* already probed succesfully */ } } set_motor(fdc, fdsu, TURNOFF); if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ return (ENXIO); #endif /* PC98 */ fd->track = FD_NO_TRACK; fd->fdc = fdc; fd->fdsu = fdsu; fd->options = 0; callout_handle_init(&fd->toffhandle); callout_handle_init(&fd->tohandle); #ifdef PC98 switch (fdt) { case FDT_144M: /* Check 3mode I/F */ fd->pc98_trans = 0; outb(0x4be, (fd->fdu << 5) | 0x10); if (!(inb(0x4be) & 0x01)) { device_set_desc(dev, "1.44M FDD"); fd->type = FD_1440; break; } printf("Warning: can't control 3mode I/F, " "fallback to 2mode.\n" "fd%d: ", fd->fdu); /* FALLTHROUGH */ case FDT_12M: #ifdef EPSON_NRDISK if (fd->fdu == nrdu) { device_set_desc(dev, "EPSON RAM DRIVE"); nrd_LED_off(); } else #endif device_set_desc(dev, "1M/640K FDD"); fd->type = FD_1200; fd->pc98_trans = 0; break; default: return (ENXIO); } #else switch (fdt) { case RTCFDT_12M: device_set_desc(dev, "1200-KB 5.25\" drive"); fd->type = FD_1200; break; case RTCFDT_144M | RTCFDT_144M_PRETENDED: device_set_desc(dev, "config-pretended 1440-MB 3.5\" drive"); fdt = RTCFDT_144M; fd->type = FD_1440; case RTCFDT_144M: device_set_desc(dev, "1440-KB 3.5\" drive"); fd->type = FD_1440; break; case RTCFDT_288M: case RTCFDT_288M_1: device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)"); fd->type = FD_1440; break; case RTCFDT_360K: device_set_desc(dev, "360-KB 5.25\" drive"); fd->type = FD_360; break; case RTCFDT_720K: printf("720-KB 3.5\" drive"); fd->type = FD_720; break; default: return (ENXIO); } #endif return (0); } static int fd_attach(device_t dev) { struct fd_data *fd; static int cdevsw_add_done = 0; fd = device_get_softc(dev); if (!cdevsw_add_done) { cdevsw_add(&fd_cdevsw); /* XXX */ cdevsw_add_done++; } EVENTHANDLER_REGISTER(dev_clone, fd_clone, 0, 1000); make_dev(&fd_cdevsw, (fd->fdu << 6), UID_ROOT, GID_OPERATOR, 0640, "fd%d", fd->fdu); /* * Export the drive to the devstat interface. */ devstat_add_entry(&fd->device_stats, device_get_name(dev), device_get_unit(dev), 512, DEVSTAT_NO_ORDERED_TAGS, DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER, DEVSTAT_PRIORITY_FD); return (0); } static int fd_detach(device_t dev) { struct fd_data *fd; fd = device_get_softc(dev); untimeout(fd_turnoff, fd, fd->toffhandle); return (0); } static device_method_t fd_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fd_probe), DEVMETHOD(device_attach, fd_attach), DEVMETHOD(device_detach, fd_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), /* XXX */ DEVMETHOD(device_resume, bus_generic_resume), /* XXX */ { 0, 0 } }; static driver_t fd_driver = { "fd", fd_methods, sizeof(struct fd_data) }; DRIVER_MODULE(fd, fdc, fd_driver, fd_devclass, 0, 0); /****************************************************************************/ /* motor control stuff */ /* remember to not deselect the drive we're working on */ /****************************************************************************/ static void set_motor(struct fdc_data *fdc, int fdsu, int turnon) { int fdout = fdc->fdout; int needspecify = 0; #ifdef PC98 outb(IO_FDPORT, (pc98_trans != 1 ? FDP_FDDEXC : 0)|FDP_PORTEXC); DELAY(10); fdout = FDO_DMAE|FDO_MTON; #else if(turnon) { fdout &= ~FDO_FDSEL; fdout |= (FDO_MOEN0 << fdsu) + fdsu; } else fdout &= ~(FDO_MOEN0 << fdsu); if(!turnon && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0) /* gonna turn off the last drive, put FDC to bed */ fdout &= ~ (FDO_FRST|FDO_FDMAEN); else { /* make sure controller is selected and specified */ if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0) needspecify = 1; fdout |= (FDO_FRST|FDO_FDMAEN); } #endif fdout_wr(fdc, fdout); fdc->fdout = fdout; TRACE1("[0x%x->FDOUT]", fdout); if (needspecify) { /* * XXX * special case: since we have just woken up the FDC * from its sleep, we silently assume the command will * be accepted, and do not test for a timeout */ #ifdef PC98 (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(4, 240), NE7_SPEC_2(2, 0), 0); #else (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0); #endif if (fdc->flags & FDC_HAS_FIFO) (void) enable_fifo(fdc); } } static void fd_turnoff(void *xfd) { int s; fd_p fd = xfd; TRACE1("[fd%d: turnoff]", fd->fdu); s = splbio(); /* * Don't turn off the motor yet if the drive is active. * * If we got here, this could only mean we missed an interrupt. * This can e. g. happen on the Y-E Date PCMCIA floppy controller * after a controller reset. Just schedule a pseudo-interrupt * so the state machine gets re-entered. */ if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fd->fdu) { fdc_intr(fd->fdc); splx(s); return; } fd->flags &= ~FD_MOTOR; set_motor(fd->fdc, fd->fdsu, TURNOFF); splx(s); } static void fd_motor_on(void *xfd) { int s; fd_p fd = xfd; s = splbio(); fd->flags &= ~FD_MOTOR_WAIT; if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT)) { fdc_intr(fd->fdc); } splx(s); } static void fd_turnon(fd_p fd) { if(!(fd->flags & FD_MOTOR)) { fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT); set_motor(fd->fdc, fd->fdsu, TURNON); timeout(fd_motor_on, fd, hz); /* in 1 sec its ok */ } } static void fdc_reset(fdc_p fdc) { /* Try a reset, keep motor on */ #ifdef PC98 set_density(fdc); if (pc98_machine_type & M_EPSON_PC98) fdout_wr(fdc, 0xe8); else fdout_wr(fdc, 0xd8); DELAY(200); fdout_wr(fdc, 0x18); DELAY(10); #else fdout_wr(fdc, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); DELAY(100); /* enable FDC, but defer interrupts a moment */ fdout_wr(fdc, fdc->fdout & ~FDO_FDMAEN); TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN); DELAY(100); fdout_wr(fdc, fdc->fdout); TRACE1("[0x%x->FDOUT]", fdc->fdout); #endif /* XXX after a reset, silently believe the FDC will accept commands */ #ifdef PC98 (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(4, 240), NE7_SPEC_2(2, 0), 0); #else (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0); #endif if (fdc->flags & FDC_HAS_FIFO) (void) enable_fifo(fdc); } /****************************************************************************/ /* fdc in/out */ /****************************************************************************/ int in_fdc(struct fdc_data *fdc) { int i, j = 100000; while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) if (i == NE7_RQM) return fdc_err(fdc, "ready for output in input\n"); if (j <= 0) return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); #ifdef FDC_DEBUG i = fddata_rd(fdc); TRACE1("[FDDATA->0x%x]", (unsigned char)i); return(i); #else /* !FDC_DEBUG */ return fddata_rd(fdc); #endif /* FDC_DEBUG */ } /* * fd_in: Like in_fdc, but allows you to see if it worked. */ static int fd_in(struct fdc_data *fdc, int *ptr) { int i, j = 100000; while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) if (i == NE7_RQM) return fdc_err(fdc, "ready for output in input\n"); if (j <= 0) return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); #ifdef FDC_DEBUG i = fddata_rd(fdc); TRACE1("[FDDATA->0x%x]", (unsigned char)i); *ptr = i; return 0; #else /* !FDC_DEBUG */ i = fddata_rd(fdc); if (ptr) *ptr = i; return 0; #endif /* FDC_DEBUG */ } int out_fdc(struct fdc_data *fdc, int x) { int i; /* Check that the direction bit is set */ i = 100000; while ((fdsts_rd(fdc) & NE7_DIO) && i-- > 0); if (i <= 0) return fdc_err(fdc, "direction bit not set\n"); /* Check that the floppy controller is ready for a command */ i = 100000; while ((fdsts_rd(fdc) & NE7_RQM) == 0 && i-- > 0); if (i <= 0) return fdc_err(fdc, bootverbose? "output ready timeout\n": 0); /* Send the command and return */ fddata_wr(fdc, x); TRACE1("[0x%x->FDDATA]", x); return (0); } /****************************************************************************/ /* fdopen/fdclose */ /****************************************************************************/ int Fdopen(dev_t dev, int flags, int mode, struct proc *p) { fdu_t fdu = FDUNIT(minor(dev)); int type = FDTYPE(minor(dev)); fd_p fd; fdc_p fdc; /* check bounds */ if ((fd = devclass_get_softc(fd_devclass, fdu)) == 0) return (ENXIO); fdc = fd->fdc; if ((fdc == NULL) || (fd->type == NO_TYPE)) return (ENXIO); if (type > NUMDENS) return (ENXIO); #ifdef PC98 if (type == 0) type = FD_1200; /* XXX backward compatibility */ else ; /* XXX any types are OK for PC-98 */ if (pc98_fd_check_ready(fdu) == -1) return(EIO); #else if (type == 0) type = fd->type; else { /* * For each type of basic drive, make sure we are trying * to open a type it can do, */ if (type != fd->type) { switch (fd->type) { case FD_360: return (ENXIO); case FD_720: if ( type != FD_820 && type != FD_800 && type != FD_640 ) return (ENXIO); break; case FD_1200: switch (type) { case FD_1480: type = FD_1480in5_25; break; case FD_1440: type = FD_1440in5_25; break; case FD_1232: break; case FD_820: type = FD_820in5_25; break; case FD_800: type = FD_800in5_25; break; case FD_720: type = FD_720in5_25; break; case FD_640: type = FD_640in5_25; break; case FD_360: type = FD_360in5_25; break; default: return(ENXIO); } break; case FD_1440: if ( type != FD_1720 && type != FD_1480 && type != FD_1200 && type != FD_820 && type != FD_800 && type != FD_720 && type != FD_640 ) return(ENXIO); break; } } } #endif fd->ft = fd_types + type - 1; fd->flags |= FD_OPEN; return 0; } int fdclose(dev_t dev, int flags, int mode, struct proc *p) { fdu_t fdu = FDUNIT(minor(dev)); struct fd_data *fd; fd = devclass_get_softc(fd_devclass, fdu); fd->flags &= ~FD_OPEN; fd->options &= ~(FDOPT_NORETRY | FDOPT_NOERRLOG | FDOPT_NOERROR); return (0); } /****************************************************************************/ /* fdstrategy */ /****************************************************************************/ void fdstrategy(struct bio *bp) { unsigned nblocks, blknum, cando; int s; fdu_t fdu; fdc_p fdc; fd_p fd; size_t fdblk; fdu = FDUNIT(minor(bp->bio_dev)); fd = devclass_get_softc(fd_devclass, fdu); if (fd == 0) panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)", (u_long)major(bp->bio_dev), (u_long)minor(bp->bio_dev)); fdc = fd->fdc; if (fd->type == NO_TYPE) { bp->bio_error = ENXIO; bp->bio_flags |= BIO_ERROR; goto bad; }; fdblk = 128 << (fd->ft->secsize); if (!(bp->bio_cmd & BIO_FORMAT)) { if (bp->bio_blkno < 0) { printf( "fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n", fdu, (u_long)bp->bio_blkno, bp->bio_bcount); bp->bio_error = EINVAL; bp->bio_flags |= BIO_ERROR; goto bad; } if ((bp->bio_bcount % fdblk) != 0) { bp->bio_error = EINVAL; bp->bio_flags |= BIO_ERROR; goto bad; } } /* * Set up block calculations. */ if (bp->bio_blkno > 20000000) { /* * Reject unreasonably high block number, prevent the * multiplication below from overflowing. */ bp->bio_error = EINVAL; bp->bio_flags |= BIO_ERROR; goto bad; } blknum = (unsigned) bp->bio_blkno * DEV_BSIZE/fdblk; nblocks = fd->ft->size; bp->bio_resid = 0; if (blknum + (bp->bio_bcount / fdblk) > nblocks) { if (blknum <= nblocks) { cando = (nblocks - blknum) * fdblk; bp->bio_resid = bp->bio_bcount - cando; if (cando == 0) goto bad; /* not actually bad but EOF */ } else { bp->bio_error = EINVAL; bp->bio_flags |= BIO_ERROR; goto bad; } } bp->bio_pblkno = bp->bio_blkno; s = splbio(); bioqdisksort(&fdc->head, bp); untimeout(fd_turnoff, fd, fd->toffhandle); /* a good idea */ /* Tell devstat we are starting on the transaction */ devstat_start_transaction(&fd->device_stats); device_busy(fd->dev); fdstart(fdc); splx(s); return; bad: biodone(bp); } /***************************************************************\ * fdstart * * We have just queued something.. if the controller is not busy * * then simulate the case where it has just finished a command * * So that it (the interrupt routine) looks on the queue for more* * work to do and picks up what we just added. * * If the controller is already busy, we need do nothing, as it * * will pick up our work when the present work completes * \***************************************************************/ static void fdstart(struct fdc_data *fdc) { int s; s = splbio(); if(fdc->state == DEVIDLE) { fdc_intr(fdc); } splx(s); } static void fd_iotimeout(void *xfdc) { fdc_p fdc; int s; fdc = xfdc; TRACE1("fd%d[fd_iotimeout()]", fdc->fdu); /* * Due to IBM's brain-dead design, the FDC has a faked ready * signal, hardwired to ready == true. Thus, any command * issued if there's no diskette in the drive will _never_ * complete, and must be aborted by resetting the FDC. * Many thanks, Big Blue! * The FDC must not be reset directly, since that would * interfere with the state machine. Instead, pretend that * the command completed but was invalid. The state machine * will reset the FDC and retry once. */ s = splbio(); fdc->status[0] = NE7_ST0_IC_IV; fdc->flags &= ~FDC_STAT_VALID; fdc->state = IOTIMEDOUT; fdc_intr(fdc); splx(s); } /* just ensure it has the right spl */ static void fd_pseudointr(void *xfdc) { int s; s = splbio(); fdc_intr(xfdc); splx(s); } /***********************************************************************\ * fdintr * * keep calling the state machine until it returns a 0 * * ALWAYS called at SPLBIO * \***********************************************************************/ static void fdc_intr(void *xfdc) { fdc_p fdc = xfdc; while(fdstate(fdc)) ; } /* * magic pseudo-DMA initialization for YE FDC. Sets count and * direction */ #define SET_BCDR(fdc,wr,cnt,port) \ bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port, \ ((cnt)-1) & 0xff); \ bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port + 1, \ ((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f))); /* * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy */ static int fdcpio(fdc_p fdc, long flags, caddr_t addr, u_int count) { u_char *cptr = (u_char *)addr; if (flags == BIO_READ) { if (fdc->state != PIOREAD) { fdc->state = PIOREAD; return(0); }; SET_BCDR(fdc, 0, count, 0); bus_space_read_multi_1(fdc->portt, fdc->porth, fdc->port_off + FDC_YE_DATAPORT, cptr, count); } else { bus_space_write_multi_1(fdc->portt, fdc->porth, fdc->port_off + FDC_YE_DATAPORT, cptr, count); SET_BCDR(fdc, 0, count, 0); }; return(1); } /***********************************************************************\ * The controller state machine. * * if it returns a non zero value, it should be called again immediatly * \***********************************************************************/ static int fdstate(fdc_p fdc) { int read, format, head, i, sec = 0, sectrac, st0, cyl, st3, idf; unsigned blknum = 0, b_cylinder = 0; fdu_t fdu = fdc->fdu; fd_p fd; register struct bio *bp; struct fd_formb *finfo = NULL; size_t fdblk; bp = fdc->bp; if (bp == NULL) { bp = bioq_first(&fdc->head); if (bp != NULL) { bioq_remove(&fdc->head, bp); fdc->bp = bp; } } if (bp == NULL) { /***********************************************\ * nothing left for this controller to do * * Force into the IDLE state, * \***********************************************/ fdc->state = DEVIDLE; if (fdc->fd) { device_printf(fdc->fdc_dev, "unexpected valid fd pointer\n"); fdc->fd = (fd_p) 0; fdc->fdu = -1; } TRACE1("[fdc%d IDLE]", fdc->fdcu); return (0); } fdu = FDUNIT(minor(bp->bio_dev)); fd = devclass_get_softc(fd_devclass, fdu); fdblk = 128 << fd->ft->secsize; if (fdc->fd && (fd != fdc->fd)) device_printf(fd->dev, "confused fd pointers\n"); read = bp->bio_cmd == BIO_READ; if (read) idf = ISADMA_READ; else idf = ISADMA_WRITE; format = bp->bio_cmd & BIO_FORMAT; if (format) { finfo = (struct fd_formb *)bp->bio_data; fd->skip = (char *)&(finfo->fd_formb_cylno(0)) - (char *)finfo; } if (fdc->state == DOSEEK || fdc->state == SEEKCOMPLETE) { blknum = (unsigned) bp->bio_pblkno * DEV_BSIZE/fdblk + fd->skip/fdblk; b_cylinder = blknum / (fd->ft->sectrac * fd->ft->heads); } TRACE1("fd%d", fdu); TRACE1("[%s]", fdstates[fdc->state]); TRACE1("(0x%x)", fd->flags); untimeout(fd_turnoff, fd, fd->toffhandle); fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); switch (fdc->state) { case DEVIDLE: case FINDWORK: /* we have found new work */ fdc->retry = 0; fd->skip = 0; fdc->fd = fd; fdc->fdu = fdu; #ifdef PC98 pc98_trans = fd->ft->trans; if (pc98_trans_prev != pc98_trans) { int i; set_density(fdc); for (i = 0; i < 10; i++) { outb(0x5f, 0); outb(0x5f, 0); } pc98_trans_prev = pc98_trans; } if (pc98_trans != fd->pc98_trans) { if (fd->type == FD_1440) { outb(0x4be, (fdu << 5) | 0x10 | (pc98_trans >> 1)); outb(0x5f, 0); outb(0x5f, 0); } fd->pc98_trans = pc98_trans; } #else fdc->fdctl_wr(fdc, fd->ft->trans); #endif TRACE1("[0x%x->FDCTL]", fd->ft->trans); /*******************************************************\ * If the next drive has a motor startup pending, then * * it will start up in its own good time * \*******************************************************/ if(fd->flags & FD_MOTOR_WAIT) { fdc->state = MOTORWAIT; return (0); /* come back later */ } /*******************************************************\ * Maybe if it's not starting, it SHOULD be starting * \*******************************************************/ #ifdef EPSON_NRDISK if (fdu != nrdu) { if (!(fd->flags & FD_MOTOR)) { fdc->state = MOTORWAIT; fd_turnon(fdu); return(0); } else /* at least make sure we are selected */ { set_motor(fdcu, fd->fdsu, TURNON); } } #else /* !EPSON_NRDISK */ if (!(fd->flags & FD_MOTOR)) { fdc->state = MOTORWAIT; fd_turnon(fd); return (0); } else /* at least make sure we are selected */ { set_motor(fdc, fd->fdsu, TURNON); } #endif if (fdc->flags & FDC_NEEDS_RESET) { fdc->state = RESETCTLR; fdc->flags &= ~FDC_NEEDS_RESET; } else fdc->state = DOSEEK; break; case DOSEEK: if (b_cylinder == (unsigned)fd->track) { fdc->state = SEEKCOMPLETE; break; } #ifdef PC98 pc98_fd_check_ready(fdu); #endif if (fd_cmd(fdc, 3, NE7CMD_SEEK, fd->fdsu, b_cylinder * fd->ft->steptrac, 0)) { /* * seek command not accepted, looks like * the FDC went off to the Saints... */ fdc->retry = 6; /* try a reset */ return(retrier(fdc)); } fd->track = FD_NO_TRACK; fdc->state = SEEKWAIT; return(0); /* will return later */ case SEEKWAIT: /* allow heads to settle */ timeout(fd_pseudointr, fdc, hz / 16); fdc->state = SEEKCOMPLETE; return(0); /* will return later */ case SEEKCOMPLETE : /* SEEK DONE, START DMA */ /* Make sure seek really happened*/ if(fd->track == FD_NO_TRACK) { int descyl = b_cylinder * fd->ft->steptrac; do { /* * This might be a "ready changed" interrupt, * which cannot really happen since the * RDY pin is hardwired to + 5 volts. This * generally indicates a "bouncing" intr * line, so do one of the following: * * When running on an enhanced FDC that is * known to not go stuck after responding * with INVALID, fetch all interrupt states * until seeing either an INVALID or a * real interrupt condition. * * When running on a dumb old NE765, give * up immediately. The controller will * provide up to four dummy RC interrupt * conditions right after reset (for the * corresponding four drives), so this is * our only chance to get notice that it * was not the FDC that caused the interrupt. */ if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) return 0; if(fdc->fdct == FDC_NE765 && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) return 0; /* hope for a real intr */ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); if (0 == descyl) { int failed = 0; /* * seek to cyl 0 requested; make sure we are * really there */ if (fd_sense_drive_status(fdc, &st3)) failed = 1; #ifdef EPSON_NRDISK if (fdu == nrdu) st3 = NE7_ST3_T0; #endif /* EPSON_NRDISK */ if ((st3 & NE7_ST3_T0) == 0) { printf( "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n", fdu, st3, NE7_ST3BITS); failed = 1; } if (failed) { if(fdc->retry < 3) fdc->retry = 3; return (retrier(fdc)); } } #ifdef EPSON_NRDISK if (fdu == nrdu) cyl = descyl; #endif if (cyl != descyl) { printf( "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", fdu, descyl, cyl, st0); if (fdc->retry < 3) fdc->retry = 3; return (retrier(fdc)); } } fd->track = b_cylinder; #ifdef EPSON_NRDISK if (fdu != nrdu) { #endif /* EPSON_NRDISK */ if (!(fdc->flags & FDC_NODMA)) isa_dmastart(idf, bp->bio_data+fd->skip, format ? bp->bio_bcount : fdblk, fdc->dmachan); sectrac = fd->ft->sectrac; sec = blknum % (sectrac * fd->ft->heads); head = sec / sectrac; sec = sec % sectrac + 1; fd->hddrv = ((head&1)<<2)+fdu; if(format || !read) { /* make sure the drive is writable */ if(fd_sense_drive_status(fdc, &st3) != 0) { /* stuck controller? */ if (!(fdc->flags & FDC_NODMA)) isa_dmadone(idf, bp->bio_data + fd->skip, format ? bp->bio_bcount : fdblk, fdc->dmachan); fdc->retry = 6; /* reset the beast */ return (retrier(fdc)); } if(st3 & NE7_ST3_WP) { /* * XXX YES! this is ugly. * in order to force the current operation * to fail, we will have to fake an FDC * error - all error handling is done * by the retrier() */ fdc->status[0] = NE7_ST0_IC_AT; fdc->status[1] = NE7_ST1_NW; fdc->status[2] = 0; fdc->status[3] = fd->track; fdc->status[4] = head; fdc->status[5] = sec; fdc->retry = 8; /* break out immediately */ fdc->state = IOTIMEDOUT; /* not really... */ return (1); } } if (format) { if (fdc->flags & FDC_NODMA) { /* * This seems to be necessary for * whatever obscure reason; if we omit * it, we end up filling the sector ID * fields of the newly formatted track * entirely with garbage, causing * `wrong cylinder' errors all over * the place when trying to read them * back. * * Umpf. */ SET_BCDR(fdc, 1, bp->bio_bcount, 0); (void)fdcpio(fdc,bp->bio_cmd, bp->bio_data+fd->skip, bp->bio_bcount); } /* formatting */ if(fd_cmd(fdc, 6, NE7CMD_FORMAT, head << 2 | fdu, finfo->fd_formb_secshift, finfo->fd_formb_nsecs, finfo->fd_formb_gaplen, finfo->fd_formb_fillbyte, 0)) { /* controller fell over */ if (!(fdc->flags & FDC_NODMA)) isa_dmadone(idf, bp->bio_data + fd->skip, format ? bp->bio_bcount : fdblk, fdc->dmachan); fdc->retry = 6; return (retrier(fdc)); } } else { if (fdc->flags & FDC_NODMA) { /* * this seems to be necessary even when * reading data */ SET_BCDR(fdc, 1, fdblk, 0); /* * perform the write pseudo-DMA before * the WRITE command is sent */ if (!read) (void)fdcpio(fdc,bp->bio_cmd, bp->bio_data+fd->skip, fdblk); } if (fd_cmd(fdc, 9, (read ? NE7CMD_READ : NE7CMD_WRITE), head << 2 | fdu, /* head & unit */ fd->track, /* track */ head, sec, /* sector + 1 */ fd->ft->secsize, /* sector size */ sectrac, /* sectors/track */ fd->ft->gap, /* gap size */ fd->ft->datalen, /* data length */ 0)) { /* the beast is sleeping again */ if (!(fdc->flags & FDC_NODMA)) isa_dmadone(idf, bp->bio_data + fd->skip, format ? bp->bio_bcount : fdblk, fdc->dmachan); fdc->retry = 6; return (retrier(fdc)); } } if (fdc->flags & FDC_NODMA) /* * if this is a read, then simply await interrupt * before performing PIO */ if (read && !fdcpio(fdc,bp->bio_cmd, bp->bio_data+fd->skip,fdblk)) { fd->tohandle = timeout(fd_iotimeout, fdc, hz); return(0); /* will return later */ }; /* * write (or format) operation will fall through and * await completion interrupt */ fdc->state = IOCOMPLETE; fd->tohandle = timeout(fd_iotimeout, fdc, hz); return (0); /* will return later */ #ifdef EPSON_NRDISK } else { nrdblkn = (nrd_t)((unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + fd->skip/fdblk); nrd_LED_on(); nrd_addrset(fdblk * nrdblkn); while (!nrd_check_ready()) DELAY(1); if (read) epson_insw(P_NRD_DATA, bp->bio_data + fd->skip, fdblk / sizeof(short)); else epson_outsw(P_NRD_DATA, bp->bio_data + fd->skip, (format ? bp->bio_bcount : fdblk) / sizeof(short)); blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + fd->skip/fdblk; sectrac = fd->ft->sectrac; sec = blknum % (sectrac * fd->ft->heads); head = sec / sectrac; sec = sec % sectrac + 1; fd->hddrv = ((head&1)<<2)+fdu; if (nrdsec++ >= nrd_sec()) nrdaddr = (nrd_t)(fd->track * 8 + head * 4); nrdsec = sec; fdc->state = IOCOMPLETE; } #endif case PIOREAD: /* * actually perform the PIO read. The IOCOMPLETE case * removes the timeout for us. */ (void)fdcpio(fdc,bp->bio_cmd,bp->bio_data+fd->skip,fdblk); fdc->state = IOCOMPLETE; /* FALLTHROUGH */ case IOCOMPLETE: /* IO DONE, post-analyze */ #ifdef EPSON_NRDISK if (fdu != nrdu) untimeout(fd_iotimeout, fdc, fd->tohandle); #else untimeout(fd_iotimeout, fdc, fd->tohandle); #endif if (fd_read_status(fdc, fd->fdsu)) { if (!(fdc->flags & FDC_NODMA)) isa_dmadone(idf, bp->bio_data + fd->skip, format ? bp->bio_bcount : fdblk, fdc->dmachan); if (fdc->retry < 6) fdc->retry = 6; /* force a reset */ return (retrier(fdc)); } fdc->state = IOTIMEDOUT; /* FALLTHROUGH */ case IOTIMEDOUT: #ifdef EPSON_NRDISK if (fdu != nrdu) { #endif /* EPSON_NRDISK */ if (!(fdc->flags & FDC_NODMA)) isa_dmadone(idf, bp->bio_data + fd->skip, format ? bp->bio_bcount : fdblk, fdc->dmachan); #ifdef EPSON_NRDISK } else nrd_LED_off(); #endif /* EPSON_NRDISK */ if (fdc->status[0] & NE7_ST0_IC) { if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT && fdc->status[1] & NE7_ST1_OR) { /* * DMA overrun. Someone hogged the bus * and didn't release it in time for the * next FDC transfer. * Just restart it, don't increment retry * count. (vak) */ fdc->state = SEEKCOMPLETE; return (1); } else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV && fdc->retry < 6) fdc->retry = 6; /* force a reset */ else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT && fdc->status[2] & NE7_ST2_WC && fdc->retry < 3) fdc->retry = 3; /* force recalibrate */ return (retrier(fdc)); } /* All OK */ fd->skip += fdblk; if (!format && fd->skip < bp->bio_bcount - bp->bio_resid) { /* set up next transfer */ fdc->state = DOSEEK; } else { /* ALL DONE */ fd->skip = 0; fdc->bp = NULL; device_unbusy(fd->dev); biofinish(bp, &fd->device_stats, 0); fdc->fd = (fd_p) 0; fdc->fdu = -1; fdc->state = FINDWORK; } return (1); case RESETCTLR: fdc_reset(fdc); fdc->retry++; fdc->state = RESETCOMPLETE; return (0); case RESETCOMPLETE: /* * Discard all the results from the reset so that they * can't cause an unexpected interrupt later. */ for (i = 0; i < 4; i++) (void)fd_sense_int(fdc, &st0, &cyl); fdc->state = STARTRECAL; /* Fall through. */ case STARTRECAL: #ifdef PC98 pc98_fd_check_ready(fdu); #endif if(fd_cmd(fdc, 2, NE7CMD_RECAL, fdu, 0)) { /* arrgl */ fdc->retry = 6; return (retrier(fdc)); } fdc->state = RECALWAIT; return (0); /* will return later */ case RECALWAIT: /* allow heads to settle */ timeout(fd_pseudointr, fdc, hz / 8); fdc->state = RECALCOMPLETE; return (0); /* will return later */ case RECALCOMPLETE: do { /* * See SEEKCOMPLETE for a comment on this: */ if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) return 0; if(fdc->fdct == FDC_NE765 && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) return 0; /* hope for a real intr */ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); #ifdef EPSON_NRDISK if (fdu == nrdu) { st0 = NE7_ST0_IC_NT; cyl = 0; } #endif if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0) { if(fdc->retry > 3) /* * a recalibrate from beyond cylinder 77 * will "fail" due to the FDC limitations; * since people used to complain much about * the failure message, try not logging * this one if it seems to be the first * time in a line */ printf("fd%d: recal failed ST0 %b cyl %d\n", fdu, st0, NE7_ST0BITS, cyl); if(fdc->retry < 3) fdc->retry = 3; return (retrier(fdc)); } fd->track = 0; /* Seek (probably) necessary */ fdc->state = DOSEEK; return (1); /* will return immediatly */ case MOTORWAIT: if(fd->flags & FD_MOTOR_WAIT) { return (0); /* time's not up yet */ } if (fdc->flags & FDC_NEEDS_RESET) { fdc->state = RESETCTLR; fdc->flags &= ~FDC_NEEDS_RESET; } else { /* * If all motors were off, then the controller was * reset, so it has lost track of the current * cylinder. Recalibrate to handle this case. * But first, discard the results of the reset. */ fdc->state = RESETCOMPLETE; } return (1); /* will return immediatly */ default: device_printf(fdc->fdc_dev, "unexpected FD int->"); if (fd_read_status(fdc, fd->fdsu) == 0) printf("FDC status :%x %x %x %x %x %x %x ", fdc->status[0], fdc->status[1], fdc->status[2], fdc->status[3], fdc->status[4], fdc->status[5], fdc->status[6] ); else printf("No status available "); if (fd_sense_int(fdc, &st0, &cyl) != 0) { printf("[controller is dead now]\n"); return (0); } printf("ST0 = %x, PCN = %x\n", st0, cyl); return (0); } /*XXX confusing: some branches return immediately, others end up here*/ return (1); /* Come back immediatly to new state */ } static int retrier(struct fdc_data *fdc) { struct bio *bp; struct fd_data *fd; int fdu; bp = fdc->bp; /* XXX shouldn't this be cached somewhere? */ fdu = FDUNIT(minor(bp->bio_dev)); fd = devclass_get_softc(fd_devclass, fdu); if (fd->options & FDOPT_NORETRY) goto fail; switch (fdc->retry) { case 0: case 1: case 2: fdc->state = SEEKCOMPLETE; break; case 3: case 4: case 5: fdc->state = STARTRECAL; break; case 6: fdc->state = RESETCTLR; break; case 7: break; default: fail: { dev_t sav_bio_dev = bp->bio_dev; int printerror = (fd->options & FDOPT_NOERRLOG) == 0; /* Trick diskerr */ bp->bio_dev = makedev(major(bp->bio_dev), (FDUNIT(minor(bp->bio_dev))<<3)|RAW_PART); if (printerror) diskerr(bp, "hard error", fdc->fd->skip / DEV_BSIZE, (struct disklabel *)NULL); bp->bio_dev = sav_bio_dev; if (printerror) { if (fdc->flags & FDC_STAT_VALID) { printf( " (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n", fdc->status[0], NE7_ST0BITS, fdc->status[1], NE7_ST1BITS, fdc->status[2], NE7_ST2BITS, fdc->status[3], fdc->status[4], fdc->status[5]); } else printf(" (No status)\n"); } } if ((fd->options & FDOPT_NOERROR) == 0) { bp->bio_flags |= BIO_ERROR; bp->bio_error = EIO; bp->bio_resid += bp->bio_bcount - fdc->fd->skip; } fdc->bp = NULL; fdc->fd->skip = 0; device_unbusy(fd->dev); biofinish(bp, &fdc->fd->device_stats, 0); fdc->state = FINDWORK; fdc->flags |= FDC_NEEDS_RESET; fdc->fd = (fd_p) 0; fdc->fdu = -1; return (1); } fdc->retry++; return (1); } static void fdbiodone(struct bio *bp) { wakeup(bp); } static int fdformat(dev, finfo, p) dev_t dev; struct fd_formb *finfo; struct proc *p; { fdu_t fdu; fd_p fd; struct bio *bp; int rv = 0, s; size_t fdblk; fdu = FDUNIT(minor(dev)); fd = devclass_get_softc(fd_devclass, fdu); fdblk = 128 << fd->ft->secsize; /* set up a buffer header for fdstrategy() */ bp = (struct bio *)malloc(sizeof(struct bio), M_TEMP, M_NOWAIT); if(bp == 0) return ENOMEM; /* * keep the process from being swapped */ PHOLD(p); bzero((void *)bp, sizeof(*bp)); bp->bio_cmd = BIO_FORMAT; /* * calculate a fake blkno, so fdstrategy() would initiate a * seek to the requested cylinder */ bp->bio_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) + finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE; bp->bio_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; bp->bio_data = (caddr_t)finfo; /* now do the format */ bp->bio_dev = dev; bp->bio_done = fdbiodone; fdstrategy(bp); /* ...and wait for it to complete */ s = splbio(); while(!(bp->bio_flags & BIO_DONE)) { rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz); if (rv == EWOULDBLOCK) break; } splx(s); if (rv == EWOULDBLOCK) { /* timed out */ rv = EIO; device_unbusy(fd->dev); } if (bp->bio_flags & BIO_ERROR) rv = bp->bio_error; /* * allow the process to be swapped */ PRELE(p); free(bp, M_TEMP); return rv; } /* * TODO: don't allocate buffer on stack. */ static int fdioctl(dev, cmd, addr, flag, p) dev_t dev; u_long cmd; caddr_t addr; int flag; struct proc *p; { fdu_t fdu = FDUNIT(minor(dev)); fd_p fd = devclass_get_softc(fd_devclass, fdu); size_t fdblk; struct fd_type *fdt; struct disklabel *dl; struct fdc_status *fsp; char buffer[DEV_BSIZE]; int error = 0; fdblk = 128 << fd->ft->secsize; #ifdef PC98 pc98_fd_check_ready(fdu); #endif switch (cmd) { case DIOCGDINFO: bzero(buffer, sizeof (buffer)); dl = (struct disklabel *)buffer; dl->d_secsize = fdblk; fdt = fd->ft; dl->d_secpercyl = fdt->size / fdt->tracks; dl->d_type = DTYPE_FLOPPY; if (readdisklabel(dkmodpart(dev, RAW_PART), dl) == NULL) error = 0; else error = EINVAL; *(struct disklabel *)addr = *dl; break; case DIOCSDINFO: if ((flag & FWRITE) == 0) error = EBADF; break; case DIOCWLABEL: if ((flag & FWRITE) == 0) error = EBADF; break; case DIOCWDINFO: if ((flag & FWRITE) == 0) { error = EBADF; break; } dl = (struct disklabel *)addr; if ((error = setdisklabel((struct disklabel *)buffer, dl, (u_long)0)) != 0) break; error = writedisklabel(dev, (struct disklabel *)buffer); break; case FD_FORM: if ((flag & FWRITE) == 0) error = EBADF; /* must be opened for writing */ else if (((struct fd_formb *)addr)->format_version != FD_FORMAT_VERSION) error = EINVAL; /* wrong version of formatting prog */ else error = fdformat(dev, (struct fd_formb *)addr, p); break; case FD_GTYPE: /* get drive type */ *(struct fd_type *)addr = *fd->ft; break; case FD_STYPE: /* set drive type */ /* this is considered harmful; only allow for superuser */ if (suser(p) != 0) return EPERM; *fd->ft = *(struct fd_type *)addr; break; case FD_GOPTS: /* get drive options */ *(int *)addr = fd->options; break; case FD_SOPTS: /* set drive options */ fd->options = *(int *)addr; break; case FD_CLRERR: if (suser(p) != 0) return EPERM; fd->fdc->fdc_errs = 0; break; case FD_GSTAT: fsp = (struct fdc_status *)addr; if ((fd->fdc->flags & FDC_STAT_VALID) == 0) return EINVAL; memcpy(fsp->status, fd->fdc->status, 7 * sizeof(u_int)); break; default: error = ENOTTY; break; } return (error); } /* * Hello emacs, these are the * Local Variables: * c-indent-level: 8 * c-continued-statement-offset: 8 * c-continued-brace-offset: 0 * c-brace-offset: -8 * c-brace-imaginary-offset: 0 * c-argdecl-indent: 8 * c-label-offset: -8 * c++-hanging-braces: 1 * c++-access-specifier-offset: -8 * c++-empty-arglist-indent: 8 * c++-friend-offset: 0 * End: */ Index: head/sys/pc98/i386/machdep.c =================================================================== --- head/sys/pc98/i386/machdep.c (revision 78134) +++ head/sys/pc98/i386/machdep.c (revision 78135) @@ -1,2582 +1,2582 @@ /*- * Copyright (c) 1992 Terrence R. Lambert. * Copyright (c) 1982, 1987, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * 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. * 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. * * from: @(#)machdep.c 7.4 (Berkeley) 6/3/91 * $FreeBSD$ */ #include "opt_atalk.h" #include "opt_compat.h" #include "opt_cpu.h" #include "opt_ddb.h" #include "opt_inet.h" #include "opt_ipx.h" #include "opt_isa.h" #include "opt_maxmem.h" #include "opt_msgbuf.h" #include "opt_npx.h" #include "opt_perfmon.h" -#include "opt_userconfig.h" +/* #include "opt_userconfig.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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* pcb.h included via sys/user.h */ #include #ifdef PERFMON #include #endif #ifdef OLD_BUS_ARCH #include #endif #include #include #ifdef PC98 #include #include #else #include #endif #include #include #include extern void init386 __P((int first)); extern void dblfault_handler __P((void)); extern void printcpuinfo(void); /* XXX header file */ extern void earlysetcpuclass(void); /* same header file */ extern void finishidentcpu(void); extern void panicifcpuunsupported(void); extern void initializecpu(void); #define CS_SECURE(cs) (ISPL(cs) == SEL_UPL) #define EFL_SECURE(ef, oef) ((((ef) ^ (oef)) & ~PSL_USERCHANGE) == 0) static void cpu_startup __P((void *)); SYSINIT(cpu, SI_SUB_CPU, SI_ORDER_FIRST, cpu_startup, NULL) #ifdef PC98 int need_pre_dma_flush; /* If 1, use wbinvd befor DMA transfer. */ int need_post_dma_flush; /* If 1, use invd after DMA transfer. */ #endif int _udatasel, _ucodesel; u_int atdevbase; #if defined(SWTCH_OPTIM_STATS) extern int swtch_optim_stats; SYSCTL_INT(_debug, OID_AUTO, swtch_optim_stats, CTLFLAG_RD, &swtch_optim_stats, 0, ""); SYSCTL_INT(_debug, OID_AUTO, tlb_flush_count, CTLFLAG_RD, &tlb_flush_count, 0, ""); #endif #ifdef PC98 static int ispc98 = 1; #else static int ispc98 = 0; #endif SYSCTL_INT(_machdep, OID_AUTO, ispc98, CTLFLAG_RD, &ispc98, 0, ""); int physmem = 0; int cold = 1; static void osendsig __P((sig_t catcher, int sig, sigset_t *mask, u_long code)); static int sysctl_hw_physmem(SYSCTL_HANDLER_ARGS) { int error = sysctl_handle_int(oidp, 0, ctob(physmem), req); return (error); } SYSCTL_PROC(_hw, HW_PHYSMEM, physmem, CTLTYPE_INT|CTLFLAG_RD, 0, 0, sysctl_hw_physmem, "I", ""); static int sysctl_hw_usermem(SYSCTL_HANDLER_ARGS) { int error = sysctl_handle_int(oidp, 0, ctob(physmem - cnt.v_wire_count), req); return (error); } SYSCTL_PROC(_hw, HW_USERMEM, usermem, CTLTYPE_INT|CTLFLAG_RD, 0, 0, sysctl_hw_usermem, "I", ""); static int sysctl_hw_availpages(SYSCTL_HANDLER_ARGS) { int error = sysctl_handle_int(oidp, 0, i386_btop(avail_end - avail_start), req); return (error); } SYSCTL_PROC(_hw, OID_AUTO, availpages, CTLTYPE_INT|CTLFLAG_RD, 0, 0, sysctl_hw_availpages, "I", ""); static int sysctl_machdep_msgbuf(SYSCTL_HANDLER_ARGS) { int error; /* Unwind the buffer, so that it's linear (possibly starting with * some initial nulls). */ error=sysctl_handle_opaque(oidp,msgbufp->msg_ptr+msgbufp->msg_bufr, msgbufp->msg_size-msgbufp->msg_bufr,req); if(error) return(error); if(msgbufp->msg_bufr>0) { error=sysctl_handle_opaque(oidp,msgbufp->msg_ptr, msgbufp->msg_bufr,req); } return(error); } SYSCTL_PROC(_machdep, OID_AUTO, msgbuf, CTLTYPE_STRING|CTLFLAG_RD, 0, 0, sysctl_machdep_msgbuf, "A","Contents of kernel message buffer"); static int msgbuf_clear; static int sysctl_machdep_msgbuf_clear(SYSCTL_HANDLER_ARGS) { int error; error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); if (!error && req->newptr) { /* Clear the buffer and reset write pointer */ bzero(msgbufp->msg_ptr,msgbufp->msg_size); msgbufp->msg_bufr=msgbufp->msg_bufx=0; msgbuf_clear=0; } return (error); } SYSCTL_PROC(_machdep, OID_AUTO, msgbuf_clear, CTLTYPE_INT|CTLFLAG_RW, &msgbuf_clear, 0, sysctl_machdep_msgbuf_clear, "I", "Clear kernel message buffer"); int Maxmem = 0; #ifdef PC98 int Maxmem_under16M = 0; #endif long dumplo; vm_offset_t phys_avail[10]; /* must be 2 less so 0 0 can signal end of chunks */ #define PHYS_AVAIL_ARRAY_END ((sizeof(phys_avail) / sizeof(vm_offset_t)) - 2) static vm_offset_t buffer_sva, buffer_eva; vm_offset_t clean_sva, clean_eva; static vm_offset_t pager_sva, pager_eva; static struct trapframe proc0_tf; #ifndef SMP static struct globaldata __globaldata; #endif struct mtx sched_lock; struct mtx Giant; static void cpu_startup(dummy) void *dummy; { register unsigned i; register caddr_t v; vm_offset_t maxaddr; vm_size_t size = 0; int firstaddr; vm_offset_t minaddr; int physmem_est; /* * Good {morning,afternoon,evening,night}. */ mtx_lock(&vm_mtx); earlysetcpuclass(); startrtclock(); printcpuinfo(); panicifcpuunsupported(); #ifdef PERFMON perfmon_init(); #endif printf("real memory = %u (%uK bytes)\n", ptoa(Maxmem), ptoa(Maxmem) / 1024); /* * Display any holes after the first chunk of extended memory. */ if (bootverbose) { int indx; printf("Physical memory chunk(s):\n"); for (indx = 0; phys_avail[indx + 1] != 0; indx += 2) { unsigned int size1 = phys_avail[indx + 1] - phys_avail[indx]; printf("0x%08x - 0x%08x, %u bytes (%u pages)\n", phys_avail[indx], phys_avail[indx + 1] - 1, size1, size1 / PAGE_SIZE); } } /* * Calculate callout wheel size */ for (callwheelsize = 1, callwheelbits = 0; callwheelsize < ncallout; callwheelsize <<= 1, ++callwheelbits) ; callwheelmask = callwheelsize - 1; /* * Allocate space for system data structures. * The first available kernel virtual address is in "v". * As pages of kernel virtual memory are allocated, "v" is incremented. * As pages of memory are allocated and cleared, * "firstaddr" is incremented. * An index into the kernel page table corresponding to the * virtual memory address maintained in "v" is kept in "mapaddr". */ /* * Make two passes. The first pass calculates how much memory is * needed and allocates it. The second pass assigns virtual * addresses to the various data structures. */ firstaddr = 0; again: v = (caddr_t)firstaddr; #define valloc(name, type, num) \ (name) = (type *)v; v = (caddr_t)((name)+(num)) #define valloclim(name, type, num, lim) \ (name) = (type *)v; v = (caddr_t)((lim) = ((name)+(num))) valloc(callout, struct callout, ncallout); valloc(callwheel, struct callout_tailq, callwheelsize); /* * Discount the physical memory larger than the size of kernel_map * to avoid eating up all of KVA space. */ if (kernel_map->first_free == NULL) { printf("Warning: no free entries in kernel_map.\n"); physmem_est = physmem; } else physmem_est = min(physmem, kernel_map->max_offset - kernel_map->min_offset); /* * The nominal buffer size (and minimum KVA allocation) is BKVASIZE. * For the first 64MB of ram nominally allocate sufficient buffers to * cover 1/4 of our ram. Beyond the first 64MB allocate additional * buffers to cover 1/20 of our ram over 64MB. * * factor represents the 1/4 x ram conversion. */ if (nbuf == 0) { int factor = 4 * BKVASIZE / PAGE_SIZE; nbuf = 50; if (physmem_est > 1024) nbuf += min((physmem_est - 1024) / factor, 16384 / factor); if (physmem_est > 16384) nbuf += (physmem_est - 16384) * 2 / (factor * 5); } /* * Do not allow the buffer_map to be more then 1/2 the size of the * kernel_map. */ if (nbuf > (kernel_map->max_offset - kernel_map->min_offset) / (BKVASIZE * 2)) { nbuf = (kernel_map->max_offset - kernel_map->min_offset) / (BKVASIZE * 2); printf("Warning: nbufs capped at %d\n", nbuf); } nswbuf = max(min(nbuf/4, 256), 16); valloc(swbuf, struct buf, nswbuf); valloc(buf, struct buf, nbuf); v = bufhashinit(v); /* * End of first pass, size has been calculated so allocate memory */ if (firstaddr == 0) { size = (vm_size_t)(v - firstaddr); firstaddr = (int)kmem_alloc(kernel_map, round_page(size)); if (firstaddr == 0) panic("startup: no room for tables"); goto again; } /* * End of second pass, addresses have been assigned */ if ((vm_size_t)(v - firstaddr) != size) panic("startup: table size inconsistency"); clean_map = kmem_suballoc(kernel_map, &clean_sva, &clean_eva, (nbuf*BKVASIZE) + (nswbuf*MAXPHYS) + pager_map_size); buffer_map = kmem_suballoc(clean_map, &buffer_sva, &buffer_eva, (nbuf*BKVASIZE)); buffer_map->system_map = 1; pager_map = kmem_suballoc(clean_map, &pager_sva, &pager_eva, (nswbuf*MAXPHYS) + pager_map_size); pager_map->system_map = 1; exec_map = kmem_suballoc(kernel_map, &minaddr, &maxaddr, (16*(ARG_MAX+(PAGE_SIZE*3)))); mtx_unlock(&vm_mtx); /* * XXX: Mbuf system machine-specific initializations should * go here, if anywhere. */ /* * Initialize callouts */ SLIST_INIT(&callfree); for (i = 0; i < ncallout; i++) { callout_init(&callout[i], 0); callout[i].c_flags = CALLOUT_LOCAL_ALLOC; SLIST_INSERT_HEAD(&callfree, &callout[i], c_links.sle); } for (i = 0; i < callwheelsize; i++) { TAILQ_INIT(&callwheel[i]); } mtx_init(&callout_lock, "callout", MTX_SPIN | MTX_RECURSE); #if defined(USERCONFIG) userconfig(); cninit(); /* the preferred console may have changed */ #endif printf("avail memory = %u (%uK bytes)\n", ptoa(cnt.v_free_count), ptoa(cnt.v_free_count) / 1024); /* * Set up buffers, so they can be used to read disk labels. */ bufinit(); vm_pager_bufferinit(); globaldata_register(GLOBALDATA); #ifndef SMP /* For SMP, we delay the cpu_setregs() until after SMP startup. */ cpu_setregs(); #endif } /* * Send an interrupt to process. * * Stack is set up to allow sigcode stored * at top to call routine, followed by kcall * to sigreturn routine below. After sigreturn * resets the signal mask, the stack, and the * frame pointer, it returns to the user * specified pc, psl. */ static void osendsig(catcher, sig, mask, code) sig_t catcher; int sig; sigset_t *mask; u_long code; { struct osigframe sf; struct osigframe *fp; struct proc *p; struct sigacts *psp; struct trapframe *regs; int oonstack; p = curproc; PROC_LOCK(p); psp = p->p_sigacts; regs = p->p_md.md_regs; oonstack = sigonstack(regs->tf_esp); /* Allocate and validate space for the signal handler context. */ if ((p->p_flag & P_ALTSTACK) && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { fp = (struct osigframe *)(p->p_sigstk.ss_sp + p->p_sigstk.ss_size - sizeof(struct osigframe)); #if defined(COMPAT_43) || defined(COMPAT_SUNOS) p->p_sigstk.ss_flags |= SS_ONSTACK; #endif } else fp = (struct osigframe *)regs->tf_esp - 1; PROC_UNLOCK(p); /* * grow_stack() will return 0 if *fp does not fit inside the stack * and the stack can not be grown. * useracc() will return FALSE if access is denied. */ if (grow_stack(p, (int)fp) == 0 || !useracc((caddr_t)fp, sizeof(*fp), VM_PROT_WRITE)) { /* * Process has trashed its stack; give it an illegal * instruction to halt it in its tracks. */ PROC_LOCK(p); SIGACTION(p, SIGILL) = SIG_DFL; SIGDELSET(p->p_sigignore, SIGILL); SIGDELSET(p->p_sigcatch, SIGILL); SIGDELSET(p->p_sigmask, SIGILL); psignal(p, SIGILL); PROC_UNLOCK(p); return; } /* Translate the signal if appropriate. */ if (p->p_sysent->sv_sigtbl && sig <= p->p_sysent->sv_sigsize) sig = p->p_sysent->sv_sigtbl[_SIG_IDX(sig)]; /* Build the argument list for the signal handler. */ sf.sf_signum = sig; sf.sf_scp = (register_t)&fp->sf_siginfo.si_sc; PROC_LOCK(p); if (SIGISMEMBER(p->p_sigacts->ps_siginfo, sig)) { /* Signal handler installed with SA_SIGINFO. */ sf.sf_arg2 = (register_t)&fp->sf_siginfo; sf.sf_siginfo.si_signo = sig; sf.sf_siginfo.si_code = code; sf.sf_ahu.sf_action = (__osiginfohandler_t *)catcher; } else { /* Old FreeBSD-style arguments. */ sf.sf_arg2 = code; sf.sf_addr = regs->tf_err; sf.sf_ahu.sf_handler = catcher; } PROC_UNLOCK(p); /* Save most if not all of trap frame. */ sf.sf_siginfo.si_sc.sc_eax = regs->tf_eax; sf.sf_siginfo.si_sc.sc_ebx = regs->tf_ebx; sf.sf_siginfo.si_sc.sc_ecx = regs->tf_ecx; sf.sf_siginfo.si_sc.sc_edx = regs->tf_edx; sf.sf_siginfo.si_sc.sc_esi = regs->tf_esi; sf.sf_siginfo.si_sc.sc_edi = regs->tf_edi; sf.sf_siginfo.si_sc.sc_cs = regs->tf_cs; sf.sf_siginfo.si_sc.sc_ds = regs->tf_ds; sf.sf_siginfo.si_sc.sc_ss = regs->tf_ss; sf.sf_siginfo.si_sc.sc_es = regs->tf_es; sf.sf_siginfo.si_sc.sc_fs = regs->tf_fs; sf.sf_siginfo.si_sc.sc_gs = rgs(); sf.sf_siginfo.si_sc.sc_isp = regs->tf_isp; /* Build the signal context to be used by osigreturn(). */ sf.sf_siginfo.si_sc.sc_onstack = (oonstack) ? 1 : 0; SIG2OSIG(*mask, sf.sf_siginfo.si_sc.sc_mask); sf.sf_siginfo.si_sc.sc_sp = regs->tf_esp; sf.sf_siginfo.si_sc.sc_fp = regs->tf_ebp; sf.sf_siginfo.si_sc.sc_pc = regs->tf_eip; sf.sf_siginfo.si_sc.sc_ps = regs->tf_eflags; sf.sf_siginfo.si_sc.sc_trapno = regs->tf_trapno; sf.sf_siginfo.si_sc.sc_err = regs->tf_err; /* * If we're a vm86 process, we want to save the segment registers. * We also change eflags to be our emulated eflags, not the actual * eflags. */ if (regs->tf_eflags & PSL_VM) { /* XXX confusing names: `tf' isn't a trapframe; `regs' is. */ struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86 = &p->p_addr->u_pcb.pcb_ext->ext_vm86; sf.sf_siginfo.si_sc.sc_gs = tf->tf_vm86_gs; sf.sf_siginfo.si_sc.sc_fs = tf->tf_vm86_fs; sf.sf_siginfo.si_sc.sc_es = tf->tf_vm86_es; sf.sf_siginfo.si_sc.sc_ds = tf->tf_vm86_ds; if (vm86->vm86_has_vme == 0) sf.sf_siginfo.si_sc.sc_ps = (tf->tf_eflags & ~(PSL_VIF | PSL_VIP)) | (vm86->vm86_eflags & (PSL_VIF | PSL_VIP)); /* See sendsig() for comments. */ tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_T | PSL_VIF | PSL_VIP); } /* Copy the sigframe out to the user's stack. */ if (copyout(&sf, fp, sizeof(*fp)) != 0) { /* * Something is wrong with the stack pointer. * ...Kill the process. */ PROC_LOCK(p); sigexit(p, SIGILL); /* NOTREACHED */ } regs->tf_esp = (int)fp; regs->tf_eip = PS_STRINGS - szosigcode; regs->tf_cs = _ucodesel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_fs = _udatasel; load_gs(_udatasel); regs->tf_ss = _udatasel; } void sendsig(catcher, sig, mask, code) sig_t catcher; int sig; sigset_t *mask; u_long code; { struct sigframe sf; struct proc *p; struct sigacts *psp; struct trapframe *regs; struct sigframe *sfp; int oonstack; p = curproc; PROC_LOCK(p); psp = p->p_sigacts; if (SIGISMEMBER(psp->ps_osigset, sig)) { PROC_UNLOCK(p); osendsig(catcher, sig, mask, code); return; } regs = p->p_md.md_regs; oonstack = sigonstack(regs->tf_esp); /* Save user context. */ bzero(&sf, sizeof(sf)); sf.sf_uc.uc_sigmask = *mask; sf.sf_uc.uc_stack = p->p_sigstk; sf.sf_uc.uc_stack.ss_flags = (p->p_flag & P_ALTSTACK) ? ((oonstack) ? SS_ONSTACK : 0) : SS_DISABLE; sf.sf_uc.uc_mcontext.mc_onstack = (oonstack) ? 1 : 0; sf.sf_uc.uc_mcontext.mc_gs = rgs(); bcopy(regs, &sf.sf_uc.uc_mcontext.mc_fs, sizeof(*regs)); /* Allocate and validate space for the signal handler context. */ if ((p->p_flag & P_ALTSTACK) != 0 && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { sfp = (struct sigframe *)(p->p_sigstk.ss_sp + p->p_sigstk.ss_size - sizeof(struct sigframe)); #if defined(COMPAT_43) || defined(COMPAT_SUNOS) p->p_sigstk.ss_flags |= SS_ONSTACK; #endif } else sfp = (struct sigframe *)regs->tf_esp - 1; PROC_UNLOCK(p); /* * grow_stack() will return 0 if *sfp does not fit inside the stack * and the stack can not be grown. * useracc() will return FALSE if access is denied. */ if (grow_stack(p, (int)sfp) == 0 || !useracc((caddr_t)sfp, sizeof(*sfp), VM_PROT_WRITE)) { /* * Process has trashed its stack; give it an illegal * instruction to halt it in its tracks. */ #ifdef DEBUG printf("process %d has trashed its stack\n", p->p_pid); #endif PROC_LOCK(p); SIGACTION(p, SIGILL) = SIG_DFL; SIGDELSET(p->p_sigignore, SIGILL); SIGDELSET(p->p_sigcatch, SIGILL); SIGDELSET(p->p_sigmask, SIGILL); psignal(p, SIGILL); PROC_UNLOCK(p); return; } /* Translate the signal if appropriate. */ if (p->p_sysent->sv_sigtbl && sig <= p->p_sysent->sv_sigsize) sig = p->p_sysent->sv_sigtbl[_SIG_IDX(sig)]; /* Build the argument list for the signal handler. */ sf.sf_signum = sig; sf.sf_ucontext = (register_t)&sfp->sf_uc; PROC_LOCK(p); if (SIGISMEMBER(p->p_sigacts->ps_siginfo, sig)) { /* Signal handler installed with SA_SIGINFO. */ sf.sf_siginfo = (register_t)&sfp->sf_si; sf.sf_ahu.sf_action = (__siginfohandler_t *)catcher; /* Fill siginfo structure. */ sf.sf_si.si_signo = sig; sf.sf_si.si_code = code; sf.sf_si.si_addr = (void *)regs->tf_err; } else { /* Old FreeBSD-style arguments. */ sf.sf_siginfo = code; sf.sf_addr = regs->tf_err; sf.sf_ahu.sf_handler = catcher; } PROC_UNLOCK(p); /* * If we're a vm86 process, we want to save the segment registers. * We also change eflags to be our emulated eflags, not the actual * eflags. */ if (regs->tf_eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86 = &p->p_addr->u_pcb.pcb_ext->ext_vm86; sf.sf_uc.uc_mcontext.mc_gs = tf->tf_vm86_gs; sf.sf_uc.uc_mcontext.mc_fs = tf->tf_vm86_fs; sf.sf_uc.uc_mcontext.mc_es = tf->tf_vm86_es; sf.sf_uc.uc_mcontext.mc_ds = tf->tf_vm86_ds; if (vm86->vm86_has_vme == 0) sf.sf_uc.uc_mcontext.mc_eflags = (tf->tf_eflags & ~(PSL_VIF | PSL_VIP)) | (vm86->vm86_eflags & (PSL_VIF | PSL_VIP)); /* * We should never have PSL_T set when returning from vm86 * mode. It may be set here if we deliver a signal before * getting to vm86 mode, so turn it off. * * Clear PSL_NT to inhibit T_TSSFLT faults on return from * syscalls made by the signal handler. This just avoids * wasting time for our lazy fixup of such faults. PSL_NT * does nothing in vm86 mode, but vm86 programs can set it * almost legitimately in probes for old cpu types. */ tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_T | PSL_VIF | PSL_VIP); } /* Copy the sigframe out to the user's stack. */ if (copyout(&sf, sfp, sizeof(*sfp)) != 0) { /* * Something is wrong with the stack pointer. * ...Kill the process. */ PROC_LOCK(p); sigexit(p, SIGILL); /* NOTREACHED */ } regs->tf_esp = (int)sfp; regs->tf_eip = PS_STRINGS - *(p->p_sysent->sv_szsigcode); regs->tf_cs = _ucodesel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_fs = _udatasel; regs->tf_ss = _udatasel; } /* * System call to cleanup state after a signal * has been taken. Reset signal mask and * stack state from context left by sendsig (above). * 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 * state to gain improper privileges. */ int osigreturn(p, uap) struct proc *p; struct osigreturn_args /* { struct osigcontext *sigcntxp; } */ *uap; { struct trapframe *regs; struct osigcontext *scp; int eflags; regs = p->p_md.md_regs; scp = uap->sigcntxp; if (!useracc((caddr_t)scp, sizeof(*scp), VM_PROT_READ)) return (EFAULT); eflags = scp->sc_ps; if (eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86; /* * if pcb_ext == 0 or vm86_inited == 0, the user hasn't * set up the vm86 area, and we can't enter vm86 mode. */ if (p->p_addr->u_pcb.pcb_ext == 0) return (EINVAL); vm86 = &p->p_addr->u_pcb.pcb_ext->ext_vm86; if (vm86->vm86_inited == 0) return (EINVAL); /* Go back to user mode if both flags are set. */ if ((eflags & PSL_VIP) && (eflags & PSL_VIF)) trapsignal(p, SIGBUS, 0); if (vm86->vm86_has_vme) { eflags = (tf->tf_eflags & ~VME_USERCHANGE) | (eflags & VME_USERCHANGE) | PSL_VM; } else { vm86->vm86_eflags = eflags; /* save VIF, VIP */ eflags = (tf->tf_eflags & ~VM_USERCHANGE) | (eflags & VM_USERCHANGE) | PSL_VM; } tf->tf_vm86_ds = scp->sc_ds; tf->tf_vm86_es = scp->sc_es; tf->tf_vm86_fs = scp->sc_fs; tf->tf_vm86_gs = scp->sc_gs; tf->tf_ds = _udatasel; tf->tf_es = _udatasel; tf->tf_fs = _udatasel; } else { /* * Don't allow users to change privileged or reserved flags. */ /* * XXX do allow users to change the privileged flag PSL_RF. * The cpu sets PSL_RF in tf_eflags for faults. Debuggers * should sometimes set it there too. tf_eflags is kept in * the signal context during signal handling and there is no * other place to remember it, so the PSL_RF bit may be * corrupted by the signal handler without us knowing. * Corruption of the PSL_RF bit at worst causes one more or * one less debugger trap, so allowing it is fairly harmless. */ if (!EFL_SECURE(eflags & ~PSL_RF, regs->tf_eflags & ~PSL_RF)) { return (EINVAL); } /* * Don't allow users to load a valid privileged %cs. Let the * hardware check for invalid selectors, excess privilege in * other selectors, invalid %eip's and invalid %esp's. */ if (!CS_SECURE(scp->sc_cs)) { trapsignal(p, SIGBUS, T_PROTFLT); return (EINVAL); } regs->tf_ds = scp->sc_ds; regs->tf_es = scp->sc_es; regs->tf_fs = scp->sc_fs; } /* Restore remaining registers. */ regs->tf_eax = scp->sc_eax; regs->tf_ebx = scp->sc_ebx; regs->tf_ecx = scp->sc_ecx; regs->tf_edx = scp->sc_edx; regs->tf_esi = scp->sc_esi; regs->tf_edi = scp->sc_edi; regs->tf_cs = scp->sc_cs; regs->tf_ss = scp->sc_ss; regs->tf_isp = scp->sc_isp; PROC_LOCK(p); #if defined(COMPAT_43) || defined(COMPAT_SUNOS) if (scp->sc_onstack & 1) p->p_sigstk.ss_flags |= SS_ONSTACK; else p->p_sigstk.ss_flags &= ~SS_ONSTACK; #endif SIGSETOLD(p->p_sigmask, scp->sc_mask); SIG_CANTMASK(p->p_sigmask); PROC_UNLOCK(p); regs->tf_ebp = scp->sc_fp; regs->tf_esp = scp->sc_sp; regs->tf_eip = scp->sc_pc; regs->tf_eflags = eflags; return (EJUSTRETURN); } int sigreturn(p, uap) struct proc *p; struct sigreturn_args /* { ucontext_t *sigcntxp; } */ *uap; { struct trapframe *regs; ucontext_t *ucp; int cs, eflags; ucp = uap->sigcntxp; if (!useracc((caddr_t)ucp, sizeof(struct osigcontext), VM_PROT_READ)) return (EFAULT); if (((struct osigcontext *)ucp)->sc_trapno == 0x01d516) return (osigreturn(p, (struct osigreturn_args *)uap)); /* * Since ucp is not an osigcontext but a ucontext_t, we have to * check again if all of it is accessible. A ucontext_t is * much larger, so instead of just checking for the pointer * being valid for the size of an osigcontext, now check for * it being valid for a whole, new-style ucontext_t. */ if (!useracc((caddr_t)ucp, sizeof(*ucp), VM_PROT_READ)) return (EFAULT); regs = p->p_md.md_regs; eflags = ucp->uc_mcontext.mc_eflags; if (eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86; /* * if pcb_ext == 0 or vm86_inited == 0, the user hasn't * set up the vm86 area, and we can't enter vm86 mode. */ if (p->p_addr->u_pcb.pcb_ext == 0) return (EINVAL); vm86 = &p->p_addr->u_pcb.pcb_ext->ext_vm86; if (vm86->vm86_inited == 0) return (EINVAL); /* Go back to user mode if both flags are set. */ if ((eflags & PSL_VIP) && (eflags & PSL_VIF)) trapsignal(p, SIGBUS, 0); if (vm86->vm86_has_vme) { eflags = (tf->tf_eflags & ~VME_USERCHANGE) | (eflags & VME_USERCHANGE) | PSL_VM; } else { vm86->vm86_eflags = eflags; /* save VIF, VIP */ eflags = (tf->tf_eflags & ~VM_USERCHANGE) | (eflags & VM_USERCHANGE) | PSL_VM; } bcopy(&ucp->uc_mcontext.mc_fs, tf, sizeof(struct trapframe)); tf->tf_eflags = eflags; tf->tf_vm86_ds = tf->tf_ds; tf->tf_vm86_es = tf->tf_es; tf->tf_vm86_fs = tf->tf_fs; tf->tf_vm86_gs = ucp->uc_mcontext.mc_gs; tf->tf_ds = _udatasel; tf->tf_es = _udatasel; tf->tf_fs = _udatasel; } else { /* * Don't allow users to change privileged or reserved flags. */ /* * XXX do allow users to change the privileged flag PSL_RF. * The cpu sets PSL_RF in tf_eflags for faults. Debuggers * should sometimes set it there too. tf_eflags is kept in * the signal context during signal handling and there is no * other place to remember it, so the PSL_RF bit may be * corrupted by the signal handler without us knowing. * Corruption of the PSL_RF bit at worst causes one more or * one less debugger trap, so allowing it is fairly harmless. */ if (!EFL_SECURE(eflags & ~PSL_RF, regs->tf_eflags & ~PSL_RF)) { printf("sigreturn: eflags = 0x%x\n", eflags); return (EINVAL); } /* * Don't allow users to load a valid privileged %cs. Let the * hardware check for invalid selectors, excess privilege in * other selectors, invalid %eip's and invalid %esp's. */ cs = ucp->uc_mcontext.mc_cs; if (!CS_SECURE(cs)) { printf("sigreturn: cs = 0x%x\n", cs); trapsignal(p, SIGBUS, T_PROTFLT); return (EINVAL); } bcopy(&ucp->uc_mcontext.mc_fs, regs, sizeof(*regs)); } PROC_LOCK(p); #if defined(COMPAT_43) || defined(COMPAT_SUNOS) if (ucp->uc_mcontext.mc_onstack & 1) p->p_sigstk.ss_flags |= SS_ONSTACK; else p->p_sigstk.ss_flags &= ~SS_ONSTACK; #endif p->p_sigmask = ucp->uc_sigmask; SIG_CANTMASK(p->p_sigmask); PROC_UNLOCK(p); return (EJUSTRETURN); } /* * Machine dependent boot() routine * * I haven't seen anything to put here yet * Possibly some stuff might be grafted back here from boot() */ void cpu_boot(int howto) { } /* * Shutdown the CPU as much as possible */ void cpu_halt(void) { for (;;) __asm__ ("hlt"); } /* * Hook to idle the CPU when possible. This currently only works in * the !SMP case, as there is no clean way to ensure that a CPU will be * woken when there is work available for it. */ static int cpu_idle_hlt = 1; SYSCTL_INT(_machdep, OID_AUTO, cpu_idle_hlt, CTLFLAG_RW, &cpu_idle_hlt, 0, "Idle loop HLT enable"); /* * Note that we have to be careful here to avoid a race between checking * procrunnable() and actually halting. If we don't do this, we may waste * the time between calling hlt and the next interrupt even though there * is a runnable process. */ void cpu_idle(void) { #ifndef SMP if (cpu_idle_hlt) { disable_intr(); if (procrunnable()) enable_intr(); else { enable_intr(); __asm __volatile("hlt"); } } #endif } /* * Clear registers on exec */ void setregs(p, entry, stack, ps_strings) struct proc *p; u_long entry; u_long stack; u_long ps_strings; { struct trapframe *regs = p->p_md.md_regs; struct pcb *pcb = &p->p_addr->u_pcb; if (pcb->pcb_ldt) user_ldt_free(pcb); bzero((char *)regs, sizeof(struct trapframe)); regs->tf_eip = entry; regs->tf_esp = stack; regs->tf_eflags = PSL_USER | (regs->tf_eflags & PSL_T); regs->tf_ss = _udatasel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_fs = _udatasel; regs->tf_cs = _ucodesel; /* PS_STRINGS value for BSD/OS binaries. It is 0 for non-BSD/OS. */ regs->tf_ebx = ps_strings; /* reset %gs as well */ if (pcb == PCPU_GET(curpcb)) load_gs(_udatasel); else pcb->pcb_gs = _udatasel; /* * Reset the hardware debug registers if they were in use. * They won't have any meaning for the newly exec'd process. */ if (pcb->pcb_flags & PCB_DBREGS) { pcb->pcb_dr0 = 0; pcb->pcb_dr1 = 0; pcb->pcb_dr2 = 0; pcb->pcb_dr3 = 0; pcb->pcb_dr6 = 0; pcb->pcb_dr7 = 0; if (pcb == PCPU_GET(curpcb)) { /* * Clear the debug registers on the running * CPU, otherwise they will end up affecting * the next process we switch to. */ reset_dbregs(); } pcb->pcb_flags &= ~PCB_DBREGS; } /* * Initialize the math emulator (if any) for the current process. * Actually, just clear the bit that says that the emulator has * been initialized. Initialization is delayed until the process * traps to the emulator (if it is done at all) mainly because * emulators don't provide an entry point for initialization. */ p->p_addr->u_pcb.pcb_flags &= ~FP_SOFTFP; /* * Arrange to trap the next npx or `fwait' instruction (see npx.c * for why fwait must be trapped at least if there is an npx or an * emulator). This is mainly to handle the case where npx0 is not * configured, since the npx routines normally set up the trap * otherwise. It should be done only at boot time, but doing it * here allows modifying `npx_exists' for testing the emulator on * systems with an npx. */ load_cr0(rcr0() | CR0_MP | CR0_TS); #ifdef DEV_NPX /* Initialize the npx (if any) for the current process. */ npxinit(__INITIAL_NPXCW__); #endif /* * XXX - Linux emulator * Make sure sure edx is 0x0 on entry. Linux binaries depend * on it. */ p->p_retval[1] = 0; } void cpu_setregs(void) { unsigned int cr0; cr0 = rcr0(); cr0 |= CR0_NE; /* Done by npxinit() */ cr0 |= CR0_MP | CR0_TS; /* Done at every execve() too. */ #ifndef I386_CPU cr0 |= CR0_WP | CR0_AM; #endif load_cr0(cr0); load_gs(_udatasel); } static int sysctl_machdep_adjkerntz(SYSCTL_HANDLER_ARGS) { int error; error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); if (!error && req->newptr) resettodr(); return (error); } SYSCTL_PROC(_machdep, CPU_ADJKERNTZ, adjkerntz, CTLTYPE_INT|CTLFLAG_RW, &adjkerntz, 0, sysctl_machdep_adjkerntz, "I", ""); SYSCTL_INT(_machdep, CPU_DISRTCSET, disable_rtc_set, CTLFLAG_RW, &disable_rtc_set, 0, ""); SYSCTL_STRUCT(_machdep, CPU_BOOTINFO, bootinfo, CTLFLAG_RD, &bootinfo, bootinfo, ""); SYSCTL_INT(_machdep, CPU_WALLCLOCK, wall_cmos_clock, CTLFLAG_RW, &wall_cmos_clock, 0, ""); /* * Initialize 386 and configure to run kernel */ /* * Initialize segments & interrupt table */ int _default_ldt; union descriptor gdt[NGDT * MAXCPU]; /* global descriptor table */ static struct gate_descriptor idt0[NIDT]; struct gate_descriptor *idt = &idt0[0]; /* interrupt descriptor table */ union descriptor ldt[NLDT]; /* local descriptor table */ #ifdef SMP /* table descriptors - used to load tables by microp */ struct region_descriptor r_gdt, r_idt; #endif int private_tss; /* flag indicating private tss */ #if defined(I586_CPU) && !defined(NO_F00F_HACK) extern int has_f00f_bug; #endif static struct i386tss dblfault_tss; static char dblfault_stack[PAGE_SIZE]; extern struct user *proc0paddr; /* software prototypes -- in more palatable form */ struct soft_segment_descriptor gdt_segs[] = { /* GNULL_SEL 0 Null Descriptor */ { 0x0, /* segment base address */ 0x0, /* length */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GCODE_SEL 1 Code Descriptor for kernel */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GDATA_SEL 2 Data Descriptor for kernel */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GPRIV_SEL 3 SMP Per-Processor Private Data Descriptor */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GPROC0_SEL 4 Proc 0 Tss Descriptor */ { 0x0, /* segment base address */ sizeof(struct i386tss)-1,/* length - all address space */ SDT_SYS386TSS, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GLDT_SEL 5 LDT Descriptor */ { (int) ldt, /* segment base address */ sizeof(ldt)-1, /* length - all address space */ SDT_SYSLDT, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GUSERLDT_SEL 6 User LDT Descriptor per process */ { (int) ldt, /* segment base address */ (512 * sizeof(union descriptor)-1), /* length */ SDT_SYSLDT, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GTGATE_SEL 7 Null Descriptor - Placeholder */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GBIOSLOWMEM_SEL 8 BIOS access to realmode segment 0x40, must be #8 in GDT */ { 0x400, /* segment base address */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GPANIC_SEL 9 Panic Tss Descriptor */ { (int) &dblfault_tss, /* segment base address */ sizeof(struct i386tss)-1,/* length - all address space */ SDT_SYS386TSS, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GBIOSCODE32_SEL 10 BIOS 32-bit interface (32bit Code) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSCODE16_SEL 11 BIOS 32-bit interface (16bit Code) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSDATA_SEL 12 BIOS 32-bit interface (Data) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSUTIL_SEL 13 BIOS 16-bit interface (Utility) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSARGS_SEL 14 BIOS 16-bit interface (Arguments) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, }; static struct soft_segment_descriptor ldt_segs[] = { /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Code Descriptor for user */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMERA, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Data Descriptor for user */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, }; void setidt(idx, func, typ, dpl, selec) int idx; inthand_t *func; int typ; int dpl; int selec; { struct gate_descriptor *ip; ip = idt + idx; ip->gd_looffset = (int)func; ip->gd_selector = selec; ip->gd_stkcpy = 0; ip->gd_xx = 0; ip->gd_type = typ; ip->gd_dpl = dpl; ip->gd_p = 1; ip->gd_hioffset = ((int)func)>>16 ; } #define IDTVEC(name) __CONCAT(X,name) extern inthand_t IDTVEC(div), IDTVEC(dbg), IDTVEC(nmi), IDTVEC(bpt), IDTVEC(ofl), IDTVEC(bnd), IDTVEC(ill), IDTVEC(dna), IDTVEC(fpusegm), IDTVEC(tss), IDTVEC(missing), IDTVEC(stk), IDTVEC(prot), IDTVEC(page), IDTVEC(mchk), IDTVEC(rsvd), IDTVEC(fpu), IDTVEC(align), IDTVEC(lcall_syscall), IDTVEC(int0x80_syscall); void sdtossd(sd, ssd) struct segment_descriptor *sd; struct soft_segment_descriptor *ssd; { ssd->ssd_base = (sd->sd_hibase << 24) | sd->sd_lobase; ssd->ssd_limit = (sd->sd_hilimit << 16) | sd->sd_lolimit; ssd->ssd_type = sd->sd_type; ssd->ssd_dpl = sd->sd_dpl; ssd->ssd_p = sd->sd_p; ssd->ssd_def32 = sd->sd_def32; ssd->ssd_gran = sd->sd_gran; } #define PHYSMAP_SIZE (2 * 8) /* * Populate the (physmap) array with base/bound pairs describing the * available physical memory in the system, then test this memory and * build the phys_avail array describing the actually-available memory. * * If we cannot accurately determine the physical memory map, then use * value from the 0xE801 call, and failing that, the RTC. * * Total memory size may be set by the kernel environment variable * hw.physmem or the compile-time define MAXMEM. */ static void getmemsize(int first) { int i, physmap_idx, pa_indx; u_int basemem, extmem; #ifdef PC98 int pg_n; u_int under16; #else struct vm86frame vmf; struct vm86context vmc; #endif vm_offset_t pa, physmap[PHYSMAP_SIZE]; pt_entry_t pte; const char *cp; #ifndef PC98 struct bios_smap *smap; #endif #ifdef PC98 /* XXX - some of EPSON machines can't use PG_N */ pg_n = PG_N; if (pc98_machine_type & M_EPSON_PC98) { switch (epson_machine_id) { #ifdef WB_CACHE default: #endif case 0x34: /* PC-486HX */ case 0x35: /* PC-486HG */ case 0x3B: /* PC-486HA */ pg_n = 0; break; } } #else bzero(&vmf, sizeof(struct vm86frame)); #endif bzero(physmap, sizeof(physmap)); /* * Perform "base memory" related probes & setup */ #ifdef PC98 under16 = pc98_getmemsize(&basemem, &extmem); #else vm86_intcall(0x12, &vmf); basemem = vmf.vmf_ax; #endif if (basemem > 640) { printf("Preposterous BIOS basemem of %uK, truncating to 640K\n", basemem); basemem = 640; } /* * XXX if biosbasemem is now < 640, there is a `hole' * between the end of base memory and the start of * ISA memory. The hole may be empty or it may * contain BIOS code or data. Map it read/write so * that the BIOS can write to it. (Memory from 0 to * the physical end of the kernel is mapped read-only * to begin with and then parts of it are remapped. * The parts that aren't remapped form holes that * remain read-only and are unused by the kernel. * The base memory area is below the physical end of * the kernel and right now forms a read-only hole. * The part of it from PAGE_SIZE to * (trunc_page(biosbasemem * 1024) - 1) will be * remapped and used by the kernel later.) * * This code is similar to the code used in * pmap_mapdev, but since no memory needs to be * allocated we simply change the mapping. */ for (pa = trunc_page(basemem * 1024); pa < ISA_HOLE_START; pa += PAGE_SIZE) { pte = (pt_entry_t)vtopte(pa + KERNBASE); *pte = pa | PG_RW | PG_V; } /* * if basemem != 640, map pages r/w into vm86 page table so * that the bios can scribble on it. */ pte = (pt_entry_t)vm86paddr; for (i = basemem / 4; i < 160; i++) pte[i] = (i << PAGE_SHIFT) | PG_V | PG_RW | PG_U; #ifndef PC98 /* * map page 1 R/W into the kernel page table so we can use it * as a buffer. The kernel will unmap this page later. */ pte = (pt_entry_t)vtopte(KERNBASE + (1 << PAGE_SHIFT)); *pte = (1 << PAGE_SHIFT) | PG_RW | PG_V; /* * get memory map with INT 15:E820 */ vmc.npages = 0; smap = (void *)vm86_addpage(&vmc, 1, KERNBASE + (1 << PAGE_SHIFT)); vm86_getptr(&vmc, (vm_offset_t)smap, &vmf.vmf_es, &vmf.vmf_di); physmap_idx = 0; vmf.vmf_ebx = 0; do { vmf.vmf_eax = 0xE820; vmf.vmf_edx = SMAP_SIG; vmf.vmf_ecx = sizeof(struct bios_smap); i = vm86_datacall(0x15, &vmf, &vmc); if (i || vmf.vmf_eax != SMAP_SIG) break; if (boothowto & RB_VERBOSE) printf("SMAP type=%02x base=%08x %08x len=%08x %08x\n", smap->type, *(u_int32_t *)((char *)&smap->base + 4), (u_int32_t)smap->base, *(u_int32_t *)((char *)&smap->length + 4), (u_int32_t)smap->length); if (smap->type != 0x01) goto next_run; if (smap->length == 0) goto next_run; if (smap->base >= 0xffffffff) { printf("%uK of memory above 4GB ignored\n", (u_int)(smap->length / 1024)); goto next_run; } for (i = 0; i <= physmap_idx; i += 2) { if (smap->base < physmap[i + 1]) { if (boothowto & RB_VERBOSE) printf( "Overlapping or non-montonic memory region, ignoring second region\n"); goto next_run; } } if (smap->base == physmap[physmap_idx + 1]) { physmap[physmap_idx + 1] += smap->length; goto next_run; } physmap_idx += 2; if (physmap_idx == PHYSMAP_SIZE) { printf( "Too many segments in the physical address map, giving up\n"); break; } physmap[physmap_idx] = smap->base; physmap[physmap_idx + 1] = smap->base + smap->length; next_run: } while (vmf.vmf_ebx != 0); if (physmap[1] != 0) goto physmap_done; /* * If we failed above, try memory map with INT 15:E801 */ vmf.vmf_ax = 0xE801; if (vm86_intcall(0x15, &vmf) == 0) { extmem = vmf.vmf_cx + vmf.vmf_dx * 64; } else { #if 0 vmf.vmf_ah = 0x88; vm86_intcall(0x15, &vmf); extmem = vmf.vmf_ax; #else /* * Prefer the RTC value for extended memory. */ extmem = rtcin(RTC_EXTLO) + (rtcin(RTC_EXTHI) << 8); #endif } /* * Special hack for chipsets that still remap the 384k hole when * there's 16MB of memory - this really confuses people that * are trying to use bus mastering ISA controllers with the * "16MB limit"; they only have 16MB, but the remapping puts * them beyond the limit. * * If extended memory is between 15-16MB (16-17MB phys address range), * chop it to 15MB. */ if ((extmem > 15 * 1024) && (extmem < 16 * 1024)) extmem = 15 * 1024; #endif physmap[0] = 0; physmap[1] = basemem * 1024; physmap_idx = 2; physmap[physmap_idx] = 0x100000; physmap[physmap_idx + 1] = physmap[physmap_idx] + extmem * 1024; #ifdef PC98 if ((under16 != 16 * 1024) && (extmem > 15 * 1024)) { /* 15M - 16M region is cut off, so need to divide chunk */ physmap[physmap_idx + 1] = under16 * 1024; physmap_idx += 2; physmap[physmap_idx] = 0x1000000; physmap[physmap_idx + 1] = physmap[2] + extmem * 1024; } #else physmap_done: #endif /* * Now, physmap contains a map of physical memory. */ #ifdef SMP /* make hole for AP bootstrap code */ physmap[1] = mp_bootaddress(physmap[1] / 1024); /* look for the MP hardware - needed for apic addresses */ i386_mp_probe(); #endif /* * Maxmem isn't the "maximum memory", it's one larger than the * highest page of the physical address space. It should be * called something like "Maxphyspage". We may adjust this * based on ``hw.physmem'' and the results of the memory test. */ Maxmem = atop(physmap[physmap_idx + 1]); #ifdef MAXMEM Maxmem = MAXMEM / 4; #endif /* * hw.physmem is a size in bytes; we also allow k, m, and g suffixes * for the appropriate modifiers. This overrides MAXMEM. */ if ((cp = getenv("hw.physmem")) != NULL) { u_int64_t AllowMem, sanity; char *ep; sanity = AllowMem = strtouq(cp, &ep, 0); if ((ep != cp) && (*ep != 0)) { switch(*ep) { case 'g': case 'G': AllowMem <<= 10; case 'm': case 'M': AllowMem <<= 10; case 'k': case 'K': AllowMem <<= 10; break; default: AllowMem = sanity = 0; } if (AllowMem < sanity) AllowMem = 0; } if (AllowMem == 0) printf("Ignoring invalid memory size of '%s'\n", cp); else Maxmem = atop(AllowMem); } if (atop(physmap[physmap_idx + 1]) != Maxmem && (boothowto & RB_VERBOSE)) printf("Physical memory use set to %uK\n", Maxmem * 4); /* * If Maxmem has been increased beyond what the system has detected, * extend the last memory segment to the new limit. */ if (atop(physmap[physmap_idx + 1]) < Maxmem) physmap[physmap_idx + 1] = ptoa(Maxmem); /* call pmap initialization to make new kernel address space */ pmap_bootstrap(first, 0); /* * Size up each available chunk of physical memory. */ physmap[0] = PAGE_SIZE; /* mask off page 0 */ pa_indx = 0; phys_avail[pa_indx++] = physmap[0]; phys_avail[pa_indx] = physmap[0]; #if 0 pte = (pt_entry_t)vtopte(KERNBASE); #else pte = (pt_entry_t)CMAP1; #endif /* * physmap is in bytes, so when converting to page boundaries, * round up the start address and round down the end address. */ for (i = 0; i <= physmap_idx; i += 2) { vm_offset_t end; end = ptoa(Maxmem); if (physmap[i + 1] < end) end = trunc_page(physmap[i + 1]); for (pa = round_page(physmap[i]); pa < end; pa += PAGE_SIZE) { int tmp, page_bad; #if 0 int *ptr = 0; #else int *ptr = (int *)CADDR1; #endif /* * block out kernel memory as not available. */ if (pa >= 0x100000 && pa < first) continue; page_bad = FALSE; /* * map page into kernel: valid, read/write,non-cacheable */ #ifdef PC98 *pte = pa | PG_V | PG_RW | pg_n; #else *pte = pa | PG_V | PG_RW | PG_N; #endif invltlb(); tmp = *(int *)ptr; /* * Test for alternating 1's and 0's */ *(volatile int *)ptr = 0xaaaaaaaa; if (*(volatile int *)ptr != 0xaaaaaaaa) { page_bad = TRUE; } /* * Test for alternating 0's and 1's */ *(volatile int *)ptr = 0x55555555; if (*(volatile int *)ptr != 0x55555555) { page_bad = TRUE; } /* * Test for all 1's */ *(volatile int *)ptr = 0xffffffff; if (*(volatile int *)ptr != 0xffffffff) { page_bad = TRUE; } /* * Test for all 0's */ *(volatile int *)ptr = 0x0; if (*(volatile int *)ptr != 0x0) { page_bad = TRUE; } /* * Restore original value. */ *(int *)ptr = tmp; /* * Adjust array of valid/good pages. */ if (page_bad == TRUE) { continue; } /* * If this good page is a continuation of the * previous set of good pages, then just increase * the end pointer. Otherwise start a new chunk. * Note that "end" points one higher than end, * making the range >= start and < end. * If we're also doing a speculative memory * test and we at or past the end, bump up Maxmem * so that we keep going. The first bad page * will terminate the loop. */ if (phys_avail[pa_indx] == pa) { phys_avail[pa_indx] += PAGE_SIZE; } else { pa_indx++; if (pa_indx == PHYS_AVAIL_ARRAY_END) { printf("Too many holes in the physical address space, giving up\n"); pa_indx--; break; } phys_avail[pa_indx++] = pa; /* start */ phys_avail[pa_indx] = pa + PAGE_SIZE; /* end */ } physmem++; } } *pte = 0; invltlb(); /* * XXX * The last chunk must contain at least one page plus the message * buffer to avoid complicating other code (message buffer address * calculation, etc.). */ while (phys_avail[pa_indx - 1] + PAGE_SIZE + round_page(MSGBUF_SIZE) >= phys_avail[pa_indx]) { physmem -= atop(phys_avail[pa_indx] - phys_avail[pa_indx - 1]); phys_avail[pa_indx--] = 0; phys_avail[pa_indx--] = 0; } Maxmem = atop(phys_avail[pa_indx]); /* Trim off space for the message buffer. */ phys_avail[pa_indx] -= round_page(MSGBUF_SIZE); avail_end = phys_avail[pa_indx]; } void init386(first) int first; { int x; struct gate_descriptor *gdp; int gsel_tss; #ifndef SMP /* table descriptors - used to load tables by microp */ struct region_descriptor r_gdt, r_idt; #endif int off; proc0.p_addr = proc0paddr; atdevbase = ISA_HOLE_START + KERNBASE; #ifdef PC98 /* * Initialize DMAC */ pc98_init_dmac(); #endif if (bootinfo.bi_modulep) { preload_metadata = (caddr_t)bootinfo.bi_modulep + KERNBASE; preload_bootstrap_relocate(KERNBASE); } else { printf("WARNING: loader(8) metadata is missing!\n"); } if (bootinfo.bi_envp) kern_envp = (caddr_t)bootinfo.bi_envp + KERNBASE; /* * make gdt memory segments, the code segment goes up to end of the * page with etext in it, the data segment goes to the end of * the address space */ /* * XXX text protection is temporarily (?) disabled. The limit was * i386_btop(round_page(etext)) - 1. */ gdt_segs[GCODE_SEL].ssd_limit = i386_btop(0) - 1; gdt_segs[GDATA_SEL].ssd_limit = i386_btop(0) - 1; #ifdef SMP gdt_segs[GPRIV_SEL].ssd_limit = i386_btop(sizeof(struct privatespace)) - 1; gdt_segs[GPRIV_SEL].ssd_base = (int) &SMP_prvspace[0]; gdt_segs[GPROC0_SEL].ssd_base = (int) &SMP_prvspace[0].globaldata.gd_common_tss; SMP_prvspace[0].globaldata.gd_prvspace = &SMP_prvspace[0].globaldata; #else gdt_segs[GPRIV_SEL].ssd_limit = i386_btop(sizeof(struct globaldata)) - 1; gdt_segs[GPRIV_SEL].ssd_base = (int) &__globaldata; gdt_segs[GPROC0_SEL].ssd_base = (int) &__globaldata.gd_common_tss; __globaldata.gd_prvspace = &__globaldata; #endif for (x = 0; x < NGDT; x++) { #ifdef BDE_DEBUGGER /* avoid overwriting db entries with APM ones */ if (x >= GAPMCODE32_SEL && x <= GAPMDATA_SEL) continue; #endif ssdtosd(&gdt_segs[x], &gdt[x].sd); } r_gdt.rd_limit = NGDT * sizeof(gdt[0]) - 1; r_gdt.rd_base = (int) gdt; lgdt(&r_gdt); /* setup curproc so that mutexes work */ PCPU_SET(curproc, &proc0); PCPU_SET(spinlocks, NULL); LIST_INIT(&proc0.p_contested); mtx_init(&sched_lock, "sched lock", MTX_SPIN | MTX_RECURSE); #ifdef SMP /* * Interrupts can happen very early, so initialize imen_mtx here, rather * than in init_locks(). */ mtx_init(&imen_mtx, "imen", MTX_SPIN); #endif /* * Giant is used early for at least debugger traps and unexpected traps. */ mtx_init(&Giant, "Giant", MTX_DEF | MTX_RECURSE); mtx_init(&proc0.p_mtx, "process lock", MTX_DEF); mtx_lock(&Giant); /* make ldt memory segments */ /* * The data segment limit must not cover the user area because we * don't want the user area to be writable in copyout() etc. (page * level protection is lost in kernel mode on 386's). Also, we * don't want the user area to be writable directly (page level * protection of the user area is not available on 486's with * CR0_WP set, because there is no user-read/kernel-write mode). * * XXX - VM_MAXUSER_ADDRESS is an end address, not a max. And it * should be spelled ...MAX_USER... */ #define VM_END_USER_RW_ADDRESS VM_MAXUSER_ADDRESS /* * The code segment limit has to cover the user area until we move * the signal trampoline out of the user area. This is safe because * the code segment cannot be written to directly. */ #define VM_END_USER_R_ADDRESS (VM_END_USER_RW_ADDRESS + UPAGES * PAGE_SIZE) ldt_segs[LUCODE_SEL].ssd_limit = i386_btop(VM_END_USER_R_ADDRESS) - 1; ldt_segs[LUDATA_SEL].ssd_limit = i386_btop(VM_END_USER_RW_ADDRESS) - 1; for (x = 0; x < sizeof ldt_segs / sizeof ldt_segs[0]; x++) ssdtosd(&ldt_segs[x], &ldt[x].sd); _default_ldt = GSEL(GLDT_SEL, SEL_KPL); lldt(_default_ldt); PCPU_SET(currentldt, _default_ldt); /* exceptions */ for (x = 0; x < NIDT; x++) setidt(x, &IDTVEC(rsvd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(0, &IDTVEC(div), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(1, &IDTVEC(dbg), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(2, &IDTVEC(nmi), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(3, &IDTVEC(bpt), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(4, &IDTVEC(ofl), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(5, &IDTVEC(bnd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(6, &IDTVEC(ill), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(7, &IDTVEC(dna), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(8, 0, SDT_SYSTASKGT, SEL_KPL, GSEL(GPANIC_SEL, SEL_KPL)); setidt(9, &IDTVEC(fpusegm), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(10, &IDTVEC(tss), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(11, &IDTVEC(missing), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(12, &IDTVEC(stk), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(13, &IDTVEC(prot), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(14, &IDTVEC(page), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(15, &IDTVEC(rsvd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(16, &IDTVEC(fpu), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(17, &IDTVEC(align), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(18, &IDTVEC(mchk), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(0x80, &IDTVEC(int0x80_syscall), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); r_idt.rd_limit = sizeof(idt0) - 1; r_idt.rd_base = (int) idt; lidt(&r_idt); /* * We need this mutex before the console probe. */ mtx_init(&clock_lock, "clk", MTX_SPIN | MTX_RECURSE); /* * Initialize the console before we print anything out. */ cninit(); #ifdef DEV_ISA isa_defaultirq(); #endif #ifdef DDB kdb_init(); if (boothowto & RB_KDB) Debugger("Boot flags requested debugger"); #endif finishidentcpu(); /* Final stage of CPU initialization */ setidt(6, &IDTVEC(ill), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(13, &IDTVEC(prot), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); initializecpu(); /* Initialize CPU registers */ /* make an initial tss so cpu can get interrupt stack on syscall! */ PCPU_SET(common_tss.tss_esp0, (int) proc0.p_addr + UPAGES*PAGE_SIZE - 16); PCPU_SET(common_tss.tss_ss0, GSEL(GDATA_SEL, SEL_KPL)); gsel_tss = GSEL(GPROC0_SEL, SEL_KPL); private_tss = 0; PCPU_SET(tss_gdt, &gdt[GPROC0_SEL].sd); PCPU_SET(common_tssd, *PCPU_GET(tss_gdt)); PCPU_SET(common_tss.tss_ioopt, (sizeof (struct i386tss)) << 16); ltr(gsel_tss); dblfault_tss.tss_esp = dblfault_tss.tss_esp0 = dblfault_tss.tss_esp1 = dblfault_tss.tss_esp2 = (int) &dblfault_stack[sizeof(dblfault_stack)]; dblfault_tss.tss_ss = dblfault_tss.tss_ss0 = dblfault_tss.tss_ss1 = dblfault_tss.tss_ss2 = GSEL(GDATA_SEL, SEL_KPL); dblfault_tss.tss_cr3 = (int)IdlePTD; dblfault_tss.tss_eip = (int) dblfault_handler; dblfault_tss.tss_eflags = PSL_KERNEL; dblfault_tss.tss_ds = dblfault_tss.tss_es = dblfault_tss.tss_gs = GSEL(GDATA_SEL, SEL_KPL); dblfault_tss.tss_fs = GSEL(GPRIV_SEL, SEL_KPL); dblfault_tss.tss_cs = GSEL(GCODE_SEL, SEL_KPL); dblfault_tss.tss_ldt = GSEL(GLDT_SEL, SEL_KPL); vm86_initialize(); getmemsize(first); /* now running on new page tables, configured,and u/iom is accessible */ /* Map the message buffer. */ for (off = 0; off < round_page(MSGBUF_SIZE); off += PAGE_SIZE) pmap_kenter((vm_offset_t)msgbufp + off, avail_end + off); msgbufinit(msgbufp, MSGBUF_SIZE); /* make a call gate to reenter kernel with */ gdp = &ldt[LSYS5CALLS_SEL].gd; x = (int) &IDTVEC(lcall_syscall); gdp->gd_looffset = x; gdp->gd_selector = GSEL(GCODE_SEL,SEL_KPL); gdp->gd_stkcpy = 1; gdp->gd_type = SDT_SYS386CGT; gdp->gd_dpl = SEL_UPL; gdp->gd_p = 1; gdp->gd_hioffset = x >> 16; /* XXX does this work? */ ldt[LBSDICALLS_SEL] = ldt[LSYS5CALLS_SEL]; ldt[LSOL26CALLS_SEL] = ldt[LSYS5CALLS_SEL]; /* transfer to user mode */ _ucodesel = LSEL(LUCODE_SEL, SEL_UPL); _udatasel = LSEL(LUDATA_SEL, SEL_UPL); /* setup proc 0's pcb */ proc0.p_addr->u_pcb.pcb_flags = 0; proc0.p_addr->u_pcb.pcb_cr3 = (int)IdlePTD; proc0.p_addr->u_pcb.pcb_ext = 0; proc0.p_md.md_regs = &proc0_tf; } #if defined(I586_CPU) && !defined(NO_F00F_HACK) static void f00f_hack(void *unused); SYSINIT(f00f_hack, SI_SUB_INTRINSIC, SI_ORDER_FIRST, f00f_hack, NULL); static void f00f_hack(void *unused) { struct gate_descriptor *new_idt; #ifndef SMP struct region_descriptor r_idt; #endif vm_offset_t tmp; if (!has_f00f_bug) return; printf("Intel Pentium detected, installing workaround for F00F bug\n"); r_idt.rd_limit = sizeof(idt0) - 1; tmp = kmem_alloc(kernel_map, PAGE_SIZE * 2); if (tmp == 0) panic("kmem_alloc returned 0"); if (((unsigned int)tmp & (PAGE_SIZE-1)) != 0) panic("kmem_alloc returned non-page-aligned memory"); /* Put the first seven entries in the lower page */ new_idt = (struct gate_descriptor*)(tmp + PAGE_SIZE - (7*8)); bcopy(idt, new_idt, sizeof(idt0)); r_idt.rd_base = (int)new_idt; lidt(&r_idt); idt = new_idt; mtx_lock(&vm_mtx); if (vm_map_protect(kernel_map, tmp, tmp + PAGE_SIZE, VM_PROT_READ, FALSE) != KERN_SUCCESS) panic("vm_map_protect failed"); mtx_unlock(&vm_mtx); return; } #endif /* defined(I586_CPU) && !NO_F00F_HACK */ int ptrace_set_pc(p, addr) struct proc *p; unsigned long addr; { p->p_md.md_regs->tf_eip = addr; return (0); } int ptrace_single_step(p) struct proc *p; { p->p_md.md_regs->tf_eflags |= PSL_T; return (0); } int ptrace_read_u_check(p, addr, len) struct proc *p; vm_offset_t addr; size_t len; { vm_offset_t gap; if ((vm_offset_t) (addr + len) < addr) return EPERM; if ((vm_offset_t) (addr + len) <= sizeof(struct user)) return 0; gap = (char *) p->p_md.md_regs - (char *) p->p_addr; if ((vm_offset_t) addr < gap) return EPERM; if ((vm_offset_t) (addr + len) <= (vm_offset_t) (gap + sizeof(struct trapframe))) return 0; return EPERM; } int ptrace_write_u(p, off, data) struct proc *p; vm_offset_t off; long data; { struct trapframe frame_copy; vm_offset_t min; struct trapframe *tp; /* * Privileged kernel state is scattered all over the user area. * Only allow write access to parts of regs and to fpregs. */ min = (char *)p->p_md.md_regs - (char *)p->p_addr; if (off >= min && off <= min + sizeof(struct trapframe) - sizeof(int)) { tp = p->p_md.md_regs; frame_copy = *tp; *(int *)((char *)&frame_copy + (off - min)) = data; if (!EFL_SECURE(frame_copy.tf_eflags, tp->tf_eflags) || !CS_SECURE(frame_copy.tf_cs)) return (EINVAL); *(int*)((char *)p->p_addr + off) = data; return (0); } min = offsetof(struct user, u_pcb) + offsetof(struct pcb, pcb_savefpu); if (off >= min && off <= min + sizeof(struct save87) - sizeof(int)) { *(int*)((char *)p->p_addr + off) = data; return (0); } return (EFAULT); } int fill_regs(p, regs) struct proc *p; struct reg *regs; { struct pcb *pcb; struct trapframe *tp; tp = p->p_md.md_regs; regs->r_fs = tp->tf_fs; regs->r_es = tp->tf_es; regs->r_ds = tp->tf_ds; regs->r_edi = tp->tf_edi; regs->r_esi = tp->tf_esi; regs->r_ebp = tp->tf_ebp; regs->r_ebx = tp->tf_ebx; regs->r_edx = tp->tf_edx; regs->r_ecx = tp->tf_ecx; regs->r_eax = tp->tf_eax; regs->r_eip = tp->tf_eip; regs->r_cs = tp->tf_cs; regs->r_eflags = tp->tf_eflags; regs->r_esp = tp->tf_esp; regs->r_ss = tp->tf_ss; pcb = &p->p_addr->u_pcb; regs->r_gs = pcb->pcb_gs; return (0); } int set_regs(p, regs) struct proc *p; struct reg *regs; { struct pcb *pcb; struct trapframe *tp; tp = p->p_md.md_regs; if (!EFL_SECURE(regs->r_eflags, tp->tf_eflags) || !CS_SECURE(regs->r_cs)) return (EINVAL); tp->tf_fs = regs->r_fs; tp->tf_es = regs->r_es; tp->tf_ds = regs->r_ds; tp->tf_edi = regs->r_edi; tp->tf_esi = regs->r_esi; tp->tf_ebp = regs->r_ebp; tp->tf_ebx = regs->r_ebx; tp->tf_edx = regs->r_edx; tp->tf_ecx = regs->r_ecx; tp->tf_eax = regs->r_eax; tp->tf_eip = regs->r_eip; tp->tf_cs = regs->r_cs; tp->tf_eflags = regs->r_eflags; tp->tf_esp = regs->r_esp; tp->tf_ss = regs->r_ss; pcb = &p->p_addr->u_pcb; pcb->pcb_gs = regs->r_gs; return (0); } int fill_fpregs(p, fpregs) struct proc *p; struct fpreg *fpregs; { bcopy(&p->p_addr->u_pcb.pcb_savefpu, fpregs, sizeof *fpregs); return (0); } int set_fpregs(p, fpregs) struct proc *p; struct fpreg *fpregs; { bcopy(fpregs, &p->p_addr->u_pcb.pcb_savefpu, sizeof *fpregs); return (0); } int fill_dbregs(p, dbregs) struct proc *p; struct dbreg *dbregs; { struct pcb *pcb; pcb = &p->p_addr->u_pcb; dbregs->dr0 = pcb->pcb_dr0; dbregs->dr1 = pcb->pcb_dr1; dbregs->dr2 = pcb->pcb_dr2; dbregs->dr3 = pcb->pcb_dr3; dbregs->dr4 = 0; dbregs->dr5 = 0; dbregs->dr6 = pcb->pcb_dr6; dbregs->dr7 = pcb->pcb_dr7; return (0); } int set_dbregs(p, dbregs) struct proc *p; struct dbreg *dbregs; { struct pcb *pcb; int i; u_int32_t mask1, mask2; /* * Don't let an illegal value for dr7 get set. Specifically, * check for undefined settings. Setting these bit patterns * result in undefined behaviour and can lead to an unexpected * TRCTRAP. */ for (i = 0, mask1 = 0x3<<16, mask2 = 0x2<<16; i < 8; i++, mask1 <<= 2, mask2 <<= 2) if ((dbregs->dr7 & mask1) == mask2) return (EINVAL); if (dbregs->dr7 & 0x0000fc00) return (EINVAL); pcb = &p->p_addr->u_pcb; /* * Don't let a process set a breakpoint that is not within the * process's address space. If a process could do this, it * could halt the system by setting a breakpoint in the kernel * (if ddb was enabled). Thus, we need to check to make sure * that no breakpoints are being enabled for addresses outside * process's address space, unless, perhaps, we were called by * uid 0. * * XXX - what about when the watched area of the user's * address space is written into from within the kernel * ... wouldn't that still cause a breakpoint to be generated * from within kernel mode? */ if (suser(p) != 0) { if (dbregs->dr7 & 0x3) { /* dr0 is enabled */ if (dbregs->dr0 >= VM_MAXUSER_ADDRESS) return (EINVAL); } if (dbregs->dr7 & (0x3<<2)) { /* dr1 is enabled */ if (dbregs->dr1 >= VM_MAXUSER_ADDRESS) return (EINVAL); } if (dbregs->dr7 & (0x3<<4)) { /* dr2 is enabled */ if (dbregs->dr2 >= VM_MAXUSER_ADDRESS) return (EINVAL); } if (dbregs->dr7 & (0x3<<6)) { /* dr3 is enabled */ if (dbregs->dr3 >= VM_MAXUSER_ADDRESS) return (EINVAL); } } pcb->pcb_dr0 = dbregs->dr0; pcb->pcb_dr1 = dbregs->dr1; pcb->pcb_dr2 = dbregs->dr2; pcb->pcb_dr3 = dbregs->dr3; pcb->pcb_dr6 = dbregs->dr6; pcb->pcb_dr7 = dbregs->dr7; pcb->pcb_flags |= PCB_DBREGS; return (0); } /* * Return > 0 if a hardware breakpoint has been hit, and the * breakpoint was in user space. Return 0, otherwise. */ int user_dbreg_trap(void) { u_int32_t dr7, dr6; /* debug registers dr6 and dr7 */ u_int32_t bp; /* breakpoint bits extracted from dr6 */ int nbp; /* number of breakpoints that triggered */ caddr_t addr[4]; /* breakpoint addresses */ int i; dr7 = rdr7(); if ((dr7 & 0x000000ff) == 0) { /* * all GE and LE bits in the dr7 register are zero, * thus the trap couldn't have been caused by the * hardware debug registers */ return 0; } nbp = 0; dr6 = rdr6(); bp = dr6 & 0x0000000f; if (!bp) { /* * None of the breakpoint bits are set meaning this * trap was not caused by any of the debug registers */ return 0; } /* * at least one of the breakpoints were hit, check to see * which ones and if any of them are user space addresses */ if (bp & 0x01) { addr[nbp++] = (caddr_t)rdr0(); } if (bp & 0x02) { addr[nbp++] = (caddr_t)rdr1(); } if (bp & 0x04) { addr[nbp++] = (caddr_t)rdr2(); } if (bp & 0x08) { addr[nbp++] = (caddr_t)rdr3(); } for (i=0; i /* * Determine the size of the transfer, and make sure it is * within the boundaries of the partition. Adjust transfer * if needed, and signal errors or early completion. */ int bounds_check_with_label(struct bio *bp, struct disklabel *lp, int wlabel) { struct partition *p = lp->d_partitions + dkpart(bp->bio_dev); int labelsect = lp->d_partitions[0].p_offset; int maxsz = p->p_size, sz = (bp->bio_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT; /* overwriting disk label ? */ /* XXX should also protect bootstrap in first 8K */ if (bp->bio_blkno + p->p_offset <= LABELSECTOR + labelsect && #if LABELSECTOR != 0 bp->bio_blkno + p->p_offset + sz > LABELSECTOR + labelsect && #endif (bp->bio_cmd == BIO_WRITE) && wlabel == 0) { bp->bio_error = EROFS; goto bad; } #if defined(DOSBBSECTOR) && defined(notyet) /* overwriting master boot record? */ if (bp->bio_blkno + p->p_offset <= DOSBBSECTOR && (bp->bio_cmd == BIO_WRITE) && wlabel == 0) { bp->bio_error = EROFS; goto bad; } #endif /* beyond partition? */ if (bp->bio_blkno < 0 || bp->bio_blkno + sz > maxsz) { /* if exactly at end of disk, return an EOF */ if (bp->bio_blkno == maxsz) { bp->bio_resid = bp->bio_bcount; return(0); } /* or truncate if part of it fits */ sz = maxsz - bp->bio_blkno; if (sz <= 0) { bp->bio_error = EINVAL; goto bad; } bp->bio_bcount = sz << DEV_BSHIFT; } bp->bio_pblkno = bp->bio_blkno + p->p_offset; return(1); bad: bp->bio_flags |= BIO_ERROR; return(-1); } #ifdef DDB /* * Provide inb() and outb() as functions. They are normally only * available as macros calling inlined functions, thus cannot be * called inside DDB. * * The actual code is stolen from , and de-inlined. */ #undef inb #undef outb /* silence compiler warnings */ u_char inb(u_int); void outb(u_int, u_char); u_char inb(u_int port) { u_char data; /* * We use %%dx and not %1 here because i/o is done at %dx and not at * %edx, while gcc generates inferior code (movw instead of movl) * if we tell it to load (u_short) port. */ __asm __volatile("inb %%dx,%0" : "=a" (data) : "d" (port)); return (data); } void outb(u_int port, u_char data) { u_char al; /* * Use an unnecessary assignment to help gcc's register allocator. * This make a large difference for gcc-1.40 and a tiny difference * for gcc-2.6.0. For gcc-1.40, al had to be ``asm("ax")'' for * best results. gcc-2.6.0 can't handle this. */ al = data; __asm __volatile("outb %0,%%dx" : : "a" (al), "d" (port)); } #endif /* DDB */ Index: head/sys/pc98/pc98/fd.c =================================================================== --- head/sys/pc98/pc98/fd.c (revision 78134) +++ head/sys/pc98/pc98/fd.c (revision 78135) @@ -1,2909 +1,2908 @@ /* * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Don Ahn. * * Libretto PCMCIA floppy support by David Horwitt (dhorwitt@ucsd.edu) * aided by the Linux floppy driver modifications from David Bateman * (dbateman@eng.uts.edu.au). * * Copyright (c) 1993, 1994 by * jc@irbs.UUCP (John Capo) * vak@zebub.msk.su (Serge Vakulenko) * ache@astral.msk.su (Andrew A. Chernov) * * Copyright (c) 1993, 1994, 1995 by * joerg_wunsch@uriah.sax.de (Joerg Wunsch) * dufault@hda.com (Peter Dufault) * * Copyright (c) 2001 Joerg Wunsch, * joerg_wunsch@uriah.sax.de (Joerg Wunsch) * * 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 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. * * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 * $FreeBSD$ * */ #include "opt_fdc.h" #include "card.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PC98 #include #include #include #include #include #else #include #include #include #include #endif /* misuse a flag to identify format operation */ /* configuration flags */ #define FDC_PRETEND_D0 (1 << 0) /* pretend drive 0 to be there */ #define FDC_NO_FIFO (1 << 2) /* do not enable FIFO */ /* internally used only, not really from CMOS: */ #define RTCFDT_144M_PRETENDED 0x1000 /* error returns for fd_cmd() */ #define FD_FAILED -1 #define FD_NOT_VALID -2 #define FDC_ERRMAX 100 /* do not log more */ #ifdef PC98 #define NUMTYPES 12 #define NUMDENS NUMTYPES #else #define NUMTYPES 17 #define NUMDENS (NUMTYPES - 7) #endif /* These defines (-1) must match index for fd_types */ #define F_TAPE_TYPE 0x020 /* bit for fd_types to indicate tape */ #define NO_TYPE 0 /* must match NO_TYPE in ft.c */ #define FD_1720 1 #define FD_1480 2 #define FD_1440 3 #define FD_1200 4 #define FD_820 5 #define FD_800 6 #define FD_720 7 #define FD_360 8 #define FD_640 9 #define FD_1232 10 #ifdef PC98 #define FD_1280 11 #define FD_1476 12 #define FDT_NONE 0 /* none present */ #define FDT_12M 1 /* 1M/640K FDD */ #define FDT_144M 2 /* 1.44M/1M/640K FDD */ #else #define FD_1480in5_25 11 #define FD_1440in5_25 12 #define FD_820in5_25 13 #define FD_800in5_25 14 #define FD_720in5_25 15 #define FD_360in5_25 16 #define FD_640in5_25 17 #endif static struct fd_type fd_types[NUMTYPES] = { #ifdef PC98 { 21,2,0xFF,0x04,82,3444,1,2,2,0x0C,2 }, /* 1.72M in 3mode */ { 18,2,0xFF,0x1B,82,2952,1,2,2,0x54,1 }, /* 1.48M in 3mode */ { 18,2,0xFF,0x1B,80,2880,1,2,2,0x54,1 }, /* 1.44M in 3mode */ { 15,2,0xFF,0x1B,80,2400,1,0,2,0x54,1 }, /* 1.2M */ { 10,2,0xFF,0x10,82,1640,1,1,2,0x30,1 }, /* 820K */ { 10,2,0xFF,0x10,80,1600,1,1,2,0x30,1 }, /* 800K */ { 9,2,0xFF,0x20,80,1440,1,1,2,0x50,1 }, /* 720K */ { 9,2,0xFF,0x20,40, 720,2,1,2,0x50,1 }, /* 360K */ { 8,2,0xFF,0x2A,80,1280,1,1,2,0x50,1 }, /* 640K */ { 8,3,0xFF,0x35,77,1232,1,0,2,0x74,1 }, /* 1.23M 1024/sec */ { 8,3,0xFF,0x35,80,1280,1,0,2,0x74,1 }, /* 1.28M 1024/sec */ { 9,3,0xFF,0x35,82,1476,1,0,2,0x47,1 }, /* 1.48M 1024/sec 9sec */ #if 0 { 10,3,0xFF,0x1B,82,1640,1,2,2,0x54,1 }, /* 1.64M in 3mode - Reserve */ #endif #else { 21,2,0xFF,0x04,82,3444,1,FDC_500KBPS,2,0x0C,2 }, /* 1.72M in HD 3.5in */ { 18,2,0xFF,0x1B,82,2952,1,FDC_500KBPS,2,0x6C,1 }, /* 1.48M in HD 3.5in */ { 18,2,0xFF,0x1B,80,2880,1,FDC_500KBPS,2,0x6C,1 }, /* 1.44M in HD 3.5in */ { 15,2,0xFF,0x1B,80,2400,1,FDC_500KBPS,2,0x54,1 }, /* 1.2M in HD 5.25/3.5 */ { 10,2,0xFF,0x10,82,1640,1,FDC_250KBPS,2,0x2E,1 }, /* 820K in HD 3.5in */ { 10,2,0xFF,0x10,80,1600,1,FDC_250KBPS,2,0x2E,1 }, /* 800K in HD 3.5in */ { 9,2,0xFF,0x20,80,1440,1,FDC_250KBPS,2,0x50,1 }, /* 720K in HD 3.5in */ { 9,2,0xFF,0x2A,40, 720,1,FDC_250KBPS,2,0x50,1 }, /* 360K in DD 5.25in */ { 8,2,0xFF,0x2A,80,1280,1,FDC_250KBPS,2,0x50,1 }, /* 640K in DD 5.25in */ { 8,3,0xFF,0x35,77,1232,1,FDC_500KBPS,2,0x74,1 }, /* 1.23M in HD 5.25in */ { 18,2,0xFF,0x02,82,2952,1,FDC_500KBPS,2,0x02,2 }, /* 1.48M in HD 5.25in */ { 18,2,0xFF,0x02,80,2880,1,FDC_500KBPS,2,0x02,2 }, /* 1.44M in HD 5.25in */ { 10,2,0xFF,0x10,82,1640,1,FDC_300KBPS,2,0x2E,1 }, /* 820K in HD 5.25in */ { 10,2,0xFF,0x10,80,1600,1,FDC_300KBPS,2,0x2E,1 }, /* 800K in HD 5.25in */ { 9,2,0xFF,0x20,80,1440,1,FDC_300KBPS,2,0x50,1 }, /* 720K in HD 5.25in */ { 9,2,0xFF,0x23,40, 720,2,FDC_300KBPS,2,0x50,1 }, /* 360K in HD 5.25in */ { 8,2,0xFF,0x2A,80,1280,1,FDC_300KBPS,2,0x50,1 }, /* 640K in HD 5.25in */ #endif }; #ifdef PC98 #define DRVS_PER_CTLR 4 /* 4 floppies */ #else #define DRVS_PER_CTLR 2 /* 2 floppies */ #endif /***********************************************************************\ * Per controller structure. * \***********************************************************************/ static devclass_t fdc_devclass; /***********************************************************************\ * Per drive structure. * * N per controller (DRVS_PER_CTLR) * \***********************************************************************/ struct fd_data { struct fdc_data *fdc; /* pointer to controller structure */ int fdsu; /* this units number on this controller */ int type; /* Drive type (FD_1440...) */ struct fd_type *ft; /* pointer to the type descriptor */ int flags; #define FD_OPEN 0x01 /* it's open */ #define FD_ACTIVE 0x02 /* it's active */ #define FD_MOTOR 0x04 /* motor should be on */ #define FD_MOTOR_WAIT 0x08 /* motor coming up */ int skip; int hddrv; #define FD_NO_TRACK -2 int track; /* where we think the head is */ int options; /* user configurable options, see ioctl_fd.h */ struct callout_handle toffhandle; struct callout_handle tohandle; struct devstat device_stats; #ifdef PC98 int pc98_trans; #endif device_t dev; fdu_t fdu; }; struct fdc_ivars { int fdunit; }; static devclass_t fd_devclass; #ifdef EPSON_NRDISK typedef unsigned int nrd_t; #define P_NRD_ADDRH 0xc24 #define P_NRD_ADDRM 0xc22 #define P_NRD_ADDRL 0xc20 #define P_NRD_CHECK 0xc20 #define P_NRD_DATA 0xc26 #define P_NRD_LED 0xc36 #define B_NRD_CHK 0x80 #define B_NRD_LED 0x40 #define A_NRD_INFO 0x2 #define A_NRD_BASE 0x400 #define NRD_STATUS 0x0 #define NRD_ST0_HD 0x04 static fdu_t nrdu=-1; static int nrdsec=0; static nrd_t nrdblkn=0; static nrd_t nrdaddr=0x0; #define nrd_check_ready() ({ \ (epson_inb(P_NRD_CHECK) & B_NRD_CHK) ? 0 : 1; \ }) #define nrd_LED_on() epson_outb(P_NRD_LED, B_NRD_LED) #define nrd_LED_off() epson_outb(P_NRD_LED, ~B_NRD_LED) #define nrd_trac() ((int)(nrd_info(nrdaddr) & 0xff)) #define nrd_head() ((int)((nrd_info(nrdaddr) >> 8) & 0xff)) #define nrd_sec() ((int)(nrd_info(nrdaddr + 2) & 0xff)) #define nrd_secsize() ((int)((nrd_info(A_NRD_INFO) >> 8) & 0xff)) #define nrd_addrset(p) nrd_addr((nrd_t)((nrd_t)p+A_NRD_BASE)) static inline void nrd_addr(addr) nrd_t addr; { epson_outb(P_NRD_ADDRH, (u_char)((addr >> 16) & 0x1f)); epson_outb(P_NRD_ADDRM, (u_char)((addr >> 8) & 0xff)); epson_outb(P_NRD_ADDRL, (u_char)(addr & 0xff)); } static inline u_short nrd_info(addr) nrd_t addr; { u_short tmp; nrd_addr(addr); outb(0x43f, 0x42); tmp = (short)inw(P_NRD_DATA); outb(0x43f, 0x40); return ((u_short)tmp); } #endif /* EPSON_NRDISK */ /***********************************************************************\ * Throughout this file the following conventions will be used: * * fd is a pointer to the fd_data struct for the drive in question * * fdc is a pointer to the fdc_data struct for the controller * * fdu is the floppy drive unit number * * fdcu is the floppy controller unit number * * fdsu is the floppy drive unit number on that controller. (sub-unit) * \***********************************************************************/ /* needed for ft driver, thus exported */ int in_fdc(struct fdc_data *); int out_fdc(struct fdc_data *, int); /* internal functions */ static void fdc_intr(void *); static void set_motor(struct fdc_data *, int, int); # define TURNON 1 # define TURNOFF 0 static timeout_t fd_turnoff; static timeout_t fd_motor_on; static void fd_turnon(struct fd_data *); static void fdc_reset(fdc_p); static int fd_in(struct fdc_data *, int *); static void fdstart(struct fdc_data *); static timeout_t fd_iotimeout; static timeout_t fd_pseudointr; static int fdstate(struct fdc_data *); static int retrier(struct fdc_data *); static int fdformat(dev_t, struct fd_formb *, struct proc *); static int enable_fifo(fdc_p fdc); static int fifo_threshold = 8; /* XXX: should be accessible via sysctl */ #define DEVIDLE 0 #define FINDWORK 1 #define DOSEEK 2 #define SEEKCOMPLETE 3 #define IOCOMPLETE 4 #define RECALCOMPLETE 5 #define STARTRECAL 6 #define RESETCTLR 7 #define SEEKWAIT 8 #define RECALWAIT 9 #define MOTORWAIT 10 #define IOTIMEDOUT 11 #define RESETCOMPLETE 12 #define PIOREAD 13 #ifdef FDC_DEBUG static char const * const fdstates[] = { "DEVIDLE", "FINDWORK", "DOSEEK", "SEEKCOMPLETE", "IOCOMPLETE", "RECALCOMPLETE", "STARTRECAL", "RESETCTLR", "SEEKWAIT", "RECALWAIT", "MOTORWAIT", "IOTIMEDOUT", "RESETCOMPLETE", "PIOREAD", }; /* CAUTION: fd_debug causes huge amounts of logging output */ static int volatile fd_debug = 0; #define TRACE0(arg) if(fd_debug) printf(arg) #define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2) #else /* FDC_DEBUG */ #define TRACE0(arg) #define TRACE1(arg1, arg2) #endif /* FDC_DEBUG */ static void fdout_wr(fdc_p fdc, u_int8_t v) { bus_space_write_1(fdc->portt, fdc->porth, FDOUT+fdc->port_off, v); } static u_int8_t fdsts_rd(fdc_p fdc) { return bus_space_read_1(fdc->portt, fdc->porth, FDSTS+fdc->port_off); } static void fddata_wr(fdc_p fdc, u_int8_t v) { bus_space_write_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off, v); } static u_int8_t fddata_rd(fdc_p fdc) { return bus_space_read_1(fdc->portt, fdc->porth, FDDATA+fdc->port_off); } #ifndef PC98 static void fdctl_wr_isa(fdc_p fdc, u_int8_t v) { bus_space_write_1(fdc->ctlt, fdc->ctlh, 0, v); } #if NCARD > 0 static void fdctl_wr_pcmcia(fdc_p fdc, u_int8_t v) { bus_space_write_1(fdc->portt, fdc->porth, FDCTL+fdc->port_off, v); } #endif #endif /* PC98 */ #if 0 static u_int8_t fdin_rd(fdc_p fdc) { return bus_space_read_1(fdc->portt, fdc->porth, FDIN); } #endif static d_open_t Fdopen; /* NOTE, not fdopen */ static d_close_t fdclose; static d_ioctl_t fdioctl; static d_strategy_t fdstrategy; #define CDEV_MAJOR 9 static struct cdevsw fd_cdevsw = { /* open */ Fdopen, /* close */ fdclose, /* read */ physread, /* write */ physwrite, /* ioctl */ fdioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ fdstrategy, /* name */ "fd", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ D_DISK, }; static int fdc_err(struct fdc_data *fdc, const char *s) { fdc->fdc_errs++; if (s) { if (fdc->fdc_errs < FDC_ERRMAX) device_printf(fdc->fdc_dev, "%s", s); else if (fdc->fdc_errs == FDC_ERRMAX) device_printf(fdc->fdc_dev, "too many errors, not " "logging any more\n"); } return FD_FAILED; } /* * fd_cmd: Send a command to the chip. Takes a varargs with this structure: * Unit number, * # of output bytes, output bytes as ints ..., * # of input bytes, input bytes as ints ... */ static int fd_cmd(struct fdc_data *fdc, int n_out, ...) { u_char cmd; int n_in; int n; va_list ap; va_start(ap, n_out); cmd = (u_char)(va_arg(ap, int)); va_end(ap); va_start(ap, n_out); for (n = 0; n < n_out; n++) { if (out_fdc(fdc, va_arg(ap, int)) < 0) { char msg[50]; snprintf(msg, sizeof(msg), "cmd %x failed at out byte %d of %d\n", cmd, n + 1, n_out); return fdc_err(fdc, msg); } } n_in = va_arg(ap, int); for (n = 0; n < n_in; n++) { int *ptr = va_arg(ap, int *); if (fd_in(fdc, ptr) < 0) { char msg[50]; snprintf(msg, sizeof(msg), "cmd %02x failed at in byte %d of %d\n", cmd, n + 1, n_in); return fdc_err(fdc, msg); } } return 0; } static int enable_fifo(fdc_p fdc) { int i, j; if ((fdc->flags & FDC_HAS_FIFO) == 0) { /* * XXX: * Cannot use fd_cmd the normal way here, since * this might be an invalid command. Thus we send the * first byte, and check for an early turn of data directon. */ if (out_fdc(fdc, I8207X_CONFIGURE) < 0) return fdc_err(fdc, "Enable FIFO failed\n"); /* If command is invalid, return */ j = 100000; while ((i = fdsts_rd(fdc) & (NE7_DIO | NE7_RQM)) != NE7_RQM && j-- > 0) if (i == (NE7_DIO | NE7_RQM)) { fdc_reset(fdc); return FD_FAILED; } if (j<0 || fd_cmd(fdc, 3, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) { fdc_reset(fdc); return fdc_err(fdc, "Enable FIFO failed\n"); } fdc->flags |= FDC_HAS_FIFO; return 0; } if (fd_cmd(fdc, 4, I8207X_CONFIGURE, 0, (fifo_threshold - 1) & 0xf, 0, 0) < 0) return fdc_err(fdc, "Re-enable FIFO failed\n"); return 0; } static int fd_sense_drive_status(fdc_p fdc, int *st3p) { int st3; if (fd_cmd(fdc, 2, NE7CMD_SENSED, fdc->fdu, 1, &st3)) { return fdc_err(fdc, "Sense Drive Status failed\n"); } if (st3p) *st3p = st3; return 0; } static int fd_sense_int(fdc_p fdc, int *st0p, int *cylp) { int cyl, st0, ret; #ifdef EPSON_NRDISK if (fdc->fdu == nrdu) { if (fdc->fd->track >= 0) nrdaddr = (fdc->fd->track + 1) * 8; else nrdaddr = 0x0; *st0p = nrd_head() ? NRD_ST0_HD : NRD_STATUS; *cylp = nrd_trac(); } else { #endif /* EPSON_NRDISK */ ret = fd_cmd(fdc, 1, NE7CMD_SENSEI, 1, &st0); if (ret) { (void)fdc_err(fdc, "sense intr err reading stat reg 0\n"); return ret; } if (st0p) *st0p = st0; if ((st0 & NE7_ST0_IC) == NE7_ST0_IC_IV) { /* * There doesn't seem to have been an interrupt. */ return FD_NOT_VALID; } if (fd_in(fdc, &cyl) < 0) { return fdc_err(fdc, "can't get cyl num\n"); } if (cylp) *cylp = cyl; #ifdef EPSON_NRDISK } #endif /* EPSON_NRDISK */ return 0; } static int fd_read_status(fdc_p fdc, int fdsu) { int i, ret; for (i = 0; i < 7; i++) { /* * XXX types are poorly chosen. Only bytes can by read * from the hardware, but fdc->status[] wants u_ints and * fd_in() gives ints. */ int status; #ifdef EPSON_NRDISK if (fdc->fdu == nrdu) { switch (i) { case 0: fdc->status[i] = nrd_head() ? NRD_ST0_HD : NRD_STATUS; break; case 1: fdc->status[i] = NRD_STATUS; break; case 2: fdc->status[i] = NRD_STATUS; break; case 3: fdc->status[i] = nrd_trac(); break; case 4: fdc->status[i] = nrd_head(); break; case 5: fdc->status[i] = nrdsec; break; case 6: fdc->status[i] = nrd_secsize(); break; } ret = 0; } else { #endif /* EPSON_NRDISK */ ret = fd_in(fdc, &status); fdc->status[i] = status; if (ret != 0) break; #ifdef EPSON_NRDISK } #endif /* EPSON_NRDISK */ } if (ret == 0) fdc->flags |= FDC_STAT_VALID; else fdc->flags &= ~FDC_STAT_VALID; return ret; } /****************************************************************************/ /* autoconfiguration stuff */ /****************************************************************************/ #ifdef PC98 static int pc98_trans = 0; /* 0 : HD , 1 : DD , 2 : 1.44 */ static int pc98_trans_prev = 0; static void set_density(fdc_p fdc) { /* always motor on */ outb(IO_FDPORT, (pc98_trans != 1 ? FDP_FDDEXC : 0) | FDP_PORTEXC); DELAY(100); fdout_wr(fdc, FDO_RST | FDO_DMAE); /* in the case of note W, always inhibit 100ms timer */ } static int pc98_fd_check_ready(fdu_t fdu) { fd_p fd = devclass_get_softc(fd_devclass, fdu); struct fdc_data *fdc = fd->fdc; int retry = 0; #ifdef EPSON_NRDISK if (fdu == nrdu) { if (nrd_check_ready()) return 0; else return -1; } #endif while (retry++ < 30000) { set_motor(fdc, fd->fdsu, TURNON); out_fdc(fdc, NE7CMD_SENSED); /* Sense Drive Status */ DELAY(100); out_fdc(fdc, fdu); /* Drive number */ DELAY(100); if ((in_fdc(fdc) & NE7_ST3_RD)){ fdout_wr(fdc, FDO_DMAE | FDO_MTON); DELAY(10); return 0; } } return -1; } #endif static int fdc_alloc_resources(struct fdc_data *fdc) { device_t dev; int ispnp, ispcmcia; dev = fdc->fdc_dev; ispnp = (fdc->flags & FDC_ISPNP) != 0; ispcmcia = (fdc->flags & FDC_ISPCMCIA) != 0; fdc->rid_ioport = fdc->rid_irq = fdc->rid_drq = 0; fdc->res_ioport = fdc->res_irq = fdc->res_drq = 0; #ifdef PC98 fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, &fdc->rid_ioport, 0ul, ~0ul, ispnp ? 1 : IO_FDCSIZE, RF_ACTIVE); #else /* * On standard ISA, we don't just use an 8 port range * (e.g. 0x3f0-0x3f7) since that covers an IDE control * register at 0x3f6. * * Isn't PC hardware wonderful. * * The Y-E Data PCMCIA FDC doesn't have this problem, it * uses the register with offset 6 for pseudo-DMA, and the * one with offset 7 as control register. */ fdc->res_ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, &fdc->rid_ioport, 0ul, ~0ul, ispcmcia ? 8 : (ispnp ? 1 : 6), RF_ACTIVE); #endif if (fdc->res_ioport == 0) { device_printf(dev, "cannot reserve I/O port range\n"); return ENXIO; } fdc->portt = rman_get_bustag(fdc->res_ioport); fdc->porth = rman_get_bushandle(fdc->res_ioport); #ifndef PC98 if (!ispcmcia) { /* * Some BIOSen report the device at 0x3f2-0x3f5,0x3f7 * and some at 0x3f0-0x3f5,0x3f7. We detect the former * by checking the size and adjust the port address * accordingly. */ if (bus_get_resource_count(dev, SYS_RES_IOPORT, 0) == 4) fdc->port_off = -2; /* * Register the control port range as rid 1 if it * isn't there already. Most PnP BIOSen will have * already done this but non-PnP configurations don't. * * And some (!!) report 0x3f2-0x3f5 and completely * leave out the control register! It seems that some * non-antique controller chips have a different * method of programming the transfer speed which * doesn't require the control register, but it's * mighty bogus as the chip still responds to the * address for the control register. */ if (bus_get_resource_count(dev, SYS_RES_IOPORT, 1) == 0) { u_long ctlstart; /* Find the control port, usually 0x3f7 */ ctlstart = rman_get_start(fdc->res_ioport) + fdc->port_off + 7; bus_set_resource(dev, SYS_RES_IOPORT, 1, ctlstart, 1); } /* * Now (finally!) allocate the control port. */ fdc->rid_ctl = 1; fdc->res_ctl = bus_alloc_resource(dev, SYS_RES_IOPORT, &fdc->rid_ctl, 0ul, ~0ul, 1, RF_ACTIVE); if (fdc->res_ctl == 0) { device_printf(dev, "cannot reserve control I/O port range\n"); return ENXIO; } fdc->ctlt = rman_get_bustag(fdc->res_ctl); fdc->ctlh = rman_get_bushandle(fdc->res_ctl); } #endif fdc->res_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &fdc->rid_irq, 0ul, ~0ul, 1, RF_ACTIVE); if (fdc->res_irq == 0) { device_printf(dev, "cannot reserve interrupt line\n"); return ENXIO; } if ((fdc->flags & FDC_NODMA) == 0) { fdc->res_drq = bus_alloc_resource(dev, SYS_RES_DRQ, &fdc->rid_drq, 0ul, ~0ul, 1, RF_ACTIVE); if (fdc->res_drq == 0) { device_printf(dev, "cannot reserve DMA request line\n"); return ENXIO; } fdc->dmachan = fdc->res_drq->r_start; } return 0; } static void fdc_release_resources(struct fdc_data *fdc) { device_t dev; dev = fdc->fdc_dev; if (fdc->res_irq != 0) { bus_deactivate_resource(dev, SYS_RES_IRQ, fdc->rid_irq, fdc->res_irq); bus_release_resource(dev, SYS_RES_IRQ, fdc->rid_irq, fdc->res_irq); } #ifndef PC98 if (fdc->res_ctl != 0) { bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, fdc->res_ctl); bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ctl, fdc->res_ctl); } #endif if (fdc->res_ioport != 0) { bus_deactivate_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, fdc->res_ioport); bus_release_resource(dev, SYS_RES_IOPORT, fdc->rid_ioport, fdc->res_ioport); } if (fdc->res_drq != 0) { bus_deactivate_resource(dev, SYS_RES_DRQ, fdc->rid_drq, fdc->res_drq); bus_release_resource(dev, SYS_RES_DRQ, fdc->rid_drq, fdc->res_drq); } } /****************************************************************************/ /* autoconfiguration stuff */ /****************************************************************************/ static struct isa_pnp_id fdc_ids[] = { {0x0007d041, "PC standard floppy disk controller"}, /* PNP0700 */ {0x0107d041, "Standard floppy controller supporting MS Device Bay Spec"}, /* PNP0701 */ {0} }; static int fdc_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { struct fdc_ivars *ivars = device_get_ivars(child); switch (which) { case FDC_IVAR_FDUNIT: *result = ivars->fdunit; break; default: return ENOENT; } return 0; } /* * fdc controller section. */ static int fdc_probe(device_t dev) { #ifdef PC98 int error; #else int error, ic_type; #endif struct fdc_data *fdc; fdc = device_get_softc(dev); bzero(fdc, sizeof *fdc); fdc->fdc_dev = dev; #ifndef PC98 fdc->fdctl_wr = fdctl_wr_isa; #endif /* Check pnp ids */ error = ISA_PNP_PROBE(device_get_parent(dev), dev, fdc_ids); if (error == ENXIO) return ENXIO; if (error == 0) fdc->flags |= FDC_ISPNP; /* Attempt to allocate our resources for the duration of the probe */ error = fdc_alloc_resources(fdc); if (error) goto out; #ifndef PC98 /* First - lets reset the floppy controller */ fdout_wr(fdc, 0); DELAY(100); fdout_wr(fdc, FDO_FRST); #endif /* see if it can handle a command */ #ifdef PC98 if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(4, 240), NE7_SPEC_2(2, 0), 0)) { error = ENXIO; goto out; } #else if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0)) { error = ENXIO; goto out; } #endif #ifndef PC98 if (fd_cmd(fdc, 1, NE7CMD_VERSION, 1, &ic_type) == 0) { ic_type = (u_char)ic_type; switch (ic_type) { case 0x80: device_set_desc(dev, "NEC 765 or clone"); fdc->fdct = FDC_NE765; break; case 0x81: device_set_desc(dev, "Intel 82077 or clone"); fdc->fdct = FDC_I82077; break; case 0x90: device_set_desc(dev, "NEC 72065B or clone"); fdc->fdct = FDC_NE72065; break; default: device_set_desc(dev, "generic floppy controller"); fdc->fdct = FDC_UNKNOWN; break; } } #endif out: fdc_release_resources(fdc); return (error); } #if NCARD > 0 static int fdc_pccard_probe(device_t dev) { int error; struct fdc_data *fdc; fdc = device_get_softc(dev); bzero(fdc, sizeof *fdc); fdc->fdc_dev = dev; #ifndef PC98 fdc->fdctl_wr = fdctl_wr_pcmcia; #endif fdc->flags |= FDC_ISPCMCIA | FDC_NODMA; /* Attempt to allocate our resources for the duration of the probe */ error = fdc_alloc_resources(fdc); if (error) goto out; #ifndef PC98 /* First - lets reset the floppy controller */ fdout_wr(fdc, 0); DELAY(100); fdout_wr(fdc, FDO_FRST); #endif /* see if it can handle a command */ if (fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0)) { error = ENXIO; goto out; } device_set_desc(dev, "Y-E Data PCMCIA floppy"); fdc->fdct = FDC_NE765; out: fdc_release_resources(fdc); return (error); } static int fdc_pccard_detach(device_t dev) { struct fdc_data *fdc; int error; fdc = device_get_softc(dev); /* have our children detached first */ if ((error = bus_generic_detach(dev))) return (error); if ((fdc->flags & FDC_ATTACHED) == 0) { device_printf(dev, "already unloaded\n"); return (0); } fdc->flags &= ~FDC_ATTACHED; BUS_TEARDOWN_INTR(device_get_parent(dev), dev, fdc->res_irq, fdc->fdc_intr); fdc_release_resources(fdc); device_printf(dev, "unload\n"); return (0); } #endif /* NCARD > 0 */ /* * Add a child device to the fdc controller. It will then be probed etc. */ static void fdc_add_child(device_t dev, const char *name, int unit) { int disabled; struct fdc_ivars *ivar; device_t child; ivar = malloc(sizeof *ivar, M_DEVBUF /* XXX */, M_NOWAIT | M_ZERO); if (ivar == NULL) return; if (resource_int_value(name, unit, "drive", &ivar->fdunit) != 0) ivar->fdunit = 0; child = device_add_child(dev, name, unit); if (child == NULL) return; device_set_ivars(child, ivar); if (resource_int_value(name, unit, "disabled", &disabled) == 0 && disabled != 0) device_disable(child); } static int fdc_attach(device_t dev) { struct fdc_data *fdc; - int i, error; - const char *name; + int i, error, dunit; + const char *name, *dname; fdc = device_get_softc(dev); error = fdc_alloc_resources(fdc); if (error) { device_printf(dev, "cannot re-aquire resources\n"); return error; } error = BUS_SETUP_INTR(device_get_parent(dev), dev, fdc->res_irq, INTR_TYPE_BIO | INTR_ENTROPY, fdc_intr, fdc, &fdc->fdc_intr); if (error) { device_printf(dev, "cannot setup interrupt\n"); return error; } fdc->fdcu = device_get_unit(dev); fdc->flags |= FDC_ATTACHED; if ((fdc->flags & FDC_NODMA) == 0) { /* Acquire the DMA channel forever, The driver will do the rest */ /* XXX should integrate with rman */ isa_dma_acquire(fdc->dmachan); isa_dmainit(fdc->dmachan, 128 << 3 /* XXX max secsize */); } fdc->state = DEVIDLE; #ifdef PC98 /* reset controller, turn motor off, clear fdout mirror reg */ fdc_reset(fdc); #else /* reset controller, turn motor off, clear fdout mirror reg */ fdout_wr(fdc, ((fdc->fdout = 0))); #endif bioq_init(&fdc->head); /* * Probe and attach any children. We should probably detect * devices from the BIOS unless overridden. */ name = device_get_nameunit(dev); - i = -1; - while ((i = resource_query_string(i, "at", name)) != -1) - fdc_add_child(dev, resource_query_name(i), - resource_query_unit(i)); + i = 0; + while ((resource_find_match(&i, &dname, &dunit, "at", name)) == 0) + fdc_add_child(dev, dname, dunit); return (bus_generic_attach(dev)); } static int fdc_print_child(device_t me, device_t child) { int retval = 0; retval += bus_print_child_header(me, child); retval += printf(" on %s drive %d\n", device_get_nameunit(me), fdc_get_fdunit(child)); return (retval); } static device_method_t fdc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fdc_probe), DEVMETHOD(device_attach, fdc_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, fdc_print_child), DEVMETHOD(bus_read_ivar, fdc_read_ivar), /* Our children never use any other bus interface methods. */ { 0, 0 } }; static driver_t fdc_driver = { "fdc", fdc_methods, sizeof(struct fdc_data) }; DRIVER_MODULE(fdc, isa, fdc_driver, fdc_devclass, 0, 0); #if NCARD > 0 static device_method_t fdc_pccard_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fdc_pccard_probe), DEVMETHOD(device_attach, fdc_attach), DEVMETHOD(device_detach, fdc_pccard_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, fdc_print_child), DEVMETHOD(bus_read_ivar, fdc_read_ivar), /* Our children never use any other bus interface methods. */ { 0, 0 } }; static driver_t fdc_pccard_driver = { "fdc", fdc_pccard_methods, sizeof(struct fdc_data) }; DRIVER_MODULE(fdc, pccard, fdc_pccard_driver, fdc_devclass, 0, 0); #endif /* NCARD > 0 */ static void fd_clone __P((void *arg, char *name, int namelen, dev_t *dev)); static struct { char *match; int minor; int link; } fd_suffix[] = { { "a", 0, 1 }, { "b", 0, 1 }, { "c", 0, 1 }, { "d", 0, 1 }, { "e", 0, 1 }, { "f", 0, 1 }, { "g", 0, 1 }, { "h", 0, 1 }, { ".1720", 1, 0 }, { ".1480", 2, 0 }, { ".1440", 3, 0 }, { ".1200", 4, 0 }, { ".820", 5, 0 }, { ".800", 6, 0 }, { ".720", 7, 0 }, { ".360", 8, 0 }, { ".640", 9, 0 }, { ".1232", 10, 0 }, #ifdef PC98 { ".1280", 11, 0 }, { ".1476", 12, 0 }, #endif { 0, 0 } }; static void fd_clone(arg, name, namelen, dev) void *arg; char *name; int namelen; dev_t *dev; { int u, d, i; char *n; dev_t pdev; if (*dev != NODEV) return; if (dev_stdclone(name, &n, "fd", &u) != 2) return; for (i = 0; ; i++) { if (fd_suffix[i].match == NULL) return; if (strcmp(n, fd_suffix[i].match)) continue; d = fd_suffix[i].minor; break; } if (fd_suffix[i].link == 0) { *dev = make_dev(&fd_cdevsw, (u << 6) + d, UID_ROOT, GID_OPERATOR, 0640, name); } else { pdev = makedev(fd_cdevsw.d_maj, (u << 6) + d); *dev = make_dev_alias(pdev, name); } } /******************************************************************/ /* * devices attached to the controller section. */ static int fd_probe(device_t dev) { #ifdef PC98 u_int fdt; #else int i; u_int fdt, st0, st3; #endif struct fd_data *fd; struct fdc_data *fdc; fdsu_t fdsu; #ifndef PC98 static int fd_fifo = 0; #endif fdsu = *(int *)device_get_ivars(dev); /* xxx cheat a bit... */ fd = device_get_softc(dev); fdc = device_get_softc(device_get_parent(dev)); bzero(fd, sizeof *fd); fd->dev = dev; fd->fdc = fdc; fd->fdsu = fdsu; fd->fdu = device_get_unit(dev); #ifdef PC98 /* look up what bios thinks we have */ switch (fd->fdu) { case 0: case 1: case 2: case 3: if ((PC98_SYSTEM_PARAMETER(0x5ae) >> fd->fdu) & 0x01) fdt = FDT_144M; #ifdef EPSON_NRDISK else if ((PC98_SYSTEM_PARAMETER(0x55c) >> fd->fdu) & 0x01) { fdt = FDT_12M; switch (epson_machine_id) { case 0x20: case 0x27: if ((PC98_SYSTEM_PARAMETER(0x488) >> fd->fdu) & 0x01) { if (nrd_check_ready()) { nrd_LED_on(); nrdu = fd->fdu; } else { fdt = FDT_NONE; } } } } #else /* !EPSON_NRDISK */ else if ((PC98_SYSTEM_PARAMETER(0x55c) >> fd->fdu) & 0x01) { fdt = FDT_12M; switch (epson_machine_id) { case 0x20: case 0x27: if ((PC98_SYSTEM_PARAMETER(0x488) >> fd->fdu) & 0x01) fdt = FDT_NONE; } } #endif /* EPSON_NRDISK */ else fdt = FDT_NONE; break; default: fdt = FDT_NONE; break; } #else #ifdef __i386__ /* look up what bios thinks we have */ switch (fd->fdu) { case 0: if ((fdc->flags & FDC_ISPCMCIA)) fdt = RTCFDT_144M; else if (device_get_flags(fdc->fdc_dev) & FDC_PRETEND_D0) fdt = RTCFDT_144M | RTCFDT_144M_PRETENDED; else fdt = (rtcin(RTC_FDISKETTE) & 0xf0); break; case 1: fdt = ((rtcin(RTC_FDISKETTE) << 4) & 0xf0); break; default: fdt = RTCFDT_NONE; break; } #else fdt = RTCFDT_144M; /* XXX probably */ #endif #endif /* is there a unit? */ #ifdef PC98 if (fdt == FDT_NONE) return (ENXIO); #else if (fdt == RTCFDT_NONE) return (ENXIO); #endif #ifndef PC98 /* select it */ set_motor(fdc, fdsu, TURNON); DELAY(1000000); /* 1 sec */ /* XXX This doesn't work before the first set_motor() */ if (fd_fifo == 0 && fdc->fdct != FDC_NE765 && fdc->fdct != FDC_UNKNOWN && (device_get_flags(fdc->fdc_dev) & FDC_NO_FIFO) == 0 && enable_fifo(fdc) == 0) { device_printf(device_get_parent(dev), "FIFO enabled, %d bytes threshold\n", fifo_threshold); } fd_fifo = 1; if ((fd_cmd(fdc, 2, NE7CMD_SENSED, fdsu, 1, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* if at track 0, first seek inwards */ /* seek some steps: */ fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0); DELAY(300000); /* ...wait a moment... */ fd_sense_int(fdc, 0, 0); /* make ctrlr happy */ } /* If we're at track 0 first seek inwards. */ if ((fd_sense_drive_status(fdc, &st3) == 0) && (st3 & NE7_ST3_T0)) { /* Seek some steps... */ if (fd_cmd(fdc, 3, NE7CMD_SEEK, fdsu, 10, 0) == 0) { /* ...wait a moment... */ DELAY(300000); /* make ctrlr happy: */ fd_sense_int(fdc, 0, 0); } } for (i = 0; i < 2; i++) { /* * we must recalibrate twice, just in case the * heads have been beyond cylinder 76, since most * FDCs still barf when attempting to recalibrate * more than 77 steps */ /* go back to 0: */ if (fd_cmd(fdc, 2, NE7CMD_RECAL, fdsu, 0) == 0) { /* a second being enough for full stroke seek*/ DELAY(i == 0 ? 1000000 : 300000); /* anything responding? */ if (fd_sense_int(fdc, &st0, 0) == 0 && (st0 & NE7_ST0_EC) == 0) break; /* already probed succesfully */ } } set_motor(fdc, fdsu, TURNOFF); if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ return (ENXIO); #endif /* PC98 */ fd->track = FD_NO_TRACK; fd->fdc = fdc; fd->fdsu = fdsu; fd->options = 0; callout_handle_init(&fd->toffhandle); callout_handle_init(&fd->tohandle); #ifdef PC98 switch (fdt) { case FDT_144M: /* Check 3mode I/F */ fd->pc98_trans = 0; outb(0x4be, (fd->fdu << 5) | 0x10); if (!(inb(0x4be) & 0x01)) { device_set_desc(dev, "1.44M FDD"); fd->type = FD_1440; break; } printf("Warning: can't control 3mode I/F, " "fallback to 2mode.\n" "fd%d: ", fd->fdu); /* FALLTHROUGH */ case FDT_12M: #ifdef EPSON_NRDISK if (fd->fdu == nrdu) { device_set_desc(dev, "EPSON RAM DRIVE"); nrd_LED_off(); } else #endif device_set_desc(dev, "1M/640K FDD"); fd->type = FD_1200; fd->pc98_trans = 0; break; default: return (ENXIO); } #else switch (fdt) { case RTCFDT_12M: device_set_desc(dev, "1200-KB 5.25\" drive"); fd->type = FD_1200; break; case RTCFDT_144M | RTCFDT_144M_PRETENDED: device_set_desc(dev, "config-pretended 1440-MB 3.5\" drive"); fdt = RTCFDT_144M; fd->type = FD_1440; case RTCFDT_144M: device_set_desc(dev, "1440-KB 3.5\" drive"); fd->type = FD_1440; break; case RTCFDT_288M: case RTCFDT_288M_1: device_set_desc(dev, "2880-KB 3.5\" drive (in 1440-KB mode)"); fd->type = FD_1440; break; case RTCFDT_360K: device_set_desc(dev, "360-KB 5.25\" drive"); fd->type = FD_360; break; case RTCFDT_720K: printf("720-KB 3.5\" drive"); fd->type = FD_720; break; default: return (ENXIO); } #endif return (0); } static int fd_attach(device_t dev) { struct fd_data *fd; static int cdevsw_add_done = 0; fd = device_get_softc(dev); if (!cdevsw_add_done) { cdevsw_add(&fd_cdevsw); /* XXX */ cdevsw_add_done++; } EVENTHANDLER_REGISTER(dev_clone, fd_clone, 0, 1000); make_dev(&fd_cdevsw, (fd->fdu << 6), UID_ROOT, GID_OPERATOR, 0640, "fd%d", fd->fdu); /* * Export the drive to the devstat interface. */ devstat_add_entry(&fd->device_stats, device_get_name(dev), device_get_unit(dev), 512, DEVSTAT_NO_ORDERED_TAGS, DEVSTAT_TYPE_FLOPPY | DEVSTAT_TYPE_IF_OTHER, DEVSTAT_PRIORITY_FD); return (0); } static int fd_detach(device_t dev) { struct fd_data *fd; fd = device_get_softc(dev); untimeout(fd_turnoff, fd, fd->toffhandle); return (0); } static device_method_t fd_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fd_probe), DEVMETHOD(device_attach, fd_attach), DEVMETHOD(device_detach, fd_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), /* XXX */ DEVMETHOD(device_resume, bus_generic_resume), /* XXX */ { 0, 0 } }; static driver_t fd_driver = { "fd", fd_methods, sizeof(struct fd_data) }; DRIVER_MODULE(fd, fdc, fd_driver, fd_devclass, 0, 0); /****************************************************************************/ /* motor control stuff */ /* remember to not deselect the drive we're working on */ /****************************************************************************/ static void set_motor(struct fdc_data *fdc, int fdsu, int turnon) { int fdout = fdc->fdout; int needspecify = 0; #ifdef PC98 outb(IO_FDPORT, (pc98_trans != 1 ? FDP_FDDEXC : 0)|FDP_PORTEXC); DELAY(10); fdout = FDO_DMAE|FDO_MTON; #else if(turnon) { fdout &= ~FDO_FDSEL; fdout |= (FDO_MOEN0 << fdsu) + fdsu; } else fdout &= ~(FDO_MOEN0 << fdsu); if(!turnon && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0) /* gonna turn off the last drive, put FDC to bed */ fdout &= ~ (FDO_FRST|FDO_FDMAEN); else { /* make sure controller is selected and specified */ if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0) needspecify = 1; fdout |= (FDO_FRST|FDO_FDMAEN); } #endif fdout_wr(fdc, fdout); fdc->fdout = fdout; TRACE1("[0x%x->FDOUT]", fdout); if (needspecify) { /* * XXX * special case: since we have just woken up the FDC * from its sleep, we silently assume the command will * be accepted, and do not test for a timeout */ #ifdef PC98 (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(4, 240), NE7_SPEC_2(2, 0), 0); #else (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0); #endif if (fdc->flags & FDC_HAS_FIFO) (void) enable_fifo(fdc); } } static void fd_turnoff(void *xfd) { int s; fd_p fd = xfd; TRACE1("[fd%d: turnoff]", fd->fdu); s = splbio(); /* * Don't turn off the motor yet if the drive is active. * * If we got here, this could only mean we missed an interrupt. * This can e. g. happen on the Y-E Date PCMCIA floppy controller * after a controller reset. Just schedule a pseudo-interrupt * so the state machine gets re-entered. */ if (fd->fdc->state != DEVIDLE && fd->fdc->fdu == fd->fdu) { fdc_intr(fd->fdc); splx(s); return; } fd->flags &= ~FD_MOTOR; set_motor(fd->fdc, fd->fdsu, TURNOFF); splx(s); } static void fd_motor_on(void *xfd) { int s; fd_p fd = xfd; s = splbio(); fd->flags &= ~FD_MOTOR_WAIT; if((fd->fdc->fd == fd) && (fd->fdc->state == MOTORWAIT)) { fdc_intr(fd->fdc); } splx(s); } static void fd_turnon(fd_p fd) { if(!(fd->flags & FD_MOTOR)) { fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT); set_motor(fd->fdc, fd->fdsu, TURNON); timeout(fd_motor_on, fd, hz); /* in 1 sec its ok */ } } static void fdc_reset(fdc_p fdc) { /* Try a reset, keep motor on */ #ifdef PC98 set_density(fdc); if (pc98_machine_type & M_EPSON_PC98) fdout_wr(fdc, 0xe8); else fdout_wr(fdc, 0xd8); DELAY(200); fdout_wr(fdc, 0x18); DELAY(10); #else fdout_wr(fdc, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); DELAY(100); /* enable FDC, but defer interrupts a moment */ fdout_wr(fdc, fdc->fdout & ~FDO_FDMAEN); TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN); DELAY(100); fdout_wr(fdc, fdc->fdout); TRACE1("[0x%x->FDOUT]", fdc->fdout); #endif /* XXX after a reset, silently believe the FDC will accept commands */ #ifdef PC98 (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(4, 240), NE7_SPEC_2(2, 0), 0); #else (void)fd_cmd(fdc, 3, NE7CMD_SPECIFY, NE7_SPEC_1(3, 240), NE7_SPEC_2(2, 0), 0); #endif if (fdc->flags & FDC_HAS_FIFO) (void) enable_fifo(fdc); } /****************************************************************************/ /* fdc in/out */ /****************************************************************************/ int in_fdc(struct fdc_data *fdc) { int i, j = 100000; while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) if (i == NE7_RQM) return fdc_err(fdc, "ready for output in input\n"); if (j <= 0) return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); #ifdef FDC_DEBUG i = fddata_rd(fdc); TRACE1("[FDDATA->0x%x]", (unsigned char)i); return(i); #else /* !FDC_DEBUG */ return fddata_rd(fdc); #endif /* FDC_DEBUG */ } /* * fd_in: Like in_fdc, but allows you to see if it worked. */ static int fd_in(struct fdc_data *fdc, int *ptr) { int i, j = 100000; while ((i = fdsts_rd(fdc) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) if (i == NE7_RQM) return fdc_err(fdc, "ready for output in input\n"); if (j <= 0) return fdc_err(fdc, bootverbose? "input ready timeout\n": 0); #ifdef FDC_DEBUG i = fddata_rd(fdc); TRACE1("[FDDATA->0x%x]", (unsigned char)i); *ptr = i; return 0; #else /* !FDC_DEBUG */ i = fddata_rd(fdc); if (ptr) *ptr = i; return 0; #endif /* FDC_DEBUG */ } int out_fdc(struct fdc_data *fdc, int x) { int i; /* Check that the direction bit is set */ i = 100000; while ((fdsts_rd(fdc) & NE7_DIO) && i-- > 0); if (i <= 0) return fdc_err(fdc, "direction bit not set\n"); /* Check that the floppy controller is ready for a command */ i = 100000; while ((fdsts_rd(fdc) & NE7_RQM) == 0 && i-- > 0); if (i <= 0) return fdc_err(fdc, bootverbose? "output ready timeout\n": 0); /* Send the command and return */ fddata_wr(fdc, x); TRACE1("[0x%x->FDDATA]", x); return (0); } /****************************************************************************/ /* fdopen/fdclose */ /****************************************************************************/ int Fdopen(dev_t dev, int flags, int mode, struct proc *p) { fdu_t fdu = FDUNIT(minor(dev)); int type = FDTYPE(minor(dev)); fd_p fd; fdc_p fdc; /* check bounds */ if ((fd = devclass_get_softc(fd_devclass, fdu)) == 0) return (ENXIO); fdc = fd->fdc; if ((fdc == NULL) || (fd->type == NO_TYPE)) return (ENXIO); if (type > NUMDENS) return (ENXIO); #ifdef PC98 if (type == 0) type = FD_1200; /* XXX backward compatibility */ else ; /* XXX any types are OK for PC-98 */ if (pc98_fd_check_ready(fdu) == -1) return(EIO); #else if (type == 0) type = fd->type; else { /* * For each type of basic drive, make sure we are trying * to open a type it can do, */ if (type != fd->type) { switch (fd->type) { case FD_360: return (ENXIO); case FD_720: if ( type != FD_820 && type != FD_800 && type != FD_640 ) return (ENXIO); break; case FD_1200: switch (type) { case FD_1480: type = FD_1480in5_25; break; case FD_1440: type = FD_1440in5_25; break; case FD_1232: break; case FD_820: type = FD_820in5_25; break; case FD_800: type = FD_800in5_25; break; case FD_720: type = FD_720in5_25; break; case FD_640: type = FD_640in5_25; break; case FD_360: type = FD_360in5_25; break; default: return(ENXIO); } break; case FD_1440: if ( type != FD_1720 && type != FD_1480 && type != FD_1200 && type != FD_820 && type != FD_800 && type != FD_720 && type != FD_640 ) return(ENXIO); break; } } } #endif fd->ft = fd_types + type - 1; fd->flags |= FD_OPEN; return 0; } int fdclose(dev_t dev, int flags, int mode, struct proc *p) { fdu_t fdu = FDUNIT(minor(dev)); struct fd_data *fd; fd = devclass_get_softc(fd_devclass, fdu); fd->flags &= ~FD_OPEN; fd->options &= ~(FDOPT_NORETRY | FDOPT_NOERRLOG | FDOPT_NOERROR); return (0); } /****************************************************************************/ /* fdstrategy */ /****************************************************************************/ void fdstrategy(struct bio *bp) { unsigned nblocks, blknum, cando; int s; fdu_t fdu; fdc_p fdc; fd_p fd; size_t fdblk; fdu = FDUNIT(minor(bp->bio_dev)); fd = devclass_get_softc(fd_devclass, fdu); if (fd == 0) panic("fdstrategy: buf for nonexistent device (%#lx, %#lx)", (u_long)major(bp->bio_dev), (u_long)minor(bp->bio_dev)); fdc = fd->fdc; if (fd->type == NO_TYPE) { bp->bio_error = ENXIO; bp->bio_flags |= BIO_ERROR; goto bad; }; fdblk = 128 << (fd->ft->secsize); if (!(bp->bio_cmd & BIO_FORMAT)) { if (bp->bio_blkno < 0) { printf( "fd%d: fdstrat: bad request blkno = %lu, bcount = %ld\n", fdu, (u_long)bp->bio_blkno, bp->bio_bcount); bp->bio_error = EINVAL; bp->bio_flags |= BIO_ERROR; goto bad; } if ((bp->bio_bcount % fdblk) != 0) { bp->bio_error = EINVAL; bp->bio_flags |= BIO_ERROR; goto bad; } } /* * Set up block calculations. */ if (bp->bio_blkno > 20000000) { /* * Reject unreasonably high block number, prevent the * multiplication below from overflowing. */ bp->bio_error = EINVAL; bp->bio_flags |= BIO_ERROR; goto bad; } blknum = (unsigned) bp->bio_blkno * DEV_BSIZE/fdblk; nblocks = fd->ft->size; bp->bio_resid = 0; if (blknum + (bp->bio_bcount / fdblk) > nblocks) { if (blknum <= nblocks) { cando = (nblocks - blknum) * fdblk; bp->bio_resid = bp->bio_bcount - cando; if (cando == 0) goto bad; /* not actually bad but EOF */ } else { bp->bio_error = EINVAL; bp->bio_flags |= BIO_ERROR; goto bad; } } bp->bio_pblkno = bp->bio_blkno; s = splbio(); bioqdisksort(&fdc->head, bp); untimeout(fd_turnoff, fd, fd->toffhandle); /* a good idea */ /* Tell devstat we are starting on the transaction */ devstat_start_transaction(&fd->device_stats); device_busy(fd->dev); fdstart(fdc); splx(s); return; bad: biodone(bp); } /***************************************************************\ * fdstart * * We have just queued something.. if the controller is not busy * * then simulate the case where it has just finished a command * * So that it (the interrupt routine) looks on the queue for more* * work to do and picks up what we just added. * * If the controller is already busy, we need do nothing, as it * * will pick up our work when the present work completes * \***************************************************************/ static void fdstart(struct fdc_data *fdc) { int s; s = splbio(); if(fdc->state == DEVIDLE) { fdc_intr(fdc); } splx(s); } static void fd_iotimeout(void *xfdc) { fdc_p fdc; int s; fdc = xfdc; TRACE1("fd%d[fd_iotimeout()]", fdc->fdu); /* * Due to IBM's brain-dead design, the FDC has a faked ready * signal, hardwired to ready == true. Thus, any command * issued if there's no diskette in the drive will _never_ * complete, and must be aborted by resetting the FDC. * Many thanks, Big Blue! * The FDC must not be reset directly, since that would * interfere with the state machine. Instead, pretend that * the command completed but was invalid. The state machine * will reset the FDC and retry once. */ s = splbio(); fdc->status[0] = NE7_ST0_IC_IV; fdc->flags &= ~FDC_STAT_VALID; fdc->state = IOTIMEDOUT; fdc_intr(fdc); splx(s); } /* just ensure it has the right spl */ static void fd_pseudointr(void *xfdc) { int s; s = splbio(); fdc_intr(xfdc); splx(s); } /***********************************************************************\ * fdintr * * keep calling the state machine until it returns a 0 * * ALWAYS called at SPLBIO * \***********************************************************************/ static void fdc_intr(void *xfdc) { fdc_p fdc = xfdc; while(fdstate(fdc)) ; } /* * magic pseudo-DMA initialization for YE FDC. Sets count and * direction */ #define SET_BCDR(fdc,wr,cnt,port) \ bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port, \ ((cnt)-1) & 0xff); \ bus_space_write_1(fdc->portt, fdc->porth, fdc->port_off + port + 1, \ ((wr ? 0x80 : 0) | ((((cnt)-1) >> 8) & 0x7f))); /* * fdcpio(): perform programmed IO read/write for YE PCMCIA floppy */ static int fdcpio(fdc_p fdc, long flags, caddr_t addr, u_int count) { u_char *cptr = (u_char *)addr; if (flags == BIO_READ) { if (fdc->state != PIOREAD) { fdc->state = PIOREAD; return(0); }; SET_BCDR(fdc, 0, count, 0); bus_space_read_multi_1(fdc->portt, fdc->porth, fdc->port_off + FDC_YE_DATAPORT, cptr, count); } else { bus_space_write_multi_1(fdc->portt, fdc->porth, fdc->port_off + FDC_YE_DATAPORT, cptr, count); SET_BCDR(fdc, 0, count, 0); }; return(1); } /***********************************************************************\ * The controller state machine. * * if it returns a non zero value, it should be called again immediatly * \***********************************************************************/ static int fdstate(fdc_p fdc) { int read, format, head, i, sec = 0, sectrac, st0, cyl, st3, idf; unsigned blknum = 0, b_cylinder = 0; fdu_t fdu = fdc->fdu; fd_p fd; register struct bio *bp; struct fd_formb *finfo = NULL; size_t fdblk; bp = fdc->bp; if (bp == NULL) { bp = bioq_first(&fdc->head); if (bp != NULL) { bioq_remove(&fdc->head, bp); fdc->bp = bp; } } if (bp == NULL) { /***********************************************\ * nothing left for this controller to do * * Force into the IDLE state, * \***********************************************/ fdc->state = DEVIDLE; if (fdc->fd) { device_printf(fdc->fdc_dev, "unexpected valid fd pointer\n"); fdc->fd = (fd_p) 0; fdc->fdu = -1; } TRACE1("[fdc%d IDLE]", fdc->fdcu); return (0); } fdu = FDUNIT(minor(bp->bio_dev)); fd = devclass_get_softc(fd_devclass, fdu); fdblk = 128 << fd->ft->secsize; if (fdc->fd && (fd != fdc->fd)) device_printf(fd->dev, "confused fd pointers\n"); read = bp->bio_cmd == BIO_READ; if (read) idf = ISADMA_READ; else idf = ISADMA_WRITE; format = bp->bio_cmd & BIO_FORMAT; if (format) { finfo = (struct fd_formb *)bp->bio_data; fd->skip = (char *)&(finfo->fd_formb_cylno(0)) - (char *)finfo; } if (fdc->state == DOSEEK || fdc->state == SEEKCOMPLETE) { blknum = (unsigned) bp->bio_pblkno * DEV_BSIZE/fdblk + fd->skip/fdblk; b_cylinder = blknum / (fd->ft->sectrac * fd->ft->heads); } TRACE1("fd%d", fdu); TRACE1("[%s]", fdstates[fdc->state]); TRACE1("(0x%x)", fd->flags); untimeout(fd_turnoff, fd, fd->toffhandle); fd->toffhandle = timeout(fd_turnoff, fd, 4 * hz); switch (fdc->state) { case DEVIDLE: case FINDWORK: /* we have found new work */ fdc->retry = 0; fd->skip = 0; fdc->fd = fd; fdc->fdu = fdu; #ifdef PC98 pc98_trans = fd->ft->trans; if (pc98_trans_prev != pc98_trans) { int i; set_density(fdc); for (i = 0; i < 10; i++) { outb(0x5f, 0); outb(0x5f, 0); } pc98_trans_prev = pc98_trans; } if (pc98_trans != fd->pc98_trans) { if (fd->type == FD_1440) { outb(0x4be, (fdu << 5) | 0x10 | (pc98_trans >> 1)); outb(0x5f, 0); outb(0x5f, 0); } fd->pc98_trans = pc98_trans; } #else fdc->fdctl_wr(fdc, fd->ft->trans); #endif TRACE1("[0x%x->FDCTL]", fd->ft->trans); /*******************************************************\ * If the next drive has a motor startup pending, then * * it will start up in its own good time * \*******************************************************/ if(fd->flags & FD_MOTOR_WAIT) { fdc->state = MOTORWAIT; return (0); /* come back later */ } /*******************************************************\ * Maybe if it's not starting, it SHOULD be starting * \*******************************************************/ #ifdef EPSON_NRDISK if (fdu != nrdu) { if (!(fd->flags & FD_MOTOR)) { fdc->state = MOTORWAIT; fd_turnon(fdu); return(0); } else /* at least make sure we are selected */ { set_motor(fdcu, fd->fdsu, TURNON); } } #else /* !EPSON_NRDISK */ if (!(fd->flags & FD_MOTOR)) { fdc->state = MOTORWAIT; fd_turnon(fd); return (0); } else /* at least make sure we are selected */ { set_motor(fdc, fd->fdsu, TURNON); } #endif if (fdc->flags & FDC_NEEDS_RESET) { fdc->state = RESETCTLR; fdc->flags &= ~FDC_NEEDS_RESET; } else fdc->state = DOSEEK; break; case DOSEEK: if (b_cylinder == (unsigned)fd->track) { fdc->state = SEEKCOMPLETE; break; } #ifdef PC98 pc98_fd_check_ready(fdu); #endif if (fd_cmd(fdc, 3, NE7CMD_SEEK, fd->fdsu, b_cylinder * fd->ft->steptrac, 0)) { /* * seek command not accepted, looks like * the FDC went off to the Saints... */ fdc->retry = 6; /* try a reset */ return(retrier(fdc)); } fd->track = FD_NO_TRACK; fdc->state = SEEKWAIT; return(0); /* will return later */ case SEEKWAIT: /* allow heads to settle */ timeout(fd_pseudointr, fdc, hz / 16); fdc->state = SEEKCOMPLETE; return(0); /* will return later */ case SEEKCOMPLETE : /* SEEK DONE, START DMA */ /* Make sure seek really happened*/ if(fd->track == FD_NO_TRACK) { int descyl = b_cylinder * fd->ft->steptrac; do { /* * This might be a "ready changed" interrupt, * which cannot really happen since the * RDY pin is hardwired to + 5 volts. This * generally indicates a "bouncing" intr * line, so do one of the following: * * When running on an enhanced FDC that is * known to not go stuck after responding * with INVALID, fetch all interrupt states * until seeing either an INVALID or a * real interrupt condition. * * When running on a dumb old NE765, give * up immediately. The controller will * provide up to four dummy RC interrupt * conditions right after reset (for the * corresponding four drives), so this is * our only chance to get notice that it * was not the FDC that caused the interrupt. */ if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) return 0; if(fdc->fdct == FDC_NE765 && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) return 0; /* hope for a real intr */ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); if (0 == descyl) { int failed = 0; /* * seek to cyl 0 requested; make sure we are * really there */ if (fd_sense_drive_status(fdc, &st3)) failed = 1; #ifdef EPSON_NRDISK if (fdu == nrdu) st3 = NE7_ST3_T0; #endif /* EPSON_NRDISK */ if ((st3 & NE7_ST3_T0) == 0) { printf( "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n", fdu, st3, NE7_ST3BITS); failed = 1; } if (failed) { if(fdc->retry < 3) fdc->retry = 3; return (retrier(fdc)); } } #ifdef EPSON_NRDISK if (fdu == nrdu) cyl = descyl; #endif if (cyl != descyl) { printf( "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", fdu, descyl, cyl, st0); if (fdc->retry < 3) fdc->retry = 3; return (retrier(fdc)); } } fd->track = b_cylinder; #ifdef EPSON_NRDISK if (fdu != nrdu) { #endif /* EPSON_NRDISK */ if (!(fdc->flags & FDC_NODMA)) isa_dmastart(idf, bp->bio_data+fd->skip, format ? bp->bio_bcount : fdblk, fdc->dmachan); sectrac = fd->ft->sectrac; sec = blknum % (sectrac * fd->ft->heads); head = sec / sectrac; sec = sec % sectrac + 1; fd->hddrv = ((head&1)<<2)+fdu; if(format || !read) { /* make sure the drive is writable */ if(fd_sense_drive_status(fdc, &st3) != 0) { /* stuck controller? */ if (!(fdc->flags & FDC_NODMA)) isa_dmadone(idf, bp->bio_data + fd->skip, format ? bp->bio_bcount : fdblk, fdc->dmachan); fdc->retry = 6; /* reset the beast */ return (retrier(fdc)); } if(st3 & NE7_ST3_WP) { /* * XXX YES! this is ugly. * in order to force the current operation * to fail, we will have to fake an FDC * error - all error handling is done * by the retrier() */ fdc->status[0] = NE7_ST0_IC_AT; fdc->status[1] = NE7_ST1_NW; fdc->status[2] = 0; fdc->status[3] = fd->track; fdc->status[4] = head; fdc->status[5] = sec; fdc->retry = 8; /* break out immediately */ fdc->state = IOTIMEDOUT; /* not really... */ return (1); } } if (format) { if (fdc->flags & FDC_NODMA) { /* * This seems to be necessary for * whatever obscure reason; if we omit * it, we end up filling the sector ID * fields of the newly formatted track * entirely with garbage, causing * `wrong cylinder' errors all over * the place when trying to read them * back. * * Umpf. */ SET_BCDR(fdc, 1, bp->bio_bcount, 0); (void)fdcpio(fdc,bp->bio_cmd, bp->bio_data+fd->skip, bp->bio_bcount); } /* formatting */ if(fd_cmd(fdc, 6, NE7CMD_FORMAT, head << 2 | fdu, finfo->fd_formb_secshift, finfo->fd_formb_nsecs, finfo->fd_formb_gaplen, finfo->fd_formb_fillbyte, 0)) { /* controller fell over */ if (!(fdc->flags & FDC_NODMA)) isa_dmadone(idf, bp->bio_data + fd->skip, format ? bp->bio_bcount : fdblk, fdc->dmachan); fdc->retry = 6; return (retrier(fdc)); } } else { if (fdc->flags & FDC_NODMA) { /* * this seems to be necessary even when * reading data */ SET_BCDR(fdc, 1, fdblk, 0); /* * perform the write pseudo-DMA before * the WRITE command is sent */ if (!read) (void)fdcpio(fdc,bp->bio_cmd, bp->bio_data+fd->skip, fdblk); } if (fd_cmd(fdc, 9, (read ? NE7CMD_READ : NE7CMD_WRITE), head << 2 | fdu, /* head & unit */ fd->track, /* track */ head, sec, /* sector + 1 */ fd->ft->secsize, /* sector size */ sectrac, /* sectors/track */ fd->ft->gap, /* gap size */ fd->ft->datalen, /* data length */ 0)) { /* the beast is sleeping again */ if (!(fdc->flags & FDC_NODMA)) isa_dmadone(idf, bp->bio_data + fd->skip, format ? bp->bio_bcount : fdblk, fdc->dmachan); fdc->retry = 6; return (retrier(fdc)); } } if (fdc->flags & FDC_NODMA) /* * if this is a read, then simply await interrupt * before performing PIO */ if (read && !fdcpio(fdc,bp->bio_cmd, bp->bio_data+fd->skip,fdblk)) { fd->tohandle = timeout(fd_iotimeout, fdc, hz); return(0); /* will return later */ }; /* * write (or format) operation will fall through and * await completion interrupt */ fdc->state = IOCOMPLETE; fd->tohandle = timeout(fd_iotimeout, fdc, hz); return (0); /* will return later */ #ifdef EPSON_NRDISK } else { nrdblkn = (nrd_t)((unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + fd->skip/fdblk); nrd_LED_on(); nrd_addrset(fdblk * nrdblkn); while (!nrd_check_ready()) DELAY(1); if (read) epson_insw(P_NRD_DATA, bp->bio_data + fd->skip, fdblk / sizeof(short)); else epson_outsw(P_NRD_DATA, bp->bio_data + fd->skip, (format ? bp->bio_bcount : fdblk) / sizeof(short)); blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + fd->skip/fdblk; sectrac = fd->ft->sectrac; sec = blknum % (sectrac * fd->ft->heads); head = sec / sectrac; sec = sec % sectrac + 1; fd->hddrv = ((head&1)<<2)+fdu; if (nrdsec++ >= nrd_sec()) nrdaddr = (nrd_t)(fd->track * 8 + head * 4); nrdsec = sec; fdc->state = IOCOMPLETE; } #endif case PIOREAD: /* * actually perform the PIO read. The IOCOMPLETE case * removes the timeout for us. */ (void)fdcpio(fdc,bp->bio_cmd,bp->bio_data+fd->skip,fdblk); fdc->state = IOCOMPLETE; /* FALLTHROUGH */ case IOCOMPLETE: /* IO DONE, post-analyze */ #ifdef EPSON_NRDISK if (fdu != nrdu) untimeout(fd_iotimeout, fdc, fd->tohandle); #else untimeout(fd_iotimeout, fdc, fd->tohandle); #endif if (fd_read_status(fdc, fd->fdsu)) { if (!(fdc->flags & FDC_NODMA)) isa_dmadone(idf, bp->bio_data + fd->skip, format ? bp->bio_bcount : fdblk, fdc->dmachan); if (fdc->retry < 6) fdc->retry = 6; /* force a reset */ return (retrier(fdc)); } fdc->state = IOTIMEDOUT; /* FALLTHROUGH */ case IOTIMEDOUT: #ifdef EPSON_NRDISK if (fdu != nrdu) { #endif /* EPSON_NRDISK */ if (!(fdc->flags & FDC_NODMA)) isa_dmadone(idf, bp->bio_data + fd->skip, format ? bp->bio_bcount : fdblk, fdc->dmachan); #ifdef EPSON_NRDISK } else nrd_LED_off(); #endif /* EPSON_NRDISK */ if (fdc->status[0] & NE7_ST0_IC) { if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT && fdc->status[1] & NE7_ST1_OR) { /* * DMA overrun. Someone hogged the bus * and didn't release it in time for the * next FDC transfer. * Just restart it, don't increment retry * count. (vak) */ fdc->state = SEEKCOMPLETE; return (1); } else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV && fdc->retry < 6) fdc->retry = 6; /* force a reset */ else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT && fdc->status[2] & NE7_ST2_WC && fdc->retry < 3) fdc->retry = 3; /* force recalibrate */ return (retrier(fdc)); } /* All OK */ fd->skip += fdblk; if (!format && fd->skip < bp->bio_bcount - bp->bio_resid) { /* set up next transfer */ fdc->state = DOSEEK; } else { /* ALL DONE */ fd->skip = 0; fdc->bp = NULL; device_unbusy(fd->dev); biofinish(bp, &fd->device_stats, 0); fdc->fd = (fd_p) 0; fdc->fdu = -1; fdc->state = FINDWORK; } return (1); case RESETCTLR: fdc_reset(fdc); fdc->retry++; fdc->state = RESETCOMPLETE; return (0); case RESETCOMPLETE: /* * Discard all the results from the reset so that they * can't cause an unexpected interrupt later. */ for (i = 0; i < 4; i++) (void)fd_sense_int(fdc, &st0, &cyl); fdc->state = STARTRECAL; /* Fall through. */ case STARTRECAL: #ifdef PC98 pc98_fd_check_ready(fdu); #endif if(fd_cmd(fdc, 2, NE7CMD_RECAL, fdu, 0)) { /* arrgl */ fdc->retry = 6; return (retrier(fdc)); } fdc->state = RECALWAIT; return (0); /* will return later */ case RECALWAIT: /* allow heads to settle */ timeout(fd_pseudointr, fdc, hz / 8); fdc->state = RECALCOMPLETE; return (0); /* will return later */ case RECALCOMPLETE: do { /* * See SEEKCOMPLETE for a comment on this: */ if (fd_sense_int(fdc, &st0, &cyl) == FD_NOT_VALID) return 0; if(fdc->fdct == FDC_NE765 && (st0 & NE7_ST0_IC) == NE7_ST0_IC_RC) return 0; /* hope for a real intr */ } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); #ifdef EPSON_NRDISK if (fdu == nrdu) { st0 = NE7_ST0_IC_NT; cyl = 0; } #endif if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0) { if(fdc->retry > 3) /* * a recalibrate from beyond cylinder 77 * will "fail" due to the FDC limitations; * since people used to complain much about * the failure message, try not logging * this one if it seems to be the first * time in a line */ printf("fd%d: recal failed ST0 %b cyl %d\n", fdu, st0, NE7_ST0BITS, cyl); if(fdc->retry < 3) fdc->retry = 3; return (retrier(fdc)); } fd->track = 0; /* Seek (probably) necessary */ fdc->state = DOSEEK; return (1); /* will return immediatly */ case MOTORWAIT: if(fd->flags & FD_MOTOR_WAIT) { return (0); /* time's not up yet */ } if (fdc->flags & FDC_NEEDS_RESET) { fdc->state = RESETCTLR; fdc->flags &= ~FDC_NEEDS_RESET; } else { /* * If all motors were off, then the controller was * reset, so it has lost track of the current * cylinder. Recalibrate to handle this case. * But first, discard the results of the reset. */ fdc->state = RESETCOMPLETE; } return (1); /* will return immediatly */ default: device_printf(fdc->fdc_dev, "unexpected FD int->"); if (fd_read_status(fdc, fd->fdsu) == 0) printf("FDC status :%x %x %x %x %x %x %x ", fdc->status[0], fdc->status[1], fdc->status[2], fdc->status[3], fdc->status[4], fdc->status[5], fdc->status[6] ); else printf("No status available "); if (fd_sense_int(fdc, &st0, &cyl) != 0) { printf("[controller is dead now]\n"); return (0); } printf("ST0 = %x, PCN = %x\n", st0, cyl); return (0); } /*XXX confusing: some branches return immediately, others end up here*/ return (1); /* Come back immediatly to new state */ } static int retrier(struct fdc_data *fdc) { struct bio *bp; struct fd_data *fd; int fdu; bp = fdc->bp; /* XXX shouldn't this be cached somewhere? */ fdu = FDUNIT(minor(bp->bio_dev)); fd = devclass_get_softc(fd_devclass, fdu); if (fd->options & FDOPT_NORETRY) goto fail; switch (fdc->retry) { case 0: case 1: case 2: fdc->state = SEEKCOMPLETE; break; case 3: case 4: case 5: fdc->state = STARTRECAL; break; case 6: fdc->state = RESETCTLR; break; case 7: break; default: fail: { dev_t sav_bio_dev = bp->bio_dev; int printerror = (fd->options & FDOPT_NOERRLOG) == 0; /* Trick diskerr */ bp->bio_dev = makedev(major(bp->bio_dev), (FDUNIT(minor(bp->bio_dev))<<3)|RAW_PART); if (printerror) diskerr(bp, "hard error", fdc->fd->skip / DEV_BSIZE, (struct disklabel *)NULL); bp->bio_dev = sav_bio_dev; if (printerror) { if (fdc->flags & FDC_STAT_VALID) { printf( " (ST0 %b ST1 %b ST2 %b cyl %u hd %u sec %u)\n", fdc->status[0], NE7_ST0BITS, fdc->status[1], NE7_ST1BITS, fdc->status[2], NE7_ST2BITS, fdc->status[3], fdc->status[4], fdc->status[5]); } else printf(" (No status)\n"); } } if ((fd->options & FDOPT_NOERROR) == 0) { bp->bio_flags |= BIO_ERROR; bp->bio_error = EIO; bp->bio_resid += bp->bio_bcount - fdc->fd->skip; } fdc->bp = NULL; fdc->fd->skip = 0; device_unbusy(fd->dev); biofinish(bp, &fdc->fd->device_stats, 0); fdc->state = FINDWORK; fdc->flags |= FDC_NEEDS_RESET; fdc->fd = (fd_p) 0; fdc->fdu = -1; return (1); } fdc->retry++; return (1); } static void fdbiodone(struct bio *bp) { wakeup(bp); } static int fdformat(dev, finfo, p) dev_t dev; struct fd_formb *finfo; struct proc *p; { fdu_t fdu; fd_p fd; struct bio *bp; int rv = 0, s; size_t fdblk; fdu = FDUNIT(minor(dev)); fd = devclass_get_softc(fd_devclass, fdu); fdblk = 128 << fd->ft->secsize; /* set up a buffer header for fdstrategy() */ bp = (struct bio *)malloc(sizeof(struct bio), M_TEMP, M_NOWAIT); if(bp == 0) return ENOMEM; /* * keep the process from being swapped */ PHOLD(p); bzero((void *)bp, sizeof(*bp)); bp->bio_cmd = BIO_FORMAT; /* * calculate a fake blkno, so fdstrategy() would initiate a * seek to the requested cylinder */ bp->bio_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) + finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE; bp->bio_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; bp->bio_data = (caddr_t)finfo; /* now do the format */ bp->bio_dev = dev; bp->bio_done = fdbiodone; fdstrategy(bp); /* ...and wait for it to complete */ s = splbio(); while(!(bp->bio_flags & BIO_DONE)) { rv = tsleep((caddr_t)bp, PRIBIO, "fdform", 20 * hz); if (rv == EWOULDBLOCK) break; } splx(s); if (rv == EWOULDBLOCK) { /* timed out */ rv = EIO; device_unbusy(fd->dev); } if (bp->bio_flags & BIO_ERROR) rv = bp->bio_error; /* * allow the process to be swapped */ PRELE(p); free(bp, M_TEMP); return rv; } /* * TODO: don't allocate buffer on stack. */ static int fdioctl(dev, cmd, addr, flag, p) dev_t dev; u_long cmd; caddr_t addr; int flag; struct proc *p; { fdu_t fdu = FDUNIT(minor(dev)); fd_p fd = devclass_get_softc(fd_devclass, fdu); size_t fdblk; struct fd_type *fdt; struct disklabel *dl; struct fdc_status *fsp; char buffer[DEV_BSIZE]; int error = 0; fdblk = 128 << fd->ft->secsize; #ifdef PC98 pc98_fd_check_ready(fdu); #endif switch (cmd) { case DIOCGDINFO: bzero(buffer, sizeof (buffer)); dl = (struct disklabel *)buffer; dl->d_secsize = fdblk; fdt = fd->ft; dl->d_secpercyl = fdt->size / fdt->tracks; dl->d_type = DTYPE_FLOPPY; if (readdisklabel(dkmodpart(dev, RAW_PART), dl) == NULL) error = 0; else error = EINVAL; *(struct disklabel *)addr = *dl; break; case DIOCSDINFO: if ((flag & FWRITE) == 0) error = EBADF; break; case DIOCWLABEL: if ((flag & FWRITE) == 0) error = EBADF; break; case DIOCWDINFO: if ((flag & FWRITE) == 0) { error = EBADF; break; } dl = (struct disklabel *)addr; if ((error = setdisklabel((struct disklabel *)buffer, dl, (u_long)0)) != 0) break; error = writedisklabel(dev, (struct disklabel *)buffer); break; case FD_FORM: if ((flag & FWRITE) == 0) error = EBADF; /* must be opened for writing */ else if (((struct fd_formb *)addr)->format_version != FD_FORMAT_VERSION) error = EINVAL; /* wrong version of formatting prog */ else error = fdformat(dev, (struct fd_formb *)addr, p); break; case FD_GTYPE: /* get drive type */ *(struct fd_type *)addr = *fd->ft; break; case FD_STYPE: /* set drive type */ /* this is considered harmful; only allow for superuser */ if (suser(p) != 0) return EPERM; *fd->ft = *(struct fd_type *)addr; break; case FD_GOPTS: /* get drive options */ *(int *)addr = fd->options; break; case FD_SOPTS: /* set drive options */ fd->options = *(int *)addr; break; case FD_CLRERR: if (suser(p) != 0) return EPERM; fd->fdc->fdc_errs = 0; break; case FD_GSTAT: fsp = (struct fdc_status *)addr; if ((fd->fdc->flags & FDC_STAT_VALID) == 0) return EINVAL; memcpy(fsp->status, fd->fdc->status, 7 * sizeof(u_int)); break; default: error = ENOTTY; break; } return (error); } /* * Hello emacs, these are the * Local Variables: * c-indent-level: 8 * c-continued-statement-offset: 8 * c-continued-brace-offset: 0 * c-brace-offset: -8 * c-brace-imaginary-offset: 0 * c-argdecl-indent: 8 * c-label-offset: -8 * c++-hanging-braces: 1 * c++-access-specifier-offset: -8 * c++-empty-arglist-indent: 8 * c++-friend-offset: 0 * End: */ Index: head/sys/pc98/pc98/machdep.c =================================================================== --- head/sys/pc98/pc98/machdep.c (revision 78134) +++ head/sys/pc98/pc98/machdep.c (revision 78135) @@ -1,2582 +1,2582 @@ /*- * Copyright (c) 1992 Terrence R. Lambert. * Copyright (c) 1982, 1987, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * 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. * 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. * * from: @(#)machdep.c 7.4 (Berkeley) 6/3/91 * $FreeBSD$ */ #include "opt_atalk.h" #include "opt_compat.h" #include "opt_cpu.h" #include "opt_ddb.h" #include "opt_inet.h" #include "opt_ipx.h" #include "opt_isa.h" #include "opt_maxmem.h" #include "opt_msgbuf.h" #include "opt_npx.h" #include "opt_perfmon.h" -#include "opt_userconfig.h" +/* #include "opt_userconfig.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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* pcb.h included via sys/user.h */ #include #ifdef PERFMON #include #endif #ifdef OLD_BUS_ARCH #include #endif #include #include #ifdef PC98 #include #include #else #include #endif #include #include #include extern void init386 __P((int first)); extern void dblfault_handler __P((void)); extern void printcpuinfo(void); /* XXX header file */ extern void earlysetcpuclass(void); /* same header file */ extern void finishidentcpu(void); extern void panicifcpuunsupported(void); extern void initializecpu(void); #define CS_SECURE(cs) (ISPL(cs) == SEL_UPL) #define EFL_SECURE(ef, oef) ((((ef) ^ (oef)) & ~PSL_USERCHANGE) == 0) static void cpu_startup __P((void *)); SYSINIT(cpu, SI_SUB_CPU, SI_ORDER_FIRST, cpu_startup, NULL) #ifdef PC98 int need_pre_dma_flush; /* If 1, use wbinvd befor DMA transfer. */ int need_post_dma_flush; /* If 1, use invd after DMA transfer. */ #endif int _udatasel, _ucodesel; u_int atdevbase; #if defined(SWTCH_OPTIM_STATS) extern int swtch_optim_stats; SYSCTL_INT(_debug, OID_AUTO, swtch_optim_stats, CTLFLAG_RD, &swtch_optim_stats, 0, ""); SYSCTL_INT(_debug, OID_AUTO, tlb_flush_count, CTLFLAG_RD, &tlb_flush_count, 0, ""); #endif #ifdef PC98 static int ispc98 = 1; #else static int ispc98 = 0; #endif SYSCTL_INT(_machdep, OID_AUTO, ispc98, CTLFLAG_RD, &ispc98, 0, ""); int physmem = 0; int cold = 1; static void osendsig __P((sig_t catcher, int sig, sigset_t *mask, u_long code)); static int sysctl_hw_physmem(SYSCTL_HANDLER_ARGS) { int error = sysctl_handle_int(oidp, 0, ctob(physmem), req); return (error); } SYSCTL_PROC(_hw, HW_PHYSMEM, physmem, CTLTYPE_INT|CTLFLAG_RD, 0, 0, sysctl_hw_physmem, "I", ""); static int sysctl_hw_usermem(SYSCTL_HANDLER_ARGS) { int error = sysctl_handle_int(oidp, 0, ctob(physmem - cnt.v_wire_count), req); return (error); } SYSCTL_PROC(_hw, HW_USERMEM, usermem, CTLTYPE_INT|CTLFLAG_RD, 0, 0, sysctl_hw_usermem, "I", ""); static int sysctl_hw_availpages(SYSCTL_HANDLER_ARGS) { int error = sysctl_handle_int(oidp, 0, i386_btop(avail_end - avail_start), req); return (error); } SYSCTL_PROC(_hw, OID_AUTO, availpages, CTLTYPE_INT|CTLFLAG_RD, 0, 0, sysctl_hw_availpages, "I", ""); static int sysctl_machdep_msgbuf(SYSCTL_HANDLER_ARGS) { int error; /* Unwind the buffer, so that it's linear (possibly starting with * some initial nulls). */ error=sysctl_handle_opaque(oidp,msgbufp->msg_ptr+msgbufp->msg_bufr, msgbufp->msg_size-msgbufp->msg_bufr,req); if(error) return(error); if(msgbufp->msg_bufr>0) { error=sysctl_handle_opaque(oidp,msgbufp->msg_ptr, msgbufp->msg_bufr,req); } return(error); } SYSCTL_PROC(_machdep, OID_AUTO, msgbuf, CTLTYPE_STRING|CTLFLAG_RD, 0, 0, sysctl_machdep_msgbuf, "A","Contents of kernel message buffer"); static int msgbuf_clear; static int sysctl_machdep_msgbuf_clear(SYSCTL_HANDLER_ARGS) { int error; error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); if (!error && req->newptr) { /* Clear the buffer and reset write pointer */ bzero(msgbufp->msg_ptr,msgbufp->msg_size); msgbufp->msg_bufr=msgbufp->msg_bufx=0; msgbuf_clear=0; } return (error); } SYSCTL_PROC(_machdep, OID_AUTO, msgbuf_clear, CTLTYPE_INT|CTLFLAG_RW, &msgbuf_clear, 0, sysctl_machdep_msgbuf_clear, "I", "Clear kernel message buffer"); int Maxmem = 0; #ifdef PC98 int Maxmem_under16M = 0; #endif long dumplo; vm_offset_t phys_avail[10]; /* must be 2 less so 0 0 can signal end of chunks */ #define PHYS_AVAIL_ARRAY_END ((sizeof(phys_avail) / sizeof(vm_offset_t)) - 2) static vm_offset_t buffer_sva, buffer_eva; vm_offset_t clean_sva, clean_eva; static vm_offset_t pager_sva, pager_eva; static struct trapframe proc0_tf; #ifndef SMP static struct globaldata __globaldata; #endif struct mtx sched_lock; struct mtx Giant; static void cpu_startup(dummy) void *dummy; { register unsigned i; register caddr_t v; vm_offset_t maxaddr; vm_size_t size = 0; int firstaddr; vm_offset_t minaddr; int physmem_est; /* * Good {morning,afternoon,evening,night}. */ mtx_lock(&vm_mtx); earlysetcpuclass(); startrtclock(); printcpuinfo(); panicifcpuunsupported(); #ifdef PERFMON perfmon_init(); #endif printf("real memory = %u (%uK bytes)\n", ptoa(Maxmem), ptoa(Maxmem) / 1024); /* * Display any holes after the first chunk of extended memory. */ if (bootverbose) { int indx; printf("Physical memory chunk(s):\n"); for (indx = 0; phys_avail[indx + 1] != 0; indx += 2) { unsigned int size1 = phys_avail[indx + 1] - phys_avail[indx]; printf("0x%08x - 0x%08x, %u bytes (%u pages)\n", phys_avail[indx], phys_avail[indx + 1] - 1, size1, size1 / PAGE_SIZE); } } /* * Calculate callout wheel size */ for (callwheelsize = 1, callwheelbits = 0; callwheelsize < ncallout; callwheelsize <<= 1, ++callwheelbits) ; callwheelmask = callwheelsize - 1; /* * Allocate space for system data structures. * The first available kernel virtual address is in "v". * As pages of kernel virtual memory are allocated, "v" is incremented. * As pages of memory are allocated and cleared, * "firstaddr" is incremented. * An index into the kernel page table corresponding to the * virtual memory address maintained in "v" is kept in "mapaddr". */ /* * Make two passes. The first pass calculates how much memory is * needed and allocates it. The second pass assigns virtual * addresses to the various data structures. */ firstaddr = 0; again: v = (caddr_t)firstaddr; #define valloc(name, type, num) \ (name) = (type *)v; v = (caddr_t)((name)+(num)) #define valloclim(name, type, num, lim) \ (name) = (type *)v; v = (caddr_t)((lim) = ((name)+(num))) valloc(callout, struct callout, ncallout); valloc(callwheel, struct callout_tailq, callwheelsize); /* * Discount the physical memory larger than the size of kernel_map * to avoid eating up all of KVA space. */ if (kernel_map->first_free == NULL) { printf("Warning: no free entries in kernel_map.\n"); physmem_est = physmem; } else physmem_est = min(physmem, kernel_map->max_offset - kernel_map->min_offset); /* * The nominal buffer size (and minimum KVA allocation) is BKVASIZE. * For the first 64MB of ram nominally allocate sufficient buffers to * cover 1/4 of our ram. Beyond the first 64MB allocate additional * buffers to cover 1/20 of our ram over 64MB. * * factor represents the 1/4 x ram conversion. */ if (nbuf == 0) { int factor = 4 * BKVASIZE / PAGE_SIZE; nbuf = 50; if (physmem_est > 1024) nbuf += min((physmem_est - 1024) / factor, 16384 / factor); if (physmem_est > 16384) nbuf += (physmem_est - 16384) * 2 / (factor * 5); } /* * Do not allow the buffer_map to be more then 1/2 the size of the * kernel_map. */ if (nbuf > (kernel_map->max_offset - kernel_map->min_offset) / (BKVASIZE * 2)) { nbuf = (kernel_map->max_offset - kernel_map->min_offset) / (BKVASIZE * 2); printf("Warning: nbufs capped at %d\n", nbuf); } nswbuf = max(min(nbuf/4, 256), 16); valloc(swbuf, struct buf, nswbuf); valloc(buf, struct buf, nbuf); v = bufhashinit(v); /* * End of first pass, size has been calculated so allocate memory */ if (firstaddr == 0) { size = (vm_size_t)(v - firstaddr); firstaddr = (int)kmem_alloc(kernel_map, round_page(size)); if (firstaddr == 0) panic("startup: no room for tables"); goto again; } /* * End of second pass, addresses have been assigned */ if ((vm_size_t)(v - firstaddr) != size) panic("startup: table size inconsistency"); clean_map = kmem_suballoc(kernel_map, &clean_sva, &clean_eva, (nbuf*BKVASIZE) + (nswbuf*MAXPHYS) + pager_map_size); buffer_map = kmem_suballoc(clean_map, &buffer_sva, &buffer_eva, (nbuf*BKVASIZE)); buffer_map->system_map = 1; pager_map = kmem_suballoc(clean_map, &pager_sva, &pager_eva, (nswbuf*MAXPHYS) + pager_map_size); pager_map->system_map = 1; exec_map = kmem_suballoc(kernel_map, &minaddr, &maxaddr, (16*(ARG_MAX+(PAGE_SIZE*3)))); mtx_unlock(&vm_mtx); /* * XXX: Mbuf system machine-specific initializations should * go here, if anywhere. */ /* * Initialize callouts */ SLIST_INIT(&callfree); for (i = 0; i < ncallout; i++) { callout_init(&callout[i], 0); callout[i].c_flags = CALLOUT_LOCAL_ALLOC; SLIST_INSERT_HEAD(&callfree, &callout[i], c_links.sle); } for (i = 0; i < callwheelsize; i++) { TAILQ_INIT(&callwheel[i]); } mtx_init(&callout_lock, "callout", MTX_SPIN | MTX_RECURSE); #if defined(USERCONFIG) userconfig(); cninit(); /* the preferred console may have changed */ #endif printf("avail memory = %u (%uK bytes)\n", ptoa(cnt.v_free_count), ptoa(cnt.v_free_count) / 1024); /* * Set up buffers, so they can be used to read disk labels. */ bufinit(); vm_pager_bufferinit(); globaldata_register(GLOBALDATA); #ifndef SMP /* For SMP, we delay the cpu_setregs() until after SMP startup. */ cpu_setregs(); #endif } /* * Send an interrupt to process. * * Stack is set up to allow sigcode stored * at top to call routine, followed by kcall * to sigreturn routine below. After sigreturn * resets the signal mask, the stack, and the * frame pointer, it returns to the user * specified pc, psl. */ static void osendsig(catcher, sig, mask, code) sig_t catcher; int sig; sigset_t *mask; u_long code; { struct osigframe sf; struct osigframe *fp; struct proc *p; struct sigacts *psp; struct trapframe *regs; int oonstack; p = curproc; PROC_LOCK(p); psp = p->p_sigacts; regs = p->p_md.md_regs; oonstack = sigonstack(regs->tf_esp); /* Allocate and validate space for the signal handler context. */ if ((p->p_flag & P_ALTSTACK) && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { fp = (struct osigframe *)(p->p_sigstk.ss_sp + p->p_sigstk.ss_size - sizeof(struct osigframe)); #if defined(COMPAT_43) || defined(COMPAT_SUNOS) p->p_sigstk.ss_flags |= SS_ONSTACK; #endif } else fp = (struct osigframe *)regs->tf_esp - 1; PROC_UNLOCK(p); /* * grow_stack() will return 0 if *fp does not fit inside the stack * and the stack can not be grown. * useracc() will return FALSE if access is denied. */ if (grow_stack(p, (int)fp) == 0 || !useracc((caddr_t)fp, sizeof(*fp), VM_PROT_WRITE)) { /* * Process has trashed its stack; give it an illegal * instruction to halt it in its tracks. */ PROC_LOCK(p); SIGACTION(p, SIGILL) = SIG_DFL; SIGDELSET(p->p_sigignore, SIGILL); SIGDELSET(p->p_sigcatch, SIGILL); SIGDELSET(p->p_sigmask, SIGILL); psignal(p, SIGILL); PROC_UNLOCK(p); return; } /* Translate the signal if appropriate. */ if (p->p_sysent->sv_sigtbl && sig <= p->p_sysent->sv_sigsize) sig = p->p_sysent->sv_sigtbl[_SIG_IDX(sig)]; /* Build the argument list for the signal handler. */ sf.sf_signum = sig; sf.sf_scp = (register_t)&fp->sf_siginfo.si_sc; PROC_LOCK(p); if (SIGISMEMBER(p->p_sigacts->ps_siginfo, sig)) { /* Signal handler installed with SA_SIGINFO. */ sf.sf_arg2 = (register_t)&fp->sf_siginfo; sf.sf_siginfo.si_signo = sig; sf.sf_siginfo.si_code = code; sf.sf_ahu.sf_action = (__osiginfohandler_t *)catcher; } else { /* Old FreeBSD-style arguments. */ sf.sf_arg2 = code; sf.sf_addr = regs->tf_err; sf.sf_ahu.sf_handler = catcher; } PROC_UNLOCK(p); /* Save most if not all of trap frame. */ sf.sf_siginfo.si_sc.sc_eax = regs->tf_eax; sf.sf_siginfo.si_sc.sc_ebx = regs->tf_ebx; sf.sf_siginfo.si_sc.sc_ecx = regs->tf_ecx; sf.sf_siginfo.si_sc.sc_edx = regs->tf_edx; sf.sf_siginfo.si_sc.sc_esi = regs->tf_esi; sf.sf_siginfo.si_sc.sc_edi = regs->tf_edi; sf.sf_siginfo.si_sc.sc_cs = regs->tf_cs; sf.sf_siginfo.si_sc.sc_ds = regs->tf_ds; sf.sf_siginfo.si_sc.sc_ss = regs->tf_ss; sf.sf_siginfo.si_sc.sc_es = regs->tf_es; sf.sf_siginfo.si_sc.sc_fs = regs->tf_fs; sf.sf_siginfo.si_sc.sc_gs = rgs(); sf.sf_siginfo.si_sc.sc_isp = regs->tf_isp; /* Build the signal context to be used by osigreturn(). */ sf.sf_siginfo.si_sc.sc_onstack = (oonstack) ? 1 : 0; SIG2OSIG(*mask, sf.sf_siginfo.si_sc.sc_mask); sf.sf_siginfo.si_sc.sc_sp = regs->tf_esp; sf.sf_siginfo.si_sc.sc_fp = regs->tf_ebp; sf.sf_siginfo.si_sc.sc_pc = regs->tf_eip; sf.sf_siginfo.si_sc.sc_ps = regs->tf_eflags; sf.sf_siginfo.si_sc.sc_trapno = regs->tf_trapno; sf.sf_siginfo.si_sc.sc_err = regs->tf_err; /* * If we're a vm86 process, we want to save the segment registers. * We also change eflags to be our emulated eflags, not the actual * eflags. */ if (regs->tf_eflags & PSL_VM) { /* XXX confusing names: `tf' isn't a trapframe; `regs' is. */ struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86 = &p->p_addr->u_pcb.pcb_ext->ext_vm86; sf.sf_siginfo.si_sc.sc_gs = tf->tf_vm86_gs; sf.sf_siginfo.si_sc.sc_fs = tf->tf_vm86_fs; sf.sf_siginfo.si_sc.sc_es = tf->tf_vm86_es; sf.sf_siginfo.si_sc.sc_ds = tf->tf_vm86_ds; if (vm86->vm86_has_vme == 0) sf.sf_siginfo.si_sc.sc_ps = (tf->tf_eflags & ~(PSL_VIF | PSL_VIP)) | (vm86->vm86_eflags & (PSL_VIF | PSL_VIP)); /* See sendsig() for comments. */ tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_T | PSL_VIF | PSL_VIP); } /* Copy the sigframe out to the user's stack. */ if (copyout(&sf, fp, sizeof(*fp)) != 0) { /* * Something is wrong with the stack pointer. * ...Kill the process. */ PROC_LOCK(p); sigexit(p, SIGILL); /* NOTREACHED */ } regs->tf_esp = (int)fp; regs->tf_eip = PS_STRINGS - szosigcode; regs->tf_cs = _ucodesel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_fs = _udatasel; load_gs(_udatasel); regs->tf_ss = _udatasel; } void sendsig(catcher, sig, mask, code) sig_t catcher; int sig; sigset_t *mask; u_long code; { struct sigframe sf; struct proc *p; struct sigacts *psp; struct trapframe *regs; struct sigframe *sfp; int oonstack; p = curproc; PROC_LOCK(p); psp = p->p_sigacts; if (SIGISMEMBER(psp->ps_osigset, sig)) { PROC_UNLOCK(p); osendsig(catcher, sig, mask, code); return; } regs = p->p_md.md_regs; oonstack = sigonstack(regs->tf_esp); /* Save user context. */ bzero(&sf, sizeof(sf)); sf.sf_uc.uc_sigmask = *mask; sf.sf_uc.uc_stack = p->p_sigstk; sf.sf_uc.uc_stack.ss_flags = (p->p_flag & P_ALTSTACK) ? ((oonstack) ? SS_ONSTACK : 0) : SS_DISABLE; sf.sf_uc.uc_mcontext.mc_onstack = (oonstack) ? 1 : 0; sf.sf_uc.uc_mcontext.mc_gs = rgs(); bcopy(regs, &sf.sf_uc.uc_mcontext.mc_fs, sizeof(*regs)); /* Allocate and validate space for the signal handler context. */ if ((p->p_flag & P_ALTSTACK) != 0 && !oonstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { sfp = (struct sigframe *)(p->p_sigstk.ss_sp + p->p_sigstk.ss_size - sizeof(struct sigframe)); #if defined(COMPAT_43) || defined(COMPAT_SUNOS) p->p_sigstk.ss_flags |= SS_ONSTACK; #endif } else sfp = (struct sigframe *)regs->tf_esp - 1; PROC_UNLOCK(p); /* * grow_stack() will return 0 if *sfp does not fit inside the stack * and the stack can not be grown. * useracc() will return FALSE if access is denied. */ if (grow_stack(p, (int)sfp) == 0 || !useracc((caddr_t)sfp, sizeof(*sfp), VM_PROT_WRITE)) { /* * Process has trashed its stack; give it an illegal * instruction to halt it in its tracks. */ #ifdef DEBUG printf("process %d has trashed its stack\n", p->p_pid); #endif PROC_LOCK(p); SIGACTION(p, SIGILL) = SIG_DFL; SIGDELSET(p->p_sigignore, SIGILL); SIGDELSET(p->p_sigcatch, SIGILL); SIGDELSET(p->p_sigmask, SIGILL); psignal(p, SIGILL); PROC_UNLOCK(p); return; } /* Translate the signal if appropriate. */ if (p->p_sysent->sv_sigtbl && sig <= p->p_sysent->sv_sigsize) sig = p->p_sysent->sv_sigtbl[_SIG_IDX(sig)]; /* Build the argument list for the signal handler. */ sf.sf_signum = sig; sf.sf_ucontext = (register_t)&sfp->sf_uc; PROC_LOCK(p); if (SIGISMEMBER(p->p_sigacts->ps_siginfo, sig)) { /* Signal handler installed with SA_SIGINFO. */ sf.sf_siginfo = (register_t)&sfp->sf_si; sf.sf_ahu.sf_action = (__siginfohandler_t *)catcher; /* Fill siginfo structure. */ sf.sf_si.si_signo = sig; sf.sf_si.si_code = code; sf.sf_si.si_addr = (void *)regs->tf_err; } else { /* Old FreeBSD-style arguments. */ sf.sf_siginfo = code; sf.sf_addr = regs->tf_err; sf.sf_ahu.sf_handler = catcher; } PROC_UNLOCK(p); /* * If we're a vm86 process, we want to save the segment registers. * We also change eflags to be our emulated eflags, not the actual * eflags. */ if (regs->tf_eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86 = &p->p_addr->u_pcb.pcb_ext->ext_vm86; sf.sf_uc.uc_mcontext.mc_gs = tf->tf_vm86_gs; sf.sf_uc.uc_mcontext.mc_fs = tf->tf_vm86_fs; sf.sf_uc.uc_mcontext.mc_es = tf->tf_vm86_es; sf.sf_uc.uc_mcontext.mc_ds = tf->tf_vm86_ds; if (vm86->vm86_has_vme == 0) sf.sf_uc.uc_mcontext.mc_eflags = (tf->tf_eflags & ~(PSL_VIF | PSL_VIP)) | (vm86->vm86_eflags & (PSL_VIF | PSL_VIP)); /* * We should never have PSL_T set when returning from vm86 * mode. It may be set here if we deliver a signal before * getting to vm86 mode, so turn it off. * * Clear PSL_NT to inhibit T_TSSFLT faults on return from * syscalls made by the signal handler. This just avoids * wasting time for our lazy fixup of such faults. PSL_NT * does nothing in vm86 mode, but vm86 programs can set it * almost legitimately in probes for old cpu types. */ tf->tf_eflags &= ~(PSL_VM | PSL_NT | PSL_T | PSL_VIF | PSL_VIP); } /* Copy the sigframe out to the user's stack. */ if (copyout(&sf, sfp, sizeof(*sfp)) != 0) { /* * Something is wrong with the stack pointer. * ...Kill the process. */ PROC_LOCK(p); sigexit(p, SIGILL); /* NOTREACHED */ } regs->tf_esp = (int)sfp; regs->tf_eip = PS_STRINGS - *(p->p_sysent->sv_szsigcode); regs->tf_cs = _ucodesel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_fs = _udatasel; regs->tf_ss = _udatasel; } /* * System call to cleanup state after a signal * has been taken. Reset signal mask and * stack state from context left by sendsig (above). * 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 * state to gain improper privileges. */ int osigreturn(p, uap) struct proc *p; struct osigreturn_args /* { struct osigcontext *sigcntxp; } */ *uap; { struct trapframe *regs; struct osigcontext *scp; int eflags; regs = p->p_md.md_regs; scp = uap->sigcntxp; if (!useracc((caddr_t)scp, sizeof(*scp), VM_PROT_READ)) return (EFAULT); eflags = scp->sc_ps; if (eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86; /* * if pcb_ext == 0 or vm86_inited == 0, the user hasn't * set up the vm86 area, and we can't enter vm86 mode. */ if (p->p_addr->u_pcb.pcb_ext == 0) return (EINVAL); vm86 = &p->p_addr->u_pcb.pcb_ext->ext_vm86; if (vm86->vm86_inited == 0) return (EINVAL); /* Go back to user mode if both flags are set. */ if ((eflags & PSL_VIP) && (eflags & PSL_VIF)) trapsignal(p, SIGBUS, 0); if (vm86->vm86_has_vme) { eflags = (tf->tf_eflags & ~VME_USERCHANGE) | (eflags & VME_USERCHANGE) | PSL_VM; } else { vm86->vm86_eflags = eflags; /* save VIF, VIP */ eflags = (tf->tf_eflags & ~VM_USERCHANGE) | (eflags & VM_USERCHANGE) | PSL_VM; } tf->tf_vm86_ds = scp->sc_ds; tf->tf_vm86_es = scp->sc_es; tf->tf_vm86_fs = scp->sc_fs; tf->tf_vm86_gs = scp->sc_gs; tf->tf_ds = _udatasel; tf->tf_es = _udatasel; tf->tf_fs = _udatasel; } else { /* * Don't allow users to change privileged or reserved flags. */ /* * XXX do allow users to change the privileged flag PSL_RF. * The cpu sets PSL_RF in tf_eflags for faults. Debuggers * should sometimes set it there too. tf_eflags is kept in * the signal context during signal handling and there is no * other place to remember it, so the PSL_RF bit may be * corrupted by the signal handler without us knowing. * Corruption of the PSL_RF bit at worst causes one more or * one less debugger trap, so allowing it is fairly harmless. */ if (!EFL_SECURE(eflags & ~PSL_RF, regs->tf_eflags & ~PSL_RF)) { return (EINVAL); } /* * Don't allow users to load a valid privileged %cs. Let the * hardware check for invalid selectors, excess privilege in * other selectors, invalid %eip's and invalid %esp's. */ if (!CS_SECURE(scp->sc_cs)) { trapsignal(p, SIGBUS, T_PROTFLT); return (EINVAL); } regs->tf_ds = scp->sc_ds; regs->tf_es = scp->sc_es; regs->tf_fs = scp->sc_fs; } /* Restore remaining registers. */ regs->tf_eax = scp->sc_eax; regs->tf_ebx = scp->sc_ebx; regs->tf_ecx = scp->sc_ecx; regs->tf_edx = scp->sc_edx; regs->tf_esi = scp->sc_esi; regs->tf_edi = scp->sc_edi; regs->tf_cs = scp->sc_cs; regs->tf_ss = scp->sc_ss; regs->tf_isp = scp->sc_isp; PROC_LOCK(p); #if defined(COMPAT_43) || defined(COMPAT_SUNOS) if (scp->sc_onstack & 1) p->p_sigstk.ss_flags |= SS_ONSTACK; else p->p_sigstk.ss_flags &= ~SS_ONSTACK; #endif SIGSETOLD(p->p_sigmask, scp->sc_mask); SIG_CANTMASK(p->p_sigmask); PROC_UNLOCK(p); regs->tf_ebp = scp->sc_fp; regs->tf_esp = scp->sc_sp; regs->tf_eip = scp->sc_pc; regs->tf_eflags = eflags; return (EJUSTRETURN); } int sigreturn(p, uap) struct proc *p; struct sigreturn_args /* { ucontext_t *sigcntxp; } */ *uap; { struct trapframe *regs; ucontext_t *ucp; int cs, eflags; ucp = uap->sigcntxp; if (!useracc((caddr_t)ucp, sizeof(struct osigcontext), VM_PROT_READ)) return (EFAULT); if (((struct osigcontext *)ucp)->sc_trapno == 0x01d516) return (osigreturn(p, (struct osigreturn_args *)uap)); /* * Since ucp is not an osigcontext but a ucontext_t, we have to * check again if all of it is accessible. A ucontext_t is * much larger, so instead of just checking for the pointer * being valid for the size of an osigcontext, now check for * it being valid for a whole, new-style ucontext_t. */ if (!useracc((caddr_t)ucp, sizeof(*ucp), VM_PROT_READ)) return (EFAULT); regs = p->p_md.md_regs; eflags = ucp->uc_mcontext.mc_eflags; if (eflags & PSL_VM) { struct trapframe_vm86 *tf = (struct trapframe_vm86 *)regs; struct vm86_kernel *vm86; /* * if pcb_ext == 0 or vm86_inited == 0, the user hasn't * set up the vm86 area, and we can't enter vm86 mode. */ if (p->p_addr->u_pcb.pcb_ext == 0) return (EINVAL); vm86 = &p->p_addr->u_pcb.pcb_ext->ext_vm86; if (vm86->vm86_inited == 0) return (EINVAL); /* Go back to user mode if both flags are set. */ if ((eflags & PSL_VIP) && (eflags & PSL_VIF)) trapsignal(p, SIGBUS, 0); if (vm86->vm86_has_vme) { eflags = (tf->tf_eflags & ~VME_USERCHANGE) | (eflags & VME_USERCHANGE) | PSL_VM; } else { vm86->vm86_eflags = eflags; /* save VIF, VIP */ eflags = (tf->tf_eflags & ~VM_USERCHANGE) | (eflags & VM_USERCHANGE) | PSL_VM; } bcopy(&ucp->uc_mcontext.mc_fs, tf, sizeof(struct trapframe)); tf->tf_eflags = eflags; tf->tf_vm86_ds = tf->tf_ds; tf->tf_vm86_es = tf->tf_es; tf->tf_vm86_fs = tf->tf_fs; tf->tf_vm86_gs = ucp->uc_mcontext.mc_gs; tf->tf_ds = _udatasel; tf->tf_es = _udatasel; tf->tf_fs = _udatasel; } else { /* * Don't allow users to change privileged or reserved flags. */ /* * XXX do allow users to change the privileged flag PSL_RF. * The cpu sets PSL_RF in tf_eflags for faults. Debuggers * should sometimes set it there too. tf_eflags is kept in * the signal context during signal handling and there is no * other place to remember it, so the PSL_RF bit may be * corrupted by the signal handler without us knowing. * Corruption of the PSL_RF bit at worst causes one more or * one less debugger trap, so allowing it is fairly harmless. */ if (!EFL_SECURE(eflags & ~PSL_RF, regs->tf_eflags & ~PSL_RF)) { printf("sigreturn: eflags = 0x%x\n", eflags); return (EINVAL); } /* * Don't allow users to load a valid privileged %cs. Let the * hardware check for invalid selectors, excess privilege in * other selectors, invalid %eip's and invalid %esp's. */ cs = ucp->uc_mcontext.mc_cs; if (!CS_SECURE(cs)) { printf("sigreturn: cs = 0x%x\n", cs); trapsignal(p, SIGBUS, T_PROTFLT); return (EINVAL); } bcopy(&ucp->uc_mcontext.mc_fs, regs, sizeof(*regs)); } PROC_LOCK(p); #if defined(COMPAT_43) || defined(COMPAT_SUNOS) if (ucp->uc_mcontext.mc_onstack & 1) p->p_sigstk.ss_flags |= SS_ONSTACK; else p->p_sigstk.ss_flags &= ~SS_ONSTACK; #endif p->p_sigmask = ucp->uc_sigmask; SIG_CANTMASK(p->p_sigmask); PROC_UNLOCK(p); return (EJUSTRETURN); } /* * Machine dependent boot() routine * * I haven't seen anything to put here yet * Possibly some stuff might be grafted back here from boot() */ void cpu_boot(int howto) { } /* * Shutdown the CPU as much as possible */ void cpu_halt(void) { for (;;) __asm__ ("hlt"); } /* * Hook to idle the CPU when possible. This currently only works in * the !SMP case, as there is no clean way to ensure that a CPU will be * woken when there is work available for it. */ static int cpu_idle_hlt = 1; SYSCTL_INT(_machdep, OID_AUTO, cpu_idle_hlt, CTLFLAG_RW, &cpu_idle_hlt, 0, "Idle loop HLT enable"); /* * Note that we have to be careful here to avoid a race between checking * procrunnable() and actually halting. If we don't do this, we may waste * the time between calling hlt and the next interrupt even though there * is a runnable process. */ void cpu_idle(void) { #ifndef SMP if (cpu_idle_hlt) { disable_intr(); if (procrunnable()) enable_intr(); else { enable_intr(); __asm __volatile("hlt"); } } #endif } /* * Clear registers on exec */ void setregs(p, entry, stack, ps_strings) struct proc *p; u_long entry; u_long stack; u_long ps_strings; { struct trapframe *regs = p->p_md.md_regs; struct pcb *pcb = &p->p_addr->u_pcb; if (pcb->pcb_ldt) user_ldt_free(pcb); bzero((char *)regs, sizeof(struct trapframe)); regs->tf_eip = entry; regs->tf_esp = stack; regs->tf_eflags = PSL_USER | (regs->tf_eflags & PSL_T); regs->tf_ss = _udatasel; regs->tf_ds = _udatasel; regs->tf_es = _udatasel; regs->tf_fs = _udatasel; regs->tf_cs = _ucodesel; /* PS_STRINGS value for BSD/OS binaries. It is 0 for non-BSD/OS. */ regs->tf_ebx = ps_strings; /* reset %gs as well */ if (pcb == PCPU_GET(curpcb)) load_gs(_udatasel); else pcb->pcb_gs = _udatasel; /* * Reset the hardware debug registers if they were in use. * They won't have any meaning for the newly exec'd process. */ if (pcb->pcb_flags & PCB_DBREGS) { pcb->pcb_dr0 = 0; pcb->pcb_dr1 = 0; pcb->pcb_dr2 = 0; pcb->pcb_dr3 = 0; pcb->pcb_dr6 = 0; pcb->pcb_dr7 = 0; if (pcb == PCPU_GET(curpcb)) { /* * Clear the debug registers on the running * CPU, otherwise they will end up affecting * the next process we switch to. */ reset_dbregs(); } pcb->pcb_flags &= ~PCB_DBREGS; } /* * Initialize the math emulator (if any) for the current process. * Actually, just clear the bit that says that the emulator has * been initialized. Initialization is delayed until the process * traps to the emulator (if it is done at all) mainly because * emulators don't provide an entry point for initialization. */ p->p_addr->u_pcb.pcb_flags &= ~FP_SOFTFP; /* * Arrange to trap the next npx or `fwait' instruction (see npx.c * for why fwait must be trapped at least if there is an npx or an * emulator). This is mainly to handle the case where npx0 is not * configured, since the npx routines normally set up the trap * otherwise. It should be done only at boot time, but doing it * here allows modifying `npx_exists' for testing the emulator on * systems with an npx. */ load_cr0(rcr0() | CR0_MP | CR0_TS); #ifdef DEV_NPX /* Initialize the npx (if any) for the current process. */ npxinit(__INITIAL_NPXCW__); #endif /* * XXX - Linux emulator * Make sure sure edx is 0x0 on entry. Linux binaries depend * on it. */ p->p_retval[1] = 0; } void cpu_setregs(void) { unsigned int cr0; cr0 = rcr0(); cr0 |= CR0_NE; /* Done by npxinit() */ cr0 |= CR0_MP | CR0_TS; /* Done at every execve() too. */ #ifndef I386_CPU cr0 |= CR0_WP | CR0_AM; #endif load_cr0(cr0); load_gs(_udatasel); } static int sysctl_machdep_adjkerntz(SYSCTL_HANDLER_ARGS) { int error; error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); if (!error && req->newptr) resettodr(); return (error); } SYSCTL_PROC(_machdep, CPU_ADJKERNTZ, adjkerntz, CTLTYPE_INT|CTLFLAG_RW, &adjkerntz, 0, sysctl_machdep_adjkerntz, "I", ""); SYSCTL_INT(_machdep, CPU_DISRTCSET, disable_rtc_set, CTLFLAG_RW, &disable_rtc_set, 0, ""); SYSCTL_STRUCT(_machdep, CPU_BOOTINFO, bootinfo, CTLFLAG_RD, &bootinfo, bootinfo, ""); SYSCTL_INT(_machdep, CPU_WALLCLOCK, wall_cmos_clock, CTLFLAG_RW, &wall_cmos_clock, 0, ""); /* * Initialize 386 and configure to run kernel */ /* * Initialize segments & interrupt table */ int _default_ldt; union descriptor gdt[NGDT * MAXCPU]; /* global descriptor table */ static struct gate_descriptor idt0[NIDT]; struct gate_descriptor *idt = &idt0[0]; /* interrupt descriptor table */ union descriptor ldt[NLDT]; /* local descriptor table */ #ifdef SMP /* table descriptors - used to load tables by microp */ struct region_descriptor r_gdt, r_idt; #endif int private_tss; /* flag indicating private tss */ #if defined(I586_CPU) && !defined(NO_F00F_HACK) extern int has_f00f_bug; #endif static struct i386tss dblfault_tss; static char dblfault_stack[PAGE_SIZE]; extern struct user *proc0paddr; /* software prototypes -- in more palatable form */ struct soft_segment_descriptor gdt_segs[] = { /* GNULL_SEL 0 Null Descriptor */ { 0x0, /* segment base address */ 0x0, /* length */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GCODE_SEL 1 Code Descriptor for kernel */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GDATA_SEL 2 Data Descriptor for kernel */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GPRIV_SEL 3 SMP Per-Processor Private Data Descriptor */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GPROC0_SEL 4 Proc 0 Tss Descriptor */ { 0x0, /* segment base address */ sizeof(struct i386tss)-1,/* length - all address space */ SDT_SYS386TSS, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GLDT_SEL 5 LDT Descriptor */ { (int) ldt, /* segment base address */ sizeof(ldt)-1, /* length - all address space */ SDT_SYSLDT, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GUSERLDT_SEL 6 User LDT Descriptor per process */ { (int) ldt, /* segment base address */ (512 * sizeof(union descriptor)-1), /* length */ SDT_SYSLDT, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GTGATE_SEL 7 Null Descriptor - Placeholder */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GBIOSLOWMEM_SEL 8 BIOS access to realmode segment 0x40, must be #8 in GDT */ { 0x400, /* segment base address */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GPANIC_SEL 9 Panic Tss Descriptor */ { (int) &dblfault_tss, /* segment base address */ sizeof(struct i386tss)-1,/* length - all address space */ SDT_SYS386TSS, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* unused - default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* GBIOSCODE32_SEL 10 BIOS 32-bit interface (32bit Code) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSCODE16_SEL 11 BIOS 32-bit interface (16bit Code) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMERA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSDATA_SEL 12 BIOS 32-bit interface (Data) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSUTIL_SEL 13 BIOS 16-bit interface (Utility) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* GBIOSARGS_SEL 14 BIOS 16-bit interface (Arguments) */ { 0, /* segment base address (overwritten) */ 0xfffff, /* length */ SDT_MEMRWA, /* segment type */ 0, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, }; static struct soft_segment_descriptor ldt_segs[] = { /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Code Descriptor for user */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMERA, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, /* Null Descriptor - overwritten by call gate */ { 0x0, /* segment base address */ 0x0, /* length - all address space */ 0, /* segment type */ 0, /* segment descriptor priority level */ 0, /* segment descriptor present */ 0, 0, 0, /* default 32 vs 16 bit size */ 0 /* limit granularity (byte/page units)*/ }, /* Data Descriptor for user */ { 0x0, /* segment base address */ 0xfffff, /* length - all address space */ SDT_MEMRWA, /* segment type */ SEL_UPL, /* segment descriptor priority level */ 1, /* segment descriptor present */ 0, 0, 1, /* default 32 vs 16 bit size */ 1 /* limit granularity (byte/page units)*/ }, }; void setidt(idx, func, typ, dpl, selec) int idx; inthand_t *func; int typ; int dpl; int selec; { struct gate_descriptor *ip; ip = idt + idx; ip->gd_looffset = (int)func; ip->gd_selector = selec; ip->gd_stkcpy = 0; ip->gd_xx = 0; ip->gd_type = typ; ip->gd_dpl = dpl; ip->gd_p = 1; ip->gd_hioffset = ((int)func)>>16 ; } #define IDTVEC(name) __CONCAT(X,name) extern inthand_t IDTVEC(div), IDTVEC(dbg), IDTVEC(nmi), IDTVEC(bpt), IDTVEC(ofl), IDTVEC(bnd), IDTVEC(ill), IDTVEC(dna), IDTVEC(fpusegm), IDTVEC(tss), IDTVEC(missing), IDTVEC(stk), IDTVEC(prot), IDTVEC(page), IDTVEC(mchk), IDTVEC(rsvd), IDTVEC(fpu), IDTVEC(align), IDTVEC(lcall_syscall), IDTVEC(int0x80_syscall); void sdtossd(sd, ssd) struct segment_descriptor *sd; struct soft_segment_descriptor *ssd; { ssd->ssd_base = (sd->sd_hibase << 24) | sd->sd_lobase; ssd->ssd_limit = (sd->sd_hilimit << 16) | sd->sd_lolimit; ssd->ssd_type = sd->sd_type; ssd->ssd_dpl = sd->sd_dpl; ssd->ssd_p = sd->sd_p; ssd->ssd_def32 = sd->sd_def32; ssd->ssd_gran = sd->sd_gran; } #define PHYSMAP_SIZE (2 * 8) /* * Populate the (physmap) array with base/bound pairs describing the * available physical memory in the system, then test this memory and * build the phys_avail array describing the actually-available memory. * * If we cannot accurately determine the physical memory map, then use * value from the 0xE801 call, and failing that, the RTC. * * Total memory size may be set by the kernel environment variable * hw.physmem or the compile-time define MAXMEM. */ static void getmemsize(int first) { int i, physmap_idx, pa_indx; u_int basemem, extmem; #ifdef PC98 int pg_n; u_int under16; #else struct vm86frame vmf; struct vm86context vmc; #endif vm_offset_t pa, physmap[PHYSMAP_SIZE]; pt_entry_t pte; const char *cp; #ifndef PC98 struct bios_smap *smap; #endif #ifdef PC98 /* XXX - some of EPSON machines can't use PG_N */ pg_n = PG_N; if (pc98_machine_type & M_EPSON_PC98) { switch (epson_machine_id) { #ifdef WB_CACHE default: #endif case 0x34: /* PC-486HX */ case 0x35: /* PC-486HG */ case 0x3B: /* PC-486HA */ pg_n = 0; break; } } #else bzero(&vmf, sizeof(struct vm86frame)); #endif bzero(physmap, sizeof(physmap)); /* * Perform "base memory" related probes & setup */ #ifdef PC98 under16 = pc98_getmemsize(&basemem, &extmem); #else vm86_intcall(0x12, &vmf); basemem = vmf.vmf_ax; #endif if (basemem > 640) { printf("Preposterous BIOS basemem of %uK, truncating to 640K\n", basemem); basemem = 640; } /* * XXX if biosbasemem is now < 640, there is a `hole' * between the end of base memory and the start of * ISA memory. The hole may be empty or it may * contain BIOS code or data. Map it read/write so * that the BIOS can write to it. (Memory from 0 to * the physical end of the kernel is mapped read-only * to begin with and then parts of it are remapped. * The parts that aren't remapped form holes that * remain read-only and are unused by the kernel. * The base memory area is below the physical end of * the kernel and right now forms a read-only hole. * The part of it from PAGE_SIZE to * (trunc_page(biosbasemem * 1024) - 1) will be * remapped and used by the kernel later.) * * This code is similar to the code used in * pmap_mapdev, but since no memory needs to be * allocated we simply change the mapping. */ for (pa = trunc_page(basemem * 1024); pa < ISA_HOLE_START; pa += PAGE_SIZE) { pte = (pt_entry_t)vtopte(pa + KERNBASE); *pte = pa | PG_RW | PG_V; } /* * if basemem != 640, map pages r/w into vm86 page table so * that the bios can scribble on it. */ pte = (pt_entry_t)vm86paddr; for (i = basemem / 4; i < 160; i++) pte[i] = (i << PAGE_SHIFT) | PG_V | PG_RW | PG_U; #ifndef PC98 /* * map page 1 R/W into the kernel page table so we can use it * as a buffer. The kernel will unmap this page later. */ pte = (pt_entry_t)vtopte(KERNBASE + (1 << PAGE_SHIFT)); *pte = (1 << PAGE_SHIFT) | PG_RW | PG_V; /* * get memory map with INT 15:E820 */ vmc.npages = 0; smap = (void *)vm86_addpage(&vmc, 1, KERNBASE + (1 << PAGE_SHIFT)); vm86_getptr(&vmc, (vm_offset_t)smap, &vmf.vmf_es, &vmf.vmf_di); physmap_idx = 0; vmf.vmf_ebx = 0; do { vmf.vmf_eax = 0xE820; vmf.vmf_edx = SMAP_SIG; vmf.vmf_ecx = sizeof(struct bios_smap); i = vm86_datacall(0x15, &vmf, &vmc); if (i || vmf.vmf_eax != SMAP_SIG) break; if (boothowto & RB_VERBOSE) printf("SMAP type=%02x base=%08x %08x len=%08x %08x\n", smap->type, *(u_int32_t *)((char *)&smap->base + 4), (u_int32_t)smap->base, *(u_int32_t *)((char *)&smap->length + 4), (u_int32_t)smap->length); if (smap->type != 0x01) goto next_run; if (smap->length == 0) goto next_run; if (smap->base >= 0xffffffff) { printf("%uK of memory above 4GB ignored\n", (u_int)(smap->length / 1024)); goto next_run; } for (i = 0; i <= physmap_idx; i += 2) { if (smap->base < physmap[i + 1]) { if (boothowto & RB_VERBOSE) printf( "Overlapping or non-montonic memory region, ignoring second region\n"); goto next_run; } } if (smap->base == physmap[physmap_idx + 1]) { physmap[physmap_idx + 1] += smap->length; goto next_run; } physmap_idx += 2; if (physmap_idx == PHYSMAP_SIZE) { printf( "Too many segments in the physical address map, giving up\n"); break; } physmap[physmap_idx] = smap->base; physmap[physmap_idx + 1] = smap->base + smap->length; next_run: } while (vmf.vmf_ebx != 0); if (physmap[1] != 0) goto physmap_done; /* * If we failed above, try memory map with INT 15:E801 */ vmf.vmf_ax = 0xE801; if (vm86_intcall(0x15, &vmf) == 0) { extmem = vmf.vmf_cx + vmf.vmf_dx * 64; } else { #if 0 vmf.vmf_ah = 0x88; vm86_intcall(0x15, &vmf); extmem = vmf.vmf_ax; #else /* * Prefer the RTC value for extended memory. */ extmem = rtcin(RTC_EXTLO) + (rtcin(RTC_EXTHI) << 8); #endif } /* * Special hack for chipsets that still remap the 384k hole when * there's 16MB of memory - this really confuses people that * are trying to use bus mastering ISA controllers with the * "16MB limit"; they only have 16MB, but the remapping puts * them beyond the limit. * * If extended memory is between 15-16MB (16-17MB phys address range), * chop it to 15MB. */ if ((extmem > 15 * 1024) && (extmem < 16 * 1024)) extmem = 15 * 1024; #endif physmap[0] = 0; physmap[1] = basemem * 1024; physmap_idx = 2; physmap[physmap_idx] = 0x100000; physmap[physmap_idx + 1] = physmap[physmap_idx] + extmem * 1024; #ifdef PC98 if ((under16 != 16 * 1024) && (extmem > 15 * 1024)) { /* 15M - 16M region is cut off, so need to divide chunk */ physmap[physmap_idx + 1] = under16 * 1024; physmap_idx += 2; physmap[physmap_idx] = 0x1000000; physmap[physmap_idx + 1] = physmap[2] + extmem * 1024; } #else physmap_done: #endif /* * Now, physmap contains a map of physical memory. */ #ifdef SMP /* make hole for AP bootstrap code */ physmap[1] = mp_bootaddress(physmap[1] / 1024); /* look for the MP hardware - needed for apic addresses */ i386_mp_probe(); #endif /* * Maxmem isn't the "maximum memory", it's one larger than the * highest page of the physical address space. It should be * called something like "Maxphyspage". We may adjust this * based on ``hw.physmem'' and the results of the memory test. */ Maxmem = atop(physmap[physmap_idx + 1]); #ifdef MAXMEM Maxmem = MAXMEM / 4; #endif /* * hw.physmem is a size in bytes; we also allow k, m, and g suffixes * for the appropriate modifiers. This overrides MAXMEM. */ if ((cp = getenv("hw.physmem")) != NULL) { u_int64_t AllowMem, sanity; char *ep; sanity = AllowMem = strtouq(cp, &ep, 0); if ((ep != cp) && (*ep != 0)) { switch(*ep) { case 'g': case 'G': AllowMem <<= 10; case 'm': case 'M': AllowMem <<= 10; case 'k': case 'K': AllowMem <<= 10; break; default: AllowMem = sanity = 0; } if (AllowMem < sanity) AllowMem = 0; } if (AllowMem == 0) printf("Ignoring invalid memory size of '%s'\n", cp); else Maxmem = atop(AllowMem); } if (atop(physmap[physmap_idx + 1]) != Maxmem && (boothowto & RB_VERBOSE)) printf("Physical memory use set to %uK\n", Maxmem * 4); /* * If Maxmem has been increased beyond what the system has detected, * extend the last memory segment to the new limit. */ if (atop(physmap[physmap_idx + 1]) < Maxmem) physmap[physmap_idx + 1] = ptoa(Maxmem); /* call pmap initialization to make new kernel address space */ pmap_bootstrap(first, 0); /* * Size up each available chunk of physical memory. */ physmap[0] = PAGE_SIZE; /* mask off page 0 */ pa_indx = 0; phys_avail[pa_indx++] = physmap[0]; phys_avail[pa_indx] = physmap[0]; #if 0 pte = (pt_entry_t)vtopte(KERNBASE); #else pte = (pt_entry_t)CMAP1; #endif /* * physmap is in bytes, so when converting to page boundaries, * round up the start address and round down the end address. */ for (i = 0; i <= physmap_idx; i += 2) { vm_offset_t end; end = ptoa(Maxmem); if (physmap[i + 1] < end) end = trunc_page(physmap[i + 1]); for (pa = round_page(physmap[i]); pa < end; pa += PAGE_SIZE) { int tmp, page_bad; #if 0 int *ptr = 0; #else int *ptr = (int *)CADDR1; #endif /* * block out kernel memory as not available. */ if (pa >= 0x100000 && pa < first) continue; page_bad = FALSE; /* * map page into kernel: valid, read/write,non-cacheable */ #ifdef PC98 *pte = pa | PG_V | PG_RW | pg_n; #else *pte = pa | PG_V | PG_RW | PG_N; #endif invltlb(); tmp = *(int *)ptr; /* * Test for alternating 1's and 0's */ *(volatile int *)ptr = 0xaaaaaaaa; if (*(volatile int *)ptr != 0xaaaaaaaa) { page_bad = TRUE; } /* * Test for alternating 0's and 1's */ *(volatile int *)ptr = 0x55555555; if (*(volatile int *)ptr != 0x55555555) { page_bad = TRUE; } /* * Test for all 1's */ *(volatile int *)ptr = 0xffffffff; if (*(volatile int *)ptr != 0xffffffff) { page_bad = TRUE; } /* * Test for all 0's */ *(volatile int *)ptr = 0x0; if (*(volatile int *)ptr != 0x0) { page_bad = TRUE; } /* * Restore original value. */ *(int *)ptr = tmp; /* * Adjust array of valid/good pages. */ if (page_bad == TRUE) { continue; } /* * If this good page is a continuation of the * previous set of good pages, then just increase * the end pointer. Otherwise start a new chunk. * Note that "end" points one higher than end, * making the range >= start and < end. * If we're also doing a speculative memory * test and we at or past the end, bump up Maxmem * so that we keep going. The first bad page * will terminate the loop. */ if (phys_avail[pa_indx] == pa) { phys_avail[pa_indx] += PAGE_SIZE; } else { pa_indx++; if (pa_indx == PHYS_AVAIL_ARRAY_END) { printf("Too many holes in the physical address space, giving up\n"); pa_indx--; break; } phys_avail[pa_indx++] = pa; /* start */ phys_avail[pa_indx] = pa + PAGE_SIZE; /* end */ } physmem++; } } *pte = 0; invltlb(); /* * XXX * The last chunk must contain at least one page plus the message * buffer to avoid complicating other code (message buffer address * calculation, etc.). */ while (phys_avail[pa_indx - 1] + PAGE_SIZE + round_page(MSGBUF_SIZE) >= phys_avail[pa_indx]) { physmem -= atop(phys_avail[pa_indx] - phys_avail[pa_indx - 1]); phys_avail[pa_indx--] = 0; phys_avail[pa_indx--] = 0; } Maxmem = atop(phys_avail[pa_indx]); /* Trim off space for the message buffer. */ phys_avail[pa_indx] -= round_page(MSGBUF_SIZE); avail_end = phys_avail[pa_indx]; } void init386(first) int first; { int x; struct gate_descriptor *gdp; int gsel_tss; #ifndef SMP /* table descriptors - used to load tables by microp */ struct region_descriptor r_gdt, r_idt; #endif int off; proc0.p_addr = proc0paddr; atdevbase = ISA_HOLE_START + KERNBASE; #ifdef PC98 /* * Initialize DMAC */ pc98_init_dmac(); #endif if (bootinfo.bi_modulep) { preload_metadata = (caddr_t)bootinfo.bi_modulep + KERNBASE; preload_bootstrap_relocate(KERNBASE); } else { printf("WARNING: loader(8) metadata is missing!\n"); } if (bootinfo.bi_envp) kern_envp = (caddr_t)bootinfo.bi_envp + KERNBASE; /* * make gdt memory segments, the code segment goes up to end of the * page with etext in it, the data segment goes to the end of * the address space */ /* * XXX text protection is temporarily (?) disabled. The limit was * i386_btop(round_page(etext)) - 1. */ gdt_segs[GCODE_SEL].ssd_limit = i386_btop(0) - 1; gdt_segs[GDATA_SEL].ssd_limit = i386_btop(0) - 1; #ifdef SMP gdt_segs[GPRIV_SEL].ssd_limit = i386_btop(sizeof(struct privatespace)) - 1; gdt_segs[GPRIV_SEL].ssd_base = (int) &SMP_prvspace[0]; gdt_segs[GPROC0_SEL].ssd_base = (int) &SMP_prvspace[0].globaldata.gd_common_tss; SMP_prvspace[0].globaldata.gd_prvspace = &SMP_prvspace[0].globaldata; #else gdt_segs[GPRIV_SEL].ssd_limit = i386_btop(sizeof(struct globaldata)) - 1; gdt_segs[GPRIV_SEL].ssd_base = (int) &__globaldata; gdt_segs[GPROC0_SEL].ssd_base = (int) &__globaldata.gd_common_tss; __globaldata.gd_prvspace = &__globaldata; #endif for (x = 0; x < NGDT; x++) { #ifdef BDE_DEBUGGER /* avoid overwriting db entries with APM ones */ if (x >= GAPMCODE32_SEL && x <= GAPMDATA_SEL) continue; #endif ssdtosd(&gdt_segs[x], &gdt[x].sd); } r_gdt.rd_limit = NGDT * sizeof(gdt[0]) - 1; r_gdt.rd_base = (int) gdt; lgdt(&r_gdt); /* setup curproc so that mutexes work */ PCPU_SET(curproc, &proc0); PCPU_SET(spinlocks, NULL); LIST_INIT(&proc0.p_contested); mtx_init(&sched_lock, "sched lock", MTX_SPIN | MTX_RECURSE); #ifdef SMP /* * Interrupts can happen very early, so initialize imen_mtx here, rather * than in init_locks(). */ mtx_init(&imen_mtx, "imen", MTX_SPIN); #endif /* * Giant is used early for at least debugger traps and unexpected traps. */ mtx_init(&Giant, "Giant", MTX_DEF | MTX_RECURSE); mtx_init(&proc0.p_mtx, "process lock", MTX_DEF); mtx_lock(&Giant); /* make ldt memory segments */ /* * The data segment limit must not cover the user area because we * don't want the user area to be writable in copyout() etc. (page * level protection is lost in kernel mode on 386's). Also, we * don't want the user area to be writable directly (page level * protection of the user area is not available on 486's with * CR0_WP set, because there is no user-read/kernel-write mode). * * XXX - VM_MAXUSER_ADDRESS is an end address, not a max. And it * should be spelled ...MAX_USER... */ #define VM_END_USER_RW_ADDRESS VM_MAXUSER_ADDRESS /* * The code segment limit has to cover the user area until we move * the signal trampoline out of the user area. This is safe because * the code segment cannot be written to directly. */ #define VM_END_USER_R_ADDRESS (VM_END_USER_RW_ADDRESS + UPAGES * PAGE_SIZE) ldt_segs[LUCODE_SEL].ssd_limit = i386_btop(VM_END_USER_R_ADDRESS) - 1; ldt_segs[LUDATA_SEL].ssd_limit = i386_btop(VM_END_USER_RW_ADDRESS) - 1; for (x = 0; x < sizeof ldt_segs / sizeof ldt_segs[0]; x++) ssdtosd(&ldt_segs[x], &ldt[x].sd); _default_ldt = GSEL(GLDT_SEL, SEL_KPL); lldt(_default_ldt); PCPU_SET(currentldt, _default_ldt); /* exceptions */ for (x = 0; x < NIDT; x++) setidt(x, &IDTVEC(rsvd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(0, &IDTVEC(div), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(1, &IDTVEC(dbg), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(2, &IDTVEC(nmi), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(3, &IDTVEC(bpt), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(4, &IDTVEC(ofl), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(5, &IDTVEC(bnd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(6, &IDTVEC(ill), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(7, &IDTVEC(dna), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(8, 0, SDT_SYSTASKGT, SEL_KPL, GSEL(GPANIC_SEL, SEL_KPL)); setidt(9, &IDTVEC(fpusegm), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(10, &IDTVEC(tss), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(11, &IDTVEC(missing), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(12, &IDTVEC(stk), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(13, &IDTVEC(prot), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(14, &IDTVEC(page), SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(15, &IDTVEC(rsvd), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(16, &IDTVEC(fpu), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(17, &IDTVEC(align), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(18, &IDTVEC(mchk), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(0x80, &IDTVEC(int0x80_syscall), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); r_idt.rd_limit = sizeof(idt0) - 1; r_idt.rd_base = (int) idt; lidt(&r_idt); /* * We need this mutex before the console probe. */ mtx_init(&clock_lock, "clk", MTX_SPIN | MTX_RECURSE); /* * Initialize the console before we print anything out. */ cninit(); #ifdef DEV_ISA isa_defaultirq(); #endif #ifdef DDB kdb_init(); if (boothowto & RB_KDB) Debugger("Boot flags requested debugger"); #endif finishidentcpu(); /* Final stage of CPU initialization */ setidt(6, &IDTVEC(ill), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); setidt(13, &IDTVEC(prot), SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); initializecpu(); /* Initialize CPU registers */ /* make an initial tss so cpu can get interrupt stack on syscall! */ PCPU_SET(common_tss.tss_esp0, (int) proc0.p_addr + UPAGES*PAGE_SIZE - 16); PCPU_SET(common_tss.tss_ss0, GSEL(GDATA_SEL, SEL_KPL)); gsel_tss = GSEL(GPROC0_SEL, SEL_KPL); private_tss = 0; PCPU_SET(tss_gdt, &gdt[GPROC0_SEL].sd); PCPU_SET(common_tssd, *PCPU_GET(tss_gdt)); PCPU_SET(common_tss.tss_ioopt, (sizeof (struct i386tss)) << 16); ltr(gsel_tss); dblfault_tss.tss_esp = dblfault_tss.tss_esp0 = dblfault_tss.tss_esp1 = dblfault_tss.tss_esp2 = (int) &dblfault_stack[sizeof(dblfault_stack)]; dblfault_tss.tss_ss = dblfault_tss.tss_ss0 = dblfault_tss.tss_ss1 = dblfault_tss.tss_ss2 = GSEL(GDATA_SEL, SEL_KPL); dblfault_tss.tss_cr3 = (int)IdlePTD; dblfault_tss.tss_eip = (int) dblfault_handler; dblfault_tss.tss_eflags = PSL_KERNEL; dblfault_tss.tss_ds = dblfault_tss.tss_es = dblfault_tss.tss_gs = GSEL(GDATA_SEL, SEL_KPL); dblfault_tss.tss_fs = GSEL(GPRIV_SEL, SEL_KPL); dblfault_tss.tss_cs = GSEL(GCODE_SEL, SEL_KPL); dblfault_tss.tss_ldt = GSEL(GLDT_SEL, SEL_KPL); vm86_initialize(); getmemsize(first); /* now running on new page tables, configured,and u/iom is accessible */ /* Map the message buffer. */ for (off = 0; off < round_page(MSGBUF_SIZE); off += PAGE_SIZE) pmap_kenter((vm_offset_t)msgbufp + off, avail_end + off); msgbufinit(msgbufp, MSGBUF_SIZE); /* make a call gate to reenter kernel with */ gdp = &ldt[LSYS5CALLS_SEL].gd; x = (int) &IDTVEC(lcall_syscall); gdp->gd_looffset = x; gdp->gd_selector = GSEL(GCODE_SEL,SEL_KPL); gdp->gd_stkcpy = 1; gdp->gd_type = SDT_SYS386CGT; gdp->gd_dpl = SEL_UPL; gdp->gd_p = 1; gdp->gd_hioffset = x >> 16; /* XXX does this work? */ ldt[LBSDICALLS_SEL] = ldt[LSYS5CALLS_SEL]; ldt[LSOL26CALLS_SEL] = ldt[LSYS5CALLS_SEL]; /* transfer to user mode */ _ucodesel = LSEL(LUCODE_SEL, SEL_UPL); _udatasel = LSEL(LUDATA_SEL, SEL_UPL); /* setup proc 0's pcb */ proc0.p_addr->u_pcb.pcb_flags = 0; proc0.p_addr->u_pcb.pcb_cr3 = (int)IdlePTD; proc0.p_addr->u_pcb.pcb_ext = 0; proc0.p_md.md_regs = &proc0_tf; } #if defined(I586_CPU) && !defined(NO_F00F_HACK) static void f00f_hack(void *unused); SYSINIT(f00f_hack, SI_SUB_INTRINSIC, SI_ORDER_FIRST, f00f_hack, NULL); static void f00f_hack(void *unused) { struct gate_descriptor *new_idt; #ifndef SMP struct region_descriptor r_idt; #endif vm_offset_t tmp; if (!has_f00f_bug) return; printf("Intel Pentium detected, installing workaround for F00F bug\n"); r_idt.rd_limit = sizeof(idt0) - 1; tmp = kmem_alloc(kernel_map, PAGE_SIZE * 2); if (tmp == 0) panic("kmem_alloc returned 0"); if (((unsigned int)tmp & (PAGE_SIZE-1)) != 0) panic("kmem_alloc returned non-page-aligned memory"); /* Put the first seven entries in the lower page */ new_idt = (struct gate_descriptor*)(tmp + PAGE_SIZE - (7*8)); bcopy(idt, new_idt, sizeof(idt0)); r_idt.rd_base = (int)new_idt; lidt(&r_idt); idt = new_idt; mtx_lock(&vm_mtx); if (vm_map_protect(kernel_map, tmp, tmp + PAGE_SIZE, VM_PROT_READ, FALSE) != KERN_SUCCESS) panic("vm_map_protect failed"); mtx_unlock(&vm_mtx); return; } #endif /* defined(I586_CPU) && !NO_F00F_HACK */ int ptrace_set_pc(p, addr) struct proc *p; unsigned long addr; { p->p_md.md_regs->tf_eip = addr; return (0); } int ptrace_single_step(p) struct proc *p; { p->p_md.md_regs->tf_eflags |= PSL_T; return (0); } int ptrace_read_u_check(p, addr, len) struct proc *p; vm_offset_t addr; size_t len; { vm_offset_t gap; if ((vm_offset_t) (addr + len) < addr) return EPERM; if ((vm_offset_t) (addr + len) <= sizeof(struct user)) return 0; gap = (char *) p->p_md.md_regs - (char *) p->p_addr; if ((vm_offset_t) addr < gap) return EPERM; if ((vm_offset_t) (addr + len) <= (vm_offset_t) (gap + sizeof(struct trapframe))) return 0; return EPERM; } int ptrace_write_u(p, off, data) struct proc *p; vm_offset_t off; long data; { struct trapframe frame_copy; vm_offset_t min; struct trapframe *tp; /* * Privileged kernel state is scattered all over the user area. * Only allow write access to parts of regs and to fpregs. */ min = (char *)p->p_md.md_regs - (char *)p->p_addr; if (off >= min && off <= min + sizeof(struct trapframe) - sizeof(int)) { tp = p->p_md.md_regs; frame_copy = *tp; *(int *)((char *)&frame_copy + (off - min)) = data; if (!EFL_SECURE(frame_copy.tf_eflags, tp->tf_eflags) || !CS_SECURE(frame_copy.tf_cs)) return (EINVAL); *(int*)((char *)p->p_addr + off) = data; return (0); } min = offsetof(struct user, u_pcb) + offsetof(struct pcb, pcb_savefpu); if (off >= min && off <= min + sizeof(struct save87) - sizeof(int)) { *(int*)((char *)p->p_addr + off) = data; return (0); } return (EFAULT); } int fill_regs(p, regs) struct proc *p; struct reg *regs; { struct pcb *pcb; struct trapframe *tp; tp = p->p_md.md_regs; regs->r_fs = tp->tf_fs; regs->r_es = tp->tf_es; regs->r_ds = tp->tf_ds; regs->r_edi = tp->tf_edi; regs->r_esi = tp->tf_esi; regs->r_ebp = tp->tf_ebp; regs->r_ebx = tp->tf_ebx; regs->r_edx = tp->tf_edx; regs->r_ecx = tp->tf_ecx; regs->r_eax = tp->tf_eax; regs->r_eip = tp->tf_eip; regs->r_cs = tp->tf_cs; regs->r_eflags = tp->tf_eflags; regs->r_esp = tp->tf_esp; regs->r_ss = tp->tf_ss; pcb = &p->p_addr->u_pcb; regs->r_gs = pcb->pcb_gs; return (0); } int set_regs(p, regs) struct proc *p; struct reg *regs; { struct pcb *pcb; struct trapframe *tp; tp = p->p_md.md_regs; if (!EFL_SECURE(regs->r_eflags, tp->tf_eflags) || !CS_SECURE(regs->r_cs)) return (EINVAL); tp->tf_fs = regs->r_fs; tp->tf_es = regs->r_es; tp->tf_ds = regs->r_ds; tp->tf_edi = regs->r_edi; tp->tf_esi = regs->r_esi; tp->tf_ebp = regs->r_ebp; tp->tf_ebx = regs->r_ebx; tp->tf_edx = regs->r_edx; tp->tf_ecx = regs->r_ecx; tp->tf_eax = regs->r_eax; tp->tf_eip = regs->r_eip; tp->tf_cs = regs->r_cs; tp->tf_eflags = regs->r_eflags; tp->tf_esp = regs->r_esp; tp->tf_ss = regs->r_ss; pcb = &p->p_addr->u_pcb; pcb->pcb_gs = regs->r_gs; return (0); } int fill_fpregs(p, fpregs) struct proc *p; struct fpreg *fpregs; { bcopy(&p->p_addr->u_pcb.pcb_savefpu, fpregs, sizeof *fpregs); return (0); } int set_fpregs(p, fpregs) struct proc *p; struct fpreg *fpregs; { bcopy(fpregs, &p->p_addr->u_pcb.pcb_savefpu, sizeof *fpregs); return (0); } int fill_dbregs(p, dbregs) struct proc *p; struct dbreg *dbregs; { struct pcb *pcb; pcb = &p->p_addr->u_pcb; dbregs->dr0 = pcb->pcb_dr0; dbregs->dr1 = pcb->pcb_dr1; dbregs->dr2 = pcb->pcb_dr2; dbregs->dr3 = pcb->pcb_dr3; dbregs->dr4 = 0; dbregs->dr5 = 0; dbregs->dr6 = pcb->pcb_dr6; dbregs->dr7 = pcb->pcb_dr7; return (0); } int set_dbregs(p, dbregs) struct proc *p; struct dbreg *dbregs; { struct pcb *pcb; int i; u_int32_t mask1, mask2; /* * Don't let an illegal value for dr7 get set. Specifically, * check for undefined settings. Setting these bit patterns * result in undefined behaviour and can lead to an unexpected * TRCTRAP. */ for (i = 0, mask1 = 0x3<<16, mask2 = 0x2<<16; i < 8; i++, mask1 <<= 2, mask2 <<= 2) if ((dbregs->dr7 & mask1) == mask2) return (EINVAL); if (dbregs->dr7 & 0x0000fc00) return (EINVAL); pcb = &p->p_addr->u_pcb; /* * Don't let a process set a breakpoint that is not within the * process's address space. If a process could do this, it * could halt the system by setting a breakpoint in the kernel * (if ddb was enabled). Thus, we need to check to make sure * that no breakpoints are being enabled for addresses outside * process's address space, unless, perhaps, we were called by * uid 0. * * XXX - what about when the watched area of the user's * address space is written into from within the kernel * ... wouldn't that still cause a breakpoint to be generated * from within kernel mode? */ if (suser(p) != 0) { if (dbregs->dr7 & 0x3) { /* dr0 is enabled */ if (dbregs->dr0 >= VM_MAXUSER_ADDRESS) return (EINVAL); } if (dbregs->dr7 & (0x3<<2)) { /* dr1 is enabled */ if (dbregs->dr1 >= VM_MAXUSER_ADDRESS) return (EINVAL); } if (dbregs->dr7 & (0x3<<4)) { /* dr2 is enabled */ if (dbregs->dr2 >= VM_MAXUSER_ADDRESS) return (EINVAL); } if (dbregs->dr7 & (0x3<<6)) { /* dr3 is enabled */ if (dbregs->dr3 >= VM_MAXUSER_ADDRESS) return (EINVAL); } } pcb->pcb_dr0 = dbregs->dr0; pcb->pcb_dr1 = dbregs->dr1; pcb->pcb_dr2 = dbregs->dr2; pcb->pcb_dr3 = dbregs->dr3; pcb->pcb_dr6 = dbregs->dr6; pcb->pcb_dr7 = dbregs->dr7; pcb->pcb_flags |= PCB_DBREGS; return (0); } /* * Return > 0 if a hardware breakpoint has been hit, and the * breakpoint was in user space. Return 0, otherwise. */ int user_dbreg_trap(void) { u_int32_t dr7, dr6; /* debug registers dr6 and dr7 */ u_int32_t bp; /* breakpoint bits extracted from dr6 */ int nbp; /* number of breakpoints that triggered */ caddr_t addr[4]; /* breakpoint addresses */ int i; dr7 = rdr7(); if ((dr7 & 0x000000ff) == 0) { /* * all GE and LE bits in the dr7 register are zero, * thus the trap couldn't have been caused by the * hardware debug registers */ return 0; } nbp = 0; dr6 = rdr6(); bp = dr6 & 0x0000000f; if (!bp) { /* * None of the breakpoint bits are set meaning this * trap was not caused by any of the debug registers */ return 0; } /* * at least one of the breakpoints were hit, check to see * which ones and if any of them are user space addresses */ if (bp & 0x01) { addr[nbp++] = (caddr_t)rdr0(); } if (bp & 0x02) { addr[nbp++] = (caddr_t)rdr1(); } if (bp & 0x04) { addr[nbp++] = (caddr_t)rdr2(); } if (bp & 0x08) { addr[nbp++] = (caddr_t)rdr3(); } for (i=0; i /* * Determine the size of the transfer, and make sure it is * within the boundaries of the partition. Adjust transfer * if needed, and signal errors or early completion. */ int bounds_check_with_label(struct bio *bp, struct disklabel *lp, int wlabel) { struct partition *p = lp->d_partitions + dkpart(bp->bio_dev); int labelsect = lp->d_partitions[0].p_offset; int maxsz = p->p_size, sz = (bp->bio_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT; /* overwriting disk label ? */ /* XXX should also protect bootstrap in first 8K */ if (bp->bio_blkno + p->p_offset <= LABELSECTOR + labelsect && #if LABELSECTOR != 0 bp->bio_blkno + p->p_offset + sz > LABELSECTOR + labelsect && #endif (bp->bio_cmd == BIO_WRITE) && wlabel == 0) { bp->bio_error = EROFS; goto bad; } #if defined(DOSBBSECTOR) && defined(notyet) /* overwriting master boot record? */ if (bp->bio_blkno + p->p_offset <= DOSBBSECTOR && (bp->bio_cmd == BIO_WRITE) && wlabel == 0) { bp->bio_error = EROFS; goto bad; } #endif /* beyond partition? */ if (bp->bio_blkno < 0 || bp->bio_blkno + sz > maxsz) { /* if exactly at end of disk, return an EOF */ if (bp->bio_blkno == maxsz) { bp->bio_resid = bp->bio_bcount; return(0); } /* or truncate if part of it fits */ sz = maxsz - bp->bio_blkno; if (sz <= 0) { bp->bio_error = EINVAL; goto bad; } bp->bio_bcount = sz << DEV_BSHIFT; } bp->bio_pblkno = bp->bio_blkno + p->p_offset; return(1); bad: bp->bio_flags |= BIO_ERROR; return(-1); } #ifdef DDB /* * Provide inb() and outb() as functions. They are normally only * available as macros calling inlined functions, thus cannot be * called inside DDB. * * The actual code is stolen from , and de-inlined. */ #undef inb #undef outb /* silence compiler warnings */ u_char inb(u_int); void outb(u_int, u_char); u_char inb(u_int port) { u_char data; /* * We use %%dx and not %1 here because i/o is done at %dx and not at * %edx, while gcc generates inferior code (movw instead of movl) * if we tell it to load (u_short) port. */ __asm __volatile("inb %%dx,%0" : "=a" (data) : "d" (port)); return (data); } void outb(u_int port, u_char data) { u_char al; /* * Use an unnecessary assignment to help gcc's register allocator. * This make a large difference for gcc-1.40 and a tiny difference * for gcc-2.6.0. For gcc-1.40, al had to be ``asm("ax")'' for * best results. gcc-2.6.0 can't handle this. */ al = data; __asm __volatile("outb %0,%%dx" : : "a" (al), "d" (port)); } #endif /* DDB */ Index: head/sys/pc98/pc98/wd.c =================================================================== --- head/sys/pc98/pc98/wd.c (revision 78134) +++ head/sys/pc98/pc98/wd.c (revision 78135) @@ -1,2120 +1,2120 @@ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * 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. * 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. * * from: @(#)wd.c 7.2 (Berkeley) 5/9/91 * $FreeBSD$ */ /* TODO: * o Bump error count after timeout. * o Satisfy ATA timing in all cases. * o Finish merging berry/sos timeout code (bump error count...). * o Don't use polling except for initialization. Need to * reorganize the state machine. Then "extra" interrupts * shouldn't happen (except maybe one for initialization). * o Support extended DOS partitions. * o Support swapping to DOS partitions. * o Handle bad sectors, clustering, disklabelling, DOS * partitions and swapping driver-independently. Use * i386/dkbad.c for bad sectors. Swapping will need new * driver entries for polled reinit and polled write). */ #include "wdc.h" #undef NWD #define NWD (NWDC * 4) /* 4 drives per wdc on PC98 */ #if NWDC > 0 #include "opt_hw_wdog.h" #include "opt_ide_delay.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PC98 #include #include #include #else #include #endif #include #include #include #include #include #include #ifndef COMPAT_OLDISA #error "The wdc device requires the old isa compatibility shims" #endif extern void wdstart(int ctrlr); #ifdef IDE_DELAY #define TIMEOUT IDE_DELAY #else #define TIMEOUT 10000 #endif #define RETRIES 5 /* number of retries before giving up */ #define RECOVERYTIME 500000 /* usec for controller to recover after err */ #define MAXTRANSFER 255 /* max size of transfer in sectors */ /* correct max is 256 but some controllers */ /* can't handle that in all cases */ #define WDOPT_32BIT 0x8000 #define WDOPT_SLEEPHACK 0x4000 #define WDOPT_DMA 0x2000 #define WDOPT_LBA 0x1000 #define WDOPT_FORCEHD(x) (((x)&0x0f00)>>8) #define WDOPT_MULTIMASK 0x00ff #ifdef PC98 static __inline u_char epson_errorf(int wdc) { u_char wdc_error; outb(wdc, inb(0x82) | 0x40); wdc_error = (u_char)epson_inb(wdc); outb(wdc, inb(0x82) & ~0x40); return ((u_char)wdc_error); } #endif /* * Drive states. Used to initialize drive. */ #define CLOSED 0 /* disk is closed. */ #define WANTOPEN 1 /* open requested, not started */ #define RECAL 2 /* doing restore */ #define OPEN 3 /* done with open */ #define PRIMARY 0 /* * Disk geometry. A small part of struct disklabel. * XXX disklabel.5 contains an old clone of disklabel.h. */ struct diskgeom { u_long d_secsize; /* # of bytes per sector */ u_long d_nsectors; /* # of data sectors per track */ u_long d_ntracks; /* # of tracks per cylinder */ u_long d_ncylinders; /* # of data cylinders per unit */ u_long d_secpercyl; /* # of data sectors per cylinder */ u_long d_secperunit; /* # of data sectors per unit */ u_long d_precompcyl; /* XXX always 0 */ }; /* * The structure of a disk drive. */ struct softc { u_int dk_bc; /* byte count left */ short dk_skip; /* blocks already transferred */ int dk_ctrlr; /* physical controller number */ int dk_ctrlr_cmd640;/* controller number for CMD640 quirk */ u_int32_t dk_unit; /* physical unit number */ u_int32_t dk_lunit; /* logical unit number */ u_int32_t dk_interface; /* interface (two ctrlrs per interface) */ char dk_state; /* control state */ u_char dk_status; /* copy of status reg. */ u_char dk_error; /* copy of error reg. */ u_char dk_timeout; /* countdown to next timeout */ u_int32_t dk_port; /* i/o port base */ u_int32_t dk_altport; /* altstatus port base */ u_long cfg_flags; /* configured characteristics */ short dk_flags; /* drive characteristics found */ #define DKFL_SINGLE 0x00004 /* sector at a time mode */ #define DKFL_ERROR 0x00008 /* processing a disk error */ #define DKFL_LABELLING 0x00080 /* readdisklabel() in progress */ #define DKFL_32BIT 0x00100 /* use 32-bit i/o mode */ #define DKFL_MULTI 0x00200 /* use multi-i/o mode */ #define DKFL_BADSCAN 0x00400 /* report all errors */ #define DKFL_USEDMA 0x00800 /* use DMA for data transfers */ #define DKFL_DMA 0x01000 /* using DMA on this transfer-- DKFL_SINGLE * overrides this */ #define DKFL_LBA 0x02000 /* use LBA for data transfers */ struct wdparams dk_params; /* ESDI/IDE drive/controller parameters */ unsigned int dk_multi; /* multi transfers */ int dk_currentiosize; /* current io size */ struct diskgeom dk_dd; /* device configuration data */ struct diskslices *dk_slices; /* virtual drives */ void *dk_dmacookie; /* handle for DMA services */ struct devstat dk_stats; /* devstat entry */ struct disk disk; }; #define WD_COUNT_RETRIES static int wdtest = 0; static struct softc *wddrives[NWD]; /* table of units */ static struct bio_queue_head drive_queue[NWD]; /* head of queue per drive */ static struct { int b_active; } wdutab[NWD]; /* static struct bio wdtab[NWDC]; */ static struct { struct bio_queue_head controller_queue; int b_errcnt; int b_active; } wdtab[NWDC]; struct wddma wddma[NWDC]; #ifdef notyet static struct bio rwdbuf[NWD]; /* buffers for raw IO */ #endif #ifdef PC98 static short wd_ctlr; static int old_epson_note; #endif static int wdprobe(struct isa_device *dvp); static int wdattach(struct isa_device *dvp); static void wdustart(struct softc *du); static int wdcontrol(struct bio *bp); static int wdcommand(struct softc *du, u_int cylinder, u_int head, u_int sector, u_int count, u_int command); static int wdsetctlr(struct softc *du); #if 0 static int wdwsetctlr(struct softc *du); #endif static int wdsetmode(int mode, void *wdinfo); static int wdgetctlr(struct softc *du); static void wderror(struct bio *bp, struct softc *du, char *mesg); static void wdflushirq(struct softc *du, int old_ipl); static int wdreset(struct softc *du); static void wdsleep(int ctrlr, char *wmesg); static timeout_t wdtimeout; static int wdunwedge(struct softc *du); static int wdwait(struct softc *du, u_char bits_wanted, int timeout); struct isa_driver wdcdriver = { INTR_TYPE_BIO, wdprobe, wdattach, "wdc", }; COMPAT_ISA_DRIVER(wdc, wdcdriver); static d_open_t wdopen; static d_strategy_t wdstrategy; #define CDEV_MAJOR 3 static struct cdevsw wd_cdevsw = { /* open */ wdopen, /* close */ nullclose, /* read */ physread, /* write */ physwrite, /* ioctl */ noioctl, /* poll */ nopoll, /* mmap */ nommap, /* strategy */ wdstrategy, /* name */ "wd", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ D_DISK, }; static struct cdevsw wddisk_cdevsw; static int atapictrlr; static int eide_quirks; /* * Here we use the pci-subsystem to find out, whether there is * a cmd640b-chip attached on this pci-bus. This public routine * will be called by ide_pci.c */ void wdc_pci(int quirks) { eide_quirks = quirks; } /* * Probe for controller. */ static int wdprobe(struct isa_device *dvp) { int unit = dvp->id_unit; int interface; struct softc *du; if (unit >= NWDC) return (0); du = malloc(sizeof *du, M_TEMP, M_NOWAIT | M_ZERO); if (du == NULL) return (0); du->dk_ctrlr = dvp->id_unit; interface = du->dk_ctrlr / 2; du->dk_interface = interface; du->dk_port = dvp->id_iobase; if (wddma[interface].wdd_candma != NULL) { du->dk_dmacookie = wddma[interface].wdd_candma(dvp->id_iobase, du->dk_ctrlr, du->dk_unit); du->dk_altport = wddma[interface].wdd_altiobase(du->dk_dmacookie); } if (du->dk_altport == 0) du->dk_altport = du->dk_port + wd_ctlr; /* check if we have registers that work */ #ifdef PC98 /* XXX ATAPI support isn't imported */ wd_ctlr = wd_ctlr_nec; /* wdreg.h */ old_epson_note=0; if (pc98_machine_type & M_EPSON_PC98 ) { switch (epson_machine_id) { case 0x20: case 0x22: case 0x2a: /* note A/W/WR */ du->dk_port = IO_WD1_EPSON; /* pc98.h */ dvp->id_iobase = IO_WD1_EPSON; /* pc98.h */ wd_ctlr = wd_ctlr_epson; /* wdreg.h */ old_epson_note = 1; /* for OLD EPSON NOTE */ break; default: break; } } du->dk_altport = du->dk_port + wd_ctlr; #if 0 if ((PC98_SYSTEM_PARAMETER(0x55d) & 3) == 0) { goto nodevice; } #endif outb(0x432,(du->dk_unit)%2); #else /* IBM-PC */ outb(du->dk_port + wd_sdh, WDSD_IBM); /* set unit 0 */ outb(du->dk_port + wd_cyl_lo, 0xa5); /* wd_cyl_lo is read/write */ if (inb(du->dk_port + wd_cyl_lo) == 0xff) { /* XXX too weak */ /* There is no master, try the ATAPI slave. */ du->dk_unit = 1; outb(du->dk_port + wd_sdh, WDSD_IBM | 0x10); outb(du->dk_port + wd_cyl_lo, 0xa5); if (inb(du->dk_port + wd_cyl_lo) == 0xff) goto nodevice; } #endif /* PC98 */ if (wdreset(du) == 0) goto reset_ok; /* test for ATAPI signature */ outb(du->dk_port + wd_sdh, WDSD_IBM); /* master */ if (inb(du->dk_port + wd_cyl_lo) == 0x14 && inb(du->dk_port + wd_cyl_hi) == 0xeb) goto reset_ok; #ifdef PC98 du->dk_unit = 2; #else du->dk_unit = 1; #endif outb(du->dk_port + wd_sdh, WDSD_IBM | 0x10); /* slave */ if (inb(du->dk_port + wd_cyl_lo) == 0x14 && inb(du->dk_port + wd_cyl_hi) == 0xeb) goto reset_ok; #ifdef PC98 du->dk_unit = 1; outb(0x432,(du->dk_unit)%2); if (wdreset(du) == 0) goto reset_ok; /* test for ATAPI signature */ outb(du->dk_port + wd_sdh, WDSD_IBM); /* master */ if (inb(du->dk_port + wd_cyl_lo) == 0x14 && inb(du->dk_port + wd_cyl_hi) == 0xeb) goto reset_ok; du->dk_unit = 3; outb(du->dk_port + wd_sdh, WDSD_IBM | 0x10); /* slave */ if (inb(du->dk_port + wd_cyl_lo) == 0x14 && inb(du->dk_port + wd_cyl_hi) == 0xeb) goto reset_ok; #endif DELAY(RECOVERYTIME); if (wdreset(du) != 0) { goto nodevice; } reset_ok: /* execute a controller only command */ if (wdcommand(du, 0, 0, 0, 0, WDCC_DIAGNOSE) != 0 || wdwait(du, 0, TIMEOUT) < 0) { goto nodevice; } /* * drive(s) did not time out during diagnostic : * Get error status and check that both drives are OK. * Table 9-2 of ATA specs suggests that we must check for * a value of 0x01 * * Strangely, some controllers will return a status of * 0x81 (drive 0 OK, drive 1 failure), and then when * the DRV bit is set, return status of 0x01 (OK) for * drive 2. (This seems to contradict the ATA spec.) */ if (old_epson_note) du->dk_error = epson_errorf(du->dk_port + wd_error); else du->dk_error = inb(du->dk_port + wd_error); if(du->dk_error != 0x01 && du->dk_error != 0) { if(du->dk_error & 0x80) { /* drive 1 failure */ /* first set the DRV bit */ u_int sdh; if (old_epson_note) sdh = epson_inb(du->dk_port+ wd_sdh); else sdh = inb(du->dk_port+ wd_sdh); sdh = sdh | 0x10; if (old_epson_note) epson_outb(du->dk_port+ wd_sdh, sdh); else outb(du->dk_port+ wd_sdh, sdh); /* Wait, to make sure drv 1 has completed diags */ if ( wdwait(du, 0, TIMEOUT) < 0) goto nodevice; /* Get status for drive 1 */ if (old_epson_note) du->dk_error = epson_errorf(du->dk_port + wd_error); else du->dk_error = inb(du->dk_port + wd_error); /* printf("Error (drv 1) : %x\n", du->dk_error); */ /* * Sometimes (apparently mostly with ATAPI * drives involved) 0x81 really means 0x81 * (drive 0 OK, drive 1 failed). */ if(du->dk_error != 0x01 && du->dk_error != 0x81) goto nodevice; } else /* drive 0 fail */ goto nodevice; } free(du, M_TEMP); return (IO_WDCSIZE); nodevice: free(du, M_TEMP); return (0); } /* * Attach each drive if possible. */ static int wdattach(struct isa_device *dvp) { int unit, lunit, flags, i; struct softc *du; struct wdparams *wp; static char buf[] = "wdcXXX"; + const char *dname; dvp->id_intr = wdintr; if (dvp->id_unit >= NWDC) return (0); if (eide_quirks & Q_CMD640B) { if (dvp->id_unit == PRIMARY) { printf("wdc0: CMD640B workaround enabled\n"); bioq_init(&wdtab[PRIMARY].controller_queue); } } else bioq_init(&wdtab[dvp->id_unit].controller_queue); sprintf(buf, "wdc%d", dvp->id_unit); - i = -1; - while ((i = resource_query_string(i, "at", buf)) != -1) { - if (strcmp(resource_query_name(i), "wd")) + i = 0; + while ((resource_find_match(&i, &dname, &lunit, "at", buf)) == 0) { + if (strcmp(dname, "wd")) /* Avoid a bit of foot shooting. */ continue; - lunit = resource_query_unit(i); if (lunit >= NWD) continue; #ifdef PC98 if ((lunit%2)!=0) { if ((PC98_SYSTEM_PARAMETER(0x457) & 0x40)==0) { continue; } } #endif if (resource_int_value("wd", lunit, "drive", &unit) != 0) continue; if (resource_int_value("wd", lunit, "flags", &flags) != 0) flags = 0; du = malloc(sizeof *du, M_TEMP, M_NOWAIT | M_ZERO); if (du == NULL) continue; if (wddrives[lunit] != NULL) panic("drive attached twice"); wddrives[lunit] = du; bioq_init(&drive_queue[lunit]); du->dk_ctrlr = dvp->id_unit; if (eide_quirks & Q_CMD640B) { du->dk_ctrlr_cmd640 = PRIMARY; } else { du->dk_ctrlr_cmd640 = du->dk_ctrlr; } du->dk_unit = unit; du->dk_lunit = lunit; du->dk_port = dvp->id_iobase; du->dk_altport = du->dk_port + wd_ctlr; /* * Use the individual device flags or the controller * flags. */ du->cfg_flags = flags | ((dvp->id_flags) >> (16 * unit)); if (wdgetctlr(du) == 0) { /* * Print out description of drive. * wdp_model may not be null terminated. */ printf("wdc%d: unit %d (wd%d): <%.*s>", dvp->id_unit, unit, lunit, (int)sizeof(du->dk_params.wdp_model), du->dk_params.wdp_model); if (du->dk_flags & DKFL_LBA) printf(", LBA"); if (du->dk_flags & DKFL_USEDMA) printf(", DMA"); if (du->dk_flags & DKFL_32BIT) printf(", 32-bit"); if (du->dk_multi > 1) printf(", multi-block-%d", du->dk_multi); if (du->cfg_flags & WDOPT_SLEEPHACK) printf(", sleep-hack"); printf("\n"); if (du->dk_params.wdp_heads == 0) printf("wd%d: size unknown, using %s values\n", lunit, du->dk_dd.d_secperunit > 17 ? "BIOS" : "fake"); printf( "wd%d: %luMB (%lu sectors), " "%lu cyls, %lu heads, %lu S/T, %lu B/S\n", lunit, du->dk_dd.d_secperunit / ((1024L * 1024L) / du->dk_dd.d_secsize), du->dk_dd.d_secperunit, du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks, du->dk_dd.d_nsectors, du->dk_dd.d_secsize); if (bootverbose) { wp = &du->dk_params; printf( "wd%d: ATA INQUIRE valid = %04x, " "dmamword = %04x, apio = %04x, " "udma = %04x\n", du->dk_lunit, wp->wdp_atavalid, wp->wdp_dmamword, wp->wdp_eidepiomodes, wp->wdp_udmamode); } /* * Start timeout routine for this drive. * XXX timeout should be per controller. */ wdtimeout(du); /* * Export the drive to the devstat interface. */ devstat_add_entry(&du->dk_stats, "wd", lunit, du->dk_dd.d_secsize, DEVSTAT_NO_ORDERED_TAGS, DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_IDE, DEVSTAT_PRIORITY_DISK); /* * Register this media as a disk */ disk_create(lunit, &du->disk, 0, &wd_cdevsw, &wddisk_cdevsw); } else { free(du, M_TEMP); wddrives[lunit] = NULL; } } /* * Probe all free IDE units, searching for ATAPI drives. */ #ifdef PC98 for (unit=0; unit<4; ++unit) { outb(0x432,unit%2); #else for (unit=0; unit<2; ++unit) { #endif /* PC98 */ for (lunit=0; lunitdk_ctrlr == dvp->id_unit && wddrives[lunit]->dk_unit == unit) goto next; if (atapi_attach (dvp->id_unit, unit, dvp->id_iobase)) atapictrlr = dvp->id_unit; next: ; } /* * Discard any interrupts generated by wdgetctlr(). wdflushirq() * doesn't work now because the ambient ipl is too high. */ if (eide_quirks & Q_CMD640B) { wdtab[PRIMARY].b_active = 2; } else { wdtab[dvp->id_unit].b_active = 2; } return (1); } /* Read/write routine for a buffer. Finds the proper unit, range checks * arguments, and schedules the transfer. Does not wait for the transfer * to complete. Multi-page transfers are supported. All I/O requests must * be a multiple of a sector in length. */ void wdstrategy(register struct bio *bp) { struct softc *du; int lunit = dkunit(bp->bio_dev); int s; /* valid unit, controller, and request? */ if (lunit >= NWD || bp->bio_blkno < 0 || (du = wddrives[lunit]) == NULL || bp->bio_bcount % DEV_BSIZE != 0) { bp->bio_error = EINVAL; bp->bio_flags |= BIO_ERROR; goto done; } #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif /* queue transfer on drive, activate drive and controller if idle */ s = splbio(); /* Pick up changes made by readdisklabel(). */ if (du->dk_flags & DKFL_LABELLING && du->dk_state > RECAL) { wdsleep(du->dk_ctrlr, "wdlab"); du->dk_state = WANTOPEN; } bioqdisksort(&drive_queue[lunit], bp); if (wdutab[lunit].b_active == 0) wdustart(du); /* start drive */ if (wdtab[du->dk_ctrlr_cmd640].b_active == 0) wdstart(du->dk_ctrlr); /* start controller */ /* Tell devstat that we have started a transaction on this drive */ devstat_start_transaction(&du->dk_stats); splx(s); return; done: /* toss transfer, we're done early */ biodone(bp); } /* * Routine to queue a command to the controller. The unit's * request is linked into the active list for the controller. * If the controller is idle, the transfer is started. */ static void wdustart(register struct softc *du) { register struct bio *bp; int ctrlr = du->dk_ctrlr_cmd640; #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif /* unit already active? */ if (wdutab[du->dk_lunit].b_active) return; bp = bioq_first(&drive_queue[du->dk_lunit]); if (bp == NULL) { /* yes, an assign */ return; } /* * store away which device we came from. */ bp->bio_driver1 = du; bioq_remove(&drive_queue[du->dk_lunit], bp); /* link onto controller queue */ bioq_insert_tail(&wdtab[ctrlr].controller_queue, bp); /* mark the drive unit as busy */ wdutab[du->dk_lunit].b_active = 1; } /* * Controller startup routine. This does the calculation, and starts * a single-sector read or write operation. Called to start a transfer, * or from the interrupt routine to continue a multi-sector transfer. * RESTRICTIONS: * 1. The transfer length must be an exact multiple of the sector size. */ void wdstart(int ctrlr) { register struct softc *du; register struct bio *bp; struct diskgeom *lp; /* XXX sic */ long blknum; long secpertrk, secpercyl; u_int lunit; u_int count; int ctrlr_atapi; if (eide_quirks & Q_CMD640B) { ctrlr = PRIMARY; ctrlr_atapi = atapictrlr; } else { ctrlr_atapi = ctrlr; } if (wdtab[ctrlr].b_active == 2) wdtab[ctrlr].b_active = 0; if (wdtab[ctrlr].b_active) return; /* is there a drive for the controller to do a transfer with? */ bp = bioq_first(&wdtab[ctrlr].controller_queue); if (bp == NULL) { if (atapi_start && atapi_start (ctrlr_atapi)) /* mark controller active in ATAPI mode */ wdtab[ctrlr].b_active = 3; return; } /* obtain controller and drive information */ lunit = dkunit(bp->bio_dev); du = wddrives[lunit]; #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif /* if not really a transfer, do control operations specially */ if (du->dk_state < OPEN) { if (du->dk_state != WANTOPEN) printf("wd%d: wdstart: weird dk_state %d\n", du->dk_lunit, du->dk_state); if (wdcontrol(bp) != 0) printf("wd%d: wdstart: wdcontrol returned nonzero, state = %d\n", du->dk_lunit, du->dk_state); return; } /* calculate transfer details */ blknum = bp->bio_pblkno + du->dk_skip; #ifdef WDDEBUG if (du->dk_skip == 0) printf("wd%d: wdstart: %s %d@%d; map ", lunit, (bp->bio_cmd == BIO_READ) ? "read" : "write", bp->bio_bcount, blknum); else { if (old_epson_note) printf(" %d)%x", du->dk_skip, epson_inb(du->dk_altport); else printf(" %d)%x", du->dk_skip, inb(du->dk_altport); } #endif lp = &du->dk_dd; secpertrk = lp->d_nsectors; secpercyl = lp->d_secpercyl; if (du->dk_skip == 0) du->dk_bc = bp->bio_bcount; wdtab[ctrlr].b_active = 1; /* mark controller active */ /* if starting a multisector transfer, or doing single transfers */ if (du->dk_skip == 0 || (du->dk_flags & DKFL_SINGLE)) { u_int command; u_int count1; long cylin, head, sector; if (du->dk_flags & DKFL_LBA) { sector = (blknum >> 0) & 0xff; cylin = (blknum >> 8) & 0xffff; head = ((blknum >> 24) & 0xf) | WDSD_LBA; } else { cylin = blknum / secpercyl; head = (blknum % secpercyl) / secpertrk; sector = blknum % secpertrk; } /* * XXX this looks like an attempt to skip bad sectors * on write. */ if (wdtab[ctrlr].b_errcnt && (bp->bio_cmd == BIO_WRITE)) du->dk_bc += DEV_BSIZE; count1 = howmany( du->dk_bc, DEV_BSIZE); du->dk_flags &= ~DKFL_MULTI; if (du->dk_flags & DKFL_SINGLE) { command = (bp->bio_cmd == BIO_READ) ? WDCC_READ : WDCC_WRITE; count1 = 1; du->dk_currentiosize = 1; } else { if((du->dk_flags & DKFL_USEDMA) && wddma[du->dk_interface].wdd_dmaverify(du->dk_dmacookie, (void *)((int)bp->bio_data + du->dk_skip * DEV_BSIZE), du->dk_bc, bp->bio_cmd == BIO_READ)) { du->dk_flags |= DKFL_DMA; if(bp->bio_cmd == BIO_READ) command = WDCC_READ_DMA; else command = WDCC_WRITE_DMA; du->dk_currentiosize = count1; } else if( (count1 > 1) && (du->dk_multi > 1)) { du->dk_flags |= DKFL_MULTI; if(bp->bio_cmd == BIO_READ) { command = WDCC_READ_MULTI; } else { command = WDCC_WRITE_MULTI; } du->dk_currentiosize = du->dk_multi; if( du->dk_currentiosize > count1) du->dk_currentiosize = count1; } else { if(bp->bio_cmd == BIO_READ) { command = WDCC_READ; } else { command = WDCC_WRITE; } du->dk_currentiosize = 1; } } /* * XXX this loop may never terminate. The code to handle * counting down of retries and eventually failing the i/o * is in wdintr() and we can't get there from here. */ if (wdtest != 0) { if (--wdtest == 0) { wdtest = 100; printf("dummy wdunwedge\n"); wdunwedge(du); } } if ((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) { wddma[du->dk_interface].wdd_dmaprep(du->dk_dmacookie, (void *)((int)bp->bio_data + du->dk_skip * DEV_BSIZE), du->dk_bc, bp->bio_cmd == BIO_READ); } while (wdcommand(du, cylin, head, sector, count1, command) != 0) { wderror(bp, du, "wdstart: timeout waiting to give command"); wdunwedge(du); } #ifdef WDDEBUG printf("cylin %ld head %ld sector %ld addr %x sts ", cylin, head, sector, (int)bp->bio_data + du->dk_skip * DEV_BSIZE); if (old_epson_note) printf("%x\n", epson_inb(du->dk_altport)); else printf("%x\n", inb(du->dk_altport)); #endif } /* * Schedule wdtimeout() to wake up after a few seconds. Retrying * unmarked bad blocks can take 3 seconds! Then it is not good that * we retry 5 times. * * On the first try, we give it 10 seconds, for drives that may need * to spin up. * * XXX wdtimeout() doesn't increment the error count so we may loop * forever. More seriously, the loop isn't forever but causes a * crash. * * TODO fix b_resid bug elsewhere (fd.c....). Fix short but positive * counts being discarded after there is an error (in physio I * think). Discarding them would be OK if the (special) file offset * was not advanced. */ if (wdtab[ctrlr].b_errcnt == 0) du->dk_timeout = 1 + 10; else du->dk_timeout = 1 + 3; /* if this is a DMA op, start DMA and go away until it's done. */ if ((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) { wddma[du->dk_interface].wdd_dmastart(du->dk_dmacookie); return; } /* If this is a read operation, just go away until it's done. */ if (bp->bio_cmd == BIO_READ) return; /* Ready to send data? */ if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT) < 0) { wderror(bp, du, "wdstart: timeout waiting for DRQ"); /* * XXX what do we do now? If we've just issued the command, * then we can treat this failure the same as a command * failure. But if we are continuing a multi-sector write, * the command was issued ages ago, so we can't simply * restart it. * * XXX we waste a lot of time unnecessarily translating block * numbers to cylin/head/sector for continued i/o's. */ } count = 1; if( du->dk_flags & DKFL_MULTI) { count = howmany(du->dk_bc, DEV_BSIZE); if( count > du->dk_multi) count = du->dk_multi; if( du->dk_currentiosize > count) du->dk_currentiosize = count; } if (!old_epson_note) { if (du->dk_flags & DKFL_32BIT) outsl(du->dk_port + wd_data, (void *)((int)bp->bio_data + du->dk_skip * DEV_BSIZE), (count * DEV_BSIZE) / sizeof(long)); else outsw(du->dk_port + wd_data, (void *)((int)bp->bio_data + du->dk_skip * DEV_BSIZE), (count * DEV_BSIZE) / sizeof(short)); } else epson_outsw(du->dk_port + wd_data, (void *)((int)bp->bio_data + du->dk_skip * DEV_BSIZE), (count * DEV_BSIZE) / sizeof(short)); du->dk_bc -= DEV_BSIZE * count; } /* Interrupt routine for the controller. Acknowledge the interrupt, check for * errors on the current operation, mark it done if necessary, and start * the next request. Also check for a partially done transfer, and * continue with the next chunk if so. */ void wdintr(void *unitnum) { register struct softc *du; register struct bio *bp; int dmastat = 0; /* Shut up GCC */ int unit = (int)unitnum; int ctrlr_atapi; if (eide_quirks & Q_CMD640B) { unit = PRIMARY; ctrlr_atapi = atapictrlr; } else { ctrlr_atapi = unit; } if (wdtab[unit].b_active == 2) return; /* intr in wdflushirq() */ if (!wdtab[unit].b_active) { #ifdef WDDEBUG /* * These happen mostly because the power-mgt part of the * bios shuts us down, and we just manage to see the * interrupt from the "SLEEP" command. */ printf("wdc%d: extra interrupt\n", unit); #endif return; } if (wdtab[unit].b_active == 3) { /* process an ATAPI interrupt */ if (atapi_intr && atapi_intr (ctrlr_atapi)) /* ATAPI op continues */ return; /* controller is free, start new op */ wdtab[unit].b_active = 0; wdstart (unit); return; } bp = bioq_first(&wdtab[unit].controller_queue); du = wddrives[dkunit(bp->bio_dev)]; #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif /* finish off DMA */ if ((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) { /* XXX SMP boxes sometimes generate an early intr. Why? */ if ((wddma[du->dk_interface].wdd_dmastatus(du->dk_dmacookie) & WDDS_INTERRUPT) == 0) return; dmastat = wddma[du->dk_interface].wdd_dmadone(du->dk_dmacookie); } du->dk_timeout = 0; /* check drive status/failure */ if (wdwait(du, 0, TIMEOUT) < 0) { wderror(bp, du, "wdintr: timeout waiting for status"); du->dk_status |= WDCS_ERR; /* XXX */ } /* is it not a transfer, but a control operation? */ if (du->dk_state < OPEN) { wdtab[unit].b_active = 0; switch (wdcontrol(bp)) { case 0: return; case 1: wdstart(unit); return; case 2: goto done; } } /* have we an error? */ if ((du->dk_status & (WDCS_ERR | WDCS_ECCCOR)) || (((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) && dmastat != WDDS_INTERRUPT)) { unsigned int errstat; oops: /* * XXX bogus inb() here */ errstat = inb(du->dk_port + wd_error); if(((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) && (errstat & WDERR_ABORT)) { wderror(bp, du, "reverting to PIO mode"); du->dk_flags &= ~DKFL_USEDMA; } else if((du->dk_flags & DKFL_MULTI) && (errstat & WDERR_ABORT)) { wderror(bp, du, "reverting to non-multi sector mode"); du->dk_multi = 1; } if (!(du->dk_status & (WDCS_ERR | WDCS_ECCCOR)) && (((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) && (dmastat != WDDS_INTERRUPT))) printf("wd%d: DMA failure, DMA status %b\n", du->dk_lunit, dmastat, WDDS_BITS); #ifdef WDDEBUG wderror(bp, du, "wdintr"); #endif if ((du->dk_flags & DKFL_SINGLE) == 0) { du->dk_flags |= DKFL_ERROR; goto outt; } if (du->dk_status & WDCS_ERR) { if (++wdtab[unit].b_errcnt < RETRIES) { wdtab[unit].b_active = 0; } else { wderror(bp, du, "hard error"); bp->bio_error = EIO; bp->bio_flags |= BIO_ERROR; /* flag the error */ } } else if (du->dk_status & WDCS_ECCCOR) wderror(bp, du, "soft ecc"); } /* * If this was a successful read operation, fetch the data. */ if (bp->bio_cmd == BIO_READ && !(bp->bio_flags & BIO_ERROR) && !((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) && wdtab[unit].b_active) { u_int chk, dummy, multisize; multisize = chk = du->dk_currentiosize * DEV_BSIZE; if( du->dk_bc < chk) { chk = du->dk_bc; if( ((chk + DEV_BSIZE - 1) / DEV_BSIZE) < du->dk_currentiosize) { du->dk_currentiosize = (chk + DEV_BSIZE - 1) / DEV_BSIZE; multisize = du->dk_currentiosize * DEV_BSIZE; } } /* ready to receive data? */ if ((du->dk_status & (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ)) != (WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ)) wderror(bp, du, "wdintr: read intr arrived early"); if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT) != 0) { wderror(bp, du, "wdintr: read error detected late"); goto oops; } /* suck in data */ if( du->dk_flags & DKFL_32BIT) insl(du->dk_port + wd_data, (void *)((int)bp->bio_data + du->dk_skip * DEV_BSIZE), chk / sizeof(long)); else insw(du->dk_port + wd_data, (void *)((int)bp->bio_data + du->dk_skip * DEV_BSIZE), chk / sizeof(short)); du->dk_bc -= chk; /* XXX for obsolete fractional sector reads. */ while (chk < multisize) { insw(du->dk_port + wd_data, &dummy, 1); chk += sizeof(short); } } /* final cleanup on DMA */ if (((bp->bio_flags & BIO_ERROR) == 0) && ((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) && wdtab[unit].b_active) { int iosize; iosize = du->dk_currentiosize * DEV_BSIZE; du->dk_bc -= iosize; } outt: if (wdtab[unit].b_active) { if ((bp->bio_flags & BIO_ERROR) == 0) { du->dk_skip += du->dk_currentiosize;/* add to successful sectors */ if (wdtab[unit].b_errcnt) wderror(bp, du, "soft error"); wdtab[unit].b_errcnt = 0; /* see if more to transfer */ if (du->dk_bc > 0 && (du->dk_flags & DKFL_ERROR) == 0) { if( (du->dk_flags & DKFL_SINGLE) || (bp->bio_cmd == BIO_WRITE)) { wdtab[unit].b_active = 0; wdstart(unit); } else { du->dk_timeout = 1 + 3; } return; /* next chunk is started */ } else if ((du->dk_flags & (DKFL_SINGLE | DKFL_ERROR)) == DKFL_ERROR) { du->dk_skip = 0; du->dk_flags &= ~DKFL_ERROR; du->dk_flags |= DKFL_SINGLE; wdtab[unit].b_active = 0; wdstart(unit); return; /* redo xfer sector by sector */ } } done: ; /* done with this transfer, with or without error */ du->dk_flags &= ~(DKFL_SINGLE|DKFL_DMA); bioq_remove( &wdtab[unit].controller_queue, bp); wdtab[unit].b_errcnt = 0; bp->bio_resid = bp->bio_bcount - du->dk_skip * DEV_BSIZE; wdutab[du->dk_lunit].b_active = 0; du->dk_skip = 0; biofinish(bp, &du->dk_stats, 0); } /* controller idle */ wdtab[unit].b_active = 0; /* anything more on drive queue? */ wdustart(du); /* anything more for controller to do? */ wdstart(unit); } /* * Initialize a drive. */ int wdopen(dev_t dev, int flags, int fmt, struct proc *p) { register unsigned int lunit; register struct softc *du; struct disklabel *dl; lunit = dkunit(dev); if (lunit >= NWD || dktype(dev) != 0) return (ENXIO); du = wddrives[lunit]; if (du == NULL) return (ENXIO); dev->si_iosize_max = 248 * 512; #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif /* Finish flushing IRQs left over from wdattach(). */ if (wdtab[du->dk_ctrlr_cmd640].b_active == 2) wdtab[du->dk_ctrlr_cmd640].b_active = 0; du->dk_flags &= ~DKFL_BADSCAN; /* spin waiting for anybody else reading the disk label */ while (du->dk_flags & DKFL_LABELLING) tsleep((caddr_t)&du->dk_flags, PZERO - 1, "wdopen", 1); wdsleep(du->dk_ctrlr, "wdopn1"); du->dk_flags |= DKFL_LABELLING; du->dk_state = WANTOPEN; dl = &du->disk.d_label; bzero(dl, sizeof(*dl)); dl->d_secsize = du->dk_dd.d_secsize; dl->d_nsectors = du->dk_dd.d_nsectors; dl->d_ntracks = du->dk_dd.d_ntracks; dl->d_ncylinders = du->dk_dd.d_ncylinders; dl->d_secpercyl = du->dk_dd.d_secpercyl; dl->d_secperunit = du->dk_dd.d_secperunit; du->dk_flags &= ~DKFL_LABELLING; wdsleep(du->dk_ctrlr, "wdopn2"); return 0; } /* * Implement operations other than read/write. * Called from wdstart or wdintr during opens. * Uses finite-state-machine to track progress of operation in progress. * Returns 0 if operation still in progress, 1 if completed, 2 if error. */ static int wdcontrol(register struct bio *bp) { register struct softc *du; int ctrlr; du = wddrives[dkunit(bp->bio_dev)]; ctrlr = du->dk_ctrlr_cmd640; #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif switch (du->dk_state) { case WANTOPEN: tryagainrecal: wdtab[ctrlr].b_active = 1; if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0) { wderror(bp, du, "wdcontrol: wdcommand failed"); goto maybe_retry; } du->dk_state = RECAL; return (0); case RECAL: if (du->dk_status & WDCS_ERR || wdsetctlr(du) != 0) { wderror(bp, du, "wdcontrol: recal failed"); maybe_retry: if (du->dk_status & WDCS_ERR) wdunwedge(du); du->dk_state = WANTOPEN; if (++wdtab[ctrlr].b_errcnt < RETRIES) goto tryagainrecal; bp->bio_error = ENXIO; /* XXX needs translation */ bp->bio_flags |= BIO_ERROR; return (2); } wdtab[ctrlr].b_errcnt = 0; du->dk_state = OPEN; /* * The rest of the initialization can be done by normal * means. */ return (1); } panic("wdcontrol"); return (2); } /* * Wait uninterruptibly until controller is not busy, then send it a command. * The wait usually terminates immediately because we waited for the previous * command to terminate. */ static int wdcommand(struct softc *du, u_int cylinder, u_int head, u_int sector, u_int count, u_int command) { u_int wdc; #ifdef PC98 unsigned char u_addr; #endif wdc = du->dk_port; if (du->cfg_flags & WDOPT_SLEEPHACK) { /* OK, so the APM bios has put the disk into SLEEP mode, * how can we tell ? Uhm, we can't. There is no * standardized way of finding out, and the only way to * wake it up is to reset it. Bummer. * * All the many and varied versions of the IDE/ATA standard * explicitly tells us not to look at these registers if * the disk is in SLEEP mode. Well, too bad really, we * have to find out if it's in sleep mode before we can * avoid reading the registers. * * I have reason to belive that most disks will return * either 0xff or 0x00 in all but the status register * when in SLEEP mode, but I have yet to see one return * 0x00, so we don't check for that yet. * * The check for WDCS_BUSY is for the case where the * bios spins up the disk for us, but doesn't initialize * it correctly /phk */ if (old_epson_note) { if(epson_inb(wdc + wd_precomp) + epson_inb(wdc + wd_cyl_lo) + epson_inb(wdc + wd_cyl_hi) + epson_inb(wdc + wd_sdh) + epson_inb(wdc + wd_sector) + epson_inb(wdc + wd_seccnt) == 6 * 0xff) { if (bootverbose) printf("wd(%d,%d): disk aSLEEP\n", du->dk_ctrlr, du->dk_unit); wdunwedge(du); } else if(epson_inb(wdc + wd_status) == WDCS_BUSY) { if (bootverbose) printf("wd(%d,%d): disk is BUSY\n", du->dk_ctrlr, du->dk_unit); wdunwedge(du); } } else { if(inb(wdc + wd_precomp) + inb(wdc + wd_cyl_lo) + inb(wdc + wd_cyl_hi) + inb(wdc + wd_sdh) + inb(wdc + wd_sector) + inb(wdc + wd_seccnt) == 6 * 0xff) { if (bootverbose) printf("wd(%d,%d): disk aSLEEP\n", du->dk_ctrlr, du->dk_unit); wdunwedge(du); } else if(inb(wdc + wd_status) == WDCS_BUSY) { if (bootverbose) printf("wd(%d,%d): disk is BUSY\n", du->dk_ctrlr, du->dk_unit); wdunwedge(du); } } } if (wdwait(du, 0, TIMEOUT) < 0) return (1); #ifdef PC98 /* u_addr = (du->dk_unit & 0xfe); */ u_addr = ((du->dk_unit)/2)<<4; #endif /* PC98 */ if( command == WDCC_FEATURES) { if (old_epson_note) epson_outb(wdc + wd_features, count); else { outb(wdc + wd_sdh, WDSD_IBM | (du->dk_unit << 4) | head); outb(wdc + wd_features, count); if ( count == WDFEA_SETXFER ) outb(wdc + wd_seccnt, sector); } } else { if (old_epson_note) { epson_outb(wdc + wd_precomp, du->dk_dd.d_precompcyl/4); epson_outb(wdc + wd_cyl_lo, cylinder); epson_outb(wdc + wd_cyl_hi, cylinder >> 8); epson_outb(wdc + wd_sdh, WDSD_IBM | u_addr | head); epson_outb(wdc + wd_sector, sector + 1); epson_outb(wdc + wd_seccnt, count); } else { outb(wdc + wd_precomp, du->dk_dd.d_precompcyl / 4); outb(wdc + wd_cyl_lo, cylinder); outb(wdc + wd_cyl_hi, cylinder >> 8); #ifdef PC98 outb(wdc + wd_sdh, WDSD_IBM | u_addr | head); #else outb(wdc + wd_sdh, WDSD_IBM | (du->dk_unit<<4) | head); #endif if (head & WDSD_LBA) outb(wdc + wd_sector, sector); else outb(wdc + wd_sector, sector + 1); outb(wdc + wd_seccnt, count); } } if (wdwait(du, (command == WDCC_DIAGNOSE || command == WDCC_IDC) ? 0 : WDCS_READY, TIMEOUT) < 0) return (1); if (old_epson_note) epson_outb(wdc + wd_command, command); else outb(wdc + wd_command, command); return (0); } static void wdsetmulti(struct softc *du) { /* * The config option flags low 8 bits define the maximum multi-block * transfer size. If the user wants the maximum that the drive * is capable of, just set the low bits of the config option to * 0x00ff. */ if ((du->cfg_flags & WDOPT_MULTIMASK) != 0 && (du->dk_multi > 1)) { int configval = du->cfg_flags & WDOPT_MULTIMASK; du->dk_multi = min(du->dk_multi, configval); if (wdcommand(du, 0, 0, 0, du->dk_multi, WDCC_SET_MULTI)) { du->dk_multi = 1; } else { if (wdwait(du, WDCS_READY, TIMEOUT) < 0) { du->dk_multi = 1; } } } else { du->dk_multi = 1; } } /* * issue IDC to drive to tell it just what geometry it is to be. */ static int wdsetctlr(struct softc *du) { int error = 0; #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif #ifdef WDDEBUG printf("wd(%d,%d): wdsetctlr: C %lu H %lu S %lu\n", du->dk_ctrlr, du->dk_unit, du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks, du->dk_dd.d_nsectors); #endif if (!(du->dk_flags & DKFL_LBA)) { if (du->dk_dd.d_ntracks == 0 || du->dk_dd.d_ntracks > 16) { struct wdparams *wp; printf("wd%d: can't handle %lu heads from partition table ", du->dk_lunit, du->dk_dd.d_ntracks); /* obtain parameters */ wp = &du->dk_params; if (wp->wdp_heads > 0 && wp->wdp_heads <= 16) { printf("(controller value %u restored)\n", wp->wdp_heads); du->dk_dd.d_ntracks = wp->wdp_heads; } else { printf("(truncating to 16)\n"); du->dk_dd.d_ntracks = 16; } } if (du->dk_dd.d_nsectors == 0 || du->dk_dd.d_nsectors > 255) { printf("wd%d: cannot handle %lu sectors (max 255)\n", du->dk_lunit, du->dk_dd.d_nsectors); error = 1; } if (error) { wdtab[du->dk_ctrlr_cmd640].b_errcnt += RETRIES; return (1); } if (wdcommand(du, du->dk_dd.d_ncylinders, du->dk_dd.d_ntracks - 1, 0, du->dk_dd.d_nsectors, WDCC_IDC) != 0 || wdwait(du, WDCS_READY, TIMEOUT) < 0) { wderror((struct bio *)NULL, du, "wdsetctlr failed"); return (1); } } wdsetmulti(du); #ifdef NOTYET /* set read caching and write caching */ wdcommand(du, 0, 0, 0, WDFEA_RCACHE, WDCC_FEATURES); wdwait(du, WDCS_READY, TIMEOUT); wdcommand(du, 0, 0, 0, WDFEA_WCACHE, WDCC_FEATURES); wdwait(du, WDCS_READY, TIMEOUT); #endif return (0); } #if 0 /* * Wait until driver is inactive, then set up controller. */ static int wdwsetctlr(struct softc *du) { int stat; int x; wdsleep(du->dk_ctrlr, "wdwset"); x = splbio(); stat = wdsetctlr(du); wdflushirq(du, x); splx(x); return (stat); } #endif /* * gross little callback function for wdddma interface. returns 1 for * success, 0 for failure. */ static int wdsetmode(int mode, void *wdinfo) { int i; struct softc *du; du = wdinfo; if (bootverbose) printf("wd%d: wdsetmode() setting transfer mode to %02x\n", du->dk_lunit, mode); i = wdcommand(du, 0, 0, mode, WDFEA_SETXFER, WDCC_FEATURES) == 0 && wdwait(du, WDCS_READY, TIMEOUT) == 0; return i; } /* * issue READP to drive to ask it what it is. */ static int wdgetctlr(struct softc *du) { int i; char tb[DEV_BSIZE], tb2[DEV_BSIZE]; struct wdparams *wp = NULL; u_long flags = du->cfg_flags; #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif again: if (wdcommand(du, 0, 0, 0, 0, WDCC_READP) != 0 || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT | WDCS_DRQ, TIMEOUT) != 0) { #ifdef PC98 if ( du->dk_unit > 1 ) return(1); #endif /* * if we failed on the second try, assume non-32bit */ if( du->dk_flags & DKFL_32BIT) goto failed; /* XXX need to check error status after final transfer. */ /* * Old drives don't support WDCC_READP. Try a seek to 0. * Some IDE controllers return trash if there is no drive * attached, so first test that the drive can be selected. * This also avoids long waits for nonexistent drives. */ if (wdwait(du, 0, TIMEOUT) < 0) return (1); if (old_epson_note) { epson_outb(du->dk_port + wd_sdh, WDSD_IBM | (du->dk_unit << 4)); DELAY(5000); /* usually unnecessary; drive select is fast */ if ((epson_inb(du->dk_port + wd_status) & (WDCS_BUSY | WDCS_READY)) != WDCS_READY || wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0 || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0) return (1); } else { outb(du->dk_port + wd_sdh, WDSD_IBM | (du->dk_unit << 4)); DELAY(5000); /* usually unnecessary; drive select is fast */ /* * Do this twice: may get a false WDCS_READY the first time. */ inb(du->dk_port + wd_status); if ((inb(du->dk_port + wd_status) & (WDCS_BUSY | WDCS_READY)) != WDCS_READY || wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) != 0 || wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0) return (1); } if (du->dk_unit == bootinfo.bi_n_bios_used) { du->dk_dd.d_secsize = DEV_BSIZE; du->dk_dd.d_nsectors = bootinfo.bi_bios_geom[du->dk_unit] & 0xff; du->dk_dd.d_ntracks = ((bootinfo.bi_bios_geom[du->dk_unit] >> 8) & 0xff) + 1; /* XXX Why 2 ? */ du->dk_dd.d_ncylinders = (bootinfo.bi_bios_geom[du->dk_unit] >> 16) + 2; du->dk_dd.d_secpercyl = du->dk_dd.d_ntracks * du->dk_dd.d_nsectors; du->dk_dd.d_secperunit = du->dk_dd.d_secpercyl * du->dk_dd.d_ncylinders; #if 0 du->dk_dd.d_partitions[WDRAW].p_size = du->dk_dd.d_secperunit; du->dk_dd.d_type = DTYPE_ST506; du->dk_dd.d_subtype |= DSTYPE_GEOMETRY; strncpy(du->dk_dd.d_typename, "Bios geometry", sizeof du->dk_dd.d_typename); strncpy(du->dk_params.wdp_model, "ST506", sizeof du->dk_params.wdp_model); #endif bootinfo.bi_n_bios_used ++; return 0; } /* * Fake minimal drive geometry for reading the MBR. * readdisklabel() may enlarge it to read the label and the * bad sector table. */ du->dk_dd.d_secsize = DEV_BSIZE; du->dk_dd.d_nsectors = 17; du->dk_dd.d_ntracks = 1; du->dk_dd.d_ncylinders = 1; du->dk_dd.d_secpercyl = 17; du->dk_dd.d_secperunit = 17; #if 0 /* * Fake maximal drive size for writing the label. */ du->dk_dd.d_partitions[RAW_PART].p_size = 64 * 16 * 1024; /* * Fake some more of the label for printing by disklabel(1) * in case there is no real label. */ du->dk_dd.d_type = DTYPE_ST506; du->dk_dd.d_subtype |= DSTYPE_GEOMETRY; strncpy(du->dk_dd.d_typename, "Fake geometry", sizeof du->dk_dd.d_typename); #endif /* Fake the model name for printing by wdattach(). */ strncpy(du->dk_params.wdp_model, "unknown", sizeof du->dk_params.wdp_model); return (0); } /* obtain parameters */ wp = &du->dk_params; if (!old_epson_note) { if (du->dk_flags & DKFL_32BIT) insl(du->dk_port + wd_data, tb, sizeof(tb) / sizeof(long)); else insw(du->dk_port + wd_data, tb, sizeof(tb) / sizeof(short)); } else epson_insw(du->dk_port + wd_data, tb, sizeof(tb) / sizeof(short)); /* try 32-bit data path (VLB IDE controller) */ if (flags & WDOPT_32BIT) { if (! (du->dk_flags & DKFL_32BIT)) { bcopy(tb, tb2, sizeof(struct wdparams)); du->dk_flags |= DKFL_32BIT; goto again; } /* check that we really have 32-bit controller */ if (bcmp (tb, tb2, sizeof(struct wdparams)) != 0) { failed: /* test failed, use 16-bit i/o mode */ bcopy(tb2, tb, sizeof(struct wdparams)); du->dk_flags &= ~DKFL_32BIT; } } bcopy(tb, wp, sizeof(struct wdparams)); /* shuffle string byte order */ for (i = 0; (unsigned)i < sizeof(wp->wdp_model); i += 2) { u_short *p; p = (u_short *) (wp->wdp_model + i); *p = ntohs(*p); } /* * Clean up the wdp_model by converting nulls to spaces, and * then removing the trailing spaces. */ for (i = 0; (unsigned)i < sizeof(wp->wdp_model); i++) { if (wp->wdp_model[i] == '\0') { wp->wdp_model[i] = ' '; } } for (i = sizeof(wp->wdp_model) - 1; (i >= 0 && wp->wdp_model[i] == ' '); i--) { wp->wdp_model[i] = '\0'; } /* * find out the drives maximum multi-block transfer capability */ du->dk_multi = wp->wdp_nsecperint & 0xff; wdsetmulti(du); /* * check drive's DMA capability */ if (wddma[du->dk_interface].wdd_candma) { du->dk_dmacookie = wddma[du->dk_interface].wdd_candma( du->dk_port, du->dk_ctrlr, du->dk_unit); /* does user want this? */ if ((du->cfg_flags & WDOPT_DMA) && /* have we got a DMA controller? */ du->dk_dmacookie && /* can said drive do DMA? */ wddma[du->dk_interface].wdd_dmainit(du->dk_dmacookie, wp, wdsetmode, du)) { du->dk_flags |= DKFL_USEDMA; } } else { du->dk_dmacookie = NULL; } #ifdef WDDEBUG printf( "\nwd(%d,%d): wdgetctlr: gc %x cyl %d trk %d sec %d type %d sz %d model %s\n", du->dk_ctrlr, du->dk_unit, wp->wdp_config, wp->wdp_cylinders, wp->wdp_heads, wp->wdp_sectors, wp->wdp_buffertype, wp->wdp_buffersize, wp->wdp_model); #endif #ifdef PC98 /* for larger than 40MB */ { long cyl = wp->wdp_cylinders * wp->wdp_heads * wp->wdp_sectors; if ( du->dk_unit > 1 ) { wp->wdp_sectors = 17; wp->wdp_heads = 8; } else { wp->wdp_sectors = bootinfo.bi_bios_geom[du->dk_unit] & 0xff; wp->wdp_heads = (bootinfo.bi_bios_geom[du->dk_unit] >> 8) & 0xff; } wp->wdp_cylinders = cyl / (wp->wdp_heads * wp->wdp_sectors); } #endif /* update disklabel given drive information */ du->dk_dd.d_secsize = DEV_BSIZE; if ((du->cfg_flags & WDOPT_LBA) && wp->wdp_lbasize) { du->dk_dd.d_nsectors = 63; if (wp->wdp_lbasize < 16*63*1024) { /* <=528.4 MB */ du->dk_dd.d_ntracks = 16; } else if (wp->wdp_lbasize < 32*63*1024) { /* <=1.057 GB */ du->dk_dd.d_ntracks = 32; } else if (wp->wdp_lbasize < 64*63*1024) { /* <=2.114 GB */ du->dk_dd.d_ntracks = 64; } else if (wp->wdp_lbasize < 128*63*1024) { /* <=4.228 GB */ du->dk_dd.d_ntracks = 128; } else if (wp->wdp_lbasize < 255*63*1024) { /* <=8.422 GB */ du->dk_dd.d_ntracks = 255; } else { /* >8.422 GB */ du->dk_dd.d_ntracks = 255; /* XXX */ } du->dk_dd.d_secpercyl= du->dk_dd.d_ntracks*du->dk_dd.d_nsectors; du->dk_dd.d_ncylinders = wp->wdp_lbasize/du->dk_dd.d_secpercyl; du->dk_dd.d_secperunit = wp->wdp_lbasize; du->dk_flags |= DKFL_LBA; } else { du->dk_dd.d_ncylinders = wp->wdp_cylinders; /* +- 1 */ du->dk_dd.d_ntracks = wp->wdp_heads; du->dk_dd.d_nsectors = wp->wdp_sectors; du->dk_dd.d_secpercyl = du->dk_dd.d_ntracks * du->dk_dd.d_nsectors; du->dk_dd.d_secperunit = du->dk_dd.d_secpercyl * du->dk_dd.d_ncylinders; if (wp->wdp_cylinders == 16383 && du->dk_dd.d_secperunit < wp->wdp_lbasize) { du->dk_dd.d_secperunit = wp->wdp_lbasize; du->dk_dd.d_ncylinders = du->dk_dd.d_secperunit / du->dk_dd.d_secpercyl; } } if (WDOPT_FORCEHD(du->cfg_flags)) { du->dk_dd.d_ntracks = WDOPT_FORCEHD(du->cfg_flags); du->dk_dd.d_secpercyl = du->dk_dd.d_ntracks * du->dk_dd.d_nsectors; du->dk_dd.d_ncylinders = du->dk_dd.d_secperunit / du->dk_dd.d_secpercyl; } if (du->dk_dd.d_ncylinders > 0x10000 && !(du->cfg_flags & WDOPT_LBA)) { du->dk_dd.d_ncylinders = 0x10000; du->dk_dd.d_secperunit = du->dk_dd.d_secpercyl * du->dk_dd.d_ncylinders; printf( "wd%d: cannot handle %d total sectors; truncating to %lu\n", du->dk_lunit, wp->wdp_lbasize, du->dk_dd.d_secperunit); } #if 0 du->dk_dd.d_partitions[RAW_PART].p_size = du->dk_dd.d_secperunit; /* dubious ... */ bcopy("ESDI/IDE", du->dk_dd.d_typename, 9); bcopy(wp->wdp_model + 20, du->dk_dd.d_packname, 14 - 1); /* better ... */ du->dk_dd.d_type = DTYPE_ESDI; du->dk_dd.d_subtype |= DSTYPE_GEOMETRY; #endif return (0); } static void wderror(struct bio *bp, struct softc *du, char *mesg) { if (bp == NULL) printf("wd%d: %s", du->dk_lunit, mesg); else diskerr(bp, mesg, du->dk_skip, dsgetlabel(bp->bio_dev, du->dk_slices)); printf(" (status %b error %b)\n", du->dk_status, WDCS_BITS, du->dk_error, WDERR_BITS); } /* * Discard any interrupts that were latched by the interrupt system while * we were doing polled i/o. */ static void wdflushirq(struct softc *du, int old_ipl) { wdtab[du->dk_ctrlr_cmd640].b_active = 2; splx(old_ipl); (void)splbio(); wdtab[du->dk_ctrlr_cmd640].b_active = 0; } /* * Reset the controller. */ static int wdreset(struct softc *du) { int err = 0; #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif if ((du->dk_flags & (DKFL_DMA|DKFL_SINGLE)) == DKFL_DMA) wddma[du->dk_interface].wdd_dmadone(du->dk_dmacookie); (void)wdwait(du, 0, TIMEOUT); #ifdef PC98 if (old_epson_note) { epson_outb(du->dk_altport, WDCTL_IDS | WDCTL_RST); DELAY(10 * 1000); epson_outb(du->dk_altport, WDCTL_IDS); if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0 || (du->dk_error = epson_errorf(du->dk_port + wd_error)) != 0x01) return (1); epson_outb(du->dk_altport, WDCTL_4BIT); err = 0; } else { #endif outb(du->dk_altport, WDCTL_IDS | WDCTL_RST); DELAY(10 * 1000); outb(du->dk_altport, WDCTL_IDS); outb(du->dk_port + wd_sdh, WDSD_IBM | (du->dk_unit << 4)); if (wdwait(du, 0, TIMEOUT) != 0) err = 1; /* no IDE drive found */ du->dk_error = inb(du->dk_port + wd_error); if (du->dk_error != 0x01) err = 1; /* the drive is incompatible */ outb(du->dk_altport, WDCTL_4BIT); #ifdef PC98 } #endif return (err); } /* * Sleep until driver is inactive. * This is used only for avoiding rare race conditions, so it is unimportant * that the sleep may be far too short or too long. */ static void wdsleep(int ctrlr, char *wmesg) { int s = splbio(); if (eide_quirks & Q_CMD640B) ctrlr = PRIMARY; while (wdtab[ctrlr].b_active) tsleep((caddr_t)&wdtab[ctrlr].b_active, PZERO - 1, wmesg, 1); splx(s); } static void wdtimeout(void *cdu) { struct softc *du; int x; static int timeouts; du = (struct softc *)cdu; x = splbio(); #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif if (du->dk_timeout != 0 && --du->dk_timeout == 0) { if(timeouts++ <= 5) { char *msg; msg = (timeouts > 5) ? "Last time I say: interrupt timeout. Probably a portable PC." : "interrupt timeout"; wderror((struct bio *)NULL, du, msg); if (du->dk_dmacookie) printf("wd%d: wdtimeout() DMA status %b\n", du->dk_lunit, wddma[du->dk_interface].wdd_dmastatus(du->dk_dmacookie), WDDS_BITS); } wdunwedge(du); wdflushirq(du, x); du->dk_skip = 0; du->dk_flags |= DKFL_SINGLE; wdstart(du->dk_ctrlr); } timeout(wdtimeout, cdu, hz); splx(x); } /* * Reset the controller after it has become wedged. This is different from * wdreset() so that wdreset() can be used in the probe and so that this * can restore the geometry . */ static int wdunwedge(struct softc *du) { struct softc *du1; int lunit; #ifdef PC98 outb(0x432,(du->dk_unit)%2); #endif /* Schedule other drives for recalibration. */ for (lunit = 0; lunit < NWD; lunit++) if ((du1 = wddrives[lunit]) != NULL && du1 != du && du1->dk_ctrlr == du->dk_ctrlr && du1->dk_state > WANTOPEN) du1->dk_state = WANTOPEN; DELAY(RECOVERYTIME); if (wdreset(du) == 0) { /* * XXX - recalibrate current drive now because some callers * aren't prepared to have its state change. */ if (wdcommand(du, 0, 0, 0, 0, WDCC_RESTORE | WD_STEP) == 0 && wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) == 0 && wdsetctlr(du) == 0) return (0); } wderror((struct bio *)NULL, du, "wdunwedge failed"); return (1); } /* * Wait uninterruptibly until controller is not busy and either certain * status bits are set or an error has occurred. * The wait is usually short unless it is for the controller to process * an entire critical command. * Return 1 for (possibly stale) controller errors, -1 for timeout errors, * or 0 for no errors. * Return controller status in du->dk_status and, if there was a controller * error, return the error code in du->dk_error. */ #ifdef WD_COUNT_RETRIES static int min_retries[NWDC]; #endif static int wdwait(struct softc *du, u_char bits_wanted, int timeout) { int wdc; u_char status; #define POLLING 1000 wdc = du->dk_port; timeout += POLLING; /* * This delay is really too long, but does not impact the performance * as much when using the multi-sector option. Shorter delays have * caused I/O errors on some drives and system configs. This should * probably be fixed if we develop a better short term delay mechanism. */ DELAY(1); do { #ifdef WD_COUNT_RETRIES if (min_retries[du->dk_ctrlr] > timeout || min_retries[du->dk_ctrlr] == 0) min_retries[du->dk_ctrlr] = timeout; #endif #ifdef PC98 if (old_epson_note) du->dk_status = status = epson_inb(wdc + wd_status); else du->dk_status = status = inb(wdc + wd_status); #else du->dk_status = status = inb(wdc + wd_status); #endif /* * Atapi drives have a very interesting feature, when attached * as a slave on the IDE bus, and there is no master. * They release the bus after getting the command. * We should reselect the drive here to get the status. */ if (status == 0xff) { outb(wdc + wd_sdh, WDSD_IBM | du->dk_unit << 4); du->dk_status = status = inb(wdc + wd_status); } if (!(status & WDCS_BUSY)) { if (status & WDCS_ERR) { if (old_epson_note) du->dk_error = epson_errorf(wdc + wd_error); else du->dk_error = inb(wdc + wd_error); /* * We once returned here. This is wrong * because the error bit is apparently only * valid after the controller has interrupted * (e.g., the error bit is stale when we wait * for DRQ for writes). So we can't depend * on the error bit at all when polling for * command completion. */ } if ((status & bits_wanted) == bits_wanted) { return (status & WDCS_ERR); } } if (timeout < TIMEOUT) /* * Switch to a polling rate of about 1 KHz so that * the timeout is almost machine-independent. The * controller is taking a long time to respond, so * an extra msec won't matter. */ DELAY(1000); else DELAY(1); } while (--timeout != 0); return (-1); } #endif /* NWDC > 0 */ Index: head/sys/sys/bus.h =================================================================== --- head/sys/sys/bus.h (revision 78134) +++ head/sys/sys/bus.h (revision 78135) @@ -1,416 +1,414 @@ /*- * Copyright (c) 1997,1998 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * * $FreeBSD$ */ #ifndef _SYS_BUS_H_ #define _SYS_BUS_H_ /* * Interface information structure. */ struct u_businfo { int ub_version; /* interface version */ #define BUS_USER_VERSION 1 int ub_generation; /* generation count */ }; /* * Device information exported to userspace. */ struct u_device { uintptr_t dv_handle; uintptr_t dv_parent; char dv_name[32]; char dv_desc[32]; char dv_drivername[32]; /* XXX more driver info? */ }; #ifdef _KERNEL #include #include /* * Forward declarations */ typedef struct device *device_t; typedef struct driver driver_t; typedef struct devclass *devclass_t; #define device_method_t kobj_method_t typedef void driver_intr_t(void*); /* * Interrupt type bits. These flags are used both by newbus interrupt * registration (nexus.c) and also in struct intrec, which defines * interrupt properties. * * XXX We should probably revisit this and remove the vestiges of the * spls implicit in names like INTR_TYPE_TTY. In the meantime, don't * confuse things by renaming them (Grog, 18 July 2000). * * We define this in terms of bits because some devices may belong * to multiple classes (and therefore need to be included in * multiple interrupt masks, which is what this really serves to * indicate. Buses which do interrupt remapping will want to * change their type to reflect what sort of devices are underneath. */ enum intr_type { INTR_TYPE_TTY = 1, INTR_TYPE_BIO = 2, INTR_TYPE_NET = 4, INTR_TYPE_CAM = 8, INTR_TYPE_MISC = 16, INTR_TYPE_CLK = 32, INTR_FAST = 128, INTR_EXCL = 256, /* exclusive interrupt */ INTR_MPSAFE = 512, /* this interrupt is SMP safe */ INTR_ENTROPY = 1024 /* this interrupt provides entropy */ }; typedef int (*devop_t)(void); struct driver { KOBJ_CLASS_FIELDS; void *priv; /* driver private data */ }; typedef enum device_state { DS_NOTPRESENT, /* not probed or probe failed */ DS_ALIVE, /* probe succeeded */ DS_ATTACHED, /* attach method called */ DS_BUSY /* device is open */ } device_state_t; /* * Definitions for drivers which need to keep simple lists of resources * for their child devices. */ struct resource; struct resource_list_entry { SLIST_ENTRY(resource_list_entry) link; int type; /* type argument to alloc_resource */ int rid; /* resource identifier */ struct resource *res; /* the real resource when allocated */ u_long start; /* start of resource range */ u_long end; /* end of resource range */ u_long count; /* count within range */ }; SLIST_HEAD(resource_list, resource_list_entry); /* * Initialise a resource list. */ void resource_list_init(struct resource_list *rl); /* * Reclaim memory used by a resource list. */ void resource_list_free(struct resource_list *rl); /* * Add a resource entry or modify an existing entry if one exists with * the same type and rid. */ void resource_list_add(struct resource_list *rl, int type, int rid, u_long start, u_long end, u_long count); /* * Find a resource entry by type and rid. */ struct resource_list_entry* resource_list_find(struct resource_list *rl, int type, int rid); /* * Delete a resource entry. */ void resource_list_delete(struct resource_list *rl, int type, int rid); /* * Implement BUS_ALLOC_RESOURCE by looking up a resource from the list * and passing the allocation up to the parent of bus. This assumes * that the first entry of device_get_ivars(child) is a struct * resource_list. This also handles 'passthrough' allocations where a * child is a remote descendant of bus by passing the allocation up to * the parent of bus. */ struct resource * resource_list_alloc(struct resource_list *rl, device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags); /* * Implement BUS_RELEASE_RESOURCE. */ int resource_list_release(struct resource_list *rl, device_t bus, device_t child, int type, int rid, struct resource *res); /* * The root bus, to which all top-level busses are attached. */ extern device_t root_bus; extern devclass_t root_devclass; void root_bus_configure(void); /* * Useful functions for implementing busses. */ int bus_generic_activate_resource(device_t dev, device_t child, int type, int rid, struct resource *r); struct resource * bus_generic_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags); int bus_generic_attach(device_t dev); int bus_generic_deactivate_resource(device_t dev, device_t child, int type, int rid, struct resource *r); int bus_generic_detach(device_t dev); void bus_generic_driver_added(device_t dev, driver_t *driver); struct resource_list * bus_generic_get_resource_list (device_t, device_t); int bus_print_child_header(device_t dev, device_t child); int bus_print_child_footer(device_t dev, device_t child); int bus_generic_print_child(device_t dev, device_t child); int bus_generic_probe(device_t dev); int bus_generic_read_ivar(device_t dev, device_t child, int which, uintptr_t *result); int bus_generic_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r); int bus_generic_resume(device_t dev); int bus_generic_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_intr_t *intr, void *arg, void **cookiep); struct resource * bus_generic_rl_alloc_resource (device_t, device_t, int, int *, u_long, u_long, u_long, u_int); void bus_generic_rl_delete_resource (device_t, device_t, int, int); int bus_generic_rl_get_resource (device_t, device_t, int, int, u_long *, u_long *); int bus_generic_rl_set_resource (device_t, device_t, int, int, u_long, u_long); int bus_generic_rl_release_resource (device_t, device_t, int, int, struct resource *); int bus_generic_shutdown(device_t dev); int bus_generic_suspend(device_t dev); int bus_generic_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie); int bus_generic_write_ivar(device_t dev, device_t child, int which, uintptr_t value); /* * Wrapper functions for the BUS_*_RESOURCE methods to make client code * a little simpler. */ struct resource *bus_alloc_resource(device_t dev, int type, int *rid, u_long start, u_long end, u_long count, u_int flags); int bus_activate_resource(device_t dev, int type, int rid, struct resource *r); int bus_deactivate_resource(device_t dev, int type, int rid, struct resource *r); int bus_release_resource(device_t dev, int type, int rid, struct resource *r); int bus_setup_intr(device_t dev, struct resource *r, int flags, driver_intr_t handler, void *arg, void **cookiep); int bus_teardown_intr(device_t dev, struct resource *r, void *cookie); int bus_set_resource(device_t dev, int type, int rid, u_long start, u_long count); int bus_get_resource(device_t dev, int type, int rid, u_long *startp, u_long *countp); u_long bus_get_resource_start(device_t dev, int type, int rid); u_long bus_get_resource_count(device_t dev, int type, int rid); void bus_delete_resource(device_t dev, int type, int rid); /* * Access functions for device. */ device_t device_add_child(device_t dev, const char *name, int unit); device_t device_add_child_ordered(device_t dev, int order, const char *name, int unit); void device_busy(device_t dev); int device_delete_child(device_t dev, device_t child); int device_detach(device_t dev); void device_disable(device_t dev); void device_enable(device_t dev); device_t device_find_child(device_t dev, const char *classname, int unit); const char *device_get_desc(device_t dev); devclass_t device_get_devclass(device_t dev); driver_t *device_get_driver(device_t dev); u_int32_t device_get_flags(device_t dev); device_t device_get_parent(device_t dev); int device_get_children(device_t dev, device_t **listp, int *countp); void *device_get_ivars(device_t dev); void device_set_ivars(device_t dev, void *ivars); const char *device_get_name(device_t dev); const char *device_get_nameunit(device_t dev); void *device_get_softc(device_t dev); device_state_t device_get_state(device_t dev); int device_get_unit(device_t dev); int device_is_alive(device_t dev); /* did probe succeed? */ int device_is_enabled(device_t dev); int device_is_quiet(device_t dev); int device_print_prettyname(device_t dev); int device_printf(device_t dev, const char *, ...) __printflike(2, 3); int device_probe_and_attach(device_t dev); void device_quiet(device_t dev); void device_set_desc(device_t dev, const char* desc); void device_set_desc_copy(device_t dev, const char* desc); int device_set_devclass(device_t dev, const char *classname); int device_set_driver(device_t dev, driver_t *driver); void device_set_flags(device_t dev, u_int32_t flags); void device_set_softc(device_t dev, void *softc); int device_set_unit(device_t dev, int unit); /* XXX DONT USE XXX */ int device_shutdown(device_t dev); void device_unbusy(device_t dev); void device_verbose(device_t dev); /* * Access functions for devclass. */ int devclass_add_driver(devclass_t dc, driver_t *driver); int devclass_delete_driver(devclass_t dc, driver_t *driver); devclass_t devclass_create(const char *classname); devclass_t devclass_find(const char *classname); driver_t *devclass_find_driver(devclass_t dc, const char *classname); const char *devclass_get_name(devclass_t dc); device_t devclass_get_device(devclass_t dc, int unit); void *devclass_get_softc(devclass_t dc, int unit); int devclass_get_devices(devclass_t dc, device_t **listp, int *countp); int devclass_get_maxunit(devclass_t dc); /* * Access functions for device resources. */ int resource_int_value(const char *name, int unit, const char *resname, int *result); int resource_long_value(const char *name, int unit, const char *resname, long *result); int resource_string_value(const char *name, int unit, const char *resname, - char **result); -int resource_query_string(int i, const char *resname, const char *value); -char *resource_query_name(int i); -int resource_query_unit(int i); -int resource_locate(int i, const char *resname); + const char **result); +int resource_find_match(int *anchor, const char **name, int *unit, + const char *resname, const char *value); +int resource_find_dev(int *anchor, const char *name, int *unit, + const char *resname, const char *value); int resource_set_int(const char *name, int unit, const char *resname, int value); int resource_set_long(const char *name, int unit, const char *resname, long value); int resource_set_string(const char *name, int unit, const char *resname, const char *value); -int resource_count(void); - /* * Functions for maintaining and checking consistency of * bus information exported to userspace. */ int bus_data_generation_check(int generation); void bus_data_generation_update(void); /* * Shorthand for constructing method tables. */ #define DEVMETHOD KOBJMETHOD /* * Some common device interfaces. */ #include "device_if.h" #include "bus_if.h" struct module; int driver_module_handler(struct module *, int, void *); /* * Module support for automatically adding drivers to busses. */ struct driver_module_data { int (*dmd_chainevh)(struct module *, int, void *); void *dmd_chainarg; const char *dmd_busname; driver_t **dmd_drivers; int dmd_ndrivers; devclass_t *dmd_devclass; }; #define DRIVER_MODULE(name, busname, driver, devclass, evh, arg) \ \ static driver_t *name##_##busname##_driver_list[] = { &driver }; \ static struct driver_module_data name##_##busname##_driver_mod = { \ evh, arg, \ #busname, \ name##_##busname##_driver_list, \ (sizeof name##_##busname##_driver_list) / \ (sizeof name##_##busname##_driver_list[0]), \ &devclass \ }; \ \ static moduledata_t name##_##busname##_mod = { \ #busname "/" #name, \ driver_module_handler, \ &name##_##busname##_driver_mod \ }; \ DECLARE_MODULE(name##_##busname, name##_##busname##_mod, \ SI_SUB_DRIVERS, SI_ORDER_MIDDLE) #define MULTI_DRIVER_MODULE(name, busname, drivers, devclass, evh, arg) \ \ static driver_t name##_##busname##_driver_list[] = drivers; \ static struct driver_module_data name##_##busname##_driver_mod = { \ evh, arg, \ #busname, \ name##_##busname##_driver_list, \ (sizeof name##_##busname##_driver_list) / \ (sizeof name##_##busname##_driver_list[0]), \ &devclass \ }; \ \ static moduledata_t name##_##busname##_mod = { \ #busname "/" #name, \ driver_module_handler, \ &name##_##busname##_driver_mod \ }; \ DECLARE_MODULE(name##_##busname, name##_##busname##_mod, \ SI_SUB_DRIVERS, SI_ORDER_MIDDLE) #endif /* _KERNEL */ #endif /* !_SYS_BUS_H_ */ Index: head/sys/sys/bus_private.h =================================================================== --- head/sys/sys/bus_private.h (revision 78134) +++ head/sys/sys/bus_private.h (revision 78135) @@ -1,132 +1,108 @@ /*- * Copyright (c) 1997,1998 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * * $FreeBSD$ */ #ifndef _SYS_BUS_PRIVATE_H_ #define _SYS_BUS_PRIVATE_H_ #include /* * Used to attach drivers to devclasses. */ typedef struct driverlink *driverlink_t; struct driverlink { driver_t *driver; TAILQ_ENTRY(driverlink) link; /* list of drivers in devclass */ }; /* * Forward declarations */ typedef TAILQ_HEAD(devclass_list, devclass) devclass_list_t; typedef TAILQ_HEAD(driver_list, driverlink) driver_list_t; typedef TAILQ_HEAD(device_list, device) device_list_t; struct devclass { TAILQ_ENTRY(devclass) link; driver_list_t drivers; /* bus devclasses store drivers for bus */ char *name; device_t *devices; /* array of devices indexed by unit */ int maxunit; /* size of devices array */ }; /* - * Resources from config(8). - */ -typedef enum { - RES_INT, RES_STRING, RES_LONG -} resource_type; - -struct config_resource { - char *name; - resource_type type; - union { - long longval; - int intval; - char* stringval; - } u; -}; - -struct config_device { - char *name; /* e.g. "lpt", "wdc" etc */ - int unit; - int resource_count; - struct config_resource *resources; -}; - -/* * Implementation of device. */ struct device { /* * A device is a kernel object. The first field must be the * current ops table for the object. */ KOBJ_FIELDS; /* * Device hierarchy. */ TAILQ_ENTRY(device) link; /* list of devices in parent */ TAILQ_ENTRY(device) devlink; /* global device list membership */ device_t parent; device_list_t children; /* list of subordinate devices */ /* * Details of this device. */ driver_t *driver; devclass_t devclass; /* device class which we are in */ int unit; char* nameunit; /* name+unit e.g. foodev0 */ char* desc; /* driver specific description */ int busy; /* count of calls to device_busy() */ device_state_t state; u_int32_t devflags; /* api level flags for device_get_flags() */ u_short flags; #define DF_ENABLED 1 /* device should be probed/attached */ #define DF_FIXEDCLASS 2 /* devclass specified at create time */ #define DF_WILDCARD 4 /* unit was originally wildcard */ #define DF_DESCMALLOCED 8 /* description was malloced */ #define DF_QUIET 16 /* don't print verbose attach message */ #define DF_DONENOMATCH 32 /* don't execute DEVICE_NOMATCH again */ #define DF_EXTERNALSOFTC 64 /* softc not allocated by us */ u_char order; /* order from device_add_child_ordered() */ u_char pad; void *ivars; void *softc; }; struct device_op_desc { unsigned int offset; /* offset in driver ops */ struct method* method; /* internal method implementation */ devop_t deflt; /* default implementation */ const char* name; /* unique name (for registration) */ }; #endif /* !_SYS_BUS_PRIVATE_H_ */