diff --git a/Kernel/Storage/IDEChannel.cpp b/Kernel/Storage/IDEChannel.cpp index ed18925f47..9c7cb58fcd 100644 --- a/Kernel/Storage/IDEChannel.cpp +++ b/Kernel/Storage/IDEChannel.cpp @@ -21,9 +21,6 @@ namespace Kernel { #define PATA_PRIMARY_IRQ 14 #define PATA_SECONDARY_IRQ 15 -#define PCI_Mass_Storage_Class 0x1 -#define PCI_IDE_Controller_Subclass 0x1 - UNMAP_AFTER_INIT NonnullRefPtr IDEChannel::create(const IDEController& controller, IOAddressGroup io_group, ChannelType type) { return adopt_ref(*new IDEChannel(controller, io_group, type)); @@ -55,6 +52,25 @@ UNMAP_AFTER_INIT void IDEChannel::initialize() dbgln_if(PATA_DEBUG, "IDEChannel: {} bus master base disabled", channel_type_string()); m_parent_controller->enable_pin_based_interrupts(); + // reset the channel + u8 device_control = m_io_group.control_base().in(); + // Wait 30 milliseconds + IO::delay(30000); + m_io_group.control_base().out(device_control | (1 << 2)); + // Wait 30 milliseconds + IO::delay(30000); + m_io_group.control_base().out(device_control); + // Wait up to 30 seconds before failing + if (!wait_until_not_busy(false, 30000)) { + dbgln("IDEChannel: reset failed, busy flag on master stuck"); + return; + } + // Wait up to 30 seconds before failing + if (!wait_until_not_busy(true, 30000)) { + dbgln("IDEChannel: reset failed, busy flag on slave stuck"); + return; + } + detect_disks(); // Note: calling to detect_disks could generate an interrupt, clear it if that's the case @@ -243,10 +259,27 @@ static void io_delay() IO::in8(0x3f6); } -void IDEChannel::wait_until_not_busy() +bool IDEChannel::wait_until_not_busy(bool slave, size_t milliseconds_timeout) { - while (m_io_group.control_base().in() & ATA_SR_BSY) - ; + IO::delay(20); + m_io_group.io_base().offset(ATA_REG_HDDEVSEL).out(0xA0 | (slave << 4)); // First, we need to select the drive itself + IO::delay(20); + size_t time_elapsed = 0; + while (m_io_group.control_base().in() & ATA_SR_BSY && time_elapsed <= milliseconds_timeout) { + IO::delay(1000); + time_elapsed++; + } + return time_elapsed != milliseconds_timeout; +} + +bool IDEChannel::wait_until_not_busy(size_t milliseconds_timeout) +{ + size_t time_elapsed = 0; + while (m_io_group.control_base().in() & ATA_SR_BSY && time_elapsed <= milliseconds_timeout) { + IO::delay(1000); + time_elapsed++; + } + return time_elapsed != milliseconds_timeout; } String IDEChannel::channel_type_string() const @@ -269,28 +302,37 @@ UNMAP_AFTER_INIT void IDEChannel::detect_disks() // There are only two possible disks connected to a channel for (auto i = 0; i < 2; i++) { // We need to select the drive and then we wait 20 microseconds... and it doesn't hurt anything so let's just do it. + IO::delay(20); m_io_group.io_base().offset(ATA_REG_HDDEVSEL).out(0xA0 | (i << 4)); // First, we need to select the drive itself IO::delay(20); + auto status = m_io_group.control_base().in(); + if (status == 0x0) { + dbgln_if(PATA_DEBUG, "IDEChannel: No {} {} disk detected!", channel_type_string().to_lowercase(), channel_string(i)); + continue; + } + m_io_group.io_base().offset(ATA_REG_SECCOUNT0).out(0); m_io_group.io_base().offset(ATA_REG_LBA0).out(0); m_io_group.io_base().offset(ATA_REG_LBA1).out(0); m_io_group.io_base().offset(ATA_REG_LBA2).out(0); m_io_group.io_base().offset(ATA_REG_COMMAND).out(ATA_CMD_IDENTIFY); // Send the ATA_IDENTIFY command - // Wait for the BSY flag to be reset - while (m_io_group.control_base().in() & ATA_SR_BSY) - ; - - if (m_io_group.control_base().in() == 0x00) { - dbgln_if(PATA_DEBUG, "IDEChannel: No {} {} disk detected!", channel_type_string().to_lowercase(), channel_string(i)); + // Wait 10 second for the BSY flag to clear + if (!wait_until_not_busy(2000)) { + dbgln_if(PATA_DEBUG, "IDEChannel: No {} {} disk detected, BSY flag was not reset!", channel_type_string().to_lowercase(), channel_string(i)); continue; } bool check_for_atapi = false; + bool device_presence = true; PATADiskDevice::InterfaceType interface_type = PATADiskDevice::InterfaceType::ATA; + size_t milliseconds_elapsed = 0; for (;;) { + // Wait about 10 seconds + if (milliseconds_elapsed > 2000) + break; u8 status = m_io_group.control_base().in(); if (status & ATA_SR_ERR) { dbgln_if(PATA_DEBUG, "IDEChannel: {} {} device is not ATA. Will check for ATAPI.", channel_type_string(), channel_string(i)); @@ -303,6 +345,22 @@ UNMAP_AFTER_INIT void IDEChannel::detect_disks() interface_type = PATADiskDevice::InterfaceType::ATA; break; } + + if (status == 0 || status == 0xFF) { + dbgln_if(PATA_DEBUG, "IDEChannel: {} {} device presence - none.", channel_type_string(), channel_string(i)); + device_presence = false; + break; + } + + IO::delay(1000); + milliseconds_elapsed++; + } + if (!device_presence) { + continue; + } + if (milliseconds_elapsed > 10000) { + dbgln_if(PATA_DEBUG, "IDEChannel: {} {} device state unknown. Timeout exceeded.", channel_type_string(), channel_string(i)); + continue; } if (check_for_atapi) { @@ -373,7 +431,8 @@ void IDEChannel::ata_access(Direction direction, bool slave_request, u64 lba, u8 head = (lba & 0xF000000) >> 24; } - wait_until_not_busy(); + // Wait 1 second + wait_until_not_busy(1000); // We need to select the drive and then we wait 20 microseconds... and it doesn't hurt anything so let's just do it. m_io_group.io_base().offset(ATA_REG_HDDEVSEL).out(0xE0 | (static_cast(slave_request) << 4) | head); diff --git a/Kernel/Storage/IDEChannel.h b/Kernel/Storage/IDEChannel.h index d7e6d03a46..610ae47ef0 100644 --- a/Kernel/Storage/IDEChannel.h +++ b/Kernel/Storage/IDEChannel.h @@ -59,6 +59,13 @@ public: { } + IOAddressGroup(const IOAddressGroup& other, IOAddress bus_master_base) + : m_io_base(other.io_base()) + , m_control_base(other.control_base()) + , m_bus_master_base(bus_master_base) + { + } + // Disable default implementations that would use surprising integer promotion. bool operator==(const IOAddressGroup&) const = delete; bool operator<=(const IOAddressGroup&) const = delete; @@ -126,7 +133,8 @@ protected: String channel_type_string() const; void try_disambiguate_error(); - void wait_until_not_busy(); + bool wait_until_not_busy(bool slave, size_t milliseconds_timeout); + bool wait_until_not_busy(size_t milliseconds_timeout); void start_request(AsyncBlockDeviceRequest&, bool, u16);