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 
   28 #include <sys/cdefs.h>
   29 __FBSDID("$FreeBSD: releng/5.2/sys/i386/i386/bios.c 121995 2003-11-03 22:22:04Z jhb $");
   30 
   31 /*
   32  * Code for dealing with the BIOS in x86 PC systems.
   33  */
   34 
   35 #include "opt_isa.h"
   36 
   37 #include <sys/param.h>
   38 #include <sys/systm.h>
   39 #include <sys/kernel.h>
   40 #include <sys/malloc.h>
   41 #include <sys/bus.h>
   42 #include <sys/pcpu.h>
   43 #include <vm/vm.h>
   44 #include <vm/pmap.h>
   45 #include <machine/md_var.h>
   46 #include <machine/segments.h>
   47 #include <machine/stdarg.h>
   48 #include <machine/vmparam.h>
   49 #include <machine/pc/bios.h>
   50 #ifdef DEV_ISA
   51 #include <isa/isavar.h>
   52 #include <isa/pnpreg.h>
   53 #include <isa/pnpvar.h>
   54 #endif
   55 
   56 #define BIOS_START      0xe0000
   57 #define BIOS_SIZE       0x20000
   58 
   59 /* exported lookup results */
   60 struct bios32_SDentry           PCIbios;
   61 struct PnPBIOS_table            *PnPBIOStable;
   62 
   63 static u_int                    bios32_SDCI;
   64 
   65 /* start fairly early */
   66 static void                     bios32_init(void *junk);
   67 SYSINIT(bios32, SI_SUB_CPU, SI_ORDER_ANY, bios32_init, NULL);
   68 
   69 /*
   70  * bios32_init
   71  *
   72  * Locate various bios32 entities.
   73  */
   74 static void
   75 bios32_init(void *junk)
   76 {
   77     u_long                      sigaddr;
   78     struct bios32_SDheader      *sdh;
   79     struct PnPBIOS_table        *pt;
   80     u_int8_t                    ck, *cv;
   81     int                         i;
   82     char                        *p;
   83     
   84     /*
   85      * BIOS32 Service Directory, PCI BIOS
   86      */
   87     
   88     /* look for the signature */
   89     if ((sigaddr = bios_sigsearch(0, "_32_", 4, 16, 0)) != 0) {
   90 
   91         /* get a virtual pointer to the structure */
   92         sdh = (struct bios32_SDheader *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr);
   93         for (cv = (u_int8_t *)sdh, ck = 0, i = 0; i < (sdh->len * 16); i++) {
   94             ck += cv[i];
   95         }
   96         /* If checksum is OK, enable use of the entrypoint */
   97         if ((ck == 0) && (BIOS_START <= sdh->entry ) &&
   98             (sdh->entry < (BIOS_START + BIOS_SIZE))) {
   99             bios32_SDCI = BIOS_PADDRTOVADDR(sdh->entry);
  100             if (bootverbose) {
  101                 printf("bios32: Found BIOS32 Service Directory header at %p\n", sdh);
  102                 printf("bios32: Entry = 0x%x (%x)  Rev = %d  Len = %d\n", 
  103                        sdh->entry, bios32_SDCI, sdh->revision, sdh->len);
  104             }
  105 
  106             /* Allow user override of PCI BIOS search */
  107             if (((p = getenv("machdep.bios.pci")) == NULL) || strcmp(p, "disable")) {
  108 
  109                 /* See if there's a PCI BIOS entrypoint here */
  110                 PCIbios.ident.id = 0x49435024;  /* PCI systems should have this */
  111                 if (!bios32_SDlookup(&PCIbios) && bootverbose)
  112                     printf("pcibios: PCI BIOS entry at 0x%x+0x%x\n", PCIbios.base, PCIbios.entry);
  113             }
  114             if (p != NULL)
  115                     freeenv(p);
  116         } else {
  117             printf("bios32: Bad BIOS32 Service Directory\n");
  118         }
  119     }
  120 
  121     /*
  122      * PnP BIOS
  123      *
  124      * Allow user override of PnP BIOS search
  125      */
  126     if ((((p = getenv("machdep.bios.pnp")) == NULL) || strcmp(p, "disable")) &&
  127         ((sigaddr = bios_sigsearch(0, "$PnP", 4, 16, 0)) != 0)) {
  128 
  129         /* get a virtual pointer to the structure */
  130         pt = (struct PnPBIOS_table *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr);
  131         for (cv = (u_int8_t *)pt, ck = 0, i = 0; i < pt->len; i++) {
  132             ck += cv[i];
  133         }
  134         /* If checksum is OK, enable use of the entrypoint */
  135         if (ck == 0) {
  136             PnPBIOStable = pt;
  137             if (bootverbose) {
  138                 printf("pnpbios: Found PnP BIOS data at %p\n", pt);
  139                 printf("pnpbios: Entry = %x:%x  Rev = %d.%d\n", 
  140                        pt->pmentrybase, pt->pmentryoffset, pt->version >> 4, pt->version & 0xf);
  141                 if ((pt->control & 0x3) == 0x01)
  142                     printf("pnpbios: Event flag at %x\n", pt->evflagaddr);
  143                 if (pt->oemdevid != 0)
  144                     printf("pnpbios: OEM ID %x\n", pt->oemdevid);
  145                 
  146             }
  147         } else {
  148             printf("pnpbios: Bad PnP BIOS data checksum\n");
  149         }
  150     }
  151     if (p != NULL)
  152             freeenv(p);
  153     if (bootverbose) {
  154             /* look for other know signatures */
  155             printf("Other BIOS signatures found:\n");
  156     }
  157 }
  158 
  159 /*
  160  * bios32_SDlookup
  161  *
  162  * Query the BIOS32 Service Directory for the service named in (ent),
  163  * returns nonzero if the lookup fails.  The caller must fill in
  164  * (ent->ident), the remainder are populated on a successful lookup.
  165  */
  166 int
  167 bios32_SDlookup(struct bios32_SDentry *ent)
  168 {
  169     struct bios_regs args;
  170 
  171     if (bios32_SDCI == 0)
  172         return (1);
  173 
  174     args.eax = ent->ident.id;           /* set up arguments */
  175     args.ebx = args.ecx = args.edx = 0;
  176     bios32(&args, bios32_SDCI, GSEL(GCODE_SEL, SEL_KPL));
  177     if ((args.eax & 0xff) == 0) {       /* success? */
  178         ent->base = args.ebx;
  179         ent->len = args.ecx;
  180         ent->entry = args.edx;
  181         ent->ventry = BIOS_PADDRTOVADDR(ent->base + ent->entry);
  182         return (0);                     /* all OK */
  183     }
  184     return (1);                         /* failed */
  185 }
  186 
  187 
  188 /*
  189  * bios_sigsearch
  190  *
  191  * Search some or all of the BIOS region for a signature string.
  192  *
  193  * (start)      Optional offset returned from this function 
  194  *              (for searching for multiple matches), or NULL
  195  *              to start the search from the base of the BIOS.
  196  *              Note that this will be a _physical_ address in
  197  *              the range 0xe0000 - 0xfffff.
  198  * (sig)        is a pointer to the byte(s) of the signature.
  199  * (siglen)     number of bytes in the signature.
  200  * (paralen)    signature paragraph (alignment) size.
  201  * (sigofs)     offset of the signature within the paragraph.
  202  *
  203  * Returns the _physical_ address of the found signature, 0 if the
  204  * signature was not found.
  205  */
  206 
  207 u_int32_t
  208 bios_sigsearch(u_int32_t start, u_char *sig, int siglen, int paralen, int sigofs)
  209 {
  210     u_char      *sp, *end;
  211     
  212     /* compute the starting address */
  213     if ((start >= BIOS_START) && (start <= (BIOS_START + BIOS_SIZE))) {
  214         sp = (char *)BIOS_PADDRTOVADDR(start);
  215     } else if (start == 0) {
  216         sp = (char *)BIOS_PADDRTOVADDR(BIOS_START);
  217     } else {
  218         return 0;                               /* bogus start address */
  219     }
  220 
  221     /* compute the end address */
  222     end = (u_char *)BIOS_PADDRTOVADDR(BIOS_START + BIOS_SIZE);
  223 
  224     /* loop searching */
  225     while ((sp + sigofs + siglen) < end) {
  226         
  227         /* compare here */
  228         if (!bcmp(sp + sigofs, sig, siglen)) {
  229             /* convert back to physical address */
  230             return((u_int32_t)BIOS_VADDRTOPADDR(sp));
  231         }
  232         sp += paralen;
  233     }
  234     return(0);
  235 }
  236 
  237 /*
  238  * do not staticize, used by bioscall.s
  239  */
  240 union {
  241     struct {
  242         u_short offset;
  243         u_short segment;
  244     } vec16;
  245     struct {
  246         u_int   offset;
  247         u_short segment;
  248     } vec32;
  249 } bioscall_vector;                      /* bios jump vector */
  250 
  251 void
  252 set_bios_selectors(struct bios_segments *seg, int flags)
  253 {
  254     struct soft_segment_descriptor ssd = {
  255         0,                      /* segment base address (overwritten) */
  256         0,                      /* length (overwritten) */
  257         SDT_MEMERA,             /* segment type (overwritten) */
  258         0,                      /* priority level */
  259         1,                      /* descriptor present */
  260         0, 0,
  261         1,                      /* descriptor size (overwritten) */
  262         0                       /* granularity == byte units */
  263     };
  264     union descriptor *p_gdt;
  265 
  266 #ifdef SMP
  267     p_gdt = &gdt[PCPU_GET(cpuid) * NGDT];
  268 #else
  269     p_gdt = gdt;
  270 #endif
  271         
  272     ssd.ssd_base = seg->code32.base;
  273     ssd.ssd_limit = seg->code32.limit;
  274     ssdtosd(&ssd, &p_gdt[GBIOSCODE32_SEL].sd);
  275 
  276     ssd.ssd_def32 = 0;
  277     if (flags & BIOSCODE_FLAG) {
  278         ssd.ssd_base = seg->code16.base;
  279         ssd.ssd_limit = seg->code16.limit;
  280         ssdtosd(&ssd, &p_gdt[GBIOSCODE16_SEL].sd);
  281     }
  282 
  283     ssd.ssd_type = SDT_MEMRWA;
  284     if (flags & BIOSDATA_FLAG) {
  285         ssd.ssd_base = seg->data.base;
  286         ssd.ssd_limit = seg->data.limit;
  287         ssdtosd(&ssd, &p_gdt[GBIOSDATA_SEL].sd);
  288     }
  289 
  290     if (flags & BIOSUTIL_FLAG) {
  291         ssd.ssd_base = seg->util.base;
  292         ssd.ssd_limit = seg->util.limit;
  293         ssdtosd(&ssd, &p_gdt[GBIOSUTIL_SEL].sd);
  294     }
  295 
  296     if (flags & BIOSARGS_FLAG) {
  297         ssd.ssd_base = seg->args.base;
  298         ssd.ssd_limit = seg->args.limit;
  299         ssdtosd(&ssd, &p_gdt[GBIOSARGS_SEL].sd);
  300     }
  301 }
  302 
  303 extern int vm86pa;
  304 extern void bios16_jmp(void);
  305 
  306 /*
  307  * this routine is really greedy with selectors, and uses 5:
  308  *
  309  * 32-bit code selector:        to return to kernel
  310  * 16-bit code selector:        for running code
  311  *        data selector:        for 16-bit data
  312  *        util selector:        extra utility selector
  313  *        args selector:        to handle pointers
  314  *
  315  * the util selector is set from the util16 entry in bios16_args, if a
  316  * "U" specifier is seen.
  317  *
  318  * See <machine/pc/bios.h> for description of format specifiers
  319  */
  320 int
  321 bios16(struct bios_args *args, char *fmt, ...)
  322 {
  323     char        *p, *stack, *stack_top;
  324     va_list     ap;
  325     int         flags = BIOSCODE_FLAG | BIOSDATA_FLAG;
  326     u_int       i, arg_start, arg_end;
  327     pt_entry_t  *pte;
  328     pd_entry_t  *ptd;
  329 
  330     arg_start = 0xffffffff;
  331     arg_end = 0;
  332 
  333     /*
  334      * Some BIOS entrypoints attempt to copy the largest-case
  335      * argument frame (in order to generalise handling for 
  336      * different entry types).  If our argument frame is 
  337      * smaller than this, the BIOS will reach off the top of
  338      * our constructed stack segment.  Pad the top of the stack
  339      * with some garbage to avoid this.
  340      */
  341     stack = (caddr_t)PAGE_SIZE - 32;
  342 
  343     va_start(ap, fmt);
  344     for (p = fmt; p && *p; p++) {
  345         switch (*p) {
  346         case 'p':                       /* 32-bit pointer */
  347             i = va_arg(ap, u_int);
  348             arg_start = min(arg_start, i);
  349             arg_end = max(arg_end, i);
  350             flags |= BIOSARGS_FLAG;
  351             stack -= 4;
  352             break;
  353 
  354         case 'i':                       /* 32-bit integer */
  355             i = va_arg(ap, u_int);
  356             stack -= 4;
  357             break;
  358 
  359         case 'U':                       /* 16-bit selector */
  360             flags |= BIOSUTIL_FLAG;
  361             /* FALLTHROUGH */
  362         case 'D':                       /* 16-bit selector */
  363         case 'C':                       /* 16-bit selector */
  364             stack -= 2;
  365             break;
  366             
  367         case 's':                       /* 16-bit integer passed as an int */
  368             i = va_arg(ap, int);
  369             stack -= 2;
  370             break;
  371 
  372         default:
  373             return (EINVAL);
  374         }
  375     }
  376 
  377     if (flags & BIOSARGS_FLAG) {
  378         if (arg_end - arg_start > ctob(16))
  379             return (EACCES);
  380         args->seg.args.base = arg_start;
  381         args->seg.args.limit = 0xffff;
  382     }
  383 
  384     args->seg.code32.base = (u_int)&bios16_jmp & PG_FRAME;
  385     args->seg.code32.limit = 0xffff;    
  386 
  387     ptd = (pd_entry_t *)rcr3();
  388 #ifdef PAE
  389     if (ptd == IdlePDPT)
  390 #else
  391     if (ptd == IdlePTD)
  392 #endif
  393     {
  394         /*
  395          * no page table, so create one and install it.
  396          */
  397         pte = (pt_entry_t *)malloc(PAGE_SIZE, M_TEMP, M_WAITOK);
  398         ptd = (pd_entry_t *)((u_int)IdlePTD + KERNBASE);
  399         *pte = (vm86pa - PAGE_SIZE) | PG_RW | PG_V;
  400         *ptd = vtophys(pte) | PG_RW | PG_V;
  401     } else {
  402         /*
  403          * this is a user-level page table 
  404          */
  405         pte = PTmap;
  406         *pte = (vm86pa - PAGE_SIZE) | PG_RW | PG_V;
  407     }
  408     pmap_invalidate_all(kernel_pmap);   /* XXX insurance for now */
  409 
  410     stack_top = stack;
  411     va_start(ap, fmt);
  412     for (p = fmt; p && *p; p++) {
  413         switch (*p) {
  414         case 'p':                       /* 32-bit pointer */
  415             i = va_arg(ap, u_int);
  416             *(u_int *)stack = (i - arg_start) |
  417                 (GSEL(GBIOSARGS_SEL, SEL_KPL) << 16);
  418             stack += 4;
  419             break;
  420 
  421         case 'i':                       /* 32-bit integer */
  422             i = va_arg(ap, u_int);
  423             *(u_int *)stack = i;
  424             stack += 4;
  425             break;
  426 
  427         case 'U':                       /* 16-bit selector */
  428             *(u_short *)stack = GSEL(GBIOSUTIL_SEL, SEL_KPL);
  429             stack += 2;
  430             break;
  431 
  432         case 'D':                       /* 16-bit selector */
  433             *(u_short *)stack = GSEL(GBIOSDATA_SEL, SEL_KPL);
  434             stack += 2;
  435             break;
  436 
  437         case 'C':                       /* 16-bit selector */
  438             *(u_short *)stack = GSEL(GBIOSCODE16_SEL, SEL_KPL);
  439             stack += 2;
  440             break;
  441 
  442         case 's':                       /* 16-bit integer passed as an int */
  443             i = va_arg(ap, int);
  444             *(u_short *)stack = i;
  445             stack += 2;
  446             break;
  447 
  448         default:
  449             return (EINVAL);
  450         }
  451     }
  452 
  453     set_bios_selectors(&args->seg, flags);
  454     bioscall_vector.vec16.offset = (u_short)args->entry;
  455     bioscall_vector.vec16.segment = GSEL(GBIOSCODE16_SEL, SEL_KPL);
  456 
  457     i = bios16_call(&args->r, stack_top);
  458 
  459     if (pte == PTmap) {
  460         *pte = 0;                       /* remove entry */
  461         /*
  462          * XXX only needs to be invlpg(0) but that doesn't work on the 386 
  463          */
  464         pmap_invalidate_all(kernel_pmap);
  465     } else {
  466         *ptd = 0;                       /* remove page table */
  467         /*
  468          * XXX only needs to be invlpg(0) but that doesn't work on the 386 
  469          */
  470         pmap_invalidate_all(kernel_pmap);
  471         free(pte, M_TEMP);              /* ... and free it */
  472     }
  473     return (i);
  474 }
  475 
  476 #ifdef DEV_ISA
  477 /*
  478  * PnP BIOS interface; enumerate devices only known to the system
  479  * BIOS and save information about them for later use.
  480  */
  481 
  482 struct pnp_sysdev 
  483 {
  484     u_int16_t   size;
  485     u_int8_t    handle;
  486     u_int32_t   devid;
  487     u_int8_t    type[3];
  488     u_int16_t   attrib;
  489 #define PNPATTR_NODISABLE       (1<<0)  /* can't be disabled */
  490 #define PNPATTR_NOCONFIG        (1<<1)  /* can't be configured */
  491 #define PNPATTR_OUTPUT          (1<<2)  /* can be primary output */
  492 #define PNPATTR_INPUT           (1<<3)  /* can be primary input */
  493 #define PNPATTR_BOOTABLE        (1<<4)  /* can be booted from */
  494 #define PNPATTR_DOCK            (1<<5)  /* is a docking station */
  495 #define PNPATTR_REMOVEABLE      (1<<6)  /* device is removeable */
  496 #define PNPATTR_CONFIG_STATIC   (0)
  497 #define PNPATTR_CONFIG_DYNAMIC  (1)
  498 #define PNPATTR_CONFIG_DYNONLY  (3)
  499 #define PNPATTR_CONFIG(a)       (((a) >> 7) & 0x3)
  500     /* device-specific data comes here */
  501     u_int8_t    devdata[0];
  502 } __packed;
  503 
  504 /* We have to cluster arguments within a 64k range for the bios16 call */
  505 struct pnp_sysdevargs
  506 {
  507     u_int16_t   next;
  508     struct pnp_sysdev node;
  509 };
  510 
  511 /*
  512  * This function is called after the bus has assigned resource
  513  * locations for a logical device.
  514  */
  515 static void
  516 pnpbios_set_config(void *arg, struct isa_config *config, int enable)
  517 {
  518 }
  519 
  520 /*
  521  * Quiz the PnP BIOS, build a list of PNP IDs and resource data.
  522  */
  523 static void
  524 pnpbios_identify(driver_t *driver, device_t parent)
  525 {
  526     struct PnPBIOS_table        *pt = PnPBIOStable;
  527     struct bios_args            args;
  528     struct pnp_sysdev           *pd;
  529     struct pnp_sysdevargs       *pda;
  530     u_int16_t                   ndevs, bigdev;
  531     int                         error, currdev;
  532     u_int8_t                    *devnodebuf, tag;
  533     u_int32_t                   *devid, *compid;
  534     int                         idx, left;
  535     device_t                    dev;
  536         
  537     /* no PnP BIOS information */
  538     if (pt == NULL)
  539         return;
  540 
  541     /* ACPI already active */
  542     if (devclass_get_softc(devclass_find("ACPI"), 0) != NULL)
  543         return;
  544 
  545     /* get count of PnP devices */
  546     bzero(&args, sizeof(args));
  547     args.seg.code16.base = BIOS_PADDRTOVADDR(pt->pmentrybase);
  548     args.seg.code16.limit = 0xffff;             /* XXX ? */
  549     args.seg.data.base = BIOS_PADDRTOVADDR(pt->pmdataseg);
  550     args.seg.data.limit = 0xffff;
  551     args.entry = pt->pmentryoffset;
  552     
  553     if ((error = bios16(&args, PNP_COUNT_DEVNODES, &ndevs, &bigdev)) || (args.r.eax & 0xff))
  554         printf("pnpbios: error %d/%x getting device count/size limit\n", error, args.r.eax);
  555     ndevs &= 0xff;                              /* clear high byte garbage */
  556     if (bootverbose)
  557         printf("pnpbios: %d devices, largest %d bytes\n", ndevs, bigdev);
  558 
  559     devnodebuf = malloc(bigdev + (sizeof(struct pnp_sysdevargs) - sizeof(struct pnp_sysdev)),
  560                         M_DEVBUF, M_NOWAIT);
  561     pda = (struct pnp_sysdevargs *)devnodebuf;
  562     pd = &pda->node;
  563 
  564     for (currdev = 0, left = ndevs; (currdev != 0xff) && (left > 0); left--) {
  565 
  566         bzero(pd, bigdev);
  567         pda->next = currdev;
  568         /* get current configuration */
  569         if ((error = bios16(&args, PNP_GET_DEVNODE, &pda->next, &pda->node, 1))) {
  570             printf("pnpbios: error %d making BIOS16 call\n", error);
  571             break;
  572         }
  573         if ((error = (args.r.eax & 0xff))) {
  574             if (bootverbose)
  575                 printf("pnpbios: %s 0x%x fetching node %d\n", error & 0x80 ? "error" : "warning", error, currdev);
  576             if (error & 0x80) 
  577                 break;
  578         }
  579         currdev = pda->next;
  580         if (pd->size < sizeof(struct pnp_sysdev)) {
  581             printf("pnpbios: bogus system node data, aborting scan\n");
  582             break;
  583         }
  584 
  585         /*
  586          * Ignore PICs so that we don't have to worry about the PICs
  587          * claiming IRQs to prevent their use.  The PIC drivers
  588          * already ensure that invalid IRQs are not used.
  589          */
  590         if (!strcmp(pnp_eisaformat(pd->devid), "PNP0000"))      /* ISA PIC */
  591             continue;
  592         if (!strcmp(pnp_eisaformat(pd->devid), "PNP0003"))      /* APIC */
  593             continue;
  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: 421af979d31cff7a32b212b8dd323286


[ 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.