Index: head/sys/amd64/amd64/autoconf.c =================================================================== --- head/sys/amd64/amd64/autoconf.c (revision 55116) +++ head/sys/amd64/amd64/autoconf.c (revision 55117) @@ -1,322 +1,326 @@ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)autoconf.c 7.1 (Berkeley) 5/9/91 * $FreeBSD$ */ /* * Setup the system to run on the current machine. * * Configure() is called at boot time and initializes the vba * device tables and the memory controller monitoring. Available * devices are determined (from possibilities mentioned in ioconf.c), * and the drivers are initialized. */ #include "opt_bootp.h" #include "opt_ffs.h" #include "opt_cd9660.h" #include "opt_nfs.h" #include "opt_nfsroot.h" #include "opt_bus.h" #include "opt_rootdevname.h" +#include "isa.h" + #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef APIC_IO #include #else #include #endif /* APIC_IO */ -#include "isa.h" +#if NISA > 0 #include + device_t isa_bus_device = 0; +#endif static void configure_first __P((void *)); static void configure __P((void *)); static void configure_final __P((void *)); #if defined(FFS) && defined(FFS_ROOT) static void setroot __P((void)); #endif SYSINIT(configure1, SI_SUB_CONFIGURE, SI_ORDER_FIRST, configure_first, NULL); /* SI_ORDER_SECOND is hookable */ SYSINIT(configure2, SI_SUB_CONFIGURE, SI_ORDER_THIRD, configure, NULL); /* SI_ORDER_MIDDLE is hookable */ SYSINIT(configure3, SI_SUB_CONFIGURE, SI_ORDER_ANY, configure_final, NULL); dev_t rootdev = NODEV; dev_t dumpdev = NODEV; device_t nexus_dev; /* * Determine i/o configuration for a machine. */ static void configure_first(dummy) void *dummy; { } static void configure(dummy) void *dummy; { /* * Activate the ICU's. Note that we are explicitly at splhigh() * at present as we have no way to disable stray PCI level triggered * interrupts until the devices have had a driver attached. This * is particularly a problem when the interrupts are shared. For * example, if IRQ 10 is shared between a disk and network device * and the disk device generates an interrupt, if we "activate" * IRQ 10 when the network driver is set up, then we will get * recursive interrupt 10's as nothing will know how to turn off * the disk device's interrupt. * * Having the ICU's active means we can probe interrupt routing to * see if a device causes the corresponding pending bit to be set. * * This is all rather inconvenient. */ #ifdef APIC_IO bsp_apic_configure(); enable_intr(); #else enable_intr(); INTREN(IRQ_SLAVE); #endif /* APIC_IO */ /* nexus0 is the top of the i386 device tree */ device_add_child(root_bus, "nexus", 0); /* initialize new bus architecture */ root_bus_configure(); #if NISA > 0 /* * Explicitly probe and attach ISA last. The isa bus saves * it's device node at attach time for us here. */ if (isa_bus_device) isa_probe_children(isa_bus_device); #endif /* * Now we're ready to handle (pending) interrupts. * XXX this is slightly misplaced. */ spl0(); /* * Allow lowering of the ipl to the lowest kernel level if we * panic (or call tsleep() before clearing `cold'). No level is * completely safe (since a panic may occur in a critical region * at splhigh()), but we want at least bio interrupts to work. */ safepri = cpl; } static void configure_final(dummy) void *dummy; { int i; cninit_finish(); if (bootverbose) { #ifdef APIC_IO imen_dump(); #endif /* APIC_IO */ /* * Print out the BIOS's idea of the disk geometries. */ printf("BIOS Geometries:\n"); for (i = 0; i < N_BIOS_GEOM; i++) { unsigned long bios_geom; int max_cylinder, max_head, max_sector; bios_geom = bootinfo.bi_bios_geom[i]; /* * XXX the bootstrap punts a 1200K floppy geometry * when the get-disk-geometry interrupt fails. Skip * drives that have this geometry. */ if (bios_geom == 0x4f010f) continue; printf(" %x:%08lx ", i, bios_geom); max_cylinder = bios_geom >> 16; max_head = (bios_geom >> 8) & 0xff; max_sector = bios_geom & 0xff; printf( "0..%d=%d cylinders, 0..%d=%d heads, 1..%d=%d sectors\n", max_cylinder, max_cylinder + 1, max_head, max_head + 1, max_sector, max_sector); } printf(" %d accounted for\n", bootinfo.bi_n_bios_used); printf("Device configuration finished.\n"); } cold = 0; } #ifdef BOOTP extern void bootpc_init(void); #endif /* * Do legacy root filesystem discovery. */ void cpu_rootconf() { #ifdef BOOTP bootpc_init(); #endif #if defined(NFS) && defined(NFS_ROOT) #if !defined(BOOTP_NFSROOT) if (nfs_diskless_valid) #endif rootdevnames[0] = "nfs:"; #endif #if defined(FFS) && defined(FFS_ROOT) if (!rootdevnames[0]) setroot(); #endif } SYSINIT(cpu_rootconf, SI_SUB_ROOT_CONF, SI_ORDER_FIRST, cpu_rootconf, NULL) u_long bootdev = 0; /* not a dev_t - encoding is different */ #if defined(FFS) && defined(FFS_ROOT) #define FDMAJOR 2 #define FDUNITSHIFT 6 /* * Attempt to find the device from which we were booted. * If we can do so, and not instructed not to do so, * set rootdevs[] and rootdevnames[] to correspond to the * boot device(s). * * This code survives in order to allow the system to be * booted from legacy environments that do not correctly * populate the kernel environment. There are significant * restrictions on the bootability of the system in this * situation; it can only be mounting root from a 'da' * 'wd' or 'fd' device, and the root filesystem must be ufs. */ static void setroot() { int majdev, mindev, unit, slice, part; dev_t newrootdev, dev; char partname[2]; char *sname; if ((bootdev & B_MAGICMASK) != B_DEVMAGIC) { printf("no B_DEVMAGIC (bootdev=%#lx)\n", bootdev); return; } majdev = B_TYPE(bootdev); dev = makebdev(majdev, 0); if (devsw(dev) == NULL) { printf("no devsw (majdev=%d bootdev=%#lx)\n", majdev, bootdev); return; } unit = B_UNIT(bootdev); slice = B_SLICE(bootdev); if (slice == WHOLE_DISK_SLICE) slice = COMPATIBILITY_SLICE; if (slice < 0 || slice >= MAX_SLICES) { printf("bad slice\n"); return; } /* * XXX kludge for inconsistent unit numbering and lack of slice * support for floppies. */ if (majdev == FDMAJOR) { slice = COMPATIBILITY_SLICE; part = RAW_PART; mindev = unit << FDUNITSHIFT; } else { part = B_PARTITION(bootdev); mindev = dkmakeminor(unit, slice, part); } newrootdev = makebdev(majdev, mindev); sname = dsname(newrootdev, unit, slice, part, partname); rootdevnames[0] = malloc(strlen(sname) + 6, M_DEVBUF, M_NOWAIT); sprintf(rootdevnames[0], "ufs:%s%s", sname, partname); /* * For properly dangerously dedicated disks (ones with a historical * bogus partition table), the boot blocks will give slice = 4, but * the kernel will only provide the compatibility slice since it * knows that slice 4 is not a real slice. Arrange to try mounting * the compatibility slice as root if mounting the slice passed by * the boot blocks fails. This handles the dangerously dedicated * case and perhaps others. */ if (slice == COMPATIBILITY_SLICE) return; slice = COMPATIBILITY_SLICE; sname = dsname(newrootdev, unit, slice, part, partname); rootdevnames[1] = malloc(strlen(sname) + 6, M_DEVBUF, M_NOWAIT); sprintf(rootdevnames[1], "ufs:%s%s", sname, partname); } #endif Index: head/sys/amd64/amd64/bios.c =================================================================== --- head/sys/amd64/amd64/bios.c (revision 55116) +++ head/sys/amd64/amd64/bios.c (revision 55117) @@ -1,623 +1,627 @@ /*- * Copyright (c) 1997 Michael Smith * Copyright (c) 1998 Jonathan Lemon * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Code for dealing with the BIOS in x86 PC systems. */ #include "opt_pnp.h" +#include "isa.h" + #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#if NISA > 0 #include +#endif #define BIOS_START 0xe0000 #define BIOS_SIZE 0x20000 /* exported lookup results */ struct bios32_SDentry PCIbios = {entry : 0}; struct PnPBIOS_table *PnPBIOStable = 0; static u_int bios32_SDCI = 0; /* start fairly early */ static void bios32_init(void *junk); SYSINIT(bios32, SI_SUB_CPU, SI_ORDER_ANY, bios32_init, NULL); /* * bios32_init * * Locate various bios32 entities. */ static void bios32_init(void *junk) { u_long sigaddr; struct bios32_SDheader *sdh; struct PnPBIOS_table *pt; u_int8_t ck, *cv; int i; /* * BIOS32 Service Directory */ /* look for the signature */ if ((sigaddr = bios_sigsearch(0, "_32_", 4, 16, 0)) != 0) { /* get a virtual pointer to the structure */ sdh = (struct bios32_SDheader *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr); for (cv = (u_int8_t *)sdh, ck = 0, i = 0; i < (sdh->len * 16); i++) { ck += cv[i]; } /* If checksum is OK, enable use of the entrypoint */ if ((ck == 0) && (sdh->entry < (BIOS_START + BIOS_SIZE))) { bios32_SDCI = BIOS_PADDRTOVADDR(sdh->entry); if (bootverbose) { printf("bios32: Found BIOS32 Service Directory header at %p\n", sdh); printf("bios32: Entry = 0x%x (%x) Rev = %d Len = %d\n", sdh->entry, bios32_SDCI, sdh->revision, sdh->len); } /* See if there's a PCI BIOS entrypoint here */ PCIbios.ident.id = 0x49435024; /* PCI systems should have this */ if (!bios32_SDlookup(&PCIbios) && bootverbose) printf("pcibios: PCI BIOS entry at 0x%x\n", PCIbios.entry); } else { printf("bios32: Bad BIOS32 Service Directory\n"); } } /* * PnP BIOS */ if ((sigaddr = bios_sigsearch(0, "$PnP", 4, 16, 0)) != 0) { /* get a virtual pointer to the structure */ pt = (struct PnPBIOS_table *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr); for (cv = (u_int8_t *)pt, ck = 0, i = 0; i < pt->len; i++) { ck += cv[i]; } /* If checksum is OK, enable use of the entrypoint */ if (ck == 0) { PnPBIOStable = pt; if (bootverbose) { printf("pnpbios: Found PnP BIOS data at %p\n", pt); printf("pnpbios: Entry = %x:%x Rev = %d.%d\n", pt->pmentrybase, pt->pmentryoffset, pt->version >> 4, pt->version & 0xf); if ((pt->control & 0x3) == 0x01) printf("pnpbios: Event flag at %x\n", pt->evflagaddr); if (pt->oemdevid != 0) printf("pnpbios: OEM ID %x\n", pt->oemdevid); } } else { printf("pnpbios: Bad PnP BIOS data checksum\n"); } } if (bootverbose) { /* look for other know signatures */ printf("Other BIOS signatures found:\n"); printf("ACPI: %08x\n", bios_sigsearch(0, "RSD PTR ", 8, 16, 0)); } } /* * bios32_SDlookup * * Query the BIOS32 Service Directory for the service named in (ent), * returns nonzero if the lookup fails. The caller must fill in * (ent->ident), the remainder are populated on a successful lookup. */ int bios32_SDlookup(struct bios32_SDentry *ent) { struct bios_regs args; if (bios32_SDCI == 0) return (1); args.eax = ent->ident.id; /* set up arguments */ args.ebx = args.ecx = args.edx = 0; bios32(&args, bios32_SDCI, GSEL(GCODE_SEL, SEL_KPL)); if ((args.eax & 0xff) == 0) { /* success? */ ent->base = args.ebx; ent->len = args.ecx; ent->entry = args.edx; return (0); /* all OK */ } return (1); /* failed */ } /* * bios_sigsearch * * Search some or all of the BIOS region for a signature string. * * (start) Optional offset returned from this function * (for searching for multiple matches), or NULL * to start the search from the base of the BIOS. * Note that this will be a _physical_ address in * the range 0xe0000 - 0xfffff. * (sig) is a pointer to the byte(s) of the signature. * (siglen) number of bytes in the signature. * (paralen) signature paragraph (alignment) size. * (sigofs) offset of the signature within the paragraph. * * Returns the _physical_ address of the found signature, 0 if the * signature was not found. */ u_int32_t bios_sigsearch(u_int32_t start, u_char *sig, int siglen, int paralen, int sigofs) { u_char *sp, *end; /* compute the starting address */ if ((start >= BIOS_START) && (start <= (BIOS_START + BIOS_SIZE))) { sp = (char *)BIOS_PADDRTOVADDR(start); } else if (start == 0) { sp = (char *)BIOS_PADDRTOVADDR(BIOS_START); } else { return 0; /* bogus start address */ } /* compute the end address */ end = (u_char *)BIOS_PADDRTOVADDR(BIOS_START + BIOS_SIZE); /* loop searching */ while ((sp + sigofs + siglen) < end) { /* compare here */ if (!bcmp(sp + sigofs, sig, siglen)) { /* convert back to physical address */ return((u_int32_t)BIOS_VADDRTOPADDR(sp)); } sp += paralen; } return(0); } /* * do not staticize, used by bioscall.s */ union { struct { u_short offset; u_short segment; } vec16; struct { u_int offset; u_short segment; } vec32; } bioscall_vector; /* bios jump vector */ void set_bios_selectors(struct bios_segments *seg, int flags) { struct soft_segment_descriptor ssd = { 0, /* segment base address (overwritten) */ 0, /* length (overwritten) */ SDT_MEMERA, /* segment type (overwritten) */ 0, /* priority level */ 1, /* descriptor present */ 0, 0, 1, /* descriptor size (overwritten) */ 0 /* granularity == byte units */ }; union descriptor *p_gdt; #ifdef SMP p_gdt = &gdt[cpuid]; #else p_gdt = gdt; #endif ssd.ssd_base = seg->code32.base; ssd.ssd_limit = seg->code32.limit; ssdtosd(&ssd, &p_gdt[GBIOSCODE32_SEL].sd); ssd.ssd_def32 = 0; if (flags & BIOSCODE_FLAG) { ssd.ssd_base = seg->code16.base; ssd.ssd_limit = seg->code16.limit; ssdtosd(&ssd, &p_gdt[GBIOSCODE16_SEL].sd); } ssd.ssd_type = SDT_MEMRWA; if (flags & BIOSDATA_FLAG) { ssd.ssd_base = seg->data.base; ssd.ssd_limit = seg->data.limit; ssdtosd(&ssd, &p_gdt[GBIOSDATA_SEL].sd); } if (flags & BIOSUTIL_FLAG) { ssd.ssd_base = seg->util.base; ssd.ssd_limit = seg->util.limit; ssdtosd(&ssd, &p_gdt[GBIOSUTIL_SEL].sd); } if (flags & BIOSARGS_FLAG) { ssd.ssd_base = seg->args.base; ssd.ssd_limit = seg->args.limit; ssdtosd(&ssd, &p_gdt[GBIOSARGS_SEL].sd); } } extern int vm86pa; extern void bios16_jmp(void); /* * this routine is really greedy with selectors, and uses 5: * * 32-bit code selector: to return to kernel * 16-bit code selector: for running code * data selector: for 16-bit data * util selector: extra utility selector * args selector: to handle pointers * * the util selector is set from the util16 entry in bios16_args, if a * "U" specifier is seen. * * See for description of format specifiers */ int bios16(struct bios_args *args, char *fmt, ...) { char *p, *stack, *stack_top; va_list ap; int flags = BIOSCODE_FLAG | BIOSDATA_FLAG; u_int i, arg_start, arg_end; u_int *pte, *ptd; arg_start = 0xffffffff; arg_end = 0; /* * Some BIOS entrypoints attempt to copy the largest-case * argument frame (in order to generalise handling for * different entry types). If our argument frame is * smaller than this, the BIOS will reach off the top of * our constructed stack segment. Pad the top of the stack * with some garbage to avoid this. */ stack = (caddr_t)PAGE_SIZE - 32; va_start(ap, fmt); for (p = fmt; p && *p; p++) { switch (*p) { case 'p': /* 32-bit pointer */ i = va_arg(ap, u_int); arg_start = min(arg_start, i); arg_end = max(arg_end, i); flags |= BIOSARGS_FLAG; stack -= 4; break; case 'i': /* 32-bit integer */ i = va_arg(ap, u_int); stack -= 4; break; case 'U': /* 16-bit selector */ flags |= BIOSUTIL_FLAG; /* FALLTHROUGH */ case 'D': /* 16-bit selector */ case 'C': /* 16-bit selector */ stack -= 2; break; case 's': /* 16-bit integer */ i = va_arg(ap, u_short); stack -= 2; break; default: return (EINVAL); } } if (flags & BIOSARGS_FLAG) { if (arg_end - arg_start > ctob(16)) return (EACCES); args->seg.args.base = arg_start; args->seg.args.limit = 0xffff; } args->seg.code32.base = (u_int)&bios16_jmp & PG_FRAME; args->seg.code32.limit = 0xffff; ptd = (u_int *)rcr3(); if (ptd == IdlePTD) { /* * no page table, so create one and install it. */ pte = (u_int *)malloc(PAGE_SIZE, M_TEMP, M_WAITOK); ptd = (u_int *)((u_int)ptd + KERNBASE); *ptd = vtophys(pte) | PG_RW | PG_V; } else { /* * this is a user-level page table */ pte = (u_int *)&PTmap; } /* * install pointer to page 0. we don't need to flush the tlb, * since there should not be a previous mapping for page 0. */ *pte = (vm86pa - PAGE_SIZE) | PG_RW | PG_V; stack_top = stack; va_start(ap, fmt); for (p = fmt; p && *p; p++) { switch (*p) { case 'p': /* 32-bit pointer */ i = va_arg(ap, u_int); *(u_int *)stack = (i - arg_start) | (GSEL(GBIOSARGS_SEL, SEL_KPL) << 16); stack += 4; break; case 'i': /* 32-bit integer */ i = va_arg(ap, u_int); *(u_int *)stack = i; stack += 4; break; case 'U': /* 16-bit selector */ *(u_short *)stack = GSEL(GBIOSUTIL_SEL, SEL_KPL); stack += 2; break; case 'D': /* 16-bit selector */ *(u_short *)stack = GSEL(GBIOSDATA_SEL, SEL_KPL); stack += 2; break; case 'C': /* 16-bit selector */ *(u_short *)stack = GSEL(GBIOSCODE16_SEL, SEL_KPL); stack += 2; break; case 's': /* 16-bit integer */ i = va_arg(ap, u_short); *(u_short *)stack = i; stack += 2; break; default: return (EINVAL); } } set_bios_selectors(&args->seg, flags); bioscall_vector.vec16.offset = (u_short)args->entry; bioscall_vector.vec16.segment = GSEL(GBIOSCODE16_SEL, SEL_KPL); i = bios16_call(&args->r, stack_top); if (pte == (u_int *)&PTmap) { *pte = 0; /* remove entry */ } else { *ptd = 0; /* remove page table */ free(pte, M_TEMP); /* ... and free it */ } /* * XXX only needs to be invlpg(0) but that doesn't work on the 386 */ invltlb(); return (i); } #ifdef PNPBIOS /* remove conditional later */ /* * PnP BIOS interface; enumerate devices only known to the system * BIOS and save information about them for later use. */ struct pnp_sysdev { u_int16_t size; u_int8_t handle; u_int32_t devid; u_int8_t type[3]; u_int16_t attrib; #define PNPATTR_NODISABLE (1<<0) /* can't be disabled */ #define PNPATTR_NOCONFIG (1<<1) /* can't be configured */ #define PNPATTR_OUTPUT (1<<2) /* can be primary output */ #define PNPATTR_INPUT (1<<3) /* can be primary input */ #define PNPATTR_BOOTABLE (1<<4) /* can be booted from */ #define PNPATTR_DOCK (1<<5) /* is a docking station */ #define PNPATTR_REMOVEABLE (1<<6) /* device is removeable */ #define PNPATTR_CONFIG_STATIC 0x00 #define PNPATTR_CONFIG_DYNAMIC 0x07 #define PNPATTR_CONFIG_DYNONLY 0x17 /* device-specific data comes here */ u_int8_t devdata[0]; } __attribute__ ((packed)); /* We have to cluster arguments within a 64k range for the bios16 call */ struct pnp_sysdevargs { u_int16_t next; struct pnp_sysdev node; }; /* * This function is called after the bus has assigned resource * locations for a logical device. */ static void pnpbios_set_config(void *arg, struct isa_config *config, int enable) { } /* * Quiz the PnP BIOS, build a list of PNP IDs and resource data. */ static void pnpbios_identify(driver_t *driver, device_t parent) { struct PnPBIOS_table *pt = PnPBIOStable; struct bios_args args; struct pnp_sysdev *pd; struct pnp_sysdevargs *pda; u_int16_t ndevs, bigdev; int error, currdev; u_int8_t *devnodebuf, tag; u_int32_t *devid, *compid; int idx, left; device_t dev; /* no PnP BIOS information */ if (pt == NULL) return; bzero(&args, sizeof(args)); args.seg.code16.base = BIOS_PADDRTOVADDR(pt->pmentrybase); args.seg.code16.limit = 0xffff; /* XXX ? */ args.seg.data.base = BIOS_PADDRTOVADDR(pt->pmdataseg); args.seg.data.limit = 0xffff; args.entry = pt->pmentryoffset; if ((error = bios16(&args, PNP_COUNT_DEVNODES, &ndevs, &bigdev)) || (args.r.eax & 0xff)) printf("pnpbios: error %d/%x getting device count/size limit\n", error, args.r.eax); ndevs &= 0xff; /* clear high byte garbage */ if (bootverbose) printf("pnpbios: %d devices, largest %d bytes\n", ndevs, bigdev); devnodebuf = malloc(bigdev + (sizeof(struct pnp_sysdevargs) - sizeof(struct pnp_sysdev)), M_DEVBUF, M_NOWAIT); pda = (struct pnp_sysdevargs *)devnodebuf; pd = &pda->node; for (currdev = 0, left = ndevs; (currdev != 0xff) && (left > 0); left--) { bzero(pd, bigdev); pda->next = currdev; /* get current configuration */ if ((error = bios16(&args, PNP_GET_DEVNODE, &pda->next, &pda->node, (u_int16_t)1))) { printf("pnpbios: error %d making BIOS16 call\n", error); break; } if ((error = (args.r.eax & 0xff))) { if (bootverbose) printf("pnpbios: %s 0x%x fetching node %d\n", error & 0x80 ? "error" : "warning", error, currdev); if (error & 0x80) break; } currdev = pda->next; if (pd->size < sizeof(struct pnp_sysdev)) { printf("pnpbios: bogus system node data, aborting scan\n"); break; } /* Add the device and parse its resources */ dev = BUS_ADD_CHILD(parent, ISA_ORDER_PNP, NULL, -1); isa_set_vendorid(dev, pd->devid); isa_set_logicalid(dev, pd->devid); ISA_SET_CONFIG_CALLBACK(parent, dev, pnpbios_set_config, 0); pnp_parse_resources(dev, &pd->devdata[0], pd->size - sizeof(struct pnp_sysdev)); if (!device_get_desc(dev)) device_set_desc_copy(dev, pnp_eisaformat(pd->devid)); /* Find device IDs */ devid = &pd->devid; compid = NULL; /* look for a compatible device ID too */ left = pd->size - sizeof(struct pnp_sysdev); idx = 0; while (idx < left) { tag = pd->devdata[idx++]; if (PNP_RES_TYPE(tag) == 0) { /* Small resource */ switch (PNP_SRES_NUM(tag)) { case PNP_TAG_COMPAT_DEVICE: compid = (u_int32_t *)(pd->devdata + idx); if (bootverbose) printf("pnpbios: node %d compat ID 0x%08x\n", pd->handle, *compid); /* FALLTHROUGH */ case PNP_TAG_END: idx = left; break; default: idx += PNP_SRES_LEN(tag); break; } } else /* Large resource, skip it */ idx += *(u_int16_t *)(pd->devdata + idx) + 2; } if (bootverbose) { printf("pnpbios: handle %d device ID %s (%08x)", pd->handle, pnp_eisaformat(*devid), *devid); if (compid != NULL) printf(" compat ID %s (%08x)", pnp_eisaformat(*compid), *compid); printf("\n"); } } } static device_method_t pnpbios_methods[] = { /* Device interface */ DEVMETHOD(device_identify, pnpbios_identify), { 0, 0 } }; static driver_t pnpbios_driver = { "pnpbios", pnpbios_methods, 1, /* no softc */ }; static devclass_t pnpbios_devclass; DRIVER_MODULE(pnpbios, isa, pnpbios_driver, pnpbios_devclass, 0, 0); #endif /* PNPBIOS */ Index: head/sys/amd64/isa/intr_machdep.c =================================================================== --- head/sys/amd64/isa/intr_machdep.c (revision 55116) +++ head/sys/amd64/isa/intr_machdep.c (revision 55117) @@ -1,844 +1,850 @@ /*- * 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. * * from: @(#)isa.c 7.2 (Berkeley) 5/13/91 * $FreeBSD$ */ /* * This file contains an aggregated module marked: * Copyright (c) 1997, Stefan Esser * All rights reserved. * See the notice for details. */ #include "opt_auto_eoi.h" +#include "isa.h" + #include #ifndef SMP #include #endif #include #include #include #include #include #include #include #include #include #if defined(APIC_IO) #include #include /** FAST_HI */ #endif /* APIC_IO */ #ifdef PC98 #include #include #include #else #include #endif #include +#if NISA > 0 #include +#endif #include #include #ifdef APIC_IO #include #endif #include "mca.h" #if NMCA > 0 #include #endif /* XXX should be in suitable include files */ #ifdef PC98 #define ICU_IMR_OFFSET 2 /* IO_ICU{1,2} + 2 */ #define ICU_SLAVEID 7 #else #define ICU_IMR_OFFSET 1 /* IO_ICU{1,2} + 1 */ #define ICU_SLAVEID 2 #endif #ifdef APIC_IO /* * This is to accommodate "mixed-mode" programming for * motherboards that don't connect the 8254 to the IO APIC. */ #define AUTO_EOI_1 1 #endif #define NR_INTRNAMES (1 + ICU_LEN + 2 * ICU_LEN) u_long *intr_countp[ICU_LEN]; inthand2_t *intr_handler[ICU_LEN]; u_int intr_mask[ICU_LEN]; static u_int* intr_mptr[ICU_LEN]; void *intr_unit[ICU_LEN]; static inthand_t *fastintr[ICU_LEN] = { &IDTVEC(fastintr0), &IDTVEC(fastintr1), &IDTVEC(fastintr2), &IDTVEC(fastintr3), &IDTVEC(fastintr4), &IDTVEC(fastintr5), &IDTVEC(fastintr6), &IDTVEC(fastintr7), &IDTVEC(fastintr8), &IDTVEC(fastintr9), &IDTVEC(fastintr10), &IDTVEC(fastintr11), &IDTVEC(fastintr12), &IDTVEC(fastintr13), &IDTVEC(fastintr14), &IDTVEC(fastintr15), #if defined(APIC_IO) &IDTVEC(fastintr16), &IDTVEC(fastintr17), &IDTVEC(fastintr18), &IDTVEC(fastintr19), &IDTVEC(fastintr20), &IDTVEC(fastintr21), &IDTVEC(fastintr22), &IDTVEC(fastintr23), #endif /* APIC_IO */ }; static inthand_t *slowintr[ICU_LEN] = { &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), #if defined(APIC_IO) &IDTVEC(intr16), &IDTVEC(intr17), &IDTVEC(intr18), &IDTVEC(intr19), &IDTVEC(intr20), &IDTVEC(intr21), &IDTVEC(intr22), &IDTVEC(intr23), #endif /* APIC_IO */ }; static inthand2_t isa_strayintr; #ifdef PC98 #define NMI_PARITY 0x04 #define NMI_EPARITY 0x02 #else #define NMI_PARITY (1 << 7) #define NMI_IOCHAN (1 << 6) #define ENMI_WATCHDOG (1 << 7) #define ENMI_BUSTIMER (1 << 6) #define ENMI_IOSTATUS (1 << 5) #endif /* * Handle a NMI, possibly a machine check. * return true to panic system, false to ignore. */ int isa_nmi(cd) int cd; { #ifdef PC98 int port = inb(0x33); if (epson_machine_id == 0x20) epson_outb(0xc16, epson_inb(0xc16) | 0x1); if (port & NMI_PARITY) { panic("BASE RAM parity error, likely hardware failure."); } else if (port & NMI_EPARITY) { panic("EXTENDED RAM parity error, likely hardware failure."); } else { printf("\nNMI Resume ??\n"); return(0); } #else /* IBM-PC */ int isa_port = inb(0x61); int eisa_port = inb(0x461); #if NMCA > 0 if (MCA_system && mca_bus_nmi()) return(0); #endif if (isa_port & NMI_PARITY) panic("RAM parity error, likely hardware failure."); if (isa_port & NMI_IOCHAN) panic("I/O channel check, likely hardware failure."); /* * On a real EISA machine, this will never happen. However it can * happen on ISA machines which implement XT style floating point * error handling (very rare). Save them from a meaningless panic. */ if (eisa_port == 0xff) return(0); if (eisa_port & ENMI_WATCHDOG) panic("EISA watchdog timer expired, likely hardware failure."); if (eisa_port & ENMI_BUSTIMER) panic("EISA bus timeout, likely hardware failure."); if (eisa_port & ENMI_IOSTATUS) panic("EISA I/O port status error."); printf("\nNMI ISA %x, EISA %x\n", isa_port, eisa_port); return(0); #endif } /* * Fill in default interrupt table (in case of spuruious interrupt * during configuration of kernel, setup interrupt control unit */ void isa_defaultirq() { int i; /* icu vectors */ for (i = 0; i < ICU_LEN; i++) icu_unset(i, (inthand2_t *)NULL); /* initialize 8259's */ #if NMCA > 0 if (MCA_system) outb(IO_ICU1, 0x19); /* reset; program device, four bytes */ else #endif outb(IO_ICU1, 0x11); /* reset; program device, four bytes */ outb(IO_ICU1+ICU_IMR_OFFSET, NRSVIDT); /* starting at this vector index */ outb(IO_ICU1+ICU_IMR_OFFSET, IRQ_SLAVE); /* slave on line 7 */ #ifdef PC98 #ifdef AUTO_EOI_1 outb(IO_ICU1+ICU_IMR_OFFSET, 0x1f); /* (master) auto EOI, 8086 mode */ #else outb(IO_ICU1+ICU_IMR_OFFSET, 0x1d); /* (master) 8086 mode */ #endif #else /* IBM-PC */ #ifdef AUTO_EOI_1 outb(IO_ICU1+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */ #else outb(IO_ICU1+ICU_IMR_OFFSET, 1); /* 8086 mode */ #endif #endif /* PC98 */ outb(IO_ICU1+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */ outb(IO_ICU1, 0x0a); /* default to IRR on read */ #ifndef PC98 outb(IO_ICU1, 0xc0 | (3 - 1)); /* pri order 3-7, 0-2 (com2 first) */ #endif /* !PC98 */ #if NMCA > 0 if (MCA_system) outb(IO_ICU2, 0x19); /* reset; program device, four bytes */ else #endif outb(IO_ICU2, 0x11); /* reset; program device, four bytes */ outb(IO_ICU2+ICU_IMR_OFFSET, NRSVIDT+8); /* staring at this vector index */ outb(IO_ICU2+ICU_IMR_OFFSET, ICU_SLAVEID); /* my slave id is 7 */ #ifdef PC98 outb(IO_ICU2+ICU_IMR_OFFSET,9); /* 8086 mode */ #else /* IBM-PC */ #ifdef AUTO_EOI_2 outb(IO_ICU2+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */ #else outb(IO_ICU2+ICU_IMR_OFFSET,1); /* 8086 mode */ #endif #endif /* PC98 */ outb(IO_ICU2+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */ outb(IO_ICU2, 0x0a); /* default to IRR on read */ } /* * Caught a stray interrupt, notify */ static void isa_strayintr(vcookiep) void *vcookiep; { int intr = (void **)vcookiep - &intr_unit[0]; /* 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 */ /* * XXX TODO print a different message for #7 if it is for a * glitch. Glitches can be distinguished from real #7's by * testing that the in-service bit is _not_ set. The test * must be done before sending an EOI so it can't be done if * we are using AUTO_EOI_1. */ if (intrcnt[1 + intr] <= 5) log(LOG_ERR, "stray irq %d\n", intr); if (intrcnt[1 + intr] == 5) log(LOG_CRIT, "too many stray irq %d's; not logging any more\n", intr); } +#if NISA > 0 /* * Return a bitmap of the current interrupt requests. This is 8259-specific * and is only suitable for use at probe time. */ intrmask_t isa_irq_pending() { u_char irr1; u_char irr2; irr1 = inb(IO_ICU1); irr2 = inb(IO_ICU2); return ((irr2 << 8) | irr1); } +#endif int update_intr_masks(void) { int intr, n=0; u_int mask,*maskptr; for (intr=0; intr < ICU_LEN; intr ++) { #if defined(APIC_IO) /* no 8259 SLAVE to ignore */ #else if (intr==ICU_SLAVEID) continue; /* ignore 8259 SLAVE output */ #endif /* APIC_IO */ maskptr = intr_mptr[intr]; if (!maskptr) continue; *maskptr |= SWI_CLOCK_MASK | (1 << intr); mask = *maskptr; if (mask != intr_mask[intr]) { #if 0 printf ("intr_mask[%2d] old=%08x new=%08x ptr=%p.\n", intr, intr_mask[intr], mask, maskptr); #endif intr_mask[intr]=mask; n++; } } return (n); } static void update_intrname(int intr, char *name) { char buf[32]; char *cp; int name_index, off, strayintr; /* * Initialise strings for bitbucket and stray interrupt counters. * These have statically allocated indices 0 and 1 through ICU_LEN. */ if (intrnames[0] == '\0') { off = sprintf(intrnames, "???") + 1; for (strayintr = 0; strayintr < ICU_LEN; strayintr++) off += sprintf(intrnames + off, "stray irq%d", strayintr) + 1; } if (name == NULL) name = "???"; if (snprintf(buf, sizeof(buf), "%s irq%d", name, intr) >= sizeof(buf)) goto use_bitbucket; /* * Search for `buf' in `intrnames'. In the usual case when it is * not found, append it to the end if there is enough space (the \0 * terminator for the previous string, if any, becomes a separator). */ for (cp = intrnames, name_index = 0; cp != eintrnames && name_index < NR_INTRNAMES; cp += strlen(cp) + 1, name_index++) { if (*cp == '\0') { if (strlen(buf) >= eintrnames - cp) break; strcpy(cp, buf); goto found; } if (strcmp(cp, buf) == 0) goto found; } use_bitbucket: printf("update_intrname: counting %s irq%d as %s\n", name, intr, intrnames); name_index = 0; found: intr_countp[intr] = &intrcnt[name_index]; } int icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags) { #ifdef FAST_HI int select; /* the select register is 8 bits */ int vector; u_int32_t value; /* the window register is 32 bits */ #endif /* FAST_HI */ u_long ef; u_int mask = (maskptr ? *maskptr : 0); #if defined(APIC_IO) if ((u_int)intr >= ICU_LEN) /* no 8259 SLAVE to ignore */ #else if ((u_int)intr >= ICU_LEN || intr == ICU_SLAVEID) #endif /* APIC_IO */ if (intr_handler[intr] != isa_strayintr) return (EBUSY); ef = read_eflags(); disable_intr(); intr_handler[intr] = handler; intr_mptr[intr] = maskptr; intr_mask[intr] = mask | SWI_CLOCK_MASK | (1 << intr); intr_unit[intr] = arg; #ifdef FAST_HI if (flags & INTR_FAST) { vector = TPR_FAST_INTS + intr; setidt(vector, fastintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); } else { vector = TPR_SLOW_INTS + intr; #ifdef APIC_INTR_REORDER #ifdef APIC_INTR_HIGHPRI_CLOCK /* XXX: Hack (kludge?) for more accurate clock. */ if (intr == apic_8254_intr || intr == 8) { vector = TPR_FAST_INTS + intr; } #endif #endif setidt(vector, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); } #ifdef APIC_INTR_REORDER set_lapic_isrloc(intr, vector); #endif /* * Reprogram the vector in the IO APIC. */ if (int_to_apicintpin[intr].ioapic >= 0) { select = int_to_apicintpin[intr].redirindex; value = io_apic_read(int_to_apicintpin[intr].ioapic, select) & ~IOART_INTVEC; io_apic_write(int_to_apicintpin[intr].ioapic, select, value | vector); } #else setidt(ICU_OFFSET + intr, flags & INTR_FAST ? fastintr[intr] : slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #endif /* FAST_HI */ INTREN(1 << intr); MPINTR_UNLOCK(); write_eflags(ef); return (0); } int icu_unset(intr, handler) int intr; inthand2_t *handler; { u_long ef; if ((u_int)intr >= ICU_LEN || handler != intr_handler[intr]) return (EINVAL); INTRDIS(1 << intr); ef = read_eflags(); disable_intr(); intr_countp[intr] = &intrcnt[1 + intr]; intr_handler[intr] = isa_strayintr; intr_mptr[intr] = NULL; intr_mask[intr] = HWI_MASK | SWI_MASK; intr_unit[intr] = &intr_unit[intr]; #ifdef FAST_HI_XXX /* XXX how do I re-create dvp here? */ setidt(flags & INTR_FAST ? TPR_FAST_INTS + intr : TPR_SLOW_INTS + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #else /* FAST_HI */ #ifdef APIC_INTR_REORDER set_lapic_isrloc(intr, ICU_OFFSET + intr); #endif setidt(ICU_OFFSET + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #endif /* FAST_HI */ MPINTR_UNLOCK(); write_eflags(ef); return (0); } /* The following notice applies beyond this point in the file */ /* * Copyright (c) 1997, Stefan Esser * 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 unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ * */ typedef struct intrec { intrmask_t mask; inthand2_t *handler; void *argument; struct intrec *next; char *name; int intr; intrmask_t *maskptr; int flags; } intrec; static intrec *intreclist_head[ICU_LEN]; /* * The interrupt multiplexer calls each of the handlers in turn. The * ipl is initially quite low. It is raised as necessary for each call * and lowered after the call. Thus out of order handling is possible * even for interrupts of the same type. This is probably no more * harmful than out of order handling in general (not harmful except * for real time response which we don't support anyway). */ static void intr_mux(void *arg) { intrec *p; intrmask_t oldspl; for (p = arg; p != NULL; p = p->next) { oldspl = splq(p->mask); p->handler(p->argument); splx(oldspl); } } static intrec* find_idesc(unsigned *maskptr, int irq) { intrec *p = intreclist_head[irq]; while (p && p->maskptr != maskptr) p = p->next; return (p); } static intrec** find_pred(intrec *idesc, int irq) { intrec **pp = &intreclist_head[irq]; intrec *p = *pp; while (p != idesc) { if (p == NULL) return (NULL); pp = &p->next; p = *pp; } return (pp); } /* * Both the low level handler and the shared interrupt multiplexer * block out further interrupts as set in the handlers "mask", while * the handler is running. In fact *maskptr should be used for this * purpose, but since this requires one more pointer dereference on * each interrupt, we rather bother update "mask" whenever *maskptr * changes. The function "update_masks" should be called **after** * all manipulation of the linked list of interrupt handlers hung * off of intrdec_head[irq] is complete, since the chain of handlers * will both determine the *maskptr values and the instances of mask * that are fixed. This function should be called with the irq for * which a new handler has been add blocked, since the masks may not * yet know about the use of this irq for a device of a certain class. */ static void update_mux_masks(void) { int irq; for (irq = 0; irq < ICU_LEN; irq++) { intrec *idesc = intreclist_head[irq]; while (idesc != NULL) { if (idesc->maskptr != NULL) { /* our copy of *maskptr may be stale, refresh */ idesc->mask = *idesc->maskptr; } idesc = idesc->next; } } } static void update_masks(intrmask_t *maskptr, int irq) { intrmask_t mask = 1 << irq; if (maskptr == NULL) return; if (find_idesc(maskptr, irq) == NULL) { /* no reference to this maskptr was found in this irq's chain */ if ((*maskptr & mask) == 0) return; /* the irq was included in the classes mask, remove it */ *maskptr &= ~mask; } else { /* a reference to this maskptr was found in this irq's chain */ if ((*maskptr & mask) != 0) return; /* put the irq into the classes mask */ *maskptr |= mask; } /* we need to update all values in the intr_mask[irq] array */ update_intr_masks(); /* update mask in chains of the interrupt multiplex handler as well */ update_mux_masks(); } /* * Add interrupt handler to linked list hung off of intreclist_head[irq] * and install shared interrupt multiplex handler, if necessary */ static int add_intrdesc(intrec *idesc) { int irq = idesc->intr; intrec *head = intreclist_head[irq]; if (head == NULL) { /* first handler for this irq, just install it */ if (icu_setup(irq, idesc->handler, idesc->argument, idesc->maskptr, idesc->flags) != 0) return (-1); update_intrname(irq, idesc->name); /* keep reference */ intreclist_head[irq] = idesc; } else { if ((idesc->flags & INTR_EXCL) != 0 || (head->flags & INTR_EXCL) != 0) { /* * can't append new handler, if either list head or * new handler do not allow interrupts to be shared */ if (bootverbose) printf("\tdevice combination doesn't support " "shared irq%d\n", irq); return (-1); } if (head->next == NULL) { /* * second handler for this irq, replace device driver's * handler by shared interrupt multiplexer function */ icu_unset(irq, head->handler); if (icu_setup(irq, intr_mux, head, 0, 0) != 0) return (-1); if (bootverbose) printf("\tusing shared irq%d.\n", irq); update_intrname(irq, "mux"); } /* just append to the end of the chain */ while (head->next != NULL) head = head->next; head->next = idesc; } update_masks(idesc->maskptr, irq); return (0); } /* * Create and activate an interrupt handler descriptor data structure. * * The dev_instance pointer is required for resource management, and will * only be passed through to resource_claim(). * * There will be functions that derive a driver and unit name from a * dev_instance variable, and those functions will be used to maintain the * interrupt counter label array referenced by systat and vmstat to report * device interrupt rates (->update_intrlabels). * * Add the interrupt handler descriptor data structure created by an * earlier call of create_intr() to the linked list for its irq and * adjust the interrupt masks if necessary. */ intrec * inthand_add(const char *name, int irq, inthand2_t handler, void *arg, intrmask_t *maskptr, int flags) { intrec *idesc; int errcode = -1; intrmask_t oldspl; if (ICU_LEN > 8 * sizeof *maskptr) { printf("create_intr: ICU_LEN of %d too high for %d bit intrmask\n", ICU_LEN, 8 * sizeof *maskptr); return (NULL); } if ((unsigned)irq >= ICU_LEN) { printf("create_intr: requested irq%d too high, limit is %d\n", irq, ICU_LEN -1); return (NULL); } idesc = malloc(sizeof *idesc, M_DEVBUF, M_WAITOK); if (idesc == NULL) return NULL; bzero(idesc, sizeof *idesc); if (name == NULL) name = "???"; idesc->name = malloc(strlen(name) + 1, M_DEVBUF, M_WAITOK); if (idesc->name == NULL) { free(idesc, M_DEVBUF); return NULL; } strcpy(idesc->name, name); idesc->handler = handler; idesc->argument = arg; idesc->maskptr = maskptr; idesc->intr = irq; idesc->flags = flags; /* block this irq */ oldspl = splq(1 << irq); /* add irq to class selected by maskptr */ errcode = add_intrdesc(idesc); splx(oldspl); if (errcode != 0) { if (bootverbose) printf("\tintr_connect(irq%d) failed, result=%d\n", irq, errcode); free(idesc->name, M_DEVBUF); free(idesc, M_DEVBUF); idesc = NULL; } return (idesc); } /* * Deactivate and remove the interrupt handler descriptor data connected * created by an earlier call of intr_connect() from the linked list and * adjust theinterrupt masks if necessary. * * Return the memory held by the interrupt handler descriptor data structure * to the system. Make sure, the handler is not actively used anymore, before. */ int inthand_remove(intrec *idesc) { intrec **hook, *head; int irq; int errcode = 0; intrmask_t oldspl; if (idesc == NULL) return (-1); irq = idesc->intr; /* find pointer that keeps the reference to this interrupt descriptor */ hook = find_pred(idesc, irq); if (hook == NULL) return (-1); /* make copy of original list head, the line after may overwrite it */ head = intreclist_head[irq]; /* unlink: make predecessor point to idesc->next instead of to idesc */ *hook = idesc->next; /* now check whether the element we removed was the list head */ if (idesc == head) { oldspl = splq(1 << irq); /* check whether the new list head is the only element on list */ head = intreclist_head[irq]; if (head != NULL) { icu_unset(irq, intr_mux); if (head->next != NULL) { /* install the multiplex handler with new list head as argument */ errcode = icu_setup(irq, intr_mux, head, 0, 0); if (errcode == 0) update_intrname(irq, NULL); } else { /* install the one remaining handler for this irq */ errcode = icu_setup(irq, head->handler, head->argument, head->maskptr, head->flags); if (errcode == 0) update_intrname(irq, head->name); } } else { /* revert to old handler, eg: strayintr */ icu_unset(irq, idesc->handler); } splx(oldspl); } update_masks(idesc->maskptr, irq); free(idesc, M_DEVBUF); return (0); } Index: head/sys/amd64/isa/nmi.c =================================================================== --- head/sys/amd64/isa/nmi.c (revision 55116) +++ head/sys/amd64/isa/nmi.c (revision 55117) @@ -1,844 +1,850 @@ /*- * 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. * * from: @(#)isa.c 7.2 (Berkeley) 5/13/91 * $FreeBSD$ */ /* * This file contains an aggregated module marked: * Copyright (c) 1997, Stefan Esser * All rights reserved. * See the notice for details. */ #include "opt_auto_eoi.h" +#include "isa.h" + #include #ifndef SMP #include #endif #include #include #include #include #include #include #include #include #include #if defined(APIC_IO) #include #include /** FAST_HI */ #endif /* APIC_IO */ #ifdef PC98 #include #include #include #else #include #endif #include +#if NISA > 0 #include +#endif #include #include #ifdef APIC_IO #include #endif #include "mca.h" #if NMCA > 0 #include #endif /* XXX should be in suitable include files */ #ifdef PC98 #define ICU_IMR_OFFSET 2 /* IO_ICU{1,2} + 2 */ #define ICU_SLAVEID 7 #else #define ICU_IMR_OFFSET 1 /* IO_ICU{1,2} + 1 */ #define ICU_SLAVEID 2 #endif #ifdef APIC_IO /* * This is to accommodate "mixed-mode" programming for * motherboards that don't connect the 8254 to the IO APIC. */ #define AUTO_EOI_1 1 #endif #define NR_INTRNAMES (1 + ICU_LEN + 2 * ICU_LEN) u_long *intr_countp[ICU_LEN]; inthand2_t *intr_handler[ICU_LEN]; u_int intr_mask[ICU_LEN]; static u_int* intr_mptr[ICU_LEN]; void *intr_unit[ICU_LEN]; static inthand_t *fastintr[ICU_LEN] = { &IDTVEC(fastintr0), &IDTVEC(fastintr1), &IDTVEC(fastintr2), &IDTVEC(fastintr3), &IDTVEC(fastintr4), &IDTVEC(fastintr5), &IDTVEC(fastintr6), &IDTVEC(fastintr7), &IDTVEC(fastintr8), &IDTVEC(fastintr9), &IDTVEC(fastintr10), &IDTVEC(fastintr11), &IDTVEC(fastintr12), &IDTVEC(fastintr13), &IDTVEC(fastintr14), &IDTVEC(fastintr15), #if defined(APIC_IO) &IDTVEC(fastintr16), &IDTVEC(fastintr17), &IDTVEC(fastintr18), &IDTVEC(fastintr19), &IDTVEC(fastintr20), &IDTVEC(fastintr21), &IDTVEC(fastintr22), &IDTVEC(fastintr23), #endif /* APIC_IO */ }; static inthand_t *slowintr[ICU_LEN] = { &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), #if defined(APIC_IO) &IDTVEC(intr16), &IDTVEC(intr17), &IDTVEC(intr18), &IDTVEC(intr19), &IDTVEC(intr20), &IDTVEC(intr21), &IDTVEC(intr22), &IDTVEC(intr23), #endif /* APIC_IO */ }; static inthand2_t isa_strayintr; #ifdef PC98 #define NMI_PARITY 0x04 #define NMI_EPARITY 0x02 #else #define NMI_PARITY (1 << 7) #define NMI_IOCHAN (1 << 6) #define ENMI_WATCHDOG (1 << 7) #define ENMI_BUSTIMER (1 << 6) #define ENMI_IOSTATUS (1 << 5) #endif /* * Handle a NMI, possibly a machine check. * return true to panic system, false to ignore. */ int isa_nmi(cd) int cd; { #ifdef PC98 int port = inb(0x33); if (epson_machine_id == 0x20) epson_outb(0xc16, epson_inb(0xc16) | 0x1); if (port & NMI_PARITY) { panic("BASE RAM parity error, likely hardware failure."); } else if (port & NMI_EPARITY) { panic("EXTENDED RAM parity error, likely hardware failure."); } else { printf("\nNMI Resume ??\n"); return(0); } #else /* IBM-PC */ int isa_port = inb(0x61); int eisa_port = inb(0x461); #if NMCA > 0 if (MCA_system && mca_bus_nmi()) return(0); #endif if (isa_port & NMI_PARITY) panic("RAM parity error, likely hardware failure."); if (isa_port & NMI_IOCHAN) panic("I/O channel check, likely hardware failure."); /* * On a real EISA machine, this will never happen. However it can * happen on ISA machines which implement XT style floating point * error handling (very rare). Save them from a meaningless panic. */ if (eisa_port == 0xff) return(0); if (eisa_port & ENMI_WATCHDOG) panic("EISA watchdog timer expired, likely hardware failure."); if (eisa_port & ENMI_BUSTIMER) panic("EISA bus timeout, likely hardware failure."); if (eisa_port & ENMI_IOSTATUS) panic("EISA I/O port status error."); printf("\nNMI ISA %x, EISA %x\n", isa_port, eisa_port); return(0); #endif } /* * Fill in default interrupt table (in case of spuruious interrupt * during configuration of kernel, setup interrupt control unit */ void isa_defaultirq() { int i; /* icu vectors */ for (i = 0; i < ICU_LEN; i++) icu_unset(i, (inthand2_t *)NULL); /* initialize 8259's */ #if NMCA > 0 if (MCA_system) outb(IO_ICU1, 0x19); /* reset; program device, four bytes */ else #endif outb(IO_ICU1, 0x11); /* reset; program device, four bytes */ outb(IO_ICU1+ICU_IMR_OFFSET, NRSVIDT); /* starting at this vector index */ outb(IO_ICU1+ICU_IMR_OFFSET, IRQ_SLAVE); /* slave on line 7 */ #ifdef PC98 #ifdef AUTO_EOI_1 outb(IO_ICU1+ICU_IMR_OFFSET, 0x1f); /* (master) auto EOI, 8086 mode */ #else outb(IO_ICU1+ICU_IMR_OFFSET, 0x1d); /* (master) 8086 mode */ #endif #else /* IBM-PC */ #ifdef AUTO_EOI_1 outb(IO_ICU1+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */ #else outb(IO_ICU1+ICU_IMR_OFFSET, 1); /* 8086 mode */ #endif #endif /* PC98 */ outb(IO_ICU1+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */ outb(IO_ICU1, 0x0a); /* default to IRR on read */ #ifndef PC98 outb(IO_ICU1, 0xc0 | (3 - 1)); /* pri order 3-7, 0-2 (com2 first) */ #endif /* !PC98 */ #if NMCA > 0 if (MCA_system) outb(IO_ICU2, 0x19); /* reset; program device, four bytes */ else #endif outb(IO_ICU2, 0x11); /* reset; program device, four bytes */ outb(IO_ICU2+ICU_IMR_OFFSET, NRSVIDT+8); /* staring at this vector index */ outb(IO_ICU2+ICU_IMR_OFFSET, ICU_SLAVEID); /* my slave id is 7 */ #ifdef PC98 outb(IO_ICU2+ICU_IMR_OFFSET,9); /* 8086 mode */ #else /* IBM-PC */ #ifdef AUTO_EOI_2 outb(IO_ICU2+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */ #else outb(IO_ICU2+ICU_IMR_OFFSET,1); /* 8086 mode */ #endif #endif /* PC98 */ outb(IO_ICU2+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */ outb(IO_ICU2, 0x0a); /* default to IRR on read */ } /* * Caught a stray interrupt, notify */ static void isa_strayintr(vcookiep) void *vcookiep; { int intr = (void **)vcookiep - &intr_unit[0]; /* 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 */ /* * XXX TODO print a different message for #7 if it is for a * glitch. Glitches can be distinguished from real #7's by * testing that the in-service bit is _not_ set. The test * must be done before sending an EOI so it can't be done if * we are using AUTO_EOI_1. */ if (intrcnt[1 + intr] <= 5) log(LOG_ERR, "stray irq %d\n", intr); if (intrcnt[1 + intr] == 5) log(LOG_CRIT, "too many stray irq %d's; not logging any more\n", intr); } +#if NISA > 0 /* * Return a bitmap of the current interrupt requests. This is 8259-specific * and is only suitable for use at probe time. */ intrmask_t isa_irq_pending() { u_char irr1; u_char irr2; irr1 = inb(IO_ICU1); irr2 = inb(IO_ICU2); return ((irr2 << 8) | irr1); } +#endif int update_intr_masks(void) { int intr, n=0; u_int mask,*maskptr; for (intr=0; intr < ICU_LEN; intr ++) { #if defined(APIC_IO) /* no 8259 SLAVE to ignore */ #else if (intr==ICU_SLAVEID) continue; /* ignore 8259 SLAVE output */ #endif /* APIC_IO */ maskptr = intr_mptr[intr]; if (!maskptr) continue; *maskptr |= SWI_CLOCK_MASK | (1 << intr); mask = *maskptr; if (mask != intr_mask[intr]) { #if 0 printf ("intr_mask[%2d] old=%08x new=%08x ptr=%p.\n", intr, intr_mask[intr], mask, maskptr); #endif intr_mask[intr]=mask; n++; } } return (n); } static void update_intrname(int intr, char *name) { char buf[32]; char *cp; int name_index, off, strayintr; /* * Initialise strings for bitbucket and stray interrupt counters. * These have statically allocated indices 0 and 1 through ICU_LEN. */ if (intrnames[0] == '\0') { off = sprintf(intrnames, "???") + 1; for (strayintr = 0; strayintr < ICU_LEN; strayintr++) off += sprintf(intrnames + off, "stray irq%d", strayintr) + 1; } if (name == NULL) name = "???"; if (snprintf(buf, sizeof(buf), "%s irq%d", name, intr) >= sizeof(buf)) goto use_bitbucket; /* * Search for `buf' in `intrnames'. In the usual case when it is * not found, append it to the end if there is enough space (the \0 * terminator for the previous string, if any, becomes a separator). */ for (cp = intrnames, name_index = 0; cp != eintrnames && name_index < NR_INTRNAMES; cp += strlen(cp) + 1, name_index++) { if (*cp == '\0') { if (strlen(buf) >= eintrnames - cp) break; strcpy(cp, buf); goto found; } if (strcmp(cp, buf) == 0) goto found; } use_bitbucket: printf("update_intrname: counting %s irq%d as %s\n", name, intr, intrnames); name_index = 0; found: intr_countp[intr] = &intrcnt[name_index]; } int icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags) { #ifdef FAST_HI int select; /* the select register is 8 bits */ int vector; u_int32_t value; /* the window register is 32 bits */ #endif /* FAST_HI */ u_long ef; u_int mask = (maskptr ? *maskptr : 0); #if defined(APIC_IO) if ((u_int)intr >= ICU_LEN) /* no 8259 SLAVE to ignore */ #else if ((u_int)intr >= ICU_LEN || intr == ICU_SLAVEID) #endif /* APIC_IO */ if (intr_handler[intr] != isa_strayintr) return (EBUSY); ef = read_eflags(); disable_intr(); intr_handler[intr] = handler; intr_mptr[intr] = maskptr; intr_mask[intr] = mask | SWI_CLOCK_MASK | (1 << intr); intr_unit[intr] = arg; #ifdef FAST_HI if (flags & INTR_FAST) { vector = TPR_FAST_INTS + intr; setidt(vector, fastintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); } else { vector = TPR_SLOW_INTS + intr; #ifdef APIC_INTR_REORDER #ifdef APIC_INTR_HIGHPRI_CLOCK /* XXX: Hack (kludge?) for more accurate clock. */ if (intr == apic_8254_intr || intr == 8) { vector = TPR_FAST_INTS + intr; } #endif #endif setidt(vector, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); } #ifdef APIC_INTR_REORDER set_lapic_isrloc(intr, vector); #endif /* * Reprogram the vector in the IO APIC. */ if (int_to_apicintpin[intr].ioapic >= 0) { select = int_to_apicintpin[intr].redirindex; value = io_apic_read(int_to_apicintpin[intr].ioapic, select) & ~IOART_INTVEC; io_apic_write(int_to_apicintpin[intr].ioapic, select, value | vector); } #else setidt(ICU_OFFSET + intr, flags & INTR_FAST ? fastintr[intr] : slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #endif /* FAST_HI */ INTREN(1 << intr); MPINTR_UNLOCK(); write_eflags(ef); return (0); } int icu_unset(intr, handler) int intr; inthand2_t *handler; { u_long ef; if ((u_int)intr >= ICU_LEN || handler != intr_handler[intr]) return (EINVAL); INTRDIS(1 << intr); ef = read_eflags(); disable_intr(); intr_countp[intr] = &intrcnt[1 + intr]; intr_handler[intr] = isa_strayintr; intr_mptr[intr] = NULL; intr_mask[intr] = HWI_MASK | SWI_MASK; intr_unit[intr] = &intr_unit[intr]; #ifdef FAST_HI_XXX /* XXX how do I re-create dvp here? */ setidt(flags & INTR_FAST ? TPR_FAST_INTS + intr : TPR_SLOW_INTS + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #else /* FAST_HI */ #ifdef APIC_INTR_REORDER set_lapic_isrloc(intr, ICU_OFFSET + intr); #endif setidt(ICU_OFFSET + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #endif /* FAST_HI */ MPINTR_UNLOCK(); write_eflags(ef); return (0); } /* The following notice applies beyond this point in the file */ /* * Copyright (c) 1997, Stefan Esser * 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 unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ * */ typedef struct intrec { intrmask_t mask; inthand2_t *handler; void *argument; struct intrec *next; char *name; int intr; intrmask_t *maskptr; int flags; } intrec; static intrec *intreclist_head[ICU_LEN]; /* * The interrupt multiplexer calls each of the handlers in turn. The * ipl is initially quite low. It is raised as necessary for each call * and lowered after the call. Thus out of order handling is possible * even for interrupts of the same type. This is probably no more * harmful than out of order handling in general (not harmful except * for real time response which we don't support anyway). */ static void intr_mux(void *arg) { intrec *p; intrmask_t oldspl; for (p = arg; p != NULL; p = p->next) { oldspl = splq(p->mask); p->handler(p->argument); splx(oldspl); } } static intrec* find_idesc(unsigned *maskptr, int irq) { intrec *p = intreclist_head[irq]; while (p && p->maskptr != maskptr) p = p->next; return (p); } static intrec** find_pred(intrec *idesc, int irq) { intrec **pp = &intreclist_head[irq]; intrec *p = *pp; while (p != idesc) { if (p == NULL) return (NULL); pp = &p->next; p = *pp; } return (pp); } /* * Both the low level handler and the shared interrupt multiplexer * block out further interrupts as set in the handlers "mask", while * the handler is running. In fact *maskptr should be used for this * purpose, but since this requires one more pointer dereference on * each interrupt, we rather bother update "mask" whenever *maskptr * changes. The function "update_masks" should be called **after** * all manipulation of the linked list of interrupt handlers hung * off of intrdec_head[irq] is complete, since the chain of handlers * will both determine the *maskptr values and the instances of mask * that are fixed. This function should be called with the irq for * which a new handler has been add blocked, since the masks may not * yet know about the use of this irq for a device of a certain class. */ static void update_mux_masks(void) { int irq; for (irq = 0; irq < ICU_LEN; irq++) { intrec *idesc = intreclist_head[irq]; while (idesc != NULL) { if (idesc->maskptr != NULL) { /* our copy of *maskptr may be stale, refresh */ idesc->mask = *idesc->maskptr; } idesc = idesc->next; } } } static void update_masks(intrmask_t *maskptr, int irq) { intrmask_t mask = 1 << irq; if (maskptr == NULL) return; if (find_idesc(maskptr, irq) == NULL) { /* no reference to this maskptr was found in this irq's chain */ if ((*maskptr & mask) == 0) return; /* the irq was included in the classes mask, remove it */ *maskptr &= ~mask; } else { /* a reference to this maskptr was found in this irq's chain */ if ((*maskptr & mask) != 0) return; /* put the irq into the classes mask */ *maskptr |= mask; } /* we need to update all values in the intr_mask[irq] array */ update_intr_masks(); /* update mask in chains of the interrupt multiplex handler as well */ update_mux_masks(); } /* * Add interrupt handler to linked list hung off of intreclist_head[irq] * and install shared interrupt multiplex handler, if necessary */ static int add_intrdesc(intrec *idesc) { int irq = idesc->intr; intrec *head = intreclist_head[irq]; if (head == NULL) { /* first handler for this irq, just install it */ if (icu_setup(irq, idesc->handler, idesc->argument, idesc->maskptr, idesc->flags) != 0) return (-1); update_intrname(irq, idesc->name); /* keep reference */ intreclist_head[irq] = idesc; } else { if ((idesc->flags & INTR_EXCL) != 0 || (head->flags & INTR_EXCL) != 0) { /* * can't append new handler, if either list head or * new handler do not allow interrupts to be shared */ if (bootverbose) printf("\tdevice combination doesn't support " "shared irq%d\n", irq); return (-1); } if (head->next == NULL) { /* * second handler for this irq, replace device driver's * handler by shared interrupt multiplexer function */ icu_unset(irq, head->handler); if (icu_setup(irq, intr_mux, head, 0, 0) != 0) return (-1); if (bootverbose) printf("\tusing shared irq%d.\n", irq); update_intrname(irq, "mux"); } /* just append to the end of the chain */ while (head->next != NULL) head = head->next; head->next = idesc; } update_masks(idesc->maskptr, irq); return (0); } /* * Create and activate an interrupt handler descriptor data structure. * * The dev_instance pointer is required for resource management, and will * only be passed through to resource_claim(). * * There will be functions that derive a driver and unit name from a * dev_instance variable, and those functions will be used to maintain the * interrupt counter label array referenced by systat and vmstat to report * device interrupt rates (->update_intrlabels). * * Add the interrupt handler descriptor data structure created by an * earlier call of create_intr() to the linked list for its irq and * adjust the interrupt masks if necessary. */ intrec * inthand_add(const char *name, int irq, inthand2_t handler, void *arg, intrmask_t *maskptr, int flags) { intrec *idesc; int errcode = -1; intrmask_t oldspl; if (ICU_LEN > 8 * sizeof *maskptr) { printf("create_intr: ICU_LEN of %d too high for %d bit intrmask\n", ICU_LEN, 8 * sizeof *maskptr); return (NULL); } if ((unsigned)irq >= ICU_LEN) { printf("create_intr: requested irq%d too high, limit is %d\n", irq, ICU_LEN -1); return (NULL); } idesc = malloc(sizeof *idesc, M_DEVBUF, M_WAITOK); if (idesc == NULL) return NULL; bzero(idesc, sizeof *idesc); if (name == NULL) name = "???"; idesc->name = malloc(strlen(name) + 1, M_DEVBUF, M_WAITOK); if (idesc->name == NULL) { free(idesc, M_DEVBUF); return NULL; } strcpy(idesc->name, name); idesc->handler = handler; idesc->argument = arg; idesc->maskptr = maskptr; idesc->intr = irq; idesc->flags = flags; /* block this irq */ oldspl = splq(1 << irq); /* add irq to class selected by maskptr */ errcode = add_intrdesc(idesc); splx(oldspl); if (errcode != 0) { if (bootverbose) printf("\tintr_connect(irq%d) failed, result=%d\n", irq, errcode); free(idesc->name, M_DEVBUF); free(idesc, M_DEVBUF); idesc = NULL; } return (idesc); } /* * Deactivate and remove the interrupt handler descriptor data connected * created by an earlier call of intr_connect() from the linked list and * adjust theinterrupt masks if necessary. * * Return the memory held by the interrupt handler descriptor data structure * to the system. Make sure, the handler is not actively used anymore, before. */ int inthand_remove(intrec *idesc) { intrec **hook, *head; int irq; int errcode = 0; intrmask_t oldspl; if (idesc == NULL) return (-1); irq = idesc->intr; /* find pointer that keeps the reference to this interrupt descriptor */ hook = find_pred(idesc, irq); if (hook == NULL) return (-1); /* make copy of original list head, the line after may overwrite it */ head = intreclist_head[irq]; /* unlink: make predecessor point to idesc->next instead of to idesc */ *hook = idesc->next; /* now check whether the element we removed was the list head */ if (idesc == head) { oldspl = splq(1 << irq); /* check whether the new list head is the only element on list */ head = intreclist_head[irq]; if (head != NULL) { icu_unset(irq, intr_mux); if (head->next != NULL) { /* install the multiplex handler with new list head as argument */ errcode = icu_setup(irq, intr_mux, head, 0, 0); if (errcode == 0) update_intrname(irq, NULL); } else { /* install the one remaining handler for this irq */ errcode = icu_setup(irq, head->handler, head->argument, head->maskptr, head->flags); if (errcode == 0) update_intrname(irq, head->name); } } else { /* revert to old handler, eg: strayintr */ icu_unset(irq, idesc->handler); } splx(oldspl); } update_masks(idesc->maskptr, irq); free(idesc, M_DEVBUF); return (0); } Index: head/sys/i386/i386/autoconf.c =================================================================== --- head/sys/i386/i386/autoconf.c (revision 55116) +++ head/sys/i386/i386/autoconf.c (revision 55117) @@ -1,322 +1,326 @@ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)autoconf.c 7.1 (Berkeley) 5/9/91 * $FreeBSD$ */ /* * Setup the system to run on the current machine. * * Configure() is called at boot time and initializes the vba * device tables and the memory controller monitoring. Available * devices are determined (from possibilities mentioned in ioconf.c), * and the drivers are initialized. */ #include "opt_bootp.h" #include "opt_ffs.h" #include "opt_cd9660.h" #include "opt_nfs.h" #include "opt_nfsroot.h" #include "opt_bus.h" #include "opt_rootdevname.h" +#include "isa.h" + #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef APIC_IO #include #else #include #endif /* APIC_IO */ -#include "isa.h" +#if NISA > 0 #include + device_t isa_bus_device = 0; +#endif static void configure_first __P((void *)); static void configure __P((void *)); static void configure_final __P((void *)); #if defined(FFS) && defined(FFS_ROOT) static void setroot __P((void)); #endif SYSINIT(configure1, SI_SUB_CONFIGURE, SI_ORDER_FIRST, configure_first, NULL); /* SI_ORDER_SECOND is hookable */ SYSINIT(configure2, SI_SUB_CONFIGURE, SI_ORDER_THIRD, configure, NULL); /* SI_ORDER_MIDDLE is hookable */ SYSINIT(configure3, SI_SUB_CONFIGURE, SI_ORDER_ANY, configure_final, NULL); dev_t rootdev = NODEV; dev_t dumpdev = NODEV; device_t nexus_dev; /* * Determine i/o configuration for a machine. */ static void configure_first(dummy) void *dummy; { } static void configure(dummy) void *dummy; { /* * Activate the ICU's. Note that we are explicitly at splhigh() * at present as we have no way to disable stray PCI level triggered * interrupts until the devices have had a driver attached. This * is particularly a problem when the interrupts are shared. For * example, if IRQ 10 is shared between a disk and network device * and the disk device generates an interrupt, if we "activate" * IRQ 10 when the network driver is set up, then we will get * recursive interrupt 10's as nothing will know how to turn off * the disk device's interrupt. * * Having the ICU's active means we can probe interrupt routing to * see if a device causes the corresponding pending bit to be set. * * This is all rather inconvenient. */ #ifdef APIC_IO bsp_apic_configure(); enable_intr(); #else enable_intr(); INTREN(IRQ_SLAVE); #endif /* APIC_IO */ /* nexus0 is the top of the i386 device tree */ device_add_child(root_bus, "nexus", 0); /* initialize new bus architecture */ root_bus_configure(); #if NISA > 0 /* * Explicitly probe and attach ISA last. The isa bus saves * it's device node at attach time for us here. */ if (isa_bus_device) isa_probe_children(isa_bus_device); #endif /* * Now we're ready to handle (pending) interrupts. * XXX this is slightly misplaced. */ spl0(); /* * Allow lowering of the ipl to the lowest kernel level if we * panic (or call tsleep() before clearing `cold'). No level is * completely safe (since a panic may occur in a critical region * at splhigh()), but we want at least bio interrupts to work. */ safepri = cpl; } static void configure_final(dummy) void *dummy; { int i; cninit_finish(); if (bootverbose) { #ifdef APIC_IO imen_dump(); #endif /* APIC_IO */ /* * Print out the BIOS's idea of the disk geometries. */ printf("BIOS Geometries:\n"); for (i = 0; i < N_BIOS_GEOM; i++) { unsigned long bios_geom; int max_cylinder, max_head, max_sector; bios_geom = bootinfo.bi_bios_geom[i]; /* * XXX the bootstrap punts a 1200K floppy geometry * when the get-disk-geometry interrupt fails. Skip * drives that have this geometry. */ if (bios_geom == 0x4f010f) continue; printf(" %x:%08lx ", i, bios_geom); max_cylinder = bios_geom >> 16; max_head = (bios_geom >> 8) & 0xff; max_sector = bios_geom & 0xff; printf( "0..%d=%d cylinders, 0..%d=%d heads, 1..%d=%d sectors\n", max_cylinder, max_cylinder + 1, max_head, max_head + 1, max_sector, max_sector); } printf(" %d accounted for\n", bootinfo.bi_n_bios_used); printf("Device configuration finished.\n"); } cold = 0; } #ifdef BOOTP extern void bootpc_init(void); #endif /* * Do legacy root filesystem discovery. */ void cpu_rootconf() { #ifdef BOOTP bootpc_init(); #endif #if defined(NFS) && defined(NFS_ROOT) #if !defined(BOOTP_NFSROOT) if (nfs_diskless_valid) #endif rootdevnames[0] = "nfs:"; #endif #if defined(FFS) && defined(FFS_ROOT) if (!rootdevnames[0]) setroot(); #endif } SYSINIT(cpu_rootconf, SI_SUB_ROOT_CONF, SI_ORDER_FIRST, cpu_rootconf, NULL) u_long bootdev = 0; /* not a dev_t - encoding is different */ #if defined(FFS) && defined(FFS_ROOT) #define FDMAJOR 2 #define FDUNITSHIFT 6 /* * Attempt to find the device from which we were booted. * If we can do so, and not instructed not to do so, * set rootdevs[] and rootdevnames[] to correspond to the * boot device(s). * * This code survives in order to allow the system to be * booted from legacy environments that do not correctly * populate the kernel environment. There are significant * restrictions on the bootability of the system in this * situation; it can only be mounting root from a 'da' * 'wd' or 'fd' device, and the root filesystem must be ufs. */ static void setroot() { int majdev, mindev, unit, slice, part; dev_t newrootdev, dev; char partname[2]; char *sname; if ((bootdev & B_MAGICMASK) != B_DEVMAGIC) { printf("no B_DEVMAGIC (bootdev=%#lx)\n", bootdev); return; } majdev = B_TYPE(bootdev); dev = makebdev(majdev, 0); if (devsw(dev) == NULL) { printf("no devsw (majdev=%d bootdev=%#lx)\n", majdev, bootdev); return; } unit = B_UNIT(bootdev); slice = B_SLICE(bootdev); if (slice == WHOLE_DISK_SLICE) slice = COMPATIBILITY_SLICE; if (slice < 0 || slice >= MAX_SLICES) { printf("bad slice\n"); return; } /* * XXX kludge for inconsistent unit numbering and lack of slice * support for floppies. */ if (majdev == FDMAJOR) { slice = COMPATIBILITY_SLICE; part = RAW_PART; mindev = unit << FDUNITSHIFT; } else { part = B_PARTITION(bootdev); mindev = dkmakeminor(unit, slice, part); } newrootdev = makebdev(majdev, mindev); sname = dsname(newrootdev, unit, slice, part, partname); rootdevnames[0] = malloc(strlen(sname) + 6, M_DEVBUF, M_NOWAIT); sprintf(rootdevnames[0], "ufs:%s%s", sname, partname); /* * For properly dangerously dedicated disks (ones with a historical * bogus partition table), the boot blocks will give slice = 4, but * the kernel will only provide the compatibility slice since it * knows that slice 4 is not a real slice. Arrange to try mounting * the compatibility slice as root if mounting the slice passed by * the boot blocks fails. This handles the dangerously dedicated * case and perhaps others. */ if (slice == COMPATIBILITY_SLICE) return; slice = COMPATIBILITY_SLICE; sname = dsname(newrootdev, unit, slice, part, partname); rootdevnames[1] = malloc(strlen(sname) + 6, M_DEVBUF, M_NOWAIT); sprintf(rootdevnames[1], "ufs:%s%s", sname, partname); } #endif Index: head/sys/i386/i386/bios.c =================================================================== --- head/sys/i386/i386/bios.c (revision 55116) +++ head/sys/i386/i386/bios.c (revision 55117) @@ -1,623 +1,627 @@ /*- * Copyright (c) 1997 Michael Smith * Copyright (c) 1998 Jonathan Lemon * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Code for dealing with the BIOS in x86 PC systems. */ #include "opt_pnp.h" +#include "isa.h" + #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#if NISA > 0 #include +#endif #define BIOS_START 0xe0000 #define BIOS_SIZE 0x20000 /* exported lookup results */ struct bios32_SDentry PCIbios = {entry : 0}; struct PnPBIOS_table *PnPBIOStable = 0; static u_int bios32_SDCI = 0; /* start fairly early */ static void bios32_init(void *junk); SYSINIT(bios32, SI_SUB_CPU, SI_ORDER_ANY, bios32_init, NULL); /* * bios32_init * * Locate various bios32 entities. */ static void bios32_init(void *junk) { u_long sigaddr; struct bios32_SDheader *sdh; struct PnPBIOS_table *pt; u_int8_t ck, *cv; int i; /* * BIOS32 Service Directory */ /* look for the signature */ if ((sigaddr = bios_sigsearch(0, "_32_", 4, 16, 0)) != 0) { /* get a virtual pointer to the structure */ sdh = (struct bios32_SDheader *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr); for (cv = (u_int8_t *)sdh, ck = 0, i = 0; i < (sdh->len * 16); i++) { ck += cv[i]; } /* If checksum is OK, enable use of the entrypoint */ if ((ck == 0) && (sdh->entry < (BIOS_START + BIOS_SIZE))) { bios32_SDCI = BIOS_PADDRTOVADDR(sdh->entry); if (bootverbose) { printf("bios32: Found BIOS32 Service Directory header at %p\n", sdh); printf("bios32: Entry = 0x%x (%x) Rev = %d Len = %d\n", sdh->entry, bios32_SDCI, sdh->revision, sdh->len); } /* See if there's a PCI BIOS entrypoint here */ PCIbios.ident.id = 0x49435024; /* PCI systems should have this */ if (!bios32_SDlookup(&PCIbios) && bootverbose) printf("pcibios: PCI BIOS entry at 0x%x\n", PCIbios.entry); } else { printf("bios32: Bad BIOS32 Service Directory\n"); } } /* * PnP BIOS */ if ((sigaddr = bios_sigsearch(0, "$PnP", 4, 16, 0)) != 0) { /* get a virtual pointer to the structure */ pt = (struct PnPBIOS_table *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr); for (cv = (u_int8_t *)pt, ck = 0, i = 0; i < pt->len; i++) { ck += cv[i]; } /* If checksum is OK, enable use of the entrypoint */ if (ck == 0) { PnPBIOStable = pt; if (bootverbose) { printf("pnpbios: Found PnP BIOS data at %p\n", pt); printf("pnpbios: Entry = %x:%x Rev = %d.%d\n", pt->pmentrybase, pt->pmentryoffset, pt->version >> 4, pt->version & 0xf); if ((pt->control & 0x3) == 0x01) printf("pnpbios: Event flag at %x\n", pt->evflagaddr); if (pt->oemdevid != 0) printf("pnpbios: OEM ID %x\n", pt->oemdevid); } } else { printf("pnpbios: Bad PnP BIOS data checksum\n"); } } if (bootverbose) { /* look for other know signatures */ printf("Other BIOS signatures found:\n"); printf("ACPI: %08x\n", bios_sigsearch(0, "RSD PTR ", 8, 16, 0)); } } /* * bios32_SDlookup * * Query the BIOS32 Service Directory for the service named in (ent), * returns nonzero if the lookup fails. The caller must fill in * (ent->ident), the remainder are populated on a successful lookup. */ int bios32_SDlookup(struct bios32_SDentry *ent) { struct bios_regs args; if (bios32_SDCI == 0) return (1); args.eax = ent->ident.id; /* set up arguments */ args.ebx = args.ecx = args.edx = 0; bios32(&args, bios32_SDCI, GSEL(GCODE_SEL, SEL_KPL)); if ((args.eax & 0xff) == 0) { /* success? */ ent->base = args.ebx; ent->len = args.ecx; ent->entry = args.edx; return (0); /* all OK */ } return (1); /* failed */ } /* * bios_sigsearch * * Search some or all of the BIOS region for a signature string. * * (start) Optional offset returned from this function * (for searching for multiple matches), or NULL * to start the search from the base of the BIOS. * Note that this will be a _physical_ address in * the range 0xe0000 - 0xfffff. * (sig) is a pointer to the byte(s) of the signature. * (siglen) number of bytes in the signature. * (paralen) signature paragraph (alignment) size. * (sigofs) offset of the signature within the paragraph. * * Returns the _physical_ address of the found signature, 0 if the * signature was not found. */ u_int32_t bios_sigsearch(u_int32_t start, u_char *sig, int siglen, int paralen, int sigofs) { u_char *sp, *end; /* compute the starting address */ if ((start >= BIOS_START) && (start <= (BIOS_START + BIOS_SIZE))) { sp = (char *)BIOS_PADDRTOVADDR(start); } else if (start == 0) { sp = (char *)BIOS_PADDRTOVADDR(BIOS_START); } else { return 0; /* bogus start address */ } /* compute the end address */ end = (u_char *)BIOS_PADDRTOVADDR(BIOS_START + BIOS_SIZE); /* loop searching */ while ((sp + sigofs + siglen) < end) { /* compare here */ if (!bcmp(sp + sigofs, sig, siglen)) { /* convert back to physical address */ return((u_int32_t)BIOS_VADDRTOPADDR(sp)); } sp += paralen; } return(0); } /* * do not staticize, used by bioscall.s */ union { struct { u_short offset; u_short segment; } vec16; struct { u_int offset; u_short segment; } vec32; } bioscall_vector; /* bios jump vector */ void set_bios_selectors(struct bios_segments *seg, int flags) { struct soft_segment_descriptor ssd = { 0, /* segment base address (overwritten) */ 0, /* length (overwritten) */ SDT_MEMERA, /* segment type (overwritten) */ 0, /* priority level */ 1, /* descriptor present */ 0, 0, 1, /* descriptor size (overwritten) */ 0 /* granularity == byte units */ }; union descriptor *p_gdt; #ifdef SMP p_gdt = &gdt[cpuid]; #else p_gdt = gdt; #endif ssd.ssd_base = seg->code32.base; ssd.ssd_limit = seg->code32.limit; ssdtosd(&ssd, &p_gdt[GBIOSCODE32_SEL].sd); ssd.ssd_def32 = 0; if (flags & BIOSCODE_FLAG) { ssd.ssd_base = seg->code16.base; ssd.ssd_limit = seg->code16.limit; ssdtosd(&ssd, &p_gdt[GBIOSCODE16_SEL].sd); } ssd.ssd_type = SDT_MEMRWA; if (flags & BIOSDATA_FLAG) { ssd.ssd_base = seg->data.base; ssd.ssd_limit = seg->data.limit; ssdtosd(&ssd, &p_gdt[GBIOSDATA_SEL].sd); } if (flags & BIOSUTIL_FLAG) { ssd.ssd_base = seg->util.base; ssd.ssd_limit = seg->util.limit; ssdtosd(&ssd, &p_gdt[GBIOSUTIL_SEL].sd); } if (flags & BIOSARGS_FLAG) { ssd.ssd_base = seg->args.base; ssd.ssd_limit = seg->args.limit; ssdtosd(&ssd, &p_gdt[GBIOSARGS_SEL].sd); } } extern int vm86pa; extern void bios16_jmp(void); /* * this routine is really greedy with selectors, and uses 5: * * 32-bit code selector: to return to kernel * 16-bit code selector: for running code * data selector: for 16-bit data * util selector: extra utility selector * args selector: to handle pointers * * the util selector is set from the util16 entry in bios16_args, if a * "U" specifier is seen. * * See for description of format specifiers */ int bios16(struct bios_args *args, char *fmt, ...) { char *p, *stack, *stack_top; va_list ap; int flags = BIOSCODE_FLAG | BIOSDATA_FLAG; u_int i, arg_start, arg_end; u_int *pte, *ptd; arg_start = 0xffffffff; arg_end = 0; /* * Some BIOS entrypoints attempt to copy the largest-case * argument frame (in order to generalise handling for * different entry types). If our argument frame is * smaller than this, the BIOS will reach off the top of * our constructed stack segment. Pad the top of the stack * with some garbage to avoid this. */ stack = (caddr_t)PAGE_SIZE - 32; va_start(ap, fmt); for (p = fmt; p && *p; p++) { switch (*p) { case 'p': /* 32-bit pointer */ i = va_arg(ap, u_int); arg_start = min(arg_start, i); arg_end = max(arg_end, i); flags |= BIOSARGS_FLAG; stack -= 4; break; case 'i': /* 32-bit integer */ i = va_arg(ap, u_int); stack -= 4; break; case 'U': /* 16-bit selector */ flags |= BIOSUTIL_FLAG; /* FALLTHROUGH */ case 'D': /* 16-bit selector */ case 'C': /* 16-bit selector */ stack -= 2; break; case 's': /* 16-bit integer */ i = va_arg(ap, u_short); stack -= 2; break; default: return (EINVAL); } } if (flags & BIOSARGS_FLAG) { if (arg_end - arg_start > ctob(16)) return (EACCES); args->seg.args.base = arg_start; args->seg.args.limit = 0xffff; } args->seg.code32.base = (u_int)&bios16_jmp & PG_FRAME; args->seg.code32.limit = 0xffff; ptd = (u_int *)rcr3(); if (ptd == IdlePTD) { /* * no page table, so create one and install it. */ pte = (u_int *)malloc(PAGE_SIZE, M_TEMP, M_WAITOK); ptd = (u_int *)((u_int)ptd + KERNBASE); *ptd = vtophys(pte) | PG_RW | PG_V; } else { /* * this is a user-level page table */ pte = (u_int *)&PTmap; } /* * install pointer to page 0. we don't need to flush the tlb, * since there should not be a previous mapping for page 0. */ *pte = (vm86pa - PAGE_SIZE) | PG_RW | PG_V; stack_top = stack; va_start(ap, fmt); for (p = fmt; p && *p; p++) { switch (*p) { case 'p': /* 32-bit pointer */ i = va_arg(ap, u_int); *(u_int *)stack = (i - arg_start) | (GSEL(GBIOSARGS_SEL, SEL_KPL) << 16); stack += 4; break; case 'i': /* 32-bit integer */ i = va_arg(ap, u_int); *(u_int *)stack = i; stack += 4; break; case 'U': /* 16-bit selector */ *(u_short *)stack = GSEL(GBIOSUTIL_SEL, SEL_KPL); stack += 2; break; case 'D': /* 16-bit selector */ *(u_short *)stack = GSEL(GBIOSDATA_SEL, SEL_KPL); stack += 2; break; case 'C': /* 16-bit selector */ *(u_short *)stack = GSEL(GBIOSCODE16_SEL, SEL_KPL); stack += 2; break; case 's': /* 16-bit integer */ i = va_arg(ap, u_short); *(u_short *)stack = i; stack += 2; break; default: return (EINVAL); } } set_bios_selectors(&args->seg, flags); bioscall_vector.vec16.offset = (u_short)args->entry; bioscall_vector.vec16.segment = GSEL(GBIOSCODE16_SEL, SEL_KPL); i = bios16_call(&args->r, stack_top); if (pte == (u_int *)&PTmap) { *pte = 0; /* remove entry */ } else { *ptd = 0; /* remove page table */ free(pte, M_TEMP); /* ... and free it */ } /* * XXX only needs to be invlpg(0) but that doesn't work on the 386 */ invltlb(); return (i); } #ifdef PNPBIOS /* remove conditional later */ /* * PnP BIOS interface; enumerate devices only known to the system * BIOS and save information about them for later use. */ struct pnp_sysdev { u_int16_t size; u_int8_t handle; u_int32_t devid; u_int8_t type[3]; u_int16_t attrib; #define PNPATTR_NODISABLE (1<<0) /* can't be disabled */ #define PNPATTR_NOCONFIG (1<<1) /* can't be configured */ #define PNPATTR_OUTPUT (1<<2) /* can be primary output */ #define PNPATTR_INPUT (1<<3) /* can be primary input */ #define PNPATTR_BOOTABLE (1<<4) /* can be booted from */ #define PNPATTR_DOCK (1<<5) /* is a docking station */ #define PNPATTR_REMOVEABLE (1<<6) /* device is removeable */ #define PNPATTR_CONFIG_STATIC 0x00 #define PNPATTR_CONFIG_DYNAMIC 0x07 #define PNPATTR_CONFIG_DYNONLY 0x17 /* device-specific data comes here */ u_int8_t devdata[0]; } __attribute__ ((packed)); /* We have to cluster arguments within a 64k range for the bios16 call */ struct pnp_sysdevargs { u_int16_t next; struct pnp_sysdev node; }; /* * This function is called after the bus has assigned resource * locations for a logical device. */ static void pnpbios_set_config(void *arg, struct isa_config *config, int enable) { } /* * Quiz the PnP BIOS, build a list of PNP IDs and resource data. */ static void pnpbios_identify(driver_t *driver, device_t parent) { struct PnPBIOS_table *pt = PnPBIOStable; struct bios_args args; struct pnp_sysdev *pd; struct pnp_sysdevargs *pda; u_int16_t ndevs, bigdev; int error, currdev; u_int8_t *devnodebuf, tag; u_int32_t *devid, *compid; int idx, left; device_t dev; /* no PnP BIOS information */ if (pt == NULL) return; bzero(&args, sizeof(args)); args.seg.code16.base = BIOS_PADDRTOVADDR(pt->pmentrybase); args.seg.code16.limit = 0xffff; /* XXX ? */ args.seg.data.base = BIOS_PADDRTOVADDR(pt->pmdataseg); args.seg.data.limit = 0xffff; args.entry = pt->pmentryoffset; if ((error = bios16(&args, PNP_COUNT_DEVNODES, &ndevs, &bigdev)) || (args.r.eax & 0xff)) printf("pnpbios: error %d/%x getting device count/size limit\n", error, args.r.eax); ndevs &= 0xff; /* clear high byte garbage */ if (bootverbose) printf("pnpbios: %d devices, largest %d bytes\n", ndevs, bigdev); devnodebuf = malloc(bigdev + (sizeof(struct pnp_sysdevargs) - sizeof(struct pnp_sysdev)), M_DEVBUF, M_NOWAIT); pda = (struct pnp_sysdevargs *)devnodebuf; pd = &pda->node; for (currdev = 0, left = ndevs; (currdev != 0xff) && (left > 0); left--) { bzero(pd, bigdev); pda->next = currdev; /* get current configuration */ if ((error = bios16(&args, PNP_GET_DEVNODE, &pda->next, &pda->node, (u_int16_t)1))) { printf("pnpbios: error %d making BIOS16 call\n", error); break; } if ((error = (args.r.eax & 0xff))) { if (bootverbose) printf("pnpbios: %s 0x%x fetching node %d\n", error & 0x80 ? "error" : "warning", error, currdev); if (error & 0x80) break; } currdev = pda->next; if (pd->size < sizeof(struct pnp_sysdev)) { printf("pnpbios: bogus system node data, aborting scan\n"); break; } /* Add the device and parse its resources */ dev = BUS_ADD_CHILD(parent, ISA_ORDER_PNP, NULL, -1); isa_set_vendorid(dev, pd->devid); isa_set_logicalid(dev, pd->devid); ISA_SET_CONFIG_CALLBACK(parent, dev, pnpbios_set_config, 0); pnp_parse_resources(dev, &pd->devdata[0], pd->size - sizeof(struct pnp_sysdev)); if (!device_get_desc(dev)) device_set_desc_copy(dev, pnp_eisaformat(pd->devid)); /* Find device IDs */ devid = &pd->devid; compid = NULL; /* look for a compatible device ID too */ left = pd->size - sizeof(struct pnp_sysdev); idx = 0; while (idx < left) { tag = pd->devdata[idx++]; if (PNP_RES_TYPE(tag) == 0) { /* Small resource */ switch (PNP_SRES_NUM(tag)) { case PNP_TAG_COMPAT_DEVICE: compid = (u_int32_t *)(pd->devdata + idx); if (bootverbose) printf("pnpbios: node %d compat ID 0x%08x\n", pd->handle, *compid); /* FALLTHROUGH */ case PNP_TAG_END: idx = left; break; default: idx += PNP_SRES_LEN(tag); break; } } else /* Large resource, skip it */ idx += *(u_int16_t *)(pd->devdata + idx) + 2; } if (bootverbose) { printf("pnpbios: handle %d device ID %s (%08x)", pd->handle, pnp_eisaformat(*devid), *devid); if (compid != NULL) printf(" compat ID %s (%08x)", pnp_eisaformat(*compid), *compid); printf("\n"); } } } static device_method_t pnpbios_methods[] = { /* Device interface */ DEVMETHOD(device_identify, pnpbios_identify), { 0, 0 } }; static driver_t pnpbios_driver = { "pnpbios", pnpbios_methods, 1, /* no softc */ }; static devclass_t pnpbios_devclass; DRIVER_MODULE(pnpbios, isa, pnpbios_driver, pnpbios_devclass, 0, 0); #endif /* PNPBIOS */ Index: head/sys/i386/isa/intr_machdep.c =================================================================== --- head/sys/i386/isa/intr_machdep.c (revision 55116) +++ head/sys/i386/isa/intr_machdep.c (revision 55117) @@ -1,844 +1,850 @@ /*- * 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. * * from: @(#)isa.c 7.2 (Berkeley) 5/13/91 * $FreeBSD$ */ /* * This file contains an aggregated module marked: * Copyright (c) 1997, Stefan Esser * All rights reserved. * See the notice for details. */ #include "opt_auto_eoi.h" +#include "isa.h" + #include #ifndef SMP #include #endif #include #include #include #include #include #include #include #include #include #if defined(APIC_IO) #include #include /** FAST_HI */ #endif /* APIC_IO */ #ifdef PC98 #include #include #include #else #include #endif #include +#if NISA > 0 #include +#endif #include #include #ifdef APIC_IO #include #endif #include "mca.h" #if NMCA > 0 #include #endif /* XXX should be in suitable include files */ #ifdef PC98 #define ICU_IMR_OFFSET 2 /* IO_ICU{1,2} + 2 */ #define ICU_SLAVEID 7 #else #define ICU_IMR_OFFSET 1 /* IO_ICU{1,2} + 1 */ #define ICU_SLAVEID 2 #endif #ifdef APIC_IO /* * This is to accommodate "mixed-mode" programming for * motherboards that don't connect the 8254 to the IO APIC. */ #define AUTO_EOI_1 1 #endif #define NR_INTRNAMES (1 + ICU_LEN + 2 * ICU_LEN) u_long *intr_countp[ICU_LEN]; inthand2_t *intr_handler[ICU_LEN]; u_int intr_mask[ICU_LEN]; static u_int* intr_mptr[ICU_LEN]; void *intr_unit[ICU_LEN]; static inthand_t *fastintr[ICU_LEN] = { &IDTVEC(fastintr0), &IDTVEC(fastintr1), &IDTVEC(fastintr2), &IDTVEC(fastintr3), &IDTVEC(fastintr4), &IDTVEC(fastintr5), &IDTVEC(fastintr6), &IDTVEC(fastintr7), &IDTVEC(fastintr8), &IDTVEC(fastintr9), &IDTVEC(fastintr10), &IDTVEC(fastintr11), &IDTVEC(fastintr12), &IDTVEC(fastintr13), &IDTVEC(fastintr14), &IDTVEC(fastintr15), #if defined(APIC_IO) &IDTVEC(fastintr16), &IDTVEC(fastintr17), &IDTVEC(fastintr18), &IDTVEC(fastintr19), &IDTVEC(fastintr20), &IDTVEC(fastintr21), &IDTVEC(fastintr22), &IDTVEC(fastintr23), #endif /* APIC_IO */ }; static inthand_t *slowintr[ICU_LEN] = { &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), #if defined(APIC_IO) &IDTVEC(intr16), &IDTVEC(intr17), &IDTVEC(intr18), &IDTVEC(intr19), &IDTVEC(intr20), &IDTVEC(intr21), &IDTVEC(intr22), &IDTVEC(intr23), #endif /* APIC_IO */ }; static inthand2_t isa_strayintr; #ifdef PC98 #define NMI_PARITY 0x04 #define NMI_EPARITY 0x02 #else #define NMI_PARITY (1 << 7) #define NMI_IOCHAN (1 << 6) #define ENMI_WATCHDOG (1 << 7) #define ENMI_BUSTIMER (1 << 6) #define ENMI_IOSTATUS (1 << 5) #endif /* * Handle a NMI, possibly a machine check. * return true to panic system, false to ignore. */ int isa_nmi(cd) int cd; { #ifdef PC98 int port = inb(0x33); if (epson_machine_id == 0x20) epson_outb(0xc16, epson_inb(0xc16) | 0x1); if (port & NMI_PARITY) { panic("BASE RAM parity error, likely hardware failure."); } else if (port & NMI_EPARITY) { panic("EXTENDED RAM parity error, likely hardware failure."); } else { printf("\nNMI Resume ??\n"); return(0); } #else /* IBM-PC */ int isa_port = inb(0x61); int eisa_port = inb(0x461); #if NMCA > 0 if (MCA_system && mca_bus_nmi()) return(0); #endif if (isa_port & NMI_PARITY) panic("RAM parity error, likely hardware failure."); if (isa_port & NMI_IOCHAN) panic("I/O channel check, likely hardware failure."); /* * On a real EISA machine, this will never happen. However it can * happen on ISA machines which implement XT style floating point * error handling (very rare). Save them from a meaningless panic. */ if (eisa_port == 0xff) return(0); if (eisa_port & ENMI_WATCHDOG) panic("EISA watchdog timer expired, likely hardware failure."); if (eisa_port & ENMI_BUSTIMER) panic("EISA bus timeout, likely hardware failure."); if (eisa_port & ENMI_IOSTATUS) panic("EISA I/O port status error."); printf("\nNMI ISA %x, EISA %x\n", isa_port, eisa_port); return(0); #endif } /* * Fill in default interrupt table (in case of spuruious interrupt * during configuration of kernel, setup interrupt control unit */ void isa_defaultirq() { int i; /* icu vectors */ for (i = 0; i < ICU_LEN; i++) icu_unset(i, (inthand2_t *)NULL); /* initialize 8259's */ #if NMCA > 0 if (MCA_system) outb(IO_ICU1, 0x19); /* reset; program device, four bytes */ else #endif outb(IO_ICU1, 0x11); /* reset; program device, four bytes */ outb(IO_ICU1+ICU_IMR_OFFSET, NRSVIDT); /* starting at this vector index */ outb(IO_ICU1+ICU_IMR_OFFSET, IRQ_SLAVE); /* slave on line 7 */ #ifdef PC98 #ifdef AUTO_EOI_1 outb(IO_ICU1+ICU_IMR_OFFSET, 0x1f); /* (master) auto EOI, 8086 mode */ #else outb(IO_ICU1+ICU_IMR_OFFSET, 0x1d); /* (master) 8086 mode */ #endif #else /* IBM-PC */ #ifdef AUTO_EOI_1 outb(IO_ICU1+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */ #else outb(IO_ICU1+ICU_IMR_OFFSET, 1); /* 8086 mode */ #endif #endif /* PC98 */ outb(IO_ICU1+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */ outb(IO_ICU1, 0x0a); /* default to IRR on read */ #ifndef PC98 outb(IO_ICU1, 0xc0 | (3 - 1)); /* pri order 3-7, 0-2 (com2 first) */ #endif /* !PC98 */ #if NMCA > 0 if (MCA_system) outb(IO_ICU2, 0x19); /* reset; program device, four bytes */ else #endif outb(IO_ICU2, 0x11); /* reset; program device, four bytes */ outb(IO_ICU2+ICU_IMR_OFFSET, NRSVIDT+8); /* staring at this vector index */ outb(IO_ICU2+ICU_IMR_OFFSET, ICU_SLAVEID); /* my slave id is 7 */ #ifdef PC98 outb(IO_ICU2+ICU_IMR_OFFSET,9); /* 8086 mode */ #else /* IBM-PC */ #ifdef AUTO_EOI_2 outb(IO_ICU2+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */ #else outb(IO_ICU2+ICU_IMR_OFFSET,1); /* 8086 mode */ #endif #endif /* PC98 */ outb(IO_ICU2+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */ outb(IO_ICU2, 0x0a); /* default to IRR on read */ } /* * Caught a stray interrupt, notify */ static void isa_strayintr(vcookiep) void *vcookiep; { int intr = (void **)vcookiep - &intr_unit[0]; /* 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 */ /* * XXX TODO print a different message for #7 if it is for a * glitch. Glitches can be distinguished from real #7's by * testing that the in-service bit is _not_ set. The test * must be done before sending an EOI so it can't be done if * we are using AUTO_EOI_1. */ if (intrcnt[1 + intr] <= 5) log(LOG_ERR, "stray irq %d\n", intr); if (intrcnt[1 + intr] == 5) log(LOG_CRIT, "too many stray irq %d's; not logging any more\n", intr); } +#if NISA > 0 /* * Return a bitmap of the current interrupt requests. This is 8259-specific * and is only suitable for use at probe time. */ intrmask_t isa_irq_pending() { u_char irr1; u_char irr2; irr1 = inb(IO_ICU1); irr2 = inb(IO_ICU2); return ((irr2 << 8) | irr1); } +#endif int update_intr_masks(void) { int intr, n=0; u_int mask,*maskptr; for (intr=0; intr < ICU_LEN; intr ++) { #if defined(APIC_IO) /* no 8259 SLAVE to ignore */ #else if (intr==ICU_SLAVEID) continue; /* ignore 8259 SLAVE output */ #endif /* APIC_IO */ maskptr = intr_mptr[intr]; if (!maskptr) continue; *maskptr |= SWI_CLOCK_MASK | (1 << intr); mask = *maskptr; if (mask != intr_mask[intr]) { #if 0 printf ("intr_mask[%2d] old=%08x new=%08x ptr=%p.\n", intr, intr_mask[intr], mask, maskptr); #endif intr_mask[intr]=mask; n++; } } return (n); } static void update_intrname(int intr, char *name) { char buf[32]; char *cp; int name_index, off, strayintr; /* * Initialise strings for bitbucket and stray interrupt counters. * These have statically allocated indices 0 and 1 through ICU_LEN. */ if (intrnames[0] == '\0') { off = sprintf(intrnames, "???") + 1; for (strayintr = 0; strayintr < ICU_LEN; strayintr++) off += sprintf(intrnames + off, "stray irq%d", strayintr) + 1; } if (name == NULL) name = "???"; if (snprintf(buf, sizeof(buf), "%s irq%d", name, intr) >= sizeof(buf)) goto use_bitbucket; /* * Search for `buf' in `intrnames'. In the usual case when it is * not found, append it to the end if there is enough space (the \0 * terminator for the previous string, if any, becomes a separator). */ for (cp = intrnames, name_index = 0; cp != eintrnames && name_index < NR_INTRNAMES; cp += strlen(cp) + 1, name_index++) { if (*cp == '\0') { if (strlen(buf) >= eintrnames - cp) break; strcpy(cp, buf); goto found; } if (strcmp(cp, buf) == 0) goto found; } use_bitbucket: printf("update_intrname: counting %s irq%d as %s\n", name, intr, intrnames); name_index = 0; found: intr_countp[intr] = &intrcnt[name_index]; } int icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags) { #ifdef FAST_HI int select; /* the select register is 8 bits */ int vector; u_int32_t value; /* the window register is 32 bits */ #endif /* FAST_HI */ u_long ef; u_int mask = (maskptr ? *maskptr : 0); #if defined(APIC_IO) if ((u_int)intr >= ICU_LEN) /* no 8259 SLAVE to ignore */ #else if ((u_int)intr >= ICU_LEN || intr == ICU_SLAVEID) #endif /* APIC_IO */ if (intr_handler[intr] != isa_strayintr) return (EBUSY); ef = read_eflags(); disable_intr(); intr_handler[intr] = handler; intr_mptr[intr] = maskptr; intr_mask[intr] = mask | SWI_CLOCK_MASK | (1 << intr); intr_unit[intr] = arg; #ifdef FAST_HI if (flags & INTR_FAST) { vector = TPR_FAST_INTS + intr; setidt(vector, fastintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); } else { vector = TPR_SLOW_INTS + intr; #ifdef APIC_INTR_REORDER #ifdef APIC_INTR_HIGHPRI_CLOCK /* XXX: Hack (kludge?) for more accurate clock. */ if (intr == apic_8254_intr || intr == 8) { vector = TPR_FAST_INTS + intr; } #endif #endif setidt(vector, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); } #ifdef APIC_INTR_REORDER set_lapic_isrloc(intr, vector); #endif /* * Reprogram the vector in the IO APIC. */ if (int_to_apicintpin[intr].ioapic >= 0) { select = int_to_apicintpin[intr].redirindex; value = io_apic_read(int_to_apicintpin[intr].ioapic, select) & ~IOART_INTVEC; io_apic_write(int_to_apicintpin[intr].ioapic, select, value | vector); } #else setidt(ICU_OFFSET + intr, flags & INTR_FAST ? fastintr[intr] : slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #endif /* FAST_HI */ INTREN(1 << intr); MPINTR_UNLOCK(); write_eflags(ef); return (0); } int icu_unset(intr, handler) int intr; inthand2_t *handler; { u_long ef; if ((u_int)intr >= ICU_LEN || handler != intr_handler[intr]) return (EINVAL); INTRDIS(1 << intr); ef = read_eflags(); disable_intr(); intr_countp[intr] = &intrcnt[1 + intr]; intr_handler[intr] = isa_strayintr; intr_mptr[intr] = NULL; intr_mask[intr] = HWI_MASK | SWI_MASK; intr_unit[intr] = &intr_unit[intr]; #ifdef FAST_HI_XXX /* XXX how do I re-create dvp here? */ setidt(flags & INTR_FAST ? TPR_FAST_INTS + intr : TPR_SLOW_INTS + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #else /* FAST_HI */ #ifdef APIC_INTR_REORDER set_lapic_isrloc(intr, ICU_OFFSET + intr); #endif setidt(ICU_OFFSET + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #endif /* FAST_HI */ MPINTR_UNLOCK(); write_eflags(ef); return (0); } /* The following notice applies beyond this point in the file */ /* * Copyright (c) 1997, Stefan Esser * 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 unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ * */ typedef struct intrec { intrmask_t mask; inthand2_t *handler; void *argument; struct intrec *next; char *name; int intr; intrmask_t *maskptr; int flags; } intrec; static intrec *intreclist_head[ICU_LEN]; /* * The interrupt multiplexer calls each of the handlers in turn. The * ipl is initially quite low. It is raised as necessary for each call * and lowered after the call. Thus out of order handling is possible * even for interrupts of the same type. This is probably no more * harmful than out of order handling in general (not harmful except * for real time response which we don't support anyway). */ static void intr_mux(void *arg) { intrec *p; intrmask_t oldspl; for (p = arg; p != NULL; p = p->next) { oldspl = splq(p->mask); p->handler(p->argument); splx(oldspl); } } static intrec* find_idesc(unsigned *maskptr, int irq) { intrec *p = intreclist_head[irq]; while (p && p->maskptr != maskptr) p = p->next; return (p); } static intrec** find_pred(intrec *idesc, int irq) { intrec **pp = &intreclist_head[irq]; intrec *p = *pp; while (p != idesc) { if (p == NULL) return (NULL); pp = &p->next; p = *pp; } return (pp); } /* * Both the low level handler and the shared interrupt multiplexer * block out further interrupts as set in the handlers "mask", while * the handler is running. In fact *maskptr should be used for this * purpose, but since this requires one more pointer dereference on * each interrupt, we rather bother update "mask" whenever *maskptr * changes. The function "update_masks" should be called **after** * all manipulation of the linked list of interrupt handlers hung * off of intrdec_head[irq] is complete, since the chain of handlers * will both determine the *maskptr values and the instances of mask * that are fixed. This function should be called with the irq for * which a new handler has been add blocked, since the masks may not * yet know about the use of this irq for a device of a certain class. */ static void update_mux_masks(void) { int irq; for (irq = 0; irq < ICU_LEN; irq++) { intrec *idesc = intreclist_head[irq]; while (idesc != NULL) { if (idesc->maskptr != NULL) { /* our copy of *maskptr may be stale, refresh */ idesc->mask = *idesc->maskptr; } idesc = idesc->next; } } } static void update_masks(intrmask_t *maskptr, int irq) { intrmask_t mask = 1 << irq; if (maskptr == NULL) return; if (find_idesc(maskptr, irq) == NULL) { /* no reference to this maskptr was found in this irq's chain */ if ((*maskptr & mask) == 0) return; /* the irq was included in the classes mask, remove it */ *maskptr &= ~mask; } else { /* a reference to this maskptr was found in this irq's chain */ if ((*maskptr & mask) != 0) return; /* put the irq into the classes mask */ *maskptr |= mask; } /* we need to update all values in the intr_mask[irq] array */ update_intr_masks(); /* update mask in chains of the interrupt multiplex handler as well */ update_mux_masks(); } /* * Add interrupt handler to linked list hung off of intreclist_head[irq] * and install shared interrupt multiplex handler, if necessary */ static int add_intrdesc(intrec *idesc) { int irq = idesc->intr; intrec *head = intreclist_head[irq]; if (head == NULL) { /* first handler for this irq, just install it */ if (icu_setup(irq, idesc->handler, idesc->argument, idesc->maskptr, idesc->flags) != 0) return (-1); update_intrname(irq, idesc->name); /* keep reference */ intreclist_head[irq] = idesc; } else { if ((idesc->flags & INTR_EXCL) != 0 || (head->flags & INTR_EXCL) != 0) { /* * can't append new handler, if either list head or * new handler do not allow interrupts to be shared */ if (bootverbose) printf("\tdevice combination doesn't support " "shared irq%d\n", irq); return (-1); } if (head->next == NULL) { /* * second handler for this irq, replace device driver's * handler by shared interrupt multiplexer function */ icu_unset(irq, head->handler); if (icu_setup(irq, intr_mux, head, 0, 0) != 0) return (-1); if (bootverbose) printf("\tusing shared irq%d.\n", irq); update_intrname(irq, "mux"); } /* just append to the end of the chain */ while (head->next != NULL) head = head->next; head->next = idesc; } update_masks(idesc->maskptr, irq); return (0); } /* * Create and activate an interrupt handler descriptor data structure. * * The dev_instance pointer is required for resource management, and will * only be passed through to resource_claim(). * * There will be functions that derive a driver and unit name from a * dev_instance variable, and those functions will be used to maintain the * interrupt counter label array referenced by systat and vmstat to report * device interrupt rates (->update_intrlabels). * * Add the interrupt handler descriptor data structure created by an * earlier call of create_intr() to the linked list for its irq and * adjust the interrupt masks if necessary. */ intrec * inthand_add(const char *name, int irq, inthand2_t handler, void *arg, intrmask_t *maskptr, int flags) { intrec *idesc; int errcode = -1; intrmask_t oldspl; if (ICU_LEN > 8 * sizeof *maskptr) { printf("create_intr: ICU_LEN of %d too high for %d bit intrmask\n", ICU_LEN, 8 * sizeof *maskptr); return (NULL); } if ((unsigned)irq >= ICU_LEN) { printf("create_intr: requested irq%d too high, limit is %d\n", irq, ICU_LEN -1); return (NULL); } idesc = malloc(sizeof *idesc, M_DEVBUF, M_WAITOK); if (idesc == NULL) return NULL; bzero(idesc, sizeof *idesc); if (name == NULL) name = "???"; idesc->name = malloc(strlen(name) + 1, M_DEVBUF, M_WAITOK); if (idesc->name == NULL) { free(idesc, M_DEVBUF); return NULL; } strcpy(idesc->name, name); idesc->handler = handler; idesc->argument = arg; idesc->maskptr = maskptr; idesc->intr = irq; idesc->flags = flags; /* block this irq */ oldspl = splq(1 << irq); /* add irq to class selected by maskptr */ errcode = add_intrdesc(idesc); splx(oldspl); if (errcode != 0) { if (bootverbose) printf("\tintr_connect(irq%d) failed, result=%d\n", irq, errcode); free(idesc->name, M_DEVBUF); free(idesc, M_DEVBUF); idesc = NULL; } return (idesc); } /* * Deactivate and remove the interrupt handler descriptor data connected * created by an earlier call of intr_connect() from the linked list and * adjust theinterrupt masks if necessary. * * Return the memory held by the interrupt handler descriptor data structure * to the system. Make sure, the handler is not actively used anymore, before. */ int inthand_remove(intrec *idesc) { intrec **hook, *head; int irq; int errcode = 0; intrmask_t oldspl; if (idesc == NULL) return (-1); irq = idesc->intr; /* find pointer that keeps the reference to this interrupt descriptor */ hook = find_pred(idesc, irq); if (hook == NULL) return (-1); /* make copy of original list head, the line after may overwrite it */ head = intreclist_head[irq]; /* unlink: make predecessor point to idesc->next instead of to idesc */ *hook = idesc->next; /* now check whether the element we removed was the list head */ if (idesc == head) { oldspl = splq(1 << irq); /* check whether the new list head is the only element on list */ head = intreclist_head[irq]; if (head != NULL) { icu_unset(irq, intr_mux); if (head->next != NULL) { /* install the multiplex handler with new list head as argument */ errcode = icu_setup(irq, intr_mux, head, 0, 0); if (errcode == 0) update_intrname(irq, NULL); } else { /* install the one remaining handler for this irq */ errcode = icu_setup(irq, head->handler, head->argument, head->maskptr, head->flags); if (errcode == 0) update_intrname(irq, head->name); } } else { /* revert to old handler, eg: strayintr */ icu_unset(irq, idesc->handler); } splx(oldspl); } update_masks(idesc->maskptr, irq); free(idesc, M_DEVBUF); return (0); } Index: head/sys/i386/isa/nmi.c =================================================================== --- head/sys/i386/isa/nmi.c (revision 55116) +++ head/sys/i386/isa/nmi.c (revision 55117) @@ -1,844 +1,850 @@ /*- * 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. * * from: @(#)isa.c 7.2 (Berkeley) 5/13/91 * $FreeBSD$ */ /* * This file contains an aggregated module marked: * Copyright (c) 1997, Stefan Esser * All rights reserved. * See the notice for details. */ #include "opt_auto_eoi.h" +#include "isa.h" + #include #ifndef SMP #include #endif #include #include #include #include #include #include #include #include #include #if defined(APIC_IO) #include #include /** FAST_HI */ #endif /* APIC_IO */ #ifdef PC98 #include #include #include #else #include #endif #include +#if NISA > 0 #include +#endif #include #include #ifdef APIC_IO #include #endif #include "mca.h" #if NMCA > 0 #include #endif /* XXX should be in suitable include files */ #ifdef PC98 #define ICU_IMR_OFFSET 2 /* IO_ICU{1,2} + 2 */ #define ICU_SLAVEID 7 #else #define ICU_IMR_OFFSET 1 /* IO_ICU{1,2} + 1 */ #define ICU_SLAVEID 2 #endif #ifdef APIC_IO /* * This is to accommodate "mixed-mode" programming for * motherboards that don't connect the 8254 to the IO APIC. */ #define AUTO_EOI_1 1 #endif #define NR_INTRNAMES (1 + ICU_LEN + 2 * ICU_LEN) u_long *intr_countp[ICU_LEN]; inthand2_t *intr_handler[ICU_LEN]; u_int intr_mask[ICU_LEN]; static u_int* intr_mptr[ICU_LEN]; void *intr_unit[ICU_LEN]; static inthand_t *fastintr[ICU_LEN] = { &IDTVEC(fastintr0), &IDTVEC(fastintr1), &IDTVEC(fastintr2), &IDTVEC(fastintr3), &IDTVEC(fastintr4), &IDTVEC(fastintr5), &IDTVEC(fastintr6), &IDTVEC(fastintr7), &IDTVEC(fastintr8), &IDTVEC(fastintr9), &IDTVEC(fastintr10), &IDTVEC(fastintr11), &IDTVEC(fastintr12), &IDTVEC(fastintr13), &IDTVEC(fastintr14), &IDTVEC(fastintr15), #if defined(APIC_IO) &IDTVEC(fastintr16), &IDTVEC(fastintr17), &IDTVEC(fastintr18), &IDTVEC(fastintr19), &IDTVEC(fastintr20), &IDTVEC(fastintr21), &IDTVEC(fastintr22), &IDTVEC(fastintr23), #endif /* APIC_IO */ }; static inthand_t *slowintr[ICU_LEN] = { &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), #if defined(APIC_IO) &IDTVEC(intr16), &IDTVEC(intr17), &IDTVEC(intr18), &IDTVEC(intr19), &IDTVEC(intr20), &IDTVEC(intr21), &IDTVEC(intr22), &IDTVEC(intr23), #endif /* APIC_IO */ }; static inthand2_t isa_strayintr; #ifdef PC98 #define NMI_PARITY 0x04 #define NMI_EPARITY 0x02 #else #define NMI_PARITY (1 << 7) #define NMI_IOCHAN (1 << 6) #define ENMI_WATCHDOG (1 << 7) #define ENMI_BUSTIMER (1 << 6) #define ENMI_IOSTATUS (1 << 5) #endif /* * Handle a NMI, possibly a machine check. * return true to panic system, false to ignore. */ int isa_nmi(cd) int cd; { #ifdef PC98 int port = inb(0x33); if (epson_machine_id == 0x20) epson_outb(0xc16, epson_inb(0xc16) | 0x1); if (port & NMI_PARITY) { panic("BASE RAM parity error, likely hardware failure."); } else if (port & NMI_EPARITY) { panic("EXTENDED RAM parity error, likely hardware failure."); } else { printf("\nNMI Resume ??\n"); return(0); } #else /* IBM-PC */ int isa_port = inb(0x61); int eisa_port = inb(0x461); #if NMCA > 0 if (MCA_system && mca_bus_nmi()) return(0); #endif if (isa_port & NMI_PARITY) panic("RAM parity error, likely hardware failure."); if (isa_port & NMI_IOCHAN) panic("I/O channel check, likely hardware failure."); /* * On a real EISA machine, this will never happen. However it can * happen on ISA machines which implement XT style floating point * error handling (very rare). Save them from a meaningless panic. */ if (eisa_port == 0xff) return(0); if (eisa_port & ENMI_WATCHDOG) panic("EISA watchdog timer expired, likely hardware failure."); if (eisa_port & ENMI_BUSTIMER) panic("EISA bus timeout, likely hardware failure."); if (eisa_port & ENMI_IOSTATUS) panic("EISA I/O port status error."); printf("\nNMI ISA %x, EISA %x\n", isa_port, eisa_port); return(0); #endif } /* * Fill in default interrupt table (in case of spuruious interrupt * during configuration of kernel, setup interrupt control unit */ void isa_defaultirq() { int i; /* icu vectors */ for (i = 0; i < ICU_LEN; i++) icu_unset(i, (inthand2_t *)NULL); /* initialize 8259's */ #if NMCA > 0 if (MCA_system) outb(IO_ICU1, 0x19); /* reset; program device, four bytes */ else #endif outb(IO_ICU1, 0x11); /* reset; program device, four bytes */ outb(IO_ICU1+ICU_IMR_OFFSET, NRSVIDT); /* starting at this vector index */ outb(IO_ICU1+ICU_IMR_OFFSET, IRQ_SLAVE); /* slave on line 7 */ #ifdef PC98 #ifdef AUTO_EOI_1 outb(IO_ICU1+ICU_IMR_OFFSET, 0x1f); /* (master) auto EOI, 8086 mode */ #else outb(IO_ICU1+ICU_IMR_OFFSET, 0x1d); /* (master) 8086 mode */ #endif #else /* IBM-PC */ #ifdef AUTO_EOI_1 outb(IO_ICU1+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */ #else outb(IO_ICU1+ICU_IMR_OFFSET, 1); /* 8086 mode */ #endif #endif /* PC98 */ outb(IO_ICU1+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */ outb(IO_ICU1, 0x0a); /* default to IRR on read */ #ifndef PC98 outb(IO_ICU1, 0xc0 | (3 - 1)); /* pri order 3-7, 0-2 (com2 first) */ #endif /* !PC98 */ #if NMCA > 0 if (MCA_system) outb(IO_ICU2, 0x19); /* reset; program device, four bytes */ else #endif outb(IO_ICU2, 0x11); /* reset; program device, four bytes */ outb(IO_ICU2+ICU_IMR_OFFSET, NRSVIDT+8); /* staring at this vector index */ outb(IO_ICU2+ICU_IMR_OFFSET, ICU_SLAVEID); /* my slave id is 7 */ #ifdef PC98 outb(IO_ICU2+ICU_IMR_OFFSET,9); /* 8086 mode */ #else /* IBM-PC */ #ifdef AUTO_EOI_2 outb(IO_ICU2+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */ #else outb(IO_ICU2+ICU_IMR_OFFSET,1); /* 8086 mode */ #endif #endif /* PC98 */ outb(IO_ICU2+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */ outb(IO_ICU2, 0x0a); /* default to IRR on read */ } /* * Caught a stray interrupt, notify */ static void isa_strayintr(vcookiep) void *vcookiep; { int intr = (void **)vcookiep - &intr_unit[0]; /* 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 */ /* * XXX TODO print a different message for #7 if it is for a * glitch. Glitches can be distinguished from real #7's by * testing that the in-service bit is _not_ set. The test * must be done before sending an EOI so it can't be done if * we are using AUTO_EOI_1. */ if (intrcnt[1 + intr] <= 5) log(LOG_ERR, "stray irq %d\n", intr); if (intrcnt[1 + intr] == 5) log(LOG_CRIT, "too many stray irq %d's; not logging any more\n", intr); } +#if NISA > 0 /* * Return a bitmap of the current interrupt requests. This is 8259-specific * and is only suitable for use at probe time. */ intrmask_t isa_irq_pending() { u_char irr1; u_char irr2; irr1 = inb(IO_ICU1); irr2 = inb(IO_ICU2); return ((irr2 << 8) | irr1); } +#endif int update_intr_masks(void) { int intr, n=0; u_int mask,*maskptr; for (intr=0; intr < ICU_LEN; intr ++) { #if defined(APIC_IO) /* no 8259 SLAVE to ignore */ #else if (intr==ICU_SLAVEID) continue; /* ignore 8259 SLAVE output */ #endif /* APIC_IO */ maskptr = intr_mptr[intr]; if (!maskptr) continue; *maskptr |= SWI_CLOCK_MASK | (1 << intr); mask = *maskptr; if (mask != intr_mask[intr]) { #if 0 printf ("intr_mask[%2d] old=%08x new=%08x ptr=%p.\n", intr, intr_mask[intr], mask, maskptr); #endif intr_mask[intr]=mask; n++; } } return (n); } static void update_intrname(int intr, char *name) { char buf[32]; char *cp; int name_index, off, strayintr; /* * Initialise strings for bitbucket and stray interrupt counters. * These have statically allocated indices 0 and 1 through ICU_LEN. */ if (intrnames[0] == '\0') { off = sprintf(intrnames, "???") + 1; for (strayintr = 0; strayintr < ICU_LEN; strayintr++) off += sprintf(intrnames + off, "stray irq%d", strayintr) + 1; } if (name == NULL) name = "???"; if (snprintf(buf, sizeof(buf), "%s irq%d", name, intr) >= sizeof(buf)) goto use_bitbucket; /* * Search for `buf' in `intrnames'. In the usual case when it is * not found, append it to the end if there is enough space (the \0 * terminator for the previous string, if any, becomes a separator). */ for (cp = intrnames, name_index = 0; cp != eintrnames && name_index < NR_INTRNAMES; cp += strlen(cp) + 1, name_index++) { if (*cp == '\0') { if (strlen(buf) >= eintrnames - cp) break; strcpy(cp, buf); goto found; } if (strcmp(cp, buf) == 0) goto found; } use_bitbucket: printf("update_intrname: counting %s irq%d as %s\n", name, intr, intrnames); name_index = 0; found: intr_countp[intr] = &intrcnt[name_index]; } int icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags) { #ifdef FAST_HI int select; /* the select register is 8 bits */ int vector; u_int32_t value; /* the window register is 32 bits */ #endif /* FAST_HI */ u_long ef; u_int mask = (maskptr ? *maskptr : 0); #if defined(APIC_IO) if ((u_int)intr >= ICU_LEN) /* no 8259 SLAVE to ignore */ #else if ((u_int)intr >= ICU_LEN || intr == ICU_SLAVEID) #endif /* APIC_IO */ if (intr_handler[intr] != isa_strayintr) return (EBUSY); ef = read_eflags(); disable_intr(); intr_handler[intr] = handler; intr_mptr[intr] = maskptr; intr_mask[intr] = mask | SWI_CLOCK_MASK | (1 << intr); intr_unit[intr] = arg; #ifdef FAST_HI if (flags & INTR_FAST) { vector = TPR_FAST_INTS + intr; setidt(vector, fastintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); } else { vector = TPR_SLOW_INTS + intr; #ifdef APIC_INTR_REORDER #ifdef APIC_INTR_HIGHPRI_CLOCK /* XXX: Hack (kludge?) for more accurate clock. */ if (intr == apic_8254_intr || intr == 8) { vector = TPR_FAST_INTS + intr; } #endif #endif setidt(vector, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); } #ifdef APIC_INTR_REORDER set_lapic_isrloc(intr, vector); #endif /* * Reprogram the vector in the IO APIC. */ if (int_to_apicintpin[intr].ioapic >= 0) { select = int_to_apicintpin[intr].redirindex; value = io_apic_read(int_to_apicintpin[intr].ioapic, select) & ~IOART_INTVEC; io_apic_write(int_to_apicintpin[intr].ioapic, select, value | vector); } #else setidt(ICU_OFFSET + intr, flags & INTR_FAST ? fastintr[intr] : slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #endif /* FAST_HI */ INTREN(1 << intr); MPINTR_UNLOCK(); write_eflags(ef); return (0); } int icu_unset(intr, handler) int intr; inthand2_t *handler; { u_long ef; if ((u_int)intr >= ICU_LEN || handler != intr_handler[intr]) return (EINVAL); INTRDIS(1 << intr); ef = read_eflags(); disable_intr(); intr_countp[intr] = &intrcnt[1 + intr]; intr_handler[intr] = isa_strayintr; intr_mptr[intr] = NULL; intr_mask[intr] = HWI_MASK | SWI_MASK; intr_unit[intr] = &intr_unit[intr]; #ifdef FAST_HI_XXX /* XXX how do I re-create dvp here? */ setidt(flags & INTR_FAST ? TPR_FAST_INTS + intr : TPR_SLOW_INTS + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #else /* FAST_HI */ #ifdef APIC_INTR_REORDER set_lapic_isrloc(intr, ICU_OFFSET + intr); #endif setidt(ICU_OFFSET + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); #endif /* FAST_HI */ MPINTR_UNLOCK(); write_eflags(ef); return (0); } /* The following notice applies beyond this point in the file */ /* * Copyright (c) 1997, Stefan Esser * 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 unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ * */ typedef struct intrec { intrmask_t mask; inthand2_t *handler; void *argument; struct intrec *next; char *name; int intr; intrmask_t *maskptr; int flags; } intrec; static intrec *intreclist_head[ICU_LEN]; /* * The interrupt multiplexer calls each of the handlers in turn. The * ipl is initially quite low. It is raised as necessary for each call * and lowered after the call. Thus out of order handling is possible * even for interrupts of the same type. This is probably no more * harmful than out of order handling in general (not harmful except * for real time response which we don't support anyway). */ static void intr_mux(void *arg) { intrec *p; intrmask_t oldspl; for (p = arg; p != NULL; p = p->next) { oldspl = splq(p->mask); p->handler(p->argument); splx(oldspl); } } static intrec* find_idesc(unsigned *maskptr, int irq) { intrec *p = intreclist_head[irq]; while (p && p->maskptr != maskptr) p = p->next; return (p); } static intrec** find_pred(intrec *idesc, int irq) { intrec **pp = &intreclist_head[irq]; intrec *p = *pp; while (p != idesc) { if (p == NULL) return (NULL); pp = &p->next; p = *pp; } return (pp); } /* * Both the low level handler and the shared interrupt multiplexer * block out further interrupts as set in the handlers "mask", while * the handler is running. In fact *maskptr should be used for this * purpose, but since this requires one more pointer dereference on * each interrupt, we rather bother update "mask" whenever *maskptr * changes. The function "update_masks" should be called **after** * all manipulation of the linked list of interrupt handlers hung * off of intrdec_head[irq] is complete, since the chain of handlers * will both determine the *maskptr values and the instances of mask * that are fixed. This function should be called with the irq for * which a new handler has been add blocked, since the masks may not * yet know about the use of this irq for a device of a certain class. */ static void update_mux_masks(void) { int irq; for (irq = 0; irq < ICU_LEN; irq++) { intrec *idesc = intreclist_head[irq]; while (idesc != NULL) { if (idesc->maskptr != NULL) { /* our copy of *maskptr may be stale, refresh */ idesc->mask = *idesc->maskptr; } idesc = idesc->next; } } } static void update_masks(intrmask_t *maskptr, int irq) { intrmask_t mask = 1 << irq; if (maskptr == NULL) return; if (find_idesc(maskptr, irq) == NULL) { /* no reference to this maskptr was found in this irq's chain */ if ((*maskptr & mask) == 0) return; /* the irq was included in the classes mask, remove it */ *maskptr &= ~mask; } else { /* a reference to this maskptr was found in this irq's chain */ if ((*maskptr & mask) != 0) return; /* put the irq into the classes mask */ *maskptr |= mask; } /* we need to update all values in the intr_mask[irq] array */ update_intr_masks(); /* update mask in chains of the interrupt multiplex handler as well */ update_mux_masks(); } /* * Add interrupt handler to linked list hung off of intreclist_head[irq] * and install shared interrupt multiplex handler, if necessary */ static int add_intrdesc(intrec *idesc) { int irq = idesc->intr; intrec *head = intreclist_head[irq]; if (head == NULL) { /* first handler for this irq, just install it */ if (icu_setup(irq, idesc->handler, idesc->argument, idesc->maskptr, idesc->flags) != 0) return (-1); update_intrname(irq, idesc->name); /* keep reference */ intreclist_head[irq] = idesc; } else { if ((idesc->flags & INTR_EXCL) != 0 || (head->flags & INTR_EXCL) != 0) { /* * can't append new handler, if either list head or * new handler do not allow interrupts to be shared */ if (bootverbose) printf("\tdevice combination doesn't support " "shared irq%d\n", irq); return (-1); } if (head->next == NULL) { /* * second handler for this irq, replace device driver's * handler by shared interrupt multiplexer function */ icu_unset(irq, head->handler); if (icu_setup(irq, intr_mux, head, 0, 0) != 0) return (-1); if (bootverbose) printf("\tusing shared irq%d.\n", irq); update_intrname(irq, "mux"); } /* just append to the end of the chain */ while (head->next != NULL) head = head->next; head->next = idesc; } update_masks(idesc->maskptr, irq); return (0); } /* * Create and activate an interrupt handler descriptor data structure. * * The dev_instance pointer is required for resource management, and will * only be passed through to resource_claim(). * * There will be functions that derive a driver and unit name from a * dev_instance variable, and those functions will be used to maintain the * interrupt counter label array referenced by systat and vmstat to report * device interrupt rates (->update_intrlabels). * * Add the interrupt handler descriptor data structure created by an * earlier call of create_intr() to the linked list for its irq and * adjust the interrupt masks if necessary. */ intrec * inthand_add(const char *name, int irq, inthand2_t handler, void *arg, intrmask_t *maskptr, int flags) { intrec *idesc; int errcode = -1; intrmask_t oldspl; if (ICU_LEN > 8 * sizeof *maskptr) { printf("create_intr: ICU_LEN of %d too high for %d bit intrmask\n", ICU_LEN, 8 * sizeof *maskptr); return (NULL); } if ((unsigned)irq >= ICU_LEN) { printf("create_intr: requested irq%d too high, limit is %d\n", irq, ICU_LEN -1); return (NULL); } idesc = malloc(sizeof *idesc, M_DEVBUF, M_WAITOK); if (idesc == NULL) return NULL; bzero(idesc, sizeof *idesc); if (name == NULL) name = "???"; idesc->name = malloc(strlen(name) + 1, M_DEVBUF, M_WAITOK); if (idesc->name == NULL) { free(idesc, M_DEVBUF); return NULL; } strcpy(idesc->name, name); idesc->handler = handler; idesc->argument = arg; idesc->maskptr = maskptr; idesc->intr = irq; idesc->flags = flags; /* block this irq */ oldspl = splq(1 << irq); /* add irq to class selected by maskptr */ errcode = add_intrdesc(idesc); splx(oldspl); if (errcode != 0) { if (bootverbose) printf("\tintr_connect(irq%d) failed, result=%d\n", irq, errcode); free(idesc->name, M_DEVBUF); free(idesc, M_DEVBUF); idesc = NULL; } return (idesc); } /* * Deactivate and remove the interrupt handler descriptor data connected * created by an earlier call of intr_connect() from the linked list and * adjust theinterrupt masks if necessary. * * Return the memory held by the interrupt handler descriptor data structure * to the system. Make sure, the handler is not actively used anymore, before. */ int inthand_remove(intrec *idesc) { intrec **hook, *head; int irq; int errcode = 0; intrmask_t oldspl; if (idesc == NULL) return (-1); irq = idesc->intr; /* find pointer that keeps the reference to this interrupt descriptor */ hook = find_pred(idesc, irq); if (hook == NULL) return (-1); /* make copy of original list head, the line after may overwrite it */ head = intreclist_head[irq]; /* unlink: make predecessor point to idesc->next instead of to idesc */ *hook = idesc->next; /* now check whether the element we removed was the list head */ if (idesc == head) { oldspl = splq(1 << irq); /* check whether the new list head is the only element on list */ head = intreclist_head[irq]; if (head != NULL) { icu_unset(irq, intr_mux); if (head->next != NULL) { /* install the multiplex handler with new list head as argument */ errcode = icu_setup(irq, intr_mux, head, 0, 0); if (errcode == 0) update_intrname(irq, NULL); } else { /* install the one remaining handler for this irq */ errcode = icu_setup(irq, head->handler, head->argument, head->maskptr, head->flags); if (errcode == 0) update_intrname(irq, head->name); } } else { /* revert to old handler, eg: strayintr */ icu_unset(irq, idesc->handler); } splx(oldspl); } update_masks(idesc->maskptr, irq); free(idesc, M_DEVBUF); return (0); }