From 5e4a32ffba35b53746a0541c1f317890aa77118f Mon Sep 17 00:00:00 2001 From: Panagiotis Date: Fri, 27 Dec 2024 19:32:27 +0200 Subject: [PATCH] Respect memory boundaries on ahci --- src/kernel/drivers/ahci.c | 83 ++++++++++++++++++++++++++++++++------- src/kernel/drivers/disk.c | 7 +++- src/kernel/include/ahci.h | 6 +-- 3 files changed, 77 insertions(+), 19 deletions(-) diff --git a/src/kernel/drivers/ahci.c b/src/kernel/drivers/ahci.c index bb4aab71..f0d78ba5 100644 --- a/src/kernel/drivers/ahci.c +++ b/src/kernel/drivers/ahci.c @@ -132,11 +132,57 @@ force_inline HBA_CMD_TBL *ahciSetUpCmdTable(ahci *ahciPtr, return cmdtbl; } +uint16_t ahciCalcPrdts(void *buff, uint32_t sectors) { + size_t bytes = sectors << 9; + size_t start = (size_t)buff; + size_t end = start + bytes; + + uint16_t prdt_count = 0; + size_t current = start; + + while (current < end) { + // max space for current prdt + size_t prdt_limit = + ((current + AHCI_BYTES_PER_PRDT) & ~(AHCI_BYTES_PER_PRDT - 1)) - + current; + size_t page_limit = ((current + PAGE_SIZE) & ~(PAGE_SIZE - 1)) - current; + + // find smallest limit (ass, ik) + size_t chunk_size = MIN(prdt_limit, page_limit); + chunk_size = MIN(chunk_size, end - current); + + // traverse + current += chunk_size; + prdt_count++; + } + + if (prdt_count > AHCI_PRDTS) { + debugf("[ahci] Wrong calculations inside disk.c! prdts{%lx} max{%lx}\n", + prdt_count, AHCI_PRDTS); + panic(); + } + + return prdt_count; +} + force_inline void ahciSetUpPRDT(HBA_CMD_HEADER *cmdheader, HBA_CMD_TBL *cmdtbl, uint16_t *buff, uint32_t count) { - // 4 MiB (16 sectors) per PRDT + size_t totalBytes = count << 9; size_t i = 0; - for (i = 0; i < cmdheader->prdtl - 1; i++) { + if (!IS_ALIGNED((size_t)buff, 0x1000)) { + size_t rounded = ((((size_t)buff) + 4095) & ~4095); + size_t needed = MIN(rounded - (size_t)buff, totalBytes); // min(gap, limit) + size_t targPhys = VirtualToPhysical((size_t)buff); + cmdtbl->prdt_entry[i].dba = SPLIT_64_LOWER(targPhys); + cmdtbl->prdt_entry[i].dbau = SPLIT_64_HIGHER(targPhys); + cmdtbl->prdt_entry[i].dbc = needed - 1; // 512 bytes per sector + cmdtbl->prdt_entry[i].i = 1; + totalBytes -= needed; + buff = (void *)((size_t)buff + needed); + i++; + } + // 4 MiB (16 sectors) per PRDT + for (; i < cmdheader->prdtl - 1; i++) { size_t targPhys = VirtualToPhysical((size_t)buff); cmdtbl->prdt_entry[i].dba = SPLIT_64_LOWER(targPhys); cmdtbl->prdt_entry[i].dbau = SPLIT_64_HIGHER(targPhys); @@ -144,15 +190,24 @@ force_inline void ahciSetUpPRDT(HBA_CMD_HEADER *cmdheader, HBA_CMD_TBL *cmdtbl, AHCI_BYTES_PER_PRDT - 1; // 4 MiB (this value should always be set to 1 // less than the actual value) cmdtbl->prdt_entry[i].i = 1; - buff += AHCI_BYTES_PER_PRDT / 2; // appropriate words - count -= AHCI_BYTES_PER_PRDT / 512; // appropriate sectors + buff += AHCI_BYTES_PER_PRDT / 2; // appropriate words + // count -= AHCI_BYTES_PER_PRDT / 512; // appropriate sectors + totalBytes -= AHCI_BYTES_PER_PRDT; + } + if (totalBytes > 0) { + size_t targPhys = VirtualToPhysical((size_t)buff); + // Last entry + cmdtbl->prdt_entry[i].dba = SPLIT_64_LOWER(targPhys); + cmdtbl->prdt_entry[i].dbau = SPLIT_64_HIGHER(targPhys); + cmdtbl->prdt_entry[i].dbc = totalBytes - 1; // 512 bytes per sector + cmdtbl->prdt_entry[i].i = 1; + } + + if (totalBytes > 0x1000) { + debugf("[ahci] FATAL! Mis-calculation, left totalBytes{%ld} > 0x1000!\n", + totalBytes); + panic(); } - size_t targPhys = VirtualToPhysical((size_t)buff); - // Last entry - cmdtbl->prdt_entry[i].dba = SPLIT_64_LOWER(targPhys); - cmdtbl->prdt_entry[i].dbau = SPLIT_64_HIGHER(targPhys); - cmdtbl->prdt_entry[i].dbc = (count << 9) - 1; // 512 bytes per sector - cmdtbl->prdt_entry[i].i = 1; } /* Port initialization (used only on startup): */ @@ -285,8 +340,8 @@ bool ahciRead(ahci *ahciPtr, uint32_t portId, HBA_PORT *port, uint32_t startl, if (slot == -1) return false; - HBA_CMD_HEADER *cmdheader = - ahciSetUpCmdHeader(ahciPtr, portId, slot, AHCI_CALC_PRDT(count), false); + HBA_CMD_HEADER *cmdheader = ahciSetUpCmdHeader( + ahciPtr, portId, slot, ahciCalcPrdts(buff, count), false); HBA_CMD_TBL *cmdtbl = ahciSetUpCmdTable(ahciPtr, cmdheader, portId, slot); ahciSetUpPRDT(cmdheader, cmdtbl, (uint16_t *)buff, count); @@ -322,8 +377,8 @@ bool ahciWrite(ahci *ahciPtr, uint32_t portId, HBA_PORT *port, uint32_t startl, if (slot == -1) return false; - HBA_CMD_HEADER *cmdheader = - ahciSetUpCmdHeader(ahciPtr, portId, slot, AHCI_CALC_PRDT(count), true); + HBA_CMD_HEADER *cmdheader = ahciSetUpCmdHeader( + ahciPtr, portId, slot, ahciCalcPrdts(buff, count), true); HBA_CMD_TBL *cmdtbl = ahciSetUpCmdTable(ahciPtr, cmdheader, portId, slot); ahciSetUpPRDT(cmdheader, cmdtbl, (uint16_t *)buff, count); diff --git a/src/kernel/drivers/disk.c b/src/kernel/drivers/disk.c index c77cbc51..9ad2265f 100644 --- a/src/kernel/drivers/disk.c +++ b/src/kernel/drivers/disk.c @@ -56,9 +56,14 @@ void diskBytes(uint8_t *target_address, uint32_t LBA, uint32_t sector_count, // todo: allow concurrent stuff force_inline void diskBytesMax(uint8_t *target_address, uint32_t LBA, size_t sector_count, bool write) { + int prdtAmnt = AHCI_PRDTS; + + if (!IS_ALIGNED((size_t)target_address, 0x1000)) + prdtAmnt--; // we later fill in for head + // calculated by: (bytesPerPRDT * PRDTamnt) / SECTOR_SIZE // ( 4MiB * 8 ) / 512 - size_t max = (AHCI_BYTES_PER_PRDT * AHCI_PRDTS) / SECTOR_SIZE; + size_t max = (AHCI_BYTES_PER_PRDT * prdtAmnt) / SECTOR_SIZE; size_t chunks = sector_count / max; size_t remainder = sector_count % max; diff --git a/src/kernel/include/ahci.h b/src/kernel/include/ahci.h index c15e5b0e..aa46ccbe 100644 --- a/src/kernel/include/ahci.h +++ b/src/kernel/include/ahci.h @@ -1,5 +1,6 @@ #include "pci.h" #include "types.h" +#include "util.h" #ifndef AHCI_H #define AHCI_H @@ -545,14 +546,11 @@ typedef struct tagHBA_CMD_TBL { // my defs (there are problems with PRDTS > 256) #define AHCI_PRDTS (256) -#define AHCI_BYTES_PER_PRDT (4194304 / 1024) +#define AHCI_BYTES_PER_PRDT (0x1000) #define AHCI_MEM_TABLE (64 + 16 + 48 + 16 * AHCI_PRDTS) #define AHCI_MEM_ALL_TABLES (AHCI_MEM_TABLE * 32) -#define AHCI_CALC_PRDT(sectors) \ - (DivRoundUp((sectors) * 512, AHCI_BYTES_PER_PRDT)) - typedef enum { FIS_TYPE_REG_H2D = 0x27, // Register FIS - host to device FIS_TYPE_REG_D2H = 0x34, // Register FIS - device to host