1 /*-
2 * Copyright (c) 1990, 1993
3 * The Regents of the University of California. 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 8.1 (Berkeley) 06/11/93
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 */
wdstrategy(bp)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 */
wdustart(du)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
wdstart()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 */
wdintr(unit)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 */
wdopen(dev,flags,fmt)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 */
wdcontrol(bp)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
wdsetctlr(dev,du)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 */
wdclose(dev,flags,fmt)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
wdioctl(dev,cmd,addr,flag)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 */
wdread(dev,uio)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
wdwrite(dev,uio)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
wdsize(dev)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
wddump(dev)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