Index: head/sys/amd64/amd64/fpu.c =================================================================== --- head/sys/amd64/amd64/fpu.c (revision 592) +++ head/sys/amd64/amd64/fpu.c (revision 593) @@ -1,564 +1,554 @@ /*- * Copyright (c) 1990 William Jolitz. * Copyright (c) 1991 The Regents of the University of California. * 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. * 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. * - * @(#)npx.c 7.2 (Berkeley) 5/12/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00154 - * -------------------- ----- ---------------------- - * - * 20 Apr 93 Bruce Evans New npx-0.5 code - * 23 May 93 Rodney W. Grimes Return a special value of -1 from - * the probe code to keep isa_config from - * printing out the I/O address when we - * are using trap 16 handling. - * + * from: @(#)npx.c 7.2 (Berkeley) 5/12/91 + * $Id$ */ -static char rcsid[] = "$Header: /a/cvs/386BSD/src/sys.386bsd/i386/isa/npx.c,v 1.1.1.1 1993/06/12 14:58:00 rgrimes Exp $"; #include "npx.h" #if NNPX > 0 #include "param.h" #include "systm.h" #include "conf.h" #include "file.h" #include "proc.h" #include "machine/cpu.h" #include "machine/pcb.h" #include "machine/trap.h" #include "ioctl.h" #include "machine/specialreg.h" #include "i386/isa/icu.h" #include "i386/isa/isa_device.h" #include "i386/isa/isa.h" /* * 387 and 287 Numeric Coprocessor Extension (NPX) Driver. */ #ifdef __GNUC__ #define disable_intr() __asm("cli") #define enable_intr() __asm("sti") #define fldcw(addr) __asm("fldcw %0" : : "m" (*addr)) #define fnclex() __asm("fnclex") #define fninit() __asm("fninit") #define fnsave(addr) __asm("fnsave %0" : "=m" (*addr) : "0" (*addr)) #define fnstcw(addr) __asm("fnstcw %0" : "=m" (*addr) : "0" (*addr)) #define fnstsw(addr) __asm("fnstsw %0" : "=m" (*addr) : "0" (*addr)) #define fp_divide_by_0() __asm("fldz; fld1; fdiv %st,%st(1); fwait") #define frstor(addr) __asm("frstor %0" : : "m" (*addr)) #define fwait() __asm("fwait") #define read_eflags() ({u_long ef; \ __asm("pushf; popl %0" : "=a" (ef)); \ ef; }) #define start_emulating() __asm("smsw %%ax; orb %0,%%al; lmsw %%ax" \ : : "n" (CR0_TS) : "ax") #define stop_emulating() __asm("clts") #define write_eflags(ef) __asm("pushl %0; popf" : : "a" ((u_long) ef)) #else /* not __GNUC__ */ void disable_intr __P((void)); void enable_intr __P((void)); void fldcw __P((caddr_t addr)); void fnclex __P((void)); void fninit __P((void)); void fnsave __P((caddr_t addr)); void fnstcw __P((caddr_t addr)); void fnstsw __P((caddr_t addr)); void fp_divide_by_0 __P((void)); void frstor __P((caddr_t addr)); void fwait __P((void)); u_long read_eflags __P((void)); void start_emulating __P((void)); void stop_emulating __P((void)); void write_eflags __P((u_long ef)); #endif /* __GNUC__ */ typedef u_char bool_t; extern struct gate_descriptor idt[]; int npxdna __P((void)); void npxexit __P((struct proc *p)); void npxinit __P((u_int control)); void npxintr __P((struct intrframe frame)); void npxsave __P((struct save87 *addr)); static int npxattach __P((struct isa_device *dvp)); static int npxprobe __P((struct isa_device *dvp)); static int npxprobe1 __P((struct isa_device *dvp)); struct isa_driver npxdriver = { npxprobe, npxattach, "npx", }; u_int npx0mask; struct proc *npxproc; static bool_t npx_ex16; static bool_t npx_exists; static struct gate_descriptor npx_idt_probeintr; static int npx_intrno; static volatile u_int npx_intrs_while_probing; static bool_t npx_irq13; static volatile u_int npx_traps_while_probing; /* * Special interrupt handlers. Someday intr0-intr15 will be used to count * interrupts. We'll still need a special exception 16 handler. The busy * latch stuff in probintr() can be moved to npxprobe(). */ void probeintr(void); asm (" .text _probeintr: ss incl _npx_intrs_while_probing pushl %eax movb $0x20,%al /* EOI (asm in strings loses cpp features) */ outb %al,$0xa0 /* IO_ICU2 */ outb %al,$0x20 /* IO_ICU1 */ movb $0,%al outb %al,$0xf0 /* clear BUSY# latch */ popl %eax iret "); void probetrap(void); asm (" .text _probetrap: ss incl _npx_traps_while_probing fnclex iret "); /* * Probe routine. Initialize cr0 to give correct behaviour for [f]wait * whether the device exists or not (XXX should be elsewhere). Set flags * to tell npxattach() what to do. Modify device struct if npx doesn't * need to use interrupts. Return 1 if device exists. */ static int npxprobe(dvp) struct isa_device *dvp; { int result; u_long save_eflags; u_char save_icu1_mask; u_char save_icu2_mask; struct gate_descriptor save_idt_npxintr; struct gate_descriptor save_idt_npxtrap; /* * This routine is now just a wrapper for npxprobe1(), to install * special npx interrupt and trap handlers, to enable npx interrupts * and to disable other interrupts. Someday isa_configure() will * install suitable handlers and run with interrupts enabled so we * won't need to do so much here. */ npx_intrno = NRSVIDT + ffs(dvp->id_irq) - 1; save_eflags = read_eflags(); disable_intr(); save_icu1_mask = inb(IO_ICU1 + 1); save_icu2_mask = inb(IO_ICU2 + 1); save_idt_npxintr = idt[npx_intrno]; save_idt_npxtrap = idt[16]; outb(IO_ICU1 + 1, ~(IRQ_SLAVE | dvp->id_irq)); outb(IO_ICU2 + 1, ~(dvp->id_irq >> 8)); setidt(16, probetrap, SDT_SYS386TGT, SEL_KPL); setidt(npx_intrno, probeintr, SDT_SYS386IGT, SEL_KPL); npx_idt_probeintr = idt[npx_intrno]; enable_intr(); result = npxprobe1(dvp); disable_intr(); outb(IO_ICU1 + 1, save_icu1_mask); outb(IO_ICU2 + 1, save_icu2_mask); idt[npx_intrno] = save_idt_npxintr; idt[16] = save_idt_npxtrap; write_eflags(save_eflags); return (result); } static int npxprobe1(dvp) struct isa_device *dvp; { int control; int status; #ifdef lint npxintr(); #endif /* * Partially reset the coprocessor, if any. Some BIOS's don't reset * it after a warm boot. */ outb(0xf1, 0); /* full reset on some systems, NOP on others */ outb(0xf0, 0); /* clear BUSY# latch */ /* * Prepare to trap all ESC (i.e., NPX) instructions and all WAIT * instructions. We must set the CR0_MP bit and use the CR0_TS * bit to control the trap, because setting the CR0_EM bit does * not cause WAIT instructions to trap. It's important to trap * WAIT instructions - otherwise the "wait" variants of no-wait * control instructions would degenerate to the "no-wait" variants * after FP context switches but work correctly otherwise. It's * particularly important to trap WAITs when there is no NPX - * otherwise the "wait" variants would always degenerate. * * Try setting CR0_NE to get correct error reporting on 486DX's. * Setting it should fail or do nothing on lesser processors. */ load_cr0(rcr0() | CR0_MP | CR0_NE); /* * But don't trap while we're probing. */ stop_emulating(); /* * Finish resetting the coprocessor, if any. If there is an error * pending, then we may get a bogus IRQ13, but probeintr() will handle * it OK. Bogus halts have never been observed, but we enabled * IRQ13 and cleared the BUSY# latch early to handle them anyway. */ fninit(); DELAY(1000); /* wait for any IRQ13 (fwait might hang) */ #ifdef DIAGNOSTIC if (npx_intrs_while_probing != 0) printf("fninit caused %u bogus npx interrupt(s)\n", npx_intrs_while_probing); if (npx_traps_while_probing != 0) printf("fninit caused %u bogus npx trap(s)\n", npx_traps_while_probing); #endif /* * Check for a status of mostly zero. */ status = 0x5a5a; fnstsw(&status); if ((status & 0xb8ff) == 0) { /* * Good, now check for a proper control word. */ control = 0x5a5a; fnstcw(&control); if ((control & 0x1f3f) == 0x033f) { npx_exists = 1; /* * We have an npx, now divide by 0 to see if exception * 16 works. */ control &= ~(1 << 2); /* enable divide by 0 trap */ fldcw(&control); npx_traps_while_probing = npx_intrs_while_probing = 0; fp_divide_by_0(); if (npx_traps_while_probing != 0) { /* * Good, exception 16 works. */ npx_ex16 = 1; dvp->id_irq = 0; /* zap the interrupt */ /* * special return value to flag that we do not * actually use any I/O registers */ return (-1); } if (npx_intrs_while_probing != 0) { /* * Bad, we are stuck with IRQ13. */ npx_irq13 = 1; npx0mask = dvp->id_irq; /* npxattach too late */ return (IO_NPXSIZE); } /* * Worse, even IRQ13 is broken. Use emulator. */ } } /* * Probe failed, but we want to get to npxattach to initialize the * emulator and say that it has been installed. XXX handle devices * that aren't really devices better. */ dvp->id_irq = 0; - return (IO_NPXSIZE); + /* + * special return value to flag that we do not + * actually use any I/O registers + */ + return (-1); } /* * Attach routine - announce which it is, and wire into system */ int npxattach(dvp) struct isa_device *dvp; { - if (npx_ex16) - printf("npx%d: Errors reported via Exception 16\n",dvp->id_unit); - else if (npx_irq13) - printf("npx%d: Errors reported via IRQ 13\n",dvp->id_unit); - else if (npx_exists) - printf("npx%d: Error reporting broken, using 387 emulator\n",dvp->id_unit); - else - printf("npx%d: 387 Emulator\n",dvp->id_unit); + if (!npx_ex16 && !npx_irq13) { + if (npx_exists) + printf("npx%d: Error reporting broken, using 387 emulator\n",dvp->id_unit); + else + printf("npx%d: 387 Emulator\n",dvp->id_unit); + } npxinit(__INITIAL_NPXCW__); return (1); /* XXX unused */ } /* * Initialize floating point unit. */ void npxinit(control) u_int control; { struct save87 dummy; if (!npx_exists) return; /* * fninit has the same h/w bugs as fnsave. Use the detoxified * fnsave to throw away any junk in the fpu. fnsave initializes * the fpu and sets npxproc = NULL as important side effects. */ npxsave(&dummy); stop_emulating(); fldcw(&control); if (curpcb != NULL) fnsave(&curpcb->pcb_savefpu); start_emulating(); } /* * Free coprocessor (if we have it). */ void npxexit(p) struct proc *p; { if (p == npxproc) { start_emulating(); npxproc = NULL; } } /* * Record the FPU state and reinitialize it all except for the control word. * Then generate a SIGFPE. * * Reinitializing the state allows naive SIGFPE handlers to longjmp without * doing any fixups. * * XXX there is currently no way to pass the full error state to signal * handlers, and if this is a nested interrupt there is no way to pass even * a status code! So there is no way to have a non-naive SIGFPE handler. At * best a handler could do an fninit followed by an fldcw of a static value. * fnclex would be of little use because it would leave junk on the FPU stack. * Returning from the handler would be even less safe than usual because * IRQ13 exception handling makes exceptions even less precise than usual. */ void npxintr(frame) struct intrframe frame; { int code; if (npxproc == NULL || !npx_exists) { /* XXX no %p in stand/printf.c. Cast to quiet gcc -Wall. */ printf("npxintr: npxproc = %lx, curproc = %lx, npx_exists = %d\n", (u_long) npxproc, (u_long) curproc, npx_exists); panic("npxintr from nowhere"); } if (npxproc != curproc) { printf("npxintr: npxproc = %lx, curproc = %lx, npx_exists = %d\n", (u_long) npxproc, (u_long) curproc, npx_exists); panic("npxintr from non-current process"); } /* * Save state. This does an implied fninit. It had better not halt * the cpu or we'll hang. */ outb(0xf0, 0); fnsave(&curpcb->pcb_savefpu); fwait(); /* * Restore control word (was clobbered by fnsave). */ fldcw(&curpcb->pcb_savefpu.sv_env.en_cw); fwait(); /* * Remember the exception status word and tag word. The current * (almost fninit'ed) fpu state is in the fpu and the exception * state just saved will soon be junk. However, the implied fninit * doesn't change the error pointers or register contents, and we * preserved the control word and will copy the status and tag * words, so the complete exception state can be recovered. */ curpcb->pcb_savefpu.sv_ex_sw = curpcb->pcb_savefpu.sv_env.en_sw; curpcb->pcb_savefpu.sv_ex_tw = curpcb->pcb_savefpu.sv_env.en_tw; /* * Pass exception to process. */ if (ISPL(frame.if_cs) == SEL_UPL) { /* * Interrupt is essentially a trap, so we can afford to call * the SIGFPE handler (if any) as soon as the interrupt * returns. * * XXX little or nothing is gained from this, and plenty is * lost - the interrupt frame has to contain the trap frame * (this is otherwise only necessary for the rescheduling trap * in doreti, and the frame for that could easily be set up * just before it is used). */ curproc->p_regs = (int *)&frame.if_es; curpcb->pcb_flags |= FM_TRAP; /* used by sendsig */ #ifdef notyet /* * Encode the appropriate code for detailed information on * this exception. */ code = XXX_ENCODE(curpcb->pcb_savefpu.sv_ex_sw); #else code = 0; /* XXX */ #endif trapsignal(curproc, SIGFPE, code); curpcb->pcb_flags &= ~FM_TRAP; } else { /* * Nested interrupt. These losers occur when: * o an IRQ13 is bogusly generated at a bogus time, e.g.: * o immediately after an fnsave or frstor of an * error state. * o a couple of 386 instructions after * "fstpl _memvar" causes a stack overflow. * These are especially nasty when combined with a * trace trap. * o an IRQ13 occurs at the same time as another higher- * priority interrupt. * * Treat them like a true async interrupt. */ psignal(npxproc, SIGFPE); } } /* * Implement device not available (DNA) exception * * It would be better to switch FP context here (only). This would require * saving the state in the proc table instead of in the pcb. */ int npxdna() { if (!npx_exists) return (0); if (npxproc != NULL) { printf("npxdna: npxproc = %lx, curproc = %lx\n", (u_long) npxproc, (u_long) curproc); panic("npxdna"); } stop_emulating(); /* * Record new context early in case frstor causes an IRQ13. */ npxproc = curproc; /* * The following frstor may cause an IRQ13 when the state being * restored has a pending error. The error will appear to have been * triggered by the current (npx) user instruction even when that * instruction is a no-wait instruction that should not trigger an * error (e.g., fnclex). On at least one 486 system all of the * no-wait instructions are broken the same as frstor, so our * treatment does not amplify the breakage. On at least one * 386/Cyrix 387 system, fnclex works correctly while frstor and * fnsave are broken, so our treatment breaks fnclex if it is the * first FPU instruction after a context switch. */ frstor(&curpcb->pcb_savefpu); return (1); } /* * Wrapper for fnsave instruction to handle h/w bugs. If there is an error * pending, then fnsave generates a bogus IRQ13 on some systems. Force * any IRQ13 to be handled immediately, and then ignore it. This routine is * often called at splhigh so it must not use many system services. In * particular, it's much easier to install a special handler than to * guarantee that it's safe to use npxintr() and its supporting code. */ void npxsave(addr) struct save87 *addr; { u_char icu1_mask; u_char icu2_mask; u_char old_icu1_mask; u_char old_icu2_mask; struct gate_descriptor save_idt_npxintr; disable_intr(); old_icu1_mask = inb(IO_ICU1 + 1); old_icu2_mask = inb(IO_ICU2 + 1); save_idt_npxintr = idt[npx_intrno]; outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0mask)); outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0mask >> 8)); idt[npx_intrno] = npx_idt_probeintr; enable_intr(); stop_emulating(); fnsave(addr); fwait(); start_emulating(); npxproc = NULL; disable_intr(); icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */ icu2_mask = inb(IO_ICU2 + 1); outb(IO_ICU1 + 1, (icu1_mask & ~npx0mask) | (old_icu1_mask & npx0mask)); outb(IO_ICU2 + 1, (icu2_mask & ~(npx0mask >> 8)) | (old_icu2_mask & (npx0mask >> 8))); idt[npx_intrno] = save_idt_npxintr; enable_intr(); /* back to usual state */ } #endif /* NNPX > 0 */ Index: head/sys/amd64/isa/isa.c =================================================================== --- head/sys/amd64/isa/isa.c (revision 592) +++ head/sys/amd64/isa/isa.c (revision 593) @@ -1,760 +1,791 @@ /*- * Copyright (c) 1991 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. * - * @(#)isa.c 7.2 (Berkeley) 5/13/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 4 00163 - * -------------------- ----- ---------------------- - * - * 18 Aug 92 Frank Maclachlan *See comments below - * 25 Mar 93 Rodney W. Grimes Added counter for stray interrupt, - * turned on logging of stray interrupts, - * Now prints maddr, msize, and flags - * after finding a device. - * 26 Apr 93 Bruce Evans New intr-0.1 code - * Rodney W. Grimes Only print io address if id_alive != -1 - * 17 May 93 Rodney W. Grimes renamed stray interrupt counters to - * work with new intr-0.1 code. - * Enabled printf for interrupt masks to - * aid in bug reports. - * 27 May 93 Guido van Rooij New routine add find_isa_dev + * from: @(#)isa.c 7.2 (Berkeley) 5/13/91 + * $Id$ */ -static char rcsid[] = "$Header: /a/cvs/386BSD/src/sys/i386/isa/isa.c,v 1.2 1993/06/18 22:18:57 rgrimes Exp $"; /* * code to manage AT bus * * 92/08/18 Frank P. MacLachlan (fpm@crash.cts.com): * Fixed uninitialized variable problem and added code to deal * with DMA page boundaries in isa_dmarangecheck(). Fixed word * mode DMA count compution and reorganized DMA setup code in * isa_dmastart() */ #include "param.h" #include "systm.h" #include "conf.h" #include "file.h" #include "buf.h" #include "uio.h" #include "syslog.h" #include "malloc.h" #include "rlist.h" #include "machine/segments.h" #include "vm/vm.h" #include "i386/isa/isa_device.h" #include "i386/isa/isa.h" #include "i386/isa/icu.h" #include "i386/isa/ic/i8237.h" #include "i386/isa/ic/i8042.h" /* ** Register definitions for DMA controller 1 (channels 0..3): */ #define DMA1_CHN(c) (IO_DMA1 + 1*(2*(c))) /* addr reg for channel c */ #define DMA1_SMSK (IO_DMA1 + 1*10) /* single mask register */ #define DMA1_MODE (IO_DMA1 + 1*11) /* mode register */ #define DMA1_FFC (IO_DMA1 + 1*12) /* clear first/last FF */ /* ** Register definitions for DMA controller 2 (channels 4..7): */ #define DMA2_CHN(c) (IO_DMA1 + 2*(2*(c))) /* addr reg for channel c */ #define DMA2_SMSK (IO_DMA2 + 2*10) /* single mask register */ #define DMA2_MODE (IO_DMA2 + 2*11) /* mode register */ #define DMA2_FFC (IO_DMA2 + 2*12) /* clear first/last FF */ int config_isadev __P((struct isa_device *, u_int *)); -#ifdef notyet -struct rlist *isa_iomem; - /* - * Configure all ISA devices + * print a conflict message */ -isa_configure() { - struct isa_device *dvp; - struct isa_driver *dp; - - splhigh(); - INTREN(IRQ_SLAVE); - /*rlist_free(&isa_iomem, 0xa0000, 0xfffff);*/ - for (dvp = isa_devtab_tty; dvp; dvp++) - (void) config_isadev(dvp, &ttymask); - for (dvp = isa_devtab_bio; dvp; dvp++) - (void) config_isadev(dvp, &biomask); - for (dvp = isa_devtab_net; dvp; dvp++) - (void) config_isadev(dvp, &netmask); - for (dvp = isa_devtab_null; dvp; dvp++) - (void) config_isadev(dvp, (u_int *) NULL); -#include "sl.h" -#if NSL > 0 - netmask |= ttymask; - ttymask |= netmask; -#endif -/* printf("biomask %x ttymask %x netmask %x\n", biomask, ttymask, netmask); */ - splnone(); +void +conflict(dvp, tmpdvp, item, reason, format) + struct isa_device *dvp, *tmpdvp; + int item; + char *reason; + char *format; +{ + printf("%s%d not probed due to %s conflict with %s%d at ", + dvp->id_driver->name, dvp->id_unit, reason, + tmpdvp->id_driver->name, tmpdvp->id_unit); + printf(format, item); + printf("\n"); } /* - * Configure an ISA device. + * Check to see if things are alread in use, like IRQ's, I/O addresses + * and Memory addresses. */ -config_isadev(isdp, mp) - struct isa_device *isdp; - u_int *mp; +int +haveseen(dvp, tmpdvp) + struct isa_device *dvp, *tmpdvp; { - struct isa_driver *dp; - static short drqseen, irqseen; - - if (dp = isdp->id_driver) { - /* if a device with i/o memory, convert to virtual address */ - if (isdp->id_maddr) { - extern unsigned int atdevbase; + int status = 0; - isdp->id_maddr -= IOM_BEGIN; - isdp->id_maddr += atdevbase; + /* + * Only check against devices that have already been found + */ + if (tmpdvp->id_alive) { + /* + * Check for I/O address conflict. We can only check the + * starting address of the device against the range of the + * device that has already been probed since we do not + * know how many I/O addresses this device uses. + */ + if (tmpdvp->id_alive != -1) { + if ((dvp->id_iobase >= tmpdvp->id_iobase) && + (dvp->id_iobase <= + (tmpdvp->id_iobase + tmpdvp->id_alive - 1))) { + conflict(dvp, tmpdvp, dvp->id_iobase, + "I/O address", "0x%x"); + status = 1; + } } - isdp->id_alive = (*dp->probe)(isdp); - if (isdp->id_alive) { - - printf("%s%d at port 0x%x ", dp->name, - isdp->id_unit, isdp->id_iobase); - - /* check for conflicts */ - if (irqseen & isdp->id_irq) { - printf("INTERRUPT CONFLICT - irq%d\n", - ffs(isdp->id_irq) - 1); - return (0); + /* + * Check for Memory address conflict. We can check for + * range overlap, but it will not catch all cases since the + * driver may adjust the msize paramater during probe, for + * now we just check that the starting address does not + * fall within any allocated region. + * XXX could add a second check after the probe for overlap, + * since at that time we would know the full range. + * XXX KERNBASE is a hack, we should have vaddr in the table! + */ + if(tmpdvp->id_maddr) { + if((KERNBASE + dvp->id_maddr >= tmpdvp->id_maddr) && + (KERNBASE + dvp->id_maddr <= + (tmpdvp->id_maddr + tmpdvp->id_msize - 1))) { + conflict(dvp, tmpdvp, dvp->id_maddr, "maddr", + "0x%x"); + status = 1; } - if (isdp->id_drq != -1 - && (drqseen & (1<id_drq))) { - printf("DMA CONFLICT - drq%d\n", isdp->id_drq); - return (0); + } + /* + * Check for IRQ conflicts. + */ + if(tmpdvp->id_irq) { + if (tmpdvp->id_irq == dvp->id_irq) { + conflict(dvp, tmpdvp, ffs(dvp->id_irq) - 1, + "irq", "%d"); + status = 1; } - /* NEED TO CHECK IOMEM CONFLICT HERE */ - - /* allocate and wire in device */ - if(isdp->id_irq) { - int intrno; - - intrno = ffs(isdp->id_irq)-1; - printf("irq %d ", intrno); - INTREN(isdp->id_irq); - if(mp)INTRMASK(*mp,isdp->id_irq); - setidt(NRSVIDT + intrno, isdp->id_intr, - SDT_SYS386IGT, SEL_KPL); - irqseen |= isdp->id_irq; + } + /* + * Check for DRQ conflicts. + */ + if(tmpdvp->id_drq != -1) { + if (tmpdvp->id_drq == dvp->id_drq) { + conflict(dvp, tmpdvp, dvp->id_drq, + "drq", "%d"); + status = 1; } - if (isdp->id_drq != -1) { - printf("drq %d ", isdp->id_drq); - drqseen |= 1 << isdp->id_drq; - } + } + } + return (status); +} - (*dp->attach)(isdp); +/* + * Search through all the isa_devtab_* tables looking for anything that + * conflicts with the current device. + */ +int +haveseen_isadev(dvp) + struct isa_device *dvp; +{ + struct isa_device *tmpdvp; + int status = 0; - printf("on isa\n"); - } - return (1); - } else return(0); + for (tmpdvp = isa_devtab_tty; tmpdvp->id_driver; tmpdvp++) { + status |= haveseen(dvp, tmpdvp); + } + for (tmpdvp = isa_devtab_bio; tmpdvp->id_driver; tmpdvp++) { + status |= haveseen(dvp, tmpdvp); + } + for (tmpdvp = isa_devtab_net; tmpdvp->id_driver; tmpdvp++) { + status |= haveseen(dvp, tmpdvp); + } + for (tmpdvp = isa_devtab_null; tmpdvp->id_driver; tmpdvp++) { + status |= haveseen(dvp, tmpdvp); + } + return(status); } -#else /* notyet */ + /* * Configure all ISA devices */ +void isa_configure() { struct isa_device *dvp; - struct isa_driver *dp; enable_intr(); splhigh(); INTREN(IRQ_SLAVE); - for (dvp = isa_devtab_tty; config_isadev(dvp,&ttymask); dvp++); - for (dvp = isa_devtab_bio; config_isadev(dvp,&biomask); dvp++); - for (dvp = isa_devtab_net; config_isadev(dvp,&netmask); dvp++); - for (dvp = isa_devtab_null; config_isadev(dvp,(u_int *) NULL); dvp++); + printf("Probing for devices on the ISA bus:\n"); + for (dvp = isa_devtab_tty; dvp->id_driver; dvp++) { + if (!haveseen_isadev(dvp)) + config_isadev(dvp,&ttymask); + } + for (dvp = isa_devtab_bio; dvp->id_driver; dvp++) { + if (!haveseen_isadev(dvp)) + config_isadev(dvp,&biomask); + } + for (dvp = isa_devtab_net; dvp->id_driver; dvp++) { + if (!haveseen_isadev(dvp)) + config_isadev(dvp,&netmask); + } + for (dvp = isa_devtab_null; dvp->id_driver; dvp++) { + if (!haveseen_isadev(dvp)) + config_isadev(dvp,(u_int *) NULL); + } +/* + * XXX We should really add the tty device to netmask when the line is + * switched to SLIPDISC, and then remove it when it is switched away from + * SLIPDISC. No need to block out ALL ttys during a splnet when only one + * of them is running slip. + */ #include "sl.h" #if NSL > 0 netmask |= ttymask; ttymask |= netmask; #endif /* biomask |= ttymask ; can some tty devices use buffers? */ printf("biomask %x ttymask %x netmask %x\n", biomask, ttymask, netmask); splnone(); } /* * Configure an ISA device. */ config_isadev(isdp, mp) struct isa_device *isdp; u_int *mp; { - struct isa_driver *dp; + struct isa_driver *dp = isdp->id_driver; - if (dp = isdp->id_driver) { - if (isdp->id_maddr) { - extern u_int atdevbase; + if (isdp->id_maddr) { + extern u_int atdevbase; - isdp->id_maddr -= 0xa0000; /* XXX should be a define */ - isdp->id_maddr += atdevbase; - } - isdp->id_alive = (*dp->probe)(isdp); - if (isdp->id_alive) { - printf("%s%d", dp->name, isdp->id_unit); - /* - * Only print the I/O address range if id_alive != -1 - * Right now this is a temporary fix just for the new - * NPX code so that if it finds a 486 that can use trap - * 16 it will not report I/O addresses. - * Rod Grimes 04/26/94 - */ - if (isdp->id_alive != -1) { - printf(" at 0x%x", isdp->id_iobase); - if ((isdp->id_iobase + isdp->id_alive - 1) != - isdp->id_iobase) - printf("-0x%x", - isdp->id_iobase + - isdp->id_alive - 1); + isdp->id_maddr -= 0xa0000; /* XXX should be a define */ + isdp->id_maddr += atdevbase; + } + isdp->id_alive = (*dp->probe)(isdp); + if (isdp->id_alive) { + /* + * Only print the I/O address range if id_alive != -1 + * Right now this is a temporary fix just for the new + * NPX code so that if it finds a 486 that can use trap + * 16 it will not report I/O addresses. + * Rod Grimes 04/26/94 + */ + printf("%s%d", dp->name, isdp->id_unit); + if (isdp->id_alive != -1) { + printf(" at 0x%x", isdp->id_iobase); + if ((isdp->id_iobase + isdp->id_alive - 1) != + isdp->id_iobase) { + printf("-0x%x", + isdp->id_iobase + + isdp->id_alive - 1); } - if(isdp->id_irq) - printf(" irq %d", ffs(isdp->id_irq)-1); - if (isdp->id_drq != -1) - printf(" drq %d", isdp->id_drq); - if (isdp->id_maddr != 0) - printf(" maddr 0x%x", kvtop(isdp->id_maddr)); - if (isdp->id_msize != 0) - printf(" msize %d", isdp->id_msize); - if (isdp->id_flags != 0) - printf(" flags 0x%x", isdp->id_flags); + } + if(isdp->id_irq) + printf(" irq %d", ffs(isdp->id_irq) - 1); + if (isdp->id_drq != -1) + printf(" drq %d", isdp->id_drq); + if (isdp->id_maddr) + printf(" maddr 0x%x", kvtop(isdp->id_maddr)); + if (isdp->id_msize) + printf(" msize %d", isdp->id_msize); + if (isdp->id_flags) + printf(" flags 0x%x", isdp->id_flags); + if (isdp->id_iobase < 0x100) + printf(" on motherboard\n"); + else printf(" on isa\n"); - (*dp->attach)(isdp); + (*dp->attach)(isdp); - if(isdp->id_irq) { - int intrno; + if(isdp->id_irq) { + int intrno; - intrno = ffs(isdp->id_irq)-1; - setidt(ICU_OFFSET+intrno, isdp->id_intr, - SDT_SYS386IGT, SEL_KPL); - if(mp) - INTRMASK(*mp,isdp->id_irq); - INTREN(isdp->id_irq); + intrno = ffs(isdp->id_irq)-1; + setidt(ICU_OFFSET+intrno, isdp->id_intr, + SDT_SYS386IGT, SEL_KPL); + if(mp) { + INTRMASK(*mp,isdp->id_irq); } + INTREN(isdp->id_irq); } - return (1); - } else return(0); + } else { + printf("%s%d not found", dp->name, isdp->id_unit); + if (isdp->id_iobase) { + printf(" at 0x%x", isdp->id_iobase); + } + printf("\n"); + } } -#endif /* (!) notyet */ #define IDTVEC(name) __CONCAT(X,name) /* default interrupt vector table entries */ extern IDTVEC(intr0), IDTVEC(intr1), IDTVEC(intr2), IDTVEC(intr3), IDTVEC(intr4), IDTVEC(intr5), IDTVEC(intr6), IDTVEC(intr7), IDTVEC(intr8), IDTVEC(intr9), IDTVEC(intr10), IDTVEC(intr11), IDTVEC(intr12), IDTVEC(intr13), IDTVEC(intr14), IDTVEC(intr15); static *defvec[16] = { &IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3), &IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7), &IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11), &IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15) }; /* out of range default interrupt vector gate entry */ extern IDTVEC(intrdefault); /* * Fill in default interrupt table (in case of spuruious interrupt * during configuration of kernel, setup interrupt control unit */ isa_defaultirq() { int i; /* icu vectors */ for (i = NRSVIDT ; i < NRSVIDT+ICU_LEN ; i++) setidt(i, defvec[i], SDT_SYS386IGT, SEL_KPL); /* out of range vectors */ for (i = NRSVIDT; i < NIDT; i++) setidt(i, &IDTVEC(intrdefault), SDT_SYS386IGT, SEL_KPL); /* initialize 8259's */ outb(IO_ICU1, 0x11); /* reset; program device, four bytes */ outb(IO_ICU1+1, NRSVIDT); /* starting at this vector index */ outb(IO_ICU1+1, 1<<2); /* slave on line 2 */ #ifdef AUTO_EOI_1 outb(IO_ICU1+1, 2 | 1); /* auto EOI, 8086 mode */ #else outb(IO_ICU1+1, 1); /* 8086 mode */ #endif outb(IO_ICU1+1, 0xff); /* leave interrupts masked */ outb(IO_ICU1, 0x0a); /* default to IRR on read */ outb(IO_ICU1, 0xc0 | (3 - 1)); /* pri order 3-7, 0-2 (com2 first) */ outb(IO_ICU2, 0x11); /* reset; program device, four bytes */ outb(IO_ICU2+1, NRSVIDT+8); /* staring at this vector index */ outb(IO_ICU2+1,2); /* my slave id is 2 */ #ifdef AUTO_EOI_2 outb(IO_ICU2+1, 2 | 1); /* auto EOI, 8086 mode */ #else outb(IO_ICU2+1,1); /* 8086 mode */ #endif outb(IO_ICU2+1, 0xff); /* leave interrupts masked */ outb(IO_ICU2, 0x0a); /* default to IRR on read */ } /* region of physical memory known to be contiguous */ vm_offset_t isaphysmem; static caddr_t dma_bounce[8]; /* XXX */ static char bounced[8]; /* XXX */ #define MAXDMASZ 512 /* XXX */ /* high byte of address is stored in this port for i-th dma channel */ static short dmapageport[8] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a }; /* * isa_dmacascade(): program 8237 DMA controller channel to accept * external dma control by a board. */ void isa_dmacascade(unsigned chan) { if (chan > 7) panic("isa_dmacascade: impossible request"); /* set dma channel mode, and set dma channel mode */ if ((chan & 4) == 0) { outb(DMA1_MODE, DMA37MD_CASCADE | chan); outb(DMA1_SMSK, chan); } else { outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3)); outb(DMA2_SMSK, chan & 3); } } /* * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment * problems by using a bounce buffer. */ void isa_dmastart(int flags, caddr_t addr, unsigned nbytes, unsigned chan) { vm_offset_t phys; int waport; caddr_t newaddr; if ( chan > 7 || (chan < 4 && nbytes > (1<<16)) || (chan >= 4 && (nbytes > (1<<17) || (u_int)addr & 1))) panic("isa_dmastart: impossible request"); if (isa_dmarangecheck(addr, nbytes, chan)) { if (dma_bounce[chan] == 0) dma_bounce[chan] = /*(caddr_t)malloc(MAXDMASZ, M_TEMP, M_WAITOK);*/ (caddr_t) isaphysmem + NBPG*chan; bounced[chan] = 1; newaddr = dma_bounce[chan]; *(int *) newaddr = 0; /* XXX */ /* copy bounce buffer on write */ if (!(flags & B_READ)) bcopy(addr, newaddr, nbytes); addr = newaddr; } /* translate to physical */ phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr); if ((chan & 4) == 0) { /* * Program one of DMA channels 0..3. These are * byte mode channels. */ /* set dma channel mode, and reset address ff */ if (flags & B_READ) outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|chan); else outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_READ|chan); outb(DMA1_FFC, 0); /* send start address */ waport = DMA1_CHN(chan); outb(waport, phys); outb(waport, phys>>8); outb(dmapageport[chan], phys>>16); /* send count */ outb(waport + 1, --nbytes); outb(waport + 1, nbytes>>8); /* unmask channel */ outb(DMA1_SMSK, chan); } else { /* * Program one of DMA channels 4..7. These are * word mode channels. */ /* set dma channel mode, and reset address ff */ if (flags & B_READ) outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3)); else outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_READ|(chan&3)); outb(DMA2_FFC, 0); /* send start address */ waport = DMA2_CHN(chan - 4); outb(waport, phys>>1); outb(waport, phys>>9); outb(dmapageport[chan], phys>>16); /* send count */ nbytes >>= 1; outb(waport + 2, --nbytes); outb(waport + 2, nbytes>>8); /* unmask channel */ outb(DMA2_SMSK, chan & 3); } } void isa_dmadone(int flags, caddr_t addr, int nbytes, int chan) { /* copy bounce buffer on read */ /*if ((flags & (B_PHYS|B_READ)) == (B_PHYS|B_READ))*/ if (bounced[chan]) { bcopy(dma_bounce[chan], addr, nbytes); bounced[chan] = 0; } } /* * Check for problems with the address range of a DMA transfer * (non-contiguous physical pages, outside of bus address space, * crossing DMA page boundaries). * Return true if special handling needed. */ isa_dmarangecheck(caddr_t va, unsigned length, unsigned chan) { vm_offset_t phys, priorpage = 0, endva; u_int dma_pgmsk = (chan & 4) ? ~(128*1024-1) : ~(64*1024-1); endva = (vm_offset_t)round_page(va + length); for (; va < (caddr_t) endva ; va += NBPG) { phys = trunc_page(pmap_extract(pmap_kernel(), (vm_offset_t)va)); #define ISARAM_END RAM_END if (phys == 0) panic("isa_dmacheck: no physical page present"); if (phys > ISARAM_END) return (1); if (priorpage) { if (priorpage + NBPG != phys) return (1); /* check if crossing a DMA page boundary */ if (((u_int)priorpage ^ (u_int)phys) & dma_pgmsk) return (1); } priorpage = phys; } return (0); } /* head of queue waiting for physmem to become available */ struct buf isa_physmemq; /* blocked waiting for resource to become free for exclusive use */ static isaphysmemflag; /* if waited for and call requested when free (B_CALL) */ static void (*isaphysmemunblock)(); /* needs to be a list */ /* * Allocate contiguous physical memory for transfer, returning * a *virtual* address to region. May block waiting for resource. * (assumed to be called at splbio()) */ caddr_t isa_allocphysmem(caddr_t va, unsigned length, void (*func)()) { isaphysmemunblock = func; while (isaphysmemflag & B_BUSY) { isaphysmemflag |= B_WANTED; sleep(&isaphysmemflag, PRIBIO); } isaphysmemflag |= B_BUSY; return((caddr_t)isaphysmem); } /* * Free contiguous physical memory used for transfer. * (assumed to be called at splbio()) */ void isa_freephysmem(caddr_t va, unsigned length) { isaphysmemflag &= ~B_BUSY; if (isaphysmemflag & B_WANTED) { isaphysmemflag &= B_WANTED; wakeup(&isaphysmemflag); if (isaphysmemunblock) (*isaphysmemunblock)(); } } /* * Handle a NMI, possibly a machine check. * return true to panic system, false to ignore. */ isa_nmi(cd) { log(LOG_CRIT, "\nNMI port 61 %x, port 70 %x\n", inb(0x61), inb(0x70)); return(0); } /* * Caught a stray interrupt, notify */ isa_strayintr(d) { /* DON'T BOTHER FOR NOW! */ /* for some reason, we get bursts of intr #7, even if not enabled! */ /* * Well the reason you got bursts of intr #7 is because someone * raised an interrupt line and dropped it before the 8259 could * prioritize it. This is documented in the intel data book. This * means you have BAD hardware! I have changed this so that only * the first 5 get logged, then it quits logging them, and puts * out a special message. rgrimes 3/25/1993 */ extern u_long intrcnt_stray; intrcnt_stray++; if (intrcnt_stray <= 5) log(LOG_ERR,"ISA strayintr %x\n", d); if (intrcnt_stray == 5) log(LOG_CRIT,"Too many ISA strayintr not logging any more\n"); } /* * Wait "n" microseconds. * Relies on timer 1 counting down from (TIMER_FREQ / hz) at * (1 * TIMER_FREQ) Hz. * Note: timer had better have been programmed before this is first used! * (The standard programming causes the timer to generate a square wave and * the counter is decremented twice every cycle.) */ #define CF (1 * TIMER_FREQ) #define TIMER_FREQ 1193182 /* XXX - should be elsewhere */ extern int hz; /* XXX - should be elsewhere */ int DELAY(n) int n; { int counter_limit; int prev_tick; int tick; int ticks_left; int sec; int usec; #ifdef DELAYDEBUG int getit_calls = 1; int n1; static int state = 0; if (state == 0) { state = 1; for (n1 = 1; n1 <= 10000000; n1 *= 10) DELAY(n1); state = 2; } if (state == 1) printf("DELAY(%d)...", n); #endif /* * Read the counter first, so that the rest of the setup overhead is * counted. Guess the initial overhead is 20 usec (on most systems it * takes about 1.5 usec for each of the i/o's in getit(). The loop * takes about 6 usec on a 486/33 and 13 usec on a 386/20. The * multiplications and divisions to scale the count take a while). */ prev_tick = getit(0, 0); n -= 20; /* * Calculate (n * (CF / 1e6)) without using floating point and without * any avoidable overflows. */ sec = n / 1000000; usec = n - sec * 1000000; ticks_left = sec * CF + usec * (CF / 1000000) + usec * ((CF % 1000000) / 1000) / 1000 + usec * (CF % 1000) / 1000000; counter_limit = TIMER_FREQ / hz; while (ticks_left > 0) { tick = getit(0, 0); #ifdef DELAYDEBUG ++getit_calls; #endif if (tick > prev_tick) ticks_left -= prev_tick - (tick - counter_limit); else ticks_left -= prev_tick - tick; prev_tick = tick; } #ifdef DELAYDEBUG if (state == 1) printf(" %d calls to getit() at %d usec each\n", getit_calls, (n + 5) / getit_calls); #endif } getit(unit, timer) { int high; int low; /* * XXX - isa.h defines bogus timers. There's no such timer as * IO_TIMER_2 = 0x48. There's a timer in the CMOS RAM chip but * its interface is quite different. Neither timer is an 8252. * We actually only call this with unit = 0 and timer = 0. It * could be static... */ /* * Protect ourself against interrupts. * XXX - sysbeep() and sysbeepstop() need protection. */ disable_intr(); /* * Latch the count for 'timer' (cc00xxxx, c = counter, x = any). */ outb(IO_TIMER1 + 3, timer << 6); low = inb(IO_TIMER1 + timer); high = inb(IO_TIMER1 + timer); enable_intr(); return ((high << 8) | low); } static beeping; static sysbeepstop(f) { /* disable counter 2 */ outb(0x61, inb(0x61) & 0xFC); if (f) timeout(sysbeepstop, 0, f); else beeping = 0; } void sysbeep(int pitch, int period) { outb(0x61, inb(0x61) | 3); /* enable counter 2 */ /* * XXX - move timer stuff to clock.c. * Program counter 2: * ccaammmb, c counter, a = access, m = mode, b = BCD * 1011x110, 11 for aa = LSB then MSB, x11 for mmm = square wave. */ outb(0x43, 0xb6); /* set command for counter 2, 2 byte write */ outb(0x42, pitch); outb(0x42, (pitch>>8)); if (!beeping) { beeping = period; timeout(sysbeepstop, period/2, period); } } /* * Pass command to keyboard controller (8042) */ unsigned kbc_8042cmd(val) { while (inb(KBSTATP)&KBS_IBF); if (val) outb(KBCMDP, val); while (inb(KBSTATP)&KBS_IBF); return (inb(KBDATAP)); } /* * find an ISA device in a given isa_devtab_* table, given * the table to search, the expected id_driver entry, and the unit number. * * this function is defined in isa_device.h, and this location is debatable; * i put it there because it's useless w/o, and directly operates on * the other stuff in that file. * */ struct isa_device *find_isadev(table, driverp, unit) struct isa_device *table; struct isa_driver *driverp; int unit; { if (driverp == NULL) /* sanity check */ return NULL; while ((table->id_driver != driverp) || (table->id_unit != unit)) { if (table->id_driver == 0) return NULL; table++; } return table; } /* * Return nonzero if a (masked) irq is pending for a given device. */ int isa_irq_pending(dvp) struct isa_device *dvp; { unsigned id_irq; id_irq = (unsigned short) dvp->id_irq; /* XXX silly type in struct */ if (id_irq & 0xff) return (inb(IO_ICU1) & id_irq); return (inb(IO_ICU2) & (id_irq >> 8)); } Index: head/sys/amd64/isa/npx.c =================================================================== --- head/sys/amd64/isa/npx.c (revision 592) +++ head/sys/amd64/isa/npx.c (revision 593) @@ -1,564 +1,554 @@ /*- * Copyright (c) 1990 William Jolitz. * Copyright (c) 1991 The Regents of the University of California. * 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. * 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. * - * @(#)npx.c 7.2 (Berkeley) 5/12/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00154 - * -------------------- ----- ---------------------- - * - * 20 Apr 93 Bruce Evans New npx-0.5 code - * 23 May 93 Rodney W. Grimes Return a special value of -1 from - * the probe code to keep isa_config from - * printing out the I/O address when we - * are using trap 16 handling. - * + * from: @(#)npx.c 7.2 (Berkeley) 5/12/91 + * $Id$ */ -static char rcsid[] = "$Header: /a/cvs/386BSD/src/sys.386bsd/i386/isa/npx.c,v 1.1.1.1 1993/06/12 14:58:00 rgrimes Exp $"; #include "npx.h" #if NNPX > 0 #include "param.h" #include "systm.h" #include "conf.h" #include "file.h" #include "proc.h" #include "machine/cpu.h" #include "machine/pcb.h" #include "machine/trap.h" #include "ioctl.h" #include "machine/specialreg.h" #include "i386/isa/icu.h" #include "i386/isa/isa_device.h" #include "i386/isa/isa.h" /* * 387 and 287 Numeric Coprocessor Extension (NPX) Driver. */ #ifdef __GNUC__ #define disable_intr() __asm("cli") #define enable_intr() __asm("sti") #define fldcw(addr) __asm("fldcw %0" : : "m" (*addr)) #define fnclex() __asm("fnclex") #define fninit() __asm("fninit") #define fnsave(addr) __asm("fnsave %0" : "=m" (*addr) : "0" (*addr)) #define fnstcw(addr) __asm("fnstcw %0" : "=m" (*addr) : "0" (*addr)) #define fnstsw(addr) __asm("fnstsw %0" : "=m" (*addr) : "0" (*addr)) #define fp_divide_by_0() __asm("fldz; fld1; fdiv %st,%st(1); fwait") #define frstor(addr) __asm("frstor %0" : : "m" (*addr)) #define fwait() __asm("fwait") #define read_eflags() ({u_long ef; \ __asm("pushf; popl %0" : "=a" (ef)); \ ef; }) #define start_emulating() __asm("smsw %%ax; orb %0,%%al; lmsw %%ax" \ : : "n" (CR0_TS) : "ax") #define stop_emulating() __asm("clts") #define write_eflags(ef) __asm("pushl %0; popf" : : "a" ((u_long) ef)) #else /* not __GNUC__ */ void disable_intr __P((void)); void enable_intr __P((void)); void fldcw __P((caddr_t addr)); void fnclex __P((void)); void fninit __P((void)); void fnsave __P((caddr_t addr)); void fnstcw __P((caddr_t addr)); void fnstsw __P((caddr_t addr)); void fp_divide_by_0 __P((void)); void frstor __P((caddr_t addr)); void fwait __P((void)); u_long read_eflags __P((void)); void start_emulating __P((void)); void stop_emulating __P((void)); void write_eflags __P((u_long ef)); #endif /* __GNUC__ */ typedef u_char bool_t; extern struct gate_descriptor idt[]; int npxdna __P((void)); void npxexit __P((struct proc *p)); void npxinit __P((u_int control)); void npxintr __P((struct intrframe frame)); void npxsave __P((struct save87 *addr)); static int npxattach __P((struct isa_device *dvp)); static int npxprobe __P((struct isa_device *dvp)); static int npxprobe1 __P((struct isa_device *dvp)); struct isa_driver npxdriver = { npxprobe, npxattach, "npx", }; u_int npx0mask; struct proc *npxproc; static bool_t npx_ex16; static bool_t npx_exists; static struct gate_descriptor npx_idt_probeintr; static int npx_intrno; static volatile u_int npx_intrs_while_probing; static bool_t npx_irq13; static volatile u_int npx_traps_while_probing; /* * Special interrupt handlers. Someday intr0-intr15 will be used to count * interrupts. We'll still need a special exception 16 handler. The busy * latch stuff in probintr() can be moved to npxprobe(). */ void probeintr(void); asm (" .text _probeintr: ss incl _npx_intrs_while_probing pushl %eax movb $0x20,%al /* EOI (asm in strings loses cpp features) */ outb %al,$0xa0 /* IO_ICU2 */ outb %al,$0x20 /* IO_ICU1 */ movb $0,%al outb %al,$0xf0 /* clear BUSY# latch */ popl %eax iret "); void probetrap(void); asm (" .text _probetrap: ss incl _npx_traps_while_probing fnclex iret "); /* * Probe routine. Initialize cr0 to give correct behaviour for [f]wait * whether the device exists or not (XXX should be elsewhere). Set flags * to tell npxattach() what to do. Modify device struct if npx doesn't * need to use interrupts. Return 1 if device exists. */ static int npxprobe(dvp) struct isa_device *dvp; { int result; u_long save_eflags; u_char save_icu1_mask; u_char save_icu2_mask; struct gate_descriptor save_idt_npxintr; struct gate_descriptor save_idt_npxtrap; /* * This routine is now just a wrapper for npxprobe1(), to install * special npx interrupt and trap handlers, to enable npx interrupts * and to disable other interrupts. Someday isa_configure() will * install suitable handlers and run with interrupts enabled so we * won't need to do so much here. */ npx_intrno = NRSVIDT + ffs(dvp->id_irq) - 1; save_eflags = read_eflags(); disable_intr(); save_icu1_mask = inb(IO_ICU1 + 1); save_icu2_mask = inb(IO_ICU2 + 1); save_idt_npxintr = idt[npx_intrno]; save_idt_npxtrap = idt[16]; outb(IO_ICU1 + 1, ~(IRQ_SLAVE | dvp->id_irq)); outb(IO_ICU2 + 1, ~(dvp->id_irq >> 8)); setidt(16, probetrap, SDT_SYS386TGT, SEL_KPL); setidt(npx_intrno, probeintr, SDT_SYS386IGT, SEL_KPL); npx_idt_probeintr = idt[npx_intrno]; enable_intr(); result = npxprobe1(dvp); disable_intr(); outb(IO_ICU1 + 1, save_icu1_mask); outb(IO_ICU2 + 1, save_icu2_mask); idt[npx_intrno] = save_idt_npxintr; idt[16] = save_idt_npxtrap; write_eflags(save_eflags); return (result); } static int npxprobe1(dvp) struct isa_device *dvp; { int control; int status; #ifdef lint npxintr(); #endif /* * Partially reset the coprocessor, if any. Some BIOS's don't reset * it after a warm boot. */ outb(0xf1, 0); /* full reset on some systems, NOP on others */ outb(0xf0, 0); /* clear BUSY# latch */ /* * Prepare to trap all ESC (i.e., NPX) instructions and all WAIT * instructions. We must set the CR0_MP bit and use the CR0_TS * bit to control the trap, because setting the CR0_EM bit does * not cause WAIT instructions to trap. It's important to trap * WAIT instructions - otherwise the "wait" variants of no-wait * control instructions would degenerate to the "no-wait" variants * after FP context switches but work correctly otherwise. It's * particularly important to trap WAITs when there is no NPX - * otherwise the "wait" variants would always degenerate. * * Try setting CR0_NE to get correct error reporting on 486DX's. * Setting it should fail or do nothing on lesser processors. */ load_cr0(rcr0() | CR0_MP | CR0_NE); /* * But don't trap while we're probing. */ stop_emulating(); /* * Finish resetting the coprocessor, if any. If there is an error * pending, then we may get a bogus IRQ13, but probeintr() will handle * it OK. Bogus halts have never been observed, but we enabled * IRQ13 and cleared the BUSY# latch early to handle them anyway. */ fninit(); DELAY(1000); /* wait for any IRQ13 (fwait might hang) */ #ifdef DIAGNOSTIC if (npx_intrs_while_probing != 0) printf("fninit caused %u bogus npx interrupt(s)\n", npx_intrs_while_probing); if (npx_traps_while_probing != 0) printf("fninit caused %u bogus npx trap(s)\n", npx_traps_while_probing); #endif /* * Check for a status of mostly zero. */ status = 0x5a5a; fnstsw(&status); if ((status & 0xb8ff) == 0) { /* * Good, now check for a proper control word. */ control = 0x5a5a; fnstcw(&control); if ((control & 0x1f3f) == 0x033f) { npx_exists = 1; /* * We have an npx, now divide by 0 to see if exception * 16 works. */ control &= ~(1 << 2); /* enable divide by 0 trap */ fldcw(&control); npx_traps_while_probing = npx_intrs_while_probing = 0; fp_divide_by_0(); if (npx_traps_while_probing != 0) { /* * Good, exception 16 works. */ npx_ex16 = 1; dvp->id_irq = 0; /* zap the interrupt */ /* * special return value to flag that we do not * actually use any I/O registers */ return (-1); } if (npx_intrs_while_probing != 0) { /* * Bad, we are stuck with IRQ13. */ npx_irq13 = 1; npx0mask = dvp->id_irq; /* npxattach too late */ return (IO_NPXSIZE); } /* * Worse, even IRQ13 is broken. Use emulator. */ } } /* * Probe failed, but we want to get to npxattach to initialize the * emulator and say that it has been installed. XXX handle devices * that aren't really devices better. */ dvp->id_irq = 0; - return (IO_NPXSIZE); + /* + * special return value to flag that we do not + * actually use any I/O registers + */ + return (-1); } /* * Attach routine - announce which it is, and wire into system */ int npxattach(dvp) struct isa_device *dvp; { - if (npx_ex16) - printf("npx%d: Errors reported via Exception 16\n",dvp->id_unit); - else if (npx_irq13) - printf("npx%d: Errors reported via IRQ 13\n",dvp->id_unit); - else if (npx_exists) - printf("npx%d: Error reporting broken, using 387 emulator\n",dvp->id_unit); - else - printf("npx%d: 387 Emulator\n",dvp->id_unit); + if (!npx_ex16 && !npx_irq13) { + if (npx_exists) + printf("npx%d: Error reporting broken, using 387 emulator\n",dvp->id_unit); + else + printf("npx%d: 387 Emulator\n",dvp->id_unit); + } npxinit(__INITIAL_NPXCW__); return (1); /* XXX unused */ } /* * Initialize floating point unit. */ void npxinit(control) u_int control; { struct save87 dummy; if (!npx_exists) return; /* * fninit has the same h/w bugs as fnsave. Use the detoxified * fnsave to throw away any junk in the fpu. fnsave initializes * the fpu and sets npxproc = NULL as important side effects. */ npxsave(&dummy); stop_emulating(); fldcw(&control); if (curpcb != NULL) fnsave(&curpcb->pcb_savefpu); start_emulating(); } /* * Free coprocessor (if we have it). */ void npxexit(p) struct proc *p; { if (p == npxproc) { start_emulating(); npxproc = NULL; } } /* * Record the FPU state and reinitialize it all except for the control word. * Then generate a SIGFPE. * * Reinitializing the state allows naive SIGFPE handlers to longjmp without * doing any fixups. * * XXX there is currently no way to pass the full error state to signal * handlers, and if this is a nested interrupt there is no way to pass even * a status code! So there is no way to have a non-naive SIGFPE handler. At * best a handler could do an fninit followed by an fldcw of a static value. * fnclex would be of little use because it would leave junk on the FPU stack. * Returning from the handler would be even less safe than usual because * IRQ13 exception handling makes exceptions even less precise than usual. */ void npxintr(frame) struct intrframe frame; { int code; if (npxproc == NULL || !npx_exists) { /* XXX no %p in stand/printf.c. Cast to quiet gcc -Wall. */ printf("npxintr: npxproc = %lx, curproc = %lx, npx_exists = %d\n", (u_long) npxproc, (u_long) curproc, npx_exists); panic("npxintr from nowhere"); } if (npxproc != curproc) { printf("npxintr: npxproc = %lx, curproc = %lx, npx_exists = %d\n", (u_long) npxproc, (u_long) curproc, npx_exists); panic("npxintr from non-current process"); } /* * Save state. This does an implied fninit. It had better not halt * the cpu or we'll hang. */ outb(0xf0, 0); fnsave(&curpcb->pcb_savefpu); fwait(); /* * Restore control word (was clobbered by fnsave). */ fldcw(&curpcb->pcb_savefpu.sv_env.en_cw); fwait(); /* * Remember the exception status word and tag word. The current * (almost fninit'ed) fpu state is in the fpu and the exception * state just saved will soon be junk. However, the implied fninit * doesn't change the error pointers or register contents, and we * preserved the control word and will copy the status and tag * words, so the complete exception state can be recovered. */ curpcb->pcb_savefpu.sv_ex_sw = curpcb->pcb_savefpu.sv_env.en_sw; curpcb->pcb_savefpu.sv_ex_tw = curpcb->pcb_savefpu.sv_env.en_tw; /* * Pass exception to process. */ if (ISPL(frame.if_cs) == SEL_UPL) { /* * Interrupt is essentially a trap, so we can afford to call * the SIGFPE handler (if any) as soon as the interrupt * returns. * * XXX little or nothing is gained from this, and plenty is * lost - the interrupt frame has to contain the trap frame * (this is otherwise only necessary for the rescheduling trap * in doreti, and the frame for that could easily be set up * just before it is used). */ curproc->p_regs = (int *)&frame.if_es; curpcb->pcb_flags |= FM_TRAP; /* used by sendsig */ #ifdef notyet /* * Encode the appropriate code for detailed information on * this exception. */ code = XXX_ENCODE(curpcb->pcb_savefpu.sv_ex_sw); #else code = 0; /* XXX */ #endif trapsignal(curproc, SIGFPE, code); curpcb->pcb_flags &= ~FM_TRAP; } else { /* * Nested interrupt. These losers occur when: * o an IRQ13 is bogusly generated at a bogus time, e.g.: * o immediately after an fnsave or frstor of an * error state. * o a couple of 386 instructions after * "fstpl _memvar" causes a stack overflow. * These are especially nasty when combined with a * trace trap. * o an IRQ13 occurs at the same time as another higher- * priority interrupt. * * Treat them like a true async interrupt. */ psignal(npxproc, SIGFPE); } } /* * Implement device not available (DNA) exception * * It would be better to switch FP context here (only). This would require * saving the state in the proc table instead of in the pcb. */ int npxdna() { if (!npx_exists) return (0); if (npxproc != NULL) { printf("npxdna: npxproc = %lx, curproc = %lx\n", (u_long) npxproc, (u_long) curproc); panic("npxdna"); } stop_emulating(); /* * Record new context early in case frstor causes an IRQ13. */ npxproc = curproc; /* * The following frstor may cause an IRQ13 when the state being * restored has a pending error. The error will appear to have been * triggered by the current (npx) user instruction even when that * instruction is a no-wait instruction that should not trigger an * error (e.g., fnclex). On at least one 486 system all of the * no-wait instructions are broken the same as frstor, so our * treatment does not amplify the breakage. On at least one * 386/Cyrix 387 system, fnclex works correctly while frstor and * fnsave are broken, so our treatment breaks fnclex if it is the * first FPU instruction after a context switch. */ frstor(&curpcb->pcb_savefpu); return (1); } /* * Wrapper for fnsave instruction to handle h/w bugs. If there is an error * pending, then fnsave generates a bogus IRQ13 on some systems. Force * any IRQ13 to be handled immediately, and then ignore it. This routine is * often called at splhigh so it must not use many system services. In * particular, it's much easier to install a special handler than to * guarantee that it's safe to use npxintr() and its supporting code. */ void npxsave(addr) struct save87 *addr; { u_char icu1_mask; u_char icu2_mask; u_char old_icu1_mask; u_char old_icu2_mask; struct gate_descriptor save_idt_npxintr; disable_intr(); old_icu1_mask = inb(IO_ICU1 + 1); old_icu2_mask = inb(IO_ICU2 + 1); save_idt_npxintr = idt[npx_intrno]; outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0mask)); outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0mask >> 8)); idt[npx_intrno] = npx_idt_probeintr; enable_intr(); stop_emulating(); fnsave(addr); fwait(); start_emulating(); npxproc = NULL; disable_intr(); icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */ icu2_mask = inb(IO_ICU2 + 1); outb(IO_ICU1 + 1, (icu1_mask & ~npx0mask) | (old_icu1_mask & npx0mask)); outb(IO_ICU2 + 1, (icu2_mask & ~(npx0mask >> 8)) | (old_icu2_mask & (npx0mask >> 8))); idt[npx_intrno] = save_idt_npxintr; enable_intr(); /* back to usual state */ } #endif /* NNPX > 0 */ Index: head/sys/i386/isa/isa.c =================================================================== --- head/sys/i386/isa/isa.c (revision 592) +++ head/sys/i386/isa/isa.c (revision 593) @@ -1,760 +1,791 @@ /*- * Copyright (c) 1991 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. * - * @(#)isa.c 7.2 (Berkeley) 5/13/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 4 00163 - * -------------------- ----- ---------------------- - * - * 18 Aug 92 Frank Maclachlan *See comments below - * 25 Mar 93 Rodney W. Grimes Added counter for stray interrupt, - * turned on logging of stray interrupts, - * Now prints maddr, msize, and flags - * after finding a device. - * 26 Apr 93 Bruce Evans New intr-0.1 code - * Rodney W. Grimes Only print io address if id_alive != -1 - * 17 May 93 Rodney W. Grimes renamed stray interrupt counters to - * work with new intr-0.1 code. - * Enabled printf for interrupt masks to - * aid in bug reports. - * 27 May 93 Guido van Rooij New routine add find_isa_dev + * from: @(#)isa.c 7.2 (Berkeley) 5/13/91 + * $Id$ */ -static char rcsid[] = "$Header: /a/cvs/386BSD/src/sys/i386/isa/isa.c,v 1.2 1993/06/18 22:18:57 rgrimes Exp $"; /* * code to manage AT bus * * 92/08/18 Frank P. MacLachlan (fpm@crash.cts.com): * Fixed uninitialized variable problem and added code to deal * with DMA page boundaries in isa_dmarangecheck(). Fixed word * mode DMA count compution and reorganized DMA setup code in * isa_dmastart() */ #include "param.h" #include "systm.h" #include "conf.h" #include "file.h" #include "buf.h" #include "uio.h" #include "syslog.h" #include "malloc.h" #include "rlist.h" #include "machine/segments.h" #include "vm/vm.h" #include "i386/isa/isa_device.h" #include "i386/isa/isa.h" #include "i386/isa/icu.h" #include "i386/isa/ic/i8237.h" #include "i386/isa/ic/i8042.h" /* ** Register definitions for DMA controller 1 (channels 0..3): */ #define DMA1_CHN(c) (IO_DMA1 + 1*(2*(c))) /* addr reg for channel c */ #define DMA1_SMSK (IO_DMA1 + 1*10) /* single mask register */ #define DMA1_MODE (IO_DMA1 + 1*11) /* mode register */ #define DMA1_FFC (IO_DMA1 + 1*12) /* clear first/last FF */ /* ** Register definitions for DMA controller 2 (channels 4..7): */ #define DMA2_CHN(c) (IO_DMA1 + 2*(2*(c))) /* addr reg for channel c */ #define DMA2_SMSK (IO_DMA2 + 2*10) /* single mask register */ #define DMA2_MODE (IO_DMA2 + 2*11) /* mode register */ #define DMA2_FFC (IO_DMA2 + 2*12) /* clear first/last FF */ int config_isadev __P((struct isa_device *, u_int *)); -#ifdef notyet -struct rlist *isa_iomem; - /* - * Configure all ISA devices + * print a conflict message */ -isa_configure() { - struct isa_device *dvp; - struct isa_driver *dp; - - splhigh(); - INTREN(IRQ_SLAVE); - /*rlist_free(&isa_iomem, 0xa0000, 0xfffff);*/ - for (dvp = isa_devtab_tty; dvp; dvp++) - (void) config_isadev(dvp, &ttymask); - for (dvp = isa_devtab_bio; dvp; dvp++) - (void) config_isadev(dvp, &biomask); - for (dvp = isa_devtab_net; dvp; dvp++) - (void) config_isadev(dvp, &netmask); - for (dvp = isa_devtab_null; dvp; dvp++) - (void) config_isadev(dvp, (u_int *) NULL); -#include "sl.h" -#if NSL > 0 - netmask |= ttymask; - ttymask |= netmask; -#endif -/* printf("biomask %x ttymask %x netmask %x\n", biomask, ttymask, netmask); */ - splnone(); +void +conflict(dvp, tmpdvp, item, reason, format) + struct isa_device *dvp, *tmpdvp; + int item; + char *reason; + char *format; +{ + printf("%s%d not probed due to %s conflict with %s%d at ", + dvp->id_driver->name, dvp->id_unit, reason, + tmpdvp->id_driver->name, tmpdvp->id_unit); + printf(format, item); + printf("\n"); } /* - * Configure an ISA device. + * Check to see if things are alread in use, like IRQ's, I/O addresses + * and Memory addresses. */ -config_isadev(isdp, mp) - struct isa_device *isdp; - u_int *mp; +int +haveseen(dvp, tmpdvp) + struct isa_device *dvp, *tmpdvp; { - struct isa_driver *dp; - static short drqseen, irqseen; - - if (dp = isdp->id_driver) { - /* if a device with i/o memory, convert to virtual address */ - if (isdp->id_maddr) { - extern unsigned int atdevbase; + int status = 0; - isdp->id_maddr -= IOM_BEGIN; - isdp->id_maddr += atdevbase; + /* + * Only check against devices that have already been found + */ + if (tmpdvp->id_alive) { + /* + * Check for I/O address conflict. We can only check the + * starting address of the device against the range of the + * device that has already been probed since we do not + * know how many I/O addresses this device uses. + */ + if (tmpdvp->id_alive != -1) { + if ((dvp->id_iobase >= tmpdvp->id_iobase) && + (dvp->id_iobase <= + (tmpdvp->id_iobase + tmpdvp->id_alive - 1))) { + conflict(dvp, tmpdvp, dvp->id_iobase, + "I/O address", "0x%x"); + status = 1; + } } - isdp->id_alive = (*dp->probe)(isdp); - if (isdp->id_alive) { - - printf("%s%d at port 0x%x ", dp->name, - isdp->id_unit, isdp->id_iobase); - - /* check for conflicts */ - if (irqseen & isdp->id_irq) { - printf("INTERRUPT CONFLICT - irq%d\n", - ffs(isdp->id_irq) - 1); - return (0); + /* + * Check for Memory address conflict. We can check for + * range overlap, but it will not catch all cases since the + * driver may adjust the msize paramater during probe, for + * now we just check that the starting address does not + * fall within any allocated region. + * XXX could add a second check after the probe for overlap, + * since at that time we would know the full range. + * XXX KERNBASE is a hack, we should have vaddr in the table! + */ + if(tmpdvp->id_maddr) { + if((KERNBASE + dvp->id_maddr >= tmpdvp->id_maddr) && + (KERNBASE + dvp->id_maddr <= + (tmpdvp->id_maddr + tmpdvp->id_msize - 1))) { + conflict(dvp, tmpdvp, dvp->id_maddr, "maddr", + "0x%x"); + status = 1; } - if (isdp->id_drq != -1 - && (drqseen & (1<id_drq))) { - printf("DMA CONFLICT - drq%d\n", isdp->id_drq); - return (0); + } + /* + * Check for IRQ conflicts. + */ + if(tmpdvp->id_irq) { + if (tmpdvp->id_irq == dvp->id_irq) { + conflict(dvp, tmpdvp, ffs(dvp->id_irq) - 1, + "irq", "%d"); + status = 1; } - /* NEED TO CHECK IOMEM CONFLICT HERE */ - - /* allocate and wire in device */ - if(isdp->id_irq) { - int intrno; - - intrno = ffs(isdp->id_irq)-1; - printf("irq %d ", intrno); - INTREN(isdp->id_irq); - if(mp)INTRMASK(*mp,isdp->id_irq); - setidt(NRSVIDT + intrno, isdp->id_intr, - SDT_SYS386IGT, SEL_KPL); - irqseen |= isdp->id_irq; + } + /* + * Check for DRQ conflicts. + */ + if(tmpdvp->id_drq != -1) { + if (tmpdvp->id_drq == dvp->id_drq) { + conflict(dvp, tmpdvp, dvp->id_drq, + "drq", "%d"); + status = 1; } - if (isdp->id_drq != -1) { - printf("drq %d ", isdp->id_drq); - drqseen |= 1 << isdp->id_drq; - } + } + } + return (status); +} - (*dp->attach)(isdp); +/* + * Search through all the isa_devtab_* tables looking for anything that + * conflicts with the current device. + */ +int +haveseen_isadev(dvp) + struct isa_device *dvp; +{ + struct isa_device *tmpdvp; + int status = 0; - printf("on isa\n"); - } - return (1); - } else return(0); + for (tmpdvp = isa_devtab_tty; tmpdvp->id_driver; tmpdvp++) { + status |= haveseen(dvp, tmpdvp); + } + for (tmpdvp = isa_devtab_bio; tmpdvp->id_driver; tmpdvp++) { + status |= haveseen(dvp, tmpdvp); + } + for (tmpdvp = isa_devtab_net; tmpdvp->id_driver; tmpdvp++) { + status |= haveseen(dvp, tmpdvp); + } + for (tmpdvp = isa_devtab_null; tmpdvp->id_driver; tmpdvp++) { + status |= haveseen(dvp, tmpdvp); + } + return(status); } -#else /* notyet */ + /* * Configure all ISA devices */ +void isa_configure() { struct isa_device *dvp; - struct isa_driver *dp; enable_intr(); splhigh(); INTREN(IRQ_SLAVE); - for (dvp = isa_devtab_tty; config_isadev(dvp,&ttymask); dvp++); - for (dvp = isa_devtab_bio; config_isadev(dvp,&biomask); dvp++); - for (dvp = isa_devtab_net; config_isadev(dvp,&netmask); dvp++); - for (dvp = isa_devtab_null; config_isadev(dvp,(u_int *) NULL); dvp++); + printf("Probing for devices on the ISA bus:\n"); + for (dvp = isa_devtab_tty; dvp->id_driver; dvp++) { + if (!haveseen_isadev(dvp)) + config_isadev(dvp,&ttymask); + } + for (dvp = isa_devtab_bio; dvp->id_driver; dvp++) { + if (!haveseen_isadev(dvp)) + config_isadev(dvp,&biomask); + } + for (dvp = isa_devtab_net; dvp->id_driver; dvp++) { + if (!haveseen_isadev(dvp)) + config_isadev(dvp,&netmask); + } + for (dvp = isa_devtab_null; dvp->id_driver; dvp++) { + if (!haveseen_isadev(dvp)) + config_isadev(dvp,(u_int *) NULL); + } +/* + * XXX We should really add the tty device to netmask when the line is + * switched to SLIPDISC, and then remove it when it is switched away from + * SLIPDISC. No need to block out ALL ttys during a splnet when only one + * of them is running slip. + */ #include "sl.h" #if NSL > 0 netmask |= ttymask; ttymask |= netmask; #endif /* biomask |= ttymask ; can some tty devices use buffers? */ printf("biomask %x ttymask %x netmask %x\n", biomask, ttymask, netmask); splnone(); } /* * Configure an ISA device. */ config_isadev(isdp, mp) struct isa_device *isdp; u_int *mp; { - struct isa_driver *dp; + struct isa_driver *dp = isdp->id_driver; - if (dp = isdp->id_driver) { - if (isdp->id_maddr) { - extern u_int atdevbase; + if (isdp->id_maddr) { + extern u_int atdevbase; - isdp->id_maddr -= 0xa0000; /* XXX should be a define */ - isdp->id_maddr += atdevbase; - } - isdp->id_alive = (*dp->probe)(isdp); - if (isdp->id_alive) { - printf("%s%d", dp->name, isdp->id_unit); - /* - * Only print the I/O address range if id_alive != -1 - * Right now this is a temporary fix just for the new - * NPX code so that if it finds a 486 that can use trap - * 16 it will not report I/O addresses. - * Rod Grimes 04/26/94 - */ - if (isdp->id_alive != -1) { - printf(" at 0x%x", isdp->id_iobase); - if ((isdp->id_iobase + isdp->id_alive - 1) != - isdp->id_iobase) - printf("-0x%x", - isdp->id_iobase + - isdp->id_alive - 1); + isdp->id_maddr -= 0xa0000; /* XXX should be a define */ + isdp->id_maddr += atdevbase; + } + isdp->id_alive = (*dp->probe)(isdp); + if (isdp->id_alive) { + /* + * Only print the I/O address range if id_alive != -1 + * Right now this is a temporary fix just for the new + * NPX code so that if it finds a 486 that can use trap + * 16 it will not report I/O addresses. + * Rod Grimes 04/26/94 + */ + printf("%s%d", dp->name, isdp->id_unit); + if (isdp->id_alive != -1) { + printf(" at 0x%x", isdp->id_iobase); + if ((isdp->id_iobase + isdp->id_alive - 1) != + isdp->id_iobase) { + printf("-0x%x", + isdp->id_iobase + + isdp->id_alive - 1); } - if(isdp->id_irq) - printf(" irq %d", ffs(isdp->id_irq)-1); - if (isdp->id_drq != -1) - printf(" drq %d", isdp->id_drq); - if (isdp->id_maddr != 0) - printf(" maddr 0x%x", kvtop(isdp->id_maddr)); - if (isdp->id_msize != 0) - printf(" msize %d", isdp->id_msize); - if (isdp->id_flags != 0) - printf(" flags 0x%x", isdp->id_flags); + } + if(isdp->id_irq) + printf(" irq %d", ffs(isdp->id_irq) - 1); + if (isdp->id_drq != -1) + printf(" drq %d", isdp->id_drq); + if (isdp->id_maddr) + printf(" maddr 0x%x", kvtop(isdp->id_maddr)); + if (isdp->id_msize) + printf(" msize %d", isdp->id_msize); + if (isdp->id_flags) + printf(" flags 0x%x", isdp->id_flags); + if (isdp->id_iobase < 0x100) + printf(" on motherboard\n"); + else printf(" on isa\n"); - (*dp->attach)(isdp); + (*dp->attach)(isdp); - if(isdp->id_irq) { - int intrno; + if(isdp->id_irq) { + int intrno; - intrno = ffs(isdp->id_irq)-1; - setidt(ICU_OFFSET+intrno, isdp->id_intr, - SDT_SYS386IGT, SEL_KPL); - if(mp) - INTRMASK(*mp,isdp->id_irq); - INTREN(isdp->id_irq); + intrno = ffs(isdp->id_irq)-1; + setidt(ICU_OFFSET+intrno, isdp->id_intr, + SDT_SYS386IGT, SEL_KPL); + if(mp) { + INTRMASK(*mp,isdp->id_irq); } + INTREN(isdp->id_irq); } - return (1); - } else return(0); + } else { + printf("%s%d not found", dp->name, isdp->id_unit); + if (isdp->id_iobase) { + printf(" at 0x%x", isdp->id_iobase); + } + printf("\n"); + } } -#endif /* (!) notyet */ #define IDTVEC(name) __CONCAT(X,name) /* default interrupt vector table entries */ extern IDTVEC(intr0), IDTVEC(intr1), IDTVEC(intr2), IDTVEC(intr3), IDTVEC(intr4), IDTVEC(intr5), IDTVEC(intr6), IDTVEC(intr7), IDTVEC(intr8), IDTVEC(intr9), IDTVEC(intr10), IDTVEC(intr11), IDTVEC(intr12), IDTVEC(intr13), IDTVEC(intr14), IDTVEC(intr15); static *defvec[16] = { &IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3), &IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7), &IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11), &IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15) }; /* out of range default interrupt vector gate entry */ extern IDTVEC(intrdefault); /* * Fill in default interrupt table (in case of spuruious interrupt * during configuration of kernel, setup interrupt control unit */ isa_defaultirq() { int i; /* icu vectors */ for (i = NRSVIDT ; i < NRSVIDT+ICU_LEN ; i++) setidt(i, defvec[i], SDT_SYS386IGT, SEL_KPL); /* out of range vectors */ for (i = NRSVIDT; i < NIDT; i++) setidt(i, &IDTVEC(intrdefault), SDT_SYS386IGT, SEL_KPL); /* initialize 8259's */ outb(IO_ICU1, 0x11); /* reset; program device, four bytes */ outb(IO_ICU1+1, NRSVIDT); /* starting at this vector index */ outb(IO_ICU1+1, 1<<2); /* slave on line 2 */ #ifdef AUTO_EOI_1 outb(IO_ICU1+1, 2 | 1); /* auto EOI, 8086 mode */ #else outb(IO_ICU1+1, 1); /* 8086 mode */ #endif outb(IO_ICU1+1, 0xff); /* leave interrupts masked */ outb(IO_ICU1, 0x0a); /* default to IRR on read */ outb(IO_ICU1, 0xc0 | (3 - 1)); /* pri order 3-7, 0-2 (com2 first) */ outb(IO_ICU2, 0x11); /* reset; program device, four bytes */ outb(IO_ICU2+1, NRSVIDT+8); /* staring at this vector index */ outb(IO_ICU2+1,2); /* my slave id is 2 */ #ifdef AUTO_EOI_2 outb(IO_ICU2+1, 2 | 1); /* auto EOI, 8086 mode */ #else outb(IO_ICU2+1,1); /* 8086 mode */ #endif outb(IO_ICU2+1, 0xff); /* leave interrupts masked */ outb(IO_ICU2, 0x0a); /* default to IRR on read */ } /* region of physical memory known to be contiguous */ vm_offset_t isaphysmem; static caddr_t dma_bounce[8]; /* XXX */ static char bounced[8]; /* XXX */ #define MAXDMASZ 512 /* XXX */ /* high byte of address is stored in this port for i-th dma channel */ static short dmapageport[8] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a }; /* * isa_dmacascade(): program 8237 DMA controller channel to accept * external dma control by a board. */ void isa_dmacascade(unsigned chan) { if (chan > 7) panic("isa_dmacascade: impossible request"); /* set dma channel mode, and set dma channel mode */ if ((chan & 4) == 0) { outb(DMA1_MODE, DMA37MD_CASCADE | chan); outb(DMA1_SMSK, chan); } else { outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3)); outb(DMA2_SMSK, chan & 3); } } /* * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment * problems by using a bounce buffer. */ void isa_dmastart(int flags, caddr_t addr, unsigned nbytes, unsigned chan) { vm_offset_t phys; int waport; caddr_t newaddr; if ( chan > 7 || (chan < 4 && nbytes > (1<<16)) || (chan >= 4 && (nbytes > (1<<17) || (u_int)addr & 1))) panic("isa_dmastart: impossible request"); if (isa_dmarangecheck(addr, nbytes, chan)) { if (dma_bounce[chan] == 0) dma_bounce[chan] = /*(caddr_t)malloc(MAXDMASZ, M_TEMP, M_WAITOK);*/ (caddr_t) isaphysmem + NBPG*chan; bounced[chan] = 1; newaddr = dma_bounce[chan]; *(int *) newaddr = 0; /* XXX */ /* copy bounce buffer on write */ if (!(flags & B_READ)) bcopy(addr, newaddr, nbytes); addr = newaddr; } /* translate to physical */ phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr); if ((chan & 4) == 0) { /* * Program one of DMA channels 0..3. These are * byte mode channels. */ /* set dma channel mode, and reset address ff */ if (flags & B_READ) outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|chan); else outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_READ|chan); outb(DMA1_FFC, 0); /* send start address */ waport = DMA1_CHN(chan); outb(waport, phys); outb(waport, phys>>8); outb(dmapageport[chan], phys>>16); /* send count */ outb(waport + 1, --nbytes); outb(waport + 1, nbytes>>8); /* unmask channel */ outb(DMA1_SMSK, chan); } else { /* * Program one of DMA channels 4..7. These are * word mode channels. */ /* set dma channel mode, and reset address ff */ if (flags & B_READ) outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3)); else outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_READ|(chan&3)); outb(DMA2_FFC, 0); /* send start address */ waport = DMA2_CHN(chan - 4); outb(waport, phys>>1); outb(waport, phys>>9); outb(dmapageport[chan], phys>>16); /* send count */ nbytes >>= 1; outb(waport + 2, --nbytes); outb(waport + 2, nbytes>>8); /* unmask channel */ outb(DMA2_SMSK, chan & 3); } } void isa_dmadone(int flags, caddr_t addr, int nbytes, int chan) { /* copy bounce buffer on read */ /*if ((flags & (B_PHYS|B_READ)) == (B_PHYS|B_READ))*/ if (bounced[chan]) { bcopy(dma_bounce[chan], addr, nbytes); bounced[chan] = 0; } } /* * Check for problems with the address range of a DMA transfer * (non-contiguous physical pages, outside of bus address space, * crossing DMA page boundaries). * Return true if special handling needed. */ isa_dmarangecheck(caddr_t va, unsigned length, unsigned chan) { vm_offset_t phys, priorpage = 0, endva; u_int dma_pgmsk = (chan & 4) ? ~(128*1024-1) : ~(64*1024-1); endva = (vm_offset_t)round_page(va + length); for (; va < (caddr_t) endva ; va += NBPG) { phys = trunc_page(pmap_extract(pmap_kernel(), (vm_offset_t)va)); #define ISARAM_END RAM_END if (phys == 0) panic("isa_dmacheck: no physical page present"); if (phys > ISARAM_END) return (1); if (priorpage) { if (priorpage + NBPG != phys) return (1); /* check if crossing a DMA page boundary */ if (((u_int)priorpage ^ (u_int)phys) & dma_pgmsk) return (1); } priorpage = phys; } return (0); } /* head of queue waiting for physmem to become available */ struct buf isa_physmemq; /* blocked waiting for resource to become free for exclusive use */ static isaphysmemflag; /* if waited for and call requested when free (B_CALL) */ static void (*isaphysmemunblock)(); /* needs to be a list */ /* * Allocate contiguous physical memory for transfer, returning * a *virtual* address to region. May block waiting for resource. * (assumed to be called at splbio()) */ caddr_t isa_allocphysmem(caddr_t va, unsigned length, void (*func)()) { isaphysmemunblock = func; while (isaphysmemflag & B_BUSY) { isaphysmemflag |= B_WANTED; sleep(&isaphysmemflag, PRIBIO); } isaphysmemflag |= B_BUSY; return((caddr_t)isaphysmem); } /* * Free contiguous physical memory used for transfer. * (assumed to be called at splbio()) */ void isa_freephysmem(caddr_t va, unsigned length) { isaphysmemflag &= ~B_BUSY; if (isaphysmemflag & B_WANTED) { isaphysmemflag &= B_WANTED; wakeup(&isaphysmemflag); if (isaphysmemunblock) (*isaphysmemunblock)(); } } /* * Handle a NMI, possibly a machine check. * return true to panic system, false to ignore. */ isa_nmi(cd) { log(LOG_CRIT, "\nNMI port 61 %x, port 70 %x\n", inb(0x61), inb(0x70)); return(0); } /* * Caught a stray interrupt, notify */ isa_strayintr(d) { /* DON'T BOTHER FOR NOW! */ /* for some reason, we get bursts of intr #7, even if not enabled! */ /* * Well the reason you got bursts of intr #7 is because someone * raised an interrupt line and dropped it before the 8259 could * prioritize it. This is documented in the intel data book. This * means you have BAD hardware! I have changed this so that only * the first 5 get logged, then it quits logging them, and puts * out a special message. rgrimes 3/25/1993 */ extern u_long intrcnt_stray; intrcnt_stray++; if (intrcnt_stray <= 5) log(LOG_ERR,"ISA strayintr %x\n", d); if (intrcnt_stray == 5) log(LOG_CRIT,"Too many ISA strayintr not logging any more\n"); } /* * Wait "n" microseconds. * Relies on timer 1 counting down from (TIMER_FREQ / hz) at * (1 * TIMER_FREQ) Hz. * Note: timer had better have been programmed before this is first used! * (The standard programming causes the timer to generate a square wave and * the counter is decremented twice every cycle.) */ #define CF (1 * TIMER_FREQ) #define TIMER_FREQ 1193182 /* XXX - should be elsewhere */ extern int hz; /* XXX - should be elsewhere */ int DELAY(n) int n; { int counter_limit; int prev_tick; int tick; int ticks_left; int sec; int usec; #ifdef DELAYDEBUG int getit_calls = 1; int n1; static int state = 0; if (state == 0) { state = 1; for (n1 = 1; n1 <= 10000000; n1 *= 10) DELAY(n1); state = 2; } if (state == 1) printf("DELAY(%d)...", n); #endif /* * Read the counter first, so that the rest of the setup overhead is * counted. Guess the initial overhead is 20 usec (on most systems it * takes about 1.5 usec for each of the i/o's in getit(). The loop * takes about 6 usec on a 486/33 and 13 usec on a 386/20. The * multiplications and divisions to scale the count take a while). */ prev_tick = getit(0, 0); n -= 20; /* * Calculate (n * (CF / 1e6)) without using floating point and without * any avoidable overflows. */ sec = n / 1000000; usec = n - sec * 1000000; ticks_left = sec * CF + usec * (CF / 1000000) + usec * ((CF % 1000000) / 1000) / 1000 + usec * (CF % 1000) / 1000000; counter_limit = TIMER_FREQ / hz; while (ticks_left > 0) { tick = getit(0, 0); #ifdef DELAYDEBUG ++getit_calls; #endif if (tick > prev_tick) ticks_left -= prev_tick - (tick - counter_limit); else ticks_left -= prev_tick - tick; prev_tick = tick; } #ifdef DELAYDEBUG if (state == 1) printf(" %d calls to getit() at %d usec each\n", getit_calls, (n + 5) / getit_calls); #endif } getit(unit, timer) { int high; int low; /* * XXX - isa.h defines bogus timers. There's no such timer as * IO_TIMER_2 = 0x48. There's a timer in the CMOS RAM chip but * its interface is quite different. Neither timer is an 8252. * We actually only call this with unit = 0 and timer = 0. It * could be static... */ /* * Protect ourself against interrupts. * XXX - sysbeep() and sysbeepstop() need protection. */ disable_intr(); /* * Latch the count for 'timer' (cc00xxxx, c = counter, x = any). */ outb(IO_TIMER1 + 3, timer << 6); low = inb(IO_TIMER1 + timer); high = inb(IO_TIMER1 + timer); enable_intr(); return ((high << 8) | low); } static beeping; static sysbeepstop(f) { /* disable counter 2 */ outb(0x61, inb(0x61) & 0xFC); if (f) timeout(sysbeepstop, 0, f); else beeping = 0; } void sysbeep(int pitch, int period) { outb(0x61, inb(0x61) | 3); /* enable counter 2 */ /* * XXX - move timer stuff to clock.c. * Program counter 2: * ccaammmb, c counter, a = access, m = mode, b = BCD * 1011x110, 11 for aa = LSB then MSB, x11 for mmm = square wave. */ outb(0x43, 0xb6); /* set command for counter 2, 2 byte write */ outb(0x42, pitch); outb(0x42, (pitch>>8)); if (!beeping) { beeping = period; timeout(sysbeepstop, period/2, period); } } /* * Pass command to keyboard controller (8042) */ unsigned kbc_8042cmd(val) { while (inb(KBSTATP)&KBS_IBF); if (val) outb(KBCMDP, val); while (inb(KBSTATP)&KBS_IBF); return (inb(KBDATAP)); } /* * find an ISA device in a given isa_devtab_* table, given * the table to search, the expected id_driver entry, and the unit number. * * this function is defined in isa_device.h, and this location is debatable; * i put it there because it's useless w/o, and directly operates on * the other stuff in that file. * */ struct isa_device *find_isadev(table, driverp, unit) struct isa_device *table; struct isa_driver *driverp; int unit; { if (driverp == NULL) /* sanity check */ return NULL; while ((table->id_driver != driverp) || (table->id_unit != unit)) { if (table->id_driver == 0) return NULL; table++; } return table; } /* * Return nonzero if a (masked) irq is pending for a given device. */ int isa_irq_pending(dvp) struct isa_device *dvp; { unsigned id_irq; id_irq = (unsigned short) dvp->id_irq; /* XXX silly type in struct */ if (id_irq & 0xff) return (inb(IO_ICU1) & id_irq); return (inb(IO_ICU2) & (id_irq >> 8)); } Index: head/sys/i386/isa/npx.c =================================================================== --- head/sys/i386/isa/npx.c (revision 592) +++ head/sys/i386/isa/npx.c (revision 593) @@ -1,564 +1,554 @@ /*- * Copyright (c) 1990 William Jolitz. * Copyright (c) 1991 The Regents of the University of California. * 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. * 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. * - * @(#)npx.c 7.2 (Berkeley) 5/12/91 - * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00154 - * -------------------- ----- ---------------------- - * - * 20 Apr 93 Bruce Evans New npx-0.5 code - * 23 May 93 Rodney W. Grimes Return a special value of -1 from - * the probe code to keep isa_config from - * printing out the I/O address when we - * are using trap 16 handling. - * + * from: @(#)npx.c 7.2 (Berkeley) 5/12/91 + * $Id$ */ -static char rcsid[] = "$Header: /a/cvs/386BSD/src/sys.386bsd/i386/isa/npx.c,v 1.1.1.1 1993/06/12 14:58:00 rgrimes Exp $"; #include "npx.h" #if NNPX > 0 #include "param.h" #include "systm.h" #include "conf.h" #include "file.h" #include "proc.h" #include "machine/cpu.h" #include "machine/pcb.h" #include "machine/trap.h" #include "ioctl.h" #include "machine/specialreg.h" #include "i386/isa/icu.h" #include "i386/isa/isa_device.h" #include "i386/isa/isa.h" /* * 387 and 287 Numeric Coprocessor Extension (NPX) Driver. */ #ifdef __GNUC__ #define disable_intr() __asm("cli") #define enable_intr() __asm("sti") #define fldcw(addr) __asm("fldcw %0" : : "m" (*addr)) #define fnclex() __asm("fnclex") #define fninit() __asm("fninit") #define fnsave(addr) __asm("fnsave %0" : "=m" (*addr) : "0" (*addr)) #define fnstcw(addr) __asm("fnstcw %0" : "=m" (*addr) : "0" (*addr)) #define fnstsw(addr) __asm("fnstsw %0" : "=m" (*addr) : "0" (*addr)) #define fp_divide_by_0() __asm("fldz; fld1; fdiv %st,%st(1); fwait") #define frstor(addr) __asm("frstor %0" : : "m" (*addr)) #define fwait() __asm("fwait") #define read_eflags() ({u_long ef; \ __asm("pushf; popl %0" : "=a" (ef)); \ ef; }) #define start_emulating() __asm("smsw %%ax; orb %0,%%al; lmsw %%ax" \ : : "n" (CR0_TS) : "ax") #define stop_emulating() __asm("clts") #define write_eflags(ef) __asm("pushl %0; popf" : : "a" ((u_long) ef)) #else /* not __GNUC__ */ void disable_intr __P((void)); void enable_intr __P((void)); void fldcw __P((caddr_t addr)); void fnclex __P((void)); void fninit __P((void)); void fnsave __P((caddr_t addr)); void fnstcw __P((caddr_t addr)); void fnstsw __P((caddr_t addr)); void fp_divide_by_0 __P((void)); void frstor __P((caddr_t addr)); void fwait __P((void)); u_long read_eflags __P((void)); void start_emulating __P((void)); void stop_emulating __P((void)); void write_eflags __P((u_long ef)); #endif /* __GNUC__ */ typedef u_char bool_t; extern struct gate_descriptor idt[]; int npxdna __P((void)); void npxexit __P((struct proc *p)); void npxinit __P((u_int control)); void npxintr __P((struct intrframe frame)); void npxsave __P((struct save87 *addr)); static int npxattach __P((struct isa_device *dvp)); static int npxprobe __P((struct isa_device *dvp)); static int npxprobe1 __P((struct isa_device *dvp)); struct isa_driver npxdriver = { npxprobe, npxattach, "npx", }; u_int npx0mask; struct proc *npxproc; static bool_t npx_ex16; static bool_t npx_exists; static struct gate_descriptor npx_idt_probeintr; static int npx_intrno; static volatile u_int npx_intrs_while_probing; static bool_t npx_irq13; static volatile u_int npx_traps_while_probing; /* * Special interrupt handlers. Someday intr0-intr15 will be used to count * interrupts. We'll still need a special exception 16 handler. The busy * latch stuff in probintr() can be moved to npxprobe(). */ void probeintr(void); asm (" .text _probeintr: ss incl _npx_intrs_while_probing pushl %eax movb $0x20,%al /* EOI (asm in strings loses cpp features) */ outb %al,$0xa0 /* IO_ICU2 */ outb %al,$0x20 /* IO_ICU1 */ movb $0,%al outb %al,$0xf0 /* clear BUSY# latch */ popl %eax iret "); void probetrap(void); asm (" .text _probetrap: ss incl _npx_traps_while_probing fnclex iret "); /* * Probe routine. Initialize cr0 to give correct behaviour for [f]wait * whether the device exists or not (XXX should be elsewhere). Set flags * to tell npxattach() what to do. Modify device struct if npx doesn't * need to use interrupts. Return 1 if device exists. */ static int npxprobe(dvp) struct isa_device *dvp; { int result; u_long save_eflags; u_char save_icu1_mask; u_char save_icu2_mask; struct gate_descriptor save_idt_npxintr; struct gate_descriptor save_idt_npxtrap; /* * This routine is now just a wrapper for npxprobe1(), to install * special npx interrupt and trap handlers, to enable npx interrupts * and to disable other interrupts. Someday isa_configure() will * install suitable handlers and run with interrupts enabled so we * won't need to do so much here. */ npx_intrno = NRSVIDT + ffs(dvp->id_irq) - 1; save_eflags = read_eflags(); disable_intr(); save_icu1_mask = inb(IO_ICU1 + 1); save_icu2_mask = inb(IO_ICU2 + 1); save_idt_npxintr = idt[npx_intrno]; save_idt_npxtrap = idt[16]; outb(IO_ICU1 + 1, ~(IRQ_SLAVE | dvp->id_irq)); outb(IO_ICU2 + 1, ~(dvp->id_irq >> 8)); setidt(16, probetrap, SDT_SYS386TGT, SEL_KPL); setidt(npx_intrno, probeintr, SDT_SYS386IGT, SEL_KPL); npx_idt_probeintr = idt[npx_intrno]; enable_intr(); result = npxprobe1(dvp); disable_intr(); outb(IO_ICU1 + 1, save_icu1_mask); outb(IO_ICU2 + 1, save_icu2_mask); idt[npx_intrno] = save_idt_npxintr; idt[16] = save_idt_npxtrap; write_eflags(save_eflags); return (result); } static int npxprobe1(dvp) struct isa_device *dvp; { int control; int status; #ifdef lint npxintr(); #endif /* * Partially reset the coprocessor, if any. Some BIOS's don't reset * it after a warm boot. */ outb(0xf1, 0); /* full reset on some systems, NOP on others */ outb(0xf0, 0); /* clear BUSY# latch */ /* * Prepare to trap all ESC (i.e., NPX) instructions and all WAIT * instructions. We must set the CR0_MP bit and use the CR0_TS * bit to control the trap, because setting the CR0_EM bit does * not cause WAIT instructions to trap. It's important to trap * WAIT instructions - otherwise the "wait" variants of no-wait * control instructions would degenerate to the "no-wait" variants * after FP context switches but work correctly otherwise. It's * particularly important to trap WAITs when there is no NPX - * otherwise the "wait" variants would always degenerate. * * Try setting CR0_NE to get correct error reporting on 486DX's. * Setting it should fail or do nothing on lesser processors. */ load_cr0(rcr0() | CR0_MP | CR0_NE); /* * But don't trap while we're probing. */ stop_emulating(); /* * Finish resetting the coprocessor, if any. If there is an error * pending, then we may get a bogus IRQ13, but probeintr() will handle * it OK. Bogus halts have never been observed, but we enabled * IRQ13 and cleared the BUSY# latch early to handle them anyway. */ fninit(); DELAY(1000); /* wait for any IRQ13 (fwait might hang) */ #ifdef DIAGNOSTIC if (npx_intrs_while_probing != 0) printf("fninit caused %u bogus npx interrupt(s)\n", npx_intrs_while_probing); if (npx_traps_while_probing != 0) printf("fninit caused %u bogus npx trap(s)\n", npx_traps_while_probing); #endif /* * Check for a status of mostly zero. */ status = 0x5a5a; fnstsw(&status); if ((status & 0xb8ff) == 0) { /* * Good, now check for a proper control word. */ control = 0x5a5a; fnstcw(&control); if ((control & 0x1f3f) == 0x033f) { npx_exists = 1; /* * We have an npx, now divide by 0 to see if exception * 16 works. */ control &= ~(1 << 2); /* enable divide by 0 trap */ fldcw(&control); npx_traps_while_probing = npx_intrs_while_probing = 0; fp_divide_by_0(); if (npx_traps_while_probing != 0) { /* * Good, exception 16 works. */ npx_ex16 = 1; dvp->id_irq = 0; /* zap the interrupt */ /* * special return value to flag that we do not * actually use any I/O registers */ return (-1); } if (npx_intrs_while_probing != 0) { /* * Bad, we are stuck with IRQ13. */ npx_irq13 = 1; npx0mask = dvp->id_irq; /* npxattach too late */ return (IO_NPXSIZE); } /* * Worse, even IRQ13 is broken. Use emulator. */ } } /* * Probe failed, but we want to get to npxattach to initialize the * emulator and say that it has been installed. XXX handle devices * that aren't really devices better. */ dvp->id_irq = 0; - return (IO_NPXSIZE); + /* + * special return value to flag that we do not + * actually use any I/O registers + */ + return (-1); } /* * Attach routine - announce which it is, and wire into system */ int npxattach(dvp) struct isa_device *dvp; { - if (npx_ex16) - printf("npx%d: Errors reported via Exception 16\n",dvp->id_unit); - else if (npx_irq13) - printf("npx%d: Errors reported via IRQ 13\n",dvp->id_unit); - else if (npx_exists) - printf("npx%d: Error reporting broken, using 387 emulator\n",dvp->id_unit); - else - printf("npx%d: 387 Emulator\n",dvp->id_unit); + if (!npx_ex16 && !npx_irq13) { + if (npx_exists) + printf("npx%d: Error reporting broken, using 387 emulator\n",dvp->id_unit); + else + printf("npx%d: 387 Emulator\n",dvp->id_unit); + } npxinit(__INITIAL_NPXCW__); return (1); /* XXX unused */ } /* * Initialize floating point unit. */ void npxinit(control) u_int control; { struct save87 dummy; if (!npx_exists) return; /* * fninit has the same h/w bugs as fnsave. Use the detoxified * fnsave to throw away any junk in the fpu. fnsave initializes * the fpu and sets npxproc = NULL as important side effects. */ npxsave(&dummy); stop_emulating(); fldcw(&control); if (curpcb != NULL) fnsave(&curpcb->pcb_savefpu); start_emulating(); } /* * Free coprocessor (if we have it). */ void npxexit(p) struct proc *p; { if (p == npxproc) { start_emulating(); npxproc = NULL; } } /* * Record the FPU state and reinitialize it all except for the control word. * Then generate a SIGFPE. * * Reinitializing the state allows naive SIGFPE handlers to longjmp without * doing any fixups. * * XXX there is currently no way to pass the full error state to signal * handlers, and if this is a nested interrupt there is no way to pass even * a status code! So there is no way to have a non-naive SIGFPE handler. At * best a handler could do an fninit followed by an fldcw of a static value. * fnclex would be of little use because it would leave junk on the FPU stack. * Returning from the handler would be even less safe than usual because * IRQ13 exception handling makes exceptions even less precise than usual. */ void npxintr(frame) struct intrframe frame; { int code; if (npxproc == NULL || !npx_exists) { /* XXX no %p in stand/printf.c. Cast to quiet gcc -Wall. */ printf("npxintr: npxproc = %lx, curproc = %lx, npx_exists = %d\n", (u_long) npxproc, (u_long) curproc, npx_exists); panic("npxintr from nowhere"); } if (npxproc != curproc) { printf("npxintr: npxproc = %lx, curproc = %lx, npx_exists = %d\n", (u_long) npxproc, (u_long) curproc, npx_exists); panic("npxintr from non-current process"); } /* * Save state. This does an implied fninit. It had better not halt * the cpu or we'll hang. */ outb(0xf0, 0); fnsave(&curpcb->pcb_savefpu); fwait(); /* * Restore control word (was clobbered by fnsave). */ fldcw(&curpcb->pcb_savefpu.sv_env.en_cw); fwait(); /* * Remember the exception status word and tag word. The current * (almost fninit'ed) fpu state is in the fpu and the exception * state just saved will soon be junk. However, the implied fninit * doesn't change the error pointers or register contents, and we * preserved the control word and will copy the status and tag * words, so the complete exception state can be recovered. */ curpcb->pcb_savefpu.sv_ex_sw = curpcb->pcb_savefpu.sv_env.en_sw; curpcb->pcb_savefpu.sv_ex_tw = curpcb->pcb_savefpu.sv_env.en_tw; /* * Pass exception to process. */ if (ISPL(frame.if_cs) == SEL_UPL) { /* * Interrupt is essentially a trap, so we can afford to call * the SIGFPE handler (if any) as soon as the interrupt * returns. * * XXX little or nothing is gained from this, and plenty is * lost - the interrupt frame has to contain the trap frame * (this is otherwise only necessary for the rescheduling trap * in doreti, and the frame for that could easily be set up * just before it is used). */ curproc->p_regs = (int *)&frame.if_es; curpcb->pcb_flags |= FM_TRAP; /* used by sendsig */ #ifdef notyet /* * Encode the appropriate code for detailed information on * this exception. */ code = XXX_ENCODE(curpcb->pcb_savefpu.sv_ex_sw); #else code = 0; /* XXX */ #endif trapsignal(curproc, SIGFPE, code); curpcb->pcb_flags &= ~FM_TRAP; } else { /* * Nested interrupt. These losers occur when: * o an IRQ13 is bogusly generated at a bogus time, e.g.: * o immediately after an fnsave or frstor of an * error state. * o a couple of 386 instructions after * "fstpl _memvar" causes a stack overflow. * These are especially nasty when combined with a * trace trap. * o an IRQ13 occurs at the same time as another higher- * priority interrupt. * * Treat them like a true async interrupt. */ psignal(npxproc, SIGFPE); } } /* * Implement device not available (DNA) exception * * It would be better to switch FP context here (only). This would require * saving the state in the proc table instead of in the pcb. */ int npxdna() { if (!npx_exists) return (0); if (npxproc != NULL) { printf("npxdna: npxproc = %lx, curproc = %lx\n", (u_long) npxproc, (u_long) curproc); panic("npxdna"); } stop_emulating(); /* * Record new context early in case frstor causes an IRQ13. */ npxproc = curproc; /* * The following frstor may cause an IRQ13 when the state being * restored has a pending error. The error will appear to have been * triggered by the current (npx) user instruction even when that * instruction is a no-wait instruction that should not trigger an * error (e.g., fnclex). On at least one 486 system all of the * no-wait instructions are broken the same as frstor, so our * treatment does not amplify the breakage. On at least one * 386/Cyrix 387 system, fnclex works correctly while frstor and * fnsave are broken, so our treatment breaks fnclex if it is the * first FPU instruction after a context switch. */ frstor(&curpcb->pcb_savefpu); return (1); } /* * Wrapper for fnsave instruction to handle h/w bugs. If there is an error * pending, then fnsave generates a bogus IRQ13 on some systems. Force * any IRQ13 to be handled immediately, and then ignore it. This routine is * often called at splhigh so it must not use many system services. In * particular, it's much easier to install a special handler than to * guarantee that it's safe to use npxintr() and its supporting code. */ void npxsave(addr) struct save87 *addr; { u_char icu1_mask; u_char icu2_mask; u_char old_icu1_mask; u_char old_icu2_mask; struct gate_descriptor save_idt_npxintr; disable_intr(); old_icu1_mask = inb(IO_ICU1 + 1); old_icu2_mask = inb(IO_ICU2 + 1); save_idt_npxintr = idt[npx_intrno]; outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0mask)); outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0mask >> 8)); idt[npx_intrno] = npx_idt_probeintr; enable_intr(); stop_emulating(); fnsave(addr); fwait(); start_emulating(); npxproc = NULL; disable_intr(); icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */ icu2_mask = inb(IO_ICU2 + 1); outb(IO_ICU1 + 1, (icu1_mask & ~npx0mask) | (old_icu1_mask & npx0mask)); outb(IO_ICU2 + 1, (icu2_mask & ~(npx0mask >> 8)) | (old_icu2_mask & (npx0mask >> 8))); idt[npx_intrno] = save_idt_npxintr; enable_intr(); /* back to usual state */ } #endif /* NNPX > 0 */