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