ATA disk returning status 0x0

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.
User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

ATA disk returning status 0x0

Post by JamesM »

OK, well, after spending an hour working out why my new compile of GNU bash didnt work, and much chin-scratching, I came to the conclusion that a 2MB executable will NOT, in fact, fit on a 1.44MB disk... (it took me 1hr to do an 'ls -l' where all became clear ;) )

Anyway, I thus decided to break out some of my old ATA code (which is hacked together with masking tape) from an old kernel of mine.

I'm basically starting off with just reading the status register, and waiting for the device to become ready. I just do:

Code: Select all

* inb(0x1f7) // get status
* outb(0x1f0, 0xec) // or it could be eb, I cant remember and haven't got my code here. It's the IDENTIFY command.
* inb(0x1f7) // this is in a loop to catch any state changes.
So on real hardware (my dev machine - I haven't got a test box yet :( ) it returns a nite state change - something that looks decent. However on bochs and qEmu, it always returns zero. I've set up a HDD device for both, and on bootup the bochs bios flashes that it's seen it.

Is there any pre-initialisation that needs to be done with emulators? or am I missing something?

Cheers,

JamesM
User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

Post by JamesM »

OK, so I've worked out *why* the drive is returning a status of 0x0 - it's not spun up / initialised. If I go to a grub console, type 'root (hd0,0)', then exit and boot my kernel, it boots fine and gives a nice status code.

What do I need to do to get my ATA drive spun up and initialised? I've tried the obvious 'Initialize drive parameters', 'Idle', 'Standby' etc. - what am I missing? do I have to probe the PCI bus or something?

Ive looked at the GRUB source code but it uses BIOS interrupts and as such I have no idea what i'm missing (I'm in PMode using direct port I/O).

Cheers guys!

JamesM
User avatar
Dex
Member
Member
Posts: 1444
Joined: Fri Jan 27, 2006 12:00 am
Contact:

Post by Dex »

Try this
Attachments
HDPORTS.ASM
(6.11 KiB) Downloaded 161 times
exkor
Member
Member
Posts: 111
Joined: Wed May 23, 2007 9:38 pm

Post by exkor »

I understand drive attached to ide controller? How about selecting master/slave first. I think DEV bit is cleared by default but I would select the drive anyway then read status, drive should be able to accept commands then and should not be ready to transfer data(dont remember which is drq and which is drdy).
frank
Member
Member
Posts: 729
Joined: Sat Dec 30, 2006 2:31 pm
Location: East Coast, USA

Post by frank »

You could try resetting the drive.
User avatar
bewing
Member
Member
Posts: 1401
Joined: Wed Feb 07, 2007 1:45 pm
Location: Eugene, OR, US

Post by bewing »

I spent a good week or two figuring this stuff all out, months ago.

How to detect/initialize an ATA drive:
The only mode that the ATA spec guarantees that ALL drives possess is PIO 28 bit mode. So use that command set until the identify command tells you that other modes are available.

Some technical details that you should probably deal with in a more finalized version of everythihg:
1) Just for safety's sake, you should clear the HOB bits in the Control Registers for the primary & secondary ATA busses (IO ports 0x3f6 and 0x376, bit 7 -- value 0x80 -- but you can clear the entire byte);
2) The next thing you should do is to check if either bus is "floating." -- The ATA bus uses "high impedance" resistors to make all the wires sit at +5 volts if there are no drives attached. 0xff is an illegal value for the status of a drive -- so if you try to check drive status, and get a 0xff, then you know that *neither a slave NOR a master drive is present on the bus*.
(If you test the SLAVE drive's status, and there IS no slave drive, then the ATA spec says that the master drive is supposed to answer for the slave.)
3) The one and only difference between slave and master drives happens during initialization, and for the process to supposedly work, you are technically supposed to query the slave drive on a bus FIRST.

