1 /** @file 2 CPU Exception Library provides DXE/SMM CPU common exception handler. 3 4 Copyright (c) 2012 - 2013, Intel Corporation. All rights reserved.<BR> 5 This program and the accompanying materials are licensed and made available under 6 the terms and conditions of the BSD License that accompanies this distribution. 7 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 "CpuExceptionCommon.h" 16 #include <Library/DebugLib.h> 17 18 // 19 // Spinlock for CPU information display 20 // 21 SPIN_LOCK mDisplayMessageSpinLock; 22 23 // 24 // Image align size for DXE/SMM 25 // 26 CONST UINTN mImageAlignSize = SIZE_4KB; 27 28 RESERVED_VECTORS_DATA mReservedVectorsData[CPU_EXCEPTION_NUM]; 29 EFI_CPU_INTERRUPT_HANDLER mExternalInterruptHandlerTable[CPU_EXCEPTION_NUM]; 30 EFI_CPU_INTERRUPT_HANDLER *mExternalInterruptHandler = NULL; 31 UINTN mEnabledInterruptNum = 0; 32 33 /** 34 Common exception handler. 35 36 @param ExceptionType Exception type. 37 @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. 38 **/ 39 VOID 40 EFIAPI 41 CommonExceptionHandler ( 42 IN EFI_EXCEPTION_TYPE ExceptionType, 43 IN EFI_SYSTEM_CONTEXT SystemContext 44 ) 45 { 46 EXCEPTION_HANDLER_CONTEXT *ExceptionHandlerContext; 47 48 ExceptionHandlerContext = (EXCEPTION_HANDLER_CONTEXT *) (UINTN) (SystemContext.SystemContextIa32); 49 50 switch (mReservedVectors[ExceptionType].Attribute) { 51 case EFI_VECTOR_HANDOFF_HOOK_BEFORE: 52 // 53 // Need to jmp to old IDT handler after this exception handler 54 // 55 ExceptionHandlerContext->ExceptionDataFlag = (mErrorCodeFlag & (1 << ExceptionType)) ? TRUE : FALSE; 56 ExceptionHandlerContext->OldIdtHandler = mReservedVectors[ExceptionType].ExceptonHandler; 57 break; 58 case EFI_VECTOR_HANDOFF_HOOK_AFTER: 59 while (TRUE) { 60 // 61 // If if anyone has gotten SPIN_LOCK for owner running hook after 62 // 63 if (AcquireSpinLockOrFail (&mReservedVectors[ExceptionType].SpinLock)) { 64 // 65 // Need to execute old IDT handler before running this exception handler 66 // 67 mReservedVectors[ExceptionType].ApicId = GetApicId (); 68 ArchSaveExceptionContext (ExceptionType, SystemContext); 69 ExceptionHandlerContext->ExceptionDataFlag = (mErrorCodeFlag & (1 << ExceptionType)) ? TRUE : FALSE; 70 ExceptionHandlerContext->OldIdtHandler = mReservedVectors[ExceptionType].ExceptonHandler; 71 return; 72 } 73 // 74 // If failed to acquire SPIN_LOCK, check if it was locked by processor itself 75 // 76 if (mReservedVectors[ExceptionType].ApicId == GetApicId ()) { 77 // 78 // Old IDT handler has been executed, then retore CPU exception content to 79 // run new exception handler. 80 // 81 ArchRestoreExceptionContext (ExceptionType, SystemContext); 82 // 83 // Rlease spin lock for ApicId 84 // 85 ReleaseSpinLock (&mReservedVectors[ExceptionType].SpinLock); 86 break; 87 } 88 CpuPause (); 89 } 90 break; 91 case 0xffffffff: 92 break; 93 default: 94 // 95 // It should never reach here 96 // 97 CpuDeadLoop (); 98 break; 99 } 100 101 if (mExternalInterruptHandler[ExceptionType] != NULL) { 102 (mExternalInterruptHandler[ExceptionType]) (ExceptionType, SystemContext); 103 } else if (ExceptionType < CPU_EXCEPTION_NUM) { 104 // 105 // Get Spinlock to display CPU information 106 // 107 while (!AcquireSpinLockOrFail (&mDisplayMessageSpinLock)) { 108 CpuPause (); 109 } 110 // 111 // Display ExceptionType, CPU information and Image information 112 // 113 DumpCpuContent (ExceptionType, SystemContext); 114 // 115 // Release Spinlock of output message 116 // 117 ReleaseSpinLock (&mDisplayMessageSpinLock); 118 // 119 // Enter a dead loop if needn't to execute old IDT handler further 120 // 121 if (mReservedVectors[ExceptionType].Attribute != EFI_VECTOR_HANDOFF_HOOK_BEFORE) { 122 CpuDeadLoop (); 123 } 124 } 125 } 126 127 /** 128 Internal worker function to update IDT entries accordling to vector attributes. 129 130 @param[in] IdtTable Pointer to IDT table. 131 @param[in] TemplateMap Pointer to a buffer where the address map is returned. 132 @param[in] IdtEntryCount IDT entries number to be updated. 133 134 **/ 135 VOID 136 UpdateIdtTable ( 137 IN IA32_IDT_GATE_DESCRIPTOR *IdtTable, 138 IN EXCEPTION_HANDLER_TEMPLATE_MAP *TemplateMap, 139 IN UINTN IdtEntryCount 140 ) 141 { 142 UINT16 CodeSegment; 143 UINTN Index; 144 UINTN InterruptHandler; 145 146 // 147 // Use current CS as the segment selector of interrupt gate in IDT 148 // 149 CodeSegment = AsmReadCs (); 150 151 for (Index = 0; Index < IdtEntryCount; Index ++) { 152 IdtTable[Index].Bits.Selector = CodeSegment; 153 // 154 // Check reserved vectors attributes 155 // 156 switch (mReservedVectors[Index].Attribute) { 157 case EFI_VECTOR_HANDOFF_DO_NOT_HOOK: 158 // 159 // Keep original IDT entry 160 // 161 continue; 162 case EFI_VECTOR_HANDOFF_HOOK_AFTER: 163 InitializeSpinLock (&mReservedVectors[Index].SpinLock); 164 CopyMem ( 165 (VOID *) mReservedVectors[Index].HookAfterStubHeaderCode, 166 (VOID *) TemplateMap->HookAfterStubHeaderStart, 167 TemplateMap->ExceptionStubHeaderSize 168 ); 169 AsmVectorNumFixup ( 170 (VOID *) mReservedVectors[Index].HookAfterStubHeaderCode, 171 (UINT8) Index, 172 (VOID *) TemplateMap->HookAfterStubHeaderStart 173 ); 174 // 175 // Go on the following code 176 // 177 case EFI_VECTOR_HANDOFF_HOOK_BEFORE: 178 // 179 // Save original IDT handler address 180 // 181 mReservedVectors[Index].ExceptonHandler = ArchGetIdtHandler (&IdtTable[Index]); 182 // 183 // Go on the following code 184 // 185 default: 186 // 187 // Update new IDT entry 188 // 189 InterruptHandler = TemplateMap->ExceptionStart + Index * TemplateMap->ExceptionStubHeaderSize; 190 ArchUpdateIdtEntry (&IdtTable[Index], InterruptHandler); 191 break; 192 } 193 } 194 195 // 196 // Save Interrupt number to global variable used for RegisterCpuInterruptHandler () 197 // 198 mEnabledInterruptNum = IdtEntryCount; 199 } 200 201 /** 202 Internal worker function to initialize exception handler. 203 204 @param[in] VectorInfo Pointer to reserved vector list. 205 206 @retval EFI_SUCCESS CPU Exception Entries have been successfully initialized 207 with default exception handlers. 208 @retval EFI_INVALID_PARAMETER VectorInfo includes the invalid content if VectorInfo is not NULL. 209 @retval EFI_UNSUPPORTED This function is not supported. 210 211 **/ 212 EFI_STATUS 213 InitializeCpuExceptionHandlersWorker ( 214 IN EFI_VECTOR_HANDOFF_INFO *VectorInfo OPTIONAL 215 ) 216 { 217 EFI_STATUS Status; 218 IA32_DESCRIPTOR IdtDescriptor; 219 UINTN IdtEntryCount; 220 EXCEPTION_HANDLER_TEMPLATE_MAP TemplateMap; 221 IA32_IDT_GATE_DESCRIPTOR *IdtTable; 222 223 mReservedVectors = mReservedVectorsData; 224 SetMem ((VOID *) mReservedVectors, sizeof (RESERVED_VECTORS_DATA) * CPU_EXCEPTION_NUM, 0xff); 225 if (VectorInfo != NULL) { 226 Status = ReadAndVerifyVectorInfo (VectorInfo, mReservedVectors, CPU_EXCEPTION_NUM); 227 if (EFI_ERROR (Status)) { 228 return EFI_INVALID_PARAMETER; 229 } 230 } 231 InitializeSpinLock (&mDisplayMessageSpinLock); 232 233 mExternalInterruptHandler = mExternalInterruptHandlerTable; 234 // 235 // Read IDT descriptor and calculate IDT size 236 // 237 AsmReadIdtr (&IdtDescriptor); 238 IdtEntryCount = (IdtDescriptor.Limit + 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR); 239 if (IdtEntryCount > CPU_EXCEPTION_NUM) { 240 // 241 // CPU exeption library only setup CPU_EXCEPTION_NUM exception handler at most 242 // 243 IdtEntryCount = CPU_EXCEPTION_NUM; 244 } 245 246 IdtTable = (IA32_IDT_GATE_DESCRIPTOR *) IdtDescriptor.Base; 247 AsmGetTemplateAddressMap (&TemplateMap); 248 ASSERT (TemplateMap.ExceptionStubHeaderSize <= HOOKAFTER_STUB_SIZE); 249 UpdateIdtTable (IdtTable, &TemplateMap, IdtEntryCount); 250 mEnabledInterruptNum = IdtEntryCount; 251 return EFI_SUCCESS; 252 } 253 254 /** 255 Registers a function to be called from the processor interrupt handler. 256 257 @param[in] InterruptType Defines which interrupt or exception to hook. 258 @param[in] InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called 259 when a processor interrupt occurs. If this parameter is NULL, then the handler 260 will be uninstalled. 261 262 @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled. 263 @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was 264 previously installed. 265 @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not 266 previously installed. 267 @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported, 268 or this function is not supported. 269 **/ 270 EFI_STATUS 271 RegisterCpuInterruptHandlerWorker ( 272 IN EFI_EXCEPTION_TYPE InterruptType, 273 IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler 274 ) 275 { 276 if (InterruptType < 0 || InterruptType >= (EFI_EXCEPTION_TYPE)mEnabledInterruptNum || 277 mReservedVectors[InterruptType].Attribute == EFI_VECTOR_HANDOFF_DO_NOT_HOOK) { 278 return EFI_UNSUPPORTED; 279 } 280 281 if (InterruptHandler == NULL && mExternalInterruptHandler[InterruptType] == NULL) { 282 return EFI_INVALID_PARAMETER; 283 } 284 285 if (InterruptHandler != NULL && mExternalInterruptHandler[InterruptType] != NULL) { 286 return EFI_ALREADY_STARTED; 287 } 288 289 mExternalInterruptHandler[InterruptType] = InterruptHandler; 290 return EFI_SUCCESS; 291 } 292 293