2023-01-23 20:13:57 +02:00
# include <BAN/ScopeGuard.h>
2023-01-25 21:39:03 +02:00
# include <kernel/Debug.h>
2023-01-23 20:13:57 +02:00
# include <kernel/APIC.h>
# include <kernel/CPUID.h>
# include <kernel/IDT.h>
# include <kernel/MMU.h>
# include <string.h>
# define LAPIC_EIO_REG 0xB0
# define LAPIC_SIV_REG 0xF0
# define LAPIC_IS_REG 0x100
# define IOAPIC_MAX_REDIRS 0x01
# define IOAPIC_REDIRS 0x10
2023-01-26 13:13:46 +02:00
# define DEBUG_PRINT_PROCESSORS 0
2023-01-23 20:13:57 +02:00
// https://uefi.org/specs/ACPI/6.5/05_ACPI_Software_Programming_Model.html#multiple-apic-description-table-madt-format
2023-01-26 02:37:34 +02:00
static constexpr uint32_t RSPD_SIZE = 20 ;
static constexpr uint32_t RSPDv2_SIZE = 36 ;
2023-01-23 20:13:57 +02:00
2023-01-26 02:37:34 +02:00
struct RSDP
2023-01-23 20:13:57 +02:00
{
2023-01-26 02:37:34 +02:00
char signature [ 8 ] ;
uint8_t checksum ;
char OEMID [ 6 ] ;
uint8_t revision ;
uint32_t rsdt_address ;
uint32_t v2_length ;
uint64_t v2_xsdt_address ;
uint8_t v2_extended_checksum ;
uint8_t v2_reserved [ 3 ] ;
} __attribute__ ( ( packed ) ) ;
2023-01-23 20:13:57 +02:00
2023-01-26 02:37:34 +02:00
struct SDTHeader
2023-01-23 20:13:57 +02:00
{
2023-01-26 02:37:34 +02:00
char signature [ 4 ] ;
uint32_t length ;
uint8_t revision ;
uint8_t checksum ;
char OEMID [ 6 ] ;
char OEM_table_id [ 8 ] ;
uint32_t OEM_revision ;
uint32_t creator_id ;
uint32_t creator_revision ;
2023-01-23 20:13:57 +02:00
} __attribute__ ( ( packed ) ) ;
struct MADT
{
2023-01-26 02:37:34 +02:00
SDTHeader header ;
2023-01-23 20:13:57 +02:00
uint32_t local_apic ;
uint32_t flags ;
} __attribute__ ( ( packed ) ) ;
struct MADTEntry
{
uint8_t type ;
uint8_t length ;
union
{
struct
{
uint8_t acpi_processor_id ;
uint8_t apic_id ;
uint32_t flags ;
} __attribute__ ( ( packed ) ) entry0 ;
struct
{
uint8_t ioapic_id ;
uint8_t reserved ;
uint32_t ioapic_address ;
uint32_t gsi_base ;
} __attribute__ ( ( packed ) ) entry1 ;
struct
{
uint8_t bus_source ;
uint8_t irq_source ;
uint32_t gsi ;
uint16_t flags ;
} __attribute__ ( ( packed ) ) entry2 ;
struct
{
uint16_t reserved ;
uint64_t address ;
} __attribute__ ( ( packed ) ) entry5 ;
} ;
} __attribute__ ( ( packed ) ) ;
union RedirectionEntry
{
struct
{
uint64_t vector : 8 ;
uint64_t delivery_mode : 3 ;
uint64_t destination_mode : 1 ;
uint64_t delivery_status : 1 ;
uint64_t pin_polarity : 1 ;
uint64_t remote_irr : 1 ;
uint64_t trigger_mode : 1 ;
uint64_t mask : 1 ;
uint64_t reserved : 39 ;
uint64_t destination : 8 ;
} ;
struct
{
uint32_t lo_dword ;
uint32_t hi_dword ;
} ;
} ;
2023-02-01 21:05:44 +02:00
static bool is_rsdp ( uintptr_t rsdp_addr )
2023-01-23 20:13:57 +02:00
{
2023-01-26 02:37:34 +02:00
const RSDP * rsdp = ( const RSDP * ) rsdp_addr ;
2023-01-23 20:13:57 +02:00
if ( memcmp ( rsdp - > signature , " RSD PTR " , 8 ) ! = 0 )
return false ;
{
uint8_t checksum = 0 ;
2023-01-26 02:37:34 +02:00
for ( uint32_t i = 0 ; i < RSPD_SIZE ; i + + )
2023-01-23 20:13:57 +02:00
checksum + = ( ( uint8_t * ) rsdp ) [ i ] ;
if ( checksum ! = 0 )
return false ;
}
if ( rsdp - > revision = = 2 )
{
uint8_t checksum = 0 ;
2023-01-26 02:37:34 +02:00
for ( uint32_t i = 0 ; i < RSPDv2_SIZE ; i + + )
checksum + = ( ( uint8_t * ) rsdp ) [ i ] ;
2023-01-23 20:13:57 +02:00
if ( checksum ! = 0 )
return false ;
}
return true ;
}
2023-02-01 21:05:44 +02:00
static uintptr_t locate_rsdp ( )
2023-01-23 20:13:57 +02:00
{
// Look in main BIOS area below 1 MB
for ( uintptr_t addr = 0x000E0000 ; addr < 0x000FFFFF ; addr + = 16 )
2023-02-01 21:05:44 +02:00
if ( is_rsdp ( addr ) )
2023-01-26 02:37:34 +02:00
return addr ;
return 0 ;
2023-01-23 20:13:57 +02:00
}
2023-02-01 21:05:44 +02:00
static bool is_valid_std_header ( const SDTHeader * header )
2023-01-23 20:13:57 +02:00
{
uint8_t sum = 0 ;
for ( uint32_t i = 0 ; i < header - > length ; i + + )
sum + = ( ( uint8_t * ) header ) [ i ] ;
return sum = = 0 ;
}
2023-02-01 21:05:44 +02:00
uintptr_t locate_madt ( uintptr_t rsdp_addr )
2023-01-23 20:13:57 +02:00
{
2023-01-26 02:37:34 +02:00
uintptr_t entry_address_base = 0 ;
ptrdiff_t entry_pointer_size = 0 ;
uint32_t entry_count = 0 ;
const RSDP * rsdp = ( const RSDP * ) rsdp_addr ;
2023-01-23 20:13:57 +02:00
if ( rsdp - > revision = = 2 )
{
2023-01-26 02:37:34 +02:00
uintptr_t xsdt_addr = rsdp - > v2_xsdt_address ;
2023-03-01 20:15:58 +02:00
MMU : : get ( ) . allocate_page ( xsdt_addr , MMU : : Flags : : ReadWrite | MMU : : Flags : : Present ) ;
2023-01-26 02:37:34 +02:00
entry_address_base = xsdt_addr + sizeof ( SDTHeader ) ;
entry_count = ( ( ( const SDTHeader * ) xsdt_addr ) - > length - sizeof ( SDTHeader ) ) / 8 ;
entry_pointer_size = 8 ;
2023-02-01 21:05:44 +02:00
MMU : : get ( ) . unallocate_page ( xsdt_addr ) ;
2023-01-23 20:13:57 +02:00
}
else
{
2023-01-26 02:37:34 +02:00
uintptr_t rsdt_addr = rsdp - > rsdt_address ;
2023-03-01 20:15:58 +02:00
MMU : : get ( ) . allocate_page ( rsdt_addr , MMU : : Flags : : ReadWrite | MMU : : Flags : : Present ) ;
2023-01-26 02:37:34 +02:00
entry_address_base = rsdt_addr + sizeof ( SDTHeader ) ;
entry_count = ( ( ( const SDTHeader * ) rsdt_addr ) - > length - sizeof ( SDTHeader ) ) / 4 ;
entry_pointer_size = 4 ;
2023-02-01 21:05:44 +02:00
MMU : : get ( ) . unallocate_page ( rsdt_addr ) ;
2023-01-23 20:13:57 +02:00
}
for ( uint32_t i = 0 ; i < entry_count ; i + + )
{
2023-01-26 02:37:34 +02:00
uintptr_t entry_addr_ptr = entry_address_base + i * entry_pointer_size ;
2023-03-01 20:15:58 +02:00
MMU : : get ( ) . allocate_page ( entry_addr_ptr , MMU : : Flags : : ReadWrite | MMU : : Flags : : Present ) ;
2023-01-26 02:37:34 +02:00
2023-03-08 22:55:44 +02:00
union dummy { uint32_t addr32 ; uint64_t addr64 ; } __attribute__ ( ( aligned ( 1 ) , packed ) ) ;
2023-03-07 00:41:13 +02:00
uintptr_t entry_addr ;
if ( entry_pointer_size = = 4 )
2023-03-08 22:55:44 +02:00
entry_addr = ( ( dummy * ) entry_addr_ptr ) - > addr32 ;
2023-03-07 00:41:13 +02:00
else
2023-03-08 22:55:44 +02:00
entry_addr = ( ( dummy * ) entry_addr_ptr ) - > addr64 ;
2023-03-07 00:41:13 +02:00
2023-03-01 20:15:58 +02:00
MMU : : get ( ) . allocate_page ( entry_addr , MMU : : Flags : : ReadWrite | MMU : : Flags : : Present ) ;
2023-01-26 02:37:34 +02:00
BAN : : ScopeGuard _ ( [ & ] ( ) {
2023-02-01 21:05:44 +02:00
MMU : : get ( ) . unallocate_page ( entry_addr ) ;
MMU : : get ( ) . unallocate_page ( entry_addr_ptr ) ;
2023-01-26 02:37:34 +02:00
} ) ;
const SDTHeader * entry = ( const SDTHeader * ) entry_addr ;
2023-02-01 21:05:44 +02:00
if ( memcmp ( entry - > signature , " APIC " , 4 ) = = 0 & & is_valid_std_header ( entry ) )
2023-01-26 02:37:34 +02:00
return entry_addr ;
2023-01-23 20:13:57 +02:00
}
2023-01-26 02:37:34 +02:00
return 0 ;
2023-01-23 20:13:57 +02:00
}
2023-02-01 21:05:44 +02:00
APIC * APIC : : create ( )
2023-01-23 20:13:57 +02:00
{
uint32_t ecx , edx ;
2023-02-01 21:05:44 +02:00
CPUID : : get_features ( ecx , edx ) ;
2023-01-23 20:13:57 +02:00
if ( ! ( edx & CPUID : : Features : : EDX_APIC ) )
{
dprintln ( " Local APIC is not available " ) ;
return nullptr ;
}
2023-02-01 21:05:44 +02:00
uintptr_t rsdp_addr = locate_rsdp ( ) ;
2023-01-26 02:37:34 +02:00
if ( ! rsdp_addr )
2023-01-23 20:13:57 +02:00
{
dprintln ( " Could not locate RSDP " ) ;
return nullptr ;
}
2023-02-01 21:05:44 +02:00
uintptr_t madt_addr = locate_madt ( rsdp_addr ) ;
2023-01-26 02:37:34 +02:00
if ( ! madt_addr )
2023-01-23 20:13:57 +02:00
{
dprintln ( " Could not find MADT in RSDP " ) ;
return nullptr ;
}
2023-03-01 20:15:58 +02:00
MMU : : get ( ) . allocate_page ( madt_addr , MMU : : Flags : : ReadWrite | MMU : : Flags : : Present ) ;
2023-01-26 02:37:34 +02:00
const MADT * madt = ( const MADT * ) madt_addr ;
2023-01-23 20:13:57 +02:00
APIC * apic = new APIC ;
apic - > m_local_apic = madt - > local_apic ;
for ( uint32_t i = 0x00 ; i < = 0xFF ; i + + )
apic - > m_irq_overrides [ i ] = i ;
uintptr_t madt_entry_addr = ( uintptr_t ) madt + sizeof ( MADT ) ;
while ( madt_entry_addr < ( uintptr_t ) madt + madt - > header . length )
{
2023-01-25 19:00:41 +02:00
const MADTEntry * entry = ( const MADTEntry * ) madt_entry_addr ;
2023-01-23 20:13:57 +02:00
switch ( entry - > type )
{
case 0 :
Processor processor ;
processor . processor_id = entry - > entry0 . acpi_processor_id ;
processor . apic_id = entry - > entry0 . apic_id ;
processor . flags = entry - > entry0 . flags & 0x03 ;
2023-02-01 21:05:44 +02:00
MUST ( apic - > m_processors . push_back ( processor ) ) ;
2023-01-23 20:13:57 +02:00
break ;
case 1 :
IOAPIC ioapic ;
ioapic . id = entry - > entry1 . ioapic_id ;
ioapic . address = entry - > entry1 . ioapic_address ;
ioapic . gsi_base = entry - > entry1 . gsi_base ;
ioapic . max_redirs = 0 ;
2023-02-01 21:05:44 +02:00
MUST ( apic - > m_io_apics . push_back ( ioapic ) ) ;
2023-01-23 20:13:57 +02:00
break ;
case 2 :
apic - > m_irq_overrides [ entry - > entry2 . irq_source ] = entry - > entry2 . gsi ;
break ;
case 5 :
apic - > m_local_apic = entry - > entry5 . address ;
break ;
default :
dprintln ( " Unhandled madt entry, type {} " , entry - > type ) ;
break ;
}
madt_entry_addr + = entry - > length ;
}
2023-02-01 21:05:44 +02:00
MMU : : get ( ) . unallocate_page ( ( uintptr_t ) madt ) ;
2023-01-23 20:13:57 +02:00
2023-02-01 21:05:44 +02:00
if ( apic - > m_local_apic = = 0 | | apic - > m_io_apics . empty ( ) )
2023-01-23 20:13:57 +02:00
{
dprintln ( " MADT did not provide necessary information " ) ;
delete apic ;
return nullptr ;
}
2023-03-01 20:15:58 +02:00
MMU : : get ( ) . allocate_page ( apic - > m_local_apic , MMU : : Flags : : ReadWrite | MMU : : Flags : : Present ) ;
2023-01-23 20:13:57 +02:00
for ( auto & io_apic : apic - > m_io_apics )
{
2023-03-01 20:15:58 +02:00
MMU : : get ( ) . allocate_page ( io_apic . address , MMU : : Flags : : ReadWrite | MMU : : Flags : : Present ) ;
2023-02-01 21:05:44 +02:00
io_apic . max_redirs = io_apic . read ( IOAPIC_MAX_REDIRS ) ;
2023-01-23 20:13:57 +02:00
}
// Mask all interrupts
2023-02-01 21:05:44 +02:00
uint32_t sivr = apic - > read_from_local_apic ( LAPIC_SIV_REG ) ;
apic - > write_to_local_apic ( LAPIC_SIV_REG , sivr | 0x1FF ) ;
2023-01-23 20:13:57 +02:00
2023-01-26 13:13:46 +02:00
# if DEBUG_PRINT_PROCESSORS
for ( auto & processor : apic - > m_processors )
{
dprintln ( " Processor{} " , processor . processor_id ) ;
dprintln ( " lapic id: {} " , processor . apic_id ) ;
dprintln ( " status: {} " , ( processor . flags & Processor : : Flags : : Enabled ) ? " enabled " : ( processor . flags & Processor : : Flags : : OnlineCapable ) ? " can be enabled " : " disabled " ) ;
}
# endif
2023-01-23 20:13:57 +02:00
return apic ;
}
2023-02-01 21:05:44 +02:00
uint32_t APIC : : read_from_local_apic ( ptrdiff_t offset )
2023-01-23 20:13:57 +02:00
{
return * ( uint32_t * ) ( m_local_apic + offset ) ;
}
2023-02-01 21:05:44 +02:00
void APIC : : write_to_local_apic ( ptrdiff_t offset , uint32_t data )
2023-01-23 20:13:57 +02:00
{
* ( uint32_t * ) ( m_local_apic + offset ) = data ;
}
2023-02-01 21:05:44 +02:00
uint32_t APIC : : IOAPIC : : read ( uint8_t offset )
2023-01-23 20:13:57 +02:00
{
volatile uint32_t * ioapic = ( volatile uint32_t * ) address ;
ioapic [ 0 ] = offset ;
return ioapic [ 4 ] ;
}
2023-02-01 21:05:44 +02:00
void APIC : : IOAPIC : : write ( uint8_t offset , uint32_t data )
2023-01-23 20:13:57 +02:00
{
volatile uint32_t * ioapic = ( volatile uint32_t * ) address ;
ioapic [ 0 ] = offset ;
ioapic [ 4 ] = data ;
}
2023-02-01 21:05:44 +02:00
void APIC : : eoi ( uint8_t )
2023-01-23 20:13:57 +02:00
{
2023-02-01 21:05:44 +02:00
write_to_local_apic ( LAPIC_EIO_REG , 0 ) ;
2023-01-23 20:13:57 +02:00
}
2023-02-01 21:05:44 +02:00
void APIC : : enable_irq ( uint8_t irq )
2023-01-23 20:13:57 +02:00
{
uint32_t gsi = m_irq_overrides [ irq ] ;
IOAPIC * ioapic = nullptr ;
for ( IOAPIC & io : m_io_apics )
{
if ( io . gsi_base < = gsi & & gsi < = io . gsi_base + io . max_redirs )
{
ioapic = & io ;
break ;
}
}
ASSERT ( ioapic ) ;
RedirectionEntry redir ;
2023-02-01 21:05:44 +02:00
redir . lo_dword = ioapic - > read ( IOAPIC_REDIRS + gsi * 2 ) ;
redir . hi_dword = ioapic - > read ( IOAPIC_REDIRS + gsi * 2 + 1 ) ;
2023-01-23 20:13:57 +02:00
redir . vector = IRQ_VECTOR_BASE + irq ;
redir . mask = 0 ;
2023-02-01 21:05:44 +02:00
redir . destination = m_processors . front ( ) . apic_id ;
2023-01-23 20:13:57 +02:00
2023-02-01 21:05:44 +02:00
ioapic - > write ( IOAPIC_REDIRS + gsi * 2 , redir . lo_dword ) ;
ioapic - > write ( IOAPIC_REDIRS + gsi * 2 + 1 , redir . hi_dword ) ;
2023-01-23 20:13:57 +02:00
}
2023-02-01 21:05:44 +02:00
bool APIC : : is_in_service ( uint8_t irq )
2023-01-23 20:13:57 +02:00
{
2023-01-30 18:52:38 +02:00
uint32_t dword = ( irq + IRQ_VECTOR_BASE ) / 32 ;
uint32_t bit = ( irq + IRQ_VECTOR_BASE ) % 32 ;
2023-02-01 21:05:44 +02:00
uint32_t isr = read_from_local_apic ( LAPIC_IS_REG + dword * 0x10 ) ;
2023-01-30 18:52:38 +02:00
return isr & ( 1 < < bit ) ;
2023-01-23 20:13:57 +02:00
}