#include "ide-types.h"
#include "atapi-types.h"
+#ifndef DEBUG_IDE
+#undef PrintDebug
+#define PrintDebug(fmt, args...)
+#endif
+
#define PRI_DEFAULT_IRQ 14
#define SEC_DEFAULT_IRQ 15
#define PRI_DEFAULT_DMA_PORT 0xc000
#define SEC_DEFAULT_DMA_PORT 0xc008
-
#define DATA_BUFFER_SIZE 2048
static const char * ide_pri_port_strs[] = {"PRI_DATA", "PRI_FEATURES", "PRI_SECT_CNT", "PRI_SECT_NUM",
struct ide_hd_state {
int accessed;
+
+ /* this is the multiple sector transfer size as configured for read/write multiple sectors*/
+ uint_t mult_sector_num;
+
+ /* This is the current op sector size:
+ * for multiple sector ops this equals mult_sector_num
+ * for standard ops this equals 1
+ */
+ uint_t cur_sector_num;
};
struct ide_drive {
struct ide_internal {
struct ide_channel channels[2];
struct vm_device * pci;
+ struct vm_device * southbridge;
struct pci_device * busmaster_pci;
};
uint32_t prd_entry_addr = channel->dma_prd_addr + (sizeof(struct ide_dma_prd) * channel->dma_tbl_index);
int ret;
-
PrintDebug("PRD table address = %x\n", channel->dma_prd_addr);
ret = read_guest_pa_memory(dev->vm, prd_entry_addr, sizeof(struct ide_dma_prd), (void *)&prd_entry);
return -1;
}
- channel->status.busy = 0;
- channel->status.ready = 1;
- channel->status.data_req = 0;
- channel->status.error = 0;
- channel->status.seek_complete = 1;
+
/*
drive->irq_flags.io_dir = 1;
// set DMA status
- channel->dma_status.active = 0;
- channel->dma_status.err = 1;
- channel->dma_status.int_gen = 1;
+
+ if (prd_entry.end_of_table) {
+ channel->dma_status.active = 0;
+ channel->dma_status.err = 0;
+ channel->dma_status.int_gen = 1;
+
+ channel->status.busy = 0;
+ channel->status.ready = 1;
+ channel->status.data_req = 0;
+ channel->status.error = 0;
+ channel->status.seek_complete = 1;
+ }
ide_raise_irq(dev, channel);
return -1;
}
}
+
+ channel->dma_cmd.val &= 0x09;
}
break;
case 0x20: // Read Sectors with Retry
case 0x21: // Read Sectors without Retry
+ drive->hd_state.cur_sector_num = 1;
+
if (ata_read_sectors(dev, channel) == -1) {
PrintError("Error reading sectors\n");
return -1;
break;
case 0x24: // Read Sectors Extended
+ drive->hd_state.cur_sector_num = 1;
+
if (ata_read_sectors_ext(dev, channel) == -1) {
PrintError("Error reading extended sectors\n");
return -1;
}
break;
+
+ case 0xc8: // Read DMA with retry
+ case 0xc9: // Read DMA
+ drive->hd_state.cur_sector_num = 1;
+
+ break;
case 0xef: // 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)
ide_raise_irq(dev, channel);
break;
+ case 0x91: // Initialize Drive Parameters
+ case 0x10: // recalibrate?
+ channel->status.error = 0;
+ channel->status.ready = 1;
+ channel->status.seek_complete = 1;
+ ide_raise_irq(dev, channel);
+ break;
+ case 0xc6: { // 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
+
+ if (tmp_sect_num > MAX_MULT_SECTORS) {
+ ide_abort_command(dev, channel);
+ break;
+ }
+
+ if (drive->sector_count == 0) {
+ drive->hd_state.mult_sector_num= 1;
+ } else {
+ drive->hd_state.mult_sector_num = drive->sector_count;
+ }
+
+ channel->status.ready = 1;
+ channel->status.error = 0;
+
+ ide_raise_irq(dev, channel);
+
+ break;
+ }
+ case 0xc4: // read multiple sectors
+ drive->hd_state.cur_sector_num = drive->hd_state.mult_sector_num;
default:
PrintError("Unimplemented IDE command (%x)\n", channel->cmd_reg);
return -1;
if ((data_offset == 0) && (drive->transfer_index > 0)) {
drive->current_lba++;
- if (ata_read(dev, channel) == -1) {
+ if (ata_read(dev, channel, drive->data_buf, 1) == -1) {
PrintError("Could not read next disk sector\n");
return -1;
}
drive->transfer_index += length;
- if ((drive->transfer_index % IDE_SECTOR_SIZE) == 0) {
+
+ /* This is the trigger for interrupt injection.
+ * For read 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 % (IDE_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("Integral Complete, still transferring more sectors\n");
static int pci_config_update(struct pci_device * pci_dev, uint_t reg_num, int length) {
- PrintDebug("Interupt register (Dev=%s), irq=%d\n", pci_dev->name, pci_dev->config_header.intr_line);
+ PrintDebug("PCI Config Update\n");
+ PrintDebug("\t\tInterupt register (Dev=%s), irq=%d\n", pci_dev->name, pci_dev->config_header.intr_line);
return 0;
}
static int init_ide_state(struct vm_device * dev) {
struct ide_internal * ide = (struct ide_internal *)(dev->private_data);
- struct v3_pci_bar bars[6];
- struct pci_device * pci_dev = NULL;
int i, j;
- for (i = 0; i < 2; i++) {
+
+ /*
+ Check if the PIIX 3 actually represents both IDE channels in a single PCI entry */
+
+ for (i = 0; i < 1; i++) {
init_channel(&(ide->channels[i]));
// JRL: this is a terrible hack...
ide->channels[i].irq = PRI_DEFAULT_IRQ + i;
- for (j = 0; j < 6; j++) {
- bars[j].type = PCI_BAR_NONE;
- }
+ if (ide->pci) {
+ struct v3_pci_bar bars[6];
+ struct pci_device * pci_dev = NULL;
- bars[4].type = PCI_BAR_IO;
- bars[4].default_base_port = PRI_DEFAULT_DMA_PORT + (i * 0x8);
- bars[4].num_ports = 8;
-
- if (i == 0) {
- bars[4].io_read = read_pri_dma_port;
- bars[4].io_write = write_pri_dma_port;
- } else {
- bars[4].io_read = read_sec_dma_port;
- bars[4].io_write = write_sec_dma_port;
- }
-
- pci_dev = v3_pci_register_device(ide->pci, PCI_STD_DEVICE, 0, "V3_IDE", -1, bars,
- pci_config_update, NULL, NULL, dev);
+ for (j = 0; j < 6; j++) {
+ bars[j].type = PCI_BAR_NONE;
+ }
- if (pci_dev == NULL) {
- PrintError("Failed to register IDE BUS %d with PCI\n", i);
- return -1;
- }
- ide->channels[i].pci_dev = pci_dev;
+ bars[4].type = PCI_BAR_IO;
+ bars[4].default_base_port = PRI_DEFAULT_DMA_PORT + (i * 0x8);
+ bars[4].num_ports = 8;
+
+ if (i == 0) {
+ bars[4].io_read = read_pri_dma_port;
+ bars[4].io_write = write_pri_dma_port;
+ } else {
+ bars[4].io_read = read_sec_dma_port;
+ bars[4].io_write = write_sec_dma_port;
+ }
- pci_dev->config_header.vendor_id = 0x1095;
- pci_dev->config_header.device_id = 0x0646;
- pci_dev->config_header.revision = 0x8f07;
- pci_dev->config_header.subclass = 0x01;
- pci_dev->config_header.class = 0x01;
+ pci_dev = v3_pci_register_device(ide->pci, PCI_STD_DEVICE, 0, "V3_IDE", -1, bars,
+ pci_config_update, NULL, NULL, dev);
- pci_dev->config_header.intr_line = PRI_DEFAULT_IRQ + i;
- pci_dev->config_header.intr_pin = 1;
- }
+ if (pci_dev == NULL) {
+ PrintError("Failed to register IDE BUS %d with PCI\n", i);
+ return -1;
+ }
+ ide->channels[i].pci_dev = pci_dev;
+ /* This is for CMD646 devices
+ pci_dev->config_header.vendor_id = 0x1095;
+ pci_dev->config_header.device_id = 0x0646;
+ pci_dev->config_header.revision = 0x8f07;
+ */
+ pci_dev->config_header.vendor_id = 0x8086;
+ pci_dev->config_header.device_id = 0x7010;
+ pci_dev->config_header.revision = 0x8000;
+
+ pci_dev->config_header.subclass = 0x01;
+ pci_dev->config_header.class = 0x01;
+
+
+ pci_dev->config_header.command = 0;
+ pci_dev->config_header.status = 0x0280;
+
+ // pci_dev->config_header.intr_line = PRI_DEFAULT_IRQ + i;
+ // pci_dev->config_header.intr_pin = 1;
+ }
+
- /* Register PIIX3 Busmaster PCI device */
- for (j = 0; j < 6; j++) {
- bars[j].type = PCI_BAR_NONE;
}
- pci_dev = v3_pci_register_device(ide->pci, PCI_STD_DEVICE, 0, "PIIX3 IDE", -1, bars,
- NULL, NULL, NULL, dev);
-
-
- ide->busmaster_pci = pci_dev;
-
- pci_dev->config_header.vendor_id = 0x8086;
- pci_dev->config_header.device_id = 0x7010;
- pci_dev->config_header.revision = 0x80;
- pci_dev->config_header.subclass = 0x01;
- pci_dev->config_header.class = 0x01;
-
-
return 0;
}
};
-struct vm_device * v3_create_ide(struct vm_device * pci) {
+struct vm_device * v3_create_ide(struct vm_device * pci, struct vm_device * southbridge) {
struct ide_internal * ide = (struct ide_internal *)V3_Malloc(sizeof(struct ide_internal));
struct vm_device * device = v3_create_device("IDE", &dev_ops, ide);
ide->pci = pci;
+ ide->southbridge = southbridge;
PrintDebug("IDE: Creating IDE bus x 2\n");
drive->drive_type = IDE_DISK;
drive->hd_state.accessed = 0;
+ drive->hd_state.mult_sector_num = 1;
drive->hd_ops = ops;