]> git.proxmox.com Git - qemu.git/blobdiff - hw/ide/core.c
Open 2.0 development tree
[qemu.git] / hw / ide / core.c
index 9926d9202bc88a72d856acc82d1f39f9db73d192..e1f4c33fb8760f460170c0efc5fbbffed92a2f70 100644 (file)
@@ -568,10 +568,18 @@ static void dma_buf_commit(IDEState *s)
     qemu_sglist_destroy(&s->sg);
 }
 
+static void ide_async_cmd_done(IDEState *s)
+{
+    if (s->bus->dma->ops->async_cmd_done) {
+        s->bus->dma->ops->async_cmd_done(s->bus->dma);
+    }
+}
+
 void ide_set_inactive(IDEState *s)
 {
     s->bus->dma->aiocb = NULL;
     s->bus->dma->ops->set_inactive(s->bus->dma);
+    ide_async_cmd_done(s);
 }
 
 void ide_dma_error(IDEState *s)
@@ -760,8 +768,8 @@ static void ide_sector_write_cb(void *opaque, int ret)
            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_ns(vm_clock) + (get_ticks_per_sec() / 1000));
+        timer_mod(s->sector_write_timer,
+                       qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (get_ticks_per_sec() / 1000));
     } else {
         ide_set_irq(s->bus);
     }
@@ -804,6 +812,7 @@ static void ide_flush_cb(void *opaque, int ret)
 
     bdrv_acct_done(s->bs, &s->acct);
     s->status = READY_STAT | SEEK_STAT;
+    ide_async_cmd_done(s);
     ide_set_irq(s->bus);
 }
 
@@ -1004,633 +1013,729 @@ void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val)
     }
 }
 
-#define HD_OK (1u << IDE_HD)
-#define CD_OK (1u << IDE_CD)
-#define CFA_OK (1u << IDE_CFATA)
-#define HD_CFA_OK (HD_OK | CFA_OK)
-#define ALL_OK (HD_OK | CD_OK | CFA_OK)
+static bool cmd_nop(IDEState *s, uint8_t cmd)
+{
+    return true;
+}
 
-/* See ACS-2 T13/2015-D Table B.2 Command codes */
-static const uint8_t ide_cmd_table[0x100] = {
-    /* NOP not implemented, mandatory for CD */
-    [CFA_REQ_EXT_ERROR_CODE]            = CFA_OK,
-    [WIN_DSM]                           = ALL_OK,
-    [WIN_DEVICE_RESET]                  = CD_OK,
-    [WIN_RECAL]                         = HD_CFA_OK,
-    [WIN_READ]                          = ALL_OK,
-    [WIN_READ_ONCE]                     = ALL_OK,
-    [WIN_READ_EXT]                      = HD_CFA_OK,
-    [WIN_READDMA_EXT]                   = HD_CFA_OK,
-    [WIN_READ_NATIVE_MAX_EXT]           = HD_CFA_OK,
-    [WIN_MULTREAD_EXT]                  = HD_CFA_OK,
-    [WIN_WRITE]                         = HD_CFA_OK,
-    [WIN_WRITE_ONCE]                    = HD_CFA_OK,
-    [WIN_WRITE_EXT]                     = HD_CFA_OK,
-    [WIN_WRITEDMA_EXT]                  = HD_CFA_OK,
-    [CFA_WRITE_SECT_WO_ERASE]           = CFA_OK,
-    [WIN_MULTWRITE_EXT]                 = HD_CFA_OK,
-    [WIN_WRITE_VERIFY]                  = HD_CFA_OK,
-    [WIN_VERIFY]                        = HD_CFA_OK,
-    [WIN_VERIFY_ONCE]                   = HD_CFA_OK,
-    [WIN_VERIFY_EXT]                    = HD_CFA_OK,
-    [WIN_SEEK]                          = HD_CFA_OK,
-    [CFA_TRANSLATE_SECTOR]              = CFA_OK,
-    [WIN_DIAGNOSE]                      = ALL_OK,
-    [WIN_SPECIFY]                       = HD_CFA_OK,
-    [WIN_STANDBYNOW2]                   = ALL_OK,
-    [WIN_IDLEIMMEDIATE2]                = ALL_OK,
-    [WIN_STANDBY2]                      = ALL_OK,
-    [WIN_SETIDLE2]                      = ALL_OK,
-    [WIN_CHECKPOWERMODE2]               = ALL_OK,
-    [WIN_SLEEPNOW2]                     = ALL_OK,
-    [WIN_PACKETCMD]                     = CD_OK,
-    [WIN_PIDENTIFY]                     = CD_OK,
-    [WIN_SMART]                         = HD_CFA_OK,
-    [CFA_ACCESS_METADATA_STORAGE]       = CFA_OK,
-    [CFA_ERASE_SECTORS]                 = CFA_OK,
-    [WIN_MULTREAD]                      = HD_CFA_OK,
-    [WIN_MULTWRITE]                     = HD_CFA_OK,
-    [WIN_SETMULT]                       = HD_CFA_OK,
-    [WIN_READDMA]                       = HD_CFA_OK,
-    [WIN_READDMA_ONCE]                  = HD_CFA_OK,
-    [WIN_WRITEDMA]                      = HD_CFA_OK,
-    [WIN_WRITEDMA_ONCE]                 = HD_CFA_OK,
-    [CFA_WRITE_MULTI_WO_ERASE]          = CFA_OK,
-    [WIN_STANDBYNOW1]                   = ALL_OK,
-    [WIN_IDLEIMMEDIATE]                 = ALL_OK,
-    [WIN_STANDBY]                       = ALL_OK,
-    [WIN_SETIDLE1]                      = ALL_OK,
-    [WIN_CHECKPOWERMODE1]               = ALL_OK,
-    [WIN_SLEEPNOW1]                     = ALL_OK,
-    [WIN_FLUSH_CACHE]                   = ALL_OK,
-    [WIN_FLUSH_CACHE_EXT]               = HD_CFA_OK,
-    [WIN_IDENTIFY]                      = ALL_OK,
-    [WIN_SETFEATURES]                   = ALL_OK,
-    [IBM_SENSE_CONDITION]               = CFA_OK,
-    [CFA_WEAR_LEVEL]                    = HD_CFA_OK,
-    [WIN_READ_NATIVE_MAX]               = ALL_OK,
-};
+static bool cmd_data_set_management(IDEState *s, uint8_t cmd)
+{
+    switch (s->feature) {
+    case DSM_TRIM:
+        if (s->bs) {
+            ide_sector_start_dma(s, IDE_DMA_TRIM);
+            return false;
+        }
+        break;
+    }
 
-static bool ide_cmd_permitted(IDEState *s, uint32_t cmd)
+    ide_abort_command(s);
+    return true;
+}
+
+static bool cmd_identify(IDEState *s, uint8_t cmd)
 {
-    return cmd < ARRAY_SIZE(ide_cmd_table)
-        && (ide_cmd_table[cmd] & (1u << s->drive_kind));
+    if (s->bs && s->drive_kind != IDE_CD) {
+        if (s->drive_kind != IDE_CFATA) {
+            ide_identify(s);
+        } else {
+            ide_cfata_identify(s);
+        }
+        s->status = READY_STAT | SEEK_STAT;
+        ide_transfer_start(s, s->io_buffer, 512, ide_transfer_stop);
+        ide_set_irq(s->bus);
+        return false;
+    } else {
+        if (s->drive_kind == IDE_CD) {
+            ide_set_signature(s);
+        }
+        ide_abort_command(s);
+    }
+
+    return true;
 }
 
