Kernel: Fix IDE controller waiting

We did not correctly wait until controller is ready to receive data on
write command. Also remove possible kernel panic if controller sends
unexpected interrupts
This commit is contained in:
Bananymous 2025-07-02 22:04:41 +03:00
parent 8aff315c7b
commit 51fd7a607d
2 changed files with 25 additions and 37 deletions

View File

@ -40,8 +40,6 @@ namespace Kernel
void select_device(bool is_secondary);
BAN::ErrorOr<DeviceType> identify(bool is_secondary, BAN::Span<uint16_t> buffer);
BAN::ErrorOr<void> block_until_irq();
uint8_t io_read(uint16_t);
void io_write(uint16_t, uint8_t);
void read_buffer(uint16_t, uint16_t*, size_t);
@ -54,7 +52,7 @@ namespace Kernel
const uint16_t m_ctrl;
Mutex m_mutex;
BAN::Atomic<bool> m_has_got_irq { false };
ThreadBlocker m_thread_blocker;
// Non-owning pointers
BAN::Vector<ATADevice*> m_devices;

View File

@ -126,26 +126,7 @@ namespace Kernel
{
if (io_read(ATA_PORT_STATUS) & ATA_STATUS_ERR)
dprintln("ATA Error: {}", error());
bool expected { false };
[[maybe_unused]] bool success = m_has_got_irq.compare_exchange(expected, true);
ASSERT(success);
}
BAN::ErrorOr<void> ATABus::block_until_irq()
{
const uint64_t timeout_ms = SystemTimer::get().ms_since_boot() + s_ata_timeout_ms;
bool expected { true };
while (!m_has_got_irq.compare_exchange(expected, false))
{
if (SystemTimer::get().ms_since_boot() >= timeout_ms)
return BAN::Error::from_errno(ETIMEDOUT);
Processor::pause();
expected = true;
}
return {};
m_thread_blocker.unblock();
}
uint8_t ATABus::io_read(uint16_t port)
@ -192,22 +173,30 @@ namespace Kernel
for (uint32_t i = 0; i < 4; i++)
io_read(ATA_PORT_ALT_STATUS);
uint64_t timeout = SystemTimer::get().ms_since_boot() + s_ata_timeout_ms;
const uint64_t start_ms = SystemTimer::get().ms_since_boot();
const uint64_t timeout_ms = start_ms + s_ata_timeout_ms;
uint8_t status;
while ((status = io_read(ATA_PORT_STATUS)) & ATA_STATUS_BSY)
if (SystemTimer::get().ms_since_boot() >= timeout)
return BAN::Error::from_errno(ETIMEDOUT);
while (wait_drq && !(status & ATA_STATUS_DRQ))
for (;;)
{
if (SystemTimer::get().ms_since_boot() >= timeout)
return BAN::Error::from_errno(ETIMEDOUT);
const uint8_t status = io_read(ATA_PORT_ALT_STATUS);
if (status & ATA_STATUS_BSY)
goto drive_not_ready;
if (!wait_drq || (status & ATA_STATUS_DRQ))
break;
if (status & ATA_STATUS_ERR)
return error();
if (status & ATA_STATUS_DF)
return BAN::Error::from_errno(EIO);
status = io_read(ATA_PORT_STATUS);
drive_not_ready:
const uint64_t current_ms = SystemTimer::get().ms_since_boot();
if (current_ms >= timeout_ms)
return BAN::Error::from_errno(ETIMEDOUT);
// NODE: poll for 5 milliseconds, then just block
// until timeout or irq
if (current_ms < start_ms + 5)
continue;
m_thread_blocker.block_with_timeout_ms(timeout_ms - current_ms, nullptr);
}
return {};
@ -249,7 +238,7 @@ namespace Kernel
for (uint32_t sector = 0; sector < sector_count; sector++)
{
TRY(block_until_irq());
TRY(wait(true));
read_buffer(ATA_PORT_DATA, (uint16_t*)buffer.data() + sector * device.words_per_sector(), device.words_per_sector());
}
@ -269,12 +258,12 @@ namespace Kernel
for (uint32_t sector = 0; sector < sector_count; sector++)
{
TRY(wait(true));
write_buffer(ATA_PORT_DATA, (uint16_t*)buffer.data() + sector * device.words_per_sector(), device.words_per_sector());
TRY(block_until_irq());
}
TRY(wait(false));
io_write(ATA_PORT_COMMAND, ATA_COMMAND_CACHE_FLUSH);
TRY(block_until_irq());
return {};
}
@ -310,9 +299,10 @@ namespace Kernel
io_lba2 = (cylinder >> 8) & 0xFF;
}
TRY(wait(false));
io_write(ATA_PORT_DRIVE_SELECT, io_select);
select_delay();
io_write(ATA_PORT_CONTROL, 0);
io_write(ATA_PORT_SECTOR_COUNT, sector_count);
io_write(ATA_PORT_LBA0, io_lba0);