PeiDxeSmmCpuException.c 12 KB

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