Skip to content
Permalink
Browse files

Kernel: Fix non-DMA writes to IDE drives

Our logic for using the ATA_CMD_CACHE_FLUSH functionality was a bit wrong,
and now it's better.

The ATA spec says these two things:

> The device shall enter the interrupt pending state when:
> 1) any command except a PIO data-in command reaches command completion
>    successfully;
> ...
> The device shall exit the interrupt pending state when:
> 1) the device is selected, BSY is cleared to zero, and the Status
>		 register is read;

This means that our sequence of actions was probably never going to work.
We were waiting in a loop checking the status register until it left the
busy state, _then_ waiting for an interrupt. Unfortunately by checking the
status register, we were _clearing_ the interrupt we were about to wait
for.

Now we just wait for the interrupt - we don't poll the status register at
all. This also means that once we get our `wait_for_irq` method sorted out
we'll spend a bunch less CPU time waiting for things to complete.
  • Loading branch information...
deoxxa authored and awesomekling committed Aug 12, 2019
1 parent 53ddbce commit 072bf8cbb9f573288b1f001c4e95357018b50a51
Showing with 22 additions and 10 deletions.
  1. +22 −10 Kernel/Devices/PATAChannel.cpp
@@ -494,27 +494,39 @@ bool PATAChannel::ata_write_sectors(u32 start_sector, u16 count, const u8* inbuf
IO::out8(m_io_base + ATA_REG_HDDEVSEL, devsel | ((start_sector >> 24) & 0xf));

IO::out8(0x3F6, 0x08);
while (!(IO::in8(m_io_base + ATA_REG_STATUS) & ATA_SR_DRDY))
;

IO::out8(m_io_base + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO);

while (!(IO::in8(m_io_base + ATA_REG_STATUS) & ATA_SR_DRQ))
;
for (int i = 0; i < count; i++) {
wait_400ns(m_io_base);
while (IO::in8(m_io_base + ATA_REG_STATUS) & ATA_SR_BSY)
;

u8 status = IO::in8(m_io_base + ATA_REG_STATUS);
ASSERT(status & ATA_SR_DRQ);
IO::repeated_out16(m_io_base + ATA_REG_DATA, inbuf, count * 256);
u8 status = IO::in8(m_io_base + ATA_REG_STATUS);
ASSERT(status & ATA_SR_DRQ);

m_interrupted = false;
enable_irq();
wait_for_irq();
#ifdef PATA_DEBUG
kprintf("PATAChannel: Writing 512 bytes (part %d) (status=%b), inbuf=%p...\n", i, status, inbuf + (512 * i));
#endif

disable_irq();
IO::repeated_out16(m_io_base + ATA_REG_DATA, inbuf + (512 * i), 256);
m_interrupted = false;
enable_irq();
wait_for_irq();
status = IO::in8(m_io_base + ATA_REG_STATUS);
ASSERT(!(status & ATA_SR_BSY));
}

disable_irq();
IO::out8(m_io_base + ATA_REG_COMMAND, ATA_CMD_CACHE_FLUSH);
while (IO::in8(m_io_base + ATA_REG_STATUS) & ATA_SR_BSY)
;
m_interrupted = false;
enable_irq();
wait_for_irq();
u8 status = IO::in8(m_io_base + ATA_REG_STATUS);
ASSERT(!(status & ATA_SR_BSY));

return !m_device_error;
}

0 comments on commit 072bf8c

Please sign in to comment.
You can’t perform that action at this time.