From 6443ec77f882cadacacb1749290390774f14f657 Mon Sep 17 00:00:00 2001 From: apio Date: Fri, 29 Mar 2024 12:11:39 +0100 Subject: [PATCH] kernel/ATA: Add support for regular ATA drives (non-ATAPI) Don't know why this took so long to figure out, I just had to pass the right value to select(). --- kernel/src/arch/x86_64/disk/ATA.cpp | 123 ++++++++++++++++++++++------ kernel/src/arch/x86_64/disk/ATA.h | 9 ++ 2 files changed, 108 insertions(+), 24 deletions(-) diff --git a/kernel/src/arch/x86_64/disk/ATA.cpp b/kernel/src/arch/x86_64/disk/ATA.cpp index 014e6fcf..d82241d7 100644 --- a/kernel/src/arch/x86_64/disk/ATA.cpp +++ b/kernel/src/arch/x86_64/disk/ATA.cpp @@ -157,13 +157,27 @@ namespace ATA void Channel::select(u8 drive) { - if (drive == m_current_drive) return; - u8 value = (u8)(drive << 4) | 0xa0; + if (value == m_current_select_value) return; + write_register(Register::DriveSelect, value); delay_400ns(); + m_current_select_value = value; + m_current_drive = drive; + } + + void Channel::select(u8 base, u8 drive) + { + u8 value = (u8)(drive << 4) | base; + if (value == m_current_select_value) return; + + write_register(Register::DriveSelect, value); + + delay_400ns(); + + m_current_select_value = value; m_current_drive = drive; } @@ -543,10 +557,61 @@ namespace ATA return true; } - Result Drive::send_packet_atapi_pio(const atapi_packet* packet, void* out, u16 response_size) + void Drive::select_lba(u64 lba, usize count) + { + if (m_is_lba48) + { + m_channel->write_register(Register::SectorCount, (u8)((count >> 8) & 0xff)); + m_channel->write_register(Register::LBALow, (u8)((lba >> 24) & 0xff)); + m_channel->write_register(Register::LBAMiddle, (u8)((lba >> 32) & 0xff)); + m_channel->write_register(Register::LBAHigh, (u8)((lba >> 40) & 0xff)); + } + m_channel->write_register(Register::SectorCount, (u8)(count & 0xff)); + m_channel->write_register(Register::LBALow, (u8)(lba & 0xff)); + m_channel->write_register(Register::LBAMiddle, (u8)((lba >> 8) & 0xff)); + m_channel->write_register(Register::LBAHigh, (u8)((lba >> 16) & 0xff)); + } + + Result Drive::read_pio_bytes(void* out, usize size) { u8* ptr = (u8*)out; + usize i = 0; + + while (i < size) + { + TRY(m_channel->wait_until_ready()); + + usize byte_count; + + if (m_is_atapi) + { + byte_count = + m_channel->read_register(Register::LBAHigh) << 8 | m_channel->read_register(Register::LBAMiddle); + } + else + byte_count = min(512ul, size - i); + + usize word_count = byte_count / 2; + + while (word_count--) + { + u16 value = m_channel->read_data(); + ptr[0] = (u8)(value & 0xff); + ptr[1] = (u8)(value >> 8); + ptr += 2; + } + + i += byte_count; + + m_channel->delay_400ns(); + } + + return {}; + } + + Result Drive::send_packet_atapi_pio(const atapi_packet* packet, void* out, u16 response_size) + { m_channel->select(m_drive_index); // We use PIO here. @@ -559,30 +624,11 @@ namespace ATA m_channel->delay_400ns(); - usize i = 0; - TRY(m_channel->wait_until_ready()); for (int j = 0; j < 6; j++) m_channel->write_data(packet->command_words[j]); - while (i < response_size) - { - TRY(m_channel->wait_until_ready()); - - usize byte_count = - m_channel->read_register(Register::LBAHigh) << 8 | m_channel->read_register(Register::LBAMiddle); - usize word_count = byte_count / 2; - - while (word_count--) - { - u16 value = m_channel->read_data(); - ptr[0] = (u8)(value & 0xff); - ptr[1] = (u8)(value >> 8); - ptr += 2; - } - - i += byte_count; - } + TRY(read_pio_bytes(out, response_size)); return {}; } @@ -704,6 +750,26 @@ namespace ATA return send_packet_atapi_pio(&read_packet, out, (u16)size); } + Result Drive::ata_read_pio(u64 lba, void* out, usize size) + { + check(lba < m_block_count); + check(size <= ARCH_PAGE_SIZE); + + usize count = ceil_div(size, m_block_size); + + m_channel->select(m_is_lba48 ? 0x40 : (0xe0 | ((lba >> 24) & 0xf)), m_drive_index); + + select_lba(lba, count); + + m_channel->write_register(Register::Command, m_is_lba48 ? CMD_ReadSectorsExt : CMD_ReadSectors); + + m_channel->delay_400ns(); + + TRY(read_pio_bytes(out, size)); + + return {}; + } + Result Drive::read_lba(u64 lba, void* out, usize nblocks) { const usize blocks_per_page = ARCH_PAGE_SIZE / m_block_size; @@ -719,7 +785,16 @@ namespace ATA return atapi_read_pio(lba, out, nblocks * m_block_size); } else - todo(); + { + while (nblocks > blocks_per_page) + { + TRY(ata_read_pio(lba, out, ARCH_PAGE_SIZE)); + lba += blocks_per_page; + nblocks -= blocks_per_page; + out = offset_ptr(out, ARCH_PAGE_SIZE); + } + return ata_read_pio(lba, out, nblocks * m_block_size); + } } void Drive::irq_handler() diff --git a/kernel/src/arch/x86_64/disk/ATA.h b/kernel/src/arch/x86_64/disk/ATA.h index c344c9ea..7ebe1acd 100644 --- a/kernel/src/arch/x86_64/disk/ATA.h +++ b/kernel/src/arch/x86_64/disk/ATA.h @@ -54,6 +54,8 @@ namespace ATA enum CommandRegister : u8 { + CMD_ReadSectors = 0x20, + CMD_ReadSectorsExt = 0x24, CMD_Identify = 0xec, CMD_Packet = 0xa0, CMD_Identify_Packet = 0xa1 @@ -180,6 +182,11 @@ namespace ATA Result atapi_read_pio(u64 lba, void* out, usize size); + Result ata_read_pio(u64 lba, void* out, usize size); + + void select_lba(u64 lba, usize count); + Result read_pio_bytes(void* out, usize size); + Channel* m_channel; u8 m_drive_index; @@ -250,6 +257,7 @@ namespace ATA } void select(u8 drive); + void select(u8 base, u8 drive); bool initialize(); @@ -270,6 +278,7 @@ namespace ATA bool m_irq_called { false }; u8 m_current_drive = (u8)-1; + u8 m_current_select_value = 0xff; Option m_drives[2]; };