diff --git a/kernel/kernel/Storage/ATA/ATABus.cpp b/kernel/kernel/Storage/ATA/ATABus.cpp index bb0b0f485e..c004fd4fed 100644 --- a/kernel/kernel/Storage/ATA/ATABus.cpp +++ b/kernel/kernel/Storage/ATA/ATABus.cpp @@ -11,6 +11,8 @@ namespace Kernel { + static constexpr uint64_t s_ata_timeout_ms = 100; + BAN::ErrorOr> ATABus::create(uint16_t base, uint16_t ctrl, uint8_t irq) { auto* bus_ptr = new ATABus(base, ctrl); @@ -73,16 +75,37 @@ namespace Kernel select_delay(); } - static bool identify_all_ones(BAN::Span identify_data) + static bool identify_all_same(BAN::Span identify_data) { - for (size_t i = 0; i < 256; i++) - if (identify_data[i] != 0xFFFF) + uint16_t value = identify_data[0]; + for (size_t i = 1; i < 256; i++) + if (identify_data[i] != value) return false; return true; } BAN::ErrorOr ATABus::identify(bool secondary, BAN::Span buffer) { + // Try to detect whether port contains device + uint8_t status = io_read(ATA_PORT_STATUS); + if (status & ATA_STATUS_BSY) + { + uint64_t timeout = SystemTimer::get().ms_since_boot() + s_ata_timeout_ms; + while ((status = io_read(ATA_PORT_STATUS)) & ATA_STATUS_BSY) + { + if (SystemTimer::get().ms_since_boot() >= timeout) + { + dprintln("BSY flag clear timeout, assuming no drive on port"); + return BAN::Error::from_errno(ETIMEDOUT); + } + } + } + if (__builtin_popcount(status) >= 4) + { + dprintln("STATUS contains garbage, assuming no drive on port"); + return BAN::Error::from_errno(EINVAL); + } + select_device(secondary); // Disable interrupts @@ -125,7 +148,7 @@ namespace Kernel ASSERT(buffer.size() >= 256); read_buffer(ATA_PORT_DATA, buffer.data(), 256); - if (identify_all_ones(buffer)) + if (identify_all_same(buffer)) return BAN::Error::from_errno(ENODEV); return type; @@ -189,12 +212,17 @@ namespace Kernel for (uint32_t i = 0; i < 4; i++) io_read(ATA_PORT_ALT_STATUS); - uint8_t status = ATA_STATUS_BSY; - while (status & ATA_STATUS_BSY) - status = io_read(ATA_PORT_STATUS); + uint64_t timeout = SystemTimer::get().ms_since_boot() + 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)) { + if (SystemTimer::get().ms_since_boot() >= timeout) + return BAN::Error::from_errno(ETIMEDOUT); if (status & ATA_STATUS_ERR) return error(); if (status & ATA_STATUS_DF)