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 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 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 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 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 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 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 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