diff --git a/sys/denali/CMakeLists.txt b/sys/denali/CMakeLists.txt index eab0515..ea177f3 100644 --- a/sys/denali/CMakeLists.txt +++ b/sys/denali/CMakeLists.txt @@ -1,6 +1,6 @@ add_executable(denali ahci/ahci_device.cpp - ahci/ahci_driver.cpp + ahci/ahci_controller.cpp ahci/command.cpp denali.cpp denali_server.cpp diff --git a/sys/denali/ahci/ahci.h b/sys/denali/ahci/ahci.h index 5e275a5..9b71c45 100644 --- a/sys/denali/ahci/ahci.h +++ b/sys/denali/ahci/ahci.h @@ -37,6 +37,10 @@ struct PciMsiCap { uint16_t message_data; } __attribute__((packed)); +const uint32_t kGlobalHostControl_HW_Reset = 1; +const uint32_t kGlobalHostControl_AHCI_Enable = (1 << 31); +const uint32_t kGlobalHostControl_Interrupt_Enable = (1 << 1); + struct AhciHba { uint32_t capabilities; uint32_t global_host_control; @@ -51,6 +55,9 @@ struct AhciHba { uint32_t bohc; // 0x28, BIOS/OS handoff control and status } __attribute__((packed)); +const uint32_t kCommand_FIS_Receive_Enable = (1 << 4); +const uint32_t kCommand_Start = 1; + struct AhciPort { uint64_t command_list_base; uint64_t fis_base; diff --git a/sys/denali/ahci/ahci_driver.cpp b/sys/denali/ahci/ahci_controller.cpp similarity index 75% rename from sys/denali/ahci/ahci_driver.cpp rename to sys/denali/ahci/ahci_controller.cpp index 64b340d..0c164e6 100644 --- a/sys/denali/ahci/ahci_driver.cpp +++ b/sys/denali/ahci/ahci_controller.cpp @@ -1,4 +1,4 @@ -#include "ahci/ahci_driver.h" +#include "ahci/ahci_controller.h" #include #include @@ -11,7 +11,7 @@ namespace { const uint64_t kGhc_InteruptEnable = 0x2; void interrupt_thread(void* void_driver) { - AhciDriver* driver = static_cast(void_driver); + AhciController* driver = static_cast(void_driver); driver->InterruptLoop(); @@ -20,31 +20,32 @@ void interrupt_thread(void* void_driver) { } // namespace -glcr::ErrorOr> AhciDriver::Init( +glcr::ErrorOr> AhciController::Init( mmth::OwnedMemoryRegion&& pci_region) { - glcr::UniquePtr driver(new AhciDriver(glcr::Move(pci_region))); - // RET_ERR(driver->LoadCapabilities()); + glcr::UniquePtr driver( + new AhciController(glcr::Move(pci_region))); RET_ERR(driver->LoadHbaRegisters()); - RET_ERR(driver->LoadDevices()); + driver->DumpCapabilities(); + RET_ERR(driver->ResetHba()); RET_ERR(driver->RegisterIrq()); - // driver->DumpCapabilities(); + RET_ERR(driver->LoadDevices()); // driver->DumpPorts(); return driver; } -glcr::ErrorOr AhciDriver::GetDevice(uint64_t id) { +glcr::ErrorOr AhciController::GetDevice(uint64_t id) { if (id >= 32) { return glcr::INVALID_ARGUMENT; } - if (devices_[id] != nullptr && !devices_[id]->IsInit()) { + if (devices_[id].empty()) { return glcr::NOT_FOUND; } - return devices_[id]; + return devices_[id].get(); } -void AhciDriver::DumpCapabilities() { +void AhciController::DumpCapabilities() { dbgln("AHCI Capabilities:"); uint32_t caps = ahci_hba_->capabilities; @@ -122,26 +123,25 @@ void AhciDriver::DumpCapabilities() { dbgln("Control {x}", ahci_hba_->global_host_control); } -void AhciDriver::DumpPorts() { +void AhciController::DumpPorts() { for (uint64_t i = 0; i < 6; i++) { - AhciDevice* dev = devices_[i]; - if (dev == nullptr || !dev->IsInit()) { + if (devices_[i].empty()) { continue; } dbgln(""); dbgln("Port {}:", i); - dev->DumpInfo(); + devices_[i]->DumpInfo(); } } -void AhciDriver::InterruptLoop() { +void AhciController::InterruptLoop() { dbgln("Starting interrupt loop"); while (true) { uint64_t bytes, caps; check(ZPortRecv(irq_port_cap_, &bytes, nullptr, &caps, nullptr)); for (uint64_t i = 0; i < 32; i++) { - if (devices_[i] != nullptr && devices_[i]->IsInit() && + if (!devices_[i].empty() && devices_[i]->IsInit() && (ahci_hba_->interrupt_status & (1 << i))) { devices_[i]->HandleIrq(); ahci_hba_->interrupt_status &= ~(1 << i); @@ -150,7 +150,7 @@ void AhciDriver::InterruptLoop() { } } -glcr::ErrorCode AhciDriver::LoadCapabilities() { +glcr::ErrorCode AhciController::LoadCapabilities() { if (!(pci_device_header_->status_reg & 0x10)) { dbgln("No caps!"); return glcr::FAILED_PRECONDITION; @@ -179,7 +179,7 @@ glcr::ErrorCode AhciDriver::LoadCapabilities() { return glcr::OK; } -glcr::ErrorCode AhciDriver::RegisterIrq() { +glcr::ErrorCode AhciController::RegisterIrq() { if (pci_device_header_->interrupt_pin == 0) { crash("Can't register IRQ without a pin num", glcr::INVALID_ARGUMENT); } @@ -205,7 +205,7 @@ glcr::ErrorCode AhciDriver::RegisterIrq() { return glcr::OK; } -glcr::ErrorCode AhciDriver::LoadHbaRegisters() { +glcr::ErrorCode AhciController::LoadHbaRegisters() { ahci_region_ = mmth::OwnedMemoryRegion ::DirectPhysical( pci_device_header_->abar, 0x1100); ahci_hba_ = reinterpret_cast(ahci_region_.vaddr()); @@ -215,15 +215,34 @@ glcr::ErrorCode AhciDriver::LoadHbaRegisters() { return glcr::OK; } -glcr::ErrorCode AhciDriver::LoadDevices() { - for (uint8_t i = 0; i < 32; i++) { +glcr::ErrorCode AhciController::ResetHba() { + ahci_hba_->global_host_control |= kGlobalHostControl_HW_Reset; + + // TODO: Consider sleeping here. + while (ahci_hba_->global_host_control & kGlobalHostControl_HW_Reset) { + continue; + } + + ahci_hba_->global_host_control |= kGlobalHostControl_AHCI_Enable; + + return static_cast(ZThreadSleep(50)); +} + +glcr::ErrorCode AhciController::LoadDevices() { + for (uint8_t i = 0; i <= num_ports_; i++) { if (!(ahci_hba_->port_implemented & (1 << i))) { - devices_[i] = nullptr; continue; } + uint64_t port_addr = reinterpret_cast(ahci_hba_) + 0x100 + (0x80 * i); + AhciPort* port = reinterpret_cast(port_addr); + if ((port->sata_status & 0x103) != 0x103) { + continue; + } + devices_[i] = new AhciDevice(reinterpret_cast(port_addr)); + devices_[i]->DumpInfo(); } return glcr::OK; } diff --git a/sys/denali/ahci/ahci_driver.h b/sys/denali/ahci/ahci_controller.h similarity index 74% rename from sys/denali/ahci/ahci_driver.h rename to sys/denali/ahci/ahci_controller.h index 0713861..ffb4774 100644 --- a/sys/denali/ahci/ahci_driver.h +++ b/sys/denali/ahci/ahci_controller.h @@ -8,9 +8,9 @@ #include "ahci/ahci.h" #include "ahci/ahci_device.h" -class AhciDriver { +class AhciController { public: - static glcr::ErrorOr> Init( + static glcr::ErrorOr> Init( mmth::OwnedMemoryRegion&& ahci_phys); glcr::ErrorCode RegisterIrq(); @@ -25,22 +25,22 @@ class AhciDriver { mmth::OwnedMemoryRegion pci_region_; PciDeviceHeader* pci_device_header_ = nullptr; mmth::OwnedMemoryRegion ahci_region_; - AhciHba* ahci_hba_ = nullptr; + volatile AhciHba* ahci_hba_ = nullptr; - // TODO: Allocate these dynamically. - AhciDevice* devices_[32]; + glcr::UniquePtr devices_[32]; Thread irq_thread_; uint64_t irq_port_cap_ = 0; - uint64_t num_ports_; - uint64_t num_commands_; + uint8_t num_ports_; + uint8_t num_commands_; glcr::ErrorCode LoadCapabilities(); glcr::ErrorCode LoadHbaRegisters(); + glcr::ErrorCode ResetHba(); glcr::ErrorCode LoadDevices(); - AhciDriver(mmth::OwnedMemoryRegion&& pci_region) + AhciController(mmth::OwnedMemoryRegion&& pci_region) : pci_region_(glcr::Move(pci_region)), pci_device_header_( reinterpret_cast(pci_region_.vaddr())) {} diff --git a/sys/denali/ahci/ahci_device.cpp b/sys/denali/ahci/ahci_device.cpp index 994f4db..b1627c2 100644 --- a/sys/denali/ahci/ahci_device.cpp +++ b/sys/denali/ahci/ahci_device.cpp @@ -6,7 +6,8 @@ AhciDevice::AhciDevice(AhciPort* port) : port_struct_(port) { if ((port_struct_->sata_status & 0x103) != 0x103) { - return; + crash("Creating device on port without a device", + glcr::FAILED_PRECONDITION); } // 0x0-0x400 -> Command List @@ -22,35 +23,46 @@ AhciDevice::AhciDevice(AhciPort* port) : port_struct_(port) { received_fis_ = reinterpret_cast(command_structures_.vaddr() + 0x400); port_struct_->fis_base = paddr + 0x400; + port_struct_->command |= kCommand_FIS_Receive_Enable; command_tables_ = reinterpret_cast(command_structures_.vaddr() + 0x500); for (uint64_t i = 0; i < 32; i++) { + // This leaves space for 2 prdt entries. command_list_->command_headers[i].command_table_base_addr = (paddr + 0x500) + (0x100 * i); + commands_[i] = nullptr; } port_struct_->interrupt_enable = 0xFFFFFFFF; - // Reset the CMD and FRE bits since we move these structures. - // FIXME: I think we need to poll these bits to make sure they become - // 0 before setting them back to one. - port_struct_->command &= ~(0x00000011); - port_struct_->command |= 0x00000011; + port_struct_->sata_error = -1; + port_struct_->command |= kCommand_Start; } glcr::ErrorCode AhciDevice::IssueCommand(Command* command) { - command->PopulateFis(command_tables_->command_fis); - command->PopulatePrdt(command_tables_->prdt); + uint64_t slot; + for (slot = 0; slot < 32; slot++) { + if (commands_[slot] == nullptr) { + break; + } + } + if (slot == 32) { + dbgln("All slots full"); + return glcr::INTERNAL; + } + CommandTable* command_table = command_tables_ + slot; + command->PopulateFis(command_tables_[slot].command_fis); + command->PopulatePrdt(command_tables_[slot].prdt); - command_list_->command_headers[0].command = + command_list_->command_headers[slot].command = (sizeof(HostToDeviceRegisterFis) / 2) & 0x1F; - command_list_->command_headers[0].prd_table_length = 1; - command_list_->command_headers[0].prd_byte_count = 0; + command_list_->command_headers[slot].prd_table_length = 1; + command_list_->command_headers[slot].prd_byte_count = 0; - commands_[0] = command; + commands_[slot] = command; - commands_issued_ |= 1; - port_struct_->command_issue |= 1; + commands_issued_ |= 1 << slot; + port_struct_->command_issue |= 1 << slot; return glcr::OK; } @@ -61,17 +73,9 @@ void AhciDevice::DumpInfo() { dbgln("Command: {x}", port_struct_->command); dbgln("Signature: {x}", port_struct_->signature); dbgln("SATA status: {x}", port_struct_->sata_status); + dbgln("SATA error: {x}", port_struct_->sata_error); dbgln("Int status: {x}", port_struct_->interrupt_status); dbgln("Int enable: {x}", port_struct_->interrupt_enable); - - // Just dump one command info for now. - for (uint64_t i = 0; i < 32; i++) { - dbgln("Command Header: {}", i); - dbgln("Command {x}", command_list_->command_headers[i].command); - dbgln("PRD Len: {x}", command_list_->command_headers[i].prd_table_length); - dbgln("Command Table {x}", - command_list_->command_headers[i].command_table_base_addr); - } } void AhciDevice::HandleIrq() { diff --git a/sys/denali/denali.cpp b/sys/denali/denali.cpp index 4173651..ba25e9a 100644 --- a/sys/denali/denali.cpp +++ b/sys/denali/denali.cpp @@ -4,7 +4,7 @@ #include #include -#include "ahci/ahci_driver.h" +#include "ahci/ahci_controller.h" #include "denali_server.h" using yellowstone::AhciInfo; @@ -19,7 +19,7 @@ uint64_t main(uint64_t init_port_cap) { check(stub.GetAhciInfo(ahci)); mmth::OwnedMemoryRegion ahci_region = mmth::OwnedMemoryRegion::FromCapability(ahci.ahci_region()); - ASSIGN_OR_RETURN(auto driver, AhciDriver::Init(glcr::Move(ahci_region))); + ASSIGN_OR_RETURN(auto driver, AhciController::Init(glcr::Move(ahci_region))); ASSIGN_OR_RETURN(glcr::UniquePtr server, DenaliServer::Create(*driver)); diff --git a/sys/denali/denali_server.cpp b/sys/denali/denali_server.cpp index 0c397c4..0d433a0 100644 --- a/sys/denali/denali_server.cpp +++ b/sys/denali/denali_server.cpp @@ -6,7 +6,7 @@ #include glcr::ErrorOr> DenaliServer::Create( - AhciDriver& driver) { + AhciController& driver) { z_cap_t cap; RET_ERR(ZEndpointCreate(&cap)); return glcr::UniquePtr(new DenaliServer(cap, driver)); diff --git a/sys/denali/denali_server.h b/sys/denali/denali_server.h index 483fabe..4e5b9dc 100644 --- a/sys/denali/denali_server.h +++ b/sys/denali/denali_server.h @@ -2,13 +2,13 @@ #include -#include "ahci/ahci_driver.h" +#include "ahci/ahci_controller.h" #include "lib/denali/denali.yunq.server.h" class DenaliServer : public DenaliServerBase { public: static glcr::ErrorOr> Create( - AhciDriver& driver); + AhciController& driver); glcr::Status HandleRead(const ReadRequest& req, ReadResponse& resp) override; glcr::Status HandleReadMany(const ReadManyRequest& req, @@ -18,8 +18,8 @@ class DenaliServer : public DenaliServerBase { static const uint64_t kBuffSize = 1024; uint8_t read_buffer_[kBuffSize]; - AhciDriver& driver_; + AhciController& driver_; - DenaliServer(z_cap_t endpoint_cap, AhciDriver& driver) + DenaliServer(z_cap_t endpoint_cap, AhciController& driver) : DenaliServerBase(endpoint_cap), driver_(driver) {} };