2020-01-18 09:38:21 +01:00
/*
2021-04-03 16:46:04 +03:00
* Copyright ( c ) 2021 , Liav A . < liavalb @ hotmail . co . il >
2020-01-18 09:38:21 +01:00
*
2021-04-22 01:24:48 -07:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-01-18 09:38:21 +01:00
*/
2019-12-31 13:04:30 +02:00
# include <AK/Optional.h>
2020-03-23 13:45:10 +01:00
# include <AK/StringView.h>
2021-06-21 17:34:09 +02:00
# include <Kernel/Arch/x86/InterruptDisabler.h>
2021-01-25 16:07:10 +01:00
# include <Kernel/Debug.h>
2019-12-31 13:04:30 +02:00
# include <Kernel/PCI/MMIOAccess.h>
2021-06-22 17:40:16 +02:00
# include <Kernel/Sections.h>
2019-12-31 13:04:30 +02:00
# include <Kernel/VM/MemoryManager.h>
2020-02-16 01:27:42 +01:00
namespace Kernel {
2020-04-08 17:14:55 +02:00
namespace PCI {
2021-04-03 16:46:04 +03:00
# define MEMORY_RANGE_PER_BUS (PCI_MMIO_CONFIG_SPACE_SIZE * PCI_MAX_FUNCTIONS_PER_DEVICE * PCI_MAX_DEVICES_PER_BUS)
2020-04-08 17:14:55 +02:00
2021-04-03 16:46:04 +03:00
u32 MMIOAccess : : segment_count ( ) const
2019-12-31 13:04:30 +02:00
{
return m_segments . size ( ) ;
}
2020-04-08 17:09:49 +02:00
2021-04-03 16:46:04 +03:00
u8 MMIOAccess : : segment_start_bus ( u32 seg ) const
2019-12-31 13:04:30 +02:00
{
2020-04-08 17:23:20 +02:00
auto segment = m_segments . get ( seg ) ;
2021-02-23 20:42:32 +01:00
VERIFY ( segment . has_value ( ) ) ;
2020-04-08 17:23:20 +02:00
return segment . value ( ) . get_start_bus ( ) ;
2019-12-31 13:04:30 +02:00
}
2020-04-08 17:09:49 +02:00
2021-04-03 16:46:04 +03:00
u8 MMIOAccess : : segment_end_bus ( u32 seg ) const
2019-12-31 13:04:30 +02:00
{
2020-04-08 17:23:20 +02:00
auto segment = m_segments . get ( seg ) ;
2021-02-23 20:42:32 +01:00
VERIFY ( segment . has_value ( ) ) ;
2020-04-08 17:23:20 +02:00
return segment . value ( ) . get_end_bus ( ) ;
2019-12-31 13:04:30 +02:00
}
2021-06-30 00:13:26 +01:00
PhysicalAddress MMIOAccess : : determine_memory_mapped_bus_region ( u32 segment , u8 bus ) const
2021-04-03 16:46:04 +03:00
{
VERIFY ( bus > = segment_start_bus ( segment ) & & bus < = segment_end_bus ( segment ) ) ;
auto seg = m_segments . get ( segment ) ;
VERIFY ( seg . has_value ( ) ) ;
return seg . value ( ) . get_paddr ( ) . offset ( MEMORY_RANGE_PER_BUS * ( bus - seg . value ( ) . get_start_bus ( ) ) ) ;
}
2021-02-19 21:29:46 +01:00
UNMAP_AFTER_INIT void MMIOAccess : : initialize ( PhysicalAddress mcfg )
2019-12-31 13:04:30 +02:00
{
2020-12-22 04:22:32 +00:00
if ( ! Access : : is_initialized ( ) ) {
2020-04-08 17:14:55 +02:00
new MMIOAccess ( mcfg ) ;
2021-03-12 13:57:56 +01:00
dbgln_if ( PCI_DEBUG , " PCI: MMIO access initialised. " ) ;
2020-12-22 04:22:32 +00:00
}
2019-12-31 13:04:30 +02:00
}
2021-02-19 21:29:46 +01:00
UNMAP_AFTER_INIT MMIOAccess : : MMIOAccess ( PhysicalAddress p_mcfg )
2020-02-24 00:59:00 +02:00
: m_mcfg ( p_mcfg )
2019-12-31 13:04:30 +02:00
{
2021-03-12 13:57:56 +01:00
dmesgln ( " PCI: Using MMIO for PCI configuration space access " ) ;
2019-12-31 13:04:30 +02:00
2020-02-24 00:59:00 +02:00
auto checkup_region = MM . allocate_kernel_region ( p_mcfg . page_base ( ) , ( PAGE_SIZE * 2 ) , " PCI MCFG Checkup " , Region : : Access : : Read | Region : : Access : : Write ) ;
2021-03-12 13:57:56 +01:00
dbgln_if ( PCI_DEBUG , " PCI: Checking MCFG Table length to choose the correct mapping size " ) ;
2020-03-08 18:29:48 +02:00
auto * sdt = ( ACPI : : Structures : : SDTHeader * ) checkup_region - > vaddr ( ) . offset ( p_mcfg . offset_in_page ( ) ) . as_ptr ( ) ;
2019-12-31 13:04:30 +02:00
u32 length = sdt - > length ;
u8 revision = sdt - > revision ;
2021-03-12 13:57:56 +01:00
dbgln ( " PCI: MCFG, length: {}, revision: {} " , length , revision ) ;
2019-12-31 13:04:30 +02:00
checkup_region - > unmap ( ) ;
2021-02-14 09:57:19 +01:00
auto mcfg_region = MM . allocate_kernel_region ( p_mcfg . page_base ( ) , page_round_up ( length ) + PAGE_SIZE , " PCI Parsing MCFG " , Region : : Access : : Read | Region : : Access : : Write ) ;
2019-12-31 13:04:30 +02:00
2020-03-08 18:29:48 +02:00
auto & mcfg = * ( ACPI : : Structures : : MCFG * ) mcfg_region - > vaddr ( ) . offset ( p_mcfg . offset_in_page ( ) ) . as_ptr ( ) ;
2021-02-07 15:33:24 +03:30
dbgln_if ( PCI_DEBUG , " PCI: Checking MCFG @ {}, {} " , VirtualAddress ( & mcfg ) , PhysicalAddress ( p_mcfg . get ( ) ) ) ;
2019-12-31 13:04:30 +02:00
2020-02-28 22:31:58 +02:00
for ( u32 index = 0 ; index < ( ( mcfg . header . length - sizeof ( ACPI : : Structures : : MCFG ) ) / sizeof ( ACPI : : Structures : : PCI_MMIO_Descriptor ) ) ; index + + ) {
2019-12-31 13:04:30 +02:00
u8 start_bus = mcfg . descriptors [ index ] . start_pci_bus ;
u8 end_bus = mcfg . descriptors [ index ] . end_pci_bus ;
u32 lower_addr = mcfg . descriptors [ index ] . base_addr ;
2020-04-08 17:23:20 +02:00
m_segments . set ( index , { PhysicalAddress ( lower_addr ) , start_bus , end_bus } ) ;
2021-03-12 13:57:56 +01:00
dmesgln ( " PCI: New PCI segment @ {}, PCI buses ({}-{}) " , PhysicalAddress { lower_addr } , start_bus , end_bus ) ;
2019-12-31 13:04:30 +02:00
}
mcfg_region - > unmap ( ) ;
2021-03-12 13:57:56 +01:00
dmesgln ( " PCI: MMIO segments: {} " , m_segments . size ( ) ) ;
2020-04-10 20:25:03 +03:00
2020-01-02 15:54:32 +02:00
InterruptDisabler disabler ;
2021-04-03 16:46:04 +03:00
VERIFY ( m_segments . contains ( 0 ) ) ;
// Note: we need to map this region before enumerating the hardware and adding
// PCI::PhysicalID objects to the vector, because get_capabilities calls
// PCI::read16 which will need this region to be mapped.
2021-06-29 23:29:42 +01:00
u8 start_bus = m_segments . get ( 0 ) . value ( ) . get_start_bus ( ) ;
m_mapped_region = MM . allocate_kernel_region ( determine_memory_mapped_bus_region ( 0 , start_bus ) , MEMORY_RANGE_PER_BUS , " PCI ECAM " , Region : : Access : : Read | Region : : Access : : Write ) ;
m_mapped_bus = start_bus ;
dbgln_if ( PCI_DEBUG , " PCI: First PCI ECAM Mapped region for starting bus {} @ {} {} " , start_bus , m_mapped_region - > vaddr ( ) , m_mapped_region - > physical_page ( 0 ) - > paddr ( ) ) ;
2020-04-10 20:25:03 +03:00
enumerate_hardware ( [ & ] ( const Address & address , ID id ) {
2020-12-22 04:03:20 +00:00
m_physical_ids . append ( { address , id , get_capabilities ( address ) } ) ;
2020-04-10 20:25:03 +03:00
} ) ;
2019-12-31 13:04:30 +02:00
}
2021-04-03 16:46:04 +03:00
void MMIOAccess : : map_bus_region ( u32 segment , u8 bus )
{
VERIFY ( m_access_lock . is_locked ( ) ) ;
if ( m_mapped_bus = = bus )
return ;
m_mapped_region = MM . allocate_kernel_region ( determine_memory_mapped_bus_region ( segment , bus ) , MEMORY_RANGE_PER_BUS , " PCI ECAM " , Region : : Access : : Read | Region : : Access : : Write ) ;
2021-06-29 23:29:42 +01:00
m_mapped_bus = bus ;
dbgln_if ( PCI_DEBUG , " PCI: New PCI ECAM Mapped region for bus {} @ {} {} " , bus , m_mapped_region - > vaddr ( ) , m_mapped_region - > physical_page ( 0 ) - > paddr ( ) ) ;
2021-04-03 16:46:04 +03:00
}
2019-12-31 13:04:30 +02:00
2021-04-03 16:46:04 +03:00
VirtualAddress MMIOAccess : : get_device_configuration_space ( Address address )
2019-12-31 13:04:30 +02:00
{
2021-04-03 16:46:04 +03:00
VERIFY ( m_access_lock . is_locked ( ) ) ;
2021-02-07 15:33:24 +03:30
dbgln_if ( PCI_DEBUG , " PCI: Getting device configuration space for {} " , address ) ;
2021-04-03 16:46:04 +03:00
map_bus_region ( address . seg ( ) , address . bus ( ) ) ;
return m_mapped_region - > vaddr ( ) . offset ( PCI_MMIO_CONFIG_SPACE_SIZE * address . function ( ) + ( PCI_MMIO_CONFIG_SPACE_SIZE * PCI_MAX_FUNCTIONS_PER_DEVICE ) * address . device ( ) ) ;
2019-12-31 13:04:30 +02:00
}
2020-04-08 17:14:55 +02:00
u8 MMIOAccess : : read8_field ( Address address , u32 field )
2019-12-31 13:04:30 +02:00
{
2021-04-03 16:46:04 +03:00
ScopedSpinLock lock ( m_access_lock ) ;
2021-02-23 20:42:32 +01:00
VERIFY ( field < = 0xfff ) ;
2021-02-07 15:33:24 +03:30
dbgln_if ( PCI_DEBUG , " PCI: MMIO Reading 8-bit field {:#08x} for {} " , field , address ) ;
2021-04-03 16:46:04 +03:00
return * ( ( volatile u8 * ) ( get_device_configuration_space ( address ) . get ( ) + ( field & 0xfff ) ) ) ;
2019-12-31 13:04:30 +02:00
}
2020-04-08 17:14:55 +02:00
u16 MMIOAccess : : read16_field ( Address address , u32 field )
2019-12-31 13:04:30 +02:00
{
2021-04-03 16:46:04 +03:00
ScopedSpinLock lock ( m_access_lock ) ;
2021-02-23 20:42:32 +01:00
VERIFY ( field < 0xfff ) ;
2021-02-07 15:33:24 +03:30
dbgln_if ( PCI_DEBUG , " PCI: MMIO Reading 16-bit field {:#08x} for {} " , field , address ) ;
2021-04-04 22:13:20 +03:00
u16 data = 0 ;
read_possibly_unaligned_data < u16 > ( get_device_configuration_space ( address ) . offset ( field & 0xfff ) . as_ptr ( ) , data ) ;
return data ;
2019-12-31 13:04:30 +02:00
}
2020-04-08 17:14:55 +02:00
u32 MMIOAccess : : read32_field ( Address address , u32 field )
2019-12-31 13:04:30 +02:00
{
2021-04-03 16:46:04 +03:00
ScopedSpinLock lock ( m_access_lock ) ;
2021-02-23 20:42:32 +01:00
VERIFY ( field < = 0xffc ) ;
2021-02-07 15:33:24 +03:30
dbgln_if ( PCI_DEBUG , " PCI: MMIO Reading 32-bit field {:#08x} for {} " , field , address ) ;
2021-04-04 22:13:20 +03:00
u32 data = 0 ;
read_possibly_unaligned_data < u32 > ( get_device_configuration_space ( address ) . offset ( field & 0xfff ) . as_ptr ( ) , data ) ;
return data ;
2019-12-31 13:04:30 +02:00
}
2020-04-08 17:14:55 +02:00
void MMIOAccess : : write8_field ( Address address , u32 field , u8 value )
2019-12-31 13:04:30 +02:00
{
2021-04-03 16:46:04 +03:00
ScopedSpinLock lock ( m_access_lock ) ;
2021-02-23 20:42:32 +01:00
VERIFY ( field < = 0xfff ) ;
2021-02-07 15:33:24 +03:30
dbgln_if ( PCI_DEBUG , " PCI: MMIO Writing 8-bit field {:#08x}, value={:#02x} for {} " , field , value , address ) ;
2021-04-03 16:46:04 +03:00
* ( ( volatile u8 * ) ( get_device_configuration_space ( address ) . get ( ) + ( field & 0xfff ) ) ) = value ;
2019-12-31 13:04:30 +02:00
}
2020-04-08 17:14:55 +02:00
void MMIOAccess : : write16_field ( Address address , u32 field , u16 value )
2019-12-31 13:04:30 +02:00
{
2021-04-03 16:46:04 +03:00
ScopedSpinLock lock ( m_access_lock ) ;
2021-02-23 20:42:32 +01:00
VERIFY ( field < 0xfff ) ;
2021-02-07 15:33:24 +03:30
dbgln_if ( PCI_DEBUG , " PCI: MMIO Writing 16-bit field {:#08x}, value={:#02x} for {} " , field , value , address ) ;
2021-04-04 22:13:20 +03:00
write_possibly_unaligned_data < u16 > ( get_device_configuration_space ( address ) . offset ( field & 0xfff ) . as_ptr ( ) , value ) ;
2019-12-31 13:04:30 +02:00
}
2020-04-08 17:14:55 +02:00
void MMIOAccess : : write32_field ( Address address , u32 field , u32 value )
2019-12-31 13:04:30 +02:00
{
2021-04-03 16:46:04 +03:00
ScopedSpinLock lock ( m_access_lock ) ;
2021-02-23 20:42:32 +01:00
VERIFY ( field < = 0xffc ) ;
2021-02-07 15:33:24 +03:30
dbgln_if ( PCI_DEBUG , " PCI: MMIO Writing 32-bit field {:#08x}, value={:#02x} for {} " , field , value , address ) ;
2021-04-04 22:13:20 +03:00
write_possibly_unaligned_data < u32 > ( get_device_configuration_space ( address ) . offset ( field & 0xfff ) . as_ptr ( ) , value ) ;
2019-12-31 13:04:30 +02:00
}
2020-04-10 20:25:03 +03:00
void MMIOAccess : : enumerate_hardware ( Function < void ( Address , ID ) > callback )
2019-12-31 13:04:30 +02:00
{
for ( u16 seg = 0 ; seg < m_segments . size ( ) ; seg + + ) {
2021-02-07 15:33:24 +03:30
dbgln_if ( PCI_DEBUG , " PCI: Enumerating Memory mapped IO segment {} " , seg ) ;
2019-12-31 13:04:30 +02:00
// Single PCI host controller.
2020-10-31 22:24:01 +02:00
if ( ( early_read8_field ( Address ( seg ) , PCI_HEADER_TYPE ) & 0x80 ) = = 0 ) {
2021-01-31 12:03:23 +01:00
enumerate_bus ( - 1 , 0 , callback , true ) ;
2019-12-31 13:04:30 +02:00
return ;
}
// Multiple PCI host controllers.
for ( u8 function = 0 ; function < 8 ; + + function ) {
2020-10-31 22:24:01 +02:00
if ( early_read16_field ( Address ( seg , 0 , 0 , function ) , PCI_VENDOR_ID ) = = PCI_NONE )
2019-12-31 13:04:30 +02:00
break ;
2021-01-31 12:03:23 +01:00
enumerate_bus ( - 1 , function , callback , false ) ;
2019-12-31 13:04:30 +02:00
}
}
}
2021-04-03 16:46:04 +03:00
MMIOAccess : : MMIOSegment : : MMIOSegment ( PhysicalAddress segment_base_addr , u8 start_bus , u8 end_bus )
2019-12-31 13:04:30 +02:00
: m_base_addr ( segment_base_addr )
, m_start_bus ( start_bus )
, m_end_bus ( end_bus )
{
}
2020-04-08 17:09:49 +02:00
2021-04-03 16:46:04 +03:00
u8 MMIOAccess : : MMIOSegment : : get_start_bus ( ) const
2019-12-31 13:04:30 +02:00
{
return m_start_bus ;
}
2020-04-08 17:09:49 +02:00
2021-04-03 16:46:04 +03:00
u8 MMIOAccess : : MMIOSegment : : get_end_bus ( ) const
2019-12-31 13:04:30 +02:00
{
return m_end_bus ;
}
2021-04-03 16:46:04 +03:00
size_t MMIOAccess : : MMIOSegment : : get_size ( ) const
2019-12-31 13:04:30 +02:00
{
return ( PCI_MMIO_CONFIG_SPACE_SIZE * PCI_MAX_FUNCTIONS_PER_DEVICE * PCI_MAX_DEVICES_PER_BUS * ( get_end_bus ( ) - get_start_bus ( ) ) ) ;
}
2021-04-03 16:46:04 +03:00
PhysicalAddress MMIOAccess : : MMIOSegment : : get_paddr ( ) const
2019-12-31 13:04:30 +02:00
{
return m_base_addr ;
}
2020-02-16 01:27:42 +01:00
}
2020-04-08 17:14:55 +02:00
}