From d175a817a5b85e9ffefb5c8ae1fb30711a5ec976 Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Mon, 8 Jun 2026 14:49:02 +0200 Subject: [PATCH] Add IPI service routines for APC, DPC, and freeze request handling --- xtoskrnl/CMakeLists.txt | 1 + xtoskrnl/includes/ke.hh | 1 + xtoskrnl/includes/ke/ipi.hh | 31 +++++ xtoskrnl/ke/ipi.cc | 247 ++++++++++++++++++++++++++++++++++++ 4 files changed, 280 insertions(+) create mode 100644 xtoskrnl/includes/ke/ipi.hh create mode 100644 xtoskrnl/ke/ipi.cc diff --git a/xtoskrnl/CMakeLists.txt b/xtoskrnl/CMakeLists.txt index 2403ccb04..a14c29ca5 100644 --- a/xtoskrnl/CMakeLists.txt +++ b/xtoskrnl/CMakeLists.txt @@ -49,6 +49,7 @@ list(APPEND XTOSKRNL_SOURCE ${XTOSKRNL_SOURCE_DIR}/ke/dpc.cc ${XTOSKRNL_SOURCE_DIR}/ke/event.cc ${XTOSKRNL_SOURCE_DIR}/ke/exports.cc + ${XTOSKRNL_SOURCE_DIR}/ke/ipi.cc ${XTOSKRNL_SOURCE_DIR}/ke/kprocess.cc ${XTOSKRNL_SOURCE_DIR}/ke/krnlinit.cc ${XTOSKRNL_SOURCE_DIR}/ke/kthread.cc diff --git a/xtoskrnl/includes/ke.hh b/xtoskrnl/includes/ke.hh index c31de7789..c01b9eef9 100644 --- a/xtoskrnl/includes/ke.hh +++ b/xtoskrnl/includes/ke.hh @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/xtoskrnl/includes/ke/ipi.hh b/xtoskrnl/includes/ke/ipi.hh new file mode 100644 index 000000000..cffcee7db --- /dev/null +++ b/xtoskrnl/includes/ke/ipi.hh @@ -0,0 +1,31 @@ +/** + * PROJECT: ExectOS + * COPYRIGHT: See COPYING.md in the top level directory + * FILE: xtoskrnl/includes/ke/ipi.hh + * DESCRIPTION: Interprocessor interrupt support + * DEVELOPERS: Aiken Harris + */ + +#ifndef __XTOSKRNL_KE_IPI_HH +#define __XTOSKRNL_KE_IPI_HH + +#include + + +/* Kernel Library */ +namespace KE +{ + class Ipi + { + public: + STATIC XTCDECL VOID HandleIpiInterrupt(IN PKTRAP_FRAME TrapFrame); + STATIC XTAPI BOOLEAN HandleIpiService(IN PKTRAP_FRAME TrapFrame, + IN PKEXCEPTION_FRAME ExceptionFrame); + STATIC XTAPI VOID SendBroadcastIpi(IN ULONG Request, + IN BOOLEAN Self); + STATIC XTAPI VOID SendIpi(IN ULONG Request, + IN PKAFFINITY_MAP TargetSet); + }; +} + +#endif /* __XTOSKRNL_KE_IPI_HH */ diff --git a/xtoskrnl/ke/ipi.cc b/xtoskrnl/ke/ipi.cc new file mode 100644 index 000000000..c2d717ac8 --- /dev/null +++ b/xtoskrnl/ke/ipi.cc @@ -0,0 +1,247 @@ +/** + * PROJECT: ExectOS + * COPYRIGHT: See COPYING.md in the top level directory + * FILE: xtoskrnl/ke/ipi.cc + * DESCRIPTION: Interprocessor interrupt support + * DEVELOPERS: Aiken Harris + */ + +#include + + +/** + * Handles an IPI interrupt. + * + * @param TrapFrame + * Supplies a pointer to the processor's trap frame captured at the moment the interrupt was delivered. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTCDECL +VOID +KE::Ipi::HandleIpiInterrupt(IN PKTRAP_FRAME TrapFrame) +{ + KRUNLEVEL RunLevel; + + /* Start the interrupt */ + HL::Irq::BeginSystemInterrupt(IPI_LEVEL, &RunLevel); + + /* Call the IPI service routine */ + HandleIpiService(TrapFrame, NULLPTR); + + /* End the interrupt */ + HL::Irq::EndInterrupt(TrapFrame, RunLevel); +} + +/** + * Services an incoming Inter-Processor Interrupt (IPI). + * + * @param TrapFrame + * Supplies a pointer to the trap frame representing the processor state at the time of the interrupt. + * + * @param ExceptionFrame + * Supplies a pointer to the exception frame. + * + * @return This routine returns TRUE if a standard IPI request (other than IPI_FREEZE) was processed, + * or FALSE otherwise. + * + * @since XT 1.0 + */ +XTAPI +BOOLEAN +KE::Ipi::HandleIpiService(IN PKTRAP_FRAME TrapFrame, + IN PKEXCEPTION_FRAME ExceptionFrame) +{ + PKPROCESSOR_CONTROL_BLOCK Prcb; + LONG_PTR RequestSummary; + + /* Get processor control block */ + Prcb = KE::Processor::GetCurrentProcessorControlBlock(); + + /* Atomically retrieve and clear the pending IPI request summary */ + RequestSummary = RTL::Atomic::Exchange64((PLONG_PTR)&Prcb->RequestSummary, 0); + + /* Check if an Asynchronous Procedure Call (APC) interrupt was requested */ + if((RequestSummary & IPI_APC) != 0) + { + /* Dispatch a software interrupt to process the APC queue */ + HL::Irq::SendSoftwareInterrupt(APC_LEVEL); + } + + /* Check if a Deferred Procedure Call (DPC) interrupt was requested */ + if((RequestSummary & IPI_DPC) != 0) + { + /* Flag the DPC request in the PRCB and dispatch a software interrupt */ + Prcb->DpcInterruptRequested = TRUE; + HL::Irq::SendSoftwareInterrupt(DISPATCH_LEVEL); + } + + /* Check if the IPI packet is ready to be processed */ + if((RequestSummary & IPI_PACKET_READY) != 0) + { + UNIMPLEMENTED; + } + + /* Check if a synchronous inter-processor event was requested */ + if((RequestSummary & IPI_SYNC_REQUEST) != 0) + { + UNIMPLEMENTED; + } + + /* Check if a debugger freeze request was issued */ + if((RequestSummary & IPI_FREEZE) != 0) + { + /* Suspend the current processor execution */ + KE::Crash::FreezeCurrentExecution(TrapFrame, ExceptionFrame); + } + + /* Return TRUE if any standard execution requests were serviced, excluding freeze events */ + return (RequestSummary & ~IPI_FREEZE) != 0; +} + +/** + * Broadcasts an Inter-Processor Interrupt (IPI) to all active processors in the system. + * + * @param Request + * Supplies the logical request operation flags to be merged into the target processors' request summaries. + * + * @param Self + * Specifies whether the broadcast IPI should also be delivered to the originating processor. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +KE::Ipi::SendBroadcastIpi(IN ULONG Request, + IN BOOLEAN Self) +{ + PKPROCESSOR_BLOCK CurrentProcessorBlock, TargetProcessorBlock; + ULONG CpuCount, Index; + BOOLEAN Interrupts; + + /* Get the number of installed CPUs */ + CpuCount = KE::Processor::GetInstalledCpus(); + + /* Check if there are any processors to broadcast to */ + if(CpuCount <= 1 && !Self) + { + /* There are no other processors, return */ + return; + } + + /* Check whether interrupts are enabled */ + Interrupts = AR::CpuFunctions::InterruptsEnabled(); + + /* Disable interrupts */ + AR::CpuFunctions::ClearInterruptFlag(); + + /* Get the current processor block */ + CurrentProcessorBlock = KE::Processor::GetCurrentProcessorBlock(); + + /* Iterate over all logical CPUs */ + for(Index = 0; Index < CpuCount; Index++) + { + /* Retrieve the target processor block by its Logical CPU Number */ + TargetProcessorBlock = KE::Processor::GetProcessorBlock(Index); + + /* Only target processors that exist and have successfully started */ + if(TargetProcessorBlock != NULLPTR && TargetProcessorBlock->Started) + { + /* Check if this processor originated the broadcast */ + if(TargetProcessorBlock->HardwareId == CurrentProcessorBlock->HardwareId) + { + /* Check if this is a self broadcast */ + if(Self) + { + /* Update the logical request summary and dispatch a physical IPI to the current processor */ + RTL::Atomic::Or64((PLONG_PTR)&TargetProcessorBlock->Prcb.RequestSummary, Request); + HL::Pic::SendSelfIpi(APIC_VECTOR_IPI); + } + } + else + { + /* Update the logical request summary and dispatch a physical IPI to the target processor */ + RTL::Atomic::Or64((PLONG_PTR)&TargetProcessorBlock->Prcb.RequestSummary, Request); + HL::Pic::SendIpi(TargetProcessorBlock->HardwareId, APIC_VECTOR_IPI, APIC_DM_FIXED, + APIC_DSH_Destination, APIC_TGM_EDGE); + } + } + } + + /* Check whether interrupts need to be re-enabled */ + if(Interrupts) + { + /* Re-enable interrupts */ + AR::CpuFunctions::SetInterruptFlag(); + } +} + +/** + * Sends an Inter-Processor Interrupt (IPI) to a specific set of target processors. + * + * @param Request + * Supplies the logical request operation flags to be merged into the target processors' * request summaries. + * + * @param TargetSet + * Supplies a pointer to the affinity map defining the processors to be interrupted. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +KE::Ipi::SendIpi(IN ULONG Request, + IN PKAFFINITY_MAP TargetSet) +{ + PKPROCESSOR_BLOCK ProcessorBlock; + ULONG BlockIndex, CpuIndex; + KAFFINITY AffinityBlock; + BOOLEAN Interrupts; + + /* Check whether interrupts are enabled */ + Interrupts = AR::CpuFunctions::InterruptsEnabled(); + + /* Disable interrupts */ + AR::CpuFunctions::ClearInterruptFlag(); + + /* Iterate through all blocks within the target affinity map */ + for(BlockIndex = 0; BlockIndex < TargetSet->Size; BlockIndex++) + { + /* Retrieve the current affinity block */ + AffinityBlock = TargetSet->Bitmap[BlockIndex]; + + /* Loop until all set bits in the current block have been processed */ + while(AffinityBlock != 0) + { + /* Find the index of the lowest set bit representing a target CPU */ + AR::CpuFunctions::ScanForwardBit(&CpuIndex, AffinityBlock); + + /* Retrieve the target processor block */ + ProcessorBlock = KE::Processor::GetProcessorBlock((BlockIndex * 64) + CpuIndex); + + /* Ensure the processor block exists */ + if(ProcessorBlock != NULLPTR) + { + /* Update the logical request summary and dispatch the physical IPI */ + RTL::Atomic::Or64((PLONG_PTR)&ProcessorBlock->Prcb.RequestSummary, Request); + HL::Pic::SendIpi(ProcessorBlock->HardwareId, APIC_VECTOR_IPI, APIC_DM_FIXED, + APIC_DSH_Destination, APIC_TGM_EDGE); + } + + /* Clear the processed bit to move on to the next CPU */ + AffinityBlock &= ~((KAFFINITY)1 << CpuIndex); + } + } + + /* Check whether interrupts need to be re-enabled */ + if(Interrupts) + { + /* Re-enable interrupts */ + AR::CpuFunctions::SetInterruptFlag(); + } +}