Now, on to the parts that *really* need to always happen. First, your above code has a big problem. Commands like Identify Device (0xec) are supposed to be sent to port 0x1f7 -- *not* 1f0 (for the primary ATA bus).
Also, you NEED to set a target drive (slave or master) before you issue it.

But, back to doing things in the proper order ...
After clearing HOB, and testing for float,
next, you verify that you have at least one responsive drive on the bus (as I said, no matter how the drive selector bit is set, the master always has to answer if the slave is addressed but does not exist).
You do this by putting any old value into one of its CHS registers (you don't know that LBA is available yet!). If a drive is there, it is supposed to allow you to read back the last value written into each CHS register. Just for fun, it's better to test more than one register.
As an example (on my secondary ATA bus) I put a 0x73 into IO port/register 0x173, and a 0x74 into IO port 0x174, then I put a 0x8b into port 0x172 (just to mess up the bits on the bus a little more) -- then I read back port 0x173 and 0x174 and make sure I get back a 0x73 and 0x74 respectively.

THEN, you do the identify tests. As I said, technically, you are supposed to do the slaves first -- but don't worry about it to start with.
To identify, send 0xB0 for slave, or 0xA0 for master, to port 0x1f6 (for the primary ATA bus). These values select CHS mode -- as I said, you don't know that LBA is available yet for this drive.
THEN send 0xEC command to port 0x1f7.
IMMEDIATELY read port 0x1f7 to check the status byte. According to the ATA spec, either the BUSY or DATA bit must be set. So a value of 0 is illegal. If you read a 0 status, the specified drive (slave or master) does *not* exist.
Then you need to read back the requested 256 shorts with proper IO delays, BUSY and DATA status bit testing, from port 0x1f0.
Then go on to the next drive and bus, until you've done them all.
User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

Post by JamesM »

Ahh bewing! you identified my problem completely!

I managed to solve the problem at around 11pm last night after looking through bluecode's source for lightOS, getting the init code and commenting out "outb" lines until it didn't work any more.

It seemed selecting the drive was what I was missing (out 0x1f6, 0x00). I think when I get some time I may add that to the ATA page on the wiki.

Cheers for your help guys,

JamesM
User avatar
lukem95
Member
Member
Posts: 536
Joined: Fri Aug 03, 2007 6:03 am
Location: Cambridge, UK

Post by lukem95 »

congrats :)

my ata drivers still very in development lol, i might end up finishing it or at least getting r/w one day!
exkor
Member
Member
Posts: 111
Joined: Wed May 23, 2007 9:38 pm

Post by exkor »

bewing wrote: master drive is supposed to answer for the slave.
Example of such situation is when SATA ports are masked as IDE controler and this IDE cntrl has only master ports implemented in silicon. Took me some time to do "Identify Device" properly. Had problems with disabling interrupts. Nasty.
nstn2879
Posts: 2
Joined: Wed Oct 17, 2007 8:35 pm

Identify SATA devices

Post by nstn2879 »

[quote="exkor"][quote="bewing"] master drive is supposed to answer for the slave.[/quote]
Example of such situation is when SATA ports are masked as IDE controler and this IDE cntrl has only master ports implemented in silicon. Took me some time to do "Identify Device" properly. Had problems with disabling interrupts. Nasty.[/quote]

how do you identify a SATA hdd with C. I'm using djgpp.
I need to get model & serial numbers.

any help is appreciated
exkor
Member
Member
Posts: 111
Joined: Wed May 23, 2007 9:38 pm

Re: Identify SATA devices

Post by exkor »

nstn2879 wrote:
how do you identify a SATA hdd with C. I'm using djgpp.
I need to get model & serial numbers.

any help is appreciated
some SATA devs masked as IDE controller hasve BAR_5, i guess you just do identify device like regular ata device.

sata ahci identified by pci classcode i didn't dig extensively. but you need to find if any port are taken by devices on sata controller first

eax points to memory

