Page 1 of 2
ATA interrupt doesn't fire after sending READ_SECTORS command
Posted: Sat Jan 24, 2026 2:05 pm
by yucarp
I am trying to implement a 28-bit ATA PIO driver right now but I am struggling with an issue. When I send the command 0x20, it doesn't raise any interrupts whatsoever. I checked if the IRQ 14 was masked but after unmasking the IRQ 14 and 2 it still doesn't work. I check whether the RDY bit is set and I send the commands according to ATA specifications but still, there aren't any interrupts. When I unmask IRQ 15 however, an interrupt happens before nothing else happening. Since the driver is a kernel-mode executable rather than a part of kernel, outb and inb is done by syscalls. Here is the code for anyone interested:
Code: Select all
int read_sector(int drive, int lba){
int poll = sys_call(5, ATA_CONTROL_BASE, 0);
while(!(poll & 0x40)) poll = sys_call(5, ATA_CONTROL_BASE, 0);
int port_num = (drive & 1) ? ATA_SECONDARY_IO_BASE : ATA_IO_BASE;
int command = (drive > 0xE1) ? 0xF0 : 0xE0;
sys_call(6, port_num + 6, command | ((lba >> 24)));
sys_call(6, port_num + 2, 0);
sys_call(6, port_num + 3, lba & 0xFF);
sys_call(6, port_num + 4, (lba >> 8) & 0xFF);
sys_call(6, port_num + 5, (lba >> 16) & 0xFF);
sys_call(6, port_num + 7, 0x20);
}
QEMU log:
Code: Select all
ide_status_read IDE PIO rd @ 0x3f6 (Alt Status); val 0x50; bus 0x555b63fa9ef0; IDEState 0x555b63fa9f78
ide_status_read IDE PIO rd @ 0x376 (Alt Status); val 0x00; bus 0x555b63faa7f0; IDEState 0x555b63faac50
ide_ioport_write IDE PIO wr @ 0x1f6 (Device/Head); val 0xa0; bus 0x555b63fa9ef0 IDEState 0x555b63fa9f78
ide_ioport_write IDE PIO wr @ 0x1f2 (Sector Count); val 0x00; bus 0x555b63fa9ef0 IDEState 0x555b63fa9f78
ide_ioport_write IDE PIO wr @ 0x1f3 (Sector Number); val 0x00; bus 0x555b63fa9ef0 IDEState 0x555b63fa9f78
ide_ioport_write IDE PIO wr @ 0x1f4 (Cylinder Low); val 0x00; bus 0x555b63fa9ef0 IDEState 0x555b63fa9f78
ide_ioport_write IDE PIO wr @ 0x1f5 (Cylinder High); val 0x00; bus 0x555b63fa9ef0 IDEState 0x555b63fa9f78
ide_ioport_write IDE PIO wr @ 0x1f7 (Command); val 0xec; bus 0x555b63fa9ef0 IDEState 0x555b63fa9f78
ide_bus_exec_cmd IDE exec cmd: bus 0x555b63fa9ef0; state 0x555b63fa9f78; cmd 0xec
ide_status_read IDE PIO rd @ 0x3f6 (Alt Status); val 0x58; bus 0x555b63fa9ef0; IDEState 0x555b63fa9f78
ide_status_read IDE PIO rd @ 0x3f6 (Alt Status); val 0x58; bus 0x555b63fa9ef0; IDEState 0x555b63fa9f78
ide_status_read IDE PIO rd @ 0x3f6 (Alt Status); val 0x58; bus 0x555b63fa9ef0; IDEState 0x555b63fa9f78
ide_ioport_write IDE PIO wr @ 0x1f6 (Device/Head); val 0xe0; bus 0x555b63fa9ef0 IDEState 0x555b63fa9f78
ide_ioport_write IDE PIO wr @ 0x1f2 (Sector Count); val 0x00; bus 0x555b63fa9ef0 IDEState 0x555b63fa9f78
ide_ioport_write IDE PIO wr @ 0x1f3 (Sector Number); val 0x00; bus 0x555b63fa9ef0 IDEState 0x555b63fa9f78
ide_ioport_write IDE PIO wr @ 0x1f4 (Cylinder Low); val 0x00; bus 0x555b63fa9ef0 IDEState 0x555b63fa9f78
ide_ioport_write IDE PIO wr @ 0x1f5 (Cylinder High); val 0x00; bus 0x555b63fa9ef0 IDEState 0x555b63fa9f78
ide_ioport_write IDE PIO wr @ 0x1f7 (Command); val 0x20; bus 0x555b63fa9ef0 IDEState 0x555b63fa9f78
ide_bus_exec_cmd IDE exec cmd: bus 0x555b63fa9ef0; state 0x555b63fa9f78; cmd 0x20
Thanks for any help
Re: ATA interrupt doesn't fire after sending READ_SECTORS command
Posted: Sat Jan 24, 2026 4:18 pm
by Octocontrabass
yucarp wrote: ↑Sat Jan 24, 2026 2:05 pmWhen I send the command 0x20, it doesn't raise any interrupts whatsoever.
I see you send command 0xEC before you send command 0x20. Did it raise any interrupts for command 0xEC?
yucarp wrote: ↑Sat Jan 24, 2026 2:05 pmSince the driver is a kernel-mode executable rather than a part of kernel, outb and inb is done by syscalls.
Why? A kernel-mode executable doesn't need syscalls to use outb and inb.
Re: ATA interrupt doesn't fire after sending READ_SECTORS command
Posted: Sat Jan 24, 2026 4:40 pm
by yucarp
Why? A kernel-mode executable doesn't need syscalls to use outb and inb.
Technically yes, but I haven't implemented relocatable executables properly yet so I can't leave a stub for inb() so this is the way I call functions from the main kernel executable.
I see you send command 0xEC before you send command 0x20. Did it raise any interrupts for command 0xEC?
No, it doesn't but the IDENTIFY command seems to work. Here is the code snippet for the IDENTIFY command:
Code: Select all
int send_identify(int port_num, int drive){
sys_call(6, port_num + 6, drive);
sys_call(6, port_num + 2, 0);
sys_call(6, port_num + 3, 0);
sys_call(6, port_num + 4, 0);
sys_call(6, port_num + 5, 0);
sys_call(6, port_num + 7, 0xEC);
return sys_call(5, port_num, 0);
}
Re: ATA interrupt doesn't fire after sending READ_SECTORS command
Posted: Sat Jan 24, 2026 5:00 pm
by Octocontrabass
yucarp wrote: ↑Sat Jan 24, 2026 4:40 pmTechnically yes, but I haven't implemented relocatable executables properly yet so I can't leave a stub for inb() so this is the way I call functions from the main kernel executable.
Or you could make them inline functions so there's no function call at all.
No, it doesn't but the IDENTIFY command seems to work.
It's not working if it doesn't raise an interrupt. It should raise an interrupt when there's data available for you to read, exactly the same way command 0x20 should raise an interrupt when there's data available for you to read.
yucarp wrote: ↑Sat Jan 24, 2026 4:40 pmCode: Select all
sys_call(6, port_num + 7, 0xEC);
return sys_call(5, port_num, 0);
You need to wait for the drive to be ready before you try to read the 512-byte IDENTIFY DEVICE data. You need to use inw() to read that data from the drive.
Re: ATA interrupt doesn't fire after sending READ_SECTORS command
Posted: Sat Jan 24, 2026 5:22 pm
by yucarp
There is a weird thing, after sending the IDENTIFY command, the interrupt comes from the 15th IRQ instead of the 14th IRQ. This really confuses me, I kept blocking the bit 15 as I didn't have any secondary bus in my emulator, but the interrupt is coming from the second bus...
Re: ATA interrupt doesn't fire after sending READ_SECTORS command
Posted: Sat Jan 24, 2026 7:05 pm
by Octocontrabass
yucarp wrote: ↑Sat Jan 24, 2026 5:22 pmThere is a weird thing, after sending the IDENTIFY command, the interrupt comes from the 15th IRQ instead of the 14th IRQ.
That sounds like a spurious interrupt. Spurious interrupts can happen if you don't wait for the interrupt to arrive before you do something that acknowledges the interrupt.
Re: ATA interrupt doesn't fire after sending READ_SECTORS command
Posted: Sun Jan 25, 2026 6:51 am
by yucarp
I tried everything but that interrupt keeps coming. I checked if the BSY bit is clear before sending the command but the interrupt happens directly after issuing the command. As it arrives as soon as I issue the command, there is nothing I can do to wait the right interrupt. Here is my new code for IDENTIFY
Code: Select all
int send_identify(int port_num, int drive){
outb(port_num + 6, drive);
outb(port_num + 2, 0);
outb(port_num + 3, 0);
outb(port_num + 4, 0);
outb(port_num + 5, 0);
uint8_t poll = inb(port_num + 7);
while(poll & 0x80) poll = inb(port_num + 7);
outb(port_num + 7, 0xEC);
poll = inb(port_num + 7);
if(!poll) return 0;
while(poll & 0x80) poll = inb(port_num + 7);
while(!(poll & 0x8)) poll = inb(port_num + 7);;
for (int i = 0; i < 256; ++i){
buffer[i] = inw(ATA_IO_BASE);
}
return 1;
}
Re: ATA interrupt doesn't fire after sending READ_SECTORS command
Posted: Sun Jan 25, 2026 1:16 pm
by Octocontrabass
yucarp wrote: ↑Sun Jan 25, 2026 6:51 amCode: Select all
outb(port_num + 7, 0xEC);
poll = inb(port_num + 7);
Reading the status register acknowledges the interrupt. Reading the status register immediately after sending a command can acknowledge the interrupt so quickly that it causes a spurious interrupt.
What happens if you wait for an IRQ here instead of polling the status register? Do you still receive a spurious interrupt?
Re: ATA interrupt doesn't fire after sending READ_SECTORS command
Posted: Sun Jan 25, 2026 1:35 pm
by yucarp
Yes, I still receive a spurious interrupt when I wait for an interrupt instead of reading the status register.
Re: ATA interrupt doesn't fire after sending READ_SECTORS command
Posted: Sun Jan 25, 2026 1:44 pm
by Octocontrabass
Have you checked the PIC's ISR to see if it's really a spurious interrupt? I can't think of any reason why a spurious interrupt would arrive if you aren't accessing any of the drive's registers after sending the command.
Re: ATA interrupt doesn't fire after sending READ_SECTORS command
Posted: Sun Jan 25, 2026 2:11 pm
by yucarp
It's doesn't come from IRQ 7, it comes from IRQ 15, which is the second ATA bus. The problem is I don't have any secondary ATA busses. I tried it on Bochs too and the same issue persists.
Re: ATA interrupt doesn't fire after sending READ_SECTORS command
Posted: Sun Jan 25, 2026 6:15 pm
by yucarp
I can't believe that a single mistake cost me 1 day of debugging... When I initialized the IRQ, I wrote irq13, irq15 instead of irq13, irq14, irq15. Now everything is correct. However the same issue persists. After reading the first 512 bytes from the device no more interrupts happen. The QEMU log is like this:
Code: Select all
ide_data_readw IDE PIO rd @ 0x1f0 (Data: Word); val 0x0000; bus 0x5619f191bf20; IDEState 0x5619f191bfa8
ide_data_readw IDE PIO rd @ 0x1f0 (Data: Word); val 0x0000; bus 0x5619f191bf20; IDEState 0x5619f191bfa8
ide_ioport_read IDE PIO rd @ 0x1f7 (Status); val 0x50; bus 0x5619f191bf20 IDEState 0x5619f191bfa8
ide_ioport_read IDE PIO rd @ 0x1f7 (Status); val 0x50; bus 0x5619f191bf20 IDEState 0x5619f191bfa8
ide_ioport_write IDE PIO wr @ 0x1f6 (Device/Head); val 0xe0; bus 0x5619f191bf20 IDEState 0x5619f191bfa8
ide_ioport_write IDE PIO wr @ 0x1f2 (Sector Count); val 0x00; bus 0x5619f191bf20 IDEState 0x5619f191bfa8
ide_ioport_write IDE PIO wr @ 0x1f3 (Sector Number); val 0x00; bus 0x5619f191bf20 IDEState 0x5619f191bfa8
ide_ioport_write IDE PIO wr @ 0x1f4 (Cylinder Low); val 0x00; bus 0x5619f191bf20 IDEState 0x5619f191bfa8
ide_ioport_write IDE PIO wr @ 0x1f5 (Cylinder High); val 0x00; bus 0x5619f191bf20 IDEState 0x5619f191bfa8
ide_ioport_write IDE PIO wr @ 0x1f7 (Command); val 0x20; bus 0x5619f191bf20 IDEState 0x5619f191bfa8
ide_bus_exec_cmd IDE exec cmd: bus 0x5619f191bf20; state 0x5619f191bfa8; cmd 0x20
ide_sector_read sector=0 nsectors=1
Code: Select all
int read_sector(int drive, int lba){
int port_num = (drive & 1) ? ATA_SECONDARY_IO_BASE : ATA_IO_BASE;
int command = (drive > 0xE1) ? 0xF0 : 0xE0;
sys_call(6, port_num + 6, command | ((lba >> 24)));
sys_call(6, port_num + 2, 0);
sys_call(6, port_num + 3, lba & 0xFF);
sys_call(6, port_num + 4, (lba >> 8) & 0xFF);
sys_call(6, port_num + 5, (lba >> 16) & 0xFF);
sys_call(6, port_num + 7, 0x20);
}
void ata_handler(struct x86Registers *regs){
for (int i = 0; i < 256; ++i){
buffer[i] = inw(ATA_IO_BASE);
}
inb(ATA_IO_BASE + 7);
}
Re: ATA interrupt doesn't fire after sending READ_SECTORS command
Posted: Mon Jan 26, 2026 11:18 am
by Octocontrabass
yucarp wrote: ↑Sun Jan 25, 2026 2:11 pmIt's doesn't come from IRQ 7, it comes from IRQ 15,
Spurious IRQ7 is more common than spurious IRQ15, but both may be spurious.
yucarp wrote: ↑Sun Jan 25, 2026 6:15 pmAfter reading the first 512 bytes from the device no more interrupts happen.
I see you acknowledge the drive's interrupt, but do you also send EOI to the interrupt controllers?
yucarp wrote: ↑Sun Jan 25, 2026 6:15 pmCode: Select all
for (int i = 0; i < 256; ++i){
buffer[i] = inw(ATA_IO_BASE);
}
inb(ATA_IO_BASE + 7);
You should read the status register first so you can check for errors.
Re: ATA interrupt doesn't fire after sending READ_SECTORS command
Posted: Mon Jan 26, 2026 1:06 pm
by yucarp
Spurious IRQ7 is more common than spurious IRQ15, but both may be spurious.
Thank you for that information, but IRQ15 was a mistake in my interrupt code rather than a spurious interrupt as far as I checked.
I see you acknowledge the drive's interrupt, but do you also send EOI to the interrupt controllers?
Oh god, I completely forgot about that... When I was setting interrupt code again I thought that all handlers automatically send EOI...

Now it works fine.
You should read the status register first so you can check for errors.
Thank you for your advice. I will consider that
Re: ATA interrupt doesn't fire after sending READ_SECTORS command
Posted: Mon Jan 26, 2026 1:28 pm
by yucarp
I have another question again... Does READ_SECTORS always read 256 sectors? The specification says that the byte that's send to 0x1F2 is the reverse of the sector count that will be read but when I set the sector count to 0xFF it still reads 256 sectors. The difference is it reads once by once instead of directly reading all sectors