HDD detection
HDD detection
Hello all!
I am trying to enumerate hard drives connected to ATA bus. I've read ATAATAPI-6-3b specification and found, that there is no 'direct' way to do it. The specification describes EXECUTE_DEVICE_DIAGNOSTICS command, but it doesn't give us a good piece of information: if bit 7 in error register is clear, it means, that Device1 is healthy, or not present. Also, the problem is that specification doesn't consider Device1 only configurations, but a large number of host controllers support it.
So, the quation is, how to enumerate hard drives in the most correct (from the point of view of specification) and working way?
TIA, Mikae.
P.S. I used search, and I've found some topics connected to HDD detection, but answers in that topics leads to the specification, in which I didn't find a way.
I am trying to enumerate hard drives connected to ATA bus. I've read ATAATAPI-6-3b specification and found, that there is no 'direct' way to do it. The specification describes EXECUTE_DEVICE_DIAGNOSTICS command, but it doesn't give us a good piece of information: if bit 7 in error register is clear, it means, that Device1 is healthy, or not present. Also, the problem is that specification doesn't consider Device1 only configurations, but a large number of host controllers support it.
So, the quation is, how to enumerate hard drives in the most correct (from the point of view of specification) and working way?
TIA, Mikae.
P.S. I used search, and I've found some topics connected to HDD detection, but answers in that topics leads to the specification, in which I didn't find a way.
Re: HDD detection
I'm afraid I don't know the direct answer, so this may be very unhelpful, but if I understand correctly, there's a maximum of two drives per ATA controller, right? And there must be some command in the ATA or ATAPI specification that allows you to query a device? Then isn't it just a matter of querying the device, and if you do not get an answer, assume it's not there? Or am I thinking to easy here?Mikae wrote:So, the quation is, how to enumerate hard drives in the most correct (from the point of view of specification) and working way?
JAL
There is no standart way defined in specs to detect if 1,2,3 or 4 devices connected to IDE controller. Some people write and then read data. Another way use identify device command. But most correct way is to reset ata drive(dont remember how) - has major disadvantage especially on cdroms - will take long time.
I suggest this:select possible drive, read its status, if status =0x7f,0,0xff (all values are unsigned bytes) then there is no drive here otherwise execute Identify device, read status after that and ensure its valid.
EDIT: BSY must be 0 after all status reads so you'll need delays, half a second maybe.
To find which features ATA drives support (is it ATA5,6,7, which features) you need to do IDENTIFY DEVICE command and not DEVICE DIAGNOSTIC.
These are useful threads to read if you encounter any error. And links in them are useful.
http://www.osdev.org/phpBB2/viewtopic.php?t=14204
http://www.osdev.org/phpBB2/viewtopic.php?t=14764
http://www.osdev.org/phpBB2/viewtopic.php?t=9802
http://www.osdev.org/phpBB2/viewtopic.php?t=10296
http://www.osdev.org/phpBB2/viewtopic.php?t=12306
http://www.osdev.org/phpBB2/viewtopic.php?t=13861
http://www.osdev.org/phpBB2/viewtopic.php?t=15087
http://www.osdev.org/phpBB2/viewtopic.php?t=6796
http://www.osdev.org/phpBB2/viewtopic.php?t=9091
I suggest this:select possible drive, read its status, if status =0x7f,0,0xff (all values are unsigned bytes) then there is no drive here otherwise execute Identify device, read status after that and ensure its valid.
EDIT: BSY must be 0 after all status reads so you'll need delays, half a second maybe.
To find which features ATA drives support (is it ATA5,6,7, which features) you need to do IDENTIFY DEVICE command and not DEVICE DIAGNOSTIC.
These are useful threads to read if you encounter any error. And links in them are useful.
http://www.osdev.org/phpBB2/viewtopic.php?t=14204
http://www.osdev.org/phpBB2/viewtopic.php?t=14764
http://www.osdev.org/phpBB2/viewtopic.php?t=9802
http://www.osdev.org/phpBB2/viewtopic.php?t=10296
http://www.osdev.org/phpBB2/viewtopic.php?t=12306
http://www.osdev.org/phpBB2/viewtopic.php?t=13861
http://www.osdev.org/phpBB2/viewtopic.php?t=15087
http://www.osdev.org/phpBB2/viewtopic.php?t=6796
http://www.osdev.org/phpBB2/viewtopic.php?t=9091
exkorWell, I think, it is possible to detect Device0 only configuration this way:
1. Set nIEN to 1. (Turn off device interrupts).
2. Send IDENTIFY_DEVICE.
3. Wait 400 ns for device to set BSY.
4. Check BSY. If it is 0 and DRQ is 0 then there is no Device1. If it is 0 and DRQ is 1, then Device1 present.
Accordingly to standard it will work perfect for Device0 only configurations. But I'm not sure about Device1 only configurations
.
1. Set nIEN to 1. (Turn off device interrupts).
2. Send IDENTIFY_DEVICE.
3. Wait 400 ns for device to set BSY.
4. Check BSY. If it is 0 and DRQ is 0 then there is no Device1. If it is 0 and DRQ is 1, then Device1 present.
Accordingly to standard it will work perfect for Device0 only configurations. But I'm not sure about Device1 only configurations

