From 679e80a7c3819727aa1abb0c60788048b37810ab Mon Sep 17 00:00:00 2001 From: Maxim Poliakovski Date: Tue, 19 Dec 2023 14:49:54 +0100 Subject: [PATCH] atahd: basic commands for disk I/O. --- devices/common/ata/atadefs.h | 3 +- devices/common/ata/atahd.cpp | 104 +++++++++++++++++++++++++---------- devices/common/ata/atahd.h | 6 +- 3 files changed, 81 insertions(+), 32 deletions(-) diff --git a/devices/common/ata/atadefs.h b/devices/common/ata/atadefs.h index c982138..e7e49bb 100644 --- a/devices/common/ata/atadefs.h +++ b/devices/common/ata/atadefs.h @@ -1,6 +1,6 @@ /* DingusPPC - The Experimental PowerPC Macintosh emulator -Copyright (C) 2018-22 divingkatae and maximum +Copyright (C) 2018-23 divingkatae and maximum (theweirdo) spatium (Contact divingkatae#1017 or powermax#2286 on Discord for more info) @@ -139,6 +139,7 @@ enum ATA_Cmd : uint8_t { WRITE_MULTIPLE = 0xC5, READ_DMA = 0xC8, WRITE_DMA = 0xCA, + FLUSH_CACHE = 0xE7, // ATA-5 WRITE_BUFFER_DMA = 0xE9, READ_BUFFER_DMA = 0xEB, IDENTIFY_DEVICE = 0xEC, diff --git a/devices/common/ata/atahd.cpp b/devices/common/ata/atahd.cpp index 94b481f..1d83c93 100644 --- a/devices/common/ata/atahd.cpp +++ b/devices/common/ata/atahd.cpp @@ -24,12 +24,13 @@ along with this program. If not, see . #include #include #include +#include #include +#include #include #include #include -#include using namespace ata_interface; @@ -65,12 +66,12 @@ void AtaHardDisk::insert_image(std::string filename) { } this->img_size = this->hdd_img.size(); + this->total_sectors = this->hdd_img.size() / ATA_HD_SEC_SIZE; } -int AtaHardDisk::perform_command() -{ - LOG_F(INFO, "%s: command 0x%x requested", this->name.c_str(), this->r_command); +int AtaHardDisk::perform_command() { this->r_status |= BSY; + this->r_error = 0; switch (this->r_command) { case NOP: @@ -82,21 +83,31 @@ int AtaHardDisk::perform_command() break; case READ_SECTOR: case READ_SECTOR_NR: { - uint16_t sec_count = this->r_sect_count ? this->r_sect_count : 256; - this->xfer_cnt = sec_count * ATA_HD_SEC_SIZE; - uint64_t offset = this->get_lba() * ATA_HD_SEC_SIZE; - hdd_img.read(buffer, offset, this->xfer_cnt); - this->data_ptr = (uint16_t *)this->buffer; - this->signal_data_ready(); - } - break; + uint16_t sec_count = this->r_sect_count ? this->r_sect_count : 256; + int xfer_size = sec_count * ATA_HD_SEC_SIZE; + uint64_t offset = this->get_lba() * ATA_HD_SEC_SIZE; + hdd_img.read(buffer, offset, xfer_size); + this->data_ptr = (uint16_t *)this->buffer; + // those commands should generate IRQ for each sector + this->prepare_xfer(xfer_size, ATA_HD_SEC_SIZE); + this->signal_data_ready(); + } + break; case WRITE_SECTOR: case WRITE_SECTOR_NR: { - uint16_t sec_count = this->r_sect_count ? this->r_sect_count : 256; - uint64_t offset = this->get_lba() * ATA_HD_SEC_SIZE; - hdd_img.write(buffer, offset, sec_count * ATA_HD_SEC_SIZE); - } - break; + uint16_t sec_count = this->r_sect_count ? this->r_sect_count : 256; + this->cur_fpos = this->get_lba() * ATA_HD_SEC_SIZE; + this->data_ptr = (uint16_t *)this->buffer; + this->cur_data_ptr = this->data_ptr; + this->prepare_xfer(sec_count * ATA_HD_SEC_SIZE, ATA_HD_SEC_SIZE); + this->post_xfer_action = [this]() { + this->hdd_img.write(this->data_ptr, this->cur_fpos, this->chunk_size); + this->cur_fpos += this->chunk_size; + }; + this->r_status |= DRQ; + this->r_status &= ~BSY; + } + break; case INIT_DEV_PARAM: // update fictive disk geometry with parameters from host this->sectors = this->r_sect_count; @@ -104,19 +115,47 @@ int AtaHardDisk::perform_command() this->r_status &= ~BSY; this->update_intrq(1); break; - case DIAGNOSTICS: { - this->r_status |= DRQ; - int ret_code = this->r_error; - this->r_status &= ~DRQ; - return ret_code; - } - break; + case DIAGNOSTICS: + this->r_error = 1; + this->device_set_signature(); + break; + case FLUSH_CACHE: // used by the XNU kernel driver + this->r_status &= ~(BSY | DRQ | ERR); + this->update_intrq(1); + break; case IDENTIFY_DEVICE: this->prepare_identify_info(); this->data_ptr = (uint16_t *)this->data_buf; - this->xfer_cnt = ATA_HD_SEC_SIZE; + this->prepare_xfer(ATA_HD_SEC_SIZE, ATA_HD_SEC_SIZE); this->signal_data_ready(); break; + case SET_FEATURES: + if (this->r_features == 3) { + switch(this->r_sect_count >> 3) { + case 0: + LOG_F(INFO, "%s: default transfer mode requested", this->name.c_str()); + break; + case 1: + LOG_F(INFO, "%s: PIO transfer mode set to 0x%X", this->name.c_str(), + this->r_sect_count & 7); + break; + case 4: + LOG_F(INFO, "%s: Multiword DMA mode set to 0x%X", this->name.c_str(), + this->r_sect_count & 7); + break; + default: + LOG_F(ERROR, "%s: unsupported transfer mode 0x%X", this->name.c_str(), + this->r_sect_count); + this->r_error |= ATA_Error::ABRT; + this->r_status |= ATA_Status::ERR; + } + } else { + LOG_F(WARNING, "%s: unsupported SET_FEATURES subcommand code 0x%X", + this->name.c_str(), this->r_features); + } + this->r_status &= ~BSY; + this->update_intrq(1); + break; default: LOG_F(ERROR, "%s: unknown ATA command 0x%x", this->name.c_str(), this->r_command); this->r_status &= ~BSY; @@ -131,10 +170,17 @@ void AtaHardDisk::prepare_identify_info() { std::memset(this->data_buf, 0, sizeof(this->data_buf)); - buf_ptr[0] = 0x0040; // ATA device, non-removable media, non-removable drive - buf_ptr[1] = 965; - buf_ptr[3] = 5; - buf_ptr[6] = 17; + buf_ptr[ 0] = 0x0040; // ATA device, non-removable media, non-removable drive + buf_ptr[49] = 0x200; // report LBA support + + // TODO: replace this fictive CHS geometry with the proper one + buf_ptr[ 1] = 965; + buf_ptr[ 3] = 5; + buf_ptr[ 6] = 17; + + // report LBA capacity + WRITE_WORD_LE_A(&buf_ptr[60], (this->total_sectors & 0xFFFFU)); + WRITE_WORD_LE_A(&buf_ptr[61], (this->total_sectors >> 16) & 0xFFFFU); } uint64_t AtaHardDisk::get_lba() { diff --git a/devices/common/ata/atahd.h b/devices/common/ata/atahd.h index 08e27d7..fa00b50 100644 --- a/devices/common/ata/atahd.h +++ b/devices/common/ata/atahd.h @@ -51,8 +51,10 @@ protected: uint64_t get_lba(); private: - ImgFile hdd_img; - uint64_t img_size; + ImgFile hdd_img; + uint64_t img_size = 0; + uint32_t total_sectors = 0; + uint64_t cur_fpos = 0; // fictive disk geometry for CHS-to-LBA translation uint16_t cylinders;