From: Jack Lange Date: Fri, 10 Apr 2009 07:06:32 +0000 (-0500) Subject: added DMA write functionality to ide harddisk X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?p=palacios.git;a=commitdiff_plain;h=326a9425e1dc7a9f7afafe2951b55f35d9ff2422 added DMA write functionality to ide harddisk --- diff --git a/palacios/include/devices/ide.h b/palacios/include/devices/ide.h index 8a68222..d299827 100644 --- a/palacios/include/devices/ide.h +++ b/palacios/include/devices/ide.h @@ -33,7 +33,6 @@ struct v3_ide_cd_ops { uint32_t (*get_capacity)(void * private_data); // Reads always operate on 2048 byte blocks int (*read)(uint8_t * buf, int block_count, uint64_t lba, void * private_data); - }; @@ -41,7 +40,7 @@ struct v3_ide_hd_ops { uint64_t (*get_capacity)(void * private_data); // Reads always operate on 2048 byte blocks int (*read)(uint8_t * buf, int sector_count, uint64_t lba, void * private_data); - + int (*write)(uint8_t * buf, int sector_count, uint64_t lba, void * private_data); }; diff --git a/palacios/src/devices/ata.h b/palacios/src/devices/ata.h index 721f2f4..0bb0022 100644 --- a/palacios/src/devices/ata.h +++ b/palacios/src/devices/ata.h @@ -127,6 +127,21 @@ static int ata_read(struct vm_device * dev, struct ide_channel * channel, uint8_ } +static int ata_write(struct vm_device * dev, struct ide_channel * channel, uint8_t * src, uint_t sect_cnt) { + struct ide_drive * drive = get_selected_drive(channel); + + PrintDebug("Writing Drive LBA=%d (count=%d)\n", (uint32_t)(drive->current_lba), sect_cnt); + + int ret = drive->hd_ops->write(src, sect_cnt, drive->current_lba, drive->private_data); + + if (ret == -1) { + PrintError("IDE: Error writing HD block (LBA=%p)\n", (void *)(addr_t)(drive->current_lba)); + return -1; + } + + return 0; +} + static int ata_get_lba(struct vm_device * dev, struct ide_channel * channel, uint64_t * lba) { @@ -152,7 +167,7 @@ static int ata_get_lba(struct vm_device * dev, struct ide_channel * channel, uin lba_addr.buf[3] = channel->drive_head.lba3; - if (lba_addr.addr + (sect_cnt * IDE_SECTOR_SIZE) > + if ((lba_addr.addr + sect_cnt) > drive->hd_ops->get_capacity(drive->private_data)) { PrintError("IDE: request size exceeds disk capacity (lba=%d) (sect_cnt=%d) (ReadEnd=%d) (capacity=%p)\n", lba_addr.addr, sect_cnt, diff --git a/palacios/src/devices/ide.c b/palacios/src/devices/ide.c index 86d2b73..361bed0 100644 --- a/palacios/src/devices/ide.c +++ b/palacios/src/devices/ide.c @@ -355,7 +355,7 @@ static void ide_abort_command(struct vm_device * dev, struct ide_channel * chann static int dma_read(struct vm_device * dev, struct ide_channel * channel); - +static int dma_write(struct vm_device * dev, struct ide_channel * channel); /* ATAPI functions */ @@ -381,8 +381,7 @@ static int dma_read(struct vm_device * dev, struct ide_channel * channel) { // Loop through the disk data while (bytes_left > 0) { - - uint32_t prd_entry_addr = channel->dma_prd_addr + (sizeof(struct ide_dma_prd) * channel->dma_tbl_index); + uint32_t prd_entry_addr = channel->dma_prd_addr + (sizeof(struct ide_dma_prd) * channel->dma_tbl_index); uint_t prd_bytes_left = 0; uint_t prd_offset = 0; int ret; @@ -396,7 +395,8 @@ static int dma_read(struct vm_device * dev, struct ide_channel * channel) { return -1; } - PrintDebug("PRD Addr: %x, PDR Len: %d, EOT: %d\n", prd_entry.base_addr, prd_entry.size, prd_entry.end_of_table); + PrintDebug("PRD Addr: %x, PRD Len: %d, EOT: %d\n", + prd_entry.base_addr, prd_entry.size, prd_entry.end_of_table); // loop through the PRD data.... @@ -463,7 +463,6 @@ static int dma_read(struct vm_device * dev, struct ide_channel * channel) { PrintError("DMA table not large enough for data transfer...\n"); return -1; } - } /* @@ -495,9 +494,92 @@ static int dma_read(struct vm_device * dev, struct ide_channel * channel) { static int dma_write(struct vm_device * dev, struct ide_channel * channel) { - // unsupported - PrintError("DMA writes currently not supported\n"); - return -1; + struct ide_drive * drive = get_selected_drive(channel); + // This is at top level scope to do the EOT test at the end + struct ide_dma_prd prd_entry; + uint_t bytes_left = drive->transfer_length; + + + PrintDebug("DMA write from %d bytes\n", bytes_left); + + // Loop through disk data + while (bytes_left > 0) { + uint32_t prd_entry_addr = channel->dma_prd_addr + (sizeof(struct ide_dma_prd) * channel->dma_tbl_index); + uint_t prd_bytes_left = 0; + uint_t prd_offset = 0; + 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); + + if (ret != sizeof(struct ide_dma_prd)) { + PrintError("Could not read PRD\n"); + return -1; + } + + PrintDebug("PRD Addr: %x, PRD Len: %d, EOT: %d\n", + prd_entry.base_addr, prd_entry.size, prd_entry.end_of_table); + + prd_bytes_left = prd_entry.size; + + while (prd_bytes_left > 0) { + uint_t bytes_to_write = 0; + + + bytes_to_write = (prd_bytes_left > IDE_SECTOR_SIZE) ? IDE_SECTOR_SIZE : prd_bytes_left; + + + ret = read_guest_pa_memory(dev->vm, prd_entry.base_addr + prd_offset, bytes_to_write, drive->data_buf); + + if (ret != bytes_to_write) { + PrintError("Faild to copy data from guest memory... (ret=%d)\n", ret); + return -1; + } + + PrintDebug("\t DMA ret=%d (prd_bytes_left=%d) (bytes_left=%d)\n", ret, prd_bytes_left, bytes_left); + + + if (ata_write(dev, channel, drive->data_buf, 1) == -1) { + PrintError("Failed to write data to disk\n"); + return -1; + } + + drive->current_lba++; + + drive->transfer_index += ret; + prd_bytes_left -= ret; + prd_offset += ret; + bytes_left -= ret; + } + + channel->dma_tbl_index++; + + if (drive->transfer_index % IDE_SECTOR_SIZE) { + PrintError("We currently don't handle sectors that span PRD descriptors\n"); + return -1; + } + + if ((prd_entry.end_of_table == 1) && (bytes_left > 0)) { + PrintError("DMA table not large enough for data transfer...\n"); + return -1; + } + } + + if (prd_entry.end_of_table) { + channel->status.busy = 0; + channel->status.ready = 1; + channel->status.data_req = 0; + channel->status.error = 0; + channel->status.seek_complete = 1; + + channel->dma_status.active = 0; + channel->dma_status.err = 0; + } + + ide_raise_irq(dev, channel); + + return 0; } @@ -755,7 +837,28 @@ static int write_cmd_port(ushort_t port, void * src, uint_t length, struct vm_de break; } + case 0xca: { // Write DMA + uint32_t sect_cnt = (drive->sector_count == 0) ? 256 : drive->sector_count; + + if (ata_get_lba(dev, channel, &(drive->current_lba)) == -1) { + ide_abort_command(dev, channel); + return 0; + } + drive->hd_state.cur_sector_num = 1; + + drive->transfer_length = sect_cnt * IDE_SECTOR_SIZE; + drive->transfer_index = 0; + + if (channel->dma_status.active == 1) { + // DMA Write + if (dma_write(dev, channel) == -1) { + PrintError("Failed DMA Write\n"); + return -1; + } + } + break; + } case 0xe0: // Standby Now 1 case 0xe1: // Set Idle Immediate case 0xe2: // Standby @@ -1531,7 +1634,7 @@ int v3_ide_register_harddisk(struct vm_device * ide_dev, /* this is something of a hack... */ drive->num_sectors = 63; drive->num_heads = 16; - drive->num_cylinders = (ops->get_capacity(private_data) / 512) / (drive->num_sectors * drive->num_heads); + drive->num_cylinders = ops->get_capacity(private_data) / (drive->num_sectors * drive->num_heads); if (ide->ide_pci) { // Hardcode this for now, but its not a good idea.... diff --git a/palacios/src/devices/ram_hd.c b/palacios/src/devices/ram_hd.c index 1094a31..3bce4fc 100644 --- a/palacios/src/devices/ram_hd.c +++ b/palacios/src/devices/ram_hd.c @@ -39,7 +39,7 @@ struct hd_state { }; -// HDs always read 2048 byte blocks... ? +// HDs always read 512 byte blocks... ? static int hd_read(uint8_t * buf, int sector_count, uint64_t lba, void * private_data) { struct vm_device * hd_dev = (struct vm_device *)private_data; struct hd_state * hd = (struct hd_state *)(hd_dev->private_data); @@ -50,7 +50,17 @@ static int hd_read(uint8_t * buf, int sector_count, uint64_t lba, void * privat memcpy(buf, (uint8_t *)(hd->disk_image + offset), length); + return 0; +} + + +static int hd_write(uint8_t * buf, int sector_count, uint64_t lba, void * private_data) { + struct vm_device * hd_dev = (struct vm_device *)private_data; + struct hd_state * hd = (struct hd_state *)(hd_dev->private_data); + int offset = lba * IDE_SECTOR_SIZE; + int length = sector_count * IDE_SECTOR_SIZE; + memcpy((uint8_t *)(hd->disk_image + offset), buf, length); return 0; } @@ -66,6 +76,7 @@ static uint64_t hd_get_capacity(void * private_data) { static struct v3_ide_hd_ops hd_ops = { .read = hd_read, + .write = hd_write, .get_capacity = hd_get_capacity, };