2023-05-10 19:15:47 +02:00
|
|
|
#include "arch/x86_64/disk/ATA.h"
|
|
|
|
#include "Log.h"
|
2023-05-13 16:13:26 +02:00
|
|
|
#include "arch/Serial.h"
|
|
|
|
#include "arch/Timer.h"
|
2023-05-10 19:15:47 +02:00
|
|
|
#include "arch/x86_64/IO.h"
|
|
|
|
#include <luna/Vector.h>
|
|
|
|
|
|
|
|
SharedPtr<ATA::Controller> g_controller;
|
|
|
|
|
2023-05-13 14:24:45 +02:00
|
|
|
static void irq_handler(Registers* regs, void* ctx)
|
|
|
|
{
|
|
|
|
((ATA::Channel*)ctx)->irq_handler(regs);
|
|
|
|
}
|
|
|
|
|
2023-05-10 19:15:47 +02:00
|
|
|
namespace ATA
|
|
|
|
{
|
|
|
|
Result<void> Controller::scan()
|
|
|
|
{
|
|
|
|
// FIXME: Propagate errors.
|
|
|
|
PCI::scan(
|
|
|
|
[](const PCI::Device& device) {
|
|
|
|
if (!g_controller)
|
|
|
|
{
|
|
|
|
auto controller = adopt_shared_if_nonnull(new (std::nothrow) Controller(device)).release_value();
|
|
|
|
kinfoln("ata: Found ATA controller on PCI bus (%x:%x:%x)", device.address.bus,
|
|
|
|
device.address.function, device.address.slot);
|
|
|
|
|
|
|
|
if (controller->initialize()) g_controller = controller;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{ .klass = 1, .subclass = 1 });
|
|
|
|
|
2023-05-11 19:27:05 +02:00
|
|
|
if (!g_controller) kwarnln("ata: No ATA controller found.");
|
|
|
|
|
2023-05-10 19:15:47 +02:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Controller::initialize()
|
|
|
|
{
|
|
|
|
if (!m_primary_channel.initialize()) return false;
|
|
|
|
|
|
|
|
return m_secondary_channel.initialize();
|
|
|
|
}
|
|
|
|
|
|
|
|
Controller::Controller(const PCI::Device& device)
|
|
|
|
: m_device(device), m_primary_channel(this, 0, {}), m_secondary_channel(this, 1, {})
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Channel::Channel(Controller* controller, u8 channel_index, Badge<Controller>)
|
|
|
|
: m_controller(controller), m_channel_index(channel_index)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2023-05-13 14:11:09 +02:00
|
|
|
u8 Channel::read_register(Register reg)
|
|
|
|
{
|
|
|
|
return IO::inb(m_io_base + (u16)reg);
|
|
|
|
}
|
|
|
|
|
2023-05-13 16:13:26 +02:00
|
|
|
u16 Channel::read_data()
|
|
|
|
{
|
|
|
|
return IO::inw(m_io_base + (u16)Register::Data);
|
|
|
|
}
|
|
|
|
|
2023-05-13 14:11:09 +02:00
|
|
|
void Channel::write_register(Register reg, u8 value)
|
|
|
|
{
|
|
|
|
IO::outb(m_io_base + (u16)reg, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
u8 Channel::read_control(ControlRegister reg)
|
|
|
|
{
|
|
|
|
return IO::inb(m_control_base + (u16)reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Channel::write_control(ControlRegister reg, u8 value)
|
|
|
|
{
|
|
|
|
IO::outb(m_control_base + (u16)reg, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Channel::delay_400ns()
|
|
|
|
{
|
2023-05-13 16:13:26 +02:00
|
|
|
// FIXME: We should use kernel_sleep(), but it doesn't support nanosecond granularity.
|
|
|
|
for (int i = 0; i < 14; i++) { read_control(ControlRegister::AltStatus); }
|
2023-05-13 14:11:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Channel::select(u8 drive)
|
|
|
|
{
|
|
|
|
if (drive == m_current_drive) return;
|
|
|
|
|
|
|
|
u8 value = (drive << 4) | 0xa0;
|
|
|
|
write_register(Register::DriveSelect, value);
|
|
|
|
|
|
|
|
delay_400ns();
|
|
|
|
|
|
|
|
m_current_drive = drive;
|
|
|
|
}
|
|
|
|
|
2023-05-13 14:24:45 +02:00
|
|
|
void Channel::irq_handler(Registers*)
|
|
|
|
{
|
|
|
|
// FIXME: Read the Busmaster register to make sure the IRQ is for this channel.
|
|
|
|
|
|
|
|
// FIXME: Also read it in case there was a DMA read error.
|
|
|
|
|
2023-05-13 16:13:26 +02:00
|
|
|
if (m_current_drive < 2 && m_drives[m_current_drive]) m_drives[m_current_drive]->irq_handler();
|
|
|
|
|
2023-05-13 14:24:45 +02:00
|
|
|
m_thread->wake_up();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Channel::wait_for_irq()
|
|
|
|
{
|
|
|
|
m_thread = Scheduler::current();
|
|
|
|
|
|
|
|
kernel_wait_for_event();
|
|
|
|
}
|
|
|
|
|
2023-05-13 16:13:26 +02:00
|
|
|
bool Channel::wait_for_reg_set(Register reg, u8 value, u64 timeout)
|
|
|
|
{
|
|
|
|
u64 begin = Timer::ticks_ms();
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
u8 reg_value = read_register(reg);
|
|
|
|
if (reg_value & value) return true;
|
|
|
|
if ((Timer::ticks_ms() - begin) >= timeout) return false;
|
|
|
|
kernel_sleep(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Channel::wait_for_reg_clear(Register reg, u8 value, u64 timeout)
|
|
|
|
{
|
|
|
|
u64 begin = Timer::ticks_ms();
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
u8 reg_value = read_register(reg);
|
|
|
|
if ((reg_value & value) == 0) return true;
|
|
|
|
if ((Timer::ticks_ms() - begin) >= timeout) return false;
|
|
|
|
kernel_sleep(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-10 19:15:47 +02:00
|
|
|
bool Channel::initialize()
|
|
|
|
{
|
|
|
|
int offset = m_channel_index ? 2 : 0;
|
|
|
|
m_is_pci_native_mode = m_controller->device().type.prog_if & (1 << offset);
|
2023-05-13 14:11:09 +02:00
|
|
|
|
|
|
|
u32 control_port_base_address;
|
|
|
|
u32 io_base_address;
|
2023-05-10 19:15:47 +02:00
|
|
|
|
|
|
|
if (m_is_pci_native_mode)
|
|
|
|
{
|
2023-05-13 14:11:09 +02:00
|
|
|
// FIXME: Properly decode BARs.
|
|
|
|
io_base_address = PCI::read32(m_controller->device().address, m_channel_index ? PCI::BAR2 : PCI::BAR0);
|
|
|
|
control_port_base_address =
|
2023-05-10 19:15:47 +02:00
|
|
|
PCI::read32(m_controller->device().address, m_channel_index ? PCI::BAR3 : PCI::BAR1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-05-13 14:11:09 +02:00
|
|
|
io_base_address = m_channel_index ? 0x170 : 0x1f0;
|
|
|
|
control_port_base_address = m_channel_index ? 0x376 : 0x3f6;
|
2023-05-10 19:15:47 +02:00
|
|
|
}
|
|
|
|
|
2023-05-13 14:11:09 +02:00
|
|
|
m_io_base = (u16)io_base_address;
|
|
|
|
m_control_base = (u16)control_port_base_address + 2;
|
|
|
|
|
|
|
|
if (m_is_pci_native_mode) m_interrupt_line = PCI::read8(m_controller->device().address, PCI::InterruptLine);
|
2023-05-10 19:15:47 +02:00
|
|
|
else
|
2023-05-13 14:11:09 +02:00
|
|
|
m_interrupt_line = m_channel_index ? 15 : 14;
|
|
|
|
|
2023-05-13 14:24:45 +02:00
|
|
|
bool ok = CPU::register_interrupt(m_interrupt_line, ::irq_handler, this);
|
|
|
|
if (!ok)
|
|
|
|
{
|
|
|
|
kerrorln("ata: Failed to register IRQ handler for ATA channel %d (IRQ %d)", m_channel_index,
|
|
|
|
m_interrupt_line);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-05-13 14:11:09 +02:00
|
|
|
for (u8 drive = 0; drive < 2; drive++)
|
|
|
|
{
|
|
|
|
ScopedKMutexLock<100> lock(m_lock);
|
2023-05-10 19:15:47 +02:00
|
|
|
|
2023-05-13 14:11:09 +02:00
|
|
|
select(drive);
|
|
|
|
|
|
|
|
if (read_register(Register::Status) == 0)
|
|
|
|
{
|
|
|
|
// No drive on this slot.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
kinfoln("ata: Channel %d has a drive on slot %d!", m_channel_index, drive);
|
2023-05-13 16:13:26 +02:00
|
|
|
|
|
|
|
auto rc = adopt_shared_if_nonnull(new (std::nothrow) Drive(this, drive, {}));
|
|
|
|
if (rc.has_error())
|
|
|
|
{
|
|
|
|
kinfoln("ata: Failed to create drive object: %s", rc.error_string());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_drives[drive] = rc.release_value();
|
|
|
|
|
|
|
|
if (!m_drives[drive]->initialize())
|
|
|
|
{
|
|
|
|
m_drives[drive] = {};
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
Drive::Drive(Channel* channel, u8 drive_index, Badge<Channel>) : m_channel(channel), m_drive_index(drive_index)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Drive::identify_ata()
|
|
|
|
{
|
|
|
|
m_channel->write_register(Register::Command, m_is_atapi ? CMD_Identify_Packet : CMD_Identify);
|
|
|
|
|
|
|
|
m_channel->delay_400ns();
|
|
|
|
|
|
|
|
if (!m_channel->wait_for_reg_clear(Register::Status, SR_Busy, 1000))
|
|
|
|
{
|
|
|
|
kwarnln("ata: Drive %d timed out clearing SR_Busy (waited for 1000 ms)", m_drive_index);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_channel->read_register(Register::Status) & SR_Error)
|
|
|
|
{
|
|
|
|
u8 lbam = m_channel->read_register(Register::LBAMiddle);
|
|
|
|
u8 lbah = m_channel->read_register(Register::LBAHigh);
|
|
|
|
|
|
|
|
if ((lbam == 0x14 && lbah == 0xeb) || (lbam == 0x69 && lbah == 0x96))
|
|
|
|
{
|
|
|
|
if (!m_is_atapi)
|
|
|
|
{
|
|
|
|
kinfoln("ata: Drive %d is ATAPI, sending IDENTIFY_PACKET command", m_drive_index);
|
|
|
|
m_is_atapi = true;
|
|
|
|
return identify_ata();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
kwarnln("ata: IDENTIFY command for drive %d returned error", m_drive_index);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_channel->wait_for_reg_set(Register::Status, SR_DataRequestReady | SR_Error, 1000))
|
|
|
|
{
|
|
|
|
kwarnln("ata: Drive %d timed out setting SR_DataRequestReady (waited for 1000 ms)", m_drive_index);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
u8 status = m_channel->read_register(Register::Status);
|
|
|
|
if (status & SR_Error)
|
|
|
|
{
|
|
|
|
kwarnln("ata: IDENTIFY command for drive %d returned error", m_drive_index);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (usize i = 0; i < 256; i++)
|
|
|
|
{
|
|
|
|
u16 data = m_channel->read_data();
|
|
|
|
m_identify_words[i] = data;
|
2023-05-13 14:11:09 +02:00
|
|
|
}
|
2023-05-10 19:15:47 +02:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2023-05-13 16:13:26 +02:00
|
|
|
|
|
|
|
bool Drive::initialize()
|
|
|
|
{
|
|
|
|
m_channel->select(m_drive_index);
|
|
|
|
|
|
|
|
m_channel->write_register(Register::SectorCount, 0);
|
|
|
|
m_channel->write_register(Register::LBALow, 0);
|
|
|
|
m_channel->write_register(Register::LBAMiddle, 0);
|
|
|
|
m_channel->write_register(Register::LBAHigh, 0);
|
|
|
|
|
|
|
|
if (!identify_ata()) return false;
|
|
|
|
|
|
|
|
m_serial = StringView::from_fixed_size_cstring((const char*)&m_identify_words[10], SERIAL_LEN);
|
|
|
|
m_revision = StringView::from_fixed_size_cstring((const char*)&m_identify_words[23], REVISION_LEN);
|
|
|
|
m_model = StringView::from_fixed_size_cstring((const char*)&m_identify_words[27], MODEL_LEN);
|
|
|
|
|
|
|
|
m_serial.trim(" ");
|
|
|
|
m_revision.trim(" ");
|
|
|
|
m_model.trim(" ");
|
|
|
|
|
|
|
|
kinfoln("ata: Drive IDENTIFY returned serial='%s', revision='%s' and model='%s'", m_serial.chars(),
|
|
|
|
m_revision.chars(), m_model.chars());
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Drive::irq_handler()
|
|
|
|
{
|
|
|
|
// Clear the IRQ flag.
|
|
|
|
u8 status = m_channel->read_register(Register::Status);
|
|
|
|
|
|
|
|
if (status & SR_Error)
|
|
|
|
{
|
|
|
|
u8 error = m_channel->read_register(Register::Error);
|
|
|
|
(void)error;
|
|
|
|
}
|
|
|
|
}
|
2023-05-10 19:15:47 +02:00
|
|
|
}
|