#include "ahci/ahci_driver.h" #include #include #include #include #include namespace { const uint64_t kGhc_InteruptEnable = 0x2; void interrupt_thread(void* void_driver) { AhciDriver* driver = static_cast(void_driver); driver->InterruptLoop(); crash("Driver returned from interrupt loop", glcr::INTERNAL); } } // namespace glcr::ErrorOr> AhciDriver::Init( mmth::OwnedMemoryRegion&& pci_region) { glcr::UniquePtr driver(new AhciDriver(glcr::Move(pci_region))); // RET_ERR(driver->LoadCapabilities()); RET_ERR(driver->LoadHbaRegisters()); RET_ERR(driver->LoadDevices()); RET_ERR(driver->RegisterIrq()); // driver->DumpCapabilities(); // driver->DumpPorts(); return driver; } glcr::ErrorOr AhciDriver::GetDevice(uint64_t id) { if (id >= 32) { return glcr::INVALID_ARGUMENT; } if (devices_[id] != nullptr && !devices_[id]->IsInit()) { return glcr::NOT_FOUND; } return devices_[id]; } void AhciDriver::DumpCapabilities() { dbgln("AHCI Capabilities:"); uint32_t caps = ahci_hba_->capabilities; if (caps & 0x20) { dbgln("External SATA"); } if (caps & 0x40) { dbgln("Enclosure Management"); } if (caps & 0x80) { dbgln("Command Completion Coalescing"); } if (caps & 0x2000) { dbgln("Partial State Capable"); } if (caps & 0x4000) { dbgln("Slumber state capable"); } if (caps & 0x8000) { dbgln("PIO Multiple DRQ Block"); } if (caps & 0x1'0000) { dbgln("FIS-Based Switching"); } if (caps & 0x2'0000) { dbgln("Port Multiplier"); } if (caps & 0x4'0000) { dbgln("AHCI mode only"); } dbgln("Speed support: {}", (caps & 0xF0'0000) >> 20); if (caps & 0x100'0000) { dbgln("Command list override"); } if (caps & 0x200'0000) { dbgln("Activity LED"); } if (caps & 0x400'0000) { dbgln("Aggresive link power management"); } if (caps & 0x800'0000) { dbgln("Staggered spin up"); } if (caps & 0x1000'0000) { dbgln("Mechanical Switch Presence"); } if (caps & 0x2000'0000) { dbgln("SNotification Register"); } if (caps & 0x4000'0000) { dbgln("Native Command Queueing"); } if (caps & 0x8000'0000) { dbgln("64bit Addressing"); } // Secondary. caps = ahci_hba_->capabilities_ext; if (caps & 0x1) { dbgln("BIOS/OS handoff"); } if (caps & 0x2) { dbgln("NVMHCI Present"); } if (caps & 0x4) { dbgln("Auto partial to slumber tranisitions"); } if (caps & 0x8) { dbgln("Device sleep"); } if (caps & 0x10) { dbgln("Aggressive device sleep management"); } dbgln("Control {x}", ahci_hba_->global_host_control); } void AhciDriver::DumpPorts() { for (uint64_t i = 0; i < 6; i++) { AhciDevice* dev = devices_[i]; if (dev == nullptr || !dev->IsInit()) { continue; } dbgln(""); dbgln("Port {}:", i); dev->DumpInfo(); } } void AhciDriver::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() && (ahci_hba_->interrupt_status & (1 << i))) { devices_[i]->HandleIrq(); ahci_hba_->interrupt_status &= ~(1 << i); } } } } glcr::ErrorCode AhciDriver::LoadCapabilities() { if (!(pci_device_header_->status_reg & 0x10)) { dbgln("No caps!"); return glcr::FAILED_PRECONDITION; } uint8_t* base = reinterpret_cast(pci_device_header_); uint16_t offset = pci_device_header_->cap_ptr; do { uint16_t* cap = reinterpret_cast(base + offset); switch (*cap & 0xFF) { case 0x01: dbgln("Power Management"); break; case 0x05: dbgln("MSI"); break; case 0x12: dbgln("SATA"); break; default: dbgln("Unrecognized cap"); break; } offset = (*cap & 0xFF00) >> 8; } while (offset); return glcr::OK; } glcr::ErrorCode AhciDriver::RegisterIrq() { if (pci_device_header_->interrupt_pin == 0) { crash("Can't register IRQ without a pin num", glcr::INVALID_ARGUMENT); } uint64_t irq_num = 0; switch (pci_device_header_->interrupt_pin) { case 1: irq_num = kZIrqPci1; break; case 2: irq_num = kZIrqPci2; break; case 3: irq_num = kZIrqPci3; break; case 4: irq_num = kZIrqPci4; break; } RET_ERR(ZIrqRegister(irq_num, &irq_port_cap_)); irq_thread_ = Thread(interrupt_thread, this); ahci_hba_->global_host_control |= kGhc_InteruptEnable; return glcr::OK; } glcr::ErrorCode AhciDriver::LoadHbaRegisters() { ahci_region_ = mmth::OwnedMemoryRegion ::DirectPhysical( pci_device_header_->abar, 0x1100); ahci_hba_ = reinterpret_cast(ahci_region_.vaddr()); num_ports_ = (ahci_hba_->capabilities & 0x1F) + 1; num_commands_ = ((ahci_hba_->capabilities & 0x1F00) >> 8) + 1; return glcr::OK; } glcr::ErrorCode AhciDriver::LoadDevices() { for (uint8_t i = 0; i < 32; i++) { if (!(ahci_hba_->port_implemented & (1 << i))) { devices_[i] = nullptr; continue; } uint64_t port_addr = reinterpret_cast(ahci_hba_) + 0x100 + (0x80 * i); devices_[i] = new AhciDevice(reinterpret_cast(port_addr)); } return glcr::OK; }