1 /* $OpenBSD: diskprobe.c,v 1.9 2012/01/11 14:47:02 jsing Exp $ */ 2 3 /* 4 * Copyright (c) 1997 Tobias Weingartner 5 * Copyright (c) 2012 Joel Sing <jsing@openbsd.org> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 */ 30 31 /* We want the disk type names from disklabel.h */ 32 #undef DKTYPENAMES 33 34 #include <sys/param.h> 35 #include <sys/queue.h> 36 #include <sys/reboot.h> 37 #include <sys/disklabel.h> 38 #include <dev/biovar.h> 39 #include <dev/softraidvar.h> 40 #include <stand/boot/bootarg.h> 41 #include <machine/biosvar.h> 42 #include <lib/libz/zlib.h> 43 #include "disk.h" 44 #include "biosdev.h" 45 #include "libsa.h" 46 47 #define MAX_CKSUMLEN MAXBSIZE / DEV_BSIZE /* Max # of blks to cksum */ 48 49 /* Local Prototypes */ 50 static int disksum(int); 51 52 /* List of disk devices we found/probed */ 53 struct disklist_lh disklist; 54 55 /* List of softraid volumes. */ 56 struct sr_boot_volume_head sr_volumes; 57 58 /* Pointer to boot device */ 59 struct diskinfo *bootdev_dip; 60 61 extern int debug; 62 extern int bios_bootdev; 63 extern int bios_cddev; 64 65 /* Probe for all BIOS floppies */ 66 static void 67 floppyprobe(void) 68 { 69 struct diskinfo *dip; 70 int i; 71 72 /* Floppies */ 73 for(i = 0; i < 4; i++) { 74 dip = alloc(sizeof(struct diskinfo)); 75 bzero(dip, sizeof(*dip)); 76 77 if(bios_getdiskinfo(i, &dip->bios_info)) { 78 #ifdef BIOS_DEBUG 79 if (debug) 80 printf(" <!fd%u>", i); 81 #endif 82 free(dip, 0); 83 break; 84 } 85 86 printf(" fd%u", i); 87 88 /* Fill out best we can - (fd?) */ 89 dip->bios_info.bsd_dev = MAKEBOOTDEV(2, 0, 0, i, RAW_PART); 90 91 /* 92 * Delay reading the disklabel until we're sure we want 93 * to boot from the floppy. Doing this avoids a delay 94 * (sometimes very long) when trying to read the label 95 * and the drive is unplugged. 96 */ 97 dip->bios_info.flags |= BDI_BADLABEL; 98 99 /* Add to queue of disks */ 100 TAILQ_INSERT_TAIL(&disklist, dip, list); 101 } 102 } 103 104 105 /* Probe for all BIOS hard disks */ 106 static void 107 hardprobe(void) 108 { 109 struct diskinfo *dip; 110 int i; 111 u_int bsdunit, type; 112 u_int scsi = 0, ide = 0; 113 const char *dc = (const char *)((0x40 << 4) + 0x75); 114 115 /* Hard disks */ 116 for (i = 0x80; i < (0x80 + *dc); i++) { 117 dip = alloc(sizeof(struct diskinfo)); 118 bzero(dip, sizeof(*dip)); 119 120 if(bios_getdiskinfo(i, &dip->bios_info)) { 121 #ifdef BIOS_DEBUG 122 if (debug) 123 printf(" <!hd%u>", i&0x7f); 124 #endif 125 free(dip, 0); 126 break; 127 } 128 129 printf(" hd%u%s", i&0x7f, (dip->bios_info.bios_edd > 0?"+":"")); 130 131 /* Try to find the label, to figure out device type */ 132 if((bios_getdisklabel(&dip->bios_info, &dip->disklabel)) ) { 133 printf("*"); 134 bsdunit = ide++; 135 type = 0; /* XXX let it be IDE */ 136 } else { 137 /* Best guess */ 138 switch (dip->disklabel.d_type) { 139 case DTYPE_SCSI: 140 type = 4; 141 bsdunit = scsi++; 142 dip->bios_info.flags |= BDI_GOODLABEL; 143 break; 144 145 case DTYPE_ESDI: 146 case DTYPE_ST506: 147 type = 0; 148 bsdunit = ide++; 149 dip->bios_info.flags |= BDI_GOODLABEL; 150 break; 151 152 default: 153 dip->bios_info.flags |= BDI_BADLABEL; 154 type = 0; /* XXX Suggest IDE */ 155 bsdunit = ide++; 156 } 157 } 158 159 dip->bios_info.checksum = 0; /* just in case */ 160 /* Fill out best we can */ 161 dip->bios_info.bsd_dev = MAKEBOOTDEV(type, 0, 0, bsdunit, RAW_PART); 162 163 /* Add to queue of disks */ 164 TAILQ_INSERT_TAIL(&disklist, dip, list); 165 } 166 } 167 168 169 static void 170 srprobe(void) 171 { 172 struct sr_boot_volume *bv, *bv1, *bv2; 173 struct sr_boot_chunk *bc, *bc1, *bc2; 174 struct sr_meta_chunk *mc; 175 struct sr_metadata *md; 176 struct diskinfo *dip; 177 struct partition *pp; 178 int i, error, volno; 179 dev_t bsd_dev; 180 daddr_t off; 181 182 /* Probe for softraid volumes. */ 183 SLIST_INIT(&sr_volumes); 184 185 md = alloc(SR_META_SIZE * 512); 186 187 TAILQ_FOREACH(dip, &disklist, list) { 188 189 /* Only check hard disks, skip those with I/O errors. */ 190 if ((dip->bios_info.bios_number & 0x80) == 0 || 191 (dip->bios_info.flags & BDI_INVALID)) 192 continue; 193 194 /* Make sure disklabel has been read. */ 195 if ((dip->bios_info.flags & (BDI_BADLABEL|BDI_GOODLABEL)) == 0) 196 continue; 197 198 for (i = 0; i < MAXPARTITIONS; i++) { 199 200 pp = &dip->disklabel.d_partitions[i]; 201 if (pp->p_fstype != FS_RAID || pp->p_size == 0) 202 continue; 203 204 /* Read softraid metadata. */ 205 bzero(md, SR_META_SIZE * 512); 206 off = DL_GETPOFFSET(pp) + SR_META_OFFSET; 207 error = biosd_io(F_READ, &dip->bios_info, off, 208 SR_META_SIZE, md); 209 if (error) 210 continue; 211 212 /* Is this valid softraid metadata? */ 213 if (md->ssdi.ssd_magic != SR_MAGIC) 214 continue; 215 216 /* XXX - validate checksum. */ 217 218 /* Locate chunk-specific metadata for this chunk. */ 219 mc = (struct sr_meta_chunk *)(md + 1); 220 mc += md->ssdi.ssd_chunk_id; 221 222 /* XXX - extract necessary optional metadata. */ 223 224 bc = alloc(sizeof(struct sr_boot_chunk)); 225 bc->sbc_diskinfo = dip; 226 bc->sbc_disk = dip->bios_info.bios_number; 227 bc->sbc_part = 'a' + i; 228 229 bsd_dev = dip->bios_info.bsd_dev; 230 bc->sbc_mm = MAKEBOOTDEV(B_TYPE(bsd_dev), 231 B_ADAPTOR(bsd_dev), B_CONTROLLER(bsd_dev), 232 B_UNIT(bsd_dev), bc->sbc_part - 'a'); 233 234 bc->sbc_chunk_id = md->ssdi.ssd_chunk_id; 235 bc->sbc_ondisk = md->ssd_ondisk; 236 bc->sbc_state = mc->scm_status; 237 238 /* Handle key disks separately... later. */ 239 if (md->ssdi.ssd_level == SR_KEYDISK_LEVEL) 240 continue; 241 242 SLIST_FOREACH(bv, &sr_volumes, sbv_link) { 243 if (bcmp(&md->ssdi.ssd_uuid, &bv->sbv_uuid, 244 sizeof(md->ssdi.ssd_uuid)) == 0) 245 break; 246 } 247 248 if (bv == NULL) { 249 bv = alloc(sizeof(struct sr_boot_volume)); 250 bv->sbv_level = md->ssdi.ssd_level; 251 bv->sbv_volid = md->ssdi.ssd_volid; 252 bv->sbv_chunk_no = md->ssdi.ssd_chunk_no; 253 bv->sbv_flags = md->ssdi.ssd_vol_flags; 254 bv->sbv_size = md->ssdi.ssd_size; 255 bv->sbv_data_offset = md->ssd_data_offset; 256 bcopy(&md->ssdi.ssd_uuid, &bv->sbv_uuid, 257 sizeof(md->ssdi.ssd_uuid)); 258 SLIST_INIT(&bv->sbv_chunks); 259 260 /* Maintain volume order. */ 261 bv2 = NULL; 262 SLIST_FOREACH(bv1, &sr_volumes, sbv_link) { 263 if (bv1->sbv_volid > bv->sbv_volid) 264 break; 265 bv2 = bv1; 266 } 267 if (bv2 == NULL) 268 SLIST_INSERT_HEAD(&sr_volumes, bv, 269 sbv_link); 270 else 271 SLIST_INSERT_AFTER(bv2, bv, sbv_link); 272 } 273 274 /* Maintain chunk order. */ 275 bc2 = NULL; 276 SLIST_FOREACH(bc1, &bv->sbv_chunks, sbc_link) { 277 if (bc1->sbc_chunk_id > bc->sbc_chunk_id) 278 break; 279 bc2 = bc1; 280 } 281 if (bc2 == NULL) 282 SLIST_INSERT_HEAD(&bv->sbv_chunks, 283 bc, sbc_link); 284 else 285 SLIST_INSERT_AFTER(bc2, bc, sbc_link); 286 287 bv->sbv_chunks_found++; 288 } 289 } 290 291 /* 292 * Assemble RAID volumes. 293 */ 294 volno = 0; 295 SLIST_FOREACH(bv, &sr_volumes, sbv_link) { 296 297 /* Skip if this is a hotspare "volume". */ 298 if (bv->sbv_level == SR_HOTSPARE_LEVEL && 299 bv->sbv_chunk_no == 1) 300 continue; 301 302 /* Determine current ondisk version. */ 303 bv->sbv_ondisk = 0; 304 SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) { 305 if (bc->sbc_ondisk > bv->sbv_ondisk) 306 bv->sbv_ondisk = bc->sbc_ondisk; 307 } 308 SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) { 309 if (bc->sbc_ondisk != bv->sbv_ondisk) 310 bc->sbc_state = BIOC_SDOFFLINE; 311 } 312 313 /* XXX - Check for duplicate chunks. */ 314 315 /* 316 * Validate that volume has sufficient chunks for 317 * read-only access. 318 * 319 * XXX - check chunk states. 320 */ 321 bv->sbv_state = BIOC_SVOFFLINE; 322 switch (bv->sbv_level) { 323 case 0: 324 case 'C': 325 case 'c': 326 if (bv->sbv_chunk_no == bv->sbv_chunks_found) 327 bv->sbv_state = BIOC_SVONLINE; 328 break; 329 330 case 1: 331 if (bv->sbv_chunk_no == bv->sbv_chunks_found) 332 bv->sbv_state = BIOC_SVONLINE; 333 else if (bv->sbv_chunks_found > 0) 334 bv->sbv_state = BIOC_SVDEGRADED; 335 break; 336 } 337 338 bv->sbv_unit = volno++; 339 if (bv->sbv_state != BIOC_SVOFFLINE) 340 printf(" sr%d%s", bv->sbv_unit, 341 bv->sbv_flags & BIOC_SCBOOTABLE ? "*" : ""); 342 } 343 344 if (md) 345 free(md, 0); 346 } 347 348 349 /* Probe for all BIOS supported disks */ 350 u_int32_t bios_cksumlen; 351 void 352 diskprobe(void) 353 { 354 struct diskinfo *dip; 355 int i; 356 357 /* These get passed to kernel */ 358 bios_diskinfo_t *bios_diskinfo; 359 360 /* Init stuff */ 361 TAILQ_INIT(&disklist); 362 363 /* Do probes */ 364 floppyprobe(); 365 #ifdef BIOS_DEBUG 366 if (debug) 367 printf(";"); 368 #endif 369 hardprobe(); 370 371 srprobe(); 372 373 /* Checksumming of hard disks */ 374 for (i = 0; disksum(i++) && i < MAX_CKSUMLEN; ) 375 ; 376 bios_cksumlen = i; 377 378 /* Get space for passing bios_diskinfo stuff to kernel */ 379 for(i = 0, dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) 380 i++; 381 bios_diskinfo = alloc(++i * sizeof(bios_diskinfo_t)); 382 383 /* Copy out the bios_diskinfo stuff */ 384 for(i = 0, dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) 385 bios_diskinfo[i++] = dip->bios_info; 386 387 bios_diskinfo[i++].bios_number = -1; 388 /* Register for kernel use */ 389 addbootarg(BOOTARG_CKSUMLEN, sizeof(u_int32_t), &bios_cksumlen); 390 addbootarg(BOOTARG_DISKINFO, i * sizeof(bios_diskinfo_t), bios_diskinfo); 391 } 392 393 394 void 395 cdprobe(void) 396 { 397 struct diskinfo *dip; 398 int cddev = bios_cddev & 0xff; 399 400 /* Another BIOS boot device... */ 401 402 if (bios_cddev == -1) /* Not been set, so don't use */ 403 return; 404 405 dip = alloc(sizeof(struct diskinfo)); 406 bzero(dip, sizeof(*dip)); 407 408 #if 0 409 if (bios_getdiskinfo(cddev, &dip->bios_info)) { 410 printf(" <!cd0>"); /* XXX */ 411 free(dip, 0); 412 return; 413 } 414 #endif 415 416 printf(" cd0"); 417 418 dip->bios_info.bios_number = cddev; 419 dip->bios_info.bios_edd = 1; /* Use the LBA calls */ 420 dip->bios_info.flags |= BDI_GOODLABEL | BDI_EL_TORITO; 421 dip->bios_info.checksum = 0; /* just in case */ 422 dip->bios_info.bsd_dev = 423 MAKEBOOTDEV(6, 0, 0, 0, RAW_PART); 424 425 /* Create an imaginary disk label */ 426 dip->disklabel.d_secsize = 2048; 427 dip->disklabel.d_ntracks = 1; 428 dip->disklabel.d_nsectors = 100; 429 dip->disklabel.d_ncylinders = 1; 430 dip->disklabel.d_secpercyl = dip->disklabel.d_ntracks * 431 dip->disklabel.d_nsectors; 432 if (dip->disklabel.d_secpercyl == 0) { 433 dip->disklabel.d_secpercyl = 100; 434 /* as long as it's not 0, since readdisklabel divides by it */ 435 } 436 437 strncpy(dip->disklabel.d_typename, "ATAPI CD-ROM", 438 sizeof(dip->disklabel.d_typename)); 439 dip->disklabel.d_type = DTYPE_ATAPI; 440 441 strncpy(dip->disklabel.d_packname, "fictitious", 442 sizeof(dip->disklabel.d_packname)); 443 dip->disklabel.d_secperunit = 100; 444 445 dip->disklabel.d_bbsize = 2048; 446 dip->disklabel.d_sbsize = 2048; 447 448 /* 'a' partition covering the "whole" disk */ 449 dip->disklabel.d_partitions[0].p_offset = 0; 450 dip->disklabel.d_partitions[0].p_size = 100; 451 dip->disklabel.d_partitions[0].p_fstype = FS_UNUSED; 452 453 /* The raw partition is special */ 454 dip->disklabel.d_partitions[RAW_PART].p_offset = 0; 455 dip->disklabel.d_partitions[RAW_PART].p_size = 100; 456 dip->disklabel.d_partitions[RAW_PART].p_fstype = FS_UNUSED; 457 458 dip->disklabel.d_npartitions = MAXPARTITIONS; 459 460 dip->disklabel.d_magic = DISKMAGIC; 461 dip->disklabel.d_magic2 = DISKMAGIC; 462 dip->disklabel.d_checksum = dkcksum(&dip->disklabel); 463 464 /* Add to queue of disks */ 465 TAILQ_INSERT_TAIL(&disklist, dip, list); 466 } 467 468 469 /* Find info on given BIOS disk */ 470 struct diskinfo * 471 dklookup(int dev) 472 { 473 struct diskinfo *dip; 474 475 for(dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) 476 if(dip->bios_info.bios_number == dev) 477 return(dip); 478 479 return(NULL); 480 } 481 482 void 483 dump_diskinfo(void) 484 { 485 struct diskinfo *dip; 486 487 printf("Disk\tBIOS#\tType\tCyls\tHeads\tSecs\tFlags\tChecksum\n"); 488 for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) { 489 bios_diskinfo_t *bdi = &dip->bios_info; 490 int d = bdi->bios_number; 491 int u = d & 0x7f; 492 char c; 493 494 if (bdi->flags & BDI_EL_TORITO) { 495 c = 'c'; 496 u = 0; 497 } else { 498 c = (d & 0x80) ? 'h' : 'f'; 499 } 500 501 printf("%cd%d\t0x%x\t%s\t%d\t%d\t%d\t0x%x\t0x%x\n", 502 c, u, d, 503 (bdi->flags & BDI_BADLABEL)?"*none*":"label", 504 bdi->bios_cylinders, bdi->bios_heads, bdi->bios_sectors, 505 bdi->flags, bdi->checksum); 506 } 507 } 508 509 /* Find BIOS portion on given BIOS disk 510 * XXX - Use dklookup() instead. 511 */ 512 bios_diskinfo_t * 513 bios_dklookup(int dev) 514 { 515 struct diskinfo *dip; 516 517 dip = dklookup(dev); 518 if(dip) 519 return(&dip->bios_info); 520 521 return(NULL); 522 } 523 524 /* 525 * Checksum one more block on all harddrives 526 * 527 * Use the adler32() function from libz, 528 * as it is quick, small, and available. 529 */ 530 int 531 disksum(int blk) 532 { 533 struct diskinfo *dip, *dip2; 534 int st, reprobe = 0; 535 char *buf; 536 537 buf = alloca(DEV_BSIZE); 538 for(dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)){ 539 bios_diskinfo_t *bdi = &dip->bios_info; 540 541 /* Skip this disk if it is not a HD or has had an I/O error */ 542 if (!(bdi->bios_number & 0x80) || bdi->flags & BDI_INVALID) 543 continue; 544 545 /* Adler32 checksum */ 546 st = biosd_io(F_READ, bdi, blk, 1, buf); 547 if (st) { 548 bdi->flags |= BDI_INVALID; 549 continue; 550 } 551 bdi->checksum = adler32(bdi->checksum, buf, DEV_BSIZE); 552 553 for(dip2 = TAILQ_FIRST(&disklist); dip2 != dip; 554 dip2 = TAILQ_NEXT(dip2, list)){ 555 bios_diskinfo_t *bd = &dip2->bios_info; 556 if ((bd->bios_number & 0x80) && 557 !(bd->flags & BDI_INVALID) && 558 bdi->checksum == bd->checksum) 559 reprobe = 1; 560 } 561 } 562 563 return (reprobe); 564 } 565