AHCI Hardware error[Solved]

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
MrLolthe1st
Member
Member
Posts: 90
Joined: Sat Sep 24, 2016 12:06 am

AHCI Hardware error[Solved]

Post by MrLolthe1st »

Hi!
Today i'm wrote my own AHCI driver, but on real hardware after command issue i'm get 0x4000000 to "interrupt status" register, on QEMU, VBox everything is ok. What it may be?
With best regards,
Aleksandr
Last edited by MrLolthe1st on Mon Aug 06, 2018 11:47 am, edited 1 time in total.
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: AHCI Hardware error

Post by BenLunt »

MrLolthe1st wrote:Hi!
Today i'm wrote my own AHCI driver, but on real hardware after command issue i'm get 0x4000000 to "interrupt status" register, on QEMU, VBox everything is ok. What it may be?
With best regards,
Aleksandr
This is why I asked in the EHCI thread if your machine was little-endian or big-endian. If your machine is big-endian and you are reading that dword, bit 30 is set which would be the same as if it was bit 1 in little-endian format (so-to-speak[1]). Bit 1 is port 1, the port your drive most likely is on.

I think you might have a big-endian, little-endian issue.

Just a thought,
Ben

[1]Technically, it would be 0x02000000, but 0x40000000 is the second bit from the left while 0x00000002 is the second bit from the right. Is this coincidence or are you getting your bit positions mixed up? On a little-endian machine (Intel, AMD, etc), bit 0 is the far right bit while bit 31 is the far left bit.

Code: Select all

Bit 31 30 29 28 27 ...  5 4 3 2 1 0
       0x40000000  ; <- bit 30
       0x00000002  ; <- bit 1
MrLolthe1st
Member
Member
Posts: 90
Joined: Sat Sep 24, 2016 12:06 am

Re: AHCI Hardware error

Post by MrLolthe1st »

I'm know about big-endian and little-endian. All my PCs and virtual machines are little-endian, there are not any troubles with bit order. There are some troubles with my realisation, may by.
May be i'm need to align address to 4096 bytes when port rebase, my malloc returns me not-aligned address of memory, in EHCI i'm allocate to queue pool, td pool, framelist and say to malloc to allocate more then need by 8192 bytes and i'm do that: qhPool = ((uint)qhPool)/4096)*4096 + 4096; for td pool, qh pool, and framelist
There is my code of read and other:

Code: Select all

void port_rebase(HBA_PORT *port, int portno)
{
	stop_cmd(port);	// Stop command engine

					// Command list offset: 1K*portno
					// Command list entry size = 32
					// Command list entry maxim count = 32
					// Command list maxim size = 32*32 = 1K per port
	port->clb = malloc(1024);
	port->clbu = 0;
	memset((void*)(port->clb), 0, 1024);

	// FIS offset: 32K+256*portno
	// FIS entry size = 256 unsigned chars per port
	port->fb = malloc(256);
	port->fbu = 0;
	memset((void*)(port->fb), 0, 256);

	// Command table offset: 40K + 8K*portno
	// Command table size = 256*32 = 8K per port
	HBA_CMD_HEADER *cmdheader = (HBA_CMD_HEADER*)(port->clb);
	for (int i = 0; i<32; i++)
	{
		cmdheader[i].prdtl = 8;	// 8 prdt entries per command table
								// 256 unsigned chars per command table, 64+16+48+16*8
								// Command table offset: 40K + 8K*portno + cmdheader_index*256
		cmdheader[i].ctba = malloc(256);
		cmdheader[i].ctbau = 0;
		memset((void*)cmdheader[i].ctba, 0, 256);
	}

	start_cmd(port);	// Start command engine
}

// Start command engine
void start_cmd(HBA_PORT *port)
{
	// Wait until CR (bit15) is cleared
    while (port->cmd & HBA_PxCMD_CR)
        //kprintf("cmd: %d %d\n", port->cmd, port->cmd & HBA_PxCMD_CR);
        ;
    port->cmd |= HBA_PxCMD_FRE;
    port->cmd |= HBA_PxCMD_ST;
}