before you send any command (include identify device) you need to ensure that status is valid. That is you need to read status first, values that I specified are invalid on all motherboard I tested(not many really)
As for method to choose - reseting the drive would most correct but also slowest. You will need to read signature after that.
1. Set nIEN to 1. (Turn off interrupt on entire channel).
1.1 Select Master/Slave
1.5 Read Status
2. Send IDENTIFY_DEVICE.
and you need to check for ERR bit each time after status read. Try on diff configuration and see if it works.
Oops forgot again DRDY has to be 1 for Ident Dev and can be any before you do Ident Packet Dev
And I think some versions of ATA(maybe ATA4+) has to put signature if this is ATAPI device after you do Ident Device on ATAPI drive.
After each successful Identify Device DRQ=1 & BSY=0 & ERR=0
As for method to choose - reseting the drive would most correct but also slowest. You will need to read signature after that.
1. Set nIEN to 1. (Turn off interrupt on entire channel).
1.1 Select Master/Slave
1.5 Read Status
2. Send IDENTIFY_DEVICE.
and you need to check for ERR bit each time after status read. Try on diff configuration and see if it works.
Oops forgot again DRDY has to be 1 for Ident Dev and can be any before you do Ident Packet Dev
And I think some versions of ATA(maybe ATA4+) has to put signature if this is ATAPI device after you do Ident Device on ATAPI drive.
After each successful Identify Device DRQ=1 & BSY=0 & ERR=0
As said above, detecting ATAPI stuff (CD-ROMs) takes a lot of time, so I agree with the literal meaning of the title to this thread -- you want to detect only ATA hard drives initially. You can go back later and do the RESET stuff for the ATAPI drives once you are in multitasking mode, and you can do it in the background.
According to my reading of the ATA spec, you should enumerate ATA hard drives as I listed here (it's also on exkor's list): http://www.osdev.org/phpBB2/viewtopic.php?t=15087
No matter how you do it, you need to test for a floating bus before you do ANYTHING. Then I use IO port read/writes to detect hardware, then IDENTIFY to detect actual drives. The spec says that after you send any command, the drive must respond immediately by setting either BSY or DRQ the very next time you read the status (or alternate status) port -- so I am not sure you need to do that wait that you mentioned. (But the spec certainly might be wrong.)
According to my reading of the ATA spec, you should enumerate ATA hard drives as I listed here (it's also on exkor's list): http://www.osdev.org/phpBB2/viewtopic.php?t=15087
No matter how you do it, you need to test for a floating bus before you do ANYTHING. Then I use IO port read/writes to detect hardware, then IDENTIFY to detect actual drives. The spec says that after you send any command, the drive must respond immediately by setting either BSY or DRQ the very next time you read the status (or alternate status) port -- so I am not sure you need to do that wait that you mentioned. (But the spec certainly might be wrong.)
bewing
Yes, you are right, I don't think about ATAPI or SATA for now.
But I have some questions about your post.
1.
2.
4.
5. What is 'DRQ data block'? I've met this phrase in the standard, in PIO data-in protocol, but there is no definition for it. I suppose, that this is a data block, which device sends per interrupt (while DRQ bit is set), but I'm not sure. I ask it, cause it is possible to send multiple sectors per one interrupt. So, in this case we can't determine, which sector failed, cause in data transfer phase a device doesn't set status register. Am I right?
TIA, Mikae
Yes, you are right, I don't think about ATAPI or SATA for now.
But I have some questions about your post.
1.
I am very bad in electricity, so what is 'impedance' from the point of view of software? Does it mean, that if there is no devices on the bus, if a read from any register gives me 0xFF?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*.
2.
If we can check presence of any drive on the bus using 'impedance', why we need this additional check? Or, even if we don't know for now, how many drives there are on the bus (0, 1 or both), we can do it using IDENTIFY_DEVICE.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.
4.
Is it important to specify mode, LBA or CHS in the case of IDENTIFY_DEVICE? Anyway, we will not use addressing in the command.These values select CHS mode -- as I said, you don't know that LBA is available yet for this drive.
5. What is 'DRQ data block'? I've met this phrase in the standard, in PIO data-in protocol, but there is no definition for it. I suppose, that this is a data block, which device sends per interrupt (while DRQ bit is set), but I'm not sure. I ask it, cause it is possible to send multiple sectors per one interrupt. So, in this case we can't determine, which sector failed, cause in data transfer phase a device doesn't set status register. Am I right?
TIA, Mikae
Yes. You have it right. Any wire that is at +5 volts is an "on" bit, as far as software goes. The resistors make all 8 wires go to +5 volts, forever (if there are no drives attached), so you always have 8 ON bits, which reads as a 0xff, when you try to read any ATA IO port, on an ATA bus with no drives.Mikae wrote: I am very bad in electricity, so what is 'impedance' from the point of view of software? Does it mean, that if there is no devices on the bus, if a read from any register gives me 0xFF?
You have a good point. That step is really optional. It can detect a hint of the presence of an ATAPI drive, in some cases.Mikae wrote: If we can check presence of any drive on the bus using 'impedance', why we need this additional check? Or, even if we don't know for now, how many drives there are on the bus (0, 1 or both), we can do it using IDENTIFY_DEVICE.
You MUST select a target drive for the IDENTIFY command -- either master or slave, on port 0x1f6, before sending the IDENTIFY. The LBA bit is on port 0x1f6 also (along with the master/slave bit). You have no choice but to set the LBA bit either on or off when you select master/slave. I am saying "set it to OFF". That is, send either 0xA0 or 0xB0 to port 0x1f6, before doing IDENTIFY.Mikae wrote: Is it important to specify mode, LBA or CHS in the case of IDENTIFY_DEVICE? Anyway, we will not use addressing in the command.
Yes, you are right on both things. It is all the words of data between interrupts.Mikae wrote: What is 'DRQ data block'? ... Am I right?
It is exactly 256 words, unless you use the READ/WRITE MULTIPLE commands, as you say. And if you use READ/WRITE MULTIPLE, you cannot tell exactly which sector failed (unless you do a "retry", using READ/WRITE SECTORS).
Thank you for detailed answers, bewing!
So, concluding all advises I can say that I have:
0. Reset devices.
1. Check alternate status register for value 0xFF. If it is 0xFF, then there are no devices on the bus.
2. Issue IDENTIFY_DEVICE command to a device.
3. Wait while BSY.
4. If there is an error, check error register. If there is ABRT in error register, issue IDENTIFY_PACKET_DEVICE.
5. If there is no error, check DRQ. If DRQ is zero, then the device is absent, else the device is present. If the device is present, read 256 words of identification information. (I've heard, that some devices report more than 256 word).
6. Repeat steps 2-5 for next device.
So, concluding all advises I can say that I have:
0. Reset devices.
1. Check alternate status register for value 0xFF. If it is 0xFF, then there are no devices on the bus.
2. Issue IDENTIFY_DEVICE command to a device.
3. Wait while BSY.
4. If there is an error, check error register. If there is ABRT in error register, issue IDENTIFY_PACKET_DEVICE.
5. If there is no error, check DRQ. If DRQ is zero, then the device is absent, else the device is present. If the device is present, read 256 words of identification information. (I've heard, that some devices report more than 256 word).
6. Repeat steps 2-5 for next device.
This should be in the wiki...Mikae wrote:0. Reset devices.
1. Check alternate status register for value 0xFF. If it is 0xFF, then there are no devices on the bus.
2. Issue IDENTIFY_DEVICE command to a device.
3. Wait while BSY.
4. If there is an error, check error register. If there is ABRT in error register, issue IDENTIFY_PACKET_DEVICE.
5. If there is no error, check DRQ. If DRQ is zero, then the device is absent, else the device is present. If the device is present, read 256 words of identification information. (I've heard, that some devices report more than 256 word).
6. Repeat steps 2-5 for next device.
JAL
0. disable interrupts, nEIN bit, you might move this step anywhere before step #2.
0.5 Select master/slave (according to bewing this would be 1.5 I think but i do it here)
1. Check alternate status register for value 0xFF. If it is 0xFF, then there are no devices on the bus. if DRDY is not set you can't do Identify Device but you can do identify packet device. Am I right here?
2. Issue IDENTIFY_DEVICE command to a device.
3. Wait while BSY. better have some timeouts here
4. If there is an error, check error register. If there is ABRT in error register, issue IDENTIFY_PACKET_DEVICE.
5. If there is no error, check DRQ. If DRQ is zero, then the device is absent, else the device is present. If the device is present, read(depending how you read you may need timeouts between eah word) 256 words of identification information. (I've heard, that some devices report more than 256 word).
5.5 read status register and alternate status reg after reading 256 words
6. Repeat steps 0.5 - 5 for next device.[/quote][/b]
7. Repeat 0-6 for next channel
8. Repeat 0-7 for next IDE cntrl
EDIT: I suggest you start coding and find out what works. I only made 1st post to point out that EXECUTE DIAGNOSTICS is not where you should look.
0.5 Select master/slave (according to bewing this would be 1.5 I think but i do it here)
1. Check alternate status register for value 0xFF. If it is 0xFF, then there are no devices on the bus. if DRDY is not set you can't do Identify Device but you can do identify packet device. Am I right here?
2. Issue IDENTIFY_DEVICE command to a device.
3. Wait while BSY. better have some timeouts here
4. If there is an error, check error register. If there is ABRT in error register, issue IDENTIFY_PACKET_DEVICE.
5. If there is no error, check DRQ. If DRQ is zero, then the device is absent, else the device is present. If the device is present, read(depending how you read you may need timeouts between eah word) 256 words of identification information. (I've heard, that some devices report more than 256 word).
5.5 read status register and alternate status reg after reading 256 words
6. Repeat steps 0.5 - 5 for next device.[/quote][/b]
7. Repeat 0-6 for next channel
8. Repeat 0-7 for next IDE cntrl
EDIT: I suggest you start coding and find out what works. I only made 1st post to point out that EXECUTE DIAGNOSTICS is not where you should look.
Well, jal -- I'd like to get it into the wiki ... but I don't feel like I am enough of an expert to write up the entry myself, yet. My code is working nicely for me on one piece of real hardware (and in bochs, but that is easy). exkor and XCHG have probably done as much reading on the subject as I have -- but I disagree with them on some things.
For example:
@exkor -- I see no reason at all to set nIEN and turn off interrupts, to do the IDENTIFY commands. The interrupts can either be ignored, or they can be used in exactly the same way that they are used during normal disk reads/writes. So, what exactly is your reasoning for turning them off?
I agree with your comments about timeouts.
I disagree with your timing on sending a value to port 0x1f6 -- you do not want to mess up the voltages on the bus (by writing anything to the bus) before you read the status the first time to check for float.
For example:
@exkor -- I see no reason at all to set nIEN and turn off interrupts, to do the IDENTIFY commands. The interrupts can either be ignored, or they can be used in exactly the same way that they are used during normal disk reads/writes. So, what exactly is your reasoning for turning them off?
I agree with your comments about timeouts.
I disagree with your timing on sending a value to port 0x1f6 -- you do not want to mess up the voltages on the bus (by writing anything to the bus) before you read the status the first time to check for float.
I'm uncertain, myself. But this is being done at boot time. The boot disk has spun up and started to boot. If there are any drives that are still spun down (DRDY not set) they are dead, I'd say.exkor wrote: if DRDY is not set you can't do Identify Device but you can do identify packet device. Am I right here?
By "timeouts" here, we should clarify that means tiny IO port delays -- jmp $+2, or a call to a ret opcode.exkor wrote: If the device is present, read (depending how you read you may need timeouts between eah word) 256 words of identification information.
I hope not, because then there is no way to know when to stop reading. The spec specifically states that an ATA compliant device will send exactly 256 words, always -- not that hardware manufacturers follow specs very well.exkor wrote: (I've heard, that some devices report more than 256 word).
AFAIK there is no reason to read both, ever, unless you are hoping to prevent an interrupt from firing. You only need to read one or the other. So, again, could you please state your reasoning on this?exkor wrote: 5.5 read status register and alternate status reg after reading 256 words
exkor
Well, I ommited some details to clarify the algorithm itself.
bewing
I think, there is a reason to turn off interrupts, cause we cannot rely on them, so they will just irritate us during the check.
About more than 256 words -- I've heard it from a person, who wrote his own ATA/SATA driver for Windows, so, I think there is a reason to believe him. But anyway, there is probability, that he is wrong. Really it is a big meanness from a device -- generic driver may not know about it. A way to avoid it, is to check DRQ bit after reading 256 words, and just read until it is reset in an empty loop. Hope, it will help (I can't check it since I never deal with such situation).
Now I'll try to describe 'final' detailed version of the algorithm:
1. Reset devices on a channel.
2. Read signatures from both devices.
3. Read value from alternate status register. If it is 0xFF, then channel is empty -- discard signatures, go to 15. If it is not 0xFF, then there is at least one device on the channel.
4. Select DeviceX in DH register.
5. Set nIEN to 1.
6. Wait for DRDY bit to be set.
7. Issue IDENTIFY_DEVICE command.
8. Wait 400 ns to allow the device to set BSY to one.
9. Wait while BSY is set.
10. Check alternate status register for error.
11. If there is an error, it means that the device is present, and its signature is correct. Check error register for ABRT error. If the error is ABRT, then issue IDENTIFY_PACKET_DEVICE. Also, it is possible to check signature of the device -- it should belong to ATAPI. If signature is not ATAPI, or there is an error other than ABRT, then there is a strange error.
12. If there is no error, check DRQ bit.
13. If it not set, then there is no device. Discard signature, go to 15.
14. If it is set, then the device is present and signature is correct. Read 256 words of device information. Read the rest of data (if DRQ is set) while DRQ is set.
15. Repeat steps 4 - 14 for next device.
16. Repeat steps 1 - 15 for next channel.
P.s. I noticed, that some devices (for example, mine) set incorrect signature byte in DH register. It is 0xA instead of 0x0 -- some devices set obsolete bits.
Well, I ommited some details to clarify the algorithm itself.
bewing
I think, there is a reason to turn off interrupts, cause we cannot rely on them, so they will just irritate us during the check.
About more than 256 words -- I've heard it from a person, who wrote his own ATA/SATA driver for Windows, so, I think there is a reason to believe him. But anyway, there is probability, that he is wrong. Really it is a big meanness from a device -- generic driver may not know about it. A way to avoid it, is to check DRQ bit after reading 256 words, and just read until it is reset in an empty loop. Hope, it will help (I can't check it since I never deal with such situation).
Now I'll try to describe 'final' detailed version of the algorithm:
1. Reset devices on a channel.
2. Read signatures from both devices.
3. Read value from alternate status register. If it is 0xFF, then channel is empty -- discard signatures, go to 15. If it is not 0xFF, then there is at least one device on the channel.
4. Select DeviceX in DH register.
5. Set nIEN to 1.
6. Wait for DRDY bit to be set.
7. Issue IDENTIFY_DEVICE command.
8. Wait 400 ns to allow the device to set BSY to one.
9. Wait while BSY is set.
10. Check alternate status register for error.
11. If there is an error, it means that the device is present, and its signature is correct. Check error register for ABRT error. If the error is ABRT, then issue IDENTIFY_PACKET_DEVICE. Also, it is possible to check signature of the device -- it should belong to ATAPI. If signature is not ATAPI, or there is an error other than ABRT, then there is a strange error.
12. If there is no error, check DRQ bit.
13. If it not set, then there is no device. Discard signature, go to 15.
14. If it is set, then the device is present and signature is correct. Read 256 words of device information. Read the rest of data (if DRQ is set) while DRQ is set.
15. Repeat steps 4 - 14 for next device.
16. Repeat steps 1 - 15 for next channel.
P.s. I noticed, that some devices (for example, mine) set incorrect signature byte in DH register. It is 0xA instead of 0x0 -- some devices set obsolete bits.
You dont have to reset device along with identify Device. BIOS will do for you. So steps 1 & 2 are gone. I dont think it'll mess things if some bootloader reads from hard drive.
I never did reset myself but I think you need select drive first before DEVICE RESET(command code = 08h). Device Reset(08h) - use is prohibited when PACKET is not implemented(ATA7 docs).
There is also software reset (bit SRST). Protocol described in section 11.2 of ATA7 Vol2 docs.
As for disabling ata interrupts - there maybe situation when they are enabled in CPU but vector in IDT is not set(not a problem if #GP working) or used by other device(if native IDE cntrl).
6. Wait for DRDY bit to be set. once again either timeouts OR if BSY=0 & DRDY!=1 then no drive
I never did reset myself but I think you need select drive first before DEVICE RESET(command code = 08h). Device Reset(08h) - use is prohibited when PACKET is not implemented(ATA7 docs).
There is also software reset (bit SRST). Protocol described in section 11.2 of ATA7 Vol2 docs.
As for disabling ata interrupts - there maybe situation when they are enabled in CPU but vector in IDT is not set(not a problem if #GP working) or used by other device(if native IDE cntrl).
6. Wait for DRDY bit to be set. once again either timeouts OR if BSY=0 & DRDY!=1 then no drive