xref: /reactos/ntoskrnl/ke/amd64/freeze.c (revision 0c2cdcae)
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