// Stop command engine
void stop_cmd(HBA_PORT *port)
{
//	kprintf("%x\n", port->cmd);
	// Clear ST (bit0)
	port->cmd &= ~HBA_PxCMD_ST;
	port->cmd &= ~HBA_PxCMD_FRE;

	//port->cmd &= ~HBA_PxCMD_FRE;
	// Wait until FR (bit14), CR (bit15) are cleared
	while (1)
	{
		if (port->cmd & HBA_PxCMD_FR)
			continue;
		if (port->cmd & HBA_PxCMD_CR)
			continue;
		break;
	}

	// Clear FRE (bit4)
}

#define HBA_PxIS_TFES   (1 << 30)
bool read(HBA_PORT *port, unsigned long long starth, uint32_t count, uint16_t *buf)
{
	port->is = 0xffff; // Clear pending interrupt bits
	int slot = find_cmdslot(port);
	if (slot == -1)
		return 0;
	uint64_t addr = 0;
	addr = (((addr | port->clbu) << 32) | port->clb);
	HBA_CMD_HEADER *cmdheader = (HBA_CMD_HEADER*)(KERNBASE + addr);
	cmdheader += slot;
	cmdheader->cfl = sizeof(FIS_REG_H2D) / sizeof(unsigned int); // Command FIS size
	cmdheader->w = 0; // Read from device
	cmdheader->c = 1; // Read from device
	cmdheader->p = 1; // Read from device
	cmdheader->prdtl = (unsigned short)((count - 1) >> 4) + 1; // PRDT entries count
	addr = 0;
	addr = (((addr | cmdheader->ctbau) << 32) | cmdheader->ctba);
	HBA_CMD_TBL *cmdtbl = (HBA_CMD_TBL*)(KERNBASE + addr);
	int i = 0;
	for (i = 0; i < cmdheader->prdtl - 1; i++)
	{
		cmdtbl->prdt_entry[i].dba = (unsigned int)(buf) & 0xFFFFFFFF;
		cmdtbl->prdt_entry[i].dbau = 0;
		cmdtbl->prdt_entry[i].dbc = 8 * 1024 - 1; // 8K unsigned chars
		cmdtbl->prdt_entry[i].i = 0;
		buf += 4 * 1024; // 4K unsigned shorts
		count -= 16; // 16 sectors
	}
	cmdtbl->prdt_entry[i].dba = (unsigned int)(buf) & 0xFFFFFFFF;
	cmdtbl->prdt_entry[i].dbau = 0;
	cmdtbl->prdt_entry[i].dbc = count << 9; // 512 unsigned chars per sector
	cmdtbl->prdt_entry[i].i = 0;
	FIS_REG_H2D *cmdfis = (FIS_REG_H2D*)(&cmdtbl->cfis);
	cmdfis->fis_type = FIS_TYPE_REG_H2D;
	cmdfis->c = 1; // Command
	cmdfis->command = 0x25;
	cmdfis->lba0 = (unsigned char)starth;
	cmdfis->lba1 = (unsigned char)(starth >> 8);
	cmdfis->lba2 = (unsigned char)(starth >> 16);
	cmdfis->device = 1 << 6; // LBA mode
	cmdfis->lba3 = (unsigned char)(starth >> 24);
	cmdfis->lba4 = (unsigned char)(starth >> 32);
	cmdfis->lba5 = (unsigned char)(starth >> 40);
	cmdfis->countl = count & 0xff;
	cmdfis->counth = count >> 8;
	//kprintf("[slot]{%d}", slot);
	port->ci = 1 << slot; // Issue command
	port->is = ~0; // Issue command
	while (1)
	{
		kprintf("[%x,%x]", port->ci, port->is);
		if ((port->ci & (1 << slot)) == 0)
			break;
		if ((port->is & (1<<26)) != 0)
		{ // Task file error
			kprintf("SATA Error\n");
			return 0;
		}
		if (port->is & HBA_PxIS_TFES)
		{ // Task file error
			kprintf("Read disk error\n");
			return 0;
		}
	}
	if ((port->is & (1 << 26)) != 0)
	{ // Task file error
		kprintf("SATA Error\n");
		return 0;
	}
	if (port->is & HBA_PxIS_TFES)
	{
		kprintf("Read disk error\n");
		return 0;
	}
	int k = 0;
	while (port->ci != 0)
	{
		k++;
		if (k > 10)
			return 0;
		PitWait(1000);
		kprintf("[%d]", port->ci);
	}
	return 1;
}

