- ported most of the SCSI disk changes from Qemu to Bochs

- minor changes in SCSI command 0x23 (this command is designed for USB floppy
  support - we have to check if it is really needed for flash sticks and
  external hard drives)
- usb_msd: fixed indention
This commit is contained in:
Volker Ruppert 2009-04-01 18:19:46 +00:00
parent 365fa7fa35
commit 2ef1be6284
3 changed files with 232 additions and 79 deletions

View File

@ -1,5 +1,5 @@
/////////////////////////////////////////////////////////////////////////
// $Id: scsi_device.cc,v 1.13 2009-03-31 20:04:56 vruppert Exp $
// $Id: scsi_device.cc,v 1.14 2009-04-01 18:19:39 vruppert Exp $
/////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2007 Volker Ruppert
@ -34,6 +34,9 @@
#define LOG_THIS
#define BX_MIN(a,b) ( ((a)>(b))?(b):(a) )
#define DEVICE_NAME "SCSI drive"
static SCSIRequest *free_requests = NULL;
scsi_device_t::scsi_device_t(device_image_t *_hdimage, int _tcq,
@ -49,6 +52,8 @@ scsi_device_t::scsi_device_t(device_image_t *_hdimage, int _tcq,
dev = _dev;
cluster_size = 1;
locked = 0;
max_lba = hdimage->hd_size / 512;
strcpy(drive_serial_str, "1.0");
put("SCSID");
}
@ -66,6 +71,8 @@ scsi_device_t::scsi_device_t(LOWLEVEL_CDROM *_cdrom, int _tcq,
dev = _dev;
cluster_size = 4;
locked = 0;
max_lba = cdrom->capacity();
strcpy(drive_serial_str, "1.0");
put("SCSIC");
}
@ -114,6 +121,7 @@ SCSIRequest* scsi_device_t::scsi_new_request(Bit32u tag)
r->tag = tag;
r->sector_count = 0;
r->buf_len = 0;
r->status = 0;
r->next = requests;
requests = r;
@ -156,14 +164,14 @@ SCSIRequest* scsi_device_t::scsi_find_request(Bit32u tag)
return r;
}
void scsi_device_t::scsi_command_complete(SCSIRequest *r, int _sense)
void scsi_device_t::scsi_command_complete(SCSIRequest *r, int status, int _sense)
{
Bit32u tag;
BX_DEBUG(("command complete tag=0x%x sense=%d", r->tag, sense));
BX_DEBUG(("command complete tag=0x%x status=%d sense=%d", r->tag, status, sense));
sense = _sense;
tag = r->tag;
scsi_remove_request(r);
completion(dev, SCSI_REASON_DONE, tag, sense);
completion(dev, SCSI_REASON_DONE, tag, status);
}
void scsi_device_t::scsi_cancel_io(Bit32u tag)
@ -181,7 +189,8 @@ void scsi_device_t::scsi_read_complete(void *req, int ret)
if (ret) {
BX_ERROR(("IO error"));
scsi_command_complete(r, SENSE_HARDWARE_ERROR);
completion(r, SCSI_REASON_DATA, r->tag, 0);
scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_NO_SENSE);
return;
}
BX_DEBUG(("data ready tag=0x%x len=%d", r->tag, r->buf_len));
@ -197,7 +206,8 @@ void scsi_device_t::scsi_read_data(Bit32u tag)
SCSIRequest *r = scsi_find_request(tag);
if (!r) {
BX_ERROR(("bad read tag 0x%x", tag));
scsi_command_complete(r, SENSE_HARDWARE_ERROR);
// ??? This is the wrong error.
scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_HARDWARE_ERROR);
return;
}
if (r->sector_count == -1) {
@ -208,7 +218,7 @@ void scsi_device_t::scsi_read_data(Bit32u tag)
}
BX_DEBUG(("read sector_count=%d", r->sector_count));
if (r->sector_count == 0) {
scsi_command_complete(r, SENSE_NO_SENSE);
scsi_command_complete(r, STATUS_GOOD, SENSE_NO_SENSE);
return;
}
@ -217,17 +227,19 @@ void scsi_device_t::scsi_read_data(Bit32u tag)
n = SCSI_DMA_BUF_SIZE / (512 * cluster_size);
r->buf_len = n * 512 * cluster_size;
if (type == SCSIDEV_TYPE_CDROM) {
cdrom->read_block(r->dma_buf, r->sector, 2048);
if (!cdrom->read_block(r->dma_buf, r->sector, 2048)) {
scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_HARDWARE_ERROR);
}
} else {
ret = (int)hdimage->lseek(r->sector * 512, SEEK_SET);
if (ret < 0) {
BX_ERROR(("could not lseek() hard drive image file"));
scsi_command_complete(r, SENSE_HARDWARE_ERROR);
scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_HARDWARE_ERROR);
}
ret = hdimage->read((bx_ptr_t)r->dma_buf, r->buf_len);
if (ret < r->buf_len) {
BX_ERROR(("could not read() hard drive image file"));
scsi_command_complete(r, SENSE_HARDWARE_ERROR);
scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_HARDWARE_ERROR);
} else {
scsi_read_complete((void*)r, 0);
}
@ -243,12 +255,12 @@ void scsi_device_t::scsi_write_complete(void *req, int ret)
if (ret) {
BX_ERROR(("IO error"));
scsi_command_complete(r, SENSE_HARDWARE_ERROR);
scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_HARDWARE_ERROR);
return;
}
if (r->sector_count == 0) {
scsi_command_complete(r, SENSE_NO_SENSE);
scsi_command_complete(r, STATUS_GOOD, SENSE_NO_SENSE);
} else {
len = r->sector_count * 512;
if (len > SCSI_DMA_BUF_SIZE) {
@ -270,7 +282,7 @@ int scsi_device_t::scsi_write_data(Bit32u tag)
r = scsi_find_request(tag);
if (!r) {
BX_ERROR(("bad write tag 0x%x", tag));
scsi_command_complete(r, SENSE_HARDWARE_ERROR);
scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_HARDWARE_ERROR);
return 1;
}
if (type == SCSIDEV_TYPE_DISK) {
@ -279,14 +291,14 @@ int scsi_device_t::scsi_write_data(Bit32u tag)
ret = (int)hdimage->lseek(r->sector * 512, SEEK_SET);
if (ret < 0) {
BX_ERROR(("could not lseek() hard drive image file"));
scsi_command_complete(r, SENSE_HARDWARE_ERROR);
scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_HARDWARE_ERROR);
}
ret = hdimage->write((bx_ptr_t)r->dma_buf, r->buf_len);
r->sector += n;
r->sector_count -= n;
if (ret < r->buf_len) {
BX_ERROR(("could not write() hard drive image file"));
scsi_command_complete(r, SENSE_HARDWARE_ERROR);
scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_HARDWARE_ERROR);
} else {
scsi_write_complete((void*)r, 0);
}
@ -295,7 +307,7 @@ int scsi_device_t::scsi_write_data(Bit32u tag)
}
} else {
BX_ERROR(("CD-ROM: write not supported"));
scsi_command_complete(r, SENSE_HARDWARE_ERROR);
scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_HARDWARE_ERROR);
}
return 0;
}
@ -313,7 +325,7 @@ Bit8u* scsi_device_t::scsi_get_buf(Bit32u tag)
Bit32s scsi_device_t::scsi_send_command(Bit32u tag, Bit8u *buf, int lun)
{
Bit64u nb_sectors;
Bit32u lba;
Bit64u lba;
Bit32s len;
int cmdlen;
int is_write;
@ -344,7 +356,9 @@ Bit32s scsi_device_t::scsi_send_command(Bit32u tag, Bit8u *buf, int lun)
cmdlen = 10;
break;
case 4:
lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
lba = buf[9] | (buf[8] << 8) | (buf[7] << 16) | (buf[6] << 24) |
((Bit64u)buf[5] << 32) | ((Bit64u)buf[4] << 40) |
((Bit64u)buf[3] << 48) | ((Bit64u)buf[2] << 56);
len = buf[13] | (buf[12] << 8) | (buf[11] << 16) | (buf[10] << 24);
cmdlen = 16;
break;
@ -359,7 +373,8 @@ Bit32s scsi_device_t::scsi_send_command(Bit32u tag, Bit8u *buf, int lun)
}
if (lun || buf[1] >> 5) {
BX_ERROR(("unimplemented LUN %d", lun ? lun : buf[1] >> 5));
goto fail;
if ((command != 0x03) && (command != 0x12)) // REQUEST SENSE and INQUIRY
goto fail;
}
switch (command) {
case 0x0:
@ -377,11 +392,128 @@ Bit32s scsi_device_t::scsi_send_command(Bit32u tag, Bit8u *buf, int lun)
break;
case 0x12:
BX_DEBUG(("inquiry (len %d)", len));
if (len < 36) {
BX_ERROR(("inquiry buffer too small (%d)", len));
if (buf[1] & 0x2) {
// Command support data - optional, not implemented
BX_ERROR(("optional INQUIRY command support request not implemented"));
goto fail;
} else if (buf[1] & 0x1) {
// Vital product data
Bit8u page_code = buf[2];
if (len < 4) {
BX_ERROR(("Error: Inquiry (EVPD[%02X]) buffer size %d is less than 4", page_code, len));
goto fail;
}
switch (page_code) {
case 0x00:
// Supported page codes, mandatory
BX_DEBUG(("Inquiry EVPD[Supported pages] buffer size %d", len));
r->buf_len = 0;
if (type == SCSIDEV_TYPE_CDROM) {
outbuf[r->buf_len++] = 5;
} else {
outbuf[r->buf_len++] = 0;
}
outbuf[r->buf_len++] = 0x00; // this page
outbuf[r->buf_len++] = 0x00;
outbuf[r->buf_len++] = 3; // number of pages
outbuf[r->buf_len++] = 0x00; // list of supported pages (this page)
outbuf[r->buf_len++] = 0x80; // unit serial number
outbuf[r->buf_len++] = 0x83; // device identification
break;
case 0x80:
{
int l;
// Device serial number, optional
if (len < 4) {
BX_ERROR(("Error: EVPD[Serial number] Inquiry buffer size %d too small, %d needed", len, 4));
goto fail;
}
BX_DEBUG(("Inquiry EVPD[Serial number] buffer size %d\n", len));
l = BX_MIN(len, (int)strlen(drive_serial_str));
r->buf_len = 0;
// Supported page codes
if (type == SCSIDEV_TYPE_CDROM) {
outbuf[r->buf_len++] = 5;
} else {
outbuf[r->buf_len++] = 0;
}
outbuf[r->buf_len++] = 0x80; // this page
outbuf[r->buf_len++] = 0x00;
outbuf[r->buf_len++] = l;
memcpy(&outbuf[r->buf_len], drive_serial_str, l);
r->buf_len += l;
}
break;
case 0x83:
{
// Device identification page, mandatory
int max_len = 255 - 8;
int id_len = strlen(DEVICE_NAME);
if (id_len > max_len)
id_len = max_len;
BX_DEBUG(("Inquiry EVPD[Device identification] buffer size %d", len));
r->buf_len = 0;
if (type == SCSIDEV_TYPE_CDROM) {
outbuf[r->buf_len++] = 5;
} else {
outbuf[r->buf_len++] = 0;
}
outbuf[r->buf_len++] = 0x83; // this page
outbuf[r->buf_len++] = 0x00;
outbuf[r->buf_len++] = 3 + id_len;
outbuf[r->buf_len++] = 0x2; // ASCII
outbuf[r->buf_len++] = 0; // not officially assigned
outbuf[r->buf_len++] = 0; // reserved
outbuf[r->buf_len++] = id_len; // length of data following
memcpy(&outbuf[r->buf_len], DEVICE_NAME, id_len);
r->buf_len += id_len;
}
break;
default:
BX_ERROR(("Error: unsupported Inquiry (EVPD[%02X]) buffer size %d", page_code, len));
goto fail;
}
// done with EVPD
break;
} else {
// Standard INQUIRY data
if (buf[2] != 0) {
BX_ERROR(("Error: Inquiry (STANDARD) page or code is non-zero [%02X]", buf[2]));
goto fail;
}
// PAGE CODE == 0
if (len < 5) {
BX_ERROR(("Error: Inquiry (STANDARD) buffer size %d is less than 5", len));
goto fail;
}
if (len < 36) {
BX_ERROR(("Error: Inquiry (STANDARD) buffer size %d is less than 36 (TODO: only 5 required)", len));
}
}
memset(outbuf, 0, 36);
if (type == SCSIDEV_TYPE_CDROM) {
if(len > SCSI_MAX_INQUIRY_LEN)
len = SCSI_MAX_INQUIRY_LEN;
memset(outbuf, 0, len);
if (lun || buf[1] >> 5) {
outbuf[0] = 0x7f; // LUN not supported
} else if (type == SCSIDEV_TYPE_CDROM) {
outbuf[0] = 5;
outbuf[1] = 0x80;
memcpy(&outbuf[16], "BOCHS CD-ROM ", 16);
@ -391,11 +523,14 @@ Bit32s scsi_device_t::scsi_send_command(Bit32u tag, Bit8u *buf, int lun)
}
memcpy(&outbuf[8], "BOCHS ", 8);
memcpy(&outbuf[32], "1.0", 4);
// Identify device as SCSI-3 rev 1.
// Some later commands are also implemented.
outbuf[2] = 3;
outbuf[3] = 2;
outbuf[4] = 31;
outbuf[3] = 2; // Format 2
outbuf[4] = len - 5; // Additional Length = (Len - 1) - 4
// Sync data transfer and TCQ.
outbuf[7] = 0x10 | (tcq ? 0x02 : 0);
r->buf_len = 36;
r->buf_len = len;
break;
case 0x16:
BX_INFO(("Reserve(6)"));
@ -423,6 +558,9 @@ Bit32s scsi_device_t::scsi_send_command(Bit32u tag, Bit8u *buf, int lun)
outbuf[2] = 0x80; /* Readonly. */
}
p += 4;
if ((page == 4) || (page == 5)) {
BX_ERROR(("mode sense: page %d not implemented", page));
}
if ((page == 8 || page == 0x3f)) {
/* Caching page. */
memset(p, 0, 20);
@ -495,25 +633,31 @@ Bit32s scsi_device_t::scsi_send_command(Bit32u tag, Bit8u *buf, int lun)
outbuf[7] = 0;
r->buf_len = 8;
} else {
scsi_command_complete(r, SENSE_NOT_READY);
scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_NOT_READY);
return 0;
}
break;
case 0x08:
case 0x28:
BX_DEBUG(("Read (sector %d, count %d)", lba, len));
case 0x88:
BX_DEBUG(("Read (sector "FMT_LL"d, count %d)", lba, len));
if (lba > max_lba)
goto illegal_lba;
r->sector = lba;
r->sector_count = len;
break;
case 0x0a:
case 0x2a:
BX_DEBUG(("Write (sector %d, count %d)", lba, len));
case 0x8a:
BX_DEBUG(("Write (sector "FMT_LL"d, count %d)", lba, len));
if (lba > max_lba)
goto illegal_lba;
r->sector = lba;
r->sector_count = len;
is_write = 1;
break;
case 0x35:
BX_DEBUG(("Syncronise cache (sector %d, count %d)", lba, len));
BX_DEBUG(("Syncronise cache (sector "FMT_LL"d, count %d)", lba, len));
// TODO: flush cache
break;
case 0x43:
@ -572,7 +716,6 @@ Bit32s scsi_device_t::scsi_send_command(Bit32u tag, Bit8u *buf, int lun)
// USBMASS-UFI10.pdf  rev 1.0  Section 4.10
BX_INFO(("READ FORMAT CAPACITIES (MMC)"));
unsigned lun = (buf[1] >> 5);
unsigned len = (buf[7]<<8) | buf[8];
#define OUR_LEN 12
@ -584,9 +727,9 @@ Bit32s scsi_device_t::scsi_send_command(Bit32u tag, Bit8u *buf, int lun)
// Current/Max Cap Header
if (type == SCSIDEV_TYPE_CDROM) {
nb_sectors = cdrom->capacity() - 1;
nb_sectors = cdrom->capacity();
} else {
nb_sectors = (hdimage->hd_size / 512) - 1;
nb_sectors = (hdimage->hd_size / 512);
}
/* Returned value is the address of the last sector.  */
outbuf[4] = (Bit8u)((nb_sectors >> 24) & 0xff);
@ -605,11 +748,14 @@ Bit32s scsi_device_t::scsi_send_command(Bit32u tag, Bit8u *buf, int lun)
default:
BX_ERROR(("Unknown SCSI command (%2.2x)", buf[0]));
fail:
scsi_command_complete(r, SENSE_ILLEGAL_REQUEST);
scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_ILLEGAL_REQUEST);
return 0;
illegal_lba:
scsi_command_complete(r, STATUS_CHECK_CONDITION, SENSE_HARDWARE_ERROR);
return 0;
}
if (r->sector_count == 0 && r->buf_len == 0) {
scsi_command_complete(r, SENSE_NO_SENSE);
scsi_command_complete(r, STATUS_GOOD, SENSE_NO_SENSE);
}
len = r->sector_count * 512 * cluster_size + r->buf_len;
if (is_write) {

View File

@ -1,5 +1,5 @@
/////////////////////////////////////////////////////////////////////////
// $Id: scsi_device.h,v 1.8 2009-03-09 12:18:40 vruppert Exp $
// $Id: scsi_device.h,v 1.9 2009-04-01 18:19:43 vruppert Exp $
/////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2007 Volker Ruppert
@ -43,7 +43,11 @@ enum scsi_reason {
#define SENSE_HARDWARE_ERROR 4
#define SENSE_ILLEGAL_REQUEST 5
#define SCSI_DMA_BUF_SIZE 65536
#define STATUS_GOOD 0
#define STATUS_CHECK_CONDITION 2
#define SCSI_DMA_BUF_SIZE 131072
#define SCSI_MAX_INQUIRY_LEN 256
typedef struct SCSIRequest {
scsi_device_t *dev;
@ -52,6 +56,7 @@ typedef struct SCSIRequest {
int sector_count;
int buf_len;
Bit8u dma_buf[SCSI_DMA_BUF_SIZE];
Bit32u status;
struct SCSIRequest *next;
} SCSIRequest;
@ -66,7 +71,7 @@ public:
void register_state(bx_list_c *parent, const char *name);
Bit32s scsi_send_command(Bit32u tag, Bit8u *buf, int lun);
void scsi_command_complete(SCSIRequest *r, int sense);
void scsi_command_complete(SCSIRequest *r, int status, int sense);
void scsi_cancel_io(Bit32u tag);
void scsi_read_complete(void *req, int ret);
void scsi_read_data(Bit32u tag);
@ -84,11 +89,13 @@ private:
LOWLEVEL_CDROM *cdrom;
SCSIRequest *requests;
int cluster_size;
Bit64u max_lba;
int sense;
int tcq;
scsi_completionfn completion;
void *dev;
bx_bool locked;
char drive_serial_str[21];
};
#endif

View File

@ -1,5 +1,5 @@
/////////////////////////////////////////////////////////////////////////
// $Id: usb_msd.cc,v 1.19 2009-03-09 14:44:06 vruppert Exp $
// $Id: usb_msd.cc,v 1.20 2009-04-01 18:19:46 vruppert Exp $
/////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2009 Volker Ruppert
@ -422,51 +422,51 @@ int usb_msd_device_c::handle_data(USBPacket *p)
ret = USB_RET_ASYNC;
break;
case USB_MSDM_CSW:
BX_DEBUG(("command status %d tag 0x%x, len %d",
s.result, s.tag, len));
if (len < 13)
return ret;
case USB_MSDM_CSW:
BX_DEBUG(("command status %d tag 0x%x, len %d",
s.result, s.tag, len));
if (len < 13)
return ret;
s.usb_len = len;
s.usb_buf = data;
send_status();
s.mode = USB_MSDM_CBW;
ret = 13;
break;
s.usb_len = len;
s.usb_buf = data;
send_status();
s.mode = USB_MSDM_CBW;
ret = 13;
break;
case USB_MSDM_DATAIN:
BX_DEBUG(("data in %d/%d", len, s.data_len));
if (len > (int)s.data_len)
case USB_MSDM_DATAIN:
BX_DEBUG(("data in %d/%d", len, s.data_len));
if (len > (int)s.data_len)
len = s.data_len;
s.usb_buf = data;
s.usb_len = len;
if (s.scsi_len) {
copy_data();
}
if (s.residue && s.usb_len) {
s.data_len -= s.usb_len;
memset(s.usb_buf, 0, s.usb_len);
if (s.data_len == 0)
s.mode = USB_MSDM_CSW;
s.usb_len = 0;
}
if (s.usb_len) {
BX_INFO(("deferring packet %p", p));
usb_defer_packet(p, this);
s.packet = p;
ret = USB_RET_ASYNC;
} else {
s.usb_buf = data;
s.usb_len = len;
if (s.scsi_len) {
copy_data();
}
if (s.residue && s.usb_len) {
s.data_len -= s.usb_len;
memset(s.usb_buf, 0, s.usb_len);
if (s.data_len == 0)
s.mode = USB_MSDM_CSW;
s.usb_len = 0;
}
if (s.usb_len) {
BX_INFO(("deferring packet %p", p));
usb_defer_packet(p, this);
s.packet = p;
ret = USB_RET_ASYNC;
} else {
ret = len;
}
break;
}
break;
default:
BX_ERROR(("USB MSD handle_data: unexpected mode at USB_TOKEN_IN"));
goto fail;
}
if (ret > 0) usb_dump_packet(data, ret);
break;
default:
BX_ERROR(("USB MSD handle_data: unexpected mode at USB_TOKEN_IN"));
goto fail;
}
if (ret > 0) usb_dump_packet(data, ret);
break;
default:
BX_ERROR(("USB MSD handle_data: bad token"));