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