UefiCpuPkg: CpuExceptionHandlerLib: RISC-V: Support backtrace

Add support for backtrace in DEBUG builds for RISC-V.

Signed-off-by: Tuan Phan <tphan@ventanamicro.com>
This commit is contained in:
Tuan Phan
2025-07-18 15:38:14 -07:00
committed by mergify[bot]
parent aee4d29d56
commit 4052e8f155
7 changed files with 370 additions and 0 deletions

View File

@@ -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

View File

@@ -0,0 +1,175 @@
/** @file
RISC-V backtrace implementation.
Copyright (c) 2016 - 2022, Hewlett Packard Enterprise Development LP. All rights reserved.<BR>
Copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.<BR>
Copyright (c) 2023, Intel Corporation. All rights reserved.<BR>
Copyright (c) 2025, Ventana Micro Systems Inc. All rights reserved.<BR>
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);
}

View File

@@ -0,0 +1,57 @@
/** @file
RISC-V backtrace definition file.
Copyright (c) 2025, Ventana Micro Systems Inc. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#ifndef BACKTRACE_H_
#define BACKTRACE_H_
#include <PiPei.h>
#include <Uefi.h>
#include <Library/DebugLib.h>
#include <Library/BaseLib.h>
#include <Library/PeCoffExtraActionLib.h>
#include <Library/PeCoffGetEntryPointLib.h>
#include <Library/UefiLib.h>
#include <Guid/DebugImageInfoTable.h>
#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_

View File

@@ -0,0 +1,71 @@
/** @file
RISC-V backtrace helper functions.
Copyright (c) 2016 - 2022, Hewlett Packard Enterprise Development LP. All rights reserved.<BR>
Copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.<BR>
Copyright (c) 2023, Intel Corporation. All rights reserved.<BR>
Copyright (c) 2025, Ventana Micro Systems Inc. All rights reserved.<BR>
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;
}

View File

@@ -0,0 +1,42 @@
/** @file
RISC-V backtrace helper functions for SEC.
Copyright (c) 2025, Ventana Micro Systems Inc. All rights reserved.<BR>
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;
}

View File

@@ -2,6 +2,9 @@
RISC-V Exception Handler library implementation.
Copyright (c) 2016 - 2022, Hewlett Packard Enterprise Development LP. All rights reserved.<BR>
Copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.<BR>
Copyright (c) 2023, Intel Corporation. All rights reserved.<BR>
Copyright (c) 2025, Ventana Micro Systems Inc. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
@@ -14,6 +17,7 @@
#include <Library/SerialPortLib.h>
#include <Library/PrintLib.h>
#include <Register/RiscV64/RiscVEncoding.h>
#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 ();
}

View File

@@ -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