spelling fixes
[qemu] / hw / ide.c
index 7945721..9d59885 100644 (file)
--- a/hw/ide.c
+++ b/hw/ide.c
  * 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
@@ -313,14 +289,18 @@ struct IDEState;
 
 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 irq;
+    openpic_t *openpic;
+    PCIDevice *pci_dev;
+    struct BMDMAState *bmdma;
+    int drive_serial;
     /* ide regs */
     uint8_t feature;
     uint8_t error;
@@ -342,7 +322,11 @@ typedef struct IDEState {
     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;
@@ -350,13 +334,33 @@ typedef struct IDEState {
     uint8_t io_buffer[MAX_MULT_SECTORS*512 + 4];
 } 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
+
+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)
 {
@@ -382,84 +386,89 @@ static void padstr8(uint8_t *buf, int buf_size, const char *src)
     }
 }
 
+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];
 
     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 << 9); /* LBA supported, no DMA */
+    put_le16(p + 51, 0x200); /* PIO transfer cycle */
+    put_le16(p + 52, 0x200); /* DMA transfer cycle */
+    put_le16(p + 53, 1); /* words 54-58 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 + 80, (1 << 1) | (1 << 2));
+    put_le16(p + 82, (1 << 14));
+    put_le16(p + 83, (1 << 14));
+    put_le16(p + 84, (1 << 14));
+    put_le16(p + 85, (1 << 14));
+    put_le16(p + 86, 0);
+    put_le16(p + 87, (1 << 14));
 }
 
 static void ide_atapi_identify(IDEState *s)
 {
     uint16_t *p;
+    char buf[20];
 
     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 */
+    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 */
     
-    stw_raw(p + 71, 30); /* in ns */
-    stw_raw(p + 72, 30); /* in ns */
+    put_le16(p + 71, 30); /* in ns */
+    put_le16(p + 72, 30); /* in ns */
 
-    stw_raw(p + 80, 0x1e); /* support up to ATA/ATAPI-4 */
+    put_le16(p + 80, 0x1e); /* support up to ATA/ATAPI-4 */
 }
 
 static void ide_set_signature(IDEState *s)
@@ -489,7 +498,15 @@ static inline void ide_abort_command(IDEState *s)
 static inline void ide_set_irq(IDEState *s)
 {
     if (!(s->cmd & IDE_CMD_DISABLE_IRQ)) {
-        pic_set_irq(s->irq, 1);
+#ifdef TARGET_PPC
+        if (s->openpic) 
+            openpic_set_irq(s->openpic, s->irq, 1);
+        else 
+#endif
+        if (s->irq == 16)
+            pci_set_irq(s->pci_dev, 0, 1);
+        else
+            pic_set_irq(s->irq, 1);
     }
 }
 
@@ -539,7 +556,7 @@ static void ide_set_sector(IDEState *s, int64_t sector_num)
         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;
     }
 }
@@ -570,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)
 {
     int64_t sector_num;
@@ -598,6 +668,62 @@ static void ide_sector_write(IDEState *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)
 {
     s->error = 0;
@@ -643,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];
 }
 
+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 */
 static void ide_atapi_cmd_reply_end(IDEState *s)
 {
@@ -664,15 +825,15 @@ 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, 
@@ -701,8 +862,8 @@ static void ide_atapi_cmd_reply_end(IDEState *s)
             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);
@@ -732,21 +893,88 @@ static void ide_atapi_cmd_reply(IDEState *s, int size, int max_size)
 }
 
 /* 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);
 }
 
+/* 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 */
+/* XXX: check this */
 static int cdrom_read_toc(IDEState *s, uint8_t *buf, int msf, int start_track)
 {
     uint8_t *q;
@@ -755,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)
         return -1;
     q = buf + 2;
-    *q++ = 1;
-    *q++ = 1;
+    *q++ = 1; /* first session */
+    *q++ = 1; /* last session */
     if (start_track <= 1) {
         *q++ = 0; /* reserved */
         *q++ = 0x14; /* ADR, control */
@@ -781,9 +1009,8 @@ static int cdrom_read_toc(IDEState *s, uint8_t *buf, int msf, int start_track)
     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;
+        lba_to_msf(q, nb_sectors);
+        q += 3;
     } else {
         cpu_to_ube32(q, nb_sectors);
         q += 4;
@@ -793,6 +1020,75 @@ static int cdrom_read_toc(IDEState *s, uint8_t *buf, int msf, int start_track)
     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)
 {
     const uint8_t *packet;
@@ -813,7 +1109,7 @@ 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, 
@@ -865,7 +1161,7 @@ static void ide_atapi_cmd(IDEState *s)
                     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);
@@ -905,8 +1201,8 @@ static void ide_atapi_cmd(IDEState *s)
         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, 
