AHCI write hangs on some hardware

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
mariuszp
Member
Member
Posts: 587
Joined: Sat Oct 16, 2010 3:38 pm

AHCI write hangs on some hardware

Post by mariuszp »

My AHCI driver works on VirtualBox but fails to work in VMWare as well as on real hardware. In those cases, it reads successfully, but for some reason fails to write. When writing, it freezes on the final loop of this code, which waits for the command issue bit to clear:

Code: Select all

		dev->port->is = dev->port->is;
		
		// we don't need to protect the device with semaphores, because only this thread
		// accesses its port.

		// first find a free command slot; we may wait for some time if the drive is
		// particularly busy.
		uint32_t slot = 0;
		while (dev->port->ci & (1 << slot))
		{
			slot = (slot + 1) % 32;
			__sync_synchronize();
		};
		
		// fill in the appropriate structure.
		// do not zero it; it was already zeroed before and only this thread updates the
		// structures so we know that any unused fields are already zero.
		cmdhead[slot].cfl = sizeof(FIS_REG_H2D)/4;
		cmdhead[slot].w = 0;
		cmdhead[slot].prdtl = 1;
		__sync_synchronize();
		
		AHCICommandTable *cmdtbl = (AHCICommandTable*) ((char*)dmaGetPtr(&dev->dmabuf) + 1024+256+8*1024+256*slot);
		cmdtbl->prdt_entry[0].dba = dmaGetPhys(&dev->iobuf) + 4096*slot;
		cmdtbl->prdt_entry[0].dbc = 512*cmd->count;
		cmdtbl->prdt_entry[0].i = 0;
		
		FIS_REG_H2D *cmdfis = (FIS_REG_H2D*)(&cmdtbl->cfis);
		cmdfis->fis_type = FIS_TYPE_REG_H2D;
		cmdfis->c = 1;
		if (cmd->type == SD_CMD_READ)
		{
			cmdfis->command = ATA_CMD_READ_DMA_EXT;
		}
		else
		{
			cmdfis->command = ATA_CMD_WRITE_DMA_EXT;
		};
		
		cmdfis->lba0 = (uint8_t)cmd->index;
		cmdfis->lba1 = (uint8_t)(cmd->index>>8);
		cmdfis->lba2 = (uint8_t)(cmd->index>>16);
		cmdfis->device = 1<<6;	// LBA mode
	 
		cmdfis->lba3 = (uint8_t)(cmd->index>>24);
		cmdfis->lba4 = (uint8_t)(cmd->index>>32);
		cmdfis->lba5 = (uint8_t)(cmd->index>>40);
		
		cmdfis->countl = (uint8_t)(cmd->count);
		cmdfis->counth = (uint8_t)(cmd->count>>8);
		
		char *hwbuf = (char*) dmaGetPtr(&dev->iobuf) + (4096*slot);
		if (cmd->type == SD_CMD_WRITE)
		{
			memcpy(hwbuf, cmd->block, 512*cmd->count);
		};
		
		// wait for the port to stop being busy
		int busy = (1 << 7) | (1 << 3);
		while (dev->port->tfd & busy) __sync_synchronize();
		__sync_synchronize();
		
		if (dev->port->tfd & (1 << 0))
		{
			panic("AHCI transfer error!");
		};
		
		dev->port->ci = (1 << slot);
		__sync_synchronize();
		
		while (dev->port->ci & (1 << slot))
		{
			if (dev->port->is & (1 << 27))
			{
				panic("AHCI error!");
			};
			__sync_synchronize();
		};
		__sync_synchronize();
		
		if (cmd->type == SD_CMD_READ)
		{
			memcpy(cmd->block, hwbuf, 512*cmd->count);
		};
The code decides whether to read or write based on the value of cmd->type (SD_CMD_READ or SD_CMD_WRITE). There must obviously be some other thing I need to do when writing. Does this code appear to be missing something?
User avatar
Ch4ozz
Member
Member
Posts: 170
Joined: Mon Jul 18, 2016 2:46 pm
Libera.chat IRC: esi

Re: AHCI write hangs on some hardware

Post by Ch4ozz »

For read I do this:

Code: Select all

cmdheader->w = 0;
And for write I do this:

Code: Select all

cmdheader->w = 1;
cmdheader->c = 1;
cmdheader->p = 1;
You only have it set to 0 here


I also clear all pending interrupt bits before I do anything with the device:

Code: Select all

port->is = 0xFFFFFFFF;
mariuszp
Member
Member
Posts: 587
Joined: Sat Oct 16, 2010 3:38 pm

Re: AHCI write hangs on some hardware

Post by mariuszp »

I do clear all pending interrupts:

Code: Select all

dev->port->is = dev->port->is;
Now I changed the code to:

Code: Select all

		if (cmd->type == SD_CMD_READ)
		{
			cmdhead[slot].w = 0;
			cmdhead[slot].p = 0;
			cmdhead[slot].c = 0;
		}
		else
		{
			cmdhead[slot].w = 1;
			cmdhead[slot].p = 1;
			cmdhead[slot].c = 1;
		};
However, this still does not fix the problem; it still works in VirtualBox, but freezes in VMWare.

Some debugging reveals that at the point where it hangs, the interrupt status is set to 0x40000000 which means that Task File Error status (TFES) is set.
mariuszp
Member
Member
Posts: 587
Joined: Sat Oct 16, 2010 3:38 pm

Re: AHCI write hangs on some hardware

Post by mariuszp »

bump
Post Reply