2020-01-18 11:38:21 +03:00
/*
2020-03-06 17:59:29 +03:00
* Copyright ( c ) 2020 , Liav A . < liavalb @ hotmail . co . il >
2020-01-18 11:38:21 +03: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 14:04:30 +03:00
# include <AK/Optional.h>
2020-03-23 15:45:10 +03:00
# include <AK/StringView.h>
2019-12-31 14:04:30 +03:00
# include <Kernel/PCI/MMIOAccess.h>
# include <Kernel/VM/MemoryManager.h>
2020-02-16 03:27:42 +03:00
namespace Kernel {
2019-12-31 14:04:30 +03:00
# define PCI_MMIO_CONFIG_SPACE_SIZE 4096
uint32_t PCI : : MMIOAccess : : get_segments_count ( )
{
return m_segments . size ( ) ;
}
uint8_t PCI : : MMIOAccess : : get_segment_start_bus ( u32 seg )
{
ASSERT ( m_segments . contains ( seg ) ) ;
return m_segments . get ( seg ) . value ( ) - > get_start_bus ( ) ;
}
uint8_t PCI : : MMIOAccess : : get_segment_end_bus ( u32 seg )
{
ASSERT ( m_segments . contains ( seg ) ) ;
return m_segments . get ( seg ) . value ( ) - > get_end_bus ( ) ;
}
2020-02-24 01:59:00 +03:00
void PCI : : MMIOAccess : : initialize ( PhysicalAddress mcfg )
2019-12-31 14:04:30 +03:00
{
if ( ! PCI : : Access : : is_initialized ( ) )
new PCI : : MMIOAccess ( mcfg ) ;
}
2020-02-24 01:59:00 +03:00
PCI : : MMIOAccess : : MMIOAccess ( PhysicalAddress p_mcfg )
: m_mcfg ( p_mcfg )
2019-12-31 14:04:30 +03:00
, m_segments ( * new HashMap < u16 , MMIOSegment * > ( ) )
2020-01-02 16:54:32 +03:00
, m_mapped_address ( ChangeableAddress ( 0xFFFF , 0xFF , 0xFF , 0xFF ) )
2019-12-31 14:04:30 +03:00
{
2020-03-01 22:45:39 +03:00
klog ( ) < < " PCI: Using MMIO Mechanism for PCI Configuartion Space Access " ;
2020-01-21 05:33:43 +03:00
m_mmio_window_region = MM . allocate_kernel_region ( PAGE_ROUND_UP ( PCI_MMIO_CONFIG_SPACE_SIZE ) , " PCI MMIO " , Region : : Access : : Read | Region : : Access : : Write ) ;
2019-12-31 14:04:30 +03:00
2020-02-24 01:59:00 +03: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 14:04:30 +03:00
# ifdef PCI_DEBUG
2020-02-24 16:54:12 +03:00
dbg ( ) < < " PCI: Checking MCFG Table length to choose the correct mapping size " ;
2019-12-31 14:04:30 +03:00
# endif
2020-02-09 17:47:15 +03:00
2020-03-08 19:29:48 +03:00
auto * sdt = ( ACPI : : Structures : : SDTHeader * ) checkup_region - > vaddr ( ) . offset ( p_mcfg . offset_in_page ( ) ) . as_ptr ( ) ;
2019-12-31 14:04:30 +03:00
u32 length = sdt - > length ;
u8 revision = sdt - > revision ;
2020-03-01 22:45:39 +03:00
klog ( ) < < " PCI: MCFG, length - " < < length < < " , revision " < < revision ;
2019-12-31 14:04:30 +03:00
checkup_region - > unmap ( ) ;
2020-02-24 01:59:00 +03: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 14:04:30 +03:00
2020-03-08 19:29:48 +03:00
auto & mcfg = * ( ACPI : : Structures : : MCFG * ) mcfg_region - > vaddr ( ) . offset ( p_mcfg . offset_in_page ( ) ) . as_ptr ( ) ;
2019-12-31 14:04:30 +03:00
# ifdef PCI_DEBUG
2020-02-24 16:54:12 +03:00
dbg ( ) < < " PCI: Checking MCFG @ V " < < & mcfg < < " , P 0x " < < String : : format ( " %x " , p_mcfg . get ( ) ) ;
2019-12-31 14:04:30 +03:00
# endif
2020-02-28 23:31:58 +03:00
for ( u32 index = 0 ; index < ( ( mcfg . header . length - sizeof ( ACPI : : Structures : : MCFG ) ) / sizeof ( ACPI : : Structures : : PCI_MMIO_Descriptor ) ) ; index + + ) {
2019-12-31 14:04:30 +03: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 ;
m_segments . set ( index , new PCI : : MMIOSegment ( PhysicalAddress ( lower_addr ) , start_bus , end_bus ) ) ;
2020-03-06 17:00:44 +03:00
klog ( ) < < " PCI: New PCI segment @ " < < PhysicalAddress ( lower_addr ) < < " , PCI buses ( " < < start_bus < < " - " < < end_bus < < " ) " ;
2019-12-31 14:04:30 +03:00
}
mcfg_region - > unmap ( ) ;
2020-03-01 22:45:39 +03:00
klog ( ) < < " PCI: MMIO segments - " < < m_segments . size ( ) ;
2020-01-02 16:54:32 +03:00
InterruptDisabler disabler ;
# ifdef PCI_DEBUG
2020-02-24 16:54:12 +03:00
dbg ( ) < < " PCI: mapped address ( " < < String : : format ( " %w " , m_mapped_address . seg ( ) ) < < " : " < < String : : format ( " %b " , m_mapped_address . bus ( ) ) < < " : " < < String : : format ( " %b " , m_mapped_address . slot ( ) ) < < " . " < < String : : format ( " %b " , m_mapped_address . function ( ) ) < < " ) " ;
2020-01-02 16:54:32 +03:00
# endif
2019-12-31 14:04:30 +03:00
map_device ( Address ( 0 , 0 , 0 , 0 ) ) ;
2020-01-02 16:54:32 +03:00
# ifdef PCI_DEBUG
2020-02-24 16:54:12 +03:00
dbg ( ) < < " PCI: Default mapped address ( " < < String : : format ( " %w " , m_mapped_address . seg ( ) ) < < " : " < < String : : format ( " %b " , m_mapped_address . bus ( ) ) < < " : " < < String : : format ( " %b " , m_mapped_address . slot ( ) ) < < " . " < < String : : format ( " %b " , m_mapped_address . function ( ) ) < < " ) " ;
2020-01-02 16:54:32 +03:00
# endif
2019-12-31 14:04:30 +03:00
}
void PCI : : MMIOAccess : : map_device ( Address address )
{
2020-01-02 16:54:32 +03:00
if ( m_mapped_address = = address )
return ;
2019-12-31 14:04:30 +03:00
// FIXME: Map and put some lock!
2020-01-02 16:54:32 +03:00
ASSERT_INTERRUPTS_DISABLED ( ) ;
2019-12-31 14:04:30 +03:00
ASSERT ( m_segments . contains ( address . seg ( ) ) ) ;
auto segment = m_segments . get ( address . seg ( ) ) ;
PhysicalAddress segment_lower_addr = segment . value ( ) - > get_paddr ( ) ;
PhysicalAddress device_physical_mmio_space = segment_lower_addr . offset (
PCI_MMIO_CONFIG_SPACE_SIZE * address . function ( ) + ( PCI_MMIO_CONFIG_SPACE_SIZE * PCI_MAX_FUNCTIONS_PER_DEVICE ) * address . slot ( ) + ( PCI_MMIO_CONFIG_SPACE_SIZE * PCI_MAX_FUNCTIONS_PER_DEVICE * PCI_MAX_DEVICES_PER_BUS ) * ( address . bus ( ) - segment . value ( ) - > get_start_bus ( ) ) ) ;
2020-01-02 16:54:32 +03:00
2019-12-31 14:04:30 +03:00
# ifdef PCI_DEBUG
2020-02-24 16:54:12 +03:00
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 ( ) ) < < " ) "
< < " V 0x " < < String : : format ( " %x " , m_mmio_window_region - > vaddr ( ) . get ( ) ) < < " P 0x " < < String : : format ( " %x " , device_physical_mmio_space . get ( ) ) ;
2019-12-31 14:04:30 +03:00
# endif
2020-02-09 17:47:15 +03:00
m_mmio_window_region - > vmobject ( ) . physical_pages ( ) [ 0 ] = PhysicalPage : : create ( device_physical_mmio_space , false , false ) ;
2020-01-21 05:33:43 +03:00
m_mmio_window_region - > remap ( ) ;
2020-01-02 16:54:32 +03:00
m_mapped_address = address ;
2019-12-31 14:04:30 +03:00
}
u8 PCI : : MMIOAccess : : read8_field ( Address address , u32 field )
{
2020-01-02 16:54:32 +03:00
InterruptDisabler disabler ;
2019-12-31 14:04:30 +03:00
ASSERT ( field < = 0xfff ) ;
# ifdef PCI_DEBUG
2020-02-24 16:54:12 +03: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 14:04:30 +03:00
# endif
map_device ( address ) ;
2020-01-02 16:54:32 +03:00
return * ( ( u8 * ) ( m_mmio_window_region - > vaddr ( ) . get ( ) + ( field & 0xfff ) ) ) ;
2019-12-31 14:04:30 +03:00
}
u16 PCI : : MMIOAccess : : read16_field ( Address address , u32 field )
{
2020-01-02 16:54:32 +03:00
InterruptDisabler disabler ;
2019-12-31 14:04:30 +03:00
ASSERT ( field < 0xfff ) ;
# ifdef PCI_DEBUG
2020-02-24 16:54:12 +03: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 14:04:30 +03:00
# endif
map_device ( address ) ;
2020-01-02 16:54:32 +03:00
return * ( ( u16 * ) ( m_mmio_window_region - > vaddr ( ) . get ( ) + ( field & 0xfff ) ) ) ;
2019-12-31 14:04:30 +03:00
}
u32 PCI : : MMIOAccess : : read32_field ( Address address , u32 field )
{
2020-01-02 16:54:32 +03:00
InterruptDisabler disabler ;
2019-12-31 14:04:30 +03:00
ASSERT ( field < = 0xffc ) ;
# ifdef PCI_DEBUG
2020-02-24 16:54:12 +03: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 14:04:30 +03:00
# endif
map_device ( address ) ;
2020-01-02 16:54:32 +03:00
return * ( ( u32 * ) ( m_mmio_window_region - > vaddr ( ) . get ( ) + ( field & 0xfff ) ) ) ;
2019-12-31 14:04:30 +03:00
}
void PCI : : MMIOAccess : : write8_field ( Address address , u32 field , u8 value )
{
2020-01-02 16:54:32 +03:00
InterruptDisabler disabler ;
2019-12-31 14:04:30 +03:00
ASSERT ( field < = 0xfff ) ;
# ifdef PCI_DEBUG
2020-02-24 16:54:12 +03: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 14:04:30 +03:00
# endif
map_device ( address ) ;
2020-01-02 16:54:32 +03:00
* ( ( u8 * ) ( m_mmio_window_region - > vaddr ( ) . get ( ) + ( field & 0xfff ) ) ) = value ;
2019-12-31 14:04:30 +03:00
}
void PCI : : MMIOAccess : : write16_field ( Address address , u32 field , u16 value )
{
2020-01-02 16:54:32 +03:00
InterruptDisabler disabler ;
2019-12-31 14:04:30 +03:00
ASSERT ( field < 0xfff ) ;
# ifdef PCI_DEBUG
2020-02-24 16:54:12 +03: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 14:04:30 +03:00
# endif
map_device ( address ) ;
2020-01-02 16:54:32 +03:00
* ( ( u16 * ) ( m_mmio_window_region - > vaddr ( ) . get ( ) + ( field & 0xfff ) ) ) = value ;
2019-12-31 14:04:30 +03:00
}
void PCI : : MMIOAccess : : write32_field ( Address address , u32 field , u32 value )
{
2020-01-02 16:54:32 +03:00
InterruptDisabler disabler ;
2019-12-31 14:04:30 +03:00
ASSERT ( field < = 0xffc ) ;
# ifdef PCI_DEBUG
2020-02-24 16:54:12 +03: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 14:04:30 +03:00
# endif
map_device ( address ) ;
2020-01-02 16:54:32 +03:00
* ( ( u32 * ) ( m_mmio_window_region - > vaddr ( ) . get ( ) + ( field & 0xfff ) ) ) = value ;
2019-12-31 14:04:30 +03:00
}
void PCI : : MMIOAccess : : enumerate_all ( Function < void ( Address , ID ) > & callback )
{
for ( u16 seg = 0 ; seg < m_segments . size ( ) ; seg + + ) {
# ifdef PCI_DEBUG
2020-02-24 16:54:12 +03:00
dbg ( ) < < " PCI: Enumerating Memory mapped IO segment " < < seg ;
2019-12-31 14:04:30 +03:00
# endif
// Single PCI host controller.
if ( ( read8_field ( Address ( seg ) , PCI_HEADER_TYPE ) & 0x80 ) = = 0 ) {
enumerate_bus ( - 1 , 0 , callback ) ;
return ;
}
// Multiple PCI host controllers.
for ( u8 function = 0 ; function < 8 ; + + function ) {
if ( read16_field ( Address ( seg , 0 , 0 , function ) , PCI_VENDOR_ID ) = = PCI_NONE )
break ;
enumerate_bus ( - 1 , function , callback ) ;
}
}
}
PCI : : MMIOSegment : : MMIOSegment ( PhysicalAddress segment_base_addr , u8 start_bus , u8 end_bus )
: m_base_addr ( segment_base_addr )
, m_start_bus ( start_bus )
, m_end_bus ( end_bus )
{
}
u8 PCI : : MMIOSegment : : get_start_bus ( )
{
return m_start_bus ;
}
u8 PCI : : MMIOSegment : : get_end_bus ( )
{
return m_end_bus ;
}
size_t PCI : : MMIOSegment : : get_size ( )
{
return ( PCI_MMIO_CONFIG_SPACE_SIZE * PCI_MAX_FUNCTIONS_PER_DEVICE * PCI_MAX_DEVICES_PER_BUS * ( get_end_bus ( ) - get_start_bus ( ) ) ) ;
}
PhysicalAddress PCI : : MMIOSegment : : get_paddr ( )
{
return m_base_addr ;
}
2020-02-16 03:27:42 +03:00
}