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 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 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 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 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 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 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 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 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 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 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 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 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 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