]> git.proxmox.com Git - qemu.git/blame - hw/scsi-disk.c
Rearrange SCSI disk emulation code.
[qemu.git] / hw / scsi-disk.c
CommitLineData
2e5d83bb
PB
1/*
2 * SCSI Device emulation
3 *
4 * Copyright (c) 2006 CodeSourcery.
5 * Based on code by Fabrice Bellard
6 *
7 * Written by Paul Brook
8 *
9 * This code is licenced under the LGPL.
10 */
11
12//#define DEBUG_SCSI
13
14#ifdef DEBUG_SCSI
15#define DPRINTF(fmt, args...) \
16do { printf("scsi-disk: " fmt , ##args); } while (0)
17#else
18#define DPRINTF(fmt, args...) do {} while(0)
19#endif
20
21#define BADF(fmt, args...) \
22do { fprintf(stderr, "scsi-disk: " fmt , ##args); } while (0)
23
24#include "vl.h"
25
26#define SENSE_NO_SENSE 0
27#define SENSE_ILLEGAL_REQUEST 5
28
29struct SCSIDevice
30{
31 int command;
32 uint32_t tag;
33 BlockDriverState *bdrv;
34 int sector_size;
35 /* When transfering data buf_pos and buf_len contain a partially
36 transferred block of data (or response to a command), and
37 sector/sector_count identify any remaining sectors. */
38 /* ??? We should probably keep track of whether the data trasfer is
39 a read or a write. Currently we rely on the host getting it right. */
40 int sector;
41 int sector_count;
42 int buf_pos;
43 int buf_len;
44 int sense;
45 char buf[2048];
46 scsi_completionfn completion;
47 void *opaque;
48};
49
50static void scsi_command_complete(SCSIDevice *s, int sense)
51{
52 s->sense = sense;
53 s->completion(s->opaque, s->tag, sense != SENSE_NO_SENSE);
54}
55
56/* Read data from a scsi device. Returns nonzero on failure. */
57int scsi_read_data(SCSIDevice *s, uint8_t *data, uint32_t len)
58{
59 uint32_t n;
60
61 DPRINTF("Read %d (%d/%d)\n", len, s->buf_len, s->sector_count);
62 if (s->buf_len == 0 && s->sector_count == 0)
63 return 1;
64
65 if (s->buf_len) {
66 n = s->buf_len;
67 if (n > len)
68 n = len;
69 memcpy(data, s->buf + s->buf_pos, n);
70 s->buf_pos += n;
71 s->buf_len -= n;
72 data += n;
73 len -= n;
74 if (s->buf_len == 0)
75 s->buf_pos = 0;
76 }
77
78 n = len / s->sector_size;
79 if (n > s->sector_count)
80 n = s->sector_count;
81
82 if (n != 0) {
83 bdrv_read(s->bdrv, s->sector, data, n);
84 data += n * s->sector_size;
85 len -= n * s->sector_size;
86 s->sector += n;
87 s->sector_count -= n;
88 }
89
90 if (len && s->sector_count) {
91 bdrv_read(s->bdrv, s->sector, s->buf, 1);
92 s->sector++;
93 s->sector_count--;
94 s->buf_pos = 0;
95 s->buf_len = s->sector_size;
96 /* Recurse to complete the partial read. */
97 return scsi_read_data(s, data, len);
98 }
99
100 if (len != 0)
101 return 1;
102
103 if (s->buf_len == 0 && s->sector_count == 0)
104 scsi_command_complete(s, SENSE_NO_SENSE);
105
106 return 0;
107}
108
109/* Read data to a scsi device. Returns nonzero on failure. */
110int scsi_write_data(SCSIDevice *s, uint8_t *data, uint32_t len)
111{
112 uint32_t n;
113
114 DPRINTF("Write %d (%d/%d)\n", len, s->buf_len, s->sector_count);
115 if (s->buf_pos != 0) {
116 BADF("Bad state on write\n");
117 return 1;
118 }
119
120 if (s->sector_count == 0)
121 return 1;
122
123 if (s->buf_len != 0 || len < s->sector_size) {
124 n = s->sector_size - s->buf_len;
125 if (n > len)
126 n = len;
127
128 memcpy(s->buf + s->buf_len, data, n);
129 data += n;
130 s->buf_len += n;
131 len -= n;
132 if (s->buf_len == s->sector_size) {
133 /* A full sector has been accumulated. Write it to disk. */
134 bdrv_write(s->bdrv, s->sector, s->buf, 1);
135 s->buf_len = 0;
136 s->sector++;
137 s->sector_count--;
138 }
139 }
140
141 n = len / s->sector_size;
142 if (n > s->sector_count)
143 n = s->sector_count;
144
145 if (n != 0) {
146 bdrv_write(s->bdrv, s->sector, data, n);
147 data += n * s->sector_size;
148 len -= n * s->sector_size;
149 s->sector += n;
150 s->sector_count -= n;
151 }
152
153 if (len >= s->sector_size)
154 return 1;
155
156 if (len && s->sector_count) {
157 /* Recurse to complete the partial write. */
158 return scsi_write_data(s, data, len);
159 }
160
161 if (len != 0)
162 return 1;
163
164 if (s->sector_count == 0)
165 scsi_command_complete(s, SENSE_NO_SENSE);
166
167 return 0;
168}
169
170/* Execute a scsi command. Returns the length of the data expected by the
171 command. This will be Positive for data transfers from the device
172 (eg. disk reads), negative for transfers to the device (eg. disk writes),
173 and zero if the command does not transfer any data. */
174
175int32_t scsi_send_command(SCSIDevice *s, uint32_t tag, uint8_t *buf)
176{
177 int64_t nb_sectors;
178 uint32_t lba;
179 uint32_t len;
180 int cmdlen;
181 int is_write;
182
183 s->command = buf[0];
184 s->tag = tag;
185 s->sector_count = 0;
186 s->buf_pos = 0;
187 s->buf_len = 0;
188 is_write = 0;
189 DPRINTF("Command: 0x%02x", buf[0]);
190 switch (s->command >> 5) {
191 case 0:
192 lba = buf[3] | (buf[2] << 8) | ((buf[1] & 0x1f) << 16);
193 len = buf[4];
194 cmdlen = 6;
195 break;
196 case 1:
197 case 2:
198 lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
199 len = buf[8] | (buf[7] << 8);
200 cmdlen = 10;
201 break;
202 case 4:
203 lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
204 len = buf[13] | (buf[12] << 8) | (buf[11] << 16) | (buf[10] << 24);
205 cmdlen = 16;
206 break;
207 case 5:
208 lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
209 len = buf[9] | (buf[8] << 8) | (buf[7] << 16) | (buf[6] << 24);
210 cmdlen = 12;
211 break;
212 default:
213 BADF("Unsupported command length\n");
214 goto fail;
215 }
216#ifdef DEBUG_SCSI
217 {
218 int i;
219 for (i = 1; i < cmdlen; i++) {
220 printf(" 0x%02x", buf[i]);
221 }
222 printf("\n");
223 }
224#endif
225 if (buf[1] >> 5) {
226 /* Only LUN 0 supported. */
227 goto fail;
228 }
229 switch (s->command) {
230 case 0x0:
231 DPRINTF("Test Unit Ready\n");
232 break;
233 case 0x03:
234 DPRINTF("Request Sense (len %d)\n", len);
235 if (len < 4)
236 goto fail;
237 memset(buf, 0, 4);
238 s->buf[0] = 0xf0;
239 s->buf[1] = 0;
240 s->buf[2] = s->sense;
241 s->buf_len = 4;
242 break;
243 case 0x12:
244 DPRINTF("Inquiry (len %d)\n", len);
245 if (len < 36) {
246 BADF("Inquiry buffer too small (%d)\n", len);
247 }
248 memset(s->buf, 0, 36);
249 if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
250 s->buf[0] = 5;
251 s->buf[1] = 0x80;
252 memcpy(&s->buf[16], "QEMU CDROM ", 16);
253 } else {
254 s->buf[0] = 0;
255 memcpy(&s->buf[16], "QEMU HARDDISK ", 16);
256 }
257 memcpy(&s->buf[8], "QEMU ", 8);
258 s->buf[2] = 3; /* SCSI-3 */
259 s->buf[3] = 2; /* Format 2 */
260 s->buf[4] = 32;
261 s->buf_len = 36;
262 break;
263 case 0x16:
264 DPRINTF("Reserve(6)\n");
265 if (buf[1] & 1)
266 goto fail;
267 break;
268 case 0x17:
269 DPRINTF("Release(6)\n");
270 if (buf[1] & 1)
271 goto fail;
272 break;
273 case 0x1a:
274 DPRINTF("Mode Sense(6) (page %d, len %d)\n", buf[2], len);
275 memset(s->buf, 0, 4);
276 s->buf[0] = 0x16; /* Mode data length (4 + 0x12). */
277 s->buf[1] = 0; /* Default media type. */
278 s->buf[2] = 0; /* Write enabled. */
279 s->buf[3] = 0; /* Block descriptor length. */
280 /* Caching page. */
281 s->buf[4 + 0] = 8;
282 s->buf[4 + 1] = 0x12;
283 s->buf[4 + 2] = 4; /* WCE */
284 if (len > 0x16)
285 len = 0x16;
286 s->buf_len = len;
287 break;
288 case 0x25:
289 DPRINTF("Read Capacity\n");
290 /* The normal LEN field for this command is zero. */
291 memset(s->buf, 0, 8);
292 bdrv_get_geometry(s->bdrv, &nb_sectors);
293 s->buf[0] = (nb_sectors >> 24) & 0xff;
294 s->buf[1] = (nb_sectors >> 16) & 0xff;
295 s->buf[2] = (nb_sectors >> 8) & 0xff;
296 s->buf[3] = nb_sectors & 0xff;
297 s->buf[4] = 0;
298 s->buf[5] = 0;
299 s->buf[6] = s->sector_size >> 8;
300 s->buf[7] = s->sector_size & 0xff;
301 s->buf_len = 8;
302 break;
303 case 0x08:
304 case 0x28:
305 DPRINTF("Read (sector %d, count %d)\n", lba, len);
306 s->sector = lba;
307 s->sector_count = len;
308 break;
309 case 0x0a:
310 case 0x2a:
311 DPRINTF("Write (sector %d, count %d)\n", lba, len);
312 s->sector = lba;
313 s->sector_count = len;
314 is_write = 1;
315 break;
316 case 0x43:
317 {
318 int start_track, format, msf;
319
320 msf = buf[1] & 2;
321 format = buf[2] & 0xf;
322 start_track = buf[6];
323 bdrv_get_geometry(s->bdrv, &nb_sectors);
324 DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1);
325 switch(format) {
326 case 0:
327 len = cdrom_read_toc(nb_sectors, s->buf, msf, start_track);
328 if (len < 0)
329 goto error_cmd;
330 s->buf_len = len;
331 break;
332 case 1:
333 /* multi session : only a single session defined */
334 memset(s->buf, 0, 12);
335 s->buf[1] = 0x0a;
336 s->buf[2] = 0x01;
337 s->buf[3] = 0x01;
338 s->buf_len = 12;
339 break;
340 case 2:
341 len = cdrom_read_toc_raw(nb_sectors, s->buf, msf, start_track);
342 if (len < 0)
343 goto error_cmd;
344 s->buf_len = len;
345 break;
346 default:
347 error_cmd:
348 DPRINTF("Read TOC error\n");
349 goto fail;
350 }
351 break;
352 }
353 case 0x56:
354 DPRINTF("Reserve(10)\n");
355 if (buf[1] & 3)
356 goto fail;
357 break;
358 case 0x57:
359 DPRINTF("Release(10)\n");
360 if (buf[1] & 3)
361 goto fail;
362 break;
363 case 0xa0:
364 DPRINTF("Report LUNs (len %d)\n", len);
365 if (len < 16)
366 goto fail;
367 memset(s->buf, 0, 16);
368 s->buf[3] = 8;
369 s->buf_len = 16;
370 break;
371 default:
372 DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
373 fail:
374 scsi_command_complete(s, SENSE_ILLEGAL_REQUEST);
375 return 0;
376 }
377 if (s->sector_count == 0 && s->buf_len == 0) {
378 scsi_command_complete(s, SENSE_NO_SENSE);
379 }
380 len = s->sector_count * s->sector_size + s->buf_len;
381 return is_write ? -len : len;
382}
383
384void scsi_disk_destroy(SCSIDevice *s)
385{
386 bdrv_close(s->bdrv);
387 qemu_free(s);
388}
389
390SCSIDevice *scsi_disk_init(BlockDriverState *bdrv,
391 scsi_completionfn completion,
392 void *opaque)
393{
394 SCSIDevice *s;
395
396 s = (SCSIDevice *)qemu_mallocz(sizeof(SCSIDevice));
397 s->bdrv = bdrv;
398 s->completion = completion;
399 s->opaque = opaque;
400 if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
401 s->sector_size = 2048;
402 } else {
403 s->sector_size = 512;
404 }
405
406 return s;
407}
408