The device driver for the hard disk consists entirely of a hardware interrupt routine, HdInt in interrupts.s (the hard disk controller will trigger this interrupt when it has finished its work), and a couple of routines, ReadSector and WriteSector in ide.s. ReadSector is very straightforward; the appropriate registers in the IDE controller are set and then, when the controller signifies that it is ready (hence the call WaitForInt), a simple loop reads the data into the buffer. WriteSector is equally simple. Note that this code only works with a straightforward IDE controller (it'll be fine with SimNow and qemu), not SATA or SCSI controllers. (I'll try to find a reference giving details of the IDE controller.)
Similarly, HdInt is not complicated. It clears the interrupt and then checks through the list of blocked tasks to find the first one that is waiting for an interrupt from the disk controller (There must be at least one, else why is the disk controller doing any work? If nothing is waiting for it something has gone seriously wrong!) Having found a waiting task a specific task switch to that task is made.
There appear to be potential problems with this device driver. In particular, it doesn't guard against two simultaneous requests. This is not a problem in practice; requests to the driver only come from a single task, the filesystem task, in the form of queued messages. There should, therefore, be no question of simultaneous accesses to the disk as the filesystem task looks after the necessary serialization.
Note: I've just realized that the above doesn't really make sense. If the only task that calls the device driver is the filesystem task then why do I need to check which task called it? I must check carefully, but I'll leave it as it is for the time being; it certainly works as is.