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 /* 27 * "Simple" VHBA device 28 */ 29 #include "vhba.h" 30 31 #define MAX_TGT 1 32 #define MAX_LUN 1 33 34 #define DISK_SIZE 32 35 #define DISK_SHIFT 9 36 #define DISK_NBLKS ((DISK_SIZE << 20) >> DISK_SHIFT) 37 #define PSEUDO_SPT 64 38 #define PSEUDO_HDS 64 39 #define PSEUDO_SPC (PSEUDO_SPT * PSEUDO_HDS) 40 41 typedef struct { 42 vhba_softc_t * vhba; 43 uint8_t * disk; 44 size_t disk_size; 45 struct task qt; 46 } vhbasimple_t; 47 48 static void vhba_task(void *, int); 49 static void vhbasimple_act(vhbasimple_t *, struct ccb_scsiio *); 50 51 void 52 vhba_init(vhba_softc_t *vhba) 53 { 54 static vhbasimple_t vhbas; 55 vhbas.vhba = vhba; 56 vhbas.disk_size = DISK_SIZE << 20; 57 vhbas.disk = malloc(vhbas.disk_size, M_DEVBUF, M_WAITOK|M_ZERO); 58 vhba->private = &vhbas; 59 TASK_INIT(&vhbas.qt, 0, vhba_task, &vhbas); 60 } 61 62 63 void 64 vhba_fini(vhba_softc_t *vhba) 65 { 66 vhbasimple_t *vhbas = vhba->private; 67 vhba->private = NULL; 68 free(vhbas->disk, M_DEVBUF); 69 } 70 71 void 72 vhba_kick(vhba_softc_t *vhba) 73 { 74 vhbasimple_t *vhbas = vhba->private; 75 taskqueue_enqueue(taskqueue_swi, &vhbas->qt); 76 } 77 78 static void 79 vhba_task(void *arg, int pending) 80 { 81 vhbasimple_t *vhbas = arg; 82 struct ccb_hdr *ccbh; 83 84 mtx_lock(&vhbas->vhba->lock); 85 while ((ccbh = TAILQ_FIRST(&vhbas->vhba->actv)) != NULL) { 86 TAILQ_REMOVE(&vhbas->vhba->actv, ccbh, sim_links.tqe); 87 vhbasimple_act(vhbas, (struct ccb_scsiio *)ccbh); 88 } 89 while ((ccbh = TAILQ_FIRST(&vhbas->vhba->done)) != NULL) { 90 TAILQ_REMOVE(&vhbas->vhba->done, ccbh, sim_links.tqe); 91 xpt_done((union ccb *)ccbh); 92 } 93 mtx_unlock(&vhbas->vhba->lock); 94 } 95 96 static void 97 vhbasimple_act(vhbasimple_t *vhbas, struct ccb_scsiio *csio) 98 { 99 char junk[128]; 100 uint8_t *cdb, *ptr, status; 101 uint32_t data_len; 102 uint64_t off; 103 104 data_len = 0; 105 status = SCSI_STATUS_OK; 106 107 memset(&csio->sense_data, 0, sizeof (csio->sense_data)); 108 cdb = csio->cdb_io.cdb_bytes; 109 110 if (csio->ccb_h.target_id >= MAX_TGT) { 111 csio->ccb_h.status = CAM_SEL_TIMEOUT; 112 TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); 113 return; 114 } 115 if (csio->ccb_h.target_lun >= MAX_LUN && cdb[0] != INQUIRY && cdb[0] != REPORT_LUNS && cdb[0] != REQUEST_SENSE) { 116 vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x25, 0x0); 117 TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); 118 return; 119 } 120 121 switch (cdb[0]) { 122 case MODE_SENSE: 123 case MODE_SENSE_10: 124 { 125 unsigned int nbyte; 126 uint8_t page = cdb[2] & SMS_PAGE_CODE; 127 uint8_t pgctl = cdb[2] & SMS_PAGE_CTRL_MASK; 128 129 switch (page) { 130 case SMS_FORMAT_DEVICE_PAGE: 131 case SMS_GEOMETRY_PAGE: 132 case SMS_CACHE_PAGE: 133 case SMS_CONTROL_MODE_PAGE: 134 case SMS_ALL_PAGES_PAGE: 135 break; 136 default: 137 vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); 138 TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); 139 return; 140 } 141 memset(junk, 0, sizeof (junk)); 142 if (cdb[1] & SMS_DBD) { 143 ptr = &junk[4]; 144 } else { 145 ptr = junk; 146 ptr[3] = 8; 147 ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff; 148 ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff; 149 ptr[6] = ((1 << DISK_SHIFT) >> 8) & 0xff; 150 ptr[7] = ((1 << DISK_SHIFT)) & 0xff; 151 152 ptr[8] = (DISK_NBLKS >> 24) & 0xff; 153 ptr[9] = (DISK_NBLKS >> 16) & 0xff; 154 ptr[10] = (DISK_NBLKS >> 8) & 0xff; 155 ptr[11] = DISK_NBLKS & 0xff; 156 ptr += 12; 157 } 158 159 if (page == SMS_ALL_PAGES_PAGE || page == SMS_FORMAT_DEVICE_PAGE) { 160 ptr[0] = SMS_FORMAT_DEVICE_PAGE; 161 ptr[1] = 24; 162 if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { 163 /* tracks per zone */ 164 /* ptr[2] = 0; */ 165 /* ptr[3] = 0; */ 166 /* alternate sectors per zone */ 167 /* ptr[4] = 0; */ 168 /* ptr[5] = 0; */ 169 /* alternate tracks per zone */ 170 /* ptr[6] = 0; */ 171 /* ptr[7] = 0; */ 172 /* alternate tracks per logical unit */ 173 /* ptr[8] = 0; */ 174 /* ptr[9] = 0; */ 175 /* sectors per track */ 176 ptr[10] = (PSEUDO_SPT >> 8) & 0xff; 177 ptr[11] = PSEUDO_SPT & 0xff; 178 /* data bytes per physical sector */ 179 ptr[12] = ((1 << DISK_SHIFT) >> 8) & 0xff; 180 ptr[13] = (1 << DISK_SHIFT) & 0xff; 181 /* interleave */ 182 /* ptr[14] = 0; */ 183 /* ptr[15] = 1; */ 184 /* track skew factor */ 185 /* ptr[16] = 0; */ 186 /* ptr[17] = 0; */ 187 /* cylinder skew factor */ 188 /* ptr[18] = 0; */ 189 /* ptr[19] = 0; */ 190 /* SSRC, HSEC, RMB, SURF */ 191 } 192 ptr += 26; 193 } 194 195 if (page == SMS_ALL_PAGES_PAGE || page == SMS_GEOMETRY_PAGE) { 196 ptr[0] = SMS_GEOMETRY_PAGE; 197 ptr[1] = 24; 198 if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { 199 uint32_t cyl = (DISK_NBLKS + ((PSEUDO_SPC - 1))) / PSEUDO_SPC; 200 /* number of cylinders */ 201 ptr[2] = (cyl >> 24) & 0xff; 202 ptr[3] = (cyl >> 16) & 0xff; 203 ptr[4] = cyl & 0xff; 204 /* number of heads */ 205 ptr[5] = PSEUDO_HDS; 206 /* starting cylinder- write precompensation */ 207 /* ptr[6] = 0; */ 208 /* ptr[7] = 0; */ 209 /* ptr[8] = 0; */ 210 /* starting cylinder- reduced write current */ 211 /* ptr[9] = 0; */ 212 /* ptr[10] = 0; */ 213 /* ptr[11] = 0; */ 214 /* drive step rate */ 215 /* ptr[12] = 0; */ 216 /* ptr[13] = 0; */ 217 /* landing zone cylinder */ 218 /* ptr[14] = 0; */ 219 /* ptr[15] = 0; */ 220 /* ptr[16] = 0; */ 221 /* RPL */ 222 /* ptr[17] = 0; */ 223 /* rotational offset */ 224 /* ptr[18] = 0; */ 225 /* medium rotation rate - 7200 RPM */ 226 ptr[20] = 0x1c; 227 ptr[21] = 0x20; 228 } 229 ptr += 26; 230 } 231 232 if (page == SMS_ALL_PAGES_PAGE || page == SMS_CACHE_PAGE) { 233 ptr[0] = SMS_CACHE_PAGE; 234 ptr[1] = 18; 235 ptr[2] = 1 << 2; 236 ptr += 20; 237 } 238 239 if (page == SMS_ALL_PAGES_PAGE || page == SMS_CONTROL_MODE_PAGE) { 240 ptr[0] = SMS_CONTROL_MODE_PAGE; 241 ptr[1] = 10; 242 if (pgctl != SMS_PAGE_CTRL_CHANGEABLE) { 243 ptr[3] = 1 << 4; /* unrestricted reordering allowed */ 244 ptr[8] = 0x75; /* 30000 ms */ 245 ptr[9] = 0x30; 246 } 247 ptr += 12; 248 } 249 nbyte = (char *)ptr - &junk[0]; 250 ptr[0] = nbyte - 4; 251 252 if (cdb[0] == MODE_SENSE) { 253 data_len = min(cdb[4], csio->dxfer_len); 254 } else { 255 uint16_t tw = (cdb[7] << 8) | cdb[8]; 256 data_len = min(tw, csio->dxfer_len); 257 } 258 data_len = min(data_len, nbyte); 259 if (data_len) { 260 memcpy(csio->data_ptr, junk, data_len); 261 } 262 csio->resid = csio->dxfer_len - data_len; 263 break; 264 } 265 case READ_6: 266 case READ_10: 267 case READ_12: 268 case READ_16: 269 case WRITE_6: 270 case WRITE_10: 271 case WRITE_12: 272 case WRITE_16: 273 if (vhba_rwparm(cdb, &off, &data_len, DISK_NBLKS, DISK_SHIFT)) { 274 vhba_fill_sense(csio, SSD_KEY_ILLEGAL_REQUEST, 0x24, 0x0); 275 break; 276 } 277 if (data_len) { 278 if ((cdb[0] & 0xf) == 8) { 279 memcpy(csio->data_ptr, &vhbas->disk[off], data_len); 280 } else { 281 memcpy(&vhbas->disk[off], csio->data_ptr, data_len); 282 } 283 csio->resid = csio->dxfer_len - data_len; 284 } else { 285 csio->resid = csio->dxfer_len; 286 } 287 break; 288 break; 289 290 case READ_CAPACITY: 291 if (cdb[2] || cdb[3] || cdb[4] || cdb[5]) { 292 vhba_fill_sense(csio, SSD_KEY_UNIT_ATTENTION, 0x24, 0x0); 293 break; 294 } 295 if (cdb[8] & 0x1) { /* PMI */ 296 csio->data_ptr[0] = 0xff; 297 csio->data_ptr[1] = 0xff; 298 csio->data_ptr[2] = 0xff; 299 csio->data_ptr[3] = 0xff; 300 } else { 301 uint64_t last_blk = DISK_NBLKS - 1; 302 if (last_blk < 0xffffffffULL) { 303 csio->data_ptr[0] = (last_blk >> 24) & 0xff; 304 csio->data_ptr[1] = (last_blk >> 16) & 0xff; 305 csio->data_ptr[2] = (last_blk >> 8) & 0xff; 306 csio->data_ptr[3] = (last_blk) & 0xff; 307 } else { 308 csio->data_ptr[0] = 0xff; 309 csio->data_ptr[1] = 0xff; 310 csio->data_ptr[2] = 0xff; 311 csio->data_ptr[3] = 0xff; 312 } 313 } 314 csio->data_ptr[4] = ((1 << DISK_SHIFT) >> 24) & 0xff; 315 csio->data_ptr[5] = ((1 << DISK_SHIFT) >> 16) & 0xff; 316 csio->data_ptr[6] = ((1 << DISK_SHIFT) >> 8) & 0xff; 317 csio->data_ptr[7] = ((1 << DISK_SHIFT)) & 0xff; 318 break; 319 320 default: 321 vhba_default_cmd(csio, MAX_LUN, NULL); 322 break; 323 } 324 csio->ccb_h.status &= ~CAM_STATUS_MASK; 325 if (csio->scsi_status != SCSI_STATUS_OK) { 326 csio->ccb_h.status |= CAM_SCSI_STATUS_ERROR; 327 if (csio->scsi_status == SCSI_STATUS_CHECK_COND) { 328 csio->ccb_h.status |= CAM_AUTOSNS_VALID; 329 } 330 } else { 331 csio->scsi_status = SCSI_STATUS_OK; 332 csio->ccb_h.status |= CAM_REQ_CMP; 333 } 334 TAILQ_INSERT_TAIL(&vhbas->vhba->done, &csio->ccb_h, sim_links.tqe); 335 } 336 DEV_MODULE(vhba_simple, vhba_modprobe, NULL); 337