Add ATA drive support #27

Merged
apio merged 28 commits from please-read-my-ata-drive into main 2023-06-16 19:40:11 +00:00
2 changed files with 93 additions and 0 deletions
Showing only changes of commit cfcde5af55 - Show all commits

View File

@ -3,6 +3,7 @@
#include "arch/Serial.h"
#include "arch/Timer.h"
#include "arch/x86_64/IO.h"
#include "memory/MemoryManager.h"
#include <luna/Vector.h>
SharedPtr<ATA::Controller> g_controller;
@ -91,6 +92,26 @@ namespace ATA
IO::outb(m_control_base + (u16)reg, value);
}
u8 Channel::read_bm(BusmasterRegister reg)
{
return IO::inb(m_busmaster_base + (u16)reg);
}
void Channel::write_bm(BusmasterRegister reg, u8 value)
{
IO::outb(m_busmaster_base + (u16)reg, value);
}
u32 Channel::read_prdt_address()
{
return IO::inl(m_busmaster_base + (u16)BusmasterRegister::PRDTAddress);
}
void Channel::write_prdt_address(u32 value)
{
IO::outl(m_busmaster_base + (u16)BusmasterRegister::PRDTAddress, value);
}
void Channel::delay_400ns()
{
// FIXME: We should use kernel_sleep(), but it doesn't support nanosecond granularity.
@ -127,6 +148,15 @@ namespace ATA
kernel_wait_for_event();
}
bool Channel::wait_for_irq_or_timeout(u64 timeout)
{
m_thread = Scheduler::current();
kernel_sleep(timeout);
return !m_thread->sleep_ticks_left;
}
bool Channel::wait_for_reg_set(Register reg, u8 value, u64 timeout)
{
u64 begin = Timer::ticks_ms();
@ -320,6 +350,31 @@ namespace ATA
kinfoln("ata: Drive IDENTIFY returned serial='%s', revision='%s' and model='%s'", m_serial.chars(),
m_revision.chars(), m_model.chars());
auto status = m_channel->read_bm(BusmasterRegister::Status);
if (status & BMS_SimplexOnly)
{
kwarnln("ata: Drive %d will not use DMA because of simplex shenanigans", m_drive_index);
m_uses_dma = false;
}
auto frame = MemoryManager::alloc_frame();
if (frame.has_error() || frame.value() > 0xffffffff)
{
kwarnln("ata: Failed to allocate memory below the 32-bit limit for the PRDT");
return false;
}
m_dma_prdt_phys = frame.release_value();
m_dma_prdt = (prdt_entry*)MMU::translate_physical_address(m_dma_prdt_phys);
frame = MemoryManager::alloc_frame();
if (frame.has_error() || frame.value() > 0xffffffff)
{
kwarnln("ata: Failed to allocate memory below the 32-bit limit for DMA memory");
return false;
}
m_dma_mem_phys = frame.release_value();
m_dma_mem = (void*)MMU::translate_physical_address(m_dma_mem_phys);
return true;
}

View File

@ -32,6 +32,13 @@ namespace ATA
DriveAddress = 1,
};
enum class BusmasterRegister : u16
{
Command = 0,
Status = 2,
PRDTAddress = 4,
};
enum StatusRegister
{
SR_Busy = 0x80,
@ -50,9 +57,28 @@ namespace ATA
CMD_Identify_Packet = 0xa1
};
enum BusMasterStatus
{
BMS_SimplexOnly = 0x80,
BMS_SlaveInit = 0x40,
BMS_MasterInit = 0x20,
BMS_IRQPending = 0x4,
BMS_DMAFailure = 0x2,
BMS_DMAMode = 0x1
};
class Controller;
class Channel;
struct prdt_entry
{
u32 address;
u16 count;
u16 flags;
};
static constexpr u16 END_OF_PRDT = (1 << 15);
class Drive
{
public:
@ -74,6 +100,12 @@ namespace ATA
};
bool m_is_atapi { false };
bool m_uses_dma { true };
volatile prdt_entry* m_dma_prdt;
u64 m_dma_prdt_phys;
volatile void* m_dma_mem;
u64 m_dma_mem_phys;
constexpr static usize SERIAL_LEN = 20;
constexpr static usize REVISION_LEN = 8;
@ -95,12 +127,18 @@ namespace ATA
u8 read_control(ControlRegister reg);
void write_control(ControlRegister reg, u8 value);
u8 read_bm(BusmasterRegister reg);
void write_bm(BusmasterRegister reg, u8 value);
u32 read_prdt_address();
void write_prdt_address(u32 value);
bool wait_for_reg_set(Register reg, u8 value, u64 timeout);
bool wait_for_reg_clear(Register reg, u8 value, u64 timeout);
void delay_400ns();
void wait_for_irq();
bool wait_for_irq_or_timeout(u64 timeout);
void irq_handler(Registers*);
void select(u8 drive);