xref: /reactos/hal/halx86/apic/apicsmp.c (revision 230f7c7f)
1 /*
2  * PROJECT:     ReactOS HAL
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     SMP specific APIC code
5  * COPYRIGHT:   Copyright 2021 Timo Kreuzer <timo.kreuzer@reactos.org>
6  *              Copyright 2023 Justin Miller <justin.miller@reactos.org>
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include <hal.h>
12 #include "apicp.h"
13 #include <smp.h>
14 
15 #define NDEBUG
16 #include <debug.h>
17 
18 
19 extern PPROCESSOR_IDENTITY HalpProcessorIdentity;
20 
21 /* INTERNAL FUNCTIONS *********************************************************/
22 
23 /*!
24     \param Vector - Specifies the interrupt vector to be delivered.
25 
26     \param MessageType - Specifies the message type sent to the CPU core
27         interrupt handler. This can be one of the following values:
28         APIC_MT_Fixed - Delivers an interrupt to the target local APIC
29             specified in Destination field.
30         APIC_MT_LowestPriority - Delivers an interrupt to the local APIC
31             executing at the lowest priority of all local APICs.
32         APIC_MT_SMI - Delivers an SMI interrupt to target local APIC(s).
33         APIC_MT_RemoteRead - Delivers a read request to read an APIC register
34             in the target local APIC specified in Destination field.
35         APIC_MT_NMI - Delivers a non-maskable interrupt to the target local
36             APIC specified in the Destination field. Vector is ignored.
37         APIC_MT_INIT - Delivers an INIT request to the target local APIC(s)
38             specified in the Destination field. TriggerMode must be
39             APIC_TGM_Edge, Vector must be 0.
40         APIC_MT_Startup - Delivers a start-up request (SIPI) to the target
41             local APIC(s) specified in Destination field. Vector specifies
42             the startup address.
43         APIC_MT_ExtInt - Delivers an external interrupt to the target local
44             APIC specified in Destination field.
45 
46     \param TriggerMode - The trigger mode of the interrupt. Can be:
47         APIC_TGM_Edge - The interrupt is edge triggered.
48         APIC_TGM_Level - The interrupt is level triggered.
49 
50     \param DestinationShortHand - Specifies where to send the interrupt.
51         APIC_DSH_Destination
52         APIC_DSH_Self
53         APIC_DSH_AllIncludingSelf
54         APIC_DSH_AllExcludingSelf
55 
56     \see "AMD64 Architecture Programmer's Manual Volume 2 System Programming"
57         Chapter 16 "Advanced Programmable Interrupt Controller (APIC)"
58         16.5 "Interprocessor Interrupts (IPI)"
59 
60  */
61 FORCEINLINE
62 VOID
ApicRequestGlobalInterrupt(_In_ UCHAR DestinationProcessor,_In_ UCHAR Vector,_In_ APIC_MT MessageType,_In_ APIC_TGM TriggerMode,_In_ APIC_DSH DestinationShortHand)63 ApicRequestGlobalInterrupt(
64     _In_ UCHAR DestinationProcessor,
65     _In_ UCHAR Vector,
66     _In_ APIC_MT MessageType,
67     _In_ APIC_TGM TriggerMode,
68     _In_ APIC_DSH DestinationShortHand)
69 {
70     ULONG Flags;
71     APIC_INTERRUPT_COMMAND_REGISTER Icr;
72 
73     /* Disable interrupts so that we can change IRR without being interrupted */
74     Flags = __readeflags();
75     _disable();
76 
77     /* Wait for the APIC to be idle */
78     do
79     {
80         Icr.Long0 = ApicRead(APIC_ICR0);
81     } while (Icr.DeliveryStatus);
82 
83     /* Setup the command register */
84     Icr.LongLong = 0;
85     Icr.Vector = Vector;
86     Icr.MessageType = MessageType;
87     Icr.DestinationMode = APIC_DM_Physical;
88     Icr.DeliveryStatus = 0;
89     Icr.Level = 0;
90     Icr.TriggerMode = TriggerMode;
91     Icr.RemoteReadStatus = 0;
92     Icr.DestinationShortHand = DestinationShortHand;
93     Icr.Destination = DestinationProcessor;
94 
95     /* Write the low dword last to send the interrupt */
96     ApicWrite(APIC_ICR1, Icr.Long1);
97     ApicWrite(APIC_ICR0, Icr.Long0);
98 
99     /* Finally, restore the original interrupt state */
100     if (Flags & EFLAGS_INTERRUPT_MASK)
101     {
102         _enable();
103     }
104 }
105 
106 
107 /* SMP SUPPORT FUNCTIONS ******************************************************/
108 
109 VOID
ApicStartApplicationProcessor(_In_ ULONG NTProcessorNumber,_In_ PHYSICAL_ADDRESS StartupLoc)110 ApicStartApplicationProcessor(
111     _In_ ULONG NTProcessorNumber,
112     _In_ PHYSICAL_ADDRESS StartupLoc)
113 {
114     ASSERT(StartupLoc.HighPart == 0);
115     ASSERT((StartupLoc.QuadPart & 0xFFF) == 0);
116     ASSERT((StartupLoc.QuadPart & 0xFFF00FFF) == 0);
117 
118     /* Init IPI */
119     ApicRequestGlobalInterrupt(HalpProcessorIdentity[NTProcessorNumber].LapicId, 0,
120         APIC_MT_INIT, APIC_TGM_Edge, APIC_DSH_Destination);
121 
122     /* De-Assert Init IPI */
123     ApicRequestGlobalInterrupt(HalpProcessorIdentity[NTProcessorNumber].LapicId, 0,
124         APIC_MT_INIT, APIC_TGM_Level, APIC_DSH_Destination);
125 
126     /* Stall execution for a bit to give APIC time: MPS Spec - B.4 */
127     KeStallExecutionProcessor(200);
128 
129     /* Startup IPI */
130     ApicRequestGlobalInterrupt(HalpProcessorIdentity[NTProcessorNumber].LapicId, (StartupLoc.LowPart) >> 12,
131         APIC_MT_Startup, APIC_TGM_Edge, APIC_DSH_Destination);
132 }
133 
134 /* HAL IPI FUNCTIONS **********************************************************/
135 
136 /*!
137  *  \brief Broadcasts an IPI with a specified vector to all processors.
138  *
139  *  \param Vector - Specifies the interrupt vector to be delivered.
140  *  \param IncludeSelf - Specifies whether to include the current processor.
141  */
142 VOID
143 NTAPI
HalpBroadcastIpiSpecifyVector(_In_ UCHAR Vector,_In_ BOOLEAN IncludeSelf)144 HalpBroadcastIpiSpecifyVector(
145     _In_ UCHAR Vector,
146     _In_ BOOLEAN IncludeSelf)
147 {
148     APIC_DSH DestinationShortHand = IncludeSelf ?
149         APIC_DSH_AllIncludingSelf : APIC_DSH_AllExcludingSelf;
150 
151     /* Request the interrupt targeted at all processors */
152     ApicRequestGlobalInterrupt(0, // Ignored
153                                Vector,
154                                APIC_MT_Fixed,
155                                APIC_TGM_Edge,
156                                DestinationShortHand);
157 }
158 
159 /*!
160  *  \brief Requests an IPI with a specified vector on the specified processors.
161  *
162  *  \param TargetSet - Specifies the set of processors to send the IPI to.
163  *  \param Vector - Specifies the interrupt vector to be delivered.
164  *
165  *  \remarks This function is exported on Windows 10.
166  */
167 VOID
168 NTAPI
HalRequestIpiSpecifyVector(_In_ KAFFINITY TargetSet,_In_ UCHAR Vector)169 HalRequestIpiSpecifyVector(
170     _In_ KAFFINITY TargetSet,
171     _In_ UCHAR Vector)
172 {
173     KAFFINITY ActiveProcessors = HalpActiveProcessors;
174     KAFFINITY RemainingSet, SetMember;
175     ULONG ProcessorIndex;
176     ULONG LApicId;
177 
178     /* Sanitize the target set */
179     TargetSet &= ActiveProcessors;
180 
181     /* Check if all processors are requested */
182     if (TargetSet == ActiveProcessors)
183     {
184         /* Send an IPI to all processors, including this processor */
185         HalpBroadcastIpiSpecifyVector(Vector, TRUE);
186         return;
187     }
188 
189     /* Check if all processors except the current one are requested */
190     if (TargetSet == (ActiveProcessors & ~KeGetCurrentPrcb()->SetMember))
191     {
192         /* Send an IPI to all processors, excluding this processor */
193         HalpBroadcastIpiSpecifyVector(Vector, FALSE);
194         return;
195     }
196 
197     /* Loop while we have more processors */
198     RemainingSet = TargetSet;
199     while (RemainingSet != 0)
200     {
201         NT_VERIFY(BitScanForwardAffinity(&ProcessorIndex, RemainingSet) != 0);
202         ASSERT(ProcessorIndex < KeNumberProcessors);
203         SetMember = AFFINITY_MASK(ProcessorIndex);
204         RemainingSet &= ~SetMember;
205 
206         /* Send the interrupt to the target processor */
207         LApicId = HalpProcessorIdentity[ProcessorIndex].LapicId;
208         ApicRequestGlobalInterrupt(LApicId,
209                                    Vector,
210                                    APIC_MT_Fixed,
211                                    APIC_TGM_Edge,
212                                    APIC_DSH_Destination);
213     }
214 }
215 
216 /*!
217  *  \brief Requests an IPI interrupt on the specified processors.
218  *
219  *  \param TargetSet - Specifies the set of processors to send the IPI to.
220  */
221 VOID
222 NTAPI
HalpRequestIpi(_In_ KAFFINITY TargetSet)223 HalpRequestIpi(
224     _In_ KAFFINITY TargetSet)
225 {
226     /* Request the IPI vector */
227     HalRequestIpiSpecifyVector(TargetSet, APIC_IPI_VECTOR);
228 }
229 
230 #ifdef _M_AMD64
231 
232 /*!
233  *  \brief Requests a software interrupt on the specified processors.
234  *
235  *  \param TargetSet - Specifies the set of processors to send the IPI to.
236  *  \param Irql - Specifies the IRQL of the software interrupt.
237  */
238 VOID
239 NTAPI
HalpSendSoftwareInterrupt(_In_ KAFFINITY TargetSet,_In_ KIRQL Irql)240 HalpSendSoftwareInterrupt(
241     _In_ KAFFINITY TargetSet,
242     _In_ KIRQL Irql)
243 {
244     UCHAR Vector;
245 
246     /* Get the vector for the requested IRQL */
247     if (Irql == APC_LEVEL)
248     {
249         Vector = APC_VECTOR;
250     }
251     else if (Irql == DISPATCH_LEVEL)
252     {
253         Vector = DISPATCH_VECTOR;
254     }
255     else
256     {
257         ASSERT(FALSE);
258         return;
259     }
260 
261     /* Request the IPI with the specified vector */
262     HalRequestIpiSpecifyVector(TargetSet, Vector);
263 }
264 
265 /*!
266  *  \brief Requests an NMI interrupt on the specified processors.
267  *
268  *  \param TargetSet - Specifies the set of processors to send the IPI to.
269  */
270 VOID
271 NTAPI
HalpSendNMI(_In_ KAFFINITY TargetSet)272 HalpSendNMI(
273     _In_ KAFFINITY TargetSet)
274 {
275     KAFFINITY RemainingSet, SetMember;
276     ULONG ProcessorIndex;
277     ULONG LApicId;
278 
279     /* Make sure we do not send an NMI to ourselves */
280     ASSERT((TargetSet & KeGetCurrentPrcb()->SetMember) == 0);
281 
282     /* Loop while we have more processors */
283     RemainingSet = TargetSet;
284     while (RemainingSet != 0)
285     {
286         NT_VERIFY(BitScanForwardAffinity(&ProcessorIndex, RemainingSet) != 0);
287         ASSERT(ProcessorIndex < KeNumberProcessors);
288         SetMember = AFFINITY_MASK(ProcessorIndex);
289         RemainingSet &= ~SetMember;
290 
291         /* Send and NMI to the target processor */
292         LApicId = HalpProcessorIdentity[ProcessorIndex].LapicId;
293         ApicRequestGlobalInterrupt(LApicId,
294                                    0,
295                                    APIC_MT_NMI,
296                                    APIC_TGM_Edge,
297                                    APIC_DSH_Destination);
298     }
299 }
300 
301 #endif // _M_AMD64
302