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;
}