@@ -918,7 +1214,7 @@ static void ide_atapi_cmd(IDEState *s)
         {
             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;
@@ -937,13 +1233,54 @@ static void ide_atapi_cmd(IDEState *s)
                                     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;
@@ -963,7 +1300,10 @@ static void ide_atapi_cmd(IDEState *s)
             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;
@@ -984,7 +1324,7 @@ static void ide_atapi_cmd(IDEState *s)
         {
             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;
@@ -1008,13 +1348,22 @@ static void ide_atapi_cmd(IDEState *s)
                 buf[3] = 0x01;
                 ide_atapi_cmd_reply(s, 12, max_len);
                 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:
-                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;
@@ -1040,17 +1389,27 @@ static void ide_atapi_cmd(IDEState *s)
         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_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    IDEState *ide_if = opaque;
+    IDEState *s;
     int unit, n;
 
 #ifdef DEBUG_IDE
@@ -1061,28 +1420,35 @@ static void ide_ioport_write(CPUState *env, uint32_t addr, uint32_t val)
     case 0:
         break;
     case 1:
-        s->feature = val;
+        /* NOTE: data is written to the two drives */
+        ide_if[0].feature = val;
+        ide_if[1].feature = val;
         break;
     case 2:
         if (val == 0)
             val = 256;
-        s->nsector = val;
+        ide_if[0].nsector = val;
+        ide_if[1].nsector = val;
         break;
     case 3:
-        s->sector = val;
+        ide_if[0].sector = val;
+        ide_if[1].sector = val;
         break;
     case 4:
-        s->lcyl = val;
+        ide_if[0].lcyl = val;
+        ide_if[1].lcyl = val;
         break;
     case 5:
-        s->hcyl = val;
+        ide_if[0].hcyl = val;
+        ide_if[1].hcyl = val;
         break;
     case 6:
+        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:
@@ -1090,11 +1456,15 @@ static void ide_ioport_write(CPUState *env, uint32_t addr, uint32_t val)
 #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) {
@@ -1137,7 +1507,7 @@ static void ide_ioport_write(CPUState *env, uint32_t addr, uint32_t val)
         case WIN_WRITE:
         case WIN_WRITE_ONCE:
             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;
@@ -1151,13 +1521,25 @@ static void ide_ioport_write(CPUState *env, uint32_t addr, uint32_t val)
             if (!s->mult_sectors)
                 goto abort_cmd;
             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:
+        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:
             ide_set_sector(s, s->nb_sectors - 1);
             s->status = READY_STAT;
@@ -1168,7 +1550,23 @@ static void ide_ioport_write(CPUState *env, uint32_t addr, uint32_t val)
             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 0x03: /* set transfer mode */
+            case 0x82: /* write cache disable */
+            case 0xaa: /* read look-ahead enable */
+            case 0x55: /* read look-ahead disable */
+                s->status = READY_STAT;
+                ide_set_irq(s);
+                break;
+            default:
+                goto abort_cmd;
+            }
+            break;
             /* ATAPI commands */
         case WIN_PIDENTIFY:
             if (s->is_cdrom) {
@@ -1190,9 +1588,10 @@ static void ide_ioport_write(CPUState *env, uint32_t addr, uint32_t val)
         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);
@@ -1206,9 +1605,10 @@ static void ide_ioport_write(CPUState *env, uint32_t addr, uint32_t val)
     }
 }
 
-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;
 
@@ -1218,27 +1618,57 @@ static uint32_t ide_ioport_read(CPUState *env, uint32_t addr1)
         ret = 0xff;
         break;
     case 1:
-        ret = s->error;
+        if (!ide_if[0].bs && !ide_if[1].bs)
+            ret = 0;
+        else
+            ret = s->error;
         break;
     case 2:
-        ret = s->nsector & 0xff;
+        if (!ide_if[0].bs && !ide_if[1].bs)
+            ret = 0;
+        else
+            ret = s->nsector & 0xff;
         break;
     case 3:
-        ret = s->sector;
+        if (!ide_if[0].bs && !ide_if[1].bs)
+            ret = 0;
+        else
+            ret = s->sector;
         break;
     case 4:
-        ret = s->lcyl;
+        if (!ide_if[0].bs && !ide_if[1].bs)
+            ret = 0;
+        else
+            ret = s->lcyl;
         break;
     case 5:
-        ret = s->hcyl;
+        if (!ide_if[0].bs && !ide_if[1].bs)
+            ret = 0;
+        else
+            ret = s->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;
+#ifdef TARGET_PPC
+        if (s->openpic) 
+            openpic_set_irq(s->openpic, s->irq, 0);
+        else 
+#endif
+        if (s->irq == 16)
+            pci_set_irq(s->pci_dev, 0, 0);
+        else
+            pic_set_irq(s->irq, 0);
         break;
     }
 #ifdef DEBUG_IDE
@@ -1247,20 +1677,26 @@ static uint32_t ide_ioport_read(CPUState *env, uint32_t addr1)
     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;
 
