Annotation of src/sys/arch/x86/x86/x86_autoconf.c, Revision 1.36
1.36 ! jmcneill 1: /* $NetBSD: x86_autoconf.c,v 1.35 2008/10/14 15:48:44 tsutsui Exp $ */
1.1 thorpej 2:
3: /*-
4: * Copyright (c) 1990 The Regents of the University of California.
5: * All rights reserved.
6: *
7: * This code is derived from software contributed to Berkeley by
8: * William Jolitz.
9: *
10: * Redistribution and use in source and binary forms, with or without
11: * modification, are permitted provided that the following conditions
12: * are met:
13: * 1. Redistributions of source code must retain the above copyright
14: * notice, this list of conditions and the following disclaimer.
15: * 2. Redistributions in binary form must reproduce the above copyright
16: * notice, this list of conditions and the following disclaimer in the
17: * documentation and/or other materials provided with the distribution.
18: * 3. Neither the name of the University nor the names of its contributors
19: * may be used to endorse or promote products derived from this software
20: * without specific prior written permission.
21: *
22: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32: * SUCH DAMAGE.
33: *
34: * @(#)autoconf.c 7.1 (Berkeley) 5/9/91
35: */
36:
37: #include <sys/cdefs.h>
1.36 ! jmcneill 38: __KERNEL_RCSID(0, "$NetBSD: x86_autoconf.c,v 1.35 2008/10/14 15:48:44 tsutsui Exp $");
1.1 thorpej 39:
40: #include <sys/param.h>
41: #include <sys/systm.h>
42: #include <sys/device.h>
43: #include <sys/disklabel.h>
44: #include <sys/conf.h>
45: #include <sys/malloc.h>
46: #include <sys/vnode.h>
47: #include <sys/fcntl.h>
1.2 thorpej 48: #include <sys/disk.h>
1.1 thorpej 49: #include <sys/proc.h>
1.2 thorpej 50: #include <sys/md5.h>
1.16 elad 51: #include <sys/kauth.h>
1.1 thorpej 52:
53: #include <machine/bootinfo.h>
1.36 ! jmcneill 54: #include <machine/pio.h>
1.1 thorpej 55:
56: #include "pci.h"
57:
58: #include <dev/isa/isavar.h>
59: #if NPCI > 0
60: #include <dev/pci/pcivar.h>
61: #endif
1.36 ! jmcneill 62: #include <dev/wsfb/genfbvar.h>
! 63: #include <dev/ic/vgareg.h>
! 64:
! 65: struct genfb_colormap_callback gfb_cb;
1.1 thorpej 66:
67: struct disklist *x86_alldisks;
68: int x86_ndisks;
69:
1.18 christos 70: static void
1.36 ! jmcneill 71: x86_genfb_set_mapreg(void *opaque, int index, int r, int g, int b)
! 72: {
! 73: outb(0x3c0 + VGA_DAC_ADDRW, index);
! 74: outb(0x3c0 + VGA_DAC_PALETTE, (uint8_t)r >> 2);
! 75: outb(0x3c0 + VGA_DAC_PALETTE, (uint8_t)g >> 2);
! 76: outb(0x3c0 + VGA_DAC_PALETTE, (uint8_t)b >> 2);
! 77: }
! 78:
! 79: static void
1.18 christos 80: handle_wedges(struct device *dv, int par)
81: {
1.26 dyoung 82: if (config_handle_wedges(dv, par) == 0)
83: return;
1.18 christos 84: booted_device = dv;
85: booted_partition = par;
86: }
87:
1.1 thorpej 88: static int
89: is_valid_disk(struct device *dv)
90: {
91:
1.9 thorpej 92: if (device_class(dv) != DV_DISK)
1.1 thorpej 93: return (0);
94:
1.17 christos 95: return (device_is_a(dv, "dk") ||
96: device_is_a(dv, "sd") ||
1.13 thorpej 97: device_is_a(dv, "wd") ||
98: device_is_a(dv, "ld") ||
99: device_is_a(dv, "ed"));
1.1 thorpej 100: }
101:
102: /*
103: * XXX Ugly bit of code. But, this is the only safe time that the
104: * match between BIOS disks and native disks can be done.
105: */
1.8 jmmv 106: static void
107: matchbiosdisks(void)
1.1 thorpej 108: {
109: struct btinfo_biosgeom *big;
110: struct bi_biosgeom_entry *be;
111: struct device *dv;
112: int i, ck, error, m, n;
113: struct vnode *tv;
114: char mbr[DEV_BSIZE];
115: int dklist_size;
1.18 christos 116: int numbig;
1.1 thorpej 117:
118: big = lookup_bootinfo(BTINFO_BIOSGEOM);
119:
120: numbig = big ? big->num : 0;
121:
122: /* First, count all native disks. */
1.27 dyoung 123: TAILQ_FOREACH(dv, &alldevs, dv_list) {
1.1 thorpej 124: if (is_valid_disk(dv))
125: x86_ndisks++;
126: }
127:
128: dklist_size = sizeof(struct disklist) + (x86_ndisks - 1) *
129: sizeof(struct nativedisk_info);
130:
131: /* XXX M_TEMP is wrong */
132: x86_alldisks = malloc(dklist_size, M_TEMP, M_NOWAIT | M_ZERO);
133: if (x86_alldisks == NULL)
134: return;
135:
136: x86_alldisks->dl_nnativedisks = x86_ndisks;
137: x86_alldisks->dl_nbiosdisks = numbig;
138: for (i = 0; i < numbig; i++) {
139: x86_alldisks->dl_biosdisks[i].bi_dev = big->disk[i].dev;
140: x86_alldisks->dl_biosdisks[i].bi_sec = big->disk[i].sec;
141: x86_alldisks->dl_biosdisks[i].bi_head = big->disk[i].head;
142: x86_alldisks->dl_biosdisks[i].bi_cyl = big->disk[i].cyl;
143: x86_alldisks->dl_biosdisks[i].bi_lbasecs = big->disk[i].totsec;
144: x86_alldisks->dl_biosdisks[i].bi_flags = big->disk[i].flags;
145: #ifdef GEOM_DEBUG
146: #ifdef notyet
147: printf("disk %x: flags %x, interface %x, device %llx\n",
148: big->disk[i].dev, big->disk[i].flags,
149: big->disk[i].interface_path, big->disk[i].device_path);
150: #endif
151: #endif
152: }
153:
154: /* XXX Code duplication from findroot(). */
155: n = -1;
1.29 xtraeme 156: TAILQ_FOREACH(dv, &alldevs, dv_list) {
1.9 thorpej 157: if (device_class(dv) != DV_DISK)
1.1 thorpej 158: continue;
159: #ifdef GEOM_DEBUG
160: printf("matchbiosdisks: trying to match (%s) %s\n",
1.34 cegger 161: device_xname(dv), device_cfdata(dv)->cf_name);
1.1 thorpej 162: #endif
163: if (is_valid_disk(dv)) {
164: n++;
165: /* XXXJRT why not just dv_xname?? */
166: snprintf(x86_alldisks->dl_nativedisks[n].ni_devname,
167: sizeof(x86_alldisks->dl_nativedisks[n].ni_devname),
1.34 cegger 168: "%s", device_xname(dv));
1.1 thorpej 169:
1.18 christos 170: if ((tv = opendisk(dv)) == NULL)
171: continue;
1.1 thorpej 172:
173: error = vn_rdwr(UIO_READ, tv, mbr, DEV_BSIZE, 0,
174: UIO_SYSSPACE, 0, NOCRED, NULL, NULL);
1.31 pooka 175: VOP_CLOSE(tv, FREAD, NOCRED);
1.1 thorpej 176: if (error) {
177: #ifdef GEOM_DEBUG
178: printf("matchbiosdisks: %s: MBR read failure\n",
1.34 cegger 179: device_xname(dv));
1.1 thorpej 180: #endif
181: continue;
182: }
183:
184: for (ck = i = 0; i < DEV_BSIZE; i++)
185: ck += mbr[i];
186: for (m = i = 0; i < numbig; i++) {
187: be = &big->disk[i];
188: #ifdef GEOM_DEBUG
189: printf("match %s with %d "
1.34 cegger 190: "dev ck %x bios ck %x\n", device_xname(dv), i,
1.1 thorpej 191: ck, be->cksum);
192: #endif
193: if (be->flags & BI_GEOM_INVALID)
194: continue;
195: if (be->cksum == ck &&
196: memcmp(&mbr[MBR_PART_OFFSET], be->dosparts,
197: MBR_PART_COUNT *
198: sizeof(struct mbr_partition)) == 0) {
199: #ifdef GEOM_DEBUG
200: printf("matched BIOS disk %x with %s\n",
1.34 cegger 201: be->dev, device_xname(dv));
1.1 thorpej 202: #endif
203: x86_alldisks->dl_nativedisks[n].
204: ni_biosmatches[m++] = i;
205: }
206: }
207: x86_alldisks->dl_nativedisks[n].ni_nmatches = m;
208: vput(tv);
209: }
210: }
211: }
212:
213: /*
214: * Helper function for findroot():
1.2 thorpej 215: * Return non-zero if wedge device matches bootinfo.
216: */
217: static int
218: match_bootwedge(struct device *dv, struct btinfo_bootwedge *biw)
219: {
220: MD5_CTX ctx;
221: struct vnode *tmpvn;
222: int error;
1.4 christos 223: uint8_t bf[DEV_BSIZE];
1.2 thorpej 224: uint8_t hash[16];
225: int found = 0;
226: daddr_t blk;
227: uint64_t nblks;
228:
229: /*
230: * If the boot loader didn't specify the sector, abort.
231: */
232: if (biw->matchblk == -1)
233: return (0);
234:
1.19 christos 235: if ((tmpvn = opendisk(dv)) == NULL)
236: return 0;
237:
1.2 thorpej 238: MD5Init(&ctx);
239: for (blk = biw->matchblk, nblks = biw->matchnblks;
240: nblks != 0; nblks--, blk++) {
1.25 christos 241: error = vn_rdwr(UIO_READ, tmpvn, (void *) bf,
1.4 christos 242: sizeof(bf), blk * DEV_BSIZE, UIO_SYSSPACE,
1.2 thorpej 243: 0, NOCRED, NULL, NULL);
244: if (error) {
245: printf("findroot: unable to read block %" PRIu64 "\n",
246: blk);
247: goto closeout;
248: }
1.4 christos 249: MD5Update(&ctx, bf, sizeof(bf));
1.2 thorpej 250: }
251: MD5Final(hash, &ctx);
252:
253: /* Compare with the provided hash. */
254: found = memcmp(biw->matchhash, hash, sizeof(hash)) == 0;
255:
256: closeout:
1.31 pooka 257: VOP_CLOSE(tmpvn, FREAD, NOCRED);
1.2 thorpej 258: vput(tmpvn);
259: return (found);
260: }
261:
262: /*
263: * Helper function for findroot():
1.1 thorpej 264: * Return non-zero if disk device matches bootinfo.
265: */
266: static int
267: match_bootdisk(struct device *dv, struct btinfo_bootdisk *bid)
268: {
269: struct vnode *tmpvn;
270: int error;
271: struct disklabel label;
272: int found = 0;
1.18 christos 273:
274: if (device_is_a(dv, "dk"))
275: return 0;
1.1 thorpej 276:
277: /*
278: * A disklabel is required here. The boot loader doesn't refuse
279: * to boot from a disk without a label, but this is normally not
280: * wanted.
281: */
282: if (bid->labelsector == -1)
283: return (0);
284:
1.18 christos 285: if ((tmpvn = opendisk(dv)) == NULL)
286: return 0;
1.2 thorpej 287:
1.31 pooka 288: error = VOP_IOCTL(tmpvn, DIOCGDINFO, &label, FREAD, NOCRED);
1.1 thorpej 289: if (error) {
290: /*
291: * XXX Can't happen -- open() would have errored out
292: * or faked one up.
293: */
1.3 xtraeme 294: printf("findroot: can't get label for dev %s (%d)\n",
1.34 cegger 295: device_xname(dv), error);
1.1 thorpej 296: goto closeout;
297: }
298:
299: /* Compare with our data. */
300: if (label.d_type == bid->label.type &&
301: label.d_checksum == bid->label.checksum &&
302: strncmp(label.d_packname, bid->label.packname, 16) == 0)
1.18 christos 303: found = 1;
1.1 thorpej 304:
305: closeout:
1.31 pooka 306: VOP_CLOSE(tmpvn, FREAD, NOCRED);
1.1 thorpej 307: vput(tmpvn);
308: return (found);
309: }
310:
311: /*
312: * Attempt to find the device from which we were booted. If we can do so,
313: * and not instructed not to do so, change rootdev to correspond to the
314: * load device.
315: */
316: static void
317: findroot(void)
318: {
1.6 jmmv 319: struct btinfo_rootdevice *biv;
1.1 thorpej 320: struct btinfo_bootdisk *bid;
1.2 thorpej 321: struct btinfo_bootwedge *biw;
1.35 tsutsui 322: struct btinfo_biosgeom *big;
1.32 joerg 323: device_t dv;
1.1 thorpej 324:
325: if (booted_device)
326: return;
327:
328: if (lookup_bootinfo(BTINFO_NETIF) != NULL) {
329: /*
330: * We got netboot interface information, but device_register()
331: * failed to match it to a configured device. Boot disk
332: * information cannot be present at the same time, so give
333: * up.
334: */
335: printf("findroot: netboot interface not found.\n");
336: return;
337: }
338:
1.6 jmmv 339: if ((biv = lookup_bootinfo(BTINFO_ROOTDEVICE)) != NULL) {
1.27 dyoung 340: TAILQ_FOREACH(dv, &alldevs, dv_list) {
1.6 jmmv 341: struct cfdata *cd;
342: size_t len;
343:
1.9 thorpej 344: if (device_class(dv) != DV_DISK)
1.6 jmmv 345: continue;
346:
1.15 thorpej 347: cd = device_cfdata(dv);
1.6 jmmv 348: len = strlen(cd->cf_name);
349:
350: if (strncmp(cd->cf_name, biv->devname, len) == 0 &&
351: biv->devname[len] - '0' == cd->cf_unit) {
1.18 christos 352: handle_wedges(dv, biv->devname[len + 1] - 'a');
1.6 jmmv 353: return;
354: }
355: }
356: }
357:
1.2 thorpej 358: if ((biw = lookup_bootinfo(BTINFO_BOOTWEDGE)) != NULL) {
359: /*
360: * Scan all disk devices for ones that match the passed data.
361: * Don't break if one is found, to get possible multiple
362: * matches - for problem tracking. Use the first match anyway
363: * because lower devices numbers are more likely to be the
364: * boot device.
365: */
1.27 dyoung 366: TAILQ_FOREACH(dv, &alldevs, dv_list) {
1.9 thorpej 367: if (device_class(dv) != DV_DISK)
1.2 thorpej 368: continue;
369:
370: if (is_valid_disk(dv)) {
371: /*
372: * Don't trust BIOS device numbers, try
373: * to match the information passed by the
374: * boot loader instead.
375: */
376: if ((biw->biosdev & 0x80) == 0 ||
377: match_bootwedge(dv, biw) == 0)
378: continue;
379: goto bootwedge_found;
380: }
381:
382: continue;
383: bootwedge_found:
384: if (booted_device) {
385: printf("WARNING: double match for boot "
386: "device (%s, %s)\n",
1.34 cegger 387: device_xname(booted_device),
388: device_xname(dv));
1.2 thorpej 389: continue;
390: }
391: dkwedge_set_bootwedge(dv, biw->startblk, biw->nblks);
392: }
393:
394: if (booted_wedge)
395: return;
396: }
397:
398: if ((bid = lookup_bootinfo(BTINFO_BOOTDISK)) != NULL) {
1.1 thorpej 399: /*
400: * Scan all disk devices for ones that match the passed data.
401: * Don't break if one is found, to get possible multiple
402: * matches - for problem tracking. Use the first match anyway
403: * because lower device numbers are more likely to be the
404: * boot device.
405: */
1.27 dyoung 406: TAILQ_FOREACH(dv, &alldevs, dv_list) {
1.9 thorpej 407: if (device_class(dv) != DV_DISK)
1.1 thorpej 408: continue;
409:
1.12 thorpej 410: if (device_is_a(dv, "fd")) {
1.1 thorpej 411: /*
412: * Assume the configured unit number matches
413: * the BIOS device number. (This is the old
414: * behavior.) Needs some ideas how to handle
415: * the BIOS's "swap floppy drive" options.
416: */
1.14 thorpej 417: /* XXX device_unit() abuse */
1.1 thorpej 418: if ((bid->biosdev & 0x80) != 0 ||
1.14 thorpej 419: device_unit(dv) != bid->biosdev)
1.1 thorpej 420: continue;
1.2 thorpej 421: goto bootdisk_found;
1.1 thorpej 422: }
423:
424: if (is_valid_disk(dv)) {
425: /*
426: * Don't trust BIOS device numbers, try
427: * to match the information passed by the
428: * boot loader instead.
429: */
430: if ((bid->biosdev & 0x80) == 0 ||
431: match_bootdisk(dv, bid) == 0)
432: continue;
1.2 thorpej 433: goto bootdisk_found;
1.1 thorpej 434: }
435:
436: continue;
1.2 thorpej 437: bootdisk_found:
1.1 thorpej 438: if (booted_device) {
439: printf("WARNING: double match for boot "
440: "device (%s, %s)\n",
1.34 cegger 441: device_xname(booted_device),
442: device_xname(dv));
1.1 thorpej 443: continue;
444: }
1.18 christos 445: handle_wedges(dv, bid->partition);
1.1 thorpej 446: }
447:
448: if (booted_device)
449: return;
1.35 tsutsui 450:
451: /*
452: * No booted device found; check CD-ROM boot at last.
453: *
454: * Our bootloader assumes CD-ROM boot if biosdev is larger
455: * than the number of hard drives recognized by the BIOS.
456: * The number of drives can be found in BTINFO_BIOSGEOM here.
457: *
458: * See src/sys/arch/i386/stand/boot/devopen.c and
459: * src/sys/arch/i386/stand/lib/bootinfo_biosgeom.c .
460: */
461: if ((big = lookup_bootinfo(BTINFO_BIOSGEOM)) != NULL &&
462: bid->biosdev > 0x80 + big->num) {
463: /*
464: * XXX
465: * There is no proper way to detect which unit is
466: * recognized as a bootable CD-ROM drive by the BIOS.
467: * Assume the first unit is the one.
468: */
469: TAILQ_FOREACH(dv, &alldevs, dv_list) {
470: if (device_class(dv) == DV_DISK &&
471: device_is_a(dv, "cd")) {
472: booted_device = dv;
473: booted_partition = 0;
474: break;
475: }
476: }
477: }
1.1 thorpej 478: }
479: }
480:
481: void
1.8 jmmv 482: cpu_rootconf(void)
1.1 thorpej 483: {
484:
485: findroot();
1.8 jmmv 486: matchbiosdisks();
1.1 thorpej 487:
1.2 thorpej 488: if (booted_wedge) {
489: KASSERT(booted_device != NULL);
490: printf("boot device: %s (%s)\n",
1.34 cegger 491: device_xname(booted_wedge), device_xname(booted_device));
1.2 thorpej 492: setroot(booted_wedge, 0);
493: } else {
494: printf("boot device: %s\n",
1.34 cegger 495: booted_device ? device_xname(booted_device) : "<unknown>");
1.2 thorpej 496: setroot(booted_device, booted_partition);
497: }
1.1 thorpej 498: }
499:
500: void
501: device_register(struct device *dev, void *aux)
502: {
1.36 ! jmcneill 503: static bool found_console = false;
1.1 thorpej 504:
505: /*
506: * Handle network interfaces here, the attachment information is
507: * not available driver-independently later.
508: *
509: * For disks, there is nothing useful available at attach time.
510: */
1.9 thorpej 511: if (device_class(dev) == DV_IFNET) {
1.1 thorpej 512: struct btinfo_netif *bin = lookup_bootinfo(BTINFO_NETIF);
513: if (bin == NULL)
514: return;
515:
516: /*
517: * We don't check the driver name against the device name
518: * passed by the boot ROM. The ROM should stay usable if
519: * the driver becomes obsolete. The physical attachment
520: * information (checked below) must be sufficient to
521: * idenfity the device.
522: */
523: if (bin->bus == BI_BUS_ISA &&
1.11 thorpej 524: device_is_a(device_parent(dev), "isa")) {
1.1 thorpej 525: struct isa_attach_args *iaa = aux;
526:
527: /* Compare IO base address */
528: /* XXXJRT What about multiple IO addrs? */
529: if (iaa->ia_nio > 0 &&
530: bin->addr.iobase == iaa->ia_io[0].ir_addr)
531: goto found;
532: }
533: #if NPCI > 0
534: if (bin->bus == BI_BUS_PCI &&
1.11 thorpej 535: device_is_a(device_parent(dev), "pci")) {
1.1 thorpej 536: struct pci_attach_args *paa = aux;
537: int b, d, f;
538:
539: /*
540: * Calculate BIOS representation of:
541: *
542: * <bus,device,function>
543: *
544: * and compare.
545: */
546: pci_decompose_tag(paa->pa_pc, paa->pa_tag, &b, &d, &f);
547: if (bin->addr.tag == ((b << 8) | (d << 3) | f))
548: goto found;
549: }
550: #endif /* NPCI > 0 */
551: }
1.36 ! jmcneill 552: #if NPCI > 0
! 553: if (device_parent(dev) && device_is_a(device_parent(dev), "pci") &&
! 554: found_console == false) {
! 555: struct btinfo_framebuffer *fbinfo;
! 556: struct pci_attach_args *pa = aux;
! 557: prop_dictionary_t dict;
! 558:
! 559: if (PCI_CLASS(pa->pa_class) == PCI_CLASS_DISPLAY) {
! 560: fbinfo = lookup_bootinfo(BTINFO_FRAMEBUFFER);
! 561: if (fbinfo == NULL || fbinfo->physaddr == 0)
! 562: return;
! 563: dict = device_properties(dev);
! 564: prop_dictionary_set_uint32(dict, "width",
! 565: fbinfo->width);
! 566: prop_dictionary_set_uint32(dict, "height",
! 567: fbinfo->height);
! 568: prop_dictionary_set_uint8(dict, "depth",
! 569: fbinfo->depth);
! 570: prop_dictionary_set_uint64(dict, "address",
! 571: fbinfo->physaddr);
! 572: prop_dictionary_set_uint16(dict, "linebytes",
! 573: fbinfo->stride);
! 574: prop_dictionary_set_bool(dict, "is_console", true);
! 575: #if notyet
! 576: prop_dictionary_set_bool(dict, "splash",
! 577: fbinfo->flags & BI_FB_SPLASH ? true : false);
! 578: #endif
! 579: if (fbinfo->depth == 8) {
! 580: gfb_cb.gcc_cookie = NULL;
! 581: gfb_cb.gcc_set_mapreg = x86_genfb_set_mapreg;
! 582: prop_dictionary_set_uint64(dict,
! 583: "cmap_callback", (uint64_t)&gfb_cb);
! 584: }
! 585: found_console = true;
! 586: return;
! 587: }
! 588: }
! 589: #endif
1.1 thorpej 590: return;
591:
592: found:
593: if (booted_device) {
594: /* XXX should be a panic() */
595: printf("WARNING: double match for boot device (%s, %s)\n",
1.34 cegger 596: device_xname(booted_device), device_xname(dev));
1.1 thorpej 597: return;
598: }
599: booted_device = dev;
600: }
CVSweb <webmaster@jp.NetBSD.org>