diff options
Diffstat (limited to 'hw/scsi-disk.c')
-rw-r--r-- | hw/scsi-disk.c | 408 |
1 files changed, 218 insertions, 190 deletions
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index b05e6547df..a8c7372d3e 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -49,14 +49,8 @@ do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0) typedef struct SCSIDiskState SCSIDiskState; -typedef struct SCSISense { - uint8_t key; -} SCSISense; - typedef struct SCSIDiskReq { SCSIRequest req; - /* ??? We should probably keep track of whether the data transfer is - a read or a write. Currently we rely on the host getting it right. */ /* Both sector and sector_count are in terms of qemu 512 byte blocks. */ uint64_t sector; uint32_t sector_count; @@ -65,6 +59,8 @@ typedef struct SCSIDiskReq { uint32_t status; } SCSIDiskReq; +typedef enum { SCSI_HD, SCSI_CD } SCSIDriveKind; + struct SCSIDiskState { SCSIDevice qdev; @@ -78,32 +74,30 @@ struct SCSIDiskState char *version; char *serial; SCSISense sense; + SCSIDriveKind drive_kind; }; static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type); static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf); -static SCSIDiskReq *scsi_new_request(SCSIDiskState *s, uint32_t tag, +static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun) { + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); SCSIRequest *req; SCSIDiskReq *r; req = scsi_req_alloc(sizeof(SCSIDiskReq), &s->qdev, tag, lun); r = DO_UPCAST(SCSIDiskReq, req, req); r->iov.iov_base = qemu_blockalign(s->bs, SCSI_DMA_BUF_SIZE); - return r; + return req; } -static void scsi_remove_request(SCSIDiskReq *r) +static void scsi_free_request(SCSIRequest *req) { - qemu_vfree(r->iov.iov_base); - scsi_req_free(&r->req); -} + SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); -static SCSIDiskReq *scsi_find_request(SCSIDiskState *s, uint32_t tag) -{ - return DO_UPCAST(SCSIDiskReq, req, scsi_req_find(&s->qdev, tag)); + qemu_vfree(r->iov.iov_base); } static void scsi_disk_clear_sense(SCSIDiskState *s) @@ -111,42 +105,33 @@ static void scsi_disk_clear_sense(SCSIDiskState *s) memset(&s->sense, 0, sizeof(s->sense)); } -static void scsi_disk_set_sense(SCSIDiskState *s, uint8_t key) -{ - s->sense.key = key; -} - -static void scsi_req_set_status(SCSIDiskReq *r, int status, int sense_code) +static void scsi_req_set_status(SCSIDiskReq *r, int status, SCSISense sense) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); r->req.status = status; - scsi_disk_set_sense(s, sense_code); + s->sense = sense; } /* Helper function for command completion. */ -static void scsi_command_complete(SCSIDiskReq *r, int status, int sense) +static void scsi_command_complete(SCSIDiskReq *r, int status, SCSISense sense) { - DPRINTF("Command complete tag=0x%x status=%d sense=%d\n", - r->req.tag, status, sense); + DPRINTF("Command complete tag=0x%x status=%d sense=%d/%d/%d\n", + r->req.tag, status, sense.key, sense.asc, sense.ascq); scsi_req_set_status(r, status, sense); scsi_req_complete(&r->req); - scsi_remove_request(r); } /* Cancel a pending data transfer. */ -static void scsi_cancel_io(SCSIDevice *d, uint32_t tag) +static void scsi_cancel_io(SCSIRequest *req) { - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - SCSIDiskReq *r; - DPRINTF("Cancel tag=0x%x\n", tag); - r = scsi_find_request(s, tag); - if (r) { - if (r->req.aiocb) - bdrv_aio_cancel(r->req.aiocb); - r->req.aiocb = NULL; - scsi_remove_request(r); + SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); + + DPRINTF("Cancel tag=0x%x\n", req->tag); + if (r->req.aiocb) { + bdrv_aio_cancel(r->req.aiocb); } + r->req.aiocb = NULL; } static void scsi_read_complete(void * opaque, int ret) @@ -167,30 +152,38 @@ static void scsi_read_complete(void * opaque, int ret) n = r->iov.iov_len / 512; r->sector += n; r->sector_count -= n; - r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->iov.iov_len); + scsi_req_data(&r->req, r->iov.iov_len); } -static void scsi_read_request(SCSIDiskReq *r) +/* Read more data from scsi device into buffer. */ +static void scsi_read_data(SCSIRequest *req) { + SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); uint32_t n; if (r->sector_count == (uint32_t)-1) { DPRINTF("Read buf_len=%zd\n", r->iov.iov_len); r->sector_count = 0; - r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->iov.iov_len); + scsi_req_data(&r->req, r->iov.iov_len); return; } DPRINTF("Read sector_count=%d\n", r->sector_count); if (r->sector_count == 0) { - scsi_command_complete(r, GOOD, NO_SENSE); + scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE)); return; } /* No data transfer may already be in progress */ assert(r->req.aiocb == NULL); + if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { + DPRINTF("Data transfer direction invalid\n"); + scsi_read_complete(r, -EINVAL); + return; + } + n = r->sector_count; if (n > SCSI_DMA_BUF_SIZE / 512) n = SCSI_DMA_BUF_SIZE / 512; @@ -204,23 +197,6 @@ static void scsi_read_request(SCSIDiskReq *r) } } -/* Read more data from scsi device into buffer. */ -static void scsi_read_data(SCSIDevice *d, uint32_t tag) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - SCSIDiskReq *r; - - r = scsi_find_request(s, tag); - if (!r) { - BADF("Bad read tag 0x%x\n", tag); - /* ??? This is the wrong error. */ - scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR); - return; - } - - scsi_read_request(r); -} - static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) { int is_read = (type == SCSI_REQ_STATUS_RETRY_READ); @@ -242,13 +218,24 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) vm_stop(VMSTOP_DISKFULL); } else { if (type == SCSI_REQ_STATUS_RETRY_READ) { - r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, 0); + scsi_req_data(&r->req, 0); + } + switch (error) { + case ENOMEM: + scsi_command_complete(r, CHECK_CONDITION, + SENSE_CODE(TARGET_FAILURE)); + break; + case EINVAL: + scsi_command_complete(r, CHECK_CONDITION, + SENSE_CODE(INVALID_FIELD)); + break; + default: + scsi_command_complete(r, CHECK_CONDITION, + SENSE_CODE(IO_ERROR)); + break; } - scsi_command_complete(r, CHECK_CONDITION, - HARDWARE_ERROR); bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read); } - return 1; } @@ -270,7 +257,7 @@ static void scsi_write_complete(void * opaque, int ret) r->sector += n; r->sector_count -= n; if (r->sector_count == 0) { - scsi_command_complete(r, GOOD, NO_SENSE); + scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE)); } else { len = r->sector_count * 512; if (len > SCSI_DMA_BUF_SIZE) { @@ -278,25 +265,32 @@ static void scsi_write_complete(void * opaque, int ret) } r->iov.iov_len = len; DPRINTF("Write complete tag=0x%x more=%d\n", r->req.tag, len); - r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, len); + scsi_req_data(&r->req, len); } } -static void scsi_write_request(SCSIDiskReq *r) +static void scsi_write_data(SCSIRequest *req) { + SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); uint32_t n; /* No data transfer may already be in progress */ assert(r->req.aiocb == NULL); + if (r->req.cmd.mode != SCSI_XFER_TO_DEV) { + DPRINTF("Data transfer direction invalid\n"); + scsi_write_complete(r, -EINVAL); + return; + } + n = r->iov.iov_len / 512; if (n) { qemu_iovec_init_external(&r->qiov, &r->iov, 1); r->req.aiocb = bdrv_aio_writev(s->bs, r->sector, &r->qiov, n, scsi_write_complete, r); if (r->req.aiocb == NULL) { - scsi_write_complete(r, -EIO); + scsi_write_complete(r, -ENOMEM); } } else { /* Invoke completion routine to fetch data from host. */ @@ -304,26 +298,6 @@ static void scsi_write_request(SCSIDiskReq *r) } } -/* Write data to a scsi device. Returns nonzero on failure. - The transfer may complete asynchronously. */ -static int scsi_write_data(SCSIDevice *d, uint32_t tag) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - SCSIDiskReq *r; - - DPRINTF("Write data tag=0x%x\n", tag); - r = scsi_find_request(s, tag); - if (!r) { - BADF("Bad write tag 0x%x\n", tag); - scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR); - return 1; - } - - scsi_write_request(r); - - return 0; -} - static void scsi_dma_restart_bh(void *opaque) { SCSIDiskState *s = opaque; @@ -344,15 +318,15 @@ static void scsi_dma_restart_bh(void *opaque) switch (status & SCSI_REQ_STATUS_RETRY_TYPE_MASK) { case SCSI_REQ_STATUS_RETRY_READ: - scsi_read_request(r); + scsi_read_data(&r->req); break; case SCSI_REQ_STATUS_RETRY_WRITE: - scsi_write_request(r); + scsi_write_data(&r->req); break; case SCSI_REQ_STATUS_RETRY_FLUSH: ret = scsi_disk_emulate_command(r, r->iov.iov_base); if (ret == 0) { - scsi_command_complete(r, GOOD, NO_SENSE); + scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE)); } } } @@ -373,19 +347,21 @@ static void scsi_dma_restart_cb(void *opaque, int running, int reason) } /* Return a pointer to the data buffer. */ -static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag) +static uint8_t *scsi_get_buf(SCSIRequest *req) { - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - SCSIDiskReq *r; + SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); - r = scsi_find_request(s, tag); - if (!r) { - BADF("Bad buffer tag 0x%x\n", tag); - return NULL; - } return (uint8_t *)r->iov.iov_base; } +/* Copy sense information into the provided buffer */ +static int scsi_get_sense(SCSIRequest *req, uint8_t *outbuf, int len) +{ + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); + + return scsi_build_sense(s->sense, outbuf, len, len > 14); +} + static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); @@ -406,7 +382,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) return -1; } - if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) { + if (s->drive_kind == SCSI_CD) { outbuf[buflen++] = 5; } else { outbuf[buflen++] = 0; @@ -424,7 +400,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) outbuf[buflen++] = 0x00; // list of supported pages (this page) outbuf[buflen++] = 0x80; // unit serial number outbuf[buflen++] = 0x83; // device identification - if (bdrv_get_type_hint(s->bs) != BDRV_TYPE_CDROM) { + if (s->drive_kind == SCSI_HD) { outbuf[buflen++] = 0xb0; // block limits outbuf[buflen++] = 0xb2; // thin provisioning } @@ -477,7 +453,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) unsigned int opt_io_size = s->qdev.conf.opt_io_size / s->qdev.blocksize; - if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) { + if (s->drive_kind == SCSI_CD) { DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n", page_code); return -1; @@ -542,12 +518,12 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) memset(outbuf, 0, buflen); - if (req->lun || req->cmd.buf[1] >> 5) { + if (req->lun) { outbuf[0] = 0x7f; /* LUN not supported */ return buflen; } - if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) { + if (s->drive_kind == SCSI_CD) { outbuf[0] = 5; outbuf[1] = 0x80; memcpy(&outbuf[16], "QEMU CD-ROM ", 16); @@ -678,7 +654,7 @@ static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p, return p[1] + 2; case 0x2a: /* CD Capabilities and Mechanical Status page. */ - if (bdrv_get_type_hint(bdrv) != BDRV_TYPE_CDROM) + if (s->drive_kind != SCSI_CD) return 0; p[0] = 0x2a; p[1] = 0x14; @@ -857,19 +833,8 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf) case REQUEST_SENSE: if (req->cmd.xfer < 4) goto illegal_request; - memset(outbuf, 0, 4); - buflen = 4; - if (s->sense.key == NOT_READY && req->cmd.xfer >= 18) { - memset(outbuf, 0, 18); - buflen = 18; - outbuf[7] = 10; - /* asc 0x3a, ascq 0: Medium not present */ - outbuf[12] = 0x3a; - outbuf[13] = 0; - } - outbuf[0] = 0xf0; - outbuf[1] = 0; - outbuf[2] = s->sense.key; + buflen = scsi_build_sense(s->sense, outbuf, req->cmd.xfer, + req->cmd.xfer > 13); scsi_disk_clear_sense(s); break; case INQUIRY: @@ -905,7 +870,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf) goto illegal_request; break; case START_STOP: - if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM && (req->cmd.buf[4] & 2)) { + if (s->drive_kind == SCSI_CD && (req->cmd.buf[4] & 2)) { /* load/eject medium */ bdrv_eject(s->bs, !(req->cmd.buf[4] & 1)); } @@ -1007,17 +972,22 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf) } break; default: - goto illegal_request; + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE)); + return -1; } - scsi_req_set_status(r, GOOD, NO_SENSE); + scsi_req_set_status(r, GOOD, SENSE_CODE(NO_SENSE)); return buflen; not_ready: - scsi_command_complete(r, CHECK_CONDITION, NOT_READY); + if (!bdrv_is_inserted(s->bs)) { + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(NO_MEDIUM)); + } else { + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(LUN_NOT_READY)); + } return -1; illegal_request: - scsi_command_complete(r, CHECK_CONDITION, ILLEGAL_REQUEST); + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_FIELD)); return -1; } @@ -1026,33 +996,23 @@ illegal_request: (eg. disk reads), negative for transfers to the device (eg. disk writes), and zero if the command does not transfer any data. */ -static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, - uint8_t *buf, int lun) +static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) { - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - uint32_t len; - int is_write; + SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); + int32_t len; uint8_t command; uint8_t *outbuf; - SCSIDiskReq *r; int rc; command = buf[0]; - r = scsi_find_request(s, tag); - if (r) { - BADF("Tag 0x%x already in use\n", tag); - scsi_cancel_io(d, tag); - } - /* ??? Tags are not unique for different luns. We only implement a - single lun, so this should not matter. */ - r = scsi_new_request(s, tag, lun); outbuf = (uint8_t *)r->iov.iov_base; - is_write = 0; DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]); if (scsi_req_parse(&r->req, buf) != 0) { BADF("Unsupported command length, command %x\n", command); - goto fail; + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE)); + return 0; } #ifdef DEBUG_SCSI { @@ -1064,11 +1024,14 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, } #endif - if (lun || buf[1] >> 5) { + if (req->lun) { /* Only LUN 0 supported. */ - DPRINTF("Unimplemented LUN %d\n", lun ? lun : buf[1] >> 5); - if (command != REQUEST_SENSE && command != INQUIRY) - goto fail; + DPRINTF("Unimplemented LUN %d\n", req->lun); + if (command != REQUEST_SENSE && command != INQUIRY) { + scsi_command_complete(r, CHECK_CONDITION, + SENSE_CODE(LUN_NOT_SUPPORTED)); + return 0; + } } switch (command) { case TEST_UNIT_READY: @@ -1101,7 +1064,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, case READ_10: case READ_12: case READ_16: - len = r->req.cmd.xfer / d->blocksize; + len = r->req.cmd.xfer / s->qdev.blocksize; DPRINTF("Read (sector %" PRId64 ", count %d)\n", r->req.cmd.lba, len); if (r->req.cmd.lba > s->max_lba) goto illegal_lba; @@ -1115,7 +1078,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, case WRITE_VERIFY: case WRITE_VERIFY_12: case WRITE_VERIFY_16: - len = r->req.cmd.xfer / d->blocksize; + len = r->req.cmd.xfer / s->qdev.blocksize; DPRINTF("Write %s(sector %" PRId64 ", count %d)\n", (command & 0xe) == 0xe ? "And Verify " : "", r->req.cmd.lba, len); @@ -1123,7 +1086,6 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, goto illegal_lba; r->sector = r->req.cmd.lba * s->cluster_size; r->sector_count = len * s->cluster_size; - is_write = 1; break; case MODE_SELECT: DPRINTF("Mode Select(6) (len %lu)\n", (long)r->req.cmd.xfer); @@ -1150,7 +1112,7 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, } break; case WRITE_SAME_16: - len = r->req.cmd.xfer / d->blocksize; + len = r->req.cmd.xfer / s->qdev.blocksize; DPRINTF("WRITE SAME(16) (sector %" PRId64 ", count %d)\n", r->req.cmd.lba, len); @@ -1176,18 +1138,20 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, break; default: DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]); + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_OPCODE)); + return 0; fail: - scsi_command_complete(r, CHECK_CONDITION, ILLEGAL_REQUEST); + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(INVALID_FIELD)); return 0; illegal_lba: - scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR); + scsi_command_complete(r, CHECK_CONDITION, SENSE_CODE(LBA_OUT_OF_RANGE)); return 0; } if (r->sector_count == 0 && r->iov.iov_len == 0) { - scsi_command_complete(r, GOOD, NO_SENSE); + scsi_command_complete(r, GOOD, SENSE_CODE(NO_SENSE)); } len = r->sector_count * 512 + r->iov.iov_len; - if (is_write) { + if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { return -len; } else { if (!r->sector_count) @@ -1196,25 +1160,12 @@ static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag, } } -static void scsi_disk_purge_requests(SCSIDiskState *s) -{ - SCSIDiskReq *r; - - while (!QTAILQ_EMPTY(&s->qdev.requests)) { - r = DO_UPCAST(SCSIDiskReq, req, QTAILQ_FIRST(&s->qdev.requests)); - if (r->req.aiocb) { - bdrv_aio_cancel(r->req.aiocb); - } - scsi_remove_request(r); - } -} - static void scsi_disk_reset(DeviceState *dev) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev.qdev, dev); uint64_t nb_sectors; - scsi_disk_purge_requests(s); + scsi_device_purge_requests(&s->qdev); bdrv_get_geometry(s->bs, &nb_sectors); nb_sectors /= s->cluster_size; @@ -1228,14 +1179,13 @@ static void scsi_destroy(SCSIDevice *dev) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); - scsi_disk_purge_requests(s); + scsi_device_purge_requests(&s->qdev); blockdev_mark_auto_del(s->qdev.conf.bs); } -static int scsi_disk_initfn(SCSIDevice *dev) +static int scsi_initfn(SCSIDevice *dev, SCSIDriveKind kind) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); - int is_cd; DriveInfo *dinfo; if (!s->qdev.conf.bs) { @@ -1243,9 +1193,9 @@ static int scsi_disk_initfn(SCSIDevice *dev) return -1; } s->bs = s->qdev.conf.bs; - is_cd = bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM; + s->drive_kind = kind; - if (!is_cd && !bdrv_is_inserted(s->bs)) { + if (kind == SCSI_HD && !bdrv_is_inserted(s->bs)) { error_report("Device needs media, but drive is empty"); return -1; } @@ -1265,7 +1215,7 @@ static int scsi_disk_initfn(SCSIDevice *dev) return -1; } - if (is_cd) { + if (kind == SCSI_CD) { s->qdev.blocksize = 2048; } else { s->qdev.blocksize = s->qdev.conf.logical_block_size; @@ -1275,35 +1225,113 @@ static int scsi_disk_initfn(SCSIDevice *dev) s->qdev.type = TYPE_DISK; qemu_add_vm_change_state_handler(scsi_dma_restart_cb, s); - bdrv_set_removable(s->bs, is_cd); + bdrv_set_removable(s->bs, kind == SCSI_CD); add_boot_device_path(s->qdev.conf.bootindex, &dev->qdev, ",0"); return 0; } -static SCSIDeviceInfo scsi_disk_info = { - .qdev.name = "scsi-disk", - .qdev.fw_name = "disk", - .qdev.desc = "virtual scsi disk or cdrom", - .qdev.size = sizeof(SCSIDiskState), - .qdev.reset = scsi_disk_reset, - .init = scsi_disk_initfn, - .destroy = scsi_destroy, - .send_command = scsi_send_command, - .read_data = scsi_read_data, - .write_data = scsi_write_data, - .cancel_io = scsi_cancel_io, - .get_buf = scsi_get_buf, - .qdev.props = (Property[]) { - DEFINE_BLOCK_PROPERTIES(SCSIDiskState, qdev.conf), - DEFINE_PROP_STRING("ver", SCSIDiskState, version), - DEFINE_PROP_STRING("serial", SCSIDiskState, serial), - DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false), - DEFINE_PROP_END_OF_LIST(), - }, +static int scsi_hd_initfn(SCSIDevice *dev) +{ + return scsi_initfn(dev, SCSI_HD); +} + +static int scsi_cd_initfn(SCSIDevice *dev) +{ + return scsi_initfn(dev, SCSI_CD); +} + +static int scsi_disk_initfn(SCSIDevice *dev) +{ + SCSIDriveKind kind; + DriveInfo *dinfo; + + if (!dev->conf.bs) { + kind = SCSI_HD; /* will die in scsi_initfn() */ + } else { + dinfo = drive_get_by_blockdev(dev->conf.bs); + kind = dinfo->media_cd ? SCSI_CD : SCSI_HD; + } + + return scsi_initfn(dev, kind); +} + +#define DEFINE_SCSI_DISK_PROPERTIES() \ + DEFINE_BLOCK_PROPERTIES(SCSIDiskState, qdev.conf), \ + DEFINE_PROP_STRING("ver", SCSIDiskState, version), \ + DEFINE_PROP_STRING("serial", SCSIDiskState, serial) + +static SCSIDeviceInfo scsi_disk_info[] = { + { + .qdev.name = "scsi-hd", + .qdev.fw_name = "disk", + .qdev.desc = "virtual SCSI disk", + .qdev.size = sizeof(SCSIDiskState), + .qdev.reset = scsi_disk_reset, + .init = scsi_hd_initfn, + .destroy = scsi_destroy, + .alloc_req = scsi_new_request, + .free_req = scsi_free_request, + .send_command = scsi_send_command, + .read_data = scsi_read_data, + .write_data = scsi_write_data, + .cancel_io = scsi_cancel_io, + .get_buf = scsi_get_buf, + .get_sense = scsi_get_sense, + .qdev.props = (Property[]) { + DEFINE_SCSI_DISK_PROPERTIES(), + DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false), + DEFINE_PROP_END_OF_LIST(), + } + },{ + .qdev.name = "scsi-cd", + .qdev.fw_name = "disk", + .qdev.desc = "virtual SCSI CD-ROM", + .qdev.size = sizeof(SCSIDiskState), + .qdev.reset = scsi_disk_reset, + .init = scsi_cd_initfn, + .destroy = scsi_destroy, + .alloc_req = scsi_new_request, + .free_req = scsi_free_request, + .send_command = scsi_send_command, + .read_data = scsi_read_data, + .write_data = scsi_write_data, + .cancel_io = scsi_cancel_io, + .get_buf = scsi_get_buf, + .get_sense = scsi_get_sense, + .qdev.props = (Property[]) { + DEFINE_SCSI_DISK_PROPERTIES(), + DEFINE_PROP_END_OF_LIST(), + }, + },{ + .qdev.name = "scsi-disk", /* legacy -device scsi-disk */ + .qdev.fw_name = "disk", + .qdev.desc = "virtual SCSI disk or CD-ROM (legacy)", + .qdev.size = sizeof(SCSIDiskState), + .qdev.reset = scsi_disk_reset, + .init = scsi_disk_initfn, + .destroy = scsi_destroy, + .alloc_req = scsi_new_request, + .free_req = scsi_free_request, + .send_command = scsi_send_command, + .read_data = scsi_read_data, + .write_data = scsi_write_data, + .cancel_io = scsi_cancel_io, + .get_buf = scsi_get_buf, + .get_sense = scsi_get_sense, + .qdev.props = (Property[]) { + DEFINE_SCSI_DISK_PROPERTIES(), + DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false), + DEFINE_PROP_END_OF_LIST(), + } + } }; static void scsi_disk_register_devices(void) { - scsi_qdev_register(&scsi_disk_info); + int i; + + for (i = 0; i < ARRAY_SIZE(scsi_disk_info); i++) { + scsi_qdev_register(&scsi_disk_info[i]); + } } device_init(scsi_disk_register_devices) |