-void ide_exec_cmd(IDEBus *bus, uint32_t val)
+static bool cmd_verify(IDEState *s, uint8_t cmd)
 {
-    uint16_t *identify_data;
-    IDEState *s;
+    bool lba48 = (cmd == WIN_VERIFY_EXT);
+
+    /* do sector number check ? */
+    ide_cmd_lba48_transform(s, lba48);
+
+    return true;
+}
+
+static bool cmd_set_multiple_mode(IDEState *s, uint8_t cmd)
+{
+    if (s->drive_kind == IDE_CFATA && s->nsector == 0) {
+        /* Disable Read and Write Multiple */
+        s->mult_sectors = 0;
+    } else if ((s->nsector & 0xff) != 0 &&
+        ((s->nsector & 0xff) > MAX_MULT_SECTORS ||
+         (s->nsector & (s->nsector - 1)) != 0)) {
+        ide_abort_command(s);
+    } else {
+        s->mult_sectors = s->nsector & 0xff;
+    }
+
+    return true;
+}
+
+static bool cmd_read_multiple(IDEState *s, uint8_t cmd)
+{
+    bool lba48 = (cmd == WIN_MULTREAD_EXT);
+
+    if (!s->bs || !s->mult_sectors) {
+        ide_abort_command(s);
+        return true;
+    }
+
+    ide_cmd_lba48_transform(s, lba48);
+    s->req_nb_sectors = s->mult_sectors;
+    ide_sector_read(s);
+    return false;
+}
+
+static bool cmd_write_multiple(IDEState *s, uint8_t cmd)
+{
+    bool lba48 = (cmd == WIN_MULTWRITE_EXT);
     int n;
-    int lba48 = 0;
 
-#if defined(DEBUG_IDE)
-    printf("ide: CMD=%02x\n", val);
-#endif
-    s = idebus_active_if(bus);
-    /* ignore commands to non existent slave */
-    if (s != bus->ifs && !s->bs)
-        return;
+    if (!s->bs || !s->mult_sectors) {
+        ide_abort_command(s);
+        return true;
+    }
 
-    /* Only DEVICE RESET is allowed while BSY or/and DRQ are set */
-    if ((s->status & (BUSY_STAT|DRQ_STAT)) && val != WIN_DEVICE_RESET)
-        return;
+    ide_cmd_lba48_transform(s, lba48);
 
-    if (!ide_cmd_permitted(s, val)) {
-        goto abort_cmd;
+    s->req_nb_sectors = s->mult_sectors;
+    n = MIN(s->nsector, s->req_nb_sectors);
+
+    s->status = SEEK_STAT | READY_STAT;
+    ide_transfer_start(s, s->io_buffer, 512 * n, ide_sector_write);
+
+    s->media_changed = 1;
+
+    return false;
+}
+
+static bool cmd_read_pio(IDEState *s, uint8_t cmd)
+{
+    bool lba48 = (cmd == WIN_READ_EXT);
+
+    if (s->drive_kind == IDE_CD) {
+        ide_set_signature(s); /* odd, but ATA4 8.27.5.2 requires it */
+        ide_abort_command(s);
+        return true;
+    }
+
+    if (!s->bs) {
+        ide_abort_command(s);
+        return true;
     }
 
-    switch(val) {
-    case WIN_DSM:
-        switch (s->feature) {
-        case DSM_TRIM:
-            if (!s->bs) {
+    ide_cmd_lba48_transform(s, lba48);
+    s->req_nb_sectors = 1;
+    ide_sector_read(s);
+
+    return false;
+}
+
+static bool cmd_write_pio(IDEState *s, uint8_t cmd)
+{
+    bool lba48 = (cmd == WIN_WRITE_EXT);
+
+    if (!s->bs) {
+        ide_abort_command(s);
+        return true;
+    }
+
+    ide_cmd_lba48_transform(s, lba48);
+
+    s->req_nb_sectors = 1;
+    s->status = SEEK_STAT | READY_STAT;
+    ide_transfer_start(s, s->io_buffer, 512, ide_sector_write);
+
+    s->media_changed = 1;
+
+    return false;
+}
+
+static bool cmd_read_dma(IDEState *s, uint8_t cmd)
+{
+    bool lba48 = (cmd == WIN_READDMA_EXT);
+
+    if (!s->bs) {
+        ide_abort_command(s);
+        return true;
+    }
+
+    ide_cmd_lba48_transform(s, lba48);
+    ide_sector_start_dma(s, IDE_DMA_READ);
+
+    return false;
+}
+
+static bool cmd_write_dma(IDEState *s, uint8_t cmd)
+{
+    bool lba48 = (cmd == WIN_WRITEDMA_EXT);
+
+    if (!s->bs) {
+        ide_abort_command(s);
+        return true;
+    }
+
+    ide_cmd_lba48_transform(s, lba48);
+    ide_sector_start_dma(s, IDE_DMA_WRITE);
+
+    s->media_changed = 1;
+
+    return false;
+}
+
+static bool cmd_flush_cache(IDEState *s, uint8_t cmd)
+{
+    ide_flush_cache(s);
+    return false;
+}
+
+static bool cmd_seek(IDEState *s, uint8_t cmd)
+{
+    /* XXX: Check that seek is within bounds */
+    return true;
+}
+
+static bool cmd_read_native_max(IDEState *s, uint8_t cmd)
+{
+    bool lba48 = (cmd == WIN_READ_NATIVE_MAX_EXT);
+
+    /* Refuse if no sectors are addressable (e.g. medium not inserted) */
+    if (s->nb_sectors == 0) {
+        ide_abort_command(s);
+        return true;
+    }
+
+    ide_cmd_lba48_transform(s, lba48);
+    ide_set_sector(s, s->nb_sectors - 1);
+
+    return true;
+}
+
+static bool cmd_check_power_mode(IDEState *s, uint8_t cmd)
+{
+    s->nsector = 0xff; /* device active or idle */
+    return true;
+}
+
+static bool cmd_set_features(IDEState *s, uint8_t cmd)
+{
+    uint16_t *identify_data;
+
+    if (!s->bs) {
+        ide_abort_command(s);
+        return true;
+    }
+
+    /* XXX: valid for CDROM ? */
+    switch (s->feature) {
+    case 0x02: /* write cache enable */
+        bdrv_set_enable_write_cache(s->bs, true);
+        identify_data = (uint16_t *)s->identify_data;
+        put_le16(identify_data + 85, (1 << 14) | (1 << 5) | 1);
+        return true;
+    case 0x82: /* write cache disable */
+        bdrv_set_enable_write_cache(s->bs, false);
+        identify_data = (uint16_t *)s->identify_data;
+        put_le16(identify_data + 85, (1 << 14) | 1);
+        ide_flush_cache(s);
+        return false;
+    case 0xcc: /* reverting to power-on defaults enable */
+    case 0x66: /* reverting to power-on defaults disable */
+    case 0xaa: /* read look-ahead enable */
+    case 0x55: /* read look-ahead disable */
+    case 0x05: /* set advanced power management mode */
+    case 0x85: /* disable advanced power management mode */
+    case 0x69: /* NOP */
+    case 0x67: /* NOP */
+    case 0x96: /* NOP */
+    case 0x9a: /* NOP */
+    case 0x42: /* enable Automatic Acoustic Mode */
+    case 0xc2: /* disable Automatic Acoustic Mode */
+        return true;
+    case 0x03: /* set transfer mode */
+        {
+            uint8_t val = s->nsector & 0x07;
+            identify_data = (uint16_t *)s->identify_data;
+
+            switch (s->nsector >> 3) {
+            case 0x00: /* pio default */
+            case 0x01: /* pio mode */
+                put_le16(identify_data + 62, 0x07);
+                put_le16(identify_data + 63, 0x07);
+                put_le16(identify_data + 88, 0x3f);
+                break;
+            case 0x02: /* sigle word dma mode*/
+                put_le16(identify_data + 62, 0x07 | (1 << (val + 8)));
+                put_le16(identify_data + 63, 0x07);
+                put_le16(identify_data + 88, 0x3f);
+                break;
+            case 0x04: /* mdma mode */
+                put_le16(identify_data + 62, 0x07);
+                put_le16(identify_data + 63, 0x07 | (1 << (val + 8)));
+                put_le16(identify_data + 88, 0x3f);
+                break;
+            case 0x08: /* udma mode */
+                put_le16(identify_data + 62, 0x07);
+                put_le16(identify_data + 63, 0x07);
+                put_le16(identify_data + 88, 0x3f | (1 << (val + 8)));
+                break;
+            default:
                 goto abort_cmd;
             }
-            ide_sector_start_dma(s, IDE_DMA_TRIM);
-            break;
-        default:
-            goto abort_cmd;
+            return true;
         }
-        break;
-    case WIN_IDENTIFY:
-        if (s->bs && s->drive_kind != IDE_CD) {
-            if (s->drive_kind != IDE_CFATA)
-                ide_identify(s);
-            else
-                ide_cfata_identify(s);
-            s->status = READY_STAT | SEEK_STAT;
-            ide_transfer_start(s, s->io_buffer, 512, ide_transfer_stop);
-        } else {
-            if (s->drive_kind == IDE_CD) {
-                ide_set_signature(s);
-            }
-            ide_abort_command(s);
-        }
-        ide_set_irq(s->bus);
-        break;
-    case WIN_SPECIFY:
-    case WIN_RECAL:
-        s->error = 0;
-        s->status = READY_STAT | SEEK_STAT;
-        ide_set_irq(s->bus);
-        break;
-    case WIN_SETMULT:
-        if (s->drive_kind == IDE_CFATA && s->nsector == 0) {
-            /* Disable Read and Write Multiple */
-            s->mult_sectors = 0;
-            s->status = READY_STAT | SEEK_STAT;
-        } else if ((s->nsector & 0xff) != 0 &&
-            ((s->nsector & 0xff) > MAX_MULT_SECTORS ||
-             (s->nsector & (s->nsector - 1)) != 0)) {
-            ide_abort_command(s);
-        } else {
-            s->mult_sectors = s->nsector & 0xff;
-            s->status = READY_STAT | SEEK_STAT;
-        }
-        ide_set_irq(s->bus);
-        break;
+    }
 
-    case WIN_VERIFY_EXT:
-        lba48 = 1;
-        /* fall through */
-    case WIN_VERIFY:
-    case WIN_VERIFY_ONCE:
-        /* do sector number check ? */
-       ide_cmd_lba48_transform(s, lba48);
+abort_cmd:
+    ide_abort_command(s);
+    return true;
+}
+
+
+/*** ATAPI commands ***/
+
+static bool cmd_identify_packet(IDEState *s, uint8_t cmd)
+{
+    ide_atapi_identify(s);
+    s->status = READY_STAT | SEEK_STAT;
+    ide_transfer_start(s, s->io_buffer, 512, ide_transfer_stop);
+    ide_set_irq(s->bus);
+    return false;
+}
+
+static bool cmd_exec_dev_diagnostic(IDEState *s, uint8_t cmd)
+{
+    ide_set_signature(s);
+
+    if (s->drive_kind == IDE_CD) {
+        s->status = 0; /* ATAPI spec (v6) section 9.10 defines packet
+                        * devices to return a clear status register
+                        * with READY_STAT *not* set. */
+    } else {
         s->status = READY_STAT | SEEK_STAT;
+        /* The bits of the error register are not as usual for this command!
+         * They are part of the regular output (this is why ERR_STAT isn't set)
+         * Device 0 passed, Device 1 passed or not present. */
+        s->error = 0x01;
         ide_set_irq(s->bus);
-        break;
+    }
 
-    case WIN_READ_EXT:
-        lba48 = 1;
-        /* fall through */
-    case WIN_READ:
-    case WIN_READ_ONCE:
-        if (s->drive_kind == IDE_CD) {
-            ide_set_signature(s); /* odd, but ATA4 8.27.5.2 requires it */
-            goto abort_cmd;
-        }
-        if (!s->bs) {
-            goto abort_cmd;
-        }
-       ide_cmd_lba48_transform(s, lba48);
-        s->req_nb_sectors = 1;
-        ide_sector_read(s);
-        break;
+    return false;
+}
 
-    case WIN_WRITE_EXT:
-        lba48 = 1;
-        /* fall through */
-    case WIN_WRITE:
-    case WIN_WRITE_ONCE:
-    case CFA_WRITE_SECT_WO_ERASE:
-    case WIN_WRITE_VERIFY:
-        if (!s->bs) {
-            goto abort_cmd;
-        }
-       ide_cmd_lba48_transform(s, lba48);
-        s->error = 0;
-        s->status = SEEK_STAT | READY_STAT;
-        s->req_nb_sectors = 1;
-        ide_transfer_start(s, s->io_buffer, 512, ide_sector_write);
+static bool cmd_device_reset(IDEState *s, uint8_t cmd)
+{
+    ide_set_signature(s);
+    s->status = 0x00; /* NOTE: READY is _not_ set */
+    s->error = 0x01;
+
+    return false;
+}
+
+static bool cmd_packet(IDEState *s, uint8_t cmd)
+{
+    /* overlapping commands not supported */
+    if (s->feature & 0x02) {
+        ide_abort_command(s);
+        return true;
+    }
+
+    s->status = READY_STAT | SEEK_STAT;
+    s->atapi_dma = s->feature & 1;
+    s->nsector = 1;
+    ide_transfer_start(s, s->io_buffer, ATAPI_PACKET_SIZE,
+                       ide_atapi_cmd);
+    return false;
+}
+
+
+/*** CF-ATA commands ***/
+
+static bool cmd_cfa_req_ext_error_code(IDEState *s, uint8_t cmd)
+{
+    s->error = 0x09;    /* miscellaneous error */
+    s->status = READY_STAT | SEEK_STAT;
+    ide_set_irq(s->bus);
+
+    return false;
+}
+
+static bool cmd_cfa_erase_sectors(IDEState *s, uint8_t cmd)
+{
+    /* WIN_SECURITY_FREEZE_LOCK has the same ID as CFA_WEAR_LEVEL and is
+     * required for Windows 8 to work with AHCI */
+
+    if (cmd == CFA_WEAR_LEVEL) {
+        s->nsector = 0;
+    }
+
+    if (cmd == CFA_ERASE_SECTORS) {
         s->media_changed = 1;
+    }
+
+    return true;
+}
+
+static bool cmd_cfa_translate_sector(IDEState *s, uint8_t cmd)
+{
+    s->status = READY_STAT | SEEK_STAT;
+
+    memset(s->io_buffer, 0, 0x200);
+    s->io_buffer[0x00] = s->hcyl;                   /* Cyl MSB */
+    s->io_buffer[0x01] = s->lcyl;                   /* Cyl LSB */
+    s->io_buffer[0x02] = s->select;                 /* Head */
+    s->io_buffer[0x03] = s->sector;                 /* Sector */
+    s->io_buffer[0x04] = ide_get_sector(s) >> 16;   /* LBA MSB */
+    s->io_buffer[0x05] = ide_get_sector(s) >> 8;    /* LBA */
+    s->io_buffer[0x06] = ide_get_sector(s) >> 0;    /* LBA LSB */
+    s->io_buffer[0x13] = 0x00;                      /* Erase flag */
+    s->io_buffer[0x18] = 0x00;                      /* Hot count */
+    s->io_buffer[0x19] = 0x00;                      /* Hot count */
+    s->io_buffer[0x1a] = 0x01;                      /* Hot count */
+
+    ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop);
+    ide_set_irq(s->bus);
+
+    return false;
+}
+
+static bool cmd_cfa_access_metadata_storage(IDEState *s, uint8_t cmd)
+{
+    switch (s->feature) {
+    case 0x02:  /* Inquiry Metadata Storage */
+        ide_cfata_metadata_inquiry(s);
         break;
+    case 0x03:  /* Read Metadata Storage */
+        ide_cfata_metadata_read(s);
+        break;
+    case 0x04:  /* Write Metadata Storage */
+        ide_cfata_metadata_write(s);
+        break;
+    default:
+        ide_abort_command(s);
+        return true;
+    }
 
-    case WIN_MULTREAD_EXT:
-        lba48 = 1;
-        /* fall through */
-    case WIN_MULTREAD:
-        if (!s->bs) {
-            goto abort_cmd;
-        }
-        if (!s->mult_sectors) {
-            goto abort_cmd;
-        }
-       ide_cmd_lba48_transform(s, lba48);
-        s->req_nb_sectors = s->mult_sectors;
-        ide_sector_read(s);
+    ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop);
+    s->status = 0x00; /* NOTE: READY is _not_ set */
+    ide_set_irq(s->bus);
+
+    return false;
+}
+
+static bool cmd_ibm_sense_condition(IDEState *s, uint8_t cmd)
+{
+    switch (s->feature) {
+    case 0x01:  /* sense temperature in device */
+        s->nsector = 0x50;      /* +20 C */
         break;
+    default:
+        ide_abort_command(s);
+        return true;
+    }
 
-    case WIN_MULTWRITE_EXT:
-        lba48 = 1;
-        /* fall through */
-    case WIN_MULTWRITE:
-    case CFA_WRITE_MULTI_WO_ERASE:
-        if (!s->bs) {
-            goto abort_cmd;
-        }
-        if (!s->mult_sectors) {
+    return true;
+}
+
+
+/*** SMART commands ***/
+
+static bool cmd_smart(IDEState *s, uint8_t cmd)
+{
+    int n;
+
+    if (s->hcyl != 0xc2 || s->lcyl != 0x4f) {
+        goto abort_cmd;
+    }
+
+    if (!s->smart_enabled && s->feature != SMART_ENABLE) {
+        goto abort_cmd;
+    }
+
+    switch (s->feature) {
+    case SMART_DISABLE:
+        s->smart_enabled = 0;
+        return true;
+
+    case SMART_ENABLE:
+        s->smart_enabled = 1;
+        return true;
+
+    case SMART_ATTR_AUTOSAVE:
+        switch (s->sector) {
+        case 0x00:
+            s->smart_autosave = 0;
+            break;
+        case 0xf1:
+            s->smart_autosave = 1;
+            break;
+        default:
             goto abort_cmd;
         }
-       ide_cmd_lba48_transform(s, lba48);
-        s->error = 0;
-        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);
-        s->media_changed = 1;
-        break;
+        return true;
 
-    case WIN_READDMA_EXT:
-        lba48 = 1;
-        /* fall through */
-    case WIN_READDMA:
-    case WIN_READDMA_ONCE:
-        if (!s->bs) {
-            goto abort_cmd;
+    case SMART_STATUS:
+        if (!s->smart_errors) {
+            s->hcyl = 0xc2;
+            s->lcyl = 0x4f;
+        } else {
+            s->hcyl = 0x2c;
+            s->lcyl = 0xf4;
         }
-       ide_cmd_lba48_transform(s, lba48);
-        ide_sector_start_dma(s, IDE_DMA_READ);
-        break;
+        return true;
 
-    case WIN_WRITEDMA_EXT:
-        lba48 = 1;
-        /* fall through */
-    case WIN_WRITEDMA:
-    case WIN_WRITEDMA_ONCE:
-        if (!s->bs) {
-            goto abort_cmd;
+    case SMART_READ_THRESH:
+        memset(s->io_buffer, 0, 0x200);
+        s->io_buffer[0] = 0x01; /* smart struct version */
+
+        for (n = 0; n < ARRAY_SIZE(smart_attributes); n++) {
+            s->io_buffer[2 + 0 + (n * 12)] = smart_attributes[n][0];
+            s->io_buffer[2 + 1 + (n * 12)] = smart_attributes[n][11];
         }
-       ide_cmd_lba48_transform(s, lba48);
-        ide_sector_start_dma(s, IDE_DMA_WRITE);
-        s->media_changed = 1;
-        break;
 
-    case WIN_READ_NATIVE_MAX_EXT:
-        lba48 = 1;
-        /* fall through */
-    case WIN_READ_NATIVE_MAX:
-        /* Refuse if no sectors are addressable (e.g. medium not inserted) */
-        if (s->nb_sectors == 0) {
-            goto abort_cmd;
+        /* checksum */
+        for (n = 0; n < 511; n++) {
+            s->io_buffer[511] += s->io_buffer[n];
         }
-       ide_cmd_lba48_transform(s, lba48);
-        ide_set_sector(s, s->nb_sectors - 1);
-        s->status = READY_STAT | SEEK_STAT;
-        ide_set_irq(s->bus);
-        break;
+        s->io_buffer[511] = 0x100 - s->io_buffer[511];
 
-    case WIN_CHECKPOWERMODE1:
-    case WIN_CHECKPOWERMODE2:
-        s->error = 0;
-        s->nsector = 0xff; /* device active or idle */
         s->status = READY_STAT | SEEK_STAT;
+        ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop);
         ide_set_irq(s->bus);
-        break;
-    case WIN_SETFEATURES:
-        if (!s->bs)
-            goto abort_cmd;
-        /* XXX: valid for CDROM ? */
-        switch(s->feature) {
-        case 0x02: /* write cache enable */
-            bdrv_set_enable_write_cache(s->bs, true);
-            identify_data = (uint16_t *)s->identify_data;
-            put_le16(identify_data + 85, (1 << 14) | (1 << 5) | 1);
-            s->status = READY_STAT | SEEK_STAT;
-            ide_set_irq(s->bus);
-            break;
-        case 0x82: /* write cache disable */
-            bdrv_set_enable_write_cache(s->bs, false);
-            identify_data = (uint16_t *)s->identify_data;
-            put_le16(identify_data + 85, (1 << 14) | 1);
-            ide_flush_cache(s);
-            break;
-        case 0xcc: /* reverting to power-on defaults enable */
-        case 0x66: /* reverting to power-on defaults disable */
-        case 0xaa: /* read look-ahead enable */
-        case 0x55: /* read look-ahead disable */
-        case 0x05: /* set advanced power management mode */
-        case 0x85: /* disable advanced power management mode */
-        case 0x69: /* NOP */
-        case 0x67: /* NOP */
-        case 0x96: /* NOP */
-        case 0x9a: /* NOP */
-        case 0x42: /* enable Automatic Acoustic Mode */
-        case 0xc2: /* disable Automatic Acoustic Mode */
-            s->status = READY_STAT | SEEK_STAT;
-            ide_set_irq(s->bus);
-            break;
-        case 0x03: { /* set transfer mode */
-               uint8_t val = s->nsector & 0x07;
-               identify_data = (uint16_t *)s->identify_data;
-
-               switch (s->nsector >> 3) {
-               case 0x00: /* pio default */
-               case 0x01: /* pio mode */
-                       put_le16(identify_data + 62,0x07);
-                       put_le16(identify_data + 63,0x07);
-                       put_le16(identify_data + 88,0x3f);
-                       break;
-                case 0x02: /* sigle word dma mode*/
-                       put_le16(identify_data + 62,0x07 | (1 << (val + 8)));
-                       put_le16(identify_data + 63,0x07);
-                       put_le16(identify_data + 88,0x3f);
-                       break;
-               case 0x04: /* mdma mode */
-                       put_le16(identify_data + 62,0x07);
-                       put_le16(identify_data + 63,0x07 | (1 << (val + 8)));
-                       put_le16(identify_data + 88,0x3f);
-                       break;
-               case 0x08: /* udma mode */
-                       put_le16(identify_data + 62,0x07);
-                       put_le16(identify_data + 63,0x07);
-                       put_le16(identify_data + 88,0x3f | (1 << (val + 8)));
-                       break;
-               default:
-                       goto abort_cmd;
-               }
-            s->status = READY_STAT | SEEK_STAT;
-            ide_set_irq(s->bus);
-            break;
-       }
-        default:
-            goto abort_cmd;
+        return false;
+
+    case SMART_READ_DATA:
+        memset(s->io_buffer, 0, 0x200);
+        s->io_buffer[0] = 0x01; /* smart struct version */
+
+        for (n = 0; n < ARRAY_SIZE(smart_attributes); n++) {
+            int i;
+            for (i = 0; i < 11; i++) {
+                s->io_buffer[2 + i + (n * 12)] = smart_attributes[n][i];
+            }
         }
-        break;
-    case WIN_FLUSH_CACHE:
-    case WIN_FLUSH_CACHE_EXT:
-        ide_flush_cache(s);
-        break;
-    case WIN_STANDBY:
-    case WIN_STANDBY2:
-    case WIN_STANDBYNOW1:
-    case WIN_STANDBYNOW2:
-    case WIN_IDLEIMMEDIATE:
-    case WIN_IDLEIMMEDIATE2:
-    case WIN_SETIDLE1:
-    case WIN_SETIDLE2:
-    case WIN_SLEEPNOW1:
-    case WIN_SLEEPNOW2:
-        s->status = READY_STAT;
-        ide_set_irq(s->bus);
-        break;
-    case WIN_SEEK:
-        /* XXX: Check that seek is within bounds */
-        s->status = READY_STAT | SEEK_STAT;
-        ide_set_irq(s->bus);
-        break;
-        /* ATAPI commands */
-    case WIN_PIDENTIFY:
-        ide_atapi_identify(s);
-        s->status = READY_STAT | SEEK_STAT;
-        ide_transfer_start(s, s->io_buffer, 512, ide_transfer_stop);
-        ide_set_irq(s->bus);
-        break;
-    case WIN_DIAGNOSE:
-        ide_set_signature(s);
-        if (s->drive_kind == IDE_CD)
-            s->status = 0; /* ATAPI spec (v6) section 9.10 defines packet
-                            * devices to return a clear status register
-                            * with READY_STAT *not* set. */
-        else
-            s->status = READY_STAT | SEEK_STAT;
-        s->error = 0x01; /* Device 0 passed, Device 1 passed or not
-                          * present.
-                          */
-        ide_set_irq(s->bus);
-        break;
-    case WIN_DEVICE_RESET:
-        ide_set_signature(s);
-        s->status = 0x00; /* NOTE: READY is _not_ set */
-        s->error = 0x01;
-        break;
-    case WIN_PACKETCMD:
-        /* overlapping commands not supported */
-        if (s->feature & 0x02)
-            goto abort_cmd;
-        s->status = READY_STAT | SEEK_STAT;
-        s->atapi_dma = s->feature & 1;
-        s->nsector = 1;
-        ide_transfer_start(s, s->io_buffer, ATAPI_PACKET_SIZE,
-                           ide_atapi_cmd);
-        break;
-    /* CF-ATA commands */
-    case CFA_REQ_EXT_ERROR_CODE:
-        s->error = 0x09;    /* miscellaneous error */
-        s->status = READY_STAT | SEEK_STAT;
-        ide_set_irq(s->bus);
-        break;
-    case CFA_ERASE_SECTORS:
-    case CFA_WEAR_LEVEL:
-#if 0
-    /* This one has the same ID as CFA_WEAR_LEVEL and is required for
-       Windows 8 to work with AHCI */
-    case WIN_SECURITY_FREEZE_LOCK:
-#endif
-        if (val == CFA_WEAR_LEVEL)
-            s->nsector = 0;
-        if (val == CFA_ERASE_SECTORS)
-            s->media_changed = 1;
-        s->error = 0x00;
-        s->status = READY_STAT | SEEK_STAT;
-        ide_set_irq(s->bus);
-        break;
-    case CFA_TRANSLATE_SECTOR:
-        s->error = 0x00;
+
+        s->io_buffer[362] = 0x02 | (s->smart_autosave ? 0x80 : 0x00);
+        if (s->smart_selftest_count == 0) {
+            s->io_buffer[363] = 0;
+        } else {
+            s->io_buffer[363] =
+                s->smart_selftest_data[3 +
+                           (s->smart_selftest_count - 1) *
+                           24];
+        }
+        s->io_buffer[364] = 0x20;
+        s->io_buffer[365] = 0x01;
+        /* offline data collection capacity: execute + self-test*/
+        s->io_buffer[367] = (1 << 4 | 1 << 3 | 1);
+        s->io_buffer[368] = 0x03; /* smart capability (1) */
+        s->io_buffer[369] = 0x00; /* smart capability (2) */
+        s->io_buffer[370] = 0x01; /* error logging supported */
+        s->io_buffer[372] = 0x02; /* minutes for poll short test */
+        s->io_buffer[373] = 0x36; /* minutes for poll ext test */
+        s->io_buffer[374] = 0x01; /* minutes for poll conveyance */
+
+        for (n = 0; n < 511; n++) {
+            s->io_buffer[511] += s->io_buffer[n];
+        }
+        s->io_buffer[511] = 0x100 - s->io_buffer[511];
+
         s->status = READY_STAT | SEEK_STAT;
-        memset(s->io_buffer, 0, 0x200);
-        s->io_buffer[0x00] = s->hcyl;                  /* Cyl MSB */
-        s->io_buffer[0x01] = s->lcyl;                  /* Cyl LSB */
-        s->io_buffer[0x02] = s->select;                        /* Head */
-        s->io_buffer[0x03] = s->sector;                        /* Sector */
-        s->io_buffer[0x04] = ide_get_sector(s) >> 16;  /* LBA MSB */
-        s->io_buffer[0x05] = ide_get_sector(s) >> 8;   /* LBA */
-        s->io_buffer[0x06] = ide_get_sector(s) >> 0;   /* LBA LSB */
-        s->io_buffer[0x13] = 0x00;                             /* Erase flag */
-        s->io_buffer[0x18] = 0x00;                             /* Hot count */
-        s->io_buffer[0x19] = 0x00;                             /* Hot count */
-        s->io_buffer[0x1a] = 0x01;                             /* Hot count */
         ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop);
         ide_set_irq(s->bus);
-        break;
-    case CFA_ACCESS_METADATA_STORAGE:
-        switch (s->feature) {
-        case 0x02:     /* Inquiry Metadata Storage */
-            ide_cfata_metadata_inquiry(s);
-            break;
-        case 0x03:     /* Read Metadata Storage */
-            ide_cfata_metadata_read(s);
+        return false;
+
+    case SMART_READ_LOG:
+        switch (s->sector) {
+        case 0x01: /* summary smart error log */
+            memset(s->io_buffer, 0, 0x200);
+            s->io_buffer[0] = 0x01;
+            s->io_buffer[1] = 0x00; /* no error entries */
+            s->io_buffer[452] = s->smart_errors & 0xff;
+            s->io_buffer[453] = (s->smart_errors & 0xff00) >> 8;
+
+            for (n = 0; n < 511; n++) {
+                s->io_buffer[511] += s->io_buffer[n];
+            }
+            s->io_buffer[511] = 0x100 - s->io_buffer[511];
             break;
-        case 0x04:     /* Write Metadata Storage */
-            ide_cfata_metadata_write(s);
+        case 0x06: /* smart self test log */
+            memset(s->io_buffer, 0, 0x200);
+            s->io_buffer[0] = 0x01;
+            if (s->smart_selftest_count == 0) {
+                s->io_buffer[508] = 0;
+            } else {
+                s->io_buffer[508] = s->smart_selftest_count;
+                for (n = 2; n < 506; n++)  {
+                    s->io_buffer[n] = s->smart_selftest_data[n];
+                }
+            }
+
+            for (n = 0; n < 511; n++) {
+                s->io_buffer[511] += s->io_buffer[n];
+            }
+            s->io_buffer[511] = 0x100 - s->io_buffer[511];
             break;
         default:
             goto abort_cmd;
         }
+        s->status = READY_STAT | SEEK_STAT;
         ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop);
-        s->status = 0x00; /* NOTE: READY is _not_ set */
         ide_set_irq(s->bus);
-        break;
-    case IBM_SENSE_CONDITION:
-        switch (s->feature) {
-        case 0x01:  /* sense temperature in device */
-            s->nsector = 0x50;      /* +20 C */
+        return false;
+
+    case SMART_EXECUTE_OFFLINE:
+        switch (s->sector) {
+        case 0: /* off-line routine */
+        case 1: /* short self test */
+        case 2: /* extended self test */
+            s->smart_selftest_count++;
+            if (s->smart_selftest_count > 21) {
+                s->smart_selftest_count = 0;
+            }
+            n = 2 + (s->smart_selftest_count - 1) * 24;
+            s->smart_selftest_data[n] = s->sector;
+            s->smart_selftest_data[n + 1] = 0x00; /* OK and finished */
+            s->smart_selftest_data[n + 2] = 0x34; /* hour count lsb */
+            s->smart_selftest_data[n + 3] = 0x12; /* hour count msb */
             break;
         default:
             goto abort_cmd;
         }
-        s->status = READY_STAT | SEEK_STAT;
-        ide_set_irq(s->bus);
-        break;
+        return true;
+    }
 
-    case WIN_SMART:
-       if (s->hcyl != 0xc2 || s->lcyl != 0x4f)
-               goto abort_cmd;
-       if (!s->smart_enabled && s->feature != SMART_ENABLE)
-               goto abort_cmd;
-       switch (s->feature) {
-       case SMART_DISABLE:
-               s->smart_enabled = 0;
-               s->status = READY_STAT | SEEK_STAT;
-               ide_set_irq(s->bus);
-               break;
-       case SMART_ENABLE:
-               s->smart_enabled = 1;
-               s->status = READY_STAT | SEEK_STAT;
-               ide_set_irq(s->bus);
-               break;
-       case SMART_ATTR_AUTOSAVE:
-               switch (s->sector) {
-               case 0x00:
-               s->smart_autosave = 0;
-               break;
-               case 0xf1:
-               s->smart_autosave = 1;
-               break;
-               default:
-               goto abort_cmd;
-               }
-               s->status = READY_STAT | SEEK_STAT;
-               ide_set_irq(s->bus);
-               break;
-       case SMART_STATUS:
-               if (!s->smart_errors) {
-               s->hcyl = 0xc2;
-               s->lcyl = 0x4f;
-               } else {
-               s->hcyl = 0x2c;
-               s->lcyl = 0xf4;
-               }
-               s->status = READY_STAT | SEEK_STAT;
-               ide_set_irq(s->bus);
-               break;
-       case SMART_READ_THRESH:
-               memset(s->io_buffer, 0, 0x200);
-               s->io_buffer[0] = 0x01; /* smart struct version */
-               for (n = 0; n < ARRAY_SIZE(smart_attributes); n++) {
-               s->io_buffer[2+0+(n*12)] = smart_attributes[n][0];
-               s->io_buffer[2+1+(n*12)] = smart_attributes[n][11];
-               }
-               for (n=0; n<511; n++) /* checksum */
-               s->io_buffer[511] += s->io_buffer[n];
-               s->io_buffer[511] = 0x100 - s->io_buffer[511];
-               s->status = READY_STAT | SEEK_STAT;
-               ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop);
-               ide_set_irq(s->bus);
-               break;
-       case SMART_READ_DATA:
-               memset(s->io_buffer, 0, 0x200);
-               s->io_buffer[0] = 0x01; /* smart struct version */
-               for (n = 0; n < ARRAY_SIZE(smart_attributes); n++) {
-                   int i;
-                   for(i = 0; i < 11; i++) {
-                       s->io_buffer[2+i+(n*12)] = smart_attributes[n][i];
-                   }
-               }
-               s->io_buffer[362] = 0x02 | (s->smart_autosave?0x80:0x00);
-               if (s->smart_selftest_count == 0) {
-               s->io_buffer[363] = 0;
-               } else {
-               s->io_buffer[363] =
-                       s->smart_selftest_data[3 + 
-                                          (s->smart_selftest_count - 1) *
-                                          24];
-               }
-               s->io_buffer[364] = 0x20; 
-               s->io_buffer[365] = 0x01; 
-               /* offline data collection capacity: execute + self-test*/
-               s->io_buffer[367] = (1<<4 | 1<<3 | 1); 
-               s->io_buffer[368] = 0x03; /* smart capability (1) */
-               s->io_buffer[369] = 0x00; /* smart capability (2) */
-               s->io_buffer[370] = 0x01; /* error logging supported */
-               s->io_buffer[372] = 0x02; /* minutes for poll short test */
-               s->io_buffer[373] = 0x36; /* minutes for poll ext test */
-               s->io_buffer[374] = 0x01; /* minutes for poll conveyance */
-
-               for (n=0; n<511; n++) 
-               s->io_buffer[511] += s->io_buffer[n];
-               s->io_buffer[511] = 0x100 - s->io_buffer[511];
-               s->status = READY_STAT | SEEK_STAT;
-               ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop);
-               ide_set_irq(s->bus);
-               break;
-       case SMART_READ_LOG:
-               switch (s->sector) {
-               case 0x01: /* summary smart error log */
-               memset(s->io_buffer, 0, 0x200);
-               s->io_buffer[0] = 0x01;
-               s->io_buffer[1] = 0x00; /* no error entries */
-               s->io_buffer[452] = s->smart_errors & 0xff;
-               s->io_buffer[453] = (s->smart_errors & 0xff00) >> 8;
-
-               for (n=0; n<511; n++)
-                       s->io_buffer[511] += s->io_buffer[n];
-               s->io_buffer[511] = 0x100 - s->io_buffer[511];
-               break;
-               case 0x06: /* smart self test log */
-               memset(s->io_buffer, 0, 0x200);
-               s->io_buffer[0] = 0x01;
-               if (s->smart_selftest_count == 0) {
-                       s->io_buffer[508] = 0;
-               } else {
-                       s->io_buffer[508] = s->smart_selftest_count;
-                       for (n=2; n<506; n++) 
-                       s->io_buffer[n] = s->smart_selftest_data[n];
-               }
-               for (n=0; n<511; n++)
-                       s->io_buffer[511] += s->io_buffer[n];
-               s->io_buffer[511] = 0x100 - s->io_buffer[511];
-               break;
-               default:
-               goto abort_cmd;
-               }
-               s->status = READY_STAT | SEEK_STAT;
-               ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop);
-               ide_set_irq(s->bus);
-               break;
-       case SMART_EXECUTE_OFFLINE:
-               switch (s->sector) {
-               case 0: /* off-line routine */
-               case 1: /* short self test */
-               case 2: /* extended self test */
-               s->smart_selftest_count++;
-               if(s->smart_selftest_count > 21)
-                       s->smart_selftest_count = 0;
-               n = 2 + (s->smart_selftest_count - 1) * 24;
-               s->smart_selftest_data[n] = s->sector;
-               s->smart_selftest_data[n+1] = 0x00; /* OK and finished */
-               s->smart_selftest_data[n+2] = 0x34; /* hour count lsb */
-               s->smart_selftest_data[n+3] = 0x12; /* hour count msb */
-               s->status = READY_STAT | SEEK_STAT;
-               ide_set_irq(s->bus);
-               break;
-               default:
-               goto abort_cmd;
-               }
-               break;
-       default:
-               goto abort_cmd;
-       }
-       break;
-    default:
-        /* should not be reachable */
-    abort_cmd:
+abort_cmd:
+    ide_abort_command(s);
+    return true;
+}
+
+#define HD_OK (1u << IDE_HD)
+#define CD_OK (1u << IDE_CD)
+#define CFA_OK (1u << IDE_CFATA)
+#define HD_CFA_OK (HD_OK | CFA_OK)
+#define ALL_OK (HD_OK | CD_OK | CFA_OK)
+
+/* Set the Disk Seek Completed status bit during completion */
+#define SET_DSC (1u << 8)
+
+/* See ACS-2 T13/2015-D Table B.2 Command codes */
+static const struct {
+    /* Returns true if the completion code should be run */
+    bool (*handler)(IDEState *s, uint8_t cmd);
+    int flags;
+} ide_cmd_table[0x100] = {
+    /* NOP not implemented, mandatory for CD */
+    [CFA_REQ_EXT_ERROR_CODE]      = { cmd_cfa_req_ext_error_code, CFA_OK },
+    [WIN_DSM]                     = { cmd_data_set_management, ALL_OK },
+    [WIN_DEVICE_RESET]            = { cmd_device_reset, CD_OK },
+    [WIN_RECAL]                   = { cmd_nop, HD_CFA_OK | SET_DSC},
+    [WIN_READ]                    = { cmd_read_pio, ALL_OK },
+    [WIN_READ_ONCE]               = { cmd_read_pio, ALL_OK },
+    [WIN_READ_EXT]                = { cmd_read_pio, HD_CFA_OK },
+    [WIN_READDMA_EXT]             = { cmd_read_dma, HD_CFA_OK },
+    [WIN_READ_NATIVE_MAX_EXT]     = { cmd_read_native_max, HD_CFA_OK | SET_DSC },
+    [WIN_MULTREAD_EXT]            = { cmd_read_multiple, HD_CFA_OK },
+    [WIN_WRITE]                   = { cmd_write_pio, HD_CFA_OK },
+    [WIN_WRITE_ONCE]              = { cmd_write_pio, HD_CFA_OK },
+    [WIN_WRITE_EXT]               = { cmd_write_pio, HD_CFA_OK },
+    [WIN_WRITEDMA_EXT]            = { cmd_write_dma, HD_CFA_OK },
+    [CFA_WRITE_SECT_WO_ERASE]     = { cmd_write_pio, CFA_OK },
+    [WIN_MULTWRITE_EXT]           = { cmd_write_multiple, HD_CFA_OK },
+    [WIN_WRITE_VERIFY]            = { cmd_write_pio, HD_CFA_OK },
+    [WIN_VERIFY]                  = { cmd_verify, HD_CFA_OK | SET_DSC },
+    [WIN_VERIFY_ONCE]             = { cmd_verify, HD_CFA_OK | SET_DSC },
+    [WIN_VERIFY_EXT]              = { cmd_verify, HD_CFA_OK | SET_DSC },
+    [WIN_SEEK]                    = { cmd_seek, HD_CFA_OK | SET_DSC },
+    [CFA_TRANSLATE_SECTOR]        = { cmd_cfa_translate_sector, CFA_OK },
+    [WIN_DIAGNOSE]                = { cmd_exec_dev_diagnostic, ALL_OK },
+    [WIN_SPECIFY]                 = { cmd_nop, HD_CFA_OK | SET_DSC },
+    [WIN_STANDBYNOW2]             = { cmd_nop, ALL_OK },
+    [WIN_IDLEIMMEDIATE2]          = { cmd_nop, ALL_OK },
+    [WIN_STANDBY2]                = { cmd_nop, ALL_OK },
+    [WIN_SETIDLE2]                = { cmd_nop, ALL_OK },
+    [WIN_CHECKPOWERMODE2]         = { cmd_check_power_mode, ALL_OK | SET_DSC },
+    [WIN_SLEEPNOW2]               = { cmd_nop, ALL_OK },
+    [WIN_PACKETCMD]               = { cmd_packet, CD_OK },
+    [WIN_PIDENTIFY]               = { cmd_identify_packet, CD_OK },
+    [WIN_SMART]                   = { cmd_smart, HD_CFA_OK | SET_DSC },
+    [CFA_ACCESS_METADATA_STORAGE] = { cmd_cfa_access_metadata_storage, CFA_OK },
+    [CFA_ERASE_SECTORS]           = { cmd_cfa_erase_sectors, CFA_OK | SET_DSC },
+    [WIN_MULTREAD]                = { cmd_read_multiple, HD_CFA_OK },
+    [WIN_MULTWRITE]               = { cmd_write_multiple, HD_CFA_OK },
+    [WIN_SETMULT]                 = { cmd_set_multiple_mode, HD_CFA_OK | SET_DSC },
+    [WIN_READDMA]                 = { cmd_read_dma, HD_CFA_OK },
+    [WIN_READDMA_ONCE]            = { cmd_read_dma, HD_CFA_OK },
+    [WIN_WRITEDMA]                = { cmd_write_dma, HD_CFA_OK },
+    [WIN_WRITEDMA_ONCE]           = { cmd_write_dma, HD_CFA_OK },
+    [CFA_WRITE_MULTI_WO_ERASE]    = { cmd_write_multiple, CFA_OK },
+    [WIN_STANDBYNOW1]             = { cmd_nop, ALL_OK },
+    [WIN_IDLEIMMEDIATE]           = { cmd_nop, ALL_OK },
+    [WIN_STANDBY]                 = { cmd_nop, ALL_OK },
+    [WIN_SETIDLE1]                = { cmd_nop, ALL_OK },
+    [WIN_CHECKPOWERMODE1]         = { cmd_check_power_mode, ALL_OK | SET_DSC },
+    [WIN_SLEEPNOW1]               = { cmd_nop, ALL_OK },
+    [WIN_FLUSH_CACHE]             = { cmd_flush_cache, ALL_OK },
+    [WIN_FLUSH_CACHE_EXT]         = { cmd_flush_cache, HD_CFA_OK },
+    [WIN_IDENTIFY]                = { cmd_identify, ALL_OK },
+    [WIN_SETFEATURES]             = { cmd_set_features, ALL_OK | SET_DSC },
+    [IBM_SENSE_CONDITION]         = { cmd_ibm_sense_condition, CFA_OK | SET_DSC },
+    [CFA_WEAR_LEVEL]              = { cmd_cfa_erase_sectors, HD_CFA_OK | SET_DSC },
+    [WIN_READ_NATIVE_MAX]         = { cmd_read_native_max, ALL_OK | SET_DSC },
+};
+
+static bool ide_cmd_permitted(IDEState *s, uint32_t cmd)
+{
+    return cmd < ARRAY_SIZE(ide_cmd_table)
+        && (ide_cmd_table[cmd].flags & (1u << s->drive_kind));
+}
+
+void ide_exec_cmd(IDEBus *bus, uint32_t val)
+{
+    IDEState *s;
+    bool complete;
+
+#if defined(DEBUG_IDE)
+    printf("ide: CMD=%02x\n", val);
+#endif
+    s = idebus_active_if(bus);
+    /* ignore commands to non existent slave */
+    if (s != bus->ifs && !s->bs)
+        return;
+
+    /* Only DEVICE RESET is allowed while BSY or/and DRQ are set */
+    if ((s->status & (BUSY_STAT|DRQ_STAT)) && val != WIN_DEVICE_RESET)
+        return;
+
+    if (!ide_cmd_permitted(s, val)) {
         ide_abort_command(s);
         ide_set_irq(s->bus);
-        break;
+        return;
+    }
+
+    s->status = READY_STAT | BUSY_STAT;
+    s->error = 0;
+
+    complete = ide_cmd_table[val].handler(s, val);
+    if (complete) {
+        s->status &= ~BUSY_STAT;
+        assert(!!s->error == !!(s->status & ERR_STAT));
+
+        if ((ide_cmd_table[val].flags & SET_DSC) && !s->error) {
+            s->status |= SEEK_STAT;
+        }
+
+        ide_set_irq(s->bus);
     }
 }
 
@@ -2058,7 +2163,7 @@ static void ide_init1(IDEBus *bus, int unit)
     s->smart_selftest_data = qemu_blockalign(s->bs, 512);
     memset(s->smart_selftest_data, 0, 512);
 
-    s->sector_write_timer = qemu_new_timer_ns(vm_clock,
+    s->sector_write_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
                                            ide_sector_write_timer_cb, s);
 }
 
@@ -2110,55 +2215,6 @@ void ide_init2(IDEBus *bus, qemu_irq irq)
     bus->dma = &ide_dma_nop;
 }
 
