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