The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/i386/i386/bios.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 1997 Michael Smith
    3  * Copyright (c) 1998 Jonathan Lemon
    4  * All rights reserved.
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   25  * SUCH DAMAGE.
   26  *
   27  * $FreeBSD: releng/5.0/sys/i386/i386/bios.c 103870 2002-09-23 18:54:32Z alfred $
   28  */
   29 
   30 /*
   31  * Code for dealing with the BIOS in x86 PC systems.
   32  */
   33 
   34 #include "opt_isa.h"
   35 
   36 #include <sys/param.h>
   37 #include <sys/systm.h>
   38 #include <sys/kernel.h>
   39 #include <sys/malloc.h>
   40 #include <sys/bus.h>
   41 #include <sys/pcpu.h>
   42 #include <vm/vm.h>
   43 #include <vm/pmap.h>
   44 #include <machine/md_var.h>
   45 #include <machine/segments.h>
   46 #include <machine/stdarg.h>
   47 #include <machine/vmparam.h>
   48 #include <machine/pc/bios.h>
   49 #ifdef DEV_ISA
   50 #include <isa/isavar.h>
   51 #include <isa/pnpreg.h>
   52 #include <isa/pnpvar.h>
   53 #endif
   54 
   55 #define BIOS_START      0xe0000
   56 #define BIOS_SIZE       0x20000
   57 
   58 /* exported lookup results */
   59 struct bios32_SDentry           PCIbios;
   60 struct PnPBIOS_table            *PnPBIOStable;
   61 
   62 static u_int                    bios32_SDCI;
   63 
   64 /* start fairly early */
   65 static void                     bios32_init(void *junk);
   66 SYSINIT(bios32, SI_SUB_CPU, SI_ORDER_ANY, bios32_init, NULL);
   67 
   68 /*
   69  * bios32_init
   70  *
   71  * Locate various bios32 entities.
   72  */
   73 static void
   74 bios32_init(void *junk)
   75 {
   76     u_long                      sigaddr;
   77     struct bios32_SDheader      *sdh;
   78     struct PnPBIOS_table        *pt;
   79     u_int8_t                    ck, *cv;
   80     int                         i;
   81     char                        *p;
   82     
   83     /*
   84      * BIOS32 Service Directory, PCI BIOS
   85      */
   86     
   87     /* look for the signature */
   88     if ((sigaddr = bios_sigsearch(0, "_32_", 4, 16, 0)) != 0) {
   89 
   90         /* get a virtual pointer to the structure */
   91         sdh = (struct bios32_SDheader *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr);
   92         for (cv = (u_int8_t *)sdh, ck = 0, i = 0; i < (sdh->len * 16); i++) {
   93             ck += cv[i];
   94         }
   95         /* If checksum is OK, enable use of the entrypoint */
   96         if ((ck == 0) && (BIOS_START <= sdh->entry ) &&
   97             (sdh->entry < (BIOS_START + BIOS_SIZE))) {
   98             bios32_SDCI = BIOS_PADDRTOVADDR(sdh->entry);
   99             if (bootverbose) {
  100                 printf("bios32: Found BIOS32 Service Directory header at %p\n", sdh);
  101                 printf("bios32: Entry = 0x%x (%x)  Rev = %d  Len = %d\n", 
  102                        sdh->entry, bios32_SDCI, sdh->revision, sdh->len);
  103             }
  104 
  105             /* Allow user override of PCI BIOS search */
  106             if (((p = getenv("machdep.bios.pci")) == NULL) || strcmp(p, "disable")) {
  107 
  108                 /* See if there's a PCI BIOS entrypoint here */
  109                 PCIbios.ident.id = 0x49435024;  /* PCI systems should have this */
  110                 if (!bios32_SDlookup(&PCIbios) && bootverbose)
  111                     printf("pcibios: PCI BIOS entry at 0x%x+0x%x\n", PCIbios.base, PCIbios.entry);
  112             }
  113             if (p != NULL)
  114                     freeenv(p);
  115         } else {
  116             printf("bios32: Bad BIOS32 Service Directory\n");
  117         }
  118     }
  119 
  120     /*
  121      * PnP BIOS
  122      *
  123      * Allow user override of PnP BIOS search
  124      */
  125     if ((((p = getenv("machdep.bios.pnp")) == NULL) || strcmp(p, "disable")) &&
  126         ((sigaddr = bios_sigsearch(0, "$PnP", 4, 16, 0)) != 0)) {
  127 
  128         /* get a virtual pointer to the structure */
  129         pt = (struct PnPBIOS_table *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr);
  130         for (cv = (u_int8_t *)pt, ck = 0, i = 0; i < pt->len; i++) {
  131             ck += cv[i];
  132         }
  133         /* If checksum is OK, enable use of the entrypoint */
  134         if (ck == 0) {
  135             PnPBIOStable = pt;
  136             if (bootverbose) {
  137                 printf("pnpbios: Found PnP BIOS data at %p\n", pt);
  138                 printf("pnpbios: Entry = %x:%x  Rev = %d.%d\n", 
  139                        pt->pmentrybase, pt->pmentryoffset, pt->version >> 4, pt->version & 0xf);
  140                 if ((pt->control & 0x3) == 0x01)
  141                     printf("pnpbios: Event flag at %x\n", pt->evflagaddr);
  142                 if (pt->oemdevid != 0)
  143                     printf("pnpbios: OEM ID %x\n", pt->oemdevid);
  144                 
  145             }
  146         } else {
  147             printf("pnpbios: Bad PnP BIOS data checksum\n");
  148         }
  149     }
  150     if (p != NULL)
  151             freeenv(p);
  152     if (bootverbose) {
  153             /* look for other know signatures */
  154             printf("Other BIOS signatures found:\n");
  155     }
  156 }
  157 
  158 /*
  159  * bios32_SDlookup
  160  *
  161  * Query the BIOS32 Service Directory for the service named in (ent),
  162  * returns nonzero if the lookup fails.  The caller must fill in
  163  * (ent->ident), the remainder are populated on a successful lookup.
  164  */
  165 int
  166 bios32_SDlookup(struct bios32_SDentry *ent)
  167 {
  168     struct bios_regs args;
  169 
  170     if (bios32_SDCI == 0)
  171         return (1);
  172 
  173     args.eax = ent->ident.id;           /* set up arguments */
  174     args.ebx = args.ecx = args.edx = 0;
  175     bios32(&args, bios32_SDCI, GSEL(GCODE_SEL, SEL_KPL));
  176     if ((args.eax & 0xff) == 0) {       /* success? */
  177         ent->base = args.ebx;
  178         ent->len = args.ecx;
  179         ent->entry = args.edx;
  180         ent->ventry = BIOS_PADDRTOVADDR(ent->base + ent->entry);
  181         return (0);                     /* all OK */
  182     }
  183     return (1);                         /* failed */
  184 }
  185 
  186 
  187 /*
  188  * bios_sigsearch
  189  *
  190  * Search some or all of the BIOS region for a signature string.
  191  *
  192  * (start)      Optional offset returned from this function 
  193  *              (for searching for multiple matches), or NULL
  194  *              to start the search from the base of the BIOS.
  195  *              Note that this will be a _physical_ address in
  196  *              the range 0xe0000 - 0xfffff.
  197  * (sig)        is a pointer to the byte(s) of the signature.
  198  * (siglen)     number of bytes in the signature.
  199  * (paralen)    signature paragraph (alignment) size.
  200  * (sigofs)     offset of the signature within the paragraph.
  201  *
  202  * Returns the _physical_ address of the found signature, 0 if the
  203  * signature was not found.
  204  */
  205 
  206 u_int32_t
  207 bios_sigsearch(u_int32_t start, u_char *sig, int siglen, int paralen, int sigofs)
  208 {
  209     u_char      *sp, *end;
  210     
  211     /* compute the starting address */
  212     if ((start >= BIOS_START) && (start <= (BIOS_START + BIOS_SIZE))) {
  213         sp = (char *)BIOS_PADDRTOVADDR(start);
  214     } else if (start == 0) {
  215         sp = (char *)BIOS_PADDRTOVADDR(BIOS_START);
  216     } else {
  217         return 0;                               /* bogus start address */
  218     }
  219 
  220     /* compute the end address */
  221     end = (u_char *)BIOS_PADDRTOVADDR(BIOS_START + BIOS_SIZE);
  222 
  223     /* loop searching */
  224     while ((sp + sigofs + siglen) < end) {
  225         
  226         /* compare here */
  227         if (!bcmp(sp + sigofs, sig, siglen)) {
  228             /* convert back to physical address */
  229             return((u_int32_t)BIOS_VADDRTOPADDR(sp));
  230         }
  231         sp += paralen;
  232     }
  233     return(0);
  234 }
  235 
  236 /*
  237  * do not staticize, used by bioscall.s
  238  */
  239 union {
  240     struct {
  241         u_short offset;
  242         u_short segment;
  243     } vec16;
  244     struct {
  245         u_int   offset;
  246         u_short segment;
  247     } vec32;
  248 } bioscall_vector;                      /* bios jump vector */
  249 
  250 void
  251 set_bios_selectors(struct bios_segments *seg, int flags)
  252 {
  253     struct soft_segment_descriptor ssd = {
  254         0,                      /* segment base address (overwritten) */
  255         0,                      /* length (overwritten) */
  256         SDT_MEMERA,             /* segment type (overwritten) */
  257         0,                      /* priority level */
  258         1,                      /* descriptor present */
  259         0, 0,
  260         1,                      /* descriptor size (overwritten) */
  261         0                       /* granularity == byte units */
  262     };
  263     union descriptor *p_gdt;
  264 
  265 #ifdef SMP
  266     p_gdt = &gdt[PCPU_GET(cpuid) * NGDT];
  267 #else
  268     p_gdt = gdt;
  269 #endif
  270         
  271     ssd.ssd_base = seg->code32.base;
  272     ssd.ssd_limit = seg->code32.limit;
  273     ssdtosd(&ssd, &p_gdt[GBIOSCODE32_SEL].sd);
  274 
  275     ssd.ssd_def32 = 0;
  276     if (flags & BIOSCODE_FLAG) {
  277         ssd.ssd_base = seg->code16.base;
  278         ssd.ssd_limit = seg->code16.limit;
  279         ssdtosd(&ssd, &p_gdt[GBIOSCODE16_SEL].sd);
  280     }
  281 
  282     ssd.ssd_type = SDT_MEMRWA;
  283     if (flags & BIOSDATA_FLAG) {
  284         ssd.ssd_base = seg->data.base;
  285         ssd.ssd_limit = seg->data.limit;
  286         ssdtosd(&ssd, &p_gdt[GBIOSDATA_SEL].sd);
  287     }
  288 
  289     if (flags & BIOSUTIL_FLAG) {
  290         ssd.ssd_base = seg->util.base;
  291         ssd.ssd_limit = seg->util.limit;
  292         ssdtosd(&ssd, &p_gdt[GBIOSUTIL_SEL].sd);
  293     }
  294 
  295     if (flags & BIOSARGS_FLAG) {
  296         ssd.ssd_base = seg->args.base;
  297         ssd.ssd_limit = seg->args.limit;
  298         ssdtosd(&ssd, &p_gdt[GBIOSARGS_SEL].sd);
  299     }
  300 }
  301 
  302 extern int vm86pa;
  303 extern void bios16_jmp(void);
  304 
  305 /*
  306  * this routine is really greedy with selectors, and uses 5:
  307  *
  308  * 32-bit code selector:        to return to kernel
  309  * 16-bit code selector:        for running code
  310  *        data selector:        for 16-bit data
  311  *        util selector:        extra utility selector
  312  *        args selector:        to handle pointers
  313  *
  314  * the util selector is set from the util16 entry in bios16_args, if a
  315  * "U" specifier is seen.
  316  *
  317  * See <machine/pc/bios.h> for description of format specifiers
  318  */
  319 int
  320 bios16(struct bios_args *args, char *fmt, ...)
  321 {
  322     char        *p, *stack, *stack_top;
  323     va_list     ap;
  324     int         flags = BIOSCODE_FLAG | BIOSDATA_FLAG;
  325     u_int       i, arg_start, arg_end;
  326     pt_entry_t  *pte;
  327     pd_entry_t  *ptd;
  328 
  329     arg_start = 0xffffffff;
  330     arg_end = 0;
  331 
  332     /*
  333      * Some BIOS entrypoints attempt to copy the largest-case
  334      * argument frame (in order to generalise handling for 
  335      * different entry types).  If our argument frame is 
  336      * smaller than this, the BIOS will reach off the top of
  337      * our constructed stack segment.  Pad the top of the stack
  338      * with some garbage to avoid this.
  339      */
  340     stack = (caddr_t)PAGE_SIZE - 32;
  341 
  342     va_start(ap, fmt);
  343     for (p = fmt; p && *p; p++) {
  344         switch (*p) {
  345         case 'p':                       /* 32-bit pointer */
  346             i = va_arg(ap, u_int);
  347             arg_start = min(arg_start, i);
  348             arg_end = max(arg_end, i);
  349             flags |= BIOSARGS_FLAG;
  350             stack -= 4;
  351             break;
  352 
  353         case 'i':                       /* 32-bit integer */
  354             i = va_arg(ap, u_int);
  355             stack -= 4;
  356             break;
  357 
  358         case 'U':                       /* 16-bit selector */
  359             flags |= BIOSUTIL_FLAG;
  360             /* FALLTHROUGH */
  361         case 'D':                       /* 16-bit selector */
  362         case 'C':                       /* 16-bit selector */
  363             stack -= 2;
  364             break;
  365             
  366         case 's':                       /* 16-bit integer passed as an int */
  367             i = va_arg(ap, int);
  368             stack -= 2;
  369             break;
  370 
  371         default:
  372             return (EINVAL);
  373         }
  374     }
  375 
  376     if (flags & BIOSARGS_FLAG) {
  377         if (arg_end - arg_start > ctob(16))
  378             return (EACCES);
  379         args->seg.args.base = arg_start;
  380         args->seg.args.limit = 0xffff;
  381     }
  382 
  383     args->seg.code32.base = (u_int)&bios16_jmp & PG_FRAME;
  384     args->seg.code32.limit = 0xffff;    
  385 
  386     ptd = (pd_entry_t *)rcr3();
  387     if (ptd == (u_int *)IdlePTD) {
  388         /*
  389          * no page table, so create one and install it.
  390          */
  391         pte = (pt_entry_t *)malloc(PAGE_SIZE, M_TEMP, M_WAITOK);
  392         ptd = (pd_entry_t *)((u_int)ptd + KERNBASE);
  393         *ptd = vtophys(pte) | PG_RW | PG_V;
  394     } else {
  395         /*
  396          * this is a user-level page table 
  397          */
  398         pte = PTmap;
  399     }
  400     /*
  401      * install pointer to page 0.  we don't need to flush the tlb,
  402      * since there should not be a previous mapping for page 0.
  403      */
  404     *pte = (vm86pa - PAGE_SIZE) | PG_RW | PG_V; 
  405 
  406     stack_top = stack;
  407     va_start(ap, fmt);
  408     for (p = fmt; p && *p; p++) {
  409         switch (*p) {
  410         case 'p':                       /* 32-bit pointer */
  411             i = va_arg(ap, u_int);
  412             *(u_int *)stack = (i - arg_start) |
  413                 (GSEL(GBIOSARGS_SEL, SEL_KPL) << 16);
  414             stack += 4;
  415             break;
  416 
  417         case 'i':                       /* 32-bit integer */
  418             i = va_arg(ap, u_int);
  419             *(u_int *)stack = i;
  420             stack += 4;
  421             break;
  422 
  423         case 'U':                       /* 16-bit selector */
  424             *(u_short *)stack = GSEL(GBIOSUTIL_SEL, SEL_KPL);
  425             stack += 2;
  426             break;
  427 
  428         case 'D':                       /* 16-bit selector */
  429             *(u_short *)stack = GSEL(GBIOSDATA_SEL, SEL_KPL);
  430             stack += 2;
  431             break;
  432 
  433         case 'C':                       /* 16-bit selector */
  434             *(u_short *)stack = GSEL(GBIOSCODE16_SEL, SEL_KPL);
  435             stack += 2;
  436             break;
  437 
  438         case 's':                       /* 16-bit integer passed as an int */
  439             i = va_arg(ap, int);
  440             *(u_short *)stack = i;
  441             stack += 2;
  442             break;
  443 
  444         default:
  445             return (EINVAL);
  446         }
  447     }
  448 
  449     set_bios_selectors(&args->seg, flags);
  450     bioscall_vector.vec16.offset = (u_short)args->entry;
  451     bioscall_vector.vec16.segment = GSEL(GBIOSCODE16_SEL, SEL_KPL);
  452 
  453     i = bios16_call(&args->r, stack_top);
  454     
  455     if (pte == PTmap) {
  456         *pte = 0;                       /* remove entry */
  457     } else {
  458         *ptd = 0;                       /* remove page table */
  459         free(pte, M_TEMP);              /* ... and free it */
  460     }
  461 
  462     /*
  463      * XXX only needs to be invlpg(0) but that doesn't work on the 386 
  464      */
  465     pmap_invalidate_all(kernel_pmap);
  466 
  467     return (i);
  468 }
  469 
  470 #ifdef DEV_ISA
  471 /*
  472  * PnP BIOS interface; enumerate devices only known to the system
  473  * BIOS and save information about them for later use.
  474  */
  475 
  476 struct pnp_sysdev 
  477 {
  478     u_int16_t   size;
  479     u_int8_t    handle;
  480     u_int32_t   devid;
  481     u_int8_t    type[3];
  482     u_int16_t   attrib;
  483 #define PNPATTR_NODISABLE       (1<<0)  /* can't be disabled */
  484 #define PNPATTR_NOCONFIG        (1<<1)  /* can't be configured */
  485 #define PNPATTR_OUTPUT          (1<<2)  /* can be primary output */
  486 #define PNPATTR_INPUT           (1<<3)  /* can be primary input */
  487 #define PNPATTR_BOOTABLE        (1<<4)  /* can be booted from */
  488 #define PNPATTR_DOCK            (1<<5)  /* is a docking station */
  489 #define PNPATTR_REMOVEABLE      (1<<6)  /* device is removeable */
  490 #define PNPATTR_CONFIG_STATIC   (0)
  491 #define PNPATTR_CONFIG_DYNAMIC  (1)
  492 #define PNPATTR_CONFIG_DYNONLY  (3)
  493 #define PNPATTR_CONFIG(a)       (((a) >> 7) & 0x3)
  494     /* device-specific data comes here */
  495     u_int8_t    devdata[0];
  496 } __packed;
  497 
  498 /* We have to cluster arguments within a 64k range for the bios16 call */
  499 struct pnp_sysdevargs
  500 {
  501     u_int16_t   next;
  502     struct pnp_sysdev node;
  503 };
  504 
  505 /*
  506  * This function is called after the bus has assigned resource
  507  * locations for a logical device.
  508  */
  509 static void
  510 pnpbios_set_config(void *arg, struct isa_config *config, int enable)
  511 {
  512 }
  513 
  514 /*
  515  * Quiz the PnP BIOS, build a list of PNP IDs and resource data.
  516  */
  517 static void
  518 pnpbios_identify(driver_t *driver, device_t parent)
  519 {
  520     struct PnPBIOS_table        *pt = PnPBIOStable;
  521     struct bios_args            args;
  522     struct pnp_sysdev           *pd;
  523     struct pnp_sysdevargs       *pda;
  524     u_int16_t                   ndevs, bigdev;
  525     int                         error, currdev;
  526     u_int8_t                    *devnodebuf, tag;
  527     u_int32_t                   *devid, *compid;
  528     int                         idx, left;
  529     device_t                    dev;
  530         
  531     /* no PnP BIOS information */
  532     if (pt == NULL)
  533         return;
  534 
  535     /* ACPI already active */
  536     if (devclass_get_softc(devclass_find("ACPI"), 0) != NULL)
  537         return;
  538 
  539     /* get count of PnP devices */
  540     bzero(&args, sizeof(args));
  541     args.seg.code16.base = BIOS_PADDRTOVADDR(pt->pmentrybase);
  542     args.seg.code16.limit = 0xffff;             /* XXX ? */
  543     args.seg.data.base = BIOS_PADDRTOVADDR(pt->pmdataseg);
  544     args.seg.data.limit = 0xffff;
  545     args.entry = pt->pmentryoffset;
  546     
  547     if ((error = bios16(&args, PNP_COUNT_DEVNODES, &ndevs, &bigdev)) || (args.r.eax & 0xff))
  548         printf("pnpbios: error %d/%x getting device count/size limit\n", error, args.r.eax);
  549     ndevs &= 0xff;                              /* clear high byte garbage */
  550     if (bootverbose)
  551         printf("pnpbios: %d devices, largest %d bytes\n", ndevs, bigdev);
  552 
  553     devnodebuf = malloc(bigdev + (sizeof(struct pnp_sysdevargs) - sizeof(struct pnp_sysdev)),
  554                         M_DEVBUF, M_NOWAIT);
  555     pda = (struct pnp_sysdevargs *)devnodebuf;
  556     pd = &pda->node;
  557 
  558     for (currdev = 0, left = ndevs; (currdev != 0xff) && (left > 0); left--) {
  559 
  560         bzero(pd, bigdev);
  561         pda->next = currdev;
  562         /* get current configuration */
  563         if ((error = bios16(&args, PNP_GET_DEVNODE, &pda->next, &pda->node, 1))) {
  564             printf("pnpbios: error %d making BIOS16 call\n", error);
  565             break;
  566         }
  567         if ((error = (args.r.eax & 0xff))) {
  568             if (bootverbose)
  569                 printf("pnpbios: %s 0x%x fetching node %d\n", error & 0x80 ? "error" : "warning", error, currdev);
  570             if (error & 0x80) 
  571                 break;
  572         }
  573         currdev = pda->next;
  574         if (pd->size < sizeof(struct pnp_sysdev)) {
  575             printf("pnpbios: bogus system node data, aborting scan\n");
  576             break;
  577         }
  578 
  579         /*
  580          * If we are in APIC_IO mode, we should ignore the ISA PIC if it
  581          * shows up.  Likewise, in !APIC_IO mode, we should ignore the
  582          * APIC (less important).
  583          * This is significant because the ISA PIC will claim IRQ 2 (which
  584          * it uses for chaining), while in APIC mode this is a valid IRQ
  585          * available for general use.
  586          */
  587 #ifdef APIC_IO
  588         if (!strcmp(pnp_eisaformat(pd->devid), "PNP0000"))      /* ISA PIC */
  589             continue;
  590 #else
  591         if (!strcmp(pnp_eisaformat(pd->devid), "PNP0003"))      /* APIC */
  592             continue;
  593 #endif  
  594         
  595         /* Add the device and parse its resources */
  596         dev = BUS_ADD_CHILD(parent, ISA_ORDER_PNP, NULL, -1);
  597         isa_set_vendorid(dev, pd->devid);
  598         isa_set_logicalid(dev, pd->devid);
  599         /*
  600          * It appears that some PnP BIOS doesn't allow us to re-enable
  601          * the embedded system device once it is disabled.  We shall
  602          * mark all system device nodes as "cannot be disabled", regardless
  603          * of actual settings in the device attribute byte.
  604          * XXX
  605         isa_set_configattr(dev, 
  606             ((pd->attrib & PNPATTR_NODISABLE) ?  0 : ISACFGATTR_CANDISABLE) |
  607             ((!(pd->attrib & PNPATTR_NOCONFIG) && 
  608                 PNPATTR_CONFIG(pd->attrib) != PNPATTR_CONFIG_STATIC)
  609                 ? ISACFGATTR_DYNAMIC : 0));
  610          */
  611         isa_set_configattr(dev, 
  612             (!(pd->attrib & PNPATTR_NOCONFIG) && 
  613                 PNPATTR_CONFIG(pd->attrib) != PNPATTR_CONFIG_STATIC)
  614                 ? ISACFGATTR_DYNAMIC : 0);
  615         ISA_SET_CONFIG_CALLBACK(parent, dev, pnpbios_set_config, 0);
  616         pnp_parse_resources(dev, &pd->devdata[0],
  617                             pd->size - sizeof(struct pnp_sysdev), 0);
  618         if (!device_get_desc(dev))
  619             device_set_desc_copy(dev, pnp_eisaformat(pd->devid));
  620 
  621         /* Find device IDs */
  622         devid = &pd->devid;
  623         compid = NULL;
  624 
  625         /* look for a compatible device ID too */
  626         left = pd->size - sizeof(struct pnp_sysdev);
  627         idx = 0;
  628         while (idx < left) {
  629             tag = pd->devdata[idx++];
  630             if (PNP_RES_TYPE(tag) == 0) {
  631                 /* Small resource */
  632                 switch (PNP_SRES_NUM(tag)) {
  633                 case PNP_TAG_COMPAT_DEVICE:
  634                     compid = (u_int32_t *)(pd->devdata + idx);
  635                     if (bootverbose)
  636                         printf("pnpbios: node %d compat ID 0x%08x\n", pd->handle, *compid);
  637                     /* FALLTHROUGH */
  638                 case PNP_TAG_END:
  639                     idx = left;
  640                     break;
  641                 default:
  642                     idx += PNP_SRES_LEN(tag);
  643                     break;
  644                 }
  645             } else
  646                 /* Large resource, skip it */
  647                 idx += *(u_int16_t *)(pd->devdata + idx) + 2;
  648         }
  649         if (bootverbose) {
  650             printf("pnpbios: handle %d device ID %s (%08x)", 
  651                    pd->handle, pnp_eisaformat(*devid), *devid);
  652             if (compid != NULL)
  653                 printf(" compat ID %s (%08x)",
  654                        pnp_eisaformat(*compid), *compid);
  655             printf("\n");
  656         }
  657     }
  658 }
  659 
  660 static device_method_t pnpbios_methods[] = {
  661         /* Device interface */
  662         DEVMETHOD(device_identify,      pnpbios_identify),
  663 
  664         { 0, 0 }
  665 };
  666 
  667 static driver_t pnpbios_driver = {
  668         "pnpbios",
  669         pnpbios_methods,
  670         1,                      /* no softc */
  671 };
  672 
  673 static devclass_t pnpbios_devclass;
  674 
  675 DRIVER_MODULE(pnpbios, isa, pnpbios_driver, pnpbios_devclass, 0, 0);
  676 #endif /* DEV_ISA */

Cache object: aea23f2a6a94eeb92d29fee14482d3c1


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.