diff --git a/devices/common/ata/atabasedevice.cpp b/devices/common/ata/atabasedevice.cpp index 7ed8da4929..13704cd982 100644 --- a/devices/common/ata/atabasedevice.cpp +++ b/devices/common/ata/atabasedevice.cpp @@ -198,17 +198,22 @@ int AtaBaseDevice::pull_data(uint8_t *buf, int len) { int xfer_size = std::min(this->xfer_cnt, len); - if (this->xfer_cnt != len) - LOG_F(WARNING, "%s: xfer_cnt != len", this->name.c_str()); + if (this->xfer_cnt != len) { + LOG_F(9, "%s: DMA pull size differs from descriptor, remaining=%d requested=%d", + this->name.c_str(), this->xfer_cnt, len); + } //for (int i = 0; i < xfer_size >> 1; i++) // this->data_ptr[i] = BYTESWAP_16(this->data_ptr[i]); std::memcpy(buf, this->data_ptr, xfer_size); + this->data_ptr = (uint16_t *)((uint8_t *)this->data_ptr + xfer_size); this->xfer_cnt -= xfer_size; if (!this->xfer_cnt) { - TimerManager::get_instance()->add_oneshot_timer(500, [this]() { + this->is_dma_xfer = false; + this->data_ptr = nullptr; + TimerManager::get_instance()->add_immediate_timer([this]() { this->r_status &= ~(BSY | DRQ); this->update_intrq(1); }); diff --git a/devices/common/ata/atahd.cpp b/devices/common/ata/atahd.cpp index 8a28a92cff..abb988025a 100644 --- a/devices/common/ata/atahd.cpp +++ b/devices/common/ata/atahd.cpp @@ -22,12 +22,14 @@ along with this program. If not, see . /** @file ATA hard disk emulation. */ #include +#include #include #include #include #include #include +#include #include #include #include @@ -120,6 +122,38 @@ int AtaHardDisk::perform_command() { this->signal_data_ready(); } break; + case READ_DMA: + case READ_DMA_NR: { + 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; + this->is_dma_xfer = true; + this->prepare_xfer(xfer_size, xfer_size); + this->r_status &= ~(BSY | DRQ | ERR); + this->r_status |= DRDY | DSC; + // TODO: Model ATA DMAREQ as a latched signal instead of relying + // on this delay to avoid racing DBDMA command startup. + this->host_obj->assert_dmareq(500); + } + break; + case WRITE_DMA: + case WRITE_DMA_NR: { + uint16_t sec_count = this->r_sect_count ? this->r_sect_count : 256; + int xfer_size = sec_count * ATA_HD_SEC_SIZE; + + this->cur_fpos = this->get_lba() * ATA_HD_SEC_SIZE; + this->is_dma_xfer = true; + this->prepare_xfer(xfer_size, xfer_size); + this->r_status &= ~(BSY | DRQ | ERR); + this->r_status |= DRDY | DSC; + // TODO: Model ATA DMAREQ as a latched signal instead of relying + // on this delay to avoid racing DBDMA command startup. + this->host_obj->assert_dmareq(500); + } + break; case WRITE_MULTIPLE: case WRITE_SECTOR: case WRITE_SECTOR_NR: { @@ -256,6 +290,30 @@ int AtaHardDisk::perform_command() { return 0; } +int AtaHardDisk::push_data(uint8_t *buf, int len) { + if (!this->xfer_cnt || !this->is_dma_xfer) + return 0; + + int xfer_size = std::min(this->xfer_cnt, len); + this->hdd_img.write(buf, this->cur_fpos, xfer_size); + this->cur_fpos += xfer_size; + this->xfer_cnt -= xfer_size; + + if (!this->xfer_cnt) { + this->is_dma_xfer = false; + TimerManager::get_instance()->add_immediate_timer([this]() { + this->r_status &= ~(BSY | DRQ | ERR); + this->r_status |= DRDY | DSC; + this->update_intrq(1); + }); + } else { + this->r_status &= ~BSY; + this->r_status |= DRQ; + } + + return xfer_size; +} + void AtaHardDisk::prepare_identify_info() { uint16_t *buf_ptr = (uint16_t *)this->data_buf; diff --git a/devices/common/ata/atahd.h b/devices/common/ata/atahd.h index 35bd96b4e5..475b7aa47d 100644 --- a/devices/common/ata/atahd.h +++ b/devices/common/ata/atahd.h @@ -53,6 +53,7 @@ class AtaHardDisk : public AtaBaseDevice void insert_image(std::string filename); int perform_command() override; + int push_data(uint8_t *buf, int len) override; protected: void prepare_identify_info(); diff --git a/devices/common/dbdma.cpp b/devices/common/dbdma.cpp index b3b59e358b..970e81c215 100644 --- a/devices/common/dbdma.cpp +++ b/devices/common/dbdma.cpp @@ -91,7 +91,7 @@ uint8_t DMAChannel::interpret_cmd() { if (cmd_struct.req_count) { res = mmu_map_dma_mem(cmd_struct.address, cmd_struct.req_count, false); this->queue_data = res.host_va; - this->res_count = 0; + this->res_count = cmd_struct.req_count; this->queue_len = cmd_struct.req_count; // don't set queue_len until all the other fields are set this->cmd_in_progress = true; switch (this->cur_cmd) { @@ -468,7 +468,8 @@ void DMAChannel::xfer_from_device() { this->xfer_dir = DMA_DIR_FROM_DEV; int got_bytes = this->dev_obj->xfer_from(this, this->queue_data, this->queue_len); - this->res_count += got_bytes; + this->queue_data += got_bytes; + this->res_count -= got_bytes; this->queue_len -= got_bytes; if (!this->queue_len) { this->finish_cmd(); @@ -487,7 +488,8 @@ void DMAChannel::xfer_to_device() { this->xfer_dir = DMA_DIR_TO_DEV; int got_bytes = this->dev_obj->xfer_to(this, this->queue_data, this->queue_len); - this->res_count += got_bytes; + this->queue_data += got_bytes; + this->res_count -= got_bytes; this->queue_len -= got_bytes; if (!this->queue_len) { this->finish_cmd(); @@ -536,14 +538,14 @@ DmaPullResult DMAChannel::pull_data(uint32_t req_len, uint32_t *avail_len, uint8 *p_data = this->queue_data; *avail_len = req_len; this->queue_len -= req_len; - this->res_count += req_len; + this->res_count -= req_len; this->queue_data += req_len; } else { // return less data than req_len LOG_F(9, "%s: Return queue_len = %d data", this->get_name().c_str(), this->queue_len); *p_data = this->queue_data; *avail_len = this->queue_len; - this->res_count += this->queue_len; + this->res_count -= this->queue_len; this->queue_len = 0; } return DmaPullResult::MoreData; // tell the caller there is more data @@ -568,7 +570,7 @@ DmaPushResult DMAChannel::push_data(const char* src_ptr, int len) { len = std::min((int)this->queue_len, len); std::memcpy(this->queue_data, src_ptr, len); this->queue_data += len; - this->res_count += len; + this->res_count -= len; this->queue_len -= len; }