-/* TODO convert users to qdev and remove */
-void ide_init2_with_non_qdev_drives(IDEBus *bus, DriveInfo *hd0,
-                                    DriveInfo *hd1, qemu_irq irq)
-{
-    int i, trans;
-    DriveInfo *dinfo;
-    uint32_t cyls, heads, secs;
-
-    for(i = 0; i < 2; i++) {
-        dinfo = i == 0 ? hd0 : hd1;
-        ide_init1(bus, i);
-        if (dinfo) {
-            cyls  = dinfo->cyls;
-            heads = dinfo->heads;
-            secs  = dinfo->secs;
-            trans = dinfo->trans;
-            if (!cyls && !heads && !secs) {
-                hd_geometry_guess(dinfo->bdrv, &cyls, &heads, &secs, &trans);
-            } else if (trans == BIOS_ATA_TRANSLATION_AUTO) {
-                trans = hd_bios_chs_auto_trans(cyls, heads, secs);
-            }
-            if (cyls < 1 || cyls > 65535) {
-                error_report("cyls must be between 1 and 65535");
-                exit(1);
-            }
-            if (heads < 1 || heads > 16) {
-                error_report("heads must be between 1 and 16");
-                exit(1);
-            }
-            if (secs < 1 || secs > 255) {
-                error_report("secs must be between 1 and 255");
-                exit(1);
-            }
-            if (ide_init_drive(&bus->ifs[i], dinfo->bdrv,
-                               dinfo->media_cd ? IDE_CD : IDE_HD,
-                               NULL, dinfo->serial, NULL, 0,
-                               cyls, heads, secs, trans) < 0) {
-                error_report("Can't set up IDE drive %s", dinfo->id);
-                exit(1);
-            }
-            bdrv_attach_dev_nofail(dinfo->bdrv, &bus->ifs[i]);
-        } else {
-            ide_reset(&bus->ifs[i]);
-        }
-    }
-    bus->irq = irq;
-    bus->dma = &ide_dma_nop;
-}
-
 static const MemoryRegionPortio ide_portio_list[] = {
     { 0, 8, 1, .read = ide_ioport_read, .write = ide_ioport_write },
     { 0, 2, 2, .read = ide_data_readw, .write = ide_data_writew },