#include "arch/x86_64/disk/ATA.h" #include "Log.h" #include "arch/Serial.h" #include "arch/Timer.h" #include "arch/x86_64/IO.h" #include SharedPtr g_controller; static void irq_handler(Registers* regs, void* ctx) { ((ATA::Channel*)ctx)->irq_handler(regs); } namespace ATA { Result 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 }); if (!g_controller) kwarnln("ata: No ATA controller found."); 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) : m_controller(controller), m_channel_index(channel_index) { } u8 Channel::read_register(Register reg) { return IO::inb(m_io_base + (u16)reg); } u16 Channel::read_data() { return IO::inw(m_io_base + (u16)Register::Data); } 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() { // FIXME: We should use kernel_sleep(), but it doesn't support nanosecond granularity. for (int i = 0; i < 14; i++) { read_control(ControlRegister::AltStatus); } } 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; } 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. if (m_current_drive < 2 && m_drives[m_current_drive]) m_drives[m_current_drive]->irq_handler(); m_thread->wake_up(); } void Channel::wait_for_irq() { m_thread = Scheduler::current(); kernel_wait_for_event(); } 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); } } bool Channel::initialize() { int offset = m_channel_index ? 2 : 0; m_is_pci_native_mode = m_controller->device().type.prog_if & (1 << offset); u32 control_port_base_address; u32 io_base_address; if (m_is_pci_native_mode) { // FIXME: Properly decode BARs. io_base_address = PCI::read32(m_controller->device().address, m_channel_index ? PCI::BAR2 : PCI::BAR0); control_port_base_address = PCI::read32(m_controller->device().address, m_channel_index ? PCI::BAR3 : PCI::BAR1); } else { io_base_address = m_channel_index ? 0x170 : 0x1f0; control_port_base_address = m_channel_index ? 0x376 : 0x3f6; } 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); else m_interrupt_line = m_channel_index ? 15 : 14; 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; } for (u8 drive = 0; drive < 2; drive++) { ScopedKMutexLock<100> lock(m_lock); 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); 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) : 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; } return true; } 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; } } }