Commit | Line | Data |
---|---|---|
15637ed4 RG |
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 | * Redistribution and use in source and binary forms, with or without | |
9 | * modification, are permitted provided that the following conditions | |
10 | * are met: | |
11 | * 1. Redistributions of source code must retain the above copyright | |
12 | * notice, this list of conditions and the following disclaimer. | |
13 | * 2. Redistributions in binary form must reproduce the above copyright | |
14 | * notice, this list of conditions and the following disclaimer in the | |
15 | * documentation and/or other materials provided with the distribution. | |
16 | * 3. All advertising materials mentioning features or use of this software | |
17 | * must display the following acknowledgement: | |
18 | * This product includes software developed by the University of | |
19 | * California, Berkeley and its contributors. | |
20 | * 4. Neither the name of the University nor the names of its contributors | |
21 | * may be used to endorse or promote products derived from this software | |
22 | * without specific prior written permission. | |
23 | * | |
24 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
25 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
30 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
33 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
34 | * SUCH DAMAGE. | |
35 | * | |
36 | * @(#)wd.c 7.3 (Berkeley) 5/4/91 | |
37 | * | |
38 | * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE | |
39 | * -------------------- ----- ---------------------- | |
40 | * CURRENT PATCH LEVEL: 1 00049 | |
41 | * -------------------- ----- ---------------------- | |
42 | * | |
43 | * 06 Sep 92 Robert D. Thrush Fix for HD not booting after install | |
44 | */ | |
45 | ||
46 | /* device driver for winchester disk */ | |
47 | ||
48 | #include "param.h" | |
49 | #include "dkbad.h" | |
50 | #include "disklabel.h" | |
51 | #include "i386/isa/isa.h" | |
52 | #include "i386/isa/wdreg.h" | |
53 | #include "saio.h" | |
54 | ||
55 | #define SMALL | |
56 | #define NWD 1 /* number of hard disk units supported, max 2 */ | |
57 | #define RETRIES 5 /* number of retries before giving up */ | |
58 | ||
59 | int noretries, wdquiet; | |
60 | /* #define WDDEBUG*/ | |
61 | ||
62 | #ifdef SMALL | |
63 | extern struct disklabel disklabel; | |
64 | #else | |
65 | struct disklabel wdsizes[NWD]; | |
66 | #endif | |
67 | ||
68 | extern cyloffset ; /* bootstrap's idea of cylinder for disklabel */ | |
69 | ||
70 | /* | |
71 | * Record for the bad block forwarding code. | |
72 | * This is initialized to be empty until the bad-sector table | |
73 | * is read from the disk. | |
74 | */ | |
75 | #define TRKSEC(trk,sec) ((trk << 8) + sec) | |
76 | ||
77 | struct dkbad dkbad[NWD]; | |
78 | static wdcport; | |
79 | ||
80 | wdopen(io) | |
81 | register struct iob *io; | |
82 | { | |
83 | register struct disklabel *dd; | |
84 | ||
85 | #ifdef WDDEBUG | |
86 | printf("wdopen "); | |
87 | #endif | |
88 | #ifdef SMALL | |
89 | dd = &disklabel; | |
90 | #else | |
91 | dd = &wdsizes[io->i_unit]; | |
92 | if (io->i_part > 8) | |
93 | _stop("Invalid partition number"); | |
94 | if(io->i_ctlr > 1) | |
95 | _stop("Invalid controller number"); | |
96 | #endif | |
97 | if (wdinit(io)) | |
98 | _stop("wd initialization error"); | |
99 | io->i_boff = dd->d_partitions[io->i_part].p_offset ; | |
100 | /*printf("boff %d ", io->i_boff);*/ | |
101 | return(0); | |
102 | } | |
103 | ||
104 | wdstrategy(io,func) | |
105 | register struct iob *io; | |
106 | { | |
107 | register int iosize; /* number of sectors to do IO for this loop */ | |
108 | register daddr_t sector; | |
109 | int nblocks, cyloff; | |
110 | int unit, partition; | |
111 | char *address; | |
112 | register struct disklabel *dd; | |
113 | ||
114 | unit = io->i_unit; | |
115 | partition = io->i_part; | |
116 | #ifdef WDDEBUG | |
117 | printf("wdstrat %d %d ", unit, partition); | |
118 | #endif | |
119 | #ifdef SMALL | |
120 | dd = &disklabel; | |
121 | #else | |
122 | dd = &wdsizes[unit]; | |
123 | #endif | |
124 | iosize = io->i_cc / dd->d_secsize; | |
125 | /* | |
126 | * Convert PGSIZE "blocks" to sectors. | |
127 | * Note: doing the conversions this way limits the partition size | |
128 | * to about 8 million sectors (1-8 Gb). | |
129 | */ | |
130 | /*printf("bn%d ", io->i_bn);*/ | |
131 | sector = (unsigned long) io->i_bn * DEV_BSIZE / dd->d_secsize; | |
132 | nblocks = dd->d_partitions[partition].p_size; | |
133 | #ifndef SMALL | |
134 | if (iosize < 0 || sector + iosize > nblocks || sector < 0) { | |
135 | #ifdef WDDEBUG | |
136 | printf("bn = %d; sectors = %d; partition = %d; fssize = %d\n", | |
137 | io->i_bn, iosize, partition, nblocks); | |
138 | #endif | |
139 | printf("wdstrategy - I/O out of filesystem boundaries\n"); | |
140 | return(-1); | |
141 | } | |
142 | if (io->i_bn * DEV_BSIZE % dd->d_secsize) { | |
143 | printf("wdstrategy - transfer starts in midsector\n"); | |
144 | return(-1); | |
145 | } | |
146 | if (io->i_cc % dd->d_secsize) { | |
147 | printf("wd: transfer of partial sector\n"); | |
148 | return(-1); | |
149 | } | |
150 | #endif | |
151 | sector += io->i_boff; | |
152 | ||
153 | address = io->i_ma; | |
154 | while (iosize > 0) { | |
155 | if (wdio(func, unit, sector, address)) | |
156 | return(-1); | |
157 | iosize--; | |
158 | sector++; | |
159 | address += dd->d_secsize; | |
160 | } | |
161 | return(io->i_cc); | |
162 | } | |
163 | ||
164 | /* | |
165 | * Routine to do a one-sector I/O operation, and wait for it | |
166 | * to complete. | |
167 | */ | |
168 | wdio(func, unit, blknm, addr) | |
169 | short *addr; | |
170 | { | |
171 | struct disklabel *dd; | |
172 | register wdc = wdcport; | |
173 | struct bt_bad *bt_ptr; | |
174 | int i; | |
175 | int retries = 0; | |
176 | long cylin, head, sector; | |
177 | u_char opcode, erro; | |
178 | ||
179 | #ifdef SMALL | |
180 | dd = &disklabel; | |
181 | #else | |
182 | dd = &wdsizes[unit]; | |
183 | #endif | |
184 | if (func == F_WRITE) | |
185 | opcode = WDCC_WRITE; | |
186 | else | |
187 | opcode = WDCC_READ; | |
188 | ||
189 | /* Calculate data for output. */ | |
190 | cylin = blknm / dd->d_secpercyl; | |
191 | head = (blknm % dd->d_secpercyl) / dd->d_nsectors; | |
192 | sector = blknm % dd->d_nsectors; | |
193 | ||
194 | /* | |
195 | * See if the current block is in the bad block list. | |
196 | */ | |
197 | if (blknm > BBSIZE/DEV_BSIZE) /* should be BBSIZE */ | |
198 | for (bt_ptr = dkbad[unit].bt_bad; bt_ptr->bt_cyl != -1; bt_ptr++) { | |
199 | if (bt_ptr->bt_cyl > cylin) | |
200 | /* Sorted list, and we passed our cylinder. quit. */ | |
201 | break; | |
202 | if (bt_ptr->bt_cyl == cylin && | |
203 | bt_ptr->bt_trksec == (head << 8) + sector) { | |
204 | /* | |
205 | * Found bad block. Calculate new block addr. | |
206 | * This starts at the end of the disk (skip the | |
207 | * last track which is used for the bad block list), | |
208 | * and works backwards to the front of the disk. | |
209 | */ | |
210 | #ifdef WDDEBUG | |
211 | printf("--- badblock code -> Old = %d; ", | |
212 | blknm); | |
213 | #endif | |
214 | printf("--- badblock code -> Old = %d; ", | |
215 | blknm); | |
216 | blknm = dd->d_secperunit - dd->d_nsectors | |
217 | - (bt_ptr - dkbad[unit].bt_bad) - 1; | |
218 | cylin = blknm / dd->d_secpercyl; | |
219 | head = (blknm % dd->d_secpercyl) / dd->d_nsectors; | |
220 | sector = blknm % dd->d_nsectors; | |
221 | #ifdef WDDEBUG | |
222 | printf("new = %d\n", blknm); | |
223 | #endif | |
224 | break; | |
225 | } | |
226 | } | |
227 | ||
228 | sector += 1; | |
229 | retry: | |
230 | #ifdef WDDEBUG | |
231 | printf("sec %d sdh %x cylin %d ", sector, | |
232 | WDSD_IBM | (unit<<4) | (head & 0xf), cylin); | |
233 | #endif | |
234 | /*printf("c %d h %d s %d ", cylin, head, sector);*/ | |
235 | outb(wdc+wd_precomp, 0xff); | |
236 | outb(wdc+wd_seccnt, 1); | |
237 | outb(wdc+wd_sector, sector); | |
238 | outb(wdc+wd_cyl_lo, cylin); | |
239 | outb(wdc+wd_cyl_hi, cylin >> 8); | |
240 | ||
241 | /* Set up the SDH register (select drive). */ | |
242 | outb(wdc+wd_sdh, WDSD_IBM | (unit<<4) | (head & 0xf)); | |
243 | while ((inb(wdc+wd_status) & WDCS_READY) == 0) ; | |
244 | ||
245 | outb(wdc+wd_command, opcode); | |
246 | while (opcode == WDCC_READ && (inb(wdc+wd_status) & WDCS_BUSY)) | |
247 | ; | |
248 | /* Did we get an error? */ | |
249 | if (opcode == WDCC_READ && (inb(wdc+wd_status) & WDCS_ERR)) | |
250 | goto error; | |
251 | ||
252 | /* Ready to remove data? */ | |
253 | while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ; | |
254 | ||
255 | if (opcode == WDCC_READ) | |
256 | insw(wdc+wd_data,addr,256); | |
257 | else outsw(wdc+wd_data,addr,256); | |
258 | ||
259 | /* Check data request (should be done). */ | |
260 | if (inb(wdc+wd_status) & WDCS_DRQ) goto error; | |
261 | ||
262 | while (opcode == WDCC_WRITE && (inb(wdc+wd_status) & WDCS_BUSY)) ; | |
263 | ||
264 | if (inb(wdc+wd_status) & WDCS_ERR) goto error; | |
265 | ||
266 | #ifdef WDDEBUG | |
267 | printf("addr %x",addr); | |
268 | #endif | |
269 | return (0); | |
270 | error: | |
271 | erro = inb(wdc+wd_error); | |
272 | if (++retries < RETRIES) | |
273 | goto retry; | |
274 | if (!wdquiet) | |
275 | printf("wd%d: hard %s error: sector %d status %b error %b\n", unit, | |
276 | opcode == WDCC_READ? "read" : "write", blknm, | |
277 | inb(wdc+wd_status), WDCS_BITS, erro, WDERR_BITS); | |
278 | return (-1); | |
279 | } | |
280 | ||
281 | wdinit(io) | |
282 | struct iob *io; | |
283 | { | |
284 | register wdc; | |
285 | struct disklabel *dd; | |
286 | unsigned int unit; | |
287 | struct dkbad *db; | |
288 | int i, errcnt = 0; | |
289 | char buf[512]; | |
290 | static open[NWD]; | |
291 | ||
292 | unit = io->i_unit; | |
293 | if (open[unit]) return(0); | |
294 | ||
295 | wdcport = io->i_ctlr ? IO_WD2 : IO_WD1; | |
296 | wdc = wdcport; | |
297 | ||
298 | #ifdef SMALL | |
299 | dd = &disklabel; | |
300 | #else | |
301 | dd = &wdsizes[unit]; | |
302 | #endif | |
303 | ||
304 | /* reset controller */ | |
305 | outb(wdc+wd_ctlr,6); | |
306 | DELAY(1000); | |
307 | outb(wdc+wd_ctlr,2); | |
308 | DELAY(1000); | |
309 | while(inb(wdc+wd_status) & WDCS_BUSY); /* 06 Sep 92*/ | |
310 | outb(wdc+wd_ctlr,8); | |
311 | ||
312 | /* set SDH, step rate, do restore to recalibrate drive */ | |
313 | tryagainrecal: | |
314 | outb(wdc+wd_sdh, WDSD_IBM | (unit << 4)); | |
315 | wdwait(); | |
316 | outb(wdc+wd_command, WDCC_RESTORE | WD_STEP); | |
317 | wdwait(); | |
318 | if ((i = inb(wdc+wd_status)) & WDCS_ERR) { | |
319 | printf("wd%d: recal status %b error %b\n", | |
320 | unit, i, WDCS_BITS, inb(wdc+wd_error), WDERR_BITS); | |
321 | if (++errcnt < 10) | |
322 | goto tryagainrecal; | |
323 | return(-1); | |
324 | } | |
325 | ||
326 | #ifndef SMALL | |
327 | /* | |
328 | * Some controllers require this (after a recal they | |
329 | * revert to a logical translation mode to compensate for | |
330 | * dos limitation on 10-bit cylinders -- *shudder* -wfj) | |
331 | * note: heads *must* be fewer than or equal to 8 to | |
332 | * compensate for some IDE drives that latch this for all time. | |
333 | */ | |
334 | outb(wdc+wd_sdh, WDSD_IBM | (unit << 4) + 8 -1); | |
335 | outb(wdc+wd_seccnt, 35 ); | |
336 | outb(wdc+wd_cyl_lo, 1224); | |
337 | outb(wdc+wd_cyl_hi, 1224/256); | |
338 | outb(wdc+wd_command, 0x91); | |
339 | while (inb(wdc+wd_status) & WDCS_BUSY) ; | |
340 | ||
341 | errcnt = 0; | |
342 | retry: | |
343 | /* | |
344 | * Read in LABELSECTOR to get the pack label and geometry. | |
345 | */ | |
346 | outb(wdc+wd_precomp, 0xff); /* sometimes this is head bit 3 */ | |
347 | outb(wdc+wd_seccnt, 1); | |
348 | outb(wdc+wd_sector, LABELSECTOR + 1); | |
349 | outb(wdc+wd_cyl_lo, (cyloffset & 0xff)); | |
350 | outb(wdc+wd_cyl_hi, (cyloffset >> 8)); | |
351 | outb(wdc+wd_sdh, WDSD_IBM | (unit << 4)); | |
352 | wdwait(); | |
353 | outb(wdc+wd_command, WDCC_READ); | |
354 | wdwait(); | |
355 | if ((i = inb(wdc+wd_status)) & WDCS_ERR) { | |
356 | int err; | |
357 | ||
358 | err = inb(wdc+wd_error); | |
359 | if (++errcnt < RETRIES) | |
360 | goto retry; | |
361 | if (!wdquiet) | |
362 | printf("wd%d: reading label, status %b error %b\n", | |
363 | unit, i, WDCS_BITS, err, WDERR_BITS); | |
364 | return(-1); | |
365 | } | |
366 | ||
367 | /* Ready to remove data? */ | |
368 | while ((inb(wdc+wd_status) & WDCS_DRQ) == 0) ; | |
369 | ||
370 | i = insw(wdc+wd_data, buf, 256); | |
371 | ||
372 | #ifdef WDDEBUG | |
373 | printf("magic %x,insw %x, %x\n", | |
374 | ((struct disklabel *) (buf + LABELOFFSET))->d_magic, i, buf); | |
375 | #endif | |
376 | if (((struct disklabel *) (buf + LABELOFFSET))->d_magic == DISKMAGIC) { | |
377 | *dd = * (struct disklabel *) (buf + LABELOFFSET); | |
378 | open[unit] = 1; | |
379 | } else { | |
380 | if (!wdquiet) | |
381 | printf("wd%d: bad disk label\n", unit); | |
382 | if (io->i_flgs & F_FILE) return(-1); | |
383 | dkbad[unit].bt_bad[0].bt_cyl = -1; | |
384 | dd->d_secpercyl = 1999999 ; dd->d_nsectors = 17 ; | |
385 | dd->d_secsize = 512; | |
386 | outb(wdc+wd_precomp, 0xff); /* force head 3 bit off */ | |
387 | return (0) ; | |
388 | } | |
389 | #ifdef WDDEBUG | |
390 | printf("magic %x sect %d\n", dd->d_magic, dd->d_nsectors); | |
391 | #endif | |
392 | #endif !SMALL | |
393 | ||
394 | /*printf("C%dH%dS%d ", dd->d_ncylinders, dd->d_ntracks, dd->d_nsectors);*/ | |
395 | ||
396 | /* now that we know the disk geometry, tell the controller */ | |
397 | outb(wdc+wd_cyl_lo, dd->d_ncylinders+1); | |
398 | outb(wdc+wd_cyl_hi, (dd->d_ncylinders+1)>>8); | |
399 | outb(wdc+wd_sdh, WDSD_IBM | (unit << 4) + dd->d_ntracks-1); | |
400 | outb(wdc+wd_seccnt, dd->d_nsectors); | |
401 | outb(wdc+wd_command, 0x91); | |
402 | while (inb(wdc+wd_status) & WDCS_BUSY) ; | |
403 | ||
404 | dkbad[unit].bt_bad[0].bt_cyl = -1; | |
405 | ||
406 | if (dd->d_flags & D_BADSECT) { | |
407 | /* | |
408 | * Read bad sector table into memory. | |
409 | */ | |
410 | i = 0; | |
411 | do { | |
412 | int blknm = dd->d_secperunit - dd->d_nsectors + i; | |
413 | errcnt = wdio(F_READ, unit, blknm, buf); | |
414 | } while (errcnt && (i += 2) < 10 && i < dd->d_nsectors); | |
415 | db = (struct dkbad *)(buf); | |
416 | #define DKBAD_MAGIC 0x4321 | |
417 | if (errcnt == 0 && db->bt_mbz == 0 && db->bt_flag == DKBAD_MAGIC) | |
418 | dkbad[unit] = *db; | |
419 | else { | |
420 | if (!wdquiet) | |
421 | printf("wd%d: error in bad-sector file\n", unit); | |
422 | dkbad[unit].bt_bad[0].bt_cyl = -1; | |
423 | } | |
424 | } | |
425 | return(0); | |
426 | } | |
427 | ||
428 | wdwait() | |
429 | { | |
430 | register wdc = wdcport; | |
431 | register i = 0; | |
432 | ||
433 | while (inb(wdc+wd_status) & WDCS_BUSY) | |
434 | ; | |
435 | while ((inb(wdc+wd_status) & WDCS_READY) == 0) | |
436 | if (i++ > 100000) | |
437 | return(-1); | |
438 | return(0); | |
439 | } |