Page 1 of 1

SATA and AHCI on real Hardware

Posted: Wed Aug 03, 2016 9:03 am
by Ch4ozz
Hello guys!
I bought a cheap laptop online to test my OS on it, and it works pretty good.
Im currently adjusting / coding new drivers to support the hardware.

With the help of the AHCI article in the wiki I coded a driver for the SATA controller and it works fine on VBox, VMWare and QEMU.
Unfortunately it doesnt work on the real hardware :(

I've got a Lenovo T400, it has a ICH9 chipset and the SATA controller PCI id is: 1.6.0 -> 0x2929 @ 0x8086

I logged some variables and read this data sheet:
http://www.intel.com/content/dam/www/pu ... v1-3-1.pdf

CAP.SAM is on all VMs 1 but on my laptop it is 0
GHC is completely 0 (therefore GHC.AE is not set either) on the laptop

The data sheet says:
The implementation of this bit is dependent upon the value of the CAP.SAM bit.
If CAP.SAM is '0', then GHC.AE shall be read-write and shall have a reset value of '0'.
If CAP.SAM is '1', then AE shall be read-only and shall have a reset value of '1'.
Which means I should activate GHC.AE manually.

If I do that, all the flags in the port.ssts are 0 (and not DET_PRESENT, IPM_ACTIVE) therefore I cant rebase them.
If I dont activate AHCI mode by writing the bit, I can rebase the ports but reading from them times out.

My code:

Code: Select all

if(pci_info->class.sub_class == 6) //AHCI
{
	printf("Device is AHCI ready!\n");
	
	abar = (HBA_MEM*)(pci_info->address5 & 0xFFFFE000);
	malloc_mark_used_adr((void*)abar);
	
	//Needed to be set for a full reset
	abar->ghc |= 0x80000000;

	Sleep(100);

	//Resetting HBA
	abar->ghc |= 1;
	while(abar->ghc & 1)
		Sleep(10);
	
	Sleep(100);
	
	//Enable AHCI if not enabled yet
	if (!(abar->ghc & 0x80000000))
		abar->ghc |= 0x80000000;
	
	int i;
	for(i = 0; i < 32; i++)
	{
		//Check for drive status
		if(!(abar->pi & (1 << i)))
			continue;
		if((abar->ports[i].ssts & 0x0F) != HBA_PORT_DET_PRESENT || ((abar->ports[i].ssts >> 8) & 0x0F) != HBA_PORT_IPM_ACTIVE)
			continue;
		
		port_rebase(&abar->ports[i], i);
		
		malloc_mark_used_adr(*(void**)&abar->ports[i]);
		
		ahci_devices[i].available = true;
		ahci_devices[i].type = abar->ports[i].sig;
		ahci_devices[i].has_data = false;
		
		device_count++;
	}
}
Im really lost and hope someone can give me a hint into the right direction.
Thanks in advance!

Re: SATA and AHCI on real Hardware

Posted: Wed Aug 03, 2016 3:15 pm
by SpyderTL
Complete shot in the dark here, but did you check the BIOS configuration to make sure it wasn't in IDE compatibility mode?

Re: SATA and AHCI on real Hardware

Posted: Wed Aug 03, 2016 3:37 pm
by BrightLight
SpyderTL wrote:Complete shot in the dark here, but did you check the BIOS configuration to make sure it wasn't in IDE compatibility mode?
In IDE compatibility mode, the AHCI controller doesn't even appear in the PCI configuration space, being replaced by an IDE controller with I/O ports in BAR0 and BAR1, and thus AHCI code would fail to detect anything. At least it's like this on my two test PCs.

Re: SATA and AHCI on real Hardware

Posted: Wed Aug 03, 2016 3:40 pm
by Ch4ozz
Yes its in AHCI mode, I checked that.
I read the data sheet up and down and tried alot of stuff but I cant get this to work.
I also already checked the linux source but it wont help either :(

For some reason when I go into AHCI mode by setting AE all the status flags are 0 and when using ACPI I cant shut down the PC anymore

Re: SATA and AHCI on real Hardware

Posted: Wed Aug 03, 2016 4:09 pm
by SpyderTL
Have you tried booting Linux, and does it seem to have any problems with that AHCI controller?

Exactly how old is that machine?

Re: SATA and AHCI on real Hardware

Posted: Wed Aug 03, 2016 4:30 pm
by Kazinsal
Perhaps the BIOS isn't releasing control of the HBA. Are you properly handling BIOS/OS Handoff Control (HBA memory offset 0x28)?

Re: SATA and AHCI on real Hardware

Posted: Wed Aug 03, 2016 4:44 pm
by Ch4ozz
SpyderTL wrote:Have you tried booting Linux, and does it seem to have any problems with that AHCI controller?

Exactly how old is that machine?
It should be from arround 2009
Booting up with windows works fine when its set to AHCI mode, it detects cdrom and hdd
Kazinsal wrote:Perhaps the BIOS isn't releasing control of the HBA. Are you properly handling BIOS/OS Handoff Control (HBA memory offset 0x28)?
Checking this right now, the cap2 tells me it supports it atleast.

EDIT: I added the following code and I get control but it doesnt change anything sadly:

Code: Select all

printf("Handoff: %X\n", abar->cap2 & 1);
		
abar->bohc |= 2;
		
while(!(abar->bohc & 2) || (abar->bohc & 1))
	Sleep(10);
		
printf("Got access to HBA!\n");


Im not sure if I have to do a specific task the BIOS did before, but actually the code should run...
Theres not much more I can do before setting the AHCI Enabled bit :(
Should I update the BIOS? Not sure ...

Re: SATA and AHCI on real Hardware

Posted: Wed Aug 03, 2016 9:11 pm
by BenLunt
Since the HBA (AHCI controller card) will have an optional ROM BIOS present, it most likely has already reset the HBA for you. It knows exactly how to do it, so I would allow it to. Therefore, skip the HBA reset until you actually need it.

However, I would do a Port Reset on each implemented port causing a COMRESET to be sent.
Write a 1 to PxSCTL.DET, wait a minimum of 1 mS, then clear PxSCTL.DET. Then wait for (with timeout) PxSSTS.DET == 3.

Also, within your code, you do

Code: Select all

while(abar->ghc & 1)
      Sleep(10);
Depending on the compiler and its options, the compiler could completely skip that whole block of code since it can't see any reason abar->ghc would ever change. Is abar set as volatile?

I suggest that you create two functions, one to read a dword from the controller and one to write a dword to the controller.

The compiler might change the read of 'abar->ghc' above to a byte read, where as the HBA might not like that byte read.

Ben
http://www.fysnet.net/media_storage_devices.htm

Re: SATA and AHCI on real Hardware

Posted: Thu Aug 04, 2016 4:46 am
by Ch4ozz
BenLunt wrote:Since the HBA (AHCI controller card) will have an optional ROM BIOS present, it most likely has already reset the HBA for you. It knows exactly how to do it, so I would allow it to. Therefore, skip the HBA reset until you actually need it.

However, I would do a Port Reset on each implemented port causing a COMRESET to be sent.
Write a 1 to PxSCTL.DET, wait a minimum of 1 mS, then clear PxSCTL.DET. Then wait for (with timeout) PxSSTS.DET == 3.

Also, within your code, you do

Code: Select all

while(abar->ghc & 1)
      Sleep(10);
Depending on the compiler and its options, the compiler could completely skip that whole block of code since it can't see any reason abar->ghc would ever change. Is abar set as volatile?

I suggest that you create two functions, one to read a dword from the controller and one to write a dword to the controller.

The compiler might change the read of 'abar->ghc' above to a byte read, where as the HBA might not like that byte read.

Ben
http://www.fysnet.net/media_storage_devices.htm
Thanks, I will try a port reset.
I decleared the struct itself and the variable abar as volatile and disabled optimization for testing (-O0) so I should be fine here.
Nevertheless I will take a peek with IDA and check if everything is alright!

EDIT: The complete reset seems to work fine, but I will remove it for now and add the port reset
Also took a look at my kernel with IDA and it looks perfectly right

So I tested a portreset using this code:

Code: Select all

//Port reset
abar->ports[i].cmd &= ~HBA_PxCMD_ST;

while(abar->ports[i].cmd & HBA_PxCMD_CR)
	Sleep(10);

Sleep(1000);

uint32_t val = (abar->ports[i].sctl & 0xFFFFFFF0) | 1;
abar->ports[i].sctl = val; 

Sleep(1000);

abar->ports[i].sctl &= 0xFFFFFFF0;

while((abar->ports[i].sctl & 0xF) != 3)
{
	printf("sctl: %X\n", (abar->ports[i].sctl & 0xF));
	Sleep(1000);
}

Sleep(1000);

abar->ports[i].serr = 0xFFFFFFFF;
Everything works fine until I hit the sctl printf.
I always get 0 and not 3 as expected :(

Aso checked the BIOS version, its from 10/17/2012

Re: SATA and AHCI on real Hardware

Posted: Thu Aug 04, 2016 10:09 am
by Ch4ozz
I rechecked my port reset code and also found interesting stuff about SSS supporting devices.
I added this code before checking DET and it finally works!

Code: Select all

//Check for drive status
if(!(abar->pi & (1 << i)))
	continue;

//If port is not idle force it to be idle
if(abar->ports[i].cmd & (HBA_PxCMD_ST | HBA_PxCMD_CR | HBA_PxCMD_FRE | HBA_PxCMD_FR))
	stop_cmd(&abar->ports[i]);

port_rebase(&abar->ports[i], i);

//Reset port, disable slumber and partial state
abar->ports[i].sctl = 0x301;
Sleep(50);
abar->ports[i].sctl = 0x300;

if(abar->cap & HBA_MEM_CAP_SSS)
{
	abar->ports[i].cmd |= (HBA_PxCMD_SUD | HBA_PxCMD_POD | HBA_PxCMD_ICC);
	Sleep(10);
}

//Clear interrupt status and error status
abar->ports[i].serr = 0xFFFFFFFF;
abar->ports[i].is = 0xFFFFFFFF;
Thanks alot guys!

Re: SATA and AHCI on real Hardware

Posted: Thu Aug 04, 2016 11:39 am
by SpyderTL
The staggered spin up flag isn't mentioned on the AHCI wiki page. If you feel like you understand it well enough, can you add a quick section about what it is, and how it affects your driver code?

Thanks.

Re: SATA and AHCI on real Hardware

Posted: Thu Aug 04, 2016 5:08 pm
by Ch4ozz
SpyderTL wrote:The staggered spin up flag isn't mentioned on the AHCI wiki page. If you feel like you understand it well enough, can you add a quick section about what it is, and how it affects your driver code?

Thanks.
I wont say that I completely understood everything it does.
As far as I understood, it supports spinning up all devices with a different level of power usage.
All I did was setting 2 more flags in order to get the device into running state:
During initial system power-on in a system that supports staggered spin-up, software needs to spin up
each attached device. In addition, when the system transitions from the S3 or greater system power
management state back to S0, the attached drives will also need to be spun up. In order to spin up the
devices attached to the HBA, software should perform the procedure outlined in section 10.1.1 for
staggered spin-up.

Power On Device (POD): This bit is read/write for HBAs that support cold presence
detection on this port as indicated by PxCMD.CPD set to ‘1’. This bit is read only ‘1’ for
HBAs that do not support cold presence detect. When set, the HBA sets the state of a
pin on the HBA to ‘1’ so that it may be used to provide power to a cold-presence
detectable port.
This has barely anything to do with staggered spin-up other then its initialization :D

Re: SATA and AHCI on real Hardware

Posted: Thu Aug 04, 2016 10:33 pm
by SpyderTL
Understood. I will eventually get around to working on my AHCI driver, and if it hasn't been done by then, I'll try to clarify these flags on the wiki page.