source: trunk/packages/xen-common/xen-common/tools/ioemu/hw/scsi-disk.c @ 34

Last change on this file since 34 was 34, checked in by hartmans, 17 years ago

Add xen and xen-common

File size: 13.3 KB
Line 
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    /* The qemu block layer uses a fixed 512 byte sector size.
35       This is the number of 512 byte blocks in a single scsi sector.  */
36    int cluster_size;
37    /* When transfering data buf_pos and buf_len contain a partially
38       transferred block of data (or response to a command), and
39       sector/sector_count identify any remaining sectors.
40       Both sector and sector_count are in terms of qemu 512 byte blocks.  */
41    /* ??? We should probably keep track of whether the data trasfer is
42       a read or a write.  Currently we rely on the host getting it right.  */
43    int sector;
44    int sector_count;
45    int buf_pos;
46    int buf_len;
47    int sense;
48    char buf[512];
49    scsi_completionfn completion;
50    void *opaque;
51};
52
53static void scsi_command_complete(SCSIDevice *s, int sense)
54{
55    s->sense = sense;
56    s->completion(s->opaque, s->tag, sense);
57}
58
59/* Read data from a scsi device.  Returns nonzero on failure.  */
60int scsi_read_data(SCSIDevice *s, uint8_t *data, uint32_t len)
61{
62    uint32_t n;
63
64    DPRINTF("Read %d (%d/%d)\n", len, s->buf_len, s->sector_count);
65    if (s->buf_len == 0 && s->sector_count == 0)
66        return 1;
67
68    if (s->buf_len) {
69        n = s->buf_len;
70        if (n > len)
71            n = len;
72        memcpy(data, s->buf + s->buf_pos, n);
73        s->buf_pos += n;
74        s->buf_len -= n;
75        data += n;
76        len -= n;
77        if (s->buf_len == 0)
78            s->buf_pos = 0;
79    }
80
81    n = len / 512;
82    if (n > s->sector_count)
83      n = s->sector_count;
84
85    if (n != 0) {
86        bdrv_read(s->bdrv, s->sector, data, n);
87        data += n * 512;
88        len -= n * 512;
89        s->sector += n;
90        s->sector_count -= n;
91    }
92
93    if (len && s->sector_count) {
94        bdrv_read(s->bdrv, s->sector, s->buf, 1);
95        s->sector++;
96        s->sector_count--;
97        s->buf_pos = 0;
98        s->buf_len = 512;
99        /* Recurse to complete the partial read.  */
100        return scsi_read_data(s, data, len);
101    }
102
103    if (len != 0)
104        return 1;
105
106    if (s->buf_len == 0 && s->sector_count == 0)
107        scsi_command_complete(s, SENSE_NO_SENSE);
108
109    return 0;
110}
111
112/* Read data to a scsi device.  Returns nonzero on failure.  */
113int scsi_write_data(SCSIDevice *s, uint8_t *data, uint32_t len)
114{
115    uint32_t n;
116
117    DPRINTF("Write %d (%d/%d)\n", len, s->buf_len, s->sector_count);
118    if (s->buf_pos != 0) {
119        BADF("Bad state on write\n");
120        return 1;
121    }
122
123    if (s->sector_count == 0)
124        return 1;
125
126    if (s->buf_len != 0 || len < 512) {
127        n = 512 - s->buf_len;
128        if (n > len)
129            n = len;
130
131        memcpy(s->buf + s->buf_len, data, n);
132        data += n;
133        s->buf_len += n;
134        len -= n;
135        if (s->buf_len == 512) {
136            /* A full sector has been accumulated. Write it to disk.  */
137            bdrv_write(s->bdrv, s->sector, s->buf, 1);
138            s->buf_len = 0;
139            s->sector++;
140            s->sector_count--;
141        }
142    }
143
144    n = len / 512;
145    if (n > s->sector_count)
146        n = s->sector_count;
147
148    if (n != 0) {
149        bdrv_write(s->bdrv, s->sector, data, n);
150        data += n * 512;
151        len -= n * 512;
152        s->sector += n;
153        s->sector_count -= n;
154    }
155
156    if (len >= 512)
157        return 1;
158
159    if (len && s->sector_count) {
160        /* Recurse to complete the partial write.  */
161        return scsi_write_data(s, data, len);
162    }
163
164    if (len != 0)
165        return 1;
166
167    if (s->sector_count == 0)
168        scsi_command_complete(s, SENSE_NO_SENSE);
169
170    return 0;
171}
172
173/* Execute a scsi command.  Returns the length of the data expected by the
174   command.  This will be Positive for data transfers from the device
175   (eg. disk reads), negative for transfers to the device (eg. disk writes),
176   and zero if the command does not transfer any data.  */
177
178int32_t scsi_send_command(SCSIDevice *s, uint32_t tag, uint8_t *buf, int lun)
179{
180    int64_t nb_sectors;
181    uint32_t lba;
182    uint32_t len;
183    int cmdlen;
184    int is_write;
185
186    s->command = buf[0];
187    s->tag = tag;
188    s->sector_count = 0;
189    s->buf_pos = 0;
190    s->buf_len = 0;
191    is_write = 0;
192    DPRINTF("Command: 0x%02x", buf[0]);
193    switch (s->command >> 5) {
194    case 0:
195        lba = buf[3] | (buf[2] << 8) | ((buf[1] & 0x1f) << 16);
196        len = buf[4];
197        cmdlen = 6;
198        break;
199    case 1:
200    case 2:
201        lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
202        len = buf[8] | (buf[7] << 8);
203        cmdlen = 10;
204        break;
205    case 4:
206        lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
207        len = buf[13] | (buf[12] << 8) | (buf[11] << 16) | (buf[10] << 24);
208        cmdlen = 16;
209        break;
210    case 5:
211        lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
212        len = buf[9] | (buf[8] << 8) | (buf[7] << 16) | (buf[6] << 24);
213        cmdlen = 12;
214        break;
215    default:
216        BADF("Unsupported command length, command %x\n", s->command);
217        goto fail;
218    }
219#ifdef DEBUG_SCSI
220    {
221        int i;
222        for (i = 1; i < cmdlen; i++) {
223            printf(" 0x%02x", buf[i]);
224        }
225        printf("\n");
226    }
227#endif
228    if (lun || buf[1] >> 5) {
229        /* Only LUN 0 supported.  */
230        DPRINTF("Unimplemented LUN %d\n", lun ? lun : buf[1] >> 5);
231        goto fail;
232    }
233    switch (s->command) {
234    case 0x0:
235        DPRINTF("Test Unit Ready\n");
236        break;
237    case 0x03:
238        DPRINTF("Request Sense (len %d)\n", len);
239        if (len < 4)
240            goto fail;
241        memset(buf, 0, 4);
242        s->buf[0] = 0xf0;
243        s->buf[1] = 0;
244        s->buf[2] = s->sense;
245        s->buf_len = 4;
246        break;
247    case 0x12:
248        DPRINTF("Inquiry (len %d)\n", len);
249        if (len < 36) {
250            BADF("Inquiry buffer too small (%d)\n", len);
251        }
252        memset(s->buf, 0, 36);
253        if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
254            s->buf[0] = 5;
255            s->buf[1] = 0x80;
256            memcpy(&s->buf[16], "QEMU CD-ROM    ", 16);
257        } else {
258            s->buf[0] = 0;
259            memcpy(&s->buf[16], "QEMU HARDDISK  ", 16);
260        }
261        memcpy(&s->buf[8], "QEMU   ", 8);
262        memcpy(&s->buf[32], QEMU_VERSION, 4);
263        /* Identify device as SCSI-3 rev 1.
264           Some later commands are also implemented. */
265        s->buf[2] = 3;
266        s->buf[3] = 2; /* Format 2 */
267        s->buf[4] = 32;
268        s->buf_len = 36;
269        break;
270    case 0x16:
271        DPRINTF("Reserve(6)\n");
272        if (buf[1] & 1)
273            goto fail;
274        break;
275    case 0x17:
276        DPRINTF("Release(6)\n");
277        if (buf[1] & 1)
278            goto fail;
279        break;
280    case 0x1a:
281    case 0x5a:
282        {
283            char *p;
284            int page;
285
286            page = buf[2] & 0x3f;
287            DPRINTF("Mode Sense (page %d, len %d)\n", page, len);
288            p = s->buf;
289            memset(p, 0, 4);
290            s->buf[1] = 0; /* Default media type.  */
291            s->buf[3] = 0; /* Block descriptor length.  */
292            if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
293                s->buf[2] = 0x80; /* Readonly.  */
294            }
295            p += 4;
296            if ((page == 8 || page == 0x3f)) {
297                /* Caching page.  */
298                p[0] = 8;
299                p[1] = 0x12;
300                p[2] = 4; /* WCE */
301                p += 19;
302            }
303            if ((page == 0x3f || page == 0x2a)
304                    && (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM)) {
305                /* CD Capabilities and Mechanical Status page. */
306                p[0] = 0x2a;
307                p[1] = 0x14;
308                p[2] = 3; // CD-R & CD-RW read
309                p[3] = 0; // Writing not supported
310                p[4] = 0x7f; /* Audio, composite, digital out,
311                                         mode 2 form 1&2, multi session */
312                p[5] = 0xff; /* CD DA, DA accurate, RW supported,
313                                         RW corrected, C2 errors, ISRC,
314                                         UPC, Bar code */
315                p[6] = 0x2d | (bdrv_is_locked(s->bdrv)? 2 : 0);
316                /* Locking supported, jumper present, eject, tray */
317                p[7] = 0; /* no volume & mute control, no
318                                      changer */
319                p[8] = (50 * 176) >> 8; // 50x read speed
320                p[9] = (50 * 176) & 0xff;
321                p[10] = 0 >> 8; // No volume
322                p[11] = 0 & 0xff;
323                p[12] = 2048 >> 8; // 2M buffer
324                p[13] = 2048 & 0xff;
325                p[14] = (16 * 176) >> 8; // 16x read speed current
326                p[15] = (16 * 176) & 0xff;
327                p[18] = (16 * 176) >> 8; // 16x write speed
328                p[19] = (16 * 176) & 0xff;
329                p[20] = (16 * 176) >> 8; // 16x write speed current
330                p[21] = (16 * 176) & 0xff;
331                p += 21;
332            }
333            s->buf_len = p - s->buf;
334            s->buf[0] = s->buf_len - 4;
335            if (s->buf_len > len)
336                s->buf_len = len;
337        }
338        break;
339    case 0x1b:
340        DPRINTF("Start Stop Unit\n");
341        break;
342    case 0x1e:
343        DPRINTF("Prevent Allow Medium Removal (prevent = %d)\n", buf[4] & 3);
344        bdrv_set_locked(s->bdrv, buf[4] & 1);
345        break;
346    case 0x25:
347        DPRINTF("Read Capacity\n");
348        /* The normal LEN field for this command is zero.  */
349        memset(s->buf, 0, 8);
350        bdrv_get_geometry(s->bdrv, &nb_sectors);
351        s->buf[0] = (nb_sectors >> 24) & 0xff;
352        s->buf[1] = (nb_sectors >> 16) & 0xff;
353        s->buf[2] = (nb_sectors >> 8) & 0xff;
354        s->buf[3] = nb_sectors & 0xff;
355        s->buf[4] = 0;
356        s->buf[5] = 0;
357        s->buf[6] = s->cluster_size * 2;
358        s->buf[7] = 0;
359        s->buf_len = 8;
360        break;
361    case 0x08:
362    case 0x28:
363        DPRINTF("Read (sector %d, count %d)\n", lba, len);
364        s->sector = lba * s->cluster_size;
365        s->sector_count = len * s->cluster_size;
366        break;
367    case 0x0a:
368    case 0x2a:
369        DPRINTF("Write (sector %d, count %d)\n", lba, len);
370        s->sector = lba * s->cluster_size;
371        s->sector_count = len * s->cluster_size;
372        is_write = 1;
373        break;
374    case 0x35:
375        DPRINTF("Syncronise cache (sector %d, count %d)\n", lba, len);
376        bdrv_flush(s->bdrv);
377        break;
378    case 0x43:
379        {
380            int start_track, format, msf, toclen;
381
382            msf = buf[1] & 2;
383            format = buf[2] & 0xf;
384            start_track = buf[6];
385            bdrv_get_geometry(s->bdrv, &nb_sectors);
386            DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1);
387            switch(format) {
388            case 0:
389                toclen = cdrom_read_toc(nb_sectors, s->buf, msf, start_track);
390                break;
391            case 1:
392                /* multi session : only a single session defined */
393                toclen = 12;
394                memset(s->buf, 0, 12);
395                s->buf[1] = 0x0a;
396                s->buf[2] = 0x01;
397                s->buf[3] = 0x01;
398                break;
399            case 2:
400                toclen = cdrom_read_toc_raw(nb_sectors, s->buf, msf, start_track);
401                break;
402            default:
403                goto error_cmd;
404            }
405            if (toclen > 0) {
406                if (len > toclen)
407                  len = toclen;
408                s->buf_len = len;
409                break;
410            }
411        error_cmd:
412            DPRINTF("Read TOC error\n");
413            goto fail;
414        }
415    case 0x46:
416        DPRINTF("Get Configuration (rt %d, maxlen %d)\n", buf[1] & 3, len);
417        memset(s->buf, 0, 8);
418        /* ??? This shoud probably return much more information.  For now
419           just return the basic header indicating the CD-ROM profile.  */
420        s->buf[7] = 8; // CD-ROM
421        s->buf_len = 8;
422        break;
423    case 0x56:
424        DPRINTF("Reserve(10)\n");
425        if (buf[1] & 3)
426            goto fail;
427        break;
428    case 0x57:
429        DPRINTF("Release(10)\n");
430        if (buf[1] & 3)
431            goto fail;
432        break;
433    case 0xa0:
434        DPRINTF("Report LUNs (len %d)\n", len);
435        if (len < 16)
436            goto fail;
437        memset(s->buf, 0, 16);
438        s->buf[3] = 8;
439        s->buf_len = 16;
440        break;
441    default:
442        DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
443    fail:
444        scsi_command_complete(s, SENSE_ILLEGAL_REQUEST);
445        return 0;
446    }
447    if (s->sector_count == 0 && s->buf_len == 0) {
448        scsi_command_complete(s, SENSE_NO_SENSE);
449    }
450    len = s->sector_count * 512 + s->buf_len;
451    return is_write ? -len : len;
452}
453
454void scsi_disk_destroy(SCSIDevice *s)
455{
456    bdrv_close(s->bdrv);
457    qemu_free(s);
458}
459
460SCSIDevice *scsi_disk_init(BlockDriverState *bdrv,
461                           scsi_completionfn completion,
462                           void *opaque)
463{
464    SCSIDevice *s;
465
466    s = (SCSIDevice *)qemu_mallocz(sizeof(SCSIDevice));
467    s->bdrv = bdrv;
468    s->completion = completion;
469    s->opaque = opaque;
470    if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
471        s->cluster_size = 4;
472    } else {
473        s->cluster_size = 1;
474    }
475
476    return s;
477}
478
Note: See TracBrowser for help on using the repository browser.