PeiDxeSmmCpuException.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. /** @file
  2. CPU Exception Library provides PEI/DXE/SMM CPU common exception handler.
  3. Copyright (c) 2012 - 2022, Intel Corporation. All rights reserved.<BR>
  4. SPDX-License-Identifier: BSD-2-Clause-Patent
  5. **/
  6. #include <Library/DebugLib.h>
  7. #include <Library/CcExitLib.h>
  8. #include "CpuExceptionCommon.h"
  9. /**
  10. Internal worker function for common exception handler.
  11. @param ExceptionType Exception type.
  12. @param SystemContext Pointer to EFI_SYSTEM_CONTEXT.
  13. @param ExceptionHandlerData Pointer to exception handler data.
  14. **/
  15. VOID
  16. CommonExceptionHandlerWorker (
  17. IN EFI_EXCEPTION_TYPE ExceptionType,
  18. IN EFI_SYSTEM_CONTEXT SystemContext,
  19. IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData
  20. )
  21. {
  22. EFI_STATUS Status;
  23. EXCEPTION_HANDLER_CONTEXT *ExceptionHandlerContext;
  24. RESERVED_VECTORS_DATA *ReservedVectors;
  25. EFI_CPU_INTERRUPT_HANDLER *ExternalInterruptHandler;
  26. switch (ExceptionType) {
  27. case VC_EXCEPTION:
  28. //
  29. // #VC needs to be handled immediately upon enabling exception handling
  30. // and therefore can't use the RegisterCpuInterruptHandler() interface.
  31. //
  32. // Handle the #VC:
  33. // On EFI_SUCCESS - Exception has been handled, return
  34. // On other - ExceptionType contains (possibly new) exception
  35. // value
  36. //
  37. Status = CcExitHandleVc (&ExceptionType, SystemContext);
  38. if (!EFI_ERROR (Status)) {
  39. return;
  40. }
  41. break;
  42. case VE_EXCEPTION:
  43. //
  44. // #VE needs to be handled immediately upon enabling exception handling
  45. // and therefore can't use the RegisterCpuInterruptHandler() interface.
  46. //
  47. // Handle the #VE:
  48. // On EFI_SUCCESS - Exception has been handled, return
  49. // On other - ExceptionType contains (possibly new) exception
  50. // value
  51. //
  52. Status = CcExitHandleVe (&ExceptionType, SystemContext);
  53. if (!EFI_ERROR (Status)) {
  54. return;
  55. }
  56. break;
  57. default:
  58. break;
  59. }
  60. ExceptionHandlerContext = (EXCEPTION_HANDLER_CONTEXT *)(UINTN)(SystemContext.SystemContextIa32);
  61. ReservedVectors = ExceptionHandlerData->ReservedVectors;
  62. ExternalInterruptHandler = ExceptionHandlerData->ExternalInterruptHandler;
  63. switch (ReservedVectors[ExceptionType].Attribute) {
  64. case EFI_VECTOR_HANDOFF_HOOK_BEFORE:
  65. //
  66. // The new exception handler registered by RegisterCpuInterruptHandler() is executed BEFORE original handler.
  67. // Save the original handler to stack so the assembly code can jump to it instead of returning from handler.
  68. //
  69. ExceptionHandlerContext->ExceptionDataFlag = (mErrorCodeFlag & (1 << ExceptionType)) ? TRUE : FALSE;
  70. ExceptionHandlerContext->OldIdtHandler = ReservedVectors[ExceptionType].ExceptonHandler;
  71. break;
  72. case EFI_VECTOR_HANDOFF_HOOK_AFTER:
  73. while (TRUE) {
  74. //
  75. // If spin-lock can be acquired, it's the first time entering here.
  76. //
  77. if (AcquireSpinLockOrFail (&ReservedVectors[ExceptionType].SpinLock)) {
  78. //
  79. // The new exception handler registered by RegisterCpuInterruptHandler() is executed AFTER original handler.
  80. // Save the original handler to stack but skip running the new handler so the original handler is executed
  81. // firstly.
  82. //
  83. ReservedVectors[ExceptionType].ApicId = GetApicId ();
  84. ArchSaveExceptionContext (ExceptionType, SystemContext, ExceptionHandlerData);
  85. ExceptionHandlerContext->ExceptionDataFlag = (mErrorCodeFlag & (1 << ExceptionType)) ? TRUE : FALSE;
  86. ExceptionHandlerContext->OldIdtHandler = ReservedVectors[ExceptionType].ExceptonHandler;
  87. return;
  88. }
  89. //
  90. // If spin-lock cannot be acquired, it's the second time entering here.
  91. // 'break' instead of 'return' is used so the new exception handler can be executed.
  92. //
  93. if (ReservedVectors[ExceptionType].ApicId == GetApicId ()) {
  94. //
  95. // Old IDT handler has been executed, then restore CPU exception content to
  96. // run new exception handler.
  97. //
  98. ArchRestoreExceptionContext (ExceptionType, SystemContext, ExceptionHandlerData);
  99. //
  100. // Release spin lock for ApicId
  101. //
  102. ReleaseSpinLock (&ReservedVectors[ExceptionType].SpinLock);
  103. break;
  104. }
  105. CpuPause ();
  106. }
  107. break;
  108. case 0xffffffff:
  109. break;
  110. default:
  111. //
  112. // It should never reach here
  113. //
  114. CpuDeadLoop ();
  115. break;
  116. }
  117. if ((ExternalInterruptHandler != NULL) &&
  118. (ExternalInterruptHandler[ExceptionType] != NULL))
  119. {
  120. (ExternalInterruptHandler[ExceptionType])(ExceptionType, SystemContext);
  121. } else if (ExceptionType < CPU_EXCEPTION_NUM) {
  122. //
  123. // Get Spinlock to display CPU information
  124. //
  125. while (!AcquireSpinLockOrFail (&ExceptionHandlerData->DisplayMessageSpinLock)) {
  126. CpuPause ();
  127. }
  128. //
  129. // Initialize the serial port before dumping.
  130. //
  131. SerialPortInitialize ();
  132. //
  133. // Display ExceptionType, CPU information and Image information
  134. //
  135. DumpImageAndCpuContent (ExceptionType, SystemContext);
  136. //
  137. // Release Spinlock of output message
  138. //
  139. ReleaseSpinLock (&ExceptionHandlerData->DisplayMessageSpinLock);
  140. //
  141. // Enter a dead loop if needn't to execute old IDT handler further
  142. //
  143. if (ReservedVectors[ExceptionType].Attribute != EFI_VECTOR_HANDOFF_HOOK_BEFORE) {
  144. CpuDeadLoop ();
  145. }
  146. }
  147. }
  148. /**
  149. Internal worker function to update IDT entries accordling to vector attributes.
  150. @param[in] IdtTable Pointer to IDT table.
  151. @param[in] TemplateMap Pointer to a buffer where the address map is
  152. returned.
  153. @param[in] ExceptionHandlerData Pointer to exception handler data.
  154. **/
  155. VOID
  156. UpdateIdtTable (
  157. IN IA32_IDT_GATE_DESCRIPTOR *IdtTable,
  158. IN EXCEPTION_HANDLER_TEMPLATE_MAP *TemplateMap,
  159. IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData
  160. )
  161. {
  162. UINT16 CodeSegment;
  163. UINTN Index;
  164. UINTN InterruptHandler;
  165. RESERVED_VECTORS_DATA *ReservedVectors;
  166. ReservedVectors = ExceptionHandlerData->ReservedVectors;
  167. //
  168. // Use current CS as the segment selector of interrupt gate in IDT
  169. //
  170. CodeSegment = AsmReadCs ();
  171. for (Index = 0; Index < ExceptionHandlerData->IdtEntryCount; Index++) {
  172. IdtTable[Index].Bits.Selector = CodeSegment;
  173. //
  174. // Check reserved vectors attributes
  175. //
  176. switch (ReservedVectors[Index].Attribute) {
  177. case EFI_VECTOR_HANDOFF_DO_NOT_HOOK:
  178. //
  179. // Keep original IDT entry
  180. //
  181. continue;
  182. case EFI_VECTOR_HANDOFF_HOOK_AFTER:
  183. InitializeSpinLock (&ReservedVectors[Index].SpinLock);
  184. CopyMem (
  185. (VOID *)ReservedVectors[Index].HookAfterStubHeaderCode,
  186. (VOID *)TemplateMap->HookAfterStubHeaderStart,
  187. TemplateMap->ExceptionStubHeaderSize
  188. );
  189. AsmVectorNumFixup (
  190. (VOID *)ReservedVectors[Index].HookAfterStubHeaderCode,
  191. (UINT8)Index,
  192. (VOID *)TemplateMap->HookAfterStubHeaderStart
  193. );
  194. //
  195. // Go on the following code
  196. //
  197. case EFI_VECTOR_HANDOFF_HOOK_BEFORE:
  198. //
  199. // Save original IDT handler address
  200. //
  201. ReservedVectors[Index].ExceptonHandler = ArchGetIdtHandler (&IdtTable[Index]);
  202. //
  203. // Go on the following code
  204. //
  205. default:
  206. //
  207. // Update new IDT entry
  208. //
  209. InterruptHandler = TemplateMap->ExceptionStart + Index * TemplateMap->ExceptionStubHeaderSize;
  210. ArchUpdateIdtEntry (&IdtTable[Index], InterruptHandler);
  211. break;
  212. }
  213. }
  214. }
  215. /**
  216. Internal worker function to initialize exception handler.
  217. @param[in] VectorInfo Pointer to reserved vector list.
  218. @param[in, out] ExceptionHandlerData Pointer to exception handler data.
  219. @retval EFI_SUCCESS CPU Exception Entries have been successfully initialized
  220. with default exception handlers.
  221. @retval EFI_INVALID_PARAMETER VectorInfo includes the invalid content if VectorInfo is not NULL.
  222. @retval EFI_UNSUPPORTED This function is not supported.
  223. **/
  224. EFI_STATUS
  225. InitializeCpuExceptionHandlersWorker (
  226. IN EFI_VECTOR_HANDOFF_INFO *VectorInfo OPTIONAL,
  227. IN OUT EXCEPTION_HANDLER_DATA *ExceptionHandlerData
  228. )
  229. {
  230. EFI_STATUS Status;
  231. IA32_DESCRIPTOR IdtDescriptor;
  232. UINTN IdtEntryCount;
  233. EXCEPTION_HANDLER_TEMPLATE_MAP TemplateMap;
  234. IA32_IDT_GATE_DESCRIPTOR *IdtTable;
  235. RESERVED_VECTORS_DATA *ReservedVectors;
  236. ReservedVectors = ExceptionHandlerData->ReservedVectors;
  237. SetMem ((VOID *)ReservedVectors, sizeof (RESERVED_VECTORS_DATA) * ExceptionHandlerData->IdtEntryCount, 0xff);
  238. if (VectorInfo != NULL) {
  239. Status = ReadAndVerifyVectorInfo (VectorInfo, ReservedVectors, ExceptionHandlerData->IdtEntryCount);
  240. if (EFI_ERROR (Status)) {
  241. return EFI_INVALID_PARAMETER;
  242. }
  243. }
  244. //
  245. // Setup the exception handlers according to IDT size, but no more than
  246. // ExceptionHandlerData->IdtEntryCount (32 in PEI and SMM, 256 in DXE) handlers.
  247. //
  248. AsmReadIdtr (&IdtDescriptor);
  249. IdtEntryCount = (IdtDescriptor.Limit + 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR);
  250. ExceptionHandlerData->IdtEntryCount = MIN (IdtEntryCount, ExceptionHandlerData->IdtEntryCount);
  251. IdtTable = (IA32_IDT_GATE_DESCRIPTOR *)IdtDescriptor.Base;
  252. AsmGetTemplateAddressMap (&TemplateMap);
  253. ASSERT (TemplateMap.ExceptionStubHeaderSize <= HOOKAFTER_STUB_SIZE);
  254. UpdateIdtTable (IdtTable, &TemplateMap, ExceptionHandlerData);
  255. return EFI_SUCCESS;
  256. }
  257. /**
  258. Registers a function to be called from the processor interrupt handler.
  259. @param[in] InterruptType Defines which interrupt or exception to hook.
  260. @param[in] InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
  261. when a processor interrupt occurs. If this parameter is NULL, then the handler
  262. will be uninstalled
  263. @param[in] ExceptionHandlerData Pointer to exception handler data.
  264. @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled.
  265. @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was
  266. previously installed.
  267. @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not
  268. previously installed.
  269. @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported,
  270. or this function is not supported.
  271. **/
  272. EFI_STATUS
  273. RegisterCpuInterruptHandlerWorker (
  274. IN EFI_EXCEPTION_TYPE InterruptType,
  275. IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler,
  276. IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData
  277. )
  278. {
  279. UINTN EnabledInterruptNum;
  280. RESERVED_VECTORS_DATA *ReservedVectors;
  281. EFI_CPU_INTERRUPT_HANDLER *ExternalInterruptHandler;
  282. EnabledInterruptNum = ExceptionHandlerData->IdtEntryCount;
  283. ReservedVectors = ExceptionHandlerData->ReservedVectors;
  284. ExternalInterruptHandler = ExceptionHandlerData->ExternalInterruptHandler;
  285. if ((InterruptType < 0) || (InterruptType >= (EFI_EXCEPTION_TYPE)EnabledInterruptNum) ||
  286. (ReservedVectors[InterruptType].Attribute == EFI_VECTOR_HANDOFF_DO_NOT_HOOK))
  287. {
  288. return EFI_UNSUPPORTED;
  289. }
  290. if ((InterruptHandler == NULL) && (ExternalInterruptHandler[InterruptType] == NULL)) {
  291. return EFI_INVALID_PARAMETER;
  292. }
  293. if ((InterruptHandler != NULL) && (ExternalInterruptHandler[InterruptType] != NULL)) {
  294. return EFI_ALREADY_STARTED;
  295. }
  296. ExternalInterruptHandler[InterruptType] = InterruptHandler;
  297. return EFI_SUCCESS;
  298. }