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

Cache object: 5fbb4df89f17a45bde8e5e5defa938bf


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