kernel/ATA: Read the CDROM's first sector using ATAPI PIO!!
Sadly, for some reason, DMA is not working right now. This is a problem, as PIO is inconvenient. But hey, it works for now!
This commit is contained in:
parent
cc8450751c
commit
2fa11a5ae3
@ -4,6 +4,7 @@
|
||||
#include "arch/Timer.h"
|
||||
#include "arch/x86_64/IO.h"
|
||||
#include "memory/MemoryManager.h"
|
||||
#include <luna/CType.h>
|
||||
#include <luna/SafeArithmetic.h>
|
||||
#include <luna/Vector.h>
|
||||
|
||||
@ -53,6 +54,15 @@ namespace ATA
|
||||
|
||||
bool Controller::initialize()
|
||||
{
|
||||
u16 command_old = PCI::read16(m_device.address, PCI::Command);
|
||||
u16 command_new = command_old;
|
||||
|
||||
command_new &= ~PCI::CMD_INTERRUPT_DISABLE;
|
||||
command_new |= PCI::CMD_IO_SPACE;
|
||||
command_new |= PCI::CMD_BUS_MASTER;
|
||||
|
||||
if (command_new != command_old) PCI::write16(m_device.address, PCI::Command, command_new);
|
||||
|
||||
if (!m_primary_channel.initialize()) return false;
|
||||
|
||||
return m_secondary_channel.initialize();
|
||||
@ -121,7 +131,7 @@ namespace ATA
|
||||
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); }
|
||||
for (int i = 0; i < 14; i++) { [[maybe_unused]] volatile u8 val = read_control(ControlRegister::AltStatus); }
|
||||
}
|
||||
|
||||
void Channel::select(u8 drive)
|
||||
@ -140,35 +150,45 @@ namespace ATA
|
||||
{
|
||||
// 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();
|
||||
m_irq_called = true;
|
||||
|
||||
if (m_thread) m_thread->wake_up();
|
||||
}
|
||||
|
||||
void Channel::prepare_for_irq()
|
||||
{
|
||||
m_thread = Scheduler::current();
|
||||
m_irq_called = false;
|
||||
}
|
||||
|
||||
void Channel::wait_for_irq()
|
||||
{
|
||||
m_thread = Scheduler::current();
|
||||
if (!m_irq_called) kernel_wait_for_event();
|
||||
|
||||
kernel_wait_for_event();
|
||||
m_irq_called = false;
|
||||
}
|
||||
|
||||
bool Channel::wait_for_irq_or_timeout(u64 timeout)
|
||||
{
|
||||
m_thread = Scheduler::current();
|
||||
|
||||
if (!m_irq_called)
|
||||
{
|
||||
kernel_sleep(timeout);
|
||||
|
||||
m_irq_called = false;
|
||||
return m_thread->sleep_ticks_left;
|
||||
}
|
||||
|
||||
m_irq_called = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
u8 reg_value = reg == Register::Status ? read_control(ControlRegister::AltStatus) : read_register(reg);
|
||||
if (reg_value & value) return true;
|
||||
if ((Timer::ticks_ms() - begin) >= timeout) return false;
|
||||
kernel_sleep(1);
|
||||
@ -180,7 +200,7 @@ namespace ATA
|
||||
u64 begin = Timer::ticks_ms();
|
||||
while (true)
|
||||
{
|
||||
u8 reg_value = read_register(reg);
|
||||
u8 reg_value = reg == Register::Status ? read_control(ControlRegister::AltStatus) : read_register(reg);
|
||||
if ((reg_value & value) == 0) return true;
|
||||
if ((Timer::ticks_ms() - begin) >= timeout) return false;
|
||||
kernel_sleep(1);
|
||||
@ -201,7 +221,7 @@ namespace ATA
|
||||
return err(EIO);
|
||||
}
|
||||
|
||||
u8 status = read_register(Register::Status);
|
||||
u8 status = read_control(ControlRegister::AltStatus);
|
||||
if (status & SR_Error)
|
||||
{
|
||||
kwarnln("ata: An error occurred in drive %d:%d while waiting for data to become available", m_channel_index,
|
||||
@ -237,7 +257,7 @@ namespace ATA
|
||||
return false;
|
||||
}
|
||||
|
||||
control_port_base_address = io_control.port();
|
||||
control_port_base_address = io_control.port() + 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -246,7 +266,7 @@ namespace ATA
|
||||
}
|
||||
|
||||
m_io_base = io_base_address;
|
||||
m_control_base = control_port_base_address + 2;
|
||||
m_control_base = control_port_base_address;
|
||||
|
||||
auto io_busmaster = m_controller->device().getBAR(4);
|
||||
if (!io_busmaster.is_iospace())
|
||||
@ -260,13 +280,7 @@ namespace ATA
|
||||
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;
|
||||
}
|
||||
write_control(ControlRegister::DeviceControl, 0);
|
||||
|
||||
for (u8 drive = 0; drive < 2; drive++)
|
||||
{
|
||||
@ -298,6 +312,26 @@ namespace ATA
|
||||
}
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
if (m_drives[drive])
|
||||
{
|
||||
if (!m_drives[drive]->post_initialize())
|
||||
{
|
||||
m_drives[drive] = {};
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -397,6 +431,8 @@ namespace ATA
|
||||
m_dma_prdt_phys = frame.release_value();
|
||||
m_dma_prdt = (prdt_entry*)MMU::translate_physical_address(m_dma_prdt_phys);
|
||||
|
||||
memset(m_dma_prdt, 0, ARCH_PAGE_SIZE);
|
||||
|
||||
frame = MemoryManager::alloc_frame();
|
||||
if (frame.has_error() || frame.value() > 0xffffffff)
|
||||
{
|
||||
@ -406,6 +442,20 @@ namespace ATA
|
||||
m_dma_mem_phys = frame.release_value();
|
||||
m_dma_mem = (void*)MMU::translate_physical_address(m_dma_mem_phys);
|
||||
|
||||
memset(const_cast<void*>(m_dma_mem), 0, ARCH_PAGE_SIZE);
|
||||
|
||||
if (m_uses_dma)
|
||||
{
|
||||
auto cmd = m_channel->read_bm(BusmasterRegister::Command);
|
||||
cmd &= ~BMC_StartStop;
|
||||
m_channel->write_bm(BusmasterRegister::Command, cmd);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Drive::post_initialize()
|
||||
{
|
||||
if (m_is_atapi)
|
||||
{
|
||||
atapi_packet packet;
|
||||
@ -453,6 +503,14 @@ namespace ATA
|
||||
kinfoln("ata: Drive %d capacity information: Block Count=%lu, Block Size=%lu, Total Capacity=%lu",
|
||||
m_drive_index, m_block_count, m_block_size, total_capacity);
|
||||
|
||||
char buf[2048];
|
||||
if (atapi_read_pio(0, buf, sizeof(buf)).has_error()) return false;
|
||||
|
||||
char readable[2049];
|
||||
for (int i = 0; i < 2048; i++) { readable[i] = _iscntrl(buf[i]) ? '.' : buf[i]; }
|
||||
readable[2048] = 0;
|
||||
kinfoln("ata: Read first sector from CD-ROM: %s", readable);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -500,6 +558,123 @@ namespace ATA
|
||||
return {};
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
Result<void> Drive::send_packet_atapi_dma(const atapi_packet* packet, void* out, u16 response_size)
|
||||
{
|
||||
check(m_uses_dma);
|
||||
|
||||
m_channel->select(m_drive_index);
|
||||
|
||||
kdbgln("have selected");
|
||||
|
||||
// We use DMA here.
|
||||
m_channel->write_register(Register::Features, 0x01);
|
||||
|
||||
m_channel->write_register(Register::LBAMiddle, 0);
|
||||
m_channel->write_register(Register::LBAHigh, 0);
|
||||
|
||||
kdbgln("will do_dma_command");
|
||||
|
||||
TRY(do_dma_command(CMD_Packet, response_size, false));
|
||||
|
||||
TRY(m_channel->wait_until_ready());
|
||||
|
||||
kdbgln("send atapi packet data");
|
||||
|
||||
for (int j = 0; j < 6; j++) m_channel->write_data(packet->command_words[j]);
|
||||
|
||||
kdbgln("do dma transfer");
|
||||
|
||||
TRY(do_dma_transfer());
|
||||
|
||||
memcpy(out, const_cast<void*>(m_dma_mem), response_size);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void> Drive::do_dma_command(u8 command, u16 count, bool write)
|
||||
{
|
||||
m_dma_prdt->address = (u32)m_dma_mem_phys;
|
||||
m_dma_prdt->count = count;
|
||||
m_dma_prdt->flags = END_OF_PRDT;
|
||||
|
||||
kdbgln("ata: do_dma_command: phys=%x, command=%x, count=%u, write=%d", m_dma_prdt->address, command, count,
|
||||
write);
|
||||
|
||||
m_channel->write_prdt_address((u32)m_dma_prdt_phys);
|
||||
|
||||
auto status = m_channel->read_bm(BusmasterRegister::Status);
|
||||
status &= ~(BMS_DMAFailure | BMS_IRQPending);
|
||||
m_channel->write_bm(BusmasterRegister::Status, status);
|
||||
|
||||
auto cmd = m_channel->read_bm(BusmasterRegister::Command);
|
||||
if (!write) cmd |= BMC_ReadWrite;
|
||||
else
|
||||
cmd &= ~BMC_ReadWrite;
|
||||
m_channel->write_bm(BusmasterRegister::Command, cmd);
|
||||
|
||||
m_channel->prepare_for_irq();
|
||||
|
||||
m_channel->write_register(Register::Command, command);
|
||||
|
||||
cmd = m_channel->read_bm(BusmasterRegister::Command);
|
||||
cmd |= BMC_StartStop;
|
||||
m_channel->write_bm(BusmasterRegister::Command, cmd);
|
||||
|
||||
m_channel->delay_400ns();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void> Drive::do_dma_transfer()
|
||||
{
|
||||
if (!m_channel->wait_for_irq_or_timeout(2000))
|
||||
{
|
||||
kwarnln("ata: Drive %d timed out (DMA)", m_drive_index);
|
||||
return err(EIO);
|
||||
}
|
||||
|
||||
u8 status = m_channel->read_control(ControlRegister::AltStatus);
|
||||
kdbgln("ata: status after irq: %#x", status);
|
||||
|
||||
m_channel->delay_400ns();
|
||||
|
||||
auto cmd = m_channel->read_bm(BusmasterRegister::Command);
|
||||
cmd &= ~BMC_StartStop;
|
||||
m_channel->write_bm(BusmasterRegister::Command, cmd);
|
||||
|
||||
status = m_channel->read_bm(BusmasterRegister::Status);
|
||||
m_channel->write_bm(BusmasterRegister::Status, status & ~(BMS_DMAFailure | BMS_IRQPending));
|
||||
|
||||
if (status & BMS_DMAFailure)
|
||||
{
|
||||
kwarnln("ata: DMA failure while trying to read drive %d", m_drive_index);
|
||||
return err(EIO);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Result<void> Drive::atapi_read_pio(u64 lba, void* out, usize size)
|
||||
{
|
||||
check(lba < m_block_count);
|
||||
check(size <= ARCH_PAGE_SIZE);
|
||||
|
||||
atapi_packet read_packet;
|
||||
memset(&read_packet, 0, sizeof(read_packet));
|
||||
read_packet.command_bytes[0] = ATAPI_Read;
|
||||
read_packet.command_bytes[2] = (lba >> 0x18) & 0xff;
|
||||
read_packet.command_bytes[3] = (lba >> 0x10) & 0xff;
|
||||
read_packet.command_bytes[4] = (lba >> 0x08) & 0xff;
|
||||
read_packet.command_bytes[5] = (lba >> 0x00) & 0xff;
|
||||
read_packet.command_bytes[9] = (u8)(size / m_block_size);
|
||||
|
||||
return send_packet_atapi_pio(&read_packet, out, (u16)size);
|
||||
}
|
||||
|
||||
void Drive::irq_handler()
|
||||
{
|
||||
// Clear the IRQ flag.
|
||||
@ -510,5 +685,12 @@ namespace ATA
|
||||
u8 error = m_channel->read_register(Register::Error);
|
||||
(void)error;
|
||||
}
|
||||
|
||||
if (m_uses_dma)
|
||||
{
|
||||
status = m_channel->read_bm(BusmasterRegister::Status);
|
||||
if (status & BMS_DMAFailure) { kwarnln("ata: DMA failure in irq"); }
|
||||
m_channel->write_bm(BusmasterRegister::Status, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -68,6 +68,12 @@ namespace ATA
|
||||
BMS_DMAMode = 0x1
|
||||
};
|
||||
|
||||
enum BusMasterCommand : u8
|
||||
{
|
||||
BMC_ReadWrite = 0x8,
|
||||
BMC_StartStop = 0x1,
|
||||
};
|
||||
|
||||
struct ATAIdentify
|
||||
{
|
||||
u16 flags;
|
||||
@ -96,6 +102,7 @@ namespace ATA
|
||||
enum ATAPICommand : u8
|
||||
{
|
||||
ATAPI_ReadCapacity = 0x25,
|
||||
ATAPI_Read = 0xa8,
|
||||
};
|
||||
|
||||
class Controller;
|
||||
@ -131,12 +138,22 @@ namespace ATA
|
||||
|
||||
bool initialize();
|
||||
|
||||
bool post_initialize();
|
||||
|
||||
void irq_handler();
|
||||
|
||||
private:
|
||||
bool identify_ata();
|
||||
|
||||
Result<void> send_packet_atapi_pio(const atapi_packet* packet, void* out, u16 response_size);
|
||||
#if 0
|
||||
Result<void> send_packet_atapi_dma(const atapi_packet* packet, void* out, u16 response_size);
|
||||
|
||||
Result<void> do_dma_command(u8 command, u16 count, bool write);
|
||||
Result<void> do_dma_transfer();
|
||||
#endif
|
||||
|
||||
Result<void> atapi_read_pio(u64 lba, void* out, usize size);
|
||||
|
||||
Channel* m_channel;
|
||||
|
||||
@ -153,7 +170,7 @@ namespace ATA
|
||||
u64 m_block_count;
|
||||
u64 m_block_size;
|
||||
|
||||
volatile prdt_entry* m_dma_prdt;
|
||||
prdt_entry* m_dma_prdt;
|
||||
u64 m_dma_prdt_phys;
|
||||
volatile void* m_dma_mem;
|
||||
u64 m_dma_mem_phys;
|
||||
@ -191,6 +208,8 @@ namespace ATA
|
||||
|
||||
void delay_400ns();
|
||||
|
||||
void prepare_for_irq();
|
||||
|
||||
void wait_for_irq();
|
||||
bool wait_for_irq_or_timeout(u64 timeout);
|
||||
void irq_handler(Registers*);
|
||||
@ -208,12 +227,14 @@ namespace ATA
|
||||
|
||||
KMutex<100> m_lock {};
|
||||
|
||||
Thread* m_thread;
|
||||
Thread* m_thread { nullptr };
|
||||
|
||||
u16 m_io_base;
|
||||
u16 m_control_base;
|
||||
u16 m_busmaster_base;
|
||||
|
||||
bool m_irq_called { false };
|
||||
|
||||
u8 m_current_drive = (u8)-1;
|
||||
|
||||
SharedPtr<Drive> m_drives[2];
|
||||
|
Loading…
Reference in New Issue
Block a user