From 446b1b8d996116b2db0d5361bc1c5c221ce9c2c6 Mon Sep 17 00:00:00 2001 From: Maxim Poliakovski Date: Wed, 22 Nov 2023 17:08:23 +0100 Subject: [PATCH] atahd: various improvements implementing basic commands. --- devices/common/ata/atahd.cpp | 104 ++++++++++++++++++++++++++++------- devices/common/ata/atahd.h | 18 +++++- 2 files changed, 100 insertions(+), 22 deletions(-) diff --git a/devices/common/ata/atahd.cpp b/devices/common/ata/atahd.cpp index abd1592..94b481f 100644 --- a/devices/common/ata/atahd.cpp +++ b/devices/common/ata/atahd.cpp @@ -22,6 +22,9 @@ along with this program. If not, see . /** @file ATA hard disk emulation. */ #include +#include +#include +#include #include #include @@ -30,12 +33,35 @@ along with this program. If not, see . using namespace ata_interface; -AtaHardDisk::AtaHardDisk() : AtaBaseDevice("ATA-HD", DEVICE_TYPE_ATA) { +AtaHardDisk::AtaHardDisk(std::string name) : AtaBaseDevice(name, DEVICE_TYPE_ATA) { +} + +int AtaHardDisk::device_postinit() { + std::string hdd_config = GET_STR_PROP("hdd_config"); + if (hdd_config.empty()) { + LOG_F(ERROR, "%s: hdd_config property is empty", this->name.c_str()); + return -1; + } + + std::string bus_id; + uint32_t dev_num; + + parse_device_path(hdd_config, bus_id, dev_num); + + auto bus_obj = dynamic_cast(gMachineObj->get_comp_by_name(bus_id)); + bus_obj->register_device(dev_num, this); + + std::string hdd_image_path = GET_STR_PROP("hdd_img"); + if (!hdd_image_path.empty()) { + this->insert_image(hdd_image_path); + } + + return 0; } void AtaHardDisk::insert_image(std::string filename) { if (!this->hdd_img.open(filename)) { - ABORT_F("AtaHardDisk: could not open image file"); + ABORT_F("%s: could not open image file", this->name.c_str()); } this->img_size = this->hdd_img.size(); @@ -45,7 +71,7 @@ int AtaHardDisk::perform_command() { LOG_F(INFO, "%s: command 0x%x requested", this->name.c_str(), this->r_command); this->r_status |= BSY; - this->r_status &= ~DRDY; + switch (this->r_command) { case NOP: break; @@ -56,27 +82,27 @@ int AtaHardDisk::perform_command() break; case READ_SECTOR: case READ_SECTOR_NR: { - this->r_status |= DRQ; - uint16_t sec_count = (this->r_sect_count == 0) ? 256 : this->r_sect_count; - uint32_t sector = (r_sect_num << 16); - sector |= ((this->r_cylinder_lo) << 8) + (this->r_cylinder_hi); - uint64_t offset = sector * ATA_HD_SEC_SIZE; - hdd_img.read(buffer, offset, sec_count * ATA_HD_SEC_SIZE); - this->r_status &= ~DRQ; + 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; case WRITE_SECTOR: case WRITE_SECTOR_NR: { - this->r_status |= DRQ; - uint16_t sec_count = (this->r_sect_count == 0) ? 256 : this->r_sect_count; - uint32_t sector = (r_sect_num << 16); - sector |= ((this->r_cylinder_lo) << 8) + (this->r_cylinder_hi); - uint64_t offset = sector * ATA_HD_SEC_SIZE; + 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); - this->r_status &= ~DRQ; } break; case INIT_DEV_PARAM: + // update fictive disk geometry with parameters from host + this->sectors = this->r_sect_count; + this->heads = (this->r_dev_head & 0xF) + 1; + this->r_status &= ~BSY; + this->update_intrq(1); break; case DIAGNOSTICS: { this->r_status |= DRQ; @@ -86,9 +112,10 @@ int AtaHardDisk::perform_command() } break; case IDENTIFY_DEVICE: - this->r_status |= DRQ; - std::memcpy(buffer, this->hd_id_data, ATA_HD_SEC_SIZE); - this->r_status &= ~DRQ; + this->prepare_identify_info(); + this->data_ptr = (uint16_t *)this->data_buf; + this->xfer_cnt = ATA_HD_SEC_SIZE; + this->signal_data_ready(); break; default: LOG_F(ERROR, "%s: unknown ATA command 0x%x", this->name.c_str(), this->r_command); @@ -96,7 +123,42 @@ int AtaHardDisk::perform_command() this->r_status |= ERR; return -1; } - this->r_status &= ~BSY; - this->r_status |= DRDY; return 0; } + +void AtaHardDisk::prepare_identify_info() { + uint16_t *buf_ptr = (uint16_t *)this->data_buf; + + 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; +} + +uint64_t AtaHardDisk::get_lba() { + if (this->r_dev_head & ATA_Dev_Head::LBA) { + return ((this->r_dev_head & 0xF) << 24) | (this->r_cylinder_hi << 16) | + (this->r_cylinder_lo << 8) | (this->r_sect_num); + } else { // translate old fashioned CHS addressing to LBA + uint16_t c = (this->r_cylinder_hi << 8) + this->r_cylinder_lo; + uint8_t h = this->r_dev_head & 0xF; + uint8_t s = this->r_sect_num; + + if (!s) { + LOG_F(ERROR, "%s: zero sector number is not allowed!", this->name.c_str()); + return -1ULL; + } else + return (this->heads * c + h) * this->sectors + s - 1; + } +} + +static const PropMap AtaHardDiskProperties = { + {"hdd_img", new StrProperty("")}, +}; + +static const DeviceDescription AtaHardDiskDescriptor = + {AtaHardDisk::create, {}, AtaHardDiskProperties}; + +REGISTER_DEVICE(AtaHardDisk, AtaHardDiskDescriptor); diff --git a/devices/common/ata/atahd.h b/devices/common/ata/atahd.h index 20965a8..08e27d7 100644 --- a/devices/common/ata/atahd.h +++ b/devices/common/ata/atahd.h @@ -34,15 +34,31 @@ along with this program. If not, see . class AtaHardDisk : public AtaBaseDevice { public: - AtaHardDisk(); + AtaHardDisk(std::string name); ~AtaHardDisk() = default; + static std::unique_ptr create() { + return std::unique_ptr(new AtaHardDisk("ATA-HD")); + } + + int device_postinit() override; + void insert_image(std::string filename); int perform_command() override; +protected: + void prepare_identify_info(); + uint64_t get_lba(); + private: ImgFile hdd_img; uint64_t img_size; + + // fictive disk geometry for CHS-to-LBA translation + uint16_t cylinders; + uint8_t heads; + uint8_t sectors; + char * buffer = new char[1 <<17]; uint8_t hd_id_data[ATA_HD_SEC_SIZE] = {};