Code: Select all

  
  mov  ecx, [eax]         ;ecx = CAP, Host Capabilities
  mov  edx, [eax+0ch]     ;edx = PI, Ports Implemented, 32bits - 32ports max
  lea  esi, [eax+100h]
  and  ecx, 11111b        ;ecx = NP, Number of ports - 1
.is_port_implemented:
  sar  edx, 1             ;lowest bit -> CF=1/0
  jnc  .next_port         ;no port if CF=0

;  bit 16(CPS bit) of PxCMD detects the device
;  bit 20(CPD) tells if bit 16 is valid, HPCP(bit 18) has to be 1 as well
;  bit 2

;  port PxSSTS - another way of detection

.next_port:
  add  esi, 80h
  sub  ecx, 1
  jns  .is_port_implemented        
no c/c++ questions for me please, 'c' hides lots of stuff that are clearly seen in assembly or people posting 'c' code don't show lots of code
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Re: Identify SATA devices

Post by Candy »

exkor wrote:no c/c++ questions for me please, 'c' hides lots of stuff that are clearly seen in assembly or people posting 'c' code don't show lots of code
C tends to hide complexity you don't want to know, that your compiler can do better and that is irrelevant. I don't consider this a bad thing - why do you?
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

The only time I have seen someone trying to answer this was when Linus Torvalds was replying to why there is no C++ in the Linux kernel. IIRC, his answer was because it 'hides the memory management stuff'.

I don't quite understand this response because I have had not had a problem with memory management in C++ - perhaps this is because I don't attempt to use a userspace runtime library for my kernel :wink:

I fail to see what inb and outb hide from the C/C++ programmer.

Cheers,
Adam
User avatar
JamesM
Member
Member
Posts: 2935
Joined: Tue Jul 10, 2007 5:27 am
Location: York, United Kingdom
Contact:

Post by JamesM »

no c/c++ questions for me please, 'c' hides lots of stuff that are clearly seen in assembly or people posting 'c' code don't show lots of code
Zealot alert!
nstn2879
Posts: 2
Joined: Wed Oct 17, 2007 8:35 pm

Post by nstn2879 »

I'm sorry to be an ignorant pest, I don't know how to compile asm with djgpp.

I can retreive all the sata drive info using int13h except for the model and serial numbers.

this is what I use to get the 1f0h ports:

Code: Select all

int hddetect(int driveselect, int master) /* drive value 0x1f0 */
{
     	int i,j,err=1;
	unsigned int controller,masterselect,drivectl,readbuff;
 	if (master > 0){masterselect = 0xa0;}else{masterselect = 0xb0;}

	readbuff=driveselect;
	drivectl=driveselect+6;
	controller=driveselect+7;

	for (i=0; i<100000; i++)
	{
     		if (inp(controller) == 0x50)   /* check if controller is ready */
		{
		i=100000;
		}
     	}
	printf("Controller 0x%x Ready=%x\t",controller,inp(controller));
     	outp(drivectl, masterselect); /* masterselect 0xa0 master */
     	outp(controller, 0xec); /* ask for data */
	for (i=0; i<1000000; i++)
	{
     		if (inp(controller) == 0x58)   /* check if data is ready */
		{
		i=1000000;
		err=0;
		}
     	}
	printf("Data Ready=%x\n",inp(controller));
     	for (i = 0; i<256; i++) /* Read the data */
          	data[i] = inpw(readbuff);

     	for(i=0,j=27;j<=46;j++)
     	{
          	model[i++]=(char)(data[j]/256);
          	model[i++]=(char)(data[j]%256);
     	}
     	model[i]='\0';

     	for(i=0,j=10;j<=19;j++)
     	{
          	serial[i++]=(char)(data[j]/256);
          	serial[i++]=(char)(data[j]%256);
     	}
     	serial[i]='\0';
	
	return err;
}
All I need is to identify the SATA model & serial numbers to help for manual selection of drives present. I cannot find any documentation except for the T13 papers:
http://www.seagate.com/support/disc/man ... 153r17.pdf

// moderator note: use the [ code ] tags!
Post Reply