13_Static_assert(
false,
"Reminder: AHCI, and other DMA stuff DEAL WITH PHYSICAL ADDRESSES ONLY! not virtual, so supply to them the translated addresses.");
18#ifdef AHCI_DEBUG_PRINT
19static void decode_serr(uint32_t serr) {
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");
69 __asm__
volatile(
"mfence" :::
"memory");
72static inline void outl_port(uint16_t port, uint32_t val) {
73 __asm__
volatile(
"outl %0, %1" ::
"a"(val),
"d"(port));
75static inline uint32_t inl_port(uint16_t port) {
77 __asm__
volatile(
"inl %1, %0" :
"=a"(val) :
"d"(port));
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);
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);
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;
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;
111 uint32_t cmd32 = pci_cfg_read32(bus, slot, func, 0x04);
112 uint16_t cmd = cmd32 & 0xFFFF;
113#ifdef AHCI_DEBUG_PRINT
117 if (!(cmd & (1 << 2))) {
119 pci_cfg_write32(bus, slot, func, 0x04, (cmd32 & 0xFFFF0000) | (uint32_t)cmd);
120#ifdef AHCI_DEBUG_PRINT
125#ifdef AHCI_DEBUG_PRINT
134#ifdef AHCI_DEBUG_PRINT
144static int find_free_slot(uint32_t mask) {
145 for (
int i = 0; i < 32; i++) {
146 if (!(mask & (1u << i))) {
156static void enable_controller(
void) {
157 hba_mem->ghc |= (1u << 31);
158 hba_mem->ghc |= (1u << 0);
160 while (hba_mem->ghc & (1u << 0));
168static bool init_one_port(
int idx) {
170 uint32_t status = p->
ssts;
171 if ((status & 0x0F) != 3)
return false;
174 p->
cmd &= ~(1u << 0);
175 p->
cmd &= ~(1u << 4);
178 while ((p->
cmd & (1u << 15)) || (p->
cmd & (1u << 14))) {
184 if (!clb)
return false;
188 assert(((uintptr_t)clb_phys & 0x3FF) == 0,
"CLB must be 1KiB-aligned (1024 byte multiple)");
189#ifdef AHCI_DEBUG_PRINT
192 p->
clb = (uint32_t)(uintptr_t)clb_phys;
193 p->
clbu = (uint32_t)((uintptr_t)clb_phys >> 32);
197 if (!fis_buf)
return false;
201#ifdef AHCI_DEBUG_PRINT
204 p->
fb = (uint32_t)(uintptr_t)fis_buf_phys;
205 p->
fbu = (uint32_t)((uintptr_t)fis_buf_phys >> 32);
208 size_t tbl_size = 256 * 32;
210 if (!cmd_tbl)
return false;
213 assert(((uintptr_t)cmd_tbl_phys & 0xFF) == 0,
"Command table block must be 256-byte aligned");
214#ifdef AHCI_DEBUG_PRINT
219 for (
int slot = 0; slot < 32; slot++) {
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);
234 assert((p->
cmd & 1) != 0,
"Port ST bit failed to set!");
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)");
252 bool s64a = !!((cap >> 31) & 1u);
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");
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");
267#ifdef AHCI_DEBUG_PRINT
268 for (
unsigned sl = 0; sl <= ncs; ++sl) {
271 uintptr_t expected = (uintptr_t)cmd_tbl_phys + sl * 256;
273 assert(hdr->
ctba == (uint32_t)(expected & 0xFFFFFFFFu),
"Header CTBA low doesn't match expected CTBA");
275 assert(hdr->
ctbau == (uint32_t)(expected >> 32),
"Header CTBAU mismatch (S64A advertised)");
278 assert(hdr->
ctbau == 0,
"Header CTBAU must be zero when S64A==0");
298#ifdef AHCI_DEBUG_PRINT
306 hba_mem = (
HBA_MEM*)(uintptr_t)bar;
307#ifdef AHCI_DEBUG_PRINT
311 ensure_ahci_busmaster_enabled();
314 uint32_t pi = hba_mem->pi;
317 if (pi & (1u << idx)) {
323 for (
int i = 0; i < port_count; i++) {
333 if (bytes == 0 || (bytes % 512 != 0)) {
342 p->
is = (uint32_t)-1;
344 int slot = find_free_slot(p->
sact | p->
ci);
348 const uint32_t TIMEOUT = 100000000;
351 while (p->
ci & (1u << slot)) {
361 hba_cmd_hdr_set_cfl(hdr, (
sizeof(
FIS_REG_H2D) + 3) / 4);
362 hba_cmd_hdr_set_w(hdr, 0);
363 hba_cmd_hdr_set_prdtl(hdr, 1);
368 uint32_t sector_count = bytes / 512;
377 fis->
lba0 = (uint8_t)(lba & 0xFF);
378 fis->
lba1 = (uint8_t)((lba >> 8) & 0xFF);
379 fis->
lba2 = (uint8_t)((lba >> 16) & 0xFF);
382 fis->
lba3 = (uint8_t)((lba >> 24) & 0xFF);
387 fis->
countl = (uint8_t)(sector_count & 0xFF);
388 fis->
counth = (uint8_t)((sector_count >> 8) & 0xFF);
397 prdt->
dba = (uint32_t)(uintptr_t)buf_phys;
398 prdt->
dbau = (uint32_t)(((uintptr_t)buf_phys) >> 32);
399 prdt->
dbc = bytes - 1;
404 cache_flush_invalidate_range(ctx->
clb, 1024);
405 cache_flush_invalidate_range(cmd, 256);
406 cache_flush_invalidate_range(buf, bytes);
407 __asm__
volatile(
"sfence; mfence" :::
"memory");
410 p->
ci = (1u << slot);
414 while (p->
ci & (1u << slot)) {
415 if (++spin >= TIMEOUT)
break;
419 if ((spin >= TIMEOUT) || (p->
tfd & ((1 << 7) | (1 << 0)))) {
420#ifdef AHCI_DEBUG_PRINT
428 __asm__
volatile(
"mfence" :::
"memory");
430 __asm__
volatile(
"mfence" :::
"memory");
432 if (hdr->
prdbc != bytes) {
433#ifdef AHCI_DEBUG_PRINT
442 cache_flush_invalidate_range(buf, bytes);
461 p->
is = (uint32_t)-1;
463 int slot = find_free_slot(p->
sact | p->
ci);
467 uint32_t sector_count = bytes / 512;
475 hba_cmd_hdr_set_cfl(hdr, (
sizeof(
FIS_REG_H2D) + 3) / 4);
476 hba_cmd_hdr_set_w(hdr, 1);
478 hba_cmd_hdr_set_prdtl(hdr, 1);
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);
496 fis->
countl = (uint8_t)(sector_count & 0xFF);
497 fis->
counth = (uint8_t)((sector_count >> 8) & 0xFF);
504#ifdef AHCI_DEBUG_PRINT
508 prdt->
dba = (uint32_t)(uintptr_t)buf_phys;
509 prdt->
dbau = (uint32_t)(((uintptr_t)buf_phys) >> 32);
510 prdt->
dbc = bytes - 1;
516 cache_flush_invalidate_range((
void*)buf, bytes);
519 cache_flush_invalidate_range(ctx->
clb, 1024);
520 cache_flush_invalidate_range(cmd, 256);
522 __asm__
volatile(
"sfence; mfence" :::
"memory");
525 p->
ci = (1u << slot);
529 const uint32_t TIMEOUT = 100000000;
530 while (p->
ci & (1u << slot)) {
531 if (++spin >= TIMEOUT)
break;
534 if (spin >= TIMEOUT) {
535#ifdef AHCI_DEBUG_PRINT
542 if (p->
tfd & ((1 << 7) | (1 << 0))) {
543#ifdef AHCI_DEBUG_PRINT
BOOT_INFO boot_info_local
BLOCK_DEVICE * ahci_get_block_device(int index)
Retrieve a pointer to the AHCI driver's BLOCK_DEVICE instance.
MTSTATUS ahci_init(void)
define AHCI_DEBUG_PRINT
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.
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.
struct _FIS_REG_H2D FIS_REG_H2D
Register - Host to Device FIS (FIS_TYPE_REG_H2D)
struct _HBA_CMD_TBL HBA_CMD_TBL
Command Table: one per slot.
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)
void register_block_device(BLOCK_DEVICE *dev)
BLOCK_DEVICE * get_block_device(int index)
struct _BLOCK_DEVICE BLOCK_DEVICE
struct _GOP_PARAMS GOP_PARAMS
struct _BOOT_INFO BOOT_INFO
FORCEINLINE void __pause(void)
uintptr_t MiTranslateVirtualToPhysical(IN void *VirtualAddress)
FORCEINLINE void * kmemset(void *dest, int64_t val, uint64_t len)
void * MmMapIoSpace(IN uintptr_t PhysicalAddress, IN size_t NumberOfBytes, IN MEMORY_CACHING_TYPE CacheType)
void * MmAllocateContigiousMemory(IN size_t NumberOfBytes, IN uint64_t HighestAcceptableAddress)
#define MT_AHCI_WRITE_FAILURE
#define MT_AHCI_PORT_FAILURE
#define MT_AHCI_GENERAL_FAILURE
#define MT_AHCI_READ_FAILURE
MTSTATUS(* read_sector)(struct _BLOCK_DEVICE *dev, uint32_t lba, void *buf, size_t bytes)
MTSTATUS(* write_sector)(struct _BLOCK_DEVICE *dev, uint32_t lba, const void *buf, size_t bytes)
HBA_PRDT_ENTRY prdt_entry[1]