1 /*- 2 * Copyright (c) 2010 by Panasas, Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice immediately at the beginning of the file, without modification, 10 * this list of conditions, and the following disclaimer. 11 * 2. The name of the author may not be used to endorse or promote products 12 * derived from this software without specific prior written permission. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 18 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 /* $FreeBSD$ */ 27 /* 28 * A VHBA device that has a medium number of device. 29 */ 30 #include "vhba.h" 31 32 #define MAX_TGT 4 33 #define MAX_LUN 10 34 35 #define DISK_SIZE 32 36 #define DISK_SHIFT 9 37 #define DISK_NBLKS ((DISK_SIZE << 20) >> DISK_SHIFT) 38 #define PSEUDO_SPT 64 39 #define PSEUDO_HDS 64 40 #define PSEUDO_SPC (PSEUDO_SPT * PSEUDO_HDS) 41 42 typedef struct { 43 vhba_softc_t * vhba; 44 uint8_t * disk; 45 size_t disk_size; 46 struct task qt; 47 } vhbamedium_t; 48 49 static void vhba_task(void *, int); 50 static void vhbamedium_act(vhbamedium_t *, struct ccb_scsiio *); 51 52 void 53 vhba_init(vhba_softc_t *vhba) 54 { 55 static vhbamedium_t vhbas; 56 vhbas.vhba = vhba; 57 vhbas.disk_size = DISK_SIZE << 20; 58 vhbas.disk = malloc(vhbas.disk_size, M_DEVBUF, M_WAITOK|M_ZERO); 59 vhba->private = &vhbas; 60 TASK_INIT(&vhbas.qt, 0, vhba_task, &vhbas); 61 } 62 63 64 void 65 vhba_fini(vhba_softc_t *vhba) 66 { 67 vhbamedium_t *vhbas = vhba->private; 68 vhba->private = NULL; 69 free(vhbas->disk, M_DEVBUF); 70 } 71 72 void 73 vhba_kick(vhba_softc_t *vhba) 74 { 75 vhbamedium_t *vhbas = vhba->private; 76 taskqueue_enqueue(taskqueue_swi, &vhbas->qt); 77 } 78 79 static void 80 vhba_task(void *arg, int pending) 81 { 82 vhbamedium_t *vhbas = arg; 83 struct ccb_hdr *ccbh; 84 85 mtx_lock(&vhbas->vhba->lock); 86 while ((ccbh = TAILQ_FIRST(&vhbas->vhba->actv)) != NULL) { 87 TAILQ_REMOVE(&vhbas->vhba->actv, ccbh, sim_links.tqe); 88 vhbamedium_act(vhbas, (struct ccb_scsiio *)ccbh); 89 } 90 while ((ccbh = TAILQ_FIRST(&vhbas->vhba->done)) != NULL) { 91 TAILQ_REMOVE(&vhbas->vhba->done, ccbh, sim_links.tqe); 92 xpt_done((union ccb *)ccbh); 93 } 94 mtx_unlock(&vhbas->vhba->lock); 95 } 96 97 static void 98 vhbamedium_act(vhbamedium_t *vhbas, struct ccb_scsiio *csio) 99 { 100 char junk[128]; 101 uint8_t *cdb, *ptr, status; 102 uint32_t data_len; 103 uint64_t off; 104 105 data_len = 0; 106 status = SCSI_STATUS_OK; 107 108 memset(&csio->sense_data, 0, sizeof (csio->sense_data)); 109 cdb = csio->cdb_io.cdb_bytes; 110 111 if (csio->ccb_h.target_id >= MAX_TGT) { 112 csio->ccb_h.status = CAM_SEL_TIMEOUT; 113 TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); 114 return; 115 } 116 if (csio->ccb_h.target_lun >= MAX_LUN && cdb[0] != INQUIRY && cdb[0] != REPORT_LUNS && cdb[0] != REQUEST_SENSE) { 117 vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x25, 0x0); 118 TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); 119 return; 120 } 121 122 switch (cdb[0]) { 123 case MODE_SENSE: 124 case MODE_SENSE_10: 125 { 126 unsigned int nbyte; 127 uint8_t page = cdb[2] & SMS_PAGE_CODE; 128 uint8_t pgctl = cdb[2] & SMS_PAGE_CTRL_MASK; 129 130 switch (page) { 131 case SMS_FORMAT_DEVICE_PAGE: 132 case SMS_GEOMETRY_PAGE: 133 case SMS_CACHE_PAGE: 134 case SMS_CONTROL_MODE_PAGE: 135 case SMS_ALL_PAGES_PAGE: 136 break; 137 default: 138 vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); 139 TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); 140 return; 141 } 142 memset(junk, 0, sizeof (junk)); 143 if (cdb[1] & SMS_DBD) { 144 ptr = &junk[4]; 145 } else { 146 ptr = junk; 147 ptr[3] = 8; 148 ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff; 149 ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff; 150 ptr[6] = ((1 << DISK_SHIFT) >> 8) & 0xff; 151 ptr[7] = ((1 << DISK_SHIFT)) & 0xff; 152 153 ptr[8] = (DISK_NBLKS >> 24) & 0xff; 154 ptr[9] = (DISK_NBLKS >> 16) & 0xff; 155 ptr[10] = (DISK_NBLKS >> 8) & 0xff; 156 ptr[11] = DISK_NBLKS & 0xff; 157 ptr += 12; 158 } 159 160 if (page == SMS_ALL_PAGES_PAGE || page == SMS_FORMAT_DEVICE_PAGE) { 161 ptr[0] = SMS_FORMAT_DEVICE_PAGE; 162 ptr[1] = 24; 163 if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { 164 /* tracks per zone */ 165 /* ptr[2] = 0; */ 166 /* ptr[3] = 0; */ 167 /* alternate sectors per zone */ 168 /* ptr[4] = 0; */ 169 /* ptr[5] = 0; */ 170 /* alternate tracks per zone */ 171 /* ptr[6] = 0; */ 172 /* ptr[7] = 0; */ 173 /* alternate tracks per logical unit */ 174 /* ptr[8] = 0; */ 175 /* ptr[9] = 0; */ 176 /* sectors per track */ 177 ptr[10] = (PSEUDO_SPT >> 8) & 0xff; 178 ptr[11] = PSEUDO_SPT & 0xff; 179 /* data bytes per physical sector */ 180 ptr[12] = ((1 << DISK_SHIFT) >> 8) & 0xff; 181 ptr[13] = (1 << DISK_SHIFT) & 0xff; 182 /* interleave */ 183 /* ptr[14] = 0; */ 184 /* ptr[15] = 1; */ 185 /* track skew factor */ 186 /* ptr[16] = 0; */ 187 /* ptr[17] = 0; */ 188 /* cylinder skew factor */ 189 /* ptr[18] = 0; */ 190 /* ptr[19] = 0; */ 191 /* SSRC, HSEC, RMB, SURF */ 192 } 193 ptr += 26; 194 } 195 196 if (page == SMS_ALL_PAGES_PAGE || page == SMS_GEOMETRY_PAGE) { 197 ptr[0] = SMS_GEOMETRY_PAGE; 198 ptr[1] = 24; 199 if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { 200 uint32_t cyl = (DISK_NBLKS + ((PSEUDO_SPC - 1))) / PSEUDO_SPC; 201 /* number of cylinders */ 202 ptr[2] = (cyl >> 24) & 0xff; 203 ptr[3] = (cyl >> 16) & 0xff; 204 ptr[4] = cyl & 0xff; 205 /* number of heads */ 206 ptr[5] = PSEUDO_HDS; 207 /* starting cylinder- write precompensation */ 208 /* ptr[6] = 0; */ 209 /* ptr[7] = 0; */ 210 /* ptr[8] = 0; */ 211 /* starting cylinder- reduced write current */ 212 /* ptr[9] = 0; */ 213 /* ptr[10] = 0; */ 214 /* ptr[11] = 0; */ 215 /* drive step rate */ 216 /* ptr[12] = 0; */ 217 /* ptr[13] = 0; */ 218 /* landing zone cylinder */ 219 /* ptr[14] = 0; */ 220 /* ptr[15] = 0; */ 221 /* ptr[16] = 0; */ 222 /* RPL */ 223 /* ptr[17] = 0; */ 224 /* rotational offset */ 225 /* ptr[18] = 0; */ 226 /* medium rotation rate - 7200 RPM */ 227 ptr[20] = 0x1c; 228 ptr[21] = 0x20; 229 } 230 ptr += 26; 231 } 232 233 if (page == SMS_ALL_PAGES_PAGE || page == SMS_CACHE_PAGE) { 234 ptr[0] = SMS_CACHE_PAGE; 235 ptr[1] = 18; 236 ptr[2] = 1 << 2; 237 ptr += 20; 238 } 239 240 if (page == SMS_ALL_PAGES_PAGE || page == SMS_CONTROL_MODE_PAGE) { 241 ptr[0] = SMS_CONTROL_MODE_PAGE; 242 ptr[1] = 10; 243 if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { 244 ptr[3] = 1 << 4; /* unrestricted reordering allowed */ 245 ptr[8] = 0x75; /* 30000 ms */ 246 ptr[9] = 0x30; 247 } 248 ptr += 12; 249 } 250 nbyte = (char *)ptr - &junk[0]; 251 ptr[0] = nbyte - 4; 252 253 if (cdb[0] == MODE_SENSE) { 254 data_len = min(cdb[4], csio->dxfer_len); 255 } else { 256 uint16_t tw = (cdb[7] << 8) | cdb[8]; 257 data_len = min(tw, csio->dxfer_len); 258 } 259 data_len = min(data_len, nbyte); 260 if (data_len) { 261 memcpy(csio->data_ptr, junk, data_len); 262 } 263 csio->resid = csio->dxfer_len - data_len; 264 break; 265 } 266 case READ_6: 267 case READ_10: 268 case READ_12: 269 case READ_16: 270 case WRITE_6: 271 case WRITE_10: 272 case WRITE_12: 273 case WRITE_16: 274 if (vhba_rwparm(cdb, &off, &data_len, DISK_NBLKS, DISK_SHIFT)) { 275 vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); 276 break; 277 } 278 if (data_len) { 279 if ((cdb[0] & 0xf) == 8) { 280 memcpy(csio->data_ptr, &vhbas->disk[off], data_len); 281 } else { 282 memcpy(&vhbas->disk[off], csio->data_ptr, data_len); 283 } 284 csio->resid = csio->dxfer_len - data_len; 285 } else { 286 csio->resid = csio->dxfer_len; 287 } 288 break; 289 break; 290 291 case READ_CAPACITY: 292 if (cdb[2] || cdb[3] || cdb[4] || cdb[5]) { 293 vhba_fill_sense(csio, SSD_KEY_UNIT_ATTENTION, 0x24, 0x0); 294 break; 295 } 296 if (cdb[8] & 0x1) { /* PMI */ 297 csio->data_ptr[0] = 0xff; 298 csio->data_ptr[1] = 0xff; 299 csio->data_ptr[2] = 0xff; 300 csio->data_ptr[3] = 0xff; 301 } else { 302 uint64_t last_blk = DISK_NBLKS - 1; 303 if (last_blk < 0xffffffffULL) { 304 csio->data_ptr[0] = (last_blk >> 24) & 0xff; 305 csio->data_ptr[1] = (last_blk >> 16) & 0xff; 306 csio->data_ptr[2] = (last_blk >> 8) & 0xff; 307 csio->data_ptr[3] = (last_blk) & 0xff; 308 } else { 309 csio->data_ptr[0] = 0xff; 310 csio->data_ptr[1] = 0xff; 311 csio->data_ptr[2] = 0xff; 312 csio->data_ptr[3] = 0xff; 313 } 314 } 315 csio->data_ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff; 316 csio->data_ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff; 317 csio->data_ptr[6] = ((1 << DISK_SHIFT) >> 8) & 0xff; 318 csio->data_ptr[7] = ((1 << DISK_SHIFT)) & 0xff; 319 break; 320 321 default: 322 vhba_default_cmd(csio, MAX_LUN, NULL); 323 break; 324 } 325 csio->ccb_h.status &= ~CAM_STATUS_MASK; 326 if (csio->scsi_status != SCSI_STATUS_OK) { 327 csio->ccb_h.status |= CAM_SCSI_STATUS_ERROR; 328 if (csio->scsi_status == SCSI_STATUS_CHECK_COND) { 329 csio->ccb_h.status |= CAM_AUTOSNS_VALID; 330 } 331 } else { 332 csio->scsi_status = SCSI_STATUS_OK; 333 csio->ccb_h.status |= CAM_REQ_CMP; 334 } 335 TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); 336 } 337 DEV_MODULE(vhba_mediaum, vhba_modprobe, NULL); 338