/** @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);
}