I have this perfectly working code in vms (QEMU, vmware, vbox) but horribly behaving in real hw. Following the list in the wiki, I come up with this function to init (PCI device is given DMA and MMIO privileges in a prior code segment)
Code:
static void init_device(volatile hba_mem_t* hba)
{
enable_ahci_mode(hba);
bios_handoff(hba);
idle_ports(hba);
ahci_reset(hba);
enable_ahci_mode(hba);
for(uint8_t bit = 0; bit < 32; bit++)
if(hba->pi & (1 << bit)) // bit is set device exists
{
volatile hba_port_t* port = &hba->ports[bit];
if(!init_port(port))
continue;
// some more stuff
}
}
where init_port is
Code:
static bool init_port(volatile hba_port_t* port)
{
uint64_t page0 = (uint64_t)pfa_calloc(1);
uint64_t cmd_address = page0;
uint32_t cmd_low = cmd_address & 0xFFFFFFFFLL;
uint32_t cmd_high = (cmd_address >> 32) & 0xFFFFFFFFLL;
uint64_t fis_address = page0 + 1024;
uint32_t fis_low = fis_address & 0xFFFFFFFFLL;
uint32_t fis_high = (fis_address >> 32) & 0xFFFFFFFFLL;
port->clb = cmd_low;
port->clbu = cmd_high;
port->fb = fis_low;
port->fbu = fis_high;
port->cmd |= HBA_PxCMD_FRE;
port->cmd |= HBA_PxCMD_SUD;
pit_prepare_one_shot(1);
pit_perform_one_shot();
uint64_t spin = 0;
while((port->ssts & HBA_PxSSTS_DET) != 3 && spin < 1000000)
spin++;
if(spin >= 1000000)
return false;
port->serr = 0xFFFFFFFF;
spin = 0;
while(port->tfd & HBA_PxTFD_STS_DRQ && port->tfd & HBA_PxTFD_STS_BSY && spin < 1000000)
spin++;
if(spin >= 1000000)
return false;
port->is = 0;
volatile hba_cmd_header_t* cmd_header = ptr(cmd_address);
uint64_t page1 = (uint64_t)pfa_calloc(1);
uint64_t page2 = (uint64_t)pfa_calloc(1);
for (uint8_t i = 0; i < 32; i++)
{
cmd_header[i].prdtl = 1;
cmd_header[i].pmp = 0;
cmd_header[i].c = true;
cmd_header[i].b = false;
cmd_header[i].r = false;
cmd_header[i].p = false;
cmd_header[i].a = false;
uint64_t cmd_addr = i < 16 ? page1 : page2;
cmd_addr += 256 * (i % 16);
uint32_t cmd_addr_low = cmd_addr & 0xFFFFFFFFull;
uint32_t cmd_addr_high = (cmd_addr >> 32) & 0xFFFFFFFFull;
cmd_header[i].ctba = cmd_addr_low;
cmd_header[i].ctbau = cmd_addr_high;
}
return true;
}
Every other function I'm not posting can be found
here.
This last init_port function is the one that fails inside the second spinloop and stops the initialization of the device; I tried following other lists of things too but they all brought the same behavior, i.e. the device is not initialized.
I'm completely clueless on why this is not working I'm in your hands