Reading From Void | OS/2 Museum

7 min read Original article ↗

Recently I came across the following question: What happens when software reads the registers of a non-existent IDE controller? That is, what happens when software reads for example ports in the 1F0h-1F7h/3F6h range (primary IDE channel) when there is no device on that channel?

For the vast majority of devices, the answer is simple, reads return with all bits set (0FFh). That’s when no device decodes the given address, i.e. no one claims the bus cycle/transaction. But IDE is different, because there often is a device which decodes those addresses, namely the IDE controller, usually on the motherboard within the southbridge chip.

The answer then might be buried somewhere deep in the Intel ICH datasheets or similar documentation. But it’s not.

There is only one curious piece of information in the Intel ICH6 and similar datasheets. These devices are compatible with earlier Intel chipsets which supported two IDE channels; the ICH6 still has all the primary and secondary channel registers, but physically there is no secondary channel. The ICH6 chipset was commonly used in PCs built around 2005, when it was common to have a SATA hard disk and a PATA CD/DVD-ROM (since SATA variants were rare if not unavailable).

The ICH6 IDE_TIMS register contains an enable bit documented as follows: “Enables the ICH6 to decode the associated Command Blocks (170–177h) and Control Block (376h). Accesses to these ranges return 00h, as the secondary channel is not implemented.”

The register reads clearly behave in an unusual way–rather than returning all bits set, they return all bits clear. But this is a very special case of a controller which does not even allow IDE devices to be attached, not the general case of a controller with no cable plugged in.

The Problem

Why would anyone care at all? The trouble is with software detection of IDE drives, more specifically detecting that they’re not present. The Intel ICH PRM for example suggests sending the IDENTIFY DEVICE or IDENTIFY PACKET DEVICE command. But that’s not so simple, and here’s why.

The IDE “task file” registers are only valid when the device is not busy, i.e. when the BSY bit (bit 7), in the status register (typically port 1F7h for the primary IDE channel) is clear. When BSY=1, writing commands generally produces undefined results, other bits in the status register are not valid, and other registers cannot be reliably accessed. The BSY bit will be set after drives power up and cleared once the device initialization completed.

The upshot is that software needs to wait for BSY=0 before doing anything further. But if there were no drive and register reads always returned with all bits set, it would look like the device was busy, and software would have to wait potentially a long time (about 30 seconds) before it could be certain that no device is attached, because it can take drives a while to initialize after power-up.

When the ICH6 always returns zeros on the secondary IDE controller ports, it looks to software like the drive is not busy, and it can access the other task file registers or write commands. And it will quickly establish that there’s no IDE device present. That’s clearly why the ICH6 behaves that way.

But what happens on a real IDE channel which may or may not have a drive or two attached?

Before considering that question, let’s complicate matters further by considering two cases of IDE device detection. There is the firmware (BIOS or EFI) which generally knows whether an IDE controller is present, but not whether a device is attached. An operating system on the other hand may not be able to determine whether an IDE interface is present at all, especially for secondary/tertiary/quaternary interfaces which often have no firmware support.

In the former case (firmware), drive detection generally “knows” its hardware and must rely on the BSY bit. Because the firmware runs very early, it must also expect that the BSY bit could take a few seconds to clear. The key is that the firmware normally knows whether given registers are IDE task file registers or not.

Operating systems may need to detect whether an IDE interface is present at all (i.e. if anything is decoding the registers). Such detection cannot rely on the BSY bit precisely because if there is no IDE interface, the registers will return 0FFh when read and it will look as if a drive were constantly busy. However, operating systems can typically safely assume that the drives are not busy when the detection starts, and if IDE registers return 0FFh (or zeros), there is certainly no IDE drive attached and most likely no IDE interface either.

The Answer

Looking for the answer in the ATA standards may seem pointless, because the case under consideration occurs when there is no ATA device. But it turns out the answer is there, in plain sight, just not in a place where one would probably look for it.

The early ATA standards (ATA-1 and ATA-2) have very little to say on the subject. In the ATA-3 draft standard (January 1997), clause 3.3 “Electrical characteristics” is the part which software engineers invariably skip. Said clause contains a table called “Driver types and required termination”, which certainly does not sound like it could possibly be relevant to software. It includes the following somewhat cryptic note: “Devices shall not have a pull-up resistor on DD7. It is recommended that a host have a 10 kΩ pull-down resistor and not a pull-up resistor on DD7 to allow a host to recognize the absence of a device at power-up. It is intended that this recommendation become mandatory in a future revision of this standard.”

Okay, that’s probably clear as mud to most software people. Are the newer ATA standards any easier to understand? The ATA-4 draft standard (August 1998) is not: “Devices shall not have a pull-up resistor on DD7. The host shall have a 10 kΩ pull-down resistor and not a pull-up resistor on DD7 to allow a host to recognize the absence of a device at power-up.” The only thing that changed is that the pull-down resistor on the host side is mandatory, as the ATA-3 specification forewarned.

Luckily, the ATA-5 draft standard (Feb 2000) finally provides a clear answer. The relevant text now reads: “Devices shall not have a pull-up resistor on DD7. The host shall have a 10 kΩ pull-down resistor and not a pull-up resistor on DD7 to allow a host to recognize the absence of a device at power-up so that a host shall detect BSY as being cleared when attempting to read the Status register of a device that is not present.”

In other words, a pull-down resistor is required on the host side, so that bit 7 always reads as clear when accessing registers on an IDE channel with no devices attached. This arrangement will allow software to see BSY=0 and make other register accesses valid. Software will then quickly notice that registers don’t retain values written to them and commands do not produce expected results.

So what happens with the other bits (DD0-DD6)? The ATA standard does not specify how the other signals should be terminated. Most likely they will then behave as usual and return as bits set.

Reality Check

Does real hardware follow the ATA specification? Quite unsurprisingly, at least some does. A test was performed on a ThinkPad T61p, which uses the ICH8M-E chipset.

The laptop’s CD-ROM was removed after power-up, so that there would be no device on the parallel ATA channel but everything was configured normally. DOS DEBUG was used to read from the corresponding ports. Sure enough, ports in the range 1F0h-1F7h and port 3F6h all returned the value 7Fh when read, suggesting that the host indeed has a pull-down resistor for the DD7 signal but not for the DD0-DD6 signals. Therefore, firmware may rely on the fact that if no device is present, the BSY bit will not be set.

What have we learned from this exercise? Perhaps “PCs are stupidly complicated, but all the complications are there for a reason”.