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         /* get a virtual pointer to the structure */
   95         sdh = (struct bios32_SDheader *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr);
   96         for (cv = (u_int8_t *)sdh, ck = 0, i = 0; i < (sdh->len * 16); i++) {
   97             ck += cv[i];
   98         }
   99         /* If checksum is OK, enable use of the entrypoint */
  100         if ((ck == 0) && (BIOS_START <= sdh->entry ) &&
  101             (sdh->entry < (BIOS_START + BIOS_SIZE))) {
  102             bios32_SDCI = BIOS_PADDRTOVADDR(sdh->entry);
  103             if (bootverbose) {
  104                 printf("bios32: Found BIOS32 Service Directory header at %p\n", sdh);
  105                 printf("bios32: Entry = 0x%x (%x)  Rev = %d  Len = %d\n", 
  106                        sdh->entry, bios32_SDCI, sdh->revision, sdh->len);
  107             }
  108 
  109             /* Allow user override of PCI BIOS search */
  110             if (((p = kern_getenv("machdep.bios.pci")) == NULL) || strcmp(p, "disable")) {
  111                 /* See if there's a PCI BIOS entrypoint here */
  112                 PCIbios.ident.id = 0x49435024;  /* PCI systems should have this */
  113                 if (!bios32_SDlookup(&PCIbios) && bootverbose)
  114                     printf("pcibios: PCI BIOS entry at 0x%x+0x%x\n", PCIbios.base, PCIbios.entry);
  115             }
  116             if (p != NULL)
  117                     freeenv(p);
  118         } else {
  119             printf("bios32: Bad BIOS32 Service Directory\n");
  120         }
  121     }
  122 
  123     /*
  124      * PnP BIOS
  125      *
  126      * Allow user override of PnP BIOS search
  127      */
  128     if ((((p = kern_getenv("machdep.bios.pnp")) == NULL) || strcmp(p, "disable")) &&
  129         ((sigaddr = bios_sigsearch(0, "$PnP", 4, 16, 0)) != 0)) {
  130         /* get a virtual pointer to the structure */
  131         pt = (struct PnPBIOS_table *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr);
  132         for (cv = (u_int8_t *)pt, ck = 0, i = 0; i < pt->len; i++) {
  133             ck += cv[i];
  134         }
  135         /* If checksum is OK, enable use of the entrypoint */
  136         if (ck == 0) {
  137             PnPBIOStable = pt;
  138             if (bootverbose) {
  139                 printf("pnpbios: Found PnP BIOS data at %p\n", pt);
  140                 printf("pnpbios: Entry = %x:%x  Rev = %d.%d\n", 
  141                        pt->pmentrybase, pt->pmentryoffset, pt->version >> 4, pt->version & 0xf);
  142                 if ((pt->control & 0x3) == 0x01)
  143                     printf("pnpbios: Event flag at %x\n", pt->evflagaddr);
  144                 if (pt->oemdevid != 0)
  145                     printf("pnpbios: OEM ID %x\n", pt->oemdevid);
  146                 
  147             }
  148         } else {
  149             printf("pnpbios: Bad PnP BIOS data checksum\n");
  150         }
  151     }
  152     if (p != NULL)
  153             freeenv(p);
  154     if (bootverbose) {
  155             /* look for other know signatures */
  156             printf("Other BIOS signatures found:\n");
  157     }
  158 }
  159 
  160 /*
  161  * bios32_SDlookup
  162  *
  163  * Query the BIOS32 Service Directory for the service named in (ent),
  164  * returns nonzero if the lookup fails.  The caller must fill in
  165  * (ent->ident), the remainder are populated on a successful lookup.
  166  */
  167 int
  168 bios32_SDlookup(struct bios32_SDentry *ent)
  169 {
  170     struct bios_regs args;
  171 
  172     if (bios32_SDCI == 0)
  173         return (1);
  174 
  175     args.eax = ent->ident.id;           /* set up arguments */
  176     args.ebx = args.ecx = args.edx = 0;
  177     bios32(&args, bios32_SDCI, GSEL(GCODE_SEL, SEL_KPL));
  178     if ((args.eax & 0xff) == 0) {       /* success? */
  179         ent->base = args.ebx;
  180         ent->len = args.ecx;
  181         ent->entry = args.edx;
  182         ent->ventry = BIOS_PADDRTOVADDR(ent->base + ent->entry);
  183         return (0);                     /* all OK */
  184     }
  185     return (1);                         /* failed */
  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         /* 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 u_long vm86phystk;
  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     void        *bios16_pmap_handle;
  328     arg_start = 0xffffffff;
  329     arg_end = 0;
  330 
  331     /*
  332      * Some BIOS entrypoints attempt to copy the largest-case
  333      * argument frame (in order to generalise handling for 
  334      * different entry types).  If our argument frame is 
  335      * smaller than this, the BIOS will reach off the top of
  336      * our constructed stack segment.  Pad the top of the stack
  337      * with some garbage to avoid this.
  338      */
  339     stack = (caddr_t)PAGE_SIZE - 32;
  340 
  341     va_start(ap, fmt);
  342     for (p = fmt; p && *p; p++) {
  343         switch (*p) {
  344         case 'p':                       /* 32-bit pointer */
  345             i = va_arg(ap, u_int);
  346             arg_start = min(arg_start, i);
  347             arg_end = max(arg_end, i);
  348             flags |= BIOSARGS_FLAG;
  349             stack -= 4;
  350             break;
  351 
  352         case 'i':                       /* 32-bit integer */
  353             i = va_arg(ap, u_int);
  354             stack -= 4;
  355             break;
  356 
  357         case 'U':                       /* 16-bit selector */
  358             flags |= BIOSUTIL_FLAG;
  359             /* FALLTHROUGH */
  360         case 'D':                       /* 16-bit selector */
  361         case 'C':                       /* 16-bit selector */
  362             stack -= 2;
  363             break;
  364             
  365         case 's':                       /* 16-bit integer passed as an int */
  366             i = va_arg(ap, int);
  367             stack -= 2;
  368             break;
  369 
  370         default:
  371             va_end(ap);
  372             return (EINVAL);
  373         }
  374     }
  375     va_end(ap);
  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 = pmap_pg_frame((u_int)&bios16_jmp);
  385     args->seg.code32.limit = 0xffff;    
  386 
  387     bios16_pmap_handle = pmap_bios16_enter();
  388 
  389     stack_top = stack;
  390     va_start(ap, fmt);
  391     for (p = fmt; p && *p; p++) {
  392         switch (*p) {
  393         case 'p':                       /* 32-bit pointer */
  394             i = va_arg(ap, u_int);
  395             *(u_int *)stack = (i - arg_start) |
  396                 (GSEL(GBIOSARGS_SEL, SEL_KPL) << 16);
  397             stack += 4;
  398             break;
  399 
  400         case 'i':                       /* 32-bit integer */
  401             i = va_arg(ap, u_int);
  402             *(u_int *)stack = i;
  403             stack += 4;
  404             break;
  405 
  406         case 'U':                       /* 16-bit selector */
  407             *(u_short *)stack = GSEL(GBIOSUTIL_SEL, SEL_KPL);
  408             stack += 2;
  409             break;
  410 
  411         case 'D':                       /* 16-bit selector */
  412             *(u_short *)stack = GSEL(GBIOSDATA_SEL, SEL_KPL);
  413             stack += 2;
  414             break;
  415 
  416         case 'C':                       /* 16-bit selector */
  417             *(u_short *)stack = GSEL(GBIOSCODE16_SEL, SEL_KPL);
  418             stack += 2;
  419             break;
  420 
  421         case 's':                       /* 16-bit integer passed as an int */
  422             i = va_arg(ap, int);
  423             *(u_short *)stack = i;
  424             stack += 2;
  425             break;
  426 
  427         default:
  428             va_end(ap);
  429             return (EINVAL);
  430         }
  431     }
  432     va_end(ap);
  433 
  434     set_bios_selectors(&args->seg, flags);
  435     bioscall_vector.vec16.offset = (u_short)args->entry;
  436     bioscall_vector.vec16.segment = GSEL(GBIOSCODE16_SEL, SEL_KPL);
  437 
  438     i = bios16_call(&args->r, stack_top);
  439     pmap_bios16_leave(bios16_pmap_handle);
  440     return (i);
  441 }
  442 
  443 int
  444 bios_oem_strings(struct bios_oem *oem, u_char *buffer, size_t maxlen)
  445 {
  446         size_t idx = 0;
  447         struct bios_oem_signature *sig;
  448         u_int from, to;
  449         u_char c, *s, *se, *str, *bios_str;
  450         size_t i, off, len, tot;
  451 
  452         if ( !oem || !buffer || maxlen<2 )
  453                 return(-1);
  454 
  455         sig = oem->signature;
  456         if (!sig)
  457                 return(-2);
  458 
  459         from = oem->range.from;
  460         to = oem->range.to;
  461         if ( (to<=from) || (from<BIOS_START) || (to>(BIOS_START+BIOS_SIZE)) )
  462                 return(-3);
  463 
  464         while (sig->anchor != NULL) {
  465                 str = sig->anchor;
  466                 len = strlen(str);
  467                 off = sig->offset;
  468                 tot = sig->totlen;
  469                 /* make sure offset doesn't go beyond bios area */
  470                 if ( (to+off)>(BIOS_START+BIOS_SIZE) ||
  471                                         ((from+off)<BIOS_START) ) {
  472                         printf("sys/i386/i386/bios.c: sig '%s' "
  473                                 "from 0x%0x to 0x%0x offset %d "
  474                                 "out of BIOS bounds 0x%0x - 0x%0x\n",
  475                                 str, from, to, off,
  476                                 BIOS_START, BIOS_START+BIOS_SIZE);
  477                         return(-4);
  478                 }
  479                 /* make sure we don't overrun return buffer */
  480                 if (idx + tot > maxlen - 1) {
  481                         printf("sys/i386/i386/bios.c: sig '%s' "
  482                                 "idx %d + tot %d = %d > maxlen-1 %d\n",
  483                                 str, idx, tot, idx+tot, maxlen-1);
  484                         return(-5);
  485                 }
  486                 bios_str = NULL;
  487                 s = (u_char *)BIOS_PADDRTOVADDR(from);
  488                 se = (u_char *)BIOS_PADDRTOVADDR(to-len);
  489                 for (; s<se; s++) {
  490                         if (!bcmp(str, s, len)) {
  491                                 bios_str = s;
  492                                 break;
  493                         }
  494                 }
  495                 /*
  496                 *  store pretty version of totlen bytes of bios string with
  497                 *  given offset; 0x20 - 0x7E are printable; uniquify spaces
  498                 */
  499                 if (bios_str) {
  500                         for (i=0; i<tot; i++) {
  501                                 c = bios_str[i+off];
  502                                 if ( (c < 0x20) || (c > 0x7E) )
  503                                         c = ' ';
  504                                 if (idx == 0) {
  505                                         if (c != ' ')
  506                                                 buffer[idx++] = c;
  507                                 } else if ( (c != ' ') ||
  508                                         ((c == ' ') && (buffer[idx-1] != ' ')) )
  509                                                 buffer[idx++] = c;
  510                         }
  511                 }
  512                 sig++;
  513         }
  514         /* remove a final trailing space */
  515         if ( (idx > 1) && (buffer[idx-1] == ' ') )
  516                 idx--;
  517         buffer[idx] = '\0';
  518         return (idx);
  519 }
  520 
  521 #ifdef DEV_ISA
  522 /*
  523  * PnP BIOS interface; enumerate devices only known to the system
  524  * BIOS and save information about them for later use.
  525  */
  526 
  527 struct pnp_sysdev 
  528 {
  529     u_int16_t   size;
  530     u_int8_t    handle;
  531     u_int32_t   devid;
  532     u_int8_t    type[3];
  533     u_int16_t   attrib;
  534 #define PNPATTR_NODISABLE       (1<<0)  /* can't be disabled */
  535 #define PNPATTR_NOCONFIG        (1<<1)  /* can't be configured */
  536 #define PNPATTR_OUTPUT          (1<<2)  /* can be primary output */
  537 #define PNPATTR_INPUT           (1<<3)  /* can be primary input */
  538 #define PNPATTR_BOOTABLE        (1<<4)  /* can be booted from */
  539 #define PNPATTR_DOCK            (1<<5)  /* is a docking station */
  540 #define PNPATTR_REMOVEABLE      (1<<6)  /* device is removeable */
  541 #define PNPATTR_CONFIG_STATIC   (0)
  542 #define PNPATTR_CONFIG_DYNAMIC  (1)
  543 #define PNPATTR_CONFIG_DYNONLY  (3)
  544 #define PNPATTR_CONFIG(a)       (((a) >> 7) & 0x3)
  545     /* device-specific data comes here */
  546     u_int8_t    devdata[0];
  547 } __packed;
  548 
  549 /* We have to cluster arguments within a 64k range for the bios16 call */
  550 struct pnp_sysdevargs
  551 {
  552     u_int16_t   next;
  553     struct pnp_sysdev node;
  554 };
  555 
  556 /*
  557  * This function is called after the bus has assigned resource
  558  * locations for a logical device.
  559  */
  560 static void
  561 pnpbios_set_config(void *arg, struct isa_config *config, int enable)
  562 {
  563 }
  564 
  565 /*
  566  * Quiz the PnP BIOS, build a list of PNP IDs and resource data.
  567  */
  568 static void
  569 pnpbios_identify(driver_t *driver, device_t parent)
  570 {
  571     struct PnPBIOS_table        *pt = PnPBIOStable;
  572     struct bios_args            args;
  573     struct pnp_sysdev           *pd;
  574     struct pnp_sysdevargs       *pda;
  575     u_int16_t                   ndevs, bigdev;
  576     int                         error, currdev;
  577     u_int8_t                    *devnodebuf, tag;
  578     u_int32_t                   *devid, *compid;
  579     int                         idx, left;
  580     device_t                    dev;
  581         
  582     /* no PnP BIOS information */
  583     if (pt == NULL)
  584         return;
  585 
  586     /* Check to see if ACPI is already active. */
  587     dev = devclass_get_device(devclass_find("acpi"), 0);
  588     if (dev != NULL && device_is_attached(dev)) 
  589         return;
  590 
  591     /* get count of PnP devices */
  592     bzero(&args, sizeof(args));
  593     args.seg.code16.base = BIOS_PADDRTOVADDR(pt->pmentrybase);
  594     args.seg.code16.limit = 0xffff;             /* XXX ? */
  595     args.seg.data.base = BIOS_PADDRTOVADDR(pt->pmdataseg);
  596     args.seg.data.limit = 0xffff;
  597     args.entry = pt->pmentryoffset;
  598     
  599     if ((error = bios16(&args, PNP_COUNT_DEVNODES, &ndevs, &bigdev)) || (args.r.eax & 0xff)) {
  600         printf("pnpbios: error %d/%x getting device count/size limit\n", error, args.r.eax);
  601         return;
  602     }
  603     ndevs &= 0xff;                              /* clear high byte garbage */
  604     if (bootverbose)
  605         printf("pnpbios: %d devices, largest %d bytes\n", ndevs, bigdev);
  606 
  607     devnodebuf = malloc(bigdev + (sizeof(struct pnp_sysdevargs) - sizeof(struct pnp_sysdev)),
  608                         M_DEVBUF, M_NOWAIT);
  609     if (devnodebuf == NULL) {
  610         printf("pnpbios: cannot allocate memory, bailing\n");
  611         return;
  612     }
  613     pda = (struct pnp_sysdevargs *)devnodebuf;
  614     pd = &pda->node;
  615 
  616     for (currdev = 0, left = ndevs; (currdev != 0xff) && (left > 0); left--) {
  617         bzero(pd, bigdev);
  618         pda->next = currdev;
  619         /* get current configuration */
  620         if ((error = bios16(&args, PNP_GET_DEVNODE, &pda->next, &pda->node, 1))) {
  621             printf("pnpbios: error %d making BIOS16 call\n", error);
  622             break;
  623         }
  624         if ((error = (args.r.eax & 0xff))) {
  625             if (bootverbose)
  626                 printf("pnpbios: %s 0x%x fetching node %d\n", error & 0x80 ? "error" : "warning", error, currdev);
  627             if (error & 0x80) 
  628                 break;
  629         }
  630         currdev = pda->next;
  631         if (pd->size < sizeof(struct pnp_sysdev)) {
  632             printf("pnpbios: bogus system node data, aborting scan\n");
  633             break;
  634         }
  635 
  636         /*
  637          * Ignore PICs so that we don't have to worry about the PICs
  638          * claiming IRQs to prevent their use.  The PIC drivers
  639          * already ensure that invalid IRQs are not used.
  640          */
  641         if (!strcmp(pnp_eisaformat(pd->devid), "PNP0000"))      /* ISA PIC */
  642             continue;
  643         if (!strcmp(pnp_eisaformat(pd->devid), "PNP0003"))      /* APIC */
  644             continue;
  645 
  646         /* Add the device and parse its resources */
  647         dev = BUS_ADD_CHILD(parent, ISA_ORDER_PNPBIOS, NULL, -1);
  648         isa_set_vendorid(dev, pd->devid);
  649         isa_set_logicalid(dev, pd->devid);
  650         /*
  651          * It appears that some PnP BIOS doesn't allow us to re-enable
  652          * the embedded system device once it is disabled.  We shall
  653          * mark all system device nodes as "cannot be disabled", regardless
  654          * of actual settings in the device attribute byte.
  655          * XXX
  656         isa_set_configattr(dev, 
  657             ((pd->attrib & PNPATTR_NODISABLE) ?  0 : ISACFGATTR_CANDISABLE) |
  658             ((!(pd->attrib & PNPATTR_NOCONFIG) && 
  659                 PNPATTR_CONFIG(pd->attrib) != PNPATTR_CONFIG_STATIC)
  660                 ? ISACFGATTR_DYNAMIC : 0));
  661          */
  662         isa_set_configattr(dev, 
  663             (!(pd->attrib & PNPATTR_NOCONFIG) && 
  664                 PNPATTR_CONFIG(pd->attrib) != PNPATTR_CONFIG_STATIC)
  665                 ? ISACFGATTR_DYNAMIC : 0);
  666         isa_set_pnpbios_handle(dev, pd->handle);
  667         ISA_SET_CONFIG_CALLBACK(parent, dev, pnpbios_set_config, 0);
  668         pnp_parse_resources(dev, &pd->devdata[0],
  669             pd->size - sizeof(struct pnp_sysdev), 0);
  670         if (!device_get_desc(dev))
  671             device_set_desc_copy(dev, pnp_eisaformat(pd->devid));
  672 
  673         /* Find device IDs */
  674         devid = &pd->devid;
  675         compid = NULL;
  676 
  677         /* look for a compatible device ID too */
  678         left = pd->size - sizeof(struct pnp_sysdev);
  679         idx = 0;
  680         while (idx < left) {
  681             tag = pd->devdata[idx++];
  682             if (PNP_RES_TYPE(tag) == 0) {
  683                 /* Small resource */
  684                 switch (PNP_SRES_NUM(tag)) {
  685                 case PNP_TAG_COMPAT_DEVICE:
  686                     compid = (u_int32_t *)(pd->devdata + idx);
  687                     if (bootverbose)
  688                         printf("pnpbios: node %d compat ID 0x%08x\n", pd->handle, *compid);
  689                     /* FALLTHROUGH */
  690                 case PNP_TAG_END:
  691                     idx = left;
  692                     break;
  693                 default:
  694                     idx += PNP_SRES_LEN(tag);
  695                     break;
  696                 }
  697             } else
  698                 /* Large resource, skip it */
  699                 idx += *(u_int16_t *)(pd->devdata + idx) + 2;
  700         }
  701         if (bootverbose) {
  702             printf("pnpbios: handle %d device ID %s (%08x)", 
  703                    pd->handle, pnp_eisaformat(*devid), *devid);
  704             if (compid != NULL)
  705                 printf(" compat ID %s (%08x)",
  706                        pnp_eisaformat(*compid), *compid);
  707             printf("\n");
  708         }
  709     }
  710 }
  711 
  712 static device_method_t pnpbios_methods[] = {
  713         /* Device interface */
  714         DEVMETHOD(device_identify,      pnpbios_identify),
  715         { 0, 0 }
  716 };
  717 
  718 static driver_t pnpbios_driver = {
  719         "pnpbios",
  720         pnpbios_methods,
  721         1,                      /* no softc */
  722 };
  723 
  724 static devclass_t pnpbios_devclass;
  725 
  726 DRIVER_MODULE(pnpbios, isa, pnpbios_driver, pnpbios_devclass, 0, 0);
  727 #endif /* DEV_ISA */

Cache object: a113083c0454930cc63adf7b393517d6


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