From 40b6b1dab3169b1a59473bed06de9a9588f24d5b Mon Sep 17 00:00:00 2001 From: Timo Kreuzer Date: Sun, 26 Nov 2023 17:32:27 +0200 Subject: [PATCH] [HAL] Implement IPI support functions --- hal/hal.spec | 4 +- hal/halx86/apic/apicp.h | 2 +- hal/halx86/apic/apicsmp.c | 177 +++++++++++++++++++++++++++++++++++-- hal/halx86/generic/up.c | 19 ++++ hal/halx86/include/smp.h | 38 ++++++++ hal/halx86/smp/ipi.c | 24 +++++ sdk/include/ndk/halfuncs.h | 17 ++++ 7 files changed, 270 insertions(+), 11 deletions(-) diff --git a/hal/hal.spec b/hal/hal.spec index 94bb141afee..2a3285e1c81 100644 --- a/hal/hal.spec +++ b/hal/hal.spec @@ -41,8 +41,8 @@ @ stdcall HalRequestIpi(long) @ fastcall HalRequestSoftwareInterrupt(long) @ stdcall HalReturnToFirmware(long) -;@ stdcall -arch=x86_64 HalSendNMI() -;@ stdcall -arch=x86_64 HalSendSoftwareInterrupt() +@ stdcall -arch=x86_64 HalSendNMI(int64) +@ stdcall -arch=x86_64 HalSendSoftwareInterrupt(int64 long) @ stdcall HalSetBusData(long long long ptr long) @ stdcall HalSetBusDataByOffset(long long long ptr long long) @ stdcall HalSetDisplayParameters(long long) diff --git a/hal/halx86/apic/apicp.h b/hal/halx86/apic/apicp.h index 245ab197f59..0a91da45992 100644 --- a/hal/halx86/apic/apicp.h +++ b/hal/halx86/apic/apicp.h @@ -151,7 +151,7 @@ typedef enum _APIC_DSH APIC_DSH_Destination, APIC_DSH_Self, APIC_DSH_AllIncludingSelf, - APIC_DSH_AllExclusingSelf + APIC_DSH_AllExcludingSelf } APIC_DSH; /* Write Constants */ diff --git a/hal/halx86/apic/apicsmp.c b/hal/halx86/apic/apicsmp.c index d8684742268..47415bc84b6 100644 --- a/hal/halx86/apic/apicsmp.c +++ b/hal/halx86/apic/apicsmp.c @@ -106,14 +106,6 @@ ApicRequestGlobalInterrupt( /* SMP SUPPORT FUNCTIONS ******************************************************/ -VOID -NTAPI -HalpRequestIpi(_In_ KAFFINITY TargetProcessors) -{ - UNIMPLEMENTED; - __debugbreak(); -} - VOID ApicStartApplicationProcessor( _In_ ULONG NTProcessorNumber, @@ -138,3 +130,172 @@ ApicStartApplicationProcessor( ApicRequestGlobalInterrupt(HalpProcessorIdentity[NTProcessorNumber].LapicId, (StartupLoc.LowPart) >> 12, APIC_MT_Startup, APIC_TGM_Edge, APIC_DSH_Destination); } + +/* HAL IPI FUNCTIONS **********************************************************/ + +/*! + * \brief Broadcasts an IPI with a specified vector to all processors. + * + * \param Vector - Specifies the interrupt vector to be delivered. + * \param IncludeSelf - Specifies whether to include the current processor. + */ +VOID +NTAPI +HalpBroadcastIpiSpecifyVector( + _In_ UCHAR Vector, + _In_ BOOLEAN IncludeSelf) +{ + APIC_DSH DestinationShortHand = IncludeSelf ? + APIC_DSH_AllIncludingSelf : APIC_DSH_AllExcludingSelf; + + /* Request the interrupt targeted at all processors */ + ApicRequestGlobalInterrupt(0, // Ignored + Vector, + APIC_MT_Fixed, + APIC_TGM_Edge, + DestinationShortHand); +} + +/*! + * \brief Requests an IPI with a specified vector on the specified processors. + * + * \param TargetSet - Specifies the set of processors to send the IPI to. + * \param Vector - Specifies the interrupt vector to be delivered. + * + * \remarks This function is exported on Windows 10. + */ +VOID +NTAPI +HalRequestIpiSpecifyVector( + _In_ KAFFINITY TargetSet, + _In_ UCHAR Vector) +{ + KAFFINITY ActiveProcessors = KeQueryActiveProcessors(); + KAFFINITY RemainingSet, SetMember; + ULONG ProcessorIndex; + ULONG LApicId; + + /* Sanitize the target set */ + TargetSet &= ActiveProcessors; + + /* Check if all processors are requested */ + if (TargetSet == ActiveProcessors) + { + /* Send an IPI to all processors, including this processor */ + HalpBroadcastIpiSpecifyVector(Vector, TRUE); + return; + } + + /* Check if all processors except the current one are requested */ + if (TargetSet == (ActiveProcessors & ~KeGetCurrentPrcb()->SetMember)) + { + /* Send an IPI to all processors, excluding this processor */ + HalpBroadcastIpiSpecifyVector(Vector, FALSE); + return; + } + + /* Loop while we have more processors */ + RemainingSet = TargetSet; + while (RemainingSet != 0) + { + NT_VERIFY(BitScanForwardAffinity(&ProcessorIndex, RemainingSet) != 0); + ASSERT(ProcessorIndex < KeNumberProcessors); + SetMember = AFFINITY_MASK(ProcessorIndex); + RemainingSet &= ~SetMember; + + /* Send the interrupt to the target processor */ + LApicId = HalpProcessorIdentity[ProcessorIndex].LapicId; + ApicRequestGlobalInterrupt(LApicId, + Vector, + APIC_MT_Fixed, + APIC_TGM_Edge, + APIC_DSH_Destination); + } +} + +/*! + * \brief Requests an IPI interrupt on the specified processors. + * + * \param TargetSet - Specifies the set of processors to send the IPI to. + */ +VOID +NTAPI +HalpRequestIpi( + _In_ KAFFINITY TargetSet) +{ + /* Request the IPI vector */ + HalRequestIpiSpecifyVector(TargetSet, APIC_IPI_VECTOR); +} + +#ifdef _M_AMD64 + +/*! + * \brief Requests a software interrupt on the specified processors. + * + * \param TargetSet - Specifies the set of processors to send the IPI to. + * \param Irql - Specifies the IRQL of the software interrupt. + */ +VOID +NTAPI +HalpSendSoftwareInterrupt( + _In_ KAFFINITY TargetSet, + _In_ KIRQL Irql) +{ + UCHAR Vector; + + /* Get the vector for the requested IRQL */ + if (Irql == APC_LEVEL) + { + Vector = APC_VECTOR; + } + else if (Irql == DISPATCH_LEVEL) + { + Vector = DISPATCH_VECTOR; + } + else + { + ASSERT(FALSE); + return; + } + + /* Request the IPI with the specified vector */ + HalRequestIpiSpecifyVector(TargetSet, Vector); +} + +/*! + * \brief Requests an NMI interrupt on the specified processors. + * + * \param TargetSet - Specifies the set of processors to send the IPI to. + */ +VOID +NTAPI +HalpSendNMI( + _In_ KAFFINITY TargetSet) +{ + KAFFINITY RemainingSet, SetMember; + ULONG ProcessorIndex; + ULONG LApicId; + + /* Make sure we do not send an NMI to ourselves */ + ASSERT((TargetSet & ~KeGetCurrentPrcb()->SetMember) == 0); + + /* Loop while we have more processors */ + RemainingSet = TargetSet; + while (RemainingSet != 0) + { + NT_VERIFY(BitScanForwardAffinity(&ProcessorIndex, RemainingSet) != 0); + ASSERT(ProcessorIndex < KeNumberProcessors); + SetMember = AFFINITY_MASK(ProcessorIndex); + RemainingSet &= ~SetMember; + + /* Send and NMI to the target processor */ + LApicId = HalpProcessorIdentity[ProcessorIndex].LapicId; + ApicRequestGlobalInterrupt(LApicId, + 0, + APIC_MT_NMI, + APIC_TGM_Edge, + APIC_DSH_Destination); + } +} + +#endif // _M_AMD64 diff --git a/hal/halx86/generic/up.c b/hal/halx86/generic/up.c index a7162774d89..dd60cc1cffb 100644 --- a/hal/halx86/generic/up.c +++ b/hal/halx86/generic/up.c @@ -32,3 +32,22 @@ HalStartNextProcessor( /* Always return false on UP systems */ return FALSE; } + +#ifdef _M_AMD64 + +VOID +NTAPI +HalSendNMI( + _In_ KAFFINITY TargetSet) +{ +} + +VOID +NTAPI +HalSendSoftwareInterrupt( + _In_ KAFFINITY TargetSet, + _In_ KIRQL Irql) +{ +} + +#endif // _M_AMD64 diff --git a/hal/halx86/include/smp.h b/hal/halx86/include/smp.h index 86b1ded5f77..d35189801c5 100644 --- a/hal/halx86/include/smp.h +++ b/hal/halx86/include/smp.h @@ -7,6 +7,15 @@ #pragma once +#define AFFINITY_MASK(Id) ((KAFFINITY)1 << (Id)) + +/* Helper to find the lowest CPU in a KAFFINITY */ +#ifdef _WIN64 +#define BitScanForwardAffinity BitScanForward64 +#else +#define BitScanForwardAffinity BitScanForward +#endif + /* This table is filled for each physical processor on system */ typedef struct _PROCESSOR_IDENTITY { @@ -53,3 +62,32 @@ VOID NTAPI HalpRequestIpi( _In_ KAFFINITY TargetProcessors); + +VOID +NTAPI +HalpBroadcastIpiSpecifyVector( + _In_ UCHAR Vector, + _In_ BOOLEAN IncludeSelf); + +VOID +NTAPI +HalRequestIpiSpecifyVector( + _In_ KAFFINITY TargetSet, + _In_ UCHAR Vector); + +#ifdef _M_AMD64 + +NTHALAPI +VOID +NTAPI +HalpSendNMI( + _In_ KAFFINITY TargetSet); + +NTHALAPI +VOID +NTAPI +HalpSendSoftwareInterrupt( + _In_ KAFFINITY TargetSet, + _In_ KIRQL Irql); + +#endif // _M_AMD64 diff --git a/hal/halx86/smp/ipi.c b/hal/halx86/smp/ipi.c index b215508c622..a2411961dae 100644 --- a/hal/halx86/smp/ipi.c +++ b/hal/halx86/smp/ipi.c @@ -22,3 +22,27 @@ HalRequestIpi( { HalpRequestIpi(TargetProcessors); } + +#ifdef _M_AMD64 + +VOID +NTAPI +HalSendNMI( + _In_ KAFFINITY TargetSet) +{ + HalpSendNMI(TargetSet); +} + +// See: +// - https://www.virtualbox.org/browser/vbox/trunk/src/VBox/Runtime/r0drv/nt/internal-r0drv-nt.h#L53 +// https://github.com/mirror/vbox/blob/b9657cd5351cf17432b664009cc25bb480dc64c1/src/VBox/Runtime/r0drv/nt/mp-r0drv-nt.cpp#L683 +VOID +NTAPI +HalSendSoftwareInterrupt( + _In_ KAFFINITY TargetSet, + _In_ KIRQL Irql) +{ + HalpSendSoftwareInterrupt(TargetSet, Irql); +} + +#endif // _M_AMD64 diff --git a/sdk/include/ndk/halfuncs.h b/sdk/include/ndk/halfuncs.h index 2c4b56075ad..d5bb1aae56d 100644 --- a/sdk/include/ndk/halfuncs.h +++ b/sdk/include/ndk/halfuncs.h @@ -192,6 +192,23 @@ HalRequestSoftwareInterrupt( _In_ KIRQL SoftwareInterruptRequested ); +#ifdef _M_AMD64 + +NTHALAPI +VOID +NTAPI +HalSendNMI( + _In_ KAFFINITY TargetSet); + +NTHALAPI +VOID +NTAPI +HalSendSoftwareInterrupt( + _In_ KAFFINITY TargetSet, + _In_ KIRQL Irql); + +#endif // _M_AMD64 + NTHALAPI VOID NTAPI