2020-02-22 20:36:40 +03:00
/*
* Copyright ( c ) 2020 , Liav A . < liavalb @ hotmail . co . il >
* 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 .
*/
# include <AK/FixedArray.h>
2020-03-23 15:45:10 +03:00
# include <AK/StringView.h>
2020-02-22 20:36:40 +03:00
# include <Kernel/ACPI/MultiProcessorParser.h>
# include <Kernel/Arch/i386/CPU.h>
2020-04-09 20:16:51 +03:00
# include <Kernel/CommandLine.h>
2020-05-23 16:46:28 +03:00
# include <Kernel/IO.h>
2020-03-06 04:19:40 +03:00
# include <Kernel/Interrupts/APIC.h>
2020-02-22 20:36:40 +03:00
# include <Kernel/Interrupts/IOAPIC.h>
# include <Kernel/Interrupts/InterruptManagement.h>
# include <Kernel/Interrupts/PIC.h>
2020-02-28 19:26:44 +03:00
# include <Kernel/Interrupts/SpuriousInterruptHandler.h>
2020-03-06 04:19:40 +03:00
# include <Kernel/Interrupts/UnhandledInterruptHandler.h>
2020-07-04 18:22:23 +03:00
# include <Kernel/API/Syscall.h>
2020-02-22 20:36:40 +03:00
# include <Kernel/VM/MemoryManager.h>
2020-05-23 16:46:28 +03:00
# include <Kernel/VM/TypedMapping.h>
2020-02-22 20:36:40 +03:00
# define PCAT_COMPAT_FLAG 0x1
namespace Kernel {
static InterruptManagement * s_interrupt_management ;
bool InterruptManagement : : initialized ( )
{
return ( s_interrupt_management ! = nullptr ) ;
}
InterruptManagement & InterruptManagement : : the ( )
{
ASSERT ( InterruptManagement : : initialized ( ) ) ;
return * s_interrupt_management ;
}
void InterruptManagement : : initialize ( )
{
ASSERT ( ! InterruptManagement : : initialized ( ) ) ;
2020-02-28 23:33:41 +03:00
s_interrupt_management = new InterruptManagement ( ) ;
2020-04-09 20:16:51 +03:00
if ( kernel_command_line ( ) . lookup ( " smp " ) . value_or ( " off " ) = = " on " )
InterruptManagement : : the ( ) . switch_to_ioapic_mode ( ) ;
else
InterruptManagement : : the ( ) . switch_to_pic_mode ( ) ;
2020-02-22 20:36:40 +03:00
}
void InterruptManagement : : enumerate_interrupt_handlers ( Function < void ( GenericInterruptHandler & ) > callback )
{
for ( int i = 0 ; i < GENERIC_INTERRUPT_HANDLERS_COUNT ; i + + ) {
auto & handler = get_interrupt_handler ( i ) ;
2020-03-05 20:15:38 +03:00
if ( handler . type ( ) ! = HandlerType : : UnhandledInterruptHandler )
2020-02-22 20:36:40 +03:00
callback ( handler ) ;
}
}
2020-02-28 23:33:41 +03:00
IRQController & InterruptManagement : : get_interrupt_controller ( int index )
2020-02-22 20:36:40 +03:00
{
2020-02-28 23:33:41 +03:00
ASSERT ( index > = 0 ) ;
ASSERT ( ! m_interrupt_controllers [ index ] . is_null ( ) ) ;
return * m_interrupt_controllers [ index ] ;
2020-02-22 20:36:40 +03:00
}
2020-03-08 03:34:46 +03:00
u8 InterruptManagement : : acquire_mapped_interrupt_number ( u8 original_irq )
2020-03-06 04:19:40 +03:00
{
if ( ! InterruptManagement : : initialized ( ) ) {
// This is necessary, because we install UnhandledInterruptHandlers before we actually initialize the Interrupt Management object...
2020-03-08 03:34:46 +03:00
return original_irq ;
2020-03-06 04:19:40 +03:00
}
2020-03-08 03:34:46 +03:00
return InterruptManagement : : the ( ) . get_mapped_interrupt_vector ( original_irq ) ;
2020-03-06 04:19:40 +03:00
}
2020-03-08 03:34:46 +03:00
u8 InterruptManagement : : acquire_irq_number ( u8 mapped_interrupt_vector )
{
ASSERT ( InterruptManagement : : initialized ( ) ) ;
return InterruptManagement : : the ( ) . get_irq_vector ( mapped_interrupt_vector ) ;
}
u8 InterruptManagement : : get_mapped_interrupt_vector ( u8 original_irq )
{
// FIXME: For SMP configuration (with IOAPICs) use a better routing scheme to make redirections more efficient.
2020-03-14 21:19:38 +03:00
// FIXME: Find a better way to handle conflict with Syscall interrupt gate.
ASSERT ( ( original_irq + IRQ_VECTOR_BASE ) ! = syscall_vector ) ;
2020-03-08 03:34:46 +03:00
return original_irq ;
}
u8 InterruptManagement : : get_irq_vector ( u8 mapped_interrupt_vector )
2020-03-06 04:19:40 +03:00
{
// FIXME: For SMP configuration (with IOAPICs) use a better routing scheme to make redirections more efficient.
2020-03-08 03:34:46 +03:00
return mapped_interrupt_vector ;
2020-03-06 04:19:40 +03:00
}
2020-02-28 23:33:41 +03:00
RefPtr < IRQController > InterruptManagement : : get_responsible_irq_controller ( u8 interrupt_vector )
2020-02-22 20:36:40 +03:00
{
2020-02-28 19:26:44 +03:00
if ( m_interrupt_controllers . size ( ) = = 1 & & m_interrupt_controllers [ 0 ] - > type ( ) = = IRQControllerType : : i8259 ) {
2020-02-28 23:33:41 +03:00
return m_interrupt_controllers [ 0 ] ;
2020-02-28 19:26:44 +03:00
}
2020-02-28 23:33:41 +03:00
for ( auto irq_controller : m_interrupt_controllers ) {
2020-03-08 13:47:33 +03:00
if ( irq_controller - > gsi_base ( ) < = interrupt_vector )
2020-02-22 20:36:40 +03:00
if ( ! irq_controller - > is_hard_disabled ( ) )
2020-02-28 23:33:41 +03:00
return irq_controller ;
2020-02-22 20:36:40 +03:00
}
2020-02-28 23:33:41 +03:00
ASSERT_NOT_REACHED ( ) ;
2020-02-22 20:36:40 +03:00
}
2020-02-28 23:33:41 +03:00
PhysicalAddress InterruptManagement : : search_for_madt ( )
2020-02-22 20:36:40 +03:00
{
2020-02-28 23:33:41 +03:00
dbg ( ) < < " Early access to ACPI tables for interrupt setup " ;
2020-04-09 18:12:58 +03:00
auto rsdp = ACPI : : StaticParsing : : find_rsdp ( ) ;
2020-05-22 14:34:53 +03:00
if ( ! rsdp . has_value ( ) )
2020-02-28 23:33:41 +03:00
return { } ;
2020-05-22 14:34:53 +03:00
return ACPI : : StaticParsing : : find_table ( rsdp . value ( ) , " APIC " ) ;
2020-02-22 20:36:40 +03:00
}
2020-02-28 23:33:41 +03:00
InterruptManagement : : InterruptManagement ( )
: m_madt ( search_for_madt ( ) )
2020-02-22 20:36:40 +03:00
{
}
2020-02-28 23:33:41 +03:00
void InterruptManagement : : switch_to_pic_mode ( )
2020-02-22 20:36:40 +03:00
{
2020-03-01 22:45:39 +03:00
klog ( ) < < " Interrupts: Switch to Legacy PIC mode " ;
2020-03-06 04:19:40 +03:00
InterruptDisabler disabler ;
m_smp_enabled = false ;
2020-04-09 21:17:02 +03:00
m_interrupt_controllers [ 0 ] = adopt ( * new PIC ( ) ) ;
2020-02-28 19:26:44 +03:00
SpuriousInterruptHandler : : initialize ( 7 ) ;
SpuriousInterruptHandler : : initialize ( 15 ) ;
2020-02-22 20:36:40 +03:00
for ( auto & irq_controller : m_interrupt_controllers ) {
ASSERT ( irq_controller ) ;
if ( irq_controller - > type ( ) = = IRQControllerType : : i82093AA ) {
irq_controller - > hard_disable ( ) ;
dbg ( ) < < " Interrupts: Detected " < < irq_controller - > model ( ) < < " - Disabled " ;
} else {
dbg ( ) < < " Interrupts: Detected " < < irq_controller - > model ( ) ;
}
}
}
2020-02-28 23:33:41 +03:00
void InterruptManagement : : switch_to_ioapic_mode ( )
2020-02-22 20:36:40 +03:00
{
2020-03-01 22:45:39 +03:00
klog ( ) < < " Interrupts: Switch to IOAPIC mode " ;
2020-03-06 04:19:40 +03:00
InterruptDisabler disabler ;
2020-04-09 21:17:02 +03:00
if ( m_madt . is_null ( ) ) {
dbg ( ) < < " Interrupts: ACPI MADT is not available, reverting to PIC mode " ;
switch_to_pic_mode ( ) ;
return ;
}
dbg ( ) < < " Interrupts: MADT @ P " < < m_madt . as_ptr ( ) ;
locate_apic_data ( ) ;
2020-03-06 04:19:40 +03:00
m_smp_enabled = true ;
2020-02-22 20:36:40 +03:00
if ( m_interrupt_controllers . size ( ) = = 1 ) {
if ( get_interrupt_controller ( 0 ) . type ( ) = = IRQControllerType : : i8259 ) {
2020-03-01 22:45:39 +03:00
klog ( ) < < " Interrupts: NO IOAPIC detected, Reverting to PIC mode. " ;
2020-02-22 20:36:40 +03:00
return ;
}
}
for ( auto & irq_controller : m_interrupt_controllers ) {
ASSERT ( irq_controller ) ;
if ( irq_controller - > type ( ) = = IRQControllerType : : i8259 ) {
irq_controller - > hard_disable ( ) ;
dbg ( ) < < " Interrupts: Detected " < < irq_controller - > model ( ) < < " Disabled " ;
} else {
dbg ( ) < < " Interrupts: Detected " < < irq_controller - > model ( ) ;
}
}
2020-05-22 14:34:53 +03:00
if ( auto mp_parser = MultiProcessorParser : : autodetect ( ) ) {
m_pci_interrupt_overrides = mp_parser - > get_pci_interrupt_redirections ( ) ;
}
2020-07-06 16:27:22 +03:00
APIC : : the ( ) . init_bsp ( ) ;
2020-02-22 20:36:40 +03:00
}
2020-02-28 23:33:41 +03:00
void InterruptManagement : : locate_apic_data ( )
2020-02-22 20:36:40 +03:00
{
2020-02-28 23:33:41 +03:00
ASSERT ( ! m_madt . is_null ( ) ) ;
2020-05-23 16:46:28 +03:00
auto madt = map_typed < ACPI : : Structures : : MADT > ( m_madt ) ;
2020-02-22 20:36:40 +03:00
2020-02-28 23:33:41 +03:00
int irq_controller_count = 0 ;
2020-05-23 16:46:28 +03:00
if ( madt - > flags & PCAT_COMPAT_FLAG ) {
2020-02-28 23:33:41 +03:00
m_interrupt_controllers [ 0 ] = adopt ( * new PIC ( ) ) ;
irq_controller_count + + ;
2020-02-22 20:36:40 +03:00
}
size_t entry_index = 0 ;
2020-05-23 16:46:28 +03:00
size_t entries_length = madt - > h . length - sizeof ( ACPI : : Structures : : MADT ) ;
auto * madt_entry = madt - > entries ;
2020-02-22 20:36:40 +03:00
while ( entries_length > 0 ) {
size_t entry_length = madt_entry - > length ;
2020-02-28 23:33:41 +03:00
if ( madt_entry - > type = = ( u8 ) ACPI : : Structures : : MADTEntryType : : IOAPIC ) {
auto * ioapic_entry = ( const ACPI : : Structures : : MADTEntries : : IOAPIC * ) madt_entry ;
2020-05-23 16:46:28 +03:00
dbg ( ) < < " IOAPIC found @ MADT entry " < < entry_index < < " , MMIO Registers @ " < < PhysicalAddress ( ioapic_entry - > ioapic_address ) ;
2020-02-28 23:33:41 +03:00
m_interrupt_controllers . resize ( 1 + irq_controller_count ) ;
2020-05-23 16:57:48 +03:00
m_interrupt_controllers [ irq_controller_count ] = adopt ( * new IOAPIC ( PhysicalAddress ( ioapic_entry - > ioapic_address ) , ioapic_entry - > gsi_base ) ) ;
2020-02-28 23:33:41 +03:00
irq_controller_count + + ;
2020-02-22 20:36:40 +03:00
}
2020-02-28 23:33:41 +03:00
if ( madt_entry - > type = = ( u8 ) ACPI : : Structures : : MADTEntryType : : InterruptSourceOverride ) {
auto * interrupt_override_entry = ( const ACPI : : Structures : : MADTEntries : : InterruptSourceOverride * ) madt_entry ;
2020-05-08 22:07:04 +03:00
m_isa_interrupt_overrides . empend (
2020-02-22 20:36:40 +03:00
interrupt_override_entry - > bus ,
interrupt_override_entry - > source ,
interrupt_override_entry - > global_system_interrupt ,
2020-05-08 22:07:04 +03:00
interrupt_override_entry - > flags ) ;
2020-02-22 20:36:40 +03:00
dbg ( ) < < " Interrupts: Overriding INT 0x " < < String : : format ( " %x " , interrupt_override_entry - > source ) < < " with GSI " < < interrupt_override_entry - > global_system_interrupt < < " , for bus 0x " < < String : : format ( " %x " , interrupt_override_entry - > bus ) ;
}
2020-05-23 14:43:34 +03:00
madt_entry = ( ACPI : : Structures : : MADTEntryHeader * ) ( VirtualAddress ( madt_entry ) . offset ( entry_length ) . get ( ) ) ;
2020-02-22 20:36:40 +03:00
entries_length - = entry_length ;
entry_index + + ;
}
}
2020-05-08 22:07:04 +03:00
2020-02-22 20:36:40 +03:00
}