kernel/ATA: Send a READ CAPACITY packet to an ATA drive on initialization

This commit is contained in:
apio 2023-05-13 22:18:44 +02:00
parent cfcde5af55
commit 46c45068e0
Signed by: apio
GPG Key ID: B8A7D06E42258954
2 changed files with 125 additions and 4 deletions

View File

@ -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<void> 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<void> 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.

View File

@ -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<void> 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<void> wait_until_ready();
void delay_400ns();
void wait_for_irq();