FreeBSD/Linux Kernel Cross Reference
sys/i386/i386/bios.c
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
|