* redistribute, and modify it as specified in the file "V3VEE_LICENSE".
*/
+#ifndef _DEVICES_ATA_H_
+#define _DEVICES_ATA_H_
+
#define MAX_MULT_SECTORS 255
+
+
+
static void ata_identify_device(struct ide_drive * drive) {
struct ide_drive_id * drive_id = (struct ide_drive_id *)(drive->data_buf);
const char* serial_number = " VT00001\0\0\0\0\0\0\0\0\0\0\0\0";
drive_id->cdrom_flag = 0;
- // Make it the simplest drive possible (1 head, 1 cyl, 1 sect/track)
- drive_id->num_cylinders = 1;
- drive_id->num_heads = 1;
- drive_id->bytes_per_track = IDE_SECTOR_SIZE;
- drive_id->bytes_per_sector = IDE_SECTOR_SIZE;
- drive_id->sectors_per_track = 1;
+ 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;
+ drive_id->bytes_per_sector = HD_SECTOR_SIZE;
+ drive_id->sectors_per_track = drive->num_sectors;
// These buffers do not contain a terminating "\0"
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
- drive_id->dma_enable = 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 Capacity (28 bit LBA)
- drive_id->lba_capacity = drive->hd_ops->get_capacity(drive->private_data);
+ drive_id->lba_capacity = drive->ops->get_capacity(drive->private_data) / HD_SECTOR_SIZE;
// Drive Capacity (48 bit LBA)
- drive_id->lba_capacity_2 = drive->hd_ops->get_capacity(drive->private_data);
+ drive_id->lba_capacity_2 = drive->ops->get_capacity(drive->private_data) / HD_SECTOR_SIZE;
// lower byte is the maximum multiple sector size...
drive_id->rw_multiples = 0x8000 | MAX_MULT_SECTORS;
-
// words 64-70, 54-58 valid
drive_id->field_valid = 0x0007; // DMA + pkg cmd valid
// 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
+ // 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[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
+ // 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
drive_id->buf[83] |= 0x0400; // supports 48 bit LBA
+ // No special features supported
+ // Pretend drive is already autoconfed to UDMA5
drive_id->dma_ultra = 0x2020; // Ultra_DMA_Mode_5_Selected | Ultra_DMA_Mode_5_Supported;
}
-static int ata_read(struct vm_device * dev, struct ide_channel * channel, uint8_t * dst, uint_t sect_cnt) {
+static int ata_read(struct ide_internal * ide, struct ide_channel * channel, uint8_t * dst, uint_t sect_cnt) {
struct ide_drive * drive = get_selected_drive(channel);
if (drive->hd_state.accessed == 0) {
drive->hd_state.accessed = 1;
}
- int ret = drive->hd_ops->read(dst, sect_cnt, drive->current_lba, drive->private_data);
+ PrintDebug(VM_NONE, VCORE_NONE,"Reading Drive LBA=%d (count=%d)\n", (uint32_t)(drive->current_lba), sect_cnt);
+
+ int ret = drive->ops->read(dst, drive->current_lba * HD_SECTOR_SIZE, sect_cnt * HD_SECTOR_SIZE, drive->private_data);
if (ret == -1) {
- PrintError("IDE: Error reading HD block (LBA=%p)\n", (void *)(addr_t)(drive->current_lba));
+ PrintError(VM_NONE, VCORE_NONE,"IDE: Error reading HD block (LBA=%p)\n", (void *)(addr_t)(drive->current_lba));
return -1;
}
-// 28 bit LBA
-static int ata_read_sectors(struct vm_device * dev, struct ide_channel * channel) {
+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);
- // 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 * IDE_SECTOR_SIZE) >
- 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,
- lba_addr.addr + (sect_cnt * IDE_SECTOR_SIZE),
- (void *)(addr_t)(drive->hd_ops->get_capacity(drive->private_data)));
- ide_abort_command(dev, channel);
- return 0;
+
+ if (drive->hd_state.accessed == 0) {
+ PrintError(VM_NONE,VCORE_NONE,"Reseting lba...\n");
+ drive->current_lba = 0;
+ drive->hd_state.accessed = 1;
}
- drive->current_lba = lba_addr.addr;
-
- if (ata_read(dev, channel, drive->data_buf, 1) == -1) {
- PrintError("Could not read disk sector\n");
+ 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);
+
+ if (ret == -1) {
+ PrintError(VM_NONE, VCORE_NONE,"IDE: Error writing HD block (LBA=%p)\n", (void *)(addr_t)(drive->current_lba));
return -1;
}
- drive->transfer_length = sect_cnt * IDE_SECTOR_SIZE;
- drive->transfer_index = 0;
+ return 0;
+}
+
+
+//
+// Grand unified conversion for various addressing modes:
+//
+// CHS => 64 bit LBA (8 bit sector count)
+// LBA28 => 64 bit LBA (8 bit sector count)
+// LBA48 => 64 bit LBA (16 bit sector count)
+static int ata_get_lba_and_size(struct ide_internal * ide, struct ide_channel * channel, uint64_t * lba, uint64_t *num_sects) {
+ struct ide_drive * drive = get_selected_drive(channel);
+
+ if (is_lba48(channel)) {
+ *num_sects = drive->lba48.sector_count;
+ *lba = drive->lba48.lba;
+ PrintDebug(VM_NONE,VCORE_NONE,"get_lba: lba48: lba=%llu, num_sects=%llu\n",*lba,*num_sects);
+ } else {
+ // LBA48 or CHS
+ // The if the sector count == 0 then read 256 sectors (cast up to handle that value)
+ *num_sects = (drive->sector_count == 0) ? 256 : drive->sector_count;
+
+ if (is_lba28(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: lba28: lba0=%u (sect), lba1=%u (cyllow), lba2=%u (cylhigh), lba3=%d (head) => lba=%llu numsects=%llu\n", drive->lba0, drive->lba1, drive->lba2, channel->drive_head.lba3, *lba, *num_sects);
+
+ } else {
+ // we are in CHS mode....
+
+ *lba =
+ ((uint64_t)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 lba=%llu num_sects=%llu....\n",
+ drive->cylinder, channel->drive_head.head_num, drive->sector_num,
+ drive->num_cylinders, drive->num_heads, drive->num_sectors, *lba,*num_sects );
+ }
+ }
+
+ if ((*lba + *num_sects) >
+ drive->ops->get_capacity(drive->private_data) / HD_SECTOR_SIZE) {
+ PrintError(VM_NONE, VCORE_NONE,"IDE: request size exceeds disk capacity (lba=%llu) (num_sects=%llu) (ReadEnd=%llu) (capacity=%llu)\n",
+ *lba, *num_sects,
+ *lba + (*num_sects * HD_SECTOR_SIZE),
+ drive->ops->get_capacity(drive->private_data));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int ata_write_sectors(struct ide_internal * ide, struct ide_channel * channel) {
+ uint64_t sect_cnt;
+ struct ide_drive * drive = get_selected_drive(channel);
+ if (ata_get_lba_and_size(ide, channel, &(drive->current_lba), §_cnt ) == -1) {
+ PrintError(VM_NONE,VCORE_NONE,"Cannot get lba+size\n");
+ ide_abort_command(ide, channel);
+ return 0;
+ }
+
+ PrintDebug(VM_NONE,VCORE_NONE,"ata write sectors: lba=%llu sect_cnt=%llu\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;
channel->status.write_fault = 0;
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;
+}
- ide_raise_irq(dev, channel);
- PrintDebug("Returning from read sectors\n");
+// 28 bit LBA or CHS
+static int ata_read_sectors(struct ide_internal * ide, struct ide_channel * channel) {
+ uint64_t sect_cnt;
+ struct ide_drive * drive = get_selected_drive(channel);
- return 0;
-}
+ if (ata_get_lba_and_size(ide, channel, &(drive->current_lba),§_cnt) == -1) {
+ PrintError(VM_NONE,VCORE_NONE,"Cannot get lba+size\n");
+ ide_abort_command(ide, channel);
+ return 0;
+ }
+
+ if (ata_read(ide, channel, drive->data_buf, 1) == -1) {
+ PrintError(VM_NONE, VCORE_NONE,"Could not read disk sector\n");
+ return -1;
+ }
+
+ drive->transfer_length = sect_cnt * HD_SECTOR_SIZE;
+ drive->transfer_index = 0;
+
+ channel->status.busy = 0;
+ channel->status.ready = 0;
+ channel->status.write_fault = 0;
+ channel->status.data_req = 1;
+ channel->status.error = 0;
-// 48 bit LBA
-static int ata_read_sectors_ext(struct vm_device * dev, struct ide_channel * channel) {
- //struct ide_drive * drive = get_selected_drive(channel);
- // The if the sector count == 0 then read 256 sectors (cast up to handle that value)
- //uint32_t sector_count = (drive->sector_count == 0) ? 256 : drive->sector_count;
+ ide_raise_irq(ide, channel);
- PrintError("Extended Sector read not implemented\n");
+ PrintDebug(VM_NONE, VCORE_NONE,"Returning from read sectors\n");
- return -1;
+ return 0;
}
+
+
+/* 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