From 4dc3617f3c1e35a8865fb1d26b2779959e6423fc Mon Sep 17 00:00:00 2001 From: Liav A Date: Sat, 13 Nov 2021 10:19:31 +0200 Subject: Kernel/Storage: Move all ATA related code to a new subdirectory Like what happened with the PCI and USB code, this feels like the right thing to do because we can improve on the ATA capabilities and keep it distinguished from the rest of the subsystem. --- Kernel/Storage/AHCI.h | 437 ----------------- Kernel/Storage/AHCIController.cpp | 204 -------- Kernel/Storage/AHCIController.h | 58 --- Kernel/Storage/AHCIPort.cpp | 834 --------------------------------- Kernel/Storage/AHCIPort.h | 128 ----- Kernel/Storage/AHCIPortHandler.cpp | 101 ---- Kernel/Storage/AHCIPortHandler.h | 72 --- Kernel/Storage/ATA.h | 201 -------- Kernel/Storage/ATA/AHCI.h | 437 +++++++++++++++++ Kernel/Storage/ATA/AHCIController.cpp | 204 ++++++++ Kernel/Storage/ATA/AHCIController.h | 58 +++ Kernel/Storage/ATA/AHCIPort.cpp | 834 +++++++++++++++++++++++++++++++++ Kernel/Storage/ATA/AHCIPort.h | 128 +++++ Kernel/Storage/ATA/AHCIPortHandler.cpp | 101 ++++ Kernel/Storage/ATA/AHCIPortHandler.h | 72 +++ Kernel/Storage/ATA/ATA.h | 201 ++++++++ Kernel/Storage/ATA/ATAController.h | 29 ++ Kernel/Storage/ATA/ATADevice.cpp | 35 ++ Kernel/Storage/ATA/ATADevice.h | 46 ++ Kernel/Storage/ATA/ATADiskDevice.cpp | 45 ++ Kernel/Storage/ATA/ATADiskDevice.h | 35 ++ Kernel/Storage/ATA/ATAPIDiscDevice.cpp | 45 ++ Kernel/Storage/ATA/ATAPIDiscDevice.h | 35 ++ Kernel/Storage/ATA/BMIDEChannel.cpp | 241 ++++++++++ Kernel/Storage/ATA/BMIDEChannel.h | 55 +++ Kernel/Storage/ATA/IDEChannel.cpp | 546 +++++++++++++++++++++ Kernel/Storage/ATA/IDEChannel.h | 160 +++++++ Kernel/Storage/ATA/IDEController.cpp | 207 ++++++++ Kernel/Storage/ATA/IDEController.h | 51 ++ Kernel/Storage/ATAController.h | 29 -- Kernel/Storage/ATADevice.cpp | 35 -- Kernel/Storage/ATADevice.h | 46 -- Kernel/Storage/ATADiskDevice.cpp | 45 -- Kernel/Storage/ATADiskDevice.h | 35 -- Kernel/Storage/ATAPIDiscDevice.cpp | 45 -- Kernel/Storage/ATAPIDiscDevice.h | 35 -- Kernel/Storage/BMIDEChannel.cpp | 241 ---------- Kernel/Storage/BMIDEChannel.h | 55 --- Kernel/Storage/IDEChannel.cpp | 546 --------------------- Kernel/Storage/IDEChannel.h | 160 ------- Kernel/Storage/IDEController.cpp | 207 -------- Kernel/Storage/IDEController.h | 51 -- Kernel/Storage/StorageManagement.cpp | 4 +- 43 files changed, 3567 insertions(+), 3567 deletions(-) delete mode 100644 Kernel/Storage/AHCI.h delete mode 100644 Kernel/Storage/AHCIController.cpp delete mode 100644 Kernel/Storage/AHCIController.h delete mode 100644 Kernel/Storage/AHCIPort.cpp delete mode 100644 Kernel/Storage/AHCIPort.h delete mode 100644 Kernel/Storage/AHCIPortHandler.cpp delete mode 100644 Kernel/Storage/AHCIPortHandler.h delete mode 100644 Kernel/Storage/ATA.h create mode 100644 Kernel/Storage/ATA/AHCI.h create mode 100644 Kernel/Storage/ATA/AHCIController.cpp create mode 100644 Kernel/Storage/ATA/AHCIController.h create mode 100644 Kernel/Storage/ATA/AHCIPort.cpp create mode 100644 Kernel/Storage/ATA/AHCIPort.h create mode 100644 Kernel/Storage/ATA/AHCIPortHandler.cpp create mode 100644 Kernel/Storage/ATA/AHCIPortHandler.h create mode 100644 Kernel/Storage/ATA/ATA.h create mode 100644 Kernel/Storage/ATA/ATAController.h create mode 100644 Kernel/Storage/ATA/ATADevice.cpp create mode 100644 Kernel/Storage/ATA/ATADevice.h create mode 100644 Kernel/Storage/ATA/ATADiskDevice.cpp create mode 100644 Kernel/Storage/ATA/ATADiskDevice.h create mode 100644 Kernel/Storage/ATA/ATAPIDiscDevice.cpp create mode 100644 Kernel/Storage/ATA/ATAPIDiscDevice.h create mode 100644 Kernel/Storage/ATA/BMIDEChannel.cpp create mode 100644 Kernel/Storage/ATA/BMIDEChannel.h create mode 100644 Kernel/Storage/ATA/IDEChannel.cpp create mode 100644 Kernel/Storage/ATA/IDEChannel.h create mode 100644 Kernel/Storage/ATA/IDEController.cpp create mode 100644 Kernel/Storage/ATA/IDEController.h delete mode 100644 Kernel/Storage/ATAController.h delete mode 100644 Kernel/Storage/ATADevice.cpp delete mode 100644 Kernel/Storage/ATADevice.h delete mode 100644 Kernel/Storage/ATADiskDevice.cpp delete mode 100644 Kernel/Storage/ATADiskDevice.h delete mode 100644 Kernel/Storage/ATAPIDiscDevice.cpp delete mode 100644 Kernel/Storage/ATAPIDiscDevice.h delete mode 100644 Kernel/Storage/BMIDEChannel.cpp delete mode 100644 Kernel/Storage/BMIDEChannel.h delete mode 100644 Kernel/Storage/IDEChannel.cpp delete mode 100644 Kernel/Storage/IDEChannel.h delete mode 100644 Kernel/Storage/IDEController.cpp delete mode 100644 Kernel/Storage/IDEController.h (limited to 'Kernel/Storage') diff --git a/Kernel/Storage/AHCI.h b/Kernel/Storage/AHCI.h deleted file mode 100644 index 9502208d3b..0000000000 --- a/Kernel/Storage/AHCI.h +++ /dev/null @@ -1,437 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Kernel::FIS { - -enum class Type : u8 { - RegisterHostToDevice = 0x27, - RegisterDeviceToHost = 0x34, - DMAActivate = 0x39, - DMASetup = 0x41, - Data = 0x46, - BISTActivate = 0x58, - PIOSetup = 0x5F, - SetDeviceBits = 0xA1 -}; - -enum class DwordCount : size_t { - RegisterHostToDevice = 5, - RegisterDeviceToHost = 5, - DMAActivate = 1, - DMASetup = 7, - PIOSetup = 5, - SetDeviceBits = 2 -}; - -enum HeaderAttributes : u8 { - C = (1 << 7), /* Updates Command register */ -}; - -struct [[gnu::packed]] Header { - u8 fis_type; - u8 port_muliplier; -}; - -} - -namespace Kernel::FIS::HostToDevice { - -struct [[gnu::packed]] Register { - Header header; - u8 command; - u8 features_low; - u8 lba_low[3]; - u8 device; - u8 lba_high[3]; - u8 features_high; - u16 count; - u8 icc; /* Isochronous Command Completion */ - u8 control; - u32 reserved; -}; - -}; - -namespace Kernel::FIS::DeviceToHost { - -struct [[gnu::packed]] Register { - Header header; - u8 status; - u8 error; - u8 lba_low[3]; - u8 device; - u8 lba_high[3]; - u8 reserved; - u16 count; - u8 reserved2[6]; -}; - -struct [[gnu::packed]] SetDeviceBits { - Header header; - u8 status; - u8 error; - u32 protocol_specific; -}; - -struct [[gnu::packed]] DMAActivate { - Header header; - u16 reserved; -}; - -struct [[gnu::packed]] PIOSetup { - Header header; - u8 status; - u8 error; - u8 lba_low[3]; - u8 device; - u8 lba_high[3]; - u8 reserved; - u16 count; - u8 reserved2; - u8 e_status; - u16 transfer_count; - u16 reserved3; -}; - -} - -namespace Kernel::FIS::BiDirectional { - -struct [[gnu::packed]] Data { - Header header; - u16 reserved; - u32 data[]; -}; - -struct [[gnu::packed]] BISTActivate { -}; -struct [[gnu::packed]] DMASetup { - Header header; - u16 reserved; - u32 dma_buffer_identifier_low; - u32 dma_buffer_identifier_high; - u32 reserved2; - u32 dma_buffer_offset; - u32 dma_transfer_count; - u32 reserved3; -}; - -} - -namespace Kernel::AHCI { - -class MaskedBitField { - -public: - explicit MaskedBitField(volatile u32& bitfield_register) - : m_bitfield(bitfield_register) - , m_bit_mask(0xffffffff) - { - } - - MaskedBitField(volatile u32& bitfield_register, u32 bit_mask) - : m_bitfield(bitfield_register) - , m_bit_mask(bit_mask) - { - } - - void set_at(u8 index) const - { - VERIFY(((1u << index) & m_bit_mask) != 0); - m_bitfield = m_bitfield | ((1u << index) & m_bit_mask); - } - - void set_all() const - { - m_bitfield = m_bitfield | (0xffffffff & m_bit_mask); - } - - bool is_set_at(u8 port_index) const - { - return m_bitfield & ((1u << port_index) & m_bit_mask); - } - - bool is_zeroed() const - { - return (m_bitfield & m_bit_mask) == 0; - } - - Vector to_vector() const - { - // FIXME: Add a sync mechanism! - Vector indices; - u32 bitfield = m_bitfield & m_bit_mask; - for (size_t index = 0; index < 32; index++) { - if (bitfield & 1) { - indices.append(index); - } - bitfield >>= 1; - } - return indices; - } - - u32 bit_mask() const { return m_bit_mask; }; - - // Disable default implementations that would use surprising integer promotion. - bool operator==(const MaskedBitField&) const = delete; - bool operator<=(const MaskedBitField&) const = delete; - bool operator>=(const MaskedBitField&) const = delete; - bool operator<(const MaskedBitField&) const = delete; - bool operator>(const MaskedBitField&) const = delete; - -private: - volatile u32& m_bitfield; - const u32 m_bit_mask; -}; - -enum Limits : u16 { - MaxPorts = 32, - MaxCommands = 32, - MaxMultiplierConnectedPorts = 16, -}; - -enum CommandHeaderAttributes : u16 { - C = (1 << 10), /* Clear Busy upon R_OK */ - P = (1 << 7), /* Prefetchable */ - W = (1 << 6), /* Write */ - A = (1 << 5), /* ATAPI */ - R = (1 << 8) /* Reset */ -}; - -enum HBACapabilities : u32 { - S64A = (u32)1 << 31, /* Supports 64-bit Addressing */ - SNCQ = 1 << 30, /* Supports Native Command Queuing */ - SSNTF = 1 << 29, /* Supports SNotification Register */ - SMPS = 1 << 28, /* Supports Mechanical Presence Switch */ - SSS = 1 << 27, /* Supports Staggered Spin-up */ - SALP = 1 << 26, /* Supports Aggressive Link Power Management */ - SAL = 1 << 25, /* Supports Activity LED */ - SCLO = 1 << 24, /* Supports Command List Override */ - SAM = 1 << 18, /* Supports AHCI mode only */ - SPM = 1 << 17, /* Supports Port Multiplier */ - FBSS = 1 << 16, /* FIS-based Switching Supported */ - PMD = 1 << 15, /* PIO Multiple DRQ Block */ - SSC = 1 << 14, /* Slumber State Capable */ - PSC = 1 << 13, /* Partial State Capable */ - CCCS = 1 << 7, /* Command Completion Coalescing Supported */ - EMS = 1 << 6, /* Enclosure Management Supported */ - SXS = 1 << 5 /* Supports External SATA */ -}; - -enum HBACapabilitiesExtended : u32 { - DESO = 1 << 5, /* DevSleep Entrance from Slumber Only */ - SADM = 1 << 4, /* Supports Aggressive Device Sleep Management */ - SDS = 1 << 3, /* Supports Device Sleep */ - APST = 1 << 2, /* Automatic Partial to Slumber Transitions */ - NVMP = 1 << 1, /* NVMHCI Present */ - BOH = 1 << 0, /* BIOS/OS Handoff */ -}; - -// This structure is not defined by the AHCI spec, but is used within the code -struct [[gnu::packed]] HBADefinedCapabilities { - size_t ports_count { 1 }; - size_t max_command_list_entries_count { 1 }; - u8 interface_speed_generation { 1 }; - bool external_sata_supported : 1 { false }; - bool enclosure_management_supported : 1 { false }; - bool command_completion_coalescing_supported : 1 { false }; - bool partial_state_capable : 1 { false }; - bool slumber_state_capable : 1 { false }; - bool pio_multiple_drq_block : 1 { false }; - bool fis_based_switching_supported : 1 { false }; - bool port_multiplier_supported : 1 { false }; - bool ahci_mode_only : 1 { true }; - bool command_list_override_supported : 1 { false }; - bool activity_led_supported : 1 { false }; - bool aggressive_link_power_management_supported : 1 { false }; - bool staggered_spin_up_supported : 1 { false }; - bool mechanical_presence_switch_supported : 1 { false }; - bool snotification_register_supported : 1 { false }; - bool native_command_queuing_supported : 1 { false }; - bool addressing_64_bit_supported : 1 { false }; - bool bios_os_handoff : 1 { false }; - bool nvmhci_present : 1 { false }; - bool automatic_partial_to_slumber_transitions : 1 { false }; - bool device_sleep_supported : 1 { false }; - bool aggressive_device_sleep_management_supported : 1 { false }; - bool devsleep_entrance_from_slumber_only : 1 { false }; -}; - -enum DeviceSignature : u32 { - ATA = 0x00000101, - ATAPI = 0xEB140101, - EnclosureManagementBridge = 0xC33C0101, - PortMultiplier = 0x96690101, - Unconnected = 0xFFFFFFFF -}; - -enum class DeviceDetectionInitialization { - NoActionRequested, - PerformInterfaceInitializationSequence, - DisableInterface -}; - -enum PortInterruptFlag : u32 { - CPD = (u32)1 << 31, /* Cold Port Detect */ - TFE = 1 << 30, /* Task File Error */ - HBF = 1 << 29, /* Host Bus Fatal Error */ - HBD = 1 << 28, /* Host Bus Data Error */ - IF = 1 << 27, /* Interface Fatal Error */ - INF = 1 << 26, /* Interface Non-fatal Error */ - OF = 1 << 24, /* Overflow */ - IPM = 1 << 23, /* Incorrect Port Multiplier */ - PRC = 1 << 22, /* PhyRdy Change */ - DMP = 1 << 7, /* Device Mechanical Presence */ - PC = 1 << 6, /* Port Connect Change */ - DP = 1 << 5, /* Descriptor Processed */ - UF = 1 << 4, /* Unknown FIS */ - SDB = 1 << 3, /* Set Device FIS */ - DS = 1 << 2, /* DMA Setup FIS */ - PS = 1 << 1, /* PIO Setup FIS */ - DHR = 1 << 0 /* Device to Host Register FIS */ -}; - -enum SErr : u32 { - DIAG_X = 1 << 26, /* Exchanged */ - DIAG_F = 1 << 25, /* Unknown FIS Type */ - DIAG_T = 1 << 24, /* Transport state transition error */ - DIAG_S = 1 << 23, /* Link sequence error */ - DIAG_H = 1 << 22, /* Handshake error */ - DIAG_C = 1 << 21, /* CRC error */ - DIAG_D = 1 << 20, /* Disparity error */ - DIAG_B = 1 << 19, /* 10B to 8B decode error */ - DIAG_W = 1 << 18, /* Comm Wake */ - DIAG_I = 1 << 17, /* Phy Internal Error */ - DIAG_N = 1 << 16, /* PhyRdy Change */ - ERR_E = 1 << 11, /* Internal error */ - ERR_P = 1 << 10, /* Protocol error */ - ERR_C = 1 << 9, /* Persistent communication or data integrity error */ - ERR_T = 1 << 8, /* Transient data integrity error */ - ERR_M = 1 << 1, /* Received communications error */ - ERR_I = 1 << 0, /* Recovered data integrity error */ -}; - -class PortInterruptStatusBitField { - -public: - explicit PortInterruptStatusBitField(volatile u32& bitfield_register) - : m_bitfield(bitfield_register) - { - } - - u32 raw_value() const { return m_bitfield; } - bool is_set(PortInterruptFlag flag) const { return m_bitfield & (u32)flag; } - void clear() { m_bitfield = 0xffffffff; } - - // Disable default implementations that would use surprising integer promotion. - bool operator==(const MaskedBitField&) const = delete; - bool operator<=(const MaskedBitField&) const = delete; - bool operator>=(const MaskedBitField&) const = delete; - bool operator<(const MaskedBitField&) const = delete; - bool operator>(const MaskedBitField&) const = delete; - -private: - volatile u32& m_bitfield; -}; - -class PortInterruptEnableBitField { - -public: - explicit PortInterruptEnableBitField(volatile u32& bitfield_register) - : m_bitfield(bitfield_register) - { - } - - u32 raw_value() const { return m_bitfield; } - bool is_set(PortInterruptFlag flag) { return m_bitfield & (u32)flag; } - void set_at(PortInterruptFlag flag) { m_bitfield = m_bitfield | static_cast(flag); } - void clear() { m_bitfield = 0; } - bool is_cleared() const { return m_bitfield == 0; } - void set_all() { m_bitfield = 0xffffffff; } - - // Disable default implementations that would use surprising integer promotion. - bool operator==(const MaskedBitField&) const = delete; - bool operator<=(const MaskedBitField&) const = delete; - bool operator>=(const MaskedBitField&) const = delete; - bool operator<(const MaskedBitField&) const = delete; - bool operator>(const MaskedBitField&) const = delete; - -private: - volatile u32& m_bitfield; -}; - -struct [[gnu::packed]] PortRegisters { - u32 clb; /* Port x Command List Base Address */ - u32 clbu; /* Port x Command List Base Address Upper 32-Bits */ - u32 fb; /* Port x FIS Base Address */ - u32 fbu; /* Port x FIS Base Address Upper 32-Bits */ - u32 is; /* Port x Interrupt Status */ - u32 ie; /* Port x Interrupt Enable */ - u32 cmd; /* Port x Command and Status */ - u32 reserved; - u32 tfd; /* Port x Task File Data */ - u32 sig; /* Port x Signature */ - u32 ssts; /* Port x Serial ATA Status (SCR0: SStatus) */ - u32 sctl; /* Port x Serial ATA Control (SCR2: SControl) */ - u32 serr; /* Port x Serial ATA Error (SCR1: SError) */ - u32 sact; /* Port x Serial ATA Active (SCR3: SActive) */ - u32 ci; /* Port x Command Issue */ - u32 sntf; /* Port x Serial ATA Notification (SCR4: SNotification) */ - u32 fbs; /* Port x FIS-based Switching Control */ - u32 devslp; /* Port x Device Sleep */ - u8 reserved2[0x70 - 0x48]; - u8 vs[16]; /* Port x Vendor Specific */ -}; - -struct [[gnu::packed]] GenericHostControl { - u32 cap; /* Host Capabilities */ - u32 ghc; /* Global Host Control */ - u32 is; /* Interrupt Status */ - u32 pi; /* Ports Implemented */ - u32 version; - u32 ccc_ctl; /* Command Completion Coalescing Control */ - u32 ccc_ports; /* Command Completion Coalsecing Ports */ - u32 em_loc; /* Enclosure Management Location */ - u32 em_ctl; /* Enclosure Management Control */ - u32 cap2; /* Host Capabilities Extended */ - u32 bohc; /* BIOS/OS Handoff Control and Status */ -}; - -struct [[gnu::packed]] HBA { - GenericHostControl control_regs; - u8 reserved[52]; - u8 nvmhci[64]; - u8 vendor_specific[96]; - PortRegisters port_regs[32]; -}; - -struct [[gnu::packed]] CommandHeader { - u16 attributes; - u16 prdtl; /* Physical Region Descriptor Table Length */ - u32 prdbc; /* Physical Region Descriptor Byte Count */ - u32 ctba; /* Command Table Descriptor Base Address */ - u32 ctbau; /* Command Table Descriptor Base Address Upper 32-bits */ - u32 reserved[4]; -}; - -struct [[gnu::packed]] PhysicalRegionDescriptor { - u32 base_low; - u32 base_high; - u32 reserved; - u32 byte_count; /* Bit 31 - Interrupt completion, Bit 0 to 21 - Data Byte Count */ -}; - -struct [[gnu::packed]] CommandTable { - u8 command_fis[64]; - u8 atapi_command[32]; - u8 reserved[32]; - PhysicalRegionDescriptor descriptors[]; -}; -} diff --git a/Kernel/Storage/AHCIController.cpp b/Kernel/Storage/AHCIController.cpp deleted file mode 100644 index e57534c37d..0000000000 --- a/Kernel/Storage/AHCIController.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -NonnullRefPtr AHCIController::initialize(PCI::DeviceIdentifier const& pci_device_identifier) -{ - return adopt_ref(*new AHCIController(pci_device_identifier)); -} - -bool AHCIController::reset() -{ - hba().control_regs.ghc = 1; - - dbgln_if(AHCI_DEBUG, "{}: AHCI Controller reset", pci_address()); - - full_memory_barrier(); - size_t retry = 0; - - while (true) { - if (retry > 1000) - return false; - if (!(hba().control_regs.ghc & 1)) - break; - IO::delay(1000); - retry++; - } - // The HBA is locked or hung if we waited more than 1 second! - return true; -} - -bool AHCIController::shutdown() -{ - TODO(); -} - -size_t AHCIController::devices_count() const -{ - size_t count = 0; - for (auto& port_handler : m_handlers) { - port_handler.enumerate_ports([&](const AHCIPort& port) { - if (port.connected_device()) - count++; - }); - } - return count; -} - -void AHCIController::start_request(const ATADevice& device, AsyncBlockDeviceRequest& request) -{ - // FIXME: For now we have one port handler, check all of them... - VERIFY(m_handlers.size() > 0); - auto port = m_handlers[0].port_at_index(device.ata_address().port); - VERIFY(port); - port->start_request(request); -} - -void AHCIController::complete_current_request(AsyncDeviceRequest::RequestResult) -{ - VERIFY_NOT_REACHED(); -} - -volatile AHCI::PortRegisters& AHCIController::port(size_t port_number) const -{ - VERIFY(port_number < (size_t)AHCI::Limits::MaxPorts); - return static_cast(hba().port_regs[port_number]); -} - -volatile AHCI::HBA& AHCIController::hba() const -{ - return static_cast(*(volatile AHCI::HBA*)(m_hba_region->vaddr().as_ptr())); -} - -AHCIController::AHCIController(PCI::DeviceIdentifier const& pci_device_identifier) - : ATAController() - , PCI::Device(pci_device_identifier.address()) - , m_hba_region(default_hba_region()) - , m_capabilities(capabilities()) -{ - initialize_hba(pci_device_identifier); -} - -AHCI::HBADefinedCapabilities AHCIController::capabilities() const -{ - u32 capabilities = hba().control_regs.cap; - u32 extended_capabilities = hba().control_regs.cap2; - - dbgln_if(AHCI_DEBUG, "{}: AHCI Controller Capabilities = {:#08x}, Extended Capabilities = {:#08x}", pci_address(), capabilities, extended_capabilities); - - return (AHCI::HBADefinedCapabilities) { - (capabilities & 0b11111) + 1, - ((capabilities >> 8) & 0b11111) + 1, - (u8)((capabilities >> 20) & 0b1111), - (capabilities & (u32)(AHCI::HBACapabilities::SXS)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::EMS)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::CCCS)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::PSC)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::SSC)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::PMD)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::FBSS)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::SPM)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::SAM)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::SCLO)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::SAL)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::SALP)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::SSS)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::SMPS)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::SSNTF)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::SNCQ)) != 0, - (capabilities & (u32)(AHCI::HBACapabilities::S64A)) != 0, - (extended_capabilities & (u32)(AHCI::HBACapabilitiesExtended::BOH)) != 0, - (extended_capabilities & (u32)(AHCI::HBACapabilitiesExtended::NVMP)) != 0, - (extended_capabilities & (u32)(AHCI::HBACapabilitiesExtended::APST)) != 0, - (extended_capabilities & (u32)(AHCI::HBACapabilitiesExtended::SDS)) != 0, - (extended_capabilities & (u32)(AHCI::HBACapabilitiesExtended::SADM)) != 0, - (extended_capabilities & (u32)(AHCI::HBACapabilitiesExtended::DESO)) != 0 - }; -} - -NonnullOwnPtr AHCIController::default_hba_region() const -{ - return MM.allocate_kernel_region(PhysicalAddress(PCI::get_BAR5(pci_address())).page_base(), Memory::page_round_up(sizeof(AHCI::HBA)), "AHCI HBA", Memory::Region::Access::ReadWrite).release_value(); -} - -AHCIController::~AHCIController() -{ -} - -void AHCIController::initialize_hba(PCI::DeviceIdentifier const& pci_device_identifier) -{ - if (!reset()) { - dmesgln("{}: AHCI controller reset failed", pci_address()); - return; - } - dmesgln("{}: AHCI controller reset", pci_address()); - dbgln("{}: AHCI command list entries count - {}", pci_address(), hba_capabilities().max_command_list_entries_count); - - u32 version = hba().control_regs.version; - dbgln_if(AHCI_DEBUG, "{}: AHCI Controller Version = {:#08x}", pci_address(), version); - - hba().control_regs.ghc = 0x80000000; // Ensure that HBA knows we are AHCI aware. - PCI::enable_interrupt_line(pci_address()); - PCI::enable_bus_mastering(pci_address()); - enable_global_interrupts(); - m_handlers.append(AHCIPortHandler::create(*this, pci_device_identifier.interrupt_line().value(), - AHCI::MaskedBitField((volatile u32&)(hba().control_regs.pi)))); -} - -void AHCIController::disable_global_interrupts() const -{ - hba().control_regs.ghc = hba().control_regs.ghc & 0xfffffffd; -} -void AHCIController::enable_global_interrupts() const -{ - hba().control_regs.ghc = hba().control_regs.ghc | (1 << 1); -} - -RefPtr AHCIController::device_by_port(u32 port_index) const -{ - for (auto& port_handler : m_handlers) { - if (!port_handler.is_responsible_for_port_index(port_index)) - continue; - - auto port = port_handler.port_at_index(port_index); - if (!port) - return nullptr; - return port->connected_device(); - } - return nullptr; -} - -RefPtr AHCIController::device(u32 index) const -{ - NonnullRefPtrVector connected_devices; - u32 pi = hba().control_regs.pi; - u32 bit = __builtin_ffsl(pi); - while (bit) { - dbgln_if(AHCI_DEBUG, "Checking implemented port {}, pi {:b}", bit - 1, pi); - pi &= ~(1u << (bit - 1)); - auto checked_device = device_by_port(bit - 1); - bit = __builtin_ffsl(pi); - if (checked_device.is_null()) - continue; - connected_devices.append(checked_device.release_nonnull()); - } - dbgln_if(AHCI_DEBUG, "Connected device count: {}, Index: {}", connected_devices.size(), index); - if (index >= connected_devices.size()) - return nullptr; - return connected_devices[index]; -} - -} diff --git a/Kernel/Storage/AHCIController.h b/Kernel/Storage/AHCIController.h deleted file mode 100644 index 5a83e58449..0000000000 --- a/Kernel/Storage/AHCIController.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class AsyncBlockDeviceRequest; -class AHCIPortHandler; -class AHCIPort; -class AHCIController final : public ATAController - , public PCI::Device { - friend class AHCIPortHandler; - friend class AHCIPort; - AK_MAKE_ETERNAL -public: - UNMAP_AFTER_INIT static NonnullRefPtr initialize(PCI::DeviceIdentifier const& pci_device_identifier); - virtual ~AHCIController() override; - - virtual RefPtr device(u32 index) const override; - virtual bool reset() override; - virtual bool shutdown() override; - virtual size_t devices_count() const override; - virtual void start_request(const ATADevice&, AsyncBlockDeviceRequest&) override; - virtual void complete_current_request(AsyncDeviceRequest::RequestResult) override; - - const AHCI::HBADefinedCapabilities& hba_capabilities() const { return m_capabilities; }; - -private: - void disable_global_interrupts() const; - void enable_global_interrupts() const; - - UNMAP_AFTER_INIT explicit AHCIController(PCI::DeviceIdentifier const&); - UNMAP_AFTER_INIT void initialize_hba(PCI::DeviceIdentifier const&); - - AHCI::HBADefinedCapabilities capabilities() const; - RefPtr device_by_port(u32 index) const; - - volatile AHCI::PortRegisters& port(size_t port_number) const; - UNMAP_AFTER_INIT NonnullOwnPtr default_hba_region() const; - volatile AHCI::HBA& hba() const; - - NonnullOwnPtr m_hba_region; - AHCI::HBADefinedCapabilities m_capabilities; - NonnullRefPtrVector m_handlers; -}; -} diff --git a/Kernel/Storage/AHCIPort.cpp b/Kernel/Storage/AHCIPort.cpp deleted file mode 100644 index d439cc296a..0000000000 --- a/Kernel/Storage/AHCIPort.cpp +++ /dev/null @@ -1,834 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -// For more information about locking in this code -// please look at Documentation/Kernel/AHCILocking.md - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -NonnullRefPtr AHCIPort::create(const AHCIPortHandler& handler, volatile AHCI::PortRegisters& registers, u32 port_index) -{ - return adopt_ref(*new AHCIPort(handler, registers, port_index)); -} - -AHCIPort::AHCIPort(const AHCIPortHandler& handler, volatile AHCI::PortRegisters& registers, u32 port_index) - : m_port_index(port_index) - , m_port_registers(registers) - , m_parent_handler(handler) - , m_interrupt_status((volatile u32&)m_port_registers.is) - , m_interrupt_enable((volatile u32&)m_port_registers.ie) -{ - if (is_interface_disabled()) { - m_disabled_by_firmware = true; - return; - } - - m_command_list_page = MM.allocate_supervisor_physical_page(); - m_fis_receive_page = MM.allocate_supervisor_physical_page(); - if (m_command_list_page.is_null() || m_fis_receive_page.is_null()) - return; - - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Command list page at {}", representative_port_index(), m_command_list_page->paddr()); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: FIS receive page at {}", representative_port_index(), m_command_list_page->paddr()); - - for (size_t index = 0; index < 1; index++) { - m_dma_buffers.append(MM.allocate_supervisor_physical_page().release_nonnull()); - } - for (size_t index = 0; index < 1; index++) { - m_command_table_pages.append(MM.allocate_supervisor_physical_page().release_nonnull()); - } - - auto region_or_error = MM.allocate_kernel_region(m_command_list_page->paddr(), PAGE_SIZE, "AHCI Port Command List", Memory::Region::Access::ReadWrite, Memory::Region::Cacheable::No); - if (region_or_error.is_error()) - TODO(); - m_command_list_region = region_or_error.release_value(); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Command list region at {}", representative_port_index(), m_command_list_region->vaddr()); -} - -void AHCIPort::clear_sata_error_register() const -{ - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Clearing SATA error register.", representative_port_index()); - m_port_registers.serr = m_port_registers.serr; -} - -void AHCIPort::handle_interrupt() -{ - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Interrupt handled, PxIS {}", representative_port_index(), m_interrupt_status.raw_value()); - if (m_interrupt_status.raw_value() == 0) { - return; - } - if (m_interrupt_status.is_set(AHCI::PortInterruptFlag::PRC) && m_interrupt_status.is_set(AHCI::PortInterruptFlag::PC)) { - clear_sata_error_register(); - if ((m_port_registers.ssts & 0xf) != 3) { - m_connected_device->prepare_for_unplug(); - StorageManagement::the().remove_device(*m_connected_device); - g_io_work->queue([this]() { - m_connected_device->before_removing(); - m_connected_device.clear(); - }); - } else { - g_io_work->queue([this]() { - reset(); - }); - } - return; - } - if (m_interrupt_status.is_set(AHCI::PortInterruptFlag::PRC)) { - clear_sata_error_register(); - m_wait_connect_for_completion = true; - } - if (m_interrupt_status.is_set(AHCI::PortInterruptFlag::INF)) { - // We need to defer the reset, because we can receive interrupts when - // resetting the device. - g_io_work->queue([this]() { - reset(); - }); - return; - } - if (m_interrupt_status.is_set(AHCI::PortInterruptFlag::IF) || m_interrupt_status.is_set(AHCI::PortInterruptFlag::TFE) || m_interrupt_status.is_set(AHCI::PortInterruptFlag::HBD) || m_interrupt_status.is_set(AHCI::PortInterruptFlag::HBF)) { - g_io_work->queue([this]() { - recover_from_fatal_error(); - }); - return; - } - if (m_interrupt_status.is_set(AHCI::PortInterruptFlag::DHR) || m_interrupt_status.is_set(AHCI::PortInterruptFlag::PS)) { - m_wait_for_completion = false; - - // Now schedule reading/writing the buffer as soon as we leave the irq handler. - // This is important so that we can safely access the buffers, which could - // trigger page faults - if (!m_current_request) { - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Request handled, probably identify request", representative_port_index()); - } else { - g_io_work->queue([this]() { - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Request handled", representative_port_index()); - MutexLocker locker(m_lock); - VERIFY(m_current_request); - VERIFY(m_current_scatter_list); - if (!m_connected_device) { - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Request success", representative_port_index()); - complete_current_request(AsyncDeviceRequest::Failure); - return; - } - if (m_current_request->request_type() == AsyncBlockDeviceRequest::Read) { - if (auto result = m_current_request->write_to_buffer(m_current_request->buffer(), m_current_scatter_list->dma_region().as_ptr(), m_connected_device->block_size() * m_current_request->block_count()); result.is_error()) { - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Request failure, memory fault occurred when reading in data.", representative_port_index()); - m_current_scatter_list = nullptr; - complete_current_request(AsyncDeviceRequest::MemoryFault); - return; - } - } - m_current_scatter_list = nullptr; - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Request success", representative_port_index()); - complete_current_request(AsyncDeviceRequest::Success); - }); - } - } - - m_interrupt_status.clear(); -} - -bool AHCIPort::is_interrupts_enabled() const -{ - return !m_interrupt_enable.is_cleared(); -} - -void AHCIPort::recover_from_fatal_error() -{ - MutexLocker locker(m_lock); - SpinlockLocker lock(m_hard_lock); - dmesgln("{}: AHCI Port {} fatal error, shutting down!", m_parent_handler->hba_controller()->pci_address(), representative_port_index()); - dmesgln("{}: AHCI Port {} fatal error, SError {}", m_parent_handler->hba_controller()->pci_address(), representative_port_index(), (u32)m_port_registers.serr); - stop_command_list_processing(); - stop_fis_receiving(); - m_interrupt_enable.clear(); -} - -void AHCIPort::eject() -{ - // FIXME: This operation (meant to be used on optical drives) doesn't work yet when I tested it on real hardware - TODO(); - - VERIFY(m_lock.is_locked()); - VERIFY(is_atapi_attached()); - VERIFY(is_operable()); - clear_sata_error_register(); - - if (!spin_until_ready()) - return; - - auto unused_command_header = try_to_find_unused_command_header(); - VERIFY(unused_command_header.has_value()); - auto* command_list_entries = (volatile AHCI::CommandHeader*)m_command_list_region->vaddr().as_ptr(); - command_list_entries[unused_command_header.value()].ctba = m_command_table_pages[unused_command_header.value()].paddr().get(); - command_list_entries[unused_command_header.value()].ctbau = 0; - command_list_entries[unused_command_header.value()].prdbc = 0; - command_list_entries[unused_command_header.value()].prdtl = 0; - - // Note: we must set the correct Dword count in this register. Real hardware - // AHCI controllers do care about this field! QEMU doesn't care if we don't - // set the correct CFL field in this register, real hardware will set an - // handshake error bit in PxSERR register if CFL is incorrect. - command_list_entries[unused_command_header.value()].attributes = (size_t)FIS::DwordCount::RegisterHostToDevice | AHCI::CommandHeaderAttributes::P | AHCI::CommandHeaderAttributes::C | AHCI::CommandHeaderAttributes::A; - - auto command_table_region = MM.allocate_kernel_region(m_command_table_pages[unused_command_header.value()].paddr().page_base(), Memory::page_round_up(sizeof(AHCI::CommandTable)), "AHCI Command Table", Memory::Region::Access::ReadWrite, Memory::Region::Cacheable::No).release_value(); - auto& command_table = *(volatile AHCI::CommandTable*)command_table_region->vaddr().as_ptr(); - memset(const_cast(command_table.command_fis), 0, 64); - auto& fis = *(volatile FIS::HostToDevice::Register*)command_table.command_fis; - fis.header.fis_type = (u8)FIS::Type::RegisterHostToDevice; - fis.command = ATA_CMD_PACKET; - - full_memory_barrier(); - memset(const_cast(command_table.atapi_command), 0, 32); - - full_memory_barrier(); - command_table.atapi_command[0] = ATAPI_CMD_EJECT; - command_table.atapi_command[1] = 0; - command_table.atapi_command[2] = 0; - command_table.atapi_command[3] = 0; - command_table.atapi_command[4] = 0b10; - command_table.atapi_command[5] = 0; - command_table.atapi_command[6] = 0; - command_table.atapi_command[7] = 0; - command_table.atapi_command[8] = 0; - command_table.atapi_command[9] = 0; - command_table.atapi_command[10] = 0; - command_table.atapi_command[11] = 0; - fis.device = 0; - fis.header.port_muliplier = fis.header.port_muliplier | (u8)FIS::HeaderAttributes::C; - - // The below loop waits until the port is no longer busy before issuing a new command - if (!spin_until_ready()) - return; - - full_memory_barrier(); - mark_command_header_ready_to_process(unused_command_header.value()); - - full_memory_barrier(); - - while (1) { - if (m_port_registers.serr != 0) { - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Eject Drive failed, SError {:#08x}", representative_port_index(), (u32)m_port_registers.serr); - try_disambiguate_sata_error(); - VERIFY_NOT_REACHED(); - } - } - dbgln("AHCI Port {}: Eject Drive", representative_port_index()); - return; -} - -bool AHCIPort::reset() -{ - MutexLocker locker(m_lock); - SpinlockLocker lock(m_hard_lock); - - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Resetting", representative_port_index()); - - if (m_disabled_by_firmware) { - dmesgln("AHCI Port {}: Disabled by firmware ", representative_port_index()); - return false; - } - full_memory_barrier(); - m_interrupt_enable.clear(); - m_interrupt_status.clear(); - full_memory_barrier(); - start_fis_receiving(); - full_memory_barrier(); - clear_sata_error_register(); - full_memory_barrier(); - if (!initiate_sata_reset(lock)) { - return false; - } - return initialize(lock); -} - -bool AHCIPort::initialize_without_reset() -{ - MutexLocker locker(m_lock); - SpinlockLocker lock(m_hard_lock); - dmesgln("AHCI Port {}: {}", representative_port_index(), try_disambiguate_sata_status()); - return initialize(lock); -} - -bool AHCIPort::initialize(SpinlockLocker& main_lock) -{ - VERIFY(m_lock.is_locked()); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Initialization. Signature = {:#08x}", representative_port_index(), static_cast(m_port_registers.sig)); - if (!is_phy_enabled()) { - // Note: If PHY is not enabled, just clear the interrupt status and enable interrupts, in case - // we are going to hotplug a device later. - m_interrupt_status.clear(); - m_interrupt_enable.set_all(); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Bailing initialization, Phy is not enabled.", representative_port_index()); - return false; - } - rebase(); - power_on(); - spin_up(); - clear_sata_error_register(); - start_fis_receiving(); - set_active_state(); - m_interrupt_status.clear(); - m_interrupt_enable.set_all(); - - full_memory_barrier(); - // This actually enables the port... - start_command_list_processing(); - full_memory_barrier(); - - size_t logical_sector_size = 512; - size_t physical_sector_size = 512; - u64 max_addressable_sector = 0; - if (identify_device(main_lock)) { - auto identify_block = Memory::map_typed(m_parent_handler->get_identify_metadata_physical_region(m_port_index)); - // Check if word 106 is valid before using it! - if ((identify_block->physical_sector_size_to_logical_sector_size >> 14) == 1) { - if (identify_block->physical_sector_size_to_logical_sector_size & (1 << 12)) { - VERIFY(identify_block->logical_sector_size != 0); - logical_sector_size = identify_block->logical_sector_size; - } - if (identify_block->physical_sector_size_to_logical_sector_size & (1 << 13)) { - physical_sector_size = logical_sector_size << (identify_block->physical_sector_size_to_logical_sector_size & 0xf); - } - } - // Check if the device supports LBA48 mode - if (identify_block->commands_and_feature_sets_supported[1] & (1 << 10)) { - max_addressable_sector = identify_block->user_addressable_logical_sectors_count; - } else { - max_addressable_sector = identify_block->max_28_bit_addressable_logical_sector; - } - if (is_atapi_attached()) { - m_port_registers.cmd = m_port_registers.cmd | (1 << 24); - } - - dmesgln("AHCI Port {}: Device found, Capacity={}, Bytes per logical sector={}, Bytes per physical sector={}", representative_port_index(), max_addressable_sector * logical_sector_size, logical_sector_size, physical_sector_size); - - // FIXME: We don't support ATAPI devices yet, so for now we don't "create" them - if (!is_atapi_attached()) { - m_connected_device = ATADiskDevice::create(m_parent_handler->hba_controller(), { m_port_index, 0 }, 0, logical_sector_size, max_addressable_sector); - } else { - dbgln("AHCI Port {}: Ignoring ATAPI devices for now as we don't currently support them.", representative_port_index()); - } - } - return true; -} - -const char* AHCIPort::try_disambiguate_sata_status() -{ - switch (m_port_registers.ssts & 0xf) { - case 0: - return "Device not detected, Phy not enabled"; - case 1: - return "Device detected, Phy disabled"; - case 3: - return "Device detected, Phy enabled"; - case 4: - return "interface disabled"; - } - VERIFY_NOT_REACHED(); -} - -void AHCIPort::try_disambiguate_sata_error() -{ - dmesgln("AHCI Port {}: SErr breakdown:", representative_port_index()); - dmesgln("AHCI Port {}: Diagnostics:", representative_port_index()); - - constexpr u32 diagnostics_bitfield = 0xFFFF0000; - if ((m_port_registers.serr & diagnostics_bitfield) > 0) { - if (m_port_registers.serr & AHCI::SErr::DIAG_X) - dmesgln("AHCI Port {}: - Exchanged", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::DIAG_F) - dmesgln("AHCI Port {}: - Unknown FIS Type", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::DIAG_T) - dmesgln("AHCI Port {}: - Transport state transition error", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::DIAG_S) - dmesgln("AHCI Port {}: - Link sequence error", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::DIAG_H) - dmesgln("AHCI Port {}: - Handshake error", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::DIAG_C) - dmesgln("AHCI Port {}: - CRC error", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::DIAG_D) - dmesgln("AHCI Port {}: - Disparity error", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::DIAG_B) - dmesgln("AHCI Port {}: - 10B to 8B decode error", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::DIAG_W) - dmesgln("AHCI Port {}: - Comm Wake", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::DIAG_I) - dmesgln("AHCI Port {}: - Phy Internal Error", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::DIAG_N) - dmesgln("AHCI Port {}: - PhyRdy Change", representative_port_index()); - } else { - dmesgln("AHCI Port {}: - No diagnostic information provided.", representative_port_index()); - } - - dmesgln("AHCI Port {}: Error(s):", representative_port_index()); - - constexpr u32 error_bitfield = 0xFFFF; - if ((m_port_registers.serr & error_bitfield) > 0) { - if (m_port_registers.serr & AHCI::SErr::ERR_E) - dmesgln("AHCI Port {}: - Internal error", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::ERR_P) - dmesgln("AHCI Port {}: - Protocol error", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::ERR_C) - dmesgln("AHCI Port {}: - Persistent communication or data integrity error", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::ERR_T) - dmesgln("AHCI Port {}: - Transient data integrity error", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::ERR_M) - dmesgln("AHCI Port {}: - Recovered communications error", representative_port_index()); - if (m_port_registers.serr & AHCI::SErr::ERR_I) - dmesgln("AHCI Port {}: - Recovered data integrity error", representative_port_index()); - } else { - dmesgln("AHCI Port {}: - No error information provided.", representative_port_index()); - } -} - -void AHCIPort::rebase() -{ - VERIFY(m_lock.is_locked()); - VERIFY(m_hard_lock.is_locked()); - VERIFY(!m_command_list_page.is_null() && !m_fis_receive_page.is_null()); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Rebasing.", representative_port_index()); - full_memory_barrier(); - stop_command_list_processing(); - stop_fis_receiving(); - full_memory_barrier(); - size_t retry = 0; - // Try to wait 1 second for HBA to clear Command List Running and FIS Receive Running - while (retry < 1000) { - if (!(m_port_registers.cmd & (1 << 15)) && !(m_port_registers.cmd & (1 << 14))) - break; - IO::delay(1000); - retry++; - } - full_memory_barrier(); - m_port_registers.clbu = 0; - m_port_registers.clb = m_command_list_page->paddr().get(); - m_port_registers.fbu = 0; - m_port_registers.fb = m_fis_receive_page->paddr().get(); -} - -bool AHCIPort::is_operable() const -{ - // Note: The definition of "operable" is somewhat ambiguous, but we determine it - // by 3 parameters as shown below. - return (!m_command_list_page.is_null()) - && (!m_fis_receive_page.is_null()) - && ((m_port_registers.cmd & (1 << 14)) != 0); -} - -void AHCIPort::set_active_state() const -{ - VERIFY(m_lock.is_locked()); - VERIFY(m_hard_lock.is_locked()); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Switching to active state.", representative_port_index()); - m_port_registers.cmd = (m_port_registers.cmd & 0x0ffffff) | (1 << 28); -} - -void AHCIPort::set_sleep_state() const -{ - VERIFY(m_lock.is_locked()); - VERIFY(m_hard_lock.is_locked()); - m_port_registers.cmd = (m_port_registers.cmd & 0x0ffffff) | (0b1000 << 28); -} - -size_t AHCIPort::calculate_descriptors_count(size_t block_count) const -{ - VERIFY(m_connected_device); - size_t needed_dma_regions_count = Memory::page_round_up((block_count * m_connected_device->block_size())) / PAGE_SIZE; - VERIFY(needed_dma_regions_count <= m_dma_buffers.size()); - return needed_dma_regions_count; -} - -Optional AHCIPort::prepare_and_set_scatter_list(AsyncBlockDeviceRequest& request) -{ - VERIFY(m_lock.is_locked()); - VERIFY(request.block_count() > 0); - - NonnullRefPtrVector allocated_dma_regions; - for (size_t index = 0; index < calculate_descriptors_count(request.block_count()); index++) { - allocated_dma_regions.append(m_dma_buffers.at(index)); - } - - m_current_scatter_list = Memory::ScatterGatherList::try_create(request, allocated_dma_regions.span(), m_connected_device->block_size()); - if (!m_current_scatter_list) - return AsyncDeviceRequest::Failure; - if (request.request_type() == AsyncBlockDeviceRequest::Write) { - if (auto result = request.read_from_buffer(request.buffer(), m_current_scatter_list->dma_region().as_ptr(), m_connected_device->block_size() * request.block_count()); result.is_error()) { - return AsyncDeviceRequest::MemoryFault; - } - } - return {}; -} - -void AHCIPort::start_request(AsyncBlockDeviceRequest& request) -{ - MutexLocker locker(m_lock); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Request start", representative_port_index()); - VERIFY(!m_current_request); - VERIFY(!m_current_scatter_list); - - m_current_request = request; - - auto result = prepare_and_set_scatter_list(request); - if (result.has_value()) { - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Request failure.", representative_port_index()); - locker.unlock(); - complete_current_request(result.value()); - return; - } - - auto success = access_device(request.request_type(), request.block_index(), request.block_count()); - if (!success) { - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Request failure.", representative_port_index()); - locker.unlock(); - complete_current_request(AsyncDeviceRequest::Failure); - return; - } -} - -void AHCIPort::complete_current_request(AsyncDeviceRequest::RequestResult result) -{ - VERIFY(m_current_request); - auto current_request = m_current_request; - m_current_request.clear(); - current_request->complete(result); -} - -bool AHCIPort::spin_until_ready() const -{ - VERIFY(m_lock.is_locked()); - size_t spin = 0; - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Spinning until ready.", representative_port_index()); - while ((m_port_registers.tfd & (ATA_SR_BSY | ATA_SR_DRQ)) && spin <= 100) { - IO::delay(1000); - spin++; - } - if (spin == 100) { - dbgln_if(AHCI_DEBUG, "AHCI Port {}: SPIN exceeded 100 milliseconds threshold", representative_port_index()); - return false; - } - return true; -} - -bool AHCIPort::access_device(AsyncBlockDeviceRequest::RequestType direction, u64 lba, u8 block_count) -{ - VERIFY(m_connected_device); - VERIFY(is_operable()); - VERIFY(m_lock.is_locked()); - VERIFY(m_current_scatter_list); - SpinlockLocker lock(m_hard_lock); - - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Do a {}, lba {}, block count {}", representative_port_index(), direction == AsyncBlockDeviceRequest::RequestType::Write ? "write" : "read", lba, block_count); - if (!spin_until_ready()) - return false; - - auto unused_command_header = try_to_find_unused_command_header(); - VERIFY(unused_command_header.has_value()); - auto* command_list_entries = (volatile AHCI::CommandHeader*)m_command_list_region->vaddr().as_ptr(); - command_list_entries[unused_command_header.value()].ctba = m_command_table_pages[unused_command_header.value()].paddr().get(); - command_list_entries[unused_command_header.value()].ctbau = 0; - command_list_entries[unused_command_header.value()].prdbc = 0; - command_list_entries[unused_command_header.value()].prdtl = m_current_scatter_list->scatters_count(); - - // Note: we must set the correct Dword count in this register. Real hardware - // AHCI controllers do care about this field! QEMU doesn't care if we don't - // set the correct CFL field in this register, real hardware will set an - // handshake error bit in PxSERR register if CFL is incorrect. - command_list_entries[unused_command_header.value()].attributes = (size_t)FIS::DwordCount::RegisterHostToDevice | AHCI::CommandHeaderAttributes::P | (is_atapi_attached() ? AHCI::CommandHeaderAttributes::A : 0) | (direction == AsyncBlockDeviceRequest::RequestType::Write ? AHCI::CommandHeaderAttributes::W : 0); - - dbgln_if(AHCI_DEBUG, "AHCI Port {}: CLE: ctba={:#08x}, ctbau={:#08x}, prdbc={:#08x}, prdtl={:#04x}, attributes={:#04x}", representative_port_index(), (u32)command_list_entries[unused_command_header.value()].ctba, (u32)command_list_entries[unused_command_header.value()].ctbau, (u32)command_list_entries[unused_command_header.value()].prdbc, (u16)command_list_entries[unused_command_header.value()].prdtl, (u16)command_list_entries[unused_command_header.value()].attributes); - - auto command_table_region = MM.allocate_kernel_region(m_command_table_pages[unused_command_header.value()].paddr().page_base(), Memory::page_round_up(sizeof(AHCI::CommandTable)), "AHCI Command Table", Memory::Region::Access::ReadWrite, Memory::Region::Cacheable::No).release_value(); - auto& command_table = *(volatile AHCI::CommandTable*)command_table_region->vaddr().as_ptr(); - - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Allocated command table at {}", representative_port_index(), command_table_region->vaddr()); - - memset(const_cast(command_table.command_fis), 0, 64); - - size_t scatter_entry_index = 0; - size_t data_transfer_count = (block_count * m_connected_device->block_size()); - for (auto scatter_page : m_current_scatter_list->vmobject().physical_pages()) { - VERIFY(data_transfer_count != 0); - VERIFY(scatter_page); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Add a transfer scatter entry @ {}", representative_port_index(), scatter_page->paddr()); - command_table.descriptors[scatter_entry_index].base_high = 0; - command_table.descriptors[scatter_entry_index].base_low = scatter_page->paddr().get(); - if (data_transfer_count <= PAGE_SIZE) { - command_table.descriptors[scatter_entry_index].byte_count = data_transfer_count - 1; - data_transfer_count = 0; - } else { - command_table.descriptors[scatter_entry_index].byte_count = PAGE_SIZE - 1; - data_transfer_count -= PAGE_SIZE; - } - scatter_entry_index++; - } - command_table.descriptors[scatter_entry_index].byte_count = (PAGE_SIZE - 1) | (1 << 31); - - memset(const_cast(command_table.atapi_command), 0, 32); - - auto& fis = *(volatile FIS::HostToDevice::Register*)command_table.command_fis; - fis.header.fis_type = (u8)FIS::Type::RegisterHostToDevice; - if (is_atapi_attached()) { - fis.command = ATA_CMD_PACKET; - TODO(); - } else { - if (direction == AsyncBlockDeviceRequest::RequestType::Write) - fis.command = ATA_CMD_WRITE_DMA_EXT; - else - fis.command = ATA_CMD_READ_DMA_EXT; - } - - full_memory_barrier(); - - fis.device = ATA_USE_LBA_ADDRESSING; - fis.header.port_muliplier = (u8)FIS::HeaderAttributes::C; - - fis.lba_high[0] = (lba >> 24) & 0xff; - fis.lba_high[1] = (lba >> 32) & 0xff; - fis.lba_high[2] = (lba >> 40) & 0xff; - fis.lba_low[0] = lba & 0xff; - fis.lba_low[1] = (lba >> 8) & 0xff; - fis.lba_low[2] = (lba >> 16) & 0xff; - fis.count = (block_count); - - // The below loop waits until the port is no longer busy before issuing a new command - if (!spin_until_ready()) - return false; - - full_memory_barrier(); - mark_command_header_ready_to_process(unused_command_header.value()); - full_memory_barrier(); - - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Do a {}, lba {}, block count {} @ {}, ended", representative_port_index(), direction == AsyncBlockDeviceRequest::RequestType::Write ? "write" : "read", lba, block_count, m_dma_buffers[0].paddr()); - return true; -} - -bool AHCIPort::identify_device(SpinlockLocker& main_lock) -{ - VERIFY(m_lock.is_locked()); - VERIFY(is_operable()); - if (!spin_until_ready()) - return false; - - auto unused_command_header = try_to_find_unused_command_header(); - VERIFY(unused_command_header.has_value()); - auto* command_list_entries = (volatile AHCI::CommandHeader*)m_command_list_region->vaddr().as_ptr(); - command_list_entries[unused_command_header.value()].ctba = m_command_table_pages[unused_command_header.value()].paddr().get(); - command_list_entries[unused_command_header.value()].ctbau = 0; - command_list_entries[unused_command_header.value()].prdbc = 512; - command_list_entries[unused_command_header.value()].prdtl = 1; - - // Note: we must set the correct Dword count in this register. Real hardware AHCI controllers do care about this field! - // QEMU doesn't care if we don't set the correct CFL field in this register, real hardware will set an handshake error bit in PxSERR register. - command_list_entries[unused_command_header.value()].attributes = (size_t)FIS::DwordCount::RegisterHostToDevice | AHCI::CommandHeaderAttributes::P; - - auto command_table_region = MM.allocate_kernel_region(m_command_table_pages[unused_command_header.value()].paddr().page_base(), Memory::page_round_up(sizeof(AHCI::CommandTable)), "AHCI Command Table", Memory::Region::Access::ReadWrite).release_value(); - auto& command_table = *(volatile AHCI::CommandTable*)command_table_region->vaddr().as_ptr(); - memset(const_cast(command_table.command_fis), 0, 64); - command_table.descriptors[0].base_high = 0; - command_table.descriptors[0].base_low = m_parent_handler->get_identify_metadata_physical_region(m_port_index).get(); - command_table.descriptors[0].byte_count = 512 - 1; - auto& fis = *(volatile FIS::HostToDevice::Register*)command_table.command_fis; - fis.header.fis_type = (u8)FIS::Type::RegisterHostToDevice; - fis.command = m_port_registers.sig == AHCI::DeviceSignature::ATAPI ? ATA_CMD_IDENTIFY_PACKET : ATA_CMD_IDENTIFY; - fis.device = 0; - fis.header.port_muliplier = fis.header.port_muliplier | (u8)FIS::HeaderAttributes::C; - - // The below loop waits until the port is no longer busy before issuing a new command - if (!spin_until_ready()) - return false; - - // FIXME: Find a better way to send IDENTIFY DEVICE and getting an interrupt! - { - main_lock.unlock(); - VERIFY_INTERRUPTS_ENABLED(); - full_memory_barrier(); - m_wait_for_completion = true; - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Marking command header at index {} as ready to identify device", representative_port_index(), unused_command_header.value()); - m_port_registers.ci = 1 << unused_command_header.value(); - full_memory_barrier(); - - while (1) { - if (m_port_registers.serr != 0) { - dbgln("AHCI Port {}: Identify failed, SError {:#08x}", representative_port_index(), (u32)m_port_registers.serr); - try_disambiguate_sata_error(); - return false; - } - if (!m_wait_for_completion) - break; - } - main_lock.lock(); - } - - return true; -} - -bool AHCIPort::shutdown() -{ - MutexLocker locker(m_lock); - SpinlockLocker lock(m_hard_lock); - rebase(); - set_interface_state(AHCI::DeviceDetectionInitialization::DisableInterface); - return true; -} - -Optional AHCIPort::try_to_find_unused_command_header() -{ - VERIFY(m_lock.is_locked()); - u32 commands_issued = m_port_registers.ci; - for (size_t index = 0; index < 32; index++) { - if (!(commands_issued & 1)) { - dbgln_if(AHCI_DEBUG, "AHCI Port {}: unused command header at index {}", representative_port_index(), index); - return index; - } - commands_issued >>= 1; - } - return {}; -} - -void AHCIPort::start_command_list_processing() const -{ - VERIFY(m_lock.is_locked()); - VERIFY(m_hard_lock.is_locked()); - VERIFY(is_operable()); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Starting command list processing.", representative_port_index()); - m_port_registers.cmd = m_port_registers.cmd | 1; -} - -void AHCIPort::mark_command_header_ready_to_process(u8 command_header_index) const -{ - VERIFY(m_lock.is_locked()); - VERIFY(m_hard_lock.is_locked()); - VERIFY(is_operable()); - VERIFY(!m_wait_for_completion); - m_wait_for_completion = true; - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Marking command header at index {} as ready to process.", representative_port_index(), command_header_index); - m_port_registers.ci = 1 << command_header_index; -} - -void AHCIPort::stop_command_list_processing() const -{ - VERIFY(m_lock.is_locked()); - VERIFY(m_hard_lock.is_locked()); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Stopping command list processing.", representative_port_index()); - m_port_registers.cmd = m_port_registers.cmd & 0xfffffffe; -} - -void AHCIPort::start_fis_receiving() const -{ - VERIFY(m_lock.is_locked()); - VERIFY(m_hard_lock.is_locked()); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Starting FIS receiving.", representative_port_index()); - m_port_registers.cmd = m_port_registers.cmd | (1 << 4); -} - -void AHCIPort::power_on() const -{ - VERIFY(m_lock.is_locked()); - VERIFY(m_hard_lock.is_locked()); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Power on. Cold presence detection? {}", representative_port_index(), (bool)(m_port_registers.cmd & (1 << 20))); - if (!(m_port_registers.cmd & (1 << 20))) - return; - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Powering on device.", representative_port_index()); - m_port_registers.cmd = m_port_registers.cmd | (1 << 2); -} - -void AHCIPort::spin_up() const -{ - VERIFY(m_lock.is_locked()); - VERIFY(m_hard_lock.is_locked()); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Spin up. Staggered spin up? {}", representative_port_index(), m_parent_handler->hba_capabilities().staggered_spin_up_supported); - if (!m_parent_handler->hba_capabilities().staggered_spin_up_supported) - return; - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Spinning up device.", representative_port_index()); - m_port_registers.cmd = m_port_registers.cmd | (1 << 1); -} - -void AHCIPort::stop_fis_receiving() const -{ - VERIFY(m_lock.is_locked()); - VERIFY(m_hard_lock.is_locked()); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Stopping FIS receiving.", representative_port_index()); - m_port_registers.cmd = m_port_registers.cmd & 0xFFFFFFEF; -} - -bool AHCIPort::initiate_sata_reset(SpinlockLocker& main_lock) -{ - VERIFY(m_lock.is_locked()); - VERIFY(m_hard_lock.is_locked()); - dbgln_if(AHCI_DEBUG, "AHCI Port {}: Initiate SATA reset", representative_port_index()); - stop_command_list_processing(); - full_memory_barrier(); - size_t retry = 0; - // Try to wait 1 second for HBA to clear Command List Running - while (retry < 5000) { - if (!(m_port_registers.cmd & (1 << 15))) - break; - // The AHCI specification says to wait now a 500 milliseconds - IO::delay(100); - retry++; - } - full_memory_barrier(); - spin_up(); - full_memory_barrier(); - set_interface_state(AHCI::DeviceDetectionInitialization::PerformInterfaceInitializationSequence); - // The AHCI specification says to wait now a 1 millisecond - IO::delay(1000); - // FIXME: Find a better way to opt-out temporarily from Scoped locking! - { - main_lock.unlock(); - VERIFY_INTERRUPTS_ENABLED(); - full_memory_barrier(); - set_interface_state(AHCI::DeviceDetectionInitialization::NoActionRequested); - full_memory_barrier(); - if (m_wait_connect_for_completion) { - retry = 0; - while (retry < 100000) { - if (is_phy_enabled()) - break; - - IO::delay(10); - retry++; - } - } - main_lock.lock(); - } - - dmesgln("AHCI Port {}: {}", representative_port_index(), try_disambiguate_sata_status()); - - full_memory_barrier(); - clear_sata_error_register(); - return (m_port_registers.ssts & 0xf) == 3; -} - -void AHCIPort::set_interface_state(AHCI::DeviceDetectionInitialization requested_action) -{ - switch (requested_action) { - case AHCI::DeviceDetectionInitialization::NoActionRequested: - m_port_registers.sctl = (m_port_registers.sctl & 0xfffffff0); - return; - case AHCI::DeviceDetectionInitialization::PerformInterfaceInitializationSequence: - m_port_registers.sctl = (m_port_registers.sctl & 0xfffffff0) | 1; - return; - case AHCI::DeviceDetectionInitialization::DisableInterface: - m_port_registers.sctl = (m_port_registers.sctl & 0xfffffff0) | 4; - return; - } - VERIFY_NOT_REACHED(); -} - -} diff --git a/Kernel/Storage/AHCIPort.h b/Kernel/Storage/AHCIPort.h deleted file mode 100644 index ad866054f0..0000000000 --- a/Kernel/Storage/AHCIPort.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class AsyncBlockDeviceRequest; - -class AHCIPortHandler; -class AHCIPort - : public RefCounted - , public Weakable { - friend class AHCIPortHandler; - friend class AHCIController; - -public: - UNMAP_AFTER_INIT static NonnullRefPtr create(const AHCIPortHandler&, volatile AHCI::PortRegisters&, u32 port_index); - - u32 port_index() const { return m_port_index; } - u32 representative_port_index() const { return port_index() + 1; } - bool is_operable() const; - bool is_hot_pluggable() const; - bool is_atapi_attached() const { return m_port_registers.sig == (u32)AHCI::DeviceSignature::ATAPI; }; - - RefPtr connected_device() const { return m_connected_device; } - - bool reset(); - UNMAP_AFTER_INIT bool initialize_without_reset(); - void handle_interrupt(); - -private: - bool is_phy_enabled() const { return (m_port_registers.ssts & 0xf) == 3; } - bool initialize(SpinlockLocker&); - - UNMAP_AFTER_INIT AHCIPort(const AHCIPortHandler&, volatile AHCI::PortRegisters&, u32 port_index); - - ALWAYS_INLINE void clear_sata_error_register() const; - - void eject(); - - const char* try_disambiguate_sata_status(); - void try_disambiguate_sata_error(); - - bool initiate_sata_reset(SpinlockLocker&); - void rebase(); - void recover_from_fatal_error(); - bool shutdown(); - ALWAYS_INLINE void spin_up() const; - ALWAYS_INLINE void power_on() const; - - void start_request(AsyncBlockDeviceRequest&); - void complete_current_request(AsyncDeviceRequest::RequestResult); - bool access_device(AsyncBlockDeviceRequest::RequestType, u64 lba, u8 block_count); - size_t calculate_descriptors_count(size_t block_count) const; - [[nodiscard]] Optional prepare_and_set_scatter_list(AsyncBlockDeviceRequest& request); - - ALWAYS_INLINE bool is_interrupts_enabled() const; - - bool spin_until_ready() const; - - bool identify_device(SpinlockLocker&); - - ALWAYS_INLINE void start_command_list_processing() const; - ALWAYS_INLINE void mark_command_header_ready_to_process(u8 command_header_index) const; - ALWAYS_INLINE void stop_command_list_processing() const; - - ALWAYS_INLINE void start_fis_receiving() const; - ALWAYS_INLINE void stop_fis_receiving() const; - - ALWAYS_INLINE void set_active_state() const; - ALWAYS_INLINE void set_sleep_state() const; - - void set_interface_state(AHCI::DeviceDetectionInitialization); - - Optional try_to_find_unused_command_header(); - - ALWAYS_INLINE bool is_interface_disabled() const { return (m_port_registers.ssts & 0xf) == 4; }; - - // Data members - - EntropySource m_entropy_source; - RefPtr m_current_request; - Spinlock m_hard_lock; - Mutex m_lock { "AHCIPort" }; - - mutable bool m_wait_for_completion { false }; - bool m_wait_connect_for_completion { false }; - - NonnullRefPtrVector m_dma_buffers; - NonnullRefPtrVector m_command_table_pages; - RefPtr m_command_list_page; - OwnPtr m_command_list_region; - RefPtr m_fis_receive_page; - RefPtr m_connected_device; - - u32 m_port_index; - volatile AHCI::PortRegisters& m_port_registers; - NonnullRefPtr m_parent_handler; - AHCI::PortInterruptStatusBitField m_interrupt_status; - AHCI::PortInterruptEnableBitField m_interrupt_enable; - - RefPtr m_current_scatter_list; - bool m_disabled_by_firmware { false }; -}; -} diff --git a/Kernel/Storage/AHCIPortHandler.cpp b/Kernel/Storage/AHCIPortHandler.cpp deleted file mode 100644 index 1dc74190b9..0000000000 --- a/Kernel/Storage/AHCIPortHandler.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include - -namespace Kernel { - -NonnullRefPtr AHCIPortHandler::create(AHCIController& controller, u8 irq, AHCI::MaskedBitField taken_ports) -{ - return adopt_ref(*new AHCIPortHandler(controller, irq, taken_ports)); -} - -AHCIPortHandler::AHCIPortHandler(AHCIController& controller, u8 irq, AHCI::MaskedBitField taken_ports) - : IRQHandler(irq) - , m_parent_controller(controller) - , m_taken_ports(taken_ports) - , m_pending_ports_interrupts(create_pending_ports_interrupts_bitfield()) -{ - // FIXME: Use the number of taken ports to determine how many pages we should allocate. - for (size_t index = 0; index < (((size_t)AHCI::Limits::MaxPorts * 512) / PAGE_SIZE); index++) { - m_identify_metadata_pages.append(MM.allocate_supervisor_physical_page().release_nonnull()); - } - - dbgln_if(AHCI_DEBUG, "AHCI Port Handler: IRQ {}", irq); - - // Clear pending interrupts, if there are any! - m_pending_ports_interrupts.set_all(); - enable_irq(); - - if (kernel_command_line().ahci_reset_mode() == AHCIResetMode::Aggressive) { - for (auto index : taken_ports.to_vector()) { - auto port = AHCIPort::create(*this, static_cast(controller.hba().port_regs[index]), index); - m_handled_ports.set(index, port); - port->reset(); - } - return; - } - for (auto index : taken_ports.to_vector()) { - auto port = AHCIPort::create(*this, static_cast(controller.hba().port_regs[index]), index); - m_handled_ports.set(index, port); - port->initialize_without_reset(); - } -} - -void AHCIPortHandler::enumerate_ports(Function callback) const -{ - for (auto& port : m_handled_ports) { - callback(*port.value); - } -} - -RefPtr AHCIPortHandler::port_at_index(u32 port_index) const -{ - VERIFY(m_taken_ports.is_set_at(port_index)); - auto it = m_handled_ports.find(port_index); - if (it == m_handled_ports.end()) - return nullptr; - return (*it).value; -} - -PhysicalAddress AHCIPortHandler::get_identify_metadata_physical_region(u32 port_index) const -{ - dbgln_if(AHCI_DEBUG, "AHCI Port Handler: Get identify metadata physical address of port {} - {}", port_index, (port_index * 512) / PAGE_SIZE); - return m_identify_metadata_pages[(port_index * 512) / PAGE_SIZE].paddr().offset((port_index * 512) % PAGE_SIZE); -} - -AHCI::MaskedBitField AHCIPortHandler::create_pending_ports_interrupts_bitfield() const -{ - return AHCI::MaskedBitField((volatile u32&)m_parent_controller->hba().control_regs.is, m_taken_ports.bit_mask()); -} - -AHCI::HBADefinedCapabilities AHCIPortHandler::hba_capabilities() const -{ - return m_parent_controller->hba_capabilities(); -} - -AHCIPortHandler::~AHCIPortHandler() -{ -} - -bool AHCIPortHandler::handle_irq(const RegisterState&) -{ - dbgln_if(AHCI_DEBUG, "AHCI Port Handler: IRQ received"); - if (m_pending_ports_interrupts.is_zeroed()) - return false; - for (auto port_index : m_pending_ports_interrupts.to_vector()) { - auto port = m_handled_ports.get(port_index); - VERIFY(port.has_value()); - dbgln_if(AHCI_DEBUG, "AHCI Port Handler: Handling IRQ for port {}", port_index); - port.value()->handle_interrupt(); - // We do this to clear the pending interrupt after we handled it. - m_pending_ports_interrupts.set_at(port_index); - } - return true; -} - -} diff --git a/Kernel/Storage/AHCIPortHandler.h b/Kernel/Storage/AHCIPortHandler.h deleted file mode 100644 index 1ef4786ede..0000000000 --- a/Kernel/Storage/AHCIPortHandler.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class AsyncBlockDeviceRequest; - -class AHCIController; -class AHCIPort; -class AHCIPortHandler final : public RefCounted - , public IRQHandler { - friend class AHCIController; - -public: - UNMAP_AFTER_INIT static NonnullRefPtr create(AHCIController&, u8 irq, AHCI::MaskedBitField taken_ports); - virtual ~AHCIPortHandler() override; - - RefPtr device_at_port(size_t port_index) const; - virtual StringView purpose() const override { return "SATA Port Handler"sv; } - - AHCI::HBADefinedCapabilities hba_capabilities() const; - NonnullRefPtr hba_controller() const { return m_parent_controller; } - PhysicalAddress get_identify_metadata_physical_region(u32 port_index) const; - - bool is_responsible_for_port_index(u32 port_index) const { return m_taken_ports.is_set_at(port_index); } - -private: - UNMAP_AFTER_INIT AHCIPortHandler(AHCIController&, u8 irq, AHCI::MaskedBitField taken_ports); - - //^ IRQHandler - virtual bool handle_irq(const RegisterState&) override; - - enum class Direction : u8 { - Read, - Write, - }; - - AHCI::MaskedBitField create_pending_ports_interrupts_bitfield() const; - - void start_request(AsyncBlockDeviceRequest&, bool, bool, u16); - void complete_current_request(AsyncDeviceRequest::RequestResult); - - void enumerate_ports(Function callback) const; - RefPtr port_at_index(u32 port_index) const; - - // Data members - HashMap> m_handled_ports; - NonnullRefPtr m_parent_controller; - NonnullRefPtrVector m_identify_metadata_pages; - AHCI::MaskedBitField m_taken_ports; - AHCI::MaskedBitField m_pending_ports_interrupts; -}; -} diff --git a/Kernel/Storage/ATA.h b/Kernel/Storage/ATA.h deleted file mode 100644 index 92ac91f9f1..0000000000 --- a/Kernel/Storage/ATA.h +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#define ATA_SR_BSY 0x80 -#define ATA_SR_DRDY 0x40 -#define ATA_SR_DF 0x20 -#define ATA_SR_DSC 0x10 -#define ATA_SR_DRQ 0x08 -#define ATA_SR_CORR 0x04 -#define ATA_SR_IDX 0x02 -#define ATA_SR_ERR 0x01 - -#define ATA_ER_BBK 0x80 -#define ATA_ER_UNC 0x40 -#define ATA_ER_MC 0x20 -#define ATA_ER_IDNF 0x10 -#define ATA_ER_MCR 0x08 -#define ATA_ER_ABRT 0x04 -#define ATA_ER_TK0NF 0x02 -#define ATA_ER_AMNF 0x01 - -#define ATA_CMD_READ_PIO 0x20 -#define ATA_CMD_READ_PIO_EXT 0x24 -#define ATA_CMD_READ_DMA 0xC8 -#define ATA_CMD_READ_DMA_EXT 0x25 -#define ATA_CMD_WRITE_PIO 0x30 -#define ATA_CMD_WRITE_PIO_EXT 0x34 -#define ATA_CMD_WRITE_DMA 0xCA -#define ATA_CMD_WRITE_DMA_EXT 0x35 -#define ATA_CMD_CACHE_FLUSH 0xE7 -#define ATA_CMD_CACHE_FLUSH_EXT 0xEA -#define ATA_CMD_PACKET 0xA0 -#define ATA_CMD_IDENTIFY_PACKET 0xA1 -#define ATA_CMD_IDENTIFY 0xEC - -#define ATAPI_CMD_READ 0xA8 -#define ATAPI_CMD_EJECT 0x1B - -#define ATA_IDENT_DEVICETYPE 0 -#define ATA_IDENT_CYLINDERS 2 -#define ATA_IDENT_HEADS 6 -#define ATA_IDENT_SECTORS 12 -#define ATA_IDENT_SERIAL 20 -#define ATA_IDENT_MODEL 54 -#define ATA_IDENT_CAPABILITIES 98 -#define ATA_IDENT_FIELDVALID 106 -#define ATA_IDENT_MAX_LBA 120 -#define ATA_IDENT_COMMANDSETS 164 -#define ATA_IDENT_MAX_LBA_EXT 200 - -#define ATA_USE_LBA_ADDRESSING (1 << 6) - -#define IDE_ATA 0x00 -#define IDE_ATAPI 0x01 - -#define ATA_REG_DATA 0x00 -#define ATA_REG_ERROR 0x01 -#define ATA_REG_FEATURES 0x01 -#define ATA_REG_SECCOUNT0 0x02 -#define ATA_REG_LBA0 0x03 -#define ATA_REG_LBA1 0x04 -#define ATA_REG_LBA2 0x05 -#define ATA_REG_HDDEVSEL 0x06 -#define ATA_REG_COMMAND 0x07 -#define ATA_REG_STATUS 0x07 -#define ATA_REG_SECCOUNT1 0x08 -#define ATA_REG_LBA3 0x09 -#define ATA_REG_LBA4 0x0A -#define ATA_REG_LBA5 0x0B -#define ATA_CTL_CONTROL 0x00 -#define ATA_CTL_ALTSTATUS 0x00 -#define ATA_CTL_DEVADDRESS 0x01 - -#define ATA_CAP_LBA 0x200 - -#include - -namespace Kernel { -struct [[gnu::packed]] ATAIdentifyBlock { - u16 general_configuration; - u16 obsolete; - u16 specific_configuration; - - u16 obsolete2; - u16 retired[2]; - u16 obsolete3; - - u16 reserved_for_cfa[2]; - u16 retired2; - u16 serial_number[10]; - - u16 retired3[2]; - u16 obsolete4; - - u16 firmware_revision[4]; - u16 model_number[20]; - - u16 maximum_logical_sectors_per_drq; - u16 trusted_computing_features; - u16 capabilities[2]; - u16 obsolete5[2]; - u16 validity_flags; - u16 obsolete6[5]; - - u16 security_features; - - u32 max_28_bit_addressable_logical_sector; - u16 obsolete7; - u16 dma_modes; - u16 pio_modes; - - u16 minimum_multiword_dma_transfer_cycle; - u16 recommended_multiword_dma_transfer_cycle; - - u16 minimum_multiword_pio_transfer_cycle_without_flow_control; - u16 minimum_multiword_pio_transfer_cycle_with_flow_control; - - u16 additional_supported; - u16 reserved3[5]; - u16 queue_depth; - - u16 serial_ata_capabilities; - u16 serial_ata_additional_capabilities; - u16 serial_ata_features_supported; - u16 serial_ata_features_enabled; - u16 major_version_number; - u16 minor_version_number; - u16 commands_and_feature_sets_supported[3]; - u16 commands_and_feature_sets_supported_or_enabled[3]; - u16 ultra_dma_modes; - - u16 timing_for_security_features[2]; - u16 apm_level; - u16 master_password_id; - - u16 hardware_reset_results; - u16 obsolete8; - - u16 stream_minimum_request_time; - u16 streaming_transfer_time_for_dma; - u16 streaming_access_latency; - u16 streaming_performance_granularity[2]; - - u64 user_addressable_logical_sectors_count; - - u16 streaming_transfer_time_for_pio; - u16 max_512_byte_blocks_per_data_set_management_command; - u16 physical_sector_size_to_logical_sector_size; - u16 inter_seek_delay_for_acoustic_testing; - u16 world_wide_name[4]; - u16 reserved4[4]; - u16 obsolete9; - - u32 logical_sector_size; - - u16 commands_and_feature_sets_supported2; - u16 commands_and_feature_sets_supported_or_enabled2; - - u16 reserved_for_expanded_supported_and_enabled_settings[6]; - u16 obsolete10; - - u16 security_status; - u16 vendor_specific[31]; - u16 reserved_for_cfa2[8]; - u16 device_nominal_form_factor; - u16 data_set_management_command_support; - u16 additional_product_id[4]; - u16 reserved5[2]; - u16 current_media_serial_number[30]; - u16 sct_command_transport; - u16 reserved6[2]; - - u16 logical_sectors_alignment_within_physical_sector; - - u32 write_read_verify_sector_mode_3_count; - u32 write_read_verify_sector_mode_2_count; - - u16 obsolete11[3]; - u16 nominal_media_rotation_rate; - u16 reserved7; - u16 obsolete12; - u16 write_read_verify_feature_set_current_mode; - u16 reserved8; - u16 transport_major_version_number; - u16 transport_minor_version_number; - u16 reserved9[6]; - - u64 extended_user_addressable_logical_sectors_count; - - u16 minimum_512_byte_data_blocks_per_download_microcode_operation; - u16 max_512_byte_data_blocks_per_download_microcode_operation; - - u16 reserved10[19]; - u16 integrity; -}; -}; diff --git a/Kernel/Storage/ATA/AHCI.h b/Kernel/Storage/ATA/AHCI.h new file mode 100644 index 0000000000..9502208d3b --- /dev/null +++ b/Kernel/Storage/ATA/AHCI.h @@ -0,0 +1,437 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Kernel::FIS { + +enum class Type : u8 { + RegisterHostToDevice = 0x27, + RegisterDeviceToHost = 0x34, + DMAActivate = 0x39, + DMASetup = 0x41, + Data = 0x46, + BISTActivate = 0x58, + PIOSetup = 0x5F, + SetDeviceBits = 0xA1 +}; + +enum class DwordCount : size_t { + RegisterHostToDevice = 5, + RegisterDeviceToHost = 5, + DMAActivate = 1, + DMASetup = 7, + PIOSetup = 5, + SetDeviceBits = 2 +}; + +enum HeaderAttributes : u8 { + C = (1 << 7), /* Updates Command register */ +}; + +struct [[gnu::packed]] Header { + u8 fis_type; + u8 port_muliplier; +}; + +} + +namespace Kernel::FIS::HostToDevice { + +struct [[gnu::packed]] Register { + Header header; + u8 command; + u8 features_low; + u8 lba_low[3]; + u8 device; + u8 lba_high[3]; + u8 features_high; + u16 count; + u8 icc; /* Isochronous Command Completion */ + u8 control; + u32 reserved; +}; + +}; + +namespace Kernel::FIS::DeviceToHost { + +struct [[gnu::packed]] Register { + Header header; + u8 status; + u8 error; + u8 lba_low[3]; + u8 device; + u8 lba_high[3]; + u8 reserved; + u16 count; + u8 reserved2[6]; +}; + +struct [[gnu::packed]] SetDeviceBits { + Header header; + u8 status; + u8 error; + u32 protocol_specific; +}; + +struct [[gnu::packed]] DMAActivate { + Header header; + u16 reserved; +}; + +struct [[gnu::packed]] PIOSetup { + Header header; + u8 status; + u8 error; + u8 lba_low[3]; + u8 device; + u8 lba_high[3]; + u8 reserved; + u16 count; + u8 reserved2; + u8 e_status; + u16 transfer_count; + u16 reserved3; +}; + +} + +namespace Kernel::FIS::BiDirectional { + +struct [[gnu::packed]] Data { + Header header; + u16 reserved; + u32 data[]; +}; + +struct [[gnu::packed]] BISTActivate { +}; +struct [[gnu::packed]] DMASetup { + Header header; + u16 reserved; + u32 dma_buffer_identifier_low; + u32 dma_buffer_identifier_high; + u32 reserved2; + u32 dma_buffer_offset; + u32 dma_transfer_count; + u32 reserved3; +}; + +} + +namespace Kernel::AHCI { + +class MaskedBitField { + +public: + explicit MaskedBitField(volatile u32& bitfield_register) + : m_bitfield(bitfield_register) + , m_bit_mask(0xffffffff) + { + } + + MaskedBitField(volatile u32& bitfield_register, u32 bit_mask) + : m_bitfield(bitfield_register) + , m_bit_mask(bit_mask) + { + } + + void set_at(u8 index) const + { + VERIFY(((1u << index) & m_bit_mask) != 0); + m_bitfield = m_bitfield | ((1u << index) & m_bit_mask); + } + + void set_all() const + { + m_bitfield = m_bitfield | (0xffffffff & m_bit_mask); + } + + bool is_set_at(u8 port_index) const + { + return m_bitfield & ((1u << port_index) & m_bit_mask); + } + + bool is_zeroed() const + { + return (m_bitfield & m_bit_mask) == 0; + } + + Vector to_vector() const + { + // FIXME: Add a sync mechanism! + Vector indices; + u32 bitfield = m_bitfield & m_bit_mask; + for (size_t index = 0; index < 32; index++) { + if (bitfield & 1) { + indices.append(index); + } + bitfield >>= 1; + } + return indices; + } + + u32 bit_mask() const { return m_bit_mask; }; + + // Disable default implementations that would use surprising integer promotion. + bool operator==(const MaskedBitField&) const = delete; + bool operator<=(const MaskedBitField&) const = delete; + bool operator>=(const MaskedBitField&) const = delete; + bool operator<(const MaskedBitField&) const = delete; + bool operator>(const MaskedBitField&) const = delete; + +private: + volatile u32& m_bitfield; + const u32 m_bit_mask; +}; + +enum Limits : u16 { + MaxPorts = 32, + MaxCommands = 32, + MaxMultiplierConnectedPorts = 16, +}; + +enum CommandHeaderAttributes : u16 { + C = (1 << 10), /* Clear Busy upon R_OK */ + P = (1 << 7), /* Prefetchable */ + W = (1 << 6), /* Write */ + A = (1 << 5), /* ATAPI */ + R = (1 << 8) /* Reset */ +}; + +enum HBACapabilities : u32 { + S64A = (u32)1 << 31, /* Supports 64-bit Addressing */ + SNCQ = 1 << 30, /* Supports Native Command Queuing */ + SSNTF = 1 << 29, /* Supports SNotification Register */ + SMPS = 1 << 28, /* Supports Mechanical Presence Switch */ + SSS = 1 << 27, /* Supports Staggered Spin-up */ + SALP = 1 << 26, /* Supports Aggressive Link Power Management */ + SAL = 1 << 25, /* Supports Activity LED */ + SCLO = 1 << 24, /* Supports Command List Override */ + SAM = 1 << 18, /* Supports AHCI mode only */ + SPM = 1 << 17, /* Supports Port Multiplier */ + FBSS = 1 << 16, /* FIS-based Switching Supported */ + PMD = 1 << 15, /* PIO Multiple DRQ Block */ + SSC = 1 << 14, /* Slumber State Capable */ + PSC = 1 << 13, /* Partial State Capable */ + CCCS = 1 << 7, /* Command Completion Coalescing Supported */ + EMS = 1 << 6, /* Enclosure Management Supported */ + SXS = 1 << 5 /* Supports External SATA */ +}; + +enum HBACapabilitiesExtended : u32 { + DESO = 1 << 5, /* DevSleep Entrance from Slumber Only */ + SADM = 1 << 4, /* Supports Aggressive Device Sleep Management */ + SDS = 1 << 3, /* Supports Device Sleep */ + APST = 1 << 2, /* Automatic Partial to Slumber Transitions */ + NVMP = 1 << 1, /* NVMHCI Present */ + BOH = 1 << 0, /* BIOS/OS Handoff */ +}; + +// This structure is not defined by the AHCI spec, but is used within the code +struct [[gnu::packed]] HBADefinedCapabilities { + size_t ports_count { 1 }; + size_t max_command_list_entries_count { 1 }; + u8 interface_speed_generation { 1 }; + bool external_sata_supported : 1 { false }; + bool enclosure_management_supported : 1 { false }; + bool command_completion_coalescing_supported : 1 { false }; + bool partial_state_capable : 1 { false }; + bool slumber_state_capable : 1 { false }; + bool pio_multiple_drq_block : 1 { false }; + bool fis_based_switching_supported : 1 { false }; + bool port_multiplier_supported : 1 { false }; + bool ahci_mode_only : 1 { true }; + bool command_list_override_supported : 1 { false }; + bool activity_led_supported : 1 { false }; + bool aggressive_link_power_management_supported : 1 { false }; + bool staggered_spin_up_supported : 1 { false }; + bool mechanical_presence_switch_supported : 1 { false }; + bool snotification_register_supported : 1 { false }; + bool native_command_queuing_supported : 1 { false }; + bool addressing_64_bit_supported : 1 { false }; + bool bios_os_handoff : 1 { false }; + bool nvmhci_present : 1 { false }; + bool automatic_partial_to_slumber_transitions : 1 { false }; + bool device_sleep_supported : 1 { false }; + bool aggressive_device_sleep_management_supported : 1 { false }; + bool devsleep_entrance_from_slumber_only : 1 { false }; +}; + +enum DeviceSignature : u32 { + ATA = 0x00000101, + ATAPI = 0xEB140101, + EnclosureManagementBridge = 0xC33C0101, + PortMultiplier = 0x96690101, + Unconnected = 0xFFFFFFFF +}; + +enum class DeviceDetectionInitialization { + NoActionRequested, + PerformInterfaceInitializationSequence, + DisableInterface +}; + +enum PortInterruptFlag : u32 { + CPD = (u32)1 << 31, /* Cold Port Detect */ + TFE = 1 << 30, /* Task File Error */ + HBF = 1 << 29, /* Host Bus Fatal Error */ + HBD = 1 << 28, /* Host Bus Data Error */ + IF = 1 << 27, /* Interface Fatal Error */ + INF = 1 << 26, /* Interface Non-fatal Error */ + OF = 1 << 24, /* Overflow */ + IPM = 1 << 23, /* Incorrect Port Multiplier */ + PRC = 1 << 22, /* PhyRdy Change */ + DMP = 1 << 7, /* Device Mechanical Presence */ + PC = 1 << 6, /* Port Connect Change */ + DP = 1 << 5, /* Descriptor Processed */ + UF = 1 << 4, /* Unknown FIS */ + SDB = 1 << 3, /* Set Device FIS */ + DS = 1 << 2, /* DMA Setup FIS */ + PS = 1 << 1, /* PIO Setup FIS */ + DHR = 1 << 0 /* Device to Host Register FIS */ +}; + +enum SErr : u32 { + DIAG_X = 1 << 26, /* Exchanged */ + DIAG_F = 1 << 25, /* Unknown FIS Type */ + DIAG_T = 1 << 24, /* Transport state transition error */ + DIAG_S = 1 << 23, /* Link sequence error */ + DIAG_H = 1 << 22, /* Handshake error */ + DIAG_C = 1 << 21, /* CRC error */ + DIAG_D = 1 << 20, /* Disparity error */ + DIAG_B = 1 << 19, /* 10B to 8B decode error */ + DIAG_W = 1 << 18, /* Comm Wake */ + DIAG_I = 1 << 17, /* Phy Internal Error */ + DIAG_N = 1 << 16, /* PhyRdy Change */ + ERR_E = 1 << 11, /* Internal error */ + ERR_P = 1 << 10, /* Protocol error */ + ERR_C = 1 << 9, /* Persistent communication or data integrity error */ + ERR_T = 1 << 8, /* Transient data integrity error */ + ERR_M = 1 << 1, /* Received communications error */ + ERR_I = 1 << 0, /* Recovered data integrity error */ +}; + +class PortInterruptStatusBitField { + +public: + explicit PortInterruptStatusBitField(volatile u32& bitfield_register) + : m_bitfield(bitfield_register) + { + } + + u32 raw_value() const { return m_bitfield; } + bool is_set(PortInterruptFlag flag) const { return m_bitfield & (u32)flag; } + void clear() { m_bitfield = 0xffffffff; } + + // Disable default implementations that would use surprising integer promotion. + bool operator==(const MaskedBitField&) const = delete; + bool operator<=(const MaskedBitField&) const = delete; + bool operator>=(const MaskedBitField&) const = delete; + bool operator<(const MaskedBitField&) const = delete; + bool operator>(const MaskedBitField&) const = delete; + +private: + volatile u32& m_bitfield; +}; + +class PortInterruptEnableBitField { + +public: + explicit PortInterruptEnableBitField(volatile u32& bitfield_register) + : m_bitfield(bitfield_register) + { + } + + u32 raw_value() const { return m_bitfield; } + bool is_set(PortInterruptFlag flag) { return m_bitfield & (u32)flag; } + void set_at(PortInterruptFlag flag) { m_bitfield = m_bitfield | static_cast(flag); } + void clear() { m_bitfield = 0; } + bool is_cleared() const { return m_bitfield == 0; } + void set_all() { m_bitfield = 0xffffffff; } + + // Disable default implementations that would use surprising integer promotion. + bool operator==(const MaskedBitField&) const = delete; + bool operator<=(const MaskedBitField&) const = delete; + bool operator>=(const MaskedBitField&) const = delete; + bool operator<(const MaskedBitField&) const = delete; + bool operator>(const MaskedBitField&) const = delete; + +private: + volatile u32& m_bitfield; +}; + +struct [[gnu::packed]] PortRegisters { + u32 clb; /* Port x Command List Base Address */ + u32 clbu; /* Port x Command List Base Address Upper 32-Bits */ + u32 fb; /* Port x FIS Base Address */ + u32 fbu; /* Port x FIS Base Address Upper 32-Bits */ + u32 is; /* Port x Interrupt Status */ + u32 ie; /* Port x Interrupt Enable */ + u32 cmd; /* Port x Command and Status */ + u32 reserved; + u32 tfd; /* Port x Task File Data */ + u32 sig; /* Port x Signature */ + u32 ssts; /* Port x Serial ATA Status (SCR0: SStatus) */ + u32 sctl; /* Port x Serial ATA Control (SCR2: SControl) */ + u32 serr; /* Port x Serial ATA Error (SCR1: SError) */ + u32 sact; /* Port x Serial ATA Active (SCR3: SActive) */ + u32 ci; /* Port x Command Issue */ + u32 sntf; /* Port x Serial ATA Notification (SCR4: SNotification) */ + u32 fbs; /* Port x FIS-based Switching Control */ + u32 devslp; /* Port x Device Sleep */ + u8 reserved2[0x70 - 0x48]; + u8 vs[16]; /* Port x Vendor Specific */ +}; + +struct [[gnu::packed]] GenericHostControl { + u32 cap; /* Host Capabilities */ + u32 ghc; /* Global Host Control */ + u32 is; /* Interrupt Status */ + u32 pi; /* Ports Implemented */ + u32 version; + u32 ccc_ctl; /* Command Completion Coalescing Control */ + u32 ccc_ports; /* Command Completion Coalsecing Ports */ + u32 em_loc; /* Enclosure Management Location */ + u32 em_ctl; /* Enclosure Management Control */ + u32 cap2; /* Host Capabilities Extended */ + u32 bohc; /* BIOS/OS Handoff Control and Status */ +}; + +struct [[gnu::packed]] HBA { + GenericHostControl control_regs; + u8 reserved[52]; + u8 nvmhci[64]; + u8 vendor_specific[96]; + PortRegisters port_regs[32]; +}; + +struct [[gnu::packed]] CommandHeader { + u16 attributes; + u16 prdtl; /* Physical Region Descriptor Table Length */ + u32 prdbc; /* Physical Region Descriptor Byte Count */ + u32 ctba; /* Command Table Descriptor Base Address */ + u32 ctbau; /* Command Table Descriptor Base Address Upper 32-bits */ + u32 reserved[4]; +}; + +struct [[gnu::packed]] PhysicalRegionDescriptor { + u32 base_low; + u32 base_high; + u32 reserved; + u32 byte_count; /* Bit 31 - Interrupt completion, Bit 0 to 21 - Data Byte Count */ +}; + +struct [[gnu::packed]] CommandTable { + u8 command_fis[64]; + u8 atapi_command[32]; + u8 reserved[32]; + PhysicalRegionDescriptor descriptors[]; +}; +} diff --git a/Kernel/Storage/ATA/AHCIController.cpp b/Kernel/Storage/ATA/AHCIController.cpp new file mode 100644 index 0000000000..1524fbfa1c --- /dev/null +++ b/Kernel/Storage/ATA/AHCIController.cpp @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +NonnullRefPtr AHCIController::initialize(PCI::DeviceIdentifier const& pci_device_identifier) +{ + return adopt_ref(*new AHCIController(pci_device_identifier)); +} + +bool AHCIController::reset() +{ + hba().control_regs.ghc = 1; + + dbgln_if(AHCI_DEBUG, "{}: AHCI Controller reset", pci_address()); + + full_memory_barrier(); + size_t retry = 0; + + while (true) { + if (retry > 1000) + return false; + if (!(hba().control_regs.ghc & 1)) + break; + IO::delay(1000); + retry++; + } + // The HBA is locked or hung if we waited more than 1 second! + return true; +} + +bool AHCIController::shutdown() +{ + TODO(); +} + +size_t AHCIController::devices_count() const +{ + size_t count = 0; + for (auto& port_handler : m_handlers) { + port_handler.enumerate_ports([&](const AHCIPort& port) { + if (port.connected_device()) + count++; + }); + } + return count; +} + +void AHCIController::start_request(const ATADevice& device, AsyncBlockDeviceRequest& request) +{ + // FIXME: For now we have one port handler, check all of them... + VERIFY(m_handlers.size() > 0); + auto port = m_handlers[0].port_at_index(device.ata_address().port); + VERIFY(port); + port->start_request(request); +} + +void AHCIController::complete_current_request(AsyncDeviceRequest::RequestResult) +{ + VERIFY_NOT_REACHED(); +} + +volatile AHCI::PortRegisters& AHCIController::port(size_t port_number) const +{ + VERIFY(port_number < (size_t)AHCI::Limits::MaxPorts); + return static_cast(hba().port_regs[port_number]); +} + +volatile AHCI::HBA& AHCIController::hba() const +{ + return static_cast(*(volatile AHCI::HBA*)(m_hba_region->vaddr().as_ptr())); +} + +AHCIController::AHCIController(PCI::DeviceIdentifier const& pci_device_identifier) + : ATAController() + , PCI::Device(pci_device_identifier.address()) + , m_hba_region(default_hba_region()) + , m_capabilities(capabilities()) +{ + initialize_hba(pci_device_identifier); +} + +AHCI::HBADefinedCapabilities AHCIController::capabilities() const +{ + u32 capabilities = hba().control_regs.cap; + u32 extended_capabilities = hba().control_regs.cap2; + + dbgln_if(AHCI_DEBUG, "{}: AHCI Controller Capabilities = {:#08x}, Extended Capabilities = {:#08x}", pci_address(), capabilities, extended_capabilities); + + return (AHCI::HBADefinedCapabilities) { + (capabilities & 0b11111) + 1, + ((capabilities >> 8) & 0b11111) + 1, + (u8)((capabilities >> 20) & 0b1111), + (capabilities & (u32)(AHCI::HBACapabilities::SXS)) != 0, + (capabilities & (u32)(AHCI::HBACapabilities::EMS)) != 0, + (capabilities & (u32)(AHCI::HBACapabilities::CCCS)) != 0, + (capabilities & (u32)(AHCI::HBACapabilities::PSC)) != 0, + (capabilities & (u32)(AHCI::HBACapabilities::SSC)) != 0, + (capabilities & (u32)(AHCI::HBACapabilities::PMD)) != 0, + (capabilities & (u32)(AHCI::HBACapabilities::FBSS)) != 0, + (capabilities & (u32)(AHCI::HBACapabilities::SPM)) != 0, + (capabilities & (u32)(AHCI::HBACapabilities::SAM)) != 0, + (capabilities & (u32)(AHCI::HBACapabilities::SCLO)) != 0, + (capabilities & (u32)(AHCI::HBACapabilities::SAL)) != 0, + (capabilities & (u32)(AHCI::HBACapabilities::SALP)) != 0, + (capabilities & (u32)(AHCI::HBACapabilities::SSS)) != 0, + (capabilities & (u32)(AHCI::HBACapabilities::SMPS)) != 0, + (capabilities & (u32)(AHCI::HBACapabilities::SSNTF)) != 0, + (capabilities & (u32)(AHCI::HBACapabilities::SNCQ)) != 0, + (capabilities & (u32)(AHCI::HBACapabilities::S64A)) != 0, + (extended_capabilities & (u32)(AHCI::HBACapabilitiesExtended::BOH)) != 0, + (extended_capabilities & (u32)(AHCI::HBACapabilitiesExtended::NVMP)) != 0, + (extended_capabilities & (u32)(AHCI::HBACapabilitiesExtended::APST)) != 0, + (extended_capabilities & (u32)(AHCI::HBACapabilitiesExtended::SDS)) != 0, + (extended_capabilities & (u32)(AHCI::HBACapabilitiesExtended::SADM)) != 0, + (extended_capabilities & (u32)(AHCI::HBACapabilitiesExtended::DESO)) != 0 + }; +} + +NonnullOwnPtr AHCIController::default_hba_region() const +{ + return MM.allocate_kernel_region(PhysicalAddress(PCI::get_BAR5(pci_address())).page_base(), Memory::page_round_up(sizeof(AHCI::HBA)), "AHCI HBA", Memory::Region::Access::ReadWrite).release_value(); +} + +AHCIController::~AHCIController() +{ +} + +void AHCIController::initialize_hba(PCI::DeviceIdentifier const& pci_device_identifier) +{ + if (!reset()) { + dmesgln("{}: AHCI controller reset failed", pci_address()); + return; + } + dmesgln("{}: AHCI controller reset", pci_address()); + dbgln("{}: AHCI command list entries count - {}", pci_address(), hba_capabilities().max_command_list_entries_count); + + u32 version = hba().control_regs.version; + dbgln_if(AHCI_DEBUG, "{}: AHCI Controller Version = {:#08x}", pci_address(), version); + + hba().control_regs.ghc = 0x80000000; // Ensure that HBA knows we are AHCI aware. + PCI::enable_interrupt_line(pci_address()); + PCI::enable_bus_mastering(pci_address()); + enable_global_interrupts(); + m_handlers.append(AHCIPortHandler::create(*this, pci_device_identifier.interrupt_line().value(), + AHCI::MaskedBitField((volatile u32&)(hba().control_regs.pi)))); +} + +void AHCIController::disable_global_interrupts() const +{ + hba().control_regs.ghc = hba().control_regs.ghc & 0xfffffffd; +} +void AHCIController::enable_global_interrupts() const +{ + hba().control_regs.ghc = hba().control_regs.ghc | (1 << 1); +} + +RefPtr AHCIController::device_by_port(u32 port_index) const +{ + for (auto& port_handler : m_handlers) { + if (!port_handler.is_responsible_for_port_index(port_index)) + continue; + + auto port = port_handler.port_at_index(port_index); + if (!port) + return nullptr; + return port->connected_device(); + } + return nullptr; +} + +RefPtr AHCIController::device(u32 index) const +{ + NonnullRefPtrVector connected_devices; + u32 pi = hba().control_regs.pi; + u32 bit = __builtin_ffsl(pi); + while (bit) { + dbgln_if(AHCI_DEBUG, "Checking implemented port {}, pi {:b}", bit - 1, pi); + pi &= ~(1u << (bit - 1)); + auto checked_device = device_by_port(bit - 1); + bit = __builtin_ffsl(pi); + if (checked_device.is_null()) + continue; + connected_devices.append(checked_device.release_nonnull()); + } + dbgln_if(AHCI_DEBUG, "Connected device count: {}, Index: {}", connected_devices.size(), index); + if (index >= connected_devices.size()) + return nullptr; + return connected_devices[index]; +} + +} diff --git a/Kernel/Storage/ATA/AHCIController.h b/Kernel/Storage/ATA/AHCIController.h new file mode 100644 index 0000000000..726696949d --- /dev/null +++ b/Kernel/Storage/ATA/AHCIController.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +class AsyncBlockDeviceRequest; +class AHCIPortHandler; +class AHCIPort; +class AHCIController final : public ATAController + , public PCI::Device { + friend class AHCIPortHandler; + friend class AHCIPort; + AK_MAKE_ETERNAL +public: + UNMAP_AFTER_INIT static NonnullRefPtr initialize(PCI::DeviceIdentifier const& pci_device_identifier); + virtual ~AHCIController() override; + + virtual RefPtr device(u32 index) const override; + virtual bool reset() override; + virtual bool shutdown() override; + virtual size_t devices_count() const override; + virtual void start_request(const ATADevice&, AsyncBlockDeviceRequest&) override; + virtual void complete_current_request(AsyncDeviceRequest::RequestResult) override; + + const AHCI::HBADefinedCapabilities& hba_capabilities() const { return m_capabilities; }; + +private: + void disable_global_interrupts() const; + void enable_global_interrupts() const; + + UNMAP_AFTER_INIT explicit AHCIController(PCI::DeviceIdentifier const&); + UNMAP_AFTER_INIT void initialize_hba(PCI::DeviceIdentifier const&); + + AHCI::HBADefinedCapabilities capabilities() const; + RefPtr device_by_port(u32 index) const; + + volatile AHCI::PortRegisters& port(size_t port_number) const; + UNMAP_AFTER_INIT NonnullOwnPtr default_hba_region() const; + volatile AHCI::HBA& hba() const; + + NonnullOwnPtr m_hba_region; + AHCI::HBADefinedCapabilities m_capabilities; + NonnullRefPtrVector m_handlers; +}; +} diff --git a/Kernel/Storage/ATA/AHCIPort.cpp b/Kernel/Storage/ATA/AHCIPort.cpp new file mode 100644 index 0000000000..9114541d88 --- /dev/null +++ b/Kernel/Storage/ATA/AHCIPort.cpp @@ -0,0 +1,834 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +// For more information about locking in this code +// please look at Documentation/Kernel/AHCILocking.md + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +NonnullRefPtr AHCIPort::create(const AHCIPortHandler& handler, volatile AHCI::PortRegisters& registers, u32 port_index) +{ + return adopt_ref(*new AHCIPort(handler, registers, port_index)); +} + +AHCIPort::AHCIPort(const AHCIPortHandler& handler, volatile AHCI::PortRegisters& registers, u32 port_index) + : m_port_index(port_index) + , m_port_registers(registers) + , m_parent_handler(handler) + , m_interrupt_status((volatile u32&)m_port_registers.is) + , m_interrupt_enable((volatile u32&)m_port_registers.ie) +{ + if (is_interface_disabled()) { + m_disabled_by_firmware = true; + return; + } + + m_command_list_page = MM.allocate_supervisor_physical_page(); + m_fis_receive_page = MM.allocate_supervisor_physical_page(); + if (m_command_list_page.is_null() || m_fis_receive_page.is_null()) + return; + + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Command list page at {}", representative_port_index(), m_command_list_page->paddr()); + dbgln_if(AHCI_DEBUG, "AHCI Port {}: FIS receive page at {}", representative_port_index(), m_command_list_page->paddr()); + + for (size_t index = 0; index < 1; index++) { + m_dma_buffers.append(MM.allocate_supervisor_physical_page().release_nonnull()); + } + for (size_t index = 0; index < 1; index++) { + m_command_table_pages.append(MM.allocate_supervisor_physical_page().release_nonnull()); + } + + auto region_or_error = MM.allocate_kernel_region(m_command_list_page->paddr(), PAGE_SIZE, "AHCI Port Command List", Memory::Region::Access::ReadWrite, Memory::Region::Cacheable::No); + if (region_or_error.is_error()) + TODO(); + m_command_list_region = region_or_error.release_value(); + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Command list region at {}", representative_port_index(), m_command_list_region->vaddr()); +} + +void AHCIPort::clear_sata_error_register() const +{ + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Clearing SATA error register.", representative_port_index()); + m_port_registers.serr = m_port_registers.serr; +} + +void AHCIPort::handle_interrupt() +{ + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Interrupt handled, PxIS {}", representative_port_index(), m_interrupt_status.raw_value()); + if (m_interrupt_status.raw_value() == 0) { + return; + } + if (m_interrupt_status.is_set(AHCI::PortInterruptFlag::PRC) && m_interrupt_status.is_set(AHCI::PortInterruptFlag::PC)) { + clear_sata_error_register(); + if ((m_port_registers.ssts & 0xf) != 3) { + m_connected_device->prepare_for_unplug(); + StorageManagement::the().remove_device(*m_connected_device); + g_io_work->queue([this]() { + m_connected_device->before_removing(); + m_connected_device.clear(); + }); + } else { + g_io_work->queue([this]() { + reset(); + }); + } + return; + } + if (m_interrupt_status.is_set(AHCI::PortInterruptFlag::PRC)) { + clear_sata_error_register(); + m_wait_connect_for_completion = true; + } + if (m_interrupt_status.is_set(AHCI::PortInterruptFlag::INF)) { + // We need to defer the reset, because we can receive interrupts when + // resetting the device. + g_io_work->queue([this]() { + reset(); + }); + return; + } + if (m_interrupt_status.is_set(AHCI::PortInterruptFlag::IF) || m_interrupt_status.is_set(AHCI::PortInterruptFlag::TFE) || m_interrupt_status.is_set(AHCI::PortInterruptFlag::HBD) || m_interrupt_status.is_set(AHCI::PortInterruptFlag::HBF)) { + g_io_work->queue([this]() { + recover_from_fatal_error(); + }); + return; + } + if (m_interrupt_status.is_set(AHCI::PortInterruptFlag::DHR) || m_interrupt_status.is_set(AHCI::PortInterruptFlag::PS)) { + m_wait_for_completion = false; + + // Now schedule reading/writing the buffer as soon as we leave the irq handler. + // This is important so that we can safely access the buffers, which could + // trigger page faults + if (!m_current_request) { + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Request handled, probably identify request", representative_port_index()); + } else { + g_io_work->queue([this]() { + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Request handled", representative_port_index()); + MutexLocker locker(m_lock); + VERIFY(m_current_request); + VERIFY(m_current_scatter_list); + if (!m_connected_device) { + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Request success", representative_port_index()); + complete_current_request(AsyncDeviceRequest::Failure); + return; + } + if (m_current_request->request_type() == AsyncBlockDeviceRequest::Read) { + if (auto result = m_current_request->write_to_buffer(m_current_request->buffer(), m_current_scatter_list->dma_region().as_ptr(), m_connected_device->block_size() * m_current_request->block_count()); result.is_error()) { + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Request failure, memory fault occurred when reading in data.", representative_port_index()); + m_current_scatter_list = nullptr; + complete_current_request(AsyncDeviceRequest::MemoryFault); + return; + } + } + m_current_scatter_list = nullptr; + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Request success", representative_port_index()); + complete_current_request(AsyncDeviceRequest::Success); + }); + } + } + + m_interrupt_status.clear(); +} + +bool AHCIPort::is_interrupts_enabled() const +{ + return !m_interrupt_enable.is_cleared(); +} + +void AHCIPort::recover_from_fatal_error() +{ + MutexLocker locker(m_lock); + SpinlockLocker lock(m_hard_lock); + dmesgln("{}: AHCI Port {} fatal error, shutting down!", m_parent_handler->hba_controller()->pci_address(), representative_port_index()); + dmesgln("{}: AHCI Port {} fatal error, SError {}", m_parent_handler->hba_controller()->pci_address(), representative_port_index(), (u32)m_port_registers.serr); + stop_command_list_processing(); + stop_fis_receiving(); + m_interrupt_enable.clear(); +} + +void AHCIPort::eject() +{ + // FIXME: This operation (meant to be used on optical drives) doesn't work yet when I tested it on real hardware + TODO(); + + VERIFY(m_lock.is_locked()); + VERIFY(is_atapi_attached()); + VERIFY(is_operable()); + clear_sata_error_register(); + + if (!spin_until_ready()) + return; + + auto unused_command_header = try_to_find_unused_command_header(); + VERIFY(unused_command_header.has_value()); + auto* command_list_entries = (volatile AHCI::CommandHeader*)m_command_list_region->vaddr().as_ptr(); + command_list_entries[unused_command_header.value()].ctba = m_command_table_pages[unused_command_header.value()].paddr().get(); + command_list_entries[unused_command_header.value()].ctbau = 0; + command_list_entries[unused_command_header.value()].prdbc = 0; + command_list_entries[unused_command_header.value()].prdtl = 0; + + // Note: we must set the correct Dword count in this register. Real hardware + // AHCI controllers do care about this field! QEMU doesn't care if we don't + // set the correct CFL field in this register, real hardware will set an + // handshake error bit in PxSERR register if CFL is incorrect. + command_list_entries[unused_command_header.value()].attributes = (size_t)FIS::DwordCount::RegisterHostToDevice | AHCI::CommandHeaderAttributes::P | AHCI::CommandHeaderAttributes::C | AHCI::CommandHeaderAttributes::A; + + auto command_table_region = MM.allocate_kernel_region(m_command_table_pages[unused_command_header.value()].paddr().page_base(), Memory::page_round_up(sizeof(AHCI::CommandTable)), "AHCI Command Table", Memory::Region::Access::ReadWrite, Memory::Region::Cacheable::No).release_value(); + auto& command_table = *(volatile AHCI::CommandTable*)command_table_region->vaddr().as_ptr(); + memset(const_cast(command_table.command_fis), 0, 64); + auto& fis = *(volatile FIS::HostToDevice::Register*)command_table.command_fis; + fis.header.fis_type = (u8)FIS::Type::RegisterHostToDevice; + fis.command = ATA_CMD_PACKET; + + full_memory_barrier(); + memset(const_cast(command_table.atapi_command), 0, 32); + + full_memory_barrier(); + command_table.atapi_command[0] = ATAPI_CMD_EJECT; + command_table.atapi_command[1] = 0; + command_table.atapi_command[2] = 0; + command_table.atapi_command[3] = 0; + command_table.atapi_command[4] = 0b10; + command_table.atapi_command[5] = 0; + command_table.atapi_command[6] = 0; + command_table.atapi_command[7] = 0; + command_table.atapi_command[8] = 0; + command_table.atapi_command[9] = 0; + command_table.atapi_command[10] = 0; + command_table.atapi_command[11] = 0; + fis.device = 0; + fis.header.port_muliplier = fis.header.port_muliplier | (u8)FIS::HeaderAttributes::C; + + // The below loop waits until the port is no longer busy before issuing a new command + if (!spin_until_ready()) + return; + + full_memory_barrier(); + mark_command_header_ready_to_process(unused_command_header.value()); + + full_memory_barrier(); + + while (1) { + if (m_port_registers.serr != 0) { + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Eject Drive failed, SError {:#08x}", representative_port_index(), (u32)m_port_registers.serr); + try_disambiguate_sata_error(); + VERIFY_NOT_REACHED(); + } + } + dbgln("AHCI Port {}: Eject Drive", representative_port_index()); + return; +} + +bool AHCIPort::reset() +{ + MutexLocker locker(m_lock); + SpinlockLocker lock(m_hard_lock); + + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Resetting", representative_port_index()); + + if (m_disabled_by_firmware) { + dmesgln("AHCI Port {}: Disabled by firmware ", representative_port_index()); + return false; + } + full_memory_barrier(); + m_interrupt_enable.clear(); + m_interrupt_status.clear(); + full_memory_barrier(); + start_fis_receiving(); + full_memory_barrier(); + clear_sata_error_register(); + full_memory_barrier(); + if (!initiate_sata_reset(lock)) { + return false; + } + return initialize(lock); +} + +bool AHCIPort::initialize_without_reset() +{ + MutexLocker locker(m_lock); + SpinlockLocker lock(m_hard_lock); + dmesgln("AHCI Port {}: {}", representative_port_index(), try_disambiguate_sata_status()); + return initialize(lock); +} + +bool AHCIPort::initialize(SpinlockLocker& main_lock) +{ + VERIFY(m_lock.is_locked()); + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Initialization. Signature = {:#08x}", representative_port_index(), static_cast(m_port_registers.sig)); + if (!is_phy_enabled()) { + // Note: If PHY is not enabled, just clear the interrupt status and enable interrupts, in case + // we are going to hotplug a device later. + m_interrupt_status.clear(); + m_interrupt_enable.set_all(); + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Bailing initialization, Phy is not enabled.", representative_port_index()); + return false; + } + rebase(); + power_on(); + spin_up(); + clear_sata_error_register(); + start_fis_receiving(); + set_active_state(); + m_interrupt_status.clear(); + m_interrupt_enable.set_all(); + + full_memory_barrier(); + // This actually enables the port... + start_command_list_processing(); + full_memory_barrier(); + + size_t logical_sector_size = 512; + size_t physical_sector_size = 512; + u64 max_addressable_sector = 0; + if (identify_device(main_lock)) { + auto identify_block = Memory::map_typed(m_parent_handler->get_identify_metadata_physical_region(m_port_index)); + // Check if word 106 is valid before using it! + if ((identify_block->physical_sector_size_to_logical_sector_size >> 14) == 1) { + if (identify_block->physical_sector_size_to_logical_sector_size & (1 << 12)) { + VERIFY(identify_block->logical_sector_size != 0); + logical_sector_size = identify_block->logical_sector_size; + } + if (identify_block->physical_sector_size_to_logical_sector_size & (1 << 13)) { + physical_sector_size = logical_sector_size << (identify_block->physical_sector_size_to_logical_sector_size & 0xf); + } + } + // Check if the device supports LBA48 mode + if (identify_block->commands_and_feature_sets_supported[1] & (1 << 10)) { + max_addressable_sector = identify_block->user_addressable_logical_sectors_count; + } else { + max_addressable_sector = identify_block->max_28_bit_addressable_logical_sector; + } + if (is_atapi_attached()) { + m_port_registers.cmd = m_port_registers.cmd | (1 << 24); + } + + dmesgln("AHCI Port {}: Device found, Capacity={}, Bytes per logical sector={}, Bytes per physical sector={}", representative_port_index(), max_addressable_sector * logical_sector_size, logical_sector_size, physical_sector_size); + + // FIXME: We don't support ATAPI devices yet, so for now we don't "create" them + if (!is_atapi_attached()) { + m_connected_device = ATADiskDevice::create(m_parent_handler->hba_controller(), { m_port_index, 0 }, 0, logical_sector_size, max_addressable_sector); + } else { + dbgln("AHCI Port {}: Ignoring ATAPI devices for now as we don't currently support them.", representative_port_index()); + } + } + return true; +} + +const char* AHCIPort::try_disambiguate_sata_status() +{ + switch (m_port_registers.ssts & 0xf) { + case 0: + return "Device not detected, Phy not enabled"; + case 1: + return "Device detected, Phy disabled"; + case 3: + return "Device detected, Phy enabled"; + case 4: + return "interface disabled"; + } + VERIFY_NOT_REACHED(); +} + +void AHCIPort::try_disambiguate_sata_error() +{ + dmesgln("AHCI Port {}: SErr breakdown:", representative_port_index()); + dmesgln("AHCI Port {}: Diagnostics:", representative_port_index()); + + constexpr u32 diagnostics_bitfield = 0xFFFF0000; + if ((m_port_registers.serr & diagnostics_bitfield) > 0) { + if (m_port_registers.serr & AHCI::SErr::DIAG_X) + dmesgln("AHCI Port {}: - Exchanged", representative_port_index()); + if (m_port_registers.serr & AHCI::SErr::DIAG_F) + dmesgln("AHCI Port {}: - Unknown FIS Type", representative_port_index()); + if (m_port_registers.serr & AHCI::SErr::DIAG_T) + dmesgln("AHCI Port {}: - Transport state transition error", representative_port_index()); + if (m_port_registers.serr & AHCI::SErr::DIAG_S) + dmesgln("AHCI Port {}: - Link sequence error", representative_port_index()); + if (m_port_registers.serr & AHCI::SErr::DIAG_H) + dmesgln("AHCI Port {}: - Handshake error", representative_port_index()); + if (m_port_registers.serr & AHCI::SErr::DIAG_C) + dmesgln("AHCI Port {}: - CRC error", representative_port_index()); + if (m_port_registers.serr & AHCI::SErr::DIAG_D) + dmesgln("AHCI Port {}: - Disparity error", representative_port_index()); + if (m_port_registers.serr & AHCI::SErr::DIAG_B) + dmesgln("AHCI Port {}: - 10B to 8B decode error", representative_port_index()); + if (m_port_registers.serr & AHCI::SErr::DIAG_W) + dmesgln("AHCI Port {}: - Comm Wake", representative_port_index()); + if (m_port_registers.serr & AHCI::SErr::DIAG_I) + dmesgln("AHCI Port {}: - Phy Internal Error", representative_port_index()); + if (m_port_registers.serr & AHCI::SErr::DIAG_N) + dmesgln("AHCI Port {}: - PhyRdy Change", representative_port_index()); + } else { + dmesgln("AHCI Port {}: - No diagnostic information provided.", representative_port_index()); + } + + dmesgln("AHCI Port {}: Error(s):", representative_port_index()); + + constexpr u32 error_bitfield = 0xFFFF; + if ((m_port_registers.serr & error_bitfield) > 0) { + if (m_port_registers.serr & AHCI::SErr::ERR_E) + dmesgln("AHCI Port {}: - Internal error", representative_port_index()); + if (m_port_registers.serr & AHCI::SErr::ERR_P) + dmesgln("AHCI Port {}: - Protocol error", representative_port_index()); + if (m_port_registers.serr & AHCI::SErr::ERR_C) + dmesgln("AHCI Port {}: - Persistent communication or data integrity error", representative_port_index()); + if (m_port_registers.serr & AHCI::SErr::ERR_T) + dmesgln("AHCI Port {}: - Transient data integrity error", representative_port_index()); + if (m_port_registers.serr & AHCI::SErr::ERR_M) + dmesgln("AHCI Port {}: - Recovered communications error", representative_port_index()); + if (m_port_registers.serr & AHCI::SErr::ERR_I) + dmesgln("AHCI Port {}: - Recovered data integrity error", representative_port_index()); + } else { + dmesgln("AHCI Port {}: - No error information provided.", representative_port_index()); + } +} + +void AHCIPort::rebase() +{ + VERIFY(m_lock.is_locked()); + VERIFY(m_hard_lock.is_locked()); + VERIFY(!m_command_list_page.is_null() && !m_fis_receive_page.is_null()); + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Rebasing.", representative_port_index()); + full_memory_barrier(); + stop_command_list_processing(); + stop_fis_receiving(); + full_memory_barrier(); + size_t retry = 0; + // Try to wait 1 second for HBA to clear Command List Running and FIS Receive Running + while (retry < 1000) { + if (!(m_port_registers.cmd & (1 << 15)) && !(m_port_registers.cmd & (1 << 14))) + break; + IO::delay(1000); + retry++; + } + full_memory_barrier(); + m_port_registers.clbu = 0; + m_port_registers.clb = m_command_list_page->paddr().get(); + m_port_registers.fbu = 0; + m_port_registers.fb = m_fis_receive_page->paddr().get(); +} + +bool AHCIPort::is_operable() const +{ + // Note: The definition of "operable" is somewhat ambiguous, but we determine it + // by 3 parameters as shown below. + return (!m_command_list_page.is_null()) + && (!m_fis_receive_page.is_null()) + && ((m_port_registers.cmd & (1 << 14)) != 0); +} + +void AHCIPort::set_active_state() const +{ + VERIFY(m_lock.is_locked()); + VERIFY(m_hard_lock.is_locked()); + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Switching to active state.", representative_port_index()); + m_port_registers.cmd = (m_port_registers.cmd & 0x0ffffff) | (1 << 28); +} + +void AHCIPort::set_sleep_state() const +{ + VERIFY(m_lock.is_locked()); + VERIFY(m_hard_lock.is_locked()); + m_port_registers.cmd = (m_port_registers.cmd & 0x0ffffff) | (0b1000 << 28); +} + +size_t AHCIPort::calculate_descriptors_count(size_t block_count) const +{ + VERIFY(m_connected_device); + size_t needed_dma_regions_count = Memory::page_round_up((block_count * m_connected_device->block_size())) / PAGE_SIZE; + VERIFY(needed_dma_regions_count <= m_dma_buffers.size()); + return needed_dma_regions_count; +} + +Optional AHCIPort::prepare_and_set_scatter_list(AsyncBlockDeviceRequest& request) +{ + VERIFY(m_lock.is_locked()); + VERIFY(request.block_count() > 0); + + NonnullRefPtrVector allocated_dma_regions; + for (size_t index = 0; index < calculate_descriptors_count(request.block_count()); index++) { + allocated_dma_regions.append(m_dma_buffers.at(index)); + } + + m_current_scatter_list = Memory::ScatterGatherList::try_create(request, allocated_dma_regions.span(), m_connected_device->block_size()); + if (!m_current_scatter_list) + return AsyncDeviceRequest::Failure; + if (request.request_type() == AsyncBlockDeviceRequest::Write) { + if (auto result = request.read_from_buffer(request.buffer(), m_current_scatter_list->dma_region().as_ptr(), m_connected_device->block_size() * request.block_count()); result.is_error()) { + return AsyncDeviceRequest::MemoryFault; + } + } + return {}; +} + +void AHCIPort::start_request(AsyncBlockDeviceRequest& request) +{ + MutexLocker locker(m_lock); + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Request start", representative_port_index()); + VERIFY(!m_current_request); + VERIFY(!m_current_scatter_list); + + m_current_request = request; + + auto result = prepare_and_set_scatter_list(request); + if (result.has_value()) { + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Request failure.", representative_port_index()); + locker.unlock(); + complete_current_request(result.value()); + return; + } + + auto success = access_device(request.request_type(), request.block_index(), request.block_count()); + if (!success) { + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Request failure.", representative_port_index()); + locker.unlock(); + complete_current_request(AsyncDeviceRequest::Failure); + return; + } +} + +void AHCIPort::complete_current_request(AsyncDeviceRequest::RequestResult result) +{ + VERIFY(m_current_request); + auto current_request = m_current_request; + m_current_request.clear(); + current_request->complete(result); +} + +bool AHCIPort::spin_until_ready() const +{ + VERIFY(m_lock.is_locked()); + size_t spin = 0; + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Spinning until ready.", representative_port_index()); + while ((m_port_registers.tfd & (ATA_SR_BSY | ATA_SR_DRQ)) && spin <= 100) { + IO::delay(1000); + spin++; + } + if (spin == 100) { + dbgln_if(AHCI_DEBUG, "AHCI Port {}: SPIN exceeded 100 milliseconds threshold", representative_port_index()); + return false; + } + return true; +} + +bool AHCIPort::access_device(AsyncBlockDeviceRequest::RequestType direction, u64 lba, u8 block_count) +{ + VERIFY(m_connected_device); + VERIFY(is_operable()); + VERIFY(m_lock.is_locked()); + VERIFY(m_current_scatter_list); + SpinlockLocker lock(m_hard_lock); + + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Do a {}, lba {}, block count {}", representative_port_index(), direction == AsyncBlockDeviceRequest::RequestType::Write ? "write" : "read", lba, block_count); + if (!spin_until_ready()) + return false; + + auto unused_command_header = try_to_find_unused_command_header(); + VERIFY(unused_command_header.has_value()); + auto* command_list_entries = (volatile AHCI::CommandHeader*)m_command_list_region->vaddr().as_ptr(); + command_list_entries[unused_command_header.value()].ctba = m_command_table_pages[unused_command_header.value()].paddr().get(); + command_list_entries[unused_command_header.value()].ctbau = 0; + command_list_entries[unused_command_header.value()].prdbc = 0; + command_list_entries[unused_command_header.value()].prdtl = m_current_scatter_list->scatters_count(); + + // Note: we must set the correct Dword count in this register. Real hardware + // AHCI controllers do care about this field! QEMU doesn't care if we don't + // set the correct CFL field in this register, real hardware will set an + // handshake error bit in PxSERR register if CFL is incorrect. + command_list_entries[unused_command_header.value()].attributes = (size_t)FIS::DwordCount::RegisterHostToDevice | AHCI::CommandHeaderAttributes::P | (is_atapi_attached() ? AHCI::CommandHeaderAttributes::A : 0) | (direction == AsyncBlockDeviceRequest::RequestType::Write ? AHCI::CommandHeaderAttributes::W : 0); + + dbgln_if(AHCI_DEBUG, "AHCI Port {}: CLE: ctba={:#08x}, ctbau={:#08x}, prdbc={:#08x}, prdtl={:#04x}, attributes={:#04x}", representative_port_index(), (u32)command_list_entries[unused_command_header.value()].ctba, (u32)command_list_entries[unused_command_header.value()].ctbau, (u32)command_list_entries[unused_command_header.value()].prdbc, (u16)command_list_entries[unused_command_header.value()].prdtl, (u16)command_list_entries[unused_command_header.value()].attributes); + + auto command_table_region = MM.allocate_kernel_region(m_command_table_pages[unused_command_header.value()].paddr().page_base(), Memory::page_round_up(sizeof(AHCI::CommandTable)), "AHCI Command Table", Memory::Region::Access::ReadWrite, Memory::Region::Cacheable::No).release_value(); + auto& command_table = *(volatile AHCI::CommandTable*)command_table_region->vaddr().as_ptr(); + + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Allocated command table at {}", representative_port_index(), command_table_region->vaddr()); + + memset(const_cast(command_table.command_fis), 0, 64); + + size_t scatter_entry_index = 0; + size_t data_transfer_count = (block_count * m_connected_device->block_size()); + for (auto scatter_page : m_current_scatter_list->vmobject().physical_pages()) { + VERIFY(data_transfer_count != 0); + VERIFY(scatter_page); + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Add a transfer scatter entry @ {}", representative_port_index(), scatter_page->paddr()); + command_table.descriptors[scatter_entry_index].base_high = 0; + command_table.descriptors[scatter_entry_index].base_low = scatter_page->paddr().get(); + if (data_transfer_count <= PAGE_SIZE) { + command_table.descriptors[scatter_entry_index].byte_count = data_transfer_count - 1; + data_transfer_count = 0; + } else { + command_table.descriptors[scatter_entry_index].byte_count = PAGE_SIZE - 1; + data_transfer_count -= PAGE_SIZE; + } + scatter_entry_index++; + } + command_table.descriptors[scatter_entry_index].byte_count = (PAGE_SIZE - 1) | (1 << 31); + + memset(const_cast(command_table.atapi_command), 0, 32); + + auto& fis = *(volatile FIS::HostToDevice::Register*)command_table.command_fis; + fis.header.fis_type = (u8)FIS::Type::RegisterHostToDevice; + if (is_atapi_attached()) { + fis.command = ATA_CMD_PACKET; + TODO(); + } else { + if (direction == AsyncBlockDeviceRequest::RequestType::Write) + fis.command = ATA_CMD_WRITE_DMA_EXT; + else + fis.command = ATA_CMD_READ_DMA_EXT; + } + + full_memory_barrier(); + + fis.device = ATA_USE_LBA_ADDRESSING; + fis.header.port_muliplier = (u8)FIS::HeaderAttributes::C; + + fis.lba_high[0] = (lba >> 24) & 0xff; + fis.lba_high[1] = (lba >> 32) & 0xff; + fis.lba_high[2] = (lba >> 40) & 0xff; + fis.lba_low[0] = lba & 0xff; + fis.lba_low[1] = (lba >> 8) & 0xff; + fis.lba_low[2] = (lba >> 16) & 0xff; + fis.count = (block_count); + + // The below loop waits until the port is no longer busy before issuing a new command + if (!spin_until_ready()) + return false; + + full_memory_barrier(); + mark_command_header_ready_to_process(unused_command_header.value()); + full_memory_barrier(); + + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Do a {}, lba {}, block count {} @ {}, ended", representative_port_index(), direction == AsyncBlockDeviceRequest::RequestType::Write ? "write" : "read", lba, block_count, m_dma_buffers[0].paddr()); + return true; +} + +bool AHCIPort::identify_device(SpinlockLocker& main_lock) +{ + VERIFY(m_lock.is_locked()); + VERIFY(is_operable()); + if (!spin_until_ready()) + return false; + + auto unused_command_header = try_to_find_unused_command_header(); + VERIFY(unused_command_header.has_value()); + auto* command_list_entries = (volatile AHCI::CommandHeader*)m_command_list_region->vaddr().as_ptr(); + command_list_entries[unused_command_header.value()].ctba = m_command_table_pages[unused_command_header.value()].paddr().get(); + command_list_entries[unused_command_header.value()].ctbau = 0; + command_list_entries[unused_command_header.value()].prdbc = 512; + command_list_entries[unused_command_header.value()].prdtl = 1; + + // Note: we must set the correct Dword count in this register. Real hardware AHCI controllers do care about this field! + // QEMU doesn't care if we don't set the correct CFL field in this register, real hardware will set an handshake error bit in PxSERR register. + command_list_entries[unused_command_header.value()].attributes = (size_t)FIS::DwordCount::RegisterHostToDevice | AHCI::CommandHeaderAttributes::P; + + auto command_table_region = MM.allocate_kernel_region(m_command_table_pages[unused_command_header.value()].paddr().page_base(), Memory::page_round_up(sizeof(AHCI::CommandTable)), "AHCI Command Table", Memory::Region::Access::ReadWrite).release_value(); + auto& command_table = *(volatile AHCI::CommandTable*)command_table_region->vaddr().as_ptr(); + memset(const_cast(command_table.command_fis), 0, 64); + command_table.descriptors[0].base_high = 0; + command_table.descriptors[0].base_low = m_parent_handler->get_identify_metadata_physical_region(m_port_index).get(); + command_table.descriptors[0].byte_count = 512 - 1; + auto& fis = *(volatile FIS::HostToDevice::Register*)command_table.command_fis; + fis.header.fis_type = (u8)FIS::Type::RegisterHostToDevice; + fis.command = m_port_registers.sig == AHCI::DeviceSignature::ATAPI ? ATA_CMD_IDENTIFY_PACKET : ATA_CMD_IDENTIFY; + fis.device = 0; + fis.header.port_muliplier = fis.header.port_muliplier | (u8)FIS::HeaderAttributes::C; + + // The below loop waits until the port is no longer busy before issuing a new command + if (!spin_until_ready()) + return false; + + // FIXME: Find a better way to send IDENTIFY DEVICE and getting an interrupt! + { + main_lock.unlock(); + VERIFY_INTERRUPTS_ENABLED(); + full_memory_barrier(); + m_wait_for_completion = true; + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Marking command header at index {} as ready to identify device", representative_port_index(), unused_command_header.value()); + m_port_registers.ci = 1 << unused_command_header.value(); + full_memory_barrier(); + + while (1) { + if (m_port_registers.serr != 0) { + dbgln("AHCI Port {}: Identify failed, SError {:#08x}", representative_port_index(), (u32)m_port_registers.serr); + try_disambiguate_sata_error(); + return false; + } + if (!m_wait_for_completion) + break; + } + main_lock.lock(); + } + + return true; +} + +bool AHCIPort::shutdown() +{ + MutexLocker locker(m_lock); + SpinlockLocker lock(m_hard_lock); + rebase(); + set_interface_state(AHCI::DeviceDetectionInitialization::DisableInterface); + return true; +} + +Optional AHCIPort::try_to_find_unused_command_header() +{ + VERIFY(m_lock.is_locked()); + u32 commands_issued = m_port_registers.ci; + for (size_t index = 0; index < 32; index++) { + if (!(commands_issued & 1)) { + dbgln_if(AHCI_DEBUG, "AHCI Port {}: unused command header at index {}", representative_port_index(), index); + return index; + } + commands_issued >>= 1; + } + return {}; +} + +void AHCIPort::start_command_list_processing() const +{ + VERIFY(m_lock.is_locked()); + VERIFY(m_hard_lock.is_locked()); + VERIFY(is_operable()); + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Starting command list processing.", representative_port_index()); + m_port_registers.cmd = m_port_registers.cmd | 1; +} + +void AHCIPort::mark_command_header_ready_to_process(u8 command_header_index) const +{ + VERIFY(m_lock.is_locked()); + VERIFY(m_hard_lock.is_locked()); + VERIFY(is_operable()); + VERIFY(!m_wait_for_completion); + m_wait_for_completion = true; + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Marking command header at index {} as ready to process.", representative_port_index(), command_header_index); + m_port_registers.ci = 1 << command_header_index; +} + +void AHCIPort::stop_command_list_processing() const +{ + VERIFY(m_lock.is_locked()); + VERIFY(m_hard_lock.is_locked()); + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Stopping command list processing.", representative_port_index()); + m_port_registers.cmd = m_port_registers.cmd & 0xfffffffe; +} + +void AHCIPort::start_fis_receiving() const +{ + VERIFY(m_lock.is_locked()); + VERIFY(m_hard_lock.is_locked()); + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Starting FIS receiving.", representative_port_index()); + m_port_registers.cmd = m_port_registers.cmd | (1 << 4); +} + +void AHCIPort::power_on() const +{ + VERIFY(m_lock.is_locked()); + VERIFY(m_hard_lock.is_locked()); + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Power on. Cold presence detection? {}", representative_port_index(), (bool)(m_port_registers.cmd & (1 << 20))); + if (!(m_port_registers.cmd & (1 << 20))) + return; + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Powering on device.", representative_port_index()); + m_port_registers.cmd = m_port_registers.cmd | (1 << 2); +} + +void AHCIPort::spin_up() const +{ + VERIFY(m_lock.is_locked()); + VERIFY(m_hard_lock.is_locked()); + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Spin up. Staggered spin up? {}", representative_port_index(), m_parent_handler->hba_capabilities().staggered_spin_up_supported); + if (!m_parent_handler->hba_capabilities().staggered_spin_up_supported) + return; + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Spinning up device.", representative_port_index()); + m_port_registers.cmd = m_port_registers.cmd | (1 << 1); +} + +void AHCIPort::stop_fis_receiving() const +{ + VERIFY(m_lock.is_locked()); + VERIFY(m_hard_lock.is_locked()); + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Stopping FIS receiving.", representative_port_index()); + m_port_registers.cmd = m_port_registers.cmd & 0xFFFFFFEF; +} + +bool AHCIPort::initiate_sata_reset(SpinlockLocker& main_lock) +{ + VERIFY(m_lock.is_locked()); + VERIFY(m_hard_lock.is_locked()); + dbgln_if(AHCI_DEBUG, "AHCI Port {}: Initiate SATA reset", representative_port_index()); + stop_command_list_processing(); + full_memory_barrier(); + size_t retry = 0; + // Try to wait 1 second for HBA to clear Command List Running + while (retry < 5000) { + if (!(m_port_registers.cmd & (1 << 15))) + break; + // The AHCI specification says to wait now a 500 milliseconds + IO::delay(100); + retry++; + } + full_memory_barrier(); + spin_up(); + full_memory_barrier(); + set_interface_state(AHCI::DeviceDetectionInitialization::PerformInterfaceInitializationSequence); + // The AHCI specification says to wait now a 1 millisecond + IO::delay(1000); + // FIXME: Find a better way to opt-out temporarily from Scoped locking! + { + main_lock.unlock(); + VERIFY_INTERRUPTS_ENABLED(); + full_memory_barrier(); + set_interface_state(AHCI::DeviceDetectionInitialization::NoActionRequested); + full_memory_barrier(); + if (m_wait_connect_for_completion) { + retry = 0; + while (retry < 100000) { + if (is_phy_enabled()) + break; + + IO::delay(10); + retry++; + } + } + main_lock.lock(); + } + + dmesgln("AHCI Port {}: {}", representative_port_index(), try_disambiguate_sata_status()); + + full_memory_barrier(); + clear_sata_error_register(); + return (m_port_registers.ssts & 0xf) == 3; +} + +void AHCIPort::set_interface_state(AHCI::DeviceDetectionInitialization requested_action) +{ + switch (requested_action) { + case AHCI::DeviceDetectionInitialization::NoActionRequested: + m_port_registers.sctl = (m_port_registers.sctl & 0xfffffff0); + return; + case AHCI::DeviceDetectionInitialization::PerformInterfaceInitializationSequence: + m_port_registers.sctl = (m_port_registers.sctl & 0xfffffff0) | 1; + return; + case AHCI::DeviceDetectionInitialization::DisableInterface: + m_port_registers.sctl = (m_port_registers.sctl & 0xfffffff0) | 4; + return; + } + VERIFY_NOT_REACHED(); +} + +} diff --git a/Kernel/Storage/ATA/AHCIPort.h b/Kernel/Storage/ATA/AHCIPort.h new file mode 100644 index 0000000000..c99eccde29 --- /dev/null +++ b/Kernel/Storage/ATA/AHCIPort.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +class AsyncBlockDeviceRequest; + +class AHCIPortHandler; +class AHCIPort + : public RefCounted + , public Weakable { + friend class AHCIPortHandler; + friend class AHCIController; + +public: + UNMAP_AFTER_INIT static NonnullRefPtr create(const AHCIPortHandler&, volatile AHCI::PortRegisters&, u32 port_index); + + u32 port_index() const { return m_port_index; } + u32 representative_port_index() const { return port_index() + 1; } + bool is_operable() const; + bool is_hot_pluggable() const; + bool is_atapi_attached() const { return m_port_registers.sig == (u32)AHCI::DeviceSignature::ATAPI; }; + + RefPtr connected_device() const { return m_connected_device; } + + bool reset(); + UNMAP_AFTER_INIT bool initialize_without_reset(); + void handle_interrupt(); + +private: + bool is_phy_enabled() const { return (m_port_registers.ssts & 0xf) == 3; } + bool initialize(SpinlockLocker&); + + UNMAP_AFTER_INIT AHCIPort(const AHCIPortHandler&, volatile AHCI::PortRegisters&, u32 port_index); + + ALWAYS_INLINE void clear_sata_error_register() const; + + void eject(); + + const char* try_disambiguate_sata_status(); + void try_disambiguate_sata_error(); + + bool initiate_sata_reset(SpinlockLocker&); + void rebase(); + void recover_from_fatal_error(); + bool shutdown(); + ALWAYS_INLINE void spin_up() const; + ALWAYS_INLINE void power_on() const; + + void start_request(AsyncBlockDeviceRequest&); + void complete_current_request(AsyncDeviceRequest::RequestResult); + bool access_device(AsyncBlockDeviceRequest::RequestType, u64 lba, u8 block_count); + size_t calculate_descriptors_count(size_t block_count) const; + [[nodiscard]] Optional prepare_and_set_scatter_list(AsyncBlockDeviceRequest& request); + + ALWAYS_INLINE bool is_interrupts_enabled() const; + + bool spin_until_ready() const; + + bool identify_device(SpinlockLocker&); + + ALWAYS_INLINE void start_command_list_processing() const; + ALWAYS_INLINE void mark_command_header_ready_to_process(u8 command_header_index) const; + ALWAYS_INLINE void stop_command_list_processing() const; + + ALWAYS_INLINE void start_fis_receiving() const; + ALWAYS_INLINE void stop_fis_receiving() const; + + ALWAYS_INLINE void set_active_state() const; + ALWAYS_INLINE void set_sleep_state() const; + + void set_interface_state(AHCI::DeviceDetectionInitialization); + + Optional try_to_find_unused_command_header(); + + ALWAYS_INLINE bool is_interface_disabled() const { return (m_port_registers.ssts & 0xf) == 4; }; + + // Data members + + EntropySource m_entropy_source; + RefPtr m_current_request; + Spinlock m_hard_lock; + Mutex m_lock { "AHCIPort" }; + + mutable bool m_wait_for_completion { false }; + bool m_wait_connect_for_completion { false }; + + NonnullRefPtrVector m_dma_buffers; + NonnullRefPtrVector m_command_table_pages; + RefPtr m_command_list_page; + OwnPtr m_command_list_region; + RefPtr m_fis_receive_page; + RefPtr m_connected_device; + + u32 m_port_index; + volatile AHCI::PortRegisters& m_port_registers; + NonnullRefPtr m_parent_handler; + AHCI::PortInterruptStatusBitField m_interrupt_status; + AHCI::PortInterruptEnableBitField m_interrupt_enable; + + RefPtr m_current_scatter_list; + bool m_disabled_by_firmware { false }; +}; +} diff --git a/Kernel/Storage/ATA/AHCIPortHandler.cpp b/Kernel/Storage/ATA/AHCIPortHandler.cpp new file mode 100644 index 0000000000..56d2559e4b --- /dev/null +++ b/Kernel/Storage/ATA/AHCIPortHandler.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Kernel { + +NonnullRefPtr AHCIPortHandler::create(AHCIController& controller, u8 irq, AHCI::MaskedBitField taken_ports) +{ + return adopt_ref(*new AHCIPortHandler(controller, irq, taken_ports)); +} + +AHCIPortHandler::AHCIPortHandler(AHCIController& controller, u8 irq, AHCI::MaskedBitField taken_ports) + : IRQHandler(irq) + , m_parent_controller(controller) + , m_taken_ports(taken_ports) + , m_pending_ports_interrupts(create_pending_ports_interrupts_bitfield()) +{ + // FIXME: Use the number of taken ports to determine how many pages we should allocate. + for (size_t index = 0; index < (((size_t)AHCI::Limits::MaxPorts * 512) / PAGE_SIZE); index++) { + m_identify_metadata_pages.append(MM.allocate_supervisor_physical_page().release_nonnull()); + } + + dbgln_if(AHCI_DEBUG, "AHCI Port Handler: IRQ {}", irq); + + // Clear pending interrupts, if there are any! + m_pending_ports_interrupts.set_all(); + enable_irq(); + + if (kernel_command_line().ahci_reset_mode() == AHCIResetMode::Aggressive) { + for (auto index : taken_ports.to_vector()) { + auto port = AHCIPort::create(*this, static_cast(controller.hba().port_regs[index]), index); + m_handled_ports.set(index, port); + port->reset(); + } + return; + } + for (auto index : taken_ports.to_vector()) { + auto port = AHCIPort::create(*this, static_cast(controller.hba().port_regs[index]), index); + m_handled_ports.set(index, port); + port->initialize_without_reset(); + } +} + +void AHCIPortHandler::enumerate_ports(Function callback) const +{ + for (auto& port : m_handled_ports) { + callback(*port.value); + } +} + +RefPtr AHCIPortHandler::port_at_index(u32 port_index) const +{ + VERIFY(m_taken_ports.is_set_at(port_index)); + auto it = m_handled_ports.find(port_index); + if (it == m_handled_ports.end()) + return nullptr; + return (*it).value; +} + +PhysicalAddress AHCIPortHandler::get_identify_metadata_physical_region(u32 port_index) const +{ + dbgln_if(AHCI_DEBUG, "AHCI Port Handler: Get identify metadata physical address of port {} - {}", port_index, (port_index * 512) / PAGE_SIZE); + return m_identify_metadata_pages[(port_index * 512) / PAGE_SIZE].paddr().offset((port_index * 512) % PAGE_SIZE); +} + +AHCI::MaskedBitField AHCIPortHandler::create_pending_ports_interrupts_bitfield() const +{ + return AHCI::MaskedBitField((volatile u32&)m_parent_controller->hba().control_regs.is, m_taken_ports.bit_mask()); +} + +AHCI::HBADefinedCapabilities AHCIPortHandler::hba_capabilities() const +{ + return m_parent_controller->hba_capabilities(); +} + +AHCIPortHandler::~AHCIPortHandler() +{ +} + +bool AHCIPortHandler::handle_irq(const RegisterState&) +{ + dbgln_if(AHCI_DEBUG, "AHCI Port Handler: IRQ received"); + if (m_pending_ports_interrupts.is_zeroed()) + return false; + for (auto port_index : m_pending_ports_interrupts.to_vector()) { + auto port = m_handled_ports.get(port_index); + VERIFY(port.has_value()); + dbgln_if(AHCI_DEBUG, "AHCI Port Handler: Handling IRQ for port {}", port_index); + port.value()->handle_interrupt(); + // We do this to clear the pending interrupt after we handled it. + m_pending_ports_interrupts.set_at(port_index); + } + return true; +} + +} diff --git a/Kernel/Storage/ATA/AHCIPortHandler.h b/Kernel/Storage/ATA/AHCIPortHandler.h new file mode 100644 index 0000000000..f44c992605 --- /dev/null +++ b/Kernel/Storage/ATA/AHCIPortHandler.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +class AsyncBlockDeviceRequest; + +class AHCIController; +class AHCIPort; +class AHCIPortHandler final : public RefCounted + , public IRQHandler { + friend class AHCIController; + +public: + UNMAP_AFTER_INIT static NonnullRefPtr create(AHCIController&, u8 irq, AHCI::MaskedBitField taken_ports); + virtual ~AHCIPortHandler() override; + + RefPtr device_at_port(size_t port_index) const; + virtual StringView purpose() const override { return "SATA Port Handler"sv; } + + AHCI::HBADefinedCapabilities hba_capabilities() const; + NonnullRefPtr hba_controller() const { return m_parent_controller; } + PhysicalAddress get_identify_metadata_physical_region(u32 port_index) const; + + bool is_responsible_for_port_index(u32 port_index) const { return m_taken_ports.is_set_at(port_index); } + +private: + UNMAP_AFTER_INIT AHCIPortHandler(AHCIController&, u8 irq, AHCI::MaskedBitField taken_ports); + + //^ IRQHandler + virtual bool handle_irq(const RegisterState&) override; + + enum class Direction : u8 { + Read, + Write, + }; + + AHCI::MaskedBitField create_pending_ports_interrupts_bitfield() const; + + void start_request(AsyncBlockDeviceRequest&, bool, bool, u16); + void complete_current_request(AsyncDeviceRequest::RequestResult); + + void enumerate_ports(Function callback) const; + RefPtr port_at_index(u32 port_index) const; + + // Data members + HashMap> m_handled_ports; + NonnullRefPtr m_parent_controller; + NonnullRefPtrVector m_identify_metadata_pages; + AHCI::MaskedBitField m_taken_ports; + AHCI::MaskedBitField m_pending_ports_interrupts; +}; +} diff --git a/Kernel/Storage/ATA/ATA.h b/Kernel/Storage/ATA/ATA.h new file mode 100644 index 0000000000..92ac91f9f1 --- /dev/null +++ b/Kernel/Storage/ATA/ATA.h @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2018-2020, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#define ATA_SR_BSY 0x80 +#define ATA_SR_DRDY 0x40 +#define ATA_SR_DF 0x20 +#define ATA_SR_DSC 0x10 +#define ATA_SR_DRQ 0x08 +#define ATA_SR_CORR 0x04 +#define ATA_SR_IDX 0x02 +#define ATA_SR_ERR 0x01 + +#define ATA_ER_BBK 0x80 +#define ATA_ER_UNC 0x40 +#define ATA_ER_MC 0x20 +#define ATA_ER_IDNF 0x10 +#define ATA_ER_MCR 0x08 +#define ATA_ER_ABRT 0x04 +#define ATA_ER_TK0NF 0x02 +#define ATA_ER_AMNF 0x01 + +#define ATA_CMD_READ_PIO 0x20 +#define ATA_CMD_READ_PIO_EXT 0x24 +#define ATA_CMD_READ_DMA 0xC8 +#define ATA_CMD_READ_DMA_EXT 0x25 +#define ATA_CMD_WRITE_PIO 0x30 +#define ATA_CMD_WRITE_PIO_EXT 0x34 +#define ATA_CMD_WRITE_DMA 0xCA +#define ATA_CMD_WRITE_DMA_EXT 0x35 +#define ATA_CMD_CACHE_FLUSH 0xE7 +#define ATA_CMD_CACHE_FLUSH_EXT 0xEA +#define ATA_CMD_PACKET 0xA0 +#define ATA_CMD_IDENTIFY_PACKET 0xA1 +#define ATA_CMD_IDENTIFY 0xEC + +#define ATAPI_CMD_READ 0xA8 +#define ATAPI_CMD_EJECT 0x1B + +#define ATA_IDENT_DEVICETYPE 0 +#define ATA_IDENT_CYLINDERS 2 +#define ATA_IDENT_HEADS 6 +#define ATA_IDENT_SECTORS 12 +#define ATA_IDENT_SERIAL 20 +#define ATA_IDENT_MODEL 54 +#define ATA_IDENT_CAPABILITIES 98 +#define ATA_IDENT_FIELDVALID 106 +#define ATA_IDENT_MAX_LBA 120 +#define ATA_IDENT_COMMANDSETS 164 +#define ATA_IDENT_MAX_LBA_EXT 200 + +#define ATA_USE_LBA_ADDRESSING (1 << 6) + +#define IDE_ATA 0x00 +#define IDE_ATAPI 0x01 + +#define ATA_REG_DATA 0x00 +#define ATA_REG_ERROR 0x01 +#define ATA_REG_FEATURES 0x01 +#define ATA_REG_SECCOUNT0 0x02 +#define ATA_REG_LBA0 0x03 +#define ATA_REG_LBA1 0x04 +#define ATA_REG_LBA2 0x05 +#define ATA_REG_HDDEVSEL 0x06 +#define ATA_REG_COMMAND 0x07 +#define ATA_REG_STATUS 0x07 +#define ATA_REG_SECCOUNT1 0x08 +#define ATA_REG_LBA3 0x09 +#define ATA_REG_LBA4 0x0A +#define ATA_REG_LBA5 0x0B +#define ATA_CTL_CONTROL 0x00 +#define ATA_CTL_ALTSTATUS 0x00 +#define ATA_CTL_DEVADDRESS 0x01 + +#define ATA_CAP_LBA 0x200 + +#include + +namespace Kernel { +struct [[gnu::packed]] ATAIdentifyBlock { + u16 general_configuration; + u16 obsolete; + u16 specific_configuration; + + u16 obsolete2; + u16 retired[2]; + u16 obsolete3; + + u16 reserved_for_cfa[2]; + u16 retired2; + u16 serial_number[10]; + + u16 retired3[2]; + u16 obsolete4; + + u16 firmware_revision[4]; + u16 model_number[20]; + + u16 maximum_logical_sectors_per_drq; + u16 trusted_computing_features; + u16 capabilities[2]; + u16 obsolete5[2]; + u16 validity_flags; + u16 obsolete6[5]; + + u16 security_features; + + u32 max_28_bit_addressable_logical_sector; + u16 obsolete7; + u16 dma_modes; + u16 pio_modes; + + u16 minimum_multiword_dma_transfer_cycle; + u16 recommended_multiword_dma_transfer_cycle; + + u16 minimum_multiword_pio_transfer_cycle_without_flow_control; + u16 minimum_multiword_pio_transfer_cycle_with_flow_control; + + u16 additional_supported; + u16 reserved3[5]; + u16 queue_depth; + + u16 serial_ata_capabilities; + u16 serial_ata_additional_capabilities; + u16 serial_ata_features_supported; + u16 serial_ata_features_enabled; + u16 major_version_number; + u16 minor_version_number; + u16 commands_and_feature_sets_supported[3]; + u16 commands_and_feature_sets_supported_or_enabled[3]; + u16 ultra_dma_modes; + + u16 timing_for_security_features[2]; + u16 apm_level; + u16 master_password_id; + + u16 hardware_reset_results; + u16 obsolete8; + + u16 stream_minimum_request_time; + u16 streaming_transfer_time_for_dma; + u16 streaming_access_latency; + u16 streaming_performance_granularity[2]; + + u64 user_addressable_logical_sectors_count; + + u16 streaming_transfer_time_for_pio; + u16 max_512_byte_blocks_per_data_set_management_command; + u16 physical_sector_size_to_logical_sector_size; + u16 inter_seek_delay_for_acoustic_testing; + u16 world_wide_name[4]; + u16 reserved4[4]; + u16 obsolete9; + + u32 logical_sector_size; + + u16 commands_and_feature_sets_supported2; + u16 commands_and_feature_sets_supported_or_enabled2; + + u16 reserved_for_expanded_supported_and_enabled_settings[6]; + u16 obsolete10; + + u16 security_status; + u16 vendor_specific[31]; + u16 reserved_for_cfa2[8]; + u16 device_nominal_form_factor; + u16 data_set_management_command_support; + u16 additional_product_id[4]; + u16 reserved5[2]; + u16 current_media_serial_number[30]; + u16 sct_command_transport; + u16 reserved6[2]; + + u16 logical_sectors_alignment_within_physical_sector; + + u32 write_read_verify_sector_mode_3_count; + u32 write_read_verify_sector_mode_2_count; + + u16 obsolete11[3]; + u16 nominal_media_rotation_rate; + u16 reserved7; + u16 obsolete12; + u16 write_read_verify_feature_set_current_mode; + u16 reserved8; + u16 transport_major_version_number; + u16 transport_minor_version_number; + u16 reserved9[6]; + + u64 extended_user_addressable_logical_sectors_count; + + u16 minimum_512_byte_data_blocks_per_download_microcode_operation; + u16 max_512_byte_data_blocks_per_download_microcode_operation; + + u16 reserved10[19]; + u16 integrity; +}; +}; diff --git a/Kernel/Storage/ATA/ATAController.h b/Kernel/Storage/ATA/ATAController.h new file mode 100644 index 0000000000..de0643f12e --- /dev/null +++ b/Kernel/Storage/ATA/ATAController.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace Kernel { + +class AsyncBlockDeviceRequest; + +class ATADevice; +class ATAController + : public StorageController + , public Weakable { +public: + virtual void start_request(const ATADevice&, AsyncBlockDeviceRequest&) = 0; + +protected: + ATAController() = default; +}; +} diff --git a/Kernel/Storage/ATA/ATADevice.cpp b/Kernel/Storage/ATA/ATADevice.cpp new file mode 100644 index 0000000000..7e24a0cc68 --- /dev/null +++ b/Kernel/Storage/ATA/ATADevice.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +ATADevice::ATADevice(const ATAController& controller, ATADevice::Address ata_address, unsigned minor_number, u16 capabilities, u16 logical_sector_size, u64 max_addressable_block, NonnullOwnPtr early_storage_name) + : StorageDevice(StorageManagement::major_number(), minor_number, logical_sector_size, max_addressable_block, move(early_storage_name)) + , m_controller(controller) + , m_ata_address(ata_address) + , m_capabilities(capabilities) +{ +} + +ATADevice::~ATADevice() +{ +} + +void ATADevice::start_request(AsyncBlockDeviceRequest& request) +{ + auto controller = m_controller.strong_ref(); + VERIFY(controller); + controller->start_request(*this, request); +} + +} diff --git a/Kernel/Storage/ATA/ATADevice.h b/Kernel/Storage/ATA/ATADevice.h new file mode 100644 index 0000000000..9c32d2d110 --- /dev/null +++ b/Kernel/Storage/ATA/ATADevice.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Kernel { + +class ATADevice : public StorageDevice { +public: + // Note: For IDE drives, port means Primary or Secondary (0 or 1), + // and subport means Master or Slave (0 or 1). + // For SATA drives (AHCI driven HBAs), a port can be a number from 0 to 31, + // and subport can be a number from 0 to 14 (only 15 devices are allowed to + // be connected to one SATA port multiplier). + struct Address { + // FIXME: u32 for this value is wasteful, because even AHCI only support 32 ports + u32 port; + u8 subport; + }; + +public: + virtual ~ATADevice() override; + + // ^BlockDevice + virtual void start_request(AsyncBlockDeviceRequest&) override; + + u16 ata_capabilites() const { return m_capabilities; } + const Address& ata_address() const { return m_ata_address; } + +protected: + ATADevice(const ATAController&, Address, unsigned, u16, u16, u64, NonnullOwnPtr); + + WeakPtr m_controller; + const Address m_ata_address; + const u16 m_capabilities; +}; + +} diff --git a/Kernel/Storage/ATA/ATADiskDevice.cpp b/Kernel/Storage/ATA/ATADiskDevice.cpp new file mode 100644 index 0000000000..3d7af895be --- /dev/null +++ b/Kernel/Storage/ATA/ATADiskDevice.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +NonnullRefPtr ATADiskDevice::create(const ATAController& controller, ATADevice::Address ata_address, u16 capabilities, u16 logical_sector_size, u64 max_addressable_block) +{ + auto minor_device_number = StorageManagement::minor_number(); + + // FIXME: We need a way of formatting strings with KString. + auto device_name = String::formatted("hd{:c}", 'a' + minor_device_number); + auto device_name_kstring = KString::must_create(device_name.view()); + + auto disk_device_or_error = DeviceManagement::try_create_device(controller, ata_address, minor_device_number, capabilities, logical_sector_size, max_addressable_block, move(device_name_kstring)); + // FIXME: Find a way to propagate errors + VERIFY(!disk_device_or_error.is_error()); + return disk_device_or_error.release_value(); +} + +ATADiskDevice::ATADiskDevice(const ATAController& controller, ATADevice::Address ata_address, unsigned minor_number, u16 capabilities, u16 logical_sector_size, u64 max_addressable_block, NonnullOwnPtr early_storage_name) + : ATADevice(controller, ata_address, minor_number, capabilities, logical_sector_size, max_addressable_block, move(early_storage_name)) +{ +} + +ATADiskDevice::~ATADiskDevice() +{ +} + +StringView ATADiskDevice::class_name() const +{ + return "ATADiskDevice"; +} + +} diff --git a/Kernel/Storage/ATA/ATADiskDevice.h b/Kernel/Storage/ATA/ATADiskDevice.h new file mode 100644 index 0000000000..6a5492f838 --- /dev/null +++ b/Kernel/Storage/ATA/ATADiskDevice.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace Kernel { + +class IDEController; +class IDEChannel; +class ATADiskDevice final : public ATADevice { + friend class IDEController; + friend class DeviceManagement; + +public: + static NonnullRefPtr create(const ATAController&, ATADevice::Address, u16 capabilities, u16 logical_sector_size, u64 max_addressable_block); + virtual ~ATADiskDevice() override; + + // ^StorageDevice + virtual CommandSet command_set() const override { return CommandSet::ATA; } + +private: + ATADiskDevice(const ATAController&, Address, unsigned, u16, u16, u64, NonnullOwnPtr); + + // ^DiskDevice + virtual StringView class_name() const override; +}; + +} diff --git a/Kernel/Storage/ATA/ATAPIDiscDevice.cpp b/Kernel/Storage/ATA/ATAPIDiscDevice.cpp new file mode 100644 index 0000000000..7c60f54a14 --- /dev/null +++ b/Kernel/Storage/ATA/ATAPIDiscDevice.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +NonnullRefPtr ATAPIDiscDevice::create(const ATAController& controller, ATADevice::Address ata_address, u16 capabilities, u64 max_addressable_block) +{ + auto minor_device_number = StorageManagement::minor_number(); + + // FIXME: We need a way of formatting strings with KString. + auto device_name = String::formatted("hd{:c}", 'a' + minor_device_number); + auto device_name_kstring = KString::must_create(device_name.view()); + + auto disc_device_or_error = DeviceManagement::try_create_device(controller, ata_address, minor_device_number, capabilities, max_addressable_block, move(device_name_kstring)); + // FIXME: Find a way to propagate errors + VERIFY(!disc_device_or_error.is_error()); + return disc_device_or_error.release_value(); +} + +ATAPIDiscDevice::ATAPIDiscDevice(const ATAController& controller, ATADevice::Address ata_address, unsigned minor_number, u16 capabilities, u64 max_addressable_block, NonnullOwnPtr early_storage_name) + : ATADevice(controller, ata_address, minor_number, capabilities, 0, max_addressable_block, move(early_storage_name)) +{ +} + +ATAPIDiscDevice::~ATAPIDiscDevice() +{ +} + +StringView ATAPIDiscDevice::class_name() const +{ + return "ATAPIDiscDevice"; +} + +} diff --git a/Kernel/Storage/ATA/ATAPIDiscDevice.h b/Kernel/Storage/ATA/ATAPIDiscDevice.h new file mode 100644 index 0000000000..0556fb97d1 --- /dev/null +++ b/Kernel/Storage/ATA/ATAPIDiscDevice.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace Kernel { + +class IDEController; +class IDEChannel; +class ATAPIDiscDevice final : public ATADevice { + friend class IDEController; + friend class DeviceManagement; + +public: + static NonnullRefPtr create(const ATAController&, ATADevice::Address, u16 capabilities, u64 max_addressable_block); + virtual ~ATAPIDiscDevice() override; + + // ^StorageDevice + virtual CommandSet command_set() const override { return CommandSet::SCSI; } + +private: + ATAPIDiscDevice(const ATAController&, Address, unsigned, u16, u64, NonnullOwnPtr); + + // ^DiskDevice + virtual StringView class_name() const override; +}; + +} diff --git a/Kernel/Storage/ATA/BMIDEChannel.cpp b/Kernel/Storage/ATA/BMIDEChannel.cpp new file mode 100644 index 0000000000..8acf1dce5d --- /dev/null +++ b/Kernel/Storage/ATA/BMIDEChannel.cpp @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +UNMAP_AFTER_INIT NonnullRefPtr BMIDEChannel::create(const IDEController& ide_controller, IDEChannel::IOAddressGroup io_group, IDEChannel::ChannelType type) +{ + return adopt_ref(*new BMIDEChannel(ide_controller, io_group, type)); +} + +UNMAP_AFTER_INIT NonnullRefPtr BMIDEChannel::create(const IDEController& ide_controller, u8 irq, IDEChannel::IOAddressGroup io_group, IDEChannel::ChannelType type) +{ + return adopt_ref(*new BMIDEChannel(ide_controller, irq, io_group, type)); +} + +UNMAP_AFTER_INIT BMIDEChannel::BMIDEChannel(const IDEController& controller, IDEChannel::IOAddressGroup io_group, IDEChannel::ChannelType type) + : IDEChannel(controller, io_group, type) +{ + initialize(); +} + +UNMAP_AFTER_INIT BMIDEChannel::BMIDEChannel(const IDEController& controller, u8 irq, IDEChannel::IOAddressGroup io_group, IDEChannel::ChannelType type) + : IDEChannel(controller, irq, io_group, type) +{ + initialize(); +} + +UNMAP_AFTER_INIT void BMIDEChannel::initialize() +{ + VERIFY(m_io_group.bus_master_base().has_value()); + // Let's try to set up DMA transfers. + PCI::enable_bus_mastering(m_parent_controller->pci_address()); + m_prdt_page = MM.allocate_supervisor_physical_page(); + m_dma_buffer_page = MM.allocate_supervisor_physical_page(); + if (m_dma_buffer_page.is_null() || m_prdt_page.is_null()) + return; + { + auto region_or_error = MM.allocate_kernel_region(m_prdt_page->paddr(), PAGE_SIZE, "IDE PRDT", Memory::Region::Access::ReadWrite); + if (region_or_error.is_error()) + TODO(); + m_prdt_region = region_or_error.release_value(); + } + { + auto region_or_error = MM.allocate_kernel_region(m_dma_buffer_page->paddr(), PAGE_SIZE, "IDE DMA region", Memory::Region::Access::ReadWrite); + if (region_or_error.is_error()) + TODO(); + m_dma_buffer_region = region_or_error.release_value(); + } + + prdt().end_of_table = 0x8000; + + // clear bus master interrupt status + m_io_group.bus_master_base().value().offset(2).out(m_io_group.bus_master_base().value().offset(2).in() | 4); +} + +static void print_ide_status(u8 status) +{ + dbgln("BMIDEChannel: print_ide_status: DRQ={} BSY={}, DRDY={}, DSC={}, DF={}, CORR={}, IDX={}, ERR={}", + (status & ATA_SR_DRQ) != 0, + (status & ATA_SR_BSY) != 0, + (status & ATA_SR_DRDY) != 0, + (status & ATA_SR_DSC) != 0, + (status & ATA_SR_DF) != 0, + (status & ATA_SR_CORR) != 0, + (status & ATA_SR_IDX) != 0, + (status & ATA_SR_ERR) != 0); +} + +bool BMIDEChannel::handle_irq(const RegisterState&) +{ + u8 status = m_io_group.io_base().offset(ATA_REG_STATUS).in(); + + m_entropy_source.add_random_event(status); + + VERIFY(m_io_group.bus_master_base().has_value()); + u8 bstatus = m_io_group.bus_master_base().value().offset(2).in(); + if (!(bstatus & 0x4)) { + // interrupt not from this device, ignore + dbgln_if(PATA_DEBUG, "BMIDEChannel: ignore interrupt"); + return false; + } + // clear bus master interrupt status + m_io_group.bus_master_base().value().offset(2).out(m_io_group.bus_master_base().value().offset(2).in() | 4); + + SpinlockLocker lock(m_request_lock); + dbgln_if(PATA_DEBUG, "BMIDEChannel: interrupt: DRQ={}, BSY={}, DRDY={}", + (status & ATA_SR_DRQ) != 0, + (status & ATA_SR_BSY) != 0, + (status & ATA_SR_DRDY) != 0); + + if (!m_current_request) { + dbgln("BMIDEChannel: IRQ but no pending request!"); + return false; + } + + if (status & ATA_SR_ERR) { + print_ide_status(status); + m_device_error = m_io_group.io_base().offset(ATA_REG_ERROR).in(); + dbgln("BMIDEChannel: Error {:#02x}!", (u8)m_device_error); + try_disambiguate_error(); + complete_current_request(AsyncDeviceRequest::Failure); + return true; + } + m_device_error = 0; + complete_current_request(AsyncDeviceRequest::Success); + return true; +} + +void BMIDEChannel::complete_current_request(AsyncDeviceRequest::RequestResult result) +{ + // NOTE: this may be called from the interrupt handler! + VERIFY(m_current_request); + VERIFY(m_request_lock.is_locked()); + + // Now schedule reading back the buffer as soon as we leave the irq handler. + // This is important so that we can safely write the buffer back, + // which could cause page faults. Note that this may be called immediately + // before Processor::deferred_call_queue returns! + g_io_work->queue([this, result]() { + dbgln_if(PATA_DEBUG, "BMIDEChannel::complete_current_request result: {}", (int)result); + SpinlockLocker lock(m_request_lock); + VERIFY(m_current_request); + auto current_request = m_current_request; + m_current_request.clear(); + + if (result == AsyncDeviceRequest::Success) { + if (current_request->request_type() == AsyncBlockDeviceRequest::Read) { + if (auto result = current_request->write_to_buffer(current_request->buffer(), m_dma_buffer_region->vaddr().as_ptr(), 512 * current_request->block_count()); result.is_error()) { + lock.unlock(); + current_request->complete(AsyncDeviceRequest::MemoryFault); + return; + } + } + + // I read somewhere that this may trigger a cache flush so let's do it. + VERIFY(m_io_group.bus_master_base().has_value()); + m_io_group.bus_master_base().value().offset(2).out(m_io_group.bus_master_base().value().offset(2).in() | 0x6); + } + + lock.unlock(); + current_request->complete(result); + }); +} + +void BMIDEChannel::ata_write_sectors(bool slave_request, u16 capabilities) +{ + VERIFY(m_lock.is_locked()); + VERIFY(!m_current_request.is_null()); + VERIFY(m_current_request->block_count() <= 256); + + SpinlockLocker m_lock(m_request_lock); + dbgln_if(PATA_DEBUG, "BMIDEChannel::ata_write_sectors ({} x {})", m_current_request->block_index(), m_current_request->block_count()); + + prdt().offset = m_dma_buffer_page->paddr().get(); + prdt().size = 512 * m_current_request->block_count(); + + if (auto result = m_current_request->read_from_buffer(m_current_request->buffer(), m_dma_buffer_region->vaddr().as_ptr(), 512 * m_current_request->block_count()); result.is_error()) { + complete_current_request(AsyncDeviceRequest::MemoryFault); + return; + } + + // Note: This is a fix for a quirk for an IDE controller on ICH7 machine. + // We need to select the drive and then we wait 10 microseconds... and it doesn't hurt anything + m_io_group.io_base().offset(ATA_REG_HDDEVSEL).out(0xA0 | ((slave_request ? 1 : 0) << 4)); + IO::delay(10); + + VERIFY(prdt().size <= PAGE_SIZE); + VERIFY(m_io_group.bus_master_base().has_value()); + // Stop bus master + m_io_group.bus_master_base().value().out(0); + + // Write the PRDT location + m_io_group.bus_master_base().value().offset(4).out(m_prdt_page->paddr().get()); + + // Turn on "Interrupt" and "Error" flag. The error flag should be cleared by hardware. + m_io_group.bus_master_base().value().offset(2).out(m_io_group.bus_master_base().value().offset(2).in() | 0x6); + + ata_access(Direction::Write, slave_request, m_current_request->block_index(), m_current_request->block_count(), capabilities); + + // Start bus master + m_io_group.bus_master_base().value().out(0x1); +} + +void BMIDEChannel::send_ata_io_command(LBAMode lba_mode, Direction direction) const +{ + if (lba_mode != LBAMode::FortyEightBit) { + m_io_group.io_base().offset(ATA_REG_COMMAND).out(direction == Direction::Read ? ATA_CMD_READ_DMA : ATA_CMD_WRITE_DMA); + } else { + m_io_group.io_base().offset(ATA_REG_COMMAND).out(direction == Direction::Read ? ATA_CMD_READ_DMA_EXT : ATA_CMD_WRITE_DMA_EXT); + } +} + +void BMIDEChannel::ata_read_sectors(bool slave_request, u16 capabilities) +{ + VERIFY(m_lock.is_locked()); + VERIFY(!m_current_request.is_null()); + VERIFY(m_current_request->block_count() <= 256); + + SpinlockLocker m_lock(m_request_lock); + dbgln_if(PATA_DEBUG, "BMIDEChannel::ata_read_sectors ({} x {})", m_current_request->block_index(), m_current_request->block_count()); + + // Note: This is a fix for a quirk for an IDE controller on ICH7 machine. + // We need to select the drive and then we wait 10 microseconds... and it doesn't hurt anything + m_io_group.io_base().offset(ATA_REG_HDDEVSEL).out(0xA0 | ((slave_request ? 1 : 0) << 4)); + IO::delay(10); + + prdt().offset = m_dma_buffer_page->paddr().get(); + prdt().size = 512 * m_current_request->block_count(); + + VERIFY(prdt().size <= PAGE_SIZE); + + VERIFY(m_io_group.bus_master_base().has_value()); + // Stop bus master + m_io_group.bus_master_base().value().out(0); + + // Write the PRDT location + m_io_group.bus_master_base().value().offset(4).out(m_prdt_page->paddr().get()); + + // Set transfer direction + m_io_group.bus_master_base().value().out(0x8); + + // Turn on "Interrupt" and "Error" flag. The error flag should be cleared by hardware. + m_io_group.bus_master_base().value().offset(2).out(m_io_group.bus_master_base().value().offset(2).in() | 0x6); + + ata_access(Direction::Read, slave_request, m_current_request->block_index(), m_current_request->block_count(), capabilities); + + // Start bus master + m_io_group.bus_master_base().value().out(0x9); +} + +} diff --git a/Kernel/Storage/ATA/BMIDEChannel.h b/Kernel/Storage/ATA/BMIDEChannel.h new file mode 100644 index 0000000000..5f948149e5 --- /dev/null +++ b/Kernel/Storage/ATA/BMIDEChannel.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Kernel { + +class AsyncBlockDeviceRequest; + +struct [[gnu::packed]] PhysicalRegionDescriptor { + u32 offset; + u16 size { 0 }; + u16 end_of_table { 0 }; +}; + +class IDEController; +class BMIDEChannel final : public IDEChannel { + friend class IDEController; + friend class PATADiskDevice; + +public: + static NonnullRefPtr create(const IDEController&, IDEChannel::IOAddressGroup, IDEChannel::ChannelType type); + static NonnullRefPtr create(const IDEController&, u8 irq, IDEChannel::IOAddressGroup, IDEChannel::ChannelType type); + virtual ~BMIDEChannel() override {}; + + virtual bool is_dma_enabled() const override { return true; }; + +private: + BMIDEChannel(const IDEController&, IDEChannel::IOAddressGroup, IDEChannel::ChannelType type); + BMIDEChannel(const IDEController&, u8 irq, IDEChannel::IOAddressGroup, IDEChannel::ChannelType type); + void initialize(); + + void complete_current_request(AsyncDeviceRequest::RequestResult); + + //^ IRQHandler + virtual bool handle_irq(const RegisterState&) override; + + //* IDEChannel + virtual void send_ata_io_command(LBAMode lba_mode, Direction direction) const override; + virtual void ata_read_sectors(bool, u16) override; + virtual void ata_write_sectors(bool, u16) override; + + PhysicalRegionDescriptor& prdt() { return *reinterpret_cast(m_prdt_region->vaddr().as_ptr()); } + OwnPtr m_prdt_region; + OwnPtr m_dma_buffer_region; + RefPtr m_prdt_page; + RefPtr m_dma_buffer_page; +}; +} diff --git a/Kernel/Storage/ATA/IDEChannel.cpp b/Kernel/Storage/ATA/IDEChannel.cpp new file mode 100644 index 0000000000..ef66424870 --- /dev/null +++ b/Kernel/Storage/ATA/IDEChannel.cpp @@ -0,0 +1,546 @@ +/* + * Copyright (c) 2018-2021, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +#define PATA_PRIMARY_IRQ 14 +#define PATA_SECONDARY_IRQ 15 + +UNMAP_AFTER_INIT NonnullRefPtr IDEChannel::create(const IDEController& controller, IOAddressGroup io_group, ChannelType type) +{ + return adopt_ref(*new IDEChannel(controller, io_group, type)); +} + +UNMAP_AFTER_INIT NonnullRefPtr IDEChannel::create(const IDEController& controller, u8 irq, IOAddressGroup io_group, ChannelType type) +{ + return adopt_ref(*new IDEChannel(controller, irq, io_group, type)); +} + +RefPtr IDEChannel::master_device() const +{ + return m_master; +} + +RefPtr IDEChannel::slave_device() const +{ + return m_slave; +} + +UNMAP_AFTER_INIT void IDEChannel::initialize() +{ + disable_irq(); + dbgln_if(PATA_DEBUG, "IDEChannel: {} IO base: {}", channel_type_string(), m_io_group.io_base()); + dbgln_if(PATA_DEBUG, "IDEChannel: {} control base: {}", channel_type_string(), m_io_group.control_base()); + if (m_io_group.bus_master_base().has_value()) + dbgln_if(PATA_DEBUG, "IDEChannel: {} bus master base: {}", channel_type_string(), m_io_group.bus_master_base().value()); + else + dbgln_if(PATA_DEBUG, "IDEChannel: {} bus master base disabled", channel_type_string()); + m_parent_controller->enable_pin_based_interrupts(); + + // reset the channel + u8 device_control = m_io_group.control_base().in(); + // Wait 30 milliseconds + IO::delay(30000); + m_io_group.control_base().out(device_control | (1 << 2)); + // Wait 30 milliseconds + IO::delay(30000); + m_io_group.control_base().out(device_control); + // Wait up to 30 seconds before failing + if (!wait_until_not_busy(false, 30000)) { + dbgln("IDEChannel: reset failed, busy flag on master stuck"); + return; + } + // Wait up to 30 seconds before failing + if (!wait_until_not_busy(true, 30000)) { + dbgln("IDEChannel: reset failed, busy flag on slave stuck"); + return; + } + + detect_disks(); + + // Note: calling to detect_disks could generate an interrupt, clear it if that's the case + clear_pending_interrupts(); +} + +UNMAP_AFTER_INIT IDEChannel::IDEChannel(const IDEController& controller, u8 irq, IOAddressGroup io_group, ChannelType type) + : IRQHandler(irq) + , m_channel_type(type) + , m_io_group(io_group) + , m_parent_controller(controller) +{ + initialize(); +} + +UNMAP_AFTER_INIT IDEChannel::IDEChannel(const IDEController& controller, IOAddressGroup io_group, ChannelType type) + : IRQHandler(type == ChannelType::Primary ? PATA_PRIMARY_IRQ : PATA_SECONDARY_IRQ) + , m_channel_type(type) + , m_io_group(io_group) + , m_parent_controller(controller) +{ + initialize(); +} + +void IDEChannel::clear_pending_interrupts() const +{ + m_io_group.io_base().offset(ATA_REG_STATUS).in(); +} + +UNMAP_AFTER_INIT IDEChannel::~IDEChannel() +{ +} + +void IDEChannel::start_request(AsyncBlockDeviceRequest& request, bool is_slave, u16 capabilities) +{ + MutexLocker locker(m_lock); + VERIFY(m_current_request.is_null()); + + dbgln_if(PATA_DEBUG, "IDEChannel::start_request"); + + m_current_request = request; + m_current_request_block_index = 0; + m_current_request_flushing_cache = false; + + if (request.request_type() == AsyncBlockDeviceRequest::Read) + ata_read_sectors(is_slave, capabilities); + else + ata_write_sectors(is_slave, capabilities); +} + +void IDEChannel::complete_current_request(AsyncDeviceRequest::RequestResult result) +{ + // NOTE: this may be called from the interrupt handler! + VERIFY(m_current_request); + VERIFY(m_request_lock.is_locked()); + + // Now schedule reading back the buffer as soon as we leave the irq handler. + // This is important so that we can safely write the buffer back, + // which could cause page faults. Note that this may be called immediately + // before Processor::deferred_call_queue returns! + g_io_work->queue([this, result]() { + dbgln_if(PATA_DEBUG, "IDEChannel::complete_current_request result: {}", (int)result); + MutexLocker locker(m_lock); + VERIFY(m_current_request); + auto current_request = m_current_request; + m_current_request.clear(); + current_request->complete(result); + }); +} + +static void print_ide_status(u8 status) +{ + dbgln("IDEChannel: print_ide_status: DRQ={} BSY={}, DRDY={}, DSC={}, DF={}, CORR={}, IDX={}, ERR={}", + (status & ATA_SR_DRQ) != 0, + (status & ATA_SR_BSY) != 0, + (status & ATA_SR_DRDY) != 0, + (status & ATA_SR_DSC) != 0, + (status & ATA_SR_DF) != 0, + (status & ATA_SR_CORR) != 0, + (status & ATA_SR_IDX) != 0, + (status & ATA_SR_ERR) != 0); +} + +void IDEChannel::try_disambiguate_error() +{ + VERIFY(m_lock.is_locked()); + dbgln("IDEChannel: Error cause:"); + + switch (m_device_error) { + case ATA_ER_BBK: + dbgln("IDEChannel: - Bad block"); + break; + case ATA_ER_UNC: + dbgln("IDEChannel: - Uncorrectable data"); + break; + case ATA_ER_MC: + dbgln("IDEChannel: - Media changed"); + break; + case ATA_ER_IDNF: + dbgln("IDEChannel: - ID mark not found"); + break; + case ATA_ER_MCR: + dbgln("IDEChannel: - Media change request"); + break; + case ATA_ER_ABRT: + dbgln("IDEChannel: - Command aborted"); + break; + case ATA_ER_TK0NF: + dbgln("IDEChannel: - Track 0 not found"); + break; + case ATA_ER_AMNF: + dbgln("IDEChannel: - No address mark"); + break; + default: + dbgln("IDEChannel: - No one knows"); + break; + } +} + +bool IDEChannel::handle_irq(const RegisterState&) +{ + u8 status = m_io_group.io_base().offset(ATA_REG_STATUS).in(); + + m_entropy_source.add_random_event(status); + + SpinlockLocker lock(m_request_lock); + dbgln_if(PATA_DEBUG, "IDEChannel: interrupt: DRQ={}, BSY={}, DRDY={}", + (status & ATA_SR_DRQ) != 0, + (status & ATA_SR_BSY) != 0, + (status & ATA_SR_DRDY) != 0); + + if (!m_current_request) { + dbgln("IDEChannel: IRQ but no pending request!"); + return false; + } + + if (status & ATA_SR_ERR) { + print_ide_status(status); + m_device_error = m_io_group.io_base().offset(ATA_REG_ERROR).in(); + dbgln("IDEChannel: Error {:#02x}!", (u8)m_device_error); + try_disambiguate_error(); + complete_current_request(AsyncDeviceRequest::Failure); + return true; + } + m_device_error = 0; + + // Now schedule reading/writing the buffer as soon as we leave the irq handler. + // This is important so that we can safely access the buffers, which could + // trigger page faults + g_io_work->queue([this]() { + MutexLocker locker(m_lock); + SpinlockLocker lock(m_request_lock); + if (m_current_request->request_type() == AsyncBlockDeviceRequest::Read) { + dbgln_if(PATA_DEBUG, "IDEChannel: Read block {}/{}", m_current_request_block_index, m_current_request->block_count()); + + if (ata_do_read_sector()) { + if (++m_current_request_block_index >= m_current_request->block_count()) { + complete_current_request(AsyncDeviceRequest::Success); + return; + } + // Wait for the next block + enable_irq(); + } + } else { + if (!m_current_request_flushing_cache) { + dbgln_if(PATA_DEBUG, "IDEChannel: Wrote block {}/{}", m_current_request_block_index, m_current_request->block_count()); + if (++m_current_request_block_index >= m_current_request->block_count()) { + // We read the last block, flush cache + VERIFY(!m_current_request_flushing_cache); + m_current_request_flushing_cache = true; + m_io_group.io_base().offset(ATA_REG_COMMAND).out(ATA_CMD_CACHE_FLUSH); + } else { + // Read next block + ata_do_write_sector(); + } + } else { + complete_current_request(AsyncDeviceRequest::Success); + } + } + }); + return true; +} + +static void io_delay() +{ + for (int i = 0; i < 4; ++i) + IO::in8(0x3f6); +} + +bool IDEChannel::wait_until_not_busy(bool slave, size_t milliseconds_timeout) +{ + IO::delay(20); + m_io_group.io_base().offset(ATA_REG_HDDEVSEL).out(0xA0 | (slave << 4)); // First, we need to select the drive itself + IO::delay(20); + size_t time_elapsed = 0; + while (m_io_group.control_base().in() & ATA_SR_BSY && time_elapsed <= milliseconds_timeout) { + IO::delay(1000); + time_elapsed++; + } + return time_elapsed <= milliseconds_timeout; +} + +bool IDEChannel::wait_until_not_busy(size_t milliseconds_timeout) +{ + size_t time_elapsed = 0; + while (m_io_group.control_base().in() & ATA_SR_BSY && time_elapsed <= milliseconds_timeout) { + IO::delay(1000); + time_elapsed++; + } + return time_elapsed <= milliseconds_timeout; +} + +StringView IDEChannel::channel_type_string() const +{ + if (m_channel_type == ChannelType::Primary) + return "Primary"sv; + + return "Secondary"sv; +} + +UNMAP_AFTER_INIT void IDEChannel::detect_disks() +{ + auto channel_string = [](u8 i) -> StringView { + if (i == 0) + return "master"sv; + + return "slave"sv; + }; + + // There are only two possible disks connected to a channel + for (auto i = 0; i < 2; i++) { + // We need to select the drive and then we wait 20 microseconds... and it doesn't hurt anything so let's just do it. + IO::delay(20); + m_io_group.io_base().offset(ATA_REG_HDDEVSEL).out(0xA0 | (i << 4)); // First, we need to select the drive itself + IO::delay(20); + + auto status = m_io_group.control_base().in(); + if (status == 0x0) { + dbgln_if(PATA_DEBUG, "IDEChannel: No {} {} disk detected!", channel_type_string(), channel_string(i)); + continue; + } + + m_io_group.io_base().offset(ATA_REG_SECCOUNT0).out(0); + m_io_group.io_base().offset(ATA_REG_LBA0).out(0); + m_io_group.io_base().offset(ATA_REG_LBA1).out(0); + m_io_group.io_base().offset(ATA_REG_LBA2).out(0); + m_io_group.io_base().offset(ATA_REG_COMMAND).out(ATA_CMD_IDENTIFY); // Send the ATA_IDENTIFY command + + // Wait 10 second for the BSY flag to clear + if (!wait_until_not_busy(2000)) { + dbgln_if(PATA_DEBUG, "IDEChannel: No {} {} disk detected, BSY flag was not reset!", channel_type_string(), channel_string(i)); + continue; + } + + bool check_for_atapi = false; + bool device_presence = true; + bool command_set_is_atapi = false; + + size_t milliseconds_elapsed = 0; + for (;;) { + // Wait about 10 seconds + if (milliseconds_elapsed > 2000) + break; + u8 status = m_io_group.control_base().in(); + if (status & ATA_SR_ERR) { + dbgln_if(PATA_DEBUG, "IDEChannel: {} {} device is not ATA. Will check for ATAPI.", channel_type_string(), channel_string(i)); + check_for_atapi = true; + break; + } + + if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRQ)) { + dbgln_if(PATA_DEBUG, "IDEChannel: {} {} device appears to be ATA.", channel_type_string(), channel_string(i)); + break; + } + + if (status == 0 || status == 0xFF) { + dbgln_if(PATA_DEBUG, "IDEChannel: {} {} device presence - none.", channel_type_string(), channel_string(i)); + device_presence = false; + break; + } + + IO::delay(1000); + milliseconds_elapsed++; + } + if (!device_presence) { + continue; + } + if (milliseconds_elapsed > 10000) { + dbgln_if(PATA_DEBUG, "IDEChannel: {} {} device state unknown. Timeout exceeded.", channel_type_string(), channel_string(i)); + continue; + } + + if (check_for_atapi) { + u8 cl = m_io_group.io_base().offset(ATA_REG_LBA1).in(); + u8 ch = m_io_group.io_base().offset(ATA_REG_LBA2).in(); + + if ((cl == 0x14 && ch == 0xEB) || (cl == 0x69 && ch == 0x96)) { + command_set_is_atapi = true; + dbgln("IDEChannel: {} {} device appears to be ATAPI. We're going to ignore it for now as we don't support it.", channel_type_string(), channel_string(i)); + continue; + } else { + dbgln("IDEChannel: {} {} device doesn't appear to be ATA or ATAPI. Ignoring it.", channel_type_string(), channel_string(i)); + continue; + } + } + + // FIXME: Handle possible OOM situation here. + ByteBuffer wbuf = ByteBuffer::create_uninitialized(512).release_value(); + ByteBuffer bbuf = ByteBuffer::create_uninitialized(512).release_value(); + u8* b = bbuf.data(); + u16* w = (u16*)wbuf.data(); + + for (u32 i = 0; i < 256; ++i) { + u16 data = m_io_group.io_base().offset(ATA_REG_DATA).in(); + *(w++) = data; + *(b++) = MSB(data); + *(b++) = LSB(data); + } + + // "Unpad" the device name string. + for (u32 i = 93; i > 54 && bbuf[i] == ' '; --i) + bbuf[i] = 0; + + volatile ATAIdentifyBlock& identify_block = (volatile ATAIdentifyBlock&)(*wbuf.data()); + + u16 capabilities = identify_block.capabilities[0]; + + // If the drive is so old that it doesn't support LBA, ignore it. + if (!(capabilities & ATA_CAP_LBA)) + continue; + u64 max_addressable_block = identify_block.max_28_bit_addressable_logical_sector; + + // if we support 48-bit LBA, use that value instead. + if (identify_block.commands_and_feature_sets_supported[1] & (1 << 10)) + max_addressable_block = identify_block.user_addressable_logical_sectors_count; + + dbgln("IDEChannel: {} {} {} device found: Name={}, Capacity={}, Capabilities={:#04x}", channel_type_string(), channel_string(i), !command_set_is_atapi ? "ATA" : "ATAPI", ((char*)bbuf.data() + 54), max_addressable_block * 512, capabilities); + // FIXME: Don't assume all drives will have logical sector size of 512 bytes. + ATADevice::Address address = { m_channel_type == ChannelType::Primary ? static_cast(0) : static_cast(1), static_cast(i) }; + if (i == 0) { + m_master = ATADiskDevice::create(m_parent_controller, address, capabilities, 512, max_addressable_block); + } else { + m_slave = ATADiskDevice::create(m_parent_controller, address, capabilities, 512, max_addressable_block); + } + } +} + +void IDEChannel::ata_access(Direction direction, bool slave_request, u64 lba, u8 block_count, u16 capabilities) +{ + VERIFY(m_lock.is_locked()); + VERIFY(m_request_lock.is_locked()); + LBAMode lba_mode; + u8 head = 0; + + VERIFY(capabilities & ATA_CAP_LBA); + if (lba >= 0x10000000) { + lba_mode = LBAMode::FortyEightBit; + head = 0; + } else { + lba_mode = LBAMode::TwentyEightBit; + head = (lba & 0xF000000) >> 24; + } + + // Wait 1 second + wait_until_not_busy(1000); + + // We need to select the drive and then we wait 20 microseconds... and it doesn't hurt anything so let's just do it. + m_io_group.io_base().offset(ATA_REG_HDDEVSEL).out(0xE0 | (static_cast(slave_request) << 4) | head); + IO::delay(20); + + if (lba_mode == LBAMode::FortyEightBit) { + m_io_group.io_base().offset(ATA_REG_SECCOUNT1).out(0); + m_io_group.io_base().offset(ATA_REG_LBA3).out((lba & 0xFF000000) >> 24); + m_io_group.io_base().offset(ATA_REG_LBA4).out((lba & 0xFF00000000ull) >> 32); + m_io_group.io_base().offset(ATA_REG_LBA5).out((lba & 0xFF0000000000ull) >> 40); + } + + m_io_group.io_base().offset(ATA_REG_SECCOUNT0).out(block_count); + m_io_group.io_base().offset(ATA_REG_LBA0).out((lba & 0x000000FF) >> 0); + m_io_group.io_base().offset(ATA_REG_LBA1).out((lba & 0x0000FF00) >> 8); + m_io_group.io_base().offset(ATA_REG_LBA2).out((lba & 0x00FF0000) >> 16); + + for (;;) { + auto status = m_io_group.control_base().in(); + if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRDY)) + break; + } + send_ata_io_command(lba_mode, direction); + enable_irq(); +} + +void IDEChannel::send_ata_io_command(LBAMode lba_mode, Direction direction) const +{ + if (lba_mode != LBAMode::FortyEightBit) { + m_io_group.io_base().offset(ATA_REG_COMMAND).out(direction == Direction::Read ? ATA_CMD_READ_PIO : ATA_CMD_WRITE_PIO); + } else { + m_io_group.io_base().offset(ATA_REG_COMMAND).out(direction == Direction::Read ? ATA_CMD_READ_PIO_EXT : ATA_CMD_WRITE_PIO_EXT); + } +} + +bool IDEChannel::ata_do_read_sector() +{ + VERIFY(m_lock.is_locked()); + VERIFY(m_request_lock.is_locked()); + VERIFY(!m_current_request.is_null()); + dbgln_if(PATA_DEBUG, "IDEChannel::ata_do_read_sector"); + auto& request = *m_current_request; + auto out_buffer = request.buffer().offset(m_current_request_block_index * 512); + auto result = request.write_to_buffer_buffered<512>(out_buffer, 512, [&](Bytes bytes) { + for (size_t i = 0; i < bytes.size(); i += sizeof(u16)) + *(u16*)bytes.offset_pointer(i) = IO::in16(m_io_group.io_base().offset(ATA_REG_DATA).get()); + return bytes.size(); + }); + if (result.is_error()) { + // TODO: Do we need to abort the PATA read if this wasn't the last block? + complete_current_request(AsyncDeviceRequest::MemoryFault); + return false; + } + return true; +} + +// FIXME: This doesn't quite work and locks up reading LBA 3. +void IDEChannel::ata_read_sectors(bool slave_request, u16 capabilities) +{ + VERIFY(m_lock.is_locked()); + VERIFY(!m_current_request.is_null()); + VERIFY(m_current_request->block_count() <= 256); + + SpinlockLocker m_lock(m_request_lock); + dbgln_if(PATA_DEBUG, "IDEChannel::ata_read_sectors"); + dbgln_if(PATA_DEBUG, "IDEChannel: Reading {} sector(s) @ LBA {}", m_current_request->block_count(), m_current_request->block_index()); + ata_access(Direction::Read, slave_request, m_current_request->block_index(), m_current_request->block_count(), capabilities); +} + +void IDEChannel::ata_do_write_sector() +{ + VERIFY(m_lock.is_locked()); + VERIFY(m_request_lock.is_locked()); + VERIFY(!m_current_request.is_null()); + auto& request = *m_current_request; + + io_delay(); + while ((m_io_group.control_base().in() & ATA_SR_BSY) || !(m_io_group.control_base().in() & ATA_SR_DRQ)) + ; + + u8 status = m_io_group.control_base().in(); + VERIFY(status & ATA_SR_DRQ); + + auto in_buffer = request.buffer().offset(m_current_request_block_index * 512); + dbgln_if(PATA_DEBUG, "IDEChannel: Writing 512 bytes (part {}) (status={:#02x})...", m_current_request_block_index, status); + auto result = request.read_from_buffer_buffered<512>(in_buffer, 512, [&](ReadonlyBytes readonly_bytes) { + for (size_t i = 0; i < readonly_bytes.size(); i += sizeof(u16)) + IO::out16(m_io_group.io_base().offset(ATA_REG_DATA).get(), *(const u16*)readonly_bytes.offset(i)); + return readonly_bytes.size(); + }); + if (result.is_error()) + complete_current_request(AsyncDeviceRequest::MemoryFault); +} + +// FIXME: I'm assuming this doesn't work based on the fact PIO read doesn't work. +void IDEChannel::ata_write_sectors(bool slave_request, u16 capabilities) +{ + VERIFY(m_lock.is_locked()); + VERIFY(!m_current_request.is_null()); + VERIFY(m_current_request->block_count() <= 256); + + SpinlockLocker m_lock(m_request_lock); + dbgln_if(PATA_DEBUG, "IDEChannel: Writing {} sector(s) @ LBA {}", m_current_request->block_count(), m_current_request->block_index()); + ata_access(Direction::Write, slave_request, m_current_request->block_index(), m_current_request->block_count(), capabilities); + ata_do_write_sector(); +} +} diff --git a/Kernel/Storage/ATA/IDEChannel.h b/Kernel/Storage/ATA/IDEChannel.h new file mode 100644 index 0000000000..ec14e00f7d --- /dev/null +++ b/Kernel/Storage/ATA/IDEChannel.h @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2018-2020, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +// +// Parallel ATA (PATA) controller driver +// +// This driver describes a logical PATA Channel. Each channel can connect up to 2 +// IDE Hard Disk Drives. The drives themselves can be either the master drive (hd0) +// or the slave drive (hd1). +// +// More information about the ATA spec for PATA can be found here: +// ftp://ftp.seagate.com/acrobat/reference/111-1c.pdf +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +class AsyncBlockDeviceRequest; + +class IDEController; +class IDEChannel : public RefCounted + , public IRQHandler { + friend class IDEController; + AK_MAKE_ETERNAL +public: + enum class ChannelType : u8 { + Primary, + Secondary + }; + + struct IOAddressGroup { + IOAddressGroup(IOAddress io_base, IOAddress control_base, IOAddress bus_master_base) + : m_io_base(io_base) + , m_control_base(control_base) + , m_bus_master_base(bus_master_base) + { + } + + IOAddressGroup(IOAddress io_base, IOAddress control_base) + : m_io_base(io_base) + , m_control_base(control_base) + , m_bus_master_base() + { + } + + IOAddressGroup(IOAddressGroup const& other, IOAddress bus_master_base) + : m_io_base(other.io_base()) + , m_control_base(other.control_base()) + , m_bus_master_base(bus_master_base) + { + } + + IOAddressGroup(IOAddressGroup const&) = default; + + // Disable default implementations that would use surprising integer promotion. + bool operator==(const IOAddressGroup&) const = delete; + bool operator<=(const IOAddressGroup&) const = delete; + bool operator>=(const IOAddressGroup&) const = delete; + bool operator<(const IOAddressGroup&) const = delete; + bool operator>(const IOAddressGroup&) const = delete; + + IOAddress io_base() const { return m_io_base; }; + IOAddress control_base() const { return m_control_base; } + Optional bus_master_base() const { return m_bus_master_base; } + + private: + IOAddress m_io_base; + IOAddress m_control_base; + Optional m_bus_master_base; + }; + +public: + static NonnullRefPtr create(const IDEController&, IOAddressGroup, ChannelType type); + static NonnullRefPtr create(const IDEController&, u8 irq, IOAddressGroup, ChannelType type); + virtual ~IDEChannel() override; + + RefPtr master_device() const; + RefPtr slave_device() const; + + virtual StringView purpose() const override { return "PATA Channel"sv; } + + virtual bool is_dma_enabled() const { return false; } + +private: + void complete_current_request(AsyncDeviceRequest::RequestResult); + void initialize(); + +protected: + enum class LBAMode : u8 { + None, // CHS + TwentyEightBit, + FortyEightBit, + }; + + enum class Direction : u8 { + Read, + Write, + }; + + IDEChannel(const IDEController&, IOAddressGroup, ChannelType type); + IDEChannel(const IDEController&, u8 irq, IOAddressGroup, ChannelType type); + //^ IRQHandler + virtual bool handle_irq(const RegisterState&) override; + + virtual void send_ata_io_command(LBAMode lba_mode, Direction direction) const; + + virtual void ata_read_sectors(bool, u16); + virtual void ata_write_sectors(bool, u16); + + void detect_disks(); + StringView channel_type_string() const; + + void try_disambiguate_error(); + bool wait_until_not_busy(bool slave, size_t milliseconds_timeout); + bool wait_until_not_busy(size_t milliseconds_timeout); + + void start_request(AsyncBlockDeviceRequest&, bool, u16); + + void clear_pending_interrupts() const; + + void ata_access(Direction, bool, u64, u8, u16); + + bool ata_do_read_sector(); + void ata_do_write_sector(); + + // Data members + ChannelType m_channel_type { ChannelType::Primary }; + + volatile u8 m_device_error { 0 }; + EntropySource m_entropy_source; + + RefPtr m_master; + RefPtr m_slave; + + RefPtr m_current_request; + u64 m_current_request_block_index { 0 }; + bool m_current_request_flushing_cache { false }; + Spinlock m_request_lock; + Mutex m_lock { "IDEChannel" }; + + IOAddressGroup m_io_group; + NonnullRefPtr m_parent_controller; +}; +} diff --git a/Kernel/Storage/ATA/IDEController.cpp b/Kernel/Storage/ATA/IDEController.cpp new file mode 100644 index 0000000000..8f70083fc5 --- /dev/null +++ b/Kernel/Storage/ATA/IDEController.cpp @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2020, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +UNMAP_AFTER_INIT NonnullRefPtr IDEController::initialize(PCI::DeviceIdentifier const& device_identifier, bool force_pio) +{ + return adopt_ref(*new IDEController(device_identifier, force_pio)); +} + +bool IDEController::reset() +{ + TODO(); +} + +bool IDEController::shutdown() +{ + TODO(); +} + +size_t IDEController::devices_count() const +{ + size_t count = 0; + for (u32 index = 0; index < 4; index++) { + if (!device(index).is_null()) + count++; + } + return count; +} + +void IDEController::start_request(const ATADevice& device, AsyncBlockDeviceRequest& request) +{ + auto& address = device.ata_address(); + VERIFY(address.subport < 2); + switch (address.port) { + case 0: + m_channels[0].start_request(request, address.subport == 0 ? false : true, device.ata_capabilites()); + return; + case 1: + m_channels[1].start_request(request, address.subport == 0 ? false : true, device.ata_capabilites()); + return; + } + VERIFY_NOT_REACHED(); +} + +void IDEController::complete_current_request(AsyncDeviceRequest::RequestResult) +{ + VERIFY_NOT_REACHED(); +} + +UNMAP_AFTER_INIT IDEController::IDEController(PCI::DeviceIdentifier const& device_identifier, bool force_pio) + : ATAController() + , PCI::Device(device_identifier.address()) + , m_prog_if(device_identifier.prog_if()) + , m_interrupt_line(device_identifier.interrupt_line()) +{ + PCI::enable_io_space(device_identifier.address()); + PCI::enable_memory_space(device_identifier.address()); + initialize(force_pio); +} + +UNMAP_AFTER_INIT IDEController::~IDEController() +{ +} + +bool IDEController::is_pci_native_mode_enabled() const +{ + return (m_prog_if.value() & 0x05) != 0; +} + +bool IDEController::is_pci_native_mode_enabled_on_primary_channel() const +{ + return (m_prog_if.value() & 0x1) == 0x1; +} + +bool IDEController::is_pci_native_mode_enabled_on_secondary_channel() const +{ + return (m_prog_if.value() & 0x4) == 0x4; +} + +bool IDEController::is_bus_master_capable() const +{ + return m_prog_if.value() & (1 << 7); +} + +static const char* detect_controller_type(u8 programming_value) +{ + switch (programming_value) { + case 0x00: + return "ISA Compatibility mode-only controller"; + case 0x05: + return "PCI native mode-only controller"; + case 0x0A: + return "ISA Compatibility mode controller, supports both channels switched to PCI native mode"; + case 0x0F: + return "PCI native mode controller, supports both channels switched to ISA compatibility mode"; + case 0x80: + return "ISA Compatibility mode-only controller, supports bus mastering"; + case 0x85: + return "PCI native mode-only controller, supports bus mastering"; + case 0x8A: + return "ISA Compatibility mode controller, supports both channels switched to PCI native mode, supports bus mastering"; + case 0x8F: + return "PCI native mode controller, supports both channels switched to ISA compatibility mode, supports bus mastering"; + default: + VERIFY_NOT_REACHED(); + } + VERIFY_NOT_REACHED(); +} + +UNMAP_AFTER_INIT void IDEController::initialize(bool force_pio) +{ + auto bus_master_base = IOAddress(PCI::get_BAR4(pci_address()) & (~1)); + dbgln("IDE controller @ {}: bus master base was set to {}", pci_address(), bus_master_base); + dbgln("IDE controller @ {}: interrupt line was set to {}", pci_address(), m_interrupt_line.value()); + dbgln("IDE controller @ {}: {}", pci_address(), detect_controller_type(m_prog_if.value())); + dbgln("IDE controller @ {}: primary channel DMA capable? {}", pci_address(), ((bus_master_base.offset(2).in() >> 5) & 0b11)); + dbgln("IDE controller @ {}: secondary channel DMA capable? {}", pci_address(), ((bus_master_base.offset(2 + 8).in() >> 5) & 0b11)); + + if (!is_bus_master_capable()) + force_pio = true; + + auto bar0 = PCI::get_BAR0(pci_address()); + auto primary_base_io = (bar0 == 0x1 || bar0 == 0) ? IOAddress(0x1F0) : IOAddress(bar0 & (~1)); + auto bar1 = PCI::get_BAR1(pci_address()); + auto primary_control_io = (bar1 == 0x1 || bar1 == 0) ? IOAddress(0x3F6) : IOAddress(bar1 & (~1)); + auto bar2 = PCI::get_BAR2(pci_address()); + auto secondary_base_io = (bar2 == 0x1 || bar2 == 0) ? IOAddress(0x170) : IOAddress(bar2 & (~1)); + auto bar3 = PCI::get_BAR3(pci_address()); + auto secondary_control_io = (bar3 == 0x1 || bar3 == 0) ? IOAddress(0x376) : IOAddress(bar3 & (~1)); + + auto irq_line = m_interrupt_line.value(); + if (is_pci_native_mode_enabled()) { + VERIFY(irq_line != 0); + } + + if (is_pci_native_mode_enabled_on_primary_channel()) { + if (force_pio) + m_channels.append(IDEChannel::create(*this, irq_line, { primary_base_io, primary_control_io }, IDEChannel::ChannelType::Primary)); + else + m_channels.append(BMIDEChannel::create(*this, irq_line, { primary_base_io, primary_control_io, bus_master_base }, IDEChannel::ChannelType::Primary)); + } else { + if (force_pio) + m_channels.append(IDEChannel::create(*this, { primary_base_io, primary_control_io }, IDEChannel::ChannelType::Primary)); + else + m_channels.append(BMIDEChannel::create(*this, { primary_base_io, primary_control_io, bus_master_base }, IDEChannel::ChannelType::Primary)); + } + + m_channels[0].enable_irq(); + + if (is_pci_native_mode_enabled_on_secondary_channel()) { + if (force_pio) + m_channels.append(IDEChannel::create(*this, irq_line, { secondary_base_io, secondary_control_io }, IDEChannel::ChannelType::Secondary)); + else + m_channels.append(BMIDEChannel::create(*this, irq_line, { secondary_base_io, secondary_control_io, bus_master_base.offset(8) }, IDEChannel::ChannelType::Secondary)); + } else { + if (force_pio) + m_channels.append(IDEChannel::create(*this, { secondary_base_io, secondary_control_io }, IDEChannel::ChannelType::Secondary)); + else + m_channels.append(BMIDEChannel::create(*this, { secondary_base_io, secondary_control_io, bus_master_base.offset(8) }, IDEChannel::ChannelType::Secondary)); + } + + m_channels[1].enable_irq(); +} + +RefPtr IDEController::device_by_channel_and_position(u32 index) const +{ + switch (index) { + case 0: + return m_channels[0].master_device(); + case 1: + return m_channels[0].slave_device(); + case 2: + return m_channels[1].master_device(); + case 3: + return m_channels[1].slave_device(); + } + VERIFY_NOT_REACHED(); +} + +RefPtr IDEController::device(u32 index) const +{ + NonnullRefPtrVector connected_devices; + for (size_t index = 0; index < 4; index++) { + auto checked_device = device_by_channel_and_position(index); + if (checked_device.is_null()) + continue; + connected_devices.append(checked_device.release_nonnull()); + } + if (index >= connected_devices.size()) + return nullptr; + return connected_devices[index]; +} +} diff --git a/Kernel/Storage/ATA/IDEController.h b/Kernel/Storage/ATA/IDEController.h new file mode 100644 index 0000000000..cea3540e9f --- /dev/null +++ b/Kernel/Storage/ATA/IDEController.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2020, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Kernel { + +class AsyncBlockDeviceRequest; + +class IDEController final : public ATAController + , public PCI::Device { + AK_MAKE_ETERNAL +public: + static NonnullRefPtr initialize(PCI::DeviceIdentifier const&, bool force_pio); + virtual ~IDEController() override; + + virtual RefPtr device(u32 index) const override; + virtual bool reset() override; + virtual bool shutdown() override; + virtual size_t devices_count() const override; + virtual void start_request(const ATADevice&, AsyncBlockDeviceRequest&) override; + virtual void complete_current_request(AsyncDeviceRequest::RequestResult) override; + + bool is_bus_master_capable() const; + bool is_pci_native_mode_enabled() const; + +private: + bool is_pci_native_mode_enabled_on_primary_channel() const; + bool is_pci_native_mode_enabled_on_secondary_channel() const; + IDEController(PCI::DeviceIdentifier const&, bool force_pio); + + RefPtr device_by_channel_and_position(u32 index) const; + void initialize(bool force_pio); + void detect_disks(); + + NonnullRefPtrVector m_channels; + // FIXME: Find a better way to get the ProgrammingInterface + PCI::ProgrammingInterface m_prog_if; + PCI::InterruptLine m_interrupt_line; +}; +} diff --git a/Kernel/Storage/ATAController.h b/Kernel/Storage/ATAController.h deleted file mode 100644 index de0643f12e..0000000000 --- a/Kernel/Storage/ATAController.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Kernel { - -class AsyncBlockDeviceRequest; - -class ATADevice; -class ATAController - : public StorageController - , public Weakable { -public: - virtual void start_request(const ATADevice&, AsyncBlockDeviceRequest&) = 0; - -protected: - ATAController() = default; -}; -} diff --git a/Kernel/Storage/ATADevice.cpp b/Kernel/Storage/ATADevice.cpp deleted file mode 100644 index 3fe516908f..0000000000 --- a/Kernel/Storage/ATADevice.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -ATADevice::ATADevice(const ATAController& controller, ATADevice::Address ata_address, unsigned minor_number, u16 capabilities, u16 logical_sector_size, u64 max_addressable_block, NonnullOwnPtr early_storage_name) - : StorageDevice(StorageManagement::major_number(), minor_number, logical_sector_size, max_addressable_block, move(early_storage_name)) - , m_controller(controller) - , m_ata_address(ata_address) - , m_capabilities(capabilities) -{ -} - -ATADevice::~ATADevice() -{ -} - -void ATADevice::start_request(AsyncBlockDeviceRequest& request) -{ - auto controller = m_controller.strong_ref(); - VERIFY(controller); - controller->start_request(*this, request); -} - -} diff --git a/Kernel/Storage/ATADevice.h b/Kernel/Storage/ATADevice.h deleted file mode 100644 index 5e59f8e4ba..0000000000 --- a/Kernel/Storage/ATADevice.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Kernel { - -class ATADevice : public StorageDevice { -public: - // Note: For IDE drives, port means Primary or Secondary (0 or 1), - // and subport means Master or Slave (0 or 1). - // For SATA drives (AHCI driven HBAs), a port can be a number from 0 to 31, - // and subport can be a number from 0 to 14 (only 15 devices are allowed to - // be connected to one SATA port multiplier). - struct Address { - // FIXME: u32 for this value is wasteful, because even AHCI only support 32 ports - u32 port; - u8 subport; - }; - -public: - virtual ~ATADevice() override; - - // ^BlockDevice - virtual void start_request(AsyncBlockDeviceRequest&) override; - - u16 ata_capabilites() const { return m_capabilities; } - const Address& ata_address() const { return m_ata_address; } - -protected: - ATADevice(const ATAController&, Address, unsigned, u16, u16, u64, NonnullOwnPtr); - - WeakPtr m_controller; - const Address m_ata_address; - const u16 m_capabilities; -}; - -} diff --git a/Kernel/Storage/ATADiskDevice.cpp b/Kernel/Storage/ATADiskDevice.cpp deleted file mode 100644 index 5eea1374c8..0000000000 --- a/Kernel/Storage/ATADiskDevice.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -NonnullRefPtr ATADiskDevice::create(const ATAController& controller, ATADevice::Address ata_address, u16 capabilities, u16 logical_sector_size, u64 max_addressable_block) -{ - auto minor_device_number = StorageManagement::minor_number(); - - // FIXME: We need a way of formatting strings with KString. - auto device_name = String::formatted("hd{:c}", 'a' + minor_device_number); - auto device_name_kstring = KString::must_create(device_name.view()); - - auto disk_device_or_error = DeviceManagement::try_create_device(controller, ata_address, minor_device_number, capabilities, logical_sector_size, max_addressable_block, move(device_name_kstring)); - // FIXME: Find a way to propagate errors - VERIFY(!disk_device_or_error.is_error()); - return disk_device_or_error.release_value(); -} - -ATADiskDevice::ATADiskDevice(const ATAController& controller, ATADevice::Address ata_address, unsigned minor_number, u16 capabilities, u16 logical_sector_size, u64 max_addressable_block, NonnullOwnPtr early_storage_name) - : ATADevice(controller, ata_address, minor_number, capabilities, logical_sector_size, max_addressable_block, move(early_storage_name)) -{ -} - -ATADiskDevice::~ATADiskDevice() -{ -} - -StringView ATADiskDevice::class_name() const -{ - return "ATADiskDevice"; -} - -} diff --git a/Kernel/Storage/ATADiskDevice.h b/Kernel/Storage/ATADiskDevice.h deleted file mode 100644 index ba52385306..0000000000 --- a/Kernel/Storage/ATADiskDevice.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class IDEController; -class IDEChannel; -class ATADiskDevice final : public ATADevice { - friend class IDEController; - friend class DeviceManagement; - -public: - static NonnullRefPtr create(const ATAController&, ATADevice::Address, u16 capabilities, u16 logical_sector_size, u64 max_addressable_block); - virtual ~ATADiskDevice() override; - - // ^StorageDevice - virtual CommandSet command_set() const override { return CommandSet::ATA; } - -private: - ATADiskDevice(const ATAController&, Address, unsigned, u16, u16, u64, NonnullOwnPtr); - - // ^DiskDevice - virtual StringView class_name() const override; -}; - -} diff --git a/Kernel/Storage/ATAPIDiscDevice.cpp b/Kernel/Storage/ATAPIDiscDevice.cpp deleted file mode 100644 index a94ffb5308..0000000000 --- a/Kernel/Storage/ATAPIDiscDevice.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -NonnullRefPtr ATAPIDiscDevice::create(const ATAController& controller, ATADevice::Address ata_address, u16 capabilities, u64 max_addressable_block) -{ - auto minor_device_number = StorageManagement::minor_number(); - - // FIXME: We need a way of formatting strings with KString. - auto device_name = String::formatted("hd{:c}", 'a' + minor_device_number); - auto device_name_kstring = KString::must_create(device_name.view()); - - auto disc_device_or_error = DeviceManagement::try_create_device(controller, ata_address, minor_device_number, capabilities, max_addressable_block, move(device_name_kstring)); - // FIXME: Find a way to propagate errors - VERIFY(!disc_device_or_error.is_error()); - return disc_device_or_error.release_value(); -} - -ATAPIDiscDevice::ATAPIDiscDevice(const ATAController& controller, ATADevice::Address ata_address, unsigned minor_number, u16 capabilities, u64 max_addressable_block, NonnullOwnPtr early_storage_name) - : ATADevice(controller, ata_address, minor_number, capabilities, 0, max_addressable_block, move(early_storage_name)) -{ -} - -ATAPIDiscDevice::~ATAPIDiscDevice() -{ -} - -StringView ATAPIDiscDevice::class_name() const -{ - return "ATAPIDiscDevice"; -} - -} diff --git a/Kernel/Storage/ATAPIDiscDevice.h b/Kernel/Storage/ATAPIDiscDevice.h deleted file mode 100644 index d7775bf20a..0000000000 --- a/Kernel/Storage/ATAPIDiscDevice.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Kernel { - -class IDEController; -class IDEChannel; -class ATAPIDiscDevice final : public ATADevice { - friend class IDEController; - friend class DeviceManagement; - -public: - static NonnullRefPtr create(const ATAController&, ATADevice::Address, u16 capabilities, u64 max_addressable_block); - virtual ~ATAPIDiscDevice() override; - - // ^StorageDevice - virtual CommandSet command_set() const override { return CommandSet::SCSI; } - -private: - ATAPIDiscDevice(const ATAController&, Address, unsigned, u16, u64, NonnullOwnPtr); - - // ^DiskDevice - virtual StringView class_name() const override; -}; - -} diff --git a/Kernel/Storage/BMIDEChannel.cpp b/Kernel/Storage/BMIDEChannel.cpp deleted file mode 100644 index 732b8baa41..0000000000 --- a/Kernel/Storage/BMIDEChannel.cpp +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT NonnullRefPtr BMIDEChannel::create(const IDEController& ide_controller, IDEChannel::IOAddressGroup io_group, IDEChannel::ChannelType type) -{ - return adopt_ref(*new BMIDEChannel(ide_controller, io_group, type)); -} - -UNMAP_AFTER_INIT NonnullRefPtr BMIDEChannel::create(const IDEController& ide_controller, u8 irq, IDEChannel::IOAddressGroup io_group, IDEChannel::ChannelType type) -{ - return adopt_ref(*new BMIDEChannel(ide_controller, irq, io_group, type)); -} - -UNMAP_AFTER_INIT BMIDEChannel::BMIDEChannel(const IDEController& controller, IDEChannel::IOAddressGroup io_group, IDEChannel::ChannelType type) - : IDEChannel(controller, io_group, type) -{ - initialize(); -} - -UNMAP_AFTER_INIT BMIDEChannel::BMIDEChannel(const IDEController& controller, u8 irq, IDEChannel::IOAddressGroup io_group, IDEChannel::ChannelType type) - : IDEChannel(controller, irq, io_group, type) -{ - initialize(); -} - -UNMAP_AFTER_INIT void BMIDEChannel::initialize() -{ - VERIFY(m_io_group.bus_master_base().has_value()); - // Let's try to set up DMA transfers. - PCI::enable_bus_mastering(m_parent_controller->pci_address()); - m_prdt_page = MM.allocate_supervisor_physical_page(); - m_dma_buffer_page = MM.allocate_supervisor_physical_page(); - if (m_dma_buffer_page.is_null() || m_prdt_page.is_null()) - return; - { - auto region_or_error = MM.allocate_kernel_region(m_prdt_page->paddr(), PAGE_SIZE, "IDE PRDT", Memory::Region::Access::ReadWrite); - if (region_or_error.is_error()) - TODO(); - m_prdt_region = region_or_error.release_value(); - } - { - auto region_or_error = MM.allocate_kernel_region(m_dma_buffer_page->paddr(), PAGE_SIZE, "IDE DMA region", Memory::Region::Access::ReadWrite); - if (region_or_error.is_error()) - TODO(); - m_dma_buffer_region = region_or_error.release_value(); - } - - prdt().end_of_table = 0x8000; - - // clear bus master interrupt status - m_io_group.bus_master_base().value().offset(2).out(m_io_group.bus_master_base().value().offset(2).in() | 4); -} - -static void print_ide_status(u8 status) -{ - dbgln("BMIDEChannel: print_ide_status: DRQ={} BSY={}, DRDY={}, DSC={}, DF={}, CORR={}, IDX={}, ERR={}", - (status & ATA_SR_DRQ) != 0, - (status & ATA_SR_BSY) != 0, - (status & ATA_SR_DRDY) != 0, - (status & ATA_SR_DSC) != 0, - (status & ATA_SR_DF) != 0, - (status & ATA_SR_CORR) != 0, - (status & ATA_SR_IDX) != 0, - (status & ATA_SR_ERR) != 0); -} - -bool BMIDEChannel::handle_irq(const RegisterState&) -{ - u8 status = m_io_group.io_base().offset(ATA_REG_STATUS).in(); - - m_entropy_source.add_random_event(status); - - VERIFY(m_io_group.bus_master_base().has_value()); - u8 bstatus = m_io_group.bus_master_base().value().offset(2).in(); - if (!(bstatus & 0x4)) { - // interrupt not from this device, ignore - dbgln_if(PATA_DEBUG, "BMIDEChannel: ignore interrupt"); - return false; - } - // clear bus master interrupt status - m_io_group.bus_master_base().value().offset(2).out(m_io_group.bus_master_base().value().offset(2).in() | 4); - - SpinlockLocker lock(m_request_lock); - dbgln_if(PATA_DEBUG, "BMIDEChannel: interrupt: DRQ={}, BSY={}, DRDY={}", - (status & ATA_SR_DRQ) != 0, - (status & ATA_SR_BSY) != 0, - (status & ATA_SR_DRDY) != 0); - - if (!m_current_request) { - dbgln("BMIDEChannel: IRQ but no pending request!"); - return false; - } - - if (status & ATA_SR_ERR) { - print_ide_status(status); - m_device_error = m_io_group.io_base().offset(ATA_REG_ERROR).in(); - dbgln("BMIDEChannel: Error {:#02x}!", (u8)m_device_error); - try_disambiguate_error(); - complete_current_request(AsyncDeviceRequest::Failure); - return true; - } - m_device_error = 0; - complete_current_request(AsyncDeviceRequest::Success); - return true; -} - -void BMIDEChannel::complete_current_request(AsyncDeviceRequest::RequestResult result) -{ - // NOTE: this may be called from the interrupt handler! - VERIFY(m_current_request); - VERIFY(m_request_lock.is_locked()); - - // Now schedule reading back the buffer as soon as we leave the irq handler. - // This is important so that we can safely write the buffer back, - // which could cause page faults. Note that this may be called immediately - // before Processor::deferred_call_queue returns! - g_io_work->queue([this, result]() { - dbgln_if(PATA_DEBUG, "BMIDEChannel::complete_current_request result: {}", (int)result); - SpinlockLocker lock(m_request_lock); - VERIFY(m_current_request); - auto current_request = m_current_request; - m_current_request.clear(); - - if (result == AsyncDeviceRequest::Success) { - if (current_request->request_type() == AsyncBlockDeviceRequest::Read) { - if (auto result = current_request->write_to_buffer(current_request->buffer(), m_dma_buffer_region->vaddr().as_ptr(), 512 * current_request->block_count()); result.is_error()) { - lock.unlock(); - current_request->complete(AsyncDeviceRequest::MemoryFault); - return; - } - } - - // I read somewhere that this may trigger a cache flush so let's do it. - VERIFY(m_io_group.bus_master_base().has_value()); - m_io_group.bus_master_base().value().offset(2).out(m_io_group.bus_master_base().value().offset(2).in() | 0x6); - } - - lock.unlock(); - current_request->complete(result); - }); -} - -void BMIDEChannel::ata_write_sectors(bool slave_request, u16 capabilities) -{ - VERIFY(m_lock.is_locked()); - VERIFY(!m_current_request.is_null()); - VERIFY(m_current_request->block_count() <= 256); - - SpinlockLocker m_lock(m_request_lock); - dbgln_if(PATA_DEBUG, "BMIDEChannel::ata_write_sectors ({} x {})", m_current_request->block_index(), m_current_request->block_count()); - - prdt().offset = m_dma_buffer_page->paddr().get(); - prdt().size = 512 * m_current_request->block_count(); - - if (auto result = m_current_request->read_from_buffer(m_current_request->buffer(), m_dma_buffer_region->vaddr().as_ptr(), 512 * m_current_request->block_count()); result.is_error()) { - complete_current_request(AsyncDeviceRequest::MemoryFault); - return; - } - - // Note: This is a fix for a quirk for an IDE controller on ICH7 machine. - // We need to select the drive and then we wait 10 microseconds... and it doesn't hurt anything - m_io_group.io_base().offset(ATA_REG_HDDEVSEL).out(0xA0 | ((slave_request ? 1 : 0) << 4)); - IO::delay(10); - - VERIFY(prdt().size <= PAGE_SIZE); - VERIFY(m_io_group.bus_master_base().has_value()); - // Stop bus master - m_io_group.bus_master_base().value().out(0); - - // Write the PRDT location - m_io_group.bus_master_base().value().offset(4).out(m_prdt_page->paddr().get()); - - // Turn on "Interrupt" and "Error" flag. The error flag should be cleared by hardware. - m_io_group.bus_master_base().value().offset(2).out(m_io_group.bus_master_base().value().offset(2).in() | 0x6); - - ata_access(Direction::Write, slave_request, m_current_request->block_index(), m_current_request->block_count(), capabilities); - - // Start bus master - m_io_group.bus_master_base().value().out(0x1); -} - -void BMIDEChannel::send_ata_io_command(LBAMode lba_mode, Direction direction) const -{ - if (lba_mode != LBAMode::FortyEightBit) { - m_io_group.io_base().offset(ATA_REG_COMMAND).out(direction == Direction::Read ? ATA_CMD_READ_DMA : ATA_CMD_WRITE_DMA); - } else { - m_io_group.io_base().offset(ATA_REG_COMMAND).out(direction == Direction::Read ? ATA_CMD_READ_DMA_EXT : ATA_CMD_WRITE_DMA_EXT); - } -} - -void BMIDEChannel::ata_read_sectors(bool slave_request, u16 capabilities) -{ - VERIFY(m_lock.is_locked()); - VERIFY(!m_current_request.is_null()); - VERIFY(m_current_request->block_count() <= 256); - - SpinlockLocker m_lock(m_request_lock); - dbgln_if(PATA_DEBUG, "BMIDEChannel::ata_read_sectors ({} x {})", m_current_request->block_index(), m_current_request->block_count()); - - // Note: This is a fix for a quirk for an IDE controller on ICH7 machine. - // We need to select the drive and then we wait 10 microseconds... and it doesn't hurt anything - m_io_group.io_base().offset(ATA_REG_HDDEVSEL).out(0xA0 | ((slave_request ? 1 : 0) << 4)); - IO::delay(10); - - prdt().offset = m_dma_buffer_page->paddr().get(); - prdt().size = 512 * m_current_request->block_count(); - - VERIFY(prdt().size <= PAGE_SIZE); - - VERIFY(m_io_group.bus_master_base().has_value()); - // Stop bus master - m_io_group.bus_master_base().value().out(0); - - // Write the PRDT location - m_io_group.bus_master_base().value().offset(4).out(m_prdt_page->paddr().get()); - - // Set transfer direction - m_io_group.bus_master_base().value().out(0x8); - - // Turn on "Interrupt" and "Error" flag. The error flag should be cleared by hardware. - m_io_group.bus_master_base().value().offset(2).out(m_io_group.bus_master_base().value().offset(2).in() | 0x6); - - ata_access(Direction::Read, slave_request, m_current_request->block_index(), m_current_request->block_count(), capabilities); - - // Start bus master - m_io_group.bus_master_base().value().out(0x9); -} - -} diff --git a/Kernel/Storage/BMIDEChannel.h b/Kernel/Storage/BMIDEChannel.h deleted file mode 100644 index c72e0a3da5..0000000000 --- a/Kernel/Storage/BMIDEChannel.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2021, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -namespace Kernel { - -class AsyncBlockDeviceRequest; - -struct [[gnu::packed]] PhysicalRegionDescriptor { - u32 offset; - u16 size { 0 }; - u16 end_of_table { 0 }; -}; - -class IDEController; -class BMIDEChannel final : public IDEChannel { - friend class IDEController; - friend class PATADiskDevice; - -public: - static NonnullRefPtr create(const IDEController&, IDEChannel::IOAddressGroup, IDEChannel::ChannelType type); - static NonnullRefPtr create(const IDEController&, u8 irq, IDEChannel::IOAddressGroup, IDEChannel::ChannelType type); - virtual ~BMIDEChannel() override {}; - - virtual bool is_dma_enabled() const override { return true; }; - -private: - BMIDEChannel(const IDEController&, IDEChannel::IOAddressGroup, IDEChannel::ChannelType type); - BMIDEChannel(const IDEController&, u8 irq, IDEChannel::IOAddressGroup, IDEChannel::ChannelType type); - void initialize(); - - void complete_current_request(AsyncDeviceRequest::RequestResult); - - //^ IRQHandler - virtual bool handle_irq(const RegisterState&) override; - - //* IDEChannel - virtual void send_ata_io_command(LBAMode lba_mode, Direction direction) const override; - virtual void ata_read_sectors(bool, u16) override; - virtual void ata_write_sectors(bool, u16) override; - - PhysicalRegionDescriptor& prdt() { return *reinterpret_cast(m_prdt_region->vaddr().as_ptr()); } - OwnPtr m_prdt_region; - OwnPtr m_dma_buffer_region; - RefPtr m_prdt_page; - RefPtr m_dma_buffer_page; -}; -} diff --git a/Kernel/Storage/IDEChannel.cpp b/Kernel/Storage/IDEChannel.cpp deleted file mode 100644 index bc3012f45b..0000000000 --- a/Kernel/Storage/IDEChannel.cpp +++ /dev/null @@ -1,546 +0,0 @@ -/* - * Copyright (c) 2018-2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -#define PATA_PRIMARY_IRQ 14 -#define PATA_SECONDARY_IRQ 15 - -UNMAP_AFTER_INIT NonnullRefPtr IDEChannel::create(const IDEController& controller, IOAddressGroup io_group, ChannelType type) -{ - return adopt_ref(*new IDEChannel(controller, io_group, type)); -} - -UNMAP_AFTER_INIT NonnullRefPtr IDEChannel::create(const IDEController& controller, u8 irq, IOAddressGroup io_group, ChannelType type) -{ - return adopt_ref(*new IDEChannel(controller, irq, io_group, type)); -} - -RefPtr IDEChannel::master_device() const -{ - return m_master; -} - -RefPtr IDEChannel::slave_device() const -{ - return m_slave; -} - -UNMAP_AFTER_INIT void IDEChannel::initialize() -{ - disable_irq(); - dbgln_if(PATA_DEBUG, "IDEChannel: {} IO base: {}", channel_type_string(), m_io_group.io_base()); - dbgln_if(PATA_DEBUG, "IDEChannel: {} control base: {}", channel_type_string(), m_io_group.control_base()); - if (m_io_group.bus_master_base().has_value()) - dbgln_if(PATA_DEBUG, "IDEChannel: {} bus master base: {}", channel_type_string(), m_io_group.bus_master_base().value()); - else - dbgln_if(PATA_DEBUG, "IDEChannel: {} bus master base disabled", channel_type_string()); - m_parent_controller->enable_pin_based_interrupts(); - - // reset the channel - u8 device_control = m_io_group.control_base().in(); - // Wait 30 milliseconds - IO::delay(30000); - m_io_group.control_base().out(device_control | (1 << 2)); - // Wait 30 milliseconds - IO::delay(30000); - m_io_group.control_base().out(device_control); - // Wait up to 30 seconds before failing - if (!wait_until_not_busy(false, 30000)) { - dbgln("IDEChannel: reset failed, busy flag on master stuck"); - return; - } - // Wait up to 30 seconds before failing - if (!wait_until_not_busy(true, 30000)) { - dbgln("IDEChannel: reset failed, busy flag on slave stuck"); - return; - } - - detect_disks(); - - // Note: calling to detect_disks could generate an interrupt, clear it if that's the case - clear_pending_interrupts(); -} - -UNMAP_AFTER_INIT IDEChannel::IDEChannel(const IDEController& controller, u8 irq, IOAddressGroup io_group, ChannelType type) - : IRQHandler(irq) - , m_channel_type(type) - , m_io_group(io_group) - , m_parent_controller(controller) -{ - initialize(); -} - -UNMAP_AFTER_INIT IDEChannel::IDEChannel(const IDEController& controller, IOAddressGroup io_group, ChannelType type) - : IRQHandler(type == ChannelType::Primary ? PATA_PRIMARY_IRQ : PATA_SECONDARY_IRQ) - , m_channel_type(type) - , m_io_group(io_group) - , m_parent_controller(controller) -{ - initialize(); -} - -void IDEChannel::clear_pending_interrupts() const -{ - m_io_group.io_base().offset(ATA_REG_STATUS).in(); -} - -UNMAP_AFTER_INIT IDEChannel::~IDEChannel() -{ -} - -void IDEChannel::start_request(AsyncBlockDeviceRequest& request, bool is_slave, u16 capabilities) -{ - MutexLocker locker(m_lock); - VERIFY(m_current_request.is_null()); - - dbgln_if(PATA_DEBUG, "IDEChannel::start_request"); - - m_current_request = request; - m_current_request_block_index = 0; - m_current_request_flushing_cache = false; - - if (request.request_type() == AsyncBlockDeviceRequest::Read) - ata_read_sectors(is_slave, capabilities); - else - ata_write_sectors(is_slave, capabilities); -} - -void IDEChannel::complete_current_request(AsyncDeviceRequest::RequestResult result) -{ - // NOTE: this may be called from the interrupt handler! - VERIFY(m_current_request); - VERIFY(m_request_lock.is_locked()); - - // Now schedule reading back the buffer as soon as we leave the irq handler. - // This is important so that we can safely write the buffer back, - // which could cause page faults. Note that this may be called immediately - // before Processor::deferred_call_queue returns! - g_io_work->queue([this, result]() { - dbgln_if(PATA_DEBUG, "IDEChannel::complete_current_request result: {}", (int)result); - MutexLocker locker(m_lock); - VERIFY(m_current_request); - auto current_request = m_current_request; - m_current_request.clear(); - current_request->complete(result); - }); -} - -static void print_ide_status(u8 status) -{ - dbgln("IDEChannel: print_ide_status: DRQ={} BSY={}, DRDY={}, DSC={}, DF={}, CORR={}, IDX={}, ERR={}", - (status & ATA_SR_DRQ) != 0, - (status & ATA_SR_BSY) != 0, - (status & ATA_SR_DRDY) != 0, - (status & ATA_SR_DSC) != 0, - (status & ATA_SR_DF) != 0, - (status & ATA_SR_CORR) != 0, - (status & ATA_SR_IDX) != 0, - (status & ATA_SR_ERR) != 0); -} - -void IDEChannel::try_disambiguate_error() -{ - VERIFY(m_lock.is_locked()); - dbgln("IDEChannel: Error cause:"); - - switch (m_device_error) { - case ATA_ER_BBK: - dbgln("IDEChannel: - Bad block"); - break; - case ATA_ER_UNC: - dbgln("IDEChannel: - Uncorrectable data"); - break; - case ATA_ER_MC: - dbgln("IDEChannel: - Media changed"); - break; - case ATA_ER_IDNF: - dbgln("IDEChannel: - ID mark not found"); - break; - case ATA_ER_MCR: - dbgln("IDEChannel: - Media change request"); - break; - case ATA_ER_ABRT: - dbgln("IDEChannel: - Command aborted"); - break; - case ATA_ER_TK0NF: - dbgln("IDEChannel: - Track 0 not found"); - break; - case ATA_ER_AMNF: - dbgln("IDEChannel: - No address mark"); - break; - default: - dbgln("IDEChannel: - No one knows"); - break; - } -} - -bool IDEChannel::handle_irq(const RegisterState&) -{ - u8 status = m_io_group.io_base().offset(ATA_REG_STATUS).in(); - - m_entropy_source.add_random_event(status); - - SpinlockLocker lock(m_request_lock); - dbgln_if(PATA_DEBUG, "IDEChannel: interrupt: DRQ={}, BSY={}, DRDY={}", - (status & ATA_SR_DRQ) != 0, - (status & ATA_SR_BSY) != 0, - (status & ATA_SR_DRDY) != 0); - - if (!m_current_request) { - dbgln("IDEChannel: IRQ but no pending request!"); - return false; - } - - if (status & ATA_SR_ERR) { - print_ide_status(status); - m_device_error = m_io_group.io_base().offset(ATA_REG_ERROR).in(); - dbgln("IDEChannel: Error {:#02x}!", (u8)m_device_error); - try_disambiguate_error(); - complete_current_request(AsyncDeviceRequest::Failure); - return true; - } - m_device_error = 0; - - // Now schedule reading/writing the buffer as soon as we leave the irq handler. - // This is important so that we can safely access the buffers, which could - // trigger page faults - g_io_work->queue([this]() { - MutexLocker locker(m_lock); - SpinlockLocker lock(m_request_lock); - if (m_current_request->request_type() == AsyncBlockDeviceRequest::Read) { - dbgln_if(PATA_DEBUG, "IDEChannel: Read block {}/{}", m_current_request_block_index, m_current_request->block_count()); - - if (ata_do_read_sector()) { - if (++m_current_request_block_index >= m_current_request->block_count()) { - complete_current_request(AsyncDeviceRequest::Success); - return; - } - // Wait for the next block - enable_irq(); - } - } else { - if (!m_current_request_flushing_cache) { - dbgln_if(PATA_DEBUG, "IDEChannel: Wrote block {}/{}", m_current_request_block_index, m_current_request->block_count()); - if (++m_current_request_block_index >= m_current_request->block_count()) { - // We read the last block, flush cache - VERIFY(!m_current_request_flushing_cache); - m_current_request_flushing_cache = true; - m_io_group.io_base().offset(ATA_REG_COMMAND).out(ATA_CMD_CACHE_FLUSH); - } else { - // Read next block - ata_do_write_sector(); - } - } else { - complete_current_request(AsyncDeviceRequest::Success); - } - } - }); - return true; -} - -static void io_delay() -{ - for (int i = 0; i < 4; ++i) - IO::in8(0x3f6); -} - -bool IDEChannel::wait_until_not_busy(bool slave, size_t milliseconds_timeout) -{ - IO::delay(20); - m_io_group.io_base().offset(ATA_REG_HDDEVSEL).out(0xA0 | (slave << 4)); // First, we need to select the drive itself - IO::delay(20); - size_t time_elapsed = 0; - while (m_io_group.control_base().in() & ATA_SR_BSY && time_elapsed <= milliseconds_timeout) { - IO::delay(1000); - time_elapsed++; - } - return time_elapsed <= milliseconds_timeout; -} - -bool IDEChannel::wait_until_not_busy(size_t milliseconds_timeout) -{ - size_t time_elapsed = 0; - while (m_io_group.control_base().in() & ATA_SR_BSY && time_elapsed <= milliseconds_timeout) { - IO::delay(1000); - time_elapsed++; - } - return time_elapsed <= milliseconds_timeout; -} - -StringView IDEChannel::channel_type_string() const -{ - if (m_channel_type == ChannelType::Primary) - return "Primary"sv; - - return "Secondary"sv; -} - -UNMAP_AFTER_INIT void IDEChannel::detect_disks() -{ - auto channel_string = [](u8 i) -> StringView { - if (i == 0) - return "master"sv; - - return "slave"sv; - }; - - // There are only two possible disks connected to a channel - for (auto i = 0; i < 2; i++) { - // We need to select the drive and then we wait 20 microseconds... and it doesn't hurt anything so let's just do it. - IO::delay(20); - m_io_group.io_base().offset(ATA_REG_HDDEVSEL).out(0xA0 | (i << 4)); // First, we need to select the drive itself - IO::delay(20); - - auto status = m_io_group.control_base().in(); - if (status == 0x0) { - dbgln_if(PATA_DEBUG, "IDEChannel: No {} {} disk detected!", channel_type_string(), channel_string(i)); - continue; - } - - m_io_group.io_base().offset(ATA_REG_SECCOUNT0).out(0); - m_io_group.io_base().offset(ATA_REG_LBA0).out(0); - m_io_group.io_base().offset(ATA_REG_LBA1).out(0); - m_io_group.io_base().offset(ATA_REG_LBA2).out(0); - m_io_group.io_base().offset(ATA_REG_COMMAND).out(ATA_CMD_IDENTIFY); // Send the ATA_IDENTIFY command - - // Wait 10 second for the BSY flag to clear - if (!wait_until_not_busy(2000)) { - dbgln_if(PATA_DEBUG, "IDEChannel: No {} {} disk detected, BSY flag was not reset!", channel_type_string(), channel_string(i)); - continue; - } - - bool check_for_atapi = false; - bool device_presence = true; - bool command_set_is_atapi = false; - - size_t milliseconds_elapsed = 0; - for (;;) { - // Wait about 10 seconds - if (milliseconds_elapsed > 2000) - break; - u8 status = m_io_group.control_base().in(); - if (status & ATA_SR_ERR) { - dbgln_if(PATA_DEBUG, "IDEChannel: {} {} device is not ATA. Will check for ATAPI.", channel_type_string(), channel_string(i)); - check_for_atapi = true; - break; - } - - if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRQ)) { - dbgln_if(PATA_DEBUG, "IDEChannel: {} {} device appears to be ATA.", channel_type_string(), channel_string(i)); - break; - } - - if (status == 0 || status == 0xFF) { - dbgln_if(PATA_DEBUG, "IDEChannel: {} {} device presence - none.", channel_type_string(), channel_string(i)); - device_presence = false; - break; - } - - IO::delay(1000); - milliseconds_elapsed++; - } - if (!device_presence) { - continue; - } - if (milliseconds_elapsed > 10000) { - dbgln_if(PATA_DEBUG, "IDEChannel: {} {} device state unknown. Timeout exceeded.", channel_type_string(), channel_string(i)); - continue; - } - - if (check_for_atapi) { - u8 cl = m_io_group.io_base().offset(ATA_REG_LBA1).in(); - u8 ch = m_io_group.io_base().offset(ATA_REG_LBA2).in(); - - if ((cl == 0x14 && ch == 0xEB) || (cl == 0x69 && ch == 0x96)) { - command_set_is_atapi = true; - dbgln("IDEChannel: {} {} device appears to be ATAPI. We're going to ignore it for now as we don't support it.", channel_type_string(), channel_string(i)); - continue; - } else { - dbgln("IDEChannel: {} {} device doesn't appear to be ATA or ATAPI. Ignoring it.", channel_type_string(), channel_string(i)); - continue; - } - } - - // FIXME: Handle possible OOM situation here. - ByteBuffer wbuf = ByteBuffer::create_uninitialized(512).release_value(); - ByteBuffer bbuf = ByteBuffer::create_uninitialized(512).release_value(); - u8* b = bbuf.data(); - u16* w = (u16*)wbuf.data(); - - for (u32 i = 0; i < 256; ++i) { - u16 data = m_io_group.io_base().offset(ATA_REG_DATA).in(); - *(w++) = data; - *(b++) = MSB(data); - *(b++) = LSB(data); - } - - // "Unpad" the device name string. - for (u32 i = 93; i > 54 && bbuf[i] == ' '; --i) - bbuf[i] = 0; - - volatile ATAIdentifyBlock& identify_block = (volatile ATAIdentifyBlock&)(*wbuf.data()); - - u16 capabilities = identify_block.capabilities[0]; - - // If the drive is so old that it doesn't support LBA, ignore it. - if (!(capabilities & ATA_CAP_LBA)) - continue; - u64 max_addressable_block = identify_block.max_28_bit_addressable_logical_sector; - - // if we support 48-bit LBA, use that value instead. - if (identify_block.commands_and_feature_sets_supported[1] & (1 << 10)) - max_addressable_block = identify_block.user_addressable_logical_sectors_count; - - dbgln("IDEChannel: {} {} {} device found: Name={}, Capacity={}, Capabilities={:#04x}", channel_type_string(), channel_string(i), !command_set_is_atapi ? "ATA" : "ATAPI", ((char*)bbuf.data() + 54), max_addressable_block * 512, capabilities); - // FIXME: Don't assume all drives will have logical sector size of 512 bytes. - ATADevice::Address address = { m_channel_type == ChannelType::Primary ? static_cast(0) : static_cast(1), static_cast(i) }; - if (i == 0) { - m_master = ATADiskDevice::create(m_parent_controller, address, capabilities, 512, max_addressable_block); - } else { - m_slave = ATADiskDevice::create(m_parent_controller, address, capabilities, 512, max_addressable_block); - } - } -} - -void IDEChannel::ata_access(Direction direction, bool slave_request, u64 lba, u8 block_count, u16 capabilities) -{ - VERIFY(m_lock.is_locked()); - VERIFY(m_request_lock.is_locked()); - LBAMode lba_mode; - u8 head = 0; - - VERIFY(capabilities & ATA_CAP_LBA); - if (lba >= 0x10000000) { - lba_mode = LBAMode::FortyEightBit; - head = 0; - } else { - lba_mode = LBAMode::TwentyEightBit; - head = (lba & 0xF000000) >> 24; - } - - // Wait 1 second - wait_until_not_busy(1000); - - // We need to select the drive and then we wait 20 microseconds... and it doesn't hurt anything so let's just do it. - m_io_group.io_base().offset(ATA_REG_HDDEVSEL).out(0xE0 | (static_cast(slave_request) << 4) | head); - IO::delay(20); - - if (lba_mode == LBAMode::FortyEightBit) { - m_io_group.io_base().offset(ATA_REG_SECCOUNT1).out(0); - m_io_group.io_base().offset(ATA_REG_LBA3).out((lba & 0xFF000000) >> 24); - m_io_group.io_base().offset(ATA_REG_LBA4).out((lba & 0xFF00000000ull) >> 32); - m_io_group.io_base().offset(ATA_REG_LBA5).out((lba & 0xFF0000000000ull) >> 40); - } - - m_io_group.io_base().offset(ATA_REG_SECCOUNT0).out(block_count); - m_io_group.io_base().offset(ATA_REG_LBA0).out((lba & 0x000000FF) >> 0); - m_io_group.io_base().offset(ATA_REG_LBA1).out((lba & 0x0000FF00) >> 8); - m_io_group.io_base().offset(ATA_REG_LBA2).out((lba & 0x00FF0000) >> 16); - - for (;;) { - auto status = m_io_group.control_base().in(); - if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRDY)) - break; - } - send_ata_io_command(lba_mode, direction); - enable_irq(); -} - -void IDEChannel::send_ata_io_command(LBAMode lba_mode, Direction direction) const -{ - if (lba_mode != LBAMode::FortyEightBit) { - m_io_group.io_base().offset(ATA_REG_COMMAND).out(direction == Direction::Read ? ATA_CMD_READ_PIO : ATA_CMD_WRITE_PIO); - } else { - m_io_group.io_base().offset(ATA_REG_COMMAND).out(direction == Direction::Read ? ATA_CMD_READ_PIO_EXT : ATA_CMD_WRITE_PIO_EXT); - } -} - -bool IDEChannel::ata_do_read_sector() -{ - VERIFY(m_lock.is_locked()); - VERIFY(m_request_lock.is_locked()); - VERIFY(!m_current_request.is_null()); - dbgln_if(PATA_DEBUG, "IDEChannel::ata_do_read_sector"); - auto& request = *m_current_request; - auto out_buffer = request.buffer().offset(m_current_request_block_index * 512); - auto result = request.write_to_buffer_buffered<512>(out_buffer, 512, [&](Bytes bytes) { - for (size_t i = 0; i < bytes.size(); i += sizeof(u16)) - *(u16*)bytes.offset_pointer(i) = IO::in16(m_io_group.io_base().offset(ATA_REG_DATA).get()); - return bytes.size(); - }); - if (result.is_error()) { - // TODO: Do we need to abort the PATA read if this wasn't the last block? - complete_current_request(AsyncDeviceRequest::MemoryFault); - return false; - } - return true; -} - -// FIXME: This doesn't quite work and locks up reading LBA 3. -void IDEChannel::ata_read_sectors(bool slave_request, u16 capabilities) -{ - VERIFY(m_lock.is_locked()); - VERIFY(!m_current_request.is_null()); - VERIFY(m_current_request->block_count() <= 256); - - SpinlockLocker m_lock(m_request_lock); - dbgln_if(PATA_DEBUG, "IDEChannel::ata_read_sectors"); - dbgln_if(PATA_DEBUG, "IDEChannel: Reading {} sector(s) @ LBA {}", m_current_request->block_count(), m_current_request->block_index()); - ata_access(Direction::Read, slave_request, m_current_request->block_index(), m_current_request->block_count(), capabilities); -} - -void IDEChannel::ata_do_write_sector() -{ - VERIFY(m_lock.is_locked()); - VERIFY(m_request_lock.is_locked()); - VERIFY(!m_current_request.is_null()); - auto& request = *m_current_request; - - io_delay(); - while ((m_io_group.control_base().in() & ATA_SR_BSY) || !(m_io_group.control_base().in() & ATA_SR_DRQ)) - ; - - u8 status = m_io_group.control_base().in(); - VERIFY(status & ATA_SR_DRQ); - - auto in_buffer = request.buffer().offset(m_current_request_block_index * 512); - dbgln_if(PATA_DEBUG, "IDEChannel: Writing 512 bytes (part {}) (status={:#02x})...", m_current_request_block_index, status); - auto result = request.read_from_buffer_buffered<512>(in_buffer, 512, [&](ReadonlyBytes readonly_bytes) { - for (size_t i = 0; i < readonly_bytes.size(); i += sizeof(u16)) - IO::out16(m_io_group.io_base().offset(ATA_REG_DATA).get(), *(const u16*)readonly_bytes.offset(i)); - return readonly_bytes.size(); - }); - if (result.is_error()) - complete_current_request(AsyncDeviceRequest::MemoryFault); -} - -// FIXME: I'm assuming this doesn't work based on the fact PIO read doesn't work. -void IDEChannel::ata_write_sectors(bool slave_request, u16 capabilities) -{ - VERIFY(m_lock.is_locked()); - VERIFY(!m_current_request.is_null()); - VERIFY(m_current_request->block_count() <= 256); - - SpinlockLocker m_lock(m_request_lock); - dbgln_if(PATA_DEBUG, "IDEChannel: Writing {} sector(s) @ LBA {}", m_current_request->block_count(), m_current_request->block_index()); - ata_access(Direction::Write, slave_request, m_current_request->block_index(), m_current_request->block_count(), capabilities); - ata_do_write_sector(); -} -} diff --git a/Kernel/Storage/IDEChannel.h b/Kernel/Storage/IDEChannel.h deleted file mode 100644 index 0d8ac71d6b..0000000000 --- a/Kernel/Storage/IDEChannel.h +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -// -// Parallel ATA (PATA) controller driver -// -// This driver describes a logical PATA Channel. Each channel can connect up to 2 -// IDE Hard Disk Drives. The drives themselves can be either the master drive (hd0) -// or the slave drive (hd1). -// -// More information about the ATA spec for PATA can be found here: -// ftp://ftp.seagate.com/acrobat/reference/111-1c.pdf -// - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class AsyncBlockDeviceRequest; - -class IDEController; -class IDEChannel : public RefCounted - , public IRQHandler { - friend class IDEController; - AK_MAKE_ETERNAL -public: - enum class ChannelType : u8 { - Primary, - Secondary - }; - - struct IOAddressGroup { - IOAddressGroup(IOAddress io_base, IOAddress control_base, IOAddress bus_master_base) - : m_io_base(io_base) - , m_control_base(control_base) - , m_bus_master_base(bus_master_base) - { - } - - IOAddressGroup(IOAddress io_base, IOAddress control_base) - : m_io_base(io_base) - , m_control_base(control_base) - , m_bus_master_base() - { - } - - IOAddressGroup(IOAddressGroup const& other, IOAddress bus_master_base) - : m_io_base(other.io_base()) - , m_control_base(other.control_base()) - , m_bus_master_base(bus_master_base) - { - } - - IOAddressGroup(IOAddressGroup const&) = default; - - // Disable default implementations that would use surprising integer promotion. - bool operator==(const IOAddressGroup&) const = delete; - bool operator<=(const IOAddressGroup&) const = delete; - bool operator>=(const IOAddressGroup&) const = delete; - bool operator<(const IOAddressGroup&) const = delete; - bool operator>(const IOAddressGroup&) const = delete; - - IOAddress io_base() const { return m_io_base; }; - IOAddress control_base() const { return m_control_base; } - Optional bus_master_base() const { return m_bus_master_base; } - - private: - IOAddress m_io_base; - IOAddress m_control_base; - Optional m_bus_master_base; - }; - -public: - static NonnullRefPtr create(const IDEController&, IOAddressGroup, ChannelType type); - static NonnullRefPtr create(const IDEController&, u8 irq, IOAddressGroup, ChannelType type); - virtual ~IDEChannel() override; - - RefPtr master_device() const; - RefPtr slave_device() const; - - virtual StringView purpose() const override { return "PATA Channel"sv; } - - virtual bool is_dma_enabled() const { return false; } - -private: - void complete_current_request(AsyncDeviceRequest::RequestResult); - void initialize(); - -protected: - enum class LBAMode : u8 { - None, // CHS - TwentyEightBit, - FortyEightBit, - }; - - enum class Direction : u8 { - Read, - Write, - }; - - IDEChannel(const IDEController&, IOAddressGroup, ChannelType type); - IDEChannel(const IDEController&, u8 irq, IOAddressGroup, ChannelType type); - //^ IRQHandler - virtual bool handle_irq(const RegisterState&) override; - - virtual void send_ata_io_command(LBAMode lba_mode, Direction direction) const; - - virtual void ata_read_sectors(bool, u16); - virtual void ata_write_sectors(bool, u16); - - void detect_disks(); - StringView channel_type_string() const; - - void try_disambiguate_error(); - bool wait_until_not_busy(bool slave, size_t milliseconds_timeout); - bool wait_until_not_busy(size_t milliseconds_timeout); - - void start_request(AsyncBlockDeviceRequest&, bool, u16); - - void clear_pending_interrupts() const; - - void ata_access(Direction, bool, u64, u8, u16); - - bool ata_do_read_sector(); - void ata_do_write_sector(); - - // Data members - ChannelType m_channel_type { ChannelType::Primary }; - - volatile u8 m_device_error { 0 }; - EntropySource m_entropy_source; - - RefPtr m_master; - RefPtr m_slave; - - RefPtr m_current_request; - u64 m_current_request_block_index { 0 }; - bool m_current_request_flushing_cache { false }; - Spinlock m_request_lock; - Mutex m_lock { "IDEChannel" }; - - IOAddressGroup m_io_group; - NonnullRefPtr m_parent_controller; -}; -} diff --git a/Kernel/Storage/IDEController.cpp b/Kernel/Storage/IDEController.cpp deleted file mode 100644 index 4566962813..0000000000 --- a/Kernel/Storage/IDEController.cpp +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -UNMAP_AFTER_INIT NonnullRefPtr IDEController::initialize(PCI::DeviceIdentifier const& device_identifier, bool force_pio) -{ - return adopt_ref(*new IDEController(device_identifier, force_pio)); -} - -bool IDEController::reset() -{ - TODO(); -} - -bool IDEController::shutdown() -{ - TODO(); -} - -size_t IDEController::devices_count() const -{ - size_t count = 0; - for (u32 index = 0; index < 4; index++) { - if (!device(index).is_null()) - count++; - } - return count; -} - -void IDEController::start_request(const ATADevice& device, AsyncBlockDeviceRequest& request) -{ - auto& address = device.ata_address(); - VERIFY(address.subport < 2); - switch (address.port) { - case 0: - m_channels[0].start_request(request, address.subport == 0 ? false : true, device.ata_capabilites()); - return; - case 1: - m_channels[1].start_request(request, address.subport == 0 ? false : true, device.ata_capabilites()); - return; - } - VERIFY_NOT_REACHED(); -} - -void IDEController::complete_current_request(AsyncDeviceRequest::RequestResult) -{ - VERIFY_NOT_REACHED(); -} - -UNMAP_AFTER_INIT IDEController::IDEController(PCI::DeviceIdentifier const& device_identifier, bool force_pio) - : ATAController() - , PCI::Device(device_identifier.address()) - , m_prog_if(device_identifier.prog_if()) - , m_interrupt_line(device_identifier.interrupt_line()) -{ - PCI::enable_io_space(device_identifier.address()); - PCI::enable_memory_space(device_identifier.address()); - initialize(force_pio); -} - -UNMAP_AFTER_INIT IDEController::~IDEController() -{ -} - -bool IDEController::is_pci_native_mode_enabled() const -{ - return (m_prog_if.value() & 0x05) != 0; -} - -bool IDEController::is_pci_native_mode_enabled_on_primary_channel() const -{ - return (m_prog_if.value() & 0x1) == 0x1; -} - -bool IDEController::is_pci_native_mode_enabled_on_secondary_channel() const -{ - return (m_prog_if.value() & 0x4) == 0x4; -} - -bool IDEController::is_bus_master_capable() const -{ - return m_prog_if.value() & (1 << 7); -} - -static const char* detect_controller_type(u8 programming_value) -{ - switch (programming_value) { - case 0x00: - return "ISA Compatibility mode-only controller"; - case 0x05: - return "PCI native mode-only controller"; - case 0x0A: - return "ISA Compatibility mode controller, supports both channels switched to PCI native mode"; - case 0x0F: - return "PCI native mode controller, supports both channels switched to ISA compatibility mode"; - case 0x80: - return "ISA Compatibility mode-only controller, supports bus mastering"; - case 0x85: - return "PCI native mode-only controller, supports bus mastering"; - case 0x8A: - return "ISA Compatibility mode controller, supports both channels switched to PCI native mode, supports bus mastering"; - case 0x8F: - return "PCI native mode controller, supports both channels switched to ISA compatibility mode, supports bus mastering"; - default: - VERIFY_NOT_REACHED(); - } - VERIFY_NOT_REACHED(); -} - -UNMAP_AFTER_INIT void IDEController::initialize(bool force_pio) -{ - auto bus_master_base = IOAddress(PCI::get_BAR4(pci_address()) & (~1)); - dbgln("IDE controller @ {}: bus master base was set to {}", pci_address(), bus_master_base); - dbgln("IDE controller @ {}: interrupt line was set to {}", pci_address(), m_interrupt_line.value()); - dbgln("IDE controller @ {}: {}", pci_address(), detect_controller_type(m_prog_if.value())); - dbgln("IDE controller @ {}: primary channel DMA capable? {}", pci_address(), ((bus_master_base.offset(2).in() >> 5) & 0b11)); - dbgln("IDE controller @ {}: secondary channel DMA capable? {}", pci_address(), ((bus_master_base.offset(2 + 8).in() >> 5) & 0b11)); - - if (!is_bus_master_capable()) - force_pio = true; - - auto bar0 = PCI::get_BAR0(pci_address()); - auto primary_base_io = (bar0 == 0x1 || bar0 == 0) ? IOAddress(0x1F0) : IOAddress(bar0 & (~1)); - auto bar1 = PCI::get_BAR1(pci_address()); - auto primary_control_io = (bar1 == 0x1 || bar1 == 0) ? IOAddress(0x3F6) : IOAddress(bar1 & (~1)); - auto bar2 = PCI::get_BAR2(pci_address()); - auto secondary_base_io = (bar2 == 0x1 || bar2 == 0) ? IOAddress(0x170) : IOAddress(bar2 & (~1)); - auto bar3 = PCI::get_BAR3(pci_address()); - auto secondary_control_io = (bar3 == 0x1 || bar3 == 0) ? IOAddress(0x376) : IOAddress(bar3 & (~1)); - - auto irq_line = m_interrupt_line.value(); - if (is_pci_native_mode_enabled()) { - VERIFY(irq_line != 0); - } - - if (is_pci_native_mode_enabled_on_primary_channel()) { - if (force_pio) - m_channels.append(IDEChannel::create(*this, irq_line, { primary_base_io, primary_control_io }, IDEChannel::ChannelType::Primary)); - else - m_channels.append(BMIDEChannel::create(*this, irq_line, { primary_base_io, primary_control_io, bus_master_base }, IDEChannel::ChannelType::Primary)); - } else { - if (force_pio) - m_channels.append(IDEChannel::create(*this, { primary_base_io, primary_control_io }, IDEChannel::ChannelType::Primary)); - else - m_channels.append(BMIDEChannel::create(*this, { primary_base_io, primary_control_io, bus_master_base }, IDEChannel::ChannelType::Primary)); - } - - m_channels[0].enable_irq(); - - if (is_pci_native_mode_enabled_on_secondary_channel()) { - if (force_pio) - m_channels.append(IDEChannel::create(*this, irq_line, { secondary_base_io, secondary_control_io }, IDEChannel::ChannelType::Secondary)); - else - m_channels.append(BMIDEChannel::create(*this, irq_line, { secondary_base_io, secondary_control_io, bus_master_base.offset(8) }, IDEChannel::ChannelType::Secondary)); - } else { - if (force_pio) - m_channels.append(IDEChannel::create(*this, { secondary_base_io, secondary_control_io }, IDEChannel::ChannelType::Secondary)); - else - m_channels.append(BMIDEChannel::create(*this, { secondary_base_io, secondary_control_io, bus_master_base.offset(8) }, IDEChannel::ChannelType::Secondary)); - } - - m_channels[1].enable_irq(); -} - -RefPtr IDEController::device_by_channel_and_position(u32 index) const -{ - switch (index) { - case 0: - return m_channels[0].master_device(); - case 1: - return m_channels[0].slave_device(); - case 2: - return m_channels[1].master_device(); - case 3: - return m_channels[1].slave_device(); - } - VERIFY_NOT_REACHED(); -} - -RefPtr IDEController::device(u32 index) const -{ - NonnullRefPtrVector connected_devices; - for (size_t index = 0; index < 4; index++) { - auto checked_device = device_by_channel_and_position(index); - if (checked_device.is_null()) - continue; - connected_devices.append(checked_device.release_nonnull()); - } - if (index >= connected_devices.size()) - return nullptr; - return connected_devices[index]; -} -} diff --git a/Kernel/Storage/IDEController.h b/Kernel/Storage/IDEController.h deleted file mode 100644 index f87158ea32..0000000000 --- a/Kernel/Storage/IDEController.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2020, Liav A. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Kernel { - -class AsyncBlockDeviceRequest; - -class IDEController final : public ATAController - , public PCI::Device { - AK_MAKE_ETERNAL -public: - static NonnullRefPtr initialize(PCI::DeviceIdentifier const&, bool force_pio); - virtual ~IDEController() override; - - virtual RefPtr device(u32 index) const override; - virtual bool reset() override; - virtual bool shutdown() override; - virtual size_t devices_count() const override; - virtual void start_request(const ATADevice&, AsyncBlockDeviceRequest&) override; - virtual void complete_current_request(AsyncDeviceRequest::RequestResult) override; - - bool is_bus_master_capable() const; - bool is_pci_native_mode_enabled() const; - -private: - bool is_pci_native_mode_enabled_on_primary_channel() const; - bool is_pci_native_mode_enabled_on_secondary_channel() const; - IDEController(PCI::DeviceIdentifier const&, bool force_pio); - - RefPtr device_by_channel_and_position(u32 index) const; - void initialize(bool force_pio); - void detect_disks(); - - NonnullRefPtrVector m_channels; - // FIXME: Find a better way to get the ProgrammingInterface - PCI::ProgrammingInterface m_prog_if; - PCI::InterruptLine m_interrupt_line; -}; -} diff --git a/Kernel/Storage/StorageManagement.cpp b/Kernel/Storage/StorageManagement.cpp index 9c1b6035a8..bed09b1f35 100644 --- a/Kernel/Storage/StorageManagement.cpp +++ b/Kernel/Storage/StorageManagement.cpp @@ -13,8 +13,8 @@ #include #include #include -#include -#include +#include +#include #include #include #include -- cgit v1.2.3