My Project
Loading...
Searching...
No Matches
ahci.c
Go to the documentation of this file.
1/*
2 * PROJECT: MatanelOS Kernel
3 * LICENSE: GPLv3
4 * PURPOSE: AHCI Driver Implementation.
5 */
6
7#include "ahci.h"
8#include "../../assert.h"
9#include "../../includes/mg.h"
10#include "../../includes/mm.h"
11
12#ifdef REMINDER
13_Static_assert(false, "Reminder: AHCI, and other DMA stuff DEAL WITH PHYSICAL ADDRESSES ONLY! not virtual, so supply to them the translated addresses.");
14#endif
15
16//#define AHCI_DEBUG_PRINT
17
18#ifdef AHCI_DEBUG_PRINT
19static void decode_serr(uint32_t serr) {
20 if (serr == 0) {
21 // gop_printf(0xFFFFFF00, "SERR: No errors\n");
22 return;
23 }
24
25 // gop_printf(0xFFFF0000, "SERR: 0x%08x - Errors detected:\n", serr);
26
27 // ERR bits (0-15) - Recoverable and non-recoverable errors
28 //if (serr & (1 << 0)) // gop_printf(0xFFFFFF00, " [0] ERR.I - Recovered Data Integrity Error\n");
29 //if (serr & (1 << 1)) // gop_printf(0xFFFFFF00, " [1] ERR.M - Recovered Communications Error\n");
30 //if (serr & (1 << 8)) // gop_printf(0xFFFFFF00, " [8] ERR.T - Transient Data Integrity Error\n");
31 //if (serr & (1 << 9)) // gop_printf(0xFFFFFF00, " [9] ERR.C - Persistent Communication/Data Integrity Error\n");
32 //if (serr & (1 << 10)) // gop_printf(0xFFFFFF00, " [10] ERR.P - Protocol Error\n");
33 //if (serr & (1 << 11)) // gop_printf(0xFFFFFF00, " [11] ERR.E - Internal Error\n");
34 //
36 //if (serr & (1 << 16)) // gop_printf(0xFFFFFF00, " [16] DIAG.N - PhyRdy Change\n");
37 //if (serr & (1 << 17)) // gop_printf(0xFFFFFF00, " [17] DIAG.I - Phy Internal Error\n");
38 //if (serr & (1 << 18)) // gop_printf(0xFFFFFF00, " [18] DIAG.W - Comm Wake\n");
39 //if (serr & (1 << 19)) // gop_printf(0xFFFFFF00, " [19] DIAG.B - 10B to 8B Decode Error\n");
40 //if (serr & (1 << 20)) // gop_printf(0xFFFFFF00, " [20] DIAG.D - Disparity Error\n");
41 //if (serr & (1 << 21)) // gop_printf(0xFFFFFF00, " [21] DIAG.C - CRC Error\n");
42 //if (serr & (1 << 22)) // gop_printf(0xFFFFFF00, " [22] DIAG.H - Handshake Error\n");
43 //if (serr & (1 << 23)) // gop_printf(0xFFFFFF00, " [23] DIAG.S - Link Sequence Error\n");
44 //if (serr & (1 << 24)) // gop_printf(0xFFFFFF00, " [24] DIAG.T - Transport State Transition Error\n");
45 //if (serr & (1 << 25)) // gop_printf(0xFFFFFF00, " [25] DIAG.F - Unknown FIS Type\n");
46 //if (serr & (1 << 26)) // gop_printf(0xFFFFFF00, " [26] DIAG.X - Exchanged\n");
47}
48#endif
49// Context per initialized port
50typedef struct _AHCI_PORT_CTX {
51 HBA_PORT* port; // MMIO base for this port
52 HBA_CMD_TBL* cmd_tbl; // Command table memory
53 void* clb; // Cmd list buffer
54 void* fis; // FIS receive buffer
55 BLOCK_DEVICE bdev; // Associated BLOCK_DEVICE interface
57
58static HBA_MEM* hba_mem;
59static AHCI_PORT_CTX ports[AHCI_MAX_PORTS];
60static int port_count;
61
62// Invalidate cache ranges of the CPU to ensure newest data is fetched from RAM.
63static inline void cache_flush_invalidate_range(void* addr, size_t len) {
64 uintptr_t p = (uintptr_t)addr & ~(uintptr_t)63;
65 uintptr_t end = (uintptr_t)addr + len;
66 for (; p < end; p += 64) {
67 __asm__ volatile("clflush (%0)" :: "r"((void*)p) : "memory");
68 }
69 __asm__ volatile("mfence" ::: "memory");
70}
71
72static inline void outl_port(uint16_t port, uint32_t val) {
73 __asm__ volatile("outl %0, %1" :: "a"(val), "d"(port));
74}
75static inline uint32_t inl_port(uint16_t port) {
76 uint32_t val;
77 __asm__ volatile("inl %1, %0" : "=a"(val) : "d"(port));
78 return val;
79}
80static uint32_t pci_cfg_read32(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset) {
81 uint32_t addr = (1u << 31) | ((uint32_t)bus << 16) | ((uint32_t)slot << 11) |
82 ((uint32_t)func << 8) | (offset & 0xFC);
83 outl_port(0xCF8, addr);
84 return inl_port(0xCFC);
85}
86static void pci_cfg_write32(uint8_t bus, uint8_t slot, uint8_t func, uint8_t offset, uint32_t val) {
87 uint32_t addr = (1u << 31) | ((uint32_t)bus << 16) | ((uint32_t)slot << 11) |
88 ((uint32_t)func << 8) | (offset & 0xFC);
89 outl_port(0xCF8, addr);
90 outl_port(0xCFC, val);
91}
92
93// Scans PCI buses and enables Bus Master bit for first AHCI class device found.
94// Call this at start of ahci_init() before enable_controller().
95static void ensure_ahci_busmaster_enabled(void) {
96 for (uint8_t bus = 0; bus < 8; ++bus) {
97 for (uint8_t slot = 0; slot < 32; ++slot) {
98 for (uint8_t func = 0; func < 8; ++func) {
99 uint32_t d0 = pci_cfg_read32(bus, slot, func, 0x00);
100 if ((d0 & 0xFFFF) == 0xFFFF) continue; // no device
101 uint32_t cl = pci_cfg_read32(bus, slot, func, 0x08);
102 uint8_t base_class = (cl >> 24) & 0xFF;
103 uint8_t sub_class = (cl >> 16) & 0xFF;
104 uint8_t prog_if = (cl >> 8) & 0xFF;
105 if (base_class == 0x01 && sub_class == 0x06 && prog_if == 0x01) {
106#ifdef AHCI_DEBUG_PRINT
107 uint32_t hdr = pci_cfg_read32(bus, slot, func, 0x00);
108 uint16_t vendor = hdr & 0xFFFF;
109 uint16_t device = (hdr >> 16) & 0xFFFF;
110#endif
111 uint32_t cmd32 = pci_cfg_read32(bus, slot, func, 0x04);
112 uint16_t cmd = cmd32 & 0xFFFF;
113#ifdef AHCI_DEBUG_PRINT
114 // gop_printf(0xFFFFFF00, "AHCI PCI at %p:%p vendor=%p device=%p\n", bus, slot, func, vendor, device);
115 // gop_printf(0xFFFFFF00, "PCI CMD before: %p\n", cmd);
116#endif
117 if (!(cmd & (1 << 2))) {
118 cmd |= (1 << 2); // set Bus Master
119 pci_cfg_write32(bus, slot, func, 0x04, (cmd32 & 0xFFFF0000) | (uint32_t)cmd);
120#ifdef AHCI_DEBUG_PRINT
121 // gop_printf(0xFFFFFF00, "Enabled PCI Bus Master bit for AHCI\n");
122#endif
123 }
124 else {
125#ifdef AHCI_DEBUG_PRINT
126 // gop_printf(0xFFFFFF00, "PCI Bus Master already enabled\n");
127#endif
128 }
129 return;
130 }
131 }
132 }
133 }
134#ifdef AHCI_DEBUG_PRINT
135 // gop_printf(0xFFFF0000, "AHCI PCI device not found while scanning PCI bus\n");
136#endif
137}
138
144static int find_free_slot(uint32_t mask) {
145 for (int i = 0; i < 32; i++) {
146 if (!(mask & (1u << i))) {
147 return i;
148 }
149 }
150 return -1;
151}
152
156static void enable_controller(void) {
157 hba_mem->ghc |= (1u << 31); // AHCI Enable.
158 hba_mem->ghc |= (1u << 0); // Global Reset.
160 while (hba_mem->ghc & (1u << 0));
161}
162
168static bool init_one_port(int idx) {
169 HBA_PORT* p = (HBA_PORT*)((uint8_t*)hba_mem + 0x100 + idx * 0x80);
170 uint32_t status = p->ssts;
171 if ((status & 0x0F) != 3) return false; // no device present
172
173 // Stop the port before configuration
174 p->cmd &= ~(1u << 0); // Clear ST (START)
175 p->cmd &= ~(1u << 4); // Clear PRE (FIS Receive Enable)
176
177 // Wait until port is idle
178 while ((p->cmd & (1u << 15)) || (p->cmd & (1u << 14))) {
179 __pause();
180 }
181
182 // Allocate and zero CLB (1 KiB)
183 void* clb = MmAllocateContigiousMemory(1024, UINT64_T_MAX);
184 if (!clb) return false;
185 kmemset(clb, 0, 1024);
186 // pass the PHYSICAL address.
187 uintptr_t clb_phys = MiTranslateVirtualToPhysical(clb);
188 assert(((uintptr_t)clb_phys & 0x3FF) == 0, "CLB must be 1KiB-aligned (1024 byte multiple)");
189#ifdef AHCI_DEBUG_PRINT
190 // gop_printf(COLOR_BLUE, "In INIT_ONE_PORT, clb_phys: %p | virt: %p\n", clb_phys, clb);
191#endif
192 p->clb = (uint32_t)(uintptr_t)clb_phys;
193 p->clbu = (uint32_t)((uintptr_t)clb_phys >> 32);
194
195 // Allocate and zero FIS receive buffer (256 B)
196 void* fis_buf = MmAllocateContigiousMemory(256, UINT64_T_MAX);
197 if (!fis_buf) return false;
198 kmemset(fis_buf, 0, 256);
199 // Again, pass the PHYSICAL.
200 uintptr_t fis_buf_phys = MiTranslateVirtualToPhysical(fis_buf);
201#ifdef AHCI_DEBUG_PRINT
202 // gop_printf(COLOR_BLUE, "In INIT_ONE_PORT, fis_buf_phys: %p | virt: %p\n", fis_buf_phys, fis_buf);
203#endif
204 p->fb = (uint32_t)(uintptr_t)fis_buf_phys;
205 p->fbu = (uint32_t)((uintptr_t)fis_buf_phys >> 32);
206
207 // Allocate and zero Command Table buffers: 256 B × 32 slots
208 size_t tbl_size = 256 * 32;
209 void* cmd_tbl = MmAllocateContigiousMemory(tbl_size, UINT64_T_MAX);
210 if (!cmd_tbl) return false;
211 kmemset(cmd_tbl, 0, tbl_size);
212 uintptr_t cmd_tbl_phys = MiTranslateVirtualToPhysical(cmd_tbl);
213 assert(((uintptr_t)cmd_tbl_phys & 0xFF) == 0, "Command table block must be 256-byte aligned");
214#ifdef AHCI_DEBUG_PRINT
215 // gop_printf(COLOR_BLUE, "In INIT_ONE_PORT, cmd_tbl_phys: %p | virt: %p\n", cmd_tbl_phys, cmd_tbl);
216#endif
217
218 // Point each command header to its table
219 for (int slot = 0; slot < 32; slot++) {
220 // Header at clb + slot*32 bytes
221 HBA_CMD_HEADER* hdr = (HBA_CMD_HEADER*)((uint8_t*)clb + slot * sizeof(HBA_CMD_HEADER));
222 uintptr_t tbl_pa_phys = (uintptr_t)cmd_tbl_phys + slot * 256;
223 hdr->ctba = (uint32_t)(tbl_pa_phys & 0xFFFFFFFF);
224 hdr->ctbau = (uint32_t)(tbl_pa_phys >> 32);
225 hba_cmd_hdr_set_prdtl(hdr, 1); // one PRDT entry
226 }
227
228 // Clear any old errors and start the port
229 p->serr = ~0U; // Clear all SERROR bits by writing 1 to them.
230 p->cmd |= (1u << 4); // Set FRE
231 p->cmd |= (1u << 0); // Set ST
232
233 // Add this assertion to ensure the port actually starts
234 assert((p->cmd & 1) != 0, "Port ST bit failed to set!");
235
236 // Save context
237 AHCI_PORT_CTX* ctx = &ports[port_count];
238 ctx->port = p;
239 ctx->clb = clb;
240 ctx->fis = fis_buf;
241 ctx->cmd_tbl = cmd_tbl;
244 ctx->bdev.dev_data = ctx;
245
246 /* CAP and slot counts */
247#ifdef AHCI_DEBUG_PRINT
248 uint32_t cap = (uint32_t)hba_mem->cap;
249 uint32_t ncs = (cap >> 8) & 0x1Fu;
250 assert(((ncs + 1) >= 1) && ((ncs + 1) <= 32), "CAP.NCS invalid (command slots out of range)");
251
252 bool s64a = !!((cap >> 31) & 1u);
253
254 /* Alignment checks */
255 assert(((uintptr_t)clb_phys & 0x3FF) == 0, "PxCLB must be 1KiB-aligned (1024 bytes)");
256 assert(((uintptr_t)fis_buf_phys & 0xFF) == 0, "PxFB (FIS) must be 256-byte aligned");
257 assert(((uintptr_t)cmd_tbl_phys & 0xFF) == 0, "Command table region must start at 256-byte boundary");
258
259 /* 64-bit addressing: if device doesn't advertise S64A, upper bits must be zero */
260 if (!s64a) {
261 assert(((uintptr_t)clb_phys >> 32) == 0, "CLB high dword must be zero when CAP.S64A==0");
262 assert(((uintptr_t)cmd_tbl_phys >> 32) == 0, "CMD_TBL high dword must be zero when CAP.S64A==0");
263 assert(((uintptr_t)fis_buf_phys >> 32) == 0, "FIS high dword must be zero when CAP.S64A==0");
264 }
265#endif
266 /* Per-header CTBA programmed correctly (for the number of slots the HBA advertises) */
267#ifdef AHCI_DEBUG_PRINT
268 for (unsigned sl = 0; sl <= ncs; ++sl) {
269#ifdef DEBUG
270 HBA_CMD_HEADER* hdr = (HBA_CMD_HEADER*)((uint8_t*)clb + sl * sizeof(HBA_CMD_HEADER));
271 uintptr_t expected = (uintptr_t)cmd_tbl_phys + sl * 256;
272#endif
273 assert(hdr->ctba == (uint32_t)(expected & 0xFFFFFFFFu), "Header CTBA low doesn't match expected CTBA");
274 if (s64a) {
275 assert(hdr->ctbau == (uint32_t)(expected >> 32), "Header CTBAU mismatch (S64A advertised)");
276 }
277 else {
278 assert(hdr->ctbau == 0, "Header CTBAU must be zero when S64A==0");
279 }
280 }
281#endif
282
283 port_count++;
284 return true;
285}
286
288extern GOP_PARAMS gop_local;
289
290bool ahci_initialized = false;
291
293 if (ahci_initialized) { return MT_SUCCESS; } // gop_printf(COLOR_RED, "AHCI Initialization got called again when already init.\n"); return MT_SUCCESS; }
294 // Use BootInfo PCI BARs.
295 for (size_t i = 0; i < boot_info_local.AhciCount; i++) {
296 uint64_t base = boot_info_local.AhciBarBases[i];
297 void* virt = MmMapIoSpace(base, VirtualPageSize, MmNonCached);
298#ifdef AHCI_DEBUG_PRINT
299 // gop_printf(COLOR_ORANGE, "Address of AHCI BAR %u (%p) is: %s\n", i, virt, MmIsAddressPresent((uintptr_t)virt) ? "Valid" : "Invalid");
300#endif
301 // Now change the values in the struct
302 boot_info_local.AhciBarBases[i] = (uint64_t)virt;
303 }
304
305 uint64_t bar = boot_info_local.AhciBarBases[0];
306 hba_mem = (HBA_MEM*)(uintptr_t)bar;
307#ifdef AHCI_DEBUG_PRINT
308 // gop_printf(0xFF00FFFF, "About to touch AHCI %u at %p | It's %s\n",0, hba_mem, MmIsAddressPresent((uintptr_t)bar) ? "Valid" : "Invalid");
309 //_cli(); __hlt();
310#endif
311 ensure_ahci_busmaster_enabled();
312 enable_controller();
313 port_count = 0; // Start from 0.
314 uint32_t pi = hba_mem->pi;
315
316 for (int idx = 0; idx < AHCI_MAX_PORTS; idx++) {
317 if (pi & (1u << idx)) {
318 init_one_port(idx);
319 }
320 }
321
322 // Register ALL block devices.
323 for (int i = 0; i < port_count; i++) {
324 register_block_device(&ports[i].bdev);
325 }
326 ahci_initialized = true;
327 return port_count > 0 ? MT_SUCCESS : MT_AHCI_PORT_FAILURE; // If it could register a port, it will return true, if it couldn't, it will return false (bugcheck)
328}
329
330MTSTATUS ahci_read_sector(BLOCK_DEVICE* dev, uint32_t lba, void* buf, size_t bytes) {
331
332 // 1. Input Validation
333 if (bytes == 0 || (bytes % 512 != 0)) {
334 // ATA DMA transfers must typically be sector-aligned
335 return MT_INVALID_PARAM;
336 }
337
339 HBA_PORT* p = ctx->port;
340
341 // 2. Clear Pending Interrupts
342 p->is = (uint32_t)-1;
343
344 int slot = find_free_slot(p->sact | p->ci);
345 if (slot < 0) return MT_AHCI_PORT_FAILURE;
346
347 uint32_t spin = 0;
348 const uint32_t TIMEOUT = 100000000;
349
350 // Wait for slot to be clear (sanity check)
351 while (p->ci & (1u << slot)) {
352 if (++spin >= TIMEOUT) return MT_AHCI_TIMEOUT;
353 }
354
355 // 3. Setup Command Table
356 HBA_CMD_TBL* cmd = (HBA_CMD_TBL*)((uint8_t*)ctx->cmd_tbl + slot * 256);
357 kmemset(cmd, 0, 256);
358
359 // 4. Setup Command Header
360 HBA_CMD_HEADER* hdr = (HBA_CMD_HEADER*)((uint8_t*)ctx->clb + slot * sizeof(HBA_CMD_HEADER));
361 hba_cmd_hdr_set_cfl(hdr, (sizeof(FIS_REG_H2D) + 3) / 4);
362 hba_cmd_hdr_set_w(hdr, 0); // Read
363 hba_cmd_hdr_set_prdtl(hdr, 1); // One PRDT entry (Assuming bytes <= 4MB)
364 hdr->prdbc = 0; // Reset transferred count
365
366 // 5. Calculate Sector Count
367 // We assume bytes is a multiple of 512 based on the check above.
368 uint32_t sector_count = bytes / 512;
369
370 // 6. Build FIS with Dynamic Sector Count
371 FIS_REG_H2D* fis = (FIS_REG_H2D*)(&cmd->cfis);
372 kmemset(fis, 0, sizeof(*fis));
374 fis->c = 1; // Command
375 fis->command = 0x25; // READ DMA EXT
376
377 fis->lba0 = (uint8_t)(lba & 0xFF);
378 fis->lba1 = (uint8_t)((lba >> 8) & 0xFF);
379 fis->lba2 = (uint8_t)((lba >> 16) & 0xFF);
380 fis->device = 1 << 6; // LBA mode
381
382 fis->lba3 = (uint8_t)((lba >> 24) & 0xFF);
383 fis->lba4 = 0; // Extended LBA not supported in this simplified LBA32 param
384 fis->lba5 = 0;
385
386 // Split sector count for LBA48 command structure
387 fis->countl = (uint8_t)(sector_count & 0xFF);
388 fis->counth = (uint8_t)((sector_count >> 8) & 0xFF);
389
390 // 7. Setup PRDT
391 HBA_PRDT_ENTRY* prdt = &cmd->prdt_entry[0];
392 uintptr_t buf_phys = MiTranslateVirtualToPhysical(buf);
393
394 // Validate PRDT limits (AHCI PRDT dbc is max 4MB)
395 if (bytes > 4 * 1024 * 1024) return MT_INVALID_PARAM;
396
397 prdt->dba = (uint32_t)(uintptr_t)buf_phys;
398 prdt->dbau = (uint32_t)(((uintptr_t)buf_phys) >> 32);
399 prdt->dbc = bytes - 1; // Zero-based count (e.g., 512 bytes -> 511)
400 prdt->i = 1; // Interrupt on Completion
401
402 // 8. Memory Fences & Cache Flushing
403 // Ensure the Table and Buffer are in RAM before the HBA fetches them
404 cache_flush_invalidate_range(ctx->clb, 1024);
405 cache_flush_invalidate_range(cmd, 256);
406 cache_flush_invalidate_range(buf, bytes); // Flush the receiving buffer to be safe (invalidate)
407 __asm__ volatile("sfence; mfence" ::: "memory");
408
409 // 9. Start Command
410 p->ci = (1u << slot);
411
412 // 10. Wait for Completion
413 spin = 0;
414 while (p->ci & (1u << slot)) {
415 if (++spin >= TIMEOUT) break;
416 }
417
418 // 11. Error Checking
419 if ((spin >= TIMEOUT) || (p->tfd & ((1 << 7) | (1 << 0)))) {
420#ifdef AHCI_DEBUG_PRINT
421 // gop_printf(COLOR_RED, "AHCI Err: TFD: %x, SERR: %x\n", p->tfd, p->serr);
422#endif
424 }
425
426 // 12. Check Result
427 // Invalidate header cache so CPU reads the updated prdbc from RAM
428 __asm__ volatile("mfence" ::: "memory");
429 cache_flush_invalidate_range(hdr, sizeof(HBA_CMD_HEADER));
430 __asm__ volatile("mfence" ::: "memory");
431
432 if (hdr->prdbc != bytes) {
433#ifdef AHCI_DEBUG_PRINT
434 // gop_printf(COLOR_RED, "AHCI Partial Read: Req %u, Got %u\n", bytes, hdr->prdbc);
435#endif
436 // Even if mismatched, return success if data was moved?
437 // Usually strictly strictly enforce equality.
439 }
440
441 // Invalidate data buffer cache so CPU reads new data from RAM
442 cache_flush_invalidate_range(buf, bytes);
443
444 // Ack interrupt
445 p->is = p->is;
446
447 return MT_SUCCESS;
448}
449
450MTSTATUS ahci_write_sector(BLOCK_DEVICE* dev, uint32_t lba, const void* buf, size_t bytes) {
451
452 // 1. Input Validation
453 if (bytes == 0 || (bytes % 512 != 0)) return MT_INVALID_PARAM;
454 // 4MB Limit per PRDT entry check
455 if (bytes > 4 * 1024 * 1024) return MT_INVALID_PARAM;
456
458 HBA_PORT* p = ctx->port;
459
460 // 2. Clear Pending Interrupts
461 p->is = (uint32_t)-1;
462
463 int slot = find_free_slot(p->sact | p->ci);
464 if (slot < 0) return MT_AHCI_GENERAL_FAILURE;
465
466 // Calculate sector count dynamically
467 uint32_t sector_count = bytes / 512;
468
469 /* Command table for this slot */
470 HBA_CMD_TBL* cmd = (HBA_CMD_TBL*)((uint8_t*)ctx->cmd_tbl + slot * 256);
471 kmemset(cmd, 0, 256);
472
473 /* Command header */
474 HBA_CMD_HEADER* hdr = (HBA_CMD_HEADER*)((uint8_t*)ctx->clb + slot * sizeof(HBA_CMD_HEADER));
475 hba_cmd_hdr_set_cfl(hdr, (sizeof(FIS_REG_H2D) + 3) / 4);
476 hba_cmd_hdr_set_w(hdr, 1); /* write */
477 hdr->prdbc = 0;
478 hba_cmd_hdr_set_prdtl(hdr, 1);
479
480 /* Build CFIS */
481 FIS_REG_H2D* fis = (FIS_REG_H2D*)(&cmd->cfis);
482 kmemset(fis, 0, sizeof(*fis));
484 fis->c = 1;
485 fis->command = 0x35; /* WRITE DMA EXT */
486 fis->device = 1 << 6; // LBA mode
487
488 fis->lba0 = (uint8_t)(lba & 0xFF);
489 fis->lba1 = (uint8_t)((lba >> 8) & 0xFF);
490 fis->lba2 = (uint8_t)((lba >> 16) & 0xFF);
491 fis->lba3 = (uint8_t)((lba >> 24) & 0xFF);
492 fis->lba4 = 0;
493 fis->lba5 = 0;
494
495 // >>> FIXED: Dynamic Sector Count <<<
496 fis->countl = (uint8_t)(sector_count & 0xFF);
497 fis->counth = (uint8_t)((sector_count >> 8) & 0xFF);
498
499 /* PRDT */
500 HBA_PRDT_ENTRY* prdt = &cmd->prdt_entry[0];
501 // translation DOES NOT write to the buffer, only makes translations.
502 uintptr_t buf_phys = MiTranslateVirtualToPhysical((void*)buf);
503
504#ifdef AHCI_DEBUG_PRINT
505 // gop_printf(COLOR_BLUE, "AHCI WRITE: phys: %p | virt: %p | bytes: %u\n", buf_phys, buf, bytes);
506#endif
507
508 prdt->dba = (uint32_t)(uintptr_t)buf_phys;
509 prdt->dbau = (uint32_t)(((uintptr_t)buf_phys) >> 32);
510 prdt->dbc = bytes - 1; // Set byte count (length - 1)
511 prdt->i = 1; // Interrupt on completion
512
513 // >>> CRITICAL: Cache Flushing for Writes <<<
514 // For writes, we must ensure the data in the CPU cache is written back to RAM
515 // before the DMA controller reads it.
516 cache_flush_invalidate_range((void*)buf, bytes); // Flush entire buffer, not just 512
517
518 // Flush metadata
519 cache_flush_invalidate_range(ctx->clb, 1024);
520 cache_flush_invalidate_range(cmd, 256);
521
522 __asm__ volatile("sfence; mfence" ::: "memory");
523
524 /* Issue */
525 p->ci = (1u << slot);
526
527 /* Wait */
528 uint32_t spin = 0;
529 const uint32_t TIMEOUT = 100000000;
530 while (p->ci & (1u << slot)) {
531 if (++spin >= TIMEOUT) break;
532 }
533
534 if (spin >= TIMEOUT) {
535#ifdef AHCI_DEBUG_PRINT
536 // gop_printf(COLOR_RED, "AHCI TIMEOUT ahci_write_sector\n");
537#endif
538 return MT_AHCI_TIMEOUT;
539 }
540
541 // IMPORTANT: Check for errors from the device
542 if (p->tfd & ((1 << 7) | (1 << 0))) {
543#ifdef AHCI_DEBUG_PRINT
544 // gop_printf(0xFFFF0000, "AHCI write error!\n");
545 // gop_printf(0xFFFFFF00, "Port TFD: %p, SERR: %p\n", (void*)(uintptr_t)p->tfd, (void*)(uintptr_t)p->serr);
546#endif
548 }
549
550 // Optional: Verify prdbc matches bytes written
551 // __asm__ volatile("mfence" ::: "memory");
552 // cache_flush_invalidate_range(hdr, sizeof(HBA_CMD_HEADER));
553 // if (hdr->prdbc != bytes) { /* Warning */ }
554
555 // clear int
556 p->is = p->is;
557
558 return MT_SUCCESS;
559}
560
561
563 return get_block_device(index);
564}
BOOT_INFO boot_info_local
Definition kernel.c:27
BLOCK_DEVICE * ahci_get_block_device(int index)
Retrieve a pointer to the AHCI driver's BLOCK_DEVICE instance.
Definition ahci.c:562
bool ahci_initialized
Definition ahci.c:290
MTSTATUS ahci_init(void)
define AHCI_DEBUG_PRINT
Definition ahci.c:292
MTSTATUS ahci_write_sector(BLOCK_DEVICE *dev, uint32_t lba, const void *buf, size_t bytes)
Write a single bytes-byte sector to given LBA on a specific BLOCK_DEVICE.
Definition ahci.c:450
struct _AHCI_PORT_CTX AHCI_PORT_CTX
MTSTATUS ahci_read_sector(BLOCK_DEVICE *dev, uint32_t lba, void *buf, size_t bytes)
Read a single bytes-byte sector from the given LBA on a specific BLOCK_DEVICE.
Definition ahci.c:330
struct _FIS_REG_H2D FIS_REG_H2D
Register - Host to Device FIS (FIS_TYPE_REG_H2D)
#define AHCI_MAX_PORTS
Definition ahci.h:17
struct _HBA_CMD_TBL HBA_CMD_TBL
Command Table: one per slot.
@ FIS_TYPE_REG_H2D
Definition ahci.h:20
struct _HBA_PRDT_ENTRY HBA_PRDT_ENTRY
Physical Region Descriptor Table Entry.
struct _HBA_CMD_HEADER HBA_CMD_HEADER
HBA Command Header (defines an AHCI Command)
volatile struct _HBA_PORT HBA_PORT
Per port registers at HBA_MEM + 0x100 + (port * 0x80)
volatile struct _HBA_MEM HBA_MEM
AHCI Register layout (Global HBA Registers)
#define assert(...)
Definition assert.h:57
void register_block_device(BLOCK_DEVICE *dev)
Definition block.c:17
BLOCK_DEVICE * get_block_device(int index)
Definition block.c:32
struct _BLOCK_DEVICE BLOCK_DEVICE
GOP_PARAMS gop_local
Definition gop.c:247
struct _GOP_PARAMS GOP_PARAMS
struct _BOOT_INFO BOOT_INFO
FORCEINLINE void __pause(void)
Definition intrin.h:224
#define UINT64_T_MAX
Definition macros.h:22
uintptr_t MiTranslateVirtualToPhysical(IN void *VirtualAddress)
Definition map.c:456
@ MmNonCached
Definition mm.h:339
FORCEINLINE void * kmemset(void *dest, int64_t val, uint64_t len)
Definition mm.h:540
#define VirtualPageSize
Definition mm.h:53
void * MmMapIoSpace(IN uintptr_t PhysicalAddress, IN size_t NumberOfBytes, IN MEMORY_CACHING_TYPE CacheType)
Definition mmio.c:269
void * MmAllocateContigiousMemory(IN size_t NumberOfBytes, IN uint64_t HighestAcceptableAddress)
Definition mmio.c:87
#define MT_AHCI_WRITE_FAILURE
Definition mtstatus.h:92
#define MT_SUCCESS
Definition mtstatus.h:22
#define MT_AHCI_PORT_FAILURE
Definition mtstatus.h:90
#define MT_AHCI_GENERAL_FAILURE
Definition mtstatus.h:94
#define MT_INVALID_PARAM
Definition mtstatus.h:24
int32_t MTSTATUS
Definition mtstatus.h:12
#define MT_AHCI_TIMEOUT
Definition mtstatus.h:93
#define MT_AHCI_READ_FAILURE
Definition mtstatus.h:91
BLOCK_DEVICE bdev
Definition ahci.c:55
void * clb
Definition ahci.c:53
HBA_PORT * port
Definition ahci.c:51
HBA_CMD_TBL * cmd_tbl
Definition ahci.c:52
void * fis
Definition ahci.c:54
void * dev_data
Definition block.h:20
MTSTATUS(* read_sector)(struct _BLOCK_DEVICE *dev, uint32_t lba, void *buf, size_t bytes)
Definition block.h:12
MTSTATUS(* write_sector)(struct _BLOCK_DEVICE *dev, uint32_t lba, const void *buf, size_t bytes)
Definition block.h:16
uint8_t command
Definition ahci.h:80
uint8_t device
Definition ahci.h:85
uint8_t lba2
Definition ahci.h:84
uint8_t c
Definition ahci.h:79
uint8_t lba4
Definition ahci.h:87
uint8_t lba0
Definition ahci.h:82
uint8_t counth
Definition ahci.h:91
uint8_t lba5
Definition ahci.h:88
uint8_t lba3
Definition ahci.h:86
uint8_t fis_type
Definition ahci.h:76
uint8_t countl
Definition ahci.h:90
uint8_t lba1
Definition ahci.h:83
uint32_t ctbau
Definition ahci.h:132
uint32_t ctba
Definition ahci.h:131
volatile uint32_t prdbc
Definition ahci.h:130
uint8_t cfis[64]
Definition ahci.h:112
HBA_PRDT_ENTRY prdt_entry[1]
Definition ahci.h:121
uint32_t ssts
Definition ahci.h:60
uint32_t clb
Definition ahci.h:50
uint32_t tfd
Definition ahci.h:58
uint32_t fb
Definition ahci.h:52
uint32_t fbu
Definition ahci.h:53
uint32_t ci
Definition ahci.h:64
uint32_t cmd
Definition ahci.h:56
uint32_t clbu
Definition ahci.h:51
uint32_t serr
Definition ahci.h:62
uint32_t is
Definition ahci.h:54
uint32_t sact
Definition ahci.h:63
uint32_t dbc
Definition ahci.h:104
uint32_t dbau
Definition ahci.h:100
uint32_t dba
Definition ahci.h:99
uint32_t i
Definition ahci.h:106