@@ -1284,7 +1720,7 @@ static void ide_cmd_write(CPUState *env, uint32_t addr, uint32_t val)
             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);
         }
     }
@@ -1293,26 +1729,26 @@ static void ide_cmd_write(CPUState *env, uint32_t addr, uint32_t val)
     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)
@@ -1320,27 +1756,27 @@ static uint32_t ide_data_readw(CPUState *env, uint32_t addr)
     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)
@@ -1388,7 +1824,7 @@ static void ide_guess_geometry(IDEState *s)
         return;
     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 */
@@ -1403,64 +1839,459 @@ static void ide_guess_geometry(IDEState *s)
     }
 }
 
-void ide_init(void)
+static void ide_init2(IDEState *ide_state, int irq,
+                      BlockDriverState *hd0, BlockDriverState *hd1)
 {
     IDEState *s;
-    int i, cylinders, iobase, iobase2;
+    static int drive_serial = 1;
+    int i, cylinders, heads, secs;
     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 {
+                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;
+                    s->cylinders = cylinders;
+                    s->heads = 16;
+                    s->sectors = 63;
+                }
+            }
+            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->irq = irq;
         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);
+}
+
+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, irq, hd0, hd1);
+    ide_init_ioport(ide_state, iobase, iobase2);
+}
+
+/***********************************************************/
+/* PCI IDE definitions */
+
+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;
+}
+
+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;
         
-        /* 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);
+        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 */
+void pci_ide_init(PCIBus *bus, BlockDriverState **hd_table)
+{
+    PCIIDEState *d;
+    uint8_t *pci_conf;
+    int i;
+
+    d = (PCIIDEState *)pci_register_device(bus, "IDE", sizeof(PCIIDEState),
+                                           -1, 
+                                           NULL, NULL);
+    pci_conf = d->dev.config;
+    pci_conf[0x00] = 0x86; // Intel
+    pci_conf[0x01] = 0x80;
+    pci_conf[0x02] = 0x00; // fake
+    pci_conf[0x03] = 0x01; // fake
+    pci_conf[0x0a] = 0x01; // class_sub = PCI_IDE
+    pci_conf[0x0b] = 0x01; // class_base = PCI_mass_storage
+    pci_conf[0x0e] = 0x80; // header_type = PCI_multifunction, generic
+
+    pci_conf[0x2c] = 0x86; // subsys vendor
+    pci_conf[0x2d] = 0x80; // subsys vendor
+    pci_conf[0x2e] = 0x00; // fake
+    pci_conf[0x2f] = 0x01; // fake
+
+    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], 16, hd_table[0], hd_table[1]);
+    ide_init2(&d->ide_if[2], 16, hd_table[2], hd_table[3]);
+}
+
+/* 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)
+{
+    PCIIDEState *d;
+    uint8_t *pci_conf;
+    
+    /* register a function 1 of PIIX3 */
+    d = (PCIIDEState *)pci_register_device(bus, "PIIX3 IDE", 
+                                           sizeof(PCIIDEState),
+                                           ((PCIDevice *)piix3_state)->devfn + 1, 
+                                           NULL, NULL);
+    pci_conf = d->dev.config;
+    pci_conf[0x00] = 0x86; // Intel
+    pci_conf[0x01] = 0x80;
+    pci_conf[0x02] = 0x10;
+    pci_conf[0x03] = 0x70;
+    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], 14, hd_table[0], hd_table[1]);
+    ide_init2(&d->ide_if[2], 15, hd_table[2], hd_table[3]);
+    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;
 }
 
-void ide_set_geometry(int n, int cyls, int heads, int secs)
+static void pmac_ide_writew (void *opaque,
+                             target_phys_addr_t addr, uint32_t val)
 {
-    ide_state[n].cylinders = cyls;
-    ide_state[n].heads = heads;
-    ide_state[n].sectors = secs;
+    addr = (addr & 0xFFF) >> 4; 
+#ifdef TARGET_WORDS_BIGENDIAN
+    val = bswap16(val);
+#endif
+    if (addr == 0) {
+        ide_data_writew(opaque, 0, val);
+    }
 }
 
-void ide_set_cdrom(int n, int is_cdrom)
+static uint32_t pmac_ide_readw (void *opaque,target_phys_addr_t addr)
 {
-    ide_state[n].is_cdrom = is_cdrom;
+    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,
+                   openpic_t *openpic, int irq)
+{
+    IDEState *ide_if;
+    int pmac_ide_memory;
+
+    ide_if = qemu_mallocz(sizeof(IDEState) * 2);
+    ide_init2(&ide_if[0], irq, hd_table[0], hd_table[1]);
+    ide_if[0].openpic = openpic;
+    ide_if[1].openpic = openpic;
+    
+    pmac_ide_memory = cpu_register_io_memory(0, pmac_ide_read,
+                                             pmac_ide_write, &ide_if[0]);
+    return pmac_ide_memory;
 }