1/* $OpenBSD: diskprobe.c,v 1.49 2024/06/04 20:31:35 krw Exp $ */ 2 3/* 4 * Copyright (c) 1997 Tobias Weingartner 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 */ 29 30/* We want the disk type names from disklabel.h */ 31#undef DKTYPENAMES 32 33#include <sys/param.h> 34#include <sys/queue.h> 35#include <sys/reboot.h> 36#include <sys/disklabel.h> 37#include <sys/hibernate.h> 38 39#include <lib/libz/zlib.h> 40#include <machine/biosvar.h> 41#include <stand/boot/bootarg.h> 42 43#include "disk.h" 44#include "biosdev.h" 45#include "libsa.h" 46 47#ifdef SOFTRAID 48#include "softraid_i386.h" 49#endif 50#ifdef EFIBOOT 51#include "efidev.h" 52#endif 53 54#define MAX_CKSUMLEN MAXBSIZE / DEV_BSIZE /* Max # of blks to cksum */ 55 56/* Local Prototypes */ 57static int disksum(int); 58 59int bootdev_has_hibernate(void); /* export for loadfile() */ 60 61/* List of disk devices we found/probed */ 62struct disklist_lh disklist; 63 64/* Pointer to boot device */ 65struct diskinfo *bootdev_dip; 66 67extern int debug; 68extern int bios_bootdev; 69extern int bios_cddev; 70 71#ifndef EFIBOOT 72static void 73diskinfo_init(struct diskinfo *dip) 74{ 75 bzero(dip, sizeof(*dip)); 76 dip->diskio = biosd_diskio; 77 dip->strategy = biosstrategy; 78} 79 80/* Probe for all BIOS floppies */ 81static void 82floppyprobe(void) 83{ 84 struct diskinfo *dip; 85 int i; 86 87 /* Floppies */ 88 for (i = 0; i < 4; i++) { 89 dip = alloc(sizeof(struct diskinfo)); 90 diskinfo_init(dip); 91 92 if (bios_getdiskinfo(i, &dip->bios_info)) { 93#ifdef BIOS_DEBUG 94 if (debug) 95 printf(" <!fd%u>", i); 96#endif 97 free(dip, sizeof(*dip)); 98 break; 99 } 100 101 printf(" fd%u", i); 102 103 /* Fill out best we can - (fd?) */ 104 dip->bios_info.bsd_dev = MAKEBOOTDEV(2, 0, 0, i, RAW_PART); 105 106 /* 107 * Delay reading the disklabel until we're sure we want 108 * to boot from the floppy. Doing this avoids a delay 109 * (sometimes very long) when trying to read the label 110 * and the drive is unplugged. 111 */ 112 dip->bios_info.flags |= BDI_BADLABEL; 113 114 /* Add to queue of disks */ 115 TAILQ_INSERT_TAIL(&disklist, dip, list); 116 } 117} 118 119/* Probe for all BIOS hard disks */ 120static void 121hardprobe(void) 122{ 123 struct diskinfo *dip; 124 int i; 125 u_int bsdunit, type; 126 u_int scsi = 0, ide = 0; 127 const char *dc = (const char *)((0x40 << 4) + 0x75); 128 129 /* Hard disks */ 130 for (i = 0x80; i < (0x80 + *dc); i++) { 131 dip = alloc(sizeof(struct diskinfo)); 132 diskinfo_init(dip); 133 134 if (bios_getdiskinfo(i, &dip->bios_info)) { 135#ifdef BIOS_DEBUG 136 if (debug) 137 printf(" <!hd%u>", i&0x7f); 138#endif 139 free(dip, sizeof(*dip)); 140 break; 141 } 142 143 printf(" hd%u%s", i&0x7f, (dip->bios_info.bios_edd > 0?"+":"")); 144 145 /* Try to find the label, to figure out device type */ 146 if ((bios_getdisklabel(&dip->bios_info, &dip->disklabel)) ) { 147 printf("*"); 148 bsdunit = ide++; 149 type = 0; /* XXX let it be IDE */ 150 } else { 151 /* Best guess */ 152 switch (dip->disklabel.d_type) { 153 case DTYPE_SCSI: 154 type = 4; 155 bsdunit = scsi++; 156 dip->bios_info.flags |= BDI_GOODLABEL; 157 break; 158 159 case DTYPE_ESDI: 160 case DTYPE_ST506: 161 type = 0; 162 bsdunit = ide++; 163 dip->bios_info.flags |= BDI_GOODLABEL; 164 break; 165 166 default: 167 dip->bios_info.flags |= BDI_BADLABEL; 168 type = 0; /* XXX Suggest IDE */ 169 bsdunit = ide++; 170 } 171 } 172 173 dip->bios_info.checksum = 0; /* just in case */ 174 /* Fill out best we can */ 175 dip->bsddev = dip->bios_info.bsd_dev = 176 MAKEBOOTDEV(type, 0, 0, bsdunit, RAW_PART); 177 check_hibernate(dip); 178 179 /* Add to queue of disks */ 180 TAILQ_INSERT_TAIL(&disklist, dip, list); 181 } 182} 183#endif 184 185#ifdef EFIBOOT 186static void 187efi_hardprobe(void) 188{ 189 int n; 190 struct diskinfo *dip, *dipt; 191 u_int bsdunit, type = 0; 192 u_int scsi= 0, ide = 0; 193 extern struct disklist_lh 194 efi_disklist; 195 196 n = 0; 197 TAILQ_FOREACH_SAFE(dip, &efi_disklist, list, dipt) { 198 TAILQ_REMOVE(&efi_disklist, dip, list); 199 printf(" hd%u", n); 200 201 dip->bios_info.bios_number = 0x80 | n; 202 /* Try to find the label, to figure out device type */ 203 if ((efi_getdisklabel(dip->efi_info, &dip->disklabel))) { 204 printf("*"); 205 bsdunit = ide++; 206 } else { 207 /* Best guess */ 208 switch (dip->disklabel.d_type) { 209 case DTYPE_SCSI: 210 type = 4; 211 bsdunit = scsi++; 212 dip->bios_info.flags |= BDI_GOODLABEL; 213 break; 214 215 case DTYPE_ESDI: 216 case DTYPE_ST506: 217 type = 0; 218 bsdunit = ide++; 219 dip->bios_info.flags |= BDI_GOODLABEL; 220 break; 221 222 default: 223 dip->bios_info.flags |= BDI_BADLABEL; 224 type = 0; /* XXX Suggest IDE */ 225 bsdunit = ide++; 226 } 227 } 228 229 dip->bios_info.checksum = 0; /* just in case */ 230 /* Fill out best we can */ 231 dip->bsddev = dip->bios_info.bsd_dev = 232 MAKEBOOTDEV(type, 0, 0, bsdunit, RAW_PART); 233 check_hibernate(dip); 234 235 /* Add to queue of disks */ 236 TAILQ_INSERT_TAIL(&disklist, dip, list); 237 n++; 238 } 239} 240#endif 241 242/* Probe for all BIOS supported disks */ 243u_int32_t bios_cksumlen; 244void 245diskprobe(void) 246{ 247 struct diskinfo *dip; 248 int i; 249 250 /* These get passed to kernel */ 251 bios_diskinfo_t *bios_diskinfo; 252 253 /* Init stuff */ 254 TAILQ_INIT(&disklist); 255 256#ifndef EFIBOOT 257 /* Do probes */ 258 floppyprobe(); 259#ifdef BIOS_DEBUG 260 if (debug) 261 printf(";"); 262#endif 263 hardprobe(); 264#else 265 efi_hardprobe(); 266#endif 267 268#ifdef SOFTRAID 269 srprobe(); 270#endif 271 272 /* Checksumming of hard disks */ 273 for (i = 0; disksum(i++) && i < MAX_CKSUMLEN; ) 274 ; 275 bios_cksumlen = i; 276 277 /* Get space for passing bios_diskinfo stuff to kernel */ 278 for (i = 0, dip = TAILQ_FIRST(&disklist); dip; 279 dip = TAILQ_NEXT(dip, list)) 280 i++; 281 bios_diskinfo = alloc(++i * sizeof(bios_diskinfo_t)); 282 283 /* Copy out the bios_diskinfo stuff */ 284 for (i = 0, dip = TAILQ_FIRST(&disklist); dip; 285 dip = TAILQ_NEXT(dip, list)) 286 bios_diskinfo[i++] = dip->bios_info; 287 288 bios_diskinfo[i++].bios_number = -1; 289 /* Register for kernel use */ 290 addbootarg(BOOTARG_CKSUMLEN, sizeof(u_int32_t), &bios_cksumlen); 291 addbootarg(BOOTARG_DISKINFO, i * sizeof(bios_diskinfo_t), 292 bios_diskinfo); 293} 294 295#ifndef EFIBOOT 296void 297cdprobe(void) 298{ 299 struct diskinfo *dip; 300 int cddev = bios_cddev & 0xff; 301 302 /* Another BIOS boot device... */ 303 304 if (bios_cddev == -1) /* Not been set, so don't use */ 305 return; 306 307 dip = alloc(sizeof(struct diskinfo)); 308 diskinfo_init(dip); 309 310#if 0 311 if (bios_getdiskinfo(cddev, &dip->bios_info)) { 312 printf(" <!cd0>"); /* XXX */ 313 free(dip, sizeof(*dip)); 314 return; 315 } 316#endif 317 318 printf(" cd0"); 319 320 dip->bios_info.bios_number = cddev; 321 dip->bios_info.bios_edd = 1; /* Use the LBA calls */ 322 dip->bios_info.flags |= BDI_GOODLABEL | BDI_EL_TORITO; 323 dip->bios_info.checksum = 0; /* just in case */ 324 dip->bios_info.bsd_dev = 325 MAKEBOOTDEV(6, 0, 0, 0, RAW_PART); 326 327 /* Create an imaginary disk label */ 328 dip->disklabel.d_secsize = 2048; 329 dip->disklabel.d_ntracks = 1; 330 dip->disklabel.d_nsectors = 100; 331 dip->disklabel.d_ncylinders = 1; 332 dip->disklabel.d_secpercyl = dip->disklabel.d_ntracks * 333 dip->disklabel.d_nsectors; 334 335 strncpy(dip->disklabel.d_typename, "ATAPI CD-ROM", 336 sizeof(dip->disklabel.d_typename)); 337 dip->disklabel.d_type = DTYPE_ATAPI; 338 339 strncpy(dip->disklabel.d_packname, "fictitious", 340 sizeof(dip->disklabel.d_packname)); 341 DL_SETDSIZE(&dip->disklabel, 100); 342 343 /* 'a' partition covering the "whole" disk */ 344 DL_SETPOFFSET(&dip->disklabel.d_partitions[0], 0); 345 DL_SETPSIZE(&dip->disklabel.d_partitions[0], 100); 346 dip->disklabel.d_partitions[0].p_fstype = FS_UNUSED; 347 348 /* The raw partition is special */ 349 DL_SETPOFFSET(&dip->disklabel.d_partitions[RAW_PART], 0); 350 DL_SETPSIZE(&dip->disklabel.d_partitions[RAW_PART], 100); 351 dip->disklabel.d_partitions[RAW_PART].p_fstype = FS_UNUSED; 352 353 dip->disklabel.d_npartitions = MAXPARTITIONS; 354 355 dip->disklabel.d_magic = DISKMAGIC; 356 dip->disklabel.d_magic2 = DISKMAGIC; 357 dip->disklabel.d_checksum = dkcksum(&dip->disklabel); 358 359 /* Add to queue of disks */ 360 TAILQ_INSERT_TAIL(&disklist, dip, list); 361} 362#endif 363 364 365/* Find info on given BIOS disk */ 366struct diskinfo * 367dklookup(int dev) 368{ 369 struct diskinfo *dip; 370 371 for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) 372 if (dip->bios_info.bios_number == dev) 373 return dip; 374 375 return NULL; 376} 377 378void 379dump_diskinfo(void) 380{ 381 struct diskinfo *dip; 382 383 printf("Disk\tBIOS#\tType\tCyls\tHeads\tSecs\tFlags\tChecksum\n"); 384 for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) { 385 bios_diskinfo_t *bdi = &dip->bios_info; 386 int d = bdi->bios_number; 387 int u = d & 0x7f; 388 char c; 389 390 if (bdi->flags & BDI_EL_TORITO) { 391 c = 'c'; 392 u = 0; 393 } else { 394 c = (d & 0x80) ? 'h' : 'f'; 395 } 396 397 printf("%cd%d\t0x%x\t%s\t%d\t%d\t%d\t0x%x\t0x%x\n", 398 c, u, d, 399 (bdi->flags & BDI_BADLABEL)?"*none*":"label", 400 bdi->bios_cylinders, bdi->bios_heads, bdi->bios_sectors, 401 bdi->flags, bdi->checksum); 402 } 403} 404 405/* Find BIOS portion on given BIOS disk 406 * XXX - Use dklookup() instead. 407 */ 408bios_diskinfo_t * 409bios_dklookup(int dev) 410{ 411 struct diskinfo *dip; 412 413 dip = dklookup(dev); 414 if (dip) 415 return &dip->bios_info; 416 417 return NULL; 418} 419 420/* 421 * Checksum one more block on all harddrives 422 * 423 * Use the adler32() function from libz, 424 * as it is quick, small, and available. 425 */ 426int 427disksum(int blk) 428{ 429 struct diskinfo *dip, *dip2; 430 int st, reprobe = 0; 431 char buf[DEV_BSIZE]; 432 433 for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) { 434 bios_diskinfo_t *bdi = &dip->bios_info; 435 436 /* Skip this disk if it is not a HD or has had an I/O error */ 437 if (!(bdi->bios_number & 0x80) || bdi->flags & BDI_INVALID) 438 continue; 439 440 /* Adler32 checksum */ 441 st = dip->diskio(F_READ, dip, blk, 1, buf); 442 if (st) { 443 bdi->flags |= BDI_INVALID; 444 continue; 445 } 446 bdi->checksum = adler32(bdi->checksum, buf, DEV_BSIZE); 447 448 for (dip2 = TAILQ_FIRST(&disklist); dip2 != dip; 449 dip2 = TAILQ_NEXT(dip2, list)) { 450 bios_diskinfo_t *bd = &dip2->bios_info; 451 if ((bd->bios_number & 0x80) && 452 !(bd->flags & BDI_INVALID) && 453 bdi->checksum == bd->checksum) 454 reprobe = 1; 455 } 456 } 457 458 return reprobe; 459} 460 461int 462bootdev_has_hibernate(void) 463{ 464 return ((bootdev_dip->bios_info.flags & BDI_HIBVALID)? 1 : 0); 465} 466 467void 468check_hibernate(struct diskinfo *dip) 469{ 470 uint8_t buf[DEV_BSIZE]; 471 daddr_t sec; 472 int error; 473 union hibernate_info *hib = (union hibernate_info *)&buf; 474 475 /* read hibernate */ 476 if (dip->disklabel.d_partitions[1].p_fstype != FS_SWAP || 477 DL_GETPSIZE(&dip->disklabel.d_partitions[1]) == 0) 478 return; 479 480 sec = DL_GETPOFFSET(&dip->disklabel.d_partitions[1]) + 481 DL_GETPSIZE(&dip->disklabel.d_partitions[1]) - 1; 482 483 error = dip->strategy(dip, F_READ, DL_SECTOBLK(&dip->disklabel, sec), 484 sizeof buf, &buf, NULL); 485 if (error == 0 && hib->magic == HIBERNATE_MAGIC) 486 dip->bios_info.flags |= BDI_HIBVALID; /* Hibernate present */ 487} 488