Rearrange SCSI disk emulation code.
[qemu] / hw / scsi-disk.c
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...) \
16 do { 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...) \
22 do { 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
29 struct 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
50 static 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.  */
57 int 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.  */
110 int 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
175 int32_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
384 void scsi_disk_destroy(SCSIDevice *s)
385 {
386     bdrv_close(s->bdrv);
387     qemu_free(s);
388 }
389
390 SCSIDevice *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