1 /*- 2 * Copyright (c) 1990 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * William Jolitz. 7 * 8 * %sccs.include.redist.c% 9 * 10 * @(#)wd.c 7.3 (Berkeley) 05/04/91 11 */ 12 13 /* device driver for winchester disk */ 14 15 #include "param.h" 16 #include "dkbad.h" 17 #include "disklabel.h" 18 #include "i386/isa/isa.h" 19 #include "i386/isa/wdreg.h" 20 #include "saio.h" 21 22 #define NWD 2 /* number of hard disk units supported, max 2 */ 23 #define RETRIES 5 /* number of retries before giving up */ 24 25 int noretries, wdquiet; 26 /*#define WDDEBUG*/ 27 28 #ifdef SMALL 29 extern struct disklabel disklabel; 30 #else 31 struct disklabel wdsizes[NWD]; 32 #endif 33 34 extern cyloffset ; /* bootstrap's idea of cylinder for disklabel */ 35 36 /* 37 * Record for the bad block forwarding code. 38 * This is initialized to be empty until the bad-sector table 39 * is read from the disk. 40 */ 41 #define TRKSEC(trk,sec) ((trk << 8) + sec) 42 43 struct dkbad dkbad[NWD]; 44 static wdcport; 45 46 wdopen(io) 47 register struct iob *io; 48 { 49 register struct disklabel *dd; 50 51 #ifdef WDDEBUG 52 printf("wdopen "); 53 #endif 54 #ifdef SMALL 55 dd = &disklabel; 56 #else 57 dd = &wdsizes[io->i_unit]; 58 if (io->i_part > 8) 59 _stop("Invalid partition number"); 60 if(io->i_ctlr > 1) 61 _stop("Invalid controller number"); 62 #endif 63 if (wdinit(io)) 64 _stop("wd initialization error"); 65 io->i_boff = dd->d_partitions[io->i_part].p_offset ; 66 return(0); 67 } 68 69 wdstrategy(io,func) 70 register struct iob *io; 71 { 72 register int iosize; /* number of sectors to do IO for this loop */ 73 register daddr_t sector; 74 int nblocks, cyloff; 75 int unit, partition; 76 char *address; 77 register struct disklabel *dd; 78 79 unit = io->i_unit; 80 partition = io->i_part; 81 #ifdef WDDEBUG 82 printf("wdstrat %d %d ", unit, partition); 83 #endif 84 #ifdef SMALL 85 dd = &disklabel; 86 #else 87 dd = &wdsizes[unit]; 88 #endif 89 iosize = io->i_cc / dd->d_secsize; 90 /* 91 * Convert PGSIZE "blocks" to sectors. 92 * Note: doing the conversions this way limits the partition size 93 * to about 8 million sectors (1-8 Gb). 94 */ 95 sector = (unsigned long) io->i_bn * DEV_BSIZE / dd->d_secsize; 96 nblocks = dd->d_partitions[partition].p_size; 97 #ifndef SMALL 98 if (iosize < 0 || sector + iosize > nblocks || sector < 0) { 99 #ifdef WDDEBUG 100 printf("bn = %d; sectors = %d; partition = %d; fssize = %d\n", 101 io->i_bn, iosize, partition, nblocks); 102 #endif 103 printf("wdstrategy - I/O out of filesystem boundaries\n"); 104 return(-1); 105 } 106 if (io->i_bn * DEV_BSIZE % dd->d_secsize) { 107 printf("wdstrategy - transfer starts in midsector\n"); 108 return(-1); 109 } 110 if (io->i_cc % dd->d_secsize) { 111 printf("wd: transfer of partial sector\n"); 112 return(-1); 113 } 114 #endif 115 116 address = io->i_ma; 117 while (iosize > 0) { 118 if (wdio(func, unit, sector, address)) 119 return(-1); 120 iosize--; 121 sector++; 122 address += dd->d_secsize; 123 } 124 return(io->i_cc); 125 } 126 127 /* 128 * Routine to do a one-sector I/O operation, and wait for it 129 * to complete. 130 */ 131 wdio(func, unit, blknm, addr) 132 short *addr; 133 { 134 struct disklabel *dd; 135 register wdc = wdcport; 136 struct bt_bad *bt_ptr; 137 int i; 138 int retries = 0; 139 long cylin, head, sector; 140 u_char opcode, erro; 141 142 #ifdef SMALL 143 dd = &disklabel; 144 #else 145 dd = &wdsizes[unit]; 146 #endif 147 if (func == F_WRITE) 148 opcode = WDCC_WRITE; 149 else 150 opcode = WDCC_READ; 151 152 /* Calculate data for output. */ 153 cylin = blknm / dd->d_secpercyl; 154 head = (blknm % dd->d_secpercyl) / dd->d_nsectors; 155 sector = blknm % dd->d_nsectors; 156 157 /* 158 * See if the current block is in the bad block list. 159 */ 160 if (blknm > BBSIZE/DEV_BSIZE) /* should be BBSIZE */ 161 for (bt_ptr = dkbad[unit].bt_bad; bt_ptr->bt_cyl != -1; bt_ptr++) { 162 if (bt_ptr->bt_cyl > cylin) 163 /* Sorted list, and we passed our cylinder. quit. */ 164 break; 165 if (bt_ptr->bt_cyl == cylin && 166 bt_ptr->bt_trksec == (head << 8) + sector) { 167 /* 168 * Found bad block. Calculate new block addr. 169 * This starts at the end of the disk (skip the 170 * last track which is used for the bad block list), 171 * and works backwards to the front of the disk. 172 */ 173 #ifdef WDDEBUG 174 printf("--- badblock code -> Old = %d; ", 175 blknm); 176 #endif 177 blknm = dd->d_secperunit - dd->d_nsectors 178 - (bt_ptr - dkbad[unit].bt_bad) - 1; 179 cylin = blknm / dd->d_secpercyl; 180 head = (blknm % dd->d_secpercyl) / dd->d_nsectors; 181 sector = blknm % dd->d_nsectors; 182 #ifdef WDDEBUG 183 printf("new = %d\n", blknm); 184 #endif 185 break; 186 } 187 } 188 189 sector += 1; 190 retry: 191 #ifdef WDDEBUG 192 printf("sec %d sdh %x cylin %d ", sector, 193 WDSD_IBM | (unit<<4) | (head & 0xf), cylin); 194 #endif 195 outb(wdc+wd_precomp, 0xff); 196 outb(wdc+wd_seccnt, 1); 197 outb(wdc+wd_sector, sector); 198 outb(wdc+wd_cyl_lo, cylin); 199 outb(wdc+wd_cyl_hi, cylin >> 8); 200 201 /* Set up the SDH register (select drive). */ 202 outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf)); 203 while ((inb(wdc+wd_status) & WDCS_READY) == 0) ; 204 205 outb(wdc+wd_command, opcode); 206 while (opcode == WDCC_READ && (inb(wdc+wd_status) & WDCS_BUSY)) 207 ; 208 /* Did we get an error? */ 209 if (opcode == WDCC_READ && (inb(wdc+wd_status) & WDCS_ERR)) 210 goto error; 211 212 /* Ready to remove data? */ 213 while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ; 214 215 if (opcode == WDCC_READ) 216 insw(wdc+wd_data,addr,256); 217 else outsw(wdc+wd_data,addr,256); 218 219 /* Check data request (should be done). */ 220 if (inb(wdc+wd_status) & WDCS_DRQ) goto error; 221 222 while (opcode == WDCC_WRITE && (inb(wdc+wd_status) & WDCS_BUSY)) ; 223 224 if (inb(wdc+wd_status) & WDCS_ERR) goto error; 225 226 #ifdef WDDEBUG 227 printf("+"); 228 #endif 229 return (0); 230 error: 231 erro = inb(wdc+wd_error); 232 if (++retries < RETRIES) 233 goto retry; 234 if (!wdquiet) 235 #ifdef SMALL 236 printf("wd%d: hard error: sector %d status %x error %x\n", unit, 237 blknm, inb(wdc+wd_status), erro); 238 #else 239 printf("wd%d: hard %s error: sector %d status %b error %b\n", unit, 240 opcode == WDCC_READ? "read" : "write", blknm, 241 inb(wdc+wd_status), WDCS_BITS, erro, WDERR_BITS); 242 #endif 243 return (-1); 244 } 245 246 wdinit(io) 247 struct iob *io; 248 { 249 register wdc; 250 struct disklabel *dd; 251 unsigned int unit; 252 struct dkbad *db; 253 int i, errcnt = 0; 254 char buf[512]; 255 static open[NWD]; 256 257 unit = io->i_unit; 258 if (open[unit]) return(0); 259 260 wdcport = io->i_ctlr ? IO_WD2 : IO_WD1; 261 wdc = wdcport; 262 263 #ifdef SMALL 264 dd = &disklabel; 265 #else 266 /* reset controller */ 267 outb(wdc+wd_ctlr, 12); wait(10); outb(wdc+wd_ctlr, 8); 268 wdwait(); 269 270 dd = &wdsizes[unit]; 271 272 tryagainrecal: 273 /* set SDH, step rate, do restore to recalibrate drive */ 274 outb(wdc+wd_sdh, WDSD_IBM | (unit << 4)); 275 wdwait(); 276 outb(wdc+wd_command, WDCC_RESTORE | WD_STEP); 277 wdwait(); 278 if ((i = inb(wdc+wd_status)) & WDCS_ERR) { 279 /*#ifdef SMALL 280 printf("wd%d: recal status %x error %x\n", 281 unit, i, inb(wdc+wd_error)); 282 #else*/ 283 printf("wd%d: recal status %b error %b\n", 284 unit, i, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS); 285 /*#endif*/ 286 if (++errcnt < 10) 287 goto tryagainrecal; 288 return(-1); 289 } 290 291 /* 292 * Some controllers require this (after a recal they 293 * revert to a logical translation mode to compensate for 294 * dos limitation on 10-bit cylinders -- *shudder* -wfj) 295 * note: cylinders *must* be fewer than or equal to 8 to 296 * compensate for some IDE drives that latch this for all time. 297 */ 298 outb(wdc+wd_sdh, WDSD_IBM | (unit << 4) + 8 -1); 299 outb(wdc+wd_seccnt, 35 ); 300 outb(wdc+wd_cyl_lo, 1224); 301 outb(wdc+wd_cyl_hi, 1224/256); 302 outb(wdc+wd_command, 0x91); 303 while (inb(wdc+wd_status) & WDCS_BUSY) ; 304 305 errcnt = 0; 306 retry: 307 /* 308 * Read in LABELSECTOR to get the pack label and geometry. 309 */ 310 outb(wdc+wd_precomp, 0xff); /* sometimes this is head bit 3 */ 311 outb(wdc+wd_seccnt, 1); 312 outb(wdc+wd_sector, LABELSECTOR + 1); 313 outb(wdc+wd_cyl_lo, (cyloffset & 0xff)); 314 outb(wdc+wd_cyl_hi, (cyloffset >> 8)); 315 outb(wdc+wd_sdh, WDSD_IBM | (unit << 4)); 316 wdwait(); 317 outb(wdc+wd_command, WDCC_READ); 318 wdwait(); 319 if ((i = inb(wdc+wd_status)) & WDCS_ERR) { 320 int err; 321 322 err = inb(wdc+wd_error); 323 if (++errcnt < RETRIES) 324 goto retry; 325 if (!wdquiet) 326 /*#ifdef SMALL 327 printf("wd%d: reading label, status %x error %x\n", 328 unit, i, err); 329 #else*/ 330 printf("wd%d: reading label, status %b error %b\n", 331 unit, i, WDCS_BITS, err, WDERR_BITS); 332 /*#endif*/ 333 return(-1); 334 } 335 336 /* Ready to remove data? */ 337 while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ; 338 339 i = insw(wdc+wd_data, buf, 256); 340 341 #ifdef WDDEBUG 342 printf("magic %x,insw %x, %x\n", 343 ((struct disklabel *) (buf + LABELOFFSET))->d_magic, i, buf); 344 #endif 345 if (((struct disklabel *) (buf + LABELOFFSET))->d_magic == DISKMAGIC) { 346 *dd = * (struct disklabel *) (buf + LABELOFFSET); 347 open[unit] = 1; 348 } else { 349 if (!wdquiet) 350 printf("wd%d: bad disk label\n", unit); 351 if (io->i_flgs & F_FILE) return(-1); 352 dkbad[unit].bt_bad[0].bt_cyl = -1; 353 dd->d_secpercyl = 1999999 ; dd->d_nsectors = 17 ; 354 dd->d_secsize = 512; 355 outb(wdc+wd_precomp, 0xff); /* force head 3 bit off */ 356 return (0) ; 357 } 358 #endif SMALL 359 #ifdef SMALL 360 #ifdef WDDEBUG 361 printf("magic %x sect %d\n", dd->d_magic, dd->d_nsectors); 362 #endif 363 #endif SMALL 364 365 366 /* now that we know the disk geometry, tell the controller */ 367 outb(wdc+wd_cyl_lo, dd->d_ncylinders); 368 outb(wdc+wd_cyl_hi, (dd->d_ncylinders)>>8); 369 outb(wdc+wd_sdh, WDSD_IBM | (unit << 4) + dd->d_ntracks-1); 370 outb(wdc+wd_seccnt, dd->d_nsectors); 371 outb(wdc+wd_command, 0x91); 372 while (inb(wdc+wd_status) & WDCS_BUSY) ; 373 374 dkbad[unit].bt_bad[0].bt_cyl = -1; 375 outb(wdc+wd_precomp, dd->d_precompcyl / 4); 376 377 /* 378 * Read bad sector table into memory. 379 */ 380 i = 0; 381 do { 382 int blknm = dd->d_secperunit - dd->d_nsectors + i; 383 errcnt = wdio(F_READ, unit, blknm, buf); 384 } while (errcnt && (i += 2) < 10 && i < dd->d_nsectors); 385 db = (struct dkbad *)(buf); 386 #define DKBAD_MAGIC 0x4321 387 if (errcnt == 0 && db->bt_mbz == 0 && db->bt_flag == DKBAD_MAGIC) 388 dkbad[unit] = *db; 389 else { 390 if (!wdquiet) 391 printf("wd%d: error in bad-sector file\n", unit); 392 dkbad[unit].bt_bad[0].bt_cyl = -1; 393 } 394 return(0); 395 } 396 397 wdwait() 398 { 399 register wdc = wdcport; 400 register i = 0; 401 402 while (inb(wdc+wd_status) & WDCS_BUSY) 403 ; 404 while ((inb(wdc+wd_status) & WDCS_READY) == 0) 405 if (i++ > 100000) 406 return(-1); 407 return(0); 408 } 409