From: Jack Lange Date: Tue, 24 Mar 2009 23:34:16 +0000 (-0500) Subject: updates to ATAPI packet command support X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?p=palacios.git;a=commitdiff_plain;h=f01ef51a3da4c56e7e9019714d489cad2fa52d46 updates to ATAPI packet command support --- diff --git a/palacios/src/devices/apic.c b/palacios/src/devices/apic.c index a26f231..13ead3c 100644 --- a/palacios/src/devices/apic.c +++ b/palacios/src/devices/apic.c @@ -288,8 +288,6 @@ static int get_highest_isr(struct apic_state * apic) { for (j = 7; j >= 0; j--) { uchar_t flag = 0x1 << j; if ((*svc_major) & flag) { - - return ((i * 8) + j); } } @@ -312,8 +310,6 @@ static int get_highest_irr(struct apic_state * apic) { for (j = 7; j >= 0; j--) { uchar_t flag = 0x1 << j; if ((*req_major) & flag) { - - return ((i * 8) + j); } } diff --git a/palacios/src/devices/atapi-types.h b/palacios/src/devices/atapi-types.h index 1f4df14..849a151 100644 --- a/palacios/src/devices/atapi-types.h +++ b/palacios/src/devices/atapi-types.h @@ -39,6 +39,14 @@ typedef enum { ASC_LOG_BLK_OOR = 0x21 /* LOGICAL BLOCK OUT OF RANGE */ } atapi_add_sense_code_t ; +struct atapi_irq_flags { + uint8_t c_d : 1; + uint8_t io_dir : 1; + uint8_t rel : 1; + uint8_t tag : 5; +} __attribute__((packed)); + + struct atapi_sense_data { union { @@ -75,6 +83,184 @@ struct atapi_read10_cmd { } __attribute__((packed)); +struct atapi_mode_sense_cmd { + uint8_t atapi_cmd; // 0x5a + uint8_t rsvd1 : 3; + uint8_t disable_blk_desc : 1; + uint8_t long_lba_acc : 1; + uint8_t lun : 3; + uint8_t page_code : 6; + uint8_t page_ctrl : 2; + uint8_t sub_page_code; + uint8_t rsvd2[3]; + uint16_t alloc_len; + uint8_t link : 1; + uint8_t flag : 1; + uint8_t naca : 1; + uint8_t rsvd3 : 3; + uint8_t vendor_specific : 2; +} __attribute__((packed)); + +struct atapi_mode_sense_hdr { + uint16_t mode_data_len; + uint8_t media_type_code; + uint8_t rsvd[2]; + uint16_t blk_desc_len; +} __attribute__((packed)); + + +struct atapi_rd_capacity_cmd { + uint8_t atapi_cmd; // 0x25 + uint8_t obsolete : 1; + uint8_t rsvd1 : 4; + uint8_t lun : 3; + uint32_t lba; + uint16_t rsvd2; + uint8_t pmi; + uint8_t rsvd3 : 7; + uint8_t link : 1; + uint8_t flag : 1; + uint8_t naca : 1; + uint8_t rsvd4 : 3; + uint8_t vendor_spec : 2; +} __attribute__((packed)); + + +struct atapi_rd_capacity_resp { + uint32_t lba; + uint32_t block_len; +} __attribute__((packed)); + + +struct atapi_rd_toc_cmd { + uint8_t atapi_cmd; // 0x43 + uint8_t rsvd1 : 1; + uint8_t msf : 1; + uint8_t rsvd2 : 3; + uint8_t lun : 3; + uint8_t format : 4; + uint8_t rsvd3 : 4; + uint8_t rsvd4[3]; + uint8_t track_num; + uint16_t alloc_len; + uint8_t link : 1; + uint8_t flag : 1; + uint8_t naca : 1; + uint8_t rsvd5 : 3; + uint8_t vendor_specific : 2; +} __attribute__((packed)); + +struct atapi_rd_toc_resp { + uint16_t data_len; + uint8_t first_track_num; + uint8_t last_track_num; + + struct { + uint8_t rsvd; + uint8_t ctrl : 4; + uint8_t adr : 4; + uint8_t track_num; + uint8_t rsvd2; + uint32_t start_addr; + } track_descs[0] __attribute__((packed)) ; + +} __attribute__((packed)); + + +struct atapi_error_recovery { + uint8_t page_code : 6; + uint8_t rsvd : 1; + uint8_t page_ctrl : 1; + uint8_t page_len; + uint8_t dcr : 1; + uint8_t dte : 1; + uint8_t per : 1; + uint8_t rsvd1 : 1; + uint8_t rc : 1; + uint8_t tb : 1; + uint8_t arre : 1; + uint8_t awre : 1; + uint8_t rd_retry_cnt; + uint8_t correct_spin; + uint8_t head_offset; + uint8_t data_strobe_offset; + uint8_t emcdr : 2; + uint8_t rsvd2 : 6; + uint8_t wr_retry_cnt; + uint8_t rsvd3; + uint16_t recovery_time_limit; +} __attribute__((packed)); + + +struct atapi_cdrom_caps { + uint8_t page_code : 6; + uint8_t rsvd : 1; + uint8_t page_ctrl : 1; + uint8_t page_len; + uint8_t cdr_rd : 1; + uint8_t cdrw_rd : 1; + uint8_t mthd_2 : 1; + uint8_t dvdrom_rd : 1; + uint8_t dvdr_rd : 1; + uint8_t dvdram_rd : 1; + uint8_t rsvd1 : 2; + uint8_t cdr_wr : 1; + uint8_t cdrw_wr : 1; + uint8_t tst_wr : 1; + uint8_t rsvd2 : 1; + uint8_t dvdr_wr : 1; + uint8_t dvdram_wr : 1; + uint8_t rsvd3 : 2; + uint8_t audio_play : 1; + uint8_t composite : 1; + uint8_t digi_port1 : 1; + uint8_t digi_port2 : 1; + uint8_t mode2_form1 : 1; + uint8_t mode2_form2 : 1; + uint8_t multisession : 1; + uint8_t BUF : 1; + uint8_t cd_da : 1; + uint8_t cdda_str_acc : 1; + uint8_t rw_supported : 1; + uint8_t rw_dc : 1; + uint8_t c2_ptrs_supp : 1; + uint8_t isrc : 1; + uint8_t upc : 1; + uint8_t rd_bar_cd_cap : 1; + uint8_t lock : 1; + uint8_t lock_state : 1; + uint8_t prevent_jmpr : 1; + uint8_t eject : 1; + uint8_t rsvd4 : 1; + uint8_t lmt : 3; + uint8_t sep_vol : 1; + uint8_t sep_chnl_mute : 1; + uint8_t sdp : 1; + uint8_t sss : 1; + uint8_t side_chg_cap : 1; + uint8_t rw_in_lead_rd : 1; + uint8_t rsvd5 : 2; + uint16_t obsolete1; + uint16_t num_vols_supp; + uint16_t lun_buf_size; // in KBytes + uint16_t obsolete2; + uint8_t obsolete3; + uint8_t rsvd6 : 1; + uint8_t bckf : 1; + uint8_t rck : 1; + uint8_t lsbf : 1; + uint8_t len : 2; + uint8_t rsvd7 : 2; + uint16_t obsolete4[2]; + uint16_t cp_mgmnt_rev_supp; + uint8_t rsvd8; + uint8_t rot_ctrl_sel : 2; + uint8_t rsvd9 : 6; + uint16_t cur_wr_spd; // KBytes/sec + uint16_t num_lun_wr_spd_dsc_tbls; + // lun write speed descriptor tables +} __attribute__((packed)); + #endif #endif diff --git a/palacios/src/devices/atapi.h b/palacios/src/devices/atapi.h index e10d4e8..30d829f 100644 --- a/palacios/src/devices/atapi.h +++ b/palacios/src/devices/atapi.h @@ -18,9 +18,69 @@ */ #define ATAPI_PACKET_SIZE 12 +#define ATAPI_BLOCK_SIZE 2048 #include "atapi-types.h" + +/* ATAPI sucks... + * The OS will write to the cylinder register the number of bytes it wants to read + * however the device can change that value + * + */ +static int atapi_update_req_len(struct vm_device * dev, struct ide_channel * channel, uint_t xfer_len) { + struct ide_drive * drive = get_selected_drive(channel); + + // PrintDebug("Updating request length (pre=%d)\n", drive->req_len); + + if (drive->req_len == 0) { + PrintError("ATAPI Error: request of length 0\n"); + return -1; + } + + + channel->status.busy = 0; + channel->status.data_req = 1; + channel->status.error = 0; + + drive->irq_flags.io_dir = 1; + drive->irq_flags.c_d = 0; + + // make count even + if (drive->req_len % 2) { + drive->req_len -= 1; + } + + // if the device can't return as much as the OS requested + if (drive->req_len > xfer_len) { + drive->req_len = xfer_len; + } + + // PrintDebug("Updating request length (post=%d)\n", drive->req_len); + + return 0; +} + + + +// This is for simple commands that don't need to sanity check the req_len +static void atapi_setup_cmd_resp(struct vm_device * dev, struct ide_channel * channel, uint_t xfer_len) { + struct ide_drive * drive = get_selected_drive(channel); + + drive->transfer_length = xfer_len; + drive->transfer_index = 0; + drive->req_len = drive->transfer_length; + + drive->irq_flags.io_dir = 1; + drive->irq_flags.c_d = 0; + + channel->status.busy = 0; + channel->status.data_req = 1; + channel->status.error = 0; + + ide_raise_irq(dev, channel); +} + static void atapi_cmd_error(struct vm_device * dev, struct ide_channel * channel, atapi_sense_key_t sense_key, atapi_add_sense_code_t asc) { struct ide_drive * drive = get_selected_drive(channel); @@ -40,16 +100,27 @@ static void atapi_cmd_error(struct vm_device * dev, struct ide_channel * channel drive->cd_state.sense.sense_key = sense_key; drive->cd_state.sense.asc = asc; + + drive->irq_flags.io_dir = 1; + drive->irq_flags.c_d = 1; + drive->irq_flags.rel = 0; + ide_raise_irq(dev, channel); } static void atapi_cmd_nop(struct vm_device * dev, struct ide_channel * channel) { + struct ide_drive * drive = get_selected_drive(channel); + channel->status.busy = 0; channel->status.ready = 1; channel->status.data_req = 0; channel->status.error = 0; + drive->irq_flags.io_dir = 1; + drive->irq_flags.c_d = 1; + drive->irq_flags.rel = 0; + ide_raise_irq(dev, channel); } @@ -68,6 +139,28 @@ static int atapi_read_chunk(struct vm_device * dev, struct ide_channel * channel return 0; } + +static int atapi_update_data_buf(struct vm_device * dev, struct ide_channel * channel) { + struct ide_drive * drive = get_selected_drive(channel); + + switch (drive->cd_state.atapi_cmd) { + case 0x28: // read(10) + case 0xa8: // read(12) + + // Update lba address to point to next block + drive->cd_state.current_lba++; + + // read the next block + return atapi_read_chunk(dev, channel); + + default: + PrintError("Unhandled ATAPI command in update buffer %x\n", drive->cd_state.atapi_cmd); + return -1; + } + + return 0; +} + static int atapi_read10(struct vm_device * dev, struct ide_channel * channel) { struct ide_drive * drive = get_selected_drive(channel); struct atapi_read10_cmd * cmd = (struct atapi_read10_cmd *)(drive->data_buf); @@ -82,25 +175,33 @@ static int atapi_read10(struct vm_device * dev, struct ide_channel * channel) { atapi_cmd_nop(dev, channel); return 0; } - + if ((lba + xfer_len) > drive->cd_ops->get_capacity(drive->private_data)) { atapi_cmd_error(dev, channel, ATAPI_SEN_ILL_REQ, ASC_LOG_BLK_OOR); - } + ide_raise_irq(dev, channel); + return 0;} + + // PrintDebug("Reading %d blocks from LBA 0x%x\n", xfer_len, lba); drive->cd_state.current_lba = lba; + // Update the request length value in the cylinder registers if (atapi_read_chunk(dev, channel) == -1) { PrintError("IDE: Could not read initial chunk from CD\n"); - return -1; + return -1; } - drive->transfer_length = xfer_len; + drive->transfer_length = xfer_len * ATAPI_BLOCK_SIZE; drive->transfer_index = 0; - channel->status.busy = 0; - channel->status.data_req = 1; - channel->status.error = 0; + // Length of ATAPI buffer sits in cylinder registers + // This is weird... The host sets this value to say what it would like to transfer, + // if it is larger than the correct size, the device shrinks it to the correct size + if (atapi_update_req_len(dev, channel, ATAPI_BLOCK_SIZE) == -1) { + PrintError("Could not update initial request length\n"); + return -1; + } ide_raise_irq(dev, channel); @@ -112,20 +213,153 @@ static int atapi_read10(struct vm_device * dev, struct ide_channel * channel) { static void atapi_req_sense(struct vm_device * dev, struct ide_channel * channel) { struct ide_drive * drive = get_selected_drive(channel); - drive->transfer_length = 18; - drive->transfer_index = 0; memcpy(drive->data_buf, drive->cd_state.sense.buf, sizeof(drive->cd_state.sense.buf)); + atapi_setup_cmd_resp(dev, channel, 18); +} + + + +static int atapi_get_capacity(struct vm_device * dev, struct ide_channel * channel) { + struct ide_drive * drive = get_selected_drive(channel); + struct atapi_rd_capacity_resp * resp = (struct atapi_rd_capacity_resp *)(drive->data_buf); + uint32_t capacity = drive->cd_ops->get_capacity(drive->private_data); + + resp->lba = le_to_be_32(capacity); + resp->block_len = le_to_be_32(ATAPI_BLOCK_SIZE); + + atapi_setup_cmd_resp(dev, channel, sizeof(struct atapi_rd_capacity_resp)); + + return 0; +} + + +static int atapi_read_toc(struct vm_device * dev, struct ide_channel * channel) { + struct ide_drive * drive = get_selected_drive(channel); + struct atapi_rd_toc_cmd * cmd = (struct atapi_rd_toc_cmd *)(drive->data_buf); + uint16_t alloc_len = be_to_le_16(cmd->alloc_len); + + + struct atapi_rd_toc_resp * resp = (struct atapi_rd_toc_resp *)(drive->data_buf); + + resp->data_len = 10; + + return -1; + +} + + +static int atapi_mode_sense_cur_values(struct vm_device * dev, struct ide_channel * channel, + struct atapi_mode_sense_cmd * sense_cmd) { + struct ide_drive * drive = get_selected_drive(channel); + struct atapi_mode_sense_hdr * hdr = (struct atapi_mode_sense_hdr *)(drive->data_buf); + uint_t resp_len = sizeof(struct atapi_mode_sense_hdr); + uint16_t alloc_len = be_to_le_16(sense_cmd->alloc_len); + PrintDebug("Page Code: %x\n", sense_cmd->page_code); + PrintDebug("Alloc len: %d\n", alloc_len); + + switch (sense_cmd->page_code) { + + case 0x01: { // error recovery + struct atapi_error_recovery * err = NULL; + err = (struct atapi_error_recovery *)(drive->data_buf + + sizeof(struct atapi_mode_sense_hdr)); + + memcpy(err, &(drive->cd_state.err_recovery), sizeof(struct atapi_error_recovery)); + + resp_len += sizeof(struct atapi_error_recovery); + + hdr->mode_data_len = le_to_be_16(resp_len - 2); + + break; + } + case 0x2a: { // CDROM caps and mech. status + struct atapi_cdrom_caps * caps = NULL; + caps = (struct atapi_cdrom_caps *)(drive->data_buf + sizeof(struct atapi_mode_sense_hdr)); + + + memset(caps, 0, sizeof(struct atapi_cdrom_caps)); + + resp_len += sizeof(struct atapi_cdrom_caps); + + hdr->mode_data_len = le_to_be_16(resp_len - 2); + + caps->page_code = 0x2a; + caps->page_len = 0x12; + caps->mode2_form1 = 1; + caps->mode2_form2 = 1; + caps->multisession = 1; + caps->isrc = 1; + caps->upc = 1; + + /* JRL TODO: These are dynamic caps */ + caps->lock = 1; + caps->lock_state = 0; + caps->eject = 1; + + caps->lmt = 1; + caps->obsolete1 = le_to_be_16(0x2c2); + caps->num_vols_supp = le_to_be_16(2); + + caps->lun_buf_size = le_to_be_16(512); + caps->obsolete2 = le_to_be_16(0x2c2); + + break; + } + case 0x0d: + case 0x0e: + case 0x3f: + default: + PrintError("ATAPI: Mode sense Page Code not supported (%x)\n", sense_cmd->page_code); + atapi_cmd_error(dev, channel, ATAPI_SEN_ILL_REQ, ASC_INV_CMD_FIELD); + ide_raise_irq(dev, channel); + return 0; + } + + + // We do this after error checking, because its only valid if everything worked + memset(hdr, 0, sizeof(struct atapi_mode_sense_hdr)); + hdr->media_type_code = 0x70; + + PrintDebug("resp_len=%d\n", resp_len); + + drive->transfer_length = (resp_len > alloc_len) ? alloc_len : resp_len; + drive->transfer_index = 0; + atapi_update_req_len(dev, channel, drive->transfer_length); + ide_raise_irq(dev, channel); + + return 0; } + +static int atapi_mode_sense(struct vm_device * dev, struct ide_channel * channel) { + struct ide_drive * drive = get_selected_drive(channel); + struct atapi_mode_sense_cmd * sense_cmd = (struct atapi_mode_sense_cmd *)(drive->data_buf); + + switch (sense_cmd->page_ctrl) { + case 0x00: // Current values + return atapi_mode_sense_cur_values(dev, channel, sense_cmd); + case 0x01: // Changeable values + case 0x02: // default values + case 0x03: // saved values + default: + PrintError("ATAPI: Mode sense mode not supported (%x)\n", sense_cmd->page_ctrl); + return -1; + } + return 0; +} + + static int atapi_handle_packet(struct vm_device * dev, struct ide_channel * channel) { struct ide_drive * drive = get_selected_drive(channel); - uint8_t command = drive->data_buf[0]; + uint8_t cmd = drive->data_buf[0]; - PrintDebug("IDE: ATAPI Command %x\n", command); + PrintDebug("IDE: ATAPI Command %x\n", cmd); - switch (command) { + drive->cd_state.atapi_cmd = cmd; + + switch (cmd) { case 0x00: // test unit ready atapi_cmd_nop(dev, channel); @@ -139,21 +373,43 @@ static int atapi_handle_packet(struct vm_device * dev, struct ide_channel * chan case 0x28: // read(10) if (atapi_read10(dev, channel) == -1) { - PrintError("IDE: Error in ATAPI read (%x)\n", command); + PrintError("IDE: Error in ATAPI read (%x)\n", cmd); return -1; } + break; + case 0x5a: // mode sense + if (atapi_mode_sense(dev, channel) == -1) { + PrintError("IDE: Error in ATAPI mode sense (%x)\n", cmd); + return -1; + } break; + + + case 0x25: // read cdrom capacity + if (atapi_get_capacity(dev, channel) == -1) { + PrintError("IDE: Error getting CDROM capacity (%x)\n", cmd); + return -1; + } + break; + + + case 0x43: // read TOC + if (atapi_read_toc(dev, channel) == -1) { + PrintError("IDE: Error getting CDROM TOC (%x)\n", cmd); + return -1; + } + break; + case 0xa8: // read(12) case 0x1b: // start/stop drive case 0xbd: // mechanism status - case 0x5a: // mode sense case 0x12: // inquiry - case 0x25: // read cdrom capacity + case 0xbe: // read cd - case 0x43: // read TOC + case 0x2b: // seek @@ -176,7 +432,7 @@ static int atapi_handle_packet(struct vm_device * dev, struct ide_channel * chan case 0x46: // ??? case 0x4a: // ??? default: - PrintError("Unhandled ATAPI command %x\n", command); + PrintError("Unhandled ATAPI command %x\n", cmd); atapi_cmd_error(dev, channel, ATAPI_SEN_ILL_REQ, ASC_INV_CMD_FIELD); ide_raise_irq(dev, channel); return -1; @@ -193,6 +449,8 @@ static void atapi_identify_device(struct ide_drive * drive) { drive->transfer_length = 512; drive->transfer_index = 0; + + memset(drive_id->buf, 0, sizeof(drive_id->buf)); drive_id->fixed_drive = 1; diff --git a/palacios/src/devices/ide.c b/palacios/src/devices/ide.c index 0d6e0b2..233470a 100644 --- a/palacios/src/devices/ide.c +++ b/palacios/src/devices/ide.c @@ -67,6 +67,8 @@ static inline const char * device_type_to_str(v3_ide_dev_type_t type) { struct ide_cd_state { struct atapi_sense_data sense; uint_t current_lba; + uint8_t atapi_cmd; + struct atapi_error_recovery err_recovery; }; struct ide_hd_state { @@ -98,19 +100,30 @@ struct ide_drive { // calculated for easy access uint_t transfer_length; + // We have a local data buffer that we use for IO port accesses uint8_t data_buf[DATA_BUFFER_SIZE]; + void * private_data; + + union { + uint8_t sector_count; // 0x1f2,0x172 + struct atapi_irq_flags irq_flags; + } __attribute__((packed)); - uint8_t sector_count; // 0x1f2,0x172 uint8_t sector_num; // 0x1f3,0x173 + union { uint16_t cylinder; + struct { uint8_t cylinder_low; // 0x1f4,0x174 uint8_t cylinder_high; // 0x1f5,0x175 } __attribute__((packed)); + + // The transfer length requested by the CPU + uint16_t req_len; } __attribute__((packed)); @@ -145,17 +158,25 @@ struct ide_internal { -static inline uint32_t be_to_le_16(const uint16_t val) { +static inline uint16_t be_to_le_16(const uint16_t val) { uint8_t * buf = (uint8_t *)&val; return (buf[0] << 8) | (buf[1]) ; } +static inline uint16_t le_to_be_16(const uint16_t val) { + return be_to_le_16(val); +} + static inline uint32_t be_to_le_32(const uint32_t val) { uint8_t * buf = (uint8_t *)&val; return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; } +static inline uint32_t le_to_be_32(const uint32_t val) { + return be_to_le_32(val); +} + static inline int get_channel_index(ushort_t port) { if (((port & 0xfff8) == 0x1f0) || @@ -203,7 +224,6 @@ static void drive_reset(struct ide_drive * drive) { memset(drive->data_buf, 0, sizeof(drive->data_buf)); - drive->transfer_length = 0; drive->transfer_index = 0; // Send the reset signal to the connected device callbacks @@ -244,14 +264,10 @@ static void ide_abort_command(struct vm_device * dev, struct ide_channel * chann } - - // Include the ATAPI interface handlers #include "atapi.h" - - static int write_cmd_port(ushort_t port, void * src, uint_t length, struct vm_device * dev) { struct ide_internal * ide = (struct ide_internal *)(dev->private_data); struct ide_channel * channel = get_selected_channel(ide, port); @@ -318,7 +334,8 @@ static int write_data_port(ushort_t port, void * src, uint_t length, struct vm_d struct ide_channel * channel = get_selected_channel(ide, port); struct ide_drive * drive = get_selected_drive(channel); - PrintDebug("IDE: Writing Data Port %x (val=%x, len=%d)\n", port, *(uint32_t *)src, length); + // PrintDebug("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; @@ -346,40 +363,127 @@ static int write_data_port(ushort_t port, void * src, uint_t length, struct vm_d } -static int read_data_port(ushort_t port, void * dst, uint_t length, struct vm_device * dev) { - struct ide_internal * ide = (struct ide_internal *)(dev->private_data); - struct ide_channel * channel = get_selected_channel(ide, port); +static int read_hd_data(uint8_t * dst, uint_t length, struct vm_device * dev, struct ide_channel * channel) { + PrintError("Harddrive data port read not implemented\n"); + return -1; +} + + + +static int read_cd_data(uint8_t * dst, uint_t length, struct vm_device * dev, struct ide_channel * channel) { struct ide_drive * drive = get_selected_drive(channel); int data_offset = drive->transfer_index % DATA_BUFFER_SIZE; + int req_offset = drive->transfer_index % drive->req_len; + + if (drive->cd_state.atapi_cmd != 0x28) { + PrintDebug("IDE: Reading CD Data (len=%d) (req_len=%d)\n", length, drive->req_len); + } - PrintDebug("IDE: Reading Data Port %x\n", port); - - if (data_offset == DATA_BUFFER_SIZE) { - // check for more data to transfer, if there isn't any then that's a problem... - /* - * if (ide_update_buffer(drive) == -1) { - * return -1; - * } - */ + if (drive->transfer_index >= drive->transfer_length) { + PrintError("Buffer Overrun... (xfer_len=%d) (cur_idx=%d) (post_idx=%d)\n", + drive->transfer_length, drive->transfer_index, + drive->transfer_index + length); return -1; } - // check for overruns... - // We will return the data padded with 0's - if (drive->transfer_index + length > drive->transfer_length) { - length = drive->transfer_length - drive->transfer_index; - memset(dst, 0, length); + + if ((data_offset == 0) && (drive->transfer_index > 0)) { + + if (drive->drive_type == IDE_CDROM) { + if (atapi_update_data_buf(dev, channel) == -1) { + PrintError("Could not update CDROM data buffer\n"); + return -1; + } + } else { + PrintError("IDE Harddrives not implemented\n"); + return -1; + } } memcpy(dst, drive->data_buf + data_offset, length); - + drive->transfer_index += length; + if ((req_offset == 0) && (drive->transfer_index > 0)) { + if (drive->transfer_index < drive->transfer_length) { + // An increment is complete, but there is still more data to be transferred... + + channel->status.data_req = 1; + + drive->irq_flags.c_d = 0; + + // Update the request length in the cylinder regs + if (atapi_update_req_len(dev, channel, drive->transfer_length - drive->transfer_index) == -1) { + PrintError("Could not update request length after completed increment\n"); + return -1; + } + } else { + // This was the final read of the request + channel->status.data_req = 0; + channel->status.ready = 1; + + drive->irq_flags.c_d = 1; + drive->irq_flags.rel = 0; + } + + drive->irq_flags.io_dir = 1; + channel->status.busy = 0; + + ide_raise_irq(dev, channel); + } + + return length; +} + + +static int read_drive_id(uint8_t * dst, uint_t length, struct vm_device * dev, struct ide_channel * channel) { + struct ide_drive * drive = get_selected_drive(channel); + + channel->status.busy = 0; + channel->status.ready = 1; + channel->status.write_fault = 0; + channel->status.seek_complete = 1; + channel->status.corrected = 0; + channel->status.error = 0; + + + memcpy(dst, drive->data_buf + drive->transfer_index, length); + drive->transfer_index += length; if (drive->transfer_index >= drive->transfer_length) { channel->status.data_req = 0; } + + return length; +} + + +static int ide_read_data_port(ushort_t port, void * dst, uint_t length, struct vm_device * dev) { + struct ide_internal * ide = (struct ide_internal *)(dev->private_data); + struct ide_channel * channel = get_selected_channel(ide, port); + struct ide_drive * drive = get_selected_drive(channel); + + // PrintDebug("IDE: Reading Data Port %x (len=%d)\n", port, length); + + if ((channel->cmd_reg == 0xec) || + (channel->cmd_reg == 0xa1)) { + return read_drive_id((uint8_t *)dst, length, dev, channel); + } + + if (drive->drive_type == IDE_CDROM) { + if (read_cd_data((uint8_t *)dst, length, dev, channel) == -1) { + PrintError("IDE: Could not read CD Data\n"); + return -1; + } + } else if (drive->drive_type == IDE_DISK) { + if (read_hd_data((uint8_t *)dst, length, dev, channel) == -1) { + PrintError("IDE: Could not read HD Data\n"); + return -1; + } + } else { + memset((uint8_t *)dst, 0, length); + } return length; } @@ -610,7 +714,7 @@ static int init_ide(struct vm_device * dev) { &read_port_std, &write_port_std); v3_dev_hook_io(dev, PRI_DATA_PORT, - &read_data_port, &write_data_port); + &ide_read_data_port, &write_data_port); v3_dev_hook_io(dev, PRI_FEATURES_PORT, &read_port_std, &write_port_std); v3_dev_hook_io(dev, PRI_SECT_CNT_PORT, @@ -631,7 +735,7 @@ static int init_ide(struct vm_device * dev) { &read_port_std, &write_port_std); v3_dev_hook_io(dev, SEC_DATA_PORT, - &read_data_port, &write_data_port); + &ide_read_data_port, &write_data_port); v3_dev_hook_io(dev, SEC_FEATURES_PORT, &read_port_std, &write_port_std); v3_dev_hook_io(dev, SEC_SECT_CNT_PORT, diff --git a/palacios/src/devices/ramdisk.c b/palacios/src/devices/ramdisk.c index 3595c36..c7668de 100644 --- a/palacios/src/devices/ramdisk.c +++ b/palacios/src/devices/ramdisk.c @@ -498,15 +498,13 @@ static int read_data_port(ushort_t port, void * dst, uint_t length, struct vm_de case 0xec: // IDENTIFY DEVICE case 0xa1: { - - controller->status.busy = 0; controller->status.drive_ready = 1; controller->status.write_fault = 0; controller->status.seek_complete = 1; controller->status.corrected_data = 0; controller->status.err = 0; - + /* value32 = controller->buffer[index]; index++; @@ -531,7 +529,7 @@ static int read_data_port(ushort_t port, void * dst, uint_t length, struct vm_de if (controller->buffer_index >= 512) { controller->status.drq = 0; } - + return length; } case 0xa0: //send packet cmd