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