diff options
author | Paolo Bonzini <pbonzini@redhat.com> | 2011-09-06 12:31:44 +0200 |
---|---|---|
committer | Kevin Wolf <kwolf@redhat.com> | 2011-09-06 15:45:20 +0200 |
commit | cfc606da0ddcef1f7228317b9f7dfa6c94c6c64f (patch) | |
tree | 0103aab27f4e9917a637dbd4984abb08692cac0a | |
parent | eae31cb99810c362955f37f82573f2e3849bcd32 (diff) |
scsi: improve MODE SENSE emulation
- do not return extra pages when requesting all pages (PAGE CODE = 0x3f)
- return correct sense code for PC = 3 (saved parameters not supported)
- do not return geometry pages for CD devices
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
-rw-r--r-- | hw/scsi-disk.c | 96 |
1 files changed, 53 insertions, 43 deletions
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index ead4163f74..9724d0fe9a 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -546,12 +546,12 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) return buflen; } -static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p, +static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, int page_control) { - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); BlockDriverState *bdrv = s->bs; int cylinders, heads, secs; + uint8_t *p = *p_outbuf; /* * If Changeable Values are requested, a mask denoting those mode parameters @@ -561,10 +561,13 @@ static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p, */ switch (page) { case 4: /* Rigid disk device geometry page. */ + if (s->qdev.type == TYPE_ROM) { + return -1; + } p[0] = 4; p[1] = 0x16; if (page_control == 1) { /* Changeable Values */ - return p[1] + 2; + break; } /* if a geometry hint is available, use it */ bdrv_get_geometry_hint(bdrv, &cylinders, &heads, &secs); @@ -590,13 +593,16 @@ static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p, /* Medium rotation rate [rpm], 5400 rpm */ p[20] = (5400 >> 8) & 0xff; p[21] = 5400 & 0xff; - return p[1] + 2; + break; case 5: /* Flexible disk device geometry page. */ + if (s->qdev.type == TYPE_ROM) { + return -1; + } p[0] = 5; p[1] = 0x1e; if (page_control == 1) { /* Changeable Values */ - return p[1] + 2; + break; } /* Transfer rate [kbit/s], 5Mbit/s */ p[2] = 5000 >> 8; @@ -629,26 +635,27 @@ static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p, /* Medium rotation rate [rpm], 5400 rpm */ p[28] = (5400 >> 8) & 0xff; p[29] = 5400 & 0xff; - return p[1] + 2; + break; case 8: /* Caching page. */ p[0] = 8; p[1] = 0x12; if (page_control == 1) { /* Changeable Values */ - return p[1] + 2; + break; } if (bdrv_enable_write_cache(s->bs)) { p[2] = 4; /* WCE */ } - return p[1] + 2; + break; case 0x2a: /* CD Capabilities and Mechanical Status page. */ - if (s->qdev.type != TYPE_ROM) - return 0; + if (s->qdev.type != TYPE_ROM) { + return -1; + } p[0] = 0x2a; p[1] = 0x14; if (page_control == 1) { /* Changeable Values */ - return p[1] + 2; + break; } p[2] = 3; // CD-R & CD-RW read p[3] = 0; // Writing not supported @@ -673,27 +680,30 @@ static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p, p[19] = (16 * 176) & 0xff; p[20] = (16 * 176) >> 8; // 16x write speed current p[21] = (16 * 176) & 0xff; - return p[1] + 2; + break; default: - return 0; + return -1; } + + *p_outbuf += p[1] + 2; + return p[1] + 2; } -static int scsi_disk_emulate_mode_sense(SCSIRequest *req, uint8_t *outbuf) +static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf) { - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); uint64_t nb_sectors; - int page, dbd, buflen, page_control; + int page, dbd, buflen, ret, page_control; uint8_t *p; uint8_t dev_specific_param; - dbd = req->cmd.buf[1] & 0x8; - page = req->cmd.buf[2] & 0x3f; - page_control = (req->cmd.buf[2] & 0xc0) >> 6; + dbd = r->req.cmd.buf[1] & 0x8; + page = r->req.cmd.buf[2] & 0x3f; + page_control = (r->req.cmd.buf[2] & 0xc0) >> 6; DPRINTF("Mode Sense(%d) (page %d, xfer %zd, page_control %d)\n", - (req->cmd.buf[0] == MODE_SENSE) ? 6 : 10, page, req->cmd.xfer, page_control); - memset(outbuf, 0, req->cmd.xfer); + (r->req.cmd.buf[0] == MODE_SENSE) ? 6 : 10, page, r->req.cmd.xfer, page_control); + memset(outbuf, 0, r->req.cmd.xfer); p = outbuf; if (bdrv_is_read_only(s->bs)) { @@ -702,7 +712,7 @@ static int scsi_disk_emulate_mode_sense(SCSIRequest *req, uint8_t *outbuf) dev_specific_param = 0x00; } - if (req->cmd.buf[0] == MODE_SENSE) { + if (r->req.cmd.buf[0] == MODE_SENSE) { p[1] = 0; /* Default media type. */ p[2] = dev_specific_param; p[3] = 0; /* Block descriptor length. */ @@ -716,7 +726,7 @@ static int scsi_disk_emulate_mode_sense(SCSIRequest *req, uint8_t *outbuf) bdrv_get_geometry(s->bs, &nb_sectors); if (!dbd && nb_sectors) { - if (req->cmd.buf[0] == MODE_SENSE) { + if (r->req.cmd.buf[0] == MODE_SENSE) { outbuf[3] = 8; /* Block descriptor length */ } else { /* MODE_SENSE_10 */ outbuf[7] = 8; /* Block descriptor length */ @@ -735,23 +745,21 @@ static int scsi_disk_emulate_mode_sense(SCSIRequest *req, uint8_t *outbuf) p += 8; } - if (page_control == 3) { /* Saved Values */ - return -1; /* ILLEGAL_REQUEST */ + if (page_control == 3) { + /* Saved Values */ + scsi_check_condition(r, SENSE_CODE(SAVING_PARAMS_NOT_SUPPORTED)); + return -1; } - switch (page) { - case 0x04: - case 0x05: - case 0x08: - case 0x2a: - p += mode_sense_page(req, page, p, page_control); - break; - case 0x3f: - p += mode_sense_page(req, 0x08, p, page_control); - p += mode_sense_page(req, 0x2a, p, page_control); - break; - default: - return -1; /* ILLEGAL_REQUEST */ + if (page == 0x3f) { + for (page = 0; page <= 0x3e; page++) { + mode_sense_page(s, page, &p, page_control); + } + } else { + ret = mode_sense_page(s, page, &p, page_control); + if (ret == -1) { + return -1; + } } buflen = p - outbuf; @@ -760,14 +768,14 @@ static int scsi_disk_emulate_mode_sense(SCSIRequest *req, uint8_t *outbuf) * following data that is available to be transferred. The mode data * length does not include itself. */ - if (req->cmd.buf[0] == MODE_SENSE) { + if (r->req.cmd.buf[0] == MODE_SENSE) { outbuf[0] = buflen - 1; } else { /* MODE_SENSE_10 */ outbuf[0] = ((buflen - 2) >> 8) & 0xff; outbuf[1] = (buflen - 2) & 0xff; } - if (buflen > req->cmd.xfer) - buflen = req->cmd.xfer; + if (buflen > r->req.cmd.xfer) + buflen = r->req.cmd.xfer; return buflen; } @@ -825,7 +833,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf) break; case MODE_SENSE: case MODE_SENSE_10: - buflen = scsi_disk_emulate_mode_sense(req, outbuf); + buflen = scsi_disk_emulate_mode_sense(r, outbuf); if (buflen < 0) goto illegal_request; break; @@ -946,7 +954,9 @@ not_ready: return -1; illegal_request: - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); + if (r->req.status == -1) { + scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); + } return -1; } |