From 3733b0f26e2cc98e51dcfab1c2dc9a37b34911cb Mon Sep 17 00:00:00 2001 From: apio Date: Sat, 13 May 2023 22:18:44 +0200 Subject: [PATCH] kernel/ATA: Send a READ CAPACITY packet to an ATA drive on initialization --- kernel/src/arch/x86_64/disk/ATA.cpp | 98 ++++++++++++++++++++++++++++- kernel/src/arch/x86_64/disk/ATA.h | 31 ++++++++- 2 files changed, 125 insertions(+), 4 deletions(-) diff --git a/kernel/src/arch/x86_64/disk/ATA.cpp b/kernel/src/arch/x86_64/disk/ATA.cpp index 6835ed98..ed06c625 100644 --- a/kernel/src/arch/x86_64/disk/ATA.cpp +++ b/kernel/src/arch/x86_64/disk/ATA.cpp @@ -77,6 +77,11 @@ namespace ATA return IO::inw(m_io_base + (u16)Register::Data); } + void Channel::write_data(u16 value) + { + IO::outw(m_io_base + (u16)Register::Data, value); + } + void Channel::write_register(Register reg, u8 value) { IO::outb(m_io_base + (u16)reg, value); @@ -154,7 +159,7 @@ namespace ATA kernel_sleep(timeout); - return !m_thread->sleep_ticks_left; + return m_thread->sleep_ticks_left; } bool Channel::wait_for_reg_set(Register reg, u8 value, u64 timeout) @@ -181,6 +186,31 @@ namespace ATA } } + Result Channel::wait_until_ready() + { + if (!wait_for_reg_clear(Register::Status, SR_Busy, 1000)) + { + kwarnln("ata: Drive %d:%d timed out (BSY)", m_channel_index, m_current_drive); + return err(EIO); + } + + if (!wait_for_reg_set(Register::Status, SR_DataRequestReady | SR_Error, 1000)) + { + kwarnln("ata: Drive %d:%d timed out (DRQ)", m_channel_index, m_current_drive); + return err(EIO); + } + + u8 status = read_register(Register::Status); + if (status & SR_Error) + { + kwarnln("ata: An error occurred in drive %d:%d while waiting for data to become available", m_channel_index, + m_current_drive); + return err(EIO); + } + + return {}; + } + bool Channel::initialize() { int offset = m_channel_index ? 2 : 0; @@ -375,9 +405,75 @@ namespace ATA m_dma_mem_phys = frame.release_value(); m_dma_mem = (void*)MMU::translate_physical_address(m_dma_mem_phys); + if (m_is_atapi) + { + atapi_packet packet; + memset(&packet, 0, sizeof(packet)); + packet.command_bytes[0] = ATAPI_ReadCapacity; + + atapi_read_capacity_reply reply; + + if (send_packet_atapi_pio(&packet, &reply, sizeof(reply)).has_error()) + { + kwarnln("ata: Failed to send Read Capacity command to ATAPI drive"); + return false; + } + + // FIXME: This assumes the host machine is little-endian. + u32 last_lba = __builtin_bswap32(reply.last_lba); + u32 sector_size = __builtin_bswap32(reply.sector_size); + + kinfoln("ata: ATAPI drive %d capacity information: Last LBA=%u, Sector Size=%u, Total Capacity=%u", + m_drive_index, last_lba, sector_size, (last_lba + 1) * sector_size); + } + return true; } + Result Drive::send_packet_atapi_pio(const atapi_packet* packet, void* out, u16 response_size) + { + u8* ptr = (u8*)out; + + m_channel->select(m_drive_index); + + // We use PIO here. + m_channel->write_register(Register::Features, 0x00); + + m_channel->write_register(Register::LBAMiddle, (u8)(response_size & 0xff)); + m_channel->write_register(Register::LBAHigh, (u8)(response_size >> 8)); + + m_channel->write_register(Register::Command, CMD_Packet); + + 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; + } + + return {}; + } + void Drive::irq_handler() { // Clear the IRQ flag. diff --git a/kernel/src/arch/x86_64/disk/ATA.h b/kernel/src/arch/x86_64/disk/ATA.h index dc980529..82c6075b 100644 --- a/kernel/src/arch/x86_64/disk/ATA.h +++ b/kernel/src/arch/x86_64/disk/ATA.h @@ -39,7 +39,7 @@ namespace ATA PRDTAddress = 4, }; - enum StatusRegister + enum StatusRegister : u8 { SR_Busy = 0x80, SR_DriveReady = 0x40, @@ -51,13 +51,14 @@ namespace ATA SR_Error = 0x01 }; - enum CommandRegister + enum CommandRegister : u8 { CMD_Identify = 0xec, + CMD_Packet = 0xa0, CMD_Identify_Packet = 0xa1 }; - enum BusMasterStatus + enum BusMasterStatus : u8 { BMS_SimplexOnly = 0x80, BMS_SlaveInit = 0x40, @@ -67,6 +68,11 @@ namespace ATA BMS_DMAMode = 0x1 }; + enum ATAPICommand : u8 + { + ATAPI_ReadCapacity = 0x25, + }; + class Controller; class Channel; @@ -77,6 +83,20 @@ namespace ATA u16 flags; }; + struct atapi_packet + { + union { + u16 command_words[6]; + u8 command_bytes[12]; + }; + }; + + struct atapi_read_capacity_reply + { + u32 last_lba; + u32 sector_size; + }; + static constexpr u16 END_OF_PRDT = (1 << 15); class Drive @@ -91,6 +111,8 @@ namespace ATA private: bool identify_ata(); + Result send_packet_atapi_pio(const atapi_packet* packet, void* out, u16 response_size); + Channel* m_channel; u8 m_drive_index; @@ -123,6 +145,7 @@ namespace ATA u8 read_register(Register reg); u16 read_data(); + void write_data(u16 value); void write_register(Register reg, u8 value); u8 read_control(ControlRegister reg); void write_control(ControlRegister reg, u8 value); @@ -135,6 +158,8 @@ namespace ATA bool wait_for_reg_set(Register reg, u8 value, u64 timeout); bool wait_for_reg_clear(Register reg, u8 value, u64 timeout); + Result wait_until_ready(); + void delay_400ns(); void wait_for_irq();