idedrv Implement DMA reading/writing with interrupts
All checks were successful
Build ISO image / build-and-deploy (push) Successful in 2m19s
Build documentation / build-and-deploy (push) Successful in 41s

This commit is contained in:
2026-04-30 18:11:21 +02:00
parent 6a0aeea88a
commit 616ba40a14

View File

@@ -113,6 +113,16 @@ static void ide_irq(void* arg, void* regs, bool user, struct reschedule_ctx* rct
struct idedrv_request* req = idedrv->current_req;
if (idedrv->bm_support) {
uint8_t bm_status = inb(idedrv->bmbase + IDE_DMA_REG_STATUS);
if (!(bm_status & IDE_DMA_STATUS_INTR))
return;
outb(idedrv->bmbase + IDE_DMA_REG_STATUS,
bm_status | IDE_DMA_STATUS_INTR | IDE_DMA_STATUS_ERROR);
}
if (req == NULL) {
(void)inb(idedrv->io + IDE_REG_STATUS);
return;
@@ -120,6 +130,13 @@ static void ide_irq(void* arg, void* regs, bool user, struct reschedule_ctx* rct
uint8_t status = inb(idedrv->io + IDE_REG_STATUS);
if (idedrv->bm_support) {
outb(idedrv->bmbase + IDE_DMA_REG_CMD, 0x00);
atomic_store(&req->done, 1);
idedrv->current_req = NULL;
return;
}
if ((status & (IDE_ERR | IDE_DF))) {
atomic_store(&req->done, 1);
idedrv->current_req = NULL;
@@ -280,10 +297,10 @@ DEFINE_DEVICE_INIT(idedrv_init) {
idedrv->prdt_entry_count = PAGE_SIZE / sizeof(struct ide_prd_entry);
idedrv->prdt = (struct ide_prd_entry*)((uintptr_t)hhdm->offset + idedrv->prdt_phys);
idedrv->bounce_buffer_phys = pmm_alloc_aligned(16, 16);
idedrv->bounce_buffer_phys = pmm_alloc_aligned(64, 16);
if (idedrv->bounce_buffer_phys >= 0xFFFFFFFF) {
pmm_free(idedrv->bounce_buffer_phys, 16);
pmm_free(idedrv->bounce_buffer_phys, 64);
pmm_free(idedrv->prdt_phys, 1);
free(idedrv);
return false;
@@ -312,8 +329,8 @@ DEFINE_DEVICE_FINI(idedrv_fini) {
irq_detach(idedrv->irq);
if (idedrv->bm_support) {
pmm_free(idedrv->bounce_buffer_phys, 16);
pmm_free(idedrv->prdt_phys, 16);
pmm_free(idedrv->bounce_buffer_phys, 64);
pmm_free(idedrv->prdt_phys, 1);
}
free(idedrv);
@@ -334,10 +351,42 @@ static int idedrv_do_read_irqs(struct idedrv* idedrv, size_t sector, size_t sect
idedrv->current_req = req;
if (idedrv->bm_support) {
size_t rem = sector_count * idedrv->sector_size;
uint32_t phys = idedrv->bounce_buffer_phys;
size_t prd_idx = 0;
while (rem > 0 && prd_idx < idedrv->prdt_entry_count) {
uint32_t chunk = (rem >= 0x10000) ? 0x10000 : rem;
idedrv->prdt[prd_idx].phys_addr = phys;
idedrv->prdt[prd_idx].size = (uint16_t)chunk;
rem -= chunk;
phys += chunk;
idedrv->prdt[prd_idx].rsvd_eot = (rem == 0) ? 0x8000 : 0x0000;
prd_idx++;
}
outl(idedrv->bmbase + IDE_DMA_REG_PRDT, (uint32_t)idedrv->prdt_phys);
outb(idedrv->bmbase + IDE_DMA_REG_CMD, 0x08);
uint8_t status = inb(idedrv->bmbase + IDE_DMA_REG_STATUS);
outb(idedrv->bmbase + IDE_DMA_REG_STATUS, status | IDE_DMA_STATUS_INTR | IDE_DMA_STATUS_ERROR);
ide_prepare(idedrv, sector, sector_count, true);
uint8_t cmd = idedrv->lba48 ? IDE_CMD_READ_DMA48 : IDE_CMD_READ_DMA28;
outb(idedrv->io + IDE_REG_CMD, cmd);
outb(idedrv->bmbase + IDE_DMA_REG_CMD, 0x08 | 0x01);
} else {
ide_prepare(idedrv, sector, sector_count, true);
uint8_t cmd = idedrv->lba48 ? IDE_CMD_READ48 : IDE_CMD_READ28;
outb(idedrv->io + IDE_REG_CMD, cmd);
}
biglock_unlock();
@@ -348,36 +397,39 @@ static int idedrv_do_read_irqs(struct idedrv* idedrv, size_t sector, size_t sect
free(req);
if (idedrv->bm_support)
memcpy(buffer, idedrv->bounce_buffer, sector_count * idedrv->sector_size);
return ST_OK;
}
static int idedrv_do_read_no_irqs(struct idedrv* idedrv, size_t sector, size_t sector_count,
void* buffer) {
if (idedrv->bm_support) {
size_t sectors_done = 0;
size_t rem = sector_count * idedrv->sector_size;
uint32_t phys = idedrv->bounce_buffer_phys;
size_t prd_idx = 0;
while (sectors_done < sector_count) {
size_t chunk_sectors = sector_count - sectors_done;
size_t max_chunk = (16 * PAGE_SIZE) / idedrv->sector_size;
while (rem > 0 && prd_idx < idedrv->prdt_entry_count) {
uint32_t chunk = (rem >= 0x10000) ? 0x10000 : rem;
idedrv->prdt[prd_idx].phys_addr = phys;
idedrv->prdt[prd_idx].size = (uint16_t)chunk;
if (chunk_sectors > max_chunk)
chunk_sectors = max_chunk;
rem -= chunk;
phys += chunk;
size_t byte_count = chunk_sectors * idedrv->sector_size;
idedrv->prdt[0].phys_addr = (uint32_t)idedrv->bounce_buffer_phys;
idedrv->prdt[0].size = (uint16_t)byte_count;
idedrv->prdt[0].rsvd_eot = 0x8000;
idedrv->prdt[prd_idx].rsvd_eot = (rem == 0) ? 0x8000 : 0x0000;
prd_idx++;
}
outl(idedrv->bmbase + IDE_DMA_REG_PRDT, (uint32_t)idedrv->prdt_phys);
outb(idedrv->bmbase + IDE_DMA_REG_CMD, 0x08);
uint8_t status = inb(idedrv->bmbase + IDE_DMA_REG_STATUS);
outb(idedrv->bmbase + IDE_DMA_REG_STATUS,
status | IDE_DMA_STATUS_INTR | IDE_DMA_STATUS_ERROR);
outb(idedrv->bmbase + IDE_DMA_REG_STATUS, status | IDE_DMA_STATUS_INTR | IDE_DMA_STATUS_ERROR);
ide_prepare(idedrv, sector + sectors_done, chunk_sectors, false);
ide_prepare(idedrv, sector, sector_count, false);
uint8_t cmd = idedrv->lba48 ? IDE_CMD_READ_DMA48 : IDE_CMD_READ_DMA28;
outb(idedrv->io + IDE_REG_CMD, cmd);
@@ -402,11 +454,7 @@ static int idedrv_do_read_no_irqs(struct idedrv* idedrv, size_t sector, size_t s
inb(idedrv->io + IDE_REG_STATUS);
memcpy((void*)((uint16_t*)buffer + (sectors_done * (idedrv->sector_size / 2))),
idedrv->bounce_buffer, byte_count);
sectors_done += chunk_sectors;
}
memcpy(buffer, idedrv->bounce_buffer, sector_count * idedrv->sector_size);
} else {
ide_prepare(idedrv, sector, sector_count, false);
@@ -438,9 +486,6 @@ DEFINE_DEVICE_OP(idedrv_read) {
if (sector + sector_count > idedrv->sector_count)
return -ST_OOB_ERROR;
if (idedrv->bm_support && ((sector_count * idedrv->sector_size) >= 16 * PAGE_SIZE))
return -ST_OOB_ERROR;
if (idedrv->current_req != NULL)
return -ST_TRY_AGAIN;
@@ -468,6 +513,39 @@ static int idedrv_do_write_irqs(struct idedrv* idedrv, size_t sector, size_t sec
idedrv->current_req = req;
if (idedrv->bm_support) {
memcpy(idedrv->bounce_buffer, buffer, sector_count * idedrv->sector_size);
size_t rem = sector_count * idedrv->sector_size;
uint32_t phys = idedrv->bounce_buffer_phys;
size_t prd_idx = 0;
while (rem > 0 && prd_idx < idedrv->prdt_entry_count) {
uint32_t chunk = (rem >= 0x10000) ? 0x10000 : rem;
idedrv->prdt[prd_idx].phys_addr = phys;
idedrv->prdt[prd_idx].size = (uint16_t)chunk;
rem -= chunk;
phys += chunk;
idedrv->prdt[prd_idx].rsvd_eot = (rem == 0) ? 0x8000 : 0x0000;
prd_idx++;
}
outl(idedrv->bmbase + IDE_DMA_REG_PRDT, (uint32_t)idedrv->prdt_phys);
outb(idedrv->bmbase + IDE_DMA_REG_CMD, 0x00);
uint8_t status = inb(idedrv->bmbase + IDE_DMA_REG_STATUS);
outb(idedrv->bmbase + IDE_DMA_REG_STATUS, status | IDE_DMA_STATUS_INTR | IDE_DMA_STATUS_ERROR);
ide_prepare(idedrv, sector, sector_count, true);
uint8_t cmd = idedrv->lba48 ? IDE_CMD_WRITE_DMA48 : IDE_CMD_WRITE_DMA28;
outb(idedrv->io + IDE_REG_CMD, cmd);
outb(idedrv->bmbase + IDE_DMA_REG_CMD, 0x01);
} else {
ide_prepare(idedrv, sector, sector_count, true);
uint8_t cmd = idedrv->lba48 ? IDE_CMD_WRITE48 : IDE_CMD_WRITE28;
@@ -482,6 +560,7 @@ static int idedrv_do_write_irqs(struct idedrv* idedrv, size_t sector, size_t sec
outsw(idedrv->io + IDE_REG_DATA, buffer, idedrv->sector_size / 2);
req->sector_done_count = 1;
}
biglock_unlock();
@@ -500,26 +579,26 @@ static int idedrv_do_write_no_irqs(struct idedrv* idedrv, size_t sector, size_t
if (idedrv->bm_support) {
memcpy(idedrv->bounce_buffer, buffer, sector_count * idedrv->sector_size);
size_t sectors_done = 0;
size_t rem = sector_count * idedrv->sector_size;
uint32_t phys = idedrv->bounce_buffer_phys;
size_t prd_idx = 0;
while (sectors_done < sector_count) {
size_t chunk_sectors = sector_count - sectors_done;
size_t max_chunk = (16 * PAGE_SIZE) / idedrv->sector_size;
while (rem > 0 && idedrv->prdt_entry_count) {
uint32_t chunk = (rem >= 0x10000) ? 0x10000 : rem;
idedrv->prdt[prd_idx].phys_addr = phys;
idedrv->prdt[prd_idx].size = (uint16_t)chunk;
if (chunk_sectors > max_chunk)
chunk_sectors = max_chunk;
rem -= chunk;
phys += chunk;
size_t byte_count = chunk_sectors * idedrv->sector_size;
idedrv->prdt[0].phys_addr = (uint32_t)idedrv->bounce_buffer_phys;
idedrv->prdt[0].size = (uint16_t)byte_count;
idedrv->prdt[0].rsvd_eot = 0x8000;
idedrv->prdt[prd_idx].rsvd_eot = (rem == 0) ? 0x8000 : 0x0000;
prd_idx++;
}
uint8_t status = inb(idedrv->bmbase + IDE_DMA_REG_STATUS);
outb(idedrv->bmbase + IDE_DMA_REG_STATUS,
status | IDE_DMA_STATUS_INTR | IDE_DMA_STATUS_ERROR);
outb(idedrv->bmbase + IDE_DMA_REG_STATUS, status | IDE_DMA_STATUS_INTR | IDE_DMA_STATUS_ERROR);
ide_prepare(idedrv, sector + sectors_done, chunk_sectors, false);
ide_prepare(idedrv, sector, sector_count, false);
uint8_t cmd = idedrv->lba48 ? IDE_CMD_WRITE_DMA48 : IDE_CMD_WRITE_DMA28;
outb(idedrv->io + IDE_REG_CMD, cmd);
@@ -543,9 +622,6 @@ static int idedrv_do_write_no_irqs(struct idedrv* idedrv, size_t sector, size_t
outb(idedrv->bmbase + IDE_DMA_REG_CMD, 0x00);
inb(idedrv->io + IDE_REG_STATUS);
sectors_done += chunk_sectors;
}
} else {
ide_prepare(idedrv, sector, sector_count, false);
@@ -579,9 +655,6 @@ DEFINE_DEVICE_OP(idedrv_write) {
if (sector + sector_count > idedrv->sector_count)
return -ST_OOB_ERROR;
if (idedrv->bm_support && ((sector_count * idedrv->sector_size) >= 16 * PAGE_SIZE))
return -ST_OOB_ERROR;
if (idedrv->current_req != NULL)
return -ST_TRY_AGAIN;