1/* $NetBSD: x86_autoconf.c,v 1.75 2016/09/21 00:00:07 jmcneill 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.75 2016/09/21 00:00:07 jmcneill 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
64struct disklist *x86_alldisks;
65int x86_ndisks;
66
67#ifdef DEBUG_GEOM
68#define DPRINTF(a) printf a
69#else
70#define DPRINTF(a)
71#endif
72
73static void
74dmatch(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
81static int
82is_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 */
99static void
100matchbiosdisks(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 */
205static int
206match_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 if (error != EINVAL) {
239 aprint_error("%s: unable to read block %"
240 PRId64 " " "of dev %s (%d)\n", __func__,
241 blk, device_xname(dv), error);
242 }
243 goto closeout;
244 }
245 MD5Update(&ctx, bf, sizeof(bf));
246 }
247 MD5Final(hash, &ctx);
248
249 /* Compare with the provided hash. */
250 found = memcmp(biw->matchhash, hash, sizeof(hash)) == 0;
251 DPRINTF(("%s: %s found=%d\n", __func__, device_xname(dv), found));
252
253 closeout:
254 VOP_CLOSE(tmpvn, FREAD, NOCRED);
255 vput(tmpvn);
256 return found;
257}
258
259/*
260 * Helper function for findroot():
261 * Return non-zero if disk device matches bootinfo.
262 */
263static int
264match_bootdisk(device_t dv, struct btinfo_bootdisk *bid)
265{
266 struct vnode *tmpvn;
267 int error;
268 struct disklabel label;
269 int found = 0;
270
271 if (device_is_a(dv, "dk")) {
272 DPRINTF(("%s: dk %s\n", __func__, device_xname(dv)));
273 return 0;
274 }
275
276 /*
277 * A disklabel is required here. The boot loader doesn't refuse
278 * to boot from a disk without a label, but this is normally not
279 * wanted.
280 */
281 if (bid->labelsector == -1) {
282 DPRINTF(("%s: no label %s\n", __func__, device_xname(dv)));
283 return 0;
284 }
285
286 if ((tmpvn = opendisk(dv)) == NULL) {
287 DPRINTF(("%s: can't open %s\n", __func__, device_xname(dv)));
288 return 0;
289 }
290
291 error = VOP_IOCTL(tmpvn, DIOCGDINFO, &label, FREAD, NOCRED);
292 if (error) {
293 /*
294 * XXX Can't happen -- open() would have errored out
295 * or faked one up.
296 */
297 printf("%s: can't get label for dev %s (%d)\n", __func__,
298 device_xname(dv), error);
299 goto closeout;
300 }
301
302 /* Compare with our data. */
303 if (label.d_type == bid->label.type &&
304 label.d_checksum == bid->label.checksum &&
305 strncmp(label.d_packname, bid->label.packname, 16) == 0)
306 found = 1;
307
308 DPRINTF(("%s: %s found=%d\n", __func__, device_xname(dv), found));
309 closeout:
310 VOP_CLOSE(tmpvn, FREAD, NOCRED);
311 vput(tmpvn);
312 return (found);
313}
314
315/*
316 * Attempt to find the device from which we were booted. If we can do so,
317 * and not instructed not to do so, change rootdev to correspond to the
318 * load device.
319 */
320static void
321findroot(void)
322{
323 struct btinfo_rootdevice *biv;
324 struct btinfo_bootdisk *bid;
325 struct btinfo_bootwedge *biw;
326 struct btinfo_biosgeom *big;
327 device_t dv;
328 deviter_t di;
329 static char bootspecbuf[sizeof(biv->devname)+1];
330
331 if (booted_device)
332 return;
333
334 if (lookup_bootinfo(BTINFO_NETIF) != NULL) {
335 /*
336 * We got netboot interface information, but device_register()
337 * failed to match it to a configured device. Boot disk
338 * information cannot be present at the same time, so give
339 * up.
340 */
341 printf("%s: netboot interface not found.\n", __func__);
342 return;
343 }
344
345 if ((biv = lookup_bootinfo(BTINFO_ROOTDEVICE)) != NULL) {
346 for (dv = deviter_first(&di, DEVITER_F_ROOT_FIRST);
347 dv != NULL;
348 dv = deviter_next(&di)) {
349 cfdata_t cd;
350 size_t len;
351
352 if (device_class(dv) != DV_DISK)
353 continue;
354
355 cd = device_cfdata(dv);
356 len = strlen(cd->cf_name);
357
358 if (strncmp(cd->cf_name, biv->devname, len) == 0 &&
359 biv->devname[len] - '0' == device_unit(dv)) {
360 booted_device = dv;
361 booted_partition = biv->devname[len + 1] - 'a';
362 booted_nblks = 0;
363 break;
364 }
365 }
366 DPRINTF(("%s: BTINFO_ROOTDEVICE %s\n", __func__,
367 booted_device ? device_xname(booted_device) : "not found"));
368 deviter_release(&di);
369 if (dv != NULL)
370 return;
371
372 if (biv->devname[0] != '\0') {
373 strlcpy(bootspecbuf, biv->devname, sizeof(bootspecbuf));
374 bootspec = bootspecbuf;
375 return;
376 }
377 }
378
379 bid = lookup_bootinfo(BTINFO_BOOTDISK);
380 biw = lookup_bootinfo(BTINFO_BOOTWEDGE);
381
382 if (biw != NULL) {
383 /*
384 * Scan all disk devices for ones that match the passed data.
385 * Don't break if one is found, to get possible multiple
386 * matches - for problem tracking. Use the first match anyway
387 * because lower devices numbers are more likely to be the
388 * boot device.
389 */
390 for (dv = deviter_first(&di, DEVITER_F_ROOT_FIRST);
391 dv != NULL;
392 dv = deviter_next(&di)) {
393 if (is_valid_disk(dv)) {
394 /*
395 * Don't trust BIOS device numbers, try
396 * to match the information passed by the
397 * boot loader instead.
398 */
399 if ((biw->biosdev & 0x80) == 0 ||
400 match_bootwedge(dv, biw) == 0)
401 continue;
402 goto bootwedge_found;
403 }
404
405 continue;
406 bootwedge_found:
407 if (booted_device) {
408 dmatch(__func__, dv);
409 continue;
410 }
411 booted_device = dv;
412 booted_partition = bid != NULL ? bid->partition : 0;
413 booted_nblks = biw->nblks;
414 booted_startblk = biw->startblk;
415 }
416 deviter_release(&di);
417
418 DPRINTF(("%s: BTINFO_BOOTWEDGE %s\n", __func__,
419 booted_device ? device_xname(booted_device) : "not found"));
420 if (booted_nblks)
421 return;
422 }
423
424 if (bid != NULL) {
425 /*
426 * Scan all disk devices for ones that match the passed data.
427 * Don't break if one is found, to get possible multiple
428 * matches - for problem tracking. Use the first match anyway
429 * because lower device numbers are more likely to be the
430 * boot device.
431 */
432 for (dv = deviter_first(&di, DEVITER_F_ROOT_FIRST);
433 dv != NULL;
434 dv = deviter_next(&di)) {
435
436 if (device_is_a(dv, "fd") &&
437 device_class(dv) == DV_DISK) {
438 /*
439 * Assume the configured unit number matches
440 * the BIOS device number. (This is the old
441 * behavior.) Needs some ideas how to handle
442 * the BIOS's "swap floppy drive" options.
443 */
444 /* XXX device_unit() abuse */
445 if ((bid->biosdev & 0x80) != 0 ||
446 device_unit(dv) != bid->biosdev)
447 continue;
448 goto bootdisk_found;
449 }
450
451 if (is_valid_disk(dv)) {
452 /*
453 * Don't trust BIOS device numbers, try
454 * to match the information passed by the
455 * boot loader instead.
456 */
457 if ((bid->biosdev & 0x80) == 0 ||
458 match_bootdisk(dv, bid) == 0)
459 continue;
460 goto bootdisk_found;
461 }
462
463 continue;
464 bootdisk_found:
465 if (booted_device) {
466 dmatch(__func__, dv);
467 continue;
468 }
469 booted_device = dv;
470 booted_partition = bid->partition;
471 booted_nblks = 0;
472 }
473 deviter_release(&di);
474
475 DPRINTF(("%s: BTINFO_BOOTDISK %s\n", __func__,
476 booted_device ? device_xname(booted_device) : "not found"));
477 if (booted_device)
478 return;
479
480 /*
481 * No booted device found; check CD-ROM boot at last.
482 *
483 * Our bootloader assumes CD-ROM boot if biosdev is larger
484 * or equal than the number of hard drives recognized by the
485 * BIOS. The number of drives can be found in BTINFO_BIOSGEOM
486 * here. For example, if we have wd0, wd1, and cd0:
487 *
488 * big->num = 2 (for wd0 and wd1)
489 * bid->biosdev = 0x80 (wd0)
490 * bid->biosdev = 0x81 (wd1)
491 * bid->biosdev = 0x82 (cd0)
492 *
493 * See src/sys/arch/i386/stand/boot/devopen.c and
494 * src/sys/arch/i386/stand/lib/bootinfo_biosgeom.c .
495 */
496 if ((big = lookup_bootinfo(BTINFO_BIOSGEOM)) != NULL &&
497 bid->biosdev >= 0x80 + big->num) {
498 /*
499 * XXX
500 * There is no proper way to detect which unit is
501 * recognized as a bootable CD-ROM drive by the BIOS.
502 * Assume the first unit is the one.
503 */
504 for (dv = deviter_first(&di, DEVITER_F_ROOT_FIRST);
505 dv != NULL;
506 dv = deviter_next(&di)) {
507 if (device_class(dv) == DV_DISK &&
508 device_is_a(dv, "cd")) {
509 booted_device = dv;
510 booted_partition = 0;
511 booted_nblks = 0;
512 break;
513 }
514 }
515 deviter_release(&di);
516 DPRINTF(("%s: BTINFO_BIOSGEOM %s\n", __func__,
517 booted_device ? device_xname(booted_device) :
518 "not found"));
519 }
520 }
521}
522
523void
524cpu_bootconf(void)
525{
526 findroot();
527 matchbiosdisks();
528}
529
530void
531cpu_rootconf(void)
532{
533 cpu_bootconf();
534
535 aprint_normal("boot device: %s\n",
536 booted_device ? device_xname(booted_device) : "<unknown>");
537 rootconf();
538}
539
540void
541device_register(device_t dev, void *aux)
542{
543 device_t isaboot, pciboot;
544
545 device_acpi_register(dev, aux);
546
547 isaboot = device_isa_register(dev, aux);
548 pciboot = device_pci_register(dev, aux);
549
550 if (isaboot == NULL && pciboot == NULL)
551 return;
552
553 if (booted_device != NULL) {
554 /* XXX should be a panic() */
555 dmatch(__func__, dev);
556 } else
557 booted_device = (isaboot != NULL) ? isaboot : pciboot;
558}
559