1 /** @file
2   IPF specific 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 "PlDebugSupport.h"
16 
17 BOOLEAN  mInHandler = FALSE;
18 
19 //
20 // number of bundles to swap in ivt
21 //
22 #define NUM_BUNDLES_IN_STUB 5
23 #define NUM_IVT_ENTRIES     64
24 
25 typedef struct {
26   BUNDLE         OrigBundles[NUM_BUNDLES_IN_STUB];
27   CALLBACK_FUNC  RegisteredCallback;
28 } IVT_ENTRY;
29 
30 IVT_ENTRY IvtEntryTable[NUM_IVT_ENTRIES];
31 
32 //
33 // IPF context record is overallocated by 512 bytes to guarantee a 512 byte alignment exists
34 // within the buffer and still have a large enough buffer to hold a whole IPF context record.
35 //
36 UINT8     IpfContextBuf[sizeof (EFI_SYSTEM_CONTEXT_IPF) + 512];
37 
38 //
39 // The PatchSaveBuffer is used to store the original bundles from the IVT where it is patched
40 // with the common handler.
41 //
42 UINT8     PatchSaveBuffer[0x400];
43 UINTN     ExternalInterruptCount;
44 
45 
46 /**
47   IPF specific DebugSupport driver initialization.
48 
49   Must be public because it's referenced from DebugSupport.c
50 
51   @retval  EFI_SUCCESS     Always.
52 
53 **/
54 EFI_STATUS
PlInitializeDebugSupportDriver(VOID)55 PlInitializeDebugSupportDriver (
56   VOID
57   )
58 {
59   ZeroMem (IvtEntryTable, sizeof (IvtEntryTable));
60   ExternalInterruptCount = 0;
61   return EFI_SUCCESS;
62 }
63 
64 /**
65   Unload handler that is called during UnloadImage() - deallocates pool memory
66   used by the driver.
67 
68   Must be public because it's referenced from DebugSuport.c
69 
70   @param  ImageHandle    The firmware allocated handle for the EFI image.
71 
72   @retval EFI_SUCCESS    Always.
73 
74 **/
75 EFI_STATUS
76 EFIAPI
PlUnloadDebugSupportDriver(IN EFI_HANDLE ImageHandle)77 PlUnloadDebugSupportDriver (
78   IN EFI_HANDLE       ImageHandle
79   )
80 {
81   EFI_EXCEPTION_TYPE  ExceptionType;
82 
83   for (ExceptionType = 0; ExceptionType < NUM_IVT_ENTRIES; ExceptionType++) {
84     ManageIvtEntryTable (ExceptionType, NULL, NULL);
85   }
86 
87   return EFI_SUCCESS;
88 }
89 
90 /**
91   C routine that is called for all registered exceptions.  This is the main
92   exception dispatcher.
93 
94   Must be public because it's referenced from AsmFuncs.s.
95 
96   @param  ExceptionType        Specifies which processor exception.
97   @param  Context              System Context.
98 **/
99 VOID
CommonHandler(IN EFI_EXCEPTION_TYPE ExceptionType,IN EFI_SYSTEM_CONTEXT Context)100 CommonHandler (
101   IN EFI_EXCEPTION_TYPE ExceptionType,
102   IN EFI_SYSTEM_CONTEXT Context
103   )
104 {
105   DEBUG_CODE_BEGIN ();
106     if (mInHandler) {
107       DEBUG ((EFI_D_INFO, "ERROR: Re-entered debugger!\n"
108                                     "       ExceptionType == %X\n"
109                                     "       Context       == %X\n"
110                                     "       Context.SystemContextIpf->CrIip  == %LX\n"
111                                     "       Context.SystemContextIpf->CrIpsr == %LX\n"
112                                     "       mInHandler     == %X\n",
113                                     (INT32)ExceptionType,
114                                     Context,
115                                     Context.SystemContextIpf->CrIip,
116                                     Context.SystemContextIpf->CrIpsr,
117                                     mInHandler));
118     }
119   DEBUG_CODE_END ();
120 
121   ASSERT (!mInHandler);
122   mInHandler = TRUE;
123   if (IvtEntryTable[ExceptionType].RegisteredCallback != NULL) {
124     if (ExceptionType != EXCEPT_IPF_EXTERNAL_INTERRUPT) {
125       IvtEntryTable[ExceptionType].RegisteredCallback (ExceptionType, Context.SystemContextIpf);
126     } else {
127       IvtEntryTable[ExceptionType].RegisteredCallback (Context.SystemContextIpf);
128     }
129   } else {
130     ASSERT (0);
131   }
132 
133   mInHandler = FALSE;
134 }
135 
136 /**
137   Given an integer number, return the physical address of the entry point in the IFT.
138 
139   @param  HandlerIndex       Index of the Handler
140   @param  EntryPoint         IFT Entrypoint
141 
142 **/
143 VOID
GetHandlerEntryPoint(UINTN HandlerIndex,VOID ** EntryPoint)144 GetHandlerEntryPoint (
145   UINTN   HandlerIndex,
146   VOID    **EntryPoint
147   )
148 {
149   UINT8 *TempPtr;
150 
151   //
152   // get base address of IVT
153   //
154   TempPtr = GetIva ();
155 
156   if (HandlerIndex < 20) {
157     //
158     // first 20 provide 64 bundles per vector
159     //
160     TempPtr += 0x400 * HandlerIndex;
161   } else {
162     //
163     // the rest provide 16 bundles per vector
164     //
165     TempPtr += 0x5000 + 0x100 * (HandlerIndex - 20);
166   }
167 
168   *EntryPoint = (VOID *) TempPtr;
169 }
170 
171 /**
172   This is the worker function that uninstalls and removes all handlers.
173 
174   @param  ExceptionType     Specifies which processor exception.
175   @param  NewBundles        New Boundles.
176   @param  NewCallback       A pointer to the new function to be registered.
177 
178   @retval EFI_ALEADY_STARTED Ivt already hooked.
179   @retval EFI_SUCCESS        Successfully uninstalled.
180 
181 **/
182 EFI_STATUS
ManageIvtEntryTable(IN EFI_EXCEPTION_TYPE ExceptionType,IN BUNDLE NewBundles[NUM_BUNDLES_IN_STUB],IN CALLBACK_FUNC NewCallback)183 ManageIvtEntryTable (
184   IN  EFI_EXCEPTION_TYPE           ExceptionType,
185   IN  BUNDLE                       NewBundles[NUM_BUNDLES_IN_STUB],
186   IN  CALLBACK_FUNC                NewCallback
187   )
188 {
189   BUNDLE  *B0Ptr;
190   UINT64  InterruptFlags;
191   EFI_TPL OldTpl;
192 
193   //
194   // Get address of bundle 0
195   //
196   GetHandlerEntryPoint (ExceptionType, (VOID **) &B0Ptr);
197 
198   if (IvtEntryTable[ExceptionType].RegisteredCallback != NULL) {
199     //
200     // we've already installed to this vector
201     //
202     if (NewCallback != NULL) {
203       //
204       // if the input handler is non-null, error
205       //
206       return EFI_ALREADY_STARTED;
207     } else {
208       //
209       // else remove the previously installed handler
210       //
211       OldTpl          = gBS->RaiseTPL (TPL_HIGH_LEVEL);
212       InterruptFlags  = ProgramInterruptFlags (DISABLE_INTERRUPTS);
213       if (ExceptionType == EXCEPT_IPF_EXTERNAL_INTERRUPT) {
214         UnchainExternalInterrupt ();
215       } else {
216         UnhookEntry (ExceptionType);
217       }
218 
219       ProgramInterruptFlags (InterruptFlags);
220       gBS->RestoreTPL (OldTpl);
221       //
222       // re-init IvtEntryTable
223       //
224       ZeroMem (&IvtEntryTable[ExceptionType], sizeof (IVT_ENTRY));
225     }
226   } else {
227     //
228     // no user handler installed on this vector
229     //
230     if (NewCallback != NULL) {
231       OldTpl          = gBS->RaiseTPL (TPL_HIGH_LEVEL);
232       InterruptFlags  = ProgramInterruptFlags (DISABLE_INTERRUPTS);
233       if (ExceptionType == EXCEPT_IPF_EXTERNAL_INTERRUPT) {
234         ChainExternalInterrupt (NewCallback);
235       } else {
236         HookEntry (ExceptionType, NewBundles, NewCallback);
237       }
238 
239       ProgramInterruptFlags (InterruptFlags);
240       gBS->RestoreTPL (OldTpl);
241     }
242   }
243 
244   return EFI_SUCCESS;
245 }
246 
247 /**
248   Saves original IVT contents and inserts a few new bundles which are fixed up
249   to store the ExceptionType and then call the common handler.
250 
251   @param  ExceptionType      Specifies which processor exception.
252   @param  NewBundles         New Boundles.
253   @param  NewCallback        A pointer to the new function to be hooked.
254 
255 **/
256 VOID
HookEntry(IN EFI_EXCEPTION_TYPE ExceptionType,IN BUNDLE NewBundles[4],IN CALLBACK_FUNC NewCallback)257 HookEntry (
258   IN  EFI_EXCEPTION_TYPE  ExceptionType,
259   IN  BUNDLE              NewBundles[4],
260   IN  CALLBACK_FUNC       NewCallback
261   )
262 {
263   BUNDLE  *FixupBundle;
264   BUNDLE  *B0Ptr;
265 
266   //
267   // Get address of bundle 0
268   //
269   GetHandlerEntryPoint (ExceptionType, (VOID **) &B0Ptr);
270 
271   //
272   // copy original bundles from IVT to IvtEntryTable so we can restore them later
273   //
274   CopyMem (
275     IvtEntryTable[ExceptionType].OrigBundles,
276     B0Ptr,
277     sizeof (BUNDLE) * NUM_BUNDLES_IN_STUB
278     );
279   //
280   // insert new B0
281   //
282   CopyMem (B0Ptr, NewBundles, sizeof (BUNDLE) * NUM_BUNDLES_IN_STUB);
283 
284   //
285   // fixup IVT entry so it stores its index and whether or not to chain...
286   //
287   FixupBundle = B0Ptr + 2;
288   FixupBundle->High |= ExceptionType << 36;
289 
290   InstructionCacheFlush (B0Ptr, 5);
291   IvtEntryTable[ExceptionType].RegisteredCallback = NewCallback;
292 }
293 
294 /**
295   Restores original IVT contents when unregistering a callback function.
296 
297   @param  ExceptionType     Specifies which processor exception.
298 
299 **/
300 VOID
UnhookEntry(IN EFI_EXCEPTION_TYPE ExceptionType)301 UnhookEntry (
302   IN  EFI_EXCEPTION_TYPE  ExceptionType
303   )
304 {
305   BUNDLE  *B0Ptr;
306 
307   //
308   // Get address of bundle 0
309   //
310   GetHandlerEntryPoint (ExceptionType, (VOID **) &B0Ptr);
311   //
312   // restore original bundles in IVT
313   //
314   CopyMem (
315     B0Ptr,
316     IvtEntryTable[ExceptionType].OrigBundles,
317     sizeof (BUNDLE) * NUM_BUNDLES_IN_STUB
318     );
319   InstructionCacheFlush (B0Ptr, 5);
320 }
321 
322 /**
323   Sets up cache flush and calls assembly function to chain external interrupt.
324 
325   Records new callback in IvtEntryTable.
326 
327   @param  NewCallback     A pointer to the interrupt handle.
328 
329 **/
330 VOID
ChainExternalInterrupt(IN CALLBACK_FUNC NewCallback)331 ChainExternalInterrupt (
332   IN  CALLBACK_FUNC  NewCallback
333   )
334 {
335   VOID  *Start;
336 
337   Start = (VOID *) ((UINT8 *) GetIva () + 0x400 * EXCEPT_IPF_EXTERNAL_INTERRUPT + 0x400);
338   IvtEntryTable[EXCEPT_IPF_EXTERNAL_INTERRUPT].RegisteredCallback = NewCallback;
339   ChainHandler ();
340   InstructionCacheFlush (Start, 0x400);
341 }
342 
343 /**
344   Sets up cache flush and calls assembly function to restore external interrupt.
345   Removes registered callback from IvtEntryTable.
346 
347 **/
348 VOID
UnchainExternalInterrupt(VOID)349 UnchainExternalInterrupt (
350   VOID
351   )
352 {
353   VOID  *Start;
354 
355   Start = (VOID *) ((UINT8 *) GetIva () + 0x400 * EXCEPT_IPF_EXTERNAL_INTERRUPT + 0x400);
356   UnchainHandler ();
357   InstructionCacheFlush (Start, 0x400);
358   IvtEntryTable[EXCEPT_IPF_EXTERNAL_INTERRUPT].RegisteredCallback = NULL;
359 }
360 
361 /**
362   Returns the maximum value that may be used for the ProcessorIndex parameter in
363   RegisterPeriodicCallback() and RegisterExceptionCallback().
364 
365   Hard coded to support only 1 processor for now.
366 
367   @param  This                  A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance.
368   @param  MaxProcessorIndex     Pointer to a caller-allocated UINTN in which the maximum supported
369                                 processor index is returned. Always 0 returned.
370 
371   @retval EFI_SUCCESS           Always returned with **MaxProcessorIndex set to 0.
372 
373 **/
374 EFI_STATUS
375 EFIAPI
GetMaximumProcessorIndex(IN EFI_DEBUG_SUPPORT_PROTOCOL * This,OUT UINTN * MaxProcessorIndex)376 GetMaximumProcessorIndex (
377   IN EFI_DEBUG_SUPPORT_PROTOCOL    *This,
378   OUT UINTN                        *MaxProcessorIndex
379   )
380 {
381   *MaxProcessorIndex = 0;
382   return (EFI_SUCCESS);
383 }
384 
385 /**
386   Registers a function to be called back periodically in interrupt context.
387 
388   @param  This                  A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance.
389   @param  ProcessorIndex        Specifies which processor the callback function applies to.
390   @param  PeriodicCallback      A pointer to a function of type PERIODIC_CALLBACK that is the main
391                                 periodic entry point of the debug agent.
392 
393   @retval EFI_SUCCESS           The function completed successfully.
394   @retval EFI_ALREADY_STARTED   Non-NULL PeriodicCallback parameter when a callback
395                                 function was previously registered.
396   @retval EFI_OUT_OF_RESOURCES  System has insufficient memory resources to register new callback
397                                 function.
398 **/
399 EFI_STATUS
400 EFIAPI
RegisterPeriodicCallback(IN EFI_DEBUG_SUPPORT_PROTOCOL * This,IN UINTN ProcessorIndex,IN EFI_PERIODIC_CALLBACK PeriodicCallback)401 RegisterPeriodicCallback (
402   IN EFI_DEBUG_SUPPORT_PROTOCOL     *This,
403   IN UINTN                          ProcessorIndex,
404   IN EFI_PERIODIC_CALLBACK          PeriodicCallback
405   )
406 {
407   return ManageIvtEntryTable (EXCEPT_IPF_EXTERNAL_INTERRUPT, NULL, PeriodicCallback);
408 }
409 
410 /**
411   Registers a function to be called when a given processor exception occurs.
412 
413   This code executes in boot services context.
414 
415   @param  This                  A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance.
416   @param  ProcessorIndex        Specifies which processor the callback function applies to.
417   @param  ExceptionCallback     A pointer to a function of type EXCEPTION_CALLBACK that is called
418                                 when the processor exception specified by ExceptionType occurs.
419   @param  ExceptionType         Specifies which processor exception to hook.
420 
421   @retval EFI_SUCCESS           The function completed successfully.
422   @retval EFI_ALREADY_STARTED   Non-NULL PeriodicCallback parameter when a callback
423                                 function was previously registered.
424   @retval EFI_OUT_OF_RESOURCES  System has insufficient memory resources to register new callback
425                                 function.
426 **/
427 EFI_STATUS
428 EFIAPI
RegisterExceptionCallback(IN EFI_DEBUG_SUPPORT_PROTOCOL * This,IN UINTN ProcessorIndex,IN EFI_EXCEPTION_CALLBACK ExceptionCallback,IN EFI_EXCEPTION_TYPE ExceptionType)429 RegisterExceptionCallback (
430   IN EFI_DEBUG_SUPPORT_PROTOCOL    *This,
431   IN UINTN                         ProcessorIndex,
432   IN EFI_EXCEPTION_CALLBACK        ExceptionCallback,
433   IN EFI_EXCEPTION_TYPE            ExceptionType
434   )
435 {
436   return ManageIvtEntryTable (
437           ExceptionType,
438           (BUNDLE *) ((EFI_PLABEL *) HookStub)->EntryPoint,
439           ExceptionCallback
440           );
441 }
442 
443 /**
444   Invalidates processor instruction cache for a memory range. Subsequent execution in this range
445   causes a fresh memory fetch to retrieve code to be executed.
446 
447   @param  This                  A pointer to the EFI_DEBUG_SUPPORT_PROTOCOL instance.
448   @param  ProcessorIndex        Specifies which processor's instruction cache is to be invalidated.
449   @param  Start                 Specifies the physical base of the memory range to be invalidated.
450   @param  Length                Specifies the minimum number of bytes in the processor's instruction
451                                 cache to invalidate.
452 
453   @retval EFI_SUCCESS           Always returned.
454 
455 **/
456 EFI_STATUS
457 EFIAPI
InvalidateInstructionCache(IN EFI_DEBUG_SUPPORT_PROTOCOL * This,IN UINTN ProcessorIndex,IN VOID * Start,IN UINTN Length)458 InvalidateInstructionCache (
459   IN EFI_DEBUG_SUPPORT_PROTOCOL    *This,
460   IN UINTN                         ProcessorIndex,
461   IN VOID                          *Start,
462   IN UINTN                         Length
463   )
464 {
465   InstructionCacheFlush (Start, Length);
466   return EFI_SUCCESS;
467 }
468