1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Processor freeze support for x64 5 * COPYRIGHT: Copyright 2023 Timo Kreuzer <timo.kreuzer@reactos.org> 6 */ 7 8 /* INCLUDES *******************************************************************/ 9 10 #include <ntoskrnl.h> 11 #define NDEBUG 12 #include <debug.h> 13 14 /* NOT INCLUDES ANYMORE ******************************************************/ 15 16 PKPRCB KiFreezeOwner; 17 18 /* FUNCTIONS *****************************************************************/ 19 20 BOOLEAN 21 KiProcessorFreezeHandler( 22 _In_ PKTRAP_FRAME TrapFrame, 23 _In_ PKEXCEPTION_FRAME ExceptionFrame) 24 { 25 PKPRCB CurrentPrcb = KeGetCurrentPrcb(); 26 27 /* Make sure this is a freeze request */ 28 if (CurrentPrcb->IpiFrozen != IPI_FROZEN_STATE_TARGET_FREEZE) 29 { 30 /* Not a freeze request, return FALSE to signal it is unhandled */ 31 return FALSE; 32 } 33 34 /* We are frozen now */ 35 CurrentPrcb->IpiFrozen = IPI_FROZEN_STATE_FROZEN; 36 37 /* Save the processor state */ 38 KiSaveProcessorState(TrapFrame, ExceptionFrame); 39 40 /* Wait for the freeze owner to release us */ 41 while (CurrentPrcb->IpiFrozen != IPI_FROZEN_STATE_THAW) 42 { 43 /* Check for Kd processor switch */ 44 if (CurrentPrcb->IpiFrozen & IPI_FROZEN_FLAG_ACTIVE) 45 { 46 KCONTINUE_STATUS ContinueStatus; 47 48 /* Enter the debugger */ 49 ContinueStatus = KdReportProcessorChange(); 50 51 /* Set the state back to frozen */ 52 CurrentPrcb->IpiFrozen = IPI_FROZEN_STATE_FROZEN; 53 54 /* If the status is ContinueSuccess, we need to release the freeze owner */ 55 if (ContinueStatus == ContinueSuccess) 56 { 57 /* Release the freeze owner */ 58 KiFreezeOwner->IpiFrozen = IPI_FROZEN_STATE_THAW; 59 } 60 } 61 62 YieldProcessor(); 63 KeMemoryBarrier(); 64 } 65 66 /* Restore the processor state */ 67 KiRestoreProcessorState(TrapFrame, ExceptionFrame); 68 69 /* We are running again now */ 70 CurrentPrcb->IpiFrozen = IPI_FROZEN_STATE_RUNNING; 71 72 /* Return TRUE to signal that we handled the freeze */ 73 return TRUE; 74 } 75 76 VOID 77 NTAPI 78 KxFreezeExecution( 79 VOID) 80 { 81 PKPRCB CurrentPrcb = KeGetCurrentPrcb(); 82 83 /* Avoid blocking on recursive debug action */ 84 if (KiFreezeOwner == CurrentPrcb) 85 { 86 return; 87 } 88 89 /* Try to acquire the freeze owner */ 90 while (InterlockedCompareExchangePointer(&KiFreezeOwner, CurrentPrcb, NULL)) 91 { 92 /* Someone else was faster. We expect an NMI to freeze any time. 93 Spin here until the freeze owner is available. */ 94 while (KiFreezeOwner != NULL) 95 { 96 YieldProcessor(); 97 KeMemoryBarrier(); 98 } 99 } 100 101 /* We are the owner now */ 102 CurrentPrcb->IpiFrozen = IPI_FROZEN_STATE_OWNER; 103 104 /* Loop all processors */ 105 for (ULONG i = 0; i < KeNumberProcessors; i++) 106 { 107 PKPRCB TargetPrcb = KiProcessorBlock[i]; 108 if (TargetPrcb != CurrentPrcb) 109 { 110 /* Nobody else is allowed to change IpiFrozen, except the freeze owner */ 111 ASSERT(TargetPrcb->IpiFrozen == IPI_FROZEN_STATE_RUNNING); 112 113 /* Request target to freeze */ 114 TargetPrcb->IpiFrozen = IPI_FROZEN_STATE_TARGET_FREEZE; 115 } 116 } 117 118 /* Send the freeze IPI */ 119 KiIpiSend(KeActiveProcessors & ~CurrentPrcb->SetMember, IPI_FREEZE); 120 121 /* Wait for all targets to be frozen */ 122 for (ULONG i = 0; i < KeNumberProcessors; i++) 123 { 124 PKPRCB TargetPrcb = KiProcessorBlock[i]; 125 if (TargetPrcb != CurrentPrcb) 126 { 127 /* Wait for the target to be frozen */ 128 while (TargetPrcb->IpiFrozen != IPI_FROZEN_STATE_FROZEN) 129 { 130 YieldProcessor(); 131 KeMemoryBarrier(); 132 } 133 } 134 } 135 136 /* All targets are frozen, we can continue */ 137 } 138 139 VOID 140 NTAPI 141 KxThawExecution( 142 VOID) 143 { 144 PKPRCB CurrentPrcb = KeGetCurrentPrcb(); 145 146 /* Loop all processors */ 147 for (ULONG i = 0; i < KeNumberProcessors; i++) 148 { 149 PKPRCB TargetPrcb = KiProcessorBlock[i]; 150 if (TargetPrcb != CurrentPrcb) 151 { 152 /* Make sure they are still frozen */ 153 ASSERT(TargetPrcb->IpiFrozen == IPI_FROZEN_STATE_FROZEN); 154 155 /* Request target to thaw */ 156 TargetPrcb->IpiFrozen = IPI_FROZEN_STATE_THAW; 157 } 158 } 159 160 /* Wait for all targets to be running */ 161 for (ULONG i = 0; i < KeNumberProcessors; i++) 162 { 163 PKPRCB TargetPrcb = KiProcessorBlock[i]; 164 if (TargetPrcb != CurrentPrcb) 165 { 166 /* Wait for the target to be running again */ 167 while (TargetPrcb->IpiFrozen != IPI_FROZEN_STATE_RUNNING) 168 { 169 YieldProcessor(); 170 KeMemoryBarrier(); 171 } 172 } 173 } 174 175 /* We are running again now */ 176 CurrentPrcb->IpiFrozen = IPI_FROZEN_STATE_RUNNING; 177 178 /* Release the freeze owner */ 179 InterlockedExchangePointer(&KiFreezeOwner, NULL); 180 } 181 182 KCONTINUE_STATUS 183 NTAPI 184 KxSwitchKdProcessor( 185 _In_ ULONG ProcessorIndex) 186 { 187 PKPRCB CurrentPrcb = KeGetCurrentPrcb(); 188 PKPRCB TargetPrcb; 189 190 /* Make sure that the processor index is valid */ 191 ASSERT(ProcessorIndex < KeNumberProcessors); 192 193 /* Inform the target processor that it's his turn now */ 194 TargetPrcb = KiProcessorBlock[ProcessorIndex]; 195 TargetPrcb->IpiFrozen |= IPI_FROZEN_FLAG_ACTIVE; 196 197 /* If we are not the freeze owner, we return back to the freeze loop */ 198 if (KiFreezeOwner != CurrentPrcb) 199 { 200 return ContinueNextProcessor; 201 } 202 203 /* Loop until it's our turn again */ 204 while (CurrentPrcb->IpiFrozen == IPI_FROZEN_STATE_OWNER) 205 { 206 YieldProcessor(); 207 KeMemoryBarrier(); 208 } 209 210 /* Check if we have been thawed */ 211 if (CurrentPrcb->IpiFrozen == IPI_FROZEN_STATE_THAW) 212 { 213 /* Another CPU has completed, we can leave the debugger now */ 214 KdpDprintf("[%u] KxSwitchKdProcessor: ContinueSuccess\n", KeGetCurrentProcessorNumber()); 215 CurrentPrcb->IpiFrozen = IPI_FROZEN_STATE_OWNER; 216 return ContinueSuccess; 217 } 218 219 /* We have been reselected, return to Kd to continue in the debugger */ 220 CurrentPrcb->IpiFrozen = IPI_FROZEN_STATE_OWNER; 221 222 return ContinueProcessorReselected; 223 } 224