xref: /original-bsd/sys/vax/stand/up.c (revision bdd86a84)
1 /*
2  * Copyright (c) 1982, 1988 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  *
6  *	@(#)up.c	7.8 (Berkeley) 09/17/88
7  */
8 
9 /*
10  * UNIBUS peripheral standalone driver with ECC correction and bad
11  * block forwarding.  Also supports header operation and write check
12  * for data and/or header.
13  */
14 #include "param.h"
15 #include "inode.h"
16 #include "fs.h"
17 #include "dkbad.h"
18 #include "disklabel.h"
19 
20 #include "../vax/pte.h"
21 
22 #include "../vaxuba/upreg.h"
23 #include "../vaxuba/ubareg.h"
24 
25 #include "saio.h"
26 #include "savax.h"
27 
28 #define RETRIES		27
29 
30 #define MAXBADDESC	126	/* max number of bad sectors recorded */
31 #define SECTSIZ		512	/* sector size in bytes */
32 #define HDRSIZ		4	/* number of bytes in sector header */
33 
34 #define	MAXUNIT		8
35 #define	MAXCTLR		1	/* all addresses must be specified */
36 static u_short	upstd[MAXCTLR] = { 0776700 };
37 static struct	disklabel uplabel[MAXNUBA][MAXCTLR][MAXUNIT];
38 static char	lbuf[SECTSIZ];
39 
40 extern	struct st upst[];
41 
42 #ifndef SMALL
43 struct  dkbad upbad[MAXNUBA][MAXCTLR][MAXUNIT];	/* bad sector table */
44 #endif
45 int 	sectsiz;				/* real sector size */
46 
47 struct	up_softc {
48 	char	gottype;
49 	char	type;
50 	char	debug;
51 #	define	UPF_BSEDEBUG	01	/* debugging bad sector forwarding */
52 #	define	UPF_ECCDEBUG	02	/* debugging ecc correction */
53 	int	retries;
54 	int	ecclim;
55 } up_softc[MAXNUBA][MAXCTLR][MAXUNIT];
56 
57 u_char	up_offset[16] = {
58 	UPOF_P400, UPOF_M400, UPOF_P400, UPOF_M400,
59 	UPOF_P800, UPOF_M800, UPOF_P800, UPOF_M800,
60 	UPOF_P1200, UPOF_M1200, UPOF_P1200, UPOF_M1200,
61 	0, 0, 0, 0
62 };
63 
64 upopen(io)
65 	register struct iob *io;
66 {
67 	register struct updevice *upaddr;
68 	register struct up_softc *sc;
69 	register struct st *st;
70 	register struct disklabel *lp;
71 	struct disklabel *dlp;
72 	register int unit;
73 	int error = 0, uba, ctlr;
74 
75 	if ((u_int)(uba = io->i_adapt) >= nuba)
76 		return (EADAPT);
77 	if ((u_int)(ctlr = io->i_ctlr) >= MAXCTLR)
78 		return (ECTLR);
79 	unit = io->i_unit;
80 	if ((u_int)unit >= MAXUNIT)
81 		return (EUNIT);
82 	upaddr = (struct updevice *)ubamem(uba, upstd[ctlr]);
83 	upaddr->upcs2 = unit;
84 	while ((upaddr->upcs1 & UP_DVA) == 0);
85 	sc = &up_softc[uba][ctlr][unit];
86 	lp = &uplabel[uba][ctlr][unit];
87 	if (sc->gottype == 0) {
88 		register int i;
89 		struct iob tio;
90 
91 #ifndef SMALL
92 		sc->retries = RETRIES;
93 		sc->ecclim = 11;
94 		sc->debug = 0;
95 #endif
96 		/* Read in the pack label. */
97 		lp->d_nsectors = 32;
98 		lp->d_secpercyl = 19*32;
99 		tio = *io;
100 		tio.i_bn = LABELSECTOR;
101 		tio.i_ma = lbuf;
102 		tio.i_cc = SECTSIZ;
103 		tio.i_flgs |= F_RDDATA;
104 		if (upstrategy(&tio, READ) != SECTSIZ)
105 			error = ERDLAB;
106 		dlp = (struct disklabel *)(lbuf + LABELOFFSET);
107 		if (error == 0 && (dlp->d_magic != DISKMAGIC ||
108 		    dlp->d_magic2 != DISKMAGIC))
109 			error = EUNLAB;
110 		if (error == 0)
111 			*lp = *dlp;
112 		else
113 #ifdef COMPAT_42
114 		    if (upmaptype(unit, upaddr, lp) == 0)
115 #endif
116 			return (error);
117 
118 #ifndef SMALL
119 		/* Read in the bad sector table. */
120 		tio.i_bn = lp->d_secpercyl * lp->d_ncylinders - lp->d_nsectors;
121 		tio.i_ma = (char *)&upbad[uba][ctlr][unit];
122 		tio.i_cc = sizeof(struct dkbad);
123 		tio.i_flgs |= F_RDDATA;
124 		for (i = 0; i < 5; i++) {
125 			if (upstrategy(&tio, READ) == sizeof(struct dkbad))
126 				break;
127 			tio.i_bn += 2;
128 		}
129 		if (i == 5) {
130 			printf("up: can't read bad sector table\n");
131 			for (i = 0; i < MAXBADDESC; i++) {
132 				upbad[uba][ctlr][unit].bt_bad[i].bt_cyl = -1;
133 				upbad[uba][ctlr][unit].bt_bad[i].bt_trksec = -1;
134 			}
135 		}
136 #endif
137 		sc->gottype = 1;
138 	}
139 	if (io->i_part >= lp->d_npartitions ||
140 	    lp->d_partitions[io->i_part].p_size == 0)
141 		return (EPART);
142 	io->i_boff = lp->d_partitions[io->i_part].p_offset;
143 	io->i_flgs &= ~F_TYPEMASK;
144 	return (0);
145 }
146 
147 upstrategy(io, func)
148 	register struct iob *io;
149 	int func;
150 {
151 	int cn, tn, sn, o;
152 	register unit = io->i_unit;
153 	register daddr_t bn;
154 	int recal, info, waitdry;
155 	register struct updevice *upaddr;
156 	register struct disklabel *lp;
157 	struct up_softc *sc;
158 	int error, rv = io->i_cc;
159 #ifndef SMALL
160 	int doprintf = 0;
161 #endif
162 
163 	upaddr = (struct updevice *)ubamem(io->i_adapt, upstd[io->i_ctlr]);
164 	sc = &up_softc[io->i_adapt][io->i_ctlr][unit];
165 	lp = &uplabel[io->i_adapt][io->i_ctlr][unit];
166 	sectsiz = SECTSIZ;
167 #ifndef SMALL
168 	if (io->i_flgs & (F_HDR|F_HCHECK))
169 		sectsiz += HDRSIZ;
170 #endif
171 	upaddr->upcs2 = unit;
172 	if ((upaddr->upds & UPDS_VV) == 0) {
173 		upaddr->upcs1 = UP_DCLR|UP_GO;
174 		upaddr->upcs1 = UP_PRESET|UP_GO;
175 		upaddr->upof = UPOF_FMT22;
176 	}
177 	if ((upaddr->upds & UPDS_DREADY) == 0) {
178 		printf("up%d not ready\n", unit);
179 		return (-1);
180 	}
181 	info = ubasetup(io, 1);
182 	upaddr->upwc = -io->i_cc / sizeof (short);
183 	recal = 0;
184 	io->i_errcnt = 0;
185 
186 restart:
187 	o = io->i_cc + (upaddr->upwc * sizeof (short));
188 	upaddr->upba = info + o;
189 	bn = io->i_bn + o / sectsiz;
190 #ifndef SMALL
191 	error = 0;
192 	if (doprintf && sc->debug & (UPF_ECCDEBUG|UPF_BSEDEBUG))
193 		printf("wc=%d o=%d i_bn=%d bn=%d\n",
194 			upaddr->upwc, o, io->i_bn, bn);
195 #endif
196 	upwaitdry(upaddr);
197 	if (upstart(io, bn, lp) != 0) {
198 		rv = -1;
199 		goto done;
200 	}
201 	upwaitrdy(upaddr);
202 	/*
203 	 * If transfer has completed, free UNIBUS
204 	 * resources and return transfer size.
205 	 */
206 	if ((upaddr->upds&UPDS_ERR) == 0 && (upaddr->upcs1&UP_TRE) == 0)
207 		goto done;
208 	bn = io->i_bn +
209 		(io->i_cc + upaddr->upwc * sizeof (short)) / sectsiz;
210 	if (upaddr->uper1 & (UPER1_DCK|UPER1_ECH))
211 		bn--;
212 	cn = bn / lp->d_secpercyl;
213 	sn = bn % lp->d_secpercyl;
214 	tn = sn / lp->d_nsectors;
215 	sn = sn % lp->d_nsectors;
216 #ifndef SMALL
217 	if (sc->debug & (UPF_ECCDEBUG|UPF_BSEDEBUG)) {
218 		printf("up error: sn%d (cyl,trk,sec)=(%d,%d,%d) ",
219 		   bn, cn, tn, sn);
220 		printf("cs2=%b er1=%b er2=%b wc=%d\n",
221 	    	  upaddr->upcs2, UPCS2_BITS, upaddr->uper1,
222 		  UPER1_BITS, upaddr->uper2, UPER2_BITS, upaddr->upwc);
223 	}
224 #endif
225 	waitdry = 0;
226 	while ((upaddr->upds&UPDS_DRY) == 0 && ++waitdry < sectsiz)
227 		DELAY(5);
228 #ifndef SMALL
229 	if (upaddr->uper1&UPER1_WLE) {
230 		/*
231 		 * Give up on write locked devices immediately.
232 		 */
233 		printf("up%d: write locked\n", unit);
234 		rv = -1;
235 		goto done;
236 	}
237 	if (upaddr->uper2 & UPER2_BSE) {
238 		if ((io->i_flgs&F_NBSF) == 0 && upecc(io, BSE) == 0)
239 			goto success;
240 		error = EBSE;
241 		goto hard;
242 	}
243 	/*
244 	 * ECC error. If a soft error, correct it;
245 	 * if correction is too large, no more retries.
246 	 */
247 	if ((upaddr->uper1 & (UPER1_DCK|UPER1_ECH|UPER1_HCRC)) == UPER1_DCK) {
248 		if (upecc(io, ECC) == 0)
249 			goto success;
250 		error = EECC;
251 		goto hard;
252 	}
253 	/*
254 	 * If the error is a header CRC, check if a replacement sector
255 	 * exists in the bad sector table.
256 	 */
257 	if ((upaddr->uper1&UPER1_HCRC) && (io->i_flgs&F_NBSF) == 0 &&
258 	     upecc(io, BSE) == 0)
259 		goto success;
260 #endif
261 	if (++io->i_errcnt > sc->retries) {
262 		/*
263 		 * After 28 retries (16 without offset, and
264 		 * 12 with offset positioning) give up.
265 		 */
266 hard:
267 #ifndef SMALL
268 		if (error == 0) {
269 			error = EHER;
270 			if (upaddr->upcs2 & UPCS2_WCE)
271 				error = EWCK;
272 		}
273 		io->i_error = error;
274 #endif
275 		printf("up error: sn%d (cyl,trk,sec)=(%d,%d,%d) ",
276 		   bn, cn, tn, sn);
277 		printf("cs2=%b er1=%b er2=%b\n",
278 		   upaddr->upcs2, UPCS2_BITS, upaddr->uper1,
279 		   UPER1_BITS, upaddr->uper2, UPER2_BITS);
280 		upaddr->upcs1 = UP_TRE|UP_DCLR|UP_GO;
281 		io->i_errblk = bn;
282 		if (io->i_errcnt >= 16) {
283 			upaddr->upof = UPOF_FMT22;
284 			upaddr->upcs1 = UP_RTC|UP_GO;
285 			upwaitdry(upaddr);
286 		}
287 		rv = -1;
288 		goto done;
289 	}
290 	/*
291 	 * Clear drive error and, every eight attempts, (starting with the
292 	 * fourth) recalibrate to clear the slate.
293 	 */
294 	upaddr->upcs1 = UP_TRE|UP_DCLR|UP_GO;
295 	if ((io->i_errcnt&07) == 4 ) {
296 		upaddr->upcs1 = UP_RECAL|UP_GO;
297 		upwaitdry(upaddr);
298 		upaddr->updc = cn;
299 		upaddr->upcs1 = UP_SEEK|UP_GO;
300 		upwaitdry(upaddr);
301 	}
302 	if (io->i_errcnt >= 16 && (func & READ)) {
303 		upaddr->upof = up_offset[io->i_errcnt & 017] | UPOF_FMT22;
304 		upaddr->upcs1 = UP_OFFSET|UP_GO;
305 		upwaitdry(upaddr);
306 	}
307 	goto restart;
308 
309 success:
310 #define	rounddown(x, y)	(((x) / (y)) * (y))
311 	upaddr->upwc = rounddown(upaddr->upwc, sectsiz / sizeof (short));
312 	if (upaddr->upwc) {
313 #ifndef SMALL
314 		doprintf++;
315 #endif
316 		goto restart;
317 	}
318 done:
319 	ubafree(io, info);
320 	/*
321 	 * If we were offset positioning,
322 	 * return to centerline.
323 	 */
324 	if (io->i_errcnt >= 16) {
325 		upaddr->upof = UPOF_FMT22;
326 		upaddr->upcs1 = UP_RTC|UP_GO;
327 		upwaitdry(upaddr);
328 	}
329 	return (rv);
330 }
331 
332 upwaitrdy(upaddr)
333 	register struct updevice *upaddr;
334 {
335 	do {
336 		DELAY(25);
337 	} while ((upaddr->upcs1 & UP_RDY) == 0);
338 }
339 
340 upwaitdry(upaddr)
341 	register struct updevice *upaddr;
342 {
343 	while ((upaddr->upds&UPDS_DRY) == 0)
344 		DELAY(25);
345 }
346 
347 #ifndef SMALL
348 /*
349  * Correct an ECC error, and restart the i/o to complete the transfer (if
350  * necessary).  This is quite complicated because the transfer may be going
351  * to an odd memory address base and/or across a page boundary.
352  */
353 upecc(io, flag)
354 	register struct iob *io;
355 	int flag;
356 {
357 	register i, unit;
358 	register struct up_softc *sc;
359 	register struct updevice *up;
360 	register struct disklabel *lp;
361 	caddr_t addr;
362 	int bn, twc, npf, mask, cn, tn, sn;
363 	daddr_t bbn;
364 
365 	/*
366 	 * Npf is the number of sectors transferred
367 	 * before the sector containing the ECC error;
368 	 * bn is the current block number.
369 	 */
370 	unit = io->i_unit;
371 	sc = &up_softc[io->i_adapt][io->i_ctlr][unit];
372 	lp = &uplabel[io->i_adapt][io->i_ctlr][unit];
373 	up = (struct updevice *)ubamem(io->i_adapt, upstd[io->i_ctlr]);
374 	twc = up->upwc;
375 	npf = ((twc * sizeof(short)) + io->i_cc) / sectsiz;
376 	if (flag == ECC)
377 		npf--;
378 	if (sc->debug & UPF_ECCDEBUG)
379 		printf("npf=%d mask=0x%x ec1=%d wc=%d\n",
380 			npf, up->upec2, up->upec1, twc);
381 	bn = io->i_bn + npf;
382 	cn = bn / lp->d_secpercyl;
383 	sn = bn % lp->d_secpercyl;
384 	tn = sn / lp->d_nsectors;
385 	sn = sn % lp->d_nsectors;
386 
387 	/*
388 	 * ECC correction.
389 	 */
390 	if (flag == ECC) {
391 		int bit, o;
392 
393 		mask = up->upec2;
394 		printf("up%d: soft ecc sn%d\n", unit, bn);
395 		for (i = mask, bit = 0; i; i >>= 1)
396 			if (i & 1)
397 				bit++;
398 		if (bit > sc->ecclim) {
399 			printf("%d-bit error\n", bit);
400 			return (1);
401 		}
402 		/*
403 		 * Compute the byte and bit position of
404 		 * the error.  o is the byte offset in
405 		 * the transfer at which the correction
406 		 * applied.
407 		 */
408 		i = up->upec1 - 1;		/* -1 makes 0 origin */
409 		bit = i & 07;
410 		o = (i & ~07) >> 3;
411 		up->upcs1 = UP_TRE|UP_DCLR|UP_GO;
412 		/*
413 		 * Correct while possible bits remain of mask.
414 		 * Since mask contains 11 bits, we continue while
415 		 * the bit offset is > -11.  Also watch out for
416 		 * end of this block and the end of the transfer.
417 		 */
418 		while (o < sectsiz && (npf*sectsiz)+o < io->i_cc && bit > -11) {
419 			/*
420 			 * addr =
421 			 *  (base address of transfer) +
422 			 *  (# sectors transferred before the error) *
423 			 *    (sector size) +
424 			 *  (byte offset to incorrect data)
425 			 */
426 			addr = io->i_ma + (npf * sectsiz) + o;
427 			/*
428 			 * No data transfer occurs with a write check,
429 			 * so don't correct the resident copy of data.
430 			 */
431 			if ((io->i_flgs & (F_CHECK|F_HCHECK)) == 0) {
432 				if (sc->debug & UPF_ECCDEBUG)
433 					printf("addr=0x%x old=0x%x ", addr,
434 						(*addr&0xff));
435 				*addr ^= (mask << bit);
436 				if (sc->debug & UPF_ECCDEBUG)
437 					printf("new=0x%x\n", (*addr&0xff));
438 			}
439 			o++, bit -= 8;
440 		}
441 		return (0);
442 	}
443 
444 	/*
445 	 * Bad sector forwarding.
446 	 */
447 	if (flag == BSE) {
448 		/*
449 		 * If not in bad sector table,
450 		 * indicate a hard error to caller.
451 		 */
452 		up->upcs1 = UP_TRE|UP_DCLR|UP_GO;
453 		if ((bbn = isbad(&upbad[io->i_adapt][io->i_ctlr][unit], cn, tn, sn)) < 0)
454 			return (1);
455 		bbn = (lp->d_ncylinders * lp->d_secpercyl) -
456 			lp->d_nsectors - 1 - bbn;
457 		twc = up->upwc + sectsiz;
458 		up->upwc = - (sectsiz / sizeof (short));
459 		if (sc->debug & UPF_BSEDEBUG)
460 			printf("revector sn %d to %d\n", sn, bbn);
461 		/*
462 	 	 * Clear the drive & read the replacement
463 		 * sector.  If this is in the middle of a
464 		 * transfer, then set up the controller
465 		 * registers in a normal fashion.
466 	 	 * The UNIBUS address need not be changed.
467 	 	 */
468 		upwaitrdy(up);
469 		if (upstart(io, bbn, lp))
470 			return (1);		/* error */
471 		io->i_errcnt = 0;		/* success */
472 		upwaitrdy(up);
473 		if ((up->upds & UPDS_ERR) || (up->upcs1 & UP_TRE)) {
474 			up->upwc = twc - sectsiz;
475 			return (1);
476 		}
477 	}
478 	if (twc)
479 		up->upwc = twc;
480 	return (0);
481 }
482 #endif /* !SMALL */
483 
484 upstart(io, bn, lp)
485 	register struct iob *io;
486 	daddr_t bn;
487 	register struct disklabel *lp;
488 {
489 	register struct updevice *upaddr;
490 	register struct up_softc *sc;
491 	int sn, tn;
492 
493 	upaddr = (struct updevice *)ubamem(io->i_adapt, upstd[io->i_ctlr]);
494 	sc = &up_softc[io->i_adapt][io->i_ctlr][io->i_unit];
495 	sn = bn % lp->d_secpercyl;
496 	tn = sn / lp->d_nsectors;
497 	sn = sn % lp->d_nsectors;
498 	upaddr->updc = bn / lp->d_secpercyl;
499 	upaddr->upda = (tn << 8) + sn;
500 	switch (io->i_flgs & F_TYPEMASK) {
501 
502 	case F_RDDATA:
503 		upaddr->upcs1 = UP_RCOM|UP_GO;
504 		break;
505 
506 	case F_WRDATA:
507 		upaddr->upcs1 = UP_WCOM|UP_GO;
508 		break;
509 
510 #ifndef SMALL
511 	case F_HDR|F_RDDATA:
512 		upaddr->upcs1 = UP_RHDR|UP_GO;
513 		break;
514 
515 	case F_HDR|F_WRDATA:
516 		upaddr->upcs1 = UP_WHDR|UP_GO;
517 		break;
518 
519 	case F_CHECK|F_WRDATA:
520 	case F_CHECK|F_RDDATA:
521 		upaddr->upcs1 = UP_WCDATA|UP_GO;
522 		break;
523 
524 	case F_HCHECK|F_WRDATA:
525 	case F_HCHECK|F_RDDATA:
526 		upaddr->upcs1 = UP_WCHDR|UP_GO;
527 		break;
528 #endif
529 
530 	default:
531 		io->i_error = ECMD;
532 		io->i_flgs &= ~F_TYPEMASK;
533 		return (1);
534 	}
535 	return (0);
536 }
537 
538 #ifndef SMALL
539 /*ARGSUSED*/
540 upioctl(io, cmd, arg)
541 	struct iob *io;
542 	int cmd;
543 	caddr_t arg;
544 {
545 	register struct up_softc *sc;
546 
547 	sc = &up_softc[io->i_adapt][io->i_ctlr][io->i_unit];
548 	switch(cmd) {
549 	case SAIODEBUG:
550 		sc->debug = (int)arg;
551 		break;
552 	case SAIODEVDATA:
553 		*(struct disklabel *)arg =
554 		    uplabel[io->i_adapt][io->i_ctlr][io->i_unit];
555 		break;
556 	case SAIOGBADINFO:
557 		*(struct dkbad *)arg =
558 		    upbad[io->i_adapt][io->i_ctlr][io->i_unit];
559 		break;
560 	case SAIOECCLIM:
561 		sc->ecclim = (int)arg;
562 		break;
563 	case SAIORETRIES:
564 		sc->retries = (int)arg;
565 		break;
566 	default:
567 		return (ECMD);
568 	}
569 	return (0);
570 }
571 #endif /* !SMALL */
572