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