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.4 (Berkeley) 10/11/92 11 */ 12 13 /* TODO:peel out buffer at low ipl, 14 speed improvement, rewrite to clean code from garbage artifacts */ 15 16 17 #include "wd.h" 18 #if NWD > 0 19 20 #include <sys/param.h> 21 #include <sys/dkbad.h> 22 #include <sys/systm.h> 23 #include <sys/conf.h> 24 #include <sys/file.h> 25 #include <sys/stat.h> 26 #include <sys/ioctl.h> 27 #include <sys/disklabel.h> 28 #include <sys/buf.h> 29 #include <sys/uio.h> 30 #include <sys/syslog.h> 31 32 #include <i386/isa/isa_device.h> 33 #include <i386/isa/icu.h> 34 #include <i386/isa/wdreg.h> 35 #include <vm/vm.h> 36 37 #define RETRIES 5 /* number of retries before giving up */ 38 #define MAXTRANSFER 32 /* max size of transfer in page clusters */ 39 40 #define wdctlr(dev) ((minor(dev) & 0x80) >> 7) 41 #define wdunit(dev) ((minor(dev) & 0x60) >> 5) 42 #define wdpart(dev) ((minor(dev) & 0x1f)) 43 44 #define b_cylin b_resid /* cylinder number for doing IO to */ 45 /* shares an entry in the buf struct */ 46 47 /* 48 * Drive states. Used for open and format operations. 49 * States < OPEN (> 0) are transient, during an open operation. 50 * OPENRAW is used for unlabeled disks, and for floppies, to inhibit 51 * bad-sector forwarding. 52 */ 53 #define RAWDISK 8 /* raw disk operation, no translation*/ 54 #define ISRAWSTATE(s) (RAWDISK&(s)) /* are we in a raw state? */ 55 #define DISKSTATE(s) (~RAWDISK&(s)) /* are we in a given state regardless 56 of raw or cooked mode? */ 57 58 #define CLOSED 0 /* disk is closed. */ 59 /* "cooked" disk states */ 60 #define WANTOPEN 1 /* open requested, not started */ 61 #define RECAL 2 /* doing restore */ 62 #define RDLABEL 3 /* reading pack label */ 63 #define RDBADTBL 4 /* reading bad-sector table */ 64 #define OPEN 5 /* done with open */ 65 66 #define WANTOPENRAW (WANTOPEN|RAWDISK) /* raw WANTOPEN */ 67 #define RECALRAW (RECAL|RAWDISK) /* raw open, doing restore */ 68 #define OPENRAW (OPEN|RAWDISK) /* open, but unlabeled disk or floppy */ 69 70 71 /* 72 * The structure of a disk drive. 73 */ 74 struct disk { 75 struct disklabel dk_dd; /* device configuration data */ 76 long dk_bc; /* byte count left */ 77 short dk_skip; /* blocks already transferred */ 78 char dk_unit; /* physical unit number */ 79 char dk_state; /* control state */ 80 u_char dk_status; /* copy of status reg. */ 81 u_char dk_error; /* copy of error reg. */ 82 short dk_open; /* open/closed refcnt */ 83 u_long dk_copenpart; /* character units open on this drive */ 84 u_long dk_bopenpart; /* block units open on this drive */ 85 u_long dk_openpart; /* all units open on this drive */ 86 short dk_wlabel; /* label writable? */ 87 }; 88 89 /* 90 * This label is used as a default when initializing a new or raw disk. 91 * It really only lets us access the first track until we know more. 92 */ 93 struct disklabel dflt_sizes = { 94 DISKMAGIC, DTYPE_ST506, 0, "default", "", 95 512, /* sector size */ 96 17, /* # of sectors per track */ 97 8, /* # of tracks per cylinder */ 98 766, /* # of cylinders per unit */ 99 17*8, /* # of sectors per cylinder */ 100 766*8*17, /* # of sectors per unit */ 101 0, /* # of spare sectors per track */ 102 0, /* # of spare sectors per cylinder */ 103 0, /* # of alt. cylinders per unit */ 104 3600, /* rotational speed */ 105 1, /* hardware sector interleave */ 106 0, /* sector 0 skew, per track */ 107 0, /* sector 0 skew, per cylinder */ 108 0, /* head switch time, usec */ 109 0, /* track-to-track seek, usec */ 110 0, /* generic flags */ 111 0,0,0,0,0, 112 0,0,0,0,0, 113 DISKMAGIC, 114 0, 115 8, 116 8192, 117 8192, 118 119 {{21600, 0, 0,0,0,0}, /* A=root filesystem */ 120 {21600, 40, 0,0,0,0}, 121 {660890, 0, 0,0,0,0}, /* C=whole disk */ 122 {216000, 80, 0,0,0,0}, 123 {0, 0, 0,0,0,0}, 124 {0, 0, 0,0,0,0}, 125 {0, 0, 0,0,0,0}, 126 {399600, 480, 0,0,0,0}} 127 }; 128 129 static struct dkbad dkbad[NWD]; 130 struct disk wddrives[NWD] = {0}; /* table of units */ 131 struct buf wdtab = {0}; 132 struct buf wdutab[NWD] = {0}; /* head of queue per drive */ 133 struct buf rwdbuf[NWD] = {0}; /* buffers for raw IO */ 134 long wdxfer[NWD] = {0}; /* count of transfers */ 135 int writeprotected[NWD] = { 0 }; 136 int wdprobe(), wdattach(), wdintr(); 137 struct isa_driver wddriver = { 138 wdprobe, wdattach, "wd", 139 }; 140 141 static wdc; 142 /* 143 * Probe routine 144 */ 145 wdprobe(dvp) 146 struct isa_device *dvp; 147 { 148 wdc = dvp->id_iobase; 149 150 #ifdef lint 151 wdintr(0); 152 #endif 153 /* XXX sorry, needs to be better */ 154 outb(wdc+wd_error, 0x5a) ; /* error register not writable */ 155 outb(wdc+wd_cyl_lo, 0xa5) ; /* but all of cyllo are implemented */ 156 if(inb(wdc+wd_error) != 0x5a && inb(wdc+wd_cyl_lo) == 0xa5) 157 return(1) ; 158 return (0); 159 } 160 161 /* 162 * attach each drive if possible. 163 */ 164 wdattach(dvp) 165 struct isa_device *dvp; 166 { 167 int unit = dvp->id_unit; 168 169 outb(wdc+wd_ctlr,12); 170 DELAY(1000); 171 outb(wdc+wd_ctlr,8); 172 } 173 174 /* Read/write routine for a buffer. Finds the proper unit, range checks 175 * arguments, and schedules the transfer. Does not wait for the transfer 176 * to complete. Multi-page transfers are supported. All I/O requests must 177 * be a multiple of a sector in length. 178 */ 179 wdstrategy(bp) 180 register struct buf *bp; /* IO operation to perform */ 181 { 182 register struct buf *dp; 183 register struct disk *du; /* Disk unit to do the IO. */ 184 register struct partition *p; 185 long maxsz, sz; 186 int unit = wdunit(bp->b_dev); 187 int s; 188 189 if ((unit >= NWD) || (bp->b_blkno < 0)) { 190 printf("wdstrat: unit = %d, blkno = %d, bcount = %d\n", 191 unit, bp->b_blkno, bp->b_bcount); 192 pg("wd:error in wdstrategy"); 193 bp->b_flags |= B_ERROR; 194 goto bad; 195 } 196 if (writeprotected[unit] && (bp->b_flags & B_READ) == 0) { 197 printf("wd%d: write protected\n", unit); 198 goto bad; 199 } 200 du = &wddrives[unit]; 201 if (DISKSTATE(du->dk_state) != OPEN) 202 goto q; 203 #ifdef old 204 /* 205 * Convert DEV_BSIZE "blocks" to sectors. 206 * Note: doing the conversions this way limits the partition size 207 * to about 8 million sectors (1-8 Gb). 208 */ 209 blknum = (unsigned long) bp->b_blkno * DEV_BSIZE / du->dk_dd.d_secsize; 210 if (((u_long) bp->b_blkno * DEV_BSIZE % du->dk_dd.d_secsize != 0) || 211 bp->b_bcount >= MAXTRANSFER * CLBYTES) { 212 bp->b_flags |= B_ERROR; 213 goto bad; 214 } 215 nblocks = du->dk_dd.d_partitions[part].p_size; 216 cyloff = du->dk_dd.d_partitions[part].p_offset; 217 if (blknum + (bp->b_bcount / du->dk_dd.d_secsize) > nblocks) { 218 if (blknum == nblocks) 219 bp->b_resid = bp->b_bcount; 220 else 221 bp->b_flags |= B_ERROR; 222 goto bad; 223 } 224 bp->b_cylin = blknum / du->dk_dd.d_secpercyl + cyloff; 225 #else 226 /* 227 * Determine the size of the transfer, and make sure it is 228 * within the boundaries of the partition. 229 */ 230 p = &du->dk_dd.d_partitions[wdpart(bp->b_dev)]; 231 maxsz = p->p_size; 232 sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT; 233 if (bp->b_blkno + p->p_offset <= LABELSECTOR && 234 #if LABELSECTOR != 0 235 bp->b_blkno + p->p_offset + sz > LABELSECTOR && 236 #endif 237 (bp->b_flags & B_READ) == 0 && du->dk_wlabel == 0) { 238 bp->b_error = EROFS; 239 goto bad; 240 } 241 if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz) { 242 /* if exactly at end of disk, return an EOF */ 243 if (bp->b_blkno == maxsz) { 244 bp->b_resid = bp->b_bcount; 245 biodone(bp); 246 return; 247 } 248 /* or truncate if part of it fits */ 249 sz = maxsz - bp->b_blkno; 250 if (sz <= 0) 251 goto bad; 252 bp->b_bcount = sz << DEV_BSHIFT; 253 } 254 bp->b_cylin = (bp->b_blkno + p->p_offset) / du->dk_dd.d_secpercyl; 255 #endif 256 q: 257 dp = &wdutab[unit]; 258 s = splhigh(); 259 disksort(dp, bp); 260 if (dp->b_active == 0) 261 wdustart(du); /* start drive if idle */ 262 if (wdtab.b_active == 0) 263 wdstart(s); /* start IO if controller idle */ 264 splx(s); 265 return; 266 267 bad: 268 bp->b_error = EINVAL; 269 biodone(bp); 270 } 271 272 /* Routine to queue a read or write command to the controller. The request is 273 * linked into the active list for the controller. If the controller is idle, 274 * the transfer is started. 275 */ 276 wdustart(du) 277 register struct disk *du; 278 { 279 register struct buf *bp, *dp; 280 281 dp = &wdutab[du->dk_unit]; 282 if (dp->b_active) 283 return; 284 bp = dp->b_actf; 285 if (bp == NULL) 286 return; 287 dp->b_forw = NULL; 288 if (wdtab.b_actf == NULL) /* link unit into active list */ 289 wdtab.b_actf = dp; 290 else 291 wdtab.b_actl->b_forw = dp; 292 wdtab.b_actl = dp; 293 dp->b_active = 1; /* mark the drive as busy */ 294 } 295 296 /* 297 * Controller startup routine. This does the calculation, and starts 298 * a single-sector read or write operation. Called to start a transfer, 299 * or from the interrupt routine to continue a multi-sector transfer. 300 * RESTRICTIONS: 301 * 1. The transfer length must be an exact multiple of the sector size. 302 */ 303 304 static wd_sebyse; 305 306 wdstart() 307 { 308 register struct disk *du; /* disk unit for IO */ 309 register struct buf *bp; 310 struct buf *dp; 311 register struct bt_bad *bt_ptr; 312 long blknum, pagcnt, cylin, head, sector; 313 long secpertrk, secpercyl, addr, i; 314 int unit, s; 315 316 loop: 317 dp = wdtab.b_actf; 318 if (dp == NULL) 319 return; 320 bp = dp->b_actf; 321 if (bp == NULL) { 322 wdtab.b_actf = dp->b_forw; 323 goto loop; 324 } 325 unit = wdunit(bp->b_dev); 326 du = &wddrives[unit]; 327 if (DISKSTATE(du->dk_state) <= RDLABEL) { 328 if (wdcontrol(bp)) { 329 dp->b_actf = bp->av_forw; 330 goto loop; /* done */ 331 } 332 return; 333 } 334 secpertrk = du->dk_dd.d_nsectors; 335 secpercyl = du->dk_dd.d_secpercyl; 336 /* 337 * Convert DEV_BSIZE "blocks" to sectors. 338 */ 339 blknum = (unsigned long) bp->b_blkno * DEV_BSIZE / du->dk_dd.d_secsize 340 + du->dk_skip; 341 #ifdef WDDEBUG 342 if (du->dk_skip == 0) { 343 dprintf(DDSK,"\nwdstart %d: %s %d@%d; map ", unit, 344 (bp->b_flags & B_READ) ? "read" : "write", 345 bp->b_bcount, blknum); 346 } else { 347 dprintf(DDSK," %d)%x", du->dk_skip, inb(wdc+wd_altsts)); 348 } 349 #endif 350 351 addr = (int) bp->b_un.b_addr; 352 if(du->dk_skip==0) du->dk_bc = bp->b_bcount; 353 cylin = blknum / secpercyl; 354 head = (blknum % secpercyl) / secpertrk; 355 sector = blknum % secpertrk; 356 if (DISKSTATE(du->dk_state) == OPEN) 357 cylin += du->dk_dd.d_partitions[wdpart(bp->b_dev)].p_offset 358 / secpercyl; 359 360 /* 361 * See if the current block is in the bad block list. 362 * (If we have one, and not formatting.) 363 */ 364 if (DISKSTATE(du->dk_state) == OPEN && wd_sebyse) 365 for (bt_ptr = dkbad[unit].bt_bad; bt_ptr->bt_cyl != -1; bt_ptr++) { 366 if (bt_ptr->bt_cyl > cylin) 367 /* Sorted list, and we passed our cylinder. quit. */ 368 break; 369 if (bt_ptr->bt_cyl == cylin && 370 bt_ptr->bt_trksec == (head << 8) + sector) { 371 /* 372 * Found bad block. Calculate new block addr. 373 * This starts at the end of the disk (skip the 374 * last track which is used for the bad block list), 375 * and works backwards to the front of the disk. 376 */ 377 #ifdef WDDEBUG 378 dprintf(DDSK,"--- badblock code -> Old = %d; ", 379 blknum); 380 #endif 381 blknum = du->dk_dd.d_secperunit - du->dk_dd.d_nsectors 382 - (bt_ptr - dkbad[unit].bt_bad) - 1; 383 cylin = blknum / secpercyl; 384 head = (blknum % secpercyl) / secpertrk; 385 sector = blknum % secpertrk; 386 #ifdef WDDEBUG 387 dprintf(DDSK, "new = %d\n", blknum); 388 #endif 389 break; 390 } 391 } 392 sector += 1; /* sectors begin with 1, not 0 */ 393 394 wdtab.b_active = 1; /* mark controller active */ 395 396 if(du->dk_skip==0 || wd_sebyse) { 397 if(wdtab.b_errcnt && (bp->b_flags & B_READ) == 0) du->dk_bc += 512; 398 while ((inb(wdc+wd_status) & WDCS_BUSY) != 0) ; 399 /*while ((inb(wdc+wd_status) & WDCS_DRQ)) inb(wdc+wd_data);*/ 400 outb(wdc+wd_precomp, 0xff); 401 /*wr(wdc+wd_precomp, du->dk_dd.dk_precompcyl / 4);*/ 402 /*if (bp->b_flags & B_FORMAT) { 403 wr(wdc+wd_sector, du->dk_dd.dk_gap3); 404 wr(wdc+wd_seccnt, du->dk_dd.dk_nsectors); 405 } else {*/ 406 if(wd_sebyse) 407 outb(wdc+wd_seccnt, 1); 408 else 409 outb(wdc+wd_seccnt, ((du->dk_bc +511) / 512)); 410 outb(wdc+wd_sector, sector); 411 412 outb(wdc+wd_cyl_lo, cylin); 413 outb(wdc+wd_cyl_hi, cylin >> 8); 414 415 /* Set up the SDH register (select drive). */ 416 outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf)); 417 while ((inb(wdc+wd_status) & WDCS_READY) == 0) ; 418 419 /*if (bp->b_flags & B_FORMAT) 420 wr(wdc+wd_command, WDCC_FORMAT); 421 else*/ 422 outb(wdc+wd_command, 423 (bp->b_flags & B_READ)? WDCC_READ : WDCC_WRITE); 424 #ifdef WDDEBUG 425 dprintf(DDSK,"sector %d cylin %d head %d addr %x sts %x\n", 426 sector, cylin, head, addr, inb(wdc+wd_altsts)); 427 #endif 428 } 429 430 /* If this is a read operation, just go away until it's done. */ 431 if (bp->b_flags & B_READ) return; 432 433 /* Ready to send data? */ 434 while ((inb(wdc+wd_status) & WDCS_DRQ) == 0); 435 436 /* ASSUMES CONTIGUOUS MEMORY */ 437 outsw (wdc+wd_data, addr+du->dk_skip*512, 256); 438 du->dk_bc -= 512; 439 } 440 441 /* 442 * these are globally defined so they can be found 443 * by the debugger easily in the case of a system crash 444 */ 445 daddr_t wd_errsector; 446 daddr_t wd_errbn; 447 unsigned char wd_errstat; 448 449 /* Interrupt routine for the controller. Acknowledge the interrupt, check for 450 * errors on the current operation, mark it done if necessary, and start 451 * the next request. Also check for a partially done transfer, and 452 * continue with the next chunk if so. 453 */ 454 wdintr(unit) 455 { 456 register struct disk *du; 457 register struct buf *bp, *dp; 458 int status; 459 char partch ; 460 static wd_haderror; 461 462 /* Shouldn't need this, but it may be a slow controller. */ 463 while ((status = inb(wdc+wd_status)) & WDCS_BUSY) ; 464 if (!wdtab.b_active) { 465 printf("wd: extra interrupt\n"); 466 return; 467 } 468 469 #ifdef WDDEBUG 470 dprintf(DDSK,"I "); 471 #endif 472 dp = wdtab.b_actf; 473 bp = dp->b_actf; 474 du = &wddrives[wdunit(bp->b_dev)]; 475 partch = wdpart(bp->b_dev) + 'a'; 476 if (DISKSTATE(du->dk_state) <= RDLABEL) { 477 if (wdcontrol(bp)) 478 goto done; 479 return; 480 } 481 if (status & (WDCS_ERR | WDCS_ECCCOR)) { 482 wd_errstat = inb(wdc+wd_error); /* save error status */ 483 #ifdef WDDEBUG 484 printf("status %x error %x\n", status, wd_errstat); 485 #endif 486 if(wd_sebyse == 0) { 487 wd_haderror = 1; 488 goto outt; 489 } 490 /*if (bp->b_flags & B_FORMAT) { 491 du->dk_status = status; 492 du->dk_error = wdp->wd_error; 493 bp->b_flags |= B_ERROR; 494 goto done; 495 }*/ 496 497 wd_errsector = (bp->b_cylin * du->dk_dd.d_secpercyl) + 498 (((unsigned long) bp->b_blkno * DEV_BSIZE / 499 du->dk_dd.d_secsize) % du->dk_dd.d_secpercyl) + 500 du->dk_skip; 501 wd_errbn = bp->b_blkno 502 + du->dk_skip * du->dk_dd.d_secsize / DEV_BSIZE ; 503 if (status & WDCS_ERR) { 504 if (++wdtab.b_errcnt < RETRIES) { 505 wdtab.b_active = 0; 506 } else { 507 printf("wd%d%c: ", du->dk_unit, partch); 508 printf( 509 "hard %s error, sn %d bn %d status %b error %b\n", 510 (bp->b_flags & B_READ)? "read":"write", 511 wd_errsector, wd_errbn, status, WDCS_BITS, 512 wd_errstat, WDERR_BITS); 513 bp->b_flags |= B_ERROR; /* flag the error */ 514 } 515 } else 516 log(LOG_WARNING,"wd%d%c: soft ecc sn %d bn %d\n", 517 du->dk_unit, partch, wd_errsector, 518 wd_errbn); 519 } 520 outt: 521 522 /* 523 * If this was a successful read operation, fetch the data. 524 */ 525 if (((bp->b_flags & (B_READ | B_ERROR)) == B_READ) && wdtab.b_active) { 526 int chk, dummy; 527 528 chk = min(256,du->dk_bc/2); 529 /* Ready to receive data? */ 530 while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ; 531 532 /*dprintf(DDSK,"addr %x\n", (int)bp->b_un.b_addr + du->dk_skip * 512);*/ 533 insw(wdc+wd_data,(int)bp->b_un.b_addr + du->dk_skip * 512 ,chk); 534 du->dk_bc -= 2*chk; 535 while (chk++ < 256) insw (wdc+wd_data,&dummy,1); 536 } 537 538 wdxfer[du->dk_unit]++; 539 if (wdtab.b_active) { 540 if ((bp->b_flags & B_ERROR) == 0) { 541 du->dk_skip++; /* Add to successful sectors. */ 542 if (wdtab.b_errcnt) { 543 log(LOG_WARNING, "wd%d%c: ", 544 du->dk_unit, partch); 545 log(LOG_WARNING, 546 "soft %s error, sn %d bn %d error %b retries %d\n", 547 (bp->b_flags & B_READ) ? "read" : "write", 548 wd_errsector, wd_errbn, wd_errstat, 549 WDERR_BITS, wdtab.b_errcnt); 550 } 551 wdtab.b_errcnt = 0; 552 553 /* see if more to transfer */ 554 /*if (du->dk_skip < (bp->b_bcount + 511) / 512) {*/ 555 if (du->dk_bc > 0 && wd_haderror == 0) { 556 wdstart(); 557 return; /* next chunk is started */ 558 } else if (wd_haderror && wd_sebyse == 0) { 559 du->dk_skip = 0; 560 wd_haderror = 0; 561 wd_sebyse = 1; 562 wdstart(); 563 return; /* redo xfer sector by sector */ 564 } 565 } 566 567 done: 568 wd_sebyse = 0; 569 /* done with this transfer, with or without error */ 570 wdtab.b_actf = dp->b_forw; 571 wdtab.b_errcnt = 0; 572 du->dk_skip = 0; 573 dp->b_active = 0; 574 dp->b_actf = bp->av_forw; 575 dp->b_errcnt = 0; 576 bp->b_resid = 0; 577 biodone(bp); 578 } 579 wdtab.b_active = 0; 580 if (dp->b_actf) 581 wdustart(du); /* requeue disk if more io to do */ 582 if (wdtab.b_actf) 583 wdstart(); /* start IO on next drive */ 584 } 585 586 /* 587 * Initialize a drive. 588 */ 589 wdopen(dev, flags, fmt) 590 dev_t dev; 591 int flags, fmt; 592 { 593 register unsigned int unit; 594 register struct buf *bp; 595 register struct disk *du; 596 int part = wdpart(dev), mask = 1 << part; 597 struct partition *pp; 598 struct dkbad *db; 599 int i, error = 0; 600 601 unit = wdunit(dev); 602 if (unit >= NWD) return (ENXIO) ; 603 du = &wddrives[unit]; 604 #ifdef notdef 605 if (du->dk_open){ 606 du->dk_open++ ; 607 return(0); /* already is open, don't mess with it */ 608 } 609 #endif 610 du->dk_unit = unit; 611 wdutab[unit].b_actf = NULL; 612 /*if (flags & O_NDELAY) 613 du->dk_state = WANTOPENRAW; 614 else*/ 615 du->dk_state = WANTOPEN; 616 /* 617 * Use the default sizes until we've read the label, 618 * or longer if there isn't one there. 619 */ 620 du->dk_dd = dflt_sizes; 621 622 /* 623 * Recal, read of disk label will be done in wdcontrol 624 * during first read operation. 625 */ 626 bp = geteblk(512); 627 bp->b_dev = dev & 0xff00; 628 bp->b_bcount = 0; 629 bp->b_blkno = LABELSECTOR; 630 bp->b_flags = B_READ; 631 wdstrategy(bp); 632 biowait(bp); 633 if (bp->b_flags & B_ERROR) { 634 error = ENXIO; 635 du->dk_state = CLOSED; 636 goto done; 637 } 638 if (du->dk_state == OPENRAW) { 639 du->dk_state = OPENRAW; 640 goto done; 641 } 642 /* 643 * Read bad sector table into memory. 644 */ 645 i = 0; 646 do { 647 bp->b_flags = B_BUSY | B_READ; 648 bp->b_blkno = du->dk_dd.d_secperunit - du->dk_dd.d_nsectors 649 + i; 650 if (du->dk_dd.d_secsize > DEV_BSIZE) 651 bp->b_blkno *= du->dk_dd.d_secsize / DEV_BSIZE; 652 else 653 bp->b_blkno /= DEV_BSIZE / du->dk_dd.d_secsize; 654 bp->b_bcount = du->dk_dd.d_secsize; 655 bp->b_cylin = du->dk_dd.d_ncylinders - 1; 656 wdstrategy(bp); 657 biowait(bp); 658 } while ((bp->b_flags & B_ERROR) && (i += 2) < 10 && 659 i < du->dk_dd.d_nsectors); 660 db = (struct dkbad *)(bp->b_un.b_addr); 661 #define DKBAD_MAGIC 0x4321 662 if ((bp->b_flags & B_ERROR) == 0 && db->bt_mbz == 0 && 663 db->bt_flag == DKBAD_MAGIC) { 664 dkbad[unit] = *db; 665 du->dk_state = OPEN; 666 } else { 667 printf("wd%d: %s bad-sector file\n", unit, 668 (bp->b_flags & B_ERROR) ? "can't read" : "format error in"); 669 error = ENXIO ; 670 du->dk_state = OPENRAW; 671 } 672 done: 673 bp->b_flags = B_INVAL | B_AGE; 674 brelse(bp); 675 if (error == 0) 676 du->dk_open = 1; 677 678 /* 679 * Warn if a partion is opened 680 * that overlaps another partition which is open 681 * unless one is the "raw" partition (whole disk). 682 */ 683 #define RAWPART 8 /* 'x' partition */ /* XXX */ 684 if ((du->dk_openpart & mask) == 0 && part != RAWPART) { 685 int start, end; 686 687 pp = &du->dk_dd.d_partitions[part]; 688 start = pp->p_offset; 689 end = pp->p_offset + pp->p_size; 690 for (pp = du->dk_dd.d_partitions; 691 pp < &du->dk_dd.d_partitions[du->dk_dd.d_npartitions]; 692 pp++) { 693 if (pp->p_offset + pp->p_size <= start || 694 pp->p_offset >= end) 695 continue; 696 if (pp - du->dk_dd.d_partitions == RAWPART) 697 continue; 698 if (du->dk_openpart & (1 << (pp - 699 du->dk_dd.d_partitions))) 700 log(LOG_WARNING, 701 "wd%d%c: overlaps open partition (%c)\n", 702 unit, part + 'a', 703 pp - du->dk_dd.d_partitions + 'a'); 704 } 705 } 706 if (part >= du->dk_dd.d_npartitions) 707 return (ENXIO); 708 du->dk_openpart |= mask; 709 switch (fmt) { 710 case S_IFCHR: 711 du->dk_copenpart |= mask; 712 break; 713 case S_IFBLK: 714 du->dk_bopenpart |= mask; 715 break; 716 } 717 return (error); 718 } 719 720 /* 721 * Implement operations other than read/write. 722 * Called from wdstart or wdintr during opens and formats. 723 * Uses finite-state-machine to track progress of operation in progress. 724 * Returns 0 if operation still in progress, 1 if completed. 725 */ 726 wdcontrol(bp) 727 register struct buf *bp; 728 { 729 register struct disk *du; 730 register unit; 731 unsigned char stat; 732 int s, cnt; 733 extern int bootdev, cyloffset; 734 735 du = &wddrives[wdunit(bp->b_dev)]; 736 unit = du->dk_unit; 737 switch (DISKSTATE(du->dk_state)) { 738 739 tryagainrecal: 740 case WANTOPEN: /* set SDH, step rate, do restore */ 741 #ifdef WDDEBUG 742 dprintf(DDSK,"wd%d: recal ", unit); 743 #endif 744 s = splbio(); /* not called from intr level ... */ 745 outb(wdc+wd_sdh, WDSD_IBM | (unit << 4)); 746 wdtab.b_active = 1; 747 outb(wdc+wd_command, WDCC_RESTORE | WD_STEP); 748 du->dk_state++; 749 splx(s); 750 return(0); 751 752 case RECAL: 753 if ((stat = inb(wdc+wd_status)) & WDCS_ERR) { 754 printf("wd%d: recal", du->dk_unit); 755 if (unit == 0) { 756 printf(": status %b error %b\n", 757 stat, WDCS_BITS, 758 inb(wdc+wd_error), WDERR_BITS); 759 if (++wdtab.b_errcnt < RETRIES) 760 goto tryagainrecal; 761 } 762 goto badopen; 763 } 764 765 /* some compaq controllers require this ... */ 766 wdsetctlr(bp->b_dev, du); 767 768 wdtab.b_errcnt = 0; 769 if (ISRAWSTATE(du->dk_state)) { 770 du->dk_state = OPENRAW; 771 return(1); 772 } 773 retry: 774 #ifdef WDDEBUG 775 dprintf(DDSK,"rdlabel "); 776 #endif 777 if( cyloffset < 0 || cyloffset > 8192) cyloffset=0; 778 /* 779 * Read in sector LABELSECTOR to get the pack label 780 * and geometry. 781 */ 782 outb(wdc+wd_precomp, 0xff);/* sometimes this is head bit 3 */ 783 outb(wdc+wd_seccnt, 1); 784 outb(wdc+wd_sector, LABELSECTOR+1); 785 /*if (bp->b_dev == bootdev) { 786 (wdc+wd_cyl_lo = cyloffset & 0xff; 787 (wdc+wd_cyl_hi = cyloffset >> 8; 788 } else { 789 (wdc+wd_cyl_lo = 0; 790 (wdc+wd_cyl_hi = 0; 791 }*/ 792 outb(wdc+wd_cyl_lo, (cyloffset & 0xff)); 793 outb(wdc+wd_cyl_hi, (cyloffset >> 8)); 794 outb(wdc+wd_sdh, WDSD_IBM | (unit << 4)); 795 outb(wdc+wd_command, WDCC_READ); 796 du->dk_state = RDLABEL; 797 return(0); 798 799 case RDLABEL: 800 if ((stat = inb(wdc+wd_status)) & WDCS_ERR) { 801 if (++wdtab.b_errcnt < RETRIES) 802 goto retry; 803 printf("wd%d: read label", unit); 804 goto badopen; 805 } 806 807 insw(wdc+wd_data, bp->b_un.b_addr, 256); 808 809 if (((struct disklabel *) 810 (bp->b_un.b_addr + LABELOFFSET))->d_magic == DISKMAGIC) { 811 du->dk_dd = 812 * (struct disklabel *) (bp->b_un.b_addr + LABELOFFSET); 813 } else { 814 printf("wd%d: bad disk label\n", du->dk_unit); 815 du->dk_state = OPENRAW; 816 } 817 818 s = splbio(); /* not called from intr level ... */ 819 while ((stat = inb(wdc+wd_status)) & WDCS_BUSY); 820 821 wdsetctlr(bp->b_dev, du); 822 823 outb(wdc+wd_seccnt, 0); 824 splx(s); 825 826 if (du->dk_state == RDLABEL) 827 du->dk_state = RDBADTBL; 828 /* 829 * The rest of the initialization can be done 830 * by normal means. 831 */ 832 return(1); 833 834 default: 835 panic("wdcontrol"); 836 } 837 /* NOTREACHED */ 838 839 badopen: 840 printf(": status %b error %b\n", 841 stat, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS); 842 du->dk_state = OPENRAW; 843 return(1); 844 } 845 846 wdsetctlr(dev, du) dev_t dev; struct disk *du; { 847 int stat; 848 849 outb(wdc+wd_cyl_lo, du->dk_dd.d_ncylinders); 850 outb(wdc+wd_cyl_hi, (du->dk_dd.d_ncylinders)>>8); 851 outb(wdc+wd_sdh, WDSD_IBM | (wdunit(dev) << 4) + du->dk_dd.d_ntracks-1); 852 outb(wdc+wd_seccnt, du->dk_dd.d_nsectors); 853 outb(wdc+wd_command, 0x91); 854 855 while ((stat = inb(wdc+wd_status)) & WDCS_BUSY) ; 856 stat = inb(wdc+wd_error); 857 return(stat); 858 } 859 860 /* ARGSUSED */ 861 wdclose(dev, flags, fmt) 862 dev_t dev; 863 int flags, fmt; 864 { 865 register struct disk *du; 866 867 du = &wddrives[wdunit(dev)]; 868 du->dk_open-- ; 869 /*if (du->dk_open == 0) du->dk_state = CLOSED ; does not work */ 870 } 871 872 wdioctl(dev,cmd,addr,flag) 873 dev_t dev; 874 caddr_t addr; 875 { 876 int unit = wdunit(dev); 877 register struct disk *du; 878 int error = 0; 879 struct uio auio; 880 struct iovec aiov; 881 /*int wdformat();*/ 882 883 du = &wddrives[unit]; 884 885 switch (cmd) { 886 887 case DIOCGDINFO: 888 *(struct disklabel *)addr = du->dk_dd; 889 break; 890 891 case DIOCGPART: 892 ((struct partinfo *)addr)->disklab = &du->dk_dd; 893 ((struct partinfo *)addr)->part = 894 &du->dk_dd.d_partitions[wdpart(dev)]; 895 break; 896 897 case DIOCSDINFO: 898 if ((flag & FWRITE) == 0) 899 error = EBADF; 900 else 901 error = setdisklabel(&du->dk_dd, 902 (struct disklabel *)addr, 903 0 /*(dk->dk_state == OPENRAW) ? 0 : dk->dk_openpart*/); 904 /*if (error == 0 && dk->dk_state == OPENRAW && 905 vdreset_drive(vddinfo[unit])) 906 dk->dk_state = OPEN;*/ 907 wdsetctlr(dev, du); 908 break; 909 910 case DIOCWLABEL: 911 if ((flag & FWRITE) == 0) 912 error = EBADF; 913 else 914 du->dk_wlabel = *(int *)addr; 915 break; 916 917 case DIOCWDINFO: 918 if ((flag & FWRITE) == 0) 919 error = EBADF; 920 else if ((error = setdisklabel(&du->dk_dd, (struct disklabel *)addr, 921 0/*(dk->dk_state == OPENRAW) ? 0 : dk->dk_openpart*/)) == 0) { 922 int wlab; 923 924 /*if (error == 0 && dk->dk_state == OPENRAW && 925 vdreset_drive(vddinfo[unit])) 926 dk->dk_state = OPEN; */ 927 wdsetctlr(dev, du); 928 929 /* simulate opening partition 0 so write succeeds */ 930 /* dk->dk_openpart |= (1 << 0); /* XXX */ 931 wlab = du->dk_wlabel; 932 du->dk_wlabel = 1; 933 error = writedisklabel(dev, wdstrategy, &du->dk_dd,wdpart(dev)); 934 /*dk->dk_openpart = dk->dk_copenpart | dk->dk_bopenpart;*/ 935 du->dk_wlabel = wlab; 936 } 937 break; 938 939 #ifdef notyet 940 case DIOCGDINFOP: 941 *(struct disklabel **)addr = &(du->dk_dd); 942 break; 943 944 case DIOCWFORMAT: 945 if ((flag & FWRITE) == 0) 946 error = EBADF; 947 else { 948 register struct format_op *fop; 949 950 fop = (struct format_op *)addr; 951 aiov.iov_base = fop->df_buf; 952 aiov.iov_len = fop->df_count; 953 auio.uio_iov = &aiov; 954 auio.uio_iovcnt = 1; 955 auio.uio_resid = fop->df_count; 956 auio.uio_segflg = 0; 957 auio.uio_offset = 958 fop->df_startblk * du->dk_dd.d_secsize; 959 error = physio(wdformat, &rwdbuf[unit], dev, B_WRITE, 960 minphys, &auio); 961 fop->df_count -= auio.uio_resid; 962 fop->df_reg[0] = du->dk_status; 963 fop->df_reg[1] = du->dk_error; 964 } 965 break; 966 #endif 967 968 default: 969 error = ENOTTY; 970 break; 971 } 972 return (error); 973 } 974 975 /*wdformat(bp) 976 struct buf *bp; 977 { 978 979 bp->b_flags |= B_FORMAT; 980 return (wdstrategy(bp)); 981 }*/ 982 983 /* 984 * Routines to do raw IO for a unit. 985 */ 986 wdread(dev, uio) /* character read routine */ 987 dev_t dev; 988 struct uio *uio; 989 { 990 int unit = wdunit(dev) ; 991 992 if (unit >= NWD) return(ENXIO); 993 return(physio(wdstrategy, &rwdbuf[unit], dev, B_READ, minphys, uio)); 994 } 995 996 997 wdwrite(dev, uio) /* character write routine */ 998 dev_t dev; 999 struct uio *uio; 1000 { 1001 int unit = wdunit(dev) ; 1002 1003 if (unit >= NWD) return(ENXIO); 1004 return(physio(wdstrategy, &rwdbuf[unit], dev, B_WRITE, minphys, uio)); 1005 } 1006 1007 wdsize(dev) 1008 dev_t dev; 1009 { 1010 register unit = wdunit(dev); 1011 register part = wdpart(dev); 1012 register struct disk *du; 1013 register val ; 1014 1015 if (unit >= NWD) return(-1); 1016 if (wddrives[unit].dk_state == 0) { 1017 val = wdopen (dev, 0); 1018 if (val < 0) 1019 return (-1); 1020 } 1021 du = &wddrives[unit]; 1022 return((int)((u_long)du->dk_dd.d_partitions[part].p_size * 1023 du->dk_dd.d_secsize / 512)); 1024 } 1025 1026 extern char *vmmap; /* poor name! */ 1027 1028 wddump(dev) /* dump core after a system crash */ 1029 dev_t dev; 1030 { 1031 register struct disk *du; /* disk unit to do the IO */ 1032 register struct bt_bad *bt_ptr; 1033 long num; /* number of sectors to write */ 1034 int unit, part; 1035 long cyloff, blknum, blkcnt; 1036 long cylin, head, sector, stat; 1037 long secpertrk, secpercyl, nblocks, i; 1038 char *addr; 1039 extern int Maxmem; 1040 static wddoingadump = 0 ; 1041 extern CMAP1; 1042 extern char CADDR1[]; 1043 1044 1045 #ifdef ARGO 1046 outb(0x461,0); /* disable failsafe timer */ 1047 #endif 1048 addr = (char *) 0; /* starting address */ 1049 /* size of memory to dump */ 1050 num = Maxmem; 1051 unit = wdunit(dev); /* eventually support floppies? */ 1052 part = wdpart(dev); /* file system */ 1053 /* check for acceptable drive number */ 1054 if (unit >= NWD) return(ENXIO); 1055 1056 du = &wddrives[unit]; 1057 /* was it ever initialized ? */ 1058 if (du->dk_state < OPEN) return (ENXIO) ; 1059 1060 /* Convert to disk sectors */ 1061 num = (u_long) num * NBPG / du->dk_dd.d_secsize; 1062 1063 /* check if controller active */ 1064 /*if (wdtab.b_active) return(EFAULT); */ 1065 if (wddoingadump) return(EFAULT); 1066 1067 secpertrk = du->dk_dd.d_nsectors; 1068 secpercyl = du->dk_dd.d_secpercyl; 1069 nblocks = du->dk_dd.d_partitions[part].p_size; 1070 cyloff = du->dk_dd.d_partitions[part].p_offset / secpercyl; 1071 1072 /*pg("xunit %x, nblocks %d, dumplo %d num %d\n", part,nblocks,dumplo,num);*/ 1073 /* check transfer bounds against partition size */ 1074 if ((dumplo < 0) || ((dumplo + num) > nblocks)) 1075 return(EINVAL); 1076 1077 /*wdtab.b_active = 1; /* mark controller active for if we 1078 panic during the dump */ 1079 wddoingadump = 1 ; i = 100000 ; 1080 while ((inb(wdc+wd_status) & WDCS_BUSY) && (i-- > 0)) ; 1081 outb(wdc+wd_sdh, WDSD_IBM | (unit << 4)); 1082 outb(wdc+wd_command, WDCC_RESTORE | WD_STEP); 1083 while (inb(wdc+wd_status) & WDCS_BUSY) ; 1084 1085 /* some compaq controllers require this ... */ 1086 wdsetctlr(dev, du); 1087 1088 blknum = dumplo; 1089 while (num > 0) { 1090 #ifdef notdef 1091 if (blkcnt > MAXTRANSFER) blkcnt = MAXTRANSFER; 1092 if ((blknum + blkcnt - 1) / secpercyl != blknum / secpercyl) 1093 blkcnt = secpercyl - (blknum % secpercyl); 1094 /* keep transfer within current cylinder */ 1095 #endif 1096 pmap_enter(kernel_pmap, vmmap, addr, VM_PROT_READ, TRUE); 1097 1098 /* compute disk address */ 1099 cylin = blknum / secpercyl; 1100 head = (blknum % secpercyl) / secpertrk; 1101 sector = blknum % secpertrk; 1102 cylin += cyloff; 1103 1104 #ifdef notyet 1105 /* 1106 * See if the current block is in the bad block list. 1107 * (If we have one.) 1108 */ 1109 for (bt_ptr = dkbad[unit].bt_bad; 1110 bt_ptr->bt_cyl != -1; bt_ptr++) { 1111 if (bt_ptr->bt_cyl > cylin) 1112 /* Sorted list, and we passed our cylinder. 1113 quit. */ 1114 break; 1115 if (bt_ptr->bt_cyl == cylin && 1116 bt_ptr->bt_trksec == (head << 8) + sector) { 1117 /* 1118 * Found bad block. Calculate new block addr. 1119 * This starts at the end of the disk (skip the 1120 * last track which is used for the bad block list), 1121 * and works backwards to the front of the disk. 1122 */ 1123 blknum = (du->dk_dd.d_secperunit) 1124 - du->dk_dd.d_nsectors 1125 - (bt_ptr - dkbad[unit].bt_bad) - 1; 1126 cylin = blknum / secpercyl; 1127 head = (blknum % secpercyl) / secpertrk; 1128 sector = blknum % secpertrk; 1129 break; 1130 } 1131 1132 #endif 1133 sector++; /* origin 1 */ 1134 1135 /* select drive. */ 1136 outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf)); 1137 while ((inb(wdc+wd_status) & WDCS_READY) == 0) ; 1138 1139 /* transfer some blocks */ 1140 outb(wdc+wd_sector, sector); 1141 outb(wdc+wd_seccnt,1); 1142 outb(wdc+wd_cyl_lo, cylin); 1143 outb(wdc+wd_cyl_hi, cylin >> 8); 1144 #ifdef notdef 1145 /* lets just talk about this first...*/ 1146 pg ("sdh 0%o sector %d cyl %d addr 0x%x", 1147 inb(wdc+wd_sdh), inb(wdc+wd_sector), 1148 inb(wdc+wd_cyl_hi)*256+inb(wdc+wd_cyl_lo), addr) ; 1149 #endif 1150 #ifdef ODYSSEUS 1151 if(cylin < 46 || cylin > 91)pg("oops"); 1152 #endif 1153 #ifdef PRIAM 1154 if(cylin < 40 || cylin > 79)pg("oops"); 1155 #endif 1156 outb(wdc+wd_command, WDCC_WRITE); 1157 1158 /* Ready to send data? */ 1159 while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ; 1160 if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ; 1161 1162 outsw (wdc+wd_data, CADDR1+((int)addr&(NBPG-1)), 256); 1163 (int) addr += 512; 1164 1165 if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ; 1166 /* Check data request (should be done). */ 1167 if (inb(wdc+wd_status) & WDCS_DRQ) return(EIO) ; 1168 1169 /* wait for completion */ 1170 for ( i = 1000000 ; inb(wdc+wd_status) & WDCS_BUSY ; i--) { 1171 if (i < 0) return (EIO) ; 1172 } 1173 /* error check the xfer */ 1174 if (inb(wdc+wd_status) & WDCS_ERR) return(EIO) ; 1175 /* update block count */ 1176 num--; 1177 blknum++ ; 1178 if (num % 100 == 0) printf(".") ; 1179 } 1180 return(0); 1181 } 1182 #endif 1183