diskprobe.c revision 1.26
1/* $OpenBSD: diskprobe.c,v 1.26 2004/03/19 13:48:18 tom 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 */ 47static int disksum(int); 48 49/* List of disk devices we found/probed */ 50struct disklist_lh disklist; 51 52/* Pointer to boot device */ 53struct diskinfo *bootdev_dip; 54 55extern int debug; 56 57/* Probe for all BIOS floppies */ 58static void 59floppyprobe(void) 60{ 61 struct diskinfo *dip; 62 int i; 63 64 /* Floppies */ 65 for (i = 0; i < 4; i++) { 66 dip = alloc(sizeof(struct diskinfo)); 67 bzero(dip, sizeof(*dip)); 68 69 if (bios_getdiskinfo(i, &dip->bios_info)) { 70#ifdef BIOS_DEBUG 71 if (debug) 72 printf(" <!fd%u>", i); 73#endif 74 free(dip, 0); 75 break; 76 } 77 78 printf(" fd%u", i); 79 80 /* Fill out best we can - (fd?) */ 81 dip->bios_info.bsd_dev = MAKEBOOTDEV(2, 0, 0, i, RAW_PART); 82 83 /* 84 * Delay reading the disklabel until we're sure we want 85 * to boot from the floppy. Doing this avoids a delay 86 * (sometimes very long) when trying to read the label 87 * and the drive is unplugged. 88 */ 89 dip->bios_info.flags |= BDI_BADLABEL; 90 91 /* Add to queue of disks */ 92 TAILQ_INSERT_TAIL(&disklist, dip, list); 93 } 94} 95 96 97/* Probe for all BIOS hard disks */ 98static void 99hardprobe(void) 100{ 101 struct diskinfo *dip; 102 int i; 103 u_int bsdunit, type; 104 u_int scsi = 0, ide = 0; 105 const char *dc = (const char *)((0x40 << 4) + 0x75); 106 107 /* Hard disks */ 108 for (i = 0x80; i < (0x80 + *dc); i++) { 109 dip = alloc(sizeof(struct diskinfo)); 110 bzero(dip, sizeof(*dip)); 111 112 if (bios_getdiskinfo(i, &dip->bios_info)) { 113#ifdef BIOS_DEBUG 114 if (debug) 115 printf(" <!hd%u>", i&0x7f); 116#endif 117 free(dip, 0); 118 break; 119 } 120 121 printf(" hd%u%s", i&0x7f, (dip->bios_info.bios_edd > 0?"+":"")); 122 123 /* Try to find the label, to figure out device type */ 124 if ((bios_getdisklabel(&dip->bios_info, &dip->disklabel)) ) { 125 printf("*"); 126 bsdunit = ide++; 127 type = 0; /* XXX let it be IDE */ 128 } else { 129 /* Best guess */ 130 switch (dip->disklabel.d_type) { 131 case DTYPE_SCSI: 132 type = 4; 133 bsdunit = scsi++; 134 dip->bios_info.flags |= BDI_GOODLABEL; 135 break; 136 137 case DTYPE_ESDI: 138 case DTYPE_ST506: 139 type = 0; 140 bsdunit = ide++; 141 dip->bios_info.flags |= BDI_GOODLABEL; 142 break; 143 144 default: 145 dip->bios_info.flags |= BDI_BADLABEL; 146 type = 0; /* XXX Suggest IDE */ 147 bsdunit = ide++; 148 } 149 } 150 151 dip->bios_info.checksum = 0; /* just in case */ 152 /* Fill out best we can */ 153 dip->bios_info.bsd_dev = 154 MAKEBOOTDEV(type, 0, 0, bsdunit, RAW_PART); 155 156 /* Add to queue of disks */ 157 TAILQ_INSERT_TAIL(&disklist, dip, list); 158 } 159} 160 161 162/* Probe for all BIOS supported disks */ 163u_int32_t bios_cksumlen; 164void 165diskprobe(void) 166{ 167 struct diskinfo *dip; 168 int i; 169 170 /* These get passed to kernel */ 171 bios_diskinfo_t *bios_diskinfo; 172 173 /* Init stuff */ 174 TAILQ_INIT(&disklist); 175 176 /* Do probes */ 177 floppyprobe(); 178#ifdef BIOS_DEBUG 179 if (debug) 180 printf(";"); 181#endif 182 hardprobe(); 183 184 /* Checksumming of hard disks */ 185 for (i = 0; disksum(i++) && i < MAX_CKSUMLEN; ) 186 ; 187 bios_cksumlen = i; 188 189 /* Get space for passing bios_diskinfo stuff to kernel */ 190 for (i = 0, dip = TAILQ_FIRST(&disklist); dip; 191 dip = TAILQ_NEXT(dip, list)) 192 i++; 193 bios_diskinfo = alloc(++i * sizeof(bios_diskinfo_t)); 194 195 /* Copy out the bios_diskinfo stuff */ 196 for (i = 0, dip = TAILQ_FIRST(&disklist); dip; 197 dip = TAILQ_NEXT(dip, list)) 198 bios_diskinfo[i++] = dip->bios_info; 199 200 bios_diskinfo[i++].bios_number = -1; 201 /* Register for kernel use */ 202 addbootarg(BOOTARG_CKSUMLEN, sizeof(u_int32_t), &bios_cksumlen); 203 addbootarg(BOOTARG_DISKINFO, i * sizeof(bios_diskinfo_t), 204 bios_diskinfo); 205} 206 207 208/* Find info on given BIOS disk */ 209struct diskinfo * 210dklookup(int dev) 211{ 212 struct diskinfo *dip; 213 214 for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) 215 if (dip->bios_info.bios_number == dev) 216 return dip; 217 218 return NULL; 219} 220 221void 222dump_diskinfo(void) 223{ 224 struct diskinfo *dip; 225 226 printf("Disk\tBIOS#\tType\tCyls\tHeads\tSecs\tFlags\tChecksum\n"); 227 for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) { 228 bios_diskinfo_t *bdi = &dip->bios_info; 229 int d = bdi->bios_number; 230 231 printf("%cd%d\t0x%x\t%s\t%d\t%d\t%d\t0x%x\t0x%x\n", 232 (d & 0x80)?'h':'f', d & 0x7F, d, 233 (bdi->flags & BDI_BADLABEL)?"*none*":"label", 234 bdi->bios_cylinders, bdi->bios_heads, bdi->bios_sectors, 235 bdi->flags, bdi->checksum); 236 } 237} 238 239/* Find BIOS portion on given BIOS disk 240 * XXX - Use dklookup() instead. 241 */ 242bios_diskinfo_t * 243bios_dklookup(int dev) 244{ 245 struct diskinfo *dip; 246 247 dip = dklookup(dev); 248 if (dip) 249 return &dip->bios_info; 250 251 return NULL; 252} 253 254/* 255 * Checksum one more block on all harddrives 256 * 257 * Use the adler32() function from libz, 258 * as it is quick, small, and available. 259 */ 260int 261disksum(int blk) 262{ 263 struct diskinfo *dip, *dip2; 264 int st, reprobe = 0; 265 char *buf; 266 267 buf = alloca(DEV_BSIZE); 268 for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) { 269 bios_diskinfo_t *bdi = &dip->bios_info; 270 271 /* Skip this disk if it is not a HD or has had an I/O error */ 272 if (!(bdi->bios_number & 0x80) || bdi->flags & BDI_INVALID) 273 continue; 274 275 /* Adler32 checksum */ 276 st = biosd_io(F_READ, bdi, blk, 1, buf); 277 if (st) { 278 bdi->flags |= BDI_INVALID; 279 continue; 280 } 281 bdi->checksum = adler32(bdi->checksum, buf, DEV_BSIZE); 282 283 for (dip2 = TAILQ_FIRST(&disklist); dip2 != dip; 284 dip2 = TAILQ_NEXT(dip2, list)) { 285 bios_diskinfo_t *bd = &dip2->bios_info; 286 if ((bd->bios_number & 0x80) && 287 !(bd->flags & BDI_INVALID) && 288 bdi->checksum == bd->checksum) 289 reprobe = 1; 290 } 291 } 292 293 return reprobe; 294} 295