Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


IDE / ATA rewrites (1st step)
Peter Dinda [Wed, 4 Mar 2015 18:22:21 +0000 (12:22 -0600)]
This adds functional support for ATA hard drives in PIO mode
giving both read and write access.  It also does a bit of
bugfixing throughout.

palacios/src/devices/ata.h
palacios/src/devices/atapi.h
palacios/src/devices/ide.c

index 0f9c8df..dd1c4b7 100644 (file)
@@ -43,7 +43,6 @@ static void ata_identify_device(struct ide_drive * drive) {
 
     drive_id->cdrom_flag = 0;
 
-    // Make it the simplest drive possible (1 head, 1 cyl, 1 sect/track)
     drive_id->num_cylinders = drive->num_cylinders;
     drive_id->num_heads = drive->num_heads;
     drive_id->bytes_per_track = drive->num_sectors * HD_SECTOR_SIZE;
@@ -61,7 +60,8 @@ static void ata_identify_device(struct ide_drive * drive) {
 
 
     // enable DMA access
-    drive_id->dma_enable = 1;
+    // PAD disable DMA capability
+    drive_id->dma_enable = 0; // 1;
 
     // enable LBA access
     drive_id->lba_enable = 1;
@@ -77,7 +77,6 @@ static void ata_identify_device(struct ide_drive * drive) {
     drive_id->rw_multiples = 0x8000 | MAX_MULT_SECTORS;
 
 
-
     // words 64-70, 54-58 valid
     drive_id->field_valid = 0x0007; // DMA + pkg cmd valid
 
@@ -85,26 +84,37 @@ static void ata_identify_device(struct ide_drive * drive) {
     // copied from CFA540A
     // drive_id->buf[63] = 0x0103; // variable (DMA stuff)
     //drive_id->buf[63] = 0x0000; // variable (DMA stuff)
+    // 0x0007 => MWDMA modes 0..2 supported - none selected
     drive_id->buf[63] = 0x0007;
 
     
-    //    drive_id->buf[64] = 0x0001; // PIO
+    // PAD: Support PIO mode 0
+    drive_id->buf[64] = 0x0001; // PIO
+
+    // MWDMA transfer min cycle time
     drive_id->buf[65] = 0x00b4;
+    // MWDMA transfer time recommended
     drive_id->buf[66] = 0x00b4;
+    // minimum pio transfer time without flow control
     drive_id->buf[67] = 0x012c;
+    // minimum pio transfer time with IORDY flow control
     drive_id->buf[68] = 0x00b4;
 
     drive_id->buf[71] = 30; // faked
     drive_id->buf[72] = 30; // faked
 
+    // queue depth set to one
+
     //    drive_id->buf[80] = 0x1e; // supports up to ATA/ATAPI-4
     drive_id->major_rev_num = 0x0040; // supports up to ATA/ATAPI-6
 
 
     drive_id->buf[83] |= 0x0400; // supports 48 bit LBA
 
+    // No special features supported
 
-    drive_id->dma_ultra = 0x2020; // Ultra_DMA_Mode_5_Selected | Ultra_DMA_Mode_5_Supported;
+    // PAD: Disable ultra DMA capability
+    //drive_id->dma_ultra = 0x2020; // Ultra_DMA_Mode_5_Selected | Ultra_DMA_Mode_5_Supported;
 
 }
 
@@ -134,6 +144,12 @@ static int ata_read(struct ide_internal * ide, struct ide_channel * channel, uin
 static int ata_write(struct ide_internal * ide, struct ide_channel * channel, uint8_t * src, uint_t sect_cnt) {
     struct ide_drive * drive = get_selected_drive(channel);
 
+    if (drive->hd_state.accessed == 0) {
+       PrintError(VM_NONE,VCORE_NONE,"Reseting lba...\n");
+       drive->current_lba = 0;
+       drive->hd_state.accessed = 1;
+    }
+
     PrintDebug(VM_NONE, VCORE_NONE,"Writing Drive LBA=%d (count=%d)\n", (uint32_t)(drive->current_lba), sect_cnt);
 
     int ret = drive->ops->write(src, drive->current_lba * HD_SECTOR_SIZE, sect_cnt * HD_SECTOR_SIZE, drive->private_data);
@@ -153,34 +169,53 @@ static int ata_get_lba(struct ide_internal * ide, struct ide_channel * channel,
     // The if the sector count == 0 then read 256 sectors (cast up to handle that value)
     uint32_t sect_cnt = (drive->sector_count == 0) ? 256 : drive->sector_count;
 
-    union {
-       uint32_t addr;
-       uint8_t buf[4];
-    } __attribute__((packed)) lba_addr;
-
-    /* LBA addr bits:
-       0-8: sector number reg  (drive->lba0)
-       8-16: low cylinder reg (drive->lba1)
-       16-24: high cylinder reg (drive->lba2)
-       24-28:  low 4 bits of drive_head reg (channel->drive_head.head_num)
-     */
-
-    lba_addr.buf[0] = drive->lba0;
-    lba_addr.buf[1] = drive->lba1;
-    lba_addr.buf[2] = drive->lba2;
-    lba_addr.buf[3] = channel->drive_head.lba3;
-
-
-    if ((lba_addr.addr + sect_cnt) > 
+    if (is_lba_enabled(channel)) { 
+       union {
+           uint32_t addr;
+           uint8_t buf[4];
+       } __attribute__((packed)) lba_addr;
+       
+       /* LBA addr bits:
+          0-8: sector number reg  (drive->lba0)
+          8-16: low cylinder reg (drive->lba1)
+          16-24: high cylinder reg (drive->lba2)
+          24-28:  low 4 bits of drive_head reg (channel->drive_head.head_num)
+       */
+       
+       
+       lba_addr.buf[0] = drive->lba0;
+       lba_addr.buf[1] = drive->lba1;
+       lba_addr.buf[2] = drive->lba2;
+       lba_addr.buf[3] = channel->drive_head.lba3;
+       
+       *lba = lba_addr.addr;
+
+       PrintDebug(VM_NONE,VCORE_NONE,"get_lba: lba0=%u (sect), lba1=%u (cyllow), lba2=%u (cylhigh), lba3=%d (head) => lba=%llu\n", drive->lba0, drive->lba1, drive->lba2, channel->drive_head.lba3, *lba);
+
+    } else {
+       // we are in CHS mode....
+
+       *lba = 
+           (drive->cylinder * drive->num_heads + 
+            channel->drive_head.head_num) * drive->num_sectors +
+           // sector number is 1 based
+           (drive->sector_num - 1);
+       
+       
+       PrintDebug(VM_NONE,VCORE_NONE,"get_lba: Huh, 1995 has returned - CHS (%u,%u,%u) addressing on drive of (%u,%u,%u) translated as %llu....\n", 
+                drive->cylinder, channel->drive_head.head_num, drive->sector_num,
+                drive->num_cylinders, drive->num_heads, drive->num_sectors, *lba );
+    }
+       
+    if ((*lba + sect_cnt) > 
        drive->ops->get_capacity(drive->private_data) / HD_SECTOR_SIZE) {
-       PrintError(VM_NONE, VCORE_NONE,"IDE: request size exceeds disk capacity (lba=%d) (sect_cnt=%d) (ReadEnd=%d) (capacity=%p)\n", 
-                  lba_addr.addr, sect_cnt, 
-                  lba_addr.addr + (sect_cnt * HD_SECTOR_SIZE),
-                  (void *)(addr_t)(drive->ops->get_capacity(drive->private_data)));
+       PrintError(VM_NONE, VCORE_NONE,"IDE: request size exceeds disk capacity (lba=%llu) (sect_cnt=%u) (ReadEnd=%llu) (capacity=%llu)\n", 
+                  *lba, sect_cnt, 
+                  *lba + (sect_cnt * HD_SECTOR_SIZE),
+                  drive->ops->get_capacity(drive->private_data));
        return -1;
     }
-
-    *lba = lba_addr.addr;
+    
     return 0;
 }
 
@@ -191,11 +226,14 @@ static int ata_write_sectors(struct ide_internal * ide,  struct ide_channel * ch
     uint32_t sect_cnt = (drive->sector_count == 0) ? 256 : drive->sector_count;
 
     if (ata_get_lba(ide, channel, &(drive->current_lba)) == -1) {
+       PrintError(VM_NONE,VCORE_NONE,"Cannot get lba\n");
         ide_abort_command(ide, channel);
         return 0;
     }
 
-    drive->transfer_length = sect_cnt * HD_SECTOR_SIZE;
+    PrintDebug(VM_NONE,VCORE_NONE,"ata write sectors: lba=%llu sect_cnt=%u\n", drive->current_lba, sect_cnt);
+
+    drive->transfer_length = sect_cnt * HD_SECTOR_SIZE ;
     drive->transfer_index = 0;
     channel->status.busy = 0;
     channel->status.ready = 0;
@@ -203,10 +241,6 @@ static int ata_write_sectors(struct ide_internal * ide,  struct ide_channel * ch
     channel->status.data_req = 1;
     channel->status.error = 0;
 
-    drive->irq_flags.io_dir = 1;
-    drive->irq_flags.c_d = 0;
-    drive->irq_flags.rel = 0;
-
     PrintDebug(VM_NONE, VCORE_NONE, "IDE: Returning from write sectors\n");
 
     return 0;
@@ -220,6 +254,7 @@ static int ata_read_sectors(struct ide_internal * ide,  struct ide_channel * cha
     uint32_t sect_cnt = (drive->sector_count == 0) ? 256 : drive->sector_count;
 
     if (ata_get_lba(ide, channel, &(drive->current_lba)) == -1) {
+       PrintError(VM_NONE,VCORE_NONE,"Cannot get lba\n");
        ide_abort_command(ide, channel);
        return 0;
     }
@@ -239,11 +274,6 @@ static int ata_read_sectors(struct ide_internal * ide,  struct ide_channel * cha
     channel->status.data_req = 1;
     channel->status.error = 0;
 
-    drive->irq_flags.io_dir = 1;
-    drive->irq_flags.c_d = 0;
-    drive->irq_flags.rel = 0;
-
-
     ide_raise_irq(ide, channel);
 
     PrintDebug(VM_NONE, VCORE_NONE,"Returning from read sectors\n");
@@ -263,4 +293,82 @@ static int ata_read_sectors_ext(struct ide_internal * ide, struct ide_channel *
     return -1;
 }
 
+/* ATA COMMANDS as per */
+/* ACS-2 T13/2015-D Table B.2 Command codes */
+#define ATA_NOP                                0x00
+#define CFA_REQ_EXT_ERROR_CODE         0x03 
+#define ATA_DSM                         0x06
+#define ATA_DEVICE_RESET               0x08
+#define ATA_RECAL                       0x10 
+#define ATA_READ                       0x20 
+#define ATA_READ_ONCE                   0x21 
+#define ATA_READ_EXT                   0x24 
+#define ATA_READDMA_EXT                        0x25 
+#define ATA_READDMA_QUEUED_EXT          0x26 
+#define ATA_READ_NATIVE_MAX_EXT                0x27 
+#define ATA_MULTREAD_EXT               0x29 
+#define ATA_WRITE                      0x30 
+#define ATA_WRITE_ONCE                  0x31 
+#define ATA_WRITE_EXT                  0x34 
+#define ATA_WRITEDMA_EXT               0x35 
+#define ATA_WRITEDMA_QUEUED_EXT                0x36 
+#define ATA_SET_MAX_EXT                 0x37 
+#define ATA_SET_MAX_EXT                        0x37 
+#define CFA_WRITE_SECT_WO_ERASE                0x38 
+#define ATA_MULTWRITE_EXT              0x39 
+#define ATA_WRITE_VERIFY                0x3C 
+#define ATA_VERIFY                     0x40 
+#define ATA_VERIFY_ONCE                 0x41 
+#define ATA_VERIFY_EXT                 0x42 
+#define ATA_SEEK                        0x70 
+#define CFA_TRANSLATE_SECTOR           0x87 
+#define ATA_DIAGNOSE                   0x90
+#define ATA_SPECIFY                     0x91 
+#define ATA_DOWNLOAD_MICROCODE         0x92
+#define ATA_STANDBYNOW2                 0x94 
+#define ATA_IDLEIMMEDIATE2              0x95 
+#define ATA_STANDBY2                    0x96 
+#define ATA_SETIDLE2                    0x97 
+#define ATA_CHECKPOWERMODE2             0x98 
+#define ATA_SLEEPNOW2                   0x99 
+#define ATA_PACKETCMD                  0xA0 
+#define ATA_PIDENTIFY                  0xA1 
+#define ATA_QUEUED_SERVICE              0xA2 
+#define ATA_SMART                      0xB0 
+#define CFA_ACCESS_METADATA_STORAGE    0xB8
+#define CFA_ERASE_SECTORS              0xC0 
+#define ATA_MULTREAD                   0xC4 
+#define ATA_MULTWRITE                  0xC5 
+#define ATA_SETMULT                    0xC6 
+#define ATA_READDMA                    0xC8 
+#define ATA_READDMA_ONCE                0xC9 
+#define ATA_WRITEDMA                   0xCA 
+#define ATA_WRITEDMA_ONCE               0xCB 
+#define ATA_WRITEDMA_QUEUED            0xCC 
+#define CFA_WRITE_MULTI_WO_ERASE       0xCD 
+#define ATA_GETMEDIASTATUS              0xDA 
+#define ATA_DOORLOCK                    0xDE 
+#define ATA_DOORUNLOCK                  0xDF 
+#define ATA_STANDBYNOW1                        0xE0
+#define ATA_IDLEIMMEDIATE              0xE1 
+#define ATA_STANDBY                    0xE2 
+#define ATA_SETIDLE1                   0xE3
+#define ATA_READ_BUFFER                        0xE4 
+#define ATA_CHECKPOWERMODE1            0xE5
+#define ATA_SLEEPNOW1                  0xE6
+#define ATA_FLUSH_CACHE                        0xE7
+#define ATA_WRITE_BUFFER               0xE8 
+#define ATA_FLUSH_CACHE_EXT            0xEA 
+#define ATA_IDENTIFY                   0xEC 
+#define ATA_MEDIAEJECT                  0xED 
+#define ATA_SETFEATURES                        0xEF 
+#define IBM_SENSE_CONDITION             0xF0 
+#define ATA_SECURITY_SET_PASS          0xF1
+#define ATA_SECURITY_UNLOCK            0xF2
+#define ATA_SECURITY_ERASE_PREPARE     0xF3
+#define ATA_SECURITY_ERASE_UNIT                0xF4
+#define ATA_SECURITY_FREEZE_LOCK       0xF5
+#define CFA_WEAR_LEVEL                  0xF5 
+#define ATA_SECURITY_DISABLE           0xF6
+
 #endif
index 7667e97..0cc9f26 100644 (file)
 #include "atapi-types.h"
 
 
-/* ACS-2 T13/2015-D Table B.2 Command codes */
-#define ATAPI_NOP                              0x00
-#define CFA_REQ_EXT_ERROR_CODE         0x03 
-#define ATAPI_DSM                         0x06
-#define ATAPI_DEVICE_RESET             0x08
-#define ATAPI_RECAL                       0x10 
-#define ATAPI_READ                     0x20 
-#define ATAPI_READ_ONCE                   0x21 
-#define ATAPI_READ_EXT                 0x24 
-#define ATAPI_READDMA_EXT                      0x25 
-#define ATAPI_READDMA_QUEUED_EXT          0x26 
-#define ATAPI_READ_NATIVE_MAX_EXT              0x27 
-#define ATAPI_MULTREAD_EXT             0x29 
-#define ATAPI_WRITE                    0x30 
-#define ATAPI_WRITE_ONCE                  0x31 
-#define ATAPI_WRITE_EXT                        0x34 
-#define ATAPI_WRITEDMA_EXT             0x35 
-#define ATAPI_WRITEDMA_QUEUED_EXT              0x36 
-#define ATAPI_SET_MAX_EXT                 0x37 
-#define ATAPI_SET_MAX_EXT                      0x37 
-#define CFA_WRITE_SECT_WO_ERASE                0x38 
-#define ATAPI_MULTWRITE_EXT            0x39 
-#define ATAPI_WRITE_VERIFY                0x3C 
-#define ATAPI_VERIFY                   0x40 
-#define ATAPI_VERIFY_ONCE                 0x41 
-#define ATAPI_VERIFY_EXT                       0x42 
-#define ATAPI_SEEK                        0x70 
-#define CFA_TRANSLATE_SECTOR           0x87 
-#define ATAPI_DIAGNOSE                 0x90
-#define ATAPI_SPECIFY                     0x91 
-#define ATAPI_DOWNLOAD_MICROCODE               0x92
-#define ATAPI_STANDBYNOW2                 0x94 
-#define ATAPI_IDLEIMMEDIATE2              0x95 
-#define ATAPI_STANDBY2                    0x96 
-#define ATAPI_SETIDLE2                    0x97 
-#define ATAPI_CHECKPOWERMODE2             0x98 
-#define ATAPI_SLEEPNOW2                   0x99 
-#define ATAPI_PACKETCMD                        0xA0 
-#define ATAPI_PIDENTIFY                        0xA1 
-#define ATAPI_QUEUED_SERVICE              0xA2 
-#define ATAPI_SMART                    0xB0 
-#define CFA_ACCESS_METADATA_STORAGE    0xB8
-#define CFA_ERASE_SECTORS              0xC0 
-#define ATAPI_MULTREAD                 0xC4 
-#define ATAPI_MULTWRITE                        0xC5 
-#define ATAPI_SETMULT                  0xC6 
-#define ATAPI_READDMA                  0xC8 
-#define ATAPI_READDMA_ONCE                0xC9 
-#define ATAPI_WRITEDMA                 0xCA 
-#define ATAPI_WRITEDMA_ONCE               0xCB 
-#define ATAPI_WRITEDMA_QUEUED          0xCC 
-#define CFA_WRITE_MULTI_WO_ERASE       0xCD 
-#define ATAPI_GETMEDIASTATUS              0xDA 
-#define ATAPI_DOORLOCK                    0xDE 
-#define ATAPI_DOORUNLOCK                  0xDF 
-#define ATAPI_STANDBYNOW1                      0xE0
-#define ATAPI_IDLEIMMEDIATE            0xE1 
-#define ATAPI_STANDBY                  0xE2 
-#define ATAPI_SETIDLE1                 0xE3
-#define ATAPI_READ_BUFFER                      0xE4 
-#define ATAPI_CHECKPOWERMODE1          0xE5
-#define ATAPI_SLEEPNOW1                        0xE6
-#define ATAPI_FLUSH_CACHE                      0xE7
-#define ATAPI_WRITE_BUFFER             0xE8 
-#define ATAPI_FLUSH_CACHE_EXT          0xEA 
-#define ATAPI_IDENTIFY                 0xEC 
-#define ATAPI_MEDIAEJECT                  0xED 
-#define ATAPI_SETFEATURES                      0xEF 
-#define IBM_SENSE_CONDITION             0xF0 
-#define ATAPI_SECURITY_SET_PASS                0xF1
-#define ATAPI_SECURITY_UNLOCK          0xF2
-#define ATAPI_SECURITY_ERASE_PREPARE   0xF3
-#define ATAPI_SECURITY_ERASE_UNIT              0xF4
-#define ATAPI_SECURITY_FREEZE_LOCK     0xF5
-#define CFA_WEAR_LEVEL                  0xF5 
-#define ATAPI_SECURITY_DISABLE         0xF6
 
 /* ATAPI sucks...
  * The OS will write to the cylinder register the number of bytes it wants to read 
index 4dd5db6..abff044 100644 (file)
@@ -156,8 +156,8 @@ struct ide_drive {
     void * private_data;
     
     union {
-       uint8_t sector_count;             // 0x1f2,0x172
-       struct atapi_irq_flags irq_flags;
+       uint8_t sector_count;             // 0x1f2,0x172  (ATA)
+       struct atapi_irq_flags irq_flags; // (ATAPI ONLY)
     } __attribute__((packed));
 
     union {
@@ -333,7 +333,7 @@ static void channel_reset(struct ide_channel * channel) {
     channel->error_reg.val = 0x01;
 
     // clear commands
-    channel->cmd_reg = 0x00;
+    channel->cmd_reg = 0;  // NOP
 
     channel->ctrl_reg.irq_disable = 0;
 }
@@ -350,6 +350,9 @@ static void channel_reset_complete(struct ide_channel * channel) {
 
 
 static void ide_abort_command(struct ide_internal * ide, struct ide_channel * channel) {
+
+    PrintDebug(VM_NONE,VCORE_NONE,"Aborting IDE Command\n");
+
     channel->status.val = 0x41; // Error + ready
     channel->error_reg.val = 0x04; // No idea...
 
@@ -377,7 +380,7 @@ static void print_prd_table(struct ide_internal * ide, struct ide_channel * chan
 
     while (1) {
        uint32_t prd_entry_addr = channel->dma_prd_addr + (sizeof(struct ide_dma_prd) * index);
-       int ret;
+       int ret = 0;
 
        ret = v3_read_gpa_memory(&(ide->vm->cores[0]), prd_entry_addr, sizeof(struct ide_dma_prd), (void *)&prd_entry);
        
@@ -810,7 +813,7 @@ static int write_cmd_port(struct guest_info * core, ushort_t port, void * src, u
     
     switch (channel->cmd_reg) {
 
-       case ATAPI_PIDENTIFY: // ATAPI Identify Device Packet
+       case ATA_PIDENTIFY: // ATAPI Identify Device Packet
            if (drive->drive_type != BLOCK_CDROM) {
                drive_reset(drive);
 
@@ -826,7 +829,7 @@ static int write_cmd_port(struct guest_info * core, ushort_t port, void * src, u
                ide_raise_irq(ide, channel);
            }
            break;
-       case ATAPI_IDENTIFY: // Identify Device
+       case ATA_IDENTIFY: // Identify Device
            if (drive->drive_type != BLOCK_DISK) {
                drive_reset(drive);
 
@@ -842,7 +845,7 @@ static int write_cmd_port(struct guest_info * core, ushort_t port, void * src, u
            }
            break;
 
-       case ATAPI_PACKETCMD: // ATAPI Command Packet
+       case ATA_PACKETCMD: // ATAPI Command Packet
            if (drive->drive_type != BLOCK_CDROM) {
                ide_abort_command(ide, channel);
            }
@@ -854,50 +857,51 @@ static int write_cmd_port(struct guest_info * core, ushort_t port, void * src, u
            channel->status.data_req = 1;
            channel->status.error = 0;
 
-           // reset the datxgoto-la buffer...
+           // reset the data buffer...
            drive->transfer_length = ATAPI_PACKET_SIZE;
            drive->transfer_index = 0;
 
            break;
 
-       case ATAPI_READ: // Read Sectors with Retry
-       case ATAPI_READ_ONCE: // Read Sectors without Retry
+       case ATA_READ: // Read Sectors with Retry
+       case ATA_READ_ONCE: // Read Sectors without Retry
            drive->hd_state.cur_sector_num = 1;
 
            if (ata_read_sectors(ide, channel) == -1) {
                PrintError(core->vm_info, core, "Error reading sectors\n");
-               return -1;
+               ide_abort_command(ide,channel);
            }
            break;
 
-       case ATAPI_READ_EXT: // Read Sectors Extended
+       case ATA_READ_EXT: // Read Sectors Extended
            drive->hd_state.cur_sector_num = 1;
 
            if (ata_read_sectors_ext(ide, channel) == -1) {
                PrintError(core->vm_info, core, "Error reading extended sectors\n");
-               return -1;
+               ide_abort_command(ide,channel);
+               
            }
            break;
 
-       case ATAPI_WRITE: {// Write Sector
+       case ATA_WRITE: 
+       case ATA_WRITE_ONCE:        {// Write Sector
            drive->hd_state.cur_sector_num = 1;
 
            if (ata_write_sectors(ide, channel) == -1) {
                PrintError(core->vm_info, core, "Error writing sectors\n");
-               return -1;
+               ide_abort_command(ide,channel);
            }
            break;
        }
 
-           
-
-       case ATAPI_READDMA: // Read DMA with retry
-       case ATAPI_READDMA_ONCE: { // Read DMA
+       case ATA_READDMA: // Read DMA with retry
+       case ATA_READDMA_ONCE: { // Read DMA
            uint32_t sect_cnt = (drive->sector_count == 0) ? 256 : drive->sector_count;
 
            if (ata_get_lba(ide, channel, &(drive->current_lba)) == -1) {
+                PrintError(core->vm_info, core, "Error getting LBA for DMA READ\n");
                ide_abort_command(ide, channel);
-               return 0;
+               return length;
            }
            
            drive->hd_state.cur_sector_num = 1;
@@ -909,18 +913,22 @@ static int write_cmd_port(struct guest_info * core, ushort_t port, void * src, u
                // DMA Read
                if (dma_read(core, ide, channel) == -1) {
                    PrintError(core->vm_info, core, "Failed DMA Read\n");
-                   return -1;
+                   ide_abort_command(ide, channel);
                }
-           }
+           } else {
+                PrintError(core->vm_info,core,"Attempt to initiate DMA read on channel that is not active\n");
+               ide_abort_command(ide, channel);
+            }
            break;
        }
 
-       case ATAPI_WRITEDMA: { // Write DMA
+       case ATA_WRITEDMA: { // Write DMA
            uint32_t sect_cnt = (drive->sector_count == 0) ? 256 : drive->sector_count;
 
            if (ata_get_lba(ide, channel, &(drive->current_lba)) == -1) {
+               PrintError(core->vm_info,core,"Cannot get lba\n");
                ide_abort_command(ide, channel);
-               return 0;
+               return length;
            }
 
            drive->hd_state.cur_sector_num = 1;
@@ -932,27 +940,30 @@ static int write_cmd_port(struct guest_info * core, ushort_t port, void * src, u
                // DMA Write
                if (dma_write(core, ide, channel) == -1) {
                    PrintError(core->vm_info, core, "Failed DMA Write\n");
-                   return -1;
+                   ide_abort_command(ide, channel);
                }
+           } else {
+               PrintError(core->vm_info,core,"Attempt to initiate DMA write with DMA inactive\n");
+               ide_abort_command(ide, channel);
            }
            break;
        }
-       case ATAPI_STANDBYNOW1: // Standby Now 1
-       case ATAPI_IDLEIMMEDIATE: // Set Idle Immediate
-       case ATAPI_STANDBY: // Standby
-       case ATAPI_SETIDLE1: // Set Idle 1
-       case ATAPI_SLEEPNOW1: // Sleep Now 1
-       case ATAPI_STANDBYNOW2: // Standby Now 2
-       case ATAPI_IDLEIMMEDIATE2: // Idle Immediate (CFA)
-       case ATAPI_STANDBY2: // Standby 2
-       case ATAPI_SETIDLE2: // Set idle 2
-       case ATAPI_SLEEPNOW2: // Sleep Now 2
+       case ATA_STANDBYNOW1: // Standby Now 1
+       case ATA_IDLEIMMEDIATE: // Set Idle Immediate
+       case ATA_STANDBY: // Standby
+       case ATA_SETIDLE1: // Set Idle 1
+       case ATA_SLEEPNOW1: // Sleep Now 1
+       case ATA_STANDBYNOW2: // Standby Now 2
+       case ATA_IDLEIMMEDIATE2: // Idle Immediate (CFA)
+       case ATA_STANDBY2: // Standby 2
+       case ATA_SETIDLE2: // Set idle 2
+       case ATA_SLEEPNOW2: // Sleep Now 2
            channel->status.val = 0;
            channel->status.ready = 1;
            ide_raise_irq(ide, channel);
            break;
 
-       case ATAPI_SETFEATURES: // Set Features
+       case ATA_SETFEATURES: // Set Features
            // Prior to this the features register has been written to. 
            // This command tells the drive to check if the new value is supported (the value is drive specific)
            // Common is that bit0=DMA enable
@@ -969,14 +980,14 @@ static int write_cmd_port(struct guest_info * core, ushort_t port, void * src, u
            ide_raise_irq(ide, channel);
            break;
 
-       case ATAPI_SPECIFY:  // Initialize Drive Parameters
-       case ATAPI_RECAL:  // recalibrate?
+       case ATA_SPECIFY:  // Initialize Drive Parameters
+       case ATA_RECAL:  // recalibrate?
            channel->status.error = 0;
            channel->status.ready = 1;
            channel->status.seek_complete = 1;
            ide_raise_irq(ide, channel);
            break;
-       case ATAPI_SETMULT: { // Set multiple mode (IDE Block mode) 
+       case ATA_SETMULT: { // Set multiple mode (IDE Block mode) 
            // This makes the drive transfer multiple sectors before generating an interrupt
            uint32_t tmp_sect_num = drive->sector_num; // GCC SUCKS
 
@@ -999,7 +1010,7 @@ static int write_cmd_port(struct guest_info * core, ushort_t port, void * src, u
            break;
        }
 
-       case ATAPI_DEVICE_RESET: // Reset Device
+       case ATA_DEVICE_RESET: // Reset Device
            drive_reset(drive);
            channel->error_reg.val = 0x01;
            channel->status.busy = 0;
@@ -1009,7 +1020,7 @@ static int write_cmd_port(struct guest_info * core, ushort_t port, void * src, u
            channel->status.error = 0;
            break;
 
-       case ATAPI_CHECKPOWERMODE1: // Check power mode
+       case ATA_CHECKPOWERMODE1: // Check power mode
            drive->sector_count = 0xff; /* 0x00=standby, 0x80=idle, 0xff=active or idle */
            channel->status.busy = 0;
            channel->status.ready = 1;
@@ -1017,65 +1028,21 @@ static int write_cmd_port(struct guest_info * core, ushort_t port, void * src, u
            channel->status.data_req = 0;
            channel->status.error = 0;
            break;
-
-       case ATAPI_MULTREAD:  // read multiple sectors
+           /*
+       case ATA_MULTREAD:  // read multiple sectors
            drive->hd_state.cur_sector_num = drive->hd_state.mult_sector_num;
+           */
+
        default:
            PrintError(core->vm_info, core, "Unimplemented IDE command (%x)\n", channel->cmd_reg);
-           return -1;
+           ide_abort_command(ide, channel);
+           break;
     }
 
     return length;
 }
 
 
-static int write_data_port(struct guest_info * core, ushort_t port, void * src, uint_t length, void * priv_data) {
-    struct ide_internal * ide = priv_data;
-    struct ide_channel * channel = get_selected_channel(ide, port);
-    struct ide_drive * drive = get_selected_drive(channel);
-
-    PrintDebug(core->vm_info, core, "IDE: Writing Data Port %x (val=%x, len=%d)\n", 
-            port, *(uint32_t *)src, length);
-
-    memcpy(drive->data_buf + drive->transfer_index, src, length);    
-    drive->transfer_index += length;
-
-    // Transfer is complete, dispatch the command
-    if (drive->transfer_index >= drive->transfer_length) {
-        switch (channel->cmd_reg) {
-
-            case ATAPI_WRITE: // Write Sectors
-
-                channel->status.busy = 1;
-                channel->status.data_req = 0;
-                    
-                if (ata_write(ide, channel, drive->data_buf, drive->transfer_length/HD_SECTOR_SIZE) == -1) {
-                    PrintError(core->vm_info, core, "Error writing to disk\n");
-                    return -1;
-                }
-
-                PrintDebug(core->vm_info, core, "IDE: Write sectors complete\n");
-
-                channel->status.error = 0;
-                channel->status.busy = 0;
-
-                ide_raise_irq(ide, channel);
-                break;
-
-            case ATAPI_PACKETCMD: // ATAPI packet command
-                if (atapi_handle_packet(core, ide, channel) == -1) {
-                    PrintError(core->vm_info, core, "Error handling ATAPI packet\n");
-                    return -1;
-                }
-                break;
-            default:
-                PrintError(core->vm_info, core, "Unhandld IDE Command %x\n", channel->cmd_reg);
-                return -1;
-        }
-    }
-
-    return length;
-}
 
 
 static int read_hd_data(uint8_t * dst, uint_t length, struct ide_internal * ide, struct ide_channel * channel) {
@@ -1083,6 +1050,9 @@ static int read_hd_data(uint8_t * dst, uint_t length, struct ide_internal * ide,
     int data_offset = drive->transfer_index % HD_SECTOR_SIZE;
 
 
+    PrintDebug(VM_NONE,VCORE_NONE, "Read HD data:  transfer_index %x transfer length %x current sector numer %x\n",
+              drive->transfer_index, drive->transfer_length, 
+              drive->hd_state.cur_sector_num);
 
     if (drive->transfer_index >= drive->transfer_length) {
        PrintError(VM_NONE, VCORE_NONE, "Buffer overrun... (xfer_len=%d) (cur_idx=%x) (post_idx=%d)\n",
@@ -1091,8 +1061,15 @@ static int read_hd_data(uint8_t * dst, uint_t length, struct ide_internal * ide,
        return -1;
     }
 
-    
+
+    if (data_offset + length > HD_SECTOR_SIZE) { 
+       PrintError(VM_NONE,VCORE_NONE,"Read spans sectors (data_offset=%d length=%u)!\n",data_offset,length);
+    }
+   
+    // For index==0, the read has been done in ata_read_sectors
     if ((data_offset == 0) && (drive->transfer_index > 0)) {
+       // advance to next sector and read it
+       
        drive->current_lba++;
 
        if (ata_read(ide, channel, drive->data_buf, 1) == -1) {
@@ -1121,22 +1098,15 @@ static int read_hd_data(uint8_t * dst, uint_t length, struct ide_internal * ide,
        (drive->transfer_index == drive->transfer_length)) {
        if (drive->transfer_index < drive->transfer_length) {
            // An increment is complete, but there is still more data to be transferred...
-           PrintDebug(VM_NONE, VCORE_NONE, "Integral Complete, still transferring more sectors\n");
+           PrintDebug(VM_NONE, VCORE_NONE, "Increment Complete, still transferring more sectors\n");
            channel->status.data_req = 1;
-
-           drive->irq_flags.c_d = 0;
        } else {
            PrintDebug(VM_NONE, VCORE_NONE, "Final Sector Transferred\n");
            // This was the final read of the request
            channel->status.data_req = 0;
-
-           
-           drive->irq_flags.c_d = 1;
-           drive->irq_flags.rel = 0;
        }
 
        channel->status.ready = 1;
-       drive->irq_flags.io_dir = 1;
        channel->status.busy = 0;
 
        ide_raise_irq(ide, channel);
@@ -1146,6 +1116,72 @@ static int read_hd_data(uint8_t * dst, uint_t length, struct ide_internal * ide,
     return length;
 }
 
+static int write_hd_data(uint8_t * src, uint_t length, struct ide_internal * ide, struct ide_channel * channel) {
+    struct ide_drive * drive = get_selected_drive(channel);
+    int data_offset = drive->transfer_index % HD_SECTOR_SIZE;
+
+
+    PrintDebug(VM_NONE,VCORE_NONE, "Write HD data:  transfer_index %x transfer length %x current sector numer %x\n",
+              drive->transfer_index, drive->transfer_length, 
+              drive->hd_state.cur_sector_num);
+
+    if (drive->transfer_index >= drive->transfer_length) {
+       PrintError(VM_NONE, VCORE_NONE, "Buffer overrun... (xfer_len=%d) (cur_idx=%x) (post_idx=%d)\n",
+                  drive->transfer_length, drive->transfer_index,
+                  drive->transfer_index + length);
+       return -1;
+    }
+
+    if (data_offset + length > HD_SECTOR_SIZE) { 
+       PrintError(VM_NONE,VCORE_NONE,"Write spans sectors (data_offset=%d length=%u)!\n",data_offset,length);
+    }
+
+    // Copy data into our buffer - there will be room due to
+    // (a) the ata_write test below is flushing sectors
+    // (b) if we somehow get a sector-stradling write (an error), this will
+    //     be OK since the buffer itself is >1 sector in memory
+    memcpy(drive->data_buf + data_offset, src, length);
+
+    drive->transfer_index += length;
+
+    if ((data_offset+length) >= HD_SECTOR_SIZE) {
+       // Write out the sector we just finished
+       if (ata_write(ide, channel, drive->data_buf, 1) == -1) {
+           PrintError(VM_NONE, VCORE_NONE, "Could not write next disk sector\n");
+           return -1;
+       }
+
+       // go onto next sector
+       drive->current_lba++;
+    }
+
+    /* This is the trigger for interrupt injection.
+     * For write single sector commands we interrupt after every sector
+     * For multi sector reads we interrupt only at end of the cluster size (mult_sector_num)
+     * cur_sector_num is configured depending on the operation we are currently running
+     * We also trigger an interrupt if this is the last byte to transfer, regardless of sector count
+     */
+    if (((drive->transfer_index % (HD_SECTOR_SIZE * drive->hd_state.cur_sector_num)) == 0) || 
+       (drive->transfer_index == drive->transfer_length)) {
+       if (drive->transfer_index < drive->transfer_length) {
+           // An increment is complete, but there is still more data to be transferred...
+           PrintDebug(VM_NONE, VCORE_NONE, "Increment Complete, still transferring more sectors\n");
+           channel->status.data_req = 1;
+       } else {
+           PrintDebug(VM_NONE, VCORE_NONE, "Final Sector Transferred\n");
+           // This was the final read of the request
+           channel->status.data_req = 0;
+       }
+
+       channel->status.ready = 1;
+       channel->status.busy = 0;
+
+       ide_raise_irq(ide, channel);
+    }
+
+    return length;
+}
+
 
 
 static int read_cd_data(uint8_t * dst, uint_t length, struct ide_internal * ide, struct ide_channel * channel) {
@@ -1237,15 +1273,16 @@ static int read_drive_id( uint8_t * dst, uint_t length, struct ide_internal * id
 }
 
 
-static int ide_read_data_port(struct guest_info * core, ushort_t port, void * dst, uint_t length, void * priv_data) {
+
+static int read_data_port(struct guest_info * core, ushort_t port, void * dst, uint_t length, void * priv_data) {
     struct ide_internal * ide = priv_data;
     struct ide_channel * channel = get_selected_channel(ide, port);
     struct ide_drive * drive = get_selected_drive(channel);
 
-    //       PrintDebug(core->vm_info, core, "IDE: Reading Data Port %x (len=%d)\n", port, length);
+    //PrintDebug(core->vm_info, core, "IDE: Reading Data Port %x (len=%d)\n", port, length);
 
-    if ((channel->cmd_reg == 0xec) ||
-       (channel->cmd_reg == 0xa1)) {
+    if ((channel->cmd_reg == ATA_IDENTIFY) ||
+       (channel->cmd_reg == ATA_PIDENTIFY)) {
        return read_drive_id((uint8_t *)dst, length, ide, channel);
     }
 
@@ -1266,6 +1303,44 @@ static int ide_read_data_port(struct guest_info * core, ushort_t port, void * ds
     return length;
 }
 
+// For the write side, we care both about
+// direct PIO writes to a drive as well as 
+// writes that pass a packet through to an CD
+static int write_data_port(struct guest_info * core, ushort_t port, void * src, uint_t length, void * priv_data) {
+    struct ide_internal * ide = priv_data;
+    struct ide_channel * channel = get_selected_channel(ide, port);
+    struct ide_drive * drive = get_selected_drive(channel);
+
+    PrintDebug(core->vm_info, core, "IDE: Writing Data Port %x (val=%x, len=%d)\n", 
+            port, *(uint32_t *)src, length);
+
+    if (drive->drive_type == BLOCK_CDROM) {
+       if (channel->cmd_reg == ATA_PACKETCMD) { 
+           // short command packet - no check for space... 
+           memcpy(drive->data_buf + drive->transfer_index, src, length);
+           drive->transfer_index += length;
+           if (drive->transfer_index >= drive->transfer_length) {
+               if (atapi_handle_packet(core, ide, channel) == -1) {
+                   PrintError(core->vm_info, core, "Error handling ATAPI packet\n");
+                   return -1;
+               }
+           }
+       } else {
+           PrintError(core->vm_info,core,"Unknown command %x on CD ROM\n",channel->cmd_reg);
+           return -1;
+       }
+    } else if (drive->drive_type == BLOCK_DISK) {
+       if (write_hd_data((uint8_t *)src, length, ide, channel) == -1) {
+           PrintError(core->vm_info, core, "IDE: Could not write HD Data\n");
+           return -1;
+       }
+    } else {
+       // nothing ... do not support writable cd
+    }
+
+    return length;
+}
+
 static int write_port_std(struct guest_info * core, ushort_t port, void * src, uint_t length, void * priv_data) {
     struct ide_internal * ide = priv_data;
     struct ide_channel * channel = get_selected_channel(ide, port);
@@ -1870,7 +1945,7 @@ static int ide_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
     PrintDebug(vm, VCORE_NONE, "Connecting to IDE IO ports\n");
 
     ret |= v3_dev_hook_io(dev, PRI_DATA_PORT, 
-                         &ide_read_data_port, &write_data_port);
+                         &read_data_port, &write_data_port);
     ret |= v3_dev_hook_io(dev, PRI_FEATURES_PORT, 
                          &read_port_std, &write_port_std);
     ret |= v3_dev_hook_io(dev, PRI_SECT_CNT_PORT, 
@@ -1887,7 +1962,7 @@ static int ide_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
                          &read_port_std, &write_cmd_port);
 
     ret |= v3_dev_hook_io(dev, SEC_DATA_PORT, 
-                         &ide_read_data_port, &write_data_port);
+                         &read_data_port, &write_data_port);
     ret |= v3_dev_hook_io(dev, SEC_FEATURES_PORT, 
                          &read_port_std, &write_port_std);
     ret |= v3_dev_hook_io(dev, SEC_SECT_CNT_PORT,