ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Generic SCSI Device support |
| 3 | * |
| 4 | * Copyright (c) 2007 Bull S.A.S. |
| 5 | * Based on code by Paul Brook |
| 6 | * Based on code by Fabrice Bellard |
| 7 | * |
| 8 | * Written by Laurent Vivier <Laurent.Vivier@bull.net> |
| 9 | * |
Matthew Fernandez | 8e31bf3 | 2011-06-26 12:21:35 +1000 | [diff] [blame] | 10 | * This code is licensed under the LGPL. |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 11 | * |
| 12 | */ |
| 13 | |
| 14 | #include "qemu-common.h" |
Markus Armbruster | 2f79201 | 2010-02-18 16:24:31 +0100 | [diff] [blame] | 15 | #include "qemu-error.h" |
Gerd Hoffmann | 43b443b | 2009-10-30 09:54:00 +0100 | [diff] [blame] | 16 | #include "scsi.h" |
Blue Swirl | 2446333 | 2010-08-24 15:22:24 +0000 | [diff] [blame] | 17 | #include "blockdev.h" |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 18 | |
Gerd Hoffmann | d52affa | 2009-08-31 14:24:04 +0200 | [diff] [blame] | 19 | #ifdef __linux__ |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 20 | |
| 21 | //#define DEBUG_SCSI |
| 22 | |
| 23 | #ifdef DEBUG_SCSI |
Blue Swirl | 001faf3 | 2009-05-13 17:53:17 +0000 | [diff] [blame] | 24 | #define DPRINTF(fmt, ...) \ |
| 25 | do { printf("scsi-generic: " fmt , ## __VA_ARGS__); } while (0) |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 26 | #else |
Blue Swirl | 001faf3 | 2009-05-13 17:53:17 +0000 | [diff] [blame] | 27 | #define DPRINTF(fmt, ...) do {} while(0) |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 28 | #endif |
| 29 | |
Blue Swirl | 001faf3 | 2009-05-13 17:53:17 +0000 | [diff] [blame] | 30 | #define BADF(fmt, ...) \ |
| 31 | do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0) |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 32 | |
| 33 | #include <stdio.h> |
| 34 | #include <sys/types.h> |
| 35 | #include <sys/stat.h> |
| 36 | #include <unistd.h> |
| 37 | #include <scsi/sg.h> |
Gerd Hoffmann | 0d65e1f | 2009-11-26 15:33:53 +0100 | [diff] [blame] | 38 | #include "scsi-defs.h" |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 39 | |
aliguori | a9dd684 | 2008-09-23 13:38:44 +0000 | [diff] [blame] | 40 | #define SCSI_SENSE_BUF_SIZE 96 |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 41 | |
Paolo Bonzini | a3b16e7 | 2011-10-12 14:53:43 +0200 | [diff] [blame] | 42 | #define SG_ERR_DRIVER_TIMEOUT 0x06 |
| 43 | #define SG_ERR_DRIVER_SENSE 0x08 |
| 44 | |
| 45 | #define SG_ERR_DID_OK 0x00 |
| 46 | #define SG_ERR_DID_NO_CONNECT 0x01 |
| 47 | #define SG_ERR_DID_BUS_BUSY 0x02 |
| 48 | #define SG_ERR_DID_TIME_OUT 0x03 |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 49 | |
| 50 | #ifndef MAX_UINT |
| 51 | #define MAX_UINT ((unsigned int)-1) |
| 52 | #endif |
| 53 | |
Gerd Hoffmann | 4c41d2e | 2009-11-26 15:33:48 +0100 | [diff] [blame] | 54 | typedef struct SCSIGenericReq { |
| 55 | SCSIRequest req; |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 56 | uint8_t *buf; |
| 57 | int buflen; |
| 58 | int len; |
| 59 | sg_io_hdr_t io_header; |
Gerd Hoffmann | 4c41d2e | 2009-11-26 15:33:48 +0100 | [diff] [blame] | 60 | } SCSIGenericReq; |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 61 | |
Paolo Bonzini | 56b1fc4 | 2011-12-15 07:24:30 -0500 | [diff] [blame] | 62 | static void scsi_generic_save_request(QEMUFile *f, SCSIRequest *req) |
| 63 | { |
| 64 | SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); |
| 65 | |
| 66 | qemu_put_sbe32s(f, &r->buflen); |
| 67 | if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) { |
| 68 | assert(!r->req.sg); |
| 69 | qemu_put_buffer(f, r->buf, r->req.cmd.xfer); |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | static void scsi_generic_load_request(QEMUFile *f, SCSIRequest *req) |
| 74 | { |
| 75 | SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); |
| 76 | |
| 77 | qemu_get_sbe32s(f, &r->buflen); |
| 78 | if (r->buflen && r->req.cmd.mode == SCSI_XFER_TO_DEV) { |
| 79 | assert(!r->req.sg); |
| 80 | qemu_get_buffer(f, r->buf, r->req.cmd.xfer); |
| 81 | } |
| 82 | } |
| 83 | |
Paolo Bonzini | ad2d30f | 2011-04-18 16:01:56 +0200 | [diff] [blame] | 84 | static void scsi_free_request(SCSIRequest *req) |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 85 | { |
Paolo Bonzini | ad2d30f | 2011-04-18 16:01:56 +0200 | [diff] [blame] | 86 | SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); |
| 87 | |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 88 | g_free(r->buf); |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 89 | } |
| 90 | |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 91 | /* Helper function for command completion. */ |
| 92 | static void scsi_command_complete(void *opaque, int ret) |
| 93 | { |
Paolo Bonzini | 682a9b2 | 2011-08-03 10:49:06 +0200 | [diff] [blame] | 94 | int status; |
Gerd Hoffmann | 4c41d2e | 2009-11-26 15:33:48 +0100 | [diff] [blame] | 95 | SCSIGenericReq *r = (SCSIGenericReq *)opaque; |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 96 | |
Paolo Bonzini | d33e0ce | 2011-05-25 16:53:46 +0200 | [diff] [blame] | 97 | r->req.aiocb = NULL; |
Paolo Bonzini | a3b16e7 | 2011-10-12 14:53:43 +0200 | [diff] [blame] | 98 | if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) { |
Paolo Bonzini | b45ef67 | 2011-08-03 10:49:07 +0200 | [diff] [blame] | 99 | r->req.sense_len = r->io_header.sb_len_wr; |
Paolo Bonzini | a3b16e7 | 2011-10-12 14:53:43 +0200 | [diff] [blame] | 100 | } |
aurel32 | 89c0f64 | 2008-10-17 08:08:56 +0000 | [diff] [blame] | 101 | |
Hannes Reinecke | a1f0cce | 2011-04-18 12:53:14 +0200 | [diff] [blame] | 102 | if (ret != 0) { |
| 103 | switch (ret) { |
Paolo Bonzini | 2e7cc4d | 2011-04-18 14:29:16 +0200 | [diff] [blame] | 104 | case -EDOM: |
Paolo Bonzini | 682a9b2 | 2011-08-03 10:49:06 +0200 | [diff] [blame] | 105 | status = TASK_SET_FULL; |
Paolo Bonzini | 2e7cc4d | 2011-04-18 14:29:16 +0200 | [diff] [blame] | 106 | break; |
Hannes Reinecke | a1f0cce | 2011-04-18 12:53:14 +0200 | [diff] [blame] | 107 | case -ENOMEM: |
Paolo Bonzini | 682a9b2 | 2011-08-03 10:49:06 +0200 | [diff] [blame] | 108 | status = CHECK_CONDITION; |
Paolo Bonzini | b45ef67 | 2011-08-03 10:49:07 +0200 | [diff] [blame] | 109 | scsi_req_build_sense(&r->req, SENSE_CODE(TARGET_FAILURE)); |
Hannes Reinecke | a1f0cce | 2011-04-18 12:53:14 +0200 | [diff] [blame] | 110 | break; |
| 111 | default: |
Paolo Bonzini | 682a9b2 | 2011-08-03 10:49:06 +0200 | [diff] [blame] | 112 | status = CHECK_CONDITION; |
Paolo Bonzini | b45ef67 | 2011-08-03 10:49:07 +0200 | [diff] [blame] | 113 | scsi_req_build_sense(&r->req, SENSE_CODE(IO_ERROR)); |
Hannes Reinecke | a1f0cce | 2011-04-18 12:53:14 +0200 | [diff] [blame] | 114 | break; |
| 115 | } |
| 116 | } else { |
Paolo Bonzini | a3b16e7 | 2011-10-12 14:53:43 +0200 | [diff] [blame] | 117 | if (r->io_header.host_status == SG_ERR_DID_NO_CONNECT || |
| 118 | r->io_header.host_status == SG_ERR_DID_BUS_BUSY || |
| 119 | r->io_header.host_status == SG_ERR_DID_TIME_OUT || |
| 120 | (r->io_header.driver_status & SG_ERR_DRIVER_TIMEOUT)) { |
Paolo Bonzini | 682a9b2 | 2011-08-03 10:49:06 +0200 | [diff] [blame] | 121 | status = BUSY; |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 122 | BADF("Driver Timeout\n"); |
Paolo Bonzini | a3b16e7 | 2011-10-12 14:53:43 +0200 | [diff] [blame] | 123 | } else if (r->io_header.host_status) { |
| 124 | status = CHECK_CONDITION; |
| 125 | scsi_req_build_sense(&r->req, SENSE_CODE(I_T_NEXUS_LOSS)); |
Paolo Bonzini | 682a9b2 | 2011-08-03 10:49:06 +0200 | [diff] [blame] | 126 | } else if (r->io_header.status) { |
| 127 | status = r->io_header.status; |
Paolo Bonzini | b45ef67 | 2011-08-03 10:49:07 +0200 | [diff] [blame] | 128 | } else if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) { |
Paolo Bonzini | 682a9b2 | 2011-08-03 10:49:06 +0200 | [diff] [blame] | 129 | status = CHECK_CONDITION; |
| 130 | } else { |
| 131 | status = GOOD; |
| 132 | } |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 133 | } |
aurel32 | 89c0f64 | 2008-10-17 08:08:56 +0000 | [diff] [blame] | 134 | DPRINTF("Command complete 0x%p tag=0x%x status=%d\n", |
Paolo Bonzini | 682a9b2 | 2011-08-03 10:49:06 +0200 | [diff] [blame] | 135 | r, r->req.tag, status); |
Gerd Hoffmann | ed3a34a | 2009-11-26 15:34:00 +0100 | [diff] [blame] | 136 | |
Paolo Bonzini | 682a9b2 | 2011-08-03 10:49:06 +0200 | [diff] [blame] | 137 | scsi_req_complete(&r->req, status); |
Paolo Bonzini | c9501c9 | 2011-10-25 12:53:35 +0200 | [diff] [blame] | 138 | if (!r->req.io_canceled) { |
| 139 | scsi_req_unref(&r->req); |
| 140 | } |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 141 | } |
| 142 | |
| 143 | /* Cancel a pending data transfer. */ |
Hannes Reinecke | 5c6c0e5 | 2011-04-18 12:35:39 +0200 | [diff] [blame] | 144 | static void scsi_cancel_io(SCSIRequest *req) |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 145 | { |
Hannes Reinecke | 5c6c0e5 | 2011-04-18 12:35:39 +0200 | [diff] [blame] | 146 | SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); |
| 147 | |
| 148 | DPRINTF("Cancel tag=0x%x\n", req->tag); |
| 149 | if (r->req.aiocb) { |
| 150 | bdrv_aio_cancel(r->req.aiocb); |
Paolo Bonzini | c9501c9 | 2011-10-25 12:53:35 +0200 | [diff] [blame] | 151 | |
| 152 | /* This reference was left in by scsi_*_data. We take ownership of |
| 153 | * it independent of whether bdrv_aio_cancel completes the request |
| 154 | * or not. */ |
| 155 | scsi_req_unref(&r->req); |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 156 | } |
Hannes Reinecke | 5c6c0e5 | 2011-04-18 12:35:39 +0200 | [diff] [blame] | 157 | r->req.aiocb = NULL; |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 158 | } |
| 159 | |
| 160 | static int execute_command(BlockDriverState *bdrv, |
Gerd Hoffmann | 4c41d2e | 2009-11-26 15:33:48 +0100 | [diff] [blame] | 161 | SCSIGenericReq *r, int direction, |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 162 | BlockDriverCompletionFunc *complete) |
| 163 | { |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 164 | r->io_header.interface_id = 'S'; |
| 165 | r->io_header.dxfer_direction = direction; |
| 166 | r->io_header.dxferp = r->buf; |
| 167 | r->io_header.dxfer_len = r->buflen; |
Gerd Hoffmann | 29362eb | 2009-11-26 15:33:51 +0100 | [diff] [blame] | 168 | r->io_header.cmdp = r->req.cmd.buf; |
| 169 | r->io_header.cmd_len = r->req.cmd.len; |
Paolo Bonzini | b45ef67 | 2011-08-03 10:49:07 +0200 | [diff] [blame] | 170 | r->io_header.mx_sb_len = sizeof(r->req.sense); |
| 171 | r->io_header.sbp = r->req.sense; |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 172 | r->io_header.timeout = MAX_UINT; |
| 173 | r->io_header.usr_ptr = r; |
| 174 | r->io_header.flags |= SG_FLAG_DIRECT_IO; |
| 175 | |
Gerd Hoffmann | 4c41d2e | 2009-11-26 15:33:48 +0100 | [diff] [blame] | 176 | r->req.aiocb = bdrv_aio_ioctl(bdrv, SG_IO, &r->io_header, complete, r); |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 177 | |
| 178 | return 0; |
| 179 | } |
| 180 | |
| 181 | static void scsi_read_complete(void * opaque, int ret) |
| 182 | { |
Gerd Hoffmann | 4c41d2e | 2009-11-26 15:33:48 +0100 | [diff] [blame] | 183 | SCSIGenericReq *r = (SCSIGenericReq *)opaque; |
Paolo Bonzini | 9b6eef8 | 2011-10-13 10:36:27 +0200 | [diff] [blame] | 184 | SCSIDevice *s = r->req.dev; |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 185 | int len; |
| 186 | |
Paolo Bonzini | d33e0ce | 2011-05-25 16:53:46 +0200 | [diff] [blame] | 187 | r->req.aiocb = NULL; |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 188 | if (ret) { |
Bernhard Kohl | aa2b1e8 | 2010-09-01 16:33:21 +0200 | [diff] [blame] | 189 | DPRINTF("IO error ret %d\n", ret); |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 190 | scsi_command_complete(r, ret); |
| 191 | return; |
| 192 | } |
| 193 | len = r->io_header.dxfer_len - r->io_header.resid; |
Gerd Hoffmann | 4c41d2e | 2009-11-26 15:33:48 +0100 | [diff] [blame] | 194 | DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len); |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 195 | |
| 196 | r->len = -1; |
Paolo Bonzini | 40f16dd | 2011-05-03 14:15:59 +0200 | [diff] [blame] | 197 | if (len == 0) { |
aurel32 | 89c0f64 | 2008-10-17 08:08:56 +0000 | [diff] [blame] | 198 | scsi_command_complete(r, 0); |
Paolo Bonzini | 40f16dd | 2011-05-03 14:15:59 +0200 | [diff] [blame] | 199 | } else { |
Paolo Bonzini | 9b6eef8 | 2011-10-13 10:36:27 +0200 | [diff] [blame] | 200 | /* Snoop READ CAPACITY output to set the blocksize. */ |
| 201 | if (r->req.cmd.buf[0] == READ_CAPACITY_10) { |
| 202 | s->blocksize = ldl_be_p(&r->buf[4]); |
Paolo Bonzini | 7877903 | 2011-10-13 10:39:50 +0200 | [diff] [blame] | 203 | s->max_lba = ldl_be_p(&r->buf[0]); |
Paolo Bonzini | 9b6eef8 | 2011-10-13 10:36:27 +0200 | [diff] [blame] | 204 | } else if (r->req.cmd.buf[0] == SERVICE_ACTION_IN_16 && |
| 205 | (r->req.cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) { |
| 206 | s->blocksize = ldl_be_p(&r->buf[8]); |
Paolo Bonzini | 7877903 | 2011-10-13 10:39:50 +0200 | [diff] [blame] | 207 | s->max_lba = ldq_be_p(&r->buf[0]); |
Paolo Bonzini | 9b6eef8 | 2011-10-13 10:36:27 +0200 | [diff] [blame] | 208 | } |
| 209 | bdrv_set_buffer_alignment(s->conf.bs, s->blocksize); |
| 210 | |
Paolo Bonzini | ab9adc8 | 2011-04-18 14:59:13 +0200 | [diff] [blame] | 211 | scsi_req_data(&r->req, len); |
Paolo Bonzini | c9501c9 | 2011-10-25 12:53:35 +0200 | [diff] [blame] | 212 | if (!r->req.io_canceled) { |
| 213 | scsi_req_unref(&r->req); |
| 214 | } |
Paolo Bonzini | 40f16dd | 2011-05-03 14:15:59 +0200 | [diff] [blame] | 215 | } |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 216 | } |
| 217 | |
| 218 | /* Read more data from scsi device into buffer. */ |
Hannes Reinecke | 5c6c0e5 | 2011-04-18 12:35:39 +0200 | [diff] [blame] | 219 | static void scsi_read_data(SCSIRequest *req) |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 220 | { |
Hannes Reinecke | 5c6c0e5 | 2011-04-18 12:35:39 +0200 | [diff] [blame] | 221 | SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); |
Paolo Bonzini | 8869e10 | 2011-10-12 12:49:35 +0200 | [diff] [blame] | 222 | SCSIDevice *s = r->req.dev; |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 223 | int ret; |
| 224 | |
Hannes Reinecke | 5c6c0e5 | 2011-04-18 12:35:39 +0200 | [diff] [blame] | 225 | DPRINTF("scsi_read_data 0x%x\n", req->tag); |
Paolo Bonzini | c9501c9 | 2011-10-25 12:53:35 +0200 | [diff] [blame] | 226 | |
| 227 | /* The request is used as the AIO opaque value, so add a ref. */ |
| 228 | scsi_req_ref(&r->req); |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 229 | if (r->len == -1) { |
| 230 | scsi_command_complete(r, 0); |
| 231 | return; |
| 232 | } |
| 233 | |
Paolo Bonzini | 8869e10 | 2011-10-12 12:49:35 +0200 | [diff] [blame] | 234 | ret = execute_command(s->conf.bs, r, SG_DXFER_FROM_DEV, scsi_read_complete); |
Hannes Reinecke | a1f0cce | 2011-04-18 12:53:14 +0200 | [diff] [blame] | 235 | if (ret < 0) { |
| 236 | scsi_command_complete(r, ret); |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 237 | } |
| 238 | } |
| 239 | |
| 240 | static void scsi_write_complete(void * opaque, int ret) |
| 241 | { |
Gerd Hoffmann | 4c41d2e | 2009-11-26 15:33:48 +0100 | [diff] [blame] | 242 | SCSIGenericReq *r = (SCSIGenericReq *)opaque; |
Paolo Bonzini | 8869e10 | 2011-10-12 12:49:35 +0200 | [diff] [blame] | 243 | SCSIDevice *s = r->req.dev; |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 244 | |
| 245 | DPRINTF("scsi_write_complete() ret = %d\n", ret); |
Paolo Bonzini | d33e0ce | 2011-05-25 16:53:46 +0200 | [diff] [blame] | 246 | r->req.aiocb = NULL; |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 247 | if (ret) { |
| 248 | DPRINTF("IO error\n"); |
| 249 | scsi_command_complete(r, ret); |
| 250 | return; |
| 251 | } |
| 252 | |
Gerd Hoffmann | 29362eb | 2009-11-26 15:33:51 +0100 | [diff] [blame] | 253 | if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 && |
Paolo Bonzini | 8869e10 | 2011-10-12 12:49:35 +0200 | [diff] [blame] | 254 | s->type == TYPE_TAPE) { |
| 255 | s->blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11]; |
| 256 | DPRINTF("block size %d\n", s->blocksize); |
aurel32 | 89c0f64 | 2008-10-17 08:08:56 +0000 | [diff] [blame] | 257 | } |
| 258 | |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 259 | scsi_command_complete(r, ret); |
| 260 | } |
| 261 | |
| 262 | /* Write data to a scsi device. Returns nonzero on failure. |
| 263 | The transfer may complete asynchronously. */ |
Paolo Bonzini | 4274121 | 2011-04-22 09:39:16 +0200 | [diff] [blame] | 264 | static void scsi_write_data(SCSIRequest *req) |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 265 | { |
Hannes Reinecke | 5c6c0e5 | 2011-04-18 12:35:39 +0200 | [diff] [blame] | 266 | SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); |
Paolo Bonzini | 8869e10 | 2011-10-12 12:49:35 +0200 | [diff] [blame] | 267 | SCSIDevice *s = r->req.dev; |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 268 | int ret; |
| 269 | |
Hannes Reinecke | 5c6c0e5 | 2011-04-18 12:35:39 +0200 | [diff] [blame] | 270 | DPRINTF("scsi_write_data 0x%x\n", req->tag); |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 271 | if (r->len == 0) { |
| 272 | r->len = r->buflen; |
Paolo Bonzini | ab9adc8 | 2011-04-18 14:59:13 +0200 | [diff] [blame] | 273 | scsi_req_data(&r->req, r->len); |
Paolo Bonzini | 4274121 | 2011-04-22 09:39:16 +0200 | [diff] [blame] | 274 | return; |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 275 | } |
| 276 | |
Paolo Bonzini | c9501c9 | 2011-10-25 12:53:35 +0200 | [diff] [blame] | 277 | /* The request is used as the AIO opaque value, so add a ref. */ |
| 278 | scsi_req_ref(&r->req); |
Paolo Bonzini | 8869e10 | 2011-10-12 12:49:35 +0200 | [diff] [blame] | 279 | ret = execute_command(s->conf.bs, r, SG_DXFER_TO_DEV, scsi_write_complete); |
Hannes Reinecke | a1f0cce | 2011-04-18 12:53:14 +0200 | [diff] [blame] | 280 | if (ret < 0) { |
| 281 | scsi_command_complete(r, ret); |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 282 | } |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 283 | } |
| 284 | |
| 285 | /* Return a pointer to the data buffer. */ |
Hannes Reinecke | 5c6c0e5 | 2011-04-18 12:35:39 +0200 | [diff] [blame] | 286 | static uint8_t *scsi_get_buf(SCSIRequest *req) |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 287 | { |
Hannes Reinecke | 5c6c0e5 | 2011-04-18 12:35:39 +0200 | [diff] [blame] | 288 | SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); |
| 289 | |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 290 | return r->buf; |
| 291 | } |
| 292 | |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 293 | /* Execute a scsi command. Returns the length of the data expected by the |
| 294 | command. This will be Positive for data transfers from the device |
| 295 | (eg. disk reads), negative for transfers to the device (eg. disk writes), |
| 296 | and zero if the command does not transfer any data. */ |
| 297 | |
Hannes Reinecke | 5c6c0e5 | 2011-04-18 12:35:39 +0200 | [diff] [blame] | 298 | static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd) |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 299 | { |
Hannes Reinecke | 5c6c0e5 | 2011-04-18 12:35:39 +0200 | [diff] [blame] | 300 | SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req); |
Paolo Bonzini | 8869e10 | 2011-10-12 12:49:35 +0200 | [diff] [blame] | 301 | SCSIDevice *s = r->req.dev; |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 302 | int ret; |
| 303 | |
Bernhard Kohl | aa2b1e8 | 2010-09-01 16:33:21 +0200 | [diff] [blame] | 304 | DPRINTF("Command: lun=%d tag=0x%x len %zd data=0x%02x", lun, tag, |
| 305 | r->req.cmd.xfer, cmd[0]); |
| 306 | |
| 307 | #ifdef DEBUG_SCSI |
| 308 | { |
| 309 | int i; |
| 310 | for (i = 1; i < r->req.cmd.len; i++) { |
| 311 | printf(" 0x%02x", cmd[i]); |
| 312 | } |
| 313 | printf("\n"); |
| 314 | } |
| 315 | #endif |
Gerd Hoffmann | 2ec749c | 2009-11-26 15:33:55 +0100 | [diff] [blame] | 316 | |
| 317 | if (r->req.cmd.xfer == 0) { |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 318 | if (r->buf != NULL) |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 319 | g_free(r->buf); |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 320 | r->buflen = 0; |
| 321 | r->buf = NULL; |
Paolo Bonzini | c9501c9 | 2011-10-25 12:53:35 +0200 | [diff] [blame] | 322 | /* The request is used as the AIO opaque value, so add a ref. */ |
| 323 | scsi_req_ref(&r->req); |
Paolo Bonzini | 8869e10 | 2011-10-12 12:49:35 +0200 | [diff] [blame] | 324 | ret = execute_command(s->conf.bs, r, SG_DXFER_NONE, scsi_command_complete); |
Hannes Reinecke | a1f0cce | 2011-04-18 12:53:14 +0200 | [diff] [blame] | 325 | if (ret < 0) { |
| 326 | scsi_command_complete(r, ret); |
| 327 | return 0; |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 328 | } |
| 329 | return 0; |
| 330 | } |
| 331 | |
Gerd Hoffmann | 2ec749c | 2009-11-26 15:33:55 +0100 | [diff] [blame] | 332 | if (r->buflen != r->req.cmd.xfer) { |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 333 | if (r->buf != NULL) |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 334 | g_free(r->buf); |
| 335 | r->buf = g_malloc(r->req.cmd.xfer); |
Gerd Hoffmann | 2ec749c | 2009-11-26 15:33:55 +0100 | [diff] [blame] | 336 | r->buflen = r->req.cmd.xfer; |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 337 | } |
| 338 | |
| 339 | memset(r->buf, 0, r->buflen); |
Gerd Hoffmann | 2ec749c | 2009-11-26 15:33:55 +0100 | [diff] [blame] | 340 | r->len = r->req.cmd.xfer; |
Gerd Hoffmann | 97a0643 | 2009-11-26 15:33:57 +0100 | [diff] [blame] | 341 | if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 342 | r->len = 0; |
Hannes Reinecke | 5c6c0e5 | 2011-04-18 12:35:39 +0200 | [diff] [blame] | 343 | return -r->req.cmd.xfer; |
Paolo Bonzini | ad2d30f | 2011-04-18 16:01:56 +0200 | [diff] [blame] | 344 | } else { |
Hannes Reinecke | 5c6c0e5 | 2011-04-18 12:35:39 +0200 | [diff] [blame] | 345 | return r->req.cmd.xfer; |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 346 | } |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 347 | } |
| 348 | |
aurel32 | 89c0f64 | 2008-10-17 08:08:56 +0000 | [diff] [blame] | 349 | static int get_stream_blocksize(BlockDriverState *bdrv) |
| 350 | { |
| 351 | uint8_t cmd[6]; |
| 352 | uint8_t buf[12]; |
| 353 | uint8_t sensebuf[8]; |
| 354 | sg_io_hdr_t io_header; |
| 355 | int ret; |
| 356 | |
| 357 | memset(cmd, 0, sizeof(cmd)); |
| 358 | memset(buf, 0, sizeof(buf)); |
| 359 | cmd[0] = MODE_SENSE; |
| 360 | cmd[4] = sizeof(buf); |
| 361 | |
| 362 | memset(&io_header, 0, sizeof(io_header)); |
| 363 | io_header.interface_id = 'S'; |
| 364 | io_header.dxfer_direction = SG_DXFER_FROM_DEV; |
| 365 | io_header.dxfer_len = sizeof(buf); |
| 366 | io_header.dxferp = buf; |
| 367 | io_header.cmdp = cmd; |
| 368 | io_header.cmd_len = sizeof(cmd); |
| 369 | io_header.mx_sb_len = sizeof(sensebuf); |
| 370 | io_header.sbp = sensebuf; |
| 371 | io_header.timeout = 6000; /* XXX */ |
| 372 | |
aliguori | 221f715 | 2009-03-28 17:28:41 +0000 | [diff] [blame] | 373 | ret = bdrv_ioctl(bdrv, SG_IO, &io_header); |
Paolo Bonzini | fe0ed71 | 2011-10-12 14:49:48 +0200 | [diff] [blame] | 374 | if (ret < 0 || io_header.driver_status || io_header.host_status) { |
aurel32 | 89c0f64 | 2008-10-17 08:08:56 +0000 | [diff] [blame] | 375 | return -1; |
Paolo Bonzini | fe0ed71 | 2011-10-12 14:49:48 +0200 | [diff] [blame] | 376 | } |
aurel32 | 89c0f64 | 2008-10-17 08:08:56 +0000 | [diff] [blame] | 377 | return (buf[9] << 16) | (buf[10] << 8) | buf[11]; |
| 378 | } |
| 379 | |
Bernhard Kohl | f8b6d67 | 2010-09-06 16:07:33 +0200 | [diff] [blame] | 380 | static void scsi_generic_reset(DeviceState *dev) |
| 381 | { |
Anthony Liguori | b9eea3e | 2011-12-15 14:50:08 -0600 | [diff] [blame] | 382 | SCSIDevice *s = SCSI_DEVICE(dev); |
Bernhard Kohl | f8b6d67 | 2010-09-06 16:07:33 +0200 | [diff] [blame] | 383 | |
Paolo Bonzini | 8869e10 | 2011-10-12 12:49:35 +0200 | [diff] [blame] | 384 | scsi_device_purge_requests(s, SENSE_CODE(RESET)); |
Bernhard Kohl | f8b6d67 | 2010-09-06 16:07:33 +0200 | [diff] [blame] | 385 | } |
| 386 | |
Paolo Bonzini | 8869e10 | 2011-10-12 12:49:35 +0200 | [diff] [blame] | 387 | static void scsi_destroy(SCSIDevice *s) |
Bernhard Kohl | f8b6d67 | 2010-09-06 16:07:33 +0200 | [diff] [blame] | 388 | { |
Paolo Bonzini | 8869e10 | 2011-10-12 12:49:35 +0200 | [diff] [blame] | 389 | scsi_device_purge_requests(s, SENSE_CODE(NO_SENSE)); |
| 390 | blockdev_mark_auto_del(s->conf.bs); |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 391 | } |
| 392 | |
Paolo Bonzini | 8869e10 | 2011-10-12 12:49:35 +0200 | [diff] [blame] | 393 | static int scsi_generic_initfn(SCSIDevice *s) |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 394 | { |
| 395 | int sg_version; |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 396 | struct sg_scsi_id scsiid; |
| 397 | |
Paolo Bonzini | 8869e10 | 2011-10-12 12:49:35 +0200 | [diff] [blame] | 398 | if (!s->conf.bs) { |
Markus Armbruster | 6a84cb1 | 2011-12-21 11:37:57 +0100 | [diff] [blame] | 399 | error_report("drive property not set"); |
Gerd Hoffmann | d52affa | 2009-08-31 14:24:04 +0200 | [diff] [blame] | 400 | return -1; |
| 401 | } |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 402 | |
Gerd Hoffmann | d52affa | 2009-08-31 14:24:04 +0200 | [diff] [blame] | 403 | /* check we are really using a /dev/sg* file */ |
Paolo Bonzini | 8869e10 | 2011-10-12 12:49:35 +0200 | [diff] [blame] | 404 | if (!bdrv_is_sg(s->conf.bs)) { |
Markus Armbruster | 6a84cb1 | 2011-12-21 11:37:57 +0100 | [diff] [blame] | 405 | error_report("not /dev/sg*"); |
Gerd Hoffmann | d52affa | 2009-08-31 14:24:04 +0200 | [diff] [blame] | 406 | return -1; |
| 407 | } |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 408 | |
Paolo Bonzini | 8869e10 | 2011-10-12 12:49:35 +0200 | [diff] [blame] | 409 | if (bdrv_get_on_error(s->conf.bs, 0) != BLOCK_ERR_STOP_ENOSPC) { |
Markus Armbruster | 620f862 | 2010-05-27 20:02:28 +0200 | [diff] [blame] | 410 | error_report("Device doesn't support drive option werror"); |
| 411 | return -1; |
| 412 | } |
Paolo Bonzini | 8869e10 | 2011-10-12 12:49:35 +0200 | [diff] [blame] | 413 | if (bdrv_get_on_error(s->conf.bs, 1) != BLOCK_ERR_REPORT) { |
Markus Armbruster | 620f862 | 2010-05-27 20:02:28 +0200 | [diff] [blame] | 414 | error_report("Device doesn't support drive option rerror"); |
| 415 | return -1; |
| 416 | } |
| 417 | |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 418 | /* check we are using a driver managing SG_IO (version 3 and after */ |
Paolo Bonzini | 8869e10 | 2011-10-12 12:49:35 +0200 | [diff] [blame] | 419 | if (bdrv_ioctl(s->conf.bs, SG_GET_VERSION_NUM, &sg_version) < 0 || |
Gerd Hoffmann | d52affa | 2009-08-31 14:24:04 +0200 | [diff] [blame] | 420 | sg_version < 30000) { |
Markus Armbruster | 6a84cb1 | 2011-12-21 11:37:57 +0100 | [diff] [blame] | 421 | error_report("scsi generic interface too old"); |
Gerd Hoffmann | d52affa | 2009-08-31 14:24:04 +0200 | [diff] [blame] | 422 | return -1; |
| 423 | } |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 424 | |
| 425 | /* get LUN of the /dev/sg? */ |
Paolo Bonzini | 8869e10 | 2011-10-12 12:49:35 +0200 | [diff] [blame] | 426 | if (bdrv_ioctl(s->conf.bs, SG_GET_SCSI_ID, &scsiid)) { |
Markus Armbruster | 6a84cb1 | 2011-12-21 11:37:57 +0100 | [diff] [blame] | 427 | error_report("SG_GET_SCSI_ID ioctl failed"); |
Gerd Hoffmann | d52affa | 2009-08-31 14:24:04 +0200 | [diff] [blame] | 428 | return -1; |
| 429 | } |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 430 | |
| 431 | /* define device state */ |
Paolo Bonzini | 8869e10 | 2011-10-12 12:49:35 +0200 | [diff] [blame] | 432 | s->type = scsiid.scsi_type; |
| 433 | DPRINTF("device type %d\n", s->type); |
Paolo Bonzini | 28b7765 | 2011-11-18 16:32:02 +0100 | [diff] [blame] | 434 | if (s->type == TYPE_DISK || s->type == TYPE_ROM) { |
| 435 | add_boot_device_path(s->conf.bootindex, &s->qdev, NULL); |
| 436 | } |
| 437 | |
Paolo Bonzini | 9b6eef8 | 2011-10-13 10:36:27 +0200 | [diff] [blame] | 438 | switch (s->type) { |
| 439 | case TYPE_TAPE: |
Paolo Bonzini | 8869e10 | 2011-10-12 12:49:35 +0200 | [diff] [blame] | 440 | s->blocksize = get_stream_blocksize(s->conf.bs); |
| 441 | if (s->blocksize == -1) { |
| 442 | s->blocksize = 0; |
| 443 | } |
Paolo Bonzini | 9b6eef8 | 2011-10-13 10:36:27 +0200 | [diff] [blame] | 444 | break; |
| 445 | |
| 446 | /* Make a guess for block devices, we'll fix it when the guest sends. |
| 447 | * READ CAPACITY. If they don't, they likely would assume these sizes |
| 448 | * anyway. (TODO: they could also send MODE SENSE). |
| 449 | */ |
| 450 | case TYPE_ROM: |
| 451 | case TYPE_WORM: |
| 452 | s->blocksize = 2048; |
| 453 | break; |
| 454 | default: |
| 455 | s->blocksize = 512; |
| 456 | break; |
aurel32 | 89c0f64 | 2008-10-17 08:08:56 +0000 | [diff] [blame] | 457 | } |
Paolo Bonzini | 8869e10 | 2011-10-12 12:49:35 +0200 | [diff] [blame] | 458 | |
| 459 | DPRINTF("block size %d\n", s->blocksize); |
Gerd Hoffmann | d52affa | 2009-08-31 14:24:04 +0200 | [diff] [blame] | 460 | return 0; |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 461 | } |
Gerd Hoffmann | d52affa | 2009-08-31 14:24:04 +0200 | [diff] [blame] | 462 | |
Paolo Bonzini | 765d152 | 2011-10-12 12:54:31 +0200 | [diff] [blame] | 463 | const SCSIReqOps scsi_generic_req_ops = { |
Paolo Bonzini | 8dbd457 | 2011-08-03 10:49:08 +0200 | [diff] [blame] | 464 | .size = sizeof(SCSIGenericReq), |
Paolo Bonzini | 12010e7 | 2011-08-03 10:49:09 +0200 | [diff] [blame] | 465 | .free_req = scsi_free_request, |
| 466 | .send_command = scsi_send_command, |
| 467 | .read_data = scsi_read_data, |
| 468 | .write_data = scsi_write_data, |
| 469 | .cancel_io = scsi_cancel_io, |
| 470 | .get_buf = scsi_get_buf, |
Paolo Bonzini | 56b1fc4 | 2011-12-15 07:24:30 -0500 | [diff] [blame] | 471 | .load_request = scsi_generic_load_request, |
| 472 | .save_request = scsi_generic_save_request, |
Paolo Bonzini | 8dbd457 | 2011-08-03 10:49:08 +0200 | [diff] [blame] | 473 | }; |
| 474 | |
| 475 | static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, |
Paolo Bonzini | 63db0f0 | 2011-10-12 12:58:31 +0200 | [diff] [blame] | 476 | uint8_t *buf, void *hba_private) |
Paolo Bonzini | 8dbd457 | 2011-08-03 10:49:08 +0200 | [diff] [blame] | 477 | { |
| 478 | SCSIRequest *req; |
| 479 | |
| 480 | req = scsi_req_alloc(&scsi_generic_req_ops, d, tag, lun, hba_private); |
| 481 | return req; |
| 482 | } |
| 483 | |
Anthony Liguori | 39bffca | 2011-12-07 21:34:16 -0600 | [diff] [blame] | 484 | static Property scsi_generic_properties[] = { |
| 485 | DEFINE_BLOCK_PROPERTIES(SCSIDevice, conf), |
| 486 | DEFINE_PROP_END_OF_LIST(), |
| 487 | }; |
| 488 | |
Anthony Liguori | b9eea3e | 2011-12-15 14:50:08 -0600 | [diff] [blame] | 489 | static void scsi_generic_class_initfn(ObjectClass *klass, void *data) |
| 490 | { |
Anthony Liguori | 39bffca | 2011-12-07 21:34:16 -0600 | [diff] [blame] | 491 | DeviceClass *dc = DEVICE_CLASS(klass); |
Anthony Liguori | b9eea3e | 2011-12-15 14:50:08 -0600 | [diff] [blame] | 492 | SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); |
| 493 | |
| 494 | sc->init = scsi_generic_initfn; |
| 495 | sc->destroy = scsi_destroy; |
| 496 | sc->alloc_req = scsi_new_request; |
Anthony Liguori | 39bffca | 2011-12-07 21:34:16 -0600 | [diff] [blame] | 497 | dc->fw_name = "disk"; |
| 498 | dc->desc = "pass through generic scsi device (/dev/sg*)"; |
| 499 | dc->reset = scsi_generic_reset; |
| 500 | dc->props = scsi_generic_properties; |
Paolo Bonzini | 56b1fc4 | 2011-12-15 07:24:30 -0500 | [diff] [blame] | 501 | dc->vmsd = &vmstate_scsi_device; |
Anthony Liguori | b9eea3e | 2011-12-15 14:50:08 -0600 | [diff] [blame] | 502 | } |
| 503 | |
Anthony Liguori | 39bffca | 2011-12-07 21:34:16 -0600 | [diff] [blame] | 504 | static TypeInfo scsi_generic_info = { |
| 505 | .name = "scsi-generic", |
| 506 | .parent = TYPE_SCSI_DEVICE, |
| 507 | .instance_size = sizeof(SCSIDevice), |
| 508 | .class_init = scsi_generic_class_initfn, |
Gerd Hoffmann | d52affa | 2009-08-31 14:24:04 +0200 | [diff] [blame] | 509 | }; |
| 510 | |
Andreas Färber | 83f7d43 | 2012-02-09 15:20:55 +0100 | [diff] [blame] | 511 | static void scsi_generic_register_types(void) |
Gerd Hoffmann | d52affa | 2009-08-31 14:24:04 +0200 | [diff] [blame] | 512 | { |
Anthony Liguori | 39bffca | 2011-12-07 21:34:16 -0600 | [diff] [blame] | 513 | type_register_static(&scsi_generic_info); |
Gerd Hoffmann | d52affa | 2009-08-31 14:24:04 +0200 | [diff] [blame] | 514 | } |
Andreas Färber | 83f7d43 | 2012-02-09 15:20:55 +0100 | [diff] [blame] | 515 | |
| 516 | type_init(scsi_generic_register_types) |
Gerd Hoffmann | d52affa | 2009-08-31 14:24:04 +0200 | [diff] [blame] | 517 | |
ths | 2cc977e | 2007-12-24 16:11:51 +0000 | [diff] [blame] | 518 | #endif /* __linux__ */ |