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.
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:
* 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?
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).
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).
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.
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.
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.
[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.
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
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?
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
I fail to see what inb and outb hide from the C/C++ programmer.
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