1 /** @file
2   IA32/x64 generic functions to support Debug Support protocol.
3 
4 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 #include "DebugSupport.h"
10 
11 //
12 // This the global main table to keep track of the interrupts
13 //
14 IDT_ENTRY                 *IdtEntryTable  = NULL;
15 
16 /**
17   Read IDT Gate Descriptor from IDT Table.
18 
19   @param  Vector            Specifies vector number.
20   @param  IdtGateDescriptor Pointer to IDT Gate Descriptor read from IDT Table.
21 
22 **/
23 VOID
ReadIdtGateDescriptor(IN EFI_EXCEPTION_TYPE Vector,OUT IA32_IDT_GATE_DESCRIPTOR * IdtGateDescriptor)24 ReadIdtGateDescriptor (
25   IN  EFI_EXCEPTION_TYPE        Vector,
26   OUT IA32_IDT_GATE_DESCRIPTOR  *IdtGateDescriptor
27   )
28 {
29  IA32_DESCRIPTOR            IdtrValue;
30  IA32_IDT_GATE_DESCRIPTOR   *IdtTable;
31 
32  AsmReadIdtr (&IdtrValue);
33  IdtTable = (IA32_IDT_GATE_DESCRIPTOR *) IdtrValue.Base;
34 
35  CopyMem ((VOID *) IdtGateDescriptor, (VOID *) &(IdtTable)[Vector], sizeof (IA32_IDT_GATE_DESCRIPTOR));
36 }
37 
38 /**
39   Write IDT Gate Descriptor into IDT Table.
40 
41   @param  Vector            Specifies vector number.
42   @param  IdtGateDescriptor Pointer to IDT Gate Descriptor written into IDT Table.
43 
44 **/
45 VOID
WriteIdtGateDescriptor(EFI_EXCEPTION_TYPE Vector,IA32_IDT_GATE_DESCRIPTOR * IdtGateDescriptor)46 WriteIdtGateDescriptor (
47   EFI_EXCEPTION_TYPE        Vector,
48   IA32_IDT_GATE_DESCRIPTOR  *IdtGateDescriptor
49   )
50 {
51  IA32_DESCRIPTOR            IdtrValue;
52  IA32_IDT_GATE_DESCRIPTOR   *IdtTable;
53 
54  AsmReadIdtr (&IdtrValue);
55  IdtTable = (IA32_IDT_GATE_DESCRIPTOR *) IdtrValue.Base;
56 
57  CopyMem ((VOID *) &(IdtTable)[Vector], (VOID *) IdtGateDescriptor, sizeof (IA32_IDT_GATE_DESCRIPTOR));
58 }
59 
60 /**
61   Creates a nes entry stub.  Then saves the current IDT entry and replaces it
62   with an interrupt gate for the new entry point.  The IdtEntryTable is updated
63   with the new registered function.
64 
65   This code executes in boot services context.  The stub entry executes in interrupt
66   context.
67 
68   @param  ExceptionType      Specifies which vector to hook.
69   @param  NewCallback        A pointer to the new function to be registered.
70 
71 **/
72 VOID
HookEntry(IN EFI_EXCEPTION_TYPE ExceptionType,IN CALLBACK_FUNC NewCallback)73 HookEntry (
74   IN EFI_EXCEPTION_TYPE            ExceptionType,
75   IN CALLBACK_FUNC                 NewCallback
76   )
77 {
78   BOOLEAN     OldIntFlagState;
79 
80   CreateEntryStub (ExceptionType, (VOID **) &IdtEntryTable[ExceptionType].StubEntry);
81 
82   //
83   // Disables CPU interrupts and returns the previous interrupt state
84   //
85   OldIntFlagState = SaveAndDisableInterrupts ();
86 
87   //
88   // gets IDT Gate descriptor by index
89   //
90   ReadIdtGateDescriptor (ExceptionType, &(IdtEntryTable[ExceptionType].OrigDesc));
91   //
92   // stores orignal interrupt handle
93   //
94   IdtEntryTable[ExceptionType].OrigVector = (DEBUG_PROC) GetInterruptHandleFromIdt (&(IdtEntryTable[ExceptionType].OrigDesc));
95 
96   //
97   // encodes new IDT Gate descriptor by stub entry
98   //
99   Vect2Desc (&IdtEntryTable[ExceptionType].NewDesc, IdtEntryTable[ExceptionType].StubEntry);
100   //
101   // stores NewCallback
102   //
103   IdtEntryTable[ExceptionType].RegisteredCallback = NewCallback;
104 
105   //
106   // writes back new IDT Gate descriptor
107   //
108   WriteIdtGateDescriptor (ExceptionType, &(IdtEntryTable[ExceptionType].NewDesc));
109 
110   //
111   // restore interrupt state
112   //
113   SetInterruptState (OldIntFlagState);
114 
115   return ;
116 }
117 
118 /**
119   Undoes HookEntry. This code executes in boot services context.
120 
121   @param  ExceptionType   Specifies which entry to unhook
122 
123 **/
124 VOID
UnhookEntry(IN EFI_EXCEPTION_TYPE ExceptionType)125 UnhookEntry (
126   IN EFI_EXCEPTION_TYPE           ExceptionType
127   )
128 {
129   BOOLEAN     OldIntFlagState;
130 
131   //
132   // Disables CPU interrupts and returns the previous interrupt state
133   //
134   OldIntFlagState = SaveAndDisableInterrupts ();
135 
136   //
137   // restore the default IDT Date Descriptor
138   //
139   WriteIdtGateDescriptor (ExceptionType, &(IdtEntryTable[ExceptionType].OrigDesc));
140 
141   //
142   // restore interrupt state
143   //
144   SetInterruptState (OldIntFlagState);
145 
146   return ;
147 }
148 
149 /**
150   Returns the maximum value that may be used for the ProcessorIndex parameter in
151   RegisterPeriodicCallback() and RegisterExceptionCallback().
152 
153   Hard coded to support only 1 processor for now.
154 
155   @param  This                  A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance.
156   @param  MaxProcessorIndex     Pointer to a caller-allocated UINTN in which the maximum supported
157                                 processor index is returned. Always 0 returned.
158 
159   @retval EFI_SUCCESS           Always returned with **MaxProcessorIndex set to 0.
160 
161 **/
162 EFI_STATUS
163 EFIAPI
GetMaximumProcessorIndex(IN EFI_DEBUG_SUPPORT_PROTOCOL * This,OUT UINTN * MaxProcessorIndex)164 GetMaximumProcessorIndex (
165   IN EFI_DEBUG_SUPPORT_PROTOCOL       *This,
166   OUT UINTN                           *MaxProcessorIndex
167   )
168 {
169   *MaxProcessorIndex = 0;
170   return EFI_SUCCESS;
171 }
172 
173 /**
174   Registers a function to be called back periodically in interrupt context.
175 
176   @param  This                  A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance.
177   @param  ProcessorIndex        Specifies which processor the callback function applies to.
178   @param  PeriodicCallback      A pointer to a function of type PERIODIC_CALLBACK that is the main
179                                 periodic entry point of the debug agent.
180 
181   @retval EFI_SUCCESS           The function completed successfully.
182   @retval EFI_ALREADY_STARTED   Non-NULL PeriodicCallback parameter when a callback
183                                 function was previously registered.
184   @retval EFI_OUT_OF_RESOURCES  System has insufficient memory resources to register new callback
185                                 function.
186 **/
187 EFI_STATUS
188 EFIAPI
RegisterPeriodicCallback(IN EFI_DEBUG_SUPPORT_PROTOCOL * This,IN UINTN ProcessorIndex,IN EFI_PERIODIC_CALLBACK PeriodicCallback)189 RegisterPeriodicCallback (
190   IN EFI_DEBUG_SUPPORT_PROTOCOL *This,
191   IN UINTN                      ProcessorIndex,
192   IN EFI_PERIODIC_CALLBACK      PeriodicCallback
193   )
194 {
195   return ManageIdtEntryTable (PeriodicCallback, SYSTEM_TIMER_VECTOR);
196 }
197 
198 /**
199   Registers a function to be called when a given processor exception occurs.
200 
201   This code executes in boot services context.
202 
203   @param  This                  A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance.
204   @param  ProcessorIndex        Specifies which processor the callback function applies to.
205   @param  ExceptionCallback     A pointer to a function of type EXCEPTION_CALLBACK that is called
206                                 when the processor exception specified by ExceptionType occurs.
207   @param  ExceptionType         Specifies which processor exception to hook.
208 
209   @retval EFI_SUCCESS           The function completed successfully.
210   @retval EFI_ALREADY_STARTED   Non-NULL PeriodicCallback parameter when a callback
211                                 function was previously registered.
212   @retval EFI_OUT_OF_RESOURCES  System has insufficient memory resources to register new callback
213                                 function.
214 **/
215 EFI_STATUS
216 EFIAPI
RegisterExceptionCallback(IN EFI_DEBUG_SUPPORT_PROTOCOL * This,IN UINTN ProcessorIndex,IN EFI_EXCEPTION_CALLBACK ExceptionCallback,IN EFI_EXCEPTION_TYPE ExceptionType)217 RegisterExceptionCallback (
218   IN EFI_DEBUG_SUPPORT_PROTOCOL *This,
219   IN UINTN                      ProcessorIndex,
220   IN EFI_EXCEPTION_CALLBACK     ExceptionCallback,
221   IN EFI_EXCEPTION_TYPE         ExceptionType
222   )
223 {
224   return ManageIdtEntryTable (ExceptionCallback, ExceptionType);
225 }
226 
227 
228 /**
229   Invalidates processor instruction cache for a memory range. Subsequent execution in this range
230   causes a fresh memory fetch to retrieve code to be executed.
231 
232   @param  This                  A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance.
233   @param  ProcessorIndex        Specifies which processor's instruction cache is to be invalidated.
234   @param  Start                 Specifies the physical base of the memory range to be invalidated.
235   @param  Length                Specifies the minimum number of bytes in the processor's instruction
236                                 cache to invalidate.
237 
238   @retval EFI_SUCCESS           Always returned.
239 
240 **/
241 EFI_STATUS
242 EFIAPI
InvalidateInstructionCache(IN EFI_DEBUG_SUPPORT_PROTOCOL * This,IN UINTN ProcessorIndex,IN VOID * Start,IN UINT64 Length)243 InvalidateInstructionCache (
244   IN EFI_DEBUG_SUPPORT_PROTOCOL       *This,
245   IN UINTN                            ProcessorIndex,
246   IN VOID                             *Start,
247   IN UINT64                           Length
248   )
249 {
250   AsmWbinvd ();
251   return EFI_SUCCESS;
252 }
253 
254 /**
255   Common piece of code that invokes the registered handlers.
256 
257   This code executes in exception context so no efi calls are allowed.
258   This code is called from assembly file.
259 
260   @param  ExceptionType     Exception type
261   @param  ContextRecord     System context
262 
263 **/
264 VOID
InterruptDistrubutionHub(EFI_EXCEPTION_TYPE ExceptionType,EFI_SYSTEM_CONTEXT_IA32 * ContextRecord)265 InterruptDistrubutionHub (
266   EFI_EXCEPTION_TYPE      ExceptionType,
267   EFI_SYSTEM_CONTEXT_IA32 *ContextRecord
268   )
269 {
270   if (IdtEntryTable[ExceptionType].RegisteredCallback != NULL) {
271     if (ExceptionType != SYSTEM_TIMER_VECTOR) {
272       IdtEntryTable[ExceptionType].RegisteredCallback (ExceptionType, ContextRecord);
273     } else {
274       OrigVector = IdtEntryTable[ExceptionType].OrigVector;
275       IdtEntryTable[ExceptionType].RegisteredCallback (ContextRecord);
276     }
277   }
278 }
279 
280 /**
281   This is the callback that is written to the Loaded Image protocol instance
282   on the image handle. It uninstalls all registered handlers and frees all entry
283   stub memory.
284 
285   @param  ImageHandle    The firmware allocated handle for the EFI image.
286 
287   @retval EFI_SUCCESS    Always.
288 
289 **/
290 EFI_STATUS
291 EFIAPI
PlUnloadDebugSupportDriver(IN EFI_HANDLE ImageHandle)292 PlUnloadDebugSupportDriver (
293   IN EFI_HANDLE ImageHandle
294   )
295 {
296   EFI_EXCEPTION_TYPE  ExceptionType;
297 
298   for (ExceptionType = 0; ExceptionType < NUM_IDT_ENTRIES; ExceptionType++) {
299     ManageIdtEntryTable (NULL, ExceptionType);
300     //
301     // Free space for each Interrupt Stub precedure.
302     //
303     if (IdtEntryTable[ExceptionType].StubEntry != NULL) {
304       FreePool ((VOID *)(UINTN)IdtEntryTable[ExceptionType].StubEntry);
305     }
306   }
307 
308   FreePool (IdtEntryTable);
309 
310   return EFI_SUCCESS;
311 }
312 
313 /**
314   Initializes driver's handler registration database.
315 
316   This code executes in boot services context.
317   Must be public because it's referenced from DebugSupport.c
318 
319   @retval  EFI_UNSUPPORTED      If IA32/x64 processor does not support FXSTOR/FXRSTOR instructions,
320                                 the context save will fail, so these processors are not supported.
321   @retval  EFI_OUT_OF_RESOURCES Fails to allocate memory.
322   @retval  EFI_SUCCESS          Initializes successfully.
323 
324 **/
325 EFI_STATUS
PlInitializeDebugSupportDriver(VOID)326 PlInitializeDebugSupportDriver (
327   VOID
328   )
329 {
330   EFI_EXCEPTION_TYPE  ExceptionType;
331 
332   //
333   // Check whether FxStor instructions are supported.
334   //
335   if (!FxStorSupport ()) {
336     return EFI_UNSUPPORTED;
337   }
338 
339   IdtEntryTable = AllocateZeroPool (sizeof (IDT_ENTRY) * NUM_IDT_ENTRIES);
340   if (IdtEntryTable == NULL) {
341     return EFI_OUT_OF_RESOURCES;
342   }
343 
344   for (ExceptionType = 0; ExceptionType < NUM_IDT_ENTRIES; ExceptionType ++) {
345     IdtEntryTable[ExceptionType].StubEntry = (DEBUG_PROC) (UINTN) AllocatePool (StubSize);
346     if (IdtEntryTable[ExceptionType].StubEntry == NULL) {
347       goto ErrorCleanup;
348     }
349 
350     //
351     // Copy Interrupt stub code.
352     //
353     CopyMem ((VOID *)(UINTN)IdtEntryTable[ExceptionType].StubEntry, InterruptEntryStub, StubSize);
354   }
355   return EFI_SUCCESS;
356 
357 ErrorCleanup:
358 
359   for (ExceptionType = 0; ExceptionType < NUM_IDT_ENTRIES; ExceptionType++) {
360     if (IdtEntryTable[ExceptionType].StubEntry != NULL) {
361       FreePool ((VOID *)(UINTN)IdtEntryTable[ExceptionType].StubEntry);
362     }
363   }
364   FreePool (IdtEntryTable);
365 
366   return EFI_OUT_OF_RESOURCES;
367 }
368