Skip to content

Commit

Permalink
Respect memory boundaries on ahci
Browse files Browse the repository at this point in the history
  • Loading branch information
malwarepad committed Dec 27, 2024
1 parent 383186c commit 5e4a32f
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 19 deletions.
83 changes: 69 additions & 14 deletions src/kernel/drivers/ahci.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,27 +132,82 @@ 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);
cmdtbl->prdt_entry[i].dbc =
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): */
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
7 changes: 6 additions & 1 deletion src/kernel/drivers/disk.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 2 additions & 4 deletions src/kernel/include/ahci.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "pci.h"
#include "types.h"
#include "util.h"

#ifndef AHCI_H
#define AHCI_H
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 5e4a32f

Please sign in to comment.