33233d68e526d254528f9d012cb7aea669cbc281
[mirror_edk2.git] / MdeModulePkg / Universal / DebugSupportDxe / Ipf / plDebugSupport.c
1 /**@file\r
2   IPF specific debug support functions\r
3 \r
4 Copyright (c) 2006, Intel Corporation                                                         \r
5 All rights reserved. This program and the accompanying materials                          \r
6 are licensed and made available under the terms and conditions of the BSD License         \r
7 which accompanies this distribution.  The full text of the license may be found at        \r
8 http://opensource.org/licenses/bsd-license.php                                            \r
9                                                                                           \r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     \r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             \r
12 \r
13 **/\r
14 \r
15 //\r
16 // private header files\r
17 //\r
18 #include "plDebugSupport.h"\r
19 \r
20 typedef struct {\r
21   UINT64  low;\r
22   UINT64  high;\r
23 } BUNDLE;\r
24 \r
25 //\r
26 // number of bundles to swap in ivt\r
27 //\r
28 #define NUM_BUNDLES_IN_STUB 5\r
29 #define NUM_IVT_ENTRIES     64\r
30 \r
31 typedef struct {\r
32   BUNDLE  OrigBundles[NUM_BUNDLES_IN_STUB];\r
33   VOID (*RegisteredCallback) ();\r
34 } IVT_ENTRY;\r
35 \r
36 STATIC\r
37 EFI_STATUS\r
38 ManageIvtEntryTable (\r
39   IN  EFI_EXCEPTION_TYPE                ExceptionType,\r
40   IN  BUNDLE                NewBundles[4],\r
41   IN  VOID                  (*NewCallback) ()\r
42   );\r
43 \r
44 STATIC\r
45 VOID\r
46 HookEntry (\r
47   IN  EFI_EXCEPTION_TYPE                ExceptionType,\r
48   IN  BUNDLE                NewBundles[4],\r
49   IN  VOID                  (*NewCallback) ()\r
50   );\r
51 \r
52 STATIC\r
53 VOID\r
54 UnhookEntry (\r
55   IN  EFI_EXCEPTION_TYPE    ExceptionType\r
56   );\r
57 \r
58 STATIC\r
59 VOID\r
60 ChainExternalInterrupt (\r
61   IN  VOID                  (*NewCallback) ()\r
62   );\r
63 \r
64 STATIC\r
65 VOID\r
66 UnchainExternalInterrupt (\r
67   VOID\r
68   );\r
69 \r
70 STATIC\r
71 VOID\r
72 GetHandlerEntryPoint (\r
73   UINTN                     HandlerIndex,\r
74   VOID                      **EntryPoint\r
75   );\r
76 \r
77 IVT_ENTRY IvtEntryTable[NUM_IVT_ENTRIES];\r
78 \r
79 //\r
80 // IPF context record is overallocated by 512 bytes to guarantee a 512 byte alignment exists\r
81 // within the buffer and still have a large enough buffer to hold a whole IPF context record.\r
82 //\r
83 UINT8     IpfContextBuf[sizeof (EFI_SYSTEM_CONTEXT_IPF) + 512];\r
84 \r
85 //\r
86 // The PatchSaveBuffer is used to store the original bundles from the IVT where it is patched\r
87 // with the common handler.\r
88 //\r
89 UINT8     PatchSaveBuffer[0x400];\r
90 UINTN     ExternalInterruptCount;\r
91 \r
92 EFI_STATUS\r
93 plInitializeDebugSupportDriver (\r
94   VOID\r
95   )\r
96 /*++\r
97 \r
98 Routine Description:\r
99   IPF specific DebugSupport driver initialization.  Must be public because it's\r
100   referenced from DebugSupport.c\r
101 \r
102 Arguments:\r
103 \r
104 Returns:\r
105 \r
106   EFI_SUCCESS\r
107 \r
108 --*/\r
109 {\r
110   SetMem (IvtEntryTable, sizeof (IvtEntryTable), 0);\r
111   ExternalInterruptCount = 0;\r
112   return EFI_SUCCESS;\r
113 }\r
114 \r
115 EFI_STATUS\r
116 EFIAPI\r
117 plUnloadDebugSupportDriver (\r
118   IN EFI_HANDLE       ImageHandle\r
119   )\r
120 /*++\r
121 \r
122 Routine Description:\r
123   Unload handler that is called during UnloadImage() - deallocates pool memory\r
124   used by the driver.  Must be public because it's referenced from DebugSuport.c\r
125 \r
126 Arguments:\r
127   ImageHandle - Image handle\r
128 \r
129 Returns:\r
130 \r
131   EFI_STATUS - anything other than EFI_SUCCESS indicates the callback was not registered.\r
132 \r
133 --*/\r
134 {\r
135   EFI_EXCEPTION_TYPE  ExceptionType;\r
136 \r
137   for (ExceptionType = 0; ExceptionType < NUM_IVT_ENTRIES; ExceptionType++) {\r
138     ManageIvtEntryTable (ExceptionType, NULL, NULL);\r
139   }\r
140 \r
141   return EFI_SUCCESS;\r
142 }\r
143 \r
144 VOID\r
145 CommonHandler (\r
146   IN EFI_EXCEPTION_TYPE ExceptionType,\r
147   IN EFI_SYSTEM_CONTEXT Context\r
148   )\r
149 /*++\r
150 \r
151 Routine Description:\r
152   C routine that is called for all registered exceptions.  This is the main\r
153   exception dispatcher.  Must be public because it's referenced from AsmFuncs.s.\r
154 \r
155 Arguments:\r
156   ExceptionType - Exception Type\r
157   Context       - System Context\r
158 \r
159 Returns:\r
160 \r
161   Nothing\r
162   \r
163 --*/\r
164 {\r
165   static BOOLEAN  InHandler = FALSE;\r
166 \r
167   DEBUG_CODE_BEGIN ();\r
168     if (InHandler) {\r
169       DEBUG ((EFI_D_INFO, "ERROR: Re-entered debugger!\n"\r
170                                     "       ExceptionType == %X\n"\r
171                                     "       Context       == %X\n"\r
172                                     "       Context.SystemContextIpf->CrIip  == %X\n"\r
173                                     "       Context.SystemContextIpf->CrIpsr == %X\n"\r
174                                     "       InHandler     == %X\n",\r
175                                     ExceptionType, \r
176                                     Context, \r
177                                     Context.SystemContextIpf->CrIip,\r
178                                     Context.SystemContextIpf->CrIpsr,\r
179                                     InHandler));\r
180     }\r
181   DEBUG_CODE_END ();\r
182 \r
183   ASSERT (!InHandler);\r
184   InHandler = TRUE;\r
185   if (IvtEntryTable[ExceptionType].RegisteredCallback != NULL) {\r
186     if (ExceptionType != EXCEPT_IPF_EXTERNAL_INTERRUPT) {\r
187       IvtEntryTable[ExceptionType].RegisteredCallback (ExceptionType, Context.SystemContextIpf);\r
188     } else {\r
189       IvtEntryTable[ExceptionType].RegisteredCallback (Context.SystemContextIpf);\r
190     }\r
191   } else {\r
192     ASSERT (0);\r
193   }\r
194 \r
195   InHandler = FALSE;\r
196 }\r
197 \r
198 STATIC\r
199 VOID\r
200 GetHandlerEntryPoint (\r
201   UINTN   HandlerIndex,\r
202   VOID    **EntryPoint\r
203   )\r
204 /*++\r
205 \r
206 Routine Description:\r
207   Given an integer number, return the physical address of the entry point in the IFT\r
208   \r
209 Arguments:\r
210   HandlerIndex - Index of the Handler \r
211   EntryPoint   - IFT Entrypoint\r
212 \r
213 Returns:\r
214 \r
215   Nothing\r
216   \r
217 --*/\r
218 {\r
219   UINT8 *TempPtr;\r
220 \r
221   //\r
222   // get base address of IVT\r
223   //\r
224   TempPtr = GetIva ();\r
225 \r
226   if (HandlerIndex < 20) {\r
227     //\r
228     // first 20 provide 64 bundles per vector\r
229     //\r
230     TempPtr += 0x400 * HandlerIndex;\r
231   } else {\r
232     //\r
233     // the rest provide 16 bundles per vector\r
234     //\r
235     TempPtr += 0x5000 + 0x100 * (HandlerIndex - 20);\r
236   }\r
237 \r
238   *EntryPoint = (VOID *) TempPtr;\r
239 }\r
240 \r
241 STATIC\r
242 EFI_STATUS\r
243 ManageIvtEntryTable (\r
244   IN  EFI_EXCEPTION_TYPE                                         ExceptionType,\r
245   IN  BUNDLE                       NewBundles[NUM_BUNDLES_IN_STUB],\r
246   IN  VOID                         (*NewCallback) ()\r
247   )\r
248 /*++\r
249 \r
250 Routine Description:\r
251   This is the worker function that installs and removes all handlers\r
252   \r
253 Arguments:\r
254   ExceptionType  - Exception Type\r
255   NewBundles     - New Boundles\r
256   NewCallback    - New Callback\r
257 \r
258 Returns:\r
259 \r
260   EFI_STATUS - any return other than EFI_SUCCESS indicates the request was not\r
261   satisfied.\r
262   EFI_ALEADY_STARTED - Ivt already hooked.\r
263   \r
264 --*/\r
265 {\r
266   BUNDLE  *B0Ptr;\r
267   UINT64  InterruptFlags;\r
268   EFI_TPL OldTpl;\r
269 \r
270   //\r
271   // Get address of bundle 0\r
272   //\r
273   GetHandlerEntryPoint (ExceptionType, (VOID **) &B0Ptr);\r
274 \r
275   if (IvtEntryTable[ExceptionType].RegisteredCallback != NULL) {\r
276     //\r
277     // we've already installed to this vector\r
278     //\r
279     if (NewCallback != NULL) {\r
280       //\r
281       // if the input handler is non-null, error\r
282       //\r
283       return EFI_ALREADY_STARTED;\r
284     } else {\r
285       //\r
286       // else remove the previously installed handler\r
287       //\r
288       OldTpl          = gBS->RaiseTPL (TPL_HIGH_LEVEL);\r
289       InterruptFlags  = ProgramInterruptFlags (DISABLE_INTERRUPTS);\r
290       if (ExceptionType == EXCEPT_IPF_EXTERNAL_INTERRUPT) {\r
291         UnchainExternalInterrupt ();\r
292       } else {\r
293         UnhookEntry (ExceptionType);\r
294       }\r
295 \r
296       ProgramInterruptFlags (InterruptFlags);\r
297       gBS->RestoreTPL (OldTpl);\r
298       //\r
299       // re-init IvtEntryTable\r
300       //\r
301       SetMem (&IvtEntryTable[ExceptionType], sizeof (IVT_ENTRY), 0);\r
302     }\r
303   } else {\r
304     //\r
305     // no user handler installed on this vector\r
306     //\r
307     if (NewCallback != NULL) {\r
308       OldTpl          = gBS->RaiseTPL (TPL_HIGH_LEVEL);\r
309       InterruptFlags  = ProgramInterruptFlags (DISABLE_INTERRUPTS);\r
310       if (ExceptionType == EXCEPT_IPF_EXTERNAL_INTERRUPT) {\r
311         ChainExternalInterrupt (NewCallback);\r
312       } else {\r
313         HookEntry (ExceptionType, NewBundles, NewCallback);\r
314       }\r
315 \r
316       ProgramInterruptFlags (InterruptFlags);\r
317       gBS->RestoreTPL (OldTpl);\r
318     }\r
319   }\r
320 \r
321   return EFI_SUCCESS;\r
322 }\r
323 \r
324 STATIC\r
325 VOID\r
326 HookEntry (\r
327   IN  EFI_EXCEPTION_TYPE  ExceptionType,\r
328   IN  BUNDLE              NewBundles[4],\r
329   IN  VOID                (*NewCallback) ()\r
330   )\r
331 /*++\r
332 \r
333 Routine Description:\r
334   Saves original IVT contents and inserts a few new bundles which are fixed up\r
335   to store the ExceptionType and then call the common handler.\r
336   \r
337 Arguments:\r
338   ExceptionType  - Exception Type\r
339   NewBundles     - New Boundles\r
340   NewCallback    - New Callback\r
341 \r
342 Returns:\r
343 \r
344   Nothing\r
345     \r
346 --*/\r
347 {\r
348   BUNDLE  *FixupBundle;\r
349   BUNDLE  *B0Ptr;\r
350 \r
351   //\r
352   // Get address of bundle 0\r
353   //\r
354   GetHandlerEntryPoint (ExceptionType, (VOID **) &B0Ptr);\r
355 \r
356   //\r
357   // copy original bundles from IVT to IvtEntryTable so we can restore them later\r
358   //\r
359   CopyMem (\r
360     IvtEntryTable[ExceptionType].OrigBundles,\r
361     B0Ptr,\r
362     sizeof (BUNDLE) * NUM_BUNDLES_IN_STUB\r
363     );\r
364   //\r
365   // insert new B0\r
366   //\r
367   CopyMem (B0Ptr, NewBundles, sizeof (BUNDLE) * NUM_BUNDLES_IN_STUB);\r
368 \r
369   //\r
370   // fixup IVT entry so it stores its index and whether or not to chain...\r
371   //\r
372   FixupBundle = B0Ptr + 2;\r
373   FixupBundle->high |= ExceptionType << 36;\r
374 \r
375   InstructionCacheFlush (B0Ptr, 5);\r
376   IvtEntryTable[ExceptionType].RegisteredCallback = NewCallback;\r
377 }\r
378 \r
379 STATIC\r
380 VOID\r
381 UnhookEntry (\r
382   IN  EFI_EXCEPTION_TYPE  ExceptionType\r
383   )\r
384 /*++\r
385 \r
386 Routine Description:\r
387   Restores original IVT contents when unregistering a callback function\r
388   \r
389 Arguments:\r
390   ExceptionType  - Exception Type\r
391   NewBundles     - New Boundles\r
392   NewCallback    - New Callback\r
393 \r
394 Returns:\r
395 \r
396   Nothing\r
397     \r
398 --*/\r
399 {\r
400   BUNDLE  *B0Ptr;\r
401 \r
402   //\r
403   // Get address of bundle 0\r
404   //\r
405   GetHandlerEntryPoint (ExceptionType, (VOID **) &B0Ptr);\r
406   //\r
407   // restore original bundles in IVT\r
408   //\r
409   CopyMem (\r
410     B0Ptr,\r
411     IvtEntryTable[ExceptionType].OrigBundles,\r
412     sizeof (BUNDLE) * NUM_BUNDLES_IN_STUB\r
413     );\r
414   InstructionCacheFlush (B0Ptr, 5);\r
415 }\r
416 \r
417 STATIC\r
418 VOID\r
419 ChainExternalInterrupt (\r
420   IN  VOID  (*NewCallback) ()\r
421   )\r
422 /*++\r
423 \r
424 Routine Description:\r
425   Sets up cache flush and calls assembly function to chain external interrupt.\r
426   Records new callback in IvtEntryTable.\r
427   \r
428 Arguments:\r
429   NewCallback    - New Callback\r
430 \r
431 Returns:\r
432 \r
433   Nothing\r
434     \r
435 --*/\r
436 {\r
437   VOID  *Start;\r
438 \r
439   Start = (VOID *) ((UINT8 *) GetIva () + 0x400 * EXCEPT_IPF_EXTERNAL_INTERRUPT + 0x400);\r
440   IvtEntryTable[EXCEPT_IPF_EXTERNAL_INTERRUPT].RegisteredCallback = NewCallback;\r
441   ChainHandler ();\r
442   InstructionCacheFlush (Start, 0x400);\r
443 }\r
444 \r
445 STATIC\r
446 VOID\r
447 UnchainExternalInterrupt (\r
448   VOID\r
449   )\r
450 /*++\r
451 \r
452 Routine Description:\r
453   Sets up cache flush and calls assembly function to restore external interrupt.\r
454   Removes registered callback from IvtEntryTable.\r
455   \r
456 Arguments:\r
457   Nothing\r
458   \r
459 Returns:\r
460 \r
461   Nothing\r
462     \r
463 --*/\r
464 {\r
465   VOID  *Start;\r
466 \r
467   Start = (VOID *) ((UINT8 *) GetIva () + 0x400 * EXCEPT_IPF_EXTERNAL_INTERRUPT + 0x400);\r
468   UnchainHandler ();\r
469   InstructionCacheFlush (Start, 0x400);\r
470   IvtEntryTable[EXCEPT_IPF_EXTERNAL_INTERRUPT].RegisteredCallback = NULL;\r
471 }\r
472 \r
473 //\r
474 // The rest of the functions in this file are all member functions for the\r
475 // DebugSupport protocol\r
476 //\r
477 \r
478 EFI_STATUS\r
479 EFIAPI\r
480 GetMaximumProcessorIndex (\r
481   IN EFI_DEBUG_SUPPORT_PROTOCOL    *This,\r
482   OUT UINTN                        *MaxProcessorIndex\r
483   )\r
484 /*++\r
485 \r
486 Routine Description: This is a DebugSupport protocol member function.  Hard\r
487   coded to support only 1 processor for now.\r
488 \r
489 Arguments:\r
490   This              - The DebugSupport instance\r
491   MaxProcessorIndex - The maximuim supported processor index\r
492 \r
493 Returns:\r
494   Always returns EFI_SUCCESS with *MaxProcessorIndex set to 0\r
495 \r
496 --*/\r
497 {\r
498   *MaxProcessorIndex = 0;\r
499   return (EFI_SUCCESS);\r
500 }\r
501 \r
502 EFI_STATUS\r
503 EFIAPI\r
504 RegisterPeriodicCallback (\r
505   IN EFI_DEBUG_SUPPORT_PROTOCOL     *This,\r
506   IN UINTN                          ProcessorIndex,\r
507   IN EFI_PERIODIC_CALLBACK          NewPeriodicCallback\r
508   )\r
509 /*++\r
510 \r
511 Routine Description:\r
512   DebugSupport protocol member function\r
513 \r
514 Arguments:\r
515   This             - The DebugSupport instance\r
516   ProcessorIndex   - Which processor the callback applies to.\r
517   PeriodicCallback - Callback function\r
518 \r
519 Returns:\r
520 \r
521   EFI_STATUS - anything other than EFI_SUCCESS indicates the callback was not registered.\r
522 \r
523 --*/\r
524 {\r
525   return ManageIvtEntryTable (EXCEPT_IPF_EXTERNAL_INTERRUPT, NULL, NewPeriodicCallback);\r
526 }\r
527 \r
528 EFI_STATUS\r
529 EFIAPI\r
530 RegisterExceptionCallback (\r
531   IN EFI_DEBUG_SUPPORT_PROTOCOL    *This,\r
532   IN UINTN                         ProcessorIndex,\r
533   IN EFI_EXCEPTION_CALLBACK        NewCallback,\r
534   IN EFI_EXCEPTION_TYPE            ExceptionType\r
535   )\r
536 /*++\r
537 \r
538 Routine Description:\r
539   DebugSupport protocol member function\r
540 \r
541 Arguments:\r
542   This             - The DebugSupport instance\r
543   ProcessorIndex   - Which processor the callback applies to.\r
544   NewCallback      - Callback function\r
545   ExceptionType    - Which exception to hook\r
546 \r
547 Returns:\r
548 \r
549   EFI_STATUS - anything other than EFI_SUCCESS indicates the callback was not registered.\r
550 \r
551 --*/\r
552 {\r
553   return ManageIvtEntryTable (\r
554           ExceptionType,\r
555           (BUNDLE *) ((EFI_PLABEL *) HookStub)->EntryPoint,\r
556           NewCallback\r
557           );\r
558 }\r
559 \r
560 EFI_STATUS\r
561 EFIAPI\r
562 InvalidateInstructionCache (\r
563   IN EFI_DEBUG_SUPPORT_PROTOCOL    *This,\r
564   IN UINTN                         ProcessorIndex,\r
565   IN VOID                          *Start,\r
566   IN UINTN                         Length\r
567   )\r
568 /*++\r
569 \r
570 Routine Description:\r
571   DebugSupport protocol member function.  Calls assembly routine to flush cache.\r
572 \r
573 Arguments:\r
574   This             - The DebugSupport instance\r
575   ProcessorIndex   - Which processor the callback applies to.\r
576   Start            - Physical base of the memory range to be invalidated\r
577   Length           - mininum number of bytes in instruction cache to invalidate\r
578 \r
579 Returns:\r
580   EFI_SUCCESS\r
581 \r
582 --*/\r
583 {\r
584   InstructionCacheFlush (Start, Length);\r
585   return (EFI_SUCCESS);\r
586 }\r