2020-01-18 09:38:21 +01:00
/*
2020-03-06 16:59:29 +02:00
* Copyright ( c ) 2020 , Liav A . < liavalb @ hotmail . co . il >
2020-01-18 09:38:21 +01:00
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
*
* 1. Redistributions of source code must retain the above copyright notice , this
* list of conditions and the following disclaimer .
*
* 2. Redistributions in binary form must reproduce the above copyright notice ,
* this list of conditions and the following disclaimer in the documentation
* and / or other materials provided with the distribution .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS "
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
* DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY ,
* OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
2019-12-31 13:04:30 +02:00
# include <AK/Optional.h>
2020-03-23 13:45:10 +01:00
# include <AK/StringView.h>
2019-12-31 13:04:30 +02:00
# include <Kernel/PCI/MMIOAccess.h>
# 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 {
class MMIOSegment {
public :
MMIOSegment ( PhysicalAddress , u8 , u8 ) ;
2020-10-31 22:24:01 +02:00
u8 get_start_bus ( ) const ;
u8 get_end_bus ( ) const ;
size_t get_size ( ) const ;
PhysicalAddress get_paddr ( ) const ;
2020-04-08 17:14:55 +02:00
private :
PhysicalAddress m_base_addr ;
u8 m_start_bus ;
u8 m_end_bus ;
} ;
2020-02-16 01:27:42 +01:00
2019-12-31 13:04:30 +02:00
# define PCI_MMIO_CONFIG_SPACE_SIZE 4096
2020-10-31 22:24:01 +02:00
DeviceConfigurationSpaceMapping : : DeviceConfigurationSpaceMapping ( Address device_address , const MMIOSegment & mmio_segment )
: m_device_address ( device_address )
, m_mapped_region ( MM . allocate_kernel_region ( PAGE_ROUND_UP ( PCI_MMIO_CONFIG_SPACE_SIZE ) , " PCI MMIO Device Access " , Region : : Access : : Read | Region : : Access : : Write ) . release_nonnull ( ) )
{
PhysicalAddress segment_lower_addr = mmio_segment . get_paddr ( ) ;
PhysicalAddress device_physical_mmio_space = segment_lower_addr . offset (
PCI_MMIO_CONFIG_SPACE_SIZE * m_device_address . function ( ) + ( PCI_MMIO_CONFIG_SPACE_SIZE * PCI_MAX_FUNCTIONS_PER_DEVICE ) * m_device_address . slot ( ) + ( PCI_MMIO_CONFIG_SPACE_SIZE * PCI_MAX_FUNCTIONS_PER_DEVICE * PCI_MAX_DEVICES_PER_BUS ) * ( m_device_address . bus ( ) - mmio_segment . get_start_bus ( ) ) ) ;
m_mapped_region - > physical_page_slot ( 0 ) = PhysicalPage : : create ( device_physical_mmio_space , false , false ) ;
m_mapped_region - > remap ( ) ;
}
2020-04-08 17:14:55 +02:00
uint32_t MMIOAccess : : segment_count ( ) const
2019-12-31 13:04:30 +02:00
{
return m_segments . size ( ) ;
}
2020-04-08 17:09:49 +02:00
2020-04-08 17:14:55 +02:00
uint8_t 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 ) ;
ASSERT ( segment . has_value ( ) ) ;
return segment . value ( ) . get_start_bus ( ) ;
2019-12-31 13:04:30 +02:00
}
2020-04-08 17:09:49 +02:00
2020-04-08 17:14:55 +02:00
uint8_t 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 ) ;
ASSERT ( segment . has_value ( ) ) ;
return segment . value ( ) . get_end_bus ( ) ;
2019-12-31 13:04:30 +02:00
}
2020-04-08 17:14:55 +02:00
void MMIOAccess : : initialize ( PhysicalAddress mcfg )
2019-12-31 13:04:30 +02:00
{
2020-04-08 17:14:55 +02:00
if ( ! Access : : is_initialized ( ) )
new MMIOAccess ( mcfg ) ;
2019-12-31 13:04:30 +02:00
}
2020-04-08 17:14:55 +02:00
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
{
2020-04-08 17:41:46 +02:00
klog ( ) < < " 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 ) ;
2019-12-31 13:04:30 +02:00
# ifdef PCI_DEBUG
2020-02-24 15:54:12 +02:00
dbg ( ) < < " PCI: Checking MCFG Table length to choose the correct mapping size " ;
2019-12-31 13:04:30 +02:00
# endif
2020-02-09 16:47:15 +02:00
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 ;
2020-03-01 21:45:39 +02:00
klog ( ) < < " PCI: MCFG, length - " < < length < < " , revision " < < revision ;
2019-12-31 13:04:30 +02:00
checkup_region - > unmap ( ) ;
2020-02-24 00:59:00 +02: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 ( ) ;
2019-12-31 13:04:30 +02:00
# ifdef PCI_DEBUG
2020-02-24 15:54:12 +02:00
dbg ( ) < < " PCI: Checking MCFG @ V " < < & mcfg < < " , P 0x " < < String : : format ( " %x " , p_mcfg . get ( ) ) ;
2019-12-31 13:04:30 +02:00
# endif
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 } ) ;
2020-03-06 15:00:44 +01:00
klog ( ) < < " PCI: New PCI segment @ " < < PhysicalAddress ( lower_addr ) < < " , PCI buses ( " < < start_bus < < " - " < < end_bus < < " ) " ;
2019-12-31 13:04:30 +02:00
}
mcfg_region - > unmap ( ) ;
2020-03-01 21:45:39 +02:00
klog ( ) < < " PCI: MMIO segments - " < < m_segments . size ( ) ;
2020-04-10 20:25:03 +03:00
2020-01-02 15:54:32 +02:00
InterruptDisabler disabler ;
2020-04-10 20:25:03 +03:00
enumerate_hardware ( [ & ] ( const Address & address , ID id ) {
m_physical_ids . append ( { address , id } ) ;
2020-10-31 22:24:01 +02:00
m_mapped_device_regions . append ( make < DeviceConfigurationSpaceMapping > ( address , m_segments . get ( address . seg ( ) ) . value ( ) ) ) ;
# ifdef PCI_DEBUG
dbg ( ) < < " PCI: Mapping device @ pci ( " < < String : : format ( " %w " , address . seg ( ) ) < < " : " < < String : : format ( " %b " , address . bus ( ) ) < < " : " < < String : : format ( " %b " , address . slot ( ) ) < < " . " < < String : : format ( " %b " , address . function ( ) ) < < " ) "
< < " " < < m_mapped_device_regions . last ( ) . vaddr ( ) < < " " < < m_mapped_device_regions . last ( ) . paddr ( ) ;
# endif
2020-04-10 20:25:03 +03:00
} ) ;
2019-12-31 13:04:30 +02:00
}
2020-10-31 22:24:01 +02:00
Optional < VirtualAddress > MMIOAccess : : get_device_configuration_space ( Address address )
2019-12-31 13:04:30 +02:00
{
2020-10-31 22:24:01 +02:00
for ( auto & mapping : m_mapped_device_regions ) {
auto checked_address = mapping . address ( ) ;
2019-12-31 13:04:30 +02:00
# ifdef PCI_DEBUG
2020-10-31 22:24:01 +02:00
dbg ( ) < < " PCI Device Configuration Space Mapping: Check if " < < checked_address < < " was requested " ;
2019-12-31 13:04:30 +02:00
# endif
2020-10-31 22:24:01 +02:00
if ( address . seg ( ) = = checked_address . seg ( )
& & address . bus ( ) = = checked_address . bus ( )
& & address . slot ( ) = = checked_address . slot ( )
& & address . function ( ) = = checked_address . function ( ) ) {
# ifdef PCI_DEBUG
dbg ( ) < < " PCI Device Configuration Space Mapping: Found " < < checked_address ;
# endif
return mapping . vaddr ( ) ;
}
}
return { } ;
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
{
2020-01-02 15:54:32 +02:00
InterruptDisabler disabler ;
2019-12-31 13:04:30 +02:00
ASSERT ( field < = 0xfff ) ;
# ifdef PCI_DEBUG
2020-02-24 15:54:12 +02:00
dbg ( ) < < " PCI: Reading field " < < field < < " , Address( " < < String : : format ( " %w " , address . seg ( ) ) < < " : " < < String : : format ( " %b " , address . bus ( ) ) < < " : " < < String : : format ( " %b " , address . slot ( ) ) < < " . " < < String : : format ( " %b " , address . function ( ) ) < < " ) " ;
2019-12-31 13:04:30 +02:00
# endif
2020-10-31 22:24:01 +02:00
return * ( ( u8 * ) ( get_device_configuration_space ( address ) . value ( ) . 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
{
2020-01-02 15:54:32 +02:00
InterruptDisabler disabler ;
2019-12-31 13:04:30 +02:00
ASSERT ( field < 0xfff ) ;
# ifdef PCI_DEBUG
2020-02-24 15:54:12 +02:00
dbg ( ) < < " PCI: Reading field " < < field < < " , Address( " < < String : : format ( " %w " , address . seg ( ) ) < < " : " < < String : : format ( " %b " , address . bus ( ) ) < < " : " < < String : : format ( " %b " , address . slot ( ) ) < < " . " < < String : : format ( " %b " , address . function ( ) ) < < " ) " ;
2019-12-31 13:04:30 +02:00
# endif
2020-10-31 22:24:01 +02:00
return * ( ( u16 * ) ( get_device_configuration_space ( address ) . value ( ) . get ( ) + ( field & 0xfff ) ) ) ;
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
{
2020-01-02 15:54:32 +02:00
InterruptDisabler disabler ;
2019-12-31 13:04:30 +02:00
ASSERT ( field < = 0xffc ) ;
# ifdef PCI_DEBUG
2020-02-24 15:54:12 +02:00
dbg ( ) < < " PCI: Reading field " < < field < < " , Address( " < < String : : format ( " %w " , address . seg ( ) ) < < " : " < < String : : format ( " %b " , address . bus ( ) ) < < " : " < < String : : format ( " %b " , address . slot ( ) ) < < " . " < < String : : format ( " %b " , address . function ( ) ) < < " ) " ;
2019-12-31 13:04:30 +02:00
# endif
2020-10-31 22:24:01 +02:00
return * ( ( u32 * ) ( get_device_configuration_space ( address ) . value ( ) . get ( ) + ( field & 0xfff ) ) ) ;
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
{
2020-01-02 15:54:32 +02:00
InterruptDisabler disabler ;
2019-12-31 13:04:30 +02:00
ASSERT ( field < = 0xfff ) ;
# ifdef PCI_DEBUG
2020-02-24 15:54:12 +02:00
dbg ( ) < < " PCI: Writing to field " < < field < < " , Address( " < < String : : format ( " %w " , address . seg ( ) ) < < " : " < < String : : format ( " %b " , address . bus ( ) ) < < " : " < < String : : format ( " %b " , address . slot ( ) ) < < " . " < < String : : format ( " %b " , address . function ( ) ) < < " ) value 0x " < < String : : format ( " %x " , value ) ;
2019-12-31 13:04:30 +02:00
# endif
2020-10-31 22:24:01 +02:00
* ( ( u8 * ) ( get_device_configuration_space ( address ) . value ( ) . 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
{
2020-01-02 15:54:32 +02:00
InterruptDisabler disabler ;
2019-12-31 13:04:30 +02:00
ASSERT ( field < 0xfff ) ;
# ifdef PCI_DEBUG
2020-02-24 15:54:12 +02:00
dbg ( ) < < " PCI: Writing to field " < < field < < " , Address( " < < String : : format ( " %w " , address . seg ( ) ) < < " : " < < String : : format ( " %b " , address . bus ( ) ) < < " : " < < String : : format ( " %b " , address . slot ( ) ) < < " . " < < String : : format ( " %b " , address . function ( ) ) < < " ) value 0x " < < String : : format ( " %x " , value ) ;
2019-12-31 13:04:30 +02:00
# endif
2020-10-31 22:24:01 +02:00
* ( ( u16 * ) ( get_device_configuration_space ( address ) . value ( ) . get ( ) + ( field & 0xfff ) ) ) = 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
{
2020-01-02 15:54:32 +02:00
InterruptDisabler disabler ;
2019-12-31 13:04:30 +02:00
ASSERT ( field < = 0xffc ) ;
# ifdef PCI_DEBUG
2020-02-24 15:54:12 +02:00
dbg ( ) < < " PCI: Writing to field " < < field < < " , Address( " < < String : : format ( " %w " , address . seg ( ) ) < < " : " < < String : : format ( " %b " , address . bus ( ) ) < < " : " < < String : : format ( " %b " , address . slot ( ) ) < < " . " < < String : : format ( " %b " , address . function ( ) ) < < " ) value 0x " < < String : : format ( " %x " , value ) ;
2019-12-31 13:04:30 +02:00
# endif
2020-10-31 22:24:01 +02:00
* ( ( u32 * ) ( get_device_configuration_space ( address ) . value ( ) . get ( ) + ( field & 0xfff ) ) ) = 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 + + ) {
# ifdef PCI_DEBUG
2020-02-24 15:54:12 +02:00
dbg ( ) < < " PCI: Enumerating Memory mapped IO segment " < < seg ;
2019-12-31 13:04:30 +02:00
# endif
// Single PCI host controller.
2020-10-31 22:24:01 +02:00
if ( ( early_read8_field ( Address ( seg ) , PCI_HEADER_TYPE ) & 0x80 ) = = 0 ) {
2019-12-31 13:04:30 +02:00
enumerate_bus ( - 1 , 0 , callback ) ;
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 ;
enumerate_bus ( - 1 , function , callback ) ;
}
}
}
2020-04-08 17:14:55 +02:00
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
2020-10-31 22:24:01 +02:00
u8 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
2020-10-31 22:24:01 +02:00
u8 MMIOSegment : : get_end_bus ( ) const
2019-12-31 13:04:30 +02:00
{
return m_end_bus ;
}
2020-10-31 22:24:01 +02:00
size_t 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 ( ) ) ) ;
}
2020-10-31 22:24:01 +02:00
PhysicalAddress 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
}