* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <string.h>
-#include <getopt.h>
-#include <inttypes.h>
-#include <unistd.h>
-#include <sys/mman.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <time.h>
-#include <sys/time.h>
-#include <malloc.h>
-#include <termios.h>
-#include <sys/poll.h>
-#include <errno.h>
-#include <sys/wait.h>
-#include <netinet/in.h>
-
-#define NO_THUNK_TYPE_SIZE
-#include "thunk.h"
-
-#include "cpu.h"
-#include "exec-all.h"
-
#include "vl.h"
/* debug IDE devices */
#define DISABLE_SEAGATE 0xFB
/* set to 1 set disable mult support */
-#define MAX_MULT_SECTORS 8
+#define MAX_MULT_SECTORS 16
/* ATAPI defines */
#define ATAPI_INT_REASON_TAG 0xf8
/* same constants as bochs */
+#define ASC_ILLEGAL_OPCODE 0x20
#define ASC_LOGICAL_BLOCK_OOR 0x21
#define ASC_INV_FIELD_IN_CMD_PACKET 0x24
#define ASC_MEDIUM_NOT_PRESENT 0x3a
typedef void EndTransferFunc(struct IDEState *);
+/* NOTE: IDEState represents in fact one drive */
typedef struct IDEState {
/* ide config */
int is_cdrom;
- int cdrom_locked;
int cylinders, heads, sectors;
int64_t nb_sectors;
int mult_sectors;
+ int identify_set;
+ uint16_t identify_data[256];
+ SetIRQFunc *set_irq;
+ void *irq_opaque;
int irq;
+ PCIDevice *pci_dev;
+ struct BMDMAState *bmdma;
+ int drive_serial;
/* ide regs */
uint8_t feature;
uint8_t error;
- uint16_t nsector; /* 0 is 256 to ease computations */
+ uint32_t nsector;
uint8_t sector;
uint8_t lcyl;
uint8_t hcyl;
+ /* other part of tf for lba48 support */
+ uint8_t hob_feature;
+ uint8_t hob_nsector;
+ uint8_t hob_sector;
+ uint8_t hob_lcyl;
+ uint8_t hob_hcyl;
+
uint8_t select;
uint8_t status;
+
/* 0x3f6 command, only meaningful for drive 0 */
uint8_t cmd;
+ /* set for lba48 access */
+ uint8_t lba48;
/* depends on bit 4 in select, only meaningful for drive 0 */
struct IDEState *cur_drive;
BlockDriverState *bs;
int elementary_transfer_size;
int io_buffer_index;
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 */
EndTransferFunc *end_transfer_func;
uint8_t *data_ptr;
uint8_t *data_end;
uint8_t io_buffer[MAX_MULT_SECTORS*512 + 4];
+ QEMUTimer *sector_write_timer; /* only used for win2k instal hack */
+ uint32_t irq_count; /* counts IRQs when using win2k install hack */
} IDEState;
-IDEState ide_state[MAX_DISKS];
-IDEState *ide_table[0x400 >> 3];
+#define BM_STATUS_DMAING 0x01
+#define BM_STATUS_ERROR 0x02
+#define BM_STATUS_INT 0x04
-static inline IDEState *get_ide_interface(uint32_t addr)
-{
- return ide_table[addr >> 3];
-}
+#define BM_CMD_START 0x01
+#define BM_CMD_READ 0x08
+
+#define IDE_TYPE_PIIX3 0
+#define IDE_TYPE_CMD646 1
+
+/* CMD646 specific */
+#define MRDMODE 0x71
+#define MRDMODE_INTR_CH0 0x04
+#define MRDMODE_INTR_CH1 0x08
+#define MRDMODE_BLK_CH0 0x10
+#define MRDMODE_BLK_CH1 0x20
+#define UDIDETCR0 0x73
+#define UDIDETCR1 0x7B
+
+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;
+
+ struct PCIIDEState *pci_dev;
+ /* current transfer state */
+ IDEState *ide_if;
+ IDEDMAFunc *dma_cb;
+} BMDMAState;
+
+typedef struct PCIIDEState {
+ PCIDevice dev;
+ IDEState ide_if[4];
+ BMDMAState bmdma[2];
+ int type; /* see IDE_TYPE_xxx */
+} PCIIDEState;
+
+static void ide_dma_start(IDEState *s, IDEDMAFunc *dma_cb);
static void padstr(char *str, const char *src, int len)
{
}
}
+static void put_le16(uint16_t *p, unsigned int v)
+{
+ *p = cpu_to_le16(v);
+}
+
static void ide_identify(IDEState *s)
{
uint16_t *p;
unsigned int oldsize;
+ char buf[20];
+
+ if (s->identify_set) {
+ memcpy(s->io_buffer, s->identify_data, sizeof(s->identify_data));
+ return;
+ }
memset(s->io_buffer, 0, 512);
p = (uint16_t *)s->io_buffer;
- stw_raw(p + 0, 0x0040);
- stw_raw(p + 1, s->cylinders);
- stw_raw(p + 3, s->heads);
- stw_raw(p + 4, 512 * s->sectors); /* sectors */
- stw_raw(p + 5, 512); /* sector size */
- stw_raw(p + 6, s->sectors);
- padstr((uint8_t *)(p + 10), "QM00001", 20); /* serial number */
- stw_raw(p + 20, 3); /* buffer type */
- stw_raw(p + 21, 512); /* cache size in sectors */
- stw_raw(p + 22, 4); /* ecc bytes */
+ put_le16(p + 0, 0x0040);
+ put_le16(p + 1, s->cylinders);
+ put_le16(p + 3, s->heads);
+ put_le16(p + 4, 512 * s->sectors); /* XXX: retired, remove ? */
+ put_le16(p + 5, 512); /* XXX: retired, remove ? */
+ put_le16(p + 6, s->sectors);
+ snprintf(buf, sizeof(buf), "QM%05d", s->drive_serial);
+ padstr((uint8_t *)(p + 10), buf, 20); /* serial number */
+ put_le16(p + 20, 3); /* XXX: retired, remove ? */
+ put_le16(p + 21, 512); /* cache size in sectors */
+ put_le16(p + 22, 4); /* ecc bytes */
padstr((uint8_t *)(p + 23), QEMU_VERSION, 8); /* firmware version */
padstr((uint8_t *)(p + 27), "QEMU HARDDISK", 40); /* model */
#if MAX_MULT_SECTORS > 1
- stw_raw(p + 47, MAX_MULT_SECTORS);
+ put_le16(p + 47, 0x8000 | MAX_MULT_SECTORS);
#endif
- stw_raw(p + 48, 1); /* dword I/O */
- stw_raw(p + 49, 1 << 9); /* LBA supported, no DMA */
- stw_raw(p + 51, 0x200); /* PIO transfer cycle */
- stw_raw(p + 52, 0x200); /* DMA transfer cycle */
- stw_raw(p + 54, s->cylinders);
- stw_raw(p + 55, s->heads);
- stw_raw(p + 56, s->sectors);
+ put_le16(p + 48, 1); /* dword I/O */
+ put_le16(p + 49, (1 << 11) | (1 << 9) | (1 << 8)); /* DMA and LBA supported */
+ put_le16(p + 51, 0x200); /* PIO transfer cycle */
+ put_le16(p + 52, 0x200); /* DMA transfer cycle */
+ put_le16(p + 53, 1 | (1 << 1) | (1 << 2)); /* words 54-58,64-70,88 are valid */
+ put_le16(p + 54, s->cylinders);
+ put_le16(p + 55, s->heads);
+ put_le16(p + 56, s->sectors);
oldsize = s->cylinders * s->heads * s->sectors;
- stw_raw(p + 57, oldsize);
- stw_raw(p + 58, oldsize >> 16);
+ put_le16(p + 57, oldsize);
+ put_le16(p + 58, oldsize >> 16);
if (s->mult_sectors)
- stw_raw(p + 59, 0x100 | s->mult_sectors);
- stw_raw(p + 60, s->nb_sectors);
- stw_raw(p + 61, s->nb_sectors >> 16);
- stw_raw(p + 80, (1 << 1) | (1 << 2));
- stw_raw(p + 82, (1 << 14));
- stw_raw(p + 83, (1 << 14));
- stw_raw(p + 84, (1 << 14));
- stw_raw(p + 85, (1 << 14));
- stw_raw(p + 86, 0);
- stw_raw(p + 87, (1 << 14));
+ put_le16(p + 59, 0x100 | s->mult_sectors);
+ put_le16(p + 60, s->nb_sectors);
+ put_le16(p + 61, s->nb_sectors >> 16);
+ put_le16(p + 63, 0x07); /* mdma0-2 supported */
+ put_le16(p + 65, 120);
+ put_le16(p + 66, 120);
+ put_le16(p + 67, 120);
+ put_le16(p + 68, 120);
+ put_le16(p + 80, 0xf0); /* ata3 -> ata6 supported */
+ put_le16(p + 81, 0x16); /* conforms to ata5 */
+ put_le16(p + 82, (1 << 14));
+ /* 13=flush_cache_ext,12=flush_cache,10=lba48 */
+ put_le16(p + 83, (1 << 14) | (1 << 13) | (1 <<12) | (1 << 10));
+ put_le16(p + 84, (1 << 14));
+ put_le16(p + 85, (1 << 14));
+ /* 13=flush_cache_ext,12=flush_cache,10=lba48 */
+ put_le16(p + 86, (1 << 14) | (1 << 13) | (1 <<12) | (1 << 10));
+ put_le16(p + 87, (1 << 14));
+ put_le16(p + 88, 0x3f | (1 << 13)); /* udma5 set and supported */
+ put_le16(p + 93, 1 | (1 << 14) | 0x2000);
+ put_le16(p + 100, s->nb_sectors);
+ put_le16(p + 101, s->nb_sectors >> 16);
+ put_le16(p + 102, s->nb_sectors >> 32);
+ put_le16(p + 103, s->nb_sectors >> 48);
+
+ memcpy(s->identify_data, p, sizeof(s->identify_data));
+ s->identify_set = 1;
}
static void ide_atapi_identify(IDEState *s)
{
uint16_t *p;
+ char buf[20];
+
+ if (s->identify_set) {
+ memcpy(s->io_buffer, s->identify_data, sizeof(s->identify_data));
+ return;
+ }
memset(s->io_buffer, 0, 512);
p = (uint16_t *)s->io_buffer;
/* Removable CDROM, 50us response, 12 byte packets */
- stw_raw(p + 0, (2 << 14) | (5 << 8) | (1 << 7) | (2 << 5) | (0 << 0));
- stw_raw(p + 1, s->cylinders);
- stw_raw(p + 3, s->heads);
- stw_raw(p + 4, 512 * s->sectors); /* sectors */
- stw_raw(p + 5, 512); /* sector size */
- stw_raw(p + 6, s->sectors);
- padstr((uint8_t *)(p + 10), "QM00001", 20); /* serial number */
- stw_raw(p + 20, 3); /* buffer type */
- stw_raw(p + 21, 512); /* cache size in sectors */
- stw_raw(p + 22, 4); /* ecc bytes */
+ put_le16(p + 0, (2 << 14) | (5 << 8) | (1 << 7) | (2 << 5) | (0 << 0));
+ snprintf(buf, sizeof(buf), "QM%05d", s->drive_serial);
+ padstr((uint8_t *)(p + 10), buf, 20); /* serial number */
+ put_le16(p + 20, 3); /* buffer type */
+ put_le16(p + 21, 512); /* cache size in sectors */
+ put_le16(p + 22, 4); /* ecc bytes */
padstr((uint8_t *)(p + 23), QEMU_VERSION, 8); /* firmware version */
padstr((uint8_t *)(p + 27), "QEMU CD-ROM", 40); /* model */
- stw_raw(p + 48, 1); /* dword I/O (XXX: should not be set on CDROM) */
- stw_raw(p + 49, 1 << 9); /* LBA supported, no DMA */
- stw_raw(p + 53, 3); /* words 64-70, 54-58 valid */
- stw_raw(p + 63, 0x103); /* DMA modes XXX: may be incorrect */
- stw_raw(p + 64, 1); /* PIO modes */
- stw_raw(p + 65, 0xb4); /* minimum DMA multiword tx cycle time */
- stw_raw(p + 66, 0xb4); /* recommended DMA multiword tx cycle time */
- stw_raw(p + 67, 0x12c); /* minimum PIO cycle time without flow control */
- stw_raw(p + 68, 0xb4); /* minimum PIO cycle time with IORDY flow control */
-
- stw_raw(p + 71, 30); /* in ns */
- stw_raw(p + 72, 30); /* in ns */
-
- stw_raw(p + 80, 0x1e); /* support up to ATA/ATAPI-4 */
+ put_le16(p + 48, 1); /* dword I/O (XXX: should not be set on CDROM) */
+ put_le16(p + 49, 1 << 9); /* LBA supported, no DMA */
+ put_le16(p + 53, 3); /* words 64-70, 54-58 valid */
+ put_le16(p + 63, 0x103); /* DMA modes XXX: may be incorrect */
+ put_le16(p + 64, 1); /* PIO modes */
+ put_le16(p + 65, 0xb4); /* minimum DMA multiword tx cycle time */
+ put_le16(p + 66, 0xb4); /* recommended DMA multiword tx cycle time */
+ put_le16(p + 67, 0x12c); /* minimum PIO cycle time without flow control */
+ put_le16(p + 68, 0xb4); /* minimum PIO cycle time with IORDY flow control */
+
+ put_le16(p + 71, 30); /* in ns */
+ put_le16(p + 72, 30); /* in ns */
+
+ put_le16(p + 80, 0x1e); /* support up to ATA/ATAPI-4 */
+
+ memcpy(s->identify_data, p, sizeof(s->identify_data));
+ s->identify_set = 1;
}
static void ide_set_signature(IDEState *s)
static inline void ide_set_irq(IDEState *s)
{
+ BMDMAState *bm = s->bmdma;
if (!(s->cmd & IDE_CMD_DISABLE_IRQ)) {
- pic_set_irq(s->irq, 1);
+ if (bm) {
+ bm->status |= BM_STATUS_INT;
+ }
+ s->set_irq(s->irq_opaque, s->irq, 1);
}
}
int64_t sector_num;
if (s->select & 0x40) {
/* lba */
- sector_num = ((s->select & 0x0f) << 24) | (s->hcyl << 16) |
- (s->lcyl << 8) | s->sector;
+ if (!s->lba48) {
+ sector_num = ((s->select & 0x0f) << 24) | (s->hcyl << 16) |
+ (s->lcyl << 8) | s->sector;
+ } else {
+ sector_num = ((int64_t)s->hob_hcyl << 40) |
+ ((int64_t) s->hob_lcyl << 32) |
+ ((int64_t) s->hob_sector << 24) |
+ ((int64_t) s->hcyl << 16) |
+ ((int64_t) s->lcyl << 8) | s->sector;
+ }
} else {
sector_num = ((s->hcyl << 8) | s->lcyl) * s->heads * s->sectors +
- (s->select & 0x0f) * s->sectors +
- (s->sector - 1);
+ (s->select & 0x0f) * s->sectors + (s->sector - 1);
}
return sector_num;
}
{
unsigned int cyl, r;
if (s->select & 0x40) {
- s->select = (s->select & 0xf0) | (sector_num >> 24);
- s->hcyl = (sector_num >> 16);
- s->lcyl = (sector_num >> 8);
- s->sector = (sector_num);
+ if (!s->lba48) {
+ s->select = (s->select & 0xf0) | (sector_num >> 24);
+ s->hcyl = (sector_num >> 16);
+ s->lcyl = (sector_num >> 8);
+ s->sector = (sector_num);
+ } else {
+ s->sector = sector_num;
+ s->lcyl = sector_num >> 8;
+ s->hcyl = sector_num >> 16;
+ s->hob_sector = sector_num >> 24;
+ s->hob_lcyl = sector_num >> 32;
+ s->hob_hcyl = sector_num >> 40;
+ }
} else {
cyl = sector_num / (s->heads * s->sectors);
r = sector_num % (s->heads * s->sectors);
s->hcyl = cyl >> 8;
s->lcyl = cyl;
- s->select = (s->select & 0xf0) | (r / s->sectors);
+ s->select = (s->select & 0xf0) | ((r / s->sectors) & 0x0f);
s->sector = (r % s->sectors) + 1;
}
}
}
}
+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_timer_cb(void *opaque)
+{
+ IDEState *s = opaque;
+ ide_set_irq(s);
+}
+
static void ide_sector_write(IDEState *s)
{
int64_t sector_num;
ide_transfer_start(s, s->io_buffer, 512 * n1, ide_sector_write);
}
ide_set_sector(s, sector_num + n);
- ide_set_irq(s);
+
+#ifdef TARGET_I386
+ if (win2k_install_hack && ((++s->irq_count % 16) == 0)) {
+ /* It seems there is a bug in the Windows 2000 installer HDD
+ IDE driver which fills the disk with empty logs when the
+ IDE write IRQ comes too early. This hack tries to correct
+ that at the expense of slower write performances. Use this
+ option _only_ to install Windows 2000. You must disable it
+ for normal use. */
+ qemu_mod_timer(s->sector_write_timer,
+ qemu_get_clock(vm_clock) + (ticks_per_sec / 1000));
+ } else
+#endif
+ {
+ 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;
+#ifdef TARGET_I386
+ if (win2k_install_hack && ((++s->irq_count % 16) == 0)) {
+ /* It seems there is a bug in the Windows 2000 installer
+ HDD IDE driver which fills the disk with empty logs
+ when the IDE write IRQ comes too early. This hack tries
+ to correct that at the expense of slower write
+ performances. Use this option _only_ to install Windows
+ 2000. You must disable it for normal use. */
+ qemu_mod_timer(s->sector_write_timer,
+ qemu_get_clock(vm_clock) + (ticks_per_sec / 1000));
+ } else
+#endif
+ 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)
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, 10);
+ buf[11] = 0x00;
+ 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 */
static void ide_atapi_cmd_reply_end(IDEState *s)
{
#endif
} else {
/* see if a new sector must be read */
- if (s->lba != -1 && s->io_buffer_index >= 2048) {
- bdrv_read(s->bs, (int64_t)s->lba << 2, s->io_buffer, 4);
+ if (s->lba != -1 && s->io_buffer_index >= s->cd_sector_size) {
+ cd_read_sector(s->bs, s->lba, s->io_buffer, s->cd_sector_size);
s->lba++;
s->io_buffer_index = 0;
}
if (s->elementary_transfer_size > 0) {
/* there are some data left to transmit in this elementary
transfer */
- size = 2048 - s->io_buffer_index;
+ size = s->cd_sector_size - s->io_buffer_index;
if (size > s->elementary_transfer_size)
size = s->elementary_transfer_size;
ide_transfer_start(s, s->io_buffer + s->io_buffer_index,
s->elementary_transfer_size = size;
/* we cannot transmit more than one sector at a time */
if (s->lba != -1) {
- if (size > (2048 - s->io_buffer_index))
- size = (2048 - s->io_buffer_index);
+ if (size > (s->cd_sector_size - s->io_buffer_index))
+ size = (s->cd_sector_size - s->io_buffer_index);
}
ide_transfer_start(s, s->io_buffer + s->io_buffer_index,
size, ide_atapi_cmd_reply_end);
}
/* 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->packet_transfer_size = nb_sectors * 2048;
+ s->packet_transfer_size = nb_sectors * sector_size;
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;
ide_atapi_cmd_reply_end(s);
}
-/* same toc as bochs. Return -1 if error or the toc length */
-static int cdrom_read_toc(IDEState *s, uint8_t *buf, int msf, int start_track)
+/* ATAPI DMA support */
+static int ide_atapi_cmd_read_dma_cb(IDEState *s,
+ target_phys_addr_t phys_addr,
+ int transfer_size1)
{
- uint8_t *q;
- int nb_sectors, len;
+ int len, transfer_size;
- if (start_track > 1 && start_track != 0xaa)
- return -1;
- q = buf + 2;
- *q++ = 1;
- *q++ = 1;
- if (start_track <= 1) {
- *q++ = 0; /* reserved */
- *q++ = 0x14; /* ADR, control */
- *q++ = 1; /* track number */
- *q++ = 0; /* reserved */
- if (msf) {
- *q++ = 0; /* reserved */
- *q++ = 0; /* minute */
- *q++ = 2; /* second */
- *q++ = 0; /* frame */
- } else {
- /* sector 0 */
- cpu_to_ube32(q, 0);
- q += 4;
+ transfer_size = transfer_size1;
+ while (transfer_size > 0) {
+#ifdef DEBUG_IDE_ATAPI
+ printf("transfer_size: %d phys_addr=%08x\n", transfer_size, phys_addr);
+#endif
+ 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;
}
- /* lead out track */
- *q++ = 0; /* reserved */
- *q++ = 0x16; /* ADR, control */
- *q++ = 0xaa; /* track number */
- *q++ = 0; /* reserved */
- nb_sectors = s->nb_sectors >> 2;
- if (msf) {
- *q++ = 0; /* reserved */
- *q++ = ((nb_sectors + 150) / 75) / 60;
- *q++ = ((nb_sectors + 150) / 75) % 60;
- *q++ = (nb_sectors + 150) % 75;
+ 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 {
- cpu_to_ube32(q, nb_sectors);
- q += 4;
+ ide_atapi_cmd_read_pio(s, lba, nb_sectors, sector_size);
}
- len = q - buf;
- cpu_to_ube16(buf, len - 2);
- return len;
}
static void ide_atapi_cmd(IDEState *s)
#endif
switch(s->io_buffer[0]) {
case GPCMD_TEST_UNIT_READY:
- if (s->bs) {
+ if (bdrv_is_inserted(s->bs)) {
ide_atapi_cmd_ok(s);
} else {
ide_atapi_cmd_error(s, SENSE_NOT_READY,
buf[12] = 0x70;
buf[13] = 3 << 5;
buf[14] = (1 << 0) | (1 << 3) | (1 << 5);
- if (s->cdrom_locked)
+ if (bdrv_is_locked(s->bs))
buf[6] |= 1 << 1;
buf[15] = 0x00;
cpu_to_ube16(&buf[16], 706);
ide_atapi_cmd_reply(s, 18, max_len);
break;
case GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
- if (s->bs) {
- s->cdrom_locked = packet[4] & 1;
+ if (bdrv_is_inserted(s->bs)) {
+ bdrv_set_locked(s->bs, packet[4] & 1);
ide_atapi_cmd_ok(s);
} else {
ide_atapi_cmd_error(s, SENSE_NOT_READY,
{
int nb_sectors, lba;
- if (!s->bs) {
+ if (!bdrv_is_inserted(s->bs)) {
ide_atapi_cmd_error(s, SENSE_NOT_READY,
ASC_MEDIUM_NOT_PRESENT);
break;
ASC_LOGICAL_BLOCK_OOR);
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;
case GPCMD_SEEK:
{
int lba;
- if (!s->bs) {
+ if (!bdrv_is_inserted(s->bs)) {
ide_atapi_cmd_error(s, SENSE_NOT_READY,
ASC_MEDIUM_NOT_PRESENT);
break;
start = packet[4] & 1;
eject = (packet[4] >> 1) & 1;
- /* XXX: currently none implemented */
+ if (eject && !start) {
+ /* eject the disk */
+ bdrv_close(s->bs);
+ }
ide_atapi_cmd_ok(s);
}
break;
{
int format, msf, start_track, len;
- if (!s->bs) {
+ if (!bdrv_is_inserted(s->bs)) {
ide_atapi_cmd_error(s, SENSE_NOT_READY,
ASC_MEDIUM_NOT_PRESENT);
break;
start_track = packet[6];
switch(format) {
case 0:
- len = cdrom_read_toc(s, buf, msf, start_track);
+ len = cdrom_read_toc(s->nb_sectors >> 2, buf, msf, start_track);
if (len < 0)
goto error_cmd;
ide_atapi_cmd_reply(s, len, max_len);
buf[3] = 0x01;
ide_atapi_cmd_reply(s, 12, max_len);
break;
+ case 2:
+ len = cdrom_read_toc_raw(s->nb_sectors >> 2, buf, msf, start_track);
+ if (len < 0)
+ goto error_cmd;
+ ide_atapi_cmd_reply(s, len, max_len);
+ break;
default:
- goto error_cmd;
+ error_cmd:
+ ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ASC_INV_FIELD_IN_CMD_PACKET);
+ break;
}
}
break;
case GPCMD_READ_CDVD_CAPACITY:
- if (!s->bs) {
+ if (!bdrv_is_inserted(s->bs)) {
ide_atapi_cmd_error(s, SENSE_NOT_READY,
ASC_MEDIUM_NOT_PRESENT);
break;
ide_atapi_cmd_reply(s, 36, max_len);
break;
default:
- error_cmd:
ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
- ASC_INV_FIELD_IN_CMD_PACKET);
+ ASC_ILLEGAL_OPCODE);
break;
}
}
-static void ide_ioport_write(CPUState *env, uint32_t addr, uint32_t val)
+/* called when the inserted state of the media has changed */
+static void cdrom_change_cb(void *opaque)
{
- IDEState *ide_if = get_ide_interface(addr);
- IDEState *s = ide_if->cur_drive;
+ IDEState *s = opaque;
+ int64_t nb_sectors;
+
+ /* XXX: send interrupt too */
+ bdrv_get_geometry(s->bs, &nb_sectors);
+ s->nb_sectors = nb_sectors;
+}
+
+static void ide_cmd_lba48_transform(IDEState *s, int lba48)
+{
+ s->lba48 = lba48;
+
+ /* handle the 'magic' 0 nsector count conversion here. to avoid
+ * fiddling with the rest of the read logic, we just store the
+ * full sector count in ->nsector and ignore ->hob_nsector from now
+ */
+ if (!s->lba48) {
+ if (!s->nsector)
+ s->nsector = 256;
+ } else {
+ if (!s->nsector && !s->hob_nsector)
+ s->nsector = 65536;
+ else {
+ int lo = s->nsector;
+ int hi = s->hob_nsector;
+
+ s->nsector = (hi << 8) | lo;
+ }
+ }
+}
+
+static void ide_clear_hob(IDEState *ide_if)
+{
+ /* any write clears HOB high bit of device control register */
+ ide_if[0].select &= ~(1 << 7);
+ ide_if[1].select &= ~(1 << 7);
+}
+
+static void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ IDEState *ide_if = opaque;
+ IDEState *s;
int unit, n;
+ int lba48 = 0;
#ifdef DEBUG_IDE
printf("IDE: write addr=0x%x val=0x%02x\n", addr, val);
#endif
+
addr &= 7;
switch(addr) {
case 0:
break;
case 1:
- s->feature = val;
+ ide_clear_hob(ide_if);
+ /* NOTE: data is written to the two drives */
+ ide_if[0].hob_feature = ide_if[0].feature;
+ ide_if[1].hob_feature = ide_if[1].feature;
+ ide_if[0].feature = val;
+ ide_if[1].feature = val;
break;
case 2:
- if (val == 0)
- val = 256;
- s->nsector = val;
+ ide_clear_hob(ide_if);
+ ide_if[0].hob_nsector = ide_if[0].nsector;
+ ide_if[1].hob_nsector = ide_if[1].nsector;
+ ide_if[0].nsector = val;
+ ide_if[1].nsector = val;
break;
case 3:
- s->sector = val;
+ ide_clear_hob(ide_if);
+ ide_if[0].hob_sector = ide_if[0].sector;
+ ide_if[1].hob_sector = ide_if[1].sector;
+ ide_if[0].sector = val;
+ ide_if[1].sector = val;
break;
case 4:
- s->lcyl = val;
+ ide_clear_hob(ide_if);
+ ide_if[0].hob_lcyl = ide_if[0].lcyl;
+ ide_if[1].hob_lcyl = ide_if[1].lcyl;
+ ide_if[0].lcyl = val;
+ ide_if[1].lcyl = val;
break;
case 5:
- s->hcyl = val;
+ ide_clear_hob(ide_if);
+ ide_if[0].hob_hcyl = ide_if[0].hcyl;
+ ide_if[1].hob_hcyl = ide_if[1].hcyl;
+ ide_if[0].hcyl = val;
+ ide_if[1].hcyl = val;
break;
case 6:
+ /* FIXME: HOB readback uses bit 7 */
+ ide_if[0].select = (val & ~0x10) | 0xa0;
+ ide_if[1].select = (val | 0x10) | 0xa0;
/* select drive */
unit = (val >> 4) & 1;
s = ide_if + unit;
ide_if->cur_drive = s;
- s->select = val;
break;
default:
case 7:
#if defined(DEBUG_IDE)
printf("ide: CMD=%02x\n", val);
#endif
+ s = ide_if->cur_drive;
+ /* ignore commands to non existant slave */
+ if (s != ide_if && !s->bs)
+ break;
+
switch(val) {
case WIN_IDENTIFY:
if (s->bs && !s->is_cdrom) {
ide_identify(s);
- s->status = READY_STAT;
+ s->status = READY_STAT | SEEK_STAT;
ide_transfer_start(s, s->io_buffer, 512, ide_transfer_stop);
} else {
if (s->is_cdrom) {
case WIN_SPECIFY:
case WIN_RECAL:
s->error = 0;
- s->status = READY_STAT;
+ s->status = READY_STAT | SEEK_STAT;
ide_set_irq(s);
break;
case WIN_SETMULT:
}
ide_set_irq(s);
break;
+ case WIN_VERIFY_EXT:
+ lba48 = 1;
case WIN_VERIFY:
case WIN_VERIFY_ONCE:
/* do sector number check ? */
+ ide_cmd_lba48_transform(s, lba48);
s->status = READY_STAT;
ide_set_irq(s);
break;
+ case WIN_READ_EXT:
+ lba48 = 1;
case WIN_READ:
case WIN_READ_ONCE:
if (!s->bs)
goto abort_cmd;
+ ide_cmd_lba48_transform(s, lba48);
s->req_nb_sectors = 1;
ide_sector_read(s);
break;
+ case WIN_WRITE_EXT:
+ lba48 = 1;
case WIN_WRITE:
case WIN_WRITE_ONCE:
+ ide_cmd_lba48_transform(s, lba48);
s->error = 0;
- s->status = SEEK_STAT;
+ s->status = SEEK_STAT | READY_STAT;
s->req_nb_sectors = 1;
ide_transfer_start(s, s->io_buffer, 512, ide_sector_write);
break;
+ case WIN_MULTREAD_EXT:
+ lba48 = 1;
case WIN_MULTREAD:
if (!s->mult_sectors)
goto abort_cmd;
+ ide_cmd_lba48_transform(s, lba48);
s->req_nb_sectors = s->mult_sectors;
ide_sector_read(s);
break;
+ case WIN_MULTWRITE_EXT:
+ lba48 = 1;
case WIN_MULTWRITE:
if (!s->mult_sectors)
goto abort_cmd;
+ ide_cmd_lba48_transform(s, lba48);
s->error = 0;
- s->status = SEEK_STAT;
+ s->status = SEEK_STAT | READY_STAT;
s->req_nb_sectors = s->mult_sectors;
n = s->nsector;
if (n > s->req_nb_sectors)
n = s->req_nb_sectors;
ide_transfer_start(s, s->io_buffer, 512 * n, ide_sector_write);
break;
+ case WIN_READDMA_EXT:
+ lba48 = 1;
+ case WIN_READDMA:
+ case WIN_READDMA_ONCE:
+ if (!s->bs)
+ goto abort_cmd;
+ ide_cmd_lba48_transform(s, lba48);
+ ide_sector_read_dma(s);
+ break;
+ case WIN_WRITEDMA_EXT:
+ lba48 = 1;
+ case WIN_WRITEDMA:
+ case WIN_WRITEDMA_ONCE:
+ if (!s->bs)
+ goto abort_cmd;
+ ide_cmd_lba48_transform(s, lba48);
+ ide_sector_write_dma(s);
+ break;
+ case WIN_READ_NATIVE_MAX_EXT:
+ lba48 = 1;
case WIN_READ_NATIVE_MAX:
+ ide_cmd_lba48_transform(s, lba48);
ide_set_sector(s, s->nb_sectors - 1);
s->status = READY_STAT;
ide_set_irq(s);
s->status = READY_STAT;
ide_set_irq(s);
break;
-
+ case WIN_SETFEATURES:
+ if (!s->bs)
+ goto abort_cmd;
+ /* XXX: valid for CDROM ? */
+ switch(s->feature) {
+ case 0x02: /* write cache enable */
+ case 0x82: /* write cache disable */
+ case 0xaa: /* read look-ahead enable */
+ case 0x55: /* read look-ahead disable */
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s);
+ break;
+ case 0x03: { /* set transfer mode */
+ uint8_t val = s->nsector & 0x07;
+
+ switch (s->nsector >> 3) {
+ case 0x00: /* pio default */
+ case 0x01: /* pio mode */
+ put_le16(s->identify_data + 63,0x07);
+ put_le16(s->identify_data + 88,0x3f);
+ break;
+ case 0x04: /* mdma mode */
+ put_le16(s->identify_data + 63,0x07 | (1 << (val + 8)));
+ put_le16(s->identify_data + 88,0x3f);
+ break;
+ case 0x08: /* udma mode */
+ put_le16(s->identify_data + 63,0x07);
+ put_le16(s->identify_data + 88,0x3f | (1 << (val + 8)));
+ break;
+ default:
+ goto abort_cmd;
+ }
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s);
+ break;
+ }
+ default:
+ goto abort_cmd;
+ }
+ break;
+ case WIN_FLUSH_CACHE:
+ case WIN_FLUSH_CACHE_EXT:
+ if (s->bs)
+ bdrv_flush(s->bs);
+ s->status = READY_STAT;
+ ide_set_irq(s);
+ break;
+ case WIN_STANDBYNOW1:
+ case WIN_IDLEIMMEDIATE:
+ s->status = READY_STAT;
+ ide_set_irq(s);
+ break;
/* ATAPI commands */
case WIN_PIDENTIFY:
if (s->is_cdrom) {
ide_atapi_identify(s);
- s->status = READY_STAT;
+ s->status = READY_STAT | SEEK_STAT;
ide_transfer_start(s, s->io_buffer, 512, ide_transfer_stop);
} else {
ide_abort_command(s);
}
ide_set_irq(s);
break;
+ case WIN_DIAGNOSE:
+ ide_set_signature(s);
+ s->status = 0x00; /* NOTE: READY is _not_ set */
+ s->error = 0x01;
+ break;
case WIN_SRST:
if (!s->is_cdrom)
goto abort_cmd;
case WIN_PACKETCMD:
if (!s->is_cdrom)
goto abort_cmd;
- /* DMA or overlapping commands not supported */
- if ((s->feature & 0x03) != 0)
+ /* overlapping commands not supported */
+ if (s->feature & 0x02)
goto abort_cmd;
+ s->atapi_dma = s->feature & 1;
s->nsector = 1;
ide_transfer_start(s, s->io_buffer, ATAPI_PACKET_SIZE,
ide_atapi_cmd);
}
}
-static uint32_t ide_ioport_read(CPUState *env, uint32_t addr1)
+static uint32_t ide_ioport_read(void *opaque, uint32_t addr1)
{
- IDEState *s = get_ide_interface(addr1)->cur_drive;
+ IDEState *ide_if = opaque;
+ IDEState *s = ide_if->cur_drive;
uint32_t addr;
- int ret;
+ int ret, hob;
addr = addr1 & 7;
+ /* FIXME: HOB readback uses bit 7, but it's always set right now */
+ //hob = s->select & (1 << 7);
+ hob = 0;
switch(addr) {
case 0:
ret = 0xff;
break;
case 1:
- ret = s->error;
+ if (!ide_if[0].bs && !ide_if[1].bs)
+ ret = 0;
+ else if (!hob)
+ ret = s->error;
+ else
+ ret = s->hob_feature;
break;
case 2:
- ret = s->nsector & 0xff;
+ if (!ide_if[0].bs && !ide_if[1].bs)
+ ret = 0;
+ else if (!hob)
+ ret = s->nsector & 0xff;
+ else
+ ret = s->hob_nsector;
break;
case 3:
- ret = s->sector;
+ if (!ide_if[0].bs && !ide_if[1].bs)
+ ret = 0;
+ else if (!hob)
+ ret = s->sector;
+ else
+ ret = s->hob_sector;
break;
case 4:
- ret = s->lcyl;
+ if (!ide_if[0].bs && !ide_if[1].bs)
+ ret = 0;
+ else if (!hob)
+ ret = s->lcyl;
+ else
+ ret = s->hob_lcyl;
break;
case 5:
- ret = s->hcyl;
+ if (!ide_if[0].bs && !ide_if[1].bs)
+ ret = 0;
+ else if (!hob)
+ ret = s->hcyl;
+ else
+ ret = s->hob_hcyl;
break;
case 6:
- ret = s->select;
+ if (!ide_if[0].bs && !ide_if[1].bs)
+ ret = 0;
+ else
+ ret = s->select;
break;
default:
case 7:
- ret = s->status;
- pic_set_irq(s->irq, 0);
+ if ((!ide_if[0].bs && !ide_if[1].bs) ||
+ (s != ide_if && !s->bs))
+ ret = 0;
+ else
+ ret = s->status;
+ s->set_irq(s->irq_opaque, s->irq, 0);
break;
}
#ifdef DEBUG_IDE
return ret;
}
-static uint32_t ide_status_read(CPUState *env, uint32_t addr)
+static uint32_t ide_status_read(void *opaque, uint32_t addr)
{
- IDEState *s = get_ide_interface(addr)->cur_drive;
+ IDEState *ide_if = opaque;
+ IDEState *s = ide_if->cur_drive;
int ret;
- ret = s->status;
+
+ if ((!ide_if[0].bs && !ide_if[1].bs) ||
+ (s != ide_if && !s->bs))
+ ret = 0;
+ else
+ ret = s->status;
#ifdef DEBUG_IDE
printf("ide: read status addr=0x%x val=%02x\n", addr, ret);
#endif
return ret;
}
-static void ide_cmd_write(CPUState *env, uint32_t addr, uint32_t val)
+static void ide_cmd_write(void *opaque, uint32_t addr, uint32_t val)
{
- IDEState *ide_if = get_ide_interface(addr);
+ IDEState *ide_if = opaque;
IDEState *s;
int i;
if (s->is_cdrom)
s->status = 0x00; /* NOTE: READY is _not_ set */
else
- s->status = READY_STAT;
+ s->status = READY_STAT | SEEK_STAT;
ide_set_signature(s);
}
}
ide_if[1].cmd = val;
}
-static void ide_data_writew(CPUState *env, uint32_t addr, uint32_t val)
+static void ide_data_writew(void *opaque, uint32_t addr, uint32_t val)
{
- IDEState *s = get_ide_interface(addr)->cur_drive;
+ IDEState *s = ((IDEState *)opaque)->cur_drive;
uint8_t *p;
p = s->data_ptr;
- *(uint16_t *)p = tswap16(val);
+ *(uint16_t *)p = le16_to_cpu(val);
p += 2;
s->data_ptr = p;
if (p >= s->data_end)
s->end_transfer_func(s);
}
-static uint32_t ide_data_readw(CPUState *env, uint32_t addr)
+static uint32_t ide_data_readw(void *opaque, uint32_t addr)
{
- IDEState *s = get_ide_interface(addr)->cur_drive;
+ IDEState *s = ((IDEState *)opaque)->cur_drive;
uint8_t *p;
int ret;
p = s->data_ptr;
- ret = tswap16(*(uint16_t *)p);
+ ret = cpu_to_le16(*(uint16_t *)p);
p += 2;
s->data_ptr = p;
if (p >= s->data_end)
return ret;
}
-static void ide_data_writel(CPUState *env, uint32_t addr, uint32_t val)
+static void ide_data_writel(void *opaque, uint32_t addr, uint32_t val)
{
- IDEState *s = get_ide_interface(addr)->cur_drive;
+ IDEState *s = ((IDEState *)opaque)->cur_drive;
uint8_t *p;
p = s->data_ptr;
- *(uint32_t *)p = tswap32(val);
+ *(uint32_t *)p = le32_to_cpu(val);
p += 4;
s->data_ptr = p;
if (p >= s->data_end)
s->end_transfer_func(s);
}
-static uint32_t ide_data_readl(CPUState *env, uint32_t addr)
+static uint32_t ide_data_readl(void *opaque, uint32_t addr)
{
- IDEState *s = get_ide_interface(addr)->cur_drive;
+ IDEState *s = ((IDEState *)opaque)->cur_drive;
uint8_t *p;
int ret;
p = s->data_ptr;
- ret = tswap32(*(uint32_t *)p);
+ ret = cpu_to_le32(*(uint32_t *)p);
p += 4;
s->data_ptr = p;
if (p >= s->data_end)
return ret;
}
+static void ide_dummy_transfer_stop(IDEState *s)
+{
+ s->data_ptr = s->io_buffer;
+ s->data_end = s->io_buffer;
+ s->io_buffer[0] = 0xff;
+ s->io_buffer[1] = 0xff;
+ s->io_buffer[2] = 0xff;
+ s->io_buffer[3] = 0xff;
+}
+
static void ide_reset(IDEState *s)
{
s->mult_sectors = MAX_MULT_SECTORS;
s->select = 0xa0;
s->status = READY_STAT;
ide_set_signature(s);
+ /* init the transfer handler so that 0xffff is returned on data
+ accesses */
+ s->end_transfer_func = ide_dummy_transfer_stop;
+ ide_dummy_transfer_stop(s);
}
struct partition {
uint32_t nr_sects; /* nr of sectors in partition */
} __attribute__((packed));
-/* try to guess the IDE geometry from the MSDOS partition table */
-static void ide_guess_geometry(IDEState *s)
+/* try to guess the disk logical geometry from the MSDOS partition table. Return 0 if OK, -1 if could not guess */
+static int guess_disk_lchs(IDEState *s,
+ int *pcylinders, int *pheads, int *psectors)
{
uint8_t buf[512];
- int ret, i;
+ int ret, i, heads, sectors, cylinders;
struct partition *p;
uint32_t nr_sects;
- if (s->cylinders != 0)
- return;
ret = bdrv_read(s->bs, 0, buf, 1);
if (ret < 0)
- return;
+ return -1;
/* test msdos magic */
if (buf[510] != 0x55 || buf[511] != 0xaa)
- return;
+ return -1;
for(i = 0; i < 4; i++) {
p = ((struct partition *)(buf + 0x1be)) + i;
- nr_sects = tswap32(p->nr_sects);
+ nr_sects = le32_to_cpu(p->nr_sects);
if (nr_sects && p->end_head) {
/* We make the assumption that the partition terminates on
a cylinder boundary */
- s->heads = p->end_head + 1;
- s->sectors = p->end_sector & 63;
- s->cylinders = s->nb_sectors / (s->heads * s->sectors);
+ heads = p->end_head + 1;
+ sectors = p->end_sector & 63;
+ if (sectors == 0)
+ continue;
+ cylinders = s->nb_sectors / (heads * sectors);
+ if (cylinders < 1 || cylinders > 16383)
+ continue;
+ *pheads = heads;
+ *psectors = sectors;
+ *pcylinders = cylinders;
#if 0
- printf("guessed partition: CHS=%d %d %d\n",
- s->cylinders, s->heads, s->sectors);
+ printf("guessed geometry: LCHS=%d %d %d\n",
+ cylinders, heads, sectors);
#endif
+ return 0;
}
}
+ return -1;
}
-void ide_init(void)
+static void ide_init2(IDEState *ide_state,
+ BlockDriverState *hd0, BlockDriverState *hd1,
+ SetIRQFunc *set_irq, void *irq_opaque, int irq)
{
IDEState *s;
- int i, cylinders, iobase, iobase2;
+ static int drive_serial = 1;
+ int i, cylinders, heads, secs, translation;
int64_t nb_sectors;
- static const int ide_iobase[2] = { 0x1f0, 0x170 };
- static const int ide_iobase2[2] = { 0x3f6, 0x376 };
- static const int ide_irq[2] = { 14, 15 };
- for(i = 0; i < MAX_DISKS; i++) {
- s = &ide_state[i];
- s->bs = bs_table[i];
+ for(i = 0; i < 2; i++) {
+ s = ide_state + i;
+ if (i == 0)
+ s->bs = hd0;
+ else
+ s->bs = hd1;
if (s->bs) {
bdrv_get_geometry(s->bs, &nb_sectors);
s->nb_sectors = nb_sectors;
- ide_guess_geometry(s);
- if (s->cylinders == 0) {
- /* if no geometry, use a LBA compatible one */
- cylinders = nb_sectors / (16 * 63);
- if (cylinders > 16383)
- cylinders = 16383;
- else if (cylinders < 2)
- cylinders = 2;
+ /* if a geometry hint is available, use it */
+ bdrv_get_geometry_hint(s->bs, &cylinders, &heads, &secs);
+ if (cylinders != 0) {
s->cylinders = cylinders;
- s->heads = 16;
- s->sectors = 63;
+ s->heads = heads;
+ s->sectors = secs;
+ } else {
+ if (guess_disk_lchs(s, &cylinders, &heads, &secs) == 0) {
+ if (heads > 16) {
+ /* if heads > 16, it means that a BIOS LBA
+ translation was active, so the default
+ hardware geometry is OK */
+ goto default_geometry;
+ } else {
+ s->cylinders = cylinders;
+ s->heads = heads;
+ s->sectors = secs;
+ /* disable any translation to be in sync with
+ the logical geometry */
+ translation = bdrv_get_translation_hint(s->bs);
+ if (translation == BIOS_ATA_TRANSLATION_AUTO) {
+ bdrv_set_translation_hint(s->bs,
+ BIOS_ATA_TRANSLATION_NONE);
+ }
+ }
+ } else {
+ default_geometry:
+ /* if no geometry, use a standard physical disk geometry */
+ cylinders = nb_sectors / (16 * 63);
+ if (cylinders > 16383)
+ cylinders = 16383;
+ else if (cylinders < 2)
+ cylinders = 2;
+ s->cylinders = cylinders;
+ s->heads = 16;
+ s->sectors = 63;
+ }
+ bdrv_set_geometry_hint(s->bs, s->cylinders, s->heads, s->sectors);
+ }
+ if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) {
+ s->is_cdrom = 1;
+ bdrv_set_change_cb(s->bs, cdrom_change_cb, s);
}
}
- s->irq = ide_irq[i >> 1];
+ s->drive_serial = drive_serial++;
+ s->set_irq = set_irq;
+ s->irq_opaque = irq_opaque;
+ s->irq = irq;
+ s->sector_write_timer = qemu_new_timer(vm_clock,
+ ide_sector_write_timer_cb, s);
ide_reset(s);
}
- for(i = 0; i < (MAX_DISKS / 2); i++) {
- iobase = ide_iobase[i];
- iobase2 = ide_iobase2[i];
- ide_table[iobase >> 3] = &ide_state[2 * i];
- if (ide_iobase2[i])
- ide_table[iobase2 >> 3] = &ide_state[2 * i];
- register_ioport_write(iobase, 8, ide_ioport_write, 1);
- register_ioport_read(iobase, 8, ide_ioport_read, 1);
- register_ioport_read(iobase2, 1, ide_status_read, 1);
- register_ioport_write(iobase2, 1, ide_cmd_write, 1);
-
- /* data ports */
- register_ioport_write(iobase, 2, ide_data_writew, 2);
- register_ioport_read(iobase, 2, ide_data_readw, 2);
- register_ioport_write(iobase, 4, ide_data_writel, 4);
- register_ioport_read(iobase, 4, ide_data_readl, 4);
+}
+
+static void ide_init_ioport(IDEState *ide_state, int iobase, int iobase2)
+{
+ register_ioport_write(iobase, 8, 1, ide_ioport_write, ide_state);
+ register_ioport_read(iobase, 8, 1, ide_ioport_read, ide_state);
+ if (iobase2) {
+ register_ioport_read(iobase2, 1, 1, ide_status_read, ide_state);
+ register_ioport_write(iobase2, 1, 1, ide_cmd_write, ide_state);
+ }
+
+ /* data ports */
+ register_ioport_write(iobase, 2, 2, ide_data_writew, ide_state);
+ register_ioport_read(iobase, 2, 2, ide_data_readw, ide_state);
+ register_ioport_write(iobase, 4, 4, ide_data_writel, ide_state);
+ register_ioport_read(iobase, 4, 4, ide_data_readl, ide_state);
+}
+
+/***********************************************************/
+/* ISA IDE definitions */
+
+void isa_ide_init(int iobase, int iobase2, int irq,
+ BlockDriverState *hd0, BlockDriverState *hd1)
+{
+ IDEState *ide_state;
+
+ ide_state = qemu_mallocz(sizeof(IDEState) * 2);
+ if (!ide_state)
+ return;
+
+ ide_init2(ide_state, hd0, hd1, pic_set_irq_new, isa_pic, irq);
+ ide_init_ioport(ide_state, iobase, iobase2);
+}
+
+/***********************************************************/
+/* PCI IDE definitions */
+
+static void cmd646_update_irq(PCIIDEState *d);
+
+static void ide_map(PCIDevice *pci_dev, int region_num,
+ uint32_t addr, uint32_t size, int type)
+{
+ PCIIDEState *d = (PCIIDEState *)pci_dev;
+ IDEState *ide_state;
+
+ if (region_num <= 3) {
+ ide_state = &d->ide_if[(region_num >> 1) * 2];
+ if (region_num & 1) {
+ register_ioport_read(addr + 2, 1, 1, ide_status_read, ide_state);
+ register_ioport_write(addr + 2, 1, 1, ide_cmd_write, ide_state);
+ } else {
+ register_ioport_write(addr, 8, 1, ide_ioport_write, ide_state);
+ register_ioport_read(addr, 8, 1, ide_ioport_read, ide_state);
+
+ /* data ports */
+ register_ioport_write(addr, 2, 2, ide_data_writew, ide_state);
+ register_ioport_read(addr, 2, 2, ide_data_readw, ide_state);
+ register_ioport_write(addr, 4, 4, ide_data_writel, ide_state);
+ register_ioport_read(addr, 4, 4, ide_data_readl, ide_state);
+ }
+ }
+}
+
+/* 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;
}
-void ide_set_geometry(int n, int cyls, int heads, int secs)
+static void ide_dma_start(IDEState *s, IDEDMAFunc *dma_cb)
{
- ide_state[n].cylinders = cyls;
- ide_state[n].heads = heads;
- ide_state[n].sectors = secs;
+ 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 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_readb(void *opaque, uint32_t addr)
+{
+ BMDMAState *bm = opaque;
+ PCIIDEState *pci_dev;
+ uint32_t val;
+
+ switch(addr & 3) {
+ case 0:
+ val = bm->cmd;
+ break;
+ case 1:
+ pci_dev = bm->pci_dev;
+ if (pci_dev->type == IDE_TYPE_CMD646) {
+ val = pci_dev->dev.config[MRDMODE];
+ } else {
+ val = 0xff;
+ }
+ break;
+ case 2:
+ val = bm->status;
+ break;
+ case 3:
+ pci_dev = bm->pci_dev;
+ if (pci_dev->type == IDE_TYPE_CMD646) {
+ if (bm == &pci_dev->bmdma[0])
+ val = pci_dev->dev.config[UDIDETCR0];
+ else
+ val = pci_dev->dev.config[UDIDETCR1];
+ } else {
+ val = 0xff;
+ }
+ break;
+ default:
+ val = 0xff;
+ break;
+ }
+#ifdef DEBUG_IDE
+ printf("bmdma: readb 0x%02x : 0x%02x\n", addr, val);
+#endif
+ return val;
}
-void ide_set_cdrom(int n, int is_cdrom)
+static void bmdma_writeb(void *opaque, uint32_t addr, uint32_t val)
{
- ide_state[n].is_cdrom = is_cdrom;
+ BMDMAState *bm = opaque;
+ PCIIDEState *pci_dev;
+#ifdef DEBUG_IDE
+ printf("bmdma: writeb 0x%02x : 0x%02x\n", addr, val);
+#endif
+ switch(addr & 3) {
+ case 1:
+ pci_dev = bm->pci_dev;
+ if (pci_dev->type == IDE_TYPE_CMD646) {
+ pci_dev->dev.config[MRDMODE] =
+ (pci_dev->dev.config[MRDMODE] & ~0x30) | (val & 0x30);
+ cmd646_update_irq(pci_dev);
+ }
+ break;
+ case 2:
+ bm->status = (val & 0x60) | (bm->status & 1) | (bm->status & ~val & 0x06);
+ break;
+ case 3:
+ pci_dev = bm->pci_dev;
+ if (pci_dev->type == IDE_TYPE_CMD646) {
+ if (bm == &pci_dev->bmdma[0])
+ pci_dev->dev.config[UDIDETCR0] = val;
+ else
+ pci_dev->dev.config[UDIDETCR1] = val;
+ }
+ break;
+ }
+}
+
+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;
+ bm->pci_dev = (PCIIDEState *)pci_dev;
+
+ register_ioport_write(addr, 1, 1, bmdma_cmd_writeb, bm);
+
+ register_ioport_write(addr + 1, 3, 1, bmdma_writeb, bm);
+ register_ioport_read(addr, 4, 1, bmdma_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;
+ }
+}
+
+/* XXX: call it also when the MRDMODE is changed from the PCI config
+ registers */
+static void cmd646_update_irq(PCIIDEState *d)
+{
+ int pci_level;
+ pci_level = ((d->dev.config[MRDMODE] & MRDMODE_INTR_CH0) &&
+ !(d->dev.config[MRDMODE] & MRDMODE_BLK_CH0)) ||
+ ((d->dev.config[MRDMODE] & MRDMODE_INTR_CH1) &&
+ !(d->dev.config[MRDMODE] & MRDMODE_BLK_CH1));
+ pci_set_irq((PCIDevice *)d, 0, pci_level);
+}
+
+/* the PCI irq level is the logical OR of the two channels */
+static void cmd646_set_irq(void *opaque, int channel, int level)
+{
+ PCIIDEState *d = opaque;
+ int irq_mask;
+
+ irq_mask = MRDMODE_INTR_CH0 << channel;
+ if (level)
+ d->dev.config[MRDMODE] |= irq_mask;
+ else
+ d->dev.config[MRDMODE] &= ~irq_mask;
+ cmd646_update_irq(d);
+}
+
+/* CMD646 PCI IDE controller */
+void pci_cmd646_ide_init(PCIBus *bus, BlockDriverState **hd_table,
+ int secondary_ide_enabled)
+{
+ PCIIDEState *d;
+ uint8_t *pci_conf;
+ int i;
+
+ d = (PCIIDEState *)pci_register_device(bus, "CMD646 IDE",
+ sizeof(PCIIDEState),
+ -1,
+ NULL, NULL);
+ d->type = IDE_TYPE_CMD646;
+ pci_conf = d->dev.config;
+ pci_conf[0x00] = 0x95; // CMD646
+ pci_conf[0x01] = 0x10;
+ pci_conf[0x02] = 0x46;
+ pci_conf[0x03] = 0x06;
+
+ pci_conf[0x08] = 0x07; // IDE controller revision
+ pci_conf[0x09] = 0x8f;
+
+ pci_conf[0x0a] = 0x01; // class_sub = PCI_IDE
+ pci_conf[0x0b] = 0x01; // class_base = PCI_mass_storage
+ pci_conf[0x0e] = 0x00; // header_type
+
+ if (secondary_ide_enabled) {
+ /* XXX: if not enabled, really disable the seconday IDE controller */
+ pci_conf[0x51] = 0x80; /* enable IDE1 */
+ }
+
+ pci_register_io_region((PCIDevice *)d, 0, 0x8,
+ PCI_ADDRESS_SPACE_IO, ide_map);
+ pci_register_io_region((PCIDevice *)d, 1, 0x4,
+ PCI_ADDRESS_SPACE_IO, ide_map);
+ pci_register_io_region((PCIDevice *)d, 2, 0x8,
+ PCI_ADDRESS_SPACE_IO, ide_map);
+ pci_register_io_region((PCIDevice *)d, 3, 0x4,
+ 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
+
+ for(i = 0; i < 4; i++)
+ d->ide_if[i].pci_dev = (PCIDevice *)d;
+ ide_init2(&d->ide_if[0], hd_table[0], hd_table[1],
+ cmd646_set_irq, d, 0);
+ ide_init2(&d->ide_if[2], hd_table[2], hd_table[3],
+ cmd646_set_irq, d, 1);
+}
+
+/* hd_table must contain 4 block drivers */
+/* NOTE: for the PIIX3, the IRQs and IOports are hardcoded */
+void pci_piix3_ide_init(PCIBus *bus, BlockDriverState **hd_table, int devfn)
+{
+ PCIIDEState *d;
+ uint8_t *pci_conf;
+
+ /* register a function 1 of PIIX3 */
+ d = (PCIIDEState *)pci_register_device(bus, "PIIX3 IDE",
+ sizeof(PCIIDEState),
+ devfn,
+ NULL, NULL);
+ d->type = IDE_TYPE_PIIX3;
+
+ pci_conf = d->dev.config;
+ pci_conf[0x00] = 0x86; // Intel
+ pci_conf[0x01] = 0x80;
+ pci_conf[0x02] = 0x10;
+ pci_conf[0x03] = 0x70;
+ pci_conf[0x09] = 0x80; // legacy ATA mode
+ pci_conf[0x0a] = 0x01; // class_sub = PCI_IDE
+ pci_conf[0x0b] = 0x01; // class_base = PCI_mass_storage
+ pci_conf[0x0e] = 0x00; // header_type
+
+ pci_register_io_region((PCIDevice *)d, 4, 0x10,
+ PCI_ADDRESS_SPACE_IO, bmdma_map);
+
+ ide_init2(&d->ide_if[0], hd_table[0], hd_table[1],
+ pic_set_irq_new, isa_pic, 14);
+ ide_init2(&d->ide_if[2], hd_table[2], hd_table[3],
+ pic_set_irq_new, isa_pic, 15);
+ ide_init_ioport(&d->ide_if[0], 0x1f0, 0x3f6);
+ ide_init_ioport(&d->ide_if[2], 0x170, 0x376);
+}
+
+/***********************************************************/
+/* MacIO based PowerPC IDE */
+
+/* PowerMac IDE memory IO */
+static void pmac_ide_writeb (void *opaque,
+ target_phys_addr_t addr, uint32_t val)
+{
+ addr = (addr & 0xFFF) >> 4;
+ switch (addr) {
+ case 1 ... 7:
+ ide_ioport_write(opaque, addr, val);
+ break;
+ case 8:
+ case 22:
+ ide_cmd_write(opaque, 0, val);
+ break;
+ default:
+ break;
+ }
+}
+
+static uint32_t pmac_ide_readb (void *opaque,target_phys_addr_t addr)
+{
+ uint8_t retval;
+
+ addr = (addr & 0xFFF) >> 4;
+ switch (addr) {
+ case 1 ... 7:
+ retval = ide_ioport_read(opaque, addr);
+ break;
+ case 8:
+ case 22:
+ retval = ide_status_read(opaque, 0);
+ break;
+ default:
+ retval = 0xFF;
+ break;
+ }
+ return retval;
+}
+
+static void pmac_ide_writew (void *opaque,
+ target_phys_addr_t addr, uint32_t val)
+{
+ addr = (addr & 0xFFF) >> 4;
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap16(val);
+#endif
+ if (addr == 0) {
+ ide_data_writew(opaque, 0, val);
+ }
+}
+
+static uint32_t pmac_ide_readw (void *opaque,target_phys_addr_t addr)
+{
+ uint16_t retval;
+
+ addr = (addr & 0xFFF) >> 4;
+ if (addr == 0) {
+ retval = ide_data_readw(opaque, 0);
+ } else {
+ retval = 0xFFFF;
+ }
+#ifdef TARGET_WORDS_BIGENDIAN
+ retval = bswap16(retval);
+#endif
+ return retval;
+}
+
+static void pmac_ide_writel (void *opaque,
+ target_phys_addr_t addr, uint32_t val)
+{
+ addr = (addr & 0xFFF) >> 4;
+#ifdef TARGET_WORDS_BIGENDIAN
+ val = bswap32(val);
+#endif
+ if (addr == 0) {
+ ide_data_writel(opaque, 0, val);
+ }
+}
+
+static uint32_t pmac_ide_readl (void *opaque,target_phys_addr_t addr)
+{
+ uint32_t retval;
+
+ addr = (addr & 0xFFF) >> 4;
+ if (addr == 0) {
+ retval = ide_data_readl(opaque, 0);
+ } else {
+ retval = 0xFFFFFFFF;
+ }
+#ifdef TARGET_WORDS_BIGENDIAN
+ retval = bswap32(retval);
+#endif
+ return retval;
+}
+
+static CPUWriteMemoryFunc *pmac_ide_write[] = {
+ pmac_ide_writeb,
+ pmac_ide_writew,
+ pmac_ide_writel,
+};
+
+static CPUReadMemoryFunc *pmac_ide_read[] = {
+ pmac_ide_readb,
+ pmac_ide_readw,
+ pmac_ide_readl,
+};
+
+/* hd_table must contain 4 block drivers */
+/* PowerMac uses memory mapped registers, not I/O. Return the memory
+ I/O index to access the ide. */
+int pmac_ide_init (BlockDriverState **hd_table,
+ SetIRQFunc *set_irq, void *irq_opaque, int irq)
+{
+ IDEState *ide_if;
+ int pmac_ide_memory;
+
+ ide_if = qemu_mallocz(sizeof(IDEState) * 2);
+ ide_init2(&ide_if[0], hd_table[0], hd_table[1],
+ set_irq, irq_opaque, irq);
+
+ pmac_ide_memory = cpu_register_io_memory(0, pmac_ide_read,
+ pmac_ide_write, &ide_if[0]);
+ return pmac_ide_memory;
}