Exception.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /** @file
  2. Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>
  3. SPDX-License-Identifier: BSD-2-Clause-Patent
  4. @par Glossary:
  5. - ESTAT - Exception Status
  6. - ECFG - Exception Configure
  7. - ERA - Exception Return Address
  8. - BADV - Bad Virtual Address
  9. - BADI - Bad Instructions
  10. - Epc or EPC or epc - Exception Program Counter
  11. - pc or PC or pc - Program Counter
  12. - CRMD - Current Mode
  13. - PRMD - Previous Mode
  14. - CsrEuen - Cpu Status Register Extern Unit Enable
  15. - fpu or fp or FP - Float Point Unit
  16. - LOONGARCH - Loongson Arch
  17. - Irq - Interrupt ReQuest
  18. **/
  19. #include "Library/Cpu.h"
  20. #include <Library/BaseMemoryLib.h>
  21. #include <Library/UefiBootServicesTableLib.h>
  22. #include <Library/UefiLib.h>
  23. #include <Library/CacheMaintenanceLib.h>
  24. #include <Library/DebugLib.h>
  25. #include "CpuDxe.h"
  26. #include <Library/PeCoffGetEntryPointLib.h>
  27. #include <Library/UefiLib.h>
  28. #include <Guid/DebugImageInfoTable.h>
  29. EFI_EXCEPTION_CALLBACK gInterruptHandler[MAX_LOONGARCH_INTERRUPT + 1];
  30. EFI_EXCEPTION_CALLBACK gDebuggerExceptionHandlers[MAX_LOONGARCH_INTERRUPT + 1];
  31. /**
  32. This function registers and enables the handler specified by InterruptHandler for a processor
  33. interrupt or exception type specified by InteruptNum. If InterruptHandler is NULL, then the
  34. handler for the processor interrupt or exception type specified by InteruptNum is uninstalled.
  35. The installed handler is called once for each processor interrupt or exception.
  36. @param InteruptNum A number of the processor's current interrupt.
  37. @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
  38. when a processor interrupt occurs. If this parameter is NULL, then the handler
  39. will be uninstalled.
  40. @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled.
  41. @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InteruptNum was
  42. previously installed.
  43. @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InteruptNum was not
  44. previously installed.
  45. @retval EFI_UNSUPPORTED The interrupt specified by InteruptNum is not supported.
  46. **/
  47. EFI_STATUS
  48. RegisterInterruptHandler (
  49. IN EFI_EXCEPTION_TYPE InteruptNum,
  50. IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler
  51. )
  52. {
  53. if (InteruptNum > MAX_LOONGARCH_INTERRUPT) {
  54. return EFI_UNSUPPORTED;
  55. }
  56. if ((InterruptHandler != NULL)
  57. && (gInterruptHandler[InteruptNum] != NULL))
  58. {
  59. return EFI_ALREADY_STARTED;
  60. }
  61. gInterruptHandler[InteruptNum] = InterruptHandler;
  62. return EFI_SUCCESS;
  63. }
  64. /**
  65. This function calls the corresponding exception handler based on the exception type.
  66. @param SystemContext The system context at the time of the exception.
  67. @retval VOID
  68. **/
  69. STATIC VOID
  70. EFIAPI
  71. CommonInterruptHandler (
  72. IN OUT EFI_SYSTEM_CONTEXT SystemContext
  73. )
  74. {
  75. INT32 Pending;
  76. INT32 InterruptNum;
  77. /*Interrupt [13-0] NMI IPI TI PCOV hw IP10-IP2 soft IP1-IP0*/
  78. Pending = ((SystemContext.SystemContextLoongArch64->ESTAT) &
  79. (SystemContext.SystemContextLoongArch64->ECFG) & 0x1fff);
  80. for (InterruptNum = 0; InterruptNum < MAX_LOONGARCH_INTERRUPT; InterruptNum++) {
  81. if (Pending & (1 << InterruptNum)) {
  82. if (gInterruptHandler[InterruptNum] != NULL) {
  83. gInterruptHandler[InterruptNum] (InterruptNum, SystemContext);
  84. } else {
  85. DEBUG ((DEBUG_INFO, "Pending: 0x%0x, InterruptNum: 0x%0x\n", Pending, InterruptNum));
  86. }
  87. }
  88. }
  89. }
  90. /**
  91. Use the EFI Debug Image Table to lookup the FaultAddress and find which PE/COFF image
  92. it came from. As long as the PE/COFF image contains a debug directory entry a
  93. string can be returned. For ELF and Mach-O images the string points to the Mach-O or ELF
  94. image. Microsoft tools contain a pointer to the PDB file that contains the debug information.
  95. @param FaultAddress Address to find PE/COFF image for.
  96. @param ImageBase Return load address of found image
  97. @param PeCoffSizeOfHeaders Return the size of the PE/COFF header for the image that was found
  98. @retval NULL FaultAddress not in a loaded PE/COFF image.
  99. @retval Path and file name of PE/COFF image.
  100. **/
  101. CHAR8 *
  102. GetImageName (
  103. IN UINTN FaultAddress,
  104. OUT UINTN *ImageBase,
  105. OUT UINTN *PeCoffSizeOfHeaders
  106. )
  107. {
  108. EFI_STATUS Status;
  109. EFI_DEBUG_IMAGE_INFO_TABLE_HEADER *DebugTableHeader;
  110. EFI_DEBUG_IMAGE_INFO *DebugTable;
  111. UINTN Entry;
  112. CHAR8 *Address;
  113. Status = EfiGetSystemConfigurationTable (&gEfiDebugImageInfoTableGuid, (VOID **)&DebugTableHeader);
  114. if (EFI_ERROR (Status)) {
  115. return NULL;
  116. }
  117. DebugTable = DebugTableHeader->EfiDebugImageInfoTable;
  118. if (DebugTable == NULL) {
  119. return NULL;
  120. }
  121. Address = (CHAR8 *)(UINTN)FaultAddress;
  122. for (Entry = 0; Entry < DebugTableHeader->TableSize; Entry++, DebugTable++) {
  123. if (DebugTable->NormalImage != NULL) {
  124. if ((DebugTable->NormalImage->ImageInfoType == EFI_DEBUG_IMAGE_INFO_TYPE_NORMAL) &&
  125. (DebugTable->NormalImage->LoadedImageProtocolInstance != NULL)) {
  126. if ((Address >= (CHAR8 *)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase) &&
  127. (Address <= ((CHAR8 *)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase + DebugTable->NormalImage->LoadedImageProtocolInstance->ImageSize))) {
  128. *ImageBase = (UINTN)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase;
  129. *PeCoffSizeOfHeaders = PeCoffGetSizeOfHeaders ((VOID *)(UINTN)*ImageBase);
  130. return PeCoffLoaderGetPdbPointer (DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase);
  131. }
  132. }
  133. }
  134. }
  135. return NULL;
  136. }
  137. /**
  138. pass a file name string that contains the path, return file name.
  139. @param FullName Path and file name
  140. @retval file name.
  141. **/
  142. STATIC
  143. CONST CHAR8 *
  144. BaseName (
  145. IN CONST CHAR8 *FullName
  146. )
  147. {
  148. CONST CHAR8 *Str;
  149. Str = FullName + AsciiStrLen (FullName);
  150. while (--Str > FullName) {
  151. if (*Str == '/' || *Str == '\\') {
  152. return Str + 1;
  153. }
  154. }
  155. return Str;
  156. }
  157. /** Default Exception Handler Function
  158. This function is called when an exception occurs that cannot be handled,
  159. and this function prints the system context information when the interrupt occurred
  160. @param SystemContext The system context at the time of the exception.
  161. @retval VOID.
  162. **/
  163. STATIC
  164. VOID
  165. EFIAPI
  166. DefaultHandler (
  167. IN OUT EFI_SYSTEM_CONTEXT SystemContext
  168. )
  169. {
  170. CHAR8 *ImageName;
  171. UINTN ImageBase;
  172. UINTN Epc;
  173. UINTN PeCoffSizeOfHeader;
  174. DEBUG ((DEBUG_ERROR, "CRMD 0x%llx\n", SystemContext.SystemContextLoongArch64->CRMD));
  175. DEBUG ((DEBUG_ERROR, "PRMD 0x%llx\n", SystemContext.SystemContextLoongArch64->PRMD));
  176. DEBUG ((DEBUG_ERROR, "ECFG 0x%llx\n", SystemContext.SystemContextLoongArch64->ECFG));
  177. DEBUG ((DEBUG_ERROR, "ESTAT 0x%llx\n", SystemContext.SystemContextLoongArch64->ESTAT));
  178. DEBUG ((DEBUG_ERROR, "ERA 0x%llx\n", SystemContext.SystemContextLoongArch64->ERA));
  179. DEBUG ((DEBUG_ERROR, "BADV 0x%llx\n", SystemContext.SystemContextLoongArch64->BADV));
  180. DEBUG ((DEBUG_ERROR, "BADI 0x%llx\n", SystemContext.SystemContextLoongArch64->BADI));
  181. Epc = SystemContext.SystemContextLoongArch64->ERA;
  182. ImageName = GetImageName (Epc, &ImageBase, &PeCoffSizeOfHeader);
  183. if (ImageName != NULL) {
  184. DEBUG ((DEBUG_ERROR, "PC 0x%012lx (0x%012lx+0x%08x) [ 0] %a\n",
  185. Epc, ImageBase,
  186. Epc - ImageBase, BaseName (ImageName)));
  187. } else {
  188. DEBUG ((DEBUG_ERROR, "PC 0x%012lx\n", Epc));
  189. }
  190. while (1);
  191. }
  192. /** Common exception entry
  193. Exception handling is the entry point for the C environment,
  194. This function does different things depending on the exception type.
  195. @param SystemContext The system context at the time of the exception.
  196. @retval VOID.
  197. **/
  198. VOID
  199. EFIAPI
  200. CommonExceptionEntry (
  201. IN OUT EFI_SYSTEM_CONTEXT SystemContext
  202. )
  203. {
  204. INT32 ExceptionType;
  205. UINT64 CsrEuen;
  206. UINT64 FpuStatus;
  207. ExceptionType = SystemContext.SystemContextLoongArch64->ESTAT & CSR_ESTAT_EXC;
  208. ExceptionType = ExceptionType >> CSR_ESTAT_EXC_SHIFT;
  209. LoongArchReadqCsrEuen (&CsrEuen);
  210. FpuStatus = CsrEuen & CSR_EUEN_FPEN;
  211. switch (ExceptionType) {
  212. case EXC_INT:
  213. /*
  214. * handle interrupt exception
  215. */
  216. CommonInterruptHandler (SystemContext);
  217. if (!FpuStatus) {
  218. LoongArchReadqCsrEuen (&CsrEuen);
  219. if (CsrEuen & CSR_EUEN_FPEN) {
  220. /*
  221. * Since Hw FP is enabled during interrupt handler,
  222. * disable FP
  223. */
  224. CsrEuen &= ~CSR_EUEN_FPEN;
  225. LoongArchWriteqCsrEuen (CsrEuen);
  226. }
  227. }
  228. break;
  229. case EXC_FPDIS:
  230. /*
  231. * Hardware FP disabled exception,
  232. * Enable and init FP registers here
  233. */
  234. LoongArchEnableFpu ();
  235. InitFpu(FPU_CSR_RN);
  236. break;
  237. default:
  238. DefaultHandler(SystemContext);
  239. break;
  240. }
  241. }
  242. /** Exception module initialization
  243. This function sets the exception base address.
  244. @param Cpu A pointer to the CPU architecture protocol structure.
  245. @retval EFI_SUCCESS Initialization succeeded
  246. @retval EFI_NOT_FOUND Could not Found resources.
  247. @retval EFI_OUT_OF_RESOURCES No enough resources.
  248. **/
  249. EFI_STATUS
  250. InitializeExceptions (
  251. IN EFI_CPU_ARCH_PROTOCOL *Cpu
  252. )
  253. {
  254. EFI_STATUS Status;
  255. BOOLEAN IrqEnabled;
  256. EFI_PHYSICAL_ADDRESS Address;
  257. ZeroMem (gInterruptHandler, sizeof (*gInterruptHandler));
  258. //
  259. // Disable interrupts
  260. //
  261. Cpu->GetInterruptState (Cpu, &IrqEnabled);
  262. Cpu->DisableInterrupt (Cpu);
  263. //
  264. // EFI does not use the FIQ, but a debugger might so we must disable
  265. // as we take over the exception vectors.
  266. //
  267. Status = gBS->AllocatePages (
  268. AllocateAnyPages,
  269. EfiRuntimeServicesData,
  270. 1,
  271. &Address
  272. );
  273. if (EFI_ERROR (Status)) {
  274. return Status;
  275. }
  276. DEBUG ((DEBUG_INFO, "Set Exception Base Address\n"));
  277. CopyMem ((char *)Address, LoongArchException, (LoongArchExceptionEnd - LoongArchException));
  278. InvalidateInstructionCacheRange ((char *)Address, (LoongArchExceptionEnd - LoongArchException));
  279. SetEbase (Address);
  280. DEBUG ((DEBUG_INFO, "LoongArchException address: 0x%p\n", Address));
  281. DEBUG ((DEBUG_INFO, "LoongArchExceptionEnd address: 0x%p\n", Address + (LoongArchExceptionEnd - LoongArchException)));
  282. DEBUG ((DEBUG_INFO, "InitializeExceptions, IrqEnabled = %x\n", IrqEnabled));
  283. if (IrqEnabled) {
  284. //
  285. // Restore interrupt state
  286. //
  287. Status = Cpu->EnableInterrupt (Cpu);
  288. }
  289. return Status;
  290. }