From: Peter Dinda Date: Wed, 4 Mar 2015 18:22:21 +0000 (-0600) Subject: IDE / ATA rewrites (1st step) X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?p=palacios.git;a=commitdiff_plain;h=db03a1fef60b6d90c02a65369292a57833a7824b IDE / ATA rewrites (1st step) 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. --- diff --git a/palacios/src/devices/ata.h b/palacios/src/devices/ata.h index 0f9c8df..dd1c4b7 100644 --- a/palacios/src/devices/ata.h +++ b/palacios/src/devices/ata.h @@ -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 diff --git a/palacios/src/devices/atapi.h b/palacios/src/devices/atapi.h index 7667e97..0cc9f26 100644 --- a/palacios/src/devices/atapi.h +++ b/palacios/src/devices/atapi.h @@ -25,82 +25,6 @@ #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 diff --git a/palacios/src/devices/ide.c b/palacios/src/devices/ide.c index 4dd5db6..abff044 100644 --- a/palacios/src/devices/ide.c +++ b/palacios/src/devices/ide.c @@ -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,