diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf b/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf index 314143b294..9e0b54ec78 100644 --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/DxeCpuExceptionHandlerLib.inf @@ -47,6 +47,9 @@ LoongArch/LoongArch64/ExceptionHandlerAsm.S | GCC [Sources.RISCV64] + RiscV/Backtrace.h + RiscV/Backtrace.c + RiscV/BacktraceHelper.c RiscV/ExceptionLib.c RiscV/ExceptionHandler.h RiscV/ExceptionHandlerAsm.S | GCC @@ -80,5 +83,8 @@ [LibraryClasses.LoongArch64] CpuLib +[Guids.RISCV64] + gEfiDebugImageInfoTableGuid + [BuildOptions] XCODE:*_*_X64_NASM_FLAGS = -D NO_ABSOLUTE_RELOCS_IN_TEXT diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/RiscV/Backtrace.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/RiscV/Backtrace.c new file mode 100644 index 0000000000..9765d721e5 --- /dev/null +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/RiscV/Backtrace.c @@ -0,0 +1,175 @@ +/** @file + RISC-V backtrace implementation. + + Copyright (c) 2016 - 2022, Hewlett Packard Enterprise Development LP. All rights reserved.
+ Copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.
+ Copyright (c) 2023, Intel Corporation. All rights reserved.
+ Copyright (c) 2025, Ventana Micro Systems Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Backtrace.h" + +#define MAX_STACK_FRAME_SIZE SIZE_16KB + +STATIC +INTN +CheckFpValid ( + IN UINTN Fp, + IN UINTN Sp + ) +{ + UINTN Low, High; + + Low = Sp + 2 * sizeof (UINTN); + High = ALIGN_VALUE (Sp, MAX_STACK_FRAME_SIZE); + + return !(Fp < Low || Fp > High || Fp & 0x07); +} + +STATIC +CONST CHAR8 * +BaseName ( + IN CONST CHAR8 *FullName + ) +{ + CONST CHAR8 *Str; + + Str = FullName + AsciiStrLen (FullName); + + while (--Str > FullName) { + if ((*Str == '/') || (*Str == '\\')) { + return Str + 1; + } + } + + return Str; +} + +/** + Helper for displaying a backtrace. + + @param Regs Pointer to SMODE_TRAP_REGISTERS. + @param FirstPdb Pointer to the first symbol file used. + @param ListImage If true, only show the full path to symbol file, else + show the PC value and its decoded components. +**/ +STATIC +VOID +DumpCpuBacktraceHelper ( + IN SMODE_TRAP_REGISTERS *Regs, + IN CHAR8 *FirstPdb, + IN BOOLEAN ListImage + ) +{ + UINTN ImageBase; + UINTN PeCoffSizeOfHeader; + BOOLEAN IsLeaf; + UINTN RootFp; + UINTN RootRa; + UINTN Sp; + UINTN Fp; + UINTN Ra; + UINTN Idx; + CHAR8 *Pdb; + CHAR8 *PrevPdb; + + RootRa = Regs->ra; + RootFp = Regs->s0; + + Idx = 0; + IsLeaf = TRUE; + Fp = RootFp; + Ra = RootRa; + PrevPdb = FirstPdb; + while (Fp != 0) { + Pdb = GetImageName (Ra, &ImageBase, &PeCoffSizeOfHeader); + if (Pdb != NULL) { + if (Pdb != PrevPdb) { + Idx++; + if (ListImage) { + DEBUG ((DEBUG_ERROR, "[% 2d] %a\n", Idx, Pdb)); + } + + PrevPdb = Pdb; + } + + if (!ListImage) { + DEBUG (( + DEBUG_ERROR, + "PC 0x%012lx (0x%012lx+0x%08x) [% 2d] %a\n", + Ra, + ImageBase, + Ra - ImageBase, + Idx, + BaseName (Pdb) + )); + } + } else if (!ListImage) { + DEBUG ((DEBUG_ERROR, "PC 0x%012lx\n", Ra)); + } + + /* + * After the prologue, the frame pointer register s0 will point + * to the Canonical Frame Address or CFA, which is the stack + * pointer value on entry to the current procedure. The previous + * frame pointer and return address pair will reside just prior + * to the current stack address held in s0. This puts the return + * address at s0 - XLEN/8, and the previous frame pointer at + * s0 - 2 * XLEN/8. + */ + Sp = Fp; + Fp -= sizeof (UINTN) * 2; + Ra = *(UINTN *)(Fp + sizeof (UINTN)); + Fp = *(UINTN *)(Fp); + if (IsLeaf && CheckFpValid (Ra, Sp)) { + /* We hit function where ra is not saved on the stack */ + Fp = Ra; + Ra = RootRa; + } + + IsLeaf = FALSE; + } +} + +/** + Display a backtrace. + + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. +**/ +VOID +EFIAPI +DumpCpuBacktrace ( + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + SMODE_TRAP_REGISTERS *Regs; + CHAR8 *Pdb; + UINTN ImageBase; + UINTN PeCoffSizeOfHeader; + + Regs = (SMODE_TRAP_REGISTERS *)SystemContext.SystemContextRiscV64; + Pdb = GetImageName (Regs->sepc, &ImageBase, &PeCoffSizeOfHeader); + if (Pdb != NULL) { + DEBUG (( + DEBUG_ERROR, + "PC 0x%012lx (0x%012lx+0x%08x) [ 0] %a\n", + Regs->sepc, + ImageBase, + Regs->sepc - ImageBase, + BaseName (Pdb) + )); + } else { + DEBUG ((DEBUG_ERROR, "PC 0x%012lx\n", Regs->sepc)); + } + + DumpCpuBacktraceHelper (Regs, Pdb, FALSE); + + if (Pdb != NULL) { + DEBUG ((DEBUG_ERROR, "\n[ 0] %a\n", Pdb)); + } + + DumpCpuBacktraceHelper (Regs, Pdb, TRUE); +} diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/RiscV/Backtrace.h b/UefiCpuPkg/Library/CpuExceptionHandlerLib/RiscV/Backtrace.h new file mode 100644 index 0000000000..6e29b9080f --- /dev/null +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/RiscV/Backtrace.h @@ -0,0 +1,57 @@ +/** @file + + RISC-V backtrace definition file. + + Copyright (c) 2025, Ventana Micro Systems Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef BACKTRACE_H_ +#define BACKTRACE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ExceptionHandler.h" + +/** + Use the EFI Debug Image Table to lookup the FaultAddress and find which PE/COFF image + it came from. As long as the PE/COFF image contains a debug directory entry a + string can be returned. For ELF and Mach-O images the string points to the Mach-O or ELF + image. Microsoft tools contain a pointer to the PDB file that contains the debug information. + + @param FaultAddress Address to find PE/COFF image for. + @param ImageBase Return load address of found image + @param PeCoffSizeOfHeaders Return the size of the PE/COFF header for the image that was found + + @retval NULL FaultAddress not in a loaded PE/COFF image. + @retval Path and file name of PE/COFF image. + +**/ +CHAR8 * +EFIAPI +GetImageName ( + IN UINTN FaultAddress, + OUT UINTN *ImageBase, + OUT UINTN *PeCoffSizeOfHeaders + ); + +/** + Display a backtrace. + + @param SystemContext Pointer to EFI_SYSTEM_CONTEXT. +**/ +VOID +EFIAPI +DumpCpuBacktrace ( + IN EFI_SYSTEM_CONTEXT SystemContext + ); + +#endif // BACKTRACE_H_ diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/RiscV/BacktraceHelper.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/RiscV/BacktraceHelper.c new file mode 100644 index 0000000000..fdc56669ae --- /dev/null +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/RiscV/BacktraceHelper.c @@ -0,0 +1,71 @@ +/** @file + RISC-V backtrace helper functions. + + Copyright (c) 2016 - 2022, Hewlett Packard Enterprise Development LP. All rights reserved.
+ Copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.
+ Copyright (c) 2023, Intel Corporation. All rights reserved.
+ Copyright (c) 2025, Ventana Micro Systems Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Backtrace.h" + +/** + Use the EFI Debug Image Table to lookup the FaultAddress and find which PE/COFF image + it came from. As long as the PE/COFF image contains a debug directory entry a + string can be returned. For ELF and Mach-O images the string points to the Mach-O or ELF + image. Microsoft tools contain a pointer to the PDB file that contains the debug information. + + @param FaultAddress Address to find PE/COFF image for. + @param ImageBase Return load address of found image + @param PeCoffSizeOfHeaders Return the size of the PE/COFF header for the image that was found + + @retval NULL FaultAddress not in a loaded PE/COFF image. + @retval Path and file name of PE/COFF image. + +**/ +CHAR8 * +EFIAPI +GetImageName ( + IN UINTN FaultAddress, + OUT UINTN *ImageBase, + OUT UINTN *PeCoffSizeOfHeaders + ) +{ + EFI_STATUS Status; + EFI_DEBUG_IMAGE_INFO_TABLE_HEADER *DebugTableHeader; + EFI_DEBUG_IMAGE_INFO *DebugTable; + UINTN Entry; + CHAR8 *Address; + + Status = EfiGetSystemConfigurationTable (&gEfiDebugImageInfoTableGuid, (VOID **)&DebugTableHeader); + if (EFI_ERROR (Status)) { + return NULL; + } + + DebugTable = DebugTableHeader->EfiDebugImageInfoTable; + if (DebugTable == NULL) { + return NULL; + } + + Address = (CHAR8 *)(UINTN)FaultAddress; + for (Entry = 0; Entry < DebugTableHeader->TableSize; Entry++, DebugTable++) { + if (DebugTable->NormalImage != NULL) { + if ((DebugTable->NormalImage->ImageInfoType == EFI_DEBUG_IMAGE_INFO_TYPE_NORMAL) && + (DebugTable->NormalImage->LoadedImageProtocolInstance != NULL)) + { + if ((Address >= (CHAR8 *)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase) && + (Address <= ((CHAR8 *)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase + DebugTable->NormalImage->LoadedImageProtocolInstance->ImageSize))) + { + *ImageBase = (UINTN)DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase; + *PeCoffSizeOfHeaders = PeCoffGetSizeOfHeaders ((VOID *)(UINTN)*ImageBase); + return PeCoffLoaderGetPdbPointer (DebugTable->NormalImage->LoadedImageProtocolInstance->ImageBase); + } + } + } + } + + return NULL; +} diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/RiscV/BacktraceHelperSec.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/RiscV/BacktraceHelperSec.c new file mode 100644 index 0000000000..10e3497bfe --- /dev/null +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/RiscV/BacktraceHelperSec.c @@ -0,0 +1,42 @@ +/** @file + RISC-V backtrace helper functions for SEC. + + Copyright (c) 2025, Ventana Micro Systems Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Backtrace.h" + +/** + Use the EFI Debug Image Table to lookup the FaultAddress and find which PE/COFF image + it came from. As long as the PE/COFF image contains a debug directory entry a + string can be returned. For ELF and Mach-O images the string points to the Mach-O or ELF + image. Microsoft tools contain a pointer to the PDB file that contains the debug information. + + @param FaultAddress Address to find PE/COFF image for. + @param ImageBase Return load address of found image + @param PeCoffSizeOfHeaders Return the size of the PE/COFF header for the image that was found + + @retval NULL FaultAddress not in a loaded PE/COFF image. + @retval Path and file name of PE/COFF image. + +**/ +CHAR8 * +EFIAPI +GetImageName ( + IN UINTN FaultAddress, + OUT UINTN *ImageBase, + OUT UINTN *PeCoffSizeOfHeaders + ) +{ + // + // This function is not implemented in SEC phase. + // It should be implemented in DXE phase. + // + *ImageBase = 0; + *PeCoffSizeOfHeaders = 0; + + return NULL; +} diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/RiscV/ExceptionLib.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/RiscV/ExceptionLib.c index 48f57f6c5a..d19c99234d 100644 --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/RiscV/ExceptionLib.c +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/RiscV/ExceptionLib.c @@ -2,6 +2,9 @@ RISC-V Exception Handler library implementation. Copyright (c) 2016 - 2022, Hewlett Packard Enterprise Development LP. All rights reserved.
+ Copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.
+ Copyright (c) 2023, Intel Corporation. All rights reserved.
+ Copyright (c) 2025, Ventana Micro Systems Inc. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent @@ -14,6 +17,7 @@ #include #include #include +#include "Backtrace.h" #include "ExceptionHandler.h" // @@ -136,11 +140,21 @@ DumpCpuContext ( ) { UINTN Printed; + UINTN RecursiveException; SMODE_TRAP_REGISTERS *Regs; Printed = 0; Regs = (SMODE_TRAP_REGISTERS *)SystemContext.SystemContextRiscV64; + RecursiveException = RiscVGetSupervisorScratch (); + if (RecursiveException == 0xdeaddead) { + InternalPrintMessage ("\nRecursive exception occurred while dumping the CPU state\n"); + + CpuDeadLoop (); + } + + RiscVSetSupervisorScratch ((UINTN)0xdeaddead); + InternalPrintMessage ( "!!!! RISCV64 Exception Type - %016x(%a) !!!!\n", ExceptionType, @@ -171,6 +185,8 @@ DumpCpuContext ( #undef REG #undef REGS + DumpCpuBacktrace (SystemContext); + DEBUG_CODE_END (); } diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf b/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf index cc872e12b0..00a5b495e0 100644 --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/SecPeiCpuExceptionHandlerLib.inf @@ -47,6 +47,9 @@ LoongArch/LoongArch64/ExceptionHandlerAsm.S | GCC [Sources.RISCV64] + RiscV/Backtrace.h + RiscV/Backtrace.c + RiscV/BacktraceHelperSec.c RiscV/ExceptionLib.c RiscV/ExceptionHandler.h RiscV/ExceptionHandlerAsm.S | GCC