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>
This commit is contained in:
parent
eae31cb998
commit
cfc606da0d
@ -546,12 +546,12 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
|
|||||||
return buflen;
|
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)
|
int page_control)
|
||||||
{
|
{
|
||||||
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev);
|
|
||||||
BlockDriverState *bdrv = s->bs;
|
BlockDriverState *bdrv = s->bs;
|
||||||
int cylinders, heads, secs;
|
int cylinders, heads, secs;
|
||||||
|
uint8_t *p = *p_outbuf;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If Changeable Values are requested, a mask denoting those mode parameters
|
* 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) {
|
switch (page) {
|
||||||
case 4: /* Rigid disk device geometry page. */
|
case 4: /* Rigid disk device geometry page. */
|
||||||
|
if (s->qdev.type == TYPE_ROM) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
p[0] = 4;
|
p[0] = 4;
|
||||||
p[1] = 0x16;
|
p[1] = 0x16;
|
||||||
if (page_control == 1) { /* Changeable Values */
|
if (page_control == 1) { /* Changeable Values */
|
||||||
return p[1] + 2;
|
break;
|
||||||
}
|
}
|
||||||
/* if a geometry hint is available, use it */
|
/* if a geometry hint is available, use it */
|
||||||
bdrv_get_geometry_hint(bdrv, &cylinders, &heads, &secs);
|
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 */
|
/* Medium rotation rate [rpm], 5400 rpm */
|
||||||
p[20] = (5400 >> 8) & 0xff;
|
p[20] = (5400 >> 8) & 0xff;
|
||||||
p[21] = 5400 & 0xff;
|
p[21] = 5400 & 0xff;
|
||||||
return p[1] + 2;
|
break;
|
||||||
|
|
||||||
case 5: /* Flexible disk device geometry page. */
|
case 5: /* Flexible disk device geometry page. */
|
||||||
|
if (s->qdev.type == TYPE_ROM) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
p[0] = 5;
|
p[0] = 5;
|
||||||
p[1] = 0x1e;
|
p[1] = 0x1e;
|
||||||
if (page_control == 1) { /* Changeable Values */
|
if (page_control == 1) { /* Changeable Values */
|
||||||
return p[1] + 2;
|
break;
|
||||||
}
|
}
|
||||||
/* Transfer rate [kbit/s], 5Mbit/s */
|
/* Transfer rate [kbit/s], 5Mbit/s */
|
||||||
p[2] = 5000 >> 8;
|
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 */
|
/* Medium rotation rate [rpm], 5400 rpm */
|
||||||
p[28] = (5400 >> 8) & 0xff;
|
p[28] = (5400 >> 8) & 0xff;
|
||||||
p[29] = 5400 & 0xff;
|
p[29] = 5400 & 0xff;
|
||||||
return p[1] + 2;
|
break;
|
||||||
|
|
||||||
case 8: /* Caching page. */
|
case 8: /* Caching page. */
|
||||||
p[0] = 8;
|
p[0] = 8;
|
||||||
p[1] = 0x12;
|
p[1] = 0x12;
|
||||||
if (page_control == 1) { /* Changeable Values */
|
if (page_control == 1) { /* Changeable Values */
|
||||||
return p[1] + 2;
|
break;
|
||||||
}
|
}
|
||||||
if (bdrv_enable_write_cache(s->bs)) {
|
if (bdrv_enable_write_cache(s->bs)) {
|
||||||
p[2] = 4; /* WCE */
|
p[2] = 4; /* WCE */
|
||||||
}
|
}
|
||||||
return p[1] + 2;
|
break;
|
||||||
|
|
||||||
case 0x2a: /* CD Capabilities and Mechanical Status page. */
|
case 0x2a: /* CD Capabilities and Mechanical Status page. */
|
||||||
if (s->qdev.type != TYPE_ROM)
|
if (s->qdev.type != TYPE_ROM) {
|
||||||
return 0;
|
return -1;
|
||||||
|
}
|
||||||
p[0] = 0x2a;
|
p[0] = 0x2a;
|
||||||
p[1] = 0x14;
|
p[1] = 0x14;
|
||||||
if (page_control == 1) { /* Changeable Values */
|
if (page_control == 1) { /* Changeable Values */
|
||||||
return p[1] + 2;
|
break;
|
||||||
}
|
}
|
||||||
p[2] = 3; // CD-R & CD-RW read
|
p[2] = 3; // CD-R & CD-RW read
|
||||||
p[3] = 0; // Writing not supported
|
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[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;
|
p[21] = (16 * 176) & 0xff;
|
||||||
return p[1] + 2;
|
break;
|
||||||
|
|
||||||
default:
|
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;
|
uint64_t nb_sectors;
|
||||||
int page, dbd, buflen, page_control;
|
int page, dbd, buflen, ret, page_control;
|
||||||
uint8_t *p;
|
uint8_t *p;
|
||||||
uint8_t dev_specific_param;
|
uint8_t dev_specific_param;
|
||||||
|
|
||||||
dbd = req->cmd.buf[1] & 0x8;
|
dbd = r->req.cmd.buf[1] & 0x8;
|
||||||
page = req->cmd.buf[2] & 0x3f;
|
page = r->req.cmd.buf[2] & 0x3f;
|
||||||
page_control = (req->cmd.buf[2] & 0xc0) >> 6;
|
page_control = (r->req.cmd.buf[2] & 0xc0) >> 6;
|
||||||
DPRINTF("Mode Sense(%d) (page %d, xfer %zd, page_control %d)\n",
|
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);
|
(r->req.cmd.buf[0] == MODE_SENSE) ? 6 : 10, page, r->req.cmd.xfer, page_control);
|
||||||
memset(outbuf, 0, req->cmd.xfer);
|
memset(outbuf, 0, r->req.cmd.xfer);
|
||||||
p = outbuf;
|
p = outbuf;
|
||||||
|
|
||||||
if (bdrv_is_read_only(s->bs)) {
|
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;
|
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[1] = 0; /* Default media type. */
|
||||||
p[2] = dev_specific_param;
|
p[2] = dev_specific_param;
|
||||||
p[3] = 0; /* Block descriptor length. */
|
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);
|
bdrv_get_geometry(s->bs, &nb_sectors);
|
||||||
if (!dbd && 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 */
|
outbuf[3] = 8; /* Block descriptor length */
|
||||||
} else { /* MODE_SENSE_10 */
|
} else { /* MODE_SENSE_10 */
|
||||||
outbuf[7] = 8; /* Block descriptor length */
|
outbuf[7] = 8; /* Block descriptor length */
|
||||||
@ -735,23 +745,21 @@ static int scsi_disk_emulate_mode_sense(SCSIRequest *req, uint8_t *outbuf)
|
|||||||
p += 8;
|
p += 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (page_control == 3) { /* Saved Values */
|
if (page_control == 3) {
|
||||||
return -1; /* ILLEGAL_REQUEST */
|
/* Saved Values */
|
||||||
|
scsi_check_condition(r, SENSE_CODE(SAVING_PARAMS_NOT_SUPPORTED));
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (page) {
|
if (page == 0x3f) {
|
||||||
case 0x04:
|
for (page = 0; page <= 0x3e; page++) {
|
||||||
case 0x05:
|
mode_sense_page(s, page, &p, page_control);
|
||||||
case 0x08:
|
}
|
||||||
case 0x2a:
|
} else {
|
||||||
p += mode_sense_page(req, page, p, page_control);
|
ret = mode_sense_page(s, page, &p, page_control);
|
||||||
break;
|
if (ret == -1) {
|
||||||
case 0x3f:
|
return -1;
|
||||||
p += mode_sense_page(req, 0x08, p, page_control);
|
}
|
||||||
p += mode_sense_page(req, 0x2a, p, page_control);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return -1; /* ILLEGAL_REQUEST */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buflen = p - outbuf;
|
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
|
* following data that is available to be transferred. The mode data
|
||||||
* length does not include itself.
|
* length does not include itself.
|
||||||
*/
|
*/
|
||||||
if (req->cmd.buf[0] == MODE_SENSE) {
|
if (r->req.cmd.buf[0] == MODE_SENSE) {
|
||||||
outbuf[0] = buflen - 1;
|
outbuf[0] = buflen - 1;
|
||||||
} else { /* MODE_SENSE_10 */
|
} else { /* MODE_SENSE_10 */
|
||||||
outbuf[0] = ((buflen - 2) >> 8) & 0xff;
|
outbuf[0] = ((buflen - 2) >> 8) & 0xff;
|
||||||
outbuf[1] = (buflen - 2) & 0xff;
|
outbuf[1] = (buflen - 2) & 0xff;
|
||||||
}
|
}
|
||||||
if (buflen > req->cmd.xfer)
|
if (buflen > r->req.cmd.xfer)
|
||||||
buflen = req->cmd.xfer;
|
buflen = r->req.cmd.xfer;
|
||||||
return buflen;
|
return buflen;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -825,7 +833,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf)
|
|||||||
break;
|
break;
|
||||||
case MODE_SENSE:
|
case MODE_SENSE:
|
||||||
case MODE_SENSE_10:
|
case MODE_SENSE_10:
|
||||||
buflen = scsi_disk_emulate_mode_sense(req, outbuf);
|
buflen = scsi_disk_emulate_mode_sense(r, outbuf);
|
||||||
if (buflen < 0)
|
if (buflen < 0)
|
||||||
goto illegal_request;
|
goto illegal_request;
|
||||||
break;
|
break;
|
||||||
@ -946,7 +954,9 @@ not_ready:
|
|||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
illegal_request:
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user