root / hw / scsi-bus.c @ 6dc06f08
History | View | Annotate | Download (31.8 kB)
1 | d52affa7 | Gerd Hoffmann | #include "hw.h" |
---|---|---|---|
2 | 2f792016 | Markus Armbruster | #include "qemu-error.h" |
3 | 43b443b6 | Gerd Hoffmann | #include "scsi.h" |
4 | 2ec749cb | Gerd Hoffmann | #include "scsi-defs.h" |
5 | d52affa7 | Gerd Hoffmann | #include "qdev.h" |
6 | 2446333c | Blue Swirl | #include "blockdev.h" |
7 | 5138efec | Paolo Bonzini | #include "trace.h" |
8 | d52affa7 | Gerd Hoffmann | |
9 | db07c0f8 | Gleb Natapov | static char *scsibus_get_fw_dev_path(DeviceState *dev); |
10 | afa46c46 | Paolo Bonzini | static int scsi_req_parse(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf); |
11 | b45ef674 | Paolo Bonzini | static int scsi_build_sense(uint8_t *in_buf, int in_len, |
12 | b45ef674 | Paolo Bonzini | uint8_t *buf, int len, bool fixed); |
13 | db07c0f8 | Gleb Natapov | |
14 | d52affa7 | Gerd Hoffmann | static struct BusInfo scsi_bus_info = { |
15 | d52affa7 | Gerd Hoffmann | .name = "SCSI",
|
16 | d52affa7 | Gerd Hoffmann | .size = sizeof(SCSIBus),
|
17 | db07c0f8 | Gleb Natapov | .get_fw_dev_path = scsibus_get_fw_dev_path, |
18 | d52affa7 | Gerd Hoffmann | .props = (Property[]) { |
19 | d52affa7 | Gerd Hoffmann | DEFINE_PROP_UINT32("scsi-id", SCSIDevice, id, -1), |
20 | 87dcd1b2 | Paolo Bonzini | DEFINE_PROP_UINT32("lun", SCSIDevice, lun, 0), |
21 | d52affa7 | Gerd Hoffmann | DEFINE_PROP_END_OF_LIST(), |
22 | d52affa7 | Gerd Hoffmann | }, |
23 | d52affa7 | Gerd Hoffmann | }; |
24 | d52affa7 | Gerd Hoffmann | static int next_scsi_bus; |
25 | d52affa7 | Gerd Hoffmann | |
26 | d52affa7 | Gerd Hoffmann | /* Create a scsi bus, and attach devices to it. */
|
27 | ca9c39fa | Gerd Hoffmann | void scsi_bus_new(SCSIBus *bus, DeviceState *host, int tcq, int ndev, |
28 | cfdc1bb0 | Paolo Bonzini | const SCSIBusOps *ops)
|
29 | d52affa7 | Gerd Hoffmann | { |
30 | ca9c39fa | Gerd Hoffmann | qbus_create_inplace(&bus->qbus, &scsi_bus_info, host, NULL);
|
31 | d52affa7 | Gerd Hoffmann | bus->busnr = next_scsi_bus++; |
32 | d52affa7 | Gerd Hoffmann | bus->tcq = tcq; |
33 | d52affa7 | Gerd Hoffmann | bus->ndev = ndev; |
34 | cfdc1bb0 | Paolo Bonzini | bus->ops = ops; |
35 | cb23117b | Gerd Hoffmann | bus->qbus.allow_hotplug = 1;
|
36 | d52affa7 | Gerd Hoffmann | } |
37 | d52affa7 | Gerd Hoffmann | |
38 | d52affa7 | Gerd Hoffmann | static int scsi_qdev_init(DeviceState *qdev, DeviceInfo *base) |
39 | d52affa7 | Gerd Hoffmann | { |
40 | d52affa7 | Gerd Hoffmann | SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev); |
41 | d52affa7 | Gerd Hoffmann | SCSIDeviceInfo *info = DO_UPCAST(SCSIDeviceInfo, qdev, base); |
42 | d52affa7 | Gerd Hoffmann | SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus); |
43 | 01985dcf | Gerd Hoffmann | int rc = -1; |
44 | d52affa7 | Gerd Hoffmann | |
45 | d52affa7 | Gerd Hoffmann | if (dev->id == -1) { |
46 | d52affa7 | Gerd Hoffmann | for (dev->id = 0; dev->id < bus->ndev; dev->id++) { |
47 | d52affa7 | Gerd Hoffmann | if (bus->devs[dev->id] == NULL) |
48 | d52affa7 | Gerd Hoffmann | break;
|
49 | d52affa7 | Gerd Hoffmann | } |
50 | d52affa7 | Gerd Hoffmann | } |
51 | d52affa7 | Gerd Hoffmann | if (dev->id >= bus->ndev) {
|
52 | 1ecda02b | Markus Armbruster | error_report("bad scsi device id: %d", dev->id);
|
53 | d52affa7 | Gerd Hoffmann | goto err;
|
54 | d52affa7 | Gerd Hoffmann | } |
55 | d52affa7 | Gerd Hoffmann | |
56 | d52affa7 | Gerd Hoffmann | if (bus->devs[dev->id]) {
|
57 | 01985dcf | Gerd Hoffmann | qdev_free(&bus->devs[dev->id]->qdev); |
58 | d52affa7 | Gerd Hoffmann | } |
59 | d52affa7 | Gerd Hoffmann | bus->devs[dev->id] = dev; |
60 | d52affa7 | Gerd Hoffmann | |
61 | d52affa7 | Gerd Hoffmann | dev->info = info; |
62 | 9af99d98 | Gerd Hoffmann | QTAILQ_INIT(&dev->requests); |
63 | 01985dcf | Gerd Hoffmann | rc = dev->info->init(dev); |
64 | 01985dcf | Gerd Hoffmann | if (rc != 0) { |
65 | 01985dcf | Gerd Hoffmann | bus->devs[dev->id] = NULL;
|
66 | 01985dcf | Gerd Hoffmann | } |
67 | d52affa7 | Gerd Hoffmann | |
68 | d52affa7 | Gerd Hoffmann | err:
|
69 | 01985dcf | Gerd Hoffmann | return rc;
|
70 | 01985dcf | Gerd Hoffmann | } |
71 | 01985dcf | Gerd Hoffmann | |
72 | 01985dcf | Gerd Hoffmann | static int scsi_qdev_exit(DeviceState *qdev) |
73 | 01985dcf | Gerd Hoffmann | { |
74 | 01985dcf | Gerd Hoffmann | SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev); |
75 | 01985dcf | Gerd Hoffmann | SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus); |
76 | 01985dcf | Gerd Hoffmann | |
77 | 01985dcf | Gerd Hoffmann | assert(bus->devs[dev->id] != NULL);
|
78 | 01985dcf | Gerd Hoffmann | if (bus->devs[dev->id]->info->destroy) {
|
79 | 01985dcf | Gerd Hoffmann | bus->devs[dev->id]->info->destroy(bus->devs[dev->id]); |
80 | 01985dcf | Gerd Hoffmann | } |
81 | 01985dcf | Gerd Hoffmann | bus->devs[dev->id] = NULL;
|
82 | 01985dcf | Gerd Hoffmann | return 0; |
83 | d52affa7 | Gerd Hoffmann | } |
84 | d52affa7 | Gerd Hoffmann | |
85 | d52affa7 | Gerd Hoffmann | void scsi_qdev_register(SCSIDeviceInfo *info)
|
86 | d52affa7 | Gerd Hoffmann | { |
87 | d52affa7 | Gerd Hoffmann | info->qdev.bus_info = &scsi_bus_info; |
88 | d52affa7 | Gerd Hoffmann | info->qdev.init = scsi_qdev_init; |
89 | cb23117b | Gerd Hoffmann | info->qdev.unplug = qdev_simple_unplug_cb; |
90 | 01985dcf | Gerd Hoffmann | info->qdev.exit = scsi_qdev_exit; |
91 | d52affa7 | Gerd Hoffmann | qdev_register(&info->qdev); |
92 | d52affa7 | Gerd Hoffmann | } |
93 | d52affa7 | Gerd Hoffmann | |
94 | d52affa7 | Gerd Hoffmann | /* handle legacy '-drive if=scsi,...' cmd line args */
|
95 | 2d1fd261 | Stefan Hajnoczi | SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv, |
96 | 2d1fd261 | Stefan Hajnoczi | int unit, bool removable) |
97 | d52affa7 | Gerd Hoffmann | { |
98 | d52affa7 | Gerd Hoffmann | const char *driver; |
99 | d52affa7 | Gerd Hoffmann | DeviceState *dev; |
100 | d52affa7 | Gerd Hoffmann | |
101 | f8b6cc00 | Markus Armbruster | driver = bdrv_is_sg(bdrv) ? "scsi-generic" : "scsi-disk"; |
102 | d52affa7 | Gerd Hoffmann | dev = qdev_create(&bus->qbus, driver); |
103 | d52affa7 | Gerd Hoffmann | qdev_prop_set_uint32(dev, "scsi-id", unit);
|
104 | 2d1fd261 | Stefan Hajnoczi | if (qdev_prop_exists(dev, "removable")) { |
105 | 2d1fd261 | Stefan Hajnoczi | qdev_prop_set_bit(dev, "removable", removable);
|
106 | 2d1fd261 | Stefan Hajnoczi | } |
107 | 18846dee | Markus Armbruster | if (qdev_prop_set_drive(dev, "drive", bdrv) < 0) { |
108 | 18846dee | Markus Armbruster | qdev_free(dev); |
109 | 18846dee | Markus Armbruster | return NULL; |
110 | 18846dee | Markus Armbruster | } |
111 | 33e66b86 | Markus Armbruster | if (qdev_init(dev) < 0) |
112 | 33e66b86 | Markus Armbruster | return NULL; |
113 | d52affa7 | Gerd Hoffmann | return DO_UPCAST(SCSIDevice, qdev, dev);
|
114 | d52affa7 | Gerd Hoffmann | } |
115 | d52affa7 | Gerd Hoffmann | |
116 | fa66b909 | Markus Armbruster | int scsi_bus_legacy_handle_cmdline(SCSIBus *bus)
|
117 | d52affa7 | Gerd Hoffmann | { |
118 | 42e766a2 | Markus Armbruster | Location loc; |
119 | d52affa7 | Gerd Hoffmann | DriveInfo *dinfo; |
120 | fa66b909 | Markus Armbruster | int res = 0, unit; |
121 | d52affa7 | Gerd Hoffmann | |
122 | 42e766a2 | Markus Armbruster | loc_push_none(&loc); |
123 | 622b520f | Hannes Reinecke | for (unit = 0; unit < bus->ndev; unit++) { |
124 | d52affa7 | Gerd Hoffmann | dinfo = drive_get(IF_SCSI, bus->busnr, unit); |
125 | d52affa7 | Gerd Hoffmann | if (dinfo == NULL) { |
126 | d52affa7 | Gerd Hoffmann | continue;
|
127 | d52affa7 | Gerd Hoffmann | } |
128 | 42e766a2 | Markus Armbruster | qemu_opts_loc_restore(dinfo->opts); |
129 | 2d1fd261 | Stefan Hajnoczi | if (!scsi_bus_legacy_add_drive(bus, dinfo->bdrv, unit, false)) { |
130 | fa66b909 | Markus Armbruster | res = -1;
|
131 | fa66b909 | Markus Armbruster | break;
|
132 | fa66b909 | Markus Armbruster | } |
133 | d52affa7 | Gerd Hoffmann | } |
134 | 42e766a2 | Markus Armbruster | loc_pop(&loc); |
135 | fa66b909 | Markus Armbruster | return res;
|
136 | d52affa7 | Gerd Hoffmann | } |
137 | 89b08ae1 | Gerd Hoffmann | |
138 | afa46c46 | Paolo Bonzini | /* SCSIReqOps implementation for invalid commands. */
|
139 | afa46c46 | Paolo Bonzini | |
140 | afa46c46 | Paolo Bonzini | static int32_t scsi_invalid_command(SCSIRequest *req, uint8_t *buf)
|
141 | afa46c46 | Paolo Bonzini | { |
142 | afa46c46 | Paolo Bonzini | scsi_req_build_sense(req, SENSE_CODE(INVALID_OPCODE)); |
143 | afa46c46 | Paolo Bonzini | scsi_req_complete(req, CHECK_CONDITION); |
144 | afa46c46 | Paolo Bonzini | return 0; |
145 | afa46c46 | Paolo Bonzini | } |
146 | afa46c46 | Paolo Bonzini | |
147 | afa46c46 | Paolo Bonzini | struct SCSIReqOps reqops_invalid_opcode = {
|
148 | afa46c46 | Paolo Bonzini | .size = sizeof(SCSIRequest),
|
149 | afa46c46 | Paolo Bonzini | .send_command = scsi_invalid_command |
150 | afa46c46 | Paolo Bonzini | }; |
151 | afa46c46 | Paolo Bonzini | |
152 | 6dc06f08 | Paolo Bonzini | /* SCSIReqOps implementation for unit attention conditions. */
|
153 | 6dc06f08 | Paolo Bonzini | |
154 | 6dc06f08 | Paolo Bonzini | static int32_t scsi_unit_attention(SCSIRequest *req, uint8_t *buf)
|
155 | 6dc06f08 | Paolo Bonzini | { |
156 | 6dc06f08 | Paolo Bonzini | if (req->dev && req->dev->unit_attention.key == UNIT_ATTENTION) {
|
157 | 6dc06f08 | Paolo Bonzini | scsi_req_build_sense(req, req->dev->unit_attention); |
158 | 6dc06f08 | Paolo Bonzini | } else if (req->bus->unit_attention.key == UNIT_ATTENTION) { |
159 | 6dc06f08 | Paolo Bonzini | scsi_req_build_sense(req, req->bus->unit_attention); |
160 | 6dc06f08 | Paolo Bonzini | } |
161 | 6dc06f08 | Paolo Bonzini | scsi_req_complete(req, CHECK_CONDITION); |
162 | 6dc06f08 | Paolo Bonzini | return 0; |
163 | 6dc06f08 | Paolo Bonzini | } |
164 | 6dc06f08 | Paolo Bonzini | |
165 | 6dc06f08 | Paolo Bonzini | struct SCSIReqOps reqops_unit_attention = {
|
166 | 6dc06f08 | Paolo Bonzini | .size = sizeof(SCSIRequest),
|
167 | 6dc06f08 | Paolo Bonzini | .send_command = scsi_unit_attention |
168 | 6dc06f08 | Paolo Bonzini | }; |
169 | 6dc06f08 | Paolo Bonzini | |
170 | fdaef069 | Paolo Bonzini | /* SCSIReqOps implementation for REPORT LUNS and for commands sent to
|
171 | fdaef069 | Paolo Bonzini | an invalid LUN. */
|
172 | fdaef069 | Paolo Bonzini | |
173 | fdaef069 | Paolo Bonzini | typedef struct SCSITargetReq SCSITargetReq; |
174 | fdaef069 | Paolo Bonzini | |
175 | fdaef069 | Paolo Bonzini | struct SCSITargetReq {
|
176 | fdaef069 | Paolo Bonzini | SCSIRequest req; |
177 | fdaef069 | Paolo Bonzini | int len;
|
178 | fdaef069 | Paolo Bonzini | uint8_t buf[64];
|
179 | fdaef069 | Paolo Bonzini | }; |
180 | fdaef069 | Paolo Bonzini | |
181 | fdaef069 | Paolo Bonzini | static void store_lun(uint8_t *outbuf, int lun) |
182 | fdaef069 | Paolo Bonzini | { |
183 | fdaef069 | Paolo Bonzini | if (lun < 256) { |
184 | fdaef069 | Paolo Bonzini | outbuf[1] = lun;
|
185 | fdaef069 | Paolo Bonzini | return;
|
186 | fdaef069 | Paolo Bonzini | } |
187 | fdaef069 | Paolo Bonzini | outbuf[1] = (lun & 255); |
188 | fdaef069 | Paolo Bonzini | outbuf[0] = (lun >> 8) | 0x40; |
189 | fdaef069 | Paolo Bonzini | } |
190 | fdaef069 | Paolo Bonzini | |
191 | fdaef069 | Paolo Bonzini | static bool scsi_target_emulate_report_luns(SCSITargetReq *r) |
192 | fdaef069 | Paolo Bonzini | { |
193 | fdaef069 | Paolo Bonzini | int len;
|
194 | fdaef069 | Paolo Bonzini | if (r->req.cmd.xfer < 16) { |
195 | fdaef069 | Paolo Bonzini | return false; |
196 | fdaef069 | Paolo Bonzini | } |
197 | fdaef069 | Paolo Bonzini | if (r->req.cmd.buf[2] > 2) { |
198 | fdaef069 | Paolo Bonzini | return false; |
199 | fdaef069 | Paolo Bonzini | } |
200 | fdaef069 | Paolo Bonzini | len = MIN(sizeof r->buf, r->req.cmd.xfer);
|
201 | fdaef069 | Paolo Bonzini | memset(r->buf, 0, len);
|
202 | fdaef069 | Paolo Bonzini | if (r->req.dev->lun != 0) { |
203 | fdaef069 | Paolo Bonzini | r->buf[3] = 16; |
204 | fdaef069 | Paolo Bonzini | r->len = 24;
|
205 | fdaef069 | Paolo Bonzini | store_lun(&r->buf[16], r->req.dev->lun);
|
206 | fdaef069 | Paolo Bonzini | } else {
|
207 | fdaef069 | Paolo Bonzini | r->buf[3] = 8; |
208 | fdaef069 | Paolo Bonzini | r->len = 16;
|
209 | fdaef069 | Paolo Bonzini | } |
210 | fdaef069 | Paolo Bonzini | return true; |
211 | fdaef069 | Paolo Bonzini | } |
212 | fdaef069 | Paolo Bonzini | |
213 | fdaef069 | Paolo Bonzini | static bool scsi_target_emulate_inquiry(SCSITargetReq *r) |
214 | fdaef069 | Paolo Bonzini | { |
215 | fdaef069 | Paolo Bonzini | assert(r->req.dev->lun != r->req.lun); |
216 | fdaef069 | Paolo Bonzini | if (r->req.cmd.buf[1] & 0x2) { |
217 | fdaef069 | Paolo Bonzini | /* Command support data - optional, not implemented */
|
218 | fdaef069 | Paolo Bonzini | return false; |
219 | fdaef069 | Paolo Bonzini | } |
220 | fdaef069 | Paolo Bonzini | |
221 | fdaef069 | Paolo Bonzini | if (r->req.cmd.buf[1] & 0x1) { |
222 | fdaef069 | Paolo Bonzini | /* Vital product data */
|
223 | fdaef069 | Paolo Bonzini | uint8_t page_code = r->req.cmd.buf[2];
|
224 | fdaef069 | Paolo Bonzini | if (r->req.cmd.xfer < 4) { |
225 | fdaef069 | Paolo Bonzini | return false; |
226 | fdaef069 | Paolo Bonzini | } |
227 | fdaef069 | Paolo Bonzini | |
228 | fdaef069 | Paolo Bonzini | r->buf[r->len++] = page_code ; /* this page */
|
229 | fdaef069 | Paolo Bonzini | r->buf[r->len++] = 0x00;
|
230 | fdaef069 | Paolo Bonzini | |
231 | fdaef069 | Paolo Bonzini | switch (page_code) {
|
232 | fdaef069 | Paolo Bonzini | case 0x00: /* Supported page codes, mandatory */ |
233 | fdaef069 | Paolo Bonzini | { |
234 | fdaef069 | Paolo Bonzini | int pages;
|
235 | fdaef069 | Paolo Bonzini | pages = r->len++; |
236 | fdaef069 | Paolo Bonzini | r->buf[r->len++] = 0x00; /* list of supported pages (this page) */ |
237 | fdaef069 | Paolo Bonzini | r->buf[pages] = r->len - pages - 1; /* number of pages */ |
238 | fdaef069 | Paolo Bonzini | break;
|
239 | fdaef069 | Paolo Bonzini | } |
240 | fdaef069 | Paolo Bonzini | default:
|
241 | fdaef069 | Paolo Bonzini | return false; |
242 | fdaef069 | Paolo Bonzini | } |
243 | fdaef069 | Paolo Bonzini | /* done with EVPD */
|
244 | fdaef069 | Paolo Bonzini | assert(r->len < sizeof(r->buf));
|
245 | fdaef069 | Paolo Bonzini | r->len = MIN(r->req.cmd.xfer, r->len); |
246 | fdaef069 | Paolo Bonzini | return true; |
247 | fdaef069 | Paolo Bonzini | } |
248 | fdaef069 | Paolo Bonzini | |
249 | fdaef069 | Paolo Bonzini | /* Standard INQUIRY data */
|
250 | fdaef069 | Paolo Bonzini | if (r->req.cmd.buf[2] != 0) { |
251 | fdaef069 | Paolo Bonzini | return false; |
252 | fdaef069 | Paolo Bonzini | } |
253 | fdaef069 | Paolo Bonzini | |
254 | fdaef069 | Paolo Bonzini | /* PAGE CODE == 0 */
|
255 | fdaef069 | Paolo Bonzini | if (r->req.cmd.xfer < 5) { |
256 | fdaef069 | Paolo Bonzini | return -1; |
257 | fdaef069 | Paolo Bonzini | } |
258 | fdaef069 | Paolo Bonzini | |
259 | fdaef069 | Paolo Bonzini | r->len = MIN(r->req.cmd.xfer, 36);
|
260 | fdaef069 | Paolo Bonzini | memset(r->buf, 0, r->len);
|
261 | fdaef069 | Paolo Bonzini | if (r->req.lun != 0) { |
262 | fdaef069 | Paolo Bonzini | r->buf[0] = TYPE_NO_LUN;
|
263 | fdaef069 | Paolo Bonzini | } else {
|
264 | fdaef069 | Paolo Bonzini | r->buf[0] = TYPE_NOT_PRESENT | TYPE_INACTIVE;
|
265 | fdaef069 | Paolo Bonzini | r->buf[2] = 5; /* Version */ |
266 | fdaef069 | Paolo Bonzini | r->buf[3] = 2 | 0x10; /* HiSup, response data format */ |
267 | fdaef069 | Paolo Bonzini | r->buf[4] = r->len - 5; /* Additional Length = (Len - 1) - 4 */ |
268 | fdaef069 | Paolo Bonzini | r->buf[7] = 0x10 | (r->req.bus->tcq ? 0x02 : 0); /* Sync, TCQ. */ |
269 | fdaef069 | Paolo Bonzini | memcpy(&r->buf[8], "QEMU ", 8); |
270 | fdaef069 | Paolo Bonzini | memcpy(&r->buf[16], "QEMU TARGET ", 16); |
271 | fdaef069 | Paolo Bonzini | strncpy((char *) &r->buf[32], QEMU_VERSION, 4); |
272 | fdaef069 | Paolo Bonzini | } |
273 | fdaef069 | Paolo Bonzini | return true; |
274 | fdaef069 | Paolo Bonzini | } |
275 | fdaef069 | Paolo Bonzini | |
276 | fdaef069 | Paolo Bonzini | static int32_t scsi_target_send_command(SCSIRequest *req, uint8_t *buf)
|
277 | fdaef069 | Paolo Bonzini | { |
278 | fdaef069 | Paolo Bonzini | SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req); |
279 | fdaef069 | Paolo Bonzini | |
280 | fdaef069 | Paolo Bonzini | switch (buf[0]) { |
281 | fdaef069 | Paolo Bonzini | case REPORT_LUNS:
|
282 | fdaef069 | Paolo Bonzini | if (!scsi_target_emulate_report_luns(r)) {
|
283 | fdaef069 | Paolo Bonzini | goto illegal_request;
|
284 | fdaef069 | Paolo Bonzini | } |
285 | fdaef069 | Paolo Bonzini | break;
|
286 | fdaef069 | Paolo Bonzini | case INQUIRY:
|
287 | fdaef069 | Paolo Bonzini | if (!scsi_target_emulate_inquiry(r)) {
|
288 | fdaef069 | Paolo Bonzini | goto illegal_request;
|
289 | fdaef069 | Paolo Bonzini | } |
290 | fdaef069 | Paolo Bonzini | break;
|
291 | 739df215 | Paolo Bonzini | case REQUEST_SENSE:
|
292 | 739df215 | Paolo Bonzini | if (req->cmd.xfer < 4) { |
293 | 739df215 | Paolo Bonzini | goto illegal_request;
|
294 | 739df215 | Paolo Bonzini | } |
295 | 739df215 | Paolo Bonzini | r->len = scsi_device_get_sense(r->req.dev, r->buf, req->cmd.xfer, |
296 | 739df215 | Paolo Bonzini | (req->cmd.buf[1] & 1) == 0); |
297 | 739df215 | Paolo Bonzini | break;
|
298 | fdaef069 | Paolo Bonzini | default:
|
299 | fdaef069 | Paolo Bonzini | scsi_req_build_sense(req, SENSE_CODE(LUN_NOT_SUPPORTED)); |
300 | fdaef069 | Paolo Bonzini | scsi_req_complete(req, CHECK_CONDITION); |
301 | fdaef069 | Paolo Bonzini | return 0; |
302 | fdaef069 | Paolo Bonzini | illegal_request:
|
303 | fdaef069 | Paolo Bonzini | scsi_req_build_sense(req, SENSE_CODE(INVALID_FIELD)); |
304 | fdaef069 | Paolo Bonzini | scsi_req_complete(req, CHECK_CONDITION); |
305 | fdaef069 | Paolo Bonzini | return 0; |
306 | fdaef069 | Paolo Bonzini | } |
307 | fdaef069 | Paolo Bonzini | |
308 | fdaef069 | Paolo Bonzini | if (!r->len) {
|
309 | fdaef069 | Paolo Bonzini | scsi_req_complete(req, GOOD); |
310 | fdaef069 | Paolo Bonzini | } |
311 | fdaef069 | Paolo Bonzini | return r->len;
|
312 | fdaef069 | Paolo Bonzini | } |
313 | fdaef069 | Paolo Bonzini | |
314 | fdaef069 | Paolo Bonzini | static void scsi_target_read_data(SCSIRequest *req) |
315 | fdaef069 | Paolo Bonzini | { |
316 | fdaef069 | Paolo Bonzini | SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req); |
317 | fdaef069 | Paolo Bonzini | uint32_t n; |
318 | fdaef069 | Paolo Bonzini | |
319 | fdaef069 | Paolo Bonzini | n = r->len; |
320 | fdaef069 | Paolo Bonzini | if (n > 0) { |
321 | fdaef069 | Paolo Bonzini | r->len = 0;
|
322 | fdaef069 | Paolo Bonzini | scsi_req_data(&r->req, n); |
323 | fdaef069 | Paolo Bonzini | } else {
|
324 | fdaef069 | Paolo Bonzini | scsi_req_complete(&r->req, GOOD); |
325 | fdaef069 | Paolo Bonzini | } |
326 | fdaef069 | Paolo Bonzini | } |
327 | fdaef069 | Paolo Bonzini | |
328 | fdaef069 | Paolo Bonzini | static uint8_t *scsi_target_get_buf(SCSIRequest *req)
|
329 | fdaef069 | Paolo Bonzini | { |
330 | fdaef069 | Paolo Bonzini | SCSITargetReq *r = DO_UPCAST(SCSITargetReq, req, req); |
331 | fdaef069 | Paolo Bonzini | |
332 | fdaef069 | Paolo Bonzini | return r->buf;
|
333 | fdaef069 | Paolo Bonzini | } |
334 | fdaef069 | Paolo Bonzini | |
335 | fdaef069 | Paolo Bonzini | struct SCSIReqOps reqops_target_command = {
|
336 | fdaef069 | Paolo Bonzini | .size = sizeof(SCSITargetReq),
|
337 | fdaef069 | Paolo Bonzini | .send_command = scsi_target_send_command, |
338 | fdaef069 | Paolo Bonzini | .read_data = scsi_target_read_data, |
339 | fdaef069 | Paolo Bonzini | .get_buf = scsi_target_get_buf, |
340 | fdaef069 | Paolo Bonzini | }; |
341 | fdaef069 | Paolo Bonzini | |
342 | fdaef069 | Paolo Bonzini | |
343 | 8dbd4574 | Paolo Bonzini | SCSIRequest *scsi_req_alloc(SCSIReqOps *reqops, SCSIDevice *d, uint32_t tag, |
344 | c5bf71a9 | Hannes Reinecke | uint32_t lun, void *hba_private)
|
345 | 89b08ae1 | Gerd Hoffmann | { |
346 | 89b08ae1 | Gerd Hoffmann | SCSIRequest *req; |
347 | 89b08ae1 | Gerd Hoffmann | |
348 | 8dbd4574 | Paolo Bonzini | req = qemu_mallocz(reqops->size); |
349 | 5c6c0e51 | Hannes Reinecke | req->refcount = 1;
|
350 | 89b08ae1 | Gerd Hoffmann | req->bus = scsi_bus_from_device(d); |
351 | 89b08ae1 | Gerd Hoffmann | req->dev = d; |
352 | 89b08ae1 | Gerd Hoffmann | req->tag = tag; |
353 | 89b08ae1 | Gerd Hoffmann | req->lun = lun; |
354 | c5bf71a9 | Hannes Reinecke | req->hba_private = hba_private; |
355 | ed3a34a3 | Gerd Hoffmann | req->status = -1;
|
356 | b45ef674 | Paolo Bonzini | req->sense_len = 0;
|
357 | 8dbd4574 | Paolo Bonzini | req->ops = reqops; |
358 | 5138efec | Paolo Bonzini | trace_scsi_req_alloc(req->dev->id, req->lun, req->tag); |
359 | 89b08ae1 | Gerd Hoffmann | return req;
|
360 | 89b08ae1 | Gerd Hoffmann | } |
361 | 89b08ae1 | Gerd Hoffmann | |
362 | c5bf71a9 | Hannes Reinecke | SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun, |
363 | c39ce112 | Paolo Bonzini | uint8_t *buf, void *hba_private)
|
364 | 43a2b339 | Paolo Bonzini | { |
365 | 6dc06f08 | Paolo Bonzini | SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, d->qdev.parent_bus); |
366 | c39ce112 | Paolo Bonzini | SCSIRequest *req; |
367 | afa46c46 | Paolo Bonzini | SCSICommand cmd; |
368 | afa46c46 | Paolo Bonzini | |
369 | afa46c46 | Paolo Bonzini | if (scsi_req_parse(&cmd, d, buf) != 0) { |
370 | afa46c46 | Paolo Bonzini | trace_scsi_req_parse_bad(d->id, lun, tag, buf[0]);
|
371 | afa46c46 | Paolo Bonzini | req = scsi_req_alloc(&reqops_invalid_opcode, d, tag, lun, hba_private); |
372 | afa46c46 | Paolo Bonzini | } else {
|
373 | afa46c46 | Paolo Bonzini | trace_scsi_req_parsed(d->id, lun, tag, buf[0],
|
374 | afa46c46 | Paolo Bonzini | cmd.mode, cmd.xfer); |
375 | afa46c46 | Paolo Bonzini | if (req->cmd.lba != -1) { |
376 | afa46c46 | Paolo Bonzini | trace_scsi_req_parsed_lba(d->id, lun, tag, buf[0],
|
377 | afa46c46 | Paolo Bonzini | cmd.lba); |
378 | afa46c46 | Paolo Bonzini | } |
379 | fdaef069 | Paolo Bonzini | |
380 | 6dc06f08 | Paolo Bonzini | if ((d->unit_attention.key == UNIT_ATTENTION ||
|
381 | 6dc06f08 | Paolo Bonzini | bus->unit_attention.key == UNIT_ATTENTION) && |
382 | 6dc06f08 | Paolo Bonzini | (buf[0] != INQUIRY &&
|
383 | 6dc06f08 | Paolo Bonzini | buf[0] != REPORT_LUNS &&
|
384 | 6dc06f08 | Paolo Bonzini | buf[0] != GET_CONFIGURATION &&
|
385 | 6dc06f08 | Paolo Bonzini | buf[0] != GET_EVENT_STATUS_NOTIFICATION)) {
|
386 | 6dc06f08 | Paolo Bonzini | req = scsi_req_alloc(&reqops_unit_attention, d, tag, lun, |
387 | 6dc06f08 | Paolo Bonzini | hba_private); |
388 | 6dc06f08 | Paolo Bonzini | } else if (lun != d->lun || |
389 | 739df215 | Paolo Bonzini | buf[0] == REPORT_LUNS ||
|
390 | 739df215 | Paolo Bonzini | buf[0] == REQUEST_SENSE) {
|
391 | fdaef069 | Paolo Bonzini | req = scsi_req_alloc(&reqops_target_command, d, tag, lun, |
392 | fdaef069 | Paolo Bonzini | hba_private); |
393 | fdaef069 | Paolo Bonzini | } else {
|
394 | fdaef069 | Paolo Bonzini | req = d->info->alloc_req(d, tag, lun, hba_private); |
395 | fdaef069 | Paolo Bonzini | } |
396 | afa46c46 | Paolo Bonzini | } |
397 | afa46c46 | Paolo Bonzini | |
398 | afa46c46 | Paolo Bonzini | req->cmd = cmd; |
399 | c39ce112 | Paolo Bonzini | return req;
|
400 | 43a2b339 | Paolo Bonzini | } |
401 | 43a2b339 | Paolo Bonzini | |
402 | 0c34459b | Paolo Bonzini | uint8_t *scsi_req_get_buf(SCSIRequest *req) |
403 | 0c34459b | Paolo Bonzini | { |
404 | 12010e7b | Paolo Bonzini | return req->ops->get_buf(req);
|
405 | 0c34459b | Paolo Bonzini | } |
406 | 0c34459b | Paolo Bonzini | |
407 | 6dc06f08 | Paolo Bonzini | static void scsi_clear_unit_attention(SCSIRequest *req) |
408 | 6dc06f08 | Paolo Bonzini | { |
409 | 6dc06f08 | Paolo Bonzini | SCSISense *ua; |
410 | 6dc06f08 | Paolo Bonzini | if (req->dev->unit_attention.key != UNIT_ATTENTION &&
|
411 | 6dc06f08 | Paolo Bonzini | req->bus->unit_attention.key != UNIT_ATTENTION) { |
412 | 6dc06f08 | Paolo Bonzini | return;
|
413 | 6dc06f08 | Paolo Bonzini | } |
414 | 6dc06f08 | Paolo Bonzini | |
415 | 6dc06f08 | Paolo Bonzini | /*
|
416 | 6dc06f08 | Paolo Bonzini | * If an INQUIRY command enters the enabled command state,
|
417 | 6dc06f08 | Paolo Bonzini | * the device server shall [not] clear any unit attention condition;
|
418 | 6dc06f08 | Paolo Bonzini | * See also MMC-6, paragraphs 6.5 and 6.6.2.
|
419 | 6dc06f08 | Paolo Bonzini | */
|
420 | 6dc06f08 | Paolo Bonzini | if (req->cmd.buf[0] == INQUIRY || |
421 | 6dc06f08 | Paolo Bonzini | req->cmd.buf[0] == GET_CONFIGURATION ||
|
422 | 6dc06f08 | Paolo Bonzini | req->cmd.buf[0] == GET_EVENT_STATUS_NOTIFICATION) {
|
423 | 6dc06f08 | Paolo Bonzini | return;
|
424 | 6dc06f08 | Paolo Bonzini | } |
425 | 6dc06f08 | Paolo Bonzini | |
426 | 6dc06f08 | Paolo Bonzini | if (req->dev->unit_attention.key == UNIT_ATTENTION) {
|
427 | 6dc06f08 | Paolo Bonzini | ua = &req->dev->unit_attention; |
428 | 6dc06f08 | Paolo Bonzini | } else {
|
429 | 6dc06f08 | Paolo Bonzini | ua = &req->bus->unit_attention; |
430 | 6dc06f08 | Paolo Bonzini | } |
431 | 6dc06f08 | Paolo Bonzini | |
432 | 6dc06f08 | Paolo Bonzini | /*
|
433 | 6dc06f08 | Paolo Bonzini | * If a REPORT LUNS command enters the enabled command state, [...]
|
434 | 6dc06f08 | Paolo Bonzini | * the device server shall clear any pending unit attention condition
|
435 | 6dc06f08 | Paolo Bonzini | * with an additional sense code of REPORTED LUNS DATA HAS CHANGED.
|
436 | 6dc06f08 | Paolo Bonzini | */
|
437 | 6dc06f08 | Paolo Bonzini | if (req->cmd.buf[0] == REPORT_LUNS && |
438 | 6dc06f08 | Paolo Bonzini | !(ua->asc == SENSE_CODE(REPORTED_LUNS_CHANGED).asc && |
439 | 6dc06f08 | Paolo Bonzini | ua->ascq == SENSE_CODE(REPORTED_LUNS_CHANGED).ascq)) { |
440 | 6dc06f08 | Paolo Bonzini | return;
|
441 | 6dc06f08 | Paolo Bonzini | } |
442 | 6dc06f08 | Paolo Bonzini | |
443 | 6dc06f08 | Paolo Bonzini | *ua = SENSE_CODE(NO_SENSE); |
444 | 6dc06f08 | Paolo Bonzini | } |
445 | 6dc06f08 | Paolo Bonzini | |
446 | 74382217 | Hannes Reinecke | int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len) |
447 | 74382217 | Hannes Reinecke | { |
448 | 6dc06f08 | Paolo Bonzini | int ret;
|
449 | 6dc06f08 | Paolo Bonzini | |
450 | b45ef674 | Paolo Bonzini | assert(len >= 14);
|
451 | b45ef674 | Paolo Bonzini | if (!req->sense_len) {
|
452 | 74382217 | Hannes Reinecke | return 0; |
453 | 74382217 | Hannes Reinecke | } |
454 | 6dc06f08 | Paolo Bonzini | |
455 | 6dc06f08 | Paolo Bonzini | ret = scsi_build_sense(req->sense, req->sense_len, buf, len, true);
|
456 | 6dc06f08 | Paolo Bonzini | |
457 | 6dc06f08 | Paolo Bonzini | /*
|
458 | 6dc06f08 | Paolo Bonzini | * FIXME: clearing unit attention conditions upon autosense should be done
|
459 | 6dc06f08 | Paolo Bonzini | * only if the UA_INTLCK_CTRL field in the Control mode page is set to 00b
|
460 | 6dc06f08 | Paolo Bonzini | * (SAM-5, 5.14).
|
461 | 6dc06f08 | Paolo Bonzini | *
|
462 | 6dc06f08 | Paolo Bonzini | * We assume UA_INTLCK_CTRL to be 00b for HBAs that support autosense, and
|
463 | 6dc06f08 | Paolo Bonzini | * 10b for HBAs that do not support it (do not call scsi_req_get_sense).
|
464 | 6dc06f08 | Paolo Bonzini | * In the latter case, scsi_req_complete clears unit attention conditions
|
465 | 6dc06f08 | Paolo Bonzini | * after moving them to the device's sense buffer.
|
466 | 6dc06f08 | Paolo Bonzini | */
|
467 | 6dc06f08 | Paolo Bonzini | scsi_clear_unit_attention(req); |
468 | 6dc06f08 | Paolo Bonzini | return ret;
|
469 | b45ef674 | Paolo Bonzini | } |
470 | b45ef674 | Paolo Bonzini | |
471 | b45ef674 | Paolo Bonzini | int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed) |
472 | b45ef674 | Paolo Bonzini | { |
473 | b45ef674 | Paolo Bonzini | return scsi_build_sense(dev->sense, dev->sense_len, buf, len, fixed);
|
474 | b45ef674 | Paolo Bonzini | } |
475 | b45ef674 | Paolo Bonzini | |
476 | b45ef674 | Paolo Bonzini | void scsi_req_build_sense(SCSIRequest *req, SCSISense sense)
|
477 | b45ef674 | Paolo Bonzini | { |
478 | b45ef674 | Paolo Bonzini | trace_scsi_req_build_sense(req->dev->id, req->lun, req->tag, |
479 | b45ef674 | Paolo Bonzini | sense.key, sense.asc, sense.ascq); |
480 | b45ef674 | Paolo Bonzini | memset(req->sense, 0, 18); |
481 | b45ef674 | Paolo Bonzini | req->sense[0] = 0xf0; |
482 | b45ef674 | Paolo Bonzini | req->sense[2] = sense.key;
|
483 | b45ef674 | Paolo Bonzini | req->sense[12] = sense.asc;
|
484 | b45ef674 | Paolo Bonzini | req->sense[13] = sense.ascq;
|
485 | b45ef674 | Paolo Bonzini | req->sense_len = 18;
|
486 | 74382217 | Hannes Reinecke | } |
487 | 74382217 | Hannes Reinecke | |
488 | c39ce112 | Paolo Bonzini | int32_t scsi_req_enqueue(SCSIRequest *req) |
489 | 89b08ae1 | Gerd Hoffmann | { |
490 | fc4f0754 | Paolo Bonzini | int32_t rc; |
491 | fc4f0754 | Paolo Bonzini | |
492 | 5c6c0e51 | Hannes Reinecke | assert(!req->enqueued); |
493 | 5c6c0e51 | Hannes Reinecke | scsi_req_ref(req); |
494 | 5c6c0e51 | Hannes Reinecke | req->enqueued = true;
|
495 | 5c6c0e51 | Hannes Reinecke | QTAILQ_INSERT_TAIL(&req->dev->requests, req, next); |
496 | fc4f0754 | Paolo Bonzini | |
497 | fc4f0754 | Paolo Bonzini | scsi_req_ref(req); |
498 | c39ce112 | Paolo Bonzini | rc = req->ops->send_command(req, req->cmd.buf); |
499 | fc4f0754 | Paolo Bonzini | scsi_req_unref(req); |
500 | fc4f0754 | Paolo Bonzini | return rc;
|
501 | 89b08ae1 | Gerd Hoffmann | } |
502 | 89b08ae1 | Gerd Hoffmann | |
503 | a1f0cce2 | Hannes Reinecke | static void scsi_req_dequeue(SCSIRequest *req) |
504 | e8637c90 | Jan Kiszka | { |
505 | 5138efec | Paolo Bonzini | trace_scsi_req_dequeue(req->dev->id, req->lun, req->tag); |
506 | e8637c90 | Jan Kiszka | if (req->enqueued) {
|
507 | e8637c90 | Jan Kiszka | QTAILQ_REMOVE(&req->dev->requests, req, next); |
508 | e8637c90 | Jan Kiszka | req->enqueued = false;
|
509 | ad2d30f7 | Paolo Bonzini | scsi_req_unref(req); |
510 | e8637c90 | Jan Kiszka | } |
511 | e8637c90 | Jan Kiszka | } |
512 | e8637c90 | Jan Kiszka | |
513 | 2599aece | Paolo Bonzini | static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) |
514 | 2ec749cb | Gerd Hoffmann | { |
515 | 2599aece | Paolo Bonzini | switch (buf[0] >> 5) { |
516 | 2ec749cb | Gerd Hoffmann | case 0: |
517 | 2599aece | Paolo Bonzini | cmd->xfer = buf[4];
|
518 | 2599aece | Paolo Bonzini | cmd->len = 6;
|
519 | 2ec749cb | Gerd Hoffmann | /* length 0 means 256 blocks */
|
520 | 2599aece | Paolo Bonzini | if (cmd->xfer == 0) { |
521 | 2599aece | Paolo Bonzini | cmd->xfer = 256;
|
522 | 2599aece | Paolo Bonzini | } |
523 | 2ec749cb | Gerd Hoffmann | break;
|
524 | 2ec749cb | Gerd Hoffmann | case 1: |
525 | 2ec749cb | Gerd Hoffmann | case 2: |
526 | 2599aece | Paolo Bonzini | cmd->xfer = buf[8] | (buf[7] << 8); |
527 | 2599aece | Paolo Bonzini | cmd->len = 10;
|
528 | 2ec749cb | Gerd Hoffmann | break;
|
529 | 2ec749cb | Gerd Hoffmann | case 4: |
530 | 2599aece | Paolo Bonzini | cmd->xfer = buf[13] | (buf[12] << 8) | (buf[11] << 16) | (buf[10] << 24); |
531 | 2599aece | Paolo Bonzini | cmd->len = 16;
|
532 | 2ec749cb | Gerd Hoffmann | break;
|
533 | 2ec749cb | Gerd Hoffmann | case 5: |
534 | 2599aece | Paolo Bonzini | cmd->xfer = buf[9] | (buf[8] << 8) | (buf[7] << 16) | (buf[6] << 24); |
535 | 2599aece | Paolo Bonzini | cmd->len = 12;
|
536 | 2ec749cb | Gerd Hoffmann | break;
|
537 | 2ec749cb | Gerd Hoffmann | default:
|
538 | 2ec749cb | Gerd Hoffmann | return -1; |
539 | 2ec749cb | Gerd Hoffmann | } |
540 | 2ec749cb | Gerd Hoffmann | |
541 | 2599aece | Paolo Bonzini | switch (buf[0]) { |
542 | 2ec749cb | Gerd Hoffmann | case TEST_UNIT_READY:
|
543 | 5e30a07d | Hannes Reinecke | case REWIND:
|
544 | 2ec749cb | Gerd Hoffmann | case START_STOP:
|
545 | 2ec749cb | Gerd Hoffmann | case SEEK_6:
|
546 | 2ec749cb | Gerd Hoffmann | case WRITE_FILEMARKS:
|
547 | 2ec749cb | Gerd Hoffmann | case SPACE:
|
548 | a5e3d9ef | Bernhard Kohl | case RESERVE:
|
549 | a5e3d9ef | Bernhard Kohl | case RELEASE:
|
550 | 2ec749cb | Gerd Hoffmann | case ERASE:
|
551 | 2ec749cb | Gerd Hoffmann | case ALLOW_MEDIUM_REMOVAL:
|
552 | 5e30a07d | Hannes Reinecke | case VERIFY_10:
|
553 | 2ec749cb | Gerd Hoffmann | case SEEK_10:
|
554 | 2ec749cb | Gerd Hoffmann | case SYNCHRONIZE_CACHE:
|
555 | 2ec749cb | Gerd Hoffmann | case LOCK_UNLOCK_CACHE:
|
556 | 2ec749cb | Gerd Hoffmann | case LOAD_UNLOAD:
|
557 | 2ec749cb | Gerd Hoffmann | case SET_CD_SPEED:
|
558 | 2ec749cb | Gerd Hoffmann | case SET_LIMITS:
|
559 | 5e30a07d | Hannes Reinecke | case WRITE_LONG_10:
|
560 | 2ec749cb | Gerd Hoffmann | case MOVE_MEDIUM:
|
561 | 2ec749cb | Gerd Hoffmann | case UPDATE_BLOCK:
|
562 | 2599aece | Paolo Bonzini | cmd->xfer = 0;
|
563 | 2ec749cb | Gerd Hoffmann | break;
|
564 | 2ec749cb | Gerd Hoffmann | case MODE_SENSE:
|
565 | 2ec749cb | Gerd Hoffmann | break;
|
566 | 5e30a07d | Hannes Reinecke | case WRITE_SAME_10:
|
567 | 2599aece | Paolo Bonzini | cmd->xfer = 1;
|
568 | 2ec749cb | Gerd Hoffmann | break;
|
569 | 5e30a07d | Hannes Reinecke | case READ_CAPACITY_10:
|
570 | 2599aece | Paolo Bonzini | cmd->xfer = 8;
|
571 | 2ec749cb | Gerd Hoffmann | break;
|
572 | 2ec749cb | Gerd Hoffmann | case READ_BLOCK_LIMITS:
|
573 | 2599aece | Paolo Bonzini | cmd->xfer = 6;
|
574 | 2ec749cb | Gerd Hoffmann | break;
|
575 | 2ec749cb | Gerd Hoffmann | case READ_POSITION:
|
576 | 2599aece | Paolo Bonzini | cmd->xfer = 20;
|
577 | 2ec749cb | Gerd Hoffmann | break;
|
578 | 2ec749cb | Gerd Hoffmann | case SEND_VOLUME_TAG:
|
579 | 2599aece | Paolo Bonzini | cmd->xfer *= 40;
|
580 | 2ec749cb | Gerd Hoffmann | break;
|
581 | 2ec749cb | Gerd Hoffmann | case MEDIUM_SCAN:
|
582 | 2599aece | Paolo Bonzini | cmd->xfer *= 8;
|
583 | 2ec749cb | Gerd Hoffmann | break;
|
584 | 2ec749cb | Gerd Hoffmann | case WRITE_10:
|
585 | 5e30a07d | Hannes Reinecke | case WRITE_VERIFY_10:
|
586 | 2ec749cb | Gerd Hoffmann | case WRITE_6:
|
587 | 2ec749cb | Gerd Hoffmann | case WRITE_12:
|
588 | 2ec749cb | Gerd Hoffmann | case WRITE_VERIFY_12:
|
589 | bd536cf3 | Gerd Hoffmann | case WRITE_16:
|
590 | bd536cf3 | Gerd Hoffmann | case WRITE_VERIFY_16:
|
591 | 2599aece | Paolo Bonzini | cmd->xfer *= dev->blocksize; |
592 | 2ec749cb | Gerd Hoffmann | break;
|
593 | 2ec749cb | Gerd Hoffmann | case READ_10:
|
594 | 2ec749cb | Gerd Hoffmann | case READ_6:
|
595 | 2ec749cb | Gerd Hoffmann | case READ_REVERSE:
|
596 | 2ec749cb | Gerd Hoffmann | case RECOVER_BUFFERED_DATA:
|
597 | 2ec749cb | Gerd Hoffmann | case READ_12:
|
598 | bd536cf3 | Gerd Hoffmann | case READ_16:
|
599 | 2599aece | Paolo Bonzini | cmd->xfer *= dev->blocksize; |
600 | 2ec749cb | Gerd Hoffmann | break;
|
601 | 2ec749cb | Gerd Hoffmann | case INQUIRY:
|
602 | 2599aece | Paolo Bonzini | cmd->xfer = buf[4] | (buf[3] << 8); |
603 | 2ec749cb | Gerd Hoffmann | break;
|
604 | c7126d5b | Nicholas Bellinger | case MAINTENANCE_OUT:
|
605 | c7126d5b | Nicholas Bellinger | case MAINTENANCE_IN:
|
606 | 2599aece | Paolo Bonzini | if (dev->type == TYPE_ROM) {
|
607 | c7126d5b | Nicholas Bellinger | /* GPCMD_REPORT_KEY and GPCMD_SEND_KEY from multi media commands */
|
608 | 2599aece | Paolo Bonzini | cmd->xfer = buf[9] | (buf[8] << 8); |
609 | c7126d5b | Nicholas Bellinger | } |
610 | c7126d5b | Nicholas Bellinger | break;
|
611 | 2ec749cb | Gerd Hoffmann | } |
612 | 2ec749cb | Gerd Hoffmann | return 0; |
613 | 2ec749cb | Gerd Hoffmann | } |
614 | 2ec749cb | Gerd Hoffmann | |
615 | 2599aece | Paolo Bonzini | static int scsi_req_stream_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) |
616 | 2ec749cb | Gerd Hoffmann | { |
617 | 2599aece | Paolo Bonzini | switch (buf[0]) { |
618 | 2ec749cb | Gerd Hoffmann | /* stream commands */
|
619 | 2ec749cb | Gerd Hoffmann | case READ_6:
|
620 | 2ec749cb | Gerd Hoffmann | case READ_REVERSE:
|
621 | 2ec749cb | Gerd Hoffmann | case RECOVER_BUFFERED_DATA:
|
622 | 2ec749cb | Gerd Hoffmann | case WRITE_6:
|
623 | 2599aece | Paolo Bonzini | cmd->len = 6;
|
624 | 2599aece | Paolo Bonzini | cmd->xfer = buf[4] | (buf[3] << 8) | (buf[2] << 16); |
625 | 2599aece | Paolo Bonzini | if (buf[1] & 0x01) { /* fixed */ |
626 | 2599aece | Paolo Bonzini | cmd->xfer *= dev->blocksize; |
627 | 2599aece | Paolo Bonzini | } |
628 | 2ec749cb | Gerd Hoffmann | break;
|
629 | 2ec749cb | Gerd Hoffmann | case REWIND:
|
630 | 2ec749cb | Gerd Hoffmann | case START_STOP:
|
631 | 2599aece | Paolo Bonzini | cmd->len = 6;
|
632 | 2599aece | Paolo Bonzini | cmd->xfer = 0;
|
633 | 2ec749cb | Gerd Hoffmann | break;
|
634 | 2ec749cb | Gerd Hoffmann | /* generic commands */
|
635 | 2ec749cb | Gerd Hoffmann | default:
|
636 | 2599aece | Paolo Bonzini | return scsi_req_length(cmd, dev, buf);
|
637 | 2ec749cb | Gerd Hoffmann | } |
638 | 2ec749cb | Gerd Hoffmann | return 0; |
639 | 2ec749cb | Gerd Hoffmann | } |
640 | 2ec749cb | Gerd Hoffmann | |
641 | 2599aece | Paolo Bonzini | static void scsi_cmd_xfer_mode(SCSICommand *cmd) |
642 | 97a06435 | Gerd Hoffmann | { |
643 | 2599aece | Paolo Bonzini | switch (cmd->buf[0]) { |
644 | 97a06435 | Gerd Hoffmann | case WRITE_6:
|
645 | 97a06435 | Gerd Hoffmann | case WRITE_10:
|
646 | 5e30a07d | Hannes Reinecke | case WRITE_VERIFY_10:
|
647 | 97a06435 | Gerd Hoffmann | case WRITE_12:
|
648 | 97a06435 | Gerd Hoffmann | case WRITE_VERIFY_12:
|
649 | bd536cf3 | Gerd Hoffmann | case WRITE_16:
|
650 | bd536cf3 | Gerd Hoffmann | case WRITE_VERIFY_16:
|
651 | 97a06435 | Gerd Hoffmann | case COPY:
|
652 | 97a06435 | Gerd Hoffmann | case COPY_VERIFY:
|
653 | 97a06435 | Gerd Hoffmann | case COMPARE:
|
654 | 97a06435 | Gerd Hoffmann | case CHANGE_DEFINITION:
|
655 | 97a06435 | Gerd Hoffmann | case LOG_SELECT:
|
656 | 97a06435 | Gerd Hoffmann | case MODE_SELECT:
|
657 | 97a06435 | Gerd Hoffmann | case MODE_SELECT_10:
|
658 | 97a06435 | Gerd Hoffmann | case SEND_DIAGNOSTIC:
|
659 | 97a06435 | Gerd Hoffmann | case WRITE_BUFFER:
|
660 | 97a06435 | Gerd Hoffmann | case FORMAT_UNIT:
|
661 | 97a06435 | Gerd Hoffmann | case REASSIGN_BLOCKS:
|
662 | 97a06435 | Gerd Hoffmann | case SEARCH_EQUAL:
|
663 | 97a06435 | Gerd Hoffmann | case SEARCH_HIGH:
|
664 | 97a06435 | Gerd Hoffmann | case SEARCH_LOW:
|
665 | 97a06435 | Gerd Hoffmann | case UPDATE_BLOCK:
|
666 | 5e30a07d | Hannes Reinecke | case WRITE_LONG_10:
|
667 | 5e30a07d | Hannes Reinecke | case WRITE_SAME_10:
|
668 | 97a06435 | Gerd Hoffmann | case SEARCH_HIGH_12:
|
669 | 97a06435 | Gerd Hoffmann | case SEARCH_EQUAL_12:
|
670 | 97a06435 | Gerd Hoffmann | case SEARCH_LOW_12:
|
671 | 97a06435 | Gerd Hoffmann | case MEDIUM_SCAN:
|
672 | 97a06435 | Gerd Hoffmann | case SEND_VOLUME_TAG:
|
673 | 01bedeba | Nicholas Bellinger | case PERSISTENT_RESERVE_OUT:
|
674 | c7126d5b | Nicholas Bellinger | case MAINTENANCE_OUT:
|
675 | 2599aece | Paolo Bonzini | cmd->mode = SCSI_XFER_TO_DEV; |
676 | 97a06435 | Gerd Hoffmann | break;
|
677 | 97a06435 | Gerd Hoffmann | default:
|
678 | 2599aece | Paolo Bonzini | if (cmd->xfer)
|
679 | 2599aece | Paolo Bonzini | cmd->mode = SCSI_XFER_FROM_DEV; |
680 | 97a06435 | Gerd Hoffmann | else {
|
681 | 2599aece | Paolo Bonzini | cmd->mode = SCSI_XFER_NONE; |
682 | 97a06435 | Gerd Hoffmann | } |
683 | 97a06435 | Gerd Hoffmann | break;
|
684 | 97a06435 | Gerd Hoffmann | } |
685 | 97a06435 | Gerd Hoffmann | } |
686 | 97a06435 | Gerd Hoffmann | |
687 | 2599aece | Paolo Bonzini | static uint64_t scsi_cmd_lba(SCSICommand *cmd)
|
688 | 2ec749cb | Gerd Hoffmann | { |
689 | 2599aece | Paolo Bonzini | uint8_t *buf = cmd->buf; |
690 | 2ec749cb | Gerd Hoffmann | uint64_t lba; |
691 | 2ec749cb | Gerd Hoffmann | |
692 | 2ec749cb | Gerd Hoffmann | switch (buf[0] >> 5) { |
693 | 2ec749cb | Gerd Hoffmann | case 0: |
694 | 2ec749cb | Gerd Hoffmann | lba = (uint64_t) buf[3] | ((uint64_t) buf[2] << 8) | |
695 | 2ec749cb | Gerd Hoffmann | (((uint64_t) buf[1] & 0x1f) << 16); |
696 | 2ec749cb | Gerd Hoffmann | break;
|
697 | 2ec749cb | Gerd Hoffmann | case 1: |
698 | 2ec749cb | Gerd Hoffmann | case 2: |
699 | 2ec749cb | Gerd Hoffmann | lba = (uint64_t) buf[5] | ((uint64_t) buf[4] << 8) | |
700 | 2ec749cb | Gerd Hoffmann | ((uint64_t) buf[3] << 16) | ((uint64_t) buf[2] << 24); |
701 | 2ec749cb | Gerd Hoffmann | break;
|
702 | 2ec749cb | Gerd Hoffmann | case 4: |
703 | 2ec749cb | Gerd Hoffmann | lba = (uint64_t) buf[9] | ((uint64_t) buf[8] << 8) | |
704 | 2ec749cb | Gerd Hoffmann | ((uint64_t) buf[7] << 16) | ((uint64_t) buf[6] << 24) | |
705 | 2ec749cb | Gerd Hoffmann | ((uint64_t) buf[5] << 32) | ((uint64_t) buf[4] << 40) | |
706 | 2ec749cb | Gerd Hoffmann | ((uint64_t) buf[3] << 48) | ((uint64_t) buf[2] << 56); |
707 | 2ec749cb | Gerd Hoffmann | break;
|
708 | 2ec749cb | Gerd Hoffmann | case 5: |
709 | 2ec749cb | Gerd Hoffmann | lba = (uint64_t) buf[5] | ((uint64_t) buf[4] << 8) | |
710 | 2ec749cb | Gerd Hoffmann | ((uint64_t) buf[3] << 16) | ((uint64_t) buf[2] << 24); |
711 | 2ec749cb | Gerd Hoffmann | break;
|
712 | 2ec749cb | Gerd Hoffmann | default:
|
713 | 2ec749cb | Gerd Hoffmann | lba = -1;
|
714 | 2ec749cb | Gerd Hoffmann | |
715 | 2ec749cb | Gerd Hoffmann | } |
716 | 2ec749cb | Gerd Hoffmann | return lba;
|
717 | 2ec749cb | Gerd Hoffmann | } |
718 | 2ec749cb | Gerd Hoffmann | |
719 | afa46c46 | Paolo Bonzini | int scsi_req_parse(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
|
720 | 2ec749cb | Gerd Hoffmann | { |
721 | 2ec749cb | Gerd Hoffmann | int rc;
|
722 | 2ec749cb | Gerd Hoffmann | |
723 | afa46c46 | Paolo Bonzini | if (dev->type == TYPE_TAPE) {
|
724 | afa46c46 | Paolo Bonzini | rc = scsi_req_stream_length(cmd, dev, buf); |
725 | 2ec749cb | Gerd Hoffmann | } else {
|
726 | afa46c46 | Paolo Bonzini | rc = scsi_req_length(cmd, dev, buf); |
727 | 2ec749cb | Gerd Hoffmann | } |
728 | 2ec749cb | Gerd Hoffmann | if (rc != 0) |
729 | 2ec749cb | Gerd Hoffmann | return rc;
|
730 | 2ec749cb | Gerd Hoffmann | |
731 | afa46c46 | Paolo Bonzini | memcpy(cmd->buf, buf, cmd->len); |
732 | afa46c46 | Paolo Bonzini | scsi_cmd_xfer_mode(cmd); |
733 | afa46c46 | Paolo Bonzini | cmd->lba = scsi_cmd_lba(cmd); |
734 | 2ec749cb | Gerd Hoffmann | return 0; |
735 | 2ec749cb | Gerd Hoffmann | } |
736 | ed3a34a3 | Gerd Hoffmann | |
737 | a1f0cce2 | Hannes Reinecke | /*
|
738 | a1f0cce2 | Hannes Reinecke | * Predefined sense codes
|
739 | a1f0cce2 | Hannes Reinecke | */
|
740 | a1f0cce2 | Hannes Reinecke | |
741 | a1f0cce2 | Hannes Reinecke | /* No sense data available */
|
742 | a1f0cce2 | Hannes Reinecke | const struct SCSISense sense_code_NO_SENSE = { |
743 | a1f0cce2 | Hannes Reinecke | .key = NO_SENSE , .asc = 0x00 , .ascq = 0x00 |
744 | a1f0cce2 | Hannes Reinecke | }; |
745 | a1f0cce2 | Hannes Reinecke | |
746 | a1f0cce2 | Hannes Reinecke | /* LUN not ready, Manual intervention required */
|
747 | a1f0cce2 | Hannes Reinecke | const struct SCSISense sense_code_LUN_NOT_READY = { |
748 | a1f0cce2 | Hannes Reinecke | .key = NOT_READY, .asc = 0x04, .ascq = 0x03 |
749 | a1f0cce2 | Hannes Reinecke | }; |
750 | a1f0cce2 | Hannes Reinecke | |
751 | a1f0cce2 | Hannes Reinecke | /* LUN not ready, Medium not present */
|
752 | a1f0cce2 | Hannes Reinecke | const struct SCSISense sense_code_NO_MEDIUM = { |
753 | a1f0cce2 | Hannes Reinecke | .key = NOT_READY, .asc = 0x3a, .ascq = 0x00 |
754 | a1f0cce2 | Hannes Reinecke | }; |
755 | a1f0cce2 | Hannes Reinecke | |
756 | a1f0cce2 | Hannes Reinecke | /* Hardware error, internal target failure */
|
757 | a1f0cce2 | Hannes Reinecke | const struct SCSISense sense_code_TARGET_FAILURE = { |
758 | a1f0cce2 | Hannes Reinecke | .key = HARDWARE_ERROR, .asc = 0x44, .ascq = 0x00 |
759 | a1f0cce2 | Hannes Reinecke | }; |
760 | a1f0cce2 | Hannes Reinecke | |
761 | a1f0cce2 | Hannes Reinecke | /* Illegal request, invalid command operation code */
|
762 | a1f0cce2 | Hannes Reinecke | const struct SCSISense sense_code_INVALID_OPCODE = { |
763 | a1f0cce2 | Hannes Reinecke | .key = ILLEGAL_REQUEST, .asc = 0x20, .ascq = 0x00 |
764 | a1f0cce2 | Hannes Reinecke | }; |
765 | a1f0cce2 | Hannes Reinecke | |
766 | a1f0cce2 | Hannes Reinecke | /* Illegal request, LBA out of range */
|
767 | a1f0cce2 | Hannes Reinecke | const struct SCSISense sense_code_LBA_OUT_OF_RANGE = { |
768 | a1f0cce2 | Hannes Reinecke | .key = ILLEGAL_REQUEST, .asc = 0x21, .ascq = 0x00 |
769 | a1f0cce2 | Hannes Reinecke | }; |
770 | a1f0cce2 | Hannes Reinecke | |
771 | a1f0cce2 | Hannes Reinecke | /* Illegal request, Invalid field in CDB */
|
772 | a1f0cce2 | Hannes Reinecke | const struct SCSISense sense_code_INVALID_FIELD = { |
773 | a1f0cce2 | Hannes Reinecke | .key = ILLEGAL_REQUEST, .asc = 0x24, .ascq = 0x00 |
774 | a1f0cce2 | Hannes Reinecke | }; |
775 | a1f0cce2 | Hannes Reinecke | |
776 | a1f0cce2 | Hannes Reinecke | /* Illegal request, LUN not supported */
|
777 | a1f0cce2 | Hannes Reinecke | const struct SCSISense sense_code_LUN_NOT_SUPPORTED = { |
778 | a1f0cce2 | Hannes Reinecke | .key = ILLEGAL_REQUEST, .asc = 0x25, .ascq = 0x00 |
779 | a1f0cce2 | Hannes Reinecke | }; |
780 | a1f0cce2 | Hannes Reinecke | |
781 | a872a304 | Paolo Bonzini | /* Illegal request, Saving parameters not supported */
|
782 | a872a304 | Paolo Bonzini | const struct SCSISense sense_code_SAVING_PARAMS_NOT_SUPPORTED = { |
783 | a872a304 | Paolo Bonzini | .key = ILLEGAL_REQUEST, .asc = 0x39, .ascq = 0x00 |
784 | a872a304 | Paolo Bonzini | }; |
785 | a872a304 | Paolo Bonzini | |
786 | a872a304 | Paolo Bonzini | /* Illegal request, Incompatible medium installed */
|
787 | a872a304 | Paolo Bonzini | const struct SCSISense sense_code_INCOMPATIBLE_MEDIUM = { |
788 | a872a304 | Paolo Bonzini | .key = ILLEGAL_REQUEST, .asc = 0x30, .ascq = 0x00 |
789 | a872a304 | Paolo Bonzini | }; |
790 | a872a304 | Paolo Bonzini | |
791 | a1f0cce2 | Hannes Reinecke | /* Command aborted, I/O process terminated */
|
792 | a1f0cce2 | Hannes Reinecke | const struct SCSISense sense_code_IO_ERROR = { |
793 | a1f0cce2 | Hannes Reinecke | .key = ABORTED_COMMAND, .asc = 0x00, .ascq = 0x06 |
794 | a1f0cce2 | Hannes Reinecke | }; |
795 | a1f0cce2 | Hannes Reinecke | |
796 | a1f0cce2 | Hannes Reinecke | /* Command aborted, I_T Nexus loss occurred */
|
797 | a1f0cce2 | Hannes Reinecke | const struct SCSISense sense_code_I_T_NEXUS_LOSS = { |
798 | a1f0cce2 | Hannes Reinecke | .key = ABORTED_COMMAND, .asc = 0x29, .ascq = 0x07 |
799 | a1f0cce2 | Hannes Reinecke | }; |
800 | a1f0cce2 | Hannes Reinecke | |
801 | a1f0cce2 | Hannes Reinecke | /* Command aborted, Logical Unit failure */
|
802 | a1f0cce2 | Hannes Reinecke | const struct SCSISense sense_code_LUN_FAILURE = { |
803 | a1f0cce2 | Hannes Reinecke | .key = ABORTED_COMMAND, .asc = 0x3e, .ascq = 0x01 |
804 | a1f0cce2 | Hannes Reinecke | }; |
805 | a1f0cce2 | Hannes Reinecke | |
806 | a872a304 | Paolo Bonzini | /* Unit attention, Power on, reset or bus device reset occurred */
|
807 | a872a304 | Paolo Bonzini | const struct SCSISense sense_code_RESET = { |
808 | a872a304 | Paolo Bonzini | .key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x00 |
809 | a872a304 | Paolo Bonzini | }; |
810 | a872a304 | Paolo Bonzini | |
811 | a872a304 | Paolo Bonzini | /* Unit attention, Medium may have changed */
|
812 | a872a304 | Paolo Bonzini | const struct SCSISense sense_code_MEDIUM_CHANGED = { |
813 | a872a304 | Paolo Bonzini | .key = UNIT_ATTENTION, .asc = 0x28, .ascq = 0x00 |
814 | a872a304 | Paolo Bonzini | }; |
815 | a872a304 | Paolo Bonzini | |
816 | a872a304 | Paolo Bonzini | /* Unit attention, Reported LUNs data has changed */
|
817 | a872a304 | Paolo Bonzini | const struct SCSISense sense_code_REPORTED_LUNS_CHANGED = { |
818 | a872a304 | Paolo Bonzini | .key = UNIT_ATTENTION, .asc = 0x3f, .ascq = 0x0e |
819 | a872a304 | Paolo Bonzini | }; |
820 | a872a304 | Paolo Bonzini | |
821 | a872a304 | Paolo Bonzini | /* Unit attention, Device internal reset */
|
822 | a872a304 | Paolo Bonzini | const struct SCSISense sense_code_DEVICE_INTERNAL_RESET = { |
823 | a872a304 | Paolo Bonzini | .key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x04 |
824 | a872a304 | Paolo Bonzini | }; |
825 | a872a304 | Paolo Bonzini | |
826 | a1f0cce2 | Hannes Reinecke | /*
|
827 | a1f0cce2 | Hannes Reinecke | * scsi_build_sense
|
828 | a1f0cce2 | Hannes Reinecke | *
|
829 | b45ef674 | Paolo Bonzini | * Convert between fixed and descriptor sense buffers
|
830 | a1f0cce2 | Hannes Reinecke | */
|
831 | b45ef674 | Paolo Bonzini | int scsi_build_sense(uint8_t *in_buf, int in_len, |
832 | b45ef674 | Paolo Bonzini | uint8_t *buf, int len, bool fixed) |
833 | a1f0cce2 | Hannes Reinecke | { |
834 | b45ef674 | Paolo Bonzini | bool fixed_in;
|
835 | b45ef674 | Paolo Bonzini | SCSISense sense; |
836 | a1f0cce2 | Hannes Reinecke | if (!fixed && len < 8) { |
837 | a1f0cce2 | Hannes Reinecke | return 0; |
838 | a1f0cce2 | Hannes Reinecke | } |
839 | a1f0cce2 | Hannes Reinecke | |
840 | b45ef674 | Paolo Bonzini | if (in_len == 0) { |
841 | b45ef674 | Paolo Bonzini | sense.key = NO_SENSE; |
842 | b45ef674 | Paolo Bonzini | sense.asc = 0;
|
843 | b45ef674 | Paolo Bonzini | sense.ascq = 0;
|
844 | b45ef674 | Paolo Bonzini | } else {
|
845 | b45ef674 | Paolo Bonzini | fixed_in = (in_buf[0] & 2) == 0; |
846 | b45ef674 | Paolo Bonzini | |
847 | b45ef674 | Paolo Bonzini | if (fixed == fixed_in) {
|
848 | b45ef674 | Paolo Bonzini | memcpy(buf, in_buf, MIN(len, in_len)); |
849 | b45ef674 | Paolo Bonzini | return MIN(len, in_len);
|
850 | b45ef674 | Paolo Bonzini | } |
851 | b45ef674 | Paolo Bonzini | |
852 | b45ef674 | Paolo Bonzini | if (fixed_in) {
|
853 | b45ef674 | Paolo Bonzini | sense.key = in_buf[2];
|
854 | b45ef674 | Paolo Bonzini | sense.asc = in_buf[12];
|
855 | b45ef674 | Paolo Bonzini | sense.ascq = in_buf[13];
|
856 | b45ef674 | Paolo Bonzini | } else {
|
857 | b45ef674 | Paolo Bonzini | sense.key = in_buf[1];
|
858 | b45ef674 | Paolo Bonzini | sense.asc = in_buf[2];
|
859 | b45ef674 | Paolo Bonzini | sense.ascq = in_buf[3];
|
860 | b45ef674 | Paolo Bonzini | } |
861 | b45ef674 | Paolo Bonzini | } |
862 | b45ef674 | Paolo Bonzini | |
863 | a1f0cce2 | Hannes Reinecke | memset(buf, 0, len);
|
864 | a1f0cce2 | Hannes Reinecke | if (fixed) {
|
865 | a1f0cce2 | Hannes Reinecke | /* Return fixed format sense buffer */
|
866 | a1f0cce2 | Hannes Reinecke | buf[0] = 0xf0; |
867 | a1f0cce2 | Hannes Reinecke | buf[2] = sense.key;
|
868 | a1f0cce2 | Hannes Reinecke | buf[7] = 7; |
869 | a1f0cce2 | Hannes Reinecke | buf[12] = sense.asc;
|
870 | a1f0cce2 | Hannes Reinecke | buf[13] = sense.ascq;
|
871 | a1f0cce2 | Hannes Reinecke | return MIN(len, 18); |
872 | a1f0cce2 | Hannes Reinecke | } else {
|
873 | a1f0cce2 | Hannes Reinecke | /* Return descriptor format sense buffer */
|
874 | a1f0cce2 | Hannes Reinecke | buf[0] = 0x72; |
875 | a1f0cce2 | Hannes Reinecke | buf[1] = sense.key;
|
876 | a1f0cce2 | Hannes Reinecke | buf[2] = sense.asc;
|
877 | a1f0cce2 | Hannes Reinecke | buf[3] = sense.ascq;
|
878 | a1f0cce2 | Hannes Reinecke | return 8; |
879 | a1f0cce2 | Hannes Reinecke | } |
880 | a1f0cce2 | Hannes Reinecke | } |
881 | a1f0cce2 | Hannes Reinecke | |
882 | ec766865 | Gerd Hoffmann | static const char *scsi_command_name(uint8_t cmd) |
883 | ec766865 | Gerd Hoffmann | { |
884 | ec766865 | Gerd Hoffmann | static const char *names[] = { |
885 | ec766865 | Gerd Hoffmann | [ TEST_UNIT_READY ] = "TEST_UNIT_READY",
|
886 | 5e30a07d | Hannes Reinecke | [ REWIND ] = "REWIND",
|
887 | ec766865 | Gerd Hoffmann | [ REQUEST_SENSE ] = "REQUEST_SENSE",
|
888 | ec766865 | Gerd Hoffmann | [ FORMAT_UNIT ] = "FORMAT_UNIT",
|
889 | ec766865 | Gerd Hoffmann | [ READ_BLOCK_LIMITS ] = "READ_BLOCK_LIMITS",
|
890 | ec766865 | Gerd Hoffmann | [ REASSIGN_BLOCKS ] = "REASSIGN_BLOCKS",
|
891 | ec766865 | Gerd Hoffmann | [ READ_6 ] = "READ_6",
|
892 | ec766865 | Gerd Hoffmann | [ WRITE_6 ] = "WRITE_6",
|
893 | ec766865 | Gerd Hoffmann | [ SEEK_6 ] = "SEEK_6",
|
894 | ec766865 | Gerd Hoffmann | [ READ_REVERSE ] = "READ_REVERSE",
|
895 | ec766865 | Gerd Hoffmann | [ WRITE_FILEMARKS ] = "WRITE_FILEMARKS",
|
896 | ec766865 | Gerd Hoffmann | [ SPACE ] = "SPACE",
|
897 | ec766865 | Gerd Hoffmann | [ INQUIRY ] = "INQUIRY",
|
898 | ec766865 | Gerd Hoffmann | [ RECOVER_BUFFERED_DATA ] = "RECOVER_BUFFERED_DATA",
|
899 | c7126d5b | Nicholas Bellinger | [ MAINTENANCE_IN ] = "MAINTENANCE_IN",
|
900 | c7126d5b | Nicholas Bellinger | [ MAINTENANCE_OUT ] = "MAINTENANCE_OUT",
|
901 | ec766865 | Gerd Hoffmann | [ MODE_SELECT ] = "MODE_SELECT",
|
902 | ec766865 | Gerd Hoffmann | [ RESERVE ] = "RESERVE",
|
903 | ec766865 | Gerd Hoffmann | [ RELEASE ] = "RELEASE",
|
904 | ec766865 | Gerd Hoffmann | [ COPY ] = "COPY",
|
905 | ec766865 | Gerd Hoffmann | [ ERASE ] = "ERASE",
|
906 | ec766865 | Gerd Hoffmann | [ MODE_SENSE ] = "MODE_SENSE",
|
907 | ec766865 | Gerd Hoffmann | [ START_STOP ] = "START_STOP",
|
908 | ec766865 | Gerd Hoffmann | [ RECEIVE_DIAGNOSTIC ] = "RECEIVE_DIAGNOSTIC",
|
909 | ec766865 | Gerd Hoffmann | [ SEND_DIAGNOSTIC ] = "SEND_DIAGNOSTIC",
|
910 | ec766865 | Gerd Hoffmann | [ ALLOW_MEDIUM_REMOVAL ] = "ALLOW_MEDIUM_REMOVAL",
|
911 | 5e30a07d | Hannes Reinecke | [ READ_CAPACITY_10 ] = "READ_CAPACITY_10",
|
912 | ec766865 | Gerd Hoffmann | [ READ_10 ] = "READ_10",
|
913 | ec766865 | Gerd Hoffmann | [ WRITE_10 ] = "WRITE_10",
|
914 | ec766865 | Gerd Hoffmann | [ SEEK_10 ] = "SEEK_10",
|
915 | 5e30a07d | Hannes Reinecke | [ WRITE_VERIFY_10 ] = "WRITE_VERIFY_10",
|
916 | 5e30a07d | Hannes Reinecke | [ VERIFY_10 ] = "VERIFY_10",
|
917 | ec766865 | Gerd Hoffmann | [ SEARCH_HIGH ] = "SEARCH_HIGH",
|
918 | ec766865 | Gerd Hoffmann | [ SEARCH_EQUAL ] = "SEARCH_EQUAL",
|
919 | ec766865 | Gerd Hoffmann | [ SEARCH_LOW ] = "SEARCH_LOW",
|
920 | ec766865 | Gerd Hoffmann | [ SET_LIMITS ] = "SET_LIMITS",
|
921 | ec766865 | Gerd Hoffmann | [ PRE_FETCH ] = "PRE_FETCH",
|
922 | 545557d4 | Blue Swirl | /* READ_POSITION and PRE_FETCH use the same operation code */
|
923 | ec766865 | Gerd Hoffmann | [ SYNCHRONIZE_CACHE ] = "SYNCHRONIZE_CACHE",
|
924 | ec766865 | Gerd Hoffmann | [ LOCK_UNLOCK_CACHE ] = "LOCK_UNLOCK_CACHE",
|
925 | ec766865 | Gerd Hoffmann | [ READ_DEFECT_DATA ] = "READ_DEFECT_DATA",
|
926 | ec766865 | Gerd Hoffmann | [ MEDIUM_SCAN ] = "MEDIUM_SCAN",
|
927 | ec766865 | Gerd Hoffmann | [ COMPARE ] = "COMPARE",
|
928 | ec766865 | Gerd Hoffmann | [ COPY_VERIFY ] = "COPY_VERIFY",
|
929 | ec766865 | Gerd Hoffmann | [ WRITE_BUFFER ] = "WRITE_BUFFER",
|
930 | ec766865 | Gerd Hoffmann | [ READ_BUFFER ] = "READ_BUFFER",
|
931 | ec766865 | Gerd Hoffmann | [ UPDATE_BLOCK ] = "UPDATE_BLOCK",
|
932 | 5e30a07d | Hannes Reinecke | [ READ_LONG_10 ] = "READ_LONG_10",
|
933 | 5e30a07d | Hannes Reinecke | [ WRITE_LONG_10 ] = "WRITE_LONG_10",
|
934 | ec766865 | Gerd Hoffmann | [ CHANGE_DEFINITION ] = "CHANGE_DEFINITION",
|
935 | 5e30a07d | Hannes Reinecke | [ WRITE_SAME_10 ] = "WRITE_SAME_10",
|
936 | 5e30a07d | Hannes Reinecke | [ UNMAP ] = "UNMAP",
|
937 | ec766865 | Gerd Hoffmann | [ READ_TOC ] = "READ_TOC",
|
938 | 5e30a07d | Hannes Reinecke | [ REPORT_DENSITY_SUPPORT ] = "REPORT_DENSITY_SUPPORT",
|
939 | 5e30a07d | Hannes Reinecke | [ GET_CONFIGURATION ] = "GET_CONFIGURATION",
|
940 | ec766865 | Gerd Hoffmann | [ LOG_SELECT ] = "LOG_SELECT",
|
941 | ec766865 | Gerd Hoffmann | [ LOG_SENSE ] = "LOG_SENSE",
|
942 | ec766865 | Gerd Hoffmann | [ MODE_SELECT_10 ] = "MODE_SELECT_10",
|
943 | ec766865 | Gerd Hoffmann | [ RESERVE_10 ] = "RESERVE_10",
|
944 | ec766865 | Gerd Hoffmann | [ RELEASE_10 ] = "RELEASE_10",
|
945 | ec766865 | Gerd Hoffmann | [ MODE_SENSE_10 ] = "MODE_SENSE_10",
|
946 | ec766865 | Gerd Hoffmann | [ PERSISTENT_RESERVE_IN ] = "PERSISTENT_RESERVE_IN",
|
947 | ec766865 | Gerd Hoffmann | [ PERSISTENT_RESERVE_OUT ] = "PERSISTENT_RESERVE_OUT",
|
948 | 5e30a07d | Hannes Reinecke | [ WRITE_FILEMARKS_16 ] = "WRITE_FILEMARKS_16",
|
949 | 5e30a07d | Hannes Reinecke | [ EXTENDED_COPY ] = "EXTENDED_COPY",
|
950 | 5e30a07d | Hannes Reinecke | [ ATA_PASSTHROUGH ] = "ATA_PASSTHROUGH",
|
951 | 5e30a07d | Hannes Reinecke | [ ACCESS_CONTROL_IN ] = "ACCESS_CONTROL_IN",
|
952 | 5e30a07d | Hannes Reinecke | [ ACCESS_CONTROL_OUT ] = "ACCESS_CONTROL_OUT",
|
953 | 5e30a07d | Hannes Reinecke | [ READ_16 ] = "READ_16",
|
954 | 5e30a07d | Hannes Reinecke | [ COMPARE_AND_WRITE ] = "COMPARE_AND_WRITE",
|
955 | 5e30a07d | Hannes Reinecke | [ WRITE_16 ] = "WRITE_16",
|
956 | 5e30a07d | Hannes Reinecke | [ WRITE_VERIFY_16 ] = "WRITE_VERIFY_16",
|
957 | 5e30a07d | Hannes Reinecke | [ VERIFY_16 ] = "VERIFY_16",
|
958 | 5e30a07d | Hannes Reinecke | [ SYNCHRONIZE_CACHE_16 ] = "SYNCHRONIZE_CACHE_16",
|
959 | 5e30a07d | Hannes Reinecke | [ LOCATE_16 ] = "LOCATE_16",
|
960 | 5e30a07d | Hannes Reinecke | [ WRITE_SAME_16 ] = "WRITE_SAME_16",
|
961 | 5e30a07d | Hannes Reinecke | [ ERASE_16 ] = "ERASE_16",
|
962 | 5e30a07d | Hannes Reinecke | [ SERVICE_ACTION_IN ] = "SERVICE_ACTION_IN",
|
963 | 5e30a07d | Hannes Reinecke | [ WRITE_LONG_16 ] = "WRITE_LONG_16",
|
964 | 5e30a07d | Hannes Reinecke | [ REPORT_LUNS ] = "REPORT_LUNS",
|
965 | 5e30a07d | Hannes Reinecke | [ BLANK ] = "BLANK",
|
966 | 5e30a07d | Hannes Reinecke | [ MAINTENANCE_IN ] = "MAINTENANCE_IN",
|
967 | 5e30a07d | Hannes Reinecke | [ MAINTENANCE_OUT ] = "MAINTENANCE_OUT",
|
968 | ec766865 | Gerd Hoffmann | [ MOVE_MEDIUM ] = "MOVE_MEDIUM",
|
969 | 5e30a07d | Hannes Reinecke | [ LOAD_UNLOAD ] = "LOAD_UNLOAD",
|
970 | ec766865 | Gerd Hoffmann | [ READ_12 ] = "READ_12",
|
971 | ec766865 | Gerd Hoffmann | [ WRITE_12 ] = "WRITE_12",
|
972 | ec766865 | Gerd Hoffmann | [ WRITE_VERIFY_12 ] = "WRITE_VERIFY_12",
|
973 | 5e30a07d | Hannes Reinecke | [ VERIFY_12 ] = "VERIFY_12",
|
974 | ec766865 | Gerd Hoffmann | [ SEARCH_HIGH_12 ] = "SEARCH_HIGH_12",
|
975 | ec766865 | Gerd Hoffmann | [ SEARCH_EQUAL_12 ] = "SEARCH_EQUAL_12",
|
976 | ec766865 | Gerd Hoffmann | [ SEARCH_LOW_12 ] = "SEARCH_LOW_12",
|
977 | ec766865 | Gerd Hoffmann | [ READ_ELEMENT_STATUS ] = "READ_ELEMENT_STATUS",
|
978 | ec766865 | Gerd Hoffmann | [ SEND_VOLUME_TAG ] = "SEND_VOLUME_TAG",
|
979 | 5e30a07d | Hannes Reinecke | [ READ_DEFECT_DATA_12 ] = "READ_DEFECT_DATA_12",
|
980 | ec766865 | Gerd Hoffmann | [ SET_CD_SPEED ] = "SET_CD_SPEED",
|
981 | ec766865 | Gerd Hoffmann | }; |
982 | ec766865 | Gerd Hoffmann | |
983 | ec766865 | Gerd Hoffmann | if (cmd >= ARRAY_SIZE(names) || names[cmd] == NULL) |
984 | ec766865 | Gerd Hoffmann | return "*UNKNOWN*"; |
985 | ec766865 | Gerd Hoffmann | return names[cmd];
|
986 | ec766865 | Gerd Hoffmann | } |
987 | ec766865 | Gerd Hoffmann | |
988 | ad2d30f7 | Paolo Bonzini | SCSIRequest *scsi_req_ref(SCSIRequest *req) |
989 | ad2d30f7 | Paolo Bonzini | { |
990 | ad2d30f7 | Paolo Bonzini | req->refcount++; |
991 | ad2d30f7 | Paolo Bonzini | return req;
|
992 | ad2d30f7 | Paolo Bonzini | } |
993 | ad2d30f7 | Paolo Bonzini | |
994 | ad2d30f7 | Paolo Bonzini | void scsi_req_unref(SCSIRequest *req)
|
995 | ad2d30f7 | Paolo Bonzini | { |
996 | ad2d30f7 | Paolo Bonzini | if (--req->refcount == 0) { |
997 | 12010e7b | Paolo Bonzini | if (req->ops->free_req) {
|
998 | 12010e7b | Paolo Bonzini | req->ops->free_req(req); |
999 | ad2d30f7 | Paolo Bonzini | } |
1000 | ad2d30f7 | Paolo Bonzini | qemu_free(req); |
1001 | ad2d30f7 | Paolo Bonzini | } |
1002 | ad2d30f7 | Paolo Bonzini | } |
1003 | ad2d30f7 | Paolo Bonzini | |
1004 | ad3376cc | Paolo Bonzini | /* Tell the device that we finished processing this chunk of I/O. It
|
1005 | ad3376cc | Paolo Bonzini | will start the next chunk or complete the command. */
|
1006 | ad3376cc | Paolo Bonzini | void scsi_req_continue(SCSIRequest *req)
|
1007 | ad3376cc | Paolo Bonzini | { |
1008 | ad3376cc | Paolo Bonzini | trace_scsi_req_continue(req->dev->id, req->lun, req->tag); |
1009 | ad3376cc | Paolo Bonzini | if (req->cmd.mode == SCSI_XFER_TO_DEV) {
|
1010 | 12010e7b | Paolo Bonzini | req->ops->write_data(req); |
1011 | ad3376cc | Paolo Bonzini | } else {
|
1012 | 12010e7b | Paolo Bonzini | req->ops->read_data(req); |
1013 | ad3376cc | Paolo Bonzini | } |
1014 | ad3376cc | Paolo Bonzini | } |
1015 | ad3376cc | Paolo Bonzini | |
1016 | ab9adc88 | Paolo Bonzini | /* Called by the devices when data is ready for the HBA. The HBA should
|
1017 | ab9adc88 | Paolo Bonzini | start a DMA operation to read or fill the device's data buffer.
|
1018 | ad3376cc | Paolo Bonzini | Once it completes, calling scsi_req_continue will restart I/O. */
|
1019 | ab9adc88 | Paolo Bonzini | void scsi_req_data(SCSIRequest *req, int len) |
1020 | ab9adc88 | Paolo Bonzini | { |
1021 | ab9adc88 | Paolo Bonzini | trace_scsi_req_data(req->dev->id, req->lun, req->tag, len); |
1022 | c6df7102 | Paolo Bonzini | req->bus->ops->transfer_data(req, len); |
1023 | ab9adc88 | Paolo Bonzini | } |
1024 | ab9adc88 | Paolo Bonzini | |
1025 | ec766865 | Gerd Hoffmann | void scsi_req_print(SCSIRequest *req)
|
1026 | ec766865 | Gerd Hoffmann | { |
1027 | ec766865 | Gerd Hoffmann | FILE *fp = stderr; |
1028 | ec766865 | Gerd Hoffmann | int i;
|
1029 | ec766865 | Gerd Hoffmann | |
1030 | ec766865 | Gerd Hoffmann | fprintf(fp, "[%s id=%d] %s",
|
1031 | ec766865 | Gerd Hoffmann | req->dev->qdev.parent_bus->name, |
1032 | ec766865 | Gerd Hoffmann | req->dev->id, |
1033 | ec766865 | Gerd Hoffmann | scsi_command_name(req->cmd.buf[0]));
|
1034 | ec766865 | Gerd Hoffmann | for (i = 1; i < req->cmd.len; i++) { |
1035 | ec766865 | Gerd Hoffmann | fprintf(fp, " 0x%02x", req->cmd.buf[i]);
|
1036 | ec766865 | Gerd Hoffmann | } |
1037 | ec766865 | Gerd Hoffmann | switch (req->cmd.mode) {
|
1038 | ec766865 | Gerd Hoffmann | case SCSI_XFER_NONE:
|
1039 | ec766865 | Gerd Hoffmann | fprintf(fp, " - none\n");
|
1040 | ec766865 | Gerd Hoffmann | break;
|
1041 | ec766865 | Gerd Hoffmann | case SCSI_XFER_FROM_DEV:
|
1042 | ec766865 | Gerd Hoffmann | fprintf(fp, " - from-dev len=%zd\n", req->cmd.xfer);
|
1043 | ec766865 | Gerd Hoffmann | break;
|
1044 | ec766865 | Gerd Hoffmann | case SCSI_XFER_TO_DEV:
|
1045 | ec766865 | Gerd Hoffmann | fprintf(fp, " - to-dev len=%zd\n", req->cmd.xfer);
|
1046 | ec766865 | Gerd Hoffmann | break;
|
1047 | ec766865 | Gerd Hoffmann | default:
|
1048 | ec766865 | Gerd Hoffmann | fprintf(fp, " - Oops\n");
|
1049 | ec766865 | Gerd Hoffmann | break;
|
1050 | ec766865 | Gerd Hoffmann | } |
1051 | ec766865 | Gerd Hoffmann | } |
1052 | ec766865 | Gerd Hoffmann | |
1053 | 682a9b21 | Paolo Bonzini | void scsi_req_complete(SCSIRequest *req, int status) |
1054 | ed3a34a3 | Gerd Hoffmann | { |
1055 | 682a9b21 | Paolo Bonzini | assert(req->status == -1);
|
1056 | 682a9b21 | Paolo Bonzini | req->status = status; |
1057 | b45ef674 | Paolo Bonzini | |
1058 | b45ef674 | Paolo Bonzini | assert(req->sense_len < sizeof(req->sense));
|
1059 | b45ef674 | Paolo Bonzini | if (status == GOOD) {
|
1060 | b45ef674 | Paolo Bonzini | req->sense_len = 0;
|
1061 | b45ef674 | Paolo Bonzini | } |
1062 | b45ef674 | Paolo Bonzini | |
1063 | b45ef674 | Paolo Bonzini | if (req->sense_len) {
|
1064 | b45ef674 | Paolo Bonzini | memcpy(req->dev->sense, req->sense, req->sense_len); |
1065 | b45ef674 | Paolo Bonzini | } |
1066 | b45ef674 | Paolo Bonzini | req->dev->sense_len = req->sense_len; |
1067 | b45ef674 | Paolo Bonzini | |
1068 | 6dc06f08 | Paolo Bonzini | /*
|
1069 | 6dc06f08 | Paolo Bonzini | * Unit attention state is now stored in the device's sense buffer
|
1070 | 6dc06f08 | Paolo Bonzini | * if the HBA didn't do autosense. Clear the pending unit attention
|
1071 | 6dc06f08 | Paolo Bonzini | * flags.
|
1072 | 6dc06f08 | Paolo Bonzini | */
|
1073 | 6dc06f08 | Paolo Bonzini | scsi_clear_unit_attention(req); |
1074 | 6dc06f08 | Paolo Bonzini | |
1075 | ad2d30f7 | Paolo Bonzini | scsi_req_ref(req); |
1076 | e8637c90 | Jan Kiszka | scsi_req_dequeue(req); |
1077 | c6df7102 | Paolo Bonzini | req->bus->ops->complete(req, req->status); |
1078 | ad2d30f7 | Paolo Bonzini | scsi_req_unref(req); |
1079 | ed3a34a3 | Gerd Hoffmann | } |
1080 | db07c0f8 | Gleb Natapov | |
1081 | 94d3f98a | Paolo Bonzini | void scsi_req_cancel(SCSIRequest *req)
|
1082 | 94d3f98a | Paolo Bonzini | { |
1083 | 12010e7b | Paolo Bonzini | if (req->ops->cancel_io) {
|
1084 | 12010e7b | Paolo Bonzini | req->ops->cancel_io(req); |
1085 | 94d3f98a | Paolo Bonzini | } |
1086 | 94d3f98a | Paolo Bonzini | scsi_req_ref(req); |
1087 | 94d3f98a | Paolo Bonzini | scsi_req_dequeue(req); |
1088 | 94d3f98a | Paolo Bonzini | if (req->bus->ops->cancel) {
|
1089 | 94d3f98a | Paolo Bonzini | req->bus->ops->cancel(req); |
1090 | 94d3f98a | Paolo Bonzini | } |
1091 | 94d3f98a | Paolo Bonzini | scsi_req_unref(req); |
1092 | 94d3f98a | Paolo Bonzini | } |
1093 | 94d3f98a | Paolo Bonzini | |
1094 | 19d110ab | Paolo Bonzini | void scsi_req_abort(SCSIRequest *req, int status) |
1095 | 19d110ab | Paolo Bonzini | { |
1096 | 12010e7b | Paolo Bonzini | if (req->ops->cancel_io) {
|
1097 | 12010e7b | Paolo Bonzini | req->ops->cancel_io(req); |
1098 | 19d110ab | Paolo Bonzini | } |
1099 | 682a9b21 | Paolo Bonzini | scsi_req_complete(req, status); |
1100 | 19d110ab | Paolo Bonzini | } |
1101 | 19d110ab | Paolo Bonzini | |
1102 | c557e889 | Paolo Bonzini | void scsi_device_purge_requests(SCSIDevice *sdev)
|
1103 | c557e889 | Paolo Bonzini | { |
1104 | c557e889 | Paolo Bonzini | SCSIRequest *req; |
1105 | c557e889 | Paolo Bonzini | |
1106 | c557e889 | Paolo Bonzini | while (!QTAILQ_EMPTY(&sdev->requests)) {
|
1107 | c557e889 | Paolo Bonzini | req = QTAILQ_FIRST(&sdev->requests); |
1108 | 94d3f98a | Paolo Bonzini | scsi_req_cancel(req); |
1109 | c557e889 | Paolo Bonzini | } |
1110 | c557e889 | Paolo Bonzini | } |
1111 | c557e889 | Paolo Bonzini | |
1112 | db07c0f8 | Gleb Natapov | static char *scsibus_get_fw_dev_path(DeviceState *dev) |
1113 | db07c0f8 | Gleb Natapov | { |
1114 | 645a8ad6 | Zhi Yong Wu | SCSIDevice *d = DO_UPCAST(SCSIDevice, qdev, dev); |
1115 | db07c0f8 | Gleb Natapov | SCSIBus *bus = scsi_bus_from_device(d); |
1116 | db07c0f8 | Gleb Natapov | char path[100]; |
1117 | db07c0f8 | Gleb Natapov | int i;
|
1118 | db07c0f8 | Gleb Natapov | |
1119 | db07c0f8 | Gleb Natapov | for (i = 0; i < bus->ndev; i++) { |
1120 | db07c0f8 | Gleb Natapov | if (bus->devs[i] == d) {
|
1121 | db07c0f8 | Gleb Natapov | break;
|
1122 | db07c0f8 | Gleb Natapov | } |
1123 | db07c0f8 | Gleb Natapov | } |
1124 | db07c0f8 | Gleb Natapov | |
1125 | db07c0f8 | Gleb Natapov | assert(i != bus->ndev); |
1126 | db07c0f8 | Gleb Natapov | |
1127 | db07c0f8 | Gleb Natapov | snprintf(path, sizeof(path), "%s@%x", qdev_fw_name(dev), i); |
1128 | db07c0f8 | Gleb Natapov | |
1129 | db07c0f8 | Gleb Natapov | return strdup(path);
|
1130 | db07c0f8 | Gleb Natapov | } |