Commit | Line | Data |
---|---|---|
60f56dfc KM |
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 | * Van Jacobson of Lawrence Berkeley Laboratory. | |
7 | * | |
8 | * %sccs.include.redist.c% | |
9 | * | |
e0f6df7b | 10 | * @(#)sd.c 7.11 (Berkeley) %G% |
60f56dfc KM |
11 | */ |
12 | ||
13 | /* | |
14 | * SCSI CCS (Command Command Set) disk driver. | |
15 | */ | |
16 | #include "sd.h" | |
17 | #if NSD > 0 | |
18 | ||
19 | #ifndef lint | |
98d7aa4d | 20 | static char rcsid[] = "$Header: sd.c,v 1.15 91/04/24 11:54:30 mike Exp $"; |
60f56dfc KM |
21 | #endif |
22 | ||
b28b3a13 KB |
23 | #include "sys/param.h" |
24 | #include "sys/systm.h" | |
25 | #include "sys/buf.h" | |
b28b3a13 KB |
26 | #include "sys/dkstat.h" |
27 | #include "sys/disklabel.h" | |
b28b3a13 | 28 | #include "sys/malloc.h" |
b28b3a13 | 29 | #include "sys/proc.h" |
60f56dfc | 30 | |
88f29710 MK |
31 | #include "device.h" |
32 | #include "scsireg.h" | |
b28b3a13 | 33 | #include "vm/vm_param.h" |
88f29710 | 34 | #include "vm/lock.h" |
b28b3a13 | 35 | #include "vm/vm_prot.h" |
e4fc9b79 | 36 | #include "vm/pmap.h" |
22d09b27 | 37 | |
60f56dfc KM |
38 | extern int scsi_test_unit_rdy(); |
39 | extern int scsi_request_sense(); | |
40 | extern int scsi_inquiry(); | |
41 | extern int scsi_read_capacity(); | |
42 | extern int scsi_tt_write(); | |
43 | extern int scsireq(); | |
44 | extern int scsiustart(); | |
45 | extern int scsigo(); | |
46 | extern void scsifree(); | |
47 | extern void scsireset(); | |
98d7aa4d | 48 | extern void scsi_delay(); |
60f56dfc | 49 | |
60f56dfc | 50 | extern void disksort(); |
60f56dfc KM |
51 | extern void biodone(); |
52 | extern int physio(); | |
53 | extern void TBIS(); | |
54 | ||
55 | int sdinit(); | |
56 | void sdstrategy(), sdstart(), sdustart(), sdgo(), sdintr(); | |
57 | ||
58 | struct driver sddriver = { | |
59 | sdinit, "sd", (int (*)())sdstart, (int (*)())sdgo, (int (*)())sdintr, | |
60 | }; | |
61 | ||
62 | struct size { | |
63 | u_long strtblk; | |
64 | u_long endblk; | |
65 | int nblocks; | |
66 | }; | |
67 | ||
68 | struct sdinfo { | |
69 | struct size part[8]; | |
70 | }; | |
71 | ||
72 | /* | |
73 | * since the SCSI standard tends to hide the disk structure, we define | |
74 | * partitions in terms of DEV_BSIZE blocks. The default partition table | |
75 | * (for an unlabeled disk) reserves 512K for a boot area, has an 8 meg | |
76 | * root and 32 meg of swap. The rest of the space on the drive goes in | |
77 | * the G partition. As usual, the C partition covers the entire disk | |
78 | * (including the boot area). | |
79 | */ | |
80 | struct sdinfo sddefaultpart = { | |
81 | 1024, 17408, 16384 , /* A */ | |
82 | 17408, 82944, 65536 , /* B */ | |
83 | 0, 0, 0 , /* C */ | |
84 | 17408, 115712, 98304 , /* D */ | |
85 | 115712, 218112, 102400 , /* E */ | |
86 | 218112, 0, 0 , /* F */ | |
87 | 82944, 0, 0 , /* G */ | |
88 | 115712, 0, 0 , /* H */ | |
89 | }; | |
90 | ||
91 | struct sd_softc { | |
92 | struct hp_device *sc_hd; | |
93 | struct devqueue sc_dq; | |
94 | int sc_format_pid; /* process using "format" mode */ | |
95 | short sc_flags; | |
96 | short sc_type; /* drive type */ | |
97 | short sc_punit; /* physical unit (scsi lun) */ | |
98 | u_short sc_bshift; /* convert device blocks to DEV_BSIZE blks */ | |
99 | u_int sc_blks; /* number of blocks on device */ | |
100 | int sc_blksize; /* device block size in bytes */ | |
101 | u_int sc_wpms; /* average xfer rate in 16 bit wds/sec. */ | |
102 | struct sdinfo sc_info; /* drive partition table & label info */ | |
103 | } sd_softc[NSD]; | |
104 | ||
105 | /* sc_flags values */ | |
106 | #define SDF_ALIVE 0x1 | |
107 | ||
108 | #ifdef DEBUG | |
109 | int sddebug = 1; | |
110 | #define SDB_ERROR 0x01 | |
111 | #define SDB_PARTIAL 0x02 | |
112 | #endif | |
113 | ||
114 | struct sdstats { | |
115 | long sdresets; | |
116 | long sdtransfers; | |
117 | long sdpartials; | |
118 | } sdstats[NSD]; | |
119 | ||
120 | struct buf sdtab[NSD]; | |
60f56dfc KM |
121 | struct scsi_fmt_cdb sdcmd[NSD]; |
122 | struct scsi_fmt_sense sdsense[NSD]; | |
123 | ||
124 | static struct scsi_fmt_cdb sd_read_cmd = { 10, CMD_READ_EXT }; | |
125 | static struct scsi_fmt_cdb sd_write_cmd = { 10, CMD_WRITE_EXT }; | |
126 | ||
88f29710 | 127 | #define sdunit(x) (minor(x) >> 3) |
60f56dfc KM |
128 | #define sdpart(x) (minor(x) & 0x7) |
129 | #define sdpunit(x) ((x) & 7) | |
130 | #define b_cylin b_resid | |
22d09b27 | 131 | |
60f56dfc KM |
132 | #define SDRETRY 2 |
133 | ||
134 | /* | |
135 | * Table of scsi commands users are allowed to access via "format" | |
136 | * mode. 0 means not legal. 1 means "immediate" (doesn't need dma). | |
137 | * -1 means needs dma and/or wait for intr. | |
138 | */ | |
139 | static char legal_cmds[256] = { | |
140 | /***** 0 1 2 3 4 5 6 7 8 9 A B C D E F */ | |
141 | /*00*/ 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
142 | /*10*/ 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, | |
143 | /*20*/ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
144 | /*30*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
145 | /*40*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
146 | /*50*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
147 | /*60*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
148 | /*70*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
149 | /*80*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
150 | /*90*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
151 | /*a0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
152 | /*b0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
153 | /*c0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
154 | /*d0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
155 | /*e0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
156 | /*f0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
157 | }; | |
158 | ||
159 | static struct scsi_inquiry inqbuf; | |
160 | static struct scsi_fmt_cdb inq = { | |
161 | 6, | |
162 | CMD_INQUIRY, 0, 0, 0, sizeof(inqbuf), 0 | |
163 | }; | |
164 | ||
165 | static u_char capbuf[8]; | |
166 | struct scsi_fmt_cdb cap = { | |
167 | 10, | |
168 | CMD_READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0 | |
169 | }; | |
170 | ||
171 | static int | |
172 | sdident(sc, hd) | |
173 | struct sd_softc *sc; | |
174 | struct hp_device *hd; | |
175 | { | |
176 | int unit; | |
177 | register int ctlr, slave; | |
178 | register int i; | |
179 | register int tries = 10; | |
98d7aa4d | 180 | char idstr[32]; |
22d09b27 | 181 | int ismo = 0; |
60f56dfc KM |
182 | |
183 | ctlr = hd->hp_ctlr; | |
184 | slave = hd->hp_slave; | |
185 | unit = sc->sc_punit; | |
98d7aa4d | 186 | scsi_delay(-1); |
60f56dfc KM |
187 | |
188 | /* | |
189 | * See if unit exists and is a disk then read block size & nblocks. | |
190 | */ | |
191 | while ((i = scsi_test_unit_rdy(ctlr, slave, unit)) != 0) { | |
22d09b27 KM |
192 | if (i == -1 || --tries < 0) { |
193 | if (ismo) | |
194 | break; | |
60f56dfc | 195 | /* doesn't exist or not a CCS device */ |
98d7aa4d | 196 | goto failed; |
22d09b27 | 197 | } |
60f56dfc KM |
198 | if (i == STS_CHECKCOND) { |
199 | u_char sensebuf[128]; | |
200 | struct scsi_xsense *sp = (struct scsi_xsense *)sensebuf; | |
201 | ||
202 | scsi_request_sense(ctlr, slave, unit, sensebuf, | |
203 | sizeof(sensebuf)); | |
22d09b27 KM |
204 | if (sp->class == 7) |
205 | switch (sp->key) { | |
206 | /* not ready -- might be MO with no media */ | |
207 | case 2: | |
208 | if (sp->len == 12 && | |
209 | sensebuf[12] == 10) /* XXX */ | |
210 | ismo = 1; | |
211 | break; | |
60f56dfc | 212 | /* drive doing an RTZ -- give it a while */ |
22d09b27 KM |
213 | case 6: |
214 | DELAY(1000000); | |
215 | break; | |
216 | default: | |
217 | break; | |
218 | } | |
60f56dfc KM |
219 | } |
220 | DELAY(1000); | |
221 | } | |
22d09b27 KM |
222 | /* |
223 | * Find out about device | |
224 | */ | |
225 | if (scsi_immed_command(ctlr, slave, unit, &inq, | |
226 | (u_char *)&inqbuf, sizeof(inqbuf), B_READ)) | |
98d7aa4d | 227 | goto failed; |
60f56dfc KM |
228 | switch (inqbuf.type) { |
229 | case 0: /* disk */ | |
230 | case 4: /* WORM */ | |
231 | case 5: /* CD-ROM */ | |
232 | case 7: /* Magneto-optical */ | |
233 | break; | |
234 | default: /* not a disk */ | |
98d7aa4d | 235 | goto failed; |
60f56dfc | 236 | } |
22d09b27 | 237 | /* |
98d7aa4d | 238 | * Get a usable id string |
22d09b27 | 239 | */ |
98d7aa4d MH |
240 | if (inqbuf.version != 1) { |
241 | bcopy("UNKNOWN", &idstr[0], 8); | |
242 | bcopy("DRIVE TYPE", &idstr[8], 11); | |
243 | } else { | |
244 | bcopy((caddr_t)&inqbuf.vendor_id, (caddr_t)idstr, 28); | |
245 | for (i = 27; i > 23; --i) | |
246 | if (idstr[i] != ' ') | |
247 | break; | |
248 | idstr[i+1] = 0; | |
249 | for (i = 23; i > 7; --i) | |
250 | if (idstr[i] != ' ') | |
251 | break; | |
252 | idstr[i+1] = 0; | |
253 | for (i = 7; i >= 0; --i) | |
254 | if (idstr[i] != ' ') | |
255 | break; | |
256 | idstr[i+1] = 0; | |
22d09b27 KM |
257 | } |
258 | i = scsi_immed_command(ctlr, slave, unit, &cap, | |
259 | (u_char *)&capbuf, sizeof(capbuf), B_READ); | |
260 | if (i) { | |
98d7aa4d MH |
261 | if (i != STS_CHECKCOND || |
262 | bcmp(&idstr[0], "HP", 3) || | |
263 | bcmp(&idstr[8], "S6300.650A", 11)) | |
264 | goto failed; | |
22d09b27 | 265 | /* XXX unformatted or non-existant MO media; fake it */ |
98d7aa4d MH |
266 | sc->sc_blks = 318664; |
267 | sc->sc_blksize = 1024; | |
22d09b27 KM |
268 | } else { |
269 | sc->sc_blks = *(u_int *)&capbuf[0]; | |
270 | sc->sc_blksize = *(int *)&capbuf[4]; | |
271 | } | |
272 | /* return value of read capacity is last valid block number */ | |
273 | sc->sc_blks++; | |
60f56dfc KM |
274 | |
275 | if (inqbuf.version != 1) | |
276 | printf("sd%d: type 0x%x, qual 0x%x, ver %d", hd->hp_unit, | |
277 | inqbuf.type, inqbuf.qual, inqbuf.version); | |
98d7aa4d | 278 | else |
60f56dfc KM |
279 | printf("sd%d: %s %s rev %s", hd->hp_unit, idstr, &idstr[8], |
280 | &idstr[24]); | |
60f56dfc KM |
281 | printf(", %d %d byte blocks\n", sc->sc_blks, sc->sc_blksize); |
282 | if (sc->sc_blksize != DEV_BSIZE) { | |
283 | if (sc->sc_blksize < DEV_BSIZE) { | |
284 | printf("sd%d: need %d byte blocks - drive ignored\n", | |
285 | unit, DEV_BSIZE); | |
98d7aa4d | 286 | goto failed; |
60f56dfc KM |
287 | } |
288 | for (i = sc->sc_blksize; i > DEV_BSIZE; i >>= 1) | |
289 | ++sc->sc_bshift; | |
290 | sc->sc_blks <<= sc->sc_bshift; | |
291 | } | |
292 | sc->sc_wpms = 32 * (60 * DEV_BSIZE / 2); /* XXX */ | |
98d7aa4d | 293 | scsi_delay(0); |
60f56dfc | 294 | return(inqbuf.type); |
98d7aa4d MH |
295 | failed: |
296 | scsi_delay(0); | |
297 | return(-1); | |
60f56dfc KM |
298 | } |
299 | ||
300 | int | |
301 | sdinit(hd) | |
302 | register struct hp_device *hd; | |
303 | { | |
304 | register struct sd_softc *sc = &sd_softc[hd->hp_unit]; | |
305 | ||
306 | sc->sc_hd = hd; | |
307 | sc->sc_punit = sdpunit(hd->hp_flags); | |
308 | sc->sc_type = sdident(sc, hd); | |
309 | if (sc->sc_type < 0) | |
310 | return(0); | |
311 | sc->sc_dq.dq_ctlr = hd->hp_ctlr; | |
312 | sc->sc_dq.dq_unit = hd->hp_unit; | |
313 | sc->sc_dq.dq_slave = hd->hp_slave; | |
314 | sc->sc_dq.dq_driver = &sddriver; | |
315 | ||
316 | /* | |
317 | * If we don't have a disk label, build a default partition | |
318 | * table with 'standard' size root & swap and everything else | |
319 | * in the G partition. | |
320 | */ | |
321 | sc->sc_info = sddefaultpart; | |
322 | /* C gets everything */ | |
323 | sc->sc_info.part[2].nblocks = sc->sc_blks; | |
324 | sc->sc_info.part[2].endblk = sc->sc_blks; | |
325 | /* G gets from end of B to end of disk */ | |
326 | sc->sc_info.part[6].nblocks = sc->sc_blks - sc->sc_info.part[1].endblk; | |
327 | sc->sc_info.part[6].endblk = sc->sc_blks; | |
328 | /* | |
329 | * We also define the D, E and F paritions as an alternative to | |
330 | * B and G. D is 48Mb, starts after A and is intended for swapping. | |
331 | * E is 50Mb, starts after D and is intended for /usr. F starts | |
332 | * after E and is what ever is left. | |
333 | */ | |
334 | if (sc->sc_blks >= sc->sc_info.part[4].endblk) { | |
335 | sc->sc_info.part[5].nblocks = | |
336 | sc->sc_blks - sc->sc_info.part[4].endblk; | |
337 | sc->sc_info.part[5].endblk = sc->sc_blks; | |
338 | } else { | |
339 | sc->sc_info.part[5].strtblk = 0; | |
340 | sc->sc_info.part[3] = sc->sc_info.part[5]; | |
341 | sc->sc_info.part[4] = sc->sc_info.part[5]; | |
342 | } | |
343 | /* | |
344 | * H is a single partition alternative to E and F. | |
345 | */ | |
346 | if (sc->sc_blks >= sc->sc_info.part[3].endblk) { | |
347 | sc->sc_info.part[7].nblocks = | |
348 | sc->sc_blks - sc->sc_info.part[3].endblk; | |
349 | sc->sc_info.part[7].endblk = sc->sc_blks; | |
350 | } else { | |
351 | sc->sc_info.part[7].strtblk = 0; | |
352 | } | |
353 | ||
354 | sc->sc_flags = SDF_ALIVE; | |
355 | return(1); | |
356 | } | |
357 | ||
358 | void | |
359 | sdreset(sc, hd) | |
360 | register struct sd_softc *sc; | |
361 | register struct hp_device *hd; | |
362 | { | |
363 | sdstats[hd->hp_unit].sdresets++; | |
364 | } | |
365 | ||
366 | int | |
88f29710 | 367 | sdopen(dev, flags, mode, p) |
60f56dfc | 368 | dev_t dev; |
88f29710 MK |
369 | int flags, mode; |
370 | struct proc *p; | |
60f56dfc KM |
371 | { |
372 | register int unit = sdunit(dev); | |
373 | register struct sd_softc *sc = &sd_softc[unit]; | |
374 | ||
375 | if (unit >= NSD) | |
376 | return(ENXIO); | |
88f29710 | 377 | if ((sc->sc_flags & SDF_ALIVE) == 0 && suser(p->p_ucred, &p->p_acflag)) |
60f56dfc KM |
378 | return(ENXIO); |
379 | ||
380 | if (sc->sc_hd->hp_dk >= 0) | |
381 | dk_wpms[sc->sc_hd->hp_dk] = sc->sc_wpms; | |
382 | return(0); | |
383 | } | |
384 | ||
385 | /* | |
386 | * This routine is called for partial block transfers and non-aligned | |
387 | * transfers (the latter only being possible on devices with a block size | |
388 | * larger than DEV_BSIZE). The operation is performed in three steps | |
389 | * using a locally allocated buffer: | |
390 | * 1. transfer any initial partial block | |
391 | * 2. transfer full blocks | |
392 | * 3. transfer any final partial block | |
393 | */ | |
394 | static void | |
395 | sdlblkstrat(bp, bsize) | |
396 | register struct buf *bp; | |
397 | register int bsize; | |
398 | { | |
399 | register struct buf *cbp = (struct buf *)malloc(sizeof(struct buf), | |
400 | M_DEVBUF, M_WAITOK); | |
401 | caddr_t cbuf = (caddr_t)malloc(bsize, M_DEVBUF, M_WAITOK); | |
402 | register int bn, resid; | |
403 | register caddr_t addr; | |
404 | ||
405 | bzero((caddr_t)cbp, sizeof(*cbp)); | |
88f29710 | 406 | cbp->b_proc = curproc; /* XXX */ |
60f56dfc KM |
407 | cbp->b_dev = bp->b_dev; |
408 | bn = bp->b_blkno; | |
409 | resid = bp->b_bcount; | |
410 | addr = bp->b_un.b_addr; | |
411 | #ifdef DEBUG | |
412 | if (sddebug & SDB_PARTIAL) | |
413 | printf("sdlblkstrat: bp %x flags %x bn %x resid %x addr %x\n", | |
414 | bp, bp->b_flags, bn, resid, addr); | |
415 | #endif | |
416 | ||
417 | while (resid > 0) { | |
418 | register int boff = dbtob(bn) & (bsize - 1); | |
419 | register int count; | |
420 | ||
421 | if (boff || resid < bsize) { | |
422 | sdstats[sdunit(bp->b_dev)].sdpartials++; | |
423 | count = MIN(resid, bsize - boff); | |
424 | cbp->b_flags = B_BUSY | B_PHYS | B_READ; | |
425 | cbp->b_blkno = bn - btodb(boff); | |
426 | cbp->b_un.b_addr = cbuf; | |
427 | cbp->b_bcount = bsize; | |
428 | #ifdef DEBUG | |
429 | if (sddebug & SDB_PARTIAL) | |
430 | printf(" readahead: bn %x cnt %x off %x addr %x\n", | |
431 | cbp->b_blkno, count, boff, addr); | |
432 | #endif | |
433 | sdstrategy(cbp); | |
434 | biowait(cbp); | |
435 | if (cbp->b_flags & B_ERROR) { | |
436 | bp->b_flags |= B_ERROR; | |
437 | bp->b_error = cbp->b_error; | |
438 | break; | |
439 | } | |
440 | if (bp->b_flags & B_READ) { | |
441 | bcopy(&cbuf[boff], addr, count); | |
442 | goto done; | |
443 | } | |
444 | bcopy(addr, &cbuf[boff], count); | |
445 | #ifdef DEBUG | |
446 | if (sddebug & SDB_PARTIAL) | |
447 | printf(" writeback: bn %x cnt %x off %x addr %x\n", | |
448 | cbp->b_blkno, count, boff, addr); | |
449 | #endif | |
450 | } else { | |
451 | count = resid & ~(bsize - 1); | |
452 | cbp->b_blkno = bn; | |
453 | cbp->b_un.b_addr = addr; | |
454 | cbp->b_bcount = count; | |
455 | #ifdef DEBUG | |
456 | if (sddebug & SDB_PARTIAL) | |
457 | printf(" fulltrans: bn %x cnt %x addr %x\n", | |
458 | cbp->b_blkno, count, addr); | |
459 | #endif | |
460 | } | |
461 | cbp->b_flags = B_BUSY | B_PHYS | (bp->b_flags & B_READ); | |
462 | sdstrategy(cbp); | |
463 | biowait(cbp); | |
464 | if (cbp->b_flags & B_ERROR) { | |
465 | bp->b_flags |= B_ERROR; | |
466 | bp->b_error = cbp->b_error; | |
467 | break; | |
468 | } | |
469 | done: | |
470 | bn += btodb(count); | |
471 | resid -= count; | |
472 | addr += count; | |
473 | #ifdef DEBUG | |
474 | if (sddebug & SDB_PARTIAL) | |
475 | printf(" done: bn %x resid %x addr %x\n", | |
476 | bn, resid, addr); | |
477 | #endif | |
478 | } | |
479 | free(cbuf, M_DEVBUF); | |
480 | free(cbp, M_DEVBUF); | |
481 | } | |
482 | ||
483 | void | |
484 | sdstrategy(bp) | |
485 | register struct buf *bp; | |
486 | { | |
60f56dfc | 487 | register int unit = sdunit(bp->b_dev); |
60f56dfc | 488 | register struct sd_softc *sc = &sd_softc[unit]; |
22d09b27 | 489 | register struct size *pinfo = &sc->sc_info.part[sdpart(bp->b_dev)]; |
60f56dfc | 490 | register struct buf *dp = &sdtab[unit]; |
22d09b27 KM |
491 | register daddr_t bn; |
492 | register int sz, s; | |
60f56dfc KM |
493 | |
494 | if (sc->sc_format_pid) { | |
88f29710 | 495 | if (sc->sc_format_pid != curproc->p_pid) { /* XXX */ |
60f56dfc | 496 | bp->b_error = EPERM; |
22d09b27 KM |
497 | bp->b_flags |= B_ERROR; |
498 | goto done; | |
60f56dfc KM |
499 | } |
500 | bp->b_cylin = 0; | |
501 | } else { | |
502 | bn = bp->b_blkno; | |
22d09b27 KM |
503 | sz = howmany(bp->b_bcount, DEV_BSIZE); |
504 | if (bn < 0 || bn + sz > pinfo->nblocks) { | |
505 | sz = pinfo->nblocks - bn; | |
506 | if (sz == 0) { | |
60f56dfc KM |
507 | bp->b_resid = bp->b_bcount; |
508 | goto done; | |
509 | } | |
22d09b27 KM |
510 | if (sz < 0) { |
511 | bp->b_error = EINVAL; | |
512 | bp->b_flags |= B_ERROR; | |
513 | goto done; | |
514 | } | |
515 | bp->b_bcount = dbtob(sz); | |
60f56dfc KM |
516 | } |
517 | /* | |
518 | * Non-aligned or partial-block transfers handled specially. | |
519 | */ | |
520 | s = sc->sc_blksize - 1; | |
521 | if ((dbtob(bn) & s) || (bp->b_bcount & s)) { | |
522 | sdlblkstrat(bp, sc->sc_blksize); | |
523 | goto done; | |
524 | } | |
22d09b27 | 525 | bp->b_cylin = (bn + pinfo->strtblk) >> sc->sc_bshift; |
60f56dfc KM |
526 | } |
527 | s = splbio(); | |
528 | disksort(dp, bp); | |
529 | if (dp->b_active == 0) { | |
530 | dp->b_active = 1; | |
531 | sdustart(unit); | |
532 | } | |
533 | splx(s); | |
534 | return; | |
60f56dfc | 535 | done: |
22d09b27 | 536 | biodone(bp); |
60f56dfc KM |
537 | } |
538 | ||
539 | void | |
540 | sdustart(unit) | |
541 | register int unit; | |
542 | { | |
543 | if (scsireq(&sd_softc[unit].sc_dq)) | |
544 | sdstart(unit); | |
545 | } | |
546 | ||
761052c0 MK |
547 | /* |
548 | * Return: | |
549 | * 0 if not really an error | |
550 | * <0 if we should do a retry | |
551 | * >0 if a fatal error | |
552 | */ | |
22d09b27 | 553 | static int |
60f56dfc KM |
554 | sderror(unit, sc, hp, stat) |
555 | int unit, stat; | |
556 | register struct sd_softc *sc; | |
557 | register struct hp_device *hp; | |
558 | { | |
761052c0 | 559 | int cond = 1; |
22d09b27 | 560 | |
60f56dfc KM |
561 | sdsense[unit].status = stat; |
562 | if (stat & STS_CHECKCOND) { | |
563 | struct scsi_xsense *sp; | |
564 | ||
565 | scsi_request_sense(hp->hp_ctlr, hp->hp_slave, | |
566 | sc->sc_punit, sdsense[unit].sense, | |
567 | sizeof(sdsense[unit].sense)); | |
568 | sp = (struct scsi_xsense *)sdsense[unit].sense; | |
569 | printf("sd%d: scsi sense class %d, code %d", unit, | |
570 | sp->class, sp->code); | |
571 | if (sp->class == 7) { | |
572 | printf(", key %d", sp->key); | |
573 | if (sp->valid) | |
574 | printf(", blk %d", *(int *)&sp->info1); | |
761052c0 MK |
575 | switch (sp->key) { |
576 | /* no sense, try again */ | |
577 | case 0: | |
578 | cond = -1; | |
579 | break; | |
580 | /* recovered error, not a problem */ | |
581 | case 1: | |
582 | cond = 0; | |
583 | break; | |
584 | } | |
60f56dfc KM |
585 | } |
586 | printf("\n"); | |
587 | } | |
761052c0 | 588 | return(cond); |
60f56dfc KM |
589 | } |
590 | ||
591 | static void | |
592 | sdfinish(unit, sc, bp) | |
593 | int unit; | |
594 | register struct sd_softc *sc; | |
595 | register struct buf *bp; | |
596 | { | |
597 | sdtab[unit].b_errcnt = 0; | |
598 | sdtab[unit].b_actf = bp->b_actf; | |
599 | bp->b_resid = 0; | |
22d09b27 | 600 | biodone(bp); |
60f56dfc KM |
601 | scsifree(&sc->sc_dq); |
602 | if (sdtab[unit].b_actf) | |
603 | sdustart(unit); | |
604 | else | |
605 | sdtab[unit].b_active = 0; | |
606 | } | |
607 | ||
608 | void | |
609 | sdstart(unit) | |
610 | register int unit; | |
611 | { | |
612 | register struct sd_softc *sc = &sd_softc[unit]; | |
613 | register struct hp_device *hp = sc->sc_hd; | |
614 | ||
615 | /* | |
616 | * we have the SCSI bus -- in format mode, we may or may not need dma | |
617 | * so check now. | |
618 | */ | |
619 | if (sc->sc_format_pid && legal_cmds[sdcmd[unit].cdb[0]] > 0) { | |
620 | register struct buf *bp = sdtab[unit].b_actf; | |
621 | register int sts; | |
622 | ||
623 | sts = scsi_immed_command(hp->hp_ctlr, hp->hp_slave, | |
624 | sc->sc_punit, &sdcmd[unit], | |
625 | bp->b_un.b_addr, bp->b_bcount, | |
626 | bp->b_flags & B_READ); | |
627 | sdsense[unit].status = sts; | |
628 | if (sts & 0xfe) { | |
22d09b27 | 629 | (void) sderror(unit, sc, hp, sts); |
60f56dfc KM |
630 | bp->b_flags |= B_ERROR; |
631 | bp->b_error = EIO; | |
632 | } | |
633 | sdfinish(unit, sc, bp); | |
634 | ||
635 | } else if (scsiustart(hp->hp_ctlr)) | |
636 | sdgo(unit); | |
637 | } | |
638 | ||
639 | void | |
640 | sdgo(unit) | |
641 | register int unit; | |
642 | { | |
643 | register struct sd_softc *sc = &sd_softc[unit]; | |
644 | register struct hp_device *hp = sc->sc_hd; | |
645 | register struct buf *bp = sdtab[unit].b_actf; | |
646 | register int pad; | |
647 | register struct scsi_fmt_cdb *cmd; | |
648 | ||
649 | if (sc->sc_format_pid) { | |
650 | cmd = &sdcmd[unit]; | |
651 | pad = 0; | |
652 | } else { | |
653 | cmd = bp->b_flags & B_READ? &sd_read_cmd : &sd_write_cmd; | |
654 | *(int *)(&cmd->cdb[2]) = bp->b_cylin; | |
655 | pad = howmany(bp->b_bcount, sc->sc_blksize); | |
656 | *(u_short *)(&cmd->cdb[7]) = pad; | |
657 | pad = (bp->b_bcount & (sc->sc_blksize - 1)) != 0; | |
658 | #ifdef DEBUG | |
659 | if (pad) | |
660 | printf("sd%d: partial block xfer -- %x bytes\n", | |
661 | unit, bp->b_bcount); | |
662 | #endif | |
663 | sdstats[unit].sdtransfers++; | |
664 | } | |
665 | if (scsigo(hp->hp_ctlr, hp->hp_slave, sc->sc_punit, bp, cmd, pad) == 0) { | |
666 | if (hp->hp_dk >= 0) { | |
667 | dk_busy |= 1 << hp->hp_dk; | |
668 | ++dk_seek[hp->hp_dk]; | |
669 | ++dk_xfer[hp->hp_dk]; | |
670 | dk_wds[hp->hp_dk] += bp->b_bcount >> 6; | |
671 | } | |
672 | return; | |
673 | } | |
674 | #ifdef DEBUG | |
675 | if (sddebug & SDB_ERROR) | |
676 | printf("sd%d: sdstart: %s adr %d blk %d len %d ecnt %d\n", | |
677 | unit, bp->b_flags & B_READ? "read" : "write", | |
678 | bp->b_un.b_addr, bp->b_cylin, bp->b_bcount, | |
679 | sdtab[unit].b_errcnt); | |
680 | #endif | |
681 | bp->b_flags |= B_ERROR; | |
682 | bp->b_error = EIO; | |
683 | sdfinish(unit, sc, bp); | |
684 | } | |
685 | ||
686 | void | |
687 | sdintr(unit, stat) | |
688 | register int unit; | |
689 | int stat; | |
690 | { | |
691 | register struct sd_softc *sc = &sd_softc[unit]; | |
692 | register struct buf *bp = sdtab[unit].b_actf; | |
693 | register struct hp_device *hp = sc->sc_hd; | |
761052c0 | 694 | int cond; |
60f56dfc KM |
695 | |
696 | if (bp == NULL) { | |
697 | printf("sd%d: bp == NULL\n", unit); | |
698 | return; | |
699 | } | |
700 | if (hp->hp_dk >= 0) | |
701 | dk_busy &=~ (1 << hp->hp_dk); | |
702 | if (stat) { | |
703 | #ifdef DEBUG | |
704 | if (sddebug & SDB_ERROR) | |
705 | printf("sd%d: sdintr: bad scsi status 0x%x\n", | |
706 | unit, stat); | |
707 | #endif | |
761052c0 MK |
708 | cond = sderror(unit, sc, hp, stat); |
709 | if (cond) { | |
710 | if (cond < 0 && sdtab[unit].b_errcnt++ < SDRETRY) { | |
ebe91f3c | 711 | #ifdef DEBUG |
761052c0 MK |
712 | if (sddebug & SDB_ERROR) |
713 | printf("sd%d: retry #%d\n", | |
714 | unit, sdtab[unit].b_errcnt); | |
ebe91f3c | 715 | #endif |
761052c0 MK |
716 | sdstart(unit); |
717 | return; | |
718 | } | |
719 | bp->b_flags |= B_ERROR; | |
720 | bp->b_error = EIO; | |
22d09b27 | 721 | } |
60f56dfc KM |
722 | } |
723 | sdfinish(unit, sc, bp); | |
724 | } | |
725 | ||
726 | int | |
88f29710 | 727 | sdread(dev, uio, flags) |
60f56dfc KM |
728 | dev_t dev; |
729 | struct uio *uio; | |
88f29710 | 730 | int flags; |
60f56dfc KM |
731 | { |
732 | register int unit = sdunit(dev); | |
733 | register int pid; | |
734 | ||
88f29710 MK |
735 | if ((pid = sd_softc[unit].sc_format_pid) && |
736 | pid != uio->uio_procp->p_pid) | |
60f56dfc KM |
737 | return (EPERM); |
738 | ||
88f29710 | 739 | return (physio(sdstrategy, NULL, dev, B_READ, minphys, uio)); |
60f56dfc KM |
740 | } |
741 | ||
742 | int | |
88f29710 | 743 | sdwrite(dev, uio, flags) |
60f56dfc KM |
744 | dev_t dev; |
745 | struct uio *uio; | |
88f29710 | 746 | int flags; |
60f56dfc KM |
747 | { |
748 | register int unit = sdunit(dev); | |
749 | register int pid; | |
750 | ||
88f29710 MK |
751 | if ((pid = sd_softc[unit].sc_format_pid) && |
752 | pid != uio->uio_procp->p_pid) | |
60f56dfc KM |
753 | return (EPERM); |
754 | ||
88f29710 | 755 | return (physio(sdstrategy, NULL, dev, B_WRITE, minphys, uio)); |
60f56dfc KM |
756 | } |
757 | ||
758 | int | |
88f29710 | 759 | sdioctl(dev, cmd, data, flag, p) |
60f56dfc KM |
760 | dev_t dev; |
761 | int cmd; | |
762 | caddr_t data; | |
763 | int flag; | |
88f29710 | 764 | struct proc *p; |
60f56dfc KM |
765 | { |
766 | register int unit = sdunit(dev); | |
767 | register struct sd_softc *sc = &sd_softc[unit]; | |
768 | ||
769 | switch (cmd) { | |
770 | default: | |
771 | return (EINVAL); | |
772 | ||
773 | case SDIOCSFORMAT: | |
774 | /* take this device into or out of "format" mode */ | |
88f29710 | 775 | if (suser(p->p_ucred, &p->p_acflag)) |
60f56dfc KM |
776 | return(EPERM); |
777 | ||
778 | if (*(int *)data) { | |
779 | if (sc->sc_format_pid) | |
780 | return (EPERM); | |
88f29710 | 781 | sc->sc_format_pid = p->p_pid; |
60f56dfc KM |
782 | } else |
783 | sc->sc_format_pid = 0; | |
784 | return (0); | |
785 | ||
786 | case SDIOCGFORMAT: | |
787 | /* find out who has the device in format mode */ | |
788 | *(int *)data = sc->sc_format_pid; | |
789 | return (0); | |
790 | ||
791 | case SDIOCSCSICOMMAND: | |
792 | /* | |
793 | * Save what user gave us as SCSI cdb to use with next | |
794 | * read or write to the char device. | |
795 | */ | |
88f29710 | 796 | if (sc->sc_format_pid != p->p_pid) |
60f56dfc KM |
797 | return (EPERM); |
798 | if (legal_cmds[((struct scsi_fmt_cdb *)data)->cdb[0]] == 0) | |
799 | return (EINVAL); | |
800 | bcopy(data, (caddr_t)&sdcmd[unit], sizeof(sdcmd[0])); | |
801 | return (0); | |
802 | ||
803 | case SDIOCSENSE: | |
804 | /* | |
805 | * return the SCSI sense data saved after the last | |
806 | * operation that completed with "check condition" status. | |
807 | */ | |
808 | bcopy((caddr_t)&sdsense[unit], data, sizeof(sdsense[0])); | |
809 | return (0); | |
810 | ||
811 | } | |
812 | /*NOTREACHED*/ | |
813 | } | |
814 | ||
815 | int | |
816 | sdsize(dev) | |
817 | dev_t dev; | |
818 | { | |
819 | register int unit = sdunit(dev); | |
820 | register struct sd_softc *sc = &sd_softc[unit]; | |
821 | ||
822 | if (unit >= NSD || (sc->sc_flags & SDF_ALIVE) == 0) | |
823 | return(-1); | |
824 | ||
825 | return(sc->sc_info.part[sdpart(dev)].nblocks); | |
826 | } | |
827 | ||
60f56dfc KM |
828 | /* |
829 | * Non-interrupt driven, non-dma dump routine. | |
830 | */ | |
831 | int | |
832 | sddump(dev) | |
833 | dev_t dev; | |
834 | { | |
835 | int part = sdpart(dev); | |
836 | int unit = sdunit(dev); | |
837 | register struct sd_softc *sc = &sd_softc[unit]; | |
838 | register struct hp_device *hp = sc->sc_hd; | |
839 | register daddr_t baddr; | |
840 | register int maddr; | |
841 | register int pages, i; | |
842 | int stat; | |
843 | extern int lowram; | |
844 | ||
845 | /* | |
846 | * Hmm... all vax drivers dump maxfree pages which is physmem minus | |
847 | * the message buffer. Is there a reason for not dumping the | |
848 | * message buffer? Savecore expects to read 'dumpsize' pages of | |
849 | * dump, where dumpsys() sets dumpsize to physmem! | |
850 | */ | |
851 | pages = physmem; | |
852 | ||
853 | /* is drive ok? */ | |
854 | if (unit >= NSD || (sc->sc_flags & SDF_ALIVE) == 0) | |
855 | return (ENXIO); | |
856 | /* dump parameters in range? */ | |
857 | if (dumplo < 0 || dumplo >= sc->sc_info.part[part].nblocks) | |
858 | return (EINVAL); | |
859 | if (dumplo + ctod(pages) > sc->sc_info.part[part].nblocks) | |
860 | pages = dtoc(sc->sc_info.part[part].nblocks - dumplo); | |
861 | maddr = lowram; | |
862 | baddr = dumplo + sc->sc_info.part[part].strtblk; | |
863 | /* scsi bus idle? */ | |
864 | if (!scsireq(&sc->sc_dq)) { | |
865 | scsireset(hp->hp_ctlr); | |
866 | sdreset(sc, sc->sc_hd); | |
867 | printf("[ drive %d reset ] ", unit); | |
868 | } | |
869 | for (i = 0; i < pages; i++) { | |
870 | #define NPGMB (1024*1024/NBPG) | |
871 | /* print out how many Mbs we have dumped */ | |
872 | if (i && (i % NPGMB) == 0) | |
873 | printf("%d ", i / NPGMB); | |
874 | #undef NPBMG | |
e0f6df7b KM |
875 | pmap_enter(pmap_kernel(), (vm_offset_t)vmmap, maddr, |
876 | VM_PROT_READ, TRUE); | |
60f56dfc KM |
877 | stat = scsi_tt_write(hp->hp_ctlr, hp->hp_slave, sc->sc_punit, |
878 | vmmap, NBPG, baddr, sc->sc_bshift); | |
879 | if (stat) { | |
880 | printf("sddump: scsi write error 0x%x\n", stat); | |
881 | return (EIO); | |
882 | } | |
883 | maddr += NBPG; | |
884 | baddr += ctod(1); | |
885 | } | |
886 | return (0); | |
887 | } | |
888 | #endif |