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/Timer.h"
|
||||||
#include "arch/x86_64/IO.h"
|
#include "arch/x86_64/IO.h"
|
||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
|
#include <luna/CType.h>
|
||||||
#include <luna/SafeArithmetic.h>
|
#include <luna/SafeArithmetic.h>
|
||||||
#include <luna/Vector.h>
|
#include <luna/Vector.h>
|
||||||
|
|
||||||
@ -53,6 +54,15 @@ namespace ATA
|
|||||||
|
|
||||||
bool Controller::initialize()
|
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;
|
if (!m_primary_channel.initialize()) return false;
|
||||||
|
|
||||||
return m_secondary_channel.initialize();
|
return m_secondary_channel.initialize();
|
||||||
@ -121,7 +131,7 @@ namespace ATA
|
|||||||
void Channel::delay_400ns()
|
void Channel::delay_400ns()
|
||||||
{
|
{
|
||||||
// FIXME: We should use kernel_sleep(), but it doesn't support nanosecond granularity.
|
// 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)
|
void Channel::select(u8 drive)
|
||||||
@ -140,27 +150,37 @@ namespace ATA
|
|||||||
{
|
{
|
||||||
// FIXME: Read the Busmaster register to make sure the IRQ is for this channel.
|
// 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();
|
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()
|
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)
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
kernel_sleep(timeout);
|
m_irq_called = false;
|
||||||
|
return true;
|
||||||
return m_thread->sleep_ticks_left;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Channel::wait_for_reg_set(Register reg, u8 value, u64 timeout)
|
bool Channel::wait_for_reg_set(Register reg, u8 value, u64 timeout)
|
||||||
@ -168,7 +188,7 @@ namespace ATA
|
|||||||
u64 begin = Timer::ticks_ms();
|
u64 begin = Timer::ticks_ms();
|
||||||
while (true)
|
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 (reg_value & value) return true;
|
||||||
if ((Timer::ticks_ms() - begin) >= timeout) return false;
|
if ((Timer::ticks_ms() - begin) >= timeout) return false;
|
||||||
kernel_sleep(1);
|
kernel_sleep(1);
|
||||||
@ -180,7 +200,7 @@ namespace ATA
|
|||||||
u64 begin = Timer::ticks_ms();
|
u64 begin = Timer::ticks_ms();
|
||||||
while (true)
|
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 ((reg_value & value) == 0) return true;
|
||||||
if ((Timer::ticks_ms() - begin) >= timeout) return false;
|
if ((Timer::ticks_ms() - begin) >= timeout) return false;
|
||||||
kernel_sleep(1);
|
kernel_sleep(1);
|
||||||
@ -201,7 +221,7 @@ namespace ATA
|
|||||||
return err(EIO);
|
return err(EIO);
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 status = read_register(Register::Status);
|
u8 status = read_control(ControlRegister::AltStatus);
|
||||||
if (status & SR_Error)
|
if (status & SR_Error)
|
||||||
{
|
{
|
||||||
kwarnln("ata: An error occurred in drive %d:%d while waiting for data to become available", m_channel_index,
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
control_port_base_address = io_control.port();
|
control_port_base_address = io_control.port() + 2;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -246,7 +266,7 @@ namespace ATA
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_io_base = io_base_address;
|
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);
|
auto io_busmaster = m_controller->device().getBAR(4);
|
||||||
if (!io_busmaster.is_iospace())
|
if (!io_busmaster.is_iospace())
|
||||||
@ -260,13 +280,7 @@ namespace ATA
|
|||||||
else
|
else
|
||||||
m_interrupt_line = m_channel_index ? 15 : 14;
|
m_interrupt_line = m_channel_index ? 15 : 14;
|
||||||
|
|
||||||
bool ok = CPU::register_interrupt(m_interrupt_line, ::irq_handler, this);
|
write_control(ControlRegister::DeviceControl, 0);
|
||||||
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++)
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,6 +431,8 @@ namespace ATA
|
|||||||
m_dma_prdt_phys = frame.release_value();
|
m_dma_prdt_phys = frame.release_value();
|
||||||
m_dma_prdt = (prdt_entry*)MMU::translate_physical_address(m_dma_prdt_phys);
|
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();
|
frame = MemoryManager::alloc_frame();
|
||||||
if (frame.has_error() || frame.value() > 0xffffffff)
|
if (frame.has_error() || frame.value() > 0xffffffff)
|
||||||
{
|
{
|
||||||
@ -406,6 +442,20 @@ namespace ATA
|
|||||||
m_dma_mem_phys = frame.release_value();
|
m_dma_mem_phys = frame.release_value();
|
||||||
m_dma_mem = (void*)MMU::translate_physical_address(m_dma_mem_phys);
|
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)
|
if (m_is_atapi)
|
||||||
{
|
{
|
||||||
atapi_packet packet;
|
atapi_packet packet;
|
||||||
@ -453,6 +503,14 @@ namespace ATA
|
|||||||
kinfoln("ata: Drive %d capacity information: Block Count=%lu, Block Size=%lu, Total Capacity=%lu",
|
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);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -500,6 +558,123 @@ namespace ATA
|
|||||||
return {};
|
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()
|
void Drive::irq_handler()
|
||||||
{
|
{
|
||||||
// Clear the IRQ flag.
|
// Clear the IRQ flag.
|
||||||
@ -510,5 +685,12 @@ namespace ATA
|
|||||||
u8 error = m_channel->read_register(Register::Error);
|
u8 error = m_channel->read_register(Register::Error);
|
||||||
(void)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
|
BMS_DMAMode = 0x1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum BusMasterCommand : u8
|
||||||
|
{
|
||||||
|
BMC_ReadWrite = 0x8,
|
||||||
|
BMC_StartStop = 0x1,
|
||||||
|
};
|
||||||
|
|
||||||
struct ATAIdentify
|
struct ATAIdentify
|
||||||
{
|
{
|
||||||
u16 flags;
|
u16 flags;
|
||||||
@ -96,6 +102,7 @@ namespace ATA
|
|||||||
enum ATAPICommand : u8
|
enum ATAPICommand : u8
|
||||||
{
|
{
|
||||||
ATAPI_ReadCapacity = 0x25,
|
ATAPI_ReadCapacity = 0x25,
|
||||||
|
ATAPI_Read = 0xa8,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Controller;
|
class Controller;
|
||||||
@ -131,12 +138,22 @@ namespace ATA
|
|||||||
|
|
||||||
bool initialize();
|
bool initialize();
|
||||||
|
|
||||||
|
bool post_initialize();
|
||||||
|
|
||||||
void irq_handler();
|
void irq_handler();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool identify_ata();
|
bool identify_ata();
|
||||||
|
|
||||||
Result<void> send_packet_atapi_pio(const atapi_packet* packet, void* out, u16 response_size);
|
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;
|
Channel* m_channel;
|
||||||
|
|
||||||
@ -153,7 +170,7 @@ namespace ATA
|
|||||||
u64 m_block_count;
|
u64 m_block_count;
|
||||||
u64 m_block_size;
|
u64 m_block_size;
|
||||||
|
|
||||||
volatile prdt_entry* m_dma_prdt;
|
prdt_entry* m_dma_prdt;
|
||||||
u64 m_dma_prdt_phys;
|
u64 m_dma_prdt_phys;
|
||||||
volatile void* m_dma_mem;
|
volatile void* m_dma_mem;
|
||||||
u64 m_dma_mem_phys;
|
u64 m_dma_mem_phys;
|
||||||
@ -191,6 +208,8 @@ namespace ATA
|
|||||||
|
|
||||||
void delay_400ns();
|
void delay_400ns();
|
||||||
|
|
||||||
|
void prepare_for_irq();
|
||||||
|
|
||||||
void wait_for_irq();
|
void wait_for_irq();
|
||||||
bool wait_for_irq_or_timeout(u64 timeout);
|
bool wait_for_irq_or_timeout(u64 timeout);
|
||||||
void irq_handler(Registers*);
|
void irq_handler(Registers*);
|
||||||
@ -208,12 +227,14 @@ namespace ATA
|
|||||||
|
|
||||||
KMutex<100> m_lock {};
|
KMutex<100> m_lock {};
|
||||||
|
|
||||||
Thread* m_thread;
|
Thread* m_thread { nullptr };
|
||||||
|
|
||||||
u16 m_io_base;
|
u16 m_io_base;
|
||||||
u16 m_control_base;
|
u16 m_control_base;
|
||||||
u16 m_busmaster_base;
|
u16 m_busmaster_base;
|
||||||
|
|
||||||
|
bool m_irq_called { false };
|
||||||
|
|
||||||
u8 m_current_drive = (u8)-1;
|
u8 m_current_drive = (u8)-1;
|
||||||
|
|
||||||
SharedPtr<Drive> m_drives[2];
|
SharedPtr<Drive> m_drives[2];
|
||||||
|
Loading…
Reference in New Issue
Block a user