BMDMA support - CDROM fixes
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@971 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
02ba45c536
commit
9808745072
572
hw/ide.c
572
hw/ide.c
|
@ -299,6 +299,7 @@ typedef struct IDEState {
|
||||||
int irq;
|
int irq;
|
||||||
openpic_t *openpic;
|
openpic_t *openpic;
|
||||||
PCIDevice *pci_dev;
|
PCIDevice *pci_dev;
|
||||||
|
struct BMDMAState *bmdma;
|
||||||
int drive_serial;
|
int drive_serial;
|
||||||
/* ide regs */
|
/* ide regs */
|
||||||
uint8_t feature;
|
uint8_t feature;
|
||||||
|
@ -321,7 +322,11 @@ typedef struct IDEState {
|
||||||
int elementary_transfer_size;
|
int elementary_transfer_size;
|
||||||
int io_buffer_index;
|
int io_buffer_index;
|
||||||
int lba;
|
int lba;
|
||||||
/* transfer handling */
|
int cd_sector_size;
|
||||||
|
int atapi_dma; /* true if dma is requested for the packet cmd */
|
||||||
|
/* ATA DMA state */
|
||||||
|
int io_buffer_size;
|
||||||
|
/* PIO transfer handling */
|
||||||
int req_nb_sectors; /* number of sectors per interrupt */
|
int req_nb_sectors; /* number of sectors per interrupt */
|
||||||
EndTransferFunc *end_transfer_func;
|
EndTransferFunc *end_transfer_func;
|
||||||
uint8_t *data_ptr;
|
uint8_t *data_ptr;
|
||||||
|
@ -329,6 +334,34 @@ typedef struct IDEState {
|
||||||
uint8_t io_buffer[MAX_MULT_SECTORS*512 + 4];
|
uint8_t io_buffer[MAX_MULT_SECTORS*512 + 4];
|
||||||
} IDEState;
|
} IDEState;
|
||||||
|
|
||||||
|
#define BM_STATUS_DMAING 0x01
|
||||||
|
#define BM_STATUS_ERROR 0x02
|
||||||
|
#define BM_STATUS_INT 0x04
|
||||||
|
|
||||||
|
#define BM_CMD_START 0x01
|
||||||
|
#define BM_CMD_READ 0x08
|
||||||
|
|
||||||
|
typedef int IDEDMAFunc(IDEState *s,
|
||||||
|
target_phys_addr_t phys_addr,
|
||||||
|
int transfer_size1);
|
||||||
|
|
||||||
|
typedef struct BMDMAState {
|
||||||
|
uint8_t cmd;
|
||||||
|
uint8_t status;
|
||||||
|
uint32_t addr;
|
||||||
|
/* current transfer state */
|
||||||
|
IDEState *ide_if;
|
||||||
|
IDEDMAFunc *dma_cb;
|
||||||
|
} BMDMAState;
|
||||||
|
|
||||||
|
typedef struct PCIIDEState {
|
||||||
|
PCIDevice dev;
|
||||||
|
IDEState ide_if[4];
|
||||||
|
BMDMAState bmdma[2];
|
||||||
|
} PCIIDEState;
|
||||||
|
|
||||||
|
static void ide_dma_start(IDEState *s, IDEDMAFunc *dma_cb);
|
||||||
|
|
||||||
static void padstr(char *str, const char *src, int len)
|
static void padstr(char *str, const char *src, int len)
|
||||||
{
|
{
|
||||||
int i, v;
|
int i, v;
|
||||||
|
@ -554,6 +587,59 @@ static void ide_sector_read(IDEState *s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ide_read_dma_cb(IDEState *s,
|
||||||
|
target_phys_addr_t phys_addr,
|
||||||
|
int transfer_size1)
|
||||||
|
{
|
||||||
|
int len, transfer_size, n;
|
||||||
|
int64_t sector_num;
|
||||||
|
|
||||||
|
transfer_size = transfer_size1;
|
||||||
|
while (transfer_size > 0) {
|
||||||
|
len = s->io_buffer_size - s->io_buffer_index;
|
||||||
|
if (len <= 0) {
|
||||||
|
/* transfert next data */
|
||||||
|
n = s->nsector;
|
||||||
|
if (n == 0)
|
||||||
|
break;
|
||||||
|
if (n > MAX_MULT_SECTORS)
|
||||||
|
n = MAX_MULT_SECTORS;
|
||||||
|
sector_num = ide_get_sector(s);
|
||||||
|
bdrv_read(s->bs, sector_num, s->io_buffer, n);
|
||||||
|
s->io_buffer_index = 0;
|
||||||
|
s->io_buffer_size = n * 512;
|
||||||
|
len = s->io_buffer_size;
|
||||||
|
sector_num += n;
|
||||||
|
ide_set_sector(s, sector_num);
|
||||||
|
s->nsector -= n;
|
||||||
|
}
|
||||||
|
if (len > transfer_size)
|
||||||
|
len = transfer_size;
|
||||||
|
cpu_physical_memory_write(phys_addr,
|
||||||
|
s->io_buffer + s->io_buffer_index, len);
|
||||||
|
s->io_buffer_index += len;
|
||||||
|
transfer_size -= len;
|
||||||
|
phys_addr += len;
|
||||||
|
}
|
||||||
|
if (s->io_buffer_index >= s->io_buffer_size && s->nsector == 0) {
|
||||||
|
s->status = READY_STAT | SEEK_STAT;
|
||||||
|
ide_set_irq(s);
|
||||||
|
#ifdef DEBUG_IDE_ATAPI
|
||||||
|
printf("dma status=0x%x\n", s->status);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return transfer_size1 - transfer_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ide_sector_read_dma(IDEState *s)
|
||||||
|
{
|
||||||
|
s->status = READY_STAT | SEEK_STAT | DRQ_STAT;
|
||||||
|
s->io_buffer_index = 0;
|
||||||
|
s->io_buffer_size = 0;
|
||||||
|
ide_dma_start(s, ide_read_dma_cb);
|
||||||
|
}
|
||||||
|
|
||||||
static void ide_sector_write(IDEState *s)
|
static void ide_sector_write(IDEState *s)
|
||||||
{
|
{
|
||||||
int64_t sector_num;
|
int64_t sector_num;
|
||||||
|
@ -582,6 +668,62 @@ static void ide_sector_write(IDEState *s)
|
||||||
ide_set_irq(s);
|
ide_set_irq(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ide_write_dma_cb(IDEState *s,
|
||||||
|
target_phys_addr_t phys_addr,
|
||||||
|
int transfer_size1)
|
||||||
|
{
|
||||||
|
int len, transfer_size, n;
|
||||||
|
int64_t sector_num;
|
||||||
|
|
||||||
|
transfer_size = transfer_size1;
|
||||||
|
for(;;) {
|
||||||
|
len = s->io_buffer_size - s->io_buffer_index;
|
||||||
|
if (len == 0) {
|
||||||
|
n = s->io_buffer_size >> 9;
|
||||||
|
sector_num = ide_get_sector(s);
|
||||||
|
bdrv_write(s->bs, sector_num, s->io_buffer,
|
||||||
|
s->io_buffer_size >> 9);
|
||||||
|
sector_num += n;
|
||||||
|
ide_set_sector(s, sector_num);
|
||||||
|
s->nsector -= n;
|
||||||
|
n = s->nsector;
|
||||||
|
if (n == 0) {
|
||||||
|
/* end of transfer */
|
||||||
|
s->status = READY_STAT | SEEK_STAT;
|
||||||
|
ide_set_irq(s);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (n > MAX_MULT_SECTORS)
|
||||||
|
n = MAX_MULT_SECTORS;
|
||||||
|
s->io_buffer_index = 0;
|
||||||
|
s->io_buffer_size = n * 512;
|
||||||
|
len = s->io_buffer_size;
|
||||||
|
}
|
||||||
|
if (transfer_size <= 0)
|
||||||
|
break;
|
||||||
|
if (len > transfer_size)
|
||||||
|
len = transfer_size;
|
||||||
|
cpu_physical_memory_read(phys_addr,
|
||||||
|
s->io_buffer + s->io_buffer_index, len);
|
||||||
|
s->io_buffer_index += len;
|
||||||
|
transfer_size -= len;
|
||||||
|
phys_addr += len;
|
||||||
|
}
|
||||||
|
return transfer_size1 - transfer_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ide_sector_write_dma(IDEState *s)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
s->status = READY_STAT | SEEK_STAT | DRQ_STAT;
|
||||||
|
n = s->nsector;
|
||||||
|
if (n > MAX_MULT_SECTORS)
|
||||||
|
n = MAX_MULT_SECTORS;
|
||||||
|
s->io_buffer_index = 0;
|
||||||
|
s->io_buffer_size = n * 512;
|
||||||
|
ide_dma_start(s, ide_write_dma_cb);
|
||||||
|
}
|
||||||
|
|
||||||
static void ide_atapi_cmd_ok(IDEState *s)
|
static void ide_atapi_cmd_ok(IDEState *s)
|
||||||
{
|
{
|
||||||
s->error = 0;
|
s->error = 0;
|
||||||
|
@ -627,6 +769,41 @@ static inline int ube32_to_cpu(const uint8_t *buf)
|
||||||
return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
|
return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void lba_to_msf(uint8_t *buf, int lba)
|
||||||
|
{
|
||||||
|
lba += 150;
|
||||||
|
buf[0] = (lba / 75) / 60;
|
||||||
|
buf[1] = (lba / 75) % 60;
|
||||||
|
buf[2] = lba % 75;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cd_read_sector(BlockDriverState *bs, int lba, uint8_t *buf,
|
||||||
|
int sector_size)
|
||||||
|
{
|
||||||
|
switch(sector_size) {
|
||||||
|
case 2048:
|
||||||
|
bdrv_read(bs, (int64_t)lba << 2, buf, 4);
|
||||||
|
break;
|
||||||
|
case 2352:
|
||||||
|
/* sync bytes */
|
||||||
|
buf[0] = 0x00;
|
||||||
|
memset(buf + 1, 0xff, 11);
|
||||||
|
buf += 12;
|
||||||
|
/* MSF */
|
||||||
|
lba_to_msf(buf, lba);
|
||||||
|
buf[3] = 0x01; /* mode 1 data */
|
||||||
|
buf += 4;
|
||||||
|
/* data */
|
||||||
|
bdrv_read(bs, (int64_t)lba << 2, buf, 4);
|
||||||
|
buf += 2048;
|
||||||
|
/* ECC */
|
||||||
|
memset(buf, 0, 288);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* The whole ATAPI transfer logic is handled in this function */
|
/* The whole ATAPI transfer logic is handled in this function */
|
||||||
static void ide_atapi_cmd_reply_end(IDEState *s)
|
static void ide_atapi_cmd_reply_end(IDEState *s)
|
||||||
{
|
{
|
||||||
|
@ -648,15 +825,15 @@ static void ide_atapi_cmd_reply_end(IDEState *s)
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
/* see if a new sector must be read */
|
/* see if a new sector must be read */
|
||||||
if (s->lba != -1 && s->io_buffer_index >= 2048) {
|
if (s->lba != -1 && s->io_buffer_index >= s->cd_sector_size) {
|
||||||
bdrv_read(s->bs, (int64_t)s->lba << 2, s->io_buffer, 4);
|
cd_read_sector(s->bs, s->lba, s->io_buffer, s->cd_sector_size);
|
||||||
s->lba++;
|
s->lba++;
|
||||||
s->io_buffer_index = 0;
|
s->io_buffer_index = 0;
|
||||||
}
|
}
|
||||||
if (s->elementary_transfer_size > 0) {
|
if (s->elementary_transfer_size > 0) {
|
||||||
/* there are some data left to transmit in this elementary
|
/* there are some data left to transmit in this elementary
|
||||||
transfer */
|
transfer */
|
||||||
size = 2048 - s->io_buffer_index;
|
size = s->cd_sector_size - s->io_buffer_index;
|
||||||
if (size > s->elementary_transfer_size)
|
if (size > s->elementary_transfer_size)
|
||||||
size = s->elementary_transfer_size;
|
size = s->elementary_transfer_size;
|
||||||
ide_transfer_start(s, s->io_buffer + s->io_buffer_index,
|
ide_transfer_start(s, s->io_buffer + s->io_buffer_index,
|
||||||
|
@ -685,8 +862,8 @@ static void ide_atapi_cmd_reply_end(IDEState *s)
|
||||||
s->elementary_transfer_size = size;
|
s->elementary_transfer_size = size;
|
||||||
/* we cannot transmit more than one sector at a time */
|
/* we cannot transmit more than one sector at a time */
|
||||||
if (s->lba != -1) {
|
if (s->lba != -1) {
|
||||||
if (size > (2048 - s->io_buffer_index))
|
if (size > (s->cd_sector_size - s->io_buffer_index))
|
||||||
size = (2048 - s->io_buffer_index);
|
size = (s->cd_sector_size - s->io_buffer_index);
|
||||||
}
|
}
|
||||||
ide_transfer_start(s, s->io_buffer + s->io_buffer_index,
|
ide_transfer_start(s, s->io_buffer + s->io_buffer_index,
|
||||||
size, ide_atapi_cmd_reply_end);
|
size, ide_atapi_cmd_reply_end);
|
||||||
|
@ -716,21 +893,88 @@ static void ide_atapi_cmd_reply(IDEState *s, int size, int max_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* start a CD-CDROM read command */
|
/* start a CD-CDROM read command */
|
||||||
static void ide_atapi_cmd_read(IDEState *s, int lba, int nb_sectors)
|
static void ide_atapi_cmd_read_pio(IDEState *s, int lba, int nb_sectors,
|
||||||
|
int sector_size)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG_IDE_ATAPI
|
|
||||||
printf("read: LBA=%d nb_sectors=%d\n", lba, nb_sectors);
|
|
||||||
#endif
|
|
||||||
s->lba = lba;
|
s->lba = lba;
|
||||||
s->packet_transfer_size = nb_sectors * 2048;
|
s->packet_transfer_size = nb_sectors * sector_size;
|
||||||
s->elementary_transfer_size = 0;
|
s->elementary_transfer_size = 0;
|
||||||
s->io_buffer_index = 2048;
|
s->io_buffer_index = sector_size;
|
||||||
|
s->cd_sector_size = sector_size;
|
||||||
|
|
||||||
s->status = READY_STAT;
|
s->status = READY_STAT;
|
||||||
ide_atapi_cmd_reply_end(s);
|
ide_atapi_cmd_reply_end(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ATAPI DMA support */
|
||||||
|
static int ide_atapi_cmd_read_dma_cb(IDEState *s,
|
||||||
|
target_phys_addr_t phys_addr,
|
||||||
|
int transfer_size1)
|
||||||
|
{
|
||||||
|
int len, transfer_size;
|
||||||
|
|
||||||
|
transfer_size = transfer_size1;
|
||||||
|
while (transfer_size > 0) {
|
||||||
|
if (s->packet_transfer_size <= 0)
|
||||||
|
break;
|
||||||
|
len = s->cd_sector_size - s->io_buffer_index;
|
||||||
|
if (len <= 0) {
|
||||||
|
/* transfert next data */
|
||||||
|
cd_read_sector(s->bs, s->lba, s->io_buffer, s->cd_sector_size);
|
||||||
|
s->lba++;
|
||||||
|
s->io_buffer_index = 0;
|
||||||
|
len = s->cd_sector_size;
|
||||||
|
}
|
||||||
|
if (len > transfer_size)
|
||||||
|
len = transfer_size;
|
||||||
|
cpu_physical_memory_write(phys_addr,
|
||||||
|
s->io_buffer + s->io_buffer_index, len);
|
||||||
|
s->packet_transfer_size -= len;
|
||||||
|
s->io_buffer_index += len;
|
||||||
|
transfer_size -= len;
|
||||||
|
phys_addr += len;
|
||||||
|
}
|
||||||
|
if (s->packet_transfer_size <= 0) {
|
||||||
|
s->status = READY_STAT;
|
||||||
|
s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD;
|
||||||
|
ide_set_irq(s);
|
||||||
|
#ifdef DEBUG_IDE_ATAPI
|
||||||
|
printf("dma status=0x%x\n", s->status);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return transfer_size1 - transfer_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* start a CD-CDROM read command with DMA */
|
||||||
|
/* XXX: test if DMA is available */
|
||||||
|
static void ide_atapi_cmd_read_dma(IDEState *s, int lba, int nb_sectors,
|
||||||
|
int sector_size)
|
||||||
|
{
|
||||||
|
s->lba = lba;
|
||||||
|
s->packet_transfer_size = nb_sectors * sector_size;
|
||||||
|
s->io_buffer_index = sector_size;
|
||||||
|
s->cd_sector_size = sector_size;
|
||||||
|
|
||||||
|
s->status = READY_STAT | DRQ_STAT;
|
||||||
|
ide_dma_start(s, ide_atapi_cmd_read_dma_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ide_atapi_cmd_read(IDEState *s, int lba, int nb_sectors,
|
||||||
|
int sector_size)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_IDE_ATAPI
|
||||||
|
printf("read: LBA=%d nb_sectors=%d\n", lba, nb_sectors);
|
||||||
|
#endif
|
||||||
|
if (s->atapi_dma) {
|
||||||
|
ide_atapi_cmd_read_dma(s, lba, nb_sectors, sector_size);
|
||||||
|
} else {
|
||||||
|
ide_atapi_cmd_read_pio(s, lba, nb_sectors, sector_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* same toc as bochs. Return -1 if error or the toc length */
|
/* same toc as bochs. Return -1 if error or the toc length */
|
||||||
|
/* XXX: check this */
|
||||||
static int cdrom_read_toc(IDEState *s, uint8_t *buf, int msf, int start_track)
|
static int cdrom_read_toc(IDEState *s, uint8_t *buf, int msf, int start_track)
|
||||||
{
|
{
|
||||||
uint8_t *q;
|
uint8_t *q;
|
||||||
|
@ -739,8 +983,8 @@ static int cdrom_read_toc(IDEState *s, uint8_t *buf, int msf, int start_track)
|
||||||
if (start_track > 1 && start_track != 0xaa)
|
if (start_track > 1 && start_track != 0xaa)
|
||||||
return -1;
|
return -1;
|
||||||
q = buf + 2;
|
q = buf + 2;
|
||||||
*q++ = 1;
|
*q++ = 1; /* first session */
|
||||||
*q++ = 1;
|
*q++ = 1; /* last session */
|
||||||
if (start_track <= 1) {
|
if (start_track <= 1) {
|
||||||
*q++ = 0; /* reserved */
|
*q++ = 0; /* reserved */
|
||||||
*q++ = 0x14; /* ADR, control */
|
*q++ = 0x14; /* ADR, control */
|
||||||
|
@ -765,9 +1009,8 @@ static int cdrom_read_toc(IDEState *s, uint8_t *buf, int msf, int start_track)
|
||||||
nb_sectors = s->nb_sectors >> 2;
|
nb_sectors = s->nb_sectors >> 2;
|
||||||
if (msf) {
|
if (msf) {
|
||||||
*q++ = 0; /* reserved */
|
*q++ = 0; /* reserved */
|
||||||
*q++ = ((nb_sectors + 150) / 75) / 60;
|
lba_to_msf(q, nb_sectors);
|
||||||
*q++ = ((nb_sectors + 150) / 75) % 60;
|
q += 3;
|
||||||
*q++ = (nb_sectors + 150) % 75;
|
|
||||||
} else {
|
} else {
|
||||||
cpu_to_ube32(q, nb_sectors);
|
cpu_to_ube32(q, nb_sectors);
|
||||||
q += 4;
|
q += 4;
|
||||||
|
@ -777,6 +1020,75 @@ static int cdrom_read_toc(IDEState *s, uint8_t *buf, int msf, int start_track)
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* mostly same info as PearPc */
|
||||||
|
static int cdrom_read_toc_raw(IDEState *s, uint8_t *buf, int msf,
|
||||||
|
int session_num)
|
||||||
|
{
|
||||||
|
uint8_t *q;
|
||||||
|
int nb_sectors, len;
|
||||||
|
|
||||||
|
q = buf + 2;
|
||||||
|
*q++ = 1; /* first session */
|
||||||
|
*q++ = 1; /* last session */
|
||||||
|
|
||||||
|
*q++ = 1; /* session number */
|
||||||
|
*q++ = 0x14; /* data track */
|
||||||
|
*q++ = 0; /* track number */
|
||||||
|
*q++ = 0xa0; /* lead-in */
|
||||||
|
*q++ = 0; /* min */
|
||||||
|
*q++ = 0; /* sec */
|
||||||
|
*q++ = 0; /* frame */
|
||||||
|
*q++ = 0;
|
||||||
|
*q++ = 1; /* first track */
|
||||||
|
*q++ = 0x00; /* disk type */
|
||||||
|
*q++ = 0x00;
|
||||||
|
|
||||||
|
*q++ = 1; /* session number */
|
||||||
|
*q++ = 0x14; /* data track */
|
||||||
|
*q++ = 0; /* track number */
|
||||||
|
*q++ = 0xa1;
|
||||||
|
*q++ = 0; /* min */
|
||||||
|
*q++ = 0; /* sec */
|
||||||
|
*q++ = 0; /* frame */
|
||||||
|
*q++ = 0;
|
||||||
|
*q++ = 1; /* last track */
|
||||||
|
*q++ = 0x00;
|
||||||
|
*q++ = 0x00;
|
||||||
|
|
||||||
|
*q++ = 1; /* session number */
|
||||||
|
*q++ = 0x14; /* data track */
|
||||||
|
*q++ = 0; /* track number */
|
||||||
|
*q++ = 0xa2; /* lead-out */
|
||||||
|
*q++ = 0; /* min */
|
||||||
|
*q++ = 0; /* sec */
|
||||||
|
*q++ = 0; /* frame */
|
||||||
|
nb_sectors = s->nb_sectors >> 2;
|
||||||
|
if (msf) {
|
||||||
|
*q++ = 0; /* reserved */
|
||||||
|
lba_to_msf(q, nb_sectors);
|
||||||
|
q += 3;
|
||||||
|
} else {
|
||||||
|
cpu_to_ube32(q, nb_sectors);
|
||||||
|
q += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
*q++ = 1; /* session number */
|
||||||
|
*q++ = 0x14; /* ADR, control */
|
||||||
|
*q++ = 0; /* track number */
|
||||||
|
*q++ = 1; /* point */
|
||||||
|
*q++ = 0; /* min */
|
||||||
|
*q++ = 0; /* sec */
|
||||||
|
*q++ = 0; /* frame */
|
||||||
|
*q++ = 0;
|
||||||
|
*q++ = 0;
|
||||||
|
*q++ = 0;
|
||||||
|
*q++ = 0;
|
||||||
|
|
||||||
|
len = q - buf;
|
||||||
|
cpu_to_ube16(buf, len - 2);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
static void ide_atapi_cmd(IDEState *s)
|
static void ide_atapi_cmd(IDEState *s)
|
||||||
{
|
{
|
||||||
const uint8_t *packet;
|
const uint8_t *packet;
|
||||||
|
@ -921,7 +1233,48 @@ static void ide_atapi_cmd(IDEState *s)
|
||||||
ASC_LOGICAL_BLOCK_OOR);
|
ASC_LOGICAL_BLOCK_OOR);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ide_atapi_cmd_read(s, lba, nb_sectors);
|
ide_atapi_cmd_read(s, lba, nb_sectors, 2048);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GPCMD_READ_CD:
|
||||||
|
{
|
||||||
|
int nb_sectors, lba, transfer_request;
|
||||||
|
|
||||||
|
if (!bdrv_is_inserted(s->bs)) {
|
||||||
|
ide_atapi_cmd_error(s, SENSE_NOT_READY,
|
||||||
|
ASC_MEDIUM_NOT_PRESENT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nb_sectors = (packet[6] << 16) | (packet[7] << 8) | packet[8];
|
||||||
|
lba = ube32_to_cpu(packet + 2);
|
||||||
|
if (nb_sectors == 0) {
|
||||||
|
ide_atapi_cmd_ok(s);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (((int64_t)(lba + nb_sectors) << 2) > s->nb_sectors) {
|
||||||
|
ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
|
||||||
|
ASC_LOGICAL_BLOCK_OOR);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
transfer_request = packet[9];
|
||||||
|
switch(transfer_request & 0xf8) {
|
||||||
|
case 0x00:
|
||||||
|
/* nothing */
|
||||||
|
ide_atapi_cmd_ok(s);
|
||||||
|
break;
|
||||||
|
case 0x10:
|
||||||
|
/* normal read */
|
||||||
|
ide_atapi_cmd_read(s, lba, nb_sectors, 2048);
|
||||||
|
break;
|
||||||
|
case 0xf8:
|
||||||
|
/* read all data */
|
||||||
|
ide_atapi_cmd_read(s, lba, nb_sectors, 2352);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
|
||||||
|
ASC_INV_FIELD_IN_CMD_PACKET);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GPCMD_SEEK:
|
case GPCMD_SEEK:
|
||||||
|
@ -995,6 +1348,12 @@ static void ide_atapi_cmd(IDEState *s)
|
||||||
buf[3] = 0x01;
|
buf[3] = 0x01;
|
||||||
ide_atapi_cmd_reply(s, 12, max_len);
|
ide_atapi_cmd_reply(s, 12, max_len);
|
||||||
break;
|
break;
|
||||||
|
case 2:
|
||||||
|
len = cdrom_read_toc_raw(s, buf, msf, start_track);
|
||||||
|
if (len < 0)
|
||||||
|
goto error_cmd;
|
||||||
|
ide_atapi_cmd_reply(s, len, max_len);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
error_cmd:
|
error_cmd:
|
||||||
ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
|
ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
|
||||||
|
@ -1169,6 +1528,18 @@ static void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
||||||
n = s->req_nb_sectors;
|
n = s->req_nb_sectors;
|
||||||
ide_transfer_start(s, s->io_buffer, 512 * n, ide_sector_write);
|
ide_transfer_start(s, s->io_buffer, 512 * n, ide_sector_write);
|
||||||
break;
|
break;
|
||||||
|
case WIN_READDMA:
|
||||||
|
case WIN_READDMA_ONCE:
|
||||||
|
if (!s->bs)
|
||||||
|
goto abort_cmd;
|
||||||
|
ide_sector_read_dma(s);
|
||||||
|
break;
|
||||||
|
case WIN_WRITEDMA:
|
||||||
|
case WIN_WRITEDMA_ONCE:
|
||||||
|
if (!s->bs)
|
||||||
|
goto abort_cmd;
|
||||||
|
ide_sector_write_dma(s);
|
||||||
|
break;
|
||||||
case WIN_READ_NATIVE_MAX:
|
case WIN_READ_NATIVE_MAX:
|
||||||
ide_set_sector(s, s->nb_sectors - 1);
|
ide_set_sector(s, s->nb_sectors - 1);
|
||||||
s->status = READY_STAT;
|
s->status = READY_STAT;
|
||||||
|
@ -1185,6 +1556,7 @@ static void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
||||||
/* XXX: valid for CDROM ? */
|
/* XXX: valid for CDROM ? */
|
||||||
switch(s->feature) {
|
switch(s->feature) {
|
||||||
case 0x02: /* write cache enable */
|
case 0x02: /* write cache enable */
|
||||||
|
case 0x03: /* set transfer mode */
|
||||||
case 0x82: /* write cache disable */
|
case 0x82: /* write cache disable */
|
||||||
case 0xaa: /* read look-ahead enable */
|
case 0xaa: /* read look-ahead enable */
|
||||||
case 0x55: /* read look-ahead disable */
|
case 0x55: /* read look-ahead disable */
|
||||||
|
@ -1216,9 +1588,10 @@ static void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
||||||
case WIN_PACKETCMD:
|
case WIN_PACKETCMD:
|
||||||
if (!s->is_cdrom)
|
if (!s->is_cdrom)
|
||||||
goto abort_cmd;
|
goto abort_cmd;
|
||||||
/* DMA or overlapping commands not supported */
|
/* overlapping commands not supported */
|
||||||
if ((s->feature & 0x03) != 0)
|
if (s->feature & 0x02)
|
||||||
goto abort_cmd;
|
goto abort_cmd;
|
||||||
|
s->atapi_dma = s->feature & 1;
|
||||||
s->nsector = 1;
|
s->nsector = 1;
|
||||||
ide_transfer_start(s, s->io_buffer, ATAPI_PACKET_SIZE,
|
ide_transfer_start(s, s->io_buffer, ATAPI_PACKET_SIZE,
|
||||||
ide_atapi_cmd);
|
ide_atapi_cmd);
|
||||||
|
@ -1549,11 +1922,6 @@ void isa_ide_init(int iobase, int iobase2, int irq,
|
||||||
/***********************************************************/
|
/***********************************************************/
|
||||||
/* PCI IDE definitions */
|
/* PCI IDE definitions */
|
||||||
|
|
||||||
typedef struct PCIIDEState {
|
|
||||||
PCIDevice dev;
|
|
||||||
IDEState ide_if[4];
|
|
||||||
} PCIIDEState;
|
|
||||||
|
|
||||||
static void ide_map(PCIDevice *pci_dev, int region_num,
|
static void ide_map(PCIDevice *pci_dev, int region_num,
|
||||||
uint32_t addr, uint32_t size, int type)
|
uint32_t addr, uint32_t size, int type)
|
||||||
{
|
{
|
||||||
|
@ -1578,6 +1946,155 @@ static void ide_map(PCIDevice *pci_dev, int region_num,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* XXX: full callback usage to prepare non blocking I/Os support -
|
||||||
|
error handling */
|
||||||
|
static void ide_dma_loop(BMDMAState *bm)
|
||||||
|
{
|
||||||
|
struct {
|
||||||
|
uint32_t addr;
|
||||||
|
uint32_t size;
|
||||||
|
} prd;
|
||||||
|
target_phys_addr_t cur_addr;
|
||||||
|
int len, i, len1;
|
||||||
|
|
||||||
|
cur_addr = bm->addr;
|
||||||
|
/* at most one page to avoid hanging if erroneous parameters */
|
||||||
|
for(i = 0; i < 512; i++) {
|
||||||
|
cpu_physical_memory_read(cur_addr, (uint8_t *)&prd, 8);
|
||||||
|
prd.addr = le32_to_cpu(prd.addr);
|
||||||
|
prd.size = le32_to_cpu(prd.size);
|
||||||
|
#ifdef DEBUG_IDE
|
||||||
|
printf("ide: dma: prd: %08x: addr=0x%08x size=0x%08x\n",
|
||||||
|
(int)cur_addr, prd.addr, prd.size);
|
||||||
|
#endif
|
||||||
|
len = prd.size & 0xfffe;
|
||||||
|
if (len == 0)
|
||||||
|
len = 0x10000;
|
||||||
|
while (len > 0) {
|
||||||
|
len1 = bm->dma_cb(bm->ide_if, prd.addr, len);
|
||||||
|
if (len1 == 0)
|
||||||
|
goto the_end;
|
||||||
|
prd.addr += len1;
|
||||||
|
len -= len1;
|
||||||
|
}
|
||||||
|
/* end of transfer */
|
||||||
|
if (prd.size & 0x80000000)
|
||||||
|
break;
|
||||||
|
cur_addr += 8;
|
||||||
|
}
|
||||||
|
/* end of transfer */
|
||||||
|
the_end:
|
||||||
|
bm->status &= ~BM_STATUS_DMAING;
|
||||||
|
bm->status |= BM_STATUS_INT;
|
||||||
|
bm->dma_cb = NULL;
|
||||||
|
bm->ide_if = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ide_dma_start(IDEState *s, IDEDMAFunc *dma_cb)
|
||||||
|
{
|
||||||
|
BMDMAState *bm = s->bmdma;
|
||||||
|
if(!bm)
|
||||||
|
return;
|
||||||
|
bm->ide_if = s;
|
||||||
|
bm->dma_cb = dma_cb;
|
||||||
|
if (bm->status & BM_STATUS_DMAING) {
|
||||||
|
ide_dma_loop(bm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t bmdma_cmd_readb(void *opaque, uint32_t addr)
|
||||||
|
{
|
||||||
|
BMDMAState *bm = opaque;
|
||||||
|
uint32_t val;
|
||||||
|
val = bm->cmd;
|
||||||
|
#ifdef DEBUG_IDE
|
||||||
|
printf("%s: 0x%08x\n", __func__, val);
|
||||||
|
#endif
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bmdma_cmd_writeb(void *opaque, uint32_t addr, uint32_t val)
|
||||||
|
{
|
||||||
|
BMDMAState *bm = opaque;
|
||||||
|
#ifdef DEBUG_IDE
|
||||||
|
printf("%s: 0x%08x\n", __func__, val);
|
||||||
|
#endif
|
||||||
|
if (!(val & BM_CMD_START)) {
|
||||||
|
/* XXX: do it better */
|
||||||
|
bm->status &= ~BM_STATUS_DMAING;
|
||||||
|
bm->cmd = val & 0x09;
|
||||||
|
} else {
|
||||||
|
bm->status |= BM_STATUS_DMAING;
|
||||||
|
bm->cmd = val & 0x09;
|
||||||
|
/* start dma transfer if possible */
|
||||||
|
if (bm->dma_cb)
|
||||||
|
ide_dma_loop(bm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t bmdma_status_readb(void *opaque, uint32_t addr)
|
||||||
|
{
|
||||||
|
BMDMAState *bm = opaque;
|
||||||
|
uint32_t val;
|
||||||
|
val = bm->status;
|
||||||
|
#ifdef DEBUG_IDE
|
||||||
|
printf("%s: 0x%08x\n", __func__, val);
|
||||||
|
#endif
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bmdma_status_writeb(void *opaque, uint32_t addr, uint32_t val)
|
||||||
|
{
|
||||||
|
BMDMAState *bm = opaque;
|
||||||
|
#ifdef DEBUG_IDE
|
||||||
|
printf("%s: 0x%08x\n", __func__, val);
|
||||||
|
#endif
|
||||||
|
bm->status = (val & 0x60) | (bm->status & 1) | (bm->status & ~val & 0x06);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t bmdma_addr_readl(void *opaque, uint32_t addr)
|
||||||
|
{
|
||||||
|
BMDMAState *bm = opaque;
|
||||||
|
uint32_t val;
|
||||||
|
val = bm->addr;
|
||||||
|
#ifdef DEBUG_IDE
|
||||||
|
printf("%s: 0x%08x\n", __func__, val);
|
||||||
|
#endif
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bmdma_addr_writel(void *opaque, uint32_t addr, uint32_t val)
|
||||||
|
{
|
||||||
|
BMDMAState *bm = opaque;
|
||||||
|
#ifdef DEBUG_IDE
|
||||||
|
printf("%s: 0x%08x\n", __func__, val);
|
||||||
|
#endif
|
||||||
|
bm->addr = val & ~3;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bmdma_map(PCIDevice *pci_dev, int region_num,
|
||||||
|
uint32_t addr, uint32_t size, int type)
|
||||||
|
{
|
||||||
|
PCIIDEState *d = (PCIIDEState *)pci_dev;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0;i < 2; i++) {
|
||||||
|
BMDMAState *bm = &d->bmdma[i];
|
||||||
|
d->ide_if[2 * i].bmdma = bm;
|
||||||
|
d->ide_if[2 * i + 1].bmdma = bm;
|
||||||
|
|
||||||
|
register_ioport_write(addr, 1, 1, bmdma_cmd_writeb, bm);
|
||||||
|
register_ioport_read(addr, 1, 1, bmdma_cmd_readb, bm);
|
||||||
|
|
||||||
|
register_ioport_write(addr + 2, 1, 1, bmdma_status_writeb, bm);
|
||||||
|
register_ioport_read(addr + 2, 1, 1, bmdma_status_readb, bm);
|
||||||
|
|
||||||
|
register_ioport_write(addr + 4, 4, 4, bmdma_addr_writel, bm);
|
||||||
|
register_ioport_read(addr + 4, 4, 4, bmdma_addr_readl, bm);
|
||||||
|
addr += 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* hd_table must contain 4 block drivers */
|
/* hd_table must contain 4 block drivers */
|
||||||
void pci_ide_init(PCIBus *bus, BlockDriverState **hd_table)
|
void pci_ide_init(PCIBus *bus, BlockDriverState **hd_table)
|
||||||
{
|
{
|
||||||
|
@ -1610,6 +2127,8 @@ void pci_ide_init(PCIBus *bus, BlockDriverState **hd_table)
|
||||||
PCI_ADDRESS_SPACE_IO, ide_map);
|
PCI_ADDRESS_SPACE_IO, ide_map);
|
||||||
pci_register_io_region((PCIDevice *)d, 3, 0x4,
|
pci_register_io_region((PCIDevice *)d, 3, 0x4,
|
||||||
PCI_ADDRESS_SPACE_IO, ide_map);
|
PCI_ADDRESS_SPACE_IO, ide_map);
|
||||||
|
pci_register_io_region((PCIDevice *)d, 4, 0x10,
|
||||||
|
PCI_ADDRESS_SPACE_IO, bmdma_map);
|
||||||
|
|
||||||
pci_conf[0x3d] = 0x01; // interrupt on pin 1
|
pci_conf[0x3d] = 0x01; // interrupt on pin 1
|
||||||
|
|
||||||
|
@ -1640,7 +2159,8 @@ void pci_piix3_ide_init(PCIBus *bus, BlockDriverState **hd_table)
|
||||||
pci_conf[0x0b] = 0x01; // class_base = PCI_mass_storage
|
pci_conf[0x0b] = 0x01; // class_base = PCI_mass_storage
|
||||||
pci_conf[0x0e] = 0x00; // header_type
|
pci_conf[0x0e] = 0x00; // header_type
|
||||||
|
|
||||||
/* XXX: must add BMDMA support to be fully compliant */
|
pci_register_io_region((PCIDevice *)d, 4, 0x10,
|
||||||
|
PCI_ADDRESS_SPACE_IO, bmdma_map);
|
||||||
|
|
||||||
ide_init2(&d->ide_if[0], 14, hd_table[0], hd_table[1]);
|
ide_init2(&d->ide_if[0], 14, hd_table[0], hd_table[1]);
|
||||||
ide_init2(&d->ide_if[2], 15, hd_table[2], hd_table[3]);
|
ide_init2(&d->ide_if[2], 15, hd_table[2], hd_table[3]);
|
||||||
|
|
Loading…
Reference in a new issue