// Find a free command list slot
int find_cmdslot(HBA_PORT *m_port)
{
	// If not set in SACT and CI, the slot is free
	uint32_t slots = (m_port->sact | m_port->ci);
	uint qq = (abar->cap & 0x0f00) >> 8;
	for (int i = 0; i<qq; i++)
	{
		if ((slots & 1) == 0)
			return i;
		slots >>= 1;
	}
	kprintf("Cannot find free command list entry\n");
	return -1;
}


void _probe_port(HBA_MEM *abar_temp)
{
	// Search disk in impelemented ports
	uint pi = abar_temp->pi;
	int i = 0;
	while (i < 32)
	{
		if (pi & 1)
		{
			int dt = check_type((HBA_PORT *)&abar_temp->ports[i]);
			if (dt == AHCI_DEV_SATA)
			{
				kprintf("SATA drive found at port %d\n", i);
				abar = abar_temp;
				port_rebase(&abar_temp->ports[i], i);
				kprintf("DONE AHCI INITIALISATION");
				unsigned short * buf = malloc(16 * 512);
				buf[0] = 1;
				kprintf("qqqss");
				int res = 0;
				int jj = 0;
				while (res == 0) {
					res = read(&abar->ports[i], 16*jj, 1, buf);
					if (res != 0)
						for (int z = 0; z < 256; z++)
						{
							kprintf(" %x", buf[z]);
						}
					PitWait(10000);
					jj++;
				}
				
			}
			else if (dt == AHCI_DEV_SATAPI)
			{
				//kprintf("\nSATAPI drive found at port %d\n", i);
			}
			else if (dt == AHCI_DEV_SEMB)
			{
				//kprintf("\nSEMB drive found at port %d\n", i);
			}
			else if (dt == AHCI_DEV_PM)
			{
				//kprintf("\nPM drive found at port %d\n", i);
			}
			else
			{
				//kprintf("\nNo drive found at port %d\n", i);
			}
		}
		pi >>= 1;
		i++;
	}
	kprintf("probe_port complete\n");
}
With best regards,
Aleksandr
User avatar
BenLunt
Member
Member
Posts: 941
Joined: Sat Nov 22, 2014 6:33 pm
Location: USA
Contact:

Re: AHCI Hardware error

Post by BenLunt »

If I remember correctly, the HBA also needs you to read and write full dwords.

See the comments I made in the EHCI thread (URL noted earlier) about reading and writing to mem-mapped I/O space.

Assuming 32-bit addresses, simple *external* functions such as:

Code: Select all

bit32u mem_read_io_regs(const bit32u base, const bit32u offset) {
  return * ((volatile bit32u *) (base + offset));
}

void mem_write_io_regs(const bit32u base, const bit32u offset, const bit32u val) {
  volatile bit32u *ptr = (volatile bit32u *) (base + offset);
  *ptr = val;
}
A 64-bit read or write is very similar. However, and this is a big however, you need to be aware that some hardware wants the low dword written first, while some hardware wants the high dword written first. For example, one of the Network Adapters that I am currently researching will execute the command when the low dword is written, not the high-dword. i.e.: It expects the high dword to have already been written when writing the low dword.

Another example is a 64-bit high-speed timer. For example, if you read the low dword and then read the high dword, the high dword could have changed while reading the low dword and you now can be off by a 4Gig value.

Change your code as suggested and see if this fixes it.
Ben
- http://www.fysnet.net/osdesign_book_series.htm
MrLolthe1st
Member
Member
Posts: 90
Joined: Sat Sep 24, 2016 12:06 am

Re: AHCI Hardware error

Post by MrLolthe1st »

Hi, Ben.
I'm changed compiler, do as you say and there aren't any effect :(
Replied to EHCI topic too.
Can you give to me your implementation of EHCI driver?
I guess, that your driver works fine.
With best regards,
Aleksandr
Post Reply