diskprobe.c revision 1.24
1/* $OpenBSD: diskprobe.c,v 1.24 2019/05/10 21:20:43 mlarkin 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_amd64.h" 49#endif 50 51#define MAX_CKSUMLEN MAXBSIZE / DEV_BSIZE /* Max # of blks to cksum */ 52 53/* Local Prototypes */ 54static int disksum(int); 55 56int bootdev_has_hibernate(void); /* export for loadfile() */ 57 58/* List of disk devices we found/probed */ 59struct disklist_lh disklist; 60 61/* Pointer to boot device */ 62struct diskinfo *bootdev_dip; 63 64extern int debug; 65extern int bios_bootdev; 66extern int bios_cddev; 67 68static void 69diskinfo_init(struct diskinfo *dip) 70{ 71 bzero(dip, sizeof(*dip)); 72 dip->diskio = biosd_diskio; 73 dip->strategy = biosstrategy; 74} 75 76/* Probe for all BIOS floppies */ 77static void 78floppyprobe(void) 79{ 80 struct diskinfo *dip; 81 int i; 82 83 /* Floppies */ 84 for (i = 0; i < 4; i++) { 85 dip = alloc(sizeof(struct diskinfo)); 86 diskinfo_init(dip); 87 88 if (bios_getdiskinfo(i, &dip->bios_info)) { 89#ifdef BIOS_DEBUG 90 if (debug) 91 printf(" <!fd%u>", i); 92#endif 93 free(dip, sizeof(*dip)); 94 break; 95 } 96 97 printf(" fd%u", i); 98 99 /* Fill out best we can - (fd?) */ 100 dip->bios_info.bsd_dev = MAKEBOOTDEV(2, 0, 0, i, RAW_PART); 101 102 /* 103 * Delay reading the disklabel until we're sure we want 104 * to boot from the floppy. Doing this avoids a delay 105 * (sometimes very long) when trying to read the label 106 * and the drive is unplugged. 107 */ 108 dip->bios_info.flags |= BDI_BADLABEL; 109 110 /* Add to queue of disks */ 111 TAILQ_INSERT_TAIL(&disklist, dip, list); 112 } 113} 114 115/* Probe for all BIOS hard disks */ 116static void 117hardprobe(void) 118{ 119 struct diskinfo *dip; 120 int i; 121 u_int bsdunit, type; 122 u_int scsi = 0, ide = 0; 123 const char *dc = (const char *)((0x40 << 4) + 0x75); 124 125 /* Hard disks */ 126 for (i = 0x80; i < (0x80 + *dc); i++) { 127 dip = alloc(sizeof(struct diskinfo)); 128 diskinfo_init(dip); 129 130 if (bios_getdiskinfo(i, &dip->bios_info)) { 131#ifdef BIOS_DEBUG 132 if (debug) 133 printf(" <!hd%u>", i&0x7f); 134#endif 135 free(dip, sizeof(*dip)); 136 break; 137 } 138 139 printf(" hd%u%s", i&0x7f, (dip->bios_info.bios_edd > 0?"+":"")); 140 141 /* Try to find the label, to figure out device type */ 142 if ((bios_getdisklabel(&dip->bios_info, &dip->disklabel)) ) { 143 printf("*"); 144 bsdunit = ide++; 145 type = 0; /* XXX let it be IDE */ 146 } else { 147 /* Best guess */ 148 switch (dip->disklabel.d_type) { 149 case DTYPE_SCSI: 150 type = 4; 151 bsdunit = scsi++; 152 dip->bios_info.flags |= BDI_GOODLABEL; 153 break; 154 155 case DTYPE_ESDI: 156 case DTYPE_ST506: 157 type = 0; 158 bsdunit = ide++; 159 dip->bios_info.flags |= BDI_GOODLABEL; 160 break; 161 162 default: 163 dip->bios_info.flags |= BDI_BADLABEL; 164 type = 0; /* XXX Suggest IDE */ 165 bsdunit = ide++; 166 } 167 } 168 169 dip->bios_info.checksum = 0; /* just in case */ 170 /* Fill out best we can */ 171 dip->bsddev = dip->bios_info.bsd_dev = 172 MAKEBOOTDEV(type, 0, 0, bsdunit, RAW_PART); 173 check_hibernate(dip); 174 175 /* Add to queue of disks */ 176 TAILQ_INSERT_TAIL(&disklist, dip, list); 177 } 178} 179 180/* Probe for all BIOS supported disks */ 181u_int32_t bios_cksumlen; 182void 183diskprobe(void) 184{ 185 struct diskinfo *dip; 186 int i; 187 188 /* These get passed to kernel */ 189 bios_diskinfo_t *bios_diskinfo; 190 191 /* Init stuff */ 192 TAILQ_INIT(&disklist); 193 194 /* Do probes */ 195 floppyprobe(); 196#ifdef BIOS_DEBUG 197 if (debug) 198 printf(";"); 199#endif 200 hardprobe(); 201 202#ifdef SOFTRAID 203 srprobe(); 204#endif 205 206 /* Checksumming of hard disks */ 207 for (i = 0; disksum(i++) && i < MAX_CKSUMLEN; ) 208 ; 209 bios_cksumlen = i; 210 211 /* Get space for passing bios_diskinfo stuff to kernel */ 212 for (i = 0, dip = TAILQ_FIRST(&disklist); dip; 213 dip = TAILQ_NEXT(dip, list)) 214 i++; 215 bios_diskinfo = alloc(++i * sizeof(bios_diskinfo_t)); 216 217 /* Copy out the bios_diskinfo stuff */ 218 for (i = 0, dip = TAILQ_FIRST(&disklist); dip; 219 dip = TAILQ_NEXT(dip, list)) 220 bios_diskinfo[i++] = dip->bios_info; 221 222 bios_diskinfo[i++].bios_number = -1; 223 /* Register for kernel use */ 224 addbootarg(BOOTARG_CKSUMLEN, sizeof(u_int32_t), &bios_cksumlen); 225 addbootarg(BOOTARG_DISKINFO, i * sizeof(bios_diskinfo_t), 226 bios_diskinfo); 227} 228 229void 230cdprobe(void) 231{ 232 struct diskinfo *dip; 233 int cddev = bios_cddev & 0xff; 234 235 /* Another BIOS boot device... */ 236 237 if (bios_cddev == -1) /* Not been set, so don't use */ 238 return; 239 240 dip = alloc(sizeof(struct diskinfo)); 241 diskinfo_init(dip); 242 243#if 0 244 if (bios_getdiskinfo(cddev, &dip->bios_info)) { 245 printf(" <!cd0>"); /* XXX */ 246 free(dip, sizeof(*dip)); 247 return; 248 } 249#endif 250 251 printf(" cd0"); 252 253 dip->bios_info.bios_number = cddev; 254 dip->bios_info.bios_edd = 1; /* Use the LBA calls */ 255 dip->bios_info.flags |= BDI_GOODLABEL | BDI_EL_TORITO; 256 dip->bios_info.checksum = 0; /* just in case */ 257 dip->bios_info.bsd_dev = 258 MAKEBOOTDEV(6, 0, 0, 0, RAW_PART); 259 260 /* Create an imaginary disk label */ 261 dip->disklabel.d_secsize = 2048; 262 dip->disklabel.d_ntracks = 1; 263 dip->disklabel.d_nsectors = 100; 264 dip->disklabel.d_ncylinders = 1; 265 dip->disklabel.d_secpercyl = dip->disklabel.d_ntracks * 266 dip->disklabel.d_nsectors; 267 268 strncpy(dip->disklabel.d_typename, "ATAPI CD-ROM", 269 sizeof(dip->disklabel.d_typename)); 270 dip->disklabel.d_type = DTYPE_ATAPI; 271 272 strncpy(dip->disklabel.d_packname, "fictitious", 273 sizeof(dip->disklabel.d_packname)); 274 DL_SETDSIZE(&dip->disklabel, 100); 275 276 dip->disklabel.d_bbsize = 2048; 277 dip->disklabel.d_sbsize = 2048; 278 279 /* 'a' partition covering the "whole" disk */ 280 DL_SETPOFFSET(&dip->disklabel.d_partitions[0], 0); 281 DL_SETPSIZE(&dip->disklabel.d_partitions[0], 100); 282 dip->disklabel.d_partitions[0].p_fstype = FS_UNUSED; 283 284 /* The raw partition is special */ 285 DL_SETPOFFSET(&dip->disklabel.d_partitions[RAW_PART], 0); 286 DL_SETPSIZE(&dip->disklabel.d_partitions[RAW_PART], 100); 287 dip->disklabel.d_partitions[RAW_PART].p_fstype = FS_UNUSED; 288 289 dip->disklabel.d_npartitions = MAXPARTITIONS; 290 291 dip->disklabel.d_magic = DISKMAGIC; 292 dip->disklabel.d_magic2 = DISKMAGIC; 293 dip->disklabel.d_checksum = dkcksum(&dip->disklabel); 294 295 /* Add to queue of disks */ 296 TAILQ_INSERT_TAIL(&disklist, dip, list); 297} 298 299/* Find info on given BIOS disk */ 300struct diskinfo * 301dklookup(int dev) 302{ 303 struct diskinfo *dip; 304 305 for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) 306 if (dip->bios_info.bios_number == dev) 307 return dip; 308 309 return NULL; 310} 311 312void 313dump_diskinfo(void) 314{ 315 struct diskinfo *dip; 316 317 printf("Disk\tBIOS#\tType\tCyls\tHeads\tSecs\tFlags\tChecksum\n"); 318 for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) { 319 bios_diskinfo_t *bdi = &dip->bios_info; 320 int d = bdi->bios_number; 321 int u = d & 0x7f; 322 char c; 323 324 if (bdi->flags & BDI_EL_TORITO) { 325 c = 'c'; 326 u = 0; 327 } else { 328 c = (d & 0x80) ? 'h' : 'f'; 329 } 330 331 printf("%cd%d\t0x%x\t%s\t%d\t%d\t%d\t0x%x\t0x%x\n", 332 c, u, d, 333 (bdi->flags & BDI_BADLABEL)?"*none*":"label", 334 bdi->bios_cylinders, bdi->bios_heads, bdi->bios_sectors, 335 bdi->flags, bdi->checksum); 336 } 337} 338 339/* Find BIOS portion on given BIOS disk 340 * XXX - Use dklookup() instead. 341 */ 342bios_diskinfo_t * 343bios_dklookup(int dev) 344{ 345 struct diskinfo *dip; 346 347 dip = dklookup(dev); 348 if (dip) 349 return &dip->bios_info; 350 351 return NULL; 352} 353 354/* 355 * Checksum one more block on all harddrives 356 * 357 * Use the adler32() function from libz, 358 * as it is quick, small, and available. 359 */ 360int 361disksum(int blk) 362{ 363 struct diskinfo *dip, *dip2; 364 int st, reprobe = 0; 365 char buf[DEV_BSIZE]; 366 367 for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) { 368 bios_diskinfo_t *bdi = &dip->bios_info; 369 370 /* Skip this disk if it is not a HD or has had an I/O error */ 371 if (!(bdi->bios_number & 0x80) || bdi->flags & BDI_INVALID) 372 continue; 373 374 /* Adler32 checksum */ 375 st = dip->diskio(F_READ, dip, blk, 1, buf); 376 if (st) { 377 bdi->flags |= BDI_INVALID; 378 continue; 379 } 380 bdi->checksum = adler32(bdi->checksum, buf, DEV_BSIZE); 381 382 for (dip2 = TAILQ_FIRST(&disklist); dip2 != dip; 383 dip2 = TAILQ_NEXT(dip2, list)) { 384 bios_diskinfo_t *bd = &dip2->bios_info; 385 if ((bd->bios_number & 0x80) && 386 !(bd->flags & BDI_INVALID) && 387 bdi->checksum == bd->checksum) 388 reprobe = 1; 389 } 390 } 391 392 return reprobe; 393} 394 395int 396bootdev_has_hibernate(void) 397{ 398 return ((bootdev_dip->bios_info.flags & BDI_HIBVALID)? 1 : 0); 399} 400 401void 402check_hibernate(struct diskinfo *dip) 403{ 404 daddr_t sec; 405 int error; 406 union hibernate_info hib; 407 408 /* read hibernate */ 409 if (dip->disklabel.d_partitions[1].p_fstype != FS_SWAP || 410 DL_GETPSIZE(&dip->disklabel.d_partitions[1]) == 0) 411 return; 412 413 sec = DL_GETPOFFSET(&dip->disklabel.d_partitions[1]) + 414 DL_GETPSIZE(&dip->disklabel.d_partitions[1]) - 415 (sizeof(union hibernate_info) / DEV_BSIZE); 416 417 error = dip->strategy(dip, F_READ, (daddr32_t)sec, sizeof hib, &hib, NULL); 418 if (error == 0 && hib.magic == HIBERNATE_MAGIC) 419 dip->bios_info.flags |= BDI_HIBVALID; /* Hibernate present */ 420} 421