1 /* $OpenBSD: diskprobe.c,v 1.15 2014/07/12 20:58:31 tedu 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 38 #include <lib/libz/zlib.h> 39 #include <machine/biosvar.h> 40 #include <stand/boot/bootarg.h> 41 42 #include "disk.h" 43 #include "biosdev.h" 44 #include "libsa.h" 45 46 #ifdef SOFTRAID 47 #include "softraid.h" 48 #endif 49 50 #define MAX_CKSUMLEN MAXBSIZE / DEV_BSIZE /* Max # of blks to cksum */ 51 52 /* Local Prototypes */ 53 static int disksum(int); 54 55 /* List of disk devices we found/probed */ 56 struct disklist_lh disklist; 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 = 162 MAKEBOOTDEV(type, 0, 0, bsdunit, RAW_PART); 163 164 /* Add to queue of disks */ 165 TAILQ_INSERT_TAIL(&disklist, dip, list); 166 } 167 } 168 169 170 /* Probe for all BIOS supported disks */ 171 u_int32_t bios_cksumlen; 172 void 173 diskprobe(void) 174 { 175 struct diskinfo *dip; 176 int i; 177 178 /* These get passed to kernel */ 179 bios_diskinfo_t *bios_diskinfo; 180 181 /* Init stuff */ 182 TAILQ_INIT(&disklist); 183 184 /* Do probes */ 185 floppyprobe(); 186 #ifdef BIOS_DEBUG 187 if (debug) 188 printf(";"); 189 #endif 190 hardprobe(); 191 192 #ifdef SOFTRAID 193 srprobe(); 194 #endif 195 196 /* Checksumming of hard disks */ 197 for (i = 0; disksum(i++) && i < MAX_CKSUMLEN; ) 198 ; 199 bios_cksumlen = i; 200 201 /* Get space for passing bios_diskinfo stuff to kernel */ 202 for (i = 0, dip = TAILQ_FIRST(&disklist); dip; 203 dip = TAILQ_NEXT(dip, list)) 204 i++; 205 bios_diskinfo = alloc(++i * sizeof(bios_diskinfo_t)); 206 207 /* Copy out the bios_diskinfo stuff */ 208 for (i = 0, dip = TAILQ_FIRST(&disklist); dip; 209 dip = TAILQ_NEXT(dip, list)) 210 bios_diskinfo[i++] = dip->bios_info; 211 212 bios_diskinfo[i++].bios_number = -1; 213 /* Register for kernel use */ 214 addbootarg(BOOTARG_CKSUMLEN, sizeof(u_int32_t), &bios_cksumlen); 215 addbootarg(BOOTARG_DISKINFO, i * sizeof(bios_diskinfo_t), 216 bios_diskinfo); 217 } 218 219 220 void 221 cdprobe(void) 222 { 223 struct diskinfo *dip; 224 int cddev = bios_cddev & 0xff; 225 226 /* Another BIOS boot device... */ 227 228 if (bios_cddev == -1) /* Not been set, so don't use */ 229 return; 230 231 dip = alloc(sizeof(struct diskinfo)); 232 bzero(dip, sizeof(*dip)); 233 234 #if 0 235 if (bios_getdiskinfo(cddev, &dip->bios_info)) { 236 printf(" <!cd0>"); /* XXX */ 237 free(dip, 0); 238 return; 239 } 240 #endif 241 242 printf(" cd0"); 243 244 dip->bios_info.bios_number = cddev; 245 dip->bios_info.bios_edd = 1; /* Use the LBA calls */ 246 dip->bios_info.flags |= BDI_GOODLABEL | BDI_EL_TORITO; 247 dip->bios_info.checksum = 0; /* just in case */ 248 dip->bios_info.bsd_dev = 249 MAKEBOOTDEV(6, 0, 0, 0, RAW_PART); 250 251 /* Create an imaginary disk label */ 252 dip->disklabel.d_secsize = 2048; 253 dip->disklabel.d_ntracks = 1; 254 dip->disklabel.d_nsectors = 100; 255 dip->disklabel.d_ncylinders = 1; 256 dip->disklabel.d_secpercyl = dip->disklabel.d_ntracks * 257 dip->disklabel.d_nsectors; 258 if (dip->disklabel.d_secpercyl == 0) { 259 dip->disklabel.d_secpercyl = 100; 260 /* as long as it's not 0, since readdisklabel divides by it */ 261 } 262 263 strncpy(dip->disklabel.d_typename, "ATAPI CD-ROM", 264 sizeof(dip->disklabel.d_typename)); 265 dip->disklabel.d_type = DTYPE_ATAPI; 266 267 strncpy(dip->disklabel.d_packname, "fictitious", 268 sizeof(dip->disklabel.d_packname)); 269 DL_SETDSIZE(&dip->disklabel, 100); 270 271 dip->disklabel.d_bbsize = 2048; 272 dip->disklabel.d_sbsize = 2048; 273 274 /* 'a' partition covering the "whole" disk */ 275 DL_SETPOFFSET(&dip->disklabel.d_partitions[0], 0); 276 DL_SETPSIZE(&dip->disklabel.d_partitions[0], 100); 277 dip->disklabel.d_partitions[0].p_fstype = FS_UNUSED; 278 279 /* The raw partition is special */ 280 DL_SETPOFFSET(&dip->disklabel.d_partitions[RAW_PART], 0); 281 DL_SETPSIZE(&dip->disklabel.d_partitions[RAW_PART], 100); 282 dip->disklabel.d_partitions[RAW_PART].p_fstype = FS_UNUSED; 283 284 dip->disklabel.d_npartitions = MAXPARTITIONS; 285 286 dip->disklabel.d_magic = DISKMAGIC; 287 dip->disklabel.d_magic2 = DISKMAGIC; 288 dip->disklabel.d_checksum = dkcksum(&dip->disklabel); 289 290 /* Add to queue of disks */ 291 TAILQ_INSERT_TAIL(&disklist, dip, list); 292 } 293 294 295 /* Find info on given BIOS disk */ 296 struct diskinfo * 297 dklookup(int dev) 298 { 299 struct diskinfo *dip; 300 301 for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) 302 if (dip->bios_info.bios_number == dev) 303 return dip; 304 305 return NULL; 306 } 307 308 void 309 dump_diskinfo(void) 310 { 311 struct diskinfo *dip; 312 313 printf("Disk\tBIOS#\tType\tCyls\tHeads\tSecs\tFlags\tChecksum\n"); 314 for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) { 315 bios_diskinfo_t *bdi = &dip->bios_info; 316 int d = bdi->bios_number; 317 int u = d & 0x7f; 318 char c; 319 320 if (bdi->flags & BDI_EL_TORITO) { 321 c = 'c'; 322 u = 0; 323 } else { 324 c = (d & 0x80) ? 'h' : 'f'; 325 } 326 327 printf("%cd%d\t0x%x\t%s\t%d\t%d\t%d\t0x%x\t0x%x\n", 328 c, u, d, 329 (bdi->flags & BDI_BADLABEL)?"*none*":"label", 330 bdi->bios_cylinders, bdi->bios_heads, bdi->bios_sectors, 331 bdi->flags, bdi->checksum); 332 } 333 } 334 335 /* Find BIOS portion on given BIOS disk 336 * XXX - Use dklookup() instead. 337 */ 338 bios_diskinfo_t * 339 bios_dklookup(int dev) 340 { 341 struct diskinfo *dip; 342 343 dip = dklookup(dev); 344 if (dip) 345 return &dip->bios_info; 346 347 return NULL; 348 } 349 350 /* 351 * Checksum one more block on all harddrives 352 * 353 * Use the adler32() function from libz, 354 * as it is quick, small, and available. 355 */ 356 int 357 disksum(int blk) 358 { 359 struct diskinfo *dip, *dip2; 360 int st, reprobe = 0; 361 char *buf; 362 363 buf = alloca(DEV_BSIZE); 364 for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) { 365 bios_diskinfo_t *bdi = &dip->bios_info; 366 367 /* Skip this disk if it is not a HD or has had an I/O error */ 368 if (!(bdi->bios_number & 0x80) || bdi->flags & BDI_INVALID) 369 continue; 370 371 /* Adler32 checksum */ 372 st = biosd_io(F_READ, bdi, blk, 1, buf); 373 if (st) { 374 bdi->flags |= BDI_INVALID; 375 continue; 376 } 377 bdi->checksum = adler32(bdi->checksum, buf, DEV_BSIZE); 378 379 for (dip2 = TAILQ_FIRST(&disklist); dip2 != dip; 380 dip2 = TAILQ_NEXT(dip2, list)) { 381 bios_diskinfo_t *bd = &dip2->bios_info; 382 if ((bd->bios_number & 0x80) && 383 !(bd->flags & BDI_INVALID) && 384 bdi->checksum == bd->checksum) 385 reprobe = 1; 386 } 387 } 388 389 return reprobe; 390 } 391