Kernel: Improve ATA device initialization

This does better detection of empty ports and fixes floating bus
read-value from 0x00 to 0xFF.
This commit is contained in:
Bananymous 2024-11-19 00:25:42 +02:00
parent 627ca432cc
commit 1de50a2a94
2 changed files with 36 additions and 48 deletions

View File

@ -35,11 +35,10 @@ namespace Kernel
{}
BAN::ErrorOr<void> initialize();
void select_device(bool secondary);
BAN::ErrorOr<DeviceType> identify(bool secondary, BAN::Span<uint16_t> buffer);
void select_device(bool is_secondary);
BAN::ErrorOr<DeviceType> identify(bool is_secondary, BAN::Span<uint16_t> buffer);
void block_until_irq();
//uint8_t device_index(const ATADevice&) const;
BAN::ErrorOr<void> block_until_irq();
uint8_t io_read(uint16_t);
void io_write(uint16_t, uint8_t);

View File

@ -19,7 +19,7 @@ namespace Kernel
if (bus_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
auto bus = BAN::RefPtr<ATABus>::adopt(bus_ptr);
if (bus->io_read(ATA_PORT_STATUS) == 0x00)
if (bus->io_read(ATA_PORT_STATUS) == 0xFF)
{
dprintln("Floating ATA bus on IO port 0x{H}", base);
return BAN::Error::from_errno(ENODEV);
@ -69,94 +69,83 @@ namespace Kernel
SystemTimer::get().sleep_ns(400);
}
void ATABus::select_device(bool secondary)
void ATABus::select_device(bool is_secondary)
{
io_write(ATA_PORT_DRIVE_SELECT, 0xA0 | ((uint8_t)secondary << 4));
io_write(ATA_PORT_DRIVE_SELECT, 0xA0 | ((uint8_t)is_secondary << 4));
select_delay();
}
BAN::ErrorOr<ATABus::DeviceType> ATABus::identify(bool secondary, BAN::Span<uint16_t> buffer)
BAN::ErrorOr<ATABus::DeviceType> ATABus::identify(bool is_secondary, BAN::Span<uint16_t> 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);
select_device(is_secondary);
// Disable interrupts
io_write(ATA_PORT_CONTROL, ATA_CONTROL_nIEN);
io_write(ATA_PORT_SECTOR_COUNT, 0);
io_write(ATA_PORT_LBA0, 0);
io_write(ATA_PORT_LBA1, 0);
io_write(ATA_PORT_LBA2, 0);
io_write(ATA_PORT_COMMAND, ATA_COMMAND_IDENTIFY);
SystemTimer::get().sleep_ms(1);
// No device on port
if (io_read(ATA_PORT_STATUS) == 0)
return BAN::Error::from_errno(EINVAL);
return BAN::Error::from_errno(ENODEV);
DeviceType type = DeviceType::ATA;
TRY(wait(false));
if (wait(true).is_error())
const uint8_t lba1 = io_read(ATA_PORT_LBA1);
const uint8_t lba2 = io_read(ATA_PORT_LBA2);
auto device_type = DeviceType::ATA;
if (lba1 || lba2)
{
uint8_t lba1 = io_read(ATA_PORT_LBA1);
uint8_t lba2 = io_read(ATA_PORT_LBA2);
if (lba1 == 0x14 && lba2 == 0xEB)
type = DeviceType::ATAPI;
device_type = DeviceType::ATAPI;
else if (lba1 == 0x69 && lba2 == 0x96)
type = DeviceType::ATAPI;
device_type = DeviceType::ATAPI;
else
{
dprintln("Unsupported device type");
dprintln("Unsupported device type {2H} {2H}", lba1, lba2);
return BAN::Error::from_errno(EINVAL);
}
io_write(ATA_PORT_COMMAND, ATA_COMMAND_IDENTIFY_PACKET);
SystemTimer::get().sleep_ms(1);
}
if (auto res = wait(true); res.is_error())
{
dprintln("Fatal error: {}", res.error());
return BAN::Error::from_errno(EINVAL);
}
}
TRY(wait(true));
ASSERT(buffer.size() >= 256);
read_buffer(ATA_PORT_DATA, buffer.data(), 256);
return type;
return device_type;
}
void ATABus::handle_irq()
{
ASSERT(!m_has_got_irq);
if (io_read(ATA_PORT_STATUS) & ATA_STATUS_ERR)
dprintln("ATA Error: {}", error());
m_has_got_irq.store(true);
bool expected { false };
[[maybe_unused]] bool success = m_has_got_irq.compare_exchange(expected, true);
ASSERT(success);
}
void ATABus::block_until_irq()
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 {};
}
uint8_t ATABus::io_read(uint16_t port)