From 7e218df5181f1c26d52ef525b30a416c60a2160b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 19 Sep 2011 17:19:21 +0200 Subject: scsi: pass correct sense code for ENOMEDIUM Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- hw/scsi-disk.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'hw/scsi-disk.c') diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 6909578..9c62569 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -231,6 +231,9 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) bdrv_iostatus_set_err(s->bs, error); } else { switch (error) { + case ENOMEDIUM: + scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); + break; case ENOMEM: scsi_check_condition(r, SENSE_CODE(TARGET_FAILURE)); break; -- cgit v1.1 From 67cc61e43077eeffc7c95a1bb25d05a12d051c67 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 13 Sep 2011 14:41:56 +0200 Subject: atapi/scsi: unify definitions for MMC The definitions in ide/internal.h are duplicates, since ATAPI commands actually come from SCSI. Use the ones in scsi-defs.h and move the missing ones there. Two exceptions: - MODE_PAGE_WRITE_PARMS conflicts with the "flexible disk geometry" page in scsi-disk.c. It is unused, so pick the latter. - GPCMD_* is left in ide/internal.h, at least for now. Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- hw/scsi-disk.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'hw/scsi-disk.c') diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 9c62569..a6ef060 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -576,7 +576,7 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, * The buffer was already menset to zero by the caller of this function. */ switch (page) { - case 4: /* Rigid disk device geometry page. */ + case MODE_PAGE_HD_GEOMETRY: if (s->qdev.type == TYPE_ROM) { return -1; } @@ -611,7 +611,7 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, p[21] = 5400 & 0xff; break; - case 5: /* Flexible disk device geometry page. */ + case MODE_PAGE_FLEXIBLE_DISK_GEOMETRY: if (s->qdev.type == TYPE_ROM) { return -1; } @@ -653,7 +653,7 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, p[29] = 5400 & 0xff; break; - case 8: /* Caching page. */ + case MODE_PAGE_CACHING: p[0] = 8; p[1] = 0x12; if (page_control == 1) { /* Changeable Values */ @@ -664,7 +664,7 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, } break; - case 0x2a: /* CD Capabilities and Mechanical Status page. */ + case MODE_PAGE_CAPABILITIES: if (s->qdev.type != TYPE_ROM) { return -1; } -- cgit v1.1 From 8a9c16f69e3ecbcc6e6bc068012986b87412c876 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 13 Sep 2011 16:26:06 +0200 Subject: scsi-disk: report media changed via unit attention sense codes Building on the previous patch, this one adds a media change callback to scsi-disk. Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- hw/scsi-disk.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) (limited to 'hw/scsi-disk.c') diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index a6ef060..880cb22 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -71,6 +71,7 @@ struct SCSIDiskState int cluster_size; uint32_t removable; uint64_t max_lba; + bool media_changed; QEMUBH *bh; char *version; char *serial; @@ -1198,7 +1199,21 @@ static void scsi_destroy(SCSIDevice *dev) static void scsi_cd_change_media_cb(void *opaque, bool load) { - ((SCSIDiskState *)opaque)->tray_open = !load; + SCSIDiskState *s = opaque; + + /* + * When a CD gets changed, we have to report an ejected state and + * then a loaded state to guests so that they detect tray + * open/close and media change events. Guests that do not use + * GET_EVENT_STATUS_NOTIFICATION to detect such tray open/close + * states rely on this behavior. + * + * media_changed governs the state machine used for unit attention + * report. media_event is used by GET EVENT STATUS NOTIFICATION. + */ + s->media_changed = load; + s->tray_open = !load; + s->qdev.unit_attention = SENSE_CODE(UNIT_ATTENTION_NO_MEDIUM); } static bool scsi_cd_is_tray_open(void *opaque) @@ -1217,6 +1232,15 @@ static const BlockDevOps scsi_cd_block_ops = { .is_medium_locked = scsi_cd_is_medium_locked, }; +static void scsi_disk_unit_attention_reported(SCSIDevice *dev) +{ + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); + if (s->media_changed) { + s->media_changed = false; + s->qdev.unit_attention = SENSE_CODE(MEDIUM_CHANGED); + } +} + static int scsi_initfn(SCSIDevice *dev, uint8_t scsi_type) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); @@ -1329,6 +1353,7 @@ static SCSIDeviceInfo scsi_disk_info[] = { .init = scsi_hd_initfn, .destroy = scsi_destroy, .alloc_req = scsi_new_request, + .unit_attention_reported = scsi_disk_unit_attention_reported, .qdev.props = (Property[]) { DEFINE_SCSI_DISK_PROPERTIES(), DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false), @@ -1343,6 +1368,7 @@ static SCSIDeviceInfo scsi_disk_info[] = { .init = scsi_cd_initfn, .destroy = scsi_destroy, .alloc_req = scsi_new_request, + .unit_attention_reported = scsi_disk_unit_attention_reported, .qdev.props = (Property[]) { DEFINE_SCSI_DISK_PROPERTIES(), DEFINE_PROP_END_OF_LIST(), @@ -1356,6 +1382,7 @@ static SCSIDeviceInfo scsi_disk_info[] = { .init = scsi_disk_initfn, .destroy = scsi_destroy, .alloc_req = scsi_new_request, + .unit_attention_reported = scsi_disk_unit_attention_reported, .qdev.props = (Property[]) { DEFINE_SCSI_DISK_PROPERTIES(), DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false), -- cgit v1.1 From f01b59319c70cb71e6e1003948e49e1aa32e3dae Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 17 Oct 2011 16:39:27 +0200 Subject: scsi-disk: fix coding style issues (braces) Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- hw/scsi-disk.c | 71 +++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 25 deletions(-) (limited to 'hw/scsi-disk.c') diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 880cb22..3a08848 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -350,9 +350,9 @@ static void scsi_dma_restart_cb(void *opaque, int running, RunState state) { SCSIDiskState *s = opaque; - if (!running) + if (!running) { return; - + } if (!s->bh) { s->bh = qemu_bh_new(scsi_dma_restart_bh, s); qemu_bh_schedule(s->bh); @@ -403,8 +403,9 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) "buffer size %zd\n", req->cmd.xfer); pages = buflen++; outbuf[buflen++] = 0x00; // list of supported pages (this page) - if (s->serial) + if (s->serial) { outbuf[buflen++] = 0x80; // unit serial number + } outbuf[buflen++] = 0x83; // device identification if (s->qdev.type == TYPE_DISK) { outbuf[buflen++] = 0xb0; // block limits @@ -423,10 +424,12 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) } l = strlen(s->serial); - if (l > req->cmd.xfer) + if (l > req->cmd.xfer) { l = req->cmd.xfer; - if (l > 20) + } + if (l > 20) { l = 20; + } DPRINTF("Inquiry EVPD[Serial number] " "buffer size %zd\n", req->cmd.xfer); @@ -441,8 +444,9 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) int max_len = 255 - 8; int id_len = strlen(bdrv_get_device_name(s->bs)); - if (id_len > max_len) + if (id_len > max_len) { id_len = max_len; + } DPRINTF("Inquiry EVPD[Device identification] " "buffer size %zd\n", req->cmd.xfer); @@ -525,9 +529,9 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) } buflen = req->cmd.xfer; - if (buflen > SCSI_MAX_INQUIRY_LEN) + if (buflen > SCSI_MAX_INQUIRY_LEN) { buflen = SCSI_MAX_INQUIRY_LEN; - + } memset(outbuf, 0, buflen); outbuf[0] = s->qdev.type & 0x1f; @@ -749,8 +753,9 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf) outbuf[7] = 8; /* Block descriptor length */ } nb_sectors /= s->cluster_size; - if (nb_sectors > 0xffffff) + if (nb_sectors > 0xffffff) { nb_sectors = 0; + } p[0] = 0; /* media density code */ p[1] = (nb_sectors >> 16) & 0xff; p[2] = (nb_sectors >> 8) & 0xff; @@ -791,8 +796,9 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf) outbuf[0] = ((buflen - 2) >> 8) & 0xff; outbuf[1] = (buflen - 2) & 0xff; } - if (buflen > r->req.cmd.xfer) + if (buflen > r->req.cmd.xfer) { buflen = r->req.cmd.xfer; + } return buflen; } @@ -826,8 +832,9 @@ static int scsi_disk_emulate_read_toc(SCSIRequest *req, uint8_t *outbuf) default: return -1; } - if (toclen > req->cmd.xfer) + if (toclen > req->cmd.xfer) { toclen = req->cmd.xfer; + } return toclen; } @@ -879,40 +886,48 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) outbuf = r->iov.iov_base; switch (req->cmd.buf[0]) { case TEST_UNIT_READY: - if (s->tray_open || !bdrv_is_inserted(s->bs)) + if (s->tray_open || !bdrv_is_inserted(s->bs)) { goto not_ready; + } break; case INQUIRY: buflen = scsi_disk_emulate_inquiry(req, outbuf); - if (buflen < 0) + if (buflen < 0) { goto illegal_request; + } break; case MODE_SENSE: case MODE_SENSE_10: buflen = scsi_disk_emulate_mode_sense(r, outbuf); - if (buflen < 0) + if (buflen < 0) { goto illegal_request; + } break; case READ_TOC: buflen = scsi_disk_emulate_read_toc(req, outbuf); - if (buflen < 0) + if (buflen < 0) { goto illegal_request; + } break; case RESERVE: - if (req->cmd.buf[1] & 1) + if (req->cmd.buf[1] & 1) { goto illegal_request; + } break; case RESERVE_10: - if (req->cmd.buf[1] & 3) + if (req->cmd.buf[1] & 3) { goto illegal_request; + } break; case RELEASE: - if (req->cmd.buf[1] & 1) + if (req->cmd.buf[1] & 1) { goto illegal_request; + } break; case RELEASE_10: - if (req->cmd.buf[1] & 3) + if (req->cmd.buf[1] & 3) { goto illegal_request; + } break; case START_STOP: if (scsi_disk_emulate_start_stop(r) < 0) { @@ -927,16 +942,18 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) /* The normal LEN field for this command is zero. */ memset(outbuf, 0, 8); bdrv_get_geometry(s->bs, &nb_sectors); - if (!nb_sectors) + if (!nb_sectors) { goto not_ready; + } nb_sectors /= s->cluster_size; /* Returned value is the address of the last sector. */ nb_sectors--; /* Remember the new size for read/write sanity checking. */ s->max_lba = nb_sectors; /* Clip to 2TB, instead of returning capacity modulo 2TB. */ - if (nb_sectors > UINT32_MAX) + if (nb_sectors > UINT32_MAX) { nb_sectors = UINT32_MAX; + } outbuf[0] = (nb_sectors >> 24) & 0xff; outbuf[1] = (nb_sectors >> 16) & 0xff; outbuf[2] = (nb_sectors >> 8) & 0xff; @@ -960,8 +977,9 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) DPRINTF("SAI READ CAPACITY(16)\n"); memset(outbuf, 0, req->cmd.xfer); bdrv_get_geometry(s->bs, &nb_sectors); - if (!nb_sectors) + if (!nb_sectors) { goto not_ready; + } nb_sectors /= s->cluster_size; /* Returned value is the address of the last sector. */ nb_sectors--; @@ -1078,8 +1096,9 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) case READ_16: 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) + if (r->req.cmd.lba > s->max_lba) { goto illegal_lba; + } r->sector = r->req.cmd.lba * s->cluster_size; r->sector_count = len * s->cluster_size; break; @@ -1094,8 +1113,9 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) DPRINTF("Write %s(sector %" PRId64 ", count %d)\n", (command & 0xe) == 0xe ? "And Verify " : "", r->req.cmd.lba, len); - if (r->req.cmd.lba > s->max_lba) + if (r->req.cmd.lba > s->max_lba) { goto illegal_lba; + } r->sector = r->req.cmd.lba * s->cluster_size; r->sector_count = len * s->cluster_size; break; @@ -1168,8 +1188,9 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { return -len; } else { - if (!r->sector_count) + if (!r->sector_count) { r->sector_count = -1; + } return len; } } -- cgit v1.1 From b6c251ab1739cd67817db003e9878f92c3feff2b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 13 Sep 2011 14:33:47 +0200 Subject: scsi-disk: add stubs for more MMC commands This patch adds a few stub implementations for MMC commands to scsi-disk, to be filled in later in the series. It also adds to scsi-defs.h constants for commands implemented by ide/atapi.c, when missing. Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- hw/scsi-disk.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 5 deletions(-) (limited to 'hw/scsi-disk.c') diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 3a08848..bdb98ef 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -567,6 +567,43 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) return buflen; } +static int scsi_read_dvd_structure(SCSIDiskState *s, SCSIDiskReq *r, + uint8_t *outbuf) +{ + scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE)); + return -1; +} + +static int scsi_get_event_status_notification(SCSIDiskState *s, + SCSIDiskReq *r, uint8_t *outbuf) +{ + scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE)); + return -1; +} + +static int scsi_get_configuration(SCSIDiskState *s, SCSIDiskReq *r, + uint8_t *outbuf) +{ + if (s->qdev.type != TYPE_ROM) { + return -1; + } + memset(outbuf, 0, 8); + /* ??? This should probably return much more information. For now + just return the basic header indicating the CD-ROM profile. */ + outbuf[7] = 8; /* CD-ROM */ + return 8; +} + +static int scsi_emulate_mechanism_status(SCSIDiskState *s, uint8_t *outbuf) +{ + if (s->qdev.type != TYPE_ROM) { + return -1; + } + memset(outbuf, 0, 8); + outbuf[5] = 1; /* CD-ROM */ + return 8; +} + static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, int page_control) { @@ -964,12 +1001,29 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) outbuf[7] = 0; buflen = 8; break; + case MECHANISM_STATUS: + buflen = scsi_emulate_mechanism_status(s, outbuf); + if (buflen < 0) { + goto illegal_request; + } + break; case GET_CONFIGURATION: - memset(outbuf, 0, 8); - /* ??? This should probably return much more information. For now - just return the basic header indicating the CD-ROM profile. */ - outbuf[7] = 8; // CD-ROM - buflen = 8; + buflen = scsi_get_configuration(s, r, outbuf); + if (buflen < 0) { + goto illegal_request; + } + break; + case GET_EVENT_STATUS_NOTIFICATION: + buflen = scsi_get_event_status_notification(s, r, outbuf); + if (buflen < 0) { + goto illegal_request; + } + break; + case READ_DVD_STRUCTURE: + buflen = scsi_read_dvd_structure(s, r, outbuf); + if (buflen < 0) { + goto illegal_request; + } break; case SERVICE_ACTION_IN_16: /* Service Action In subcommands. */ @@ -1073,7 +1127,10 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) case ALLOW_MEDIUM_REMOVAL: case READ_CAPACITY_10: case READ_TOC: + case READ_DVD_STRUCTURE: case GET_CONFIGURATION: + case GET_EVENT_STATUS_NOTIFICATION: + case MECHANISM_STATUS: case SERVICE_ACTION_IN_16: case VERIFY_10: rc = scsi_disk_emulate_command(r); -- cgit v1.1 From a8f4bbe2900f0ebdead032fb8da137777bddd925 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 13 Sep 2011 14:50:15 +0200 Subject: scsi-disk: store valid mode pages in a table A small refactoring of the MODE SENSE implementation in scsi-disk. Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- hw/scsi-disk.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'hw/scsi-disk.c') diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index bdb98ef..8f3ada6 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -607,10 +607,23 @@ static int scsi_emulate_mechanism_status(SCSIDiskState *s, uint8_t *outbuf) static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, int page_control) { + static const int mode_sense_valid[0x3f] = { + [MODE_PAGE_HD_GEOMETRY] = (1 << TYPE_DISK), + [MODE_PAGE_FLEXIBLE_DISK_GEOMETRY] = (1 << TYPE_DISK), + [MODE_PAGE_CACHING] = (1 << TYPE_DISK) | (1 << TYPE_ROM), + [MODE_PAGE_CAPABILITIES] = (1 << TYPE_ROM), + }; + BlockDriverState *bdrv = s->bs; int cylinders, heads, secs; uint8_t *p = *p_outbuf; + if ((mode_sense_valid[page] & (1 << s->qdev.type)) == 0) { + return -1; + } + + p[0] = page; + /* * If Changeable Values are requested, a mask denoting those mode parameters * that are changeable shall be returned. As we currently don't support @@ -619,10 +632,6 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, */ switch (page) { case MODE_PAGE_HD_GEOMETRY: - if (s->qdev.type == TYPE_ROM) { - return -1; - } - p[0] = 4; p[1] = 0x16; if (page_control == 1) { /* Changeable Values */ break; @@ -654,10 +663,6 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, break; case MODE_PAGE_FLEXIBLE_DISK_GEOMETRY: - if (s->qdev.type == TYPE_ROM) { - return -1; - } - p[0] = 5; p[1] = 0x1e; if (page_control == 1) { /* Changeable Values */ break; @@ -707,10 +712,6 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, break; case MODE_PAGE_CAPABILITIES: - if (s->qdev.type != TYPE_ROM) { - return -1; - } - p[0] = 0x2a; p[1] = 0x14; if (page_control == 1) { /* Changeable Values */ break; -- cgit v1.1 From a07c7dcd6f33b668747148ac28c0e147f958aa18 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 13 Sep 2011 15:08:22 +0200 Subject: atapi/scsi-disk: make mode page values coherent between the two This patch adds to scsi-disk the missing mode page 0x01 for both disk and CD-ROM drives, and mode page 0x0e for CD drives only. A few offsets were wrong in atapi.c. Also change the 2Ah mode page to expose DVD media read capabilities in the IDE cdrom. This lets you run dvd+rw-mediainfo on the virtual DVD drives. Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- hw/scsi-disk.c | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) (limited to 'hw/scsi-disk.c') diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 8f3ada6..116e562 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -611,6 +611,8 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, [MODE_PAGE_HD_GEOMETRY] = (1 << TYPE_DISK), [MODE_PAGE_FLEXIBLE_DISK_GEOMETRY] = (1 << TYPE_DISK), [MODE_PAGE_CACHING] = (1 << TYPE_DISK) | (1 << TYPE_ROM), + [MODE_PAGE_R_W_ERROR] = (1 << TYPE_DISK) | (1 << TYPE_ROM), + [MODE_PAGE_AUDIO_CTL] = (1 << TYPE_ROM), [MODE_PAGE_CAPABILITIES] = (1 << TYPE_ROM), }; @@ -711,13 +713,26 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, } break; + case MODE_PAGE_R_W_ERROR: + p[1] = 10; + p[2] = 0x80; /* Automatic Write Reallocation Enabled */ + if (s->qdev.type == TYPE_ROM) { + p[3] = 0x20; /* Read Retry Count */ + } + break; + + case MODE_PAGE_AUDIO_CTL: + p[1] = 14; + break; + case MODE_PAGE_CAPABILITIES: p[1] = 0x14; if (page_control == 1) { /* Changeable Values */ break; } - p[2] = 3; // CD-R & CD-RW read - p[3] = 0; // Writing not supported + + p[2] = 0x3b; /* CD-R & CD-RW read */ + p[3] = 0; /* Writing not supported */ p[4] = 0x7f; /* Audio, composite, digital out, mode 2 form 1&2, multi session */ p[5] = 0xff; /* CD DA, DA accurate, RW supported, @@ -727,17 +742,17 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, /* Locking supported, jumper present, eject, tray */ p[7] = 0; /* no volume & mute control, no changer */ - p[8] = (50 * 176) >> 8; // 50x read speed + p[8] = (50 * 176) >> 8; /* 50x read speed */ p[9] = (50 * 176) & 0xff; - p[10] = 0 >> 8; // No volume - p[11] = 0 & 0xff; - p[12] = 2048 >> 8; // 2M buffer + p[10] = 2 >> 8; /* Two volume levels */ + p[11] = 2 & 0xff; + p[12] = 2048 >> 8; /* 2M buffer */ p[13] = 2048 & 0xff; - p[14] = (16 * 176) >> 8; // 16x read speed current + p[14] = (16 * 176) >> 8; /* 16x read speed current */ p[15] = (16 * 176) & 0xff; - p[18] = (16 * 176) >> 8; // 16x write speed + p[18] = (16 * 176) >> 8; /* 16x write speed */ p[19] = (16 * 176) & 0xff; - p[20] = (16 * 176) >> 8; // 16x write speed current + p[20] = (16 * 176) >> 8; /* 16x write speed current */ p[21] = (16 * 176) & 0xff; break; -- cgit v1.1 From 430ee2f26f01f146f3757467b3f0b802ad309ff8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 13 Sep 2011 15:22:31 +0200 Subject: scsi-disk: support DVD profile in GET CONFIGURATION Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- hw/scsi-disk.c | 50 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 8 deletions(-) (limited to 'hw/scsi-disk.c') diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 116e562..38b196b 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -567,6 +567,19 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) return buflen; } +static inline bool media_is_dvd(SCSIDiskState *s) +{ + uint64_t nb_sectors; + if (s->qdev.type != TYPE_ROM) { + return false; + } + if (!bdrv_is_inserted(s->bs)) { + return false; + } + bdrv_get_geometry(s->bs, &nb_sectors); + return nb_sectors > CD_MAX_SECTORS; +} + static int scsi_read_dvd_structure(SCSIDiskState *s, SCSIDiskReq *r, uint8_t *outbuf) { @@ -581,17 +594,38 @@ static int scsi_get_event_status_notification(SCSIDiskState *s, return -1; } -static int scsi_get_configuration(SCSIDiskState *s, SCSIDiskReq *r, - uint8_t *outbuf) +static int scsi_get_configuration(SCSIDiskState *s, uint8_t *outbuf) { + int current; + if (s->qdev.type != TYPE_ROM) { return -1; } - memset(outbuf, 0, 8); - /* ??? This should probably return much more information. For now - just return the basic header indicating the CD-ROM profile. */ - outbuf[7] = 8; /* CD-ROM */ - return 8; + current = media_is_dvd(s) ? MMC_PROFILE_DVD_ROM : MMC_PROFILE_CD_ROM; + memset(outbuf, 0, 40); + stl_be_p(&outbuf[0], 36); /* Bytes after the data length field */ + stw_be_p(&outbuf[6], current); + /* outbuf[8] - outbuf[19]: Feature 0 - Profile list */ + outbuf[10] = 0x03; /* persistent, current */ + outbuf[11] = 8; /* two profiles */ + stw_be_p(&outbuf[12], MMC_PROFILE_DVD_ROM); + outbuf[14] = (current == MMC_PROFILE_DVD_ROM); + stw_be_p(&outbuf[16], MMC_PROFILE_CD_ROM); + outbuf[18] = (current == MMC_PROFILE_CD_ROM); + /* outbuf[20] - outbuf[31]: Feature 1 - Core feature */ + stw_be_p(&outbuf[20], 1); + outbuf[22] = 0x08 | 0x03; /* version 2, persistent, current */ + outbuf[23] = 8; + stl_be_p(&outbuf[24], 1); /* SCSI */ + outbuf[28] = 1; /* DBE = 1, mandatory */ + /* outbuf[32] - outbuf[39]: Feature 3 - Removable media feature */ + stw_be_p(&outbuf[32], 3); + outbuf[34] = 0x08 | 0x03; /* version 2, persistent, current */ + outbuf[35] = 4; + outbuf[36] = 0x39; /* tray, load=1, eject=1, unlocked at powerup, lock=1 */ + /* TODO: Random readable, CD read, DVD read, drive serial number, + power management */ + return 40; } static int scsi_emulate_mechanism_status(SCSIDiskState *s, uint8_t *outbuf) @@ -1024,7 +1058,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) } break; case GET_CONFIGURATION: - buflen = scsi_get_configuration(s, r, outbuf); + buflen = scsi_get_configuration(s, outbuf); if (buflen < 0) { goto illegal_request; } -- cgit v1.1 From ceb792ef299a1bb0144c56fab45631d17c35e7f5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 13 Sep 2011 15:57:15 +0200 Subject: scsi-disk: support READ DVD STRUCTURE Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- hw/scsi-disk.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) (limited to 'hw/scsi-disk.c') diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 38b196b..8f8a94d 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -580,10 +580,110 @@ static inline bool media_is_dvd(SCSIDiskState *s) return nb_sectors > CD_MAX_SECTORS; } +static inline bool media_is_cd(SCSIDiskState *s) +{ + uint64_t nb_sectors; + if (s->qdev.type != TYPE_ROM) { + return false; + } + if (!bdrv_is_inserted(s->bs)) { + return false; + } + bdrv_get_geometry(s->bs, &nb_sectors); + return nb_sectors <= CD_MAX_SECTORS; +} + static int scsi_read_dvd_structure(SCSIDiskState *s, SCSIDiskReq *r, uint8_t *outbuf) { - scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE)); + static const int rds_caps_size[5] = { + [0] = 2048 + 4, + [1] = 4 + 4, + [3] = 188 + 4, + [4] = 2048 + 4, + }; + + uint8_t media = r->req.cmd.buf[1]; + uint8_t layer = r->req.cmd.buf[6]; + uint8_t format = r->req.cmd.buf[7]; + int size = -1; + + if (s->qdev.type != TYPE_ROM) { + return -1; + } + if (media != 0) { + scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); + return -1; + } + + if (format != 0xff) { + if (s->tray_open || !bdrv_is_inserted(s->bs)) { + scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); + return -1; + } + if (media_is_cd(s)) { + scsi_check_condition(r, SENSE_CODE(INCOMPATIBLE_FORMAT)); + return -1; + } + if (format >= ARRAY_SIZE(rds_caps_size)) { + return -1; + } + size = rds_caps_size[format]; + memset(outbuf, 0, size); + } + + switch (format) { + case 0x00: { + /* Physical format information */ + uint64_t nb_sectors; + if (layer != 0) { + goto fail; + } + bdrv_get_geometry(s->bs, &nb_sectors); + + outbuf[4] = 1; /* DVD-ROM, part version 1 */ + outbuf[5] = 0xf; /* 120mm disc, minimum rate unspecified */ + outbuf[6] = 1; /* one layer, read-only (per MMC-2 spec) */ + outbuf[7] = 0; /* default densities */ + + stl_be_p(&outbuf[12], (nb_sectors >> 2) - 1); /* end sector */ + stl_be_p(&outbuf[16], (nb_sectors >> 2) - 1); /* l0 end sector */ + break; + } + + case 0x01: /* DVD copyright information, all zeros */ + break; + + case 0x03: /* BCA information - invalid field for no BCA info */ + return -1; + + case 0x04: /* DVD disc manufacturing information, all zeros */ + break; + + case 0xff: { /* List capabilities */ + int i; + size = 4; + for (i = 0; i < ARRAY_SIZE(rds_caps_size); i++) { + if (!rds_caps_size[i]) { + continue; + } + outbuf[size] = i; + outbuf[size + 1] = 0x40; /* Not writable, readable */ + stw_be_p(&outbuf[size + 2], rds_caps_size[i]); + size += 4; + } + break; + } + + default: + return -1; + } + + /* Size of buffer, not including 2 byte size field */ + stw_be_p(outbuf, size - 2); + return size; + +fail: return -1; } -- cgit v1.1 From 3c2f7c12c2e19707cb4e28dd57180f7be3dd4950 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 13 Sep 2011 16:26:51 +0200 Subject: scsi-disk: report media changed via GET EVENT STATUS NOTIFICATION This adds support for media change notification via the GET EVENT STATUS NOTIFICATION command, used by Linux versions 2.6.38 and newer. Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- hw/scsi-disk.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 4 deletions(-) (limited to 'hw/scsi-disk.c') diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 8f8a94d..dd2b605 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -72,6 +72,7 @@ struct SCSIDiskState uint32_t removable; uint64_t max_lba; bool media_changed; + bool media_event; QEMUBH *bh; char *version; char *serial; @@ -687,11 +688,58 @@ fail: return -1; } -static int scsi_get_event_status_notification(SCSIDiskState *s, - SCSIDiskReq *r, uint8_t *outbuf) +static int scsi_event_status_media(SCSIDiskState *s, uint8_t *outbuf) { - scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE)); - return -1; + uint8_t event_code, media_status; + + media_status = 0; + if (s->tray_open) { + media_status = MS_TRAY_OPEN; + } else if (bdrv_is_inserted(s->bs)) { + media_status = MS_MEDIA_PRESENT; + } + + /* Event notification descriptor */ + event_code = MEC_NO_CHANGE; + if (media_status != MS_TRAY_OPEN && s->media_event) { + event_code = MEC_NEW_MEDIA; + s->media_event = false; + } + + outbuf[0] = event_code; + outbuf[1] = media_status; + + /* These fields are reserved, just clear them. */ + outbuf[2] = 0; + outbuf[3] = 0; + return 4; +} + +static int scsi_get_event_status_notification(SCSIDiskState *s, SCSIDiskReq *r, + uint8_t *outbuf) +{ + int size; + uint8_t *buf = r->req.cmd.buf; + uint8_t notification_class_request = buf[4]; + if (s->qdev.type != TYPE_ROM) { + return -1; + } + if ((buf[1] & 1) == 0) { + /* asynchronous */ + return -1; + } + + size = 4; + outbuf[0] = outbuf[1] = 0; + outbuf[3] = 1 << GESN_MEDIA; /* supported events */ + if (notification_class_request & (1 << GESN_MEDIA)) { + outbuf[2] = GESN_MEDIA; + size += scsi_event_status_media(s, &outbuf[size]); + } else { + outbuf[2] = 0x80; + } + stw_be_p(outbuf, size - 4); + return size; } static int scsi_get_configuration(SCSIDiskState *s, uint8_t *outbuf) @@ -1442,6 +1490,7 @@ static void scsi_cd_change_media_cb(void *opaque, bool load) s->media_changed = load; s->tray_open = !load; s->qdev.unit_attention = SENSE_CODE(UNIT_ATTENTION_NO_MEDIUM); + s->media_event = true; } static bool scsi_cd_is_tray_open(void *opaque) -- cgit v1.1 From afd4030c16d290e460cc93f8f9e353516b5451a2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 13 Aug 2011 15:44:45 +0200 Subject: scsi: move tcq/ndev to SCSIBusOps (now SCSIBusInfo) Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- hw/scsi-disk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'hw/scsi-disk.c') diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index dd2b605..50fc3d6 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -564,7 +564,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) } /* Sync data transfer and TCQ. */ - outbuf[7] = 0x10 | (req->bus->tcq ? 0x02 : 0); + outbuf[7] = 0x10 | (req->bus->info->tcq ? 0x02 : 0); return buflen; } -- cgit v1.1 From 7cec78b6f0e9a3b33732afd14a811849d219ffbc Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 3 Oct 2011 13:57:06 +0200 Subject: scsi-disk: fail READ CAPACITY if LBA != 0 but PMI == 0 Tested by the Windows Logo Kit SCSI Compliance test. From SBC-3, paragraph 5.25: "The LOGICAL BLOCK ADDRESS field shall be set to zero if the PMI bit is set to zero. If the PMI bit is set to zero and the LOGICAL BLOCK ADDRESS field is not set to zero, then the device server shall terminate the command with CHECK CONDITION status with the sense key set to ILLEGAL REQUEST and the additional sense code set to INVALID FIELD IN CDB". Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- hw/scsi-disk.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'hw/scsi-disk.c') diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 50fc3d6..6b139ac 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -1180,6 +1180,9 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) if (!nb_sectors) { goto not_ready; } + if ((req->cmd.buf[8] & 1) == 0 && req->cmd.lba) { + goto illegal_request; + } nb_sectors /= s->cluster_size; /* Returned value is the address of the last sector. */ nb_sectors--; @@ -1232,6 +1235,9 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) if (!nb_sectors) { goto not_ready; } + if ((req->cmd.buf[14] & 1) == 0 && req->cmd.lba) { + goto illegal_request; + } nb_sectors /= s->cluster_size; /* Returned value is the address of the last sector. */ nb_sectors--; -- cgit v1.1 From 628e95b61650f00607e8ddcf952b2e93f6ac3c89 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 4 Oct 2011 15:36:05 +0200 Subject: scsi-disk: fix retrying a flush Flush does not go anymore through scsi_disk_emulate_command. Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- hw/scsi-disk.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'hw/scsi-disk.c') diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 6b139ac..e800ab3 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -81,7 +81,7 @@ struct SCSIDiskState }; static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type); -static int scsi_disk_emulate_command(SCSIDiskReq *r); +static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf); static void scsi_free_request(SCSIRequest *req) { @@ -325,7 +325,6 @@ static void scsi_dma_restart_bh(void *opaque) r = DO_UPCAST(SCSIDiskReq, req, req); if (r->status & SCSI_REQ_STATUS_RETRY) { int status = r->status; - int ret; r->status &= ~(SCSI_REQ_STATUS_RETRY | SCSI_REQ_STATUS_RETRY_TYPE_MASK); @@ -338,10 +337,8 @@ static void scsi_dma_restart_bh(void *opaque) scsi_write_data(&r->req); break; case SCSI_REQ_STATUS_RETRY_FLUSH: - ret = scsi_disk_emulate_command(r); - if (ret == 0) { - scsi_req_complete(&r->req, GOOD); - } + scsi_send_command(&r->req, r->req.cmd.buf); + break; } } } -- cgit v1.1 From 44740c38168bd46e2a99094a42cbdbb781299604 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 12 Oct 2011 12:54:16 +0200 Subject: scsi-disk: do not duplicate BlockDriverState member Same as for scsi-generic, avoid duplication even if it causes longer lines. Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- hw/scsi-disk.c | 92 ++++++++++++++++++++++++++++------------------------------ 1 file changed, 45 insertions(+), 47 deletions(-) (limited to 'hw/scsi-disk.c') diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index e800ab3..8a05031 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -65,7 +65,6 @@ typedef struct SCSIDiskReq { struct SCSIDiskState { SCSIDevice qdev; - BlockDriverState *bs; /* The qemu block layer uses a fixed 512 byte sector size. This is the number of 512 byte blocks in a single scsi sector. */ int cluster_size; @@ -119,7 +118,7 @@ static uint32_t scsi_init_iovec(SCSIDiskReq *r) if (!r->iov.iov_base) { r->buflen = SCSI_DMA_BUF_SIZE; - r->iov.iov_base = qemu_blockalign(s->bs, r->buflen); + r->iov.iov_base = qemu_blockalign(s->qdev.conf.bs, r->buflen); } r->iov.iov_len = MIN(r->sector_count * 512, r->buflen); qemu_iovec_init_external(&r->qiov, &r->iov, 1); @@ -134,7 +133,7 @@ static void scsi_read_complete(void * opaque, int ret) if (r->req.aiocb != NULL) { r->req.aiocb = NULL; - bdrv_acct_done(s->bs, &r->acct); + bdrv_acct_done(s->qdev.conf.bs, &r->acct); } if (ret) { @@ -158,7 +157,7 @@ static void scsi_flush_complete(void * opaque, int ret) if (r->req.aiocb != NULL) { r->req.aiocb = NULL; - bdrv_acct_done(s->bs, &r->acct); + bdrv_acct_done(s->qdev.conf.bs, &r->acct); } if (ret < 0) { @@ -203,8 +202,8 @@ static void scsi_read_data(SCSIRequest *req) scsi_read_complete(r, -ENOMEDIUM); } n = scsi_init_iovec(r); - bdrv_acct_start(s->bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ); - r->req.aiocb = bdrv_aio_readv(s->bs, r->sector, &r->qiov, n, + bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ); + r->req.aiocb = bdrv_aio_readv(s->qdev.conf.bs, r->sector, &r->qiov, n, scsi_read_complete, r); if (r->req.aiocb == NULL) { scsi_read_complete(r, -EIO); @@ -215,10 +214,10 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) { int is_read = (type == SCSI_REQ_STATUS_RETRY_READ); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - BlockErrorAction action = bdrv_get_on_error(s->bs, is_read); + BlockErrorAction action = bdrv_get_on_error(s->qdev.conf.bs, is_read); if (action == BLOCK_ERR_IGNORE) { - bdrv_mon_event(s->bs, BDRV_ACTION_IGNORE, is_read); + bdrv_mon_event(s->qdev.conf.bs, BDRV_ACTION_IGNORE, is_read); return 0; } @@ -228,9 +227,9 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) type &= SCSI_REQ_STATUS_RETRY_TYPE_MASK; r->status |= SCSI_REQ_STATUS_RETRY | type; - bdrv_mon_event(s->bs, BDRV_ACTION_STOP, is_read); + bdrv_mon_event(s->qdev.conf.bs, BDRV_ACTION_STOP, is_read); vm_stop(RUN_STATE_IO_ERROR); - bdrv_iostatus_set_err(s->bs, error); + bdrv_iostatus_set_err(s->qdev.conf.bs, error); } else { switch (error) { case ENOMEDIUM: @@ -246,7 +245,7 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) scsi_check_condition(r, SENSE_CODE(IO_ERROR)); break; } - bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read); + bdrv_mon_event(s->qdev.conf.bs, BDRV_ACTION_REPORT, is_read); } return 1; } @@ -259,7 +258,7 @@ static void scsi_write_complete(void * opaque, int ret) if (r->req.aiocb != NULL) { r->req.aiocb = NULL; - bdrv_acct_done(s->bs, &r->acct); + bdrv_acct_done(s->qdev.conf.bs, &r->acct); } if (ret) { @@ -300,8 +299,8 @@ static void scsi_write_data(SCSIRequest *req) if (s->tray_open) { scsi_write_complete(r, -ENOMEDIUM); } - bdrv_acct_start(s->bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_WRITE); - r->req.aiocb = bdrv_aio_writev(s->bs, r->sector, &r->qiov, n, + bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_WRITE); + r->req.aiocb = bdrv_aio_writev(s->qdev.conf.bs, r->sector, &r->qiov, n, scsi_write_complete, r); if (r->req.aiocb == NULL) { scsi_write_complete(r, -ENOMEM); @@ -440,7 +439,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) case 0x83: /* Device identification page, mandatory */ { int max_len = 255 - 8; - int id_len = strlen(bdrv_get_device_name(s->bs)); + int id_len = strlen(bdrv_get_device_name(s->qdev.conf.bs)); if (id_len > max_len) { id_len = max_len; @@ -454,7 +453,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) outbuf[buflen++] = 0; // reserved outbuf[buflen++] = id_len; // length of data following - memcpy(outbuf+buflen, bdrv_get_device_name(s->bs), id_len); + memcpy(outbuf+buflen, bdrv_get_device_name(s->qdev.conf.bs), id_len); buflen += id_len; break; } @@ -571,10 +570,10 @@ static inline bool media_is_dvd(SCSIDiskState *s) if (s->qdev.type != TYPE_ROM) { return false; } - if (!bdrv_is_inserted(s->bs)) { + if (!bdrv_is_inserted(s->qdev.conf.bs)) { return false; } - bdrv_get_geometry(s->bs, &nb_sectors); + bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors); return nb_sectors > CD_MAX_SECTORS; } @@ -584,10 +583,10 @@ static inline bool media_is_cd(SCSIDiskState *s) if (s->qdev.type != TYPE_ROM) { return false; } - if (!bdrv_is_inserted(s->bs)) { + if (!bdrv_is_inserted(s->qdev.conf.bs)) { return false; } - bdrv_get_geometry(s->bs, &nb_sectors); + bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors); return nb_sectors <= CD_MAX_SECTORS; } @@ -615,7 +614,7 @@ static int scsi_read_dvd_structure(SCSIDiskState *s, SCSIDiskReq *r, } if (format != 0xff) { - if (s->tray_open || !bdrv_is_inserted(s->bs)) { + if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) { scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); return -1; } @@ -637,7 +636,7 @@ static int scsi_read_dvd_structure(SCSIDiskState *s, SCSIDiskReq *r, if (layer != 0) { goto fail; } - bdrv_get_geometry(s->bs, &nb_sectors); + bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors); outbuf[4] = 1; /* DVD-ROM, part version 1 */ outbuf[5] = 0xf; /* 120mm disc, minimum rate unspecified */ @@ -692,7 +691,7 @@ static int scsi_event_status_media(SCSIDiskState *s, uint8_t *outbuf) media_status = 0; if (s->tray_open) { media_status = MS_TRAY_OPEN; - } else if (bdrv_is_inserted(s->bs)) { + } else if (bdrv_is_inserted(s->qdev.conf.bs)) { media_status = MS_MEDIA_PRESENT; } @@ -795,7 +794,7 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, [MODE_PAGE_CAPABILITIES] = (1 << TYPE_ROM), }; - BlockDriverState *bdrv = s->bs; + BlockDriverState *bdrv = s->qdev.conf.bs; int cylinders, heads, secs; uint8_t *p = *p_outbuf; @@ -887,7 +886,7 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, if (page_control == 1) { /* Changeable Values */ break; } - if (bdrv_enable_write_cache(s->bs)) { + if (bdrv_enable_write_cache(s->qdev.conf.bs)) { p[2] = 4; /* WCE */ } break; @@ -959,7 +958,7 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf) memset(outbuf, 0, r->req.cmd.xfer); p = outbuf; - if (bdrv_is_read_only(s->bs)) { + if (bdrv_is_read_only(s->qdev.conf.bs)) { dev_specific_param = 0x80; /* Readonly. */ } else { dev_specific_param = 0x00; @@ -977,7 +976,7 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf) p += 8; } - bdrv_get_geometry(s->bs, &nb_sectors); + bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors); if (!dbd && nb_sectors) { if (r->req.cmd.buf[0] == MODE_SENSE) { outbuf[3] = 8; /* Block descriptor length */ @@ -1043,7 +1042,7 @@ static int scsi_disk_emulate_read_toc(SCSIRequest *req, uint8_t *outbuf) msf = req->cmd.buf[1] & 2; format = req->cmd.buf[2] & 0xf; start_track = req->cmd.buf[6]; - bdrv_get_geometry(s->bs, &nb_sectors); + bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors); DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1); nb_sectors /= s->cluster_size; switch (format) { @@ -1080,12 +1079,12 @@ static int scsi_disk_emulate_start_stop(SCSIDiskReq *r) if (s->qdev.type == TYPE_ROM && loej) { if (!start && !s->tray_open && s->tray_locked) { scsi_check_condition(r, - bdrv_is_inserted(s->bs) + bdrv_is_inserted(s->qdev.conf.bs) ? SENSE_CODE(ILLEGAL_REQ_REMOVAL_PREVENTED) : SENSE_CODE(NOT_READY_REMOVAL_PREVENTED)); return -1; } - bdrv_eject(s->bs, !start); + bdrv_eject(s->qdev.conf.bs, !start); s->tray_open = !start; } return 0; @@ -1112,13 +1111,13 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) goto illegal_request; } r->buflen = MAX(4096, req->cmd.xfer); - r->iov.iov_base = qemu_blockalign(s->bs, r->buflen); + r->iov.iov_base = qemu_blockalign(s->qdev.conf.bs, r->buflen); } outbuf = r->iov.iov_base; switch (req->cmd.buf[0]) { case TEST_UNIT_READY: - if (s->tray_open || !bdrv_is_inserted(s->bs)) { + if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) { goto not_ready; } break; @@ -1168,12 +1167,12 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) break; case ALLOW_MEDIUM_REMOVAL: s->tray_locked = req->cmd.buf[4] & 1; - bdrv_lock_medium(s->bs, req->cmd.buf[4] & 1); + bdrv_lock_medium(s->qdev.conf.bs, req->cmd.buf[4] & 1); break; case READ_CAPACITY_10: /* The normal LEN field for this command is zero. */ memset(outbuf, 0, 8); - bdrv_get_geometry(s->bs, &nb_sectors); + bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors); if (!nb_sectors) { goto not_ready; } @@ -1228,7 +1227,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) if ((req->cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) { DPRINTF("SAI READ CAPACITY(16)\n"); memset(outbuf, 0, req->cmd.xfer); - bdrv_get_geometry(s->bs, &nb_sectors); + bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors); if (!nb_sectors) { goto not_ready; } @@ -1275,7 +1274,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) return buflen; not_ready: - if (s->tray_open || !bdrv_is_inserted(s->bs)) { + if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) { scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); } else { scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY)); @@ -1342,8 +1341,8 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) r->iov.iov_len = rc; break; case SYNCHRONIZE_CACHE: - bdrv_acct_start(s->bs, &r->acct, 0, BDRV_ACCT_FLUSH); - r->req.aiocb = bdrv_aio_flush(s->bs, scsi_flush_complete, r); + bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH); + r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_flush_complete, r); if (r->req.aiocb == NULL) { scsi_flush_complete(r, -EIO); } @@ -1418,7 +1417,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) goto fail; } - rc = bdrv_discard(s->bs, r->req.cmd.lba * s->cluster_size, + rc = bdrv_discard(s->qdev.conf.bs, r->req.cmd.lba * s->cluster_size, len * s->cluster_size); if (rc < 0) { /* XXX: better error code ?*/ @@ -1460,7 +1459,7 @@ static void scsi_disk_reset(DeviceState *dev) scsi_device_purge_requests(&s->qdev, SENSE_CODE(RESET)); - bdrv_get_geometry(s->bs, &nb_sectors); + bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors); nb_sectors /= s->cluster_size; if (nb_sectors) { nb_sectors--; @@ -1530,16 +1529,15 @@ static int scsi_initfn(SCSIDevice *dev, uint8_t scsi_type) error_report("scsi-disk: drive property not set"); return -1; } - s->bs = s->qdev.conf.bs; - if (scsi_type == TYPE_DISK && !bdrv_is_inserted(s->bs)) { + if (scsi_type == TYPE_DISK && !bdrv_is_inserted(s->qdev.conf.bs)) { error_report("Device needs media, but drive is empty"); return -1; } if (!s->serial) { /* try to fall back to value set with legacy -drive serial=... */ - dinfo = drive_get_by_blockdev(s->bs); + dinfo = drive_get_by_blockdev(s->qdev.conf.bs); if (*dinfo->serial) { s->serial = g_strdup(dinfo->serial); } @@ -1549,13 +1547,13 @@ static int scsi_initfn(SCSIDevice *dev, uint8_t scsi_type) s->version = g_strdup(QEMU_VERSION); } - if (bdrv_is_sg(s->bs)) { + if (bdrv_is_sg(s->qdev.conf.bs)) { error_report("scsi-disk: unwanted /dev/sg*"); return -1; } if (scsi_type == TYPE_ROM) { - bdrv_set_dev_ops(s->bs, &scsi_cd_block_ops, s); + bdrv_set_dev_ops(s->qdev.conf.bs, &scsi_cd_block_ops, s); s->qdev.blocksize = 2048; } else if (scsi_type == TYPE_DISK) { s->qdev.blocksize = s->qdev.conf.logical_block_size; @@ -1564,11 +1562,11 @@ static int scsi_initfn(SCSIDevice *dev, uint8_t scsi_type) return -1; } s->cluster_size = s->qdev.blocksize / 512; - bdrv_set_buffer_alignment(s->bs, s->qdev.blocksize); + bdrv_set_buffer_alignment(s->qdev.conf.bs, s->qdev.blocksize); s->qdev.type = scsi_type; qemu_add_vm_change_state_handler(scsi_dma_restart_cb, s); - bdrv_iostatus_enable(s->bs); + bdrv_iostatus_enable(s->qdev.conf.bs); add_boot_device_path(s->qdev.conf.bootindex, &dev->qdev, ",0"); return 0; } -- cgit v1.1 From 69377307b25f12235560d21050808a8de5793199 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 13 Oct 2011 10:35:46 +0200 Subject: scsi-disk: remove cluster_size This field is redundant, and having it makes it more complicated to share reqops between the upcoming scsi-block and scsi-generic. Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- hw/scsi-disk.c | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) (limited to 'hw/scsi-disk.c') diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 8a05031..f89e6c5 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -65,9 +65,6 @@ typedef struct SCSIDiskReq { struct SCSIDiskState { SCSIDevice qdev; - /* The qemu block layer uses a fixed 512 byte sector size. - This is the number of 512 byte blocks in a single scsi sector. */ - int cluster_size; uint32_t removable; uint64_t max_lba; bool media_changed; @@ -854,7 +851,7 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, bdrv_get_geometry_hint(bdrv, &cylinders, &heads, &secs); p[4] = heads & 0xff; p[5] = secs & 0xff; - p[6] = s->cluster_size * 2; + p[6] = s->qdev.blocksize >> 8; p[8] = (cylinders >> 8) & 0xff; p[9] = cylinders & 0xff; /* Write precomp start cylinder, disabled */ @@ -983,7 +980,7 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf) } else { /* MODE_SENSE_10 */ outbuf[7] = 8; /* Block descriptor length */ } - nb_sectors /= s->cluster_size; + nb_sectors /= (s->qdev.blocksize / 512); if (nb_sectors > 0xffffff) { nb_sectors = 0; } @@ -993,7 +990,7 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf) p[3] = nb_sectors & 0xff; p[4] = 0; /* reserved */ p[5] = 0; /* bytes 5-7 are the sector size in bytes */ - p[6] = s->cluster_size * 2; + p[6] = s->qdev.blocksize >> 8; p[7] = 0; p += 8; } @@ -1044,7 +1041,7 @@ static int scsi_disk_emulate_read_toc(SCSIRequest *req, uint8_t *outbuf) start_track = req->cmd.buf[6]; bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors); DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1); - nb_sectors /= s->cluster_size; + nb_sectors /= s->qdev.blocksize / 512; switch (format) { case 0: toclen = cdrom_read_toc(nb_sectors, outbuf, msf, start_track); @@ -1179,7 +1176,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) if ((req->cmd.buf[8] & 1) == 0 && req->cmd.lba) { goto illegal_request; } - nb_sectors /= s->cluster_size; + nb_sectors /= s->qdev.blocksize / 512; /* Returned value is the address of the last sector. */ nb_sectors--; /* Remember the new size for read/write sanity checking. */ @@ -1194,7 +1191,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) outbuf[3] = nb_sectors & 0xff; outbuf[4] = 0; outbuf[5] = 0; - outbuf[6] = s->cluster_size * 2; + outbuf[6] = s->qdev.blocksize >> 8; outbuf[7] = 0; buflen = 8; break; @@ -1234,7 +1231,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) if ((req->cmd.buf[14] & 1) == 0 && req->cmd.lba) { goto illegal_request; } - nb_sectors /= s->cluster_size; + nb_sectors /= s->qdev.blocksize / 512; /* Returned value is the address of the last sector. */ nb_sectors--; /* Remember the new size for read/write sanity checking. */ @@ -1249,7 +1246,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) outbuf[7] = nb_sectors & 0xff; outbuf[8] = 0; outbuf[9] = 0; - outbuf[10] = s->cluster_size * 2; + outbuf[10] = s->qdev.blocksize >> 8; outbuf[11] = 0; outbuf[12] = 0; outbuf[13] = get_physical_block_exp(&s->qdev.conf); @@ -1356,8 +1353,8 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) if (r->req.cmd.lba > s->max_lba) { goto illegal_lba; } - r->sector = r->req.cmd.lba * s->cluster_size; - r->sector_count = len * s->cluster_size; + r->sector = r->req.cmd.lba * (s->qdev.blocksize / 512); + r->sector_count = len * (s->qdev.blocksize / 512); break; case WRITE_6: case WRITE_10: @@ -1373,8 +1370,8 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) if (r->req.cmd.lba > s->max_lba) { goto illegal_lba; } - r->sector = r->req.cmd.lba * s->cluster_size; - r->sector_count = len * s->cluster_size; + r->sector = r->req.cmd.lba * (s->qdev.blocksize / 512); + r->sector_count = len * (s->qdev.blocksize / 512); break; case MODE_SELECT: DPRINTF("Mode Select(6) (len %lu)\n", (long)r->req.cmd.xfer); @@ -1417,8 +1414,9 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) goto fail; } - rc = bdrv_discard(s->qdev.conf.bs, r->req.cmd.lba * s->cluster_size, - len * s->cluster_size); + rc = bdrv_discard(s->qdev.conf.bs, + r->req.cmd.lba * (s->qdev.blocksize / 512), + len * (s->qdev.blocksize / 512)); if (rc < 0) { /* XXX: better error code ?*/ goto fail; @@ -1460,7 +1458,7 @@ static void scsi_disk_reset(DeviceState *dev) scsi_device_purge_requests(&s->qdev, SENSE_CODE(RESET)); bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors); - nb_sectors /= s->cluster_size; + nb_sectors /= s->qdev.blocksize / 512; if (nb_sectors) { nb_sectors--; } @@ -1561,7 +1559,6 @@ static int scsi_initfn(SCSIDevice *dev, uint8_t scsi_type) error_report("scsi-disk: Unhandled SCSI type %02x", scsi_type); return -1; } - s->cluster_size = s->qdev.blocksize / 512; bdrv_set_buffer_alignment(s->qdev.conf.bs, s->qdev.blocksize); s->qdev.type = scsi_type; -- cgit v1.1 From e39be4823215e511d0e85aa33b2a5ade71064c72 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 12 Oct 2011 14:27:40 +0200 Subject: scsi-disk: small clean up to INQUIRY Set s->removable, s->qdev.blocksize and s->qdev.type in the callers of scsi_initfn. With this in place, s->qdev.type is allowed, and we can just reuse it as the first byte in VPD data (just like we do in standard INQUIRY data). Also set s->removable is set consistently and we can use it. Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- hw/scsi-disk.c | 46 +++++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 25 deletions(-) (limited to 'hw/scsi-disk.c') diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index f89e6c5..af1001d 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -381,11 +381,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) return -1; } - if (s->qdev.type == TYPE_ROM) { - outbuf[buflen++] = 5; - } else { - outbuf[buflen++] = 0; - } + outbuf[buflen++] = s->qdev.type & 0x1f; outbuf[buflen++] = page_code ; // this page outbuf[buflen++] = 0x00; @@ -529,11 +525,10 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) memset(outbuf, 0, buflen); outbuf[0] = s->qdev.type & 0x1f; + outbuf[1] = s->removable ? 0x80 : 0; if (s->qdev.type == TYPE_ROM) { - outbuf[1] = 0x80; memcpy(&outbuf[16], "QEMU CD-ROM ", 16); } else { - outbuf[1] = s->removable ? 0x80 : 0; memcpy(&outbuf[16], "QEMU HARDDISK ", 16); } memcpy(&outbuf[8], "QEMU ", 8); @@ -1518,7 +1513,7 @@ static void scsi_disk_unit_attention_reported(SCSIDevice *dev) } } -static int scsi_initfn(SCSIDevice *dev, uint8_t scsi_type) +static int scsi_initfn(SCSIDevice *dev) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); DriveInfo *dinfo; @@ -1528,7 +1523,7 @@ static int scsi_initfn(SCSIDevice *dev, uint8_t scsi_type) return -1; } - if (scsi_type == TYPE_DISK && !bdrv_is_inserted(s->qdev.conf.bs)) { + if (!s->removable && !bdrv_is_inserted(s->qdev.conf.bs)) { error_report("Device needs media, but drive is empty"); return -1; } @@ -1550,18 +1545,11 @@ static int scsi_initfn(SCSIDevice *dev, uint8_t scsi_type) return -1; } - if (scsi_type == TYPE_ROM) { + if (s->removable) { bdrv_set_dev_ops(s->qdev.conf.bs, &scsi_cd_block_ops, s); - s->qdev.blocksize = 2048; - } else if (scsi_type == TYPE_DISK) { - s->qdev.blocksize = s->qdev.conf.logical_block_size; - } else { - error_report("scsi-disk: Unhandled SCSI type %02x", scsi_type); - return -1; } bdrv_set_buffer_alignment(s->qdev.conf.bs, s->qdev.blocksize); - s->qdev.type = scsi_type; qemu_add_vm_change_state_handler(scsi_dma_restart_cb, s); bdrv_iostatus_enable(s->qdev.conf.bs); add_boot_device_path(s->qdev.conf.bootindex, &dev->qdev, ",0"); @@ -1570,27 +1558,35 @@ static int scsi_initfn(SCSIDevice *dev, uint8_t scsi_type) static int scsi_hd_initfn(SCSIDevice *dev) { - return scsi_initfn(dev, TYPE_DISK); + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); + s->qdev.blocksize = s->qdev.conf.logical_block_size; + s->qdev.type = TYPE_DISK; + return scsi_initfn(&s->qdev); } static int scsi_cd_initfn(SCSIDevice *dev) { - return scsi_initfn(dev, TYPE_ROM); + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); + s->qdev.blocksize = 2048; + s->qdev.type = TYPE_ROM; + s->removable = true; + return scsi_initfn(&s->qdev); } static int scsi_disk_initfn(SCSIDevice *dev) { DriveInfo *dinfo; - uint8_t scsi_type; if (!dev->conf.bs) { - scsi_type = TYPE_DISK; /* will die in scsi_initfn() */ - } else { - dinfo = drive_get_by_blockdev(dev->conf.bs); - scsi_type = dinfo->media_cd ? TYPE_ROM : TYPE_DISK; + return scsi_initfn(dev); /* ... and die there */ } - return scsi_initfn(dev, scsi_type); + dinfo = drive_get_by_blockdev(dev->conf.bs); + if (dinfo->media_cd) { + return scsi_cd_initfn(dev); + } else { + return scsi_hd_initfn(dev); + } } static SCSIReqOps scsi_disk_reqops = { -- cgit v1.1 From 7877903aa0ef318017441c32605bc64650e6a326 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 13 Oct 2011 10:39:50 +0200 Subject: scsi: move max_lba to SCSIDevice The field is only in scsi-disk for now. Moving it up to SCSIDevice makes it easier to reuse the scsi-generic reqops elsewhere. At the same time, make scsi-generic get max_lba from snooped READ CAPACITY commands as well. Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- hw/scsi-disk.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'hw/scsi-disk.c') diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index af1001d..74990a8 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -66,7 +66,6 @@ struct SCSIDiskState { SCSIDevice qdev; uint32_t removable; - uint64_t max_lba; bool media_changed; bool media_event; QEMUBH *bh; @@ -1175,7 +1174,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) /* Returned value is the address of the last sector. */ nb_sectors--; /* Remember the new size for read/write sanity checking. */ - s->max_lba = nb_sectors; + s->qdev.max_lba = nb_sectors; /* Clip to 2TB, instead of returning capacity modulo 2TB. */ if (nb_sectors > UINT32_MAX) { nb_sectors = UINT32_MAX; @@ -1230,7 +1229,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r) /* Returned value is the address of the last sector. */ nb_sectors--; /* Remember the new size for read/write sanity checking. */ - s->max_lba = nb_sectors; + s->qdev.max_lba = nb_sectors; outbuf[0] = (nb_sectors >> 56) & 0xff; outbuf[1] = (nb_sectors >> 48) & 0xff; outbuf[2] = (nb_sectors >> 40) & 0xff; @@ -1345,7 +1344,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) case READ_16: 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) { + if (r->req.cmd.lba > s->qdev.max_lba) { goto illegal_lba; } r->sector = r->req.cmd.lba * (s->qdev.blocksize / 512); @@ -1362,7 +1361,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) DPRINTF("Write %s(sector %" PRId64 ", count %d)\n", (command & 0xe) == 0xe ? "And Verify " : "", r->req.cmd.lba, len); - if (r->req.cmd.lba > s->max_lba) { + if (r->req.cmd.lba > s->qdev.max_lba) { goto illegal_lba; } r->sector = r->req.cmd.lba * (s->qdev.blocksize / 512); @@ -1388,7 +1387,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) case SEEK_10: DPRINTF("Seek(%d) (sector %" PRId64 ")\n", command == SEEK_6 ? 6 : 10, r->req.cmd.lba); - if (r->req.cmd.lba > s->max_lba) { + if (r->req.cmd.lba > s->qdev.max_lba) { goto illegal_lba; } break; @@ -1398,7 +1397,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) DPRINTF("WRITE SAME(16) (sector %" PRId64 ", count %d)\n", r->req.cmd.lba, len); - if (r->req.cmd.lba > s->max_lba) { + if (r->req.cmd.lba > s->qdev.max_lba) { goto illegal_lba; } @@ -1457,7 +1456,7 @@ static void scsi_disk_reset(DeviceState *dev) if (nb_sectors) { nb_sectors--; } - s->max_lba = nb_sectors; + s->qdev.max_lba = nb_sectors; } static void scsi_destroy(SCSIDevice *dev) -- cgit v1.1 From adcf2754b99dcab33c5c0c523eb3b970c1a24f6c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 12 Oct 2011 12:57:59 +0200 Subject: scsi: make reqops const Also delete a stale occurrence of SCSIReqOps inside SCSIDeviceInfo. Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- hw/scsi-disk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'hw/scsi-disk.c') diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 74990a8..77673f2 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -1588,7 +1588,7 @@ static int scsi_disk_initfn(SCSIDevice *dev) } } -static SCSIReqOps scsi_disk_reqops = { +static const SCSIReqOps scsi_disk_reqops = { .size = sizeof(SCSIDiskReq), .free_req = scsi_free_request, .send_command = scsi_send_command, -- cgit v1.1 From 63db0f0eee3c0b2cc3a06b36daf50c4e7801ea1b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 12 Oct 2011 12:58:31 +0200 Subject: scsi: pass cdb to alloc_req This will let scsi-block choose between passthrough and emulation. Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- hw/scsi-disk.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'hw/scsi-disk.c') diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 77673f2..415f81d 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -1598,8 +1598,8 @@ static const SCSIReqOps scsi_disk_reqops = { .get_buf = scsi_get_buf, }; -static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, - uint32_t lun, void *hba_private) +static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, + uint8_t *buf, void *hba_private) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); SCSIRequest *req; -- cgit v1.1 From c7bae6a75b5b43d560f5b18386447842f9bfec37 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 25 Oct 2011 12:53:34 +0200 Subject: scsi-disk: bump SCSIRequest reference count until aio completion runs In some cases a request may be canceled before the completion callback runs. Keep a reference to the request between starting an AIO operation and the corresponding scsi_req_cancel or scsi_*_complete. When a request has to be retried, the request can be dropped because scsi_dma_restart_bh only looks at requests that are enqueued. As such, they always have at least a reference. Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- hw/scsi-disk.c | 48 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) (limited to 'hw/scsi-disk.c') diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 415f81d..bb07737 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -102,8 +102,14 @@ static void scsi_cancel_io(SCSIRequest *req) SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); DPRINTF("Cancel tag=0x%x\n", req->tag); + r->status &= ~(SCSI_REQ_STATUS_RETRY | SCSI_REQ_STATUS_RETRY_TYPE_MASK); if (r->req.aiocb) { bdrv_aio_cancel(r->req.aiocb); + + /* This reference was left in by scsi_*_data. We take ownership of + * it the moment scsi_req_cancel is called, independent of whether + * bdrv_aio_cancel completes the request or not. */ + scsi_req_unref(&r->req); } r->req.aiocb = NULL; } @@ -134,7 +140,7 @@ static void scsi_read_complete(void * opaque, int ret) if (ret) { if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_READ)) { - return; + goto done; } } @@ -144,6 +150,11 @@ static void scsi_read_complete(void * opaque, int ret) r->sector += n; r->sector_count -= n; scsi_req_data(&r->req, r->qiov.size); + +done: + if (!r->req.io_canceled) { + scsi_req_unref(&r->req); + } } static void scsi_flush_complete(void * opaque, int ret) @@ -158,11 +169,16 @@ static void scsi_flush_complete(void * opaque, int ret) if (ret < 0) { if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_FLUSH)) { - return; + goto done; } } scsi_req_complete(&r->req, GOOD); + +done: + if (!r->req.io_canceled) { + scsi_req_unref(&r->req); + } } /* Read more data from scsi device into buffer. */ @@ -188,6 +204,8 @@ static void scsi_read_data(SCSIRequest *req) /* No data transfer may already be in progress */ assert(r->req.aiocb == NULL); + /* The request is used as the AIO opaque value, so add a ref. */ + scsi_req_ref(&r->req); if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { DPRINTF("Data transfer direction invalid\n"); scsi_read_complete(r, -EINVAL); @@ -196,7 +214,9 @@ static void scsi_read_data(SCSIRequest *req) if (s->tray_open) { scsi_read_complete(r, -ENOMEDIUM); + return; } + n = scsi_init_iovec(r); bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ); r->req.aiocb = bdrv_aio_readv(s->qdev.conf.bs, r->sector, &r->qiov, n, @@ -206,6 +226,13 @@ static void scsi_read_data(SCSIRequest *req) } } +/* + * scsi_handle_rw_error has two return values. 0 means that the error + * must be ignored, 1 means that the error has been processed and the + * caller should not do anything else for this request. Note that + * scsi_handle_rw_error always manages its reference counts, independent + * of the return value. + */ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) { int is_read = (type == SCSI_REQ_STATUS_RETRY_READ); @@ -226,6 +253,11 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) bdrv_mon_event(s->qdev.conf.bs, BDRV_ACTION_STOP, is_read); vm_stop(RUN_STATE_IO_ERROR); bdrv_iostatus_set_err(s->qdev.conf.bs, error); + + /* No need to save a reference, because scsi_dma_restart_bh just + * looks at the request list. If a request is canceled, the + * retry request is just dropped. + */ } else { switch (error) { case ENOMEDIUM: @@ -259,7 +291,7 @@ static void scsi_write_complete(void * opaque, int ret) if (ret) { if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_WRITE)) { - return; + goto done; } } @@ -273,6 +305,11 @@ static void scsi_write_complete(void * opaque, int ret) DPRINTF("Write complete tag=0x%x more=%d\n", r->req.tag, r->qiov.size); scsi_req_data(&r->req, r->qiov.size); } + +done: + if (!r->req.io_canceled) { + scsi_req_unref(&r->req); + } } static void scsi_write_data(SCSIRequest *req) @@ -284,6 +321,8 @@ static void scsi_write_data(SCSIRequest *req) /* No data transfer may already be in progress */ assert(r->req.aiocb == NULL); + /* The request is used as the AIO opaque value, so add a ref. */ + scsi_req_ref(&r->req); if (r->req.cmd.mode != SCSI_XFER_TO_DEV) { DPRINTF("Data transfer direction invalid\n"); scsi_write_complete(r, -EINVAL); @@ -294,6 +333,7 @@ static void scsi_write_data(SCSIRequest *req) if (n) { if (s->tray_open) { scsi_write_complete(r, -ENOMEDIUM); + return; } bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_WRITE); r->req.aiocb = bdrv_aio_writev(s->qdev.conf.bs, r->sector, &r->qiov, n, @@ -1332,6 +1372,8 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf) r->iov.iov_len = rc; break; case SYNCHRONIZE_CACHE: + /* The request is used as the AIO opaque value, so add a ref. */ + scsi_req_ref(&r->req); bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH); r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_flush_complete, r); if (r->req.aiocb == NULL) { -- cgit v1.1 From 71544d30a6f8b91574552a61cd5bd122a77e82d1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 25 Oct 2011 12:53:36 +0200 Subject: scsi: push request restart to SCSIDevice The request restart mechanism is generic and could be reused for scsi-generic. In the meanwhile, pushing it to SCSIDevice avoids that scsi_dma_restart_bh looks at SCSIGenericReqs when working on a scsi-block device. The code is the same that is already in hw/scsi-disk.c, with the type flags replaced by req->cmd.mode and a more generic way to requeue SCSI_XFER_NONE commands. I also added a missing call to qemu_del_vm_change_state_handler. Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- hw/scsi-disk.c | 76 ++++++---------------------------------------------------- 1 file changed, 7 insertions(+), 69 deletions(-) (limited to 'hw/scsi-disk.c') diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index bb07737..352d41f 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -42,12 +42,6 @@ do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0) #define SCSI_DMA_BUF_SIZE 131072 #define SCSI_MAX_INQUIRY_LEN 256 -#define SCSI_REQ_STATUS_RETRY 0x01 -#define SCSI_REQ_STATUS_RETRY_TYPE_MASK 0x06 -#define SCSI_REQ_STATUS_RETRY_READ 0x00 -#define SCSI_REQ_STATUS_RETRY_WRITE 0x02 -#define SCSI_REQ_STATUS_RETRY_FLUSH 0x04 - typedef struct SCSIDiskState SCSIDiskState; typedef struct SCSIDiskReq { @@ -58,7 +52,6 @@ typedef struct SCSIDiskReq { uint32_t buflen; struct iovec iov; QEMUIOVector qiov; - uint32_t status; BlockAcctCookie acct; } SCSIDiskReq; @@ -75,8 +68,7 @@ struct SCSIDiskState bool tray_locked; }; -static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type); -static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf); +static int scsi_handle_rw_error(SCSIDiskReq *r, int error); static void scsi_free_request(SCSIRequest *req) { @@ -102,7 +94,6 @@ static void scsi_cancel_io(SCSIRequest *req) SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); DPRINTF("Cancel tag=0x%x\n", req->tag); - r->status &= ~(SCSI_REQ_STATUS_RETRY | SCSI_REQ_STATUS_RETRY_TYPE_MASK); if (r->req.aiocb) { bdrv_aio_cancel(r->req.aiocb); @@ -139,7 +130,7 @@ static void scsi_read_complete(void * opaque, int ret) } if (ret) { - if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_READ)) { + if (scsi_handle_rw_error(r, -ret)) { goto done; } } @@ -168,7 +159,7 @@ static void scsi_flush_complete(void * opaque, int ret) } if (ret < 0) { - if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_FLUSH)) { + if (scsi_handle_rw_error(r, -ret)) { goto done; } } @@ -233,9 +224,9 @@ static void scsi_read_data(SCSIRequest *req) * scsi_handle_rw_error always manages its reference counts, independent * of the return value. */ -static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) +static int scsi_handle_rw_error(SCSIDiskReq *r, int error) { - int is_read = (type == SCSI_REQ_STATUS_RETRY_READ); + int is_read = (r->req.cmd.xfer == SCSI_XFER_FROM_DEV); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); BlockErrorAction action = bdrv_get_on_error(s->qdev.conf.bs, is_read); @@ -247,17 +238,10 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) if ((error == ENOSPC && action == BLOCK_ERR_STOP_ENOSPC) || action == BLOCK_ERR_STOP_ANY) { - type &= SCSI_REQ_STATUS_RETRY_TYPE_MASK; - r->status |= SCSI_REQ_STATUS_RETRY | type; - bdrv_mon_event(s->qdev.conf.bs, BDRV_ACTION_STOP, is_read); vm_stop(RUN_STATE_IO_ERROR); bdrv_iostatus_set_err(s->qdev.conf.bs, error); - - /* No need to save a reference, because scsi_dma_restart_bh just - * looks at the request list. If a request is canceled, the - * retry request is just dropped. - */ + scsi_req_retry(&r->req); } else { switch (error) { case ENOMEDIUM: @@ -290,7 +274,7 @@ static void scsi_write_complete(void * opaque, int ret) } if (ret) { - if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_WRITE)) { + if (scsi_handle_rw_error(r, -ret)) { goto done; } } @@ -347,51 +331,6 @@ static void scsi_write_data(SCSIRequest *req) } } -static void scsi_dma_restart_bh(void *opaque) -{ - SCSIDiskState *s = opaque; - SCSIRequest *req; - SCSIDiskReq *r; - - qemu_bh_delete(s->bh); - s->bh = NULL; - - QTAILQ_FOREACH(req, &s->qdev.requests, next) { - r = DO_UPCAST(SCSIDiskReq, req, req); - if (r->status & SCSI_REQ_STATUS_RETRY) { - int status = r->status; - - r->status &= - ~(SCSI_REQ_STATUS_RETRY | SCSI_REQ_STATUS_RETRY_TYPE_MASK); - - switch (status & SCSI_REQ_STATUS_RETRY_TYPE_MASK) { - case SCSI_REQ_STATUS_RETRY_READ: - scsi_read_data(&r->req); - break; - case SCSI_REQ_STATUS_RETRY_WRITE: - scsi_write_data(&r->req); - break; - case SCSI_REQ_STATUS_RETRY_FLUSH: - scsi_send_command(&r->req, r->req.cmd.buf); - break; - } - } - } -} - -static void scsi_dma_restart_cb(void *opaque, int running, RunState state) -{ - SCSIDiskState *s = opaque; - - if (!running) { - return; - } - if (!s->bh) { - s->bh = qemu_bh_new(scsi_dma_restart_bh, s); - qemu_bh_schedule(s->bh); - } -} - /* Return a pointer to the data buffer. */ static uint8_t *scsi_get_buf(SCSIRequest *req) { @@ -1591,7 +1530,6 @@ static int scsi_initfn(SCSIDevice *dev) } bdrv_set_buffer_alignment(s->qdev.conf.bs, s->qdev.blocksize); - qemu_add_vm_change_state_handler(scsi_dma_restart_cb, s); bdrv_iostatus_enable(s->qdev.conf.bs); add_boot_device_path(s->qdev.conf.bootindex, &dev->qdev, ",0"); return 0; -- cgit v1.1 From 336a6915bc7089fb20fea4ba99972ad9a97c5f52 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 25 Oct 2011 12:53:37 +0200 Subject: scsi-disk: add scsi-block for device passthrough scsi-block is a new device that supports device passthrough of Linux block devices (i.e. /dev/sda, not /dev/sg0). It uses SG_IO for commands other than I/O commands, and regular AIO read/writes for I/O commands. Besides being simpler to configure (no mapping required to scsi-generic device names), this removes the need for a large bounce buffer and, in the future, will get scatter/gather support for free from scsi-disk. Signed-off-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- hw/scsi-disk.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) (limited to 'hw/scsi-disk.c') diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 352d41f..1c04872 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -39,6 +39,10 @@ do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0) #include "blockdev.h" #include "block_int.h" +#ifdef __linux +#include +#endif + #define SCSI_DMA_BUF_SIZE 131072 #define SCSI_MAX_INQUIRY_LEN 256 @@ -1588,6 +1592,105 @@ static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, return req; } +#ifdef __linux__ +static int get_device_type(SCSIDiskState *s) +{ + BlockDriverState *bdrv = s->qdev.conf.bs; + uint8_t cmd[16]; + uint8_t buf[36]; + uint8_t sensebuf[8]; + sg_io_hdr_t io_header; + int ret; + + memset(cmd, 0, sizeof(cmd)); + memset(buf, 0, sizeof(buf)); + cmd[0] = INQUIRY; + cmd[4] = sizeof(buf); + + memset(&io_header, 0, sizeof(io_header)); + io_header.interface_id = 'S'; + io_header.dxfer_direction = SG_DXFER_FROM_DEV; + io_header.dxfer_len = sizeof(buf); + io_header.dxferp = buf; + io_header.cmdp = cmd; + io_header.cmd_len = sizeof(cmd); + io_header.mx_sb_len = sizeof(sensebuf); + io_header.sbp = sensebuf; + io_header.timeout = 6000; /* XXX */ + + ret = bdrv_ioctl(bdrv, SG_IO, &io_header); + if (ret < 0 || io_header.driver_status || io_header.host_status) { + return -1; + } + s->qdev.type = buf[0]; + s->removable = (buf[1] & 0x80) != 0; + return 0; +} + +static int scsi_block_initfn(SCSIDevice *dev) +{ + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); + int sg_version; + int rc; + + if (!s->qdev.conf.bs) { + error_report("scsi-block: drive property not set"); + return -1; + } + + /* check we are using a driver managing SG_IO (version 3 and after) */ + if (bdrv_ioctl(s->qdev.conf.bs, SG_GET_VERSION_NUM, &sg_version) < 0 || + sg_version < 30000) { + error_report("scsi-block: scsi generic interface too old"); + return -1; + } + + /* get device type from INQUIRY data */ + rc = get_device_type(s); + if (rc < 0) { + error_report("scsi-block: INQUIRY failed"); + return -1; + } + + /* Make a guess for the block size, we'll fix it when the guest sends. + * READ CAPACITY. If they don't, they likely would assume these sizes + * anyway. (TODO: check in /sys). + */ + if (s->qdev.type == TYPE_ROM || s->qdev.type == TYPE_WORM) { + s->qdev.blocksize = 2048; + } else { + s->qdev.blocksize = 512; + } + return scsi_initfn(&s->qdev); +} + +static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag, + uint32_t lun, uint8_t *buf, + void *hba_private) +{ + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); + + switch (buf[0]) { + case READ_6: + case READ_10: + case READ_12: + case READ_16: + case WRITE_6: + case WRITE_10: + case WRITE_12: + case WRITE_16: + case WRITE_VERIFY_10: + case WRITE_VERIFY_12: + case WRITE_VERIFY_16: + return scsi_req_alloc(&scsi_disk_reqops, &s->qdev, tag, lun, + hba_private); + } + + return scsi_req_alloc(&scsi_generic_req_ops, &s->qdev, tag, lun, + hba_private); +} +#endif + #define DEFINE_SCSI_DISK_PROPERTIES() \ DEFINE_BLOCK_PROPERTIES(SCSIDiskState, qdev.conf), \ DEFINE_PROP_STRING("ver", SCSIDiskState, version), \ @@ -1623,6 +1726,21 @@ static SCSIDeviceInfo scsi_disk_info[] = { DEFINE_SCSI_DISK_PROPERTIES(), DEFINE_PROP_END_OF_LIST(), }, +#ifdef __linux__ + },{ + .qdev.name = "scsi-block", + .qdev.fw_name = "disk", + .qdev.desc = "SCSI block device passthrough", + .qdev.size = sizeof(SCSIDiskState), + .qdev.reset = scsi_disk_reset, + .init = scsi_block_initfn, + .destroy = scsi_destroy, + .alloc_req = scsi_block_new_request, + .qdev.props = (Property[]) { + DEFINE_SCSI_DISK_PROPERTIES(), + DEFINE_PROP_END_OF_LIST(), + }, +#endif },{ .qdev.name = "scsi-disk", /* legacy -device scsi-disk */ .qdev.fw_name = "disk", -- cgit v1.1