2021-02-26 01:25:45 -03:00
/*
* Copyright ( c ) 2021 , Liav A . < liavalb @ hotmail . co . il >
*
2021-04-22 05:24:48 -03:00
* SPDX - License - Identifier : BSD - 2 - Clause
2021-02-26 01:25:45 -03:00
*/
2021-05-14 07:46:01 -03:00
// For more information about locking in this code
// please look at Documentation/Kernel/AHCILocking.md
2021-02-26 01:25:45 -03:00
# include <AK/Atomic.h>
2022-09-02 05:23:32 -03:00
# include <Kernel/Arch/Delay.h>
2021-08-21 20:37:17 -03:00
# include <Kernel/Locking/Spinlock.h>
2021-08-06 05:45:34 -03:00
# include <Kernel/Memory/MemoryManager.h>
# include <Kernel/Memory/ScatterGatherList.h>
# include <Kernel/Memory/TypedMapping.h>
2021-11-19 06:52:07 -03:00
# include <Kernel/Storage/ATA/AHCI/Port.h>
2021-11-13 05:19:31 -03:00
# include <Kernel/Storage/ATA/ATADiskDevice.h>
2021-11-20 11:42:00 -03:00
# include <Kernel/Storage/ATA/Definitions.h>
2021-08-27 02:08:43 -03:00
# include <Kernel/Storage/StorageManagement.h>
2021-03-18 20:32:42 -03:00
# include <Kernel/WorkQueue.h>
2021-02-26 01:25:45 -03:00
namespace Kernel {
2022-08-19 15:53:40 -03:00
UNMAP_AFTER_INIT ErrorOr < NonnullLockRefPtr < AHCIPort > > AHCIPort : : create ( AHCIController const & controller , AHCI : : HBADefinedCapabilities hba_capabilities , volatile AHCI : : PortRegisters & registers , u32 port_index )
2021-02-26 01:25:45 -03:00
{
2022-07-14 09:27:22 -03:00
auto identify_buffer_page = MUST ( MM . allocate_physical_page ( ) ) ;
2022-08-19 15:53:40 -03:00
auto port = TRY ( adopt_nonnull_lock_ref_or_enomem ( new ( nothrow ) AHCIPort ( controller , move ( identify_buffer_page ) , hba_capabilities , registers , port_index ) ) ) ;
2022-04-01 14:49:22 -03:00
TRY ( port - > allocate_resources_and_initialize_ports ( ) ) ;
return port ;
2021-02-26 01:25:45 -03:00
}
2022-04-01 14:49:22 -03:00
ErrorOr < void > AHCIPort : : allocate_resources_and_initialize_ports ( )
2021-02-26 01:25:45 -03:00
{
if ( is_interface_disabled ( ) ) {
m_disabled_by_firmware = true ;
2022-04-01 14:49:22 -03:00
return { } ;
2021-02-26 01:25:45 -03:00
}
2022-07-14 09:27:22 -03:00
m_fis_receive_page = TRY ( MM . allocate_physical_page ( ) ) ;
2021-03-18 02:24:39 -03:00
2021-02-26 01:25:45 -03:00
for ( size_t index = 0 ; index < 1 ; index + + ) {
2022-07-14 09:27:22 -03:00
auto dma_page = TRY ( MM . allocate_physical_page ( ) ) ;
2022-04-01 14:49:22 -03:00
m_dma_buffers . append ( move ( dma_page ) ) ;
2021-02-26 01:25:45 -03:00
}
for ( size_t index = 0 ; index < 1 ; index + + ) {
2022-07-14 09:27:22 -03:00
auto command_table_page = TRY ( MM . allocate_physical_page ( ) ) ;
2022-04-01 14:49:22 -03:00
m_command_table_pages . append ( move ( command_table_page ) ) ;
2021-02-26 01:25:45 -03:00
}
2021-09-05 20:36:14 -03:00
2022-07-11 14:32:29 -03:00
m_command_list_region = TRY ( MM . allocate_dma_buffer_page ( " AHCI Port Command List " sv , Memory : : Region : : Access : : ReadWrite , m_command_list_page ) ) ;
2022-01-06 13:31:05 -03:00
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_fis_receive_page - > paddr ( ) ) ;
2021-03-18 02:24:39 -03:00
dbgln_if ( AHCI_DEBUG , " AHCI Port {}: Command list region at {} " , representative_port_index ( ) , m_command_list_region - > vaddr ( ) ) ;
2022-04-01 14:49:22 -03:00
return { } ;
}
2022-08-24 10:56:26 -03:00
UNMAP_AFTER_INIT AHCIPort : : AHCIPort ( AHCIController const & controller , NonnullRefPtr < Memory : : PhysicalPage > identify_buffer_page , AHCI : : HBADefinedCapabilities hba_capabilities , volatile AHCI : : PortRegisters & registers , u32 port_index )
2022-04-01 14:49:22 -03:00
: m_port_index ( port_index )
2022-04-01 17:45:07 -03:00
, m_hba_capabilities ( hba_capabilities )
2022-04-01 19:47:44 -03:00
, m_identify_buffer_page ( move ( identify_buffer_page ) )
2022-04-01 14:49:22 -03:00
, m_port_registers ( registers )
2022-04-01 17:45:07 -03:00
, m_parent_controller ( controller )
2022-04-01 14:49:22 -03:00
, m_interrupt_status ( ( u32 volatile & ) m_port_registers . is )
, m_interrupt_enable ( ( u32 volatile & ) m_port_registers . ie )
{
2021-02-26 01:25:45 -03:00
}
void AHCIPort : : clear_sata_error_register ( ) const
{
2021-03-18 02:24:39 -03:00
dbgln_if ( AHCI_DEBUG , " AHCI Port {}: Clearing SATA error register. " , representative_port_index ( ) ) ;
2021-02-26 01:25:45 -03:00
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 ;
}
2021-08-27 02:08:43 -03:00
if ( m_interrupt_status . is_set ( AHCI : : PortInterruptFlag : : PRC ) & & m_interrupt_status . is_set ( AHCI : : PortInterruptFlag : : PC ) ) {
clear_sata_error_register ( ) ;
2022-01-26 16:22:28 -03:00
if ( ( m_port_registers . ssts & 0xf ) ! = 3 & & m_connected_device ) {
2021-08-27 02:08:43 -03:00
m_connected_device - > prepare_for_unplug ( ) ;
StorageManagement : : the ( ) . remove_device ( * m_connected_device ) ;
2022-03-06 17:07:04 -03:00
auto work_item_creation_result = g_io_work - > try_queue ( [ this ] ( ) {
2021-08-27 02:08:43 -03:00
m_connected_device . clear ( ) ;
} ) ;
2022-03-06 17:07:04 -03:00
if ( work_item_creation_result . is_error ( ) ) {
auto current_request = m_current_request ;
m_current_request . clear ( ) ;
current_request - > complete ( AsyncDeviceRequest : : OutOfMemory ) ;
}
2021-08-27 02:08:43 -03:00
} else {
2022-03-06 17:07:04 -03:00
auto work_item_creation_result = g_io_work - > try_queue ( [ this ] ( ) {
2021-08-27 02:08:43 -03:00
reset ( ) ;
} ) ;
2022-03-06 17:07:04 -03:00
if ( work_item_creation_result . is_error ( ) ) {
auto current_request = m_current_request ;
m_current_request . clear ( ) ;
current_request - > complete ( AsyncDeviceRequest : : OutOfMemory ) ;
}
2021-08-27 02:08:43 -03:00
}
return ;
}
2021-05-14 19:37:38 -03:00
if ( m_interrupt_status . is_set ( AHCI : : PortInterruptFlag : : PRC ) ) {
2021-03-18 20:32:42 -03:00
clear_sata_error_register ( ) ;
2021-02-26 01:25:45 -03:00
}
if ( m_interrupt_status . is_set ( AHCI : : PortInterruptFlag : : INF ) ) {
2021-03-19 16:05:47 -03:00
// We need to defer the reset, because we can receive interrupts when
// resetting the device.
2022-03-06 17:07:04 -03:00
auto work_item_creation_result = g_io_work - > try_queue ( [ this ] ( ) {
2021-03-19 16:05:47 -03:00
reset ( ) ;
} ) ;
2022-03-06 17:07:04 -03:00
if ( work_item_creation_result . is_error ( ) ) {
auto current_request = m_current_request ;
m_current_request . clear ( ) ;
current_request - > complete ( AsyncDeviceRequest : : OutOfMemory ) ;
}
2021-02-26 01:25:45 -03:00
return ;
}
2021-03-18 02:36:13 -03:00
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 ) ) {
2022-03-06 17:07:04 -03:00
auto work_item_creation_result = g_io_work - > try_queue ( [ this ] ( ) {
2021-03-18 20:32:42 -03:00
recover_from_fatal_error ( ) ;
} ) ;
2022-03-06 17:07:04 -03:00
if ( work_item_creation_result . is_error ( ) ) {
auto current_request = m_current_request ;
m_current_request . clear ( ) ;
current_request - > complete ( AsyncDeviceRequest : : OutOfMemory ) ;
}
2021-03-18 20:32:42 -03:00
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 {
2022-03-06 17:07:04 -03:00
auto work_item_creation_result = g_io_work - > try_queue ( [ this ] ( ) {
2021-03-18 20:32:42 -03:00
dbgln_if ( AHCI_DEBUG , " AHCI Port {}: Request handled " , representative_port_index ( ) ) ;
2021-07-17 20:13:34 -03:00
MutexLocker locker ( m_lock ) ;
2021-03-18 20:32:42 -03:00
VERIFY ( m_current_request ) ;
VERIFY ( m_current_scatter_list ) ;
2021-08-27 02:08:43 -03:00
if ( ! m_connected_device ) {
dbgln_if ( AHCI_DEBUG , " AHCI Port {}: Request success " , representative_port_index ( ) ) ;
complete_current_request ( AsyncDeviceRequest : : Failure ) ;
return ;
}
2021-03-18 20:32:42 -03:00
if ( m_current_request - > request_type ( ) = = AsyncBlockDeviceRequest : : Read ) {
2021-09-07 07:09:52 -03:00
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 ( ) ) {
2021-03-18 20:32:42 -03:00
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 ) ;
} ) ;
2022-03-06 17:07:04 -03:00
if ( work_item_creation_result . is_error ( ) ) {
auto current_request = m_current_request ;
m_current_request . clear ( ) ;
current_request - > complete ( AsyncDeviceRequest : : OutOfMemory ) ;
}
2021-03-18 20:32:42 -03:00
}
2021-02-26 01:25:45 -03:00
}
2021-03-18 20:32:42 -03:00
2021-02-26 01:25:45 -03:00
m_interrupt_status . clear ( ) ;
}
bool AHCIPort : : is_interrupts_enabled ( ) const
{
return ! m_interrupt_enable . is_cleared ( ) ;
}
void AHCIPort : : recover_from_fatal_error ( )
{
2021-07-17 20:13:34 -03:00
MutexLocker locker ( m_lock ) ;
2021-08-21 20:49:22 -03:00
SpinlockLocker lock ( m_hard_lock ) ;
2022-08-19 15:53:40 -03:00
LockRefPtr < AHCIController > controller = m_parent_controller . strong_ref ( ) ;
2022-04-01 17:45:07 -03:00
if ( ! controller ) {
dmesgln ( " AHCI Port {}: fatal error, controller not available " , representative_port_index ( ) ) ;
return ;
}
dmesgln ( " {}: AHCI Port {} fatal error, shutting down! " , controller - > pci_address ( ) , representative_port_index ( ) ) ;
dmesgln ( " {}: AHCI Port {} fatal error, SError {} " , controller - > pci_address ( ) , representative_port_index ( ) , ( u32 ) m_port_registers . serr ) ;
2021-02-26 01:25:45 -03:00
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 ;
2022-07-11 14:32:29 -03:00
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 ) ) . value ( ) , " AHCI Command Table " sv , Memory : : Region : : Access : : ReadWrite , Memory : : Region : : Cacheable : : No ) . release_value ( ) ;
2021-02-26 01:25:45 -03:00
auto & command_table = * ( volatile AHCI : : CommandTable * ) command_table_region - > vaddr ( ) . as_ptr ( ) ;
memset ( const_cast < u8 * > ( 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 < u8 * > ( 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 ) {
2021-07-21 20:30:24 -03:00
dbgln_if ( AHCI_DEBUG , " AHCI Port {}: Eject Drive failed, SError {:#08x} " , representative_port_index ( ) , ( u32 ) m_port_registers . serr ) ;
2021-03-15 04:29:39 -03:00
try_disambiguate_sata_error ( ) ;
2021-02-26 01:25:45 -03:00
VERIFY_NOT_REACHED ( ) ;
}
}
dbgln ( " AHCI Port {}: Eject Drive " , representative_port_index ( ) ) ;
return ;
}
bool AHCIPort : : reset ( )
{
2021-07-17 20:13:34 -03:00
MutexLocker locker ( m_lock ) ;
2021-08-21 20:49:22 -03:00
SpinlockLocker lock ( m_hard_lock ) ;
2021-02-26 01:25:45 -03:00
2021-03-18 02:24:39 -03:00
dbgln_if ( AHCI_DEBUG , " AHCI Port {}: Resetting " , representative_port_index ( ) ) ;
2021-02-26 01:25:45 -03:00
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 ( ) ;
2021-11-13 08:07:55 -03:00
if ( ! initiate_sata_reset ( ) ) {
2021-02-26 01:25:45 -03:00
return false ;
}
2021-11-13 05:32:49 -03:00
return initialize ( ) ;
2021-03-12 13:35:16 -03:00
}
2022-04-01 14:52:37 -03:00
UNMAP_AFTER_INIT bool AHCIPort : : initialize_without_reset ( )
2021-03-12 13:35:16 -03:00
{
2021-07-17 20:13:34 -03:00
MutexLocker locker ( m_lock ) ;
2021-08-21 20:49:22 -03:00
SpinlockLocker lock ( m_hard_lock ) ;
2021-03-12 13:35:16 -03:00
dmesgln ( " AHCI Port {}: {} " , representative_port_index ( ) , try_disambiguate_sata_status ( ) ) ;
2021-11-13 05:32:49 -03:00
return initialize ( ) ;
2021-03-12 13:35:16 -03:00
}
2021-11-13 05:32:49 -03:00
bool AHCIPort : : initialize ( )
2021-03-12 13:35:16 -03:00
{
VERIFY ( m_lock . is_locked ( ) ) ;
2021-07-21 20:30:24 -03:00
dbgln_if ( AHCI_DEBUG , " AHCI Port {}: Initialization. Signature = {:#08x} " , representative_port_index ( ) , static_cast < u32 > ( m_port_registers . sig ) ) ;
2021-03-18 02:24:39 -03:00
if ( ! is_phy_enabled ( ) ) {
2021-05-28 19:22:27 -03:00
// 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 ( ) ;
2021-03-18 02:24:39 -03:00
dbgln_if ( AHCI_DEBUG , " AHCI Port {}: Bailing initialization, Phy is not enabled. " , representative_port_index ( ) ) ;
2021-03-12 13:35:16 -03:00
return false ;
2021-03-18 02:24:39 -03:00
}
2021-02-26 01:25:45 -03:00
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 ;
2021-03-16 16:23:16 -03:00
u64 max_addressable_sector = 0 ;
2022-04-01 17:45:07 -03:00
2021-11-13 05:32:49 -03:00
if ( identify_device ( ) ) {
2022-04-01 19:47:44 -03:00
auto identify_block = Memory : : map_typed < ATAIdentifyBlock > ( m_identify_buffer_page - > paddr ( ) ) . release_value_but_fixme_should_propagate_errors ( ) ;
2021-02-26 01:25:45 -03:00
// 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 ) ;
}
2021-03-16 16:23:16 -03:00
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 ) ;
2021-02-26 01:25:45 -03:00
// FIXME: We don't support ATAPI devices yet, so for now we don't "create" them
if ( ! is_atapi_attached ( ) ) {
2022-08-19 15:53:40 -03:00
LockRefPtr < AHCIController > controller = m_parent_controller . strong_ref ( ) ;
2022-04-01 19:47:44 -03:00
if ( ! controller ) {
dmesgln ( " AHCI Port {}: Device found, but parent controller is not available, abort. " , representative_port_index ( ) ) ;
return false ;
}
2022-04-01 17:45:07 -03:00
m_connected_device = ATADiskDevice : : create ( * controller , { m_port_index , 0 } , 0 , logical_sector_size , max_addressable_sector ) ;
2021-03-18 02:24:39 -03:00
} else {
dbgln ( " AHCI Port {}: Ignoring ATAPI devices for now as we don't currently support them. " , representative_port_index ( ) ) ;
2021-02-26 01:25:45 -03:00
}
}
return true ;
}
2022-04-01 14:58:27 -03:00
char const * AHCIPort : : try_disambiguate_sata_status ( )
2021-02-26 01:25:45 -03:00
{
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 ( ) ;
}
2021-03-15 04:29:39 -03:00
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 )
2021-05-15 14:28:46 -03:00
dmesgln ( " AHCI Port {}: - Recovered communications error " , representative_port_index ( ) ) ;
2021-03-15 04:29:39 -03:00
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 ( ) ) ;
}
}
2021-02-26 01:25:45 -03:00
void AHCIPort : : rebase ( )
{
VERIFY ( m_lock . is_locked ( ) ) ;
2021-03-18 20:32:42 -03:00
VERIFY ( m_hard_lock . is_locked ( ) ) ;
2021-02-26 01:25:45 -03:00
VERIFY ( ! m_command_list_page . is_null ( ) & & ! m_fis_receive_page . is_null ( ) ) ;
2021-03-18 02:24:39 -03:00
dbgln_if ( AHCI_DEBUG , " AHCI Port {}: Rebasing. " , representative_port_index ( ) ) ;
2021-02-26 01:25:45 -03:00
full_memory_barrier ( ) ;
stop_command_list_processing ( ) ;
stop_fis_receiving ( ) ;
full_memory_barrier ( ) ;
2021-11-13 09:20:48 -03:00
2021-02-26 01:25:45 -03:00
// Try to wait 1 second for HBA to clear Command List Running and FIS Receive Running
2021-11-13 09:20:48 -03:00
wait_until_condition_met_or_timeout ( 1000 , 1000 , [ this ] ( ) - > bool {
return ! ( m_port_registers . cmd & ( 1 < < 15 ) ) & & ! ( m_port_registers . cmd & ( 1 < < 14 ) ) ;
} ) ;
2021-02-26 01:25:45 -03:00
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 ( ) ) ;
2021-03-18 20:32:42 -03:00
VERIFY ( m_hard_lock . is_locked ( ) ) ;
2021-03-18 02:24:39 -03:00
dbgln_if ( AHCI_DEBUG , " AHCI Port {}: Switching to active state. " , representative_port_index ( ) ) ;
2021-02-26 01:25:45 -03:00
m_port_registers . cmd = ( m_port_registers . cmd & 0x0ffffff ) | ( 1 < < 28 ) ;
}
void AHCIPort : : set_sleep_state ( ) const
{
VERIFY ( m_lock . is_locked ( ) ) ;
2021-03-18 20:32:42 -03:00
VERIFY ( m_hard_lock . is_locked ( ) ) ;
2021-02-26 01:25:45 -03:00
m_port_registers . cmd = ( m_port_registers . cmd & 0x0ffffff ) | ( 0b1000 < < 28 ) ;
}
2021-02-26 09:38:17 -03:00
size_t AHCIPort : : calculate_descriptors_count ( size_t block_count ) const
2021-02-26 01:25:45 -03:00
{
2021-02-26 09:38:17 -03:00
VERIFY ( m_connected_device ) ;
2021-12-24 11:22:11 -03:00
size_t needed_dma_regions_count = Memory : : page_round_up ( ( block_count * m_connected_device - > block_size ( ) ) ) . value ( ) / PAGE_SIZE ;
2021-02-26 09:38:17 -03:00
VERIFY ( needed_dma_regions_count < = m_dma_buffers . size ( ) ) ;
return needed_dma_regions_count ;
}
2021-02-26 01:25:45 -03:00
2021-02-26 09:38:17 -03:00
Optional < AsyncDeviceRequest : : RequestResult > AHCIPort : : prepare_and_set_scatter_list ( AsyncBlockDeviceRequest & request )
{
VERIFY ( m_lock . is_locked ( ) ) ;
VERIFY ( request . block_count ( ) > 0 ) ;
2021-02-26 01:25:45 -03:00
2022-08-24 10:56:26 -03:00
NonnullRefPtrVector < Memory : : PhysicalPage > allocated_dma_regions ;
2021-02-26 09:38:17 -03:00
for ( size_t index = 0 ; index < calculate_descriptors_count ( request . block_count ( ) ) ; index + + ) {
allocated_dma_regions . append ( m_dma_buffers . at ( index ) ) ;
}
2021-08-06 08:49:36 -03:00
m_current_scatter_list = Memory : : ScatterGatherList : : try_create ( request , allocated_dma_regions . span ( ) , m_connected_device - > block_size ( ) ) ;
2021-05-14 09:06:29 -03:00
if ( ! m_current_scatter_list )
return AsyncDeviceRequest : : Failure ;
2021-02-26 01:25:45 -03:00
if ( request . request_type ( ) = = AsyncBlockDeviceRequest : : Write ) {
2021-09-07 07:09:52 -03:00
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 ( ) ) {
2021-02-26 09:38:17 -03:00
return AsyncDeviceRequest : : MemoryFault ;
2021-02-26 01:25:45 -03:00
}
}
2021-02-26 09:38:17 -03:00
return { } ;
}
void AHCIPort : : start_request ( AsyncBlockDeviceRequest & request )
{
2021-07-17 20:13:34 -03:00
MutexLocker locker ( m_lock ) ;
2021-03-18 02:24:39 -03:00
dbgln_if ( AHCI_DEBUG , " AHCI Port {}: Request start " , representative_port_index ( ) ) ;
2021-03-18 20:32:42 -03:00
VERIFY ( ! m_current_request ) ;
VERIFY ( ! m_current_scatter_list ) ;
m_current_request = request ;
2021-02-26 09:38:17 -03:00
auto result = prepare_and_set_scatter_list ( request ) ;
if ( result . has_value ( ) ) {
2021-03-18 02:24:39 -03:00
dbgln_if ( AHCI_DEBUG , " AHCI Port {}: Request failure. " , representative_port_index ( ) ) ;
2021-05-14 07:03:16 -03:00
locker . unlock ( ) ;
2021-03-18 20:32:42 -03:00
complete_current_request ( result . value ( ) ) ;
2021-02-26 09:38:17 -03:00
return ;
}
2021-02-26 01:25:45 -03:00
auto success = access_device ( request . request_type ( ) , request . block_index ( ) , request . block_count ( ) ) ;
if ( ! success ) {
2021-03-18 02:24:39 -03:00
dbgln_if ( AHCI_DEBUG , " AHCI Port {}: Request failure. " , representative_port_index ( ) ) ;
2021-05-14 07:03:16 -03:00
locker . unlock ( ) ;
2021-03-18 20:32:42 -03:00
complete_current_request ( AsyncDeviceRequest : : Failure ) ;
2021-02-26 01:25:45 -03:00
return ;
}
}
2021-03-18 20:32:42 -03:00
void AHCIPort : : complete_current_request ( AsyncDeviceRequest : : RequestResult result )
2021-02-26 01:25:45 -03:00
{
2021-03-18 20:32:42 -03:00
VERIFY ( m_current_request ) ;
2021-03-19 14:19:15 -03:00
auto current_request = m_current_request ;
2021-03-18 20:32:42 -03:00
m_current_request . clear ( ) ;
2021-03-19 14:19:15 -03:00
current_request - > complete ( result ) ;
2021-02-26 01:25:45 -03:00
}
bool AHCIPort : : spin_until_ready ( ) const
{
VERIFY ( m_lock . is_locked ( ) ) ;
size_t spin = 0 ;
2021-03-18 02:24:39 -03:00
dbgln_if ( AHCI_DEBUG , " AHCI Port {}: Spinning until ready. " , representative_port_index ( ) ) ;
2021-02-26 01:25:45 -03:00
while ( ( m_port_registers . tfd & ( ATA_SR_BSY | ATA_SR_DRQ ) ) & & spin < = 100 ) {
2022-09-02 05:23:32 -03:00
microseconds_delay ( 1000 ) ;
2021-02-26 01:25:45 -03:00
spin + + ;
}
if ( spin = = 100 ) {
2021-04-18 05:30:03 -03:00
dbgln_if ( AHCI_DEBUG , " AHCI Port {}: SPIN exceeded 100 milliseconds threshold " , representative_port_index ( ) ) ;
2021-02-26 01:25:45 -03:00
return false ;
}
return true ;
}
bool AHCIPort : : access_device ( AsyncBlockDeviceRequest : : RequestType direction , u64 lba , u8 block_count )
{
2021-02-26 09:38:17 -03:00
VERIFY ( m_connected_device ) ;
2021-02-26 01:25:45 -03:00
VERIFY ( is_operable ( ) ) ;
2021-03-18 20:32:42 -03:00
VERIFY ( m_lock . is_locked ( ) ) ;
2021-02-26 09:38:17 -03:00
VERIFY ( m_current_scatter_list ) ;
2021-08-21 20:49:22 -03:00
SpinlockLocker lock ( m_hard_lock ) ;
2021-02-26 01:25:45 -03:00
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 ;
2021-03-18 22:08:23 -03:00
command_list_entries [ unused_command_header . value ( ) ] . prdbc = 0 ;
2021-02-26 09:38:17 -03:00
command_list_entries [ unused_command_header . value ( ) ] . prdtl = m_current_scatter_list - > scatters_count ( ) ;
2021-02-26 01:25:45 -03:00
// 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.
2021-06-15 08:25:51 -03:00
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 ) ;
2021-02-26 01:25:45 -03:00
2021-07-21 20:30:24 -03:00
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 ) ;
2021-03-18 02:24:39 -03:00
2022-07-11 14:32:29 -03:00
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 ) ) . value ( ) , " AHCI Command Table " sv , Memory : : Region : : Access : : ReadWrite , Memory : : Region : : Cacheable : : No ) . release_value ( ) ;
2021-02-26 01:25:45 -03:00
auto & command_table = * ( volatile AHCI : : CommandTable * ) command_table_region - > vaddr ( ) . as_ptr ( ) ;
2021-03-18 02:24:39 -03:00
dbgln_if ( AHCI_DEBUG , " AHCI Port {}: Allocated command table at {} " , representative_port_index ( ) , command_table_region - > vaddr ( ) ) ;
2021-02-26 01:25:45 -03:00
memset ( const_cast < u8 * > ( command_table . command_fis ) , 0 , 64 ) ;
2021-02-26 09:38:17 -03:00
size_t scatter_entry_index = 0 ;
2021-03-18 22:08:23 -03:00
size_t data_transfer_count = ( block_count * m_connected_device - > block_size ( ) ) ;
2021-02-26 09:38:17 -03:00
for ( auto scatter_page : m_current_scatter_list - > vmobject ( ) . physical_pages ( ) ) {
2021-03-18 22:08:23 -03:00
VERIFY ( data_transfer_count ! = 0 ) ;
2021-02-26 09:38:17 -03:00
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 ( ) ;
2021-03-18 22:08:23 -03:00
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 ;
}
2021-02-26 09:38:17 -03:00
scatter_entry_index + + ;
}
2021-03-18 20:32:42 -03:00
command_table . descriptors [ scatter_entry_index ] . byte_count = ( PAGE_SIZE - 1 ) | ( 1 < < 31 ) ;
2021-02-26 09:38:17 -03:00
2021-02-26 01:25:45 -03:00
memset ( const_cast < u8 * > ( 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 ;
}
2021-11-13 05:32:49 -03:00
bool AHCIPort : : identify_device ( )
2021-02-26 01:25:45 -03:00
{
VERIFY ( m_lock . is_locked ( ) ) ;
VERIFY ( is_operable ( ) ) ;
if ( ! spin_until_ready ( ) )
return false ;
2022-08-19 15:53:40 -03:00
LockRefPtr < AHCIController > controller = m_parent_controller . strong_ref ( ) ;
2022-04-01 17:45:07 -03:00
if ( ! controller )
return false ;
2021-02-26 01:25:45 -03:00
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.
2021-06-15 08:25:51 -03:00
command_list_entries [ unused_command_header . value ( ) ] . attributes = ( size_t ) FIS : : DwordCount : : RegisterHostToDevice | AHCI : : CommandHeaderAttributes : : P ;
2021-02-26 01:25:45 -03:00
2022-07-11 14:32:29 -03:00
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 ) ) . value ( ) , " AHCI Command Table " sv , Memory : : Region : : Access : : ReadWrite ) . release_value ( ) ;
2021-02-26 01:25:45 -03:00
auto & command_table = * ( volatile AHCI : : CommandTable * ) command_table_region - > vaddr ( ) . as_ptr ( ) ;
memset ( const_cast < u8 * > ( command_table . command_fis ) , 0 , 64 ) ;
command_table . descriptors [ 0 ] . base_high = 0 ;
2022-04-01 19:47:44 -03:00
command_table . descriptors [ 0 ] . base_low = m_identify_buffer_page - > paddr ( ) . get ( ) ;
2021-02-26 01:25:45 -03:00
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 ;
2021-11-20 12:28:59 -03:00
fis . command = m_port_registers . sig = = ATA : : DeviceSignature : : ATAPI ? ATA_CMD_IDENTIFY_PACKET : ATA_CMD_IDENTIFY ;
2021-02-26 01:25:45 -03:00
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 ;
2021-11-13 05:32:49 -03:00
// Just in case we have a pending interrupt.
m_interrupt_enable . clear ( ) ;
m_interrupt_status . clear ( ) ;
full_memory_barrier ( ) ;
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 ( ) ;
size_t time_elapsed = 0 ;
bool success = false ;
while ( 1 ) {
// Note: We allow it to spin for 256 milliseconds, which should be enough for a device to respond.
if ( time_elapsed > = 256 ) {
break ;
2021-02-26 01:25:45 -03:00
}
2021-11-13 05:32:49 -03:00
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 ( ) ;
break ;
}
if ( ! ( m_port_registers . ci & ( 1 < < unused_command_header . value ( ) ) ) ) {
success = true ;
break ;
}
2022-09-02 05:23:32 -03:00
microseconds_delay ( 1000 ) ; // delay with 1 milliseconds
2021-11-13 05:32:49 -03:00
time_elapsed + + ;
2021-02-26 01:25:45 -03:00
}
2021-03-18 20:32:42 -03:00
2021-11-13 05:32:49 -03:00
// Note: We probably ended up triggering an interrupt but we don't really want to handle it,
// so just get rid of it.
// FIXME: Do that in a better way so we don't need to actually remember this every time
// we need to do this.
m_interrupt_status . clear ( ) ;
m_interrupt_enable . set_all ( ) ;
return success ;
2021-02-26 01:25:45 -03:00
}
2021-11-13 09:20:48 -03:00
void AHCIPort : : wait_until_condition_met_or_timeout ( size_t delay_in_microseconds , size_t retries , Function < bool ( void ) > condition_being_met ) const
{
size_t retry = 0 ;
while ( retry < retries ) {
if ( condition_being_met ( ) )
break ;
2022-09-02 05:23:32 -03:00
microseconds_delay ( delay_in_microseconds ) ;
2021-11-13 09:20:48 -03:00
retry + + ;
}
}
2021-02-26 01:25:45 -03:00
bool AHCIPort : : shutdown ( )
{
2021-07-17 20:13:34 -03:00
MutexLocker locker ( m_lock ) ;
2021-08-21 20:49:22 -03:00
SpinlockLocker lock ( m_hard_lock ) ;
2021-02-26 01:25:45 -03:00
rebase ( ) ;
set_interface_state ( AHCI : : DeviceDetectionInitialization : : DisableInterface ) ;
return true ;
}
Optional < u8 > 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 ( ) ) ;
2021-03-18 20:32:42 -03:00
VERIFY ( m_hard_lock . is_locked ( ) ) ;
2021-02-26 01:25:45 -03:00
VERIFY ( is_operable ( ) ) ;
2021-03-18 02:24:39 -03:00
dbgln_if ( AHCI_DEBUG , " AHCI Port {}: Starting command list processing. " , representative_port_index ( ) ) ;
2021-02-26 01:25:45 -03:00
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 ( ) ) ;
2021-03-18 20:32:42 -03:00
VERIFY ( m_hard_lock . is_locked ( ) ) ;
2021-02-26 01:25:45 -03:00
VERIFY ( is_operable ( ) ) ;
2021-03-18 20:32:42 -03:00
VERIFY ( ! m_wait_for_completion ) ;
m_wait_for_completion = true ;
2021-03-18 02:24:39 -03:00
dbgln_if ( AHCI_DEBUG , " AHCI Port {}: Marking command header at index {} as ready to process. " , representative_port_index ( ) , command_header_index ) ;
2021-02-26 01:25:45 -03:00
m_port_registers . ci = 1 < < command_header_index ;
}
void AHCIPort : : stop_command_list_processing ( ) const
{
VERIFY ( m_lock . is_locked ( ) ) ;
2021-03-18 20:32:42 -03:00
VERIFY ( m_hard_lock . is_locked ( ) ) ;
2021-03-18 02:24:39 -03:00
dbgln_if ( AHCI_DEBUG , " AHCI Port {}: Stopping command list processing. " , representative_port_index ( ) ) ;
2021-02-26 01:25:45 -03:00
m_port_registers . cmd = m_port_registers . cmd & 0xfffffffe ;
}
void AHCIPort : : start_fis_receiving ( ) const
{
VERIFY ( m_lock . is_locked ( ) ) ;
2021-03-18 20:32:42 -03:00
VERIFY ( m_hard_lock . is_locked ( ) ) ;
2021-03-18 02:24:39 -03:00
dbgln_if ( AHCI_DEBUG , " AHCI Port {}: Starting FIS receiving. " , representative_port_index ( ) ) ;
2021-02-26 01:25:45 -03:00
m_port_registers . cmd = m_port_registers . cmd | ( 1 < < 4 ) ;
}
void AHCIPort : : power_on ( ) const
{
VERIFY ( m_lock . is_locked ( ) ) ;
2021-03-18 20:32:42 -03:00
VERIFY ( m_hard_lock . is_locked ( ) ) ;
2021-03-18 02:24:39 -03:00
dbgln_if ( AHCI_DEBUG , " AHCI Port {}: Power on. Cold presence detection? {} " , representative_port_index ( ) , ( bool ) ( m_port_registers . cmd & ( 1 < < 20 ) ) ) ;
2021-02-26 01:25:45 -03:00
if ( ! ( m_port_registers . cmd & ( 1 < < 20 ) ) )
return ;
2021-03-18 02:24:39 -03:00
dbgln_if ( AHCI_DEBUG , " AHCI Port {}: Powering on device. " , representative_port_index ( ) ) ;
2021-02-26 01:25:45 -03:00
m_port_registers . cmd = m_port_registers . cmd | ( 1 < < 2 ) ;
}
void AHCIPort : : spin_up ( ) const
{
VERIFY ( m_lock . is_locked ( ) ) ;
2021-03-18 20:32:42 -03:00
VERIFY ( m_hard_lock . is_locked ( ) ) ;
2022-04-01 17:45:07 -03:00
dbgln_if ( AHCI_DEBUG , " AHCI Port {}: Spin up. Staggered spin up? {} " , representative_port_index ( ) , m_hba_capabilities . staggered_spin_up_supported ) ;
if ( ! m_hba_capabilities . staggered_spin_up_supported )
2021-02-26 01:25:45 -03:00
return ;
2021-03-18 02:24:39 -03:00
dbgln_if ( AHCI_DEBUG , " AHCI Port {}: Spinning up device. " , representative_port_index ( ) ) ;
2021-02-26 01:25:45 -03:00
m_port_registers . cmd = m_port_registers . cmd | ( 1 < < 1 ) ;
}
void AHCIPort : : stop_fis_receiving ( ) const
{
VERIFY ( m_lock . is_locked ( ) ) ;
2021-03-18 20:32:42 -03:00
VERIFY ( m_hard_lock . is_locked ( ) ) ;
2021-03-18 02:24:39 -03:00
dbgln_if ( AHCI_DEBUG , " AHCI Port {}: Stopping FIS receiving. " , representative_port_index ( ) ) ;
2021-02-26 01:25:45 -03:00
m_port_registers . cmd = m_port_registers . cmd & 0xFFFFFFEF ;
}
2021-11-13 08:07:55 -03:00
bool AHCIPort : : initiate_sata_reset ( )
2021-02-26 01:25:45 -03:00
{
VERIFY ( m_lock . is_locked ( ) ) ;
2021-03-18 20:32:42 -03:00
VERIFY ( m_hard_lock . is_locked ( ) ) ;
2021-03-18 02:24:39 -03:00
dbgln_if ( AHCI_DEBUG , " AHCI Port {}: Initiate SATA reset " , representative_port_index ( ) ) ;
2021-02-26 01:25:45 -03:00
stop_command_list_processing ( ) ;
full_memory_barrier ( ) ;
2021-11-13 09:20:48 -03:00
// Note: The AHCI specification says to wait now a 500 milliseconds
2021-02-26 01:25:45 -03:00
// Try to wait 1 second for HBA to clear Command List Running
2021-11-13 09:20:48 -03:00
wait_until_condition_met_or_timeout ( 100 , 5000 , [ this ] ( ) - > bool {
return ! ( m_port_registers . cmd & ( 1 < < 15 ) ) ;
} ) ;
2021-02-26 01:25:45 -03:00
full_memory_barrier ( ) ;
spin_up ( ) ;
full_memory_barrier ( ) ;
set_interface_state ( AHCI : : DeviceDetectionInitialization : : PerformInterfaceInitializationSequence ) ;
2021-03-07 16:49:02 -03:00
// The AHCI specification says to wait now a 1 millisecond
2022-09-02 05:23:32 -03:00
microseconds_delay ( 1000 ) ;
2021-11-13 08:07:55 -03:00
full_memory_barrier ( ) ;
set_interface_state ( AHCI : : DeviceDetectionInitialization : : NoActionRequested ) ;
full_memory_barrier ( ) ;
2021-11-13 09:20:48 -03:00
wait_until_condition_met_or_timeout ( 10 , 1000 , [ this ] ( ) - > bool {
return is_phy_enabled ( ) ;
} ) ;
2021-02-26 01:25:45 -03:00
dmesgln ( " AHCI Port {}: {} " , representative_port_index ( ) , try_disambiguate_sata_status ( ) ) ;
full_memory_barrier ( ) ;
clear_sata_error_register ( ) ;
2021-03-12 13:27:38 -03:00
return ( m_port_registers . ssts & 0xf ) = = 3 ;
2021-02-26 01:25:45 -03:00
}
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 ( ) ;
}
}