Home | History | Annotate | Line # | Download | only in x86
x86_autoconf.c revision 1.71
      1 /*	$NetBSD: x86_autoconf.c,v 1.70 2014/04/03 15:21:52 christos Exp $	*/
      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>
     38 __KERNEL_RCSID(0, "$NetBSD: x86_autoconf.c,v 1.70 2014/04/03 15:21:52 christos Exp $");
     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>
     48 #include <sys/disk.h>
     49 #include <sys/proc.h>
     50 #include <sys/md5.h>
     51 #include <sys/kauth.h>
     52 
     53 #include <machine/autoconf.h>
     54 #include <machine/bootinfo.h>
     55 #include <machine/pio.h>
     56 
     57 #include "acpica.h"
     58 #include "wsdisplay.h"
     59 
     60 #if NACPICA > 0
     61 #include <dev/acpi/acpivar.h>
     62 #endif
     63 
     64 struct disklist *x86_alldisks;
     65 int x86_ndisks;
     66 
     67 #ifdef DEBUG_GEOM
     68 #define DPRINTF(a) printf a
     69 #else
     70 #define DPRINTF(a)
     71 #endif
     72 
     73 static void
     74 dmatch(const char *func, device_t dv)
     75 {
     76 
     77 	printf("WARNING: %s: double match for boot device (%s, %s)\n",
     78 	    func, device_xname(booted_device), device_xname(dv));
     79 }
     80 
     81 static int
     82 is_valid_disk(device_t dv)
     83 {
     84 
     85 	if (device_class(dv) != DV_DISK)
     86 		return (0);
     87 
     88 	return (device_is_a(dv, "dk") ||
     89 		device_is_a(dv, "sd") ||
     90 		device_is_a(dv, "wd") ||
     91 		device_is_a(dv, "ld") ||
     92 		device_is_a(dv, "ed"));
     93 }
     94 
     95 /*
     96  * XXX Ugly bit of code.  But, this is the only safe time that the
     97  * match between BIOS disks and native disks can be done.
     98  */
     99 static void
    100 matchbiosdisks(void)
    101 {
    102 	struct btinfo_biosgeom *big;
    103 	struct bi_biosgeom_entry *be;
    104 	device_t dv;
    105 	deviter_t di;
    106 	int i, ck, error, m, n;
    107 	struct vnode *tv;
    108 	char mbr[DEV_BSIZE];
    109 	int dklist_size;
    110 	int numbig;
    111 
    112 	if (x86_ndisks)
    113 		return;
    114 	big = lookup_bootinfo(BTINFO_BIOSGEOM);
    115 
    116 	numbig = big ? big->num : 0;
    117 
    118 	/* First, count all native disks. */
    119 	for (dv = deviter_first(&di, DEVITER_F_ROOT_FIRST); dv != NULL;
    120 	     dv = deviter_next(&di)) {
    121 		if (is_valid_disk(dv))
    122 			x86_ndisks++;
    123 	}
    124 	deviter_release(&di);
    125 
    126 	dklist_size = sizeof(struct disklist) + (x86_ndisks - 1) *
    127 	    sizeof(struct nativedisk_info);
    128 
    129 	/* XXX M_TEMP is wrong */
    130 	x86_alldisks = malloc(dklist_size, M_TEMP, M_NOWAIT | M_ZERO);
    131 	if (x86_alldisks == NULL)
    132 		return;
    133 
    134 	x86_alldisks->dl_nnativedisks = x86_ndisks;
    135 	x86_alldisks->dl_nbiosdisks = numbig;
    136 	for (i = 0; i < numbig; i++) {
    137 		x86_alldisks->dl_biosdisks[i].bi_dev = big->disk[i].dev;
    138 		x86_alldisks->dl_biosdisks[i].bi_sec = big->disk[i].sec;
    139 		x86_alldisks->dl_biosdisks[i].bi_head = big->disk[i].head;
    140 		x86_alldisks->dl_biosdisks[i].bi_cyl = big->disk[i].cyl;
    141 		x86_alldisks->dl_biosdisks[i].bi_lbasecs = big->disk[i].totsec;
    142 		x86_alldisks->dl_biosdisks[i].bi_flags = big->disk[i].flags;
    143 		DPRINTF(("%s: disk %x: flags %x",
    144 		    __func__, big->disk[i].dev, big->disk[i].flags));
    145 #ifdef BIOSDISK_EXTINFO_V3
    146 		DPRINTF((", interface %x, device %llx",
    147 		    big->disk[i].interface_path, big->disk[i].device_path));
    148 #endif
    149 		DPRINTF(("\n"));
    150 	}
    151 
    152 	/* XXX Code duplication from findroot(). */
    153 	n = -1;
    154 	for (dv = deviter_first(&di, DEVITER_F_ROOT_FIRST); dv != NULL;
    155 	     dv = deviter_next(&di)) {
    156 		if (!is_valid_disk(dv))
    157 			continue;
    158 		DPRINTF(("%s: trying to match (%s) %s: ", __func__,
    159 		    device_xname(dv), device_cfdata(dv)->cf_name));
    160 		n++;
    161 		snprintf(x86_alldisks->dl_nativedisks[n].ni_devname,
    162 		    sizeof(x86_alldisks->dl_nativedisks[n].ni_devname),
    163 		    "%s", device_xname(dv));
    164 
    165 		if ((tv = opendisk(dv)) == NULL) {
    166 			DPRINTF(("cannot open\n"));
    167 			continue;
    168 		}
    169 
    170 		error = vn_rdwr(UIO_READ, tv, mbr, DEV_BSIZE, 0, UIO_SYSSPACE,
    171 		    0, NOCRED, NULL, NULL);
    172 		VOP_CLOSE(tv, FREAD, NOCRED);
    173 		vput(tv);
    174 		if (error) {
    175 			DPRINTF(("MBR read failure %d\n", error));
    176 			continue;
    177 		}
    178 
    179 		for (ck = i = 0; i < DEV_BSIZE; i++)
    180 			ck += mbr[i];
    181 		for (m = i = 0; i < numbig; i++) {
    182 			be = &big->disk[i];
    183 			if (be->flags & BI_GEOM_INVALID)
    184 				continue;
    185 			DPRINTF(("matched with %d dev ck %x bios ck %x\n",
    186 			    i, ck, be->cksum));
    187 			if (be->cksum == ck && memcmp(&mbr[MBR_PART_OFFSET],
    188 			    be->mbrparts, MBR_PART_COUNT
    189 			    * sizeof(struct mbr_partition)) == 0) {
    190 				DPRINTF(("%s: matched BIOS disk %x with %s\n",
    191 				    __func__, be->dev, device_xname(dv)));
    192 				x86_alldisks->dl_nativedisks[n].
    193 				    ni_biosmatches[m++] = i;
    194 			}
    195 		}
    196 		x86_alldisks->dl_nativedisks[n].ni_nmatches = m;
    197 	}
    198 	deviter_release(&di);
    199 }
    200 
    201 /*
    202  * Helper function for findroot():
    203  * Return non-zero if wedge device matches bootinfo.
    204  */
    205 static int
    206 match_bootwedge(device_t dv, struct btinfo_bootwedge *biw)
    207 {
    208 	MD5_CTX ctx;
    209 	struct vnode *tmpvn;
    210 	int error;
    211 	uint8_t bf[DEV_BSIZE];
    212 	uint8_t hash[16];
    213 	int found = 0;
    214 	daddr_t blk;
    215 	uint64_t nblks;
    216 
    217 	/*
    218 	 * If the boot loader didn't specify the sector, abort.
    219 	 */
    220 	if (biw->matchblk == -1) {
    221 		DPRINTF(("%s: no sector specified for %s\n", __func__,
    222 			device_xname(dv)));
    223 		return 0;
    224 	}
    225 
    226 	if ((tmpvn = opendisk(dv)) == NULL) {
    227 		DPRINTF(("%s: can't open %s\n", __func__, device_xname(dv)));
    228 		return 0;
    229 	}
    230 
    231 	MD5Init(&ctx);
    232 	for (blk = biw->matchblk, nblks = biw->matchnblks;
    233 	     nblks != 0; nblks--, blk++) {
    234 		error = vn_rdwr(UIO_READ, tmpvn, (void *) bf,
    235 		    sizeof(bf), blk * DEV_BSIZE, UIO_SYSSPACE,
    236 		    0, NOCRED, NULL, NULL);
    237 		if (error) {
    238 			printf("%s: unable to read block %" PRId64 " "
    239 			    "of dev %s (%d)\n", __func__,
    240 			    blk, device_xname(dv), error);
    241 			goto closeout;
    242 		}
    243 		MD5Update(&ctx, bf, sizeof(bf));
    244 	}
    245 	MD5Final(hash, &ctx);
    246 
    247 	/* Compare with the provided hash. */
    248 	found = memcmp(biw->matchhash, hash, sizeof(hash)) == 0;
    249 	DPRINTF(("%s: %s found=%d\n", __func__, device_xname(dv), found));
    250 
    251  closeout:
    252 	VOP_CLOSE(tmpvn, FREAD, NOCRED);
    253 	vput(tmpvn);
    254 	return found;
    255 }
    256 
    257 /*
    258  * Helper function for findroot():
    259  * Return non-zero if disk device matches bootinfo.
    260  */
    261 static int
    262 match_bootdisk(device_t dv, struct btinfo_bootdisk *bid)
    263 {
    264 	struct vnode *tmpvn;
    265 	int error;
    266 	struct disklabel label;
    267 	int found = 0;
    268 
    269 	if (device_is_a(dv, "dk")) {
    270 		DPRINTF(("%s: dk %s\n", __func__, device_xname(dv)));
    271 		return 0;
    272 	}
    273 
    274 	/*
    275 	 * A disklabel is required here.  The boot loader doesn't refuse
    276 	 * to boot from a disk without a label, but this is normally not
    277 	 * wanted.
    278 	 */
    279 	if (bid->labelsector == -1) {
    280 		DPRINTF(("%s: no label %s\n", __func__, device_xname(dv)));
    281 		return 0;
    282 	}
    283 
    284 	if ((tmpvn = opendisk(dv)) == NULL) {
    285 		DPRINTF(("%s: can't open %s\n", __func__, device_xname(dv)));
    286 		return 0;
    287 	}
    288 
    289 	error = VOP_IOCTL(tmpvn, DIOCGDINFO, &label, FREAD, NOCRED);
    290 	if (error) {
    291 		/*
    292 		 * XXX Can't happen -- open() would have errored out
    293 		 * or faked one up.
    294 		 */
    295 		printf("%s: can't get label for dev %s (%d)\n", __func__,
    296 		    device_xname(dv), error);
    297 		goto closeout;
    298 	}
    299 
    300 	/* Compare with our data. */
    301 	if (label.d_type == bid->label.type &&
    302 	    label.d_checksum == bid->label.checksum &&
    303 	    strncmp(label.d_packname, bid->label.packname, 16) == 0)
    304 		found = 1;
    305 
    306 	DPRINTF(("%s: %s found=%d\n", __func__, device_xname(dv), found));
    307  closeout:
    308 	VOP_CLOSE(tmpvn, FREAD, NOCRED);
    309 	vput(tmpvn);
    310 	return (found);
    311 }
    312 
    313 /*
    314  * Attempt to find the device from which we were booted.  If we can do so,
    315  * and not instructed not to do so, change rootdev to correspond to the
    316  * load device.
    317  */
    318 static void
    319 findroot(void)
    320 {
    321 	struct btinfo_rootdevice *biv;
    322 	struct btinfo_bootdisk *bid;
    323 	struct btinfo_bootwedge *biw;
    324 	struct btinfo_biosgeom *big;
    325 	device_t dv;
    326 	deviter_t di;
    327 
    328 	if (booted_device)
    329 		return;
    330 
    331 	if (lookup_bootinfo(BTINFO_NETIF) != NULL) {
    332 		/*
    333 		 * We got netboot interface information, but device_register()
    334 		 * failed to match it to a configured device.  Boot disk
    335 		 * information cannot be present at the same time, so give
    336 		 * up.
    337 		 */
    338 		printf("%s: netboot interface not found.\n", __func__);
    339 		return;
    340 	}
    341 
    342 	if ((biv = lookup_bootinfo(BTINFO_ROOTDEVICE)) != NULL) {
    343 		for (dv = deviter_first(&di, DEVITER_F_ROOT_FIRST);
    344 		     dv != NULL;
    345 		     dv = deviter_next(&di)) {
    346 			cfdata_t cd;
    347 			size_t len;
    348 
    349 			if (device_class(dv) != DV_DISK)
    350 				continue;
    351 
    352 			cd = device_cfdata(dv);
    353 			len = strlen(cd->cf_name);
    354 
    355 			if (strncmp(cd->cf_name, biv->devname, len) == 0 &&
    356 			    biv->devname[len] - '0' == device_unit(dv)) {
    357 				booted_device = dv;
    358 				booted_partition = biv->devname[len + 1] - 'a';
    359 				booted_nblks = 0;
    360 				break;
    361 			}
    362 		}
    363 		DPRINTF(("%s: BTINFO_ROOTDEVICE %s\n", __func__,
    364 		    booted_device ? device_xname(booted_device) : "not found"));
    365 		deviter_release(&di);
    366 		if (dv != NULL)
    367 			return;
    368 	}
    369 
    370 	bid = lookup_bootinfo(BTINFO_BOOTDISK);
    371 	biw = lookup_bootinfo(BTINFO_BOOTWEDGE);
    372 
    373 	if (biw != NULL) {
    374 		/*
    375 		 * Scan all disk devices for ones that match the passed data.
    376 		 * Don't break if one is found, to get possible multiple
    377 		 * matches - for problem tracking.  Use the first match anyway
    378 		 * because lower devices numbers are more likely to be the
    379 		 * boot device.
    380 		 */
    381 		for (dv = deviter_first(&di, DEVITER_F_ROOT_FIRST);
    382 		     dv != NULL;
    383 		     dv = deviter_next(&di)) {
    384 			if (is_valid_disk(dv)) {
    385 				/*
    386 				 * Don't trust BIOS device numbers, try
    387 				 * to match the information passed by the
    388 				 * boot loader instead.
    389 				 */
    390 				if ((biw->biosdev & 0x80) == 0 ||
    391 				    match_bootwedge(dv, biw) == 0)
    392 				    	continue;
    393 				goto bootwedge_found;
    394 			}
    395 
    396 			continue;
    397  bootwedge_found:
    398 			if (booted_device) {
    399 				dmatch(__func__, dv);
    400 				continue;
    401 			}
    402 			booted_device = dv;
    403 			booted_partition = bid != NULL ? bid->partition : 0;
    404 			booted_nblks = biw->nblks;
    405 			booted_startblk = biw->startblk;
    406 		}
    407 		deviter_release(&di);
    408 
    409 		DPRINTF(("%s: BTINFO_BOOTWEDGE %s\n", __func__,
    410 		    booted_device ? device_xname(booted_device) : "not found"));
    411 		if (booted_nblks)
    412 			return;
    413 	}
    414 
    415 	if (bid != NULL) {
    416 		/*
    417 		 * Scan all disk devices for ones that match the passed data.
    418 		 * Don't break if one is found, to get possible multiple
    419 		 * matches - for problem tracking.  Use the first match anyway
    420 		 * because lower device numbers are more likely to be the
    421 		 * boot device.
    422 		 */
    423 		for (dv = deviter_first(&di, DEVITER_F_ROOT_FIRST);
    424 		     dv != NULL;
    425 		     dv = deviter_next(&di)) {
    426 				continue;
    427 
    428 			if (device_is_a(dv, "fd") &&
    429 			    device_class(dv) == DV_DISK) {
    430 				/*
    431 				 * Assume the configured unit number matches
    432 				 * the BIOS device number.  (This is the old
    433 				 * behavior.)  Needs some ideas how to handle
    434 				 * the BIOS's "swap floppy drive" options.
    435 				 */
    436 				/* XXX device_unit() abuse */
    437 				if ((bid->biosdev & 0x80) != 0 ||
    438 				    device_unit(dv) != bid->biosdev)
    439 				    	continue;
    440 				goto bootdisk_found;
    441 			}
    442 
    443 			if (is_valid_disk(dv)) {
    444 				/*
    445 				 * Don't trust BIOS device numbers, try
    446 				 * to match the information passed by the
    447 				 * boot loader instead.
    448 				 */
    449 				if ((bid->biosdev & 0x80) == 0 ||
    450 				    match_bootdisk(dv, bid) == 0)
    451 				    	continue;
    452 				goto bootdisk_found;
    453 			}
    454 
    455 			continue;
    456  bootdisk_found:
    457 			if (booted_device) {
    458 				dmatch(__func__, dv);
    459 				continue;
    460 			}
    461 			booted_device = dv;
    462 			booted_partition = bid->partition;
    463 			booted_nblks = 0;
    464 		}
    465 		deviter_release(&di);
    466 
    467 		DPRINTF(("%s: BTINFO_BOOTDISK %s\n", __func__,
    468 		    booted_device ? device_xname(booted_device) : "not found"));
    469 		if (booted_device)
    470 			return;
    471 
    472 		/*
    473 		 * No booted device found; check CD-ROM boot at last.
    474 		 *
    475 		 * Our bootloader assumes CD-ROM boot if biosdev is larger
    476 		 * or equal than the number of hard drives recognized by the
    477 		 * BIOS. The number of drives can be found in BTINFO_BIOSGEOM
    478 		 * here. For example, if we have wd0, wd1, and cd0:
    479 		 *
    480 		 *	big->num = 2 (for wd0 and wd1)
    481 		 *	bid->biosdev = 0x80 (wd0)
    482 		 *	bid->biosdev = 0x81 (wd1)
    483 		 *	bid->biosdev = 0x82 (cd0)
    484 		 *
    485 		 * See src/sys/arch/i386/stand/boot/devopen.c and
    486 		 * src/sys/arch/i386/stand/lib/bootinfo_biosgeom.c .
    487 		 */
    488 		if ((big = lookup_bootinfo(BTINFO_BIOSGEOM)) != NULL &&
    489 		    bid->biosdev >= 0x80 + big->num) {
    490 			/*
    491 			 * XXX
    492 			 * There is no proper way to detect which unit is
    493 			 * recognized as a bootable CD-ROM drive by the BIOS.
    494 			 * Assume the first unit is the one.
    495 			 */
    496 			for (dv = deviter_first(&di, DEVITER_F_ROOT_FIRST);
    497 			     dv != NULL;
    498 			     dv = deviter_next(&di)) {
    499 				if (device_class(dv) == DV_DISK &&
    500 				    device_is_a(dv, "cd")) {
    501 					booted_device = dv;
    502 					booted_partition = 0;
    503 					booted_nblks = 0;
    504 					break;
    505 				}
    506 			}
    507 			deviter_release(&di);
    508 			DPRINTF(("%s: BTINFO_BIOSGEOM %s\n", __func__,
    509 			    booted_device ? device_xname(booted_device) :
    510 			    "not found"));
    511 		}
    512 	}
    513 }
    514 
    515 void
    516 cpu_bootconf(void)
    517 {
    518 	findroot();
    519 	matchbiosdisks();
    520 }
    521 
    522 void
    523 cpu_rootconf(void)
    524 {
    525 	cpu_bootconf();
    526 
    527 	aprint_normal("boot device: %s\n",
    528 	    booted_device ? device_xname(booted_device) : "<unknown>");
    529 	rootconf();
    530 }
    531 
    532 void
    533 device_register(device_t dev, void *aux)
    534 {
    535 	device_t isaboot, pciboot;
    536 
    537 	isaboot = device_isa_register(dev, aux);
    538 	pciboot = device_pci_register(dev, aux);
    539 
    540 	if (isaboot == NULL && pciboot == NULL)
    541 		return;
    542 
    543 	if (booted_device != NULL) {
    544 		/* XXX should be a panic() */
    545 		dmatch(__func__, dev);
    546 	} else
    547 		booted_device = (isaboot != NULL) ? isaboot : pciboot;
    548 }
    549