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