memcpy(drive_id->firmware_rev, firmware, strlen(firmware));
memcpy(drive_id->model_num, drive->model, 40);
- // 32 bits access
+ // 32 bits access for PIO supported
drive_id->dword_io = 1;
// enable DMA access
- // PAD disable DMA capability
- drive_id->dma_enable = 0; // 1;
+ // We want guest to assume UDMA5
+ // but any DMA model looks the same to the guest
+ drive_id->dma_enable = 1;
// enable LBA access
drive_id->lba_enable = 1;
drive_id->buf[63] = 0x0007;
- // PAD: Support PIO mode 0
+ // We wll support PIO mode 0
+ // Maybe revisit this later to allow advanced modes
+ // We really want the guest to use DMA
drive_id->buf[64] = 0x0001; // PIO
// MWDMA transfer min cycle time
drive_id->buf[72] = 30; // faked
// queue depth set to one
+ // We should not expect queued DMAs
// drive_id->buf[80] = 0x1e; // supports up to ATA/ATAPI-4
drive_id->major_rev_num = 0x0040; // supports up to ATA/ATAPI-6
// No special features supported
- // PAD: Disable ultra DMA capability
- //drive_id->dma_ultra = 0x2020; // Ultra_DMA_Mode_5_Selected | Ultra_DMA_Mode_5_Supported;
+ // Pretend drive is already autoconfed to UDMA5
+ drive_id->dma_ultra = 0x2020; // Ultra_DMA_Mode_5_Selected | Ultra_DMA_Mode_5_Supported;
}
#define DMA_CHANNEL_FLAG 0x08
+/*
+ Note that DMA model is as follows:
+
+ 1. Write the PRD pointer to the busmaster (DMA engine)
+ 2. Start the transfer on the device
+ 3. Tell the busmaster to start shoveling data (active DMA)
+*/
+
static int write_dma_port(struct guest_info * core, ushort_t port, void * src, uint_t length, void * private_data) {
struct ide_internal * ide = (struct ide_internal *)private_data;
uint16_t port_offset = port & (DMA_CHANNEL_FLAG - 1);
switch (port_offset) {
case DMA_CMD_PORT:
channel->dma_cmd.val = *(uint8_t *)src;
+
+ PrintDebug(core->vm_info, core, "IDE: dma command write: 0x%x\n", channel->dma_cmd.val);
if (channel->dma_cmd.start == 0) {
channel->dma_tbl_index = 0;
} else {
+ // Launch DMA operation, interrupt at end
+
channel->dma_status.active = 1;
if (channel->dma_cmd.read == 1) {
- // DMA Read
+ // DMA Read the whole thing - dma_read will raise irq
if (dma_read(core, ide, channel) == -1) {
PrintError(core->vm_info, core, "Failed DMA Read\n");
return -1;
}
} else {
- // DMA write
+ // DMA write the whole thing - dma_write will raiase irw
if (dma_write(core, ide, channel) == -1) {
PrintError(core->vm_info, core, "Failed DMA Write\n");
return -1;
}
}
-
- channel->dma_cmd.val &= 0x09;
+
+ // DMA complete
+ // Note that guest cannot abort a DMA transfer
+ channel->dma_cmd.start = 0;
}
break;
case DMA_STATUS_PORT: {
+ // This is intended to clear status
+
uint8_t val = *(uint8_t *)src;
if (length != 1) {
- PrintError(core->vm_info, core, "Invalid read length for DMA status port\n");
+ PrintError(core->vm_info, core, "Invalid write length for DMA status port\n");
return -1;
}
- // weirdness
+ // but preserve certain bits
channel->dma_status.val = ((val & 0x60) |
(channel->dma_status.val & 0x01) |
(channel->dma_status.val & ~val & 0x06));
switch (channel->cmd_reg) {
- case ATA_PIDENTIFY: // ATAPI Identify Device Packet
+ case ATA_PIDENTIFY: // ATAPI Identify Device Packet (CDROM)
if (drive->drive_type != BLOCK_CDROM) {
drive_reset(drive);
ide_raise_irq(ide, channel);
}
break;
+
case ATA_IDENTIFY: // Identify Device
if (drive->drive_type != BLOCK_DISK) {
drive_reset(drive);
}
break;
- case ATA_PACKETCMD: // ATAPI Command Packet
+ case ATA_PACKETCMD: // ATAPI Command Packet (CDROM)
if (drive->drive_type != BLOCK_CDROM) {
ide_abort_command(ide, channel);
}
}
break;
- case ATA_READDMA: // Read DMA with retry
- case ATA_READDMA_ONCE: { // Read DMA
+ case ATA_READDMA: // Read DMA with retry
+ case ATA_READDMA_ONCE: // Read DMA without retry
+ case ATA_READDMA_EXT: { // Read DMA (LBA48)
uint64_t sect_cnt;
if (ata_get_lba_and_size(ide, channel, &(drive->current_lba), §_cnt) == -1) {
return length;
}
- drive->hd_state.cur_sector_num = 1;
+ drive->hd_state.cur_sector_num = 1; // Not used for DMA
drive->transfer_length = sect_cnt * HD_SECTOR_SIZE;
drive->transfer_index = 0;
- if (channel->dma_status.active == 1) {
- // DMA Read
- if (dma_read(core, ide, channel) == -1) {
- PrintError(core->vm_info, core, "Failed DMA Read\n");
- 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);
- }
+ // Now we wait for the transfer to be intiated by flipping the
+ // bus-master start bit
break;
}
- case ATA_WRITEDMA: { // Write DMA
+ case ATA_WRITEDMA: // Write DMA with retry
+ case ATA_WRITEDMA_ONCE: // Write DMA without retry
+ case ATA_WRITEDMA_EXT: { // Write DMA (LBA48)
+
uint64_t sect_cnt;
if (ata_get_lba_and_size(ide, channel, &(drive->current_lba),§_cnt) == -1) {
return length;
}
- drive->hd_state.cur_sector_num = 1;
+ drive->hd_state.cur_sector_num = 1; // Not used for DMA
drive->transfer_length = sect_cnt * HD_SECTOR_SIZE;
drive->transfer_index = 0;
- if (channel->dma_status.active == 1) {
- // DMA Write
- if (dma_write(core, ide, channel) == -1) {
- PrintError(core->vm_info, core, "Failed DMA Write\n");
- 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);
- }
+ // Now we wait for the transfer to be intiated by flipping the
+ // bus-master start bit
break;
}
+
case ATA_STANDBYNOW1: // Standby Now 1
case ATA_IDLEIMMEDIATE: // Set Idle Immediate
case ATA_STANDBY: // Standby
}
ide->southbridge = (struct v3_southbridge *)(southbridge->private_data);
+ } else {
+ PrintError(vm,VCORE_NONE,"Strange - you don't have a PCI bus\n");
}
PrintDebug(vm, VCORE_NONE, "IDE: Creating IDE bus x 2\n");
struct pci_device * pci_dev = NULL;
int i;
- PrintDebug(vm, VCORE_NONE, "Connecting IDE to PCI bus\n");
+ V3_Print(vm, VCORE_NONE, "Connecting IDE to PCI bus\n");
for (i = 0; i < 6; i++) {
bars[i].type = PCI_BAR_NONE;