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