summaryrefslogtreecommitdiff
path: root/UefiCpuPkg/Library/CpuExceptionHandlerLib/RiscV/Backtrace.c
blob: 9765d721e5367606d42d01d7f93c2d572effee6e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
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);
}