From 616ba40a14f492bba5f15b545f5c5655b2b36c52 Mon Sep 17 00:00:00 2001 From: kamkow1 Date: Thu, 30 Apr 2026 18:11:21 +0200 Subject: [PATCH] idedrv Implement DMA reading/writing with interrupts --- kernel/device/storage/idedrv.c | 263 +++++++++++++++++++++------------ 1 file changed, 168 insertions(+), 95 deletions(-) diff --git a/kernel/device/storage/idedrv.c b/kernel/device/storage/idedrv.c index c7667fe..b54314e 100644 --- a/kernel/device/storage/idedrv.c +++ b/kernel/device/storage/idedrv.c @@ -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; - ide_prepare(idedrv, sector, sector_count, true); + if (idedrv->bm_support) { + size_t rem = sector_count * idedrv->sector_size; + uint32_t phys = idedrv->bounce_buffer_phys; + size_t prd_idx = 0; - uint8_t cmd = idedrv->lba48 ? IDE_CMD_READ48 : IDE_CMD_READ28; - outb(idedrv->io + IDE_REG_CMD, cmd); + 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,65 +397,64 @@ 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[prd_idx].rsvd_eot = (rem == 0) ? 0x8000 : 0x0000; + prd_idx++; + } - 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; + outl(idedrv->bmbase + IDE_DMA_REG_PRDT, (uint32_t)idedrv->prdt_phys); - outl(idedrv->bmbase + IDE_DMA_REG_PRDT, (uint32_t)idedrv->prdt_phys); + outb(idedrv->bmbase + IDE_DMA_REG_CMD, 0x08); - 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); - 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, false); - ide_prepare(idedrv, sector + sectors_done, chunk_sectors, false); + uint8_t cmd = idedrv->lba48 ? IDE_CMD_READ_DMA48 : IDE_CMD_READ_DMA28; + outb(idedrv->io + IDE_REG_CMD, cmd); - 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); - outb(idedrv->bmbase + IDE_DMA_REG_CMD, 0x08 | 0x01); + for (;;) { + uint8_t bm_status = inb(idedrv->bmbase + IDE_DMA_REG_STATUS); - for (;;) { - uint8_t bm_status = inb(idedrv->bmbase + IDE_DMA_REG_STATUS); + if (!(bm_status & IDE_DMA_STATUS_ACTIVE)) + break; - if (!(bm_status & IDE_DMA_STATUS_ACTIVE)) - break; - - if ((bm_status & IDE_DMA_STATUS_ERROR)) { - outb(idedrv->bmbase + IDE_DMA_REG_CMD, 0x00); - return -ST_XDRV_READ_ERROR; - } - - spin_lock_relax(); + if ((bm_status & IDE_DMA_STATUS_ERROR)) { + outb(idedrv->bmbase + IDE_DMA_REG_CMD, 0x00); + return -ST_XDRV_READ_ERROR; } - outb(idedrv->bmbase + IDE_DMA_REG_CMD, 0x00); - - 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; + spin_lock_relax(); } + + outb(idedrv->bmbase + IDE_DMA_REG_CMD, 0x00); + + inb(idedrv->io + IDE_REG_STATUS); + + 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,21 +513,55 @@ static int idedrv_do_write_irqs(struct idedrv* idedrv, size_t sector, size_t sec idedrv->current_req = req; - ide_prepare(idedrv, sector, sector_count, true); + if (idedrv->bm_support) { + memcpy(idedrv->bounce_buffer, buffer, sector_count * idedrv->sector_size); - uint8_t cmd = idedrv->lba48 ? IDE_CMD_WRITE48 : IDE_CMD_WRITE28; - outb(idedrv->io + IDE_REG_CMD, cmd); + size_t rem = sector_count * idedrv->sector_size; + uint32_t phys = idedrv->bounce_buffer_phys; + size_t prd_idx = 0; - if (!ide_wait(idedrv->io, 100000, true, true)) { - idedrv->current_req = NULL; - free(req); - return -ST_XDRV_WRITE_ERROR; + 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; + outb(idedrv->io + IDE_REG_CMD, cmd); + + if (!ide_wait(idedrv->io, 100000, true, true)) { + idedrv->current_req = NULL; + free(req); + return -ST_XDRV_WRITE_ERROR; + } + + outsw(idedrv->io + IDE_REG_DATA, buffer, idedrv->sector_size / 2); + + req->sector_done_count = 1; } - outsw(idedrv->io + IDE_REG_DATA, buffer, idedrv->sector_size / 2); - - req->sector_done_count = 1; - biglock_unlock(); while (!atomic_load(&req->done)) @@ -500,52 +579,49 @@ 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[prd_idx].rsvd_eot = (rem == 0) ? 0x8000 : 0x0000; + prd_idx++; + } - 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; + 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); - 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, false); - ide_prepare(idedrv, sector + sectors_done, chunk_sectors, false); + uint8_t cmd = idedrv->lba48 ? IDE_CMD_WRITE_DMA48 : IDE_CMD_WRITE_DMA28; + outb(idedrv->io + IDE_REG_CMD, cmd); - 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); - outb(idedrv->bmbase + IDE_DMA_REG_CMD, 0x01); + for (;;) { + uint8_t bm_status = inb(idedrv->bmbase + IDE_DMA_REG_STATUS); - for (;;) { - uint8_t bm_status = inb(idedrv->bmbase + IDE_DMA_REG_STATUS); + if (!(bm_status & IDE_DMA_STATUS_ACTIVE)) + break; - if (!(bm_status & IDE_DMA_STATUS_ACTIVE)) - break; - - if ((bm_status & IDE_DMA_STATUS_ERROR)) { - outb(idedrv->bmbase + IDE_DMA_REG_CMD, 0x00); - return -ST_XDRV_WRITE_ERROR; - } - - spin_lock_relax(); + if ((bm_status & IDE_DMA_STATUS_ERROR)) { + outb(idedrv->bmbase + IDE_DMA_REG_CMD, 0x00); + return -ST_XDRV_WRITE_ERROR; } - outb(idedrv->bmbase + IDE_DMA_REG_CMD, 0x00); - - inb(idedrv->io + IDE_REG_STATUS); - - sectors_done += chunk_sectors; + spin_lock_relax(); } + + outb(idedrv->bmbase + IDE_DMA_REG_CMD, 0x00); + + inb(idedrv->io + IDE_REG_STATUS); } 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;