From c07fd265f0eef381051b8fe715d2666c56c95b36 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Tue, 19 Nov 2024 00:28:43 +0200 Subject: [PATCH] Kernel: Add support for ATA CHS addressing and cleanup code I thought that I had an PC without LBA support so I implement support for CHS. Turns out that my ATA device detection was broken and was no device on that port and initialize data was just garbage. Now that I added CHS, I guess I should just keep it in :) Both ATA read and write are now combined into a single function to avoid code duplication. --- kernel/include/kernel/Errors.h | 1 - kernel/include/kernel/Storage/ATA/ATABus.h | 2 + kernel/include/kernel/Storage/ATA/ATADevice.h | 6 +- kernel/kernel/Errors.cpp | 1 - kernel/kernel/Storage/ATA/ATABus.cpp | 96 ++++++++++--------- kernel/kernel/Storage/ATA/ATADevice.cpp | 25 +++-- 6 files changed, 75 insertions(+), 56 deletions(-) diff --git a/kernel/include/kernel/Errors.h b/kernel/include/kernel/Errors.h index 6429ec4620..dc1cd939ed 100644 --- a/kernel/include/kernel/Errors.h +++ b/kernel/include/kernel/Errors.h @@ -14,7 +14,6 @@ namespace Kernel Ext2_NoInodes, Storage_Boundaries, Storage_GPTHeader, - ATA_NoLBA, ATA_AMNF, ATA_TKZNF, ATA_ABRT, diff --git a/kernel/include/kernel/Storage/ATA/ATABus.h b/kernel/include/kernel/Storage/ATA/ATABus.h index 98aaa2b900..ec04c84da0 100644 --- a/kernel/include/kernel/Storage/ATA/ATABus.h +++ b/kernel/include/kernel/Storage/ATA/ATABus.h @@ -35,6 +35,8 @@ namespace Kernel {} BAN::ErrorOr initialize(); + BAN::ErrorOr send_command(ATADevice&, uint64_t lba, uint64_t sector_count, bool write); + void select_device(bool is_secondary); BAN::ErrorOr identify(bool is_secondary, BAN::Span buffer); diff --git a/kernel/include/kernel/Storage/ATA/ATADevice.h b/kernel/include/kernel/Storage/ATA/ATADevice.h index 02c477db55..63d79b576e 100644 --- a/kernel/include/kernel/Storage/ATA/ATADevice.h +++ b/kernel/include/kernel/Storage/ATA/ATADevice.h @@ -26,11 +26,12 @@ namespace Kernel uint32_t words_per_sector() const { return m_sector_words; } uint64_t sector_count() const { return m_lba_count; } + bool has_lba() const { return m_has_lba; } BAN::StringView model() const { return m_model; } - BAN::StringView name() const { return m_name; } + BAN::StringView name() const override { return m_name; } - virtual dev_t rdev() const override { return m_rdev; } + dev_t rdev() const override { return m_rdev; } protected: ATABaseDevice(); @@ -42,6 +43,7 @@ namespace Kernel uint32_t m_command_set; uint32_t m_sector_words; uint64_t m_lba_count; + bool m_has_lba; char m_model[41]; char m_name[4] {}; diff --git a/kernel/kernel/Errors.cpp b/kernel/kernel/Errors.cpp index a4700ffcb3..5175fbbf62 100644 --- a/kernel/kernel/Errors.cpp +++ b/kernel/kernel/Errors.cpp @@ -17,7 +17,6 @@ namespace Kernel "Ext2 filesystem out of inodes", "Attempted to access outside of device boundaries", "Device has invalid GPT header", - "Device does not support LBA addressing", "Address mark not found", "Track zero not found", "Aborted command", diff --git a/kernel/kernel/Storage/ATA/ATABus.cpp b/kernel/kernel/Storage/ATA/ATABus.cpp index 346dc288b6..e69982ab7a 100644 --- a/kernel/kernel/Storage/ATA/ATABus.cpp +++ b/kernel/kernel/Storage/ATA/ATABus.cpp @@ -245,29 +245,12 @@ namespace Kernel LockGuard _(m_mutex); - if (lba < (1 << 28)) - { - // LBA28 - io_write(ATA_PORT_DRIVE_SELECT, 0xE0 | ((uint8_t)device.is_secondary() << 4) | ((lba >> 24) & 0x0F)); - select_delay(); - io_write(ATA_PORT_CONTROL, 0); + TRY(send_command(device, lba, sector_count, false)); - io_write(ATA_PORT_SECTOR_COUNT, sector_count); - io_write(ATA_PORT_LBA0, (uint8_t)(lba >> 0)); - io_write(ATA_PORT_LBA1, (uint8_t)(lba >> 8)); - io_write(ATA_PORT_LBA2, (uint8_t)(lba >> 16)); - io_write(ATA_PORT_COMMAND, ATA_COMMAND_READ_SECTORS); - - for (uint32_t sector = 0; sector < sector_count; sector++) - { - block_until_irq(); - read_buffer(ATA_PORT_DATA, (uint16_t*)buffer.data() + sector * device.words_per_sector(), device.words_per_sector()); - } - } - else + for (uint32_t sector = 0; sector < sector_count; sector++) { - // LBA48 - ASSERT(false); + TRY(block_until_irq()); + read_buffer(ATA_PORT_DATA, (uint16_t*)buffer.data() + sector * device.words_per_sector(), device.words_per_sector()); } return {}; @@ -282,33 +265,60 @@ namespace Kernel LockGuard _(m_mutex); - if (lba < (1 << 28)) - { - // LBA28 - io_write(ATA_PORT_DRIVE_SELECT, 0xE0 | ((uint8_t)device.is_secondary() << 4) | ((lba >> 24) & 0x0F)); - select_delay(); - io_write(ATA_PORT_CONTROL, 0); + TRY(send_command(device, lba, sector_count, true)); - io_write(ATA_PORT_SECTOR_COUNT, sector_count); - io_write(ATA_PORT_LBA0, (uint8_t)(lba >> 0)); - io_write(ATA_PORT_LBA1, (uint8_t)(lba >> 8)); - io_write(ATA_PORT_LBA2, (uint8_t)(lba >> 16)); - io_write(ATA_PORT_COMMAND, ATA_COMMAND_WRITE_SECTORS); - - for (uint32_t sector = 0; sector < sector_count; sector++) - { - write_buffer(ATA_PORT_DATA, (uint16_t*)buffer.data() + sector * device.words_per_sector(), device.words_per_sector()); - block_until_irq(); - } - } - else + for (uint32_t sector = 0; sector < sector_count; sector++) { - // LBA48 - ASSERT(false); + write_buffer(ATA_PORT_DATA, (uint16_t*)buffer.data() + sector * device.words_per_sector(), device.words_per_sector()); + TRY(block_until_irq()); } io_write(ATA_PORT_COMMAND, ATA_COMMAND_CACHE_FLUSH); - block_until_irq(); + TRY(block_until_irq()); + + return {}; + } + + BAN::ErrorOr ATABus::send_command(ATADevice& device, uint64_t lba, uint64_t sector_count, bool write) + { + uint8_t io_select = 0; + uint8_t io_lba0 = 0; + uint8_t io_lba1 = 0; + uint8_t io_lba2 = 0; + + if (lba >= (1 << 28)) + { + dwarnln("LBA48 addressing not supported"); + return BAN::Error::from_errno(ENOTSUP); + } + else if (device.has_lba()) + { + io_select = 0xE0 | ((uint8_t)device.is_secondary() << 4) | ((lba >> 24) & 0x0F); + io_lba0 = (lba >> 0) & 0xFF; + io_lba1 = (lba >> 8) & 0xFF; + io_lba2 = (lba >> 16) & 0xFF; + } + else + { + const uint8_t sector = (lba % 63) + 1; + const uint8_t head = (lba + 1 - sector) % (16 * 63) / 63; + const uint16_t cylinder = (lba + 1 - sector) / (16 * 63); + + io_select = 0xA0 | ((uint8_t)device.is_secondary() << 4) | head; + io_lba0 = sector; + io_lba1 = (cylinder >> 0) & 0xFF; + io_lba2 = (cylinder >> 8) & 0xFF; + } + + 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); + io_write(ATA_PORT_LBA1, io_lba1); + io_write(ATA_PORT_LBA2, io_lba2); + io_write(ATA_PORT_COMMAND, write ? ATA_COMMAND_WRITE_SECTORS : ATA_COMMAND_READ_SECTORS); return {}; } diff --git a/kernel/kernel/Storage/ATA/ATADevice.cpp b/kernel/kernel/Storage/ATA/ATADevice.cpp index 5ca3f65205..29e9c349be 100644 --- a/kernel/kernel/Storage/ATA/ATADevice.cpp +++ b/kernel/kernel/Storage/ATA/ATADevice.cpp @@ -27,21 +27,20 @@ namespace Kernel { ASSERT(identify_data.size() >= 256); - m_signature = identify_data[ATA_IDENTIFY_SIGNATURE]; + m_signature = identify_data[ATA_IDENTIFY_SIGNATURE]; m_capabilities = identify_data[ATA_IDENTIFY_CAPABILITIES]; - m_command_set = 0; - m_command_set |= (uint32_t)(identify_data[ATA_IDENTIFY_COMMAND_SET + 0] << 0); - m_command_set |= (uint32_t)(identify_data[ATA_IDENTIFY_COMMAND_SET + 1] << 16); + m_command_set = static_cast(identify_data[ATA_IDENTIFY_COMMAND_SET + 0]) << 0; + m_command_set |= static_cast(identify_data[ATA_IDENTIFY_COMMAND_SET + 1]) << 16; - if (!(m_capabilities & ATA_CAPABILITIES_LBA)) - return BAN::Error::from_error_code(ErrorCode::ATA_NoLBA); + m_has_lba = !!(m_capabilities & ATA_CAPABILITIES_LBA); if ((identify_data[ATA_IDENTIFY_SECTOR_INFO] & (1 << 15)) == 0 && (identify_data[ATA_IDENTIFY_SECTOR_INFO] & (1 << 14)) != 0 && (identify_data[ATA_IDENTIFY_SECTOR_INFO] & (1 << 12)) != 0) { - m_sector_words = *(uint32_t*)(identify_data.data() + ATA_IDENTIFY_SECTOR_WORDS); + m_sector_words = static_cast(identify_data[ATA_IDENTIFY_SECTOR_WORDS + 0]) << 0; + m_sector_words |= static_cast(identify_data[ATA_IDENTIFY_SECTOR_WORDS + 1]) << 16; } else { @@ -50,9 +49,17 @@ namespace Kernel m_lba_count = 0; if (m_command_set & ATA_COMMANDSET_LBA48_SUPPORTED) - m_lba_count = *(uint64_t*)(identify_data.data() + ATA_IDENTIFY_LBA_COUNT_EXT); + { + m_lba_count = static_cast(identify_data[ATA_IDENTIFY_LBA_COUNT_EXT + 0]) << 0; + m_lba_count |= static_cast(identify_data[ATA_IDENTIFY_LBA_COUNT_EXT + 1]) << 16; + m_lba_count |= static_cast(identify_data[ATA_IDENTIFY_LBA_COUNT_EXT + 2]) << 32; + m_lba_count |= static_cast(identify_data[ATA_IDENTIFY_LBA_COUNT_EXT + 3]) << 48; + } if (m_lba_count < (1 << 28)) - m_lba_count = *(uint32_t*)(identify_data.data() + ATA_IDENTIFY_LBA_COUNT); + { + m_lba_count = static_cast(identify_data[ATA_IDENTIFY_LBA_COUNT + 0]) << 0; + m_lba_count |= static_cast(identify_data[ATA_IDENTIFY_LBA_COUNT + 1]) << 16; + } for (int i = 0; i < 20; i++) {