Kernel: Cleanup very old AHCI code
This commit is contained in:
@@ -26,17 +26,17 @@ namespace Kernel
|
|||||||
|
|
||||||
virtual BAN::ErrorOr<void> read_sectors_impl(uint64_t lba, uint64_t sector_count, BAN::ByteSpan) override;
|
virtual BAN::ErrorOr<void> read_sectors_impl(uint64_t lba, uint64_t sector_count, BAN::ByteSpan) override;
|
||||||
virtual BAN::ErrorOr<void> write_sectors_impl(uint64_t lba, uint64_t sector_count, BAN::ConstByteSpan) override;
|
virtual BAN::ErrorOr<void> write_sectors_impl(uint64_t lba, uint64_t sector_count, BAN::ConstByteSpan) override;
|
||||||
BAN::ErrorOr<void> send_command_and_block(uint64_t lba, uint64_t sector_count, Command command);
|
BAN::ErrorOr<void> send_command_and_block(uint64_t lba, uint64_t sector_count, paddr_t paddr, Command command);
|
||||||
|
|
||||||
BAN::Optional<uint32_t> find_free_command_slot();
|
uint32_t find_free_command_slot();
|
||||||
|
|
||||||
void handle_irq();
|
void handle_irq();
|
||||||
|
|
||||||
BAN::ErrorOr<void> block_until_command_completed(uint32_t command_slot);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Mutex m_mutex;
|
Mutex m_mutex;
|
||||||
|
|
||||||
|
uint32_t m_free_slots { 0 };
|
||||||
|
|
||||||
BAN::RefPtr<AHCIController> m_controller;
|
BAN::RefPtr<AHCIController> m_controller;
|
||||||
volatile HBAPortMemorySpace* const m_port;
|
volatile HBAPortMemorySpace* const m_port;
|
||||||
|
|
||||||
@@ -45,6 +45,9 @@ namespace Kernel
|
|||||||
// TODO: can we read straight to user buffer?
|
// TODO: can we read straight to user buffer?
|
||||||
BAN::UniqPtr<DMARegion> m_data_dma_region;
|
BAN::UniqPtr<DMARegion> m_data_dma_region;
|
||||||
|
|
||||||
|
SpinLock m_command_lock;
|
||||||
|
ThreadBlocker m_command_blocker;
|
||||||
|
|
||||||
friend class AHCIController;
|
friend class AHCIController;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,11 @@ namespace Kernel
|
|||||||
TRY(allocate_buffers());
|
TRY(allocate_buffers());
|
||||||
TRY(rebase());
|
TRY(rebase());
|
||||||
|
|
||||||
|
if (const uint32_t command_slots = m_controller->command_slot_count(); command_slots < 32)
|
||||||
|
m_free_slots = (1u << command_slots) - 1;
|
||||||
|
else
|
||||||
|
m_free_slots = -1;
|
||||||
|
|
||||||
// enable interrupts
|
// enable interrupts
|
||||||
m_port->ie = 0xFFFFFFFF;
|
m_port->ie = 0xFFFFFFFF;
|
||||||
|
|
||||||
@@ -51,13 +56,13 @@ namespace Kernel
|
|||||||
|
|
||||||
BAN::ErrorOr<void> AHCIDevice::allocate_buffers()
|
BAN::ErrorOr<void> AHCIDevice::allocate_buffers()
|
||||||
{
|
{
|
||||||
uint32_t command_slot_count = m_controller->command_slot_count();
|
const uint32_t command_slot_count = m_controller->command_slot_count();
|
||||||
size_t needed_bytes = (sizeof(HBACommandHeader) + sizeof(HBACommandTable)) * command_slot_count + sizeof(ReceivedFIS);
|
const size_t needed_bytes = (sizeof(HBACommandHeader) + sizeof(HBACommandTable)) * command_slot_count + sizeof(ReceivedFIS);
|
||||||
|
|
||||||
m_dma_region = TRY(DMARegion::create(needed_bytes));
|
m_dma_region = TRY(DMARegion::create(needed_bytes));
|
||||||
memset((void*)m_dma_region->vaddr(), 0x00, m_dma_region->size());
|
memset((void*)m_dma_region->vaddr(), 0x00, m_dma_region->size());
|
||||||
|
|
||||||
m_data_dma_region = TRY(DMARegion::create(PAGE_SIZE));
|
m_data_dma_region = TRY(DMARegion::create(PAGE_SIZE, PageTable::Normal));
|
||||||
memset((void*)m_data_dma_region->vaddr(), 0x00, m_data_dma_region->size());
|
memset((void*)m_data_dma_region->vaddr(), 0x00, m_data_dma_region->size());
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
@@ -67,25 +72,25 @@ namespace Kernel
|
|||||||
{
|
{
|
||||||
ASSERT(m_dma_region);
|
ASSERT(m_dma_region);
|
||||||
|
|
||||||
uint32_t command_slot_count = m_controller->command_slot_count();
|
const uint32_t command_slot_count = m_controller->command_slot_count();
|
||||||
|
|
||||||
stop_cmd(m_port);
|
stop_cmd(m_port);
|
||||||
|
|
||||||
uint64_t fis_paddr = m_dma_region->paddr();
|
const paddr_t fis_paddr = m_dma_region->paddr();
|
||||||
m_port->fb = fis_paddr & 0xFFFFFFFF;
|
m_port->fb = fis_paddr & 0xFFFFFFFF;
|
||||||
m_port->fbu = fis_paddr >> 32;
|
m_port->fbu = fis_paddr >> 32;
|
||||||
|
|
||||||
uint64_t command_list_paddr = fis_paddr + sizeof(ReceivedFIS);
|
const paddr_t command_list_paddr = fis_paddr + sizeof(ReceivedFIS);
|
||||||
m_port->clb = command_list_paddr & 0xFFFFFFFF;
|
m_port->clb = command_list_paddr & 0xFFFFFFFF;
|
||||||
m_port->clbu = command_list_paddr >> 32;
|
m_port->clbu = command_list_paddr >> 32;
|
||||||
|
|
||||||
auto* command_headers = (HBACommandHeader*)m_dma_region->paddr_to_vaddr(command_list_paddr);
|
auto* command_headers = reinterpret_cast<volatile HBACommandHeader*>(m_dma_region->paddr_to_vaddr(command_list_paddr));
|
||||||
paddr_t command_table_paddr = command_list_paddr + command_slot_count * sizeof(HBACommandHeader);
|
const paddr_t command_table_paddr = command_list_paddr + command_slot_count * sizeof(HBACommandHeader);
|
||||||
for (uint32_t i = 0; i < command_slot_count; i++)
|
for (uint32_t i = 0; i < command_slot_count; i++)
|
||||||
{
|
{
|
||||||
uint64_t command_table_entry_paddr = command_table_paddr + i * sizeof(HBACommandTable);
|
const paddr_t command_table_entry_paddr = command_table_paddr + i * sizeof(HBACommandTable);
|
||||||
command_headers[i].prdtl = s_hba_prdt_count;
|
command_headers[i].prdtl = s_hba_prdt_count;
|
||||||
command_headers[i].ctba = command_table_entry_paddr & 0xFFFFFFFF;
|
command_headers[i].ctba = command_table_entry_paddr & 0xFFFFFFFF;
|
||||||
command_headers[i].ctbau = command_table_entry_paddr >> 32;
|
command_headers[i].ctbau = command_table_entry_paddr >> 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,35 +103,37 @@ namespace Kernel
|
|||||||
{
|
{
|
||||||
ASSERT(m_data_dma_region);
|
ASSERT(m_data_dma_region);
|
||||||
|
|
||||||
m_port->is = ~(uint32_t)0;
|
const auto slot = find_free_command_slot();
|
||||||
|
|
||||||
auto slot = find_free_command_slot();
|
const paddr_t command_header_paddr = (static_cast<paddr_t>(m_port->clbu) << 32) | m_port->clb;
|
||||||
ASSERT(slot.has_value());
|
volatile auto& command_header = reinterpret_cast<volatile HBACommandHeader*>(m_dma_region->paddr_to_vaddr(command_header_paddr))[slot];
|
||||||
|
command_header.cfl = sizeof(FISRegisterH2D) / sizeof(uint32_t);
|
||||||
volatile auto& command_header = reinterpret_cast<volatile HBACommandHeader*>(m_dma_region->paddr_to_vaddr(m_port->clb))[slot.value()];
|
command_header.w = 0;
|
||||||
command_header.cfl = sizeof(FISRegisterH2D) / sizeof(uint32_t);
|
|
||||||
command_header.w = 0;
|
|
||||||
command_header.prdtl = 1;
|
command_header.prdtl = 1;
|
||||||
|
|
||||||
volatile auto& command_table = *reinterpret_cast<volatile HBACommandTable*>(m_dma_region->paddr_to_vaddr(command_header.ctba));
|
const paddr_t command_table_paddr = (static_cast<paddr_t>(command_header.ctbau) << 32) | command_header.ctba;
|
||||||
memset(const_cast<HBACommandTable*>(&command_table), 0x00, sizeof(HBACommandTable));
|
volatile auto& command_table = *reinterpret_cast<volatile HBACommandTable*>(m_dma_region->paddr_to_vaddr(command_table_paddr));
|
||||||
command_table.prdt_entry[0].dba = m_data_dma_region->paddr();
|
command_table.prdt_entry[0].dba = m_data_dma_region->paddr() & 0xFFFFFFFF;
|
||||||
command_table.prdt_entry[0].dbc = 511;
|
command_table.prdt_entry[0].dbau = m_data_dma_region->paddr() >> 32;
|
||||||
command_table.prdt_entry[0].i = 1;
|
command_table.prdt_entry[0].dbc = 511;
|
||||||
|
command_table.prdt_entry[0].i = 1;
|
||||||
|
|
||||||
volatile auto& command = *reinterpret_cast<volatile FISRegisterH2D*>(command_table.cfis);
|
volatile auto& command = *reinterpret_cast<volatile FISRegisterH2D*>(command_table.cfis);
|
||||||
command.fis_type = FIS_TYPE_REGISTER_H2D;
|
command.fis_type = FIS_TYPE_REGISTER_H2D;
|
||||||
command.c = 1;
|
command.c = 1;
|
||||||
command.command = ATA_COMMAND_IDENTIFY;
|
command.command = ATA_COMMAND_IDENTIFY;
|
||||||
|
|
||||||
const uint64_t timeout_ms = SystemTimer::get().ms_since_boot() + s_ata_timeout_ms;
|
SpinLockGuard _(m_command_lock);
|
||||||
while (m_port->tfd & (ATA_STATUS_BSY | ATA_STATUS_DRQ))
|
|
||||||
if (SystemTimer::get().ms_since_boot() >= timeout_ms)
|
|
||||||
return BAN::Error::from_errno(ETIMEDOUT);
|
|
||||||
|
|
||||||
m_port->ci = 1 << slot.value();
|
m_port->ci = 1u << slot;
|
||||||
|
|
||||||
TRY(block_until_command_completed(slot.value()));
|
while (m_port->ci & (1u << slot))
|
||||||
|
{
|
||||||
|
BlockableSpinLock block(m_command_lock);
|
||||||
|
m_command_blocker.block_indefinite(&block);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_free_slots |= 1u << slot;
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -150,49 +157,33 @@ namespace Kernel
|
|||||||
|
|
||||||
void AHCIDevice::handle_irq()
|
void AHCIDevice::handle_irq()
|
||||||
{
|
{
|
||||||
const uint32_t is = m_port->is;
|
while (const uint32_t is = m_port->is)
|
||||||
m_port->is = is;
|
|
||||||
|
|
||||||
const uint32_t serr = m_port->serr;
|
|
||||||
m_port->serr = serr;
|
|
||||||
if (auto err = serr & 0xFFFF)
|
|
||||||
print_error(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
BAN::ErrorOr<void> AHCIDevice::block_until_command_completed(uint32_t command_slot)
|
|
||||||
{
|
|
||||||
static constexpr uint64_t poll_timeout_ms = 10;
|
|
||||||
|
|
||||||
const auto start_time_ms = SystemTimer::get().ms_since_boot();
|
|
||||||
|
|
||||||
while (SystemTimer::get().ms_since_boot() < start_time_ms + poll_timeout_ms)
|
|
||||||
if (!(m_port->ci & (1 << command_slot)))
|
|
||||||
return {};
|
|
||||||
|
|
||||||
// FIXME: This should actually block once ThreadBlocker support blocking with timeout.
|
|
||||||
// This doesn't allow scheduler to go properly idle.
|
|
||||||
while (SystemTimer::get().ms_since_boot() < start_time_ms + s_ata_timeout_ms)
|
|
||||||
{
|
{
|
||||||
Processor::yield();
|
m_port->is = is;
|
||||||
if (!(m_port->ci & (1 << command_slot)))
|
|
||||||
return {};
|
SpinLockGuard _(m_command_lock);
|
||||||
|
m_command_blocker.unblock();
|
||||||
}
|
}
|
||||||
|
|
||||||
return BAN::Error::from_errno(ETIMEDOUT);
|
if (const uint32_t serr = m_port->serr & 0xFFFF)
|
||||||
|
{
|
||||||
|
m_port->serr = serr;
|
||||||
|
print_error(serr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<void> AHCIDevice::read_sectors_impl(uint64_t lba, uint64_t sector_count, BAN::ByteSpan buffer)
|
BAN::ErrorOr<void> AHCIDevice::read_sectors_impl(uint64_t lba, uint64_t sector_count, BAN::ByteSpan buffer)
|
||||||
{
|
{
|
||||||
|
ASSERT(buffer.size() >= sector_count * sector_size());
|
||||||
|
|
||||||
LockGuard _(m_mutex);
|
LockGuard _(m_mutex);
|
||||||
|
|
||||||
ASSERT(buffer.size() >= sector_count * sector_size());
|
const size_t max_sectors = m_data_dma_region->size() / sector_size();
|
||||||
const size_t sectors_per_page = PAGE_SIZE / sector_size();
|
for (uint64_t sector_off = 0; sector_off < sector_count; sector_off += max_sectors)
|
||||||
for (uint64_t sector_off = 0; sector_off < sector_count; sector_off += sectors_per_page)
|
|
||||||
{
|
{
|
||||||
uint64_t to_read = BAN::Math::min<uint64_t>(sector_count - sector_off, sectors_per_page);
|
const uint64_t to_read = BAN::Math::min<uint64_t>(sector_count - sector_off, max_sectors);
|
||||||
|
TRY(send_command_and_block(lba + sector_off, to_read, m_data_dma_region->paddr(), Command::Read));
|
||||||
TRY(send_command_and_block(lba + sector_off, to_read, Command::Read));
|
memcpy(buffer.data() + sector_off * sector_size(), reinterpret_cast<void*>(m_data_dma_region->vaddr()), to_read * sector_size());
|
||||||
memcpy(buffer.data() + sector_off * sector_size(), (void*)m_data_dma_region->vaddr(), to_read * sector_size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
@@ -200,36 +191,31 @@ namespace Kernel
|
|||||||
|
|
||||||
BAN::ErrorOr<void> AHCIDevice::write_sectors_impl(uint64_t lba, uint64_t sector_count, BAN::ConstByteSpan buffer)
|
BAN::ErrorOr<void> AHCIDevice::write_sectors_impl(uint64_t lba, uint64_t sector_count, BAN::ConstByteSpan buffer)
|
||||||
{
|
{
|
||||||
|
ASSERT(buffer.size() >= sector_count * sector_size());
|
||||||
|
|
||||||
LockGuard _(m_mutex);
|
LockGuard _(m_mutex);
|
||||||
|
|
||||||
ASSERT(buffer.size() >= sector_count * sector_size());
|
const size_t max_sectors = m_data_dma_region->size() / sector_size();
|
||||||
const size_t sectors_per_page = PAGE_SIZE / sector_size();
|
for (uint64_t sector_off = 0; sector_off < sector_count; sector_off += max_sectors)
|
||||||
for (uint64_t sector_off = 0; sector_off < sector_count; sector_off += sectors_per_page)
|
|
||||||
{
|
{
|
||||||
uint64_t to_read = BAN::Math::min<uint64_t>(sector_count - sector_off, sectors_per_page);
|
const uint64_t to_write = BAN::Math::min<uint64_t>(sector_count - sector_off, max_sectors);
|
||||||
|
memcpy(reinterpret_cast<void*>(m_data_dma_region->vaddr()), buffer.data() + sector_off * sector_size(), to_write * sector_size());
|
||||||
memcpy((void*)m_data_dma_region->vaddr(), buffer.data() + sector_off * sector_size(), to_read * sector_size());
|
TRY(send_command_and_block(lba + sector_off, to_write, m_data_dma_region->paddr(), Command::Write));
|
||||||
TRY(send_command_and_block(lba + sector_off, to_read, Command::Write));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<void> AHCIDevice::send_command_and_block(uint64_t lba, uint64_t sector_count, Command command)
|
BAN::ErrorOr<void> AHCIDevice::send_command_and_block(uint64_t lba, uint64_t sector_count, paddr_t paddr, Command command)
|
||||||
{
|
{
|
||||||
ASSERT(m_dma_region);
|
ASSERT(m_dma_region);
|
||||||
ASSERT(m_data_dma_region);
|
|
||||||
ASSERT(0 < sector_count && sector_count <= 0xFFFF + 1);
|
ASSERT(0 < sector_count && sector_count <= 0xFFFF + 1);
|
||||||
|
|
||||||
ASSERT(sector_count * sector_size() <= m_data_dma_region->size());
|
const auto slot = find_free_command_slot();
|
||||||
|
|
||||||
m_port->is = ~(uint32_t)0;
|
const paddr_t command_header_paddr = (static_cast<paddr_t>(m_port->clbu) << 32) | m_port->clb;
|
||||||
|
volatile auto& command_header = reinterpret_cast<volatile HBACommandHeader*>(m_dma_region->paddr_to_vaddr(command_header_paddr))[slot];
|
||||||
auto slot = find_free_command_slot();
|
command_header.cfl = sizeof(FISRegisterH2D) / sizeof(uint32_t);
|
||||||
ASSERT(slot.has_value());
|
|
||||||
|
|
||||||
volatile auto& command_header = reinterpret_cast<volatile HBACommandHeader*>(m_dma_region->paddr_to_vaddr(m_port->clb))[slot.value()];
|
|
||||||
command_header.cfl = sizeof(FISRegisterH2D) / sizeof(uint32_t);
|
|
||||||
command_header.prdtl = 1;
|
command_header.prdtl = 1;
|
||||||
switch (command)
|
switch (command)
|
||||||
{
|
{
|
||||||
@@ -243,18 +229,17 @@ namespace Kernel
|
|||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
volatile auto& command_table = *reinterpret_cast<HBACommandTable*>(m_dma_region->paddr_to_vaddr(command_header.ctba));
|
const paddr_t command_table_paddr = (static_cast<paddr_t>(command_header.ctbau) << 32) | command_header.ctba;
|
||||||
memset(const_cast<HBACommandTable*>(&command_table), 0x00, sizeof(HBACommandTable));
|
volatile auto& command_table = *reinterpret_cast<HBACommandTable*>(m_dma_region->paddr_to_vaddr(command_table_paddr));
|
||||||
uint64_t data_dma_paddr64 = m_data_dma_region->paddr();
|
|
||||||
command_table.prdt_entry[0].dba = data_dma_paddr64 & 0xFFFFFFFF;
|
command_table.prdt_entry[0].dba = paddr & 0xFFFFFFFF;
|
||||||
command_table.prdt_entry[0].dbau = data_dma_paddr64 >> 32;
|
command_table.prdt_entry[0].dbau = paddr >> 32;
|
||||||
command_table.prdt_entry[0].dbc = sector_count * sector_size() - 1;
|
command_table.prdt_entry[0].dbc = sector_count * sector_size() - 1;
|
||||||
command_table.prdt_entry[0].i = 1;
|
command_table.prdt_entry[0].i = 1;
|
||||||
|
|
||||||
volatile auto& fis_command = *reinterpret_cast<volatile FISRegisterH2D*>(command_table.cfis);
|
volatile auto& fis_command = *reinterpret_cast<volatile FISRegisterH2D*>(command_table.cfis);
|
||||||
memset(const_cast<FISRegisterH2D*>(&fis_command), 0x00, sizeof(FISRegisterH2D));
|
|
||||||
fis_command.fis_type = FIS_TYPE_REGISTER_H2D;
|
fis_command.fis_type = FIS_TYPE_REGISTER_H2D;
|
||||||
fis_command.c = 1;
|
fis_command.c = 1;
|
||||||
|
|
||||||
const bool needs_extended = lba >= (1 << 24) || sector_count > 0xFF;
|
const bool needs_extended = lba >= (1 << 24) || sector_count > 0xFF;
|
||||||
ASSERT (!needs_extended || (m_command_set & ATA_COMMANDSET_LBA48_SUPPORTED));
|
ASSERT (!needs_extended || (m_command_set & ATA_COMMANDSET_LBA48_SUPPORTED));
|
||||||
@@ -283,23 +268,37 @@ namespace Kernel
|
|||||||
fis_command.count_lo = (sector_count >> 0) & 0xFF;
|
fis_command.count_lo = (sector_count >> 0) & 0xFF;
|
||||||
fis_command.count_hi = (sector_count >> 8) & 0xFF;
|
fis_command.count_hi = (sector_count >> 8) & 0xFF;
|
||||||
|
|
||||||
while (m_port->tfd & (ATA_STATUS_BSY | ATA_STATUS_DRQ))
|
SpinLockGuard _(m_command_lock);
|
||||||
continue;
|
|
||||||
|
|
||||||
m_port->ci = 1 << slot.value();
|
m_port->ci = 1u << slot;
|
||||||
|
|
||||||
TRY(block_until_command_completed(slot.value()));
|
while (m_port->ci & (1u << slot))
|
||||||
|
{
|
||||||
|
BlockableSpinLock block(m_command_lock);
|
||||||
|
m_command_blocker.block_indefinite(&block);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_free_slots |= 1u << slot;
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::Optional<uint32_t> AHCIDevice::find_free_command_slot()
|
uint32_t AHCIDevice::find_free_command_slot()
|
||||||
{
|
{
|
||||||
uint32_t slots = m_port->sact | m_port->ci;
|
SpinLockGuard _(m_command_lock);
|
||||||
for (uint32_t i = 0; i < m_controller->command_slot_count(); i++)
|
|
||||||
if (!(slots & (1 << i)))
|
for (;;)
|
||||||
return i;
|
{
|
||||||
return {};
|
if (const uint32_t usable_slots = ~(m_port->sact | m_port->ci) & m_free_slots)
|
||||||
|
{
|
||||||
|
const uint32_t slot = __builtin_ctz(usable_slots);
|
||||||
|
m_free_slots &= ~(1u << slot);
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockableSpinLock block(m_command_lock);
|
||||||
|
m_command_blocker.block_indefinite(&block);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user