From 5a92173586648d5a177eb176214eb77924c0a9ed Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Mon, 11 May 2026 00:07:21 +0200 Subject: [PATCH] Implement targeted IPI broadcasting using processor block array --- sdk/xtdk/amd64/ketypes.h | 2 + sdk/xtdk/i686/ketypes.h | 2 + xtoskrnl/hl/acpi.cc | 18 ++++++++ xtoskrnl/hl/x86/pic.cc | 74 +++++++++++++++---------------- xtoskrnl/includes/hl/acpi.hh | 1 + xtoskrnl/includes/ke/proc.hh | 7 +++ xtoskrnl/ke/amd64/proc.cc | 86 ++++++++++++++++++++++++++++++++++++ xtoskrnl/ke/data.cc | 6 +++ xtoskrnl/ke/i686/proc.cc | 86 ++++++++++++++++++++++++++++++++++++ 9 files changed, 244 insertions(+), 38 deletions(-) diff --git a/sdk/xtdk/amd64/ketypes.h b/sdk/xtdk/amd64/ketypes.h index 9ccc36f..5790d02 100644 --- a/sdk/xtdk/amd64/ketypes.h +++ b/sdk/xtdk/amd64/ketypes.h @@ -524,6 +524,8 @@ typedef struct _KPROCESSOR_BLOCK KAFFINITY SetMember; ULONG StallScaleFactor; UCHAR CpuNumber; + ULONG HardwareId; + VOLATILE BOOLEAN Started; PINTERRUPT_HANDLER InterruptDispatchTable[256]; } KPROCESSOR_BLOCK, *PKPROCESSOR_BLOCK; diff --git a/sdk/xtdk/i686/ketypes.h b/sdk/xtdk/i686/ketypes.h index 462082f..37a0d4d 100644 --- a/sdk/xtdk/i686/ketypes.h +++ b/sdk/xtdk/i686/ketypes.h @@ -483,6 +483,8 @@ typedef struct _KPROCESSOR_BLOCK KAFFINITY SetMember; ULONG StallScaleFactor; UCHAR CpuNumber; + ULONG HardwareId; + VOLATILE BOOLEAN Started; PINTERRUPT_HANDLER InterruptDispatchTable[256]; } KPROCESSOR_BLOCK, *PKPROCESSOR_BLOCK; diff --git a/xtoskrnl/hl/acpi.cc b/xtoskrnl/hl/acpi.cc index b0adceb..661d9d3 100644 --- a/xtoskrnl/hl/acpi.cc +++ b/xtoskrnl/hl/acpi.cc @@ -127,6 +127,24 @@ HL::Acpi::GetAcpiTimerInfo(OUT PACPI_TIMER_INFO *AcpiTimerInfo) } } +/** + * Gets the ACPI system information structure containing processor and topology data. + * + * @param SystemInfo + * Supplies a pointer to the memory area where the pointer to the system information structure will be stored. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +HL::Acpi::GetSystemInformation(OUT PACPI_SYSTEM_INFO *SystemInfo) +{ + /* Return a pointer to the ACPI system information */ + *SystemInfo = &HL::Acpi::SystemInfo; +} + /** * Performs an initialization of the ACPI subsystem. * diff --git a/xtoskrnl/hl/x86/pic.cc b/xtoskrnl/hl/x86/pic.cc index a2e7f9f..41b4737 100644 --- a/xtoskrnl/hl/x86/pic.cc +++ b/xtoskrnl/hl/x86/pic.cc @@ -402,6 +402,9 @@ HL::Pic::InitializeApic(VOID) WriteApicRegister(APIC_LDR, (1UL << CpuNumber) << 24); } + /* Report the APIC ID to the kernel logic */ + KE::Processor::RegisterHardwareId(GetCpuApicId()); + /* Configure the spurious interrupt vector */ SpuriousRegister.Long = ReadApicRegister(APIC_SIVR); SpuriousRegister.Vector = APIC_VECTOR_SPURIOUS; @@ -781,58 +784,53 @@ VOID HL::Pic::SendBroadcastIpi(IN ULONG Vector, IN BOOLEAN Self) { - APIC_COMMAND_REGISTER Register; + PKPROCESSOR_BLOCK CurrentProcessorBlock, TargetProcessorBlock; + PACPI_SYSTEM_INFO SysInfo; BOOLEAN Interrupts; + ULONG Index; - /* SMP not implemented */ - if(TRUE) + /* Get the current processor block */ + CurrentProcessorBlock = KE::Processor::GetCurrentProcessorBlock(); + if(CurrentProcessorBlock == NULLPTR) { - /* Check if IPI is addressed to the current CPU */ - if(Self) - { - /* Send IPI to the current CPU */ - SendSelfIpi(Vector); - return; - } - else - { - /* Nothing to do */ - return; - } + /* Processor block not available, return */ + return; } + /* Get the ACPI system information */ + HL::Acpi::GetSystemInformation(&SysInfo); + /* Check whether interrupts are enabled */ Interrupts = AR::CpuFunc::InterruptsEnabled(); /* Disable interrupts */ AR::CpuFunc::ClearInterruptFlag(); - /* Prepare the APIC command register */ - Register.LongLong = 0; - Register.DeliveryMode = APIC_DM_FIXED; - Register.DestinationShortHand = Self ? APIC_DSH_AllIncludingSelf : APIC_DSH_AllExclusingSelf; - Register.Level = 1; - Register.TriggerMode = APIC_TGM_EDGE; - Register.Vector = Vector; + /* Iterate over all logical CPUs */ + for(Index = 0; Index < SysInfo->CpuCount; Index++) + { + /* Retrieve the target processor block by its Logical CPU Number */ + TargetProcessorBlock = KE::Processor::GetProcessorBlock(Index); - /* Check current APIC mode */ - if(ApicMode == APIC_MODE_X2APIC) - { - /* In x2APIC mode, writing the full 64-bit value to the ICR MSR is sufficient */ - WriteApicRegister(APIC_ICR0, Register.LongLong); - } - else - { - /* Wait for the APIC to clear the delivery status */ - while((ReadApicRegister(APIC_ICR0) & 0x1000) != 0) + /* Only send to processors that exist and have successfully started */ + if(TargetProcessorBlock != NULLPTR && TargetProcessorBlock->Started) { - /* Yield the processor */ - AR::CpuFunc::YieldProcessor(); + /* Check if this processor originated the broadcast */ + if(TargetProcessorBlock->HardwareId == CurrentProcessorBlock->HardwareId) + { + /* Check if this is a self broadcast */ + if(Self) + { + /* Dispatch the IPI to the current processor */ + SendSelfIpi(Vector); + } + } + else + { + /* Dispatch the IPI to the target processor */ + SendIpi(TargetProcessorBlock->HardwareId, Vector); + } } - - /* In xAPIC compatibility mode, write the command to the ICR registers */ - WriteApicRegister(APIC_ICR1, Register.Long1); - WriteApicRegister(APIC_ICR0, Register.Long0); } /* Check whether interrupts need to be re-enabled */ diff --git a/xtoskrnl/includes/hl/acpi.hh b/xtoskrnl/includes/hl/acpi.hh index 4e1a246..a1bbce4 100644 --- a/xtoskrnl/includes/hl/acpi.hh +++ b/xtoskrnl/includes/hl/acpi.hh @@ -30,6 +30,7 @@ namespace HL STATIC XTAPI XTSTATUS GetAcpiTable(IN ULONG Signature, OUT PACPI_DESCRIPTION_HEADER *AcpiTable); STATIC XTAPI VOID GetAcpiTimerInfo(OUT PACPI_TIMER_INFO *AcpiTimerInfo); + STATIC XTAPI VOID GetSystemInformation(OUT PACPI_SYSTEM_INFO *SystemInfo); STATIC XTAPI XTSTATUS InitializeAcpi(VOID); STATIC XTAPI XTSTATUS InitializeAcpiSystemInformation(VOID); diff --git a/xtoskrnl/includes/ke/proc.hh b/xtoskrnl/includes/ke/proc.hh index 08ee47a..e9b63c9 100644 --- a/xtoskrnl/includes/ke/proc.hh +++ b/xtoskrnl/includes/ke/proc.hh @@ -17,11 +17,18 @@ namespace KE { class Processor { + private: + STATIC ULONG InstalledCpus; + STATIC PKPROCESSOR_BLOCK *ProcessorBlocks; + public: STATIC XTAPI PKPROCESSOR_BLOCK GetCurrentProcessorBlock(VOID); STATIC XTAPI PKPROCESSOR_CONTROL_BLOCK GetCurrentProcessorControlBlock(VOID); STATIC XTAPI ULONG GetCurrentProcessorNumber(VOID); STATIC XTAPI PKTHREAD GetCurrentThread(VOID); + STATIC XTAPI PKPROCESSOR_BLOCK GetProcessorBlock(IN ULONG CpuNumber); + STATIC XTAPI XTSTATUS InitializeProcessorStructures(IN ULONG CpuCount); + STATIC XTAPI VOID RegisterHardwareId(IN ULONG HardwareId); STATIC XTAPI VOID SaveProcessorState(OUT PKPROCESSOR_STATE CpuState); }; } diff --git a/xtoskrnl/ke/amd64/proc.cc b/xtoskrnl/ke/amd64/proc.cc index 3666888..7622b80 100644 --- a/xtoskrnl/ke/amd64/proc.cc +++ b/xtoskrnl/ke/amd64/proc.cc @@ -66,6 +66,92 @@ KE::Processor::GetCurrentThread(VOID) return (PKTHREAD)AR::CpuFunc::ReadGSQuadWord(FIELD_OFFSET(KPROCESSOR_BLOCK, Prcb.CurrentThread)); } +/** + * Gets the processor block for the specified processor number. + * + * @param CpuNumber + * Supplies the zero-indexed processor number. + * + * @return This routine returns a pointer to the processor block, or NULLPTR if invalid. + * + * @since XT 1.0 + */ +XTAPI +PKPROCESSOR_BLOCK +KE::Processor::GetProcessorBlock(IN ULONG CpuNumber) +{ + /* Check if the requested CPU number is within dynamic bounds */ + if(ProcessorBlocks == NULLPTR || CpuNumber >= InstalledCpus) + { + /* Invalid CPU number, return NULLPTR */ + return NULLPTR; + } + + /* Return requested processor block */ + return ProcessorBlocks[CpuNumber]; +} + +/** + * Initializes the global processor structures by allocating an array of processor block pointers. + * + * @param CpuCount + * Supplies the total number of processors present in the system. + * + * @return This routine returns a status code indicating the success or failure of the allocation. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +KE::Processor::InitializeProcessorStructures(IN ULONG CpuCount) +{ + XTSTATUS Status; + + /* Save number of CPUs installed */ + InstalledCpus = CpuCount; + + /* Allocate an array of pointers */ + Status = MM::Allocator::AllocatePool(NonPagedPool, + CpuCount * sizeof(PKPROCESSOR_BLOCK), + (PVOID*)&ProcessorBlocks); + if(Status != STATUS_SUCCESS) + { + /* Failed to allocate memory, return error */ + return Status; + } + + /* Zero the array initially */ + RTL::Memory::ZeroMemory(ProcessorBlocks, CpuCount * sizeof(PKPROCESSOR_BLOCK)); + + /* Return success */ + return STATUS_SUCCESS; +} + +/** + * Registers the hardware APIC ID for the currently executing processor. + * + * @param ApicId + * Supplies the hardware APIC ID to register in the processor block. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +KE::Processor::RegisterHardwareId(IN ULONG HardwareId) +{ + PKPROCESSOR_BLOCK CurrentBlock; + + /* Retrieve the processor block for the executing core */ + CurrentBlock = GetCurrentProcessorBlock(); + if(CurrentBlock != NULLPTR) + { + /* Register the hardware identifier for IPI targeting */ + CurrentBlock->HardwareId = HardwareId; + } +} + /** * Saves the current processor state. * diff --git a/xtoskrnl/ke/data.cc b/xtoskrnl/ke/data.cc index 1b2852c..9ae23c1 100644 --- a/xtoskrnl/ke/data.cc +++ b/xtoskrnl/ke/data.cc @@ -21,6 +21,12 @@ ETHREAD KE::KThread::InitialThread = {}; /* Kernel UBSAN active frame flag */ BOOLEAN KE::KUbsan::ActiveFrame = FALSE; +/* Total number of installed processors in the system */ +ULONG KE::Processor::InstalledCpus; + +/* Array of pointers to processor control blocks */ +PKPROCESSOR_BLOCK *KE::Processor::ProcessorBlocks; + /* Kernel shared data (KSD) */ PKSHARED_DATA KE::SharedData::KernelSharedData; diff --git a/xtoskrnl/ke/i686/proc.cc b/xtoskrnl/ke/i686/proc.cc index c405675..84f8f89 100644 --- a/xtoskrnl/ke/i686/proc.cc +++ b/xtoskrnl/ke/i686/proc.cc @@ -66,6 +66,92 @@ KE::Processor::GetCurrentThread(VOID) return (PKTHREAD)AR::CpuFunc::ReadFSDualWord(FIELD_OFFSET(KPROCESSOR_BLOCK, Prcb.CurrentThread)); } +/** + * Gets the processor block for the specified processor number. + * + * @param CpuNumber + * Supplies the zero-indexed processor number. + * + * @return This routine returns a pointer to the processor block, or NULLPTR if invalid. + * + * @since XT 1.0 + */ +XTAPI +PKPROCESSOR_BLOCK +KE::Processor::GetProcessorBlock(IN ULONG CpuNumber) +{ + /* Check if the requested CPU number is within dynamic bounds */ + if(ProcessorBlocks == NULLPTR || CpuNumber >= InstalledCpus) + { + /* Invalid CPU number, return NULLPTR */ + return NULLPTR; + } + + /* Return requested processor block */ + return ProcessorBlocks[CpuNumber]; +} + +/** + * Initializes the global processor structures by allocating an array of processor block pointers. + * + * @param CpuCount + * Supplies the total number of processors present in the system. + * + * @return This routine returns a status code indicating the success or failure of the allocation. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +KE::Processor::InitializeProcessorStructures(IN ULONG CpuCount) +{ + XTSTATUS Status; + + /* Save number of CPUs installed */ + InstalledCpus = CpuCount; + + /* Allocate an array of pointers */ + Status = MM::Allocator::AllocatePool(NonPagedPool, + CpuCount * sizeof(PKPROCESSOR_BLOCK), + (PVOID*)&ProcessorBlocks); + if(Status != STATUS_SUCCESS) + { + /* Failed to allocate memory, return error */ + return Status; + } + + /* Zero the array initially */ + RTL::Memory::ZeroMemory(ProcessorBlocks, CpuCount * sizeof(PKPROCESSOR_BLOCK)); + + /* Return success */ + return STATUS_SUCCESS; +} + +/** + * Registers the hardware APIC ID for the currently executing processor. + * + * @param ApicId + * Supplies the hardware APIC ID to register in the processor block. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +KE::Processor::RegisterHardwareId(IN ULONG HardwareId) +{ + PKPROCESSOR_BLOCK CurrentBlock; + + /* Retrieve the processor block for the executing core */ + CurrentBlock = GetCurrentProcessorBlock(); + if(CurrentBlock != NULLPTR) + { + /* Register the hardware identifier for IPI targeting */ + CurrentBlock->HardwareId = HardwareId; + } +} + /** * Saves the current processor state. *