public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [RFC 0/1] Stack trace support in X64 exception handling
@ 2017-11-14 12:47 Paulo Alcantara
  2017-11-14 12:47 ` [RFC 1/1] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support Paulo Alcantara
                   ` (2 more replies)
  0 siblings, 3 replies; 82+ messages in thread
From: Paulo Alcantara @ 2017-11-14 12:47 UTC (permalink / raw)
  To: edk2-devel
  Cc: Paulo Alcantara, Rick Bramley, Andrew Fish, Eric Dong,
	Laszlo Ersek

Hi,

This series adds stack trace support during a X64 CPU exception.

Informations like back trace, stack contents and image module names
(that were part of the call stack) will be dumped out.

We already have such support in ARM/AArch64 (IIRC) exception handling
(thanks to Ard), and then I thought we'd also deserve it in X64 and
IA-32 platforms.

What do you think guys?

BTW, I've tested this only with OVMF (X64 only), using:
  - gcc-6.3.0, GCC5, NOOPT

Any other tests  would be really appreciable.

Thanks!
Paulo

Repo:   https://github.com/pcacjr/edk2.git
Branch: stacktrace_x64

Cc: Rick Bramley <richard.bramley@hp.com>
Cc: Andrew Fish <afish@apple.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---

Paulo Alcantara (1):
  UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support

 UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 344 +++++++++++++++++++-
 1 file changed, 342 insertions(+), 2 deletions(-)

-- 
2.11.0



^ permalink raw reply	[flat|nested] 82+ messages in thread

* [RFC 1/1] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
  2017-11-14 12:47 [RFC 0/1] Stack trace support in X64 exception handling Paulo Alcantara
@ 2017-11-14 12:47 ` Paulo Alcantara
  2017-11-14 14:01   ` Andrew Fish
  2017-12-29  3:48   ` [RFC v4 0/6] Stack trace support in X64 exception handling Paulo Alcantara
  2017-11-14 13:21 ` [RFC 0/1] " Paulo Alcantara
  2017-11-16  1:18 ` [RFC v2 0/3] " Paulo Alcantara
  2 siblings, 2 replies; 82+ messages in thread
From: Paulo Alcantara @ 2017-11-14 12:47 UTC (permalink / raw)
  To: edk2-devel; +Cc: Paulo Alcantara, Eric Dong, Laszlo Ersek

This patch adds stack trace support during a X64 CPU exception.

It will dump out back trace, stack contents as well as image module
names that were part of the call stack.

Contributed-under: TianoCore Contribution Agreement 1.1
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 344 +++++++++++++++++++-
 1 file changed, 342 insertions(+), 2 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
index 65f0cff680..7048247be3 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
@@ -14,6 +14,11 @@
 
 #include "CpuExceptionCommon.h"
 
+//
+// Unknown PDB file name
+//
+GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
+
 /**
   Return address map of exception handler template so that C code can generate
   exception tables.
@@ -243,6 +248,325 @@ DumpCpuContext (
 }
 
 /**
+  Dump stack contents.
+
+  @param[in]  ImageBase            Base address of PE/COFF image.
+  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
+  @param[out] PdbFileName          File name of PDB file.
+**/
+STATIC
+VOID
+GetPdbFileName (
+  IN  UINTN    ImageBase,
+  OUT CHAR8    **PdbAbsoluteFilePath,
+  OUT CHAR8    **PdbFileName
+  )
+{
+  VOID   *PdbPointer;
+  CHAR8  *Str;
+
+  //
+  // Get PDB file name from PE/COFF image
+  //
+  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
+  if (PdbPointer == NULL) {
+    //
+    // No PDB file name found. Set it to an unknown file name.
+    //
+    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
+    if (PdbAbsoluteFilePath != NULL) {
+      *PdbAbsoluteFilePath = NULL;
+    }
+  } else {
+    //
+    // Get file name portion out of PDB file in PE/COFF image
+    //
+    Str = (CHAR8 *)((UINTN)PdbPointer +
+                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
+    for (; *Str != '/' && *Str != '\\'; Str--) {
+      ;
+    }
+
+    //
+    // Set PDB file name (also skip trailing path separator: '/' or '\\')
+    //
+    *PdbFileName = Str + 1;
+
+    if (PdbAbsoluteFilePath != NULL) {
+      //
+      // Set absolute file path of PDB file
+      //
+      *PdbAbsoluteFilePath = PdbPointer;
+    }
+  }
+}
+
+/**
+  Dump stack contents.
+
+  @param[in]  CurrentRsp         Current stack pointer address.
+  @param[in]  UnwondStacksCount  Count of unwond stack frames.
+**/
+STATIC
+VOID
+DumpStackContents (
+  IN UINT64  CurrentRsp,
+  IN INTN    UnwondStacksCount
+  )
+{
+  if (UnwondStacksCount == 0) {
+    return;
+  }
+
+  //
+  // Dump out stack contents
+  //
+  InternalPrintMessage ("\nStack dump:\n");
+  while (UnwondStacksCount-- > 0) {
+    InternalPrintMessage (
+      "0x%016lx: %016lx %016lx\n",
+      CurrentRsp,
+      *(UINT64 *)CurrentRsp,
+      *(UINT64 *)((UINTN)CurrentRsp + 8)
+      );
+
+    //
+    // As per Microsoft x64 ABI, the stack pointer must be aligned on a 16 byte
+    // boundary.
+    //
+    CurrentRsp = CurrentRsp + 16;
+  }
+}
+
+/**
+  Dump all image module names from call stack.
+
+  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+**/
+STATIC
+VOID
+DumpImageModuleNames (
+  IN EFI_SYSTEM_CONTEXT   SystemContext
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Rip;
+  UINTN       ImageBase;
+  VOID        *EntryPoint;
+  CHAR8       *PdbAbsoluteFilePath;
+  CHAR8       *PdbFileName;
+  UINT64      Rbp;
+
+  //
+  // Set current RIP address
+  //
+  Rip = SystemContext.SystemContextX64->Rip;
+
+  //
+  // Set current frame pointer address
+  //
+  Rbp = SystemContext.SystemContextX64->Rbp;
+
+  //
+  // Get initial PE/COFF image base address from current RIP
+  //
+  ImageBase = PeCoffSearchImageBase (Rip);
+  if (ImageBase == 0) {
+    InternalPrintMessage ("!!!! Could not find image module names. !!!!");
+    return;
+  }
+
+  //
+  // Get initial PE/COFF image's entry point
+  //
+  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
+  if (EFI_ERROR (Status)) {
+    EntryPoint = NULL;
+  }
+
+  //
+  // Get file name and absolute path of initial PDB file
+  //
+  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
+
+  //
+  // Print out initial image module name (if any)
+  //
+  if (PdbAbsoluteFilePath != NULL) {
+    InternalPrintMessage (
+      "\n%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
+      PdbFileName,
+      ImageBase,
+      (UINTN)EntryPoint
+      );
+    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
+  }
+
+  //
+  // Walk through call stack and find next module names
+  //
+  for (;;) {
+    //
+    // Set RIP with return address from current stack frame
+    //
+    Rip = *(UINT64 *)((UINTN)Rbp + 8);
+
+    //
+    // Check if RIP is within another PE/COFF image base address
+    //
+    if (Rip < ImageBase) {
+      //
+      // Search for the respective PE/COFF image based on RIP
+      //
+      ImageBase = PeCoffSearchImageBase (Rip);
+      if (ImageBase == 0) {
+        //
+        // Stop stack trace
+        //
+        break;
+      }
+
+      //
+      // Get PE/COFF image's entry point
+      //
+      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
+      if (EFI_ERROR (Status)) {
+        EntryPoint = NULL;
+      }
+
+      //
+      // Get file name and absolute path of PDB file
+      //
+      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
+
+      //
+      // Print out image module name (if any)
+      //
+      if (PdbAbsoluteFilePath != NULL) {
+        InternalPrintMessage (
+          "%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
+          PdbFileName,
+          ImageBase,
+          (UINTN)EntryPoint
+          );
+        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
+      }
+    }
+
+    //
+    // Unwind the stack
+    //
+    Rbp = *(UINT64 *)(UINTN)Rbp;
+  }
+}
+
+/**
+  Dump stack trace.
+
+  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
+  @param[out] UnwondStacksCount  Count of unwond stack frames.
+**/
+STATIC
+VOID
+DumpStackTrace (
+  IN  EFI_SYSTEM_CONTEXT   SystemContext,
+  OUT INTN                 *UnwondStacksCount
+  )
+{
+  UINT64  Rip;
+  UINT64  Rbp;
+  UINTN   ImageBase;
+  CHAR8   *PdbFileName;
+
+  //
+  // Initialize count of unwond stacks
+  //
+  *UnwondStacksCount = 0;
+
+  //
+  // Set current RIP address
+  //
+  Rip = SystemContext.SystemContextX64->Rip;
+
+  //
+  // Set current frame pointer address
+  //
+  Rbp = SystemContext.SystemContextX64->Rbp;
+
+  //
+  // Get initial PE/COFF image base address from current RIP
+  //
+  ImageBase = PeCoffSearchImageBase (Rip);
+  if (ImageBase == 0) {
+    InternalPrintMessage ("!!!! Could not find backtrace information. !!!!");
+    return;
+  }
+
+  //
+  // Get PDB file name from initial PE/COFF image
+  //
+  GetPdbFileName (ImageBase, NULL, &PdbFileName);
+
+  //
+  // Print out back trace
+  //
+  InternalPrintMessage ("\nBack trace:\n");
+
+  for (;;) {
+    //
+    // Print stack frame in the following format:
+    //
+    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName> | ????]
+    //
+    InternalPrintMessage (
+      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
+      *UnwondStacksCount,
+      Rip,
+      ImageBase,
+      Rip - ImageBase - 1,
+      Rbp,
+      PdbFileName
+      );
+
+    //
+    // Set RIP with return address from current stack frame
+    //
+    Rip = *(UINT64 *)((UINTN)Rbp + 8);
+
+    //
+    // Check if RIP is within another PE/COFF image base address
+    //
+    if (Rip < ImageBase) {
+      //
+      // Search for the respective PE/COFF image based on RIP
+      //
+      ImageBase = PeCoffSearchImageBase (Rip);
+      if (ImageBase == 0) {
+        //
+        // Stop stack trace
+        //
+        break;
+      }
+
+      //
+      // Get PDB file name
+      //
+      GetPdbFileName (ImageBase, NULL, &PdbFileName);
+    }
+
+    //
+    // Unwind the stack
+    //
+    Rbp = *(UINT64 *)(UINTN)Rbp;
+
+    //
+    // Increment count of unwond stacks
+    //
+    (*UnwondStacksCount)++;
+  }
+}
+
+/**
   Display CPU information.
 
   @param ExceptionType  Exception type.
@@ -254,9 +578,25 @@ DumpImageAndCpuContent (
   IN EFI_SYSTEM_CONTEXT   SystemContext
   )
 {
+  INTN UnwondStacksCount;
+
+  //
+  // Dump CPU context
+  //
   DumpCpuContext (ExceptionType, SystemContext);
+
+  //
+  // Dump stack trace
+  //
+  DumpStackTrace (SystemContext, &UnwondStacksCount);
+
+  //
+  // Dump image module names
+  //
+  DumpImageModuleNames (SystemContext);
+
   //
-  // Dump module image base and module entry point by RIP
+  // Dump stack contents
   //
-  DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
+  DumpStackContents (SystemContext.SystemContextX64->Rsp, UnwondStacksCount);
 }
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 82+ messages in thread

* Re: [RFC 0/1] Stack trace support in X64 exception handling
  2017-11-14 12:47 [RFC 0/1] Stack trace support in X64 exception handling Paulo Alcantara
  2017-11-14 12:47 ` [RFC 1/1] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support Paulo Alcantara
@ 2017-11-14 13:21 ` Paulo Alcantara
  2017-11-14 14:03   ` 答复: " Fan Jeff
  2017-11-16  1:18 ` [RFC v2 0/3] " Paulo Alcantara
  2 siblings, 1 reply; 82+ messages in thread
From: Paulo Alcantara @ 2017-11-14 13:21 UTC (permalink / raw)
  To: edk2-devel; +Cc: Rick Bramley, Andrew Fish, Eric Dong, Laszlo Ersek

[-- Attachment #1: Type: text/plain, Size: 1296 bytes --]

Hi,

On 14/11/2017 10:47, Paulo Alcantara wrote:
> Hi,
> 
> This series adds stack trace support during a X64 CPU exception.
> 
> Informations like back trace, stack contents and image module names
> (that were part of the call stack) will be dumped out.
> 
> We already have such support in ARM/AArch64 (IIRC) exception handling
> (thanks to Ard), and then I thought we'd also deserve it in X64 and
> IA-32 platforms.
> 
> What do you think guys?
> 
> BTW, I've tested this only with OVMF (X64 only), using:
>    - gcc-6.3.0, GCC5, NOOPT
> 
> Any other tests  would be really appreciable.

I've attached a file to show you how the trace would look like.

Thanks!
Paulo

> 
> Thanks!
> Paulo
> 
> Repo:   https://github.com/pcacjr/edk2.git
> Branch: stacktrace_x64
> 
> Cc: Rick Bramley <richard.bramley@hp.com>
> Cc: Andrew Fish <afish@apple.com>
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Contributed-under: TianoCore Contribution Agreement 1.1
> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> ---
> 
> Paulo Alcantara (1):
>    UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
> 
>   UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 344 +++++++++++++++++++-
>   1 file changed, 342 insertions(+), 2 deletions(-)
> 


[-- Attachment #2: trace.txt --]
[-- Type: text/plain, Size: 4685 bytes --]

!!!! X64 Exception Type - 0E(#PF - Page-Fault)  CPU Apic ID - 00000000 !!!!
ExceptionData - 0000000000000002  I:0 R:0 U:0 W:1 P:0 PK:0 S:0
RIP  - 000000007E510F7F, CS  - 0000000000000038, RFLAGS - 0000000000010202
RAX  - 0000000000000000, RCX - 000000007EA01318, RDX - 000000007F6EE018
RBX  - 0000000000810248, RSP - 000000007F762C70, RBP - 000000007F762CB0
RSI  - 0000000000000007, RDI - 000000007EA01418
R8   - 000000007E513A88, R9  - 000000007EA01798, R10 - 0000000000000036
R11  - 00000000000000D7, R12 - 0000000000000000, R13 - 0000000000000000
R14  - 0000000000000000, R15 - 0000000000000000
DS   - 0000000000000030, ES  - 0000000000000030, FS  - 0000000000000030
GS   - 0000000000000030, SS  - 0000000000000030
CR0  - 0000000080010033, CR2 - 0000000000000000, CR3 - 000000007F701000
CR4  - 0000000000000668, CR8 - 0000000000000000
DR0  - 0000000000000000, DR1 - 0000000000000000, DR2 - 0000000000000000
DR3  - 0000000000000000, DR6 - 00000000FFFF0FF0, DR7 - 0000000000000400
GDTR - 000000007F6EEA98 0000000000000047, LDTR - 0000000000000000
IDTR - 000000007EEF2018 0000000000000FFF,   TR - 0000000000000000
FXSAVE_STATE - 000000007F7628D0

Back trace:
0 0x000000007E510F7F @ 0x000000007E509000+0x7F7E (0x000000007F762CB0) in PartitionDxe.dll
1 0x000000007E51135D @ 0x000000007E509000+0x835C (0x000000007F762CE0) in PartitionDxe.dll
2 0x000000007E50C116 @ 0x000000007E509000+0x3115 (0x000000007F762D20) in PartitionDxe.dll
3 0x000000007F776972 @ 0x000000007E509000+0x126D971 (0x000000007F762DB0) in PartitionDxe.dll
4 0x000000007F78EE08 @ 0x000000007E509000+0x1285E07 (0x000000007F762E30) in PartitionDxe.dll
5 0x000000007F791343 @ 0x000000007E509000+0x1288342 (0x000000007F762F60) in PartitionDxe.dll
6 0x000000007F791AC7 @ 0x000000007E509000+0x1288AC6 (0x000000007F762F90) in PartitionDxe.dll
7 0x000000007F767DDB @ 0x000000007E509000+0x125EDDA (0x000000007F762FC0) in PartitionDxe.dll
8 0x000000007F7DF75F @ 0x000000007E509000+0x12D675E (0x000000007B7DC840) in PartitionDxe.dll
9 0x000000007F7E5546 @ 0x000000007E509000+0x12DC545 (0x000000007B7DC8C0) in PartitionDxe.dll
10 0x000000007F7E4312 @ 0x000000007E509000+0x12DB311 (0x000000007B7DCA30) in PartitionDxe.dll
11 0x000000007F7F0DB9 @ 0x000000007E509000+0x12E7DB8 (0x000000007B7DCF80) in PartitionDxe.dll
12 0x00000000008286E9 @ 0x0000000000820140+0x85A8 (0x000000007B7DD4D0) in PeiCore.dll
13 0x000000000083092F @ 0x0000000000820140+0x107EE (0x0000000000817600) in PeiCore.dll
14 0x0000000000831574 @ 0x0000000000820140+0x11433 (0x00000000008176D0) in PeiCore.dll
15 0x0000000000828D9B @ 0x0000000000820140+0x8C5A (0x0000000000817C20) in PeiCore.dll
16 0x000000000083238A @ 0x0000000000820140+0x12249 (0x0000000000817C50) in PeiCore.dll
17 0x0000000000824312 @ 0x0000000000820140+0x41D1 (0x0000000000817C80) in PeiCore.dll
18 0x00000000FFFD4291 @ 0x0000000000820140+0xFF7B4150 (0x0000000000817CE0) in PeiCore.dll
19 0x00000000FFFCF578 @ 0x0000000000820140+0xFF7AF437 (0x0000000000817D10) in PeiCore.dll
20 0x00000000FFFD422C @ 0x0000000000820140+0xFF7B40EB (0x0000000000817FD0) in PeiCore.dll
21 0x00000000FFFD4489 @ 0x0000000000820140+0xFF7B4348 (0x00000000FFFCC000) in PeiCore.dll

PartitionDxe.dll (ImageBase=0x000000007E509000, EntryPoint=0x000000007E50C01F):
/home/pcacjr/src/edk2.git/Build/OvmfX64/NOOPT_GCC5/X64/MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe/DEBUG/PartitionDxe.dll
PeiCore.dll (ImageBase=0x0000000000820140, EntryPoint=0x00000000008242EC):
/home/pcacjr/src/edk2.git/Build/OvmfX64/NOOPT_GCC5/X64/MdeModulePkg/Core/Pei/PeiMain/DEBUG/PeiCore.dll

Stack dump:
0x000000007F762C70: 000000007E5137E0 0000000000000000
0x000000007F762C80: 000000007E513A88 0000000000000100
0x000000007F762C90: 000000007F762CB0 0000000000000000
0x000000007F762CA0: 000000007F762CE0 0000000000000000
0x000000007F762CB0: 000000007F762CE0 000000007E51135D
0x000000007F762CC0: 000000007EA01318 000000007F6EE018
0x000000007F762CD0: 000000077F776852 0000000000000000
0x000000007F762CE0: 000000007F762D20 000000007E50C116
0x000000007F762CF0: 000000007EA01318 000000007F6EE018
0x000000007F762D00: 0000000000000000 0000000000000000
0x000000007F762D10: 0000000000000000 0000000000000000
0x000000007F762D20: 000000007F762DB0 000000007F776972
0x000000007F762D30: 000000007EA01318 000000007F6EE018
0x000000007F762D40: 0000000000000000 0000000000000004
0x000000007F762D50: 000000007F79A1A8 000000007F79AF90
0x000000007F762D60: 000000007F762DB0 0000000000000000
0x000000007F762D70: 000000007F79A180 00000000000000C8
0x000000007F762D80: 0000000000000000 000000007EA01418
0x000000007F762D90: 000000007EA01318 0000000000000000
0x000000007F762DA0: 000000007F79A1A8 000000007F79AF90
0x000000007F762DB0: 000000007F762E30 000000007F78EE08

^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC 1/1] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
  2017-11-14 12:47 ` [RFC 1/1] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support Paulo Alcantara
@ 2017-11-14 14:01   ` Andrew Fish
  2017-11-14 14:26     ` 答复: " Fan Jeff
  2017-11-14 15:30     ` Paulo Alcantara
  2017-12-29  3:48   ` [RFC v4 0/6] Stack trace support in X64 exception handling Paulo Alcantara
  1 sibling, 2 replies; 82+ messages in thread
From: Andrew Fish @ 2017-11-14 14:01 UTC (permalink / raw)
  To: Paulo Alcantara; +Cc: edk2-devel, Laszlo Ersek, Eric Dong

Paulo,

Cool feature. How does this code deal with VC++ that code does not store the frame pointer and requires symbols to unwind. 

Also on the page fault you can print the fault address since it is in CR2. 

It should be possible to post process the text file and make a symbolicated backtrace. 

Thanks,

Andrew Fish

> On Nov 14, 2017, at 4:47 AM, Paulo Alcantara <pcacjr@zytor.com> wrote:
> 
> This patch adds stack trace support during a X64 CPU exception.
> 
> It will dump out back trace, stack contents as well as image module
> names that were part of the call stack.
> 
> Contributed-under: TianoCore Contribution Agreement 1.1
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> ---
> UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 344 +++++++++++++++++++-
> 1 file changed, 342 insertions(+), 2 deletions(-)
> 
> diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> index 65f0cff680..7048247be3 100644
> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> @@ -14,6 +14,11 @@
> 
> #include "CpuExceptionCommon.h"
> 
> +//
> +// Unknown PDB file name
> +//
> +GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
> +
> /**
>   Return address map of exception handler template so that C code can generate
>   exception tables.
> @@ -243,6 +248,325 @@ DumpCpuContext (
> }
> 
> /**
> +  Dump stack contents.
> +
> +  @param[in]  ImageBase            Base address of PE/COFF image.
> +  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
> +  @param[out] PdbFileName          File name of PDB file.
> +**/
> +STATIC
> +VOID
> +GetPdbFileName (
> +  IN  UINTN    ImageBase,
> +  OUT CHAR8    **PdbAbsoluteFilePath,
> +  OUT CHAR8    **PdbFileName
> +  )
> +{
> +  VOID   *PdbPointer;
> +  CHAR8  *Str;
> +
> +  //
> +  // Get PDB file name from PE/COFF image
> +  //
> +  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
> +  if (PdbPointer == NULL) {
> +    //
> +    // No PDB file name found. Set it to an unknown file name.
> +    //
> +    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
> +    if (PdbAbsoluteFilePath != NULL) {
> +      *PdbAbsoluteFilePath = NULL;
> +    }
> +  } else {
> +    //
> +    // Get file name portion out of PDB file in PE/COFF image
> +    //
> +    Str = (CHAR8 *)((UINTN)PdbPointer +
> +                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
> +    for (; *Str != '/' && *Str != '\\'; Str--) {
> +      ;
> +    }
> +
> +    //
> +    // Set PDB file name (also skip trailing path separator: '/' or '\\')
> +    //
> +    *PdbFileName = Str + 1;
> +
> +    if (PdbAbsoluteFilePath != NULL) {
> +      //
> +      // Set absolute file path of PDB file
> +      //
> +      *PdbAbsoluteFilePath = PdbPointer;
> +    }
> +  }
> +}
> +
> +/**
> +  Dump stack contents.
> +
> +  @param[in]  CurrentRsp         Current stack pointer address.
> +  @param[in]  UnwondStacksCount  Count of unwond stack frames.
> +**/
> +STATIC
> +VOID
> +DumpStackContents (
> +  IN UINT64  CurrentRsp,
> +  IN INTN    UnwondStacksCount
> +  )
> +{
> +  if (UnwondStacksCount == 0) {
> +    return;
> +  }
> +
> +  //
> +  // Dump out stack contents
> +  //
> +  InternalPrintMessage ("\nStack dump:\n");
> +  while (UnwondStacksCount-- > 0) {
> +    InternalPrintMessage (
> +      "0x%016lx: %016lx %016lx\n",
> +      CurrentRsp,
> +      *(UINT64 *)CurrentRsp,
> +      *(UINT64 *)((UINTN)CurrentRsp + 8)
> +      );
> +
> +    //
> +    // As per Microsoft x64 ABI, the stack pointer must be aligned on a 16 byte
> +    // boundary.
> +    //
> +    CurrentRsp = CurrentRsp + 16;
> +  }
> +}
> +
> +/**
> +  Dump all image module names from call stack.
> +
> +  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
> +**/
> +STATIC
> +VOID
> +DumpImageModuleNames (
> +  IN EFI_SYSTEM_CONTEXT   SystemContext
> +  )
> +{
> +  EFI_STATUS  Status;
> +  UINT64      Rip;
> +  UINTN       ImageBase;
> +  VOID        *EntryPoint;
> +  CHAR8       *PdbAbsoluteFilePath;
> +  CHAR8       *PdbFileName;
> +  UINT64      Rbp;
> +
> +  //
> +  // Set current RIP address
> +  //
> +  Rip = SystemContext.SystemContextX64->Rip;
> +
> +  //
> +  // Set current frame pointer address
> +  //
> +  Rbp = SystemContext.SystemContextX64->Rbp;
> +
> +  //
> +  // Get initial PE/COFF image base address from current RIP
> +  //
> +  ImageBase = PeCoffSearchImageBase (Rip);
> +  if (ImageBase == 0) {
> +    InternalPrintMessage ("!!!! Could not find image module names. !!!!");
> +    return;
> +  }
> +
> +  //
> +  // Get initial PE/COFF image's entry point
> +  //
> +  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
> +  if (EFI_ERROR (Status)) {
> +    EntryPoint = NULL;
> +  }
> +
> +  //
> +  // Get file name and absolute path of initial PDB file
> +  //
> +  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
> +
> +  //
> +  // Print out initial image module name (if any)
> +  //
> +  if (PdbAbsoluteFilePath != NULL) {
> +    InternalPrintMessage (
> +      "\n%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
> +      PdbFileName,
> +      ImageBase,
> +      (UINTN)EntryPoint
> +      );
> +    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
> +  }
> +
> +  //
> +  // Walk through call stack and find next module names
> +  //
> +  for (;;) {
> +    //
> +    // Set RIP with return address from current stack frame
> +    //
> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
> +
> +    //
> +    // Check if RIP is within another PE/COFF image base address
> +    //
> +    if (Rip < ImageBase) {
> +      //
> +      // Search for the respective PE/COFF image based on RIP
> +      //
> +      ImageBase = PeCoffSearchImageBase (Rip);
> +      if (ImageBase == 0) {
> +        //
> +        // Stop stack trace
> +        //
> +        break;
> +      }
> +
> +      //
> +      // Get PE/COFF image's entry point
> +      //
> +      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
> +      if (EFI_ERROR (Status)) {
> +        EntryPoint = NULL;
> +      }
> +
> +      //
> +      // Get file name and absolute path of PDB file
> +      //
> +      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
> +
> +      //
> +      // Print out image module name (if any)
> +      //
> +      if (PdbAbsoluteFilePath != NULL) {
> +        InternalPrintMessage (
> +          "%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
> +          PdbFileName,
> +          ImageBase,
> +          (UINTN)EntryPoint
> +          );
> +        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
> +      }
> +    }
> +
> +    //
> +    // Unwind the stack
> +    //
> +    Rbp = *(UINT64 *)(UINTN)Rbp;
> +  }
> +}
> +
> +/**
> +  Dump stack trace.
> +
> +  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
> +  @param[out] UnwondStacksCount  Count of unwond stack frames.
> +**/
> +STATIC
> +VOID
> +DumpStackTrace (
> +  IN  EFI_SYSTEM_CONTEXT   SystemContext,
> +  OUT INTN                 *UnwondStacksCount
> +  )
> +{
> +  UINT64  Rip;
> +  UINT64  Rbp;
> +  UINTN   ImageBase;
> +  CHAR8   *PdbFileName;
> +
> +  //
> +  // Initialize count of unwond stacks
> +  //
> +  *UnwondStacksCount = 0;
> +
> +  //
> +  // Set current RIP address
> +  //
> +  Rip = SystemContext.SystemContextX64->Rip;
> +
> +  //
> +  // Set current frame pointer address
> +  //
> +  Rbp = SystemContext.SystemContextX64->Rbp;
> +
> +  //
> +  // Get initial PE/COFF image base address from current RIP
> +  //
> +  ImageBase = PeCoffSearchImageBase (Rip);
> +  if (ImageBase == 0) {
> +    InternalPrintMessage ("!!!! Could not find backtrace information. !!!!");
> +    return;
> +  }
> +
> +  //
> +  // Get PDB file name from initial PE/COFF image
> +  //
> +  GetPdbFileName (ImageBase, NULL, &PdbFileName);
> +
> +  //
> +  // Print out back trace
> +  //
> +  InternalPrintMessage ("\nBack trace:\n");
> +
> +  for (;;) {
> +    //
> +    // Print stack frame in the following format:
> +    //
> +    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName> | ????]
> +    //
> +    InternalPrintMessage (
> +      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
> +      *UnwondStacksCount,
> +      Rip,
> +      ImageBase,
> +      Rip - ImageBase - 1,
> +      Rbp,
> +      PdbFileName
> +      );
> +
> +    //
> +    // Set RIP with return address from current stack frame
> +    //
> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
> +
> +    //
> +    // Check if RIP is within another PE/COFF image base address
> +    //
> +    if (Rip < ImageBase) {
> +      //
> +      // Search for the respective PE/COFF image based on RIP
> +      //
> +      ImageBase = PeCoffSearchImageBase (Rip);
> +      if (ImageBase == 0) {
> +        //
> +        // Stop stack trace
> +        //
> +        break;
> +      }
> +
> +      //
> +      // Get PDB file name
> +      //
> +      GetPdbFileName (ImageBase, NULL, &PdbFileName);
> +    }
> +
> +    //
> +    // Unwind the stack
> +    //
> +    Rbp = *(UINT64 *)(UINTN)Rbp;
> +
> +    //
> +    // Increment count of unwond stacks
> +    //
> +    (*UnwondStacksCount)++;
> +  }
> +}
> +
> +/**
>   Display CPU information.
> 
>   @param ExceptionType  Exception type.
> @@ -254,9 +578,25 @@ DumpImageAndCpuContent (
>   IN EFI_SYSTEM_CONTEXT   SystemContext
>   )
> {
> +  INTN UnwondStacksCount;
> +
> +  //
> +  // Dump CPU context
> +  //
>   DumpCpuContext (ExceptionType, SystemContext);
> +
> +  //
> +  // Dump stack trace
> +  //
> +  DumpStackTrace (SystemContext, &UnwondStacksCount);
> +
> +  //
> +  // Dump image module names
> +  //
> +  DumpImageModuleNames (SystemContext);
> +
>   //
> -  // Dump module image base and module entry point by RIP
> +  // Dump stack contents
>   //
> -  DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
> +  DumpStackContents (SystemContext.SystemContextX64->Rsp, UnwondStacksCount);
> }
> -- 
> 2.11.0
> 
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org
> https://lists.01.org/mailman/listinfo/edk2-devel



^ permalink raw reply	[flat|nested] 82+ messages in thread

* 答复: [RFC 0/1] Stack trace support in X64 exception handling
  2017-11-14 13:21 ` [RFC 0/1] " Paulo Alcantara
@ 2017-11-14 14:03   ` Fan Jeff
  2017-11-14 14:12     ` 答复: " Fan Jeff
  2017-11-14 15:37     ` Paulo Alcantara
  0 siblings, 2 replies; 82+ messages in thread
From: Fan Jeff @ 2017-11-14 14:03 UTC (permalink / raw)
  To: Paulo Alcantara, edk2-devel@lists.01.org
  Cc: Rick Bramley, Laszlo Ersek, Andrew Fish, Eric Dong

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="gb2312", Size: 2239 bytes --]

Paul,

I like this feature very much. Actually, I did some POC one year ago but I did finalize it.

In my POC, I could use EBP to tack the stack frame on IAS32 arch.
But for x64, I tried to use ¨Ckeepexceptiontable flag to explain stack frame from the debug section of image.
I may workson MSFT toolchain, but it did now work well for GCC toolchain.

I think Eric could help to verify MSFT for your patch. If it works well, that¡¯s will be great!

Say again, I like this feature!!!:-)

Thanks!
Jeff


·¢¼þÈË: Paulo Alcantara<mailto:pcacjr@zytor.com>
·¢ËÍʱ¼ä: 2017Äê11ÔÂ14ÈÕ 21:23
ÊÕ¼þÈË: edk2-devel@lists.01.org<mailto:edk2-devel@lists.01.org>
³­ËÍ: Rick Bramley<mailto:richard.bramley@hp.com>; Laszlo Ersek<mailto:lersek@redhat.com>; Andrew Fish<mailto:afish@apple.com>; Eric Dong<mailto:eric.dong@intel.com>
Ö÷Ìâ: Re: [edk2] [RFC 0/1] Stack trace support in X64 exception handling

Hi,

On 14/11/2017 10:47, Paulo Alcantara wrote:
> Hi,
>
> This series adds stack trace support during a X64 CPU exception.
>
> Informations like back trace, stack contents and image module names
> (that were part of the call stack) will be dumped out.
>
> We already have such support in ARM/AArch64 (IIRC) exception handling
> (thanks to Ard), and then I thought we'd also deserve it in X64 and
> IA-32 platforms.
>
> What do you think guys?
>
> BTW, I've tested this only with OVMF (X64 only), using:
>    - gcc-6.3.0, GCC5, NOOPT
>
> Any other tests  would be really appreciable.

I've attached a file to show you how the trace would look like.

Thanks!
Paulo

>
> Thanks!
> Paulo
>
> Repo:   https://github.com/pcacjr/edk2.git
> Branch: stacktrace_x64
>
> Cc: Rick Bramley <richard.bramley@hp.com>
> Cc: Andrew Fish <afish@apple.com>
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Contributed-under: TianoCore Contribution Agreement 1.1
> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> ---
>
> Paulo Alcantara (1):
>    UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
>
>   UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 344 +++++++++++++++++++-
>   1 file changed, 342 insertions(+), 2 deletions(-)
>


^ permalink raw reply	[flat|nested] 82+ messages in thread

* 答复: 答复: [RFC 0/1] Stack trace support in X64 exception handling
  2017-11-14 14:03   ` 答复: " Fan Jeff
@ 2017-11-14 14:12     ` Fan Jeff
  2017-11-14 15:37     ` Paulo Alcantara
  1 sibling, 0 replies; 82+ messages in thread
From: Fan Jeff @ 2017-11-14 14:12 UTC (permalink / raw)
  To: Paulo Alcantara, edk2-devel@lists.01.org
  Cc: Rick Bramley, Laszlo Ersek, Andrew Fish, Eric Dong

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="gb2312", Size: 2718 bytes --]

Paul,



Sorry, correct some import words in my last mail. ¡°I did NOT finalize my POC¡±.:-)



Jeff



________________________________
From: edk2-devel <edk2-devel-bounces@lists.01.org> on behalf of Fan Jeff <vanjeff_919@hotmail.com>
Sent: Tuesday, November 14, 2017 10:03:20 PM
To: Paulo Alcantara; edk2-devel@lists.01.org
Cc: Rick Bramley; Laszlo Ersek; Andrew Fish; Eric Dong
Subject: [edk2] ´ð¸´: [RFC 0/1] Stack trace support in X64 exception handling

Paul,

I like this feature very much. Actually, I did some POC one year ago but I did finalize it.

In my POC, I could use EBP to tack the stack frame on IAS32 arch.
But for x64, I tried to use ¨Ckeepexceptiontable flag to explain stack frame from the debug section of image.
I may workson MSFT toolchain, but it did now work well for GCC toolchain.

I think Eric could help to verify MSFT for your patch. If it works well, that¡¯s will be great!

Say again, I like this feature!!!:-)

Thanks!
Jeff


·¢¼þÈË: Paulo Alcantara<mailto:pcacjr@zytor.com>
·¢ËÍʱ¼ä: 2017Äê11ÔÂ14ÈÕ 21:23
ÊÕ¼þÈË: edk2-devel@lists.01.org<mailto:edk2-devel@lists.01.org>
³­ËÍ: Rick Bramley<mailto:richard.bramley@hp.com>; Laszlo Ersek<mailto:lersek@redhat.com>; Andrew Fish<mailto:afish@apple.com>; Eric Dong<mailto:eric.dong@intel.com>
Ö÷Ìâ: Re: [edk2] [RFC 0/1] Stack trace support in X64 exception handling

Hi,

On 14/11/2017 10:47, Paulo Alcantara wrote:
> Hi,
>
> This series adds stack trace support during a X64 CPU exception.
>
> Informations like back trace, stack contents and image module names
> (that were part of the call stack) will be dumped out.
>
> We already have such support in ARM/AArch64 (IIRC) exception handling
> (thanks to Ard), and then I thought we'd also deserve it in X64 and
> IA-32 platforms.
>
> What do you think guys?
>
> BTW, I've tested this only with OVMF (X64 only), using:
>    - gcc-6.3.0, GCC5, NOOPT
>
> Any other tests  would be really appreciable.

I've attached a file to show you how the trace would look like.

Thanks!
Paulo

>
> Thanks!
> Paulo
>
> Repo:   https://github.com/pcacjr/edk2.git
> Branch: stacktrace_x64
>
> Cc: Rick Bramley <richard.bramley@hp.com>
> Cc: Andrew Fish <afish@apple.com>
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Contributed-under: TianoCore Contribution Agreement 1.1
> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> ---
>
> Paulo Alcantara (1):
>    UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
>
>   UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 344 +++++++++++++++++++-
>   1 file changed, 342 insertions(+), 2 deletions(-)
>


^ permalink raw reply	[flat|nested] 82+ messages in thread

* 答复: [RFC 1/1] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
  2017-11-14 14:01   ` Andrew Fish
@ 2017-11-14 14:26     ` Fan Jeff
  2017-11-14 14:38       ` Andrew Fish
  2017-11-14 15:30     ` Paulo Alcantara
  1 sibling, 1 reply; 82+ messages in thread
From: Fan Jeff @ 2017-11-14 14:26 UTC (permalink / raw)
  To: Andrew Fish, Paulo Alcantara
  Cc: edk2-devel@lists.01.org, Laszlo Ersek, Eric Dong

Andrew,

We could use he EIP offset in Paul’s trace message and work with the generated map file under debug directory for debug trace.

Jeff

发件人: Andrew Fish<mailto:afish@apple.com>
发送时间: 2017年11月14日 22:01
收件人: Paulo Alcantara<mailto:pcacjr@zytor.com>
抄送: edk2-devel@lists.01.org<mailto:edk2-devel@lists.01.org>; Laszlo Ersek<mailto:lersek@redhat.com>; Eric Dong<mailto:eric.dong@intel.com>
主题: Re: [edk2] [RFC 1/1] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support

Paulo,

Cool feature. How does this code deal with VC++ that code does not store the frame pointer and requires symbols to unwind.

Also on the page fault you can print the fault address since it is in CR2.

It should be possible to post process the text file and make a symbolicated backtrace.

Thanks,

Andrew Fish

> On Nov 14, 2017, at 4:47 AM, Paulo Alcantara <pcacjr@zytor.com> wrote:
>
> This patch adds stack trace support during a X64 CPU exception.
>
> It will dump out back trace, stack contents as well as image module
> names that were part of the call stack.
>
> Contributed-under: TianoCore Contribution Agreement 1.1
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> ---
> UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 344 +++++++++++++++++++-
> 1 file changed, 342 insertions(+), 2 deletions(-)
>
> diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> index 65f0cff680..7048247be3 100644
> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> @@ -14,6 +14,11 @@
>
> #include "CpuExceptionCommon.h"
>
> +//
> +// Unknown PDB file name
> +//
> +GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
> +
> /**
>   Return address map of exception handler template so that C code can generate
>   exception tables.
> @@ -243,6 +248,325 @@ DumpCpuContext (
> }
>
> /**
> +  Dump stack contents.
> +
> +  @param[in]  ImageBase            Base address of PE/COFF image.
> +  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
> +  @param[out] PdbFileName          File name of PDB file.
> +**/
> +STATIC
> +VOID
> +GetPdbFileName (
> +  IN  UINTN    ImageBase,
> +  OUT CHAR8    **PdbAbsoluteFilePath,
> +  OUT CHAR8    **PdbFileName
> +  )
> +{
> +  VOID   *PdbPointer;
> +  CHAR8  *Str;
> +
> +  //
> +  // Get PDB file name from PE/COFF image
> +  //
> +  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
> +  if (PdbPointer == NULL) {
> +    //
> +    // No PDB file name found. Set it to an unknown file name.
> +    //
> +    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
> +    if (PdbAbsoluteFilePath != NULL) {
> +      *PdbAbsoluteFilePath = NULL;
> +    }
> +  } else {
> +    //
> +    // Get file name portion out of PDB file in PE/COFF image
> +    //
> +    Str = (CHAR8 *)((UINTN)PdbPointer +
> +                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
> +    for (; *Str != '/' && *Str != '\\'; Str--) {
> +      ;
> +    }
> +
> +    //
> +    // Set PDB file name (also skip trailing path separator: '/' or '\\')
> +    //
> +    *PdbFileName = Str + 1;
> +
> +    if (PdbAbsoluteFilePath != NULL) {
> +      //
> +      // Set absolute file path of PDB file
> +      //
> +      *PdbAbsoluteFilePath = PdbPointer;
> +    }
> +  }
> +}
> +
> +/**
> +  Dump stack contents.
> +
> +  @param[in]  CurrentRsp         Current stack pointer address.
> +  @param[in]  UnwondStacksCount  Count of unwond stack frames.
> +**/
> +STATIC
> +VOID
> +DumpStackContents (
> +  IN UINT64  CurrentRsp,
> +  IN INTN    UnwondStacksCount
> +  )
> +{
> +  if (UnwondStacksCount == 0) {
> +    return;
> +  }
> +
> +  //
> +  // Dump out stack contents
> +  //
> +  InternalPrintMessage ("\nStack dump:\n");
> +  while (UnwondStacksCount-- > 0) {
> +    InternalPrintMessage (
> +      "0x%016lx: %016lx %016lx\n",
> +      CurrentRsp,
> +      *(UINT64 *)CurrentRsp,
> +      *(UINT64 *)((UINTN)CurrentRsp + 8)
> +      );
> +
> +    //
> +    // As per Microsoft x64 ABI, the stack pointer must be aligned on a 16 byte
> +    // boundary.
> +    //
> +    CurrentRsp = CurrentRsp + 16;
> +  }
> +}
> +
> +/**
> +  Dump all image module names from call stack.
> +
> +  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
> +**/
> +STATIC
> +VOID
> +DumpImageModuleNames (
> +  IN EFI_SYSTEM_CONTEXT   SystemContext
> +  )
> +{
> +  EFI_STATUS  Status;
> +  UINT64      Rip;
> +  UINTN       ImageBase;
> +  VOID        *EntryPoint;
> +  CHAR8       *PdbAbsoluteFilePath;
> +  CHAR8       *PdbFileName;
> +  UINT64      Rbp;
> +
> +  //
> +  // Set current RIP address
> +  //
> +  Rip = SystemContext.SystemContextX64->Rip;
> +
> +  //
> +  // Set current frame pointer address
> +  //
> +  Rbp = SystemContext.SystemContextX64->Rbp;
> +
> +  //
> +  // Get initial PE/COFF image base address from current RIP
> +  //
> +  ImageBase = PeCoffSearchImageBase (Rip);
> +  if (ImageBase == 0) {
> +    InternalPrintMessage ("!!!! Could not find image module names. !!!!");
> +    return;
> +  }
> +
> +  //
> +  // Get initial PE/COFF image's entry point
> +  //
> +  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
> +  if (EFI_ERROR (Status)) {
> +    EntryPoint = NULL;
> +  }
> +
> +  //
> +  // Get file name and absolute path of initial PDB file
> +  //
> +  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
> +
> +  //
> +  // Print out initial image module name (if any)
> +  //
> +  if (PdbAbsoluteFilePath != NULL) {
> +    InternalPrintMessage (
> +      "\n%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
> +      PdbFileName,
> +      ImageBase,
> +      (UINTN)EntryPoint
> +      );
> +    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
> +  }
> +
> +  //
> +  // Walk through call stack and find next module names
> +  //
> +  for (;;) {
> +    //
> +    // Set RIP with return address from current stack frame
> +    //
> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
> +
> +    //
> +    // Check if RIP is within another PE/COFF image base address
> +    //
> +    if (Rip < ImageBase) {
> +      //
> +      // Search for the respective PE/COFF image based on RIP
> +      //
> +      ImageBase = PeCoffSearchImageBase (Rip);
> +      if (ImageBase == 0) {
> +        //
> +        // Stop stack trace
> +        //
> +        break;
> +      }
> +
> +      //
> +      // Get PE/COFF image's entry point
> +      //
> +      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
> +      if (EFI_ERROR (Status)) {
> +        EntryPoint = NULL;
> +      }
> +
> +      //
> +      // Get file name and absolute path of PDB file
> +      //
> +      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
> +
> +      //
> +      // Print out image module name (if any)
> +      //
> +      if (PdbAbsoluteFilePath != NULL) {
> +        InternalPrintMessage (
> +          "%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
> +          PdbFileName,
> +          ImageBase,
> +          (UINTN)EntryPoint
> +          );
> +        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
> +      }
> +    }
> +
> +    //
> +    // Unwind the stack
> +    //
> +    Rbp = *(UINT64 *)(UINTN)Rbp;
> +  }
> +}
> +
> +/**
> +  Dump stack trace.
> +
> +  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
> +  @param[out] UnwondStacksCount  Count of unwond stack frames.
> +**/
> +STATIC
> +VOID
> +DumpStackTrace (
> +  IN  EFI_SYSTEM_CONTEXT   SystemContext,
> +  OUT INTN                 *UnwondStacksCount
> +  )
> +{
> +  UINT64  Rip;
> +  UINT64  Rbp;
> +  UINTN   ImageBase;
> +  CHAR8   *PdbFileName;
> +
> +  //
> +  // Initialize count of unwond stacks
> +  //
> +  *UnwondStacksCount = 0;
> +
> +  //
> +  // Set current RIP address
> +  //
> +  Rip = SystemContext.SystemContextX64->Rip;
> +
> +  //
> +  // Set current frame pointer address
> +  //
> +  Rbp = SystemContext.SystemContextX64->Rbp;
> +
> +  //
> +  // Get initial PE/COFF image base address from current RIP
> +  //
> +  ImageBase = PeCoffSearchImageBase (Rip);
> +  if (ImageBase == 0) {
> +    InternalPrintMessage ("!!!! Could not find backtrace information. !!!!");
> +    return;
> +  }
> +
> +  //
> +  // Get PDB file name from initial PE/COFF image
> +  //
> +  GetPdbFileName (ImageBase, NULL, &PdbFileName);
> +
> +  //
> +  // Print out back trace
> +  //
> +  InternalPrintMessage ("\nBack trace:\n");
> +
> +  for (;;) {
> +    //
> +    // Print stack frame in the following format:
> +    //
> +    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName> | ????]
> +    //
> +    InternalPrintMessage (
> +      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
> +      *UnwondStacksCount,
> +      Rip,
> +      ImageBase,
> +      Rip - ImageBase - 1,
> +      Rbp,
> +      PdbFileName
> +      );
> +
> +    //
> +    // Set RIP with return address from current stack frame
> +    //
> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
> +
> +    //
> +    // Check if RIP is within another PE/COFF image base address
> +    //
> +    if (Rip < ImageBase) {
> +      //
> +      // Search for the respective PE/COFF image based on RIP
> +      //
> +      ImageBase = PeCoffSearchImageBase (Rip);
> +      if (ImageBase == 0) {
> +        //
> +        // Stop stack trace
> +        //
> +        break;
> +      }
> +
> +      //
> +      // Get PDB file name
> +      //
> +      GetPdbFileName (ImageBase, NULL, &PdbFileName);
> +    }
> +
> +    //
> +    // Unwind the stack
> +    //
> +    Rbp = *(UINT64 *)(UINTN)Rbp;
> +
> +    //
> +    // Increment count of unwond stacks
> +    //
> +    (*UnwondStacksCount)++;
> +  }
> +}
> +
> +/**
>   Display CPU information.
>
>   @param ExceptionType  Exception type.
> @@ -254,9 +578,25 @@ DumpImageAndCpuContent (
>   IN EFI_SYSTEM_CONTEXT   SystemContext
>   )
> {
> +  INTN UnwondStacksCount;
> +
> +  //
> +  // Dump CPU context
> +  //
>   DumpCpuContext (ExceptionType, SystemContext);
> +
> +  //
> +  // Dump stack trace
> +  //
> +  DumpStackTrace (SystemContext, &UnwondStacksCount);
> +
> +  //
> +  // Dump image module names
> +  //
> +  DumpImageModuleNames (SystemContext);
> +
>   //
> -  // Dump module image base and module entry point by RIP
> +  // Dump stack contents
>   //
> -  DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
> +  DumpStackContents (SystemContext.SystemContextX64->Rsp, UnwondStacksCount);
> }
> --
> 2.11.0
>
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org
> https://lists.01.org/mailman/listinfo/edk2-devel

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel


^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC 1/1] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
  2017-11-14 14:26     ` 答复: " Fan Jeff
@ 2017-11-14 14:38       ` Andrew Fish
  0 siblings, 0 replies; 82+ messages in thread
From: Andrew Fish @ 2017-11-14 14:38 UTC (permalink / raw)
  To: Fan Jeff
  Cc: Paulo Alcantara, edk2-devel@lists.01.org, Laszlo Ersek, Eric Dong


> On Nov 14, 2017, at 6:26 AM, Fan Jeff <vanjeff_919@hotmail.com> wrote:
> 
> Andrew,
>  
> We could use he EIP offset in Paul’s trace message and work with the generated map file under debug directory for debug trace.

It would also be possible to use gdb. 

Given
0 0x000000007E510F7F @ 0x000000007E509000+0x7F7E (0x000000007F762CB0) in PartitionDxe.dll

If you load PartitionDxe.dll into gdb you can then do "l *0x7F7E" to dump the source. 

I'm mapping lldb behavior to gdb, but it should be close. 

Thanks,

Andrew Fish

>  
> Jeff
>  
> 发件人: Andrew Fish <mailto:afish@apple.com>
> 发送时间: 2017年11月14日 22:01
> 收件人: Paulo Alcantara <mailto:pcacjr@zytor.com>
> 抄送: edk2-devel@lists.01.org <mailto:edk2-devel@lists.01.org>; Laszlo Ersek <mailto:lersek@redhat.com>; Eric Dong <mailto:eric.dong@intel.com>
> 主题: Re: [edk2] [RFC 1/1] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
>  
> Paulo,
> 
> Cool feature. How does this code deal with VC++ that code does not store the frame pointer and requires symbols to unwind. 
> 
> Also on the page fault you can print the fault address since it is in CR2. 
> 
> It should be possible to post process the text file and make a symbolicated backtrace. 
> 
> Thanks,
> 
> Andrew Fish
> 
> > On Nov 14, 2017, at 4:47 AM, Paulo Alcantara <pcacjr@zytor.com> wrote:
> > 
> > This patch adds stack trace support during a X64 CPU exception.
> > 
> > It will dump out back trace, stack contents as well as image module
> > names that were part of the call stack.
> > 
> > Contributed-under: TianoCore Contribution Agreement 1.1
> > Cc: Eric Dong <eric.dong@intel.com>
> > Cc: Laszlo Ersek <lersek@redhat.com>
> > Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> > ---
> > UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 344 +++++++++++++++++++-
> > 1 file changed, 342 insertions(+), 2 deletions(-)
> > 
> > diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> > index 65f0cff680..7048247be3 100644
> > --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> > +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> > @@ -14,6 +14,11 @@
> > 
> > #include "CpuExceptionCommon.h"
> > 
> > +//
> > +// Unknown PDB file name
> > +//
> > +GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
> > +
> > /**
> >   Return address map of exception handler template so that C code can generate
> >   exception tables.
> > @@ -243,6 +248,325 @@ DumpCpuContext (
> > }
> > 
> > /**
> > +  Dump stack contents.
> > +
> > +  @param[in]  ImageBase            Base address of PE/COFF image.
> > +  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
> > +  @param[out] PdbFileName          File name of PDB file.
> > +**/
> > +STATIC
> > +VOID
> > +GetPdbFileName (
> > +  IN  UINTN    ImageBase,
> > +  OUT CHAR8    **PdbAbsoluteFilePath,
> > +  OUT CHAR8    **PdbFileName
> > +  )
> > +{
> > +  VOID   *PdbPointer;
> > +  CHAR8  *Str;
> > +
> > +  //
> > +  // Get PDB file name from PE/COFF image
> > +  //
> > +  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
> > +  if (PdbPointer == NULL) {
> > +    //
> > +    // No PDB file name found. Set it to an unknown file name.
> > +    //
> > +    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
> > +    if (PdbAbsoluteFilePath != NULL) {
> > +      *PdbAbsoluteFilePath = NULL;
> > +    }
> > +  } else {
> > +    //
> > +    // Get file name portion out of PDB file in PE/COFF image
> > +    //
> > +    Str = (CHAR8 *)((UINTN)PdbPointer +
> > +                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
> > +    for (; *Str != '/' && *Str != '\\'; Str--) {
> > +      ;
> > +    }
> > +
> > +    //
> > +    // Set PDB file name (also skip trailing path separator: '/' or '\\')
> > +    //
> > +    *PdbFileName = Str + 1;
> > +
> > +    if (PdbAbsoluteFilePath != NULL) {
> > +      //
> > +      // Set absolute file path of PDB file
> > +      //
> > +      *PdbAbsoluteFilePath = PdbPointer;
> > +    }
> > +  }
> > +}
> > +
> > +/**
> > +  Dump stack contents.
> > +
> > +  @param[in]  CurrentRsp         Current stack pointer address.
> > +  @param[in]  UnwondStacksCount  Count of unwond stack frames.
> > +**/
> > +STATIC
> > +VOID
> > +DumpStackContents (
> > +  IN UINT64  CurrentRsp,
> > +  IN INTN    UnwondStacksCount
> > +  )
> > +{
> > +  if (UnwondStacksCount == 0) {
> > +    return;
> > +  }
> > +
> > +  //
> > +  // Dump out stack contents
> > +  //
> > +  InternalPrintMessage ("\nStack dump:\n");
> > +  while (UnwondStacksCount-- > 0) {
> > +    InternalPrintMessage (
> > +      "0x%016lx: %016lx %016lx\n",
> > +      CurrentRsp,
> > +      *(UINT64 *)CurrentRsp,
> > +      *(UINT64 *)((UINTN)CurrentRsp + 8)
> > +      );
> > +
> > +    //
> > +    // As per Microsoft x64 ABI, the stack pointer must be aligned on a 16 byte
> > +    // boundary.
> > +    //
> > +    CurrentRsp = CurrentRsp + 16;
> > +  }
> > +}
> > +
> > +/**
> > +  Dump all image module names from call stack.
> > +
> > +  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
> > +**/
> > +STATIC
> > +VOID
> > +DumpImageModuleNames (
> > +  IN EFI_SYSTEM_CONTEXT   SystemContext
> > +  )
> > +{
> > +  EFI_STATUS  Status;
> > +  UINT64      Rip;
> > +  UINTN       ImageBase;
> > +  VOID        *EntryPoint;
> > +  CHAR8       *PdbAbsoluteFilePath;
> > +  CHAR8       *PdbFileName;
> > +  UINT64      Rbp;
> > +
> > +  //
> > +  // Set current RIP address
> > +  //
> > +  Rip = SystemContext.SystemContextX64->Rip;
> > +
> > +  //
> > +  // Set current frame pointer address
> > +  //
> > +  Rbp = SystemContext.SystemContextX64->Rbp;
> > +
> > +  //
> > +  // Get initial PE/COFF image base address from current RIP
> > +  //
> > +  ImageBase = PeCoffSearchImageBase (Rip);
> > +  if (ImageBase == 0) {
> > +    InternalPrintMessage ("!!!! Could not find image module names. !!!!");
> > +    return;
> > +  }
> > +
> > +  //
> > +  // Get initial PE/COFF image's entry point
> > +  //
> > +  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
> > +  if (EFI_ERROR (Status)) {
> > +    EntryPoint = NULL;
> > +  }
> > +
> > +  //
> > +  // Get file name and absolute path of initial PDB file
> > +  //
> > +  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
> > +
> > +  //
> > +  // Print out initial image module name (if any)
> > +  //
> > +  if (PdbAbsoluteFilePath != NULL) {
> > +    InternalPrintMessage (
> > +      "\n%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
> > +      PdbFileName,
> > +      ImageBase,
> > +      (UINTN)EntryPoint
> > +      );
> > +    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
> > +  }
> > +
> > +  //
> > +  // Walk through call stack and find next module names
> > +  //
> > +  for (;;) {
> > +    //
> > +    // Set RIP with return address from current stack frame
> > +    //
> > +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
> > +
> > +    //
> > +    // Check if RIP is within another PE/COFF image base address
> > +    //
> > +    if (Rip < ImageBase) {
> > +      //
> > +      // Search for the respective PE/COFF image based on RIP
> > +      //
> > +      ImageBase = PeCoffSearchImageBase (Rip);
> > +      if (ImageBase == 0) {
> > +        //
> > +        // Stop stack trace
> > +        //
> > +        break;
> > +      }
> > +
> > +      //
> > +      // Get PE/COFF image's entry point
> > +      //
> > +      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
> > +      if (EFI_ERROR (Status)) {
> > +        EntryPoint = NULL;
> > +      }
> > +
> > +      //
> > +      // Get file name and absolute path of PDB file
> > +      //
> > +      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
> > +
> > +      //
> > +      // Print out image module name (if any)
> > +      //
> > +      if (PdbAbsoluteFilePath != NULL) {
> > +        InternalPrintMessage (
> > +          "%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
> > +          PdbFileName,
> > +          ImageBase,
> > +          (UINTN)EntryPoint
> > +          );
> > +        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
> > +      }
> > +    }
> > +
> > +    //
> > +    // Unwind the stack
> > +    //
> > +    Rbp = *(UINT64 *)(UINTN)Rbp;
> > +  }
> > +}
> > +
> > +/**
> > +  Dump stack trace.
> > +
> > +  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
> > +  @param[out] UnwondStacksCount  Count of unwond stack frames.
> > +**/
> > +STATIC
> > +VOID
> > +DumpStackTrace (
> > +  IN  EFI_SYSTEM_CONTEXT   SystemContext,
> > +  OUT INTN                 *UnwondStacksCount
> > +  )
> > +{
> > +  UINT64  Rip;
> > +  UINT64  Rbp;
> > +  UINTN   ImageBase;
> > +  CHAR8   *PdbFileName;
> > +
> > +  //
> > +  // Initialize count of unwond stacks
> > +  //
> > +  *UnwondStacksCount = 0;
> > +
> > +  //
> > +  // Set current RIP address
> > +  //
> > +  Rip = SystemContext.SystemContextX64->Rip;
> > +
> > +  //
> > +  // Set current frame pointer address
> > +  //
> > +  Rbp = SystemContext.SystemContextX64->Rbp;
> > +
> > +  //
> > +  // Get initial PE/COFF image base address from current RIP
> > +  //
> > +  ImageBase = PeCoffSearchImageBase (Rip);
> > +  if (ImageBase == 0) {
> > +    InternalPrintMessage ("!!!! Could not find backtrace information. !!!!");
> > +    return;
> > +  }
> > +
> > +  //
> > +  // Get PDB file name from initial PE/COFF image
> > +  //
> > +  GetPdbFileName (ImageBase, NULL, &PdbFileName);
> > +
> > +  //
> > +  // Print out back trace
> > +  //
> > +  InternalPrintMessage ("\nBack trace:\n");
> > +
> > +  for (;;) {
> > +    //
> > +    // Print stack frame in the following format:
> > +    //
> > +    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName> | ????]
> > +    //
> > +    InternalPrintMessage (
> > +      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
> > +      *UnwondStacksCount,
> > +      Rip,
> > +      ImageBase,
> > +      Rip - ImageBase - 1,
> > +      Rbp,
> > +      PdbFileName
> > +      );
> > +
> > +    //
> > +    // Set RIP with return address from current stack frame
> > +    //
> > +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
> > +
> > +    //
> > +    // Check if RIP is within another PE/COFF image base address
> > +    //
> > +    if (Rip < ImageBase) {
> > +      //
> > +      // Search for the respective PE/COFF image based on RIP
> > +      //
> > +      ImageBase = PeCoffSearchImageBase (Rip);
> > +      if (ImageBase == 0) {
> > +        //
> > +        // Stop stack trace
> > +        //
> > +        break;
> > +      }
> > +
> > +      //
> > +      // Get PDB file name
> > +      //
> > +      GetPdbFileName (ImageBase, NULL, &PdbFileName);
> > +    }
> > +
> > +    //
> > +    // Unwind the stack
> > +    //
> > +    Rbp = *(UINT64 *)(UINTN)Rbp;
> > +
> > +    //
> > +    // Increment count of unwond stacks
> > +    //
> > +    (*UnwondStacksCount)++;
> > +  }
> > +}
> > +
> > +/**
> >   Display CPU information.
> > 
> >   @param ExceptionType  Exception type.
> > @@ -254,9 +578,25 @@ DumpImageAndCpuContent (
> >   IN EFI_SYSTEM_CONTEXT   SystemContext
> >   )
> > {
> > +  INTN UnwondStacksCount;
> > +
> > +  //
> > +  // Dump CPU context
> > +  //
> >   DumpCpuContext (ExceptionType, SystemContext);
> > +
> > +  //
> > +  // Dump stack trace
> > +  //
> > +  DumpStackTrace (SystemContext, &UnwondStacksCount);
> > +
> > +  //
> > +  // Dump image module names
> > +  //
> > +  DumpImageModuleNames (SystemContext);
> > +
> >   //
> > -  // Dump module image base and module entry point by RIP
> > +  // Dump stack contents
> >   //
> > -  DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
> > +  DumpStackContents (SystemContext.SystemContextX64->Rsp, UnwondStacksCount);
> > }
> > -- 
> > 2.11.0
> > 
> > _______________________________________________
> > edk2-devel mailing list
> > edk2-devel@lists.01.org
> > https://lists.01.org/mailman/listinfo/edk2-devel <https://lists.01.org/mailman/listinfo/edk2-devel>
> 
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org <mailto:edk2-devel@lists.01.org>
> https://lists.01.org/mailman/listinfo/edk2-devel <https://lists.01.org/mailman/listinfo/edk2-devel>


^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC 1/1] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
  2017-11-14 14:01   ` Andrew Fish
  2017-11-14 14:26     ` 答复: " Fan Jeff
@ 2017-11-14 15:30     ` Paulo Alcantara
  2017-11-14 16:51       ` Brian J. Johnson
  1 sibling, 1 reply; 82+ messages in thread
From: Paulo Alcantara @ 2017-11-14 15:30 UTC (permalink / raw)
  To: Andrew Fish; +Cc: edk2-devel, Laszlo Ersek, Eric Dong

Hi Andrew,

On 14/11/2017 12:01, Andrew Fish wrote:
C> Paulo,
> 
> Cool feature. How does this code deal with VC++ that code does not store the frame pointer and requires symbols to unwind.

I haven't tested in with MSVC, so I'd hope to get some help from Intel's 
guys to help supporting and testing it :-)

Regarding the symbols, I performed some tests by writing a userspace 
PE/COFF application [1] and tried to:

(a) handle the RUNTIME_FUNCTION entries in exception table (.pdata 
section) to find the function starting address and then its respective 
symbol name in COFF symbol table.

(b) Walk through UNWIND_INFO entries in .xdata section to figure out 
which CPU register a function used as a frame pointer or if it didn't 
use any at
all.

The problem I had with (a) was that the COFF symbol table is not mapped 
directory into the image's address space -- that is, the 
"PointerToSymbolTable" in File Header *should* be 0 as per PE/COFF 
format specific when handling an executable file rather a object file. 
Additionally, if it's non-zero, it contains a file offset rather than a 
RVA address, so impossible to parse it at runtime.

In (b), I realized that the CodeView format data in debug directory 
should be kept in a separate file (PDB file?) and they aren't mapped 
into image's address space as well.

I don't have so much experience with PE/COFF format, so please correct 
me if I'm mistaken.

IMHO, there should be exist a function like AsmGetFrameAddress() and/or 
AsmGetStackAddress() which would get implemented for both GCC and MSVC 
toolchains.

Thank you very much for your comments!

> Also on the page fault you can print the fault address since it is in CR2.

Good point! We should also do that.

> It should be possible to post process the text file and make a symbolicated backtrace.

Yes.

Thanks!
Paulo

> 
> Thanks,
> 
> Andrew Fish
> 
>> On Nov 14, 2017, at 4:47 AM, Paulo Alcantara <pcacjr@zytor.com> wrote:
>>
>> This patch adds stack trace support during a X64 CPU exception.
>>
>> It will dump out back trace, stack contents as well as image module
>> names that were part of the call stack.
>>
>> Contributed-under: TianoCore Contribution Agreement 1.1
>> Cc: Eric Dong <eric.dong@intel.com>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
>> ---
>> UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 344 +++++++++++++++++++-
>> 1 file changed, 342 insertions(+), 2 deletions(-)
>>
>> diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
>> index 65f0cff680..7048247be3 100644
>> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
>> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
>> @@ -14,6 +14,11 @@
>>
>> #include "CpuExceptionCommon.h"
>>
>> +//
>> +// Unknown PDB file name
>> +//
>> +GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
>> +
>> /**
>>    Return address map of exception handler template so that C code can generate
>>    exception tables.
>> @@ -243,6 +248,325 @@ DumpCpuContext (
>> }
>>
>> /**
>> +  Dump stack contents.
>> +
>> +  @param[in]  ImageBase            Base address of PE/COFF image.
>> +  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
>> +  @param[out] PdbFileName          File name of PDB file.
>> +**/
>> +STATIC
>> +VOID
>> +GetPdbFileName (
>> +  IN  UINTN    ImageBase,
>> +  OUT CHAR8    **PdbAbsoluteFilePath,
>> +  OUT CHAR8    **PdbFileName
>> +  )
>> +{
>> +  VOID   *PdbPointer;
>> +  CHAR8  *Str;
>> +
>> +  //
>> +  // Get PDB file name from PE/COFF image
>> +  //
>> +  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
>> +  if (PdbPointer == NULL) {
>> +    //
>> +    // No PDB file name found. Set it to an unknown file name.
>> +    //
>> +    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
>> +    if (PdbAbsoluteFilePath != NULL) {
>> +      *PdbAbsoluteFilePath = NULL;
>> +    }
>> +  } else {
>> +    //
>> +    // Get file name portion out of PDB file in PE/COFF image
>> +    //
>> +    Str = (CHAR8 *)((UINTN)PdbPointer +
>> +                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
>> +    for (; *Str != '/' && *Str != '\\'; Str--) {
>> +      ;
>> +    }
>> +
>> +    //
>> +    // Set PDB file name (also skip trailing path separator: '/' or '\\')
>> +    //
>> +    *PdbFileName = Str + 1;
>> +
>> +    if (PdbAbsoluteFilePath != NULL) {
>> +      //
>> +      // Set absolute file path of PDB file
>> +      //
>> +      *PdbAbsoluteFilePath = PdbPointer;
>> +    }
>> +  }
>> +}
>> +
>> +/**
>> +  Dump stack contents.
>> +
>> +  @param[in]  CurrentRsp         Current stack pointer address.
>> +  @param[in]  UnwondStacksCount  Count of unwond stack frames.
>> +**/
>> +STATIC
>> +VOID
>> +DumpStackContents (
>> +  IN UINT64  CurrentRsp,
>> +  IN INTN    UnwondStacksCount
>> +  )
>> +{
>> +  if (UnwondStacksCount == 0) {
>> +    return;
>> +  }
>> +
>> +  //
>> +  // Dump out stack contents
>> +  //
>> +  InternalPrintMessage ("\nStack dump:\n");
>> +  while (UnwondStacksCount-- > 0) {
>> +    InternalPrintMessage (
>> +      "0x%016lx: %016lx %016lx\n",
>> +      CurrentRsp,
>> +      *(UINT64 *)CurrentRsp,
>> +      *(UINT64 *)((UINTN)CurrentRsp + 8)
>> +      );
>> +
>> +    //
>> +    // As per Microsoft x64 ABI, the stack pointer must be aligned on a 16 byte
>> +    // boundary.
>> +    //
>> +    CurrentRsp = CurrentRsp + 16;
>> +  }
>> +}
>> +
>> +/**
>> +  Dump all image module names from call stack.
>> +
>> +  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
>> +**/
>> +STATIC
>> +VOID
>> +DumpImageModuleNames (
>> +  IN EFI_SYSTEM_CONTEXT   SystemContext
>> +  )
>> +{
>> +  EFI_STATUS  Status;
>> +  UINT64      Rip;
>> +  UINTN       ImageBase;
>> +  VOID        *EntryPoint;
>> +  CHAR8       *PdbAbsoluteFilePath;
>> +  CHAR8       *PdbFileName;
>> +  UINT64      Rbp;
>> +
>> +  //
>> +  // Set current RIP address
>> +  //
>> +  Rip = SystemContext.SystemContextX64->Rip;
>> +
>> +  //
>> +  // Set current frame pointer address
>> +  //
>> +  Rbp = SystemContext.SystemContextX64->Rbp;
>> +
>> +  //
>> +  // Get initial PE/COFF image base address from current RIP
>> +  //
>> +  ImageBase = PeCoffSearchImageBase (Rip);
>> +  if (ImageBase == 0) {
>> +    InternalPrintMessage ("!!!! Could not find image module names. !!!!");
>> +    return;
>> +  }
>> +
>> +  //
>> +  // Get initial PE/COFF image's entry point
>> +  //
>> +  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
>> +  if (EFI_ERROR (Status)) {
>> +    EntryPoint = NULL;
>> +  }
>> +
>> +  //
>> +  // Get file name and absolute path of initial PDB file
>> +  //
>> +  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
>> +
>> +  //
>> +  // Print out initial image module name (if any)
>> +  //
>> +  if (PdbAbsoluteFilePath != NULL) {
>> +    InternalPrintMessage (
>> +      "\n%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
>> +      PdbFileName,
>> +      ImageBase,
>> +      (UINTN)EntryPoint
>> +      );
>> +    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
>> +  }
>> +
>> +  //
>> +  // Walk through call stack and find next module names
>> +  //
>> +  for (;;) {
>> +    //
>> +    // Set RIP with return address from current stack frame
>> +    //
>> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
>> +
>> +    //
>> +    // Check if RIP is within another PE/COFF image base address
>> +    //
>> +    if (Rip < ImageBase) {
>> +      //
>> +      // Search for the respective PE/COFF image based on RIP
>> +      //
>> +      ImageBase = PeCoffSearchImageBase (Rip);
>> +      if (ImageBase == 0) {
>> +        //
>> +        // Stop stack trace
>> +        //
>> +        break;
>> +      }
>> +
>> +      //
>> +      // Get PE/COFF image's entry point
>> +      //
>> +      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
>> +      if (EFI_ERROR (Status)) {
>> +        EntryPoint = NULL;
>> +      }
>> +
>> +      //
>> +      // Get file name and absolute path of PDB file
>> +      //
>> +      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
>> +
>> +      //
>> +      // Print out image module name (if any)
>> +      //
>> +      if (PdbAbsoluteFilePath != NULL) {
>> +        InternalPrintMessage (
>> +          "%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
>> +          PdbFileName,
>> +          ImageBase,
>> +          (UINTN)EntryPoint
>> +          );
>> +        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
>> +      }
>> +    }
>> +
>> +    //
>> +    // Unwind the stack
>> +    //
>> +    Rbp = *(UINT64 *)(UINTN)Rbp;
>> +  }
>> +}
>> +
>> +/**
>> +  Dump stack trace.
>> +
>> +  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
>> +  @param[out] UnwondStacksCount  Count of unwond stack frames.
>> +**/
>> +STATIC
>> +VOID
>> +DumpStackTrace (
>> +  IN  EFI_SYSTEM_CONTEXT   SystemContext,
>> +  OUT INTN                 *UnwondStacksCount
>> +  )
>> +{
>> +  UINT64  Rip;
>> +  UINT64  Rbp;
>> +  UINTN   ImageBase;
>> +  CHAR8   *PdbFileName;
>> +
>> +  //
>> +  // Initialize count of unwond stacks
>> +  //
>> +  *UnwondStacksCount = 0;
>> +
>> +  //
>> +  // Set current RIP address
>> +  //
>> +  Rip = SystemContext.SystemContextX64->Rip;
>> +
>> +  //
>> +  // Set current frame pointer address
>> +  //
>> +  Rbp = SystemContext.SystemContextX64->Rbp;
>> +
>> +  //
>> +  // Get initial PE/COFF image base address from current RIP
>> +  //
>> +  ImageBase = PeCoffSearchImageBase (Rip);
>> +  if (ImageBase == 0) {
>> +    InternalPrintMessage ("!!!! Could not find backtrace information. !!!!");
>> +    return;
>> +  }
>> +
>> +  //
>> +  // Get PDB file name from initial PE/COFF image
>> +  //
>> +  GetPdbFileName (ImageBase, NULL, &PdbFileName);
>> +
>> +  //
>> +  // Print out back trace
>> +  //
>> +  InternalPrintMessage ("\nBack trace:\n");
>> +
>> +  for (;;) {
>> +    //
>> +    // Print stack frame in the following format:
>> +    //
>> +    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName> | ????]
>> +    //
>> +    InternalPrintMessage (
>> +      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
>> +      *UnwondStacksCount,
>> +      Rip,
>> +      ImageBase,
>> +      Rip - ImageBase - 1,
>> +      Rbp,
>> +      PdbFileName
>> +      );
>> +
>> +    //
>> +    // Set RIP with return address from current stack frame
>> +    //
>> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
>> +
>> +    //
>> +    // Check if RIP is within another PE/COFF image base address
>> +    //
>> +    if (Rip < ImageBase) {
>> +      //
>> +      // Search for the respective PE/COFF image based on RIP
>> +      //
>> +      ImageBase = PeCoffSearchImageBase (Rip);
>> +      if (ImageBase == 0) {
>> +        //
>> +        // Stop stack trace
>> +        //
>> +        break;
>> +      }
>> +
>> +      //
>> +      // Get PDB file name
>> +      //
>> +      GetPdbFileName (ImageBase, NULL, &PdbFileName);
>> +    }
>> +
>> +    //
>> +    // Unwind the stack
>> +    //
>> +    Rbp = *(UINT64 *)(UINTN)Rbp;
>> +
>> +    //
>> +    // Increment count of unwond stacks
>> +    //
>> +    (*UnwondStacksCount)++;
>> +  }
>> +}
>> +
>> +/**
>>    Display CPU information.
>>
>>    @param ExceptionType  Exception type.
>> @@ -254,9 +578,25 @@ DumpImageAndCpuContent (
>>    IN EFI_SYSTEM_CONTEXT   SystemContext
>>    )
>> {
>> +  INTN UnwondStacksCount;
>> +
>> +  //
>> +  // Dump CPU context
>> +  //
>>    DumpCpuContext (ExceptionType, SystemContext);
>> +
>> +  //
>> +  // Dump stack trace
>> +  //
>> +  DumpStackTrace (SystemContext, &UnwondStacksCount);
>> +
>> +  //
>> +  // Dump image module names
>> +  //
>> +  DumpImageModuleNames (SystemContext);
>> +
>>    //
>> -  // Dump module image base and module entry point by RIP
>> +  // Dump stack contents
>>    //
>> -  DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
>> +  DumpStackContents (SystemContext.SystemContextX64->Rsp, UnwondStacksCount);
>> }
>> -- 
>> 2.11.0
>>
>> _______________________________________________
>> edk2-devel mailing list
>> edk2-devel@lists.01.org
>> https://lists.01.org/mailman/listinfo/edk2-devel
> 


^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: 答复: [RFC 0/1] Stack trace support in X64 exception handling
  2017-11-14 14:03   ` 答复: " Fan Jeff
  2017-11-14 14:12     ` 答复: " Fan Jeff
@ 2017-11-14 15:37     ` Paulo Alcantara
  2017-11-14 16:33       ` Brian J. Johnson
  2017-11-15 13:21       ` 答复: 答复: " Fan Jeff
  1 sibling, 2 replies; 82+ messages in thread
From: Paulo Alcantara @ 2017-11-14 15:37 UTC (permalink / raw)
  To: Fan Jeff, edk2-devel@lists.01.org
  Cc: Rick Bramley, Laszlo Ersek, Andrew Fish, Eric Dong

Hi Fan,

On 14/11/2017 12:03, Fan Jeff wrote:
> Paul,
> 
> I like this feature very much. Actually, I did some POC one year ago but 
> I did finalize it.
> 
> In my POC, I could use EBP to tack the stack frame on IAS32 arch.
> 
> But for x64, I tried to use –keepexceptiontable flag to explain stack 
> frame from the debug section of image.
> 
> I may workson MSFT toolchain, but it did now work well for GCC toolchain.
> 
> I think Eric could help to verify MSFT for your patch. If it works well, 
> that’s will be great!
> 
> Say again, I like this feature!!!:-)

Cool! Your help would be really appreciable! If we get this working for 
X64 in both toolchains, that should be easy to port it to IA-32 as well.

Thank you very much for willing to help on that.

Paulo

> 
> Thanks!
> 
> Jeff
> 
> *发件人: *Paulo Alcantara <mailto:pcacjr@zytor.com>
> *发送时间: *2017年11月14日21:23
> *收件人: *edk2-devel@lists.01.org <mailto:edk2-devel@lists.01.org>
> *抄送: *Rick Bramley <mailto:richard.bramley@hp.com>; Laszlo Ersek 
> <mailto:lersek@redhat.com>; Andrew Fish <mailto:afish@apple.com>; Eric 
> Dong <mailto:eric.dong@intel.com>
> *主题: *Re: [edk2] [RFC 0/1] Stack trace support in X64 exception handling
> 
> Hi,
> 
> On 14/11/2017 10:47, Paulo Alcantara wrote:
>> Hi,
>> 
>> This series adds stack trace support during a X64 CPU exception.
>> 
>> Informations like back trace, stack contents and image module names
>> (that were part of the call stack) will be dumped out.
>> 
>> We already have such support in ARM/AArch64 (IIRC) exception handling
>> (thanks to Ard), and then I thought we'd also deserve it in X64 and
>> IA-32 platforms.
>> 
>> What do you think guys?
>> 
>> BTW, I've tested this only with OVMF (X64 only), using:
>>    - gcc-6.3.0, GCC5, NOOPT
>> 
>> Any other tests  would be really appreciable.
> 
> I've attached a file to show you how the trace would look like.
> 
> Thanks!
> Paulo
> 
>> 
>> Thanks!
>> Paulo
>> 
>> Repo:   https://github.com/pcacjr/edk2.git
>> Branch: stacktrace_x64
>> 
>> Cc: Rick Bramley <richard.bramley@hp.com>
>> Cc: Andrew Fish <afish@apple.com>
>> Cc: Eric Dong <eric.dong@intel.com>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Contributed-under: TianoCore Contribution Agreement 1.1
>> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
>> ---
>> 
>> Paulo Alcantara (1):
>>    UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
>> 
>>   UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 344 +++++++++++++++++++-
>>   1 file changed, 342 insertions(+), 2 deletions(-)
>> 
> 


^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: 答复: [RFC 0/1] Stack trace support in X64 exception handling
  2017-11-14 15:37     ` Paulo Alcantara
@ 2017-11-14 16:33       ` Brian J. Johnson
  2017-11-14 17:23         ` Andrew Fish
  2017-11-15 13:21       ` 答复: 答复: " Fan Jeff
  1 sibling, 1 reply; 82+ messages in thread
From: Brian J. Johnson @ 2017-11-14 16:33 UTC (permalink / raw)
  To: Paulo Alcantara, Fan Jeff, edk2-devel@lists.01.org
  Cc: Rick Bramley, Laszlo Ersek, Andrew Fish, Eric Dong

On 11/14/2017 09:37 AM, Paulo Alcantara wrote:
> Hi Fan,
> 
> On 14/11/2017 12:03, Fan Jeff wrote:
>> Paul,
>>
>> I like this feature very much. Actually, I did some POC one year ago 
>> but I did finalize it.
>>
>> In my POC, I could use EBP to tack the stack frame on IAS32 arch.
>>
>> But for x64, I tried to use –keepexceptiontable flag to explain stack 
>> frame from the debug section of image.
>>
>> I may workson MSFT toolchain, but it did now work well for GCC toolchain.
>>
>> I think Eric could help to verify MSFT for your patch. If it works 
>> well, that’s will be great!
>>
>> Say again, I like this feature!!!:-)
> 
> Cool! Your help would be really appreciable! If we get this working for 
> X64 in both toolchains, that should be easy to port it to IA-32 as well.
> 
> Thank you very much for willing to help on that.
> 
> Paulo

Great feature!  You do need some sort of sanity check on the RIP and RBP 
values, though, so if the stack gets corrupted or the RIP is nonsense 
from following a bad pointer, you don't start dereferencing garbage 
addresses and trigger an exception loop.

For at least some versions of Microsoft's IA32 compiler, it's possible 
to compile using EBP as a stack frame base pointer (like gcc) by using 
the "/Oy-" switch.  The proposed unwind code should work in that case. 
The X64 compiler doesn't support this switch, though.

AFAIK the only way to unwind the stack with Microsoft's X64 compilers is 
to parse the unwind info in the .pdata and .xdata sections.  Genfw.exe 
usually strips those sections, but the "--keepexceptiontable" flag will 
preserve them, as Jeff pointed out.  I've looked hard for open source 
code to decode them, but haven't found any, even though the format is 
well documented.  And I haven't gotten around to writing it myself.  I'd 
love it if someone could contribute the code!

Another possibility is to use the branch history MSRs available on some 
x86-family processors.  Recent Intel processors can use them as a stack, 
as opposed to a circular list, so they can record a backtrace directly. 
(I'm not familiar with AMD processors' capabilities.)  You can enable 
call stack recording like this:

   #define LBR_ON_FLAG   0x0000000000000001
   #define IA32_DEBUGCTL 0x1D9
   #define CALL_STACK_SET_FLAG 0x3C4
   #define CALL_STACK_CLR_FLAG 0xFC7
   #define MSR_LBR_SELECT 0x1C8

   //
   // Enable branch recording
   //
   LbControl = AsmReadMsr64 ((UINT32)IA32_DEBUGCTL);
   LbControl |= LBR_ON_FLAG;
   AsmWriteMsr64 ((UINT32)IA32_DEBUGCTL, LbControl);

   //
   // Configure for call stack
   //
   LbSelect = AsmReadMsr64 ((UINT32)MSR_LBR_SELECT);
   LbSelect &= CALL_STACK_CLR_FLAG;
   LbSelect |= CALL_STACK_SET_FLAG;
   AsmWriteMsr64((UINT32)MSR_LBR_SELECT, LbSelect);

The EIP/RIP values are logged in MSR_SANDY_BRIDGE_LASTBRANCH_n_FROM_IP 
and MSR_SANDY_BRIDGE_LASTBRANCH_n_TO_IP, and the current depth is 
tracked in MSR_LASTBRANCH_TOS.  This works quite well.  Gen10 (Sky Lake) 
processors support 32 LASTBRANCH_n MSR pairs, which is sufficient in 
almost all cases.

Different processor generations have different branch recording 
capabilities, and different numbers of LASTBRANCH_n MSRs; see Intel's 
manuals for details.

Thanks,
Brian

> 
>>
>> Thanks!
>>
>> Jeff
>>
>> *发件人: *Paulo Alcantara <mailto:pcacjr@zytor.com>
>> *发送时间: *2017年11月14日21:23
>> *收件人: *edk2-devel@lists.01.org <mailto:edk2-devel@lists.01.org>
>> *抄送: *Rick Bramley <mailto:richard.bramley@hp.com>; Laszlo Ersek 
>> <mailto:lersek@redhat.com>; Andrew Fish <mailto:afish@apple.com>; Eric 
>> Dong <mailto:eric.dong@intel.com>
>> *主题: *Re: [edk2] [RFC 0/1] Stack trace support in X64 exception 
>> handling
>>
>> Hi,
>>
>> On 14/11/2017 10:47, Paulo Alcantara wrote:
>>> Hi,
>>>
>>> This series adds stack trace support during a X64 CPU exception.
>>>
>>> Informations like back trace, stack contents and image module names
>>> (that were part of the call stack) will be dumped out.
>>>
>>> We already have such support in ARM/AArch64 (IIRC) exception handling
>>> (thanks to Ard), and then I thought we'd also deserve it in X64 and
>>> IA-32 platforms.
>>>
>>> What do you think guys?
>>>
>>> BTW, I've tested this only with OVMF (X64 only), using:
>>>     - gcc-6.3.0, GCC5, NOOPT
>>>
>>> Any other tests  would be really appreciable.
>>
>> I've attached a file to show you how the trace would look like.
>>
>> Thanks!
>> Paulo
>>
>>>
>>> Thanks!
>>> Paulo
>>>
>>> Repo:   https://github.com/pcacjr/edk2.git
>>> Branch: stacktrace_x64
>>>
>>> Cc: Rick Bramley <richard.bramley@hp.com>
>>> Cc: Andrew Fish <afish@apple.com>
>>> Cc: Eric Dong <eric.dong@intel.com>
>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>> Contributed-under: TianoCore Contribution Agreement 1.1
>>> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
>>> ---
>>>
>>> Paulo Alcantara (1):
>>>     UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
>>>
>>>    
>>> UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c 
>>> | 344 +++++++++++++++++++-
>>>    1 file changed, 342 insertions(+), 2 deletions(-)
>>>
>>
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org
> https://lists.01.org/mailman/listinfo/edk2-devel


-- 

                                                 Brian

--------------------------------------------------------------------

    "Most people would like to be delivered from temptation but would
     like it to keep in touch."
                                            -- Robert Orben


^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC 1/1] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
  2017-11-14 15:30     ` Paulo Alcantara
@ 2017-11-14 16:51       ` Brian J. Johnson
  0 siblings, 0 replies; 82+ messages in thread
From: Brian J. Johnson @ 2017-11-14 16:51 UTC (permalink / raw)
  To: Paulo Alcantara, Andrew Fish; +Cc: edk2-devel, Laszlo Ersek, Eric Dong

On 11/14/2017 09:30 AM, Paulo Alcantara wrote:
> Hi Andrew,
> 
> On 14/11/2017 12:01, Andrew Fish wrote:
> C> Paulo,
>>
>> Cool feature. How does this code deal with VC++ that code does not 
>> store the frame pointer and requires symbols to unwind.
> 
> I haven't tested in with MSVC, so I'd hope to get some help from Intel's 
> guys to help supporting and testing it :-)
> 
> Regarding the symbols, I performed some tests by writing a userspace 
> PE/COFF application [1] and tried to:
> 
> (a) handle the RUNTIME_FUNCTION entries in exception table (.pdata 
> section) to find the function starting address and then its respective 
> symbol name in COFF symbol table.
> 
> (b) Walk through UNWIND_INFO entries in .xdata section to figure out 
> which CPU register a function used as a frame pointer or if it didn't 
> use any at
> all.
> 
> The problem I had with (a) was that the COFF symbol table is not mapped 
> directory into the image's address space -- that is, the 
> "PointerToSymbolTable" in File Header *should* be 0 as per PE/COFF 
> format specific when handling an executable file rather a object file. 
> Additionally, if it's non-zero, it contains a file offset rather than a 
> RVA address, so impossible to parse it at runtime.
> 
> In (b), I realized that the CodeView format data in debug directory 
> should be kept in a separate file (PDB file?) and they aren't mapped 
> into image's address space as well.
> 
> I don't have so much experience with PE/COFF format, so please correct 
> me if I'm mistaken.
> 

You are correct that unfortunately, Microsoft's compilers don't put 
symbolic information in the executable file, they put it in a separate 
PDB file.  And the PDB file format is not documented (although the Wine 
project has reverse engineered parts of it) and changes with different 
compiler versions.  I've struggled with it before, and concluded that 
the only feasible way to parse it is to use the APIs Microsoft provides 
for that purpose, such as dbghelp.dll.  That doesn't work inside a BIOS, 
of course.

It is possible to define a simple, compiler-agnostic symbol table format 
and write a build-time tool to extract symbol data from the PDB files, 
convert it, and insert it into each module.  GenFw is a handy place to 
generate symbol data, since it's reformatting the images already.  I 
actually have code which does this....  I'd have to get permission from 
my company's Open Source Review Board to release it, though.  That would 
take time.

Brian


> IMHO, there should be exist a function like AsmGetFrameAddress() and/or 
> AsmGetStackAddress() which would get implemented for both GCC and MSVC 
> toolchains.
> 
> Thank you very much for your comments!
> 
>> Also on the page fault you can print the fault address since it is in 
>> CR2.
> 
> Good point! We should also do that.
> 
>> It should be possible to post process the text file and make a 
>> symbolicated backtrace.
> 
> Yes.
> 
> Thanks!
> Paulo
> 
>>
>> Thanks,
>>
>> Andrew Fish
>>
>>> On Nov 14, 2017, at 4:47 AM, Paulo Alcantara <pcacjr@zytor.com> wrote:
>>>
>>> This patch adds stack trace support during a X64 CPU exception.
>>>
>>> It will dump out back trace, stack contents as well as image module
>>> names that were part of the call stack.
>>>
>>> Contributed-under: TianoCore Contribution Agreement 1.1
>>> Cc: Eric Dong <eric.dong@intel.com>
>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
>>> ---
>>> UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c 
>>> | 344 +++++++++++++++++++-
>>> 1 file changed, 342 insertions(+), 2 deletions(-)
>>>
>>> diff --git 
>>> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c 
>>>
>>> index 65f0cff680..7048247be3 100644
>>> --- 
>>> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
>>> +++ 
>>> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
>>> @@ -14,6 +14,11 @@
>>>
>>> #include "CpuExceptionCommon.h"
>>>
>>> +//
>>> +// Unknown PDB file name
>>> +//
>>> +GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = 
>>> "????";
>>> +
>>> /**
>>>    Return address map of exception handler template so that C code 
>>> can generate
>>>    exception tables.
>>> @@ -243,6 +248,325 @@ DumpCpuContext (
>>> }
>>>
>>> /**
>>> +  Dump stack contents.
>>> +
>>> +  @param[in]  ImageBase            Base address of PE/COFF image.
>>> +  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
>>> +  @param[out] PdbFileName          File name of PDB file.
>>> +**/
>>> +STATIC
>>> +VOID
>>> +GetPdbFileName (
>>> +  IN  UINTN    ImageBase,
>>> +  OUT CHAR8    **PdbAbsoluteFilePath,
>>> +  OUT CHAR8    **PdbFileName
>>> +  )
>>> +{
>>> +  VOID   *PdbPointer;
>>> +  CHAR8  *Str;
>>> +
>>> +  //
>>> +  // Get PDB file name from PE/COFF image
>>> +  //
>>> +  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
>>> +  if (PdbPointer == NULL) {
>>> +    //
>>> +    // No PDB file name found. Set it to an unknown file name.
>>> +    //
>>> +    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
>>> +    if (PdbAbsoluteFilePath != NULL) {
>>> +      *PdbAbsoluteFilePath = NULL;
>>> +    }
>>> +  } else {
>>> +    //
>>> +    // Get file name portion out of PDB file in PE/COFF image
>>> +    //
>>> +    Str = (CHAR8 *)((UINTN)PdbPointer +
>>> +                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
>>> +    for (; *Str != '/' && *Str != '\\'; Str--) {
>>> +      ;
>>> +    }
>>> +
>>> +    //
>>> +    // Set PDB file name (also skip trailing path separator: '/' or 
>>> '\\')
>>> +    //
>>> +    *PdbFileName = Str + 1;
>>> +
>>> +    if (PdbAbsoluteFilePath != NULL) {
>>> +      //
>>> +      // Set absolute file path of PDB file
>>> +      //
>>> +      *PdbAbsoluteFilePath = PdbPointer;
>>> +    }
>>> +  }
>>> +}
>>> +
>>> +/**
>>> +  Dump stack contents.
>>> +
>>> +  @param[in]  CurrentRsp         Current stack pointer address.
>>> +  @param[in]  UnwondStacksCount  Count of unwond stack frames.
>>> +**/
>>> +STATIC
>>> +VOID
>>> +DumpStackContents (
>>> +  IN UINT64  CurrentRsp,
>>> +  IN INTN    UnwondStacksCount
>>> +  )
>>> +{
>>> +  if (UnwondStacksCount == 0) {
>>> +    return;
>>> +  }
>>> +
>>> +  //
>>> +  // Dump out stack contents
>>> +  //
>>> +  InternalPrintMessage ("\nStack dump:\n");
>>> +  while (UnwondStacksCount-- > 0) {
>>> +    InternalPrintMessage (
>>> +      "0x%016lx: %016lx %016lx\n",
>>> +      CurrentRsp,
>>> +      *(UINT64 *)CurrentRsp,
>>> +      *(UINT64 *)((UINTN)CurrentRsp + 8)
>>> +      );
>>> +
>>> +    //
>>> +    // As per Microsoft x64 ABI, the stack pointer must be aligned 
>>> on a 16 byte
>>> +    // boundary.
>>> +    //
>>> +    CurrentRsp = CurrentRsp + 16;
>>> +  }
>>> +}
>>> +
>>> +/**
>>> +  Dump all image module names from call stack.
>>> +
>>> +  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
>>> +**/
>>> +STATIC
>>> +VOID
>>> +DumpImageModuleNames (
>>> +  IN EFI_SYSTEM_CONTEXT   SystemContext
>>> +  )
>>> +{
>>> +  EFI_STATUS  Status;
>>> +  UINT64      Rip;
>>> +  UINTN       ImageBase;
>>> +  VOID        *EntryPoint;
>>> +  CHAR8       *PdbAbsoluteFilePath;
>>> +  CHAR8       *PdbFileName;
>>> +  UINT64      Rbp;
>>> +
>>> +  //
>>> +  // Set current RIP address
>>> +  //
>>> +  Rip = SystemContext.SystemContextX64->Rip;
>>> +
>>> +  //
>>> +  // Set current frame pointer address
>>> +  //
>>> +  Rbp = SystemContext.SystemContextX64->Rbp;
>>> +
>>> +  //
>>> +  // Get initial PE/COFF image base address from current RIP
>>> +  //
>>> +  ImageBase = PeCoffSearchImageBase (Rip);
>>> +  if (ImageBase == 0) {
>>> +    InternalPrintMessage ("!!!! Could not find image module names. 
>>> !!!!");
>>> +    return;
>>> +  }
>>> +
>>> +  //
>>> +  // Get initial PE/COFF image's entry point
>>> +  //
>>> +  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
>>> +  if (EFI_ERROR (Status)) {
>>> +    EntryPoint = NULL;
>>> +  }
>>> +
>>> +  //
>>> +  // Get file name and absolute path of initial PDB file
>>> +  //
>>> +  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
>>> +
>>> +  //
>>> +  // Print out initial image module name (if any)
>>> +  //
>>> +  if (PdbAbsoluteFilePath != NULL) {
>>> +    InternalPrintMessage (
>>> +      "\n%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
>>> +      PdbFileName,
>>> +      ImageBase,
>>> +      (UINTN)EntryPoint
>>> +      );
>>> +    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
>>> +  }
>>> +
>>> +  //
>>> +  // Walk through call stack and find next module names
>>> +  //
>>> +  for (;;) {
>>> +    //
>>> +    // Set RIP with return address from current stack frame
>>> +    //
>>> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
>>> +
>>> +    //
>>> +    // Check if RIP is within another PE/COFF image base address
>>> +    //
>>> +    if (Rip < ImageBase) {
>>> +      //
>>> +      // Search for the respective PE/COFF image based on RIP
>>> +      //
>>> +      ImageBase = PeCoffSearchImageBase (Rip);
>>> +      if (ImageBase == 0) {
>>> +        //
>>> +        // Stop stack trace
>>> +        //
>>> +        break;
>>> +      }
>>> +
>>> +      //
>>> +      // Get PE/COFF image's entry point
>>> +      //
>>> +      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, 
>>> &EntryPoint);
>>> +      if (EFI_ERROR (Status)) {
>>> +        EntryPoint = NULL;
>>> +      }
>>> +
>>> +      //
>>> +      // Get file name and absolute path of PDB file
>>> +      //
>>> +      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
>>> +
>>> +      //
>>> +      // Print out image module name (if any)
>>> +      //
>>> +      if (PdbAbsoluteFilePath != NULL) {
>>> +        InternalPrintMessage (
>>> +          "%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
>>> +          PdbFileName,
>>> +          ImageBase,
>>> +          (UINTN)EntryPoint
>>> +          );
>>> +        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
>>> +      }
>>> +    }
>>> +
>>> +    //
>>> +    // Unwind the stack
>>> +    //
>>> +    Rbp = *(UINT64 *)(UINTN)Rbp;
>>> +  }
>>> +}
>>> +
>>> +/**
>>> +  Dump stack trace.
>>> +
>>> +  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
>>> +  @param[out] UnwondStacksCount  Count of unwond stack frames.
>>> +**/
>>> +STATIC
>>> +VOID
>>> +DumpStackTrace (
>>> +  IN  EFI_SYSTEM_CONTEXT   SystemContext,
>>> +  OUT INTN                 *UnwondStacksCount
>>> +  )
>>> +{
>>> +  UINT64  Rip;
>>> +  UINT64  Rbp;
>>> +  UINTN   ImageBase;
>>> +  CHAR8   *PdbFileName;
>>> +
>>> +  //
>>> +  // Initialize count of unwond stacks
>>> +  //
>>> +  *UnwondStacksCount = 0;
>>> +
>>> +  //
>>> +  // Set current RIP address
>>> +  //
>>> +  Rip = SystemContext.SystemContextX64->Rip;
>>> +
>>> +  //
>>> +  // Set current frame pointer address
>>> +  //
>>> +  Rbp = SystemContext.SystemContextX64->Rbp;
>>> +
>>> +  //
>>> +  // Get initial PE/COFF image base address from current RIP
>>> +  //
>>> +  ImageBase = PeCoffSearchImageBase (Rip);
>>> +  if (ImageBase == 0) {
>>> +    InternalPrintMessage ("!!!! Could not find backtrace 
>>> information. !!!!");
>>> +    return;
>>> +  }
>>> +
>>> +  //
>>> +  // Get PDB file name from initial PE/COFF image
>>> +  //
>>> +  GetPdbFileName (ImageBase, NULL, &PdbFileName);
>>> +
>>> +  //
>>> +  // Print out back trace
>>> +  //
>>> +  InternalPrintMessage ("\nBack trace:\n");
>>> +
>>> +  for (;;) {
>>> +    //
>>> +    // Print stack frame in the following format:
>>> +    //
>>> +    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName> | ????]
>>> +    //
>>> +    InternalPrintMessage (
>>> +      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
>>> +      *UnwondStacksCount,
>>> +      Rip,
>>> +      ImageBase,
>>> +      Rip - ImageBase - 1,
>>> +      Rbp,
>>> +      PdbFileName
>>> +      );
>>> +
>>> +    //
>>> +    // Set RIP with return address from current stack frame
>>> +    //
>>> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
>>> +
>>> +    //
>>> +    // Check if RIP is within another PE/COFF image base address
>>> +    //
>>> +    if (Rip < ImageBase) {
>>> +      //
>>> +      // Search for the respective PE/COFF image based on RIP
>>> +      //
>>> +      ImageBase = PeCoffSearchImageBase (Rip);
>>> +      if (ImageBase == 0) {
>>> +        //
>>> +        // Stop stack trace
>>> +        //
>>> +        break;
>>> +      }
>>> +
>>> +      //
>>> +      // Get PDB file name
>>> +      //
>>> +      GetPdbFileName (ImageBase, NULL, &PdbFileName);
>>> +    }
>>> +
>>> +    //
>>> +    // Unwind the stack
>>> +    //
>>> +    Rbp = *(UINT64 *)(UINTN)Rbp;
>>> +
>>> +    //
>>> +    // Increment count of unwond stacks
>>> +    //
>>> +    (*UnwondStacksCount)++;
>>> +  }
>>> +}
>>> +
>>> +/**
>>>    Display CPU information.
>>>
>>>    @param ExceptionType  Exception type.
>>> @@ -254,9 +578,25 @@ DumpImageAndCpuContent (
>>>    IN EFI_SYSTEM_CONTEXT   SystemContext
>>>    )
>>> {
>>> +  INTN UnwondStacksCount;
>>> +
>>> +  //
>>> +  // Dump CPU context
>>> +  //
>>>    DumpCpuContext (ExceptionType, SystemContext);
>>> +
>>> +  //
>>> +  // Dump stack trace
>>> +  //
>>> +  DumpStackTrace (SystemContext, &UnwondStacksCount);
>>> +
>>> +  //
>>> +  // Dump image module names
>>> +  //
>>> +  DumpImageModuleNames (SystemContext);
>>> +
>>>    //
>>> -  // Dump module image base and module entry point by RIP
>>> +  // Dump stack contents
>>>    //
>>> -  DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
>>> +  DumpStackContents (SystemContext.SystemContextX64->Rsp, 
>>> UnwondStacksCount);
>>> }
>>> -- 
>>> 2.11.0
>>>
>>> _______________________________________________
>>> edk2-devel mailing list
>>> edk2-devel@lists.01.org
>>> https://lists.01.org/mailman/listinfo/edk2-devel
>>
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org
> https://lists.01.org/mailman/listinfo/edk2-devel


-- 
Brian J. Johnson
Enterprise X86 Lab

Hewlett Packard Enterprise
brian.johnson@hpe.com



^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC 0/1] Stack trace support in X64 exception handling
  2017-11-14 16:33       ` Brian J. Johnson
@ 2017-11-14 17:23         ` Andrew Fish
  2017-11-14 17:41           ` Brian J. Johnson
  0 siblings, 1 reply; 82+ messages in thread
From: Andrew Fish @ 2017-11-14 17:23 UTC (permalink / raw)
  To: Brian J. Johnson
  Cc: Paulo Alcantara, Fan Jeff, edk2-devel@lists.01.org, Rick Bramley,
	Laszlo Ersek, Eric Dong


> On Nov 14, 2017, at 8:33 AM, Brian J. Johnson <brian.johnson@hpe.com> wrote:
> 
> On 11/14/2017 09:37 AM, Paulo Alcantara wrote:
>> Hi Fan,
>> On 14/11/2017 12:03, Fan Jeff wrote:
>>> Paul,
>>> 
>>> I like this feature very much. Actually, I did some POC one year ago but I did finalize it.
>>> 
>>> In my POC, I could use EBP to tack the stack frame on IAS32 arch.
>>> 
>>> But for x64, I tried to use –keepexceptiontable flag to explain stack frame from the debug section of image.
>>> 
>>> I may workson MSFT toolchain, but it did now work well for GCC toolchain.
>>> 
>>> I think Eric could help to verify MSFT for your patch. If it works well, that’s will be great!
>>> 
>>> Say again, I like this feature!!!:-)
>> Cool! Your help would be really appreciable! If we get this working for X64 in both toolchains, that should be easy to port it to IA-32 as well.
>> Thank you very much for willing to help on that.
>> Paulo
> 
> Great feature!  You do need some sort of sanity check on the RIP and RBP values, though, so if the stack gets corrupted or the RIP is nonsense from following a bad pointer, you don't start dereferencing garbage addresses and trigger an exception loop.
> 

Brian,

This was a long time ago and my memory might be fuzzy.... I think we talked to some debugger folks about unwinding the stack and they mentioned it was common for the C runtime to have a return address or frame pointer have a zero value so the unwind logic knows when to stop. This is in addition to generic sanity checking. 

We got an extra push $0 added to the stack switch to help with stack unwind. 
https://github.com/tianocore/edk2/blob/master/MdePkg/Library/BaseLib/X64/SwitchStack.S <https://github.com/tianocore/edk2/blob/master/MdePkg/Library/BaseLib/X64/SwitchStack.S>

If might be a good idea to have a PCD for the max number of stack frames to display as a fallback for the error check. For X64 you may also have to add a check for a non-cononical address as that will GP fault. 

Thanks,

Andrew Fish


> For at least some versions of Microsoft's IA32 compiler, it's possible to compile using EBP as a stack frame base pointer (like gcc) by using the "/Oy-" switch.  The proposed unwind code should work in that case. The X64 compiler doesn't support this switch, though.
> 
> AFAIK the only way to unwind the stack with Microsoft's X64 compilers is to parse the unwind info in the .pdata and .xdata sections.  Genfw.exe usually strips those sections, but the "--keepexceptiontable" flag will preserve them, as Jeff pointed out.  I've looked hard for open source code to decode them, but haven't found any, even though the format is well documented.  And I haven't gotten around to writing it myself.  I'd love it if someone could contribute the code!
> 
> Another possibility is to use the branch history MSRs available on some x86-family processors.  Recent Intel processors can use them as a stack, as opposed to a circular list, so they can record a backtrace directly. (I'm not familiar with AMD processors' capabilities.)  You can enable call stack recording like this:
> 
>  #define LBR_ON_FLAG   0x0000000000000001
>  #define IA32_DEBUGCTL 0x1D9
>  #define CALL_STACK_SET_FLAG 0x3C4
>  #define CALL_STACK_CLR_FLAG 0xFC7
>  #define MSR_LBR_SELECT 0x1C8
> 
>  //
>  // Enable branch recording
>  //
>  LbControl = AsmReadMsr64 ((UINT32)IA32_DEBUGCTL);
>  LbControl |= LBR_ON_FLAG;
>  AsmWriteMsr64 ((UINT32)IA32_DEBUGCTL, LbControl);
> 
>  //
>  // Configure for call stack
>  //
>  LbSelect = AsmReadMsr64 ((UINT32)MSR_LBR_SELECT);
>  LbSelect &= CALL_STACK_CLR_FLAG;
>  LbSelect |= CALL_STACK_SET_FLAG;
>  AsmWriteMsr64((UINT32)MSR_LBR_SELECT, LbSelect);
> 
> The EIP/RIP values are logged in MSR_SANDY_BRIDGE_LASTBRANCH_n_FROM_IP and MSR_SANDY_BRIDGE_LASTBRANCH_n_TO_IP, and the current depth is tracked in MSR_LASTBRANCH_TOS.  This works quite well.  Gen10 (Sky Lake) processors support 32 LASTBRANCH_n MSR pairs, which is sufficient in almost all cases.
> 
> Different processor generations have different branch recording capabilities, and different numbers of LASTBRANCH_n MSRs; see Intel's manuals for details.
> 
> Thanks,
> Brian
> 
>>> 
>>> Thanks!
>>> 
>>> Jeff
>>> 
>>> *发件人: *Paulo Alcantara <mailto:pcacjr@zytor.com>
>>> *发送时间: *2017年11月14日21:23
>>> *收件人: *edk2-devel@lists.01.org <mailto:edk2-devel@lists.01.org>
>>> *抄送: *Rick Bramley <mailto:richard.bramley@hp.com>; Laszlo Ersek <mailto:lersek@redhat.com>; Andrew Fish <mailto:afish@apple.com>; Eric Dong <mailto:eric.dong@intel.com>
>>> *主题: *Re: [edk2] [RFC 0/1] Stack trace support in X64 exception handling
>>> 
>>> Hi,
>>> 
>>> On 14/11/2017 10:47, Paulo Alcantara wrote:
>>>> Hi,
>>>> 
>>>> This series adds stack trace support during a X64 CPU exception.
>>>> 
>>>> Informations like back trace, stack contents and image module names
>>>> (that were part of the call stack) will be dumped out.
>>>> 
>>>> We already have such support in ARM/AArch64 (IIRC) exception handling
>>>> (thanks to Ard), and then I thought we'd also deserve it in X64 and
>>>> IA-32 platforms.
>>>> 
>>>> What do you think guys?
>>>> 
>>>> BTW, I've tested this only with OVMF (X64 only), using:
>>>>     - gcc-6.3.0, GCC5, NOOPT
>>>> 
>>>> Any other tests  would be really appreciable.
>>> 
>>> I've attached a file to show you how the trace would look like.
>>> 
>>> Thanks!
>>> Paulo
>>> 
>>>> 
>>>> Thanks!
>>>> Paulo
>>>> 
>>>> Repo:   https://github.com/pcacjr/edk2.git
>>>> Branch: stacktrace_x64
>>>> 
>>>> Cc: Rick Bramley <richard.bramley@hp.com>
>>>> Cc: Andrew Fish <afish@apple.com>
>>>> Cc: Eric Dong <eric.dong@intel.com>
>>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>>> Contributed-under: TianoCore Contribution Agreement 1.1
>>>> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
>>>> ---
>>>> 
>>>> Paulo Alcantara (1):
>>>>     UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
>>>> 
>>>>   UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 344 +++++++++++++++++++-
>>>>    1 file changed, 342 insertions(+), 2 deletions(-)
>>>> 
>>> 
>> _______________________________________________
>> edk2-devel mailing list
>> edk2-devel@lists.01.org
>> https://lists.01.org/mailman/listinfo/edk2-devel
> 
> 
> -- 
> 
>                                                Brian
> 
> --------------------------------------------------------------------
> 
>   "Most people would like to be delivered from temptation but would
>    like it to keep in touch."
>                                           -- Robert Orben
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org <mailto:edk2-devel@lists.01.org>
> https://lists.01.org/mailman/listinfo/edk2-devel <https://lists.01.org/mailman/listinfo/edk2-devel>


^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC 0/1] Stack trace support in X64 exception handling
  2017-11-14 17:23         ` Andrew Fish
@ 2017-11-14 17:41           ` Brian J. Johnson
  2017-11-14 17:56             ` Paulo Alcantara
  0 siblings, 1 reply; 82+ messages in thread
From: Brian J. Johnson @ 2017-11-14 17:41 UTC (permalink / raw)
  To: Andrew Fish
  Cc: Paulo Alcantara, Fan Jeff, edk2-devel@lists.01.org, Rick Bramley,
	Laszlo Ersek, Eric Dong

On 11/14/2017 11:23 AM, Andrew Fish wrote:
> 
>> On Nov 14, 2017, at 8:33 AM, Brian J. Johnson <brian.johnson@hpe.com 
>> <mailto:brian.johnson@hpe.com>> wrote:
>>
>> On 11/14/2017 09:37 AM, Paulo Alcantara wrote:
>>> Hi Fan,
>>> On 14/11/2017 12:03, Fan Jeff wrote:
>>>> Paul,
>>>>
>>>> I like this feature very much. Actually, I did some POC one year ago 
>>>> but I did finalize it.
>>>>
>>>> In my POC, I could use EBP to tack the stack frame on IAS32 arch.
>>>>
>>>> But for x64, I tried to use –keepexceptiontable flag to explain 
>>>> stack frame from the debug section of image.
>>>>
>>>> I may workson MSFT toolchain, but it did now work well for GCC 
>>>> toolchain.
>>>>
>>>> I think Eric could help to verify MSFT for your patch. If it works 
>>>> well, that’s will be great!
>>>>
>>>> Say again, I like this feature!!!:-)
>>> Cool! Your help would be really appreciable! If we get this working 
>>> for X64 in both toolchains, that should be easy to port it to IA-32 
>>> as well.
>>> Thank you very much for willing to help on that.
>>> Paulo
>>
>> Great feature!  You do need some sort of sanity check on the RIP and 
>> RBP values, though, so if the stack gets corrupted or the RIP is 
>> nonsense from following a bad pointer, you don't start dereferencing 
>> garbage addresses and trigger an exception loop.
>>
> 
> Brian,
> 
> This was a long time ago and my memory might be fuzzy.... I think we 
> talked to some debugger folks about unwinding the stack and they 
> mentioned it was common for the C runtime to have a return address or 
> frame pointer have a zero value so the unwind logic knows when to stop. 
> This is in addition to generic sanity checking.
> 
> We got an extra push $0 added to the stack switch to help with stack 
> unwind.
> https://github.com/tianocore/edk2/blob/master/MdePkg/Library/BaseLib/X64/SwitchStack.S
> 
> If might be a good idea to have a PCD for the max number of stack frames 
> to display as a fallback for the error check. For X64 you may also have 
> to add a check for a non-cononical address as that will GP fault.
> 

Good idea.

Regarding sanity checks:  I've had good luck validating code locations 
(EIP values) by using a modified PeCoffExtraActionLib to track the top 
and bottom of the range where images have been loaded.  (I've actually 
used two ranges:  one for code executed from firmware space, and one for 
code executed from RAM.)

I'm not sure offhand if there's a platform-independent way to validate 
stack pointer values.  For most PC-like systems, just ensuring that it's 
larger than 1 or 2M (to avoid NULL pointers and the legacy spaces) and 
less than about 3G (or the low memory size, if that's known) may be 
enough to avoid an exception loop.

Brian

> Thanks,
> 
> Andrew Fish
> 
> 
>> For at least some versions of Microsoft's IA32 compiler, it's possible 
>> to compile using EBP as a stack frame base pointer (like gcc) by using 
>> the "/Oy-" switch.  The proposed unwind code should work in that case. 
>> The X64 compiler doesn't support this switch, though.
>>
>> AFAIK the only way to unwind the stack with Microsoft's X64 compilers 
>> is to parse the unwind info in the .pdata and .xdata sections. 
>>  Genfw.exe usually strips those sections, but the 
>> "--keepexceptiontable" flag will preserve them, as Jeff pointed out. 
>>  I've looked hard for open source code to decode them, but haven't 
>> found any, even though the format is well documented.  And I haven't 
>> gotten around to writing it myself.  I'd love it if someone could 
>> contribute the code!
>>
>> Another possibility is to use the branch history MSRs available on 
>> some x86-family processors.  Recent Intel processors can use them as a 
>> stack, as opposed to a circular list, so they can record a backtrace 
>> directly. (I'm not familiar with AMD processors' capabilities.)  You 
>> can enable call stack recording like this:
>>
>>  #define LBR_ON_FLAG   0x0000000000000001
>>  #define IA32_DEBUGCTL 0x1D9
>>  #define CALL_STACK_SET_FLAG 0x3C4
>>  #define CALL_STACK_CLR_FLAG 0xFC7
>>  #define MSR_LBR_SELECT 0x1C8
>>
>>  //
>>  // Enable branch recording
>>  //
>>  LbControl = AsmReadMsr64 ((UINT32)IA32_DEBUGCTL);
>>  LbControl |= LBR_ON_FLAG;
>>  AsmWriteMsr64 ((UINT32)IA32_DEBUGCTL, LbControl);
>>
>>  //
>>  // Configure for call stack
>>  //
>>  LbSelect = AsmReadMsr64 ((UINT32)MSR_LBR_SELECT);
>>  LbSelect &= CALL_STACK_CLR_FLAG;
>>  LbSelect |= CALL_STACK_SET_FLAG;
>>  AsmWriteMsr64((UINT32)MSR_LBR_SELECT, LbSelect);
>>
>> The EIP/RIP values are logged in MSR_SANDY_BRIDGE_LASTBRANCH_n_FROM_IP 
>> and MSR_SANDY_BRIDGE_LASTBRANCH_n_TO_IP, and the current depth is 
>> tracked in MSR_LASTBRANCH_TOS.  This works quite well.  Gen10 (Sky 
>> Lake) processors support 32 LASTBRANCH_n MSR pairs, which is 
>> sufficient in almost all cases.
>>
>> Different processor generations have different branch recording 
>> capabilities, and different numbers of LASTBRANCH_n MSRs; see Intel's 
>> manuals for details.
>>
>> Thanks,
>> Brian
>>
>>>>
>>>> Thanks!
>>>>
>>>> Jeff
>>>>
>>>> *发件人: *Paulo Alcantara <mailto:pcacjr@zytor.com>
>>>> *发送时间: *2017年11月14日21:23
>>>> *收件人: *edk2-devel@lists.01.org <mailto:edk2-devel@lists.01.org> 
>>>> <mailto:edk2-devel@lists.01.org>
>>>> *抄送: *Rick Bramley <mailto:richard.bramley@hp.com>; Laszlo Ersek 
>>>> <mailto:lersek@redhat.com>; Andrew Fish <mailto:afish@apple.com>; 
>>>> Eric Dong <mailto:eric.dong@intel.com>
>>>> *主题: *Re: [edk2] [RFC 0/1] Stack trace support in X64 exception 
>>>> handling
>>>>
>>>> Hi,
>>>>
>>>> On 14/11/2017 10:47, Paulo Alcantara wrote:
>>>>> Hi,
>>>>>
>>>>> This series adds stack trace support during a X64 CPU exception.
>>>>>
>>>>> Informations like back trace, stack contents and image module names
>>>>> (that were part of the call stack) will be dumped out.
>>>>>
>>>>> We already have such support in ARM/AArch64 (IIRC) exception handling
>>>>> (thanks to Ard), and then I thought we'd also deserve it in X64 and
>>>>> IA-32 platforms.
>>>>>
>>>>> What do you think guys?
>>>>>
>>>>> BTW, I've tested this only with OVMF (X64 only), using:
>>>>> - gcc-6.3.0, GCC5, NOOPT
>>>>>
>>>>> Any other tests  would be really appreciable.
>>>>
>>>> I've attached a file to show you how the trace would look like.
>>>>
>>>> Thanks!
>>>> Paulo
>>>>
>>>>>
>>>>> Thanks!
>>>>> Paulo
>>>>>
>>>>> Repo: https://github.com/pcacjr/edk2.git
>>>>> Branch: stacktrace_x64
>>>>>
>>>>> Cc: Rick Bramley <richard.bramley@hp.com 
>>>>> <mailto:richard.bramley@hp.com>>
>>>>> Cc: Andrew Fish <afish@apple.com <mailto:afish@apple.com>>
>>>>> Cc: Eric Dong <eric.dong@intel.com <mailto:eric.dong@intel.com>>
>>>>> Cc: Laszlo Ersek <lersek@redhat.com <mailto:lersek@redhat.com>>
>>>>> Contributed-under: TianoCore Contribution Agreement 1.1
>>>>> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com 
>>>>> <mailto:pcacjr@zytor.com>>
>>>>> ---
>>>>>
>>>>> Paulo Alcantara (1):
>>>>> UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
>>>>>
>>>>>   UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c 
>>>>> | 344 +++++++++++++++++++-
>>>>> 1 file changed, 342 insertions(+), 2 deletions(-)
>>>>>
>>>>
>>> _______________________________________________
>>> edk2-devel mailing list
>>> edk2-devel@lists.01.org <mailto:edk2-devel@lists.01.org>
>>> https://lists.01.org/mailman/listinfo/edk2-devel
>>
>>
>> --
>>
>>                                                Brian
>>
>> --------------------------------------------------------------------
>>
>>   "Most people would like to be delivered from temptation but would
>>    like it to keep in touch."
>>                                           -- Robert Orben
>> _______________________________________________
>> edk2-devel mailing list
>> edk2-devel@lists.01.org <mailto:edk2-devel@lists.01.org>
>> https://lists.01.org/mailman/listinfo/edk2-devel
> 


-- 
Brian J. Johnson
Enterprise X86 Lab

Hewlett Packard Enterprise
brian.johnson@hpe.com



^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC 0/1] Stack trace support in X64 exception handling
  2017-11-14 17:41           ` Brian J. Johnson
@ 2017-11-14 17:56             ` Paulo Alcantara
  0 siblings, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2017-11-14 17:56 UTC (permalink / raw)
  To: Brian J. Johnson, Andrew Fish
  Cc: Fan Jeff, edk2-devel@lists.01.org, Rick Bramley, Laszlo Ersek,
	Eric Dong

Hi,

On 14/11/2017 15:41, Brian J. Johnson wrote:
> On 11/14/2017 11:23 AM, Andrew Fish wrote:
>>
>>> On Nov 14, 2017, at 8:33 AM, Brian J. Johnson <brian.johnson@hpe.com 
>>> <mailto:brian.johnson@hpe.com>> wrote:
>>>
>>> On 11/14/2017 09:37 AM, Paulo Alcantara wrote:
>>>> Hi Fan,
>>>> On 14/11/2017 12:03, Fan Jeff wrote:
>>>>> Paul,
>>>>>
>>>>> I like this feature very much. Actually, I did some POC one year 
>>>>> ago but I did finalize it.
>>>>>
>>>>> In my POC, I could use EBP to tack the stack frame on IAS32 arch.
>>>>>
>>>>> But for x64, I tried to use –keepexceptiontable flag to explain 
>>>>> stack frame from the debug section of image.
>>>>>
>>>>> I may workson MSFT toolchain, but it did now work well for GCC 
>>>>> toolchain.
>>>>>
>>>>> I think Eric could help to verify MSFT for your patch. If it works 
>>>>> well, that’s will be great!
>>>>>
>>>>> Say again, I like this feature!!!:-)
>>>> Cool! Your help would be really appreciable! If we get this working 
>>>> for X64 in both toolchains, that should be easy to port it to IA-3 2 
>>>> as well.
>>>> Thank you very much for willing to help on that.
>>>> Paulo
>>>
>>> Great feature!  You do need some sort of sanity check on the RIP and 
>>> RBP values, though, so if the stack gets corrupted or the RIP is 
>>> nonsense from following a bad pointer, you don't start dereferencing 
>>> garbage addresses and trigger an exception loop.
>>>
>>
>> Brian,
>>
>> This was a long time ago and my memory might be fuzzy.... I think we 
>> talked to some debugger folks about unwinding the stack and they 
>> mentioned it was common for the C runtime to have a return address or 
>> frame pointer have a zero value so the unwind logic knows when to 
>> stop. This is in addition to generic sanity checking.
>>
>> We got an extra push $0 added to the stack switch to help with stack 
>> unwind.
>> https://github.com/tianocore/edk2/blob/master/MdePkg/Library/BaseLib/X64/SwitchStack.S 
>>
>>
>> If might be a good idea to have a PCD for the max number of stack 
>> frames to display as a fallback for the error check. For X64 you may 
>> also have to add a check for a non-cononical address as that will GP 
>> fault.
>>
> 
> Good idea.
> 
> Regarding sanity checks:  I've had good luck validating code locations 
> (EIP values) by using a modified PeCoffExtraActionLib to track the top 
> and bottom of the range where images have been loaded.  (I've actually 
> used two ranges:  one for code executed from firmware space, and one for 
> code executed from RAM.)
> 
> I'm not sure offhand if there's a platform-independent way to validate 
> stack pointer values.  For most PC-like systems, just ensuring that it's 
> larger than 1 or 2M (to avoid NULL pointers and the legacy spaces) and 
> less than about 3G (or the low memory size, if that's known) may be 
> enough to avoid an exception loop.

Yeah, I agree with you guys. We certainly should be validating the RIP 
and RSP values and then avoiding the exception loop.

For the RIP value, I think we should validate it in 
PeCoffSearchImageBase(), so if it's outside PE/COFF image's address 
space, then we should return an address of zero and no trace would be 
printed out.

Since we already have a "SizeOfImage" field in PE/COFF Optional Header 
and it's available in the process' image, we might end up with checking 
whether RIP is between ImageBase through ImageBase + SizeOfImage - 1.

For the RSP value, I have no idea :-)

Thanks!
Paulo

> 
> Brian
> 
>> Thanks,
>>
>> Andrew Fish
>>
>>
>>> For at least some versions of Microsoft's IA32 compiler, it's 
>>> possible to compile using EBP as a stack frame base pointer (like 
>>> gcc) by using the "/Oy-" switch.  The proposed unwind code should 
>>> work in that case. The X64 compiler doesn't support this switch, though.
>>>
>>> AFAIK the only way to unwind the stack with Microsoft's X64 compilers 
>>> is to parse the unwind info in the .pdata and .xdata sections. 
>>>  Genfw.exe usually strips those sections, but the 
>>> "--keepexceptiontable" flag will preserve them, as Jeff pointed out. 
>>>  I've looked hard for open source code to decode them, but haven't 
>>> found any, even though the format is well documented.  And I haven't 
>>> gotten around to writing it myself.  I'd love it if someone could 
>>> contribute the code!
>>>
>>> Another possibility is to use the branch history MSRs available on 
>>> some x86-family processors.  Recent Intel processors can use them as 
>>> a stack, as opposed to a circular list, so they can record a 
>>> backtrace directly. (I'm not familiar with AMD processors' 
>>> capabilities.)  You can enable call stack recording like this:
>>>
>>>  #define LBR_ON_FLAG   0x0000000000000001
>>>  #define IA32_DEBUGCTL 0x1D9
>>>  #define CALL_STACK_SET_FLAG 0x3C4
>>>  #define CALL_STACK_CLR_FLAG 0xFC7
>>>  #define MSR_LBR_SELECT 0x1C8
>>>
>>>  //
>>>  // Enable branch recording
>>>  //
>>>  LbControl = AsmReadMsr64 ((UINT32)IA32_DEBUGCTL);
>>>  LbControl |= LBR_ON_FLAG;
>>>  AsmWriteMsr64 ((UINT32)IA32_DEBUGCTL, LbControl);
>>>
>>>  //
>>>  // Configure for call stack
>>>  //
>>>  LbSelect = AsmReadMsr64 ((UINT32)MSR_LBR_SELECT);
>>>  LbSelect &= CALL_STACK_CLR_FLAG;
>>>  LbSelect |= CALL_STACK_SET_FLAG;
>>>  AsmWriteMsr64((UINT32)MSR_LBR_SELECT, LbSelect);
>>>
>>> The EIP/RIP values are logged in 
>>> MSR_SANDY_BRIDGE_LASTBRANCH_n_FROM_IP and 
>>> MSR_SANDY_BRIDGE_LASTBRANCH_n_TO_IP, and the current depth is tracked 
>>> in MSR_LASTBRANCH_TOS.  This works quite well.  Gen10 (Sky Lake) 
>>> processors support 32 LASTBRANCH_n MSR pairs, which is sufficient in 
>>> almost all cases.
>>>
>>> Different processor generations have different branch recording 
>>> capabilities, and different numbers of LASTBRANCH_n MSRs; see Intel's 
>>> manuals for details.
>>>
>>> Thanks,
>>> Brian
>>>
>>>>>
>>>>> Thanks!
>>>>>
>>>>> Jeff
>>>>>
>>>>> *发件人: *Paulo Alcantara <mailto:pcacjr@zytor.com>
>>>>> *发送时间: *2017年11月14日21:23
>>>>> *收件人: *edk2-devel@lists.01.org <mailto:edk2-devel@lists.01.org> 
>>>>> <mailto:edk2-devel@lists.01.org>
>>>>> *抄送: *Rick Bramley <mailto:richard.bramley@hp.com>; Laszlo Ersek 
>>>>> <mailto:lersek@redhat.com>; Andrew Fish <mailto:afish@apple.com>; 
>>>>> Eric Dong <mailto:eric.dong@intel.com>
>>>>> *主题: *Re: [edk2] [RFC 0/1] Stack trace support in X64 exception 
>>>>> handling
>>>>>
>>>>> Hi,
>>>>>
>>>>> On 14/11/2017 10:47, Paulo Alcantara wrote:
>>>>>> Hi,
>>>>>>
>>>>>> This series adds stack trace support during a X64 CPU exception.
>>>>>>
>>>>>> Informations like back trace, stack contents and image module names
>>>>>> (that were part of the call stack) will be dumped out.
>>>>>>
>>>>>> We already have such support in ARM/AArch64 (IIRC) exception handling
>>>>>> (thanks to Ard), and then I thought we'd also deserve it in X64 and
>>>>>> IA-32 platforms.
>>>>>>
>>>>>> What do you think guys?
>>>>>>
>>>>>> BTW, I've tested this only with OVMF (X64 only), using:
>>>>>> - gcc-6.3.0, GCC5, NOOPT
>>>>>>
>>>>>> Any other tests  would be really appreciable.
>>>>>
>>>>> I've attached a file to show you how the trace would look like.
>>>>>
>>>>> Thanks!
>>>>> Paulo
>>>>>
>>>>>>
>>>>>> Thanks!
>>>>>> Paulo
>>>>>>
>>>>>> Repo: https://github.com/pcacjr/edk2.git
>>>>>> Branch: stacktrace_x64
>>>>>>
>>>>>> Cc: Rick Bramley <richard.bramley@hp.com 
>>>>>> <mailto:richard.bramley@hp.com>>
>>>>>> Cc: Andrew Fish <afish@apple.com <mailto:afish@apple.com>>
>>>>>> Cc: Eric Dong <eric.dong@intel.com <mailto:eric.dong@intel.com>>
>>>>>> Cc: Laszlo Ersek <lersek@redhat.com <mailto:lersek@redhat.com>>
>>>>>> Contributed-under: TianoCore Contribution Agreement 1.1
>>>>>> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com 
>>>>>> <mailto:pcacjr@zytor.com>>
>>>>>> ---
>>>>>>
>>>>>> Paulo Alcantara (1):
>>>>>> UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
>>>>>>
>>>>>>   UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c 
>>>>>> | 344 +++++++++++++++++++-
>>>>>> 1 file changed, 342 insertions(+), 2 deletions(-)
>>>>>>
>>>>>
>>>> _______________________________________________
>>>> edk2-devel mailing list
>>>> edk2-devel@lists.01.org <mailto:edk2-devel@lists.01.org>
>>>> https://lists.01.org/mailman/listinfo/edk2-devel
>>>
>>>
>>> -- 
>>>
>>>                                                Brian
>>>
>>> --------------------------------------------------------------------
>>>
>>>   "Most people would like to be delivered from temptation but would
>>>    like it to keep in touch."
>>>                                           -- Robert Orben
>>> _______________________________________________
>>> edk2-devel mailing list
>>> edk2-devel@lists.01.org <mailto:edk2-devel@lists.01.org>
>>> https://lists.01.org/mailman/listinfo/edk2-devel
>>
> 
> 


^ permalink raw reply	[flat|nested] 82+ messages in thread

* 答复: 答复: [RFC 0/1] Stack trace support in X64 exception handling
  2017-11-14 15:37     ` Paulo Alcantara
  2017-11-14 16:33       ` Brian J. Johnson
@ 2017-11-15 13:21       ` Fan Jeff
  2017-11-15 14:41         ` Paulo Alcantara
  1 sibling, 1 reply; 82+ messages in thread
From: Fan Jeff @ 2017-11-15 13:21 UTC (permalink / raw)
  To: Paulo Alcantara, edk2-devel@lists.01.org
  Cc: Rick Bramley, Laszlo Ersek, Andrew Fish, Eric Dong

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="gb2312", Size: 3296 bytes --]

Paulo,



I will try to validate my code firstly and try to integrate into your patch.



Before my part ready, it¡¯s also welcome if you or others provide yours own solution on X64 MSFT toolchain.



In my mind, IA32 arch should use the same solution with yours, have you tested your solution on IA32 arch with GCC toolchain?



Thanks!

Jeff



________________________________
From: Paulo Alcantara <pcacjr@zytor.com>
Sent: Tuesday, November 14, 2017 11:37:35 PM
To: Fan Jeff; edk2-devel@lists.01.org
Cc: Rick Bramley; Laszlo Ersek; Andrew Fish; Eric Dong
Subject: Re: ´ð¸´: [edk2] [RFC 0/1] Stack trace support in X64 exception handling

Hi Fan,

On 14/11/2017 12:03, Fan Jeff wrote:
> Paul,
>
> I like this feature very much. Actually, I did some POC one year ago but
> I did finalize it.
>
> In my POC, I could use EBP to tack the stack frame on IAS32 arch.
>
> But for x64, I tried to use ¨Ckeepexceptiontable flag to explain stack
> frame from the debug section of image.
>
> I may workson MSFT toolchain, but it did now work well for GCC toolchain.
>
> I think Eric could help to verify MSFT for your patch. If it works well,
> that¡¯s will be great!
>
> Say again, I like this feature!!!:-)

Cool! Your help would be really appreciable! If we get this working for
X64 in both toolchains, that should be easy to port it to IA-32 as well.

Thank you very much for willing to help on that.

Paulo

>
> Thanks!
>
> Jeff
>
> *·¢¼þÈË: *Paulo Alcantara <mailto:pcacjr@zytor.com>
> *·¢ËÍʱ¼ä: *2017Äê11ÔÂ14ÈÕ21:23
> *ÊÕ¼þÈË: *edk2-devel@lists.01.org <mailto:edk2-devel@lists.01.org>
> *³­ËÍ: *Rick Bramley <mailto:richard.bramley@hp.com>; Laszlo Ersek
> <mailto:lersek@redhat.com>; Andrew Fish <mailto:afish@apple.com>; Eric
> Dong <mailto:eric.dong@intel.com>
> *Ö÷Ìâ: *Re: [edk2] [RFC 0/1] Stack trace support in X64 exception handling
>
> Hi,
>
> On 14/11/2017 10:47, Paulo Alcantara wrote:
>> Hi,
>>
>> This series adds stack trace support during a X64 CPU exception.
>>
>> Informations like back trace, stack contents and image module names
>> (that were part of the call stack) will be dumped out.
>>
>> We already have such support in ARM/AArch64 (IIRC) exception handling
>> (thanks to Ard), and then I thought we'd also deserve it in X64 and
>> IA-32 platforms.
>>
>> What do you think guys?
>>
>> BTW, I've tested this only with OVMF (X64 only), using:
>>    - gcc-6.3.0, GCC5, NOOPT
>>
>> Any other tests  would be really appreciable.
>
> I've attached a file to show you how the trace would look like.
>
> Thanks!
> Paulo
>
>>
>> Thanks!
>> Paulo
>>
>> Repo:   https://github.com/pcacjr/edk2.git
>> Branch: stacktrace_x64
>>
>> Cc: Rick Bramley <richard.bramley@hp.com>
>> Cc: Andrew Fish <afish@apple.com>
>> Cc: Eric Dong <eric.dong@intel.com>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Contributed-under: TianoCore Contribution Agreement 1.1
>> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
>> ---
>>
>> Paulo Alcantara (1):
>>    UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
>>
>>   UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 344 +++++++++++++++++++-
>>   1 file changed, 342 insertions(+), 2 deletions(-)
>>
>

^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: 答复: 答复: [RFC 0/1] Stack trace support in X64 exception handling
  2017-11-15 13:21       ` 答复: 答复: " Fan Jeff
@ 2017-11-15 14:41         ` Paulo Alcantara
  2017-11-15 14:52           ` 答复: " Fan Jeff
  0 siblings, 1 reply; 82+ messages in thread
From: Paulo Alcantara @ 2017-11-15 14:41 UTC (permalink / raw)
  To: Fan Jeff, edk2-devel@lists.01.org
  Cc: Rick Bramley, Laszlo Ersek, Andrew Fish, Eric Dong

Hi Fan,

On 15/11/2017 11:21, Fan Jeff wrote:
> Paulo,
> 
> I will try to validate my code firstly and try to integrate into your 
> patch.

OK. Thank you very much for the effort!

> 
> Before my part ready, it’s also welcome if you or others provide yours 
> own solution on X64 MSFT toolchain.
Yes - that would be great. If I get this working on IA32 with GCC 
toolchain shortly, then I'll try to come up with a solution for MSVC.

> In my mind, IA32 arch should use the same solution with yours, have you 
> tested your solution on IA32 arch with GCC toolchain?

In theory, yes. :-) I haven't tested it yet, but I'll starting working 
it on now.

BTW, do you think it's appropriate to just let you know that I pushed 
out another commit with IA32 support, or do you think it's better to 
send a v2 with it?

Besides we shouldn't forget the suggestions from Andrew and Brian to 
validate the RIP and RSP values, as well as to have a kind of PCD to 
limit the number of stack frames printed out -- which I think that are 
really important even if unlikely.

Thanks!
Paulo

> 
> Thanks!
> 
> Jeff
> 
> ------------------------------------------------------------------------
> *From:* Paulo Alcantara <pcacjr@zytor.com>
> *Sent:* Tuesday, November 14, 2017 11:37:35 PM
> *To:* Fan Jeff; edk2-devel@lists.01.org
> *Cc:* Rick Bramley; Laszlo Ersek; Andrew Fish; Eric Dong
> *Subject:* Re: 答复: [edk2] [RFC 0/1] Stack trace support in X64 
> exception handling
> Hi Fan,
> 
> On 14/11/2017 12:03, Fan Jeff wrote:
>> Paul,
>> 
>> I like this feature very much. Actually, I did some POC one year ago but 
>> I did finalize it.
>> 
>> In my POC, I could use EBP to tack the stack frame on IAS32 arch.
>> 
>> But for x64, I tried to use –keepexceptiontable flag to explain stack 
>> frame from the debug section of image.
>> 
>> I may workson MSFT toolchain, but it did now work well for GCC toolchain.
>> 
>> I think Eric could help to verify MSFT for your patch. If it works well, 
>> that’s will be great!
>> 
>> Say again, I like this feature!!!:-)
> 
> Cool! Your help would be really appreciable! If we get this working for
> X64 in both toolchains, that should be easy to port it to IA-32 as well.
> 
> Thank you very much for willing to help on that.
> 
> Paulo
> 
>> 
>> Thanks!
>> 
>> Jeff
>> 
>> *发件人: *Paulo Alcantara <mailto:pcacjr@zytor.com>
>> *发送时间: *2017年11月14日21:23
>> *收件人: *edk2-devel@lists.01.org <mailto:edk2-devel@lists.01.org>
>> *抄送: *Rick Bramley <mailto:richard.bramley@hp.com>; Laszlo Ersek
>> <mailto:lersek@redhat.com>; Andrew Fish <mailto:afish@apple.com>; Eric
>> Dong <mailto:eric.dong@intel.com>
>> *主题: *Re: [edk2] [RFC 0/1] Stack trace support in X64 exception handling
>> 
>> Hi,
>> 
>> On 14/11/2017 10:47, Paulo Alcantara wrote:
>>> Hi,
>>> 
>>> This series adds stack trace support during a X64 CPU exception.
>>> 
>>> Informations like back trace, stack contents and image module names
>>> (that were part of the call stack) will be dumped out.
>>> 
>>> We already have such support in ARM/AArch64 (IIRC) exception handling
>>> (thanks to Ard), and then I thought we'd also deserve it in X64 and
>>> IA-32 platforms.
>>> 
>>> What do you think guys?
>>> 
>>> BTW, I've tested this only with OVMF (X64 only), using:
>>>    - gcc-6.3.0, GCC5, NOOPT
>>> 
>>> Any other tests  would be really appreciable.
>> 
>> I've attached a file to show you how the trace would look like.
>> 
>> Thanks!
>> Paulo
>> 
>>> 
>>> Thanks!
>>> Paulo
>>> 
>>> Repo:   https://github.com/pcacjr/edk2.git
>>> Branch: stacktrace_x64
>>> 
>>> Cc: Rick Bramley <richard.bramley@hp.com>
>>> Cc: Andrew Fish <afish@apple.com>
>>> Cc: Eric Dong <eric.dong@intel.com>
>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>> Contributed-under: TianoCore Contribution Agreement 1.1
>>> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
>>> ---
>>> 
>>> Paulo Alcantara (1):
>>>    UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
>>> 
>>>   UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 344 +++++++++++++++++++-
>>>   1 file changed, 342 insertions(+), 2 deletions(-)
>>> 
>> 


^ permalink raw reply	[flat|nested] 82+ messages in thread

* 答复: 答复: 答复: [RFC 0/1] Stack trace support in X64 exception handling
  2017-11-15 14:41         ` Paulo Alcantara
@ 2017-11-15 14:52           ` Fan Jeff
  0 siblings, 0 replies; 82+ messages in thread
From: Fan Jeff @ 2017-11-15 14:52 UTC (permalink / raw)
  To: Paulo Alcantara, edk2-devel@lists.01.org
  Cc: Rick Bramley, Laszlo Ersek, Andrew Fish, Eric Dong

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="gb2312", Size: 4734 bytes --]

Paulo,



I have no strong opinion on IA32 support  in v2 patch or in separate patch.



I think UefiCpuPkg owner/reviewer may provide their own suggestions on this point.



Thanks!

Jeff





________________________________
From: Paulo Alcantara <pcacjr@zytor.com>
Sent: Wednesday, November 15, 2017 10:41:00 PM
To: Fan Jeff; edk2-devel@lists.01.org
Cc: Rick Bramley; Laszlo Ersek; Andrew Fish; Eric Dong
Subject: Re: ´ð¸´: ´ð¸´: [edk2] [RFC 0/1] Stack trace support in X64 exception handling

Hi Fan,

On 15/11/2017 11:21, Fan Jeff wrote:
> Paulo,
>
> I will try to validate my code firstly and try to integrate into your
> patch.

OK. Thank you very much for the effort!

>
> Before my part ready, it¡¯s also welcome if you or others provide yours
> own solution on X64 MSFT toolchain.
Yes - that would be great. If I get this working on IA32 with GCC
toolchain shortly, then I'll try to come up with a solution for MSVC.

> In my mind, IA32 arch should use the same solution with yours, have you
> tested your solution on IA32 arch with GCC toolchain?

In theory, yes. :-) I haven't tested it yet, but I'll starting working
it on now.

BTW, do you think it's appropriate to just let you know that I pushed
out another commit with IA32 support, or do you think it's better to
send a v2 with it?

Besides we shouldn't forget the suggestions from Andrew and Brian to
validate the RIP and RSP values, as well as to have a kind of PCD to
limit the number of stack frames printed out -- which I think that are
really important even if unlikely.

Thanks!
Paulo

>
> Thanks!
>
> Jeff
>
> ------------------------------------------------------------------------
> *From:* Paulo Alcantara <pcacjr@zytor.com>
> *Sent:* Tuesday, November 14, 2017 11:37:35 PM
> *To:* Fan Jeff; edk2-devel@lists.01.org
> *Cc:* Rick Bramley; Laszlo Ersek; Andrew Fish; Eric Dong
> *Subject:* Re: ´ð¸´: [edk2] [RFC 0/1] Stack trace support in X64
> exception handling
> Hi Fan,
>
> On 14/11/2017 12:03, Fan Jeff wrote:
>> Paul,
>>
>> I like this feature very much. Actually, I did some POC one year ago but
>> I did finalize it.
>>
>> In my POC, I could use EBP to tack the stack frame on IAS32 arch.
>>
>> But for x64, I tried to use ¨Ckeepexceptiontable flag to explain stack
>> frame from the debug section of image.
>>
>> I may workson MSFT toolchain, but it did now work well for GCC toolchain.
>>
>> I think Eric could help to verify MSFT for your patch. If it works well,
>> that¡¯s will be great!
>>
>> Say again, I like this feature!!!:-)
>
> Cool! Your help would be really appreciable! If we get this working for
> X64 in both toolchains, that should be easy to port it to IA-32 as well.
>
> Thank you very much for willing to help on that.
>
> Paulo
>
>>
>> Thanks!
>>
>> Jeff
>>
>> *·¢¼þÈË: *Paulo Alcantara <mailto:pcacjr@zytor.com>
>> *·¢ËÍʱ¼ä: *2017Äê11ÔÂ14ÈÕ21:23
>> *ÊÕ¼þÈË: *edk2-devel@lists.01.org <mailto:edk2-devel@lists.01.org>
>> *³­ËÍ: *Rick Bramley <mailto:richard.bramley@hp.com>; Laszlo Ersek
>> <mailto:lersek@redhat.com>; Andrew Fish <mailto:afish@apple.com>; Eric
>> Dong <mailto:eric.dong@intel.com>
>> *Ö÷Ìâ: *Re: [edk2] [RFC 0/1] Stack trace support in X64 exception handling
>>
>> Hi,
>>
>> On 14/11/2017 10:47, Paulo Alcantara wrote:
>>> Hi,
>>>
>>> This series adds stack trace support during a X64 CPU exception.
>>>
>>> Informations like back trace, stack contents and image module names
>>> (that were part of the call stack) will be dumped out.
>>>
>>> We already have such support in ARM/AArch64 (IIRC) exception handling
>>> (thanks to Ard), and then I thought we'd also deserve it in X64 and
>>> IA-32 platforms.
>>>
>>> What do you think guys?
>>>
>>> BTW, I've tested this only with OVMF (X64 only), using:
>>>    - gcc-6.3.0, GCC5, NOOPT
>>>
>>> Any other tests  would be really appreciable.
>>
>> I've attached a file to show you how the trace would look like.
>>
>> Thanks!
>> Paulo
>>
>>>
>>> Thanks!
>>> Paulo
>>>
>>> Repo:   https://github.com/pcacjr/edk2.git
>>> Branch: stacktrace_x64
>>>
>>> Cc: Rick Bramley <richard.bramley@hp.com>
>>> Cc: Andrew Fish <afish@apple.com>
>>> Cc: Eric Dong <eric.dong@intel.com>
>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>> Contributed-under: TianoCore Contribution Agreement 1.1
>>> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
>>> ---
>>>
>>> Paulo Alcantara (1):
>>>    UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
>>>
>>>   UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 344 +++++++++++++++++++-
>>>   1 file changed, 342 insertions(+), 2 deletions(-)
>>>
>>

^ permalink raw reply	[flat|nested] 82+ messages in thread

* [RFC v2 0/3] Stack trace support in X64 exception handling
  2017-11-14 12:47 [RFC 0/1] Stack trace support in X64 exception handling Paulo Alcantara
  2017-11-14 12:47 ` [RFC 1/1] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support Paulo Alcantara
  2017-11-14 13:21 ` [RFC 0/1] " Paulo Alcantara
@ 2017-11-16  1:18 ` Paulo Alcantara
  2017-11-16  1:18   ` [RFC v2 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support Paulo Alcantara
                     ` (4 more replies)
  2 siblings, 5 replies; 82+ messages in thread
From: Paulo Alcantara @ 2017-11-16  1:18 UTC (permalink / raw)
  To: edk2-devel
  Cc: Paulo Alcantara, Rick Bramley, Andrew Fish, Eric Dong,
	Laszlo Ersek, Brian J. Johnson, Jeff Fan

Hi,

This series adds stack trace support during a X64 CPU exception.

Informations like back trace, stack contents and image module names
(that were part of the call stack) will be dumped out.

We already have such support in ARM/AArch64 (IIRC) exception handling
(thanks to Ard), and then I thought we'd also deserve it in X64 and
IA-32 platforms.

What do you think guys?

BTW, I've tested this only with OVMF (X64 only), using:
  - gcc-6.3.0, GCC5, NOOPT

Any other tests  would be really appreciable.

Thanks!
Paulo

Repo:   https://github.com/pcacjr/edk2.git
Branch: stacktrace_v2

Cc: Rick Bramley <richard.bramley@hp.com>
Cc: Andrew Fish <afish@apple.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: "Brian J. Johnson" <brian.johnson@hpe.com>
Cc: Jeff Fan <jeff.fan@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---

v1 -> v2:
  * Add IA32 arch support (GCC toolchain only)
  * Replace hard-coded stack alignment value (16) with
    CPU_STACK_ALIGNMENT.
  * Check for proper stack and frame pointer alignments.
  * Fix initialization of UnwoundStacksCount to 1.
  * Move GetPdbFileName() to common code since it will be used by both
    IA32 and X64 implementations.

Paulo Alcantara (3):
  UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
  UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName()
  UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support

 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c        | 102 ++++---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h        |  25 +-
 UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c | 303 ++++++++++++++++++-
 UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c  | 310 +++++++++++++++++++-
 4 files changed, 682 insertions(+), 58 deletions(-)

-- 
2.14.3



^ permalink raw reply	[flat|nested] 82+ messages in thread

* [RFC v2 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
  2017-11-16  1:18 ` [RFC v2 0/3] " Paulo Alcantara
@ 2017-11-16  1:18   ` Paulo Alcantara
  2017-11-16  1:57     ` Yao, Jiewen
  2017-11-16 15:43     ` Brian J. Johnson
  2017-11-16  1:18   ` [RFC v2 2/3] UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName() Paulo Alcantara
                     ` (3 subsequent siblings)
  4 siblings, 2 replies; 82+ messages in thread
From: Paulo Alcantara @ 2017-11-16  1:18 UTC (permalink / raw)
  To: edk2-devel; +Cc: Paulo Alcantara, Eric Dong, Laszlo Ersek

This patch adds stack trace support during a X64 CPU exception.

It will dump out back trace, stack contents as well as image module
names that were part of the call stack.

Contributed-under: TianoCore Contribution Agreement 1.1
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 369 +++++++++++++++++++-
 1 file changed, 367 insertions(+), 2 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
index 65f0cff680..11cd7c9e1c 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
@@ -14,6 +14,11 @@
 
 #include "CpuExceptionCommon.h"
 
+//
+// Unknown PDB file name
+//
+GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
+
 /**
   Return address map of exception handler template so that C code can generate
   exception tables.
@@ -242,6 +247,350 @@ DumpCpuContext (
     );
 }
 
+/**
+  Get absolute path and file name of PDB file in PE/COFF image.
+
+  @param[in]  ImageBase            Base address of PE/COFF image.
+  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
+  @param[out] PdbFileName          File name of PDB file.
+**/
+STATIC
+VOID
+GetPdbFileName (
+  IN  UINTN    ImageBase,
+  OUT CHAR8    **PdbAbsoluteFilePath,
+  OUT CHAR8    **PdbFileName
+  )
+{
+  VOID   *PdbPointer;
+  CHAR8  *Str;
+
+  //
+  // Get PDB file name from PE/COFF image
+  //
+  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
+  if (PdbPointer == NULL) {
+    //
+    // No PDB file name found. Set it to an unknown file name.
+    //
+    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
+    if (PdbAbsoluteFilePath != NULL) {
+      *PdbAbsoluteFilePath = NULL;
+    }
+  } else {
+    //
+    // Get file name portion out of PDB file in PE/COFF image
+    //
+    Str = (CHAR8 *)((UINTN)PdbPointer +
+                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
+    for (; *Str != '/' && *Str != '\\'; Str--) {
+      ;
+    }
+
+    //
+    // Set PDB file name (also skip trailing path separator: '/' or '\\')
+    //
+    *PdbFileName = Str + 1;
+
+    if (PdbAbsoluteFilePath != NULL) {
+      //
+      // Set absolute file path of PDB file
+      //
+      *PdbAbsoluteFilePath = PdbPointer;
+    }
+  }
+}
+
+/**
+  Dump stack contents.
+
+  @param[in]  CurrentRsp         Current stack pointer address.
+  @param[in]  UnwondStacksCount  Count of unwond stack frames.
+**/
+STATIC
+VOID
+DumpStackContents (
+  IN UINT64  CurrentRsp,
+  IN INTN    UnwondStacksCount
+  )
+{
+  //
+  // Check for proper stack pointer alignment
+  //
+  if (((UINTN)CurrentRsp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
+    InternalPrintMessage ("!!!! Unaligned stack pointer. !!!!\n");
+    return;
+  }
+
+  //
+  // Dump out stack contents
+  //
+  InternalPrintMessage ("\nStack dump:\n");
+  while (UnwondStacksCount-- > 0) {
+    InternalPrintMessage (
+      "0x%016lx: %016lx %016lx\n",
+      CurrentRsp,
+      *(UINT64 *)CurrentRsp,
+      *(UINT64 *)((UINTN)CurrentRsp + 8)
+      );
+
+    //
+    // Point to next stack
+    //
+    CurrentRsp += CPU_STACK_ALIGNMENT;
+  }
+}
+
+/**
+  Dump all image module names from call stack.
+
+  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+**/
+STATIC
+VOID
+DumpImageModuleNames (
+  IN EFI_SYSTEM_CONTEXT   SystemContext
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Rip;
+  UINTN       ImageBase;
+  VOID        *EntryPoint;
+  CHAR8       *PdbAbsoluteFilePath;
+  CHAR8       *PdbFileName;
+  UINT64      Rbp;
+
+  //
+  // Set current RIP address
+  //
+  Rip = SystemContext.SystemContextX64->Rip;
+
+  //
+  // Set current frame pointer address
+  //
+  Rbp = SystemContext.SystemContextX64->Rbp;
+
+  //
+  // Check for proper frame pointer alignment
+  //
+  if (((UINTN)Rbp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
+    InternalPrintMessage ("!!!! Unaligned frame pointer. !!!!\n");
+    return;
+  }
+
+  //
+  // Get initial PE/COFF image base address from current RIP
+  //
+  ImageBase = PeCoffSearchImageBase (Rip);
+  if (ImageBase == 0) {
+    InternalPrintMessage ("!!!! Could not find image module names. !!!!");
+    return;
+  }
+
+  //
+  // Get initial PE/COFF image's entry point
+  //
+  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
+  if (EFI_ERROR (Status)) {
+    EntryPoint = NULL;
+  }
+
+  //
+  // Get file name and absolute path of initial PDB file
+  //
+  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
+
+  //
+  // Print out initial image module name (if any)
+  //
+  if (PdbAbsoluteFilePath != NULL) {
+    InternalPrintMessage (
+      "\n%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
+      PdbFileName,
+      ImageBase,
+      (UINTN)EntryPoint
+      );
+    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
+  }
+
+  //
+  // Walk through call stack and find next module names
+  //
+  for (;;) {
+    //
+    // Set RIP with return address from current stack frame
+    //
+    Rip = *(UINT64 *)((UINTN)Rbp + 8);
+
+    //
+    // If RIP is zero, then stop unwinding the stack
+    //
+    if (Rip == 0) {
+      break;
+    }
+
+    //
+    // Check if RIP is within another PE/COFF image base address
+    //
+    if (Rip < ImageBase) {
+      //
+      // Search for the respective PE/COFF image based on RIP
+      //
+      ImageBase = PeCoffSearchImageBase (Rip);
+      if (ImageBase == 0) {
+        //
+        // Stop stack trace
+        //
+        break;
+      }
+
+      //
+      // Get PE/COFF image's entry point
+      //
+      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
+      if (EFI_ERROR (Status)) {
+        EntryPoint = NULL;
+      }
+
+      //
+      // Get file name and absolute path of PDB file
+      //
+      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
+
+      //
+      // Print out image module name (if any)
+      //
+      if (PdbAbsoluteFilePath != NULL) {
+        InternalPrintMessage (
+          "%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
+          PdbFileName,
+          ImageBase,
+          (UINTN)EntryPoint
+          );
+        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
+      }
+    }
+
+    //
+    // Unwind the stack
+    //
+    Rbp = *(UINT64 *)(UINTN)Rbp;
+  }
+}
+
+/**
+  Dump stack trace.
+
+  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
+  @param[out] UnwondStacksCount  Count of unwond stack frames.
+**/
+STATIC
+VOID
+DumpStackTrace (
+  IN  EFI_SYSTEM_CONTEXT   SystemContext,
+  OUT INTN                 *UnwondStacksCount
+  )
+{
+  UINT64  Rip;
+  UINT64  Rbp;
+  UINTN   ImageBase;
+  CHAR8   *PdbFileName;
+
+  //
+  // Set current RIP address
+  //
+  Rip = SystemContext.SystemContextX64->Rip;
+
+  //
+  // Set current frame pointer address
+  //
+  Rbp = SystemContext.SystemContextX64->Rbp;
+
+  //
+  // Get initial PE/COFF image base address from current RIP
+  //
+  ImageBase = PeCoffSearchImageBase (Rip);
+  if (ImageBase == 0) {
+    InternalPrintMessage ("!!!! Could not find backtrace information. !!!!");
+    return;
+  }
+
+  //
+  // Get PDB file name from initial PE/COFF image
+  //
+  GetPdbFileName (ImageBase, NULL, &PdbFileName);
+
+  //
+  // Initialize count of unwond stacks
+  //
+  *UnwondStacksCount = 1;
+
+  //
+  // Print out back trace
+  //
+  InternalPrintMessage ("\nCall trace:\n");
+
+  for (;;) {
+    //
+    // Print stack frame in the following format:
+    //
+    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName> | ????]
+    //
+    InternalPrintMessage (
+      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
+      *UnwondStacksCount - 1,
+      Rip,
+      ImageBase,
+      Rip - ImageBase - 1,
+      Rbp,
+      PdbFileName
+      );
+
+    //
+    // Set RIP with return address from current stack frame
+    //
+    Rip = *(UINT64 *)((UINTN)Rbp + 8);
+
+    //
+    // If RIP is zero, then stop unwinding the stack
+    //
+    if (Rip == 0) {
+      break;
+    }
+
+    //
+    // Check if RIP is within another PE/COFF image base address
+    //
+    if (Rip < ImageBase) {
+      //
+      // Search for the respective PE/COFF image based on RIP
+      //
+      ImageBase = PeCoffSearchImageBase (Rip);
+      if (ImageBase == 0) {
+        //
+        // Stop stack trace
+        //
+        break;
+      }
+
+      //
+      // Get PDB file name
+      //
+      GetPdbFileName (ImageBase, NULL, &PdbFileName);
+    }
+
+    //
+    // Unwind the stack
+    //
+    Rbp = *(UINT64 *)(UINTN)Rbp;
+
+    //
+    // Increment count of unwond stacks
+    //
+    (*UnwondStacksCount)++;
+  }
+}
+
 /**
   Display CPU information.
 
@@ -254,9 +603,25 @@ DumpImageAndCpuContent (
   IN EFI_SYSTEM_CONTEXT   SystemContext
   )
 {
+  INTN UnwondStacksCount;
+
+  //
+  // Dump CPU context
+  //
   DumpCpuContext (ExceptionType, SystemContext);
+
+  //
+  // Dump stack trace
+  //
+  DumpStackTrace (SystemContext, &UnwondStacksCount);
+
+  //
+  // Dump image module names
+  //
+  DumpImageModuleNames (SystemContext);
+
   //
-  // Dump module image base and module entry point by RIP
+  // Dump stack contents
   //
-  DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
+  DumpStackContents (SystemContext.SystemContextX64->Rsp, UnwondStacksCount);
 }
-- 
2.14.3



^ permalink raw reply related	[flat|nested] 82+ messages in thread

* [RFC v2 2/3] UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName()
  2017-11-16  1:18 ` [RFC v2 0/3] " Paulo Alcantara
  2017-11-16  1:18   ` [RFC v2 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support Paulo Alcantara
@ 2017-11-16  1:18   ` Paulo Alcantara
  2017-11-16  1:18   ` [RFC v2 3/3] UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support Paulo Alcantara
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2017-11-16  1:18 UTC (permalink / raw)
  To: edk2-devel; +Cc: Paulo Alcantara, Eric Dong, Laszlo Ersek

This function will be used by both IA32 and X64 exception handling in
order to print out image module names during stack unwinding.

Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c       | 60 +++++++++++++++++++-
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h       | 14 +++++
 UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 59 -------------------
 3 files changed, 73 insertions(+), 60 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
index dbfaae1d30..f62ab8c48c 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
@@ -54,6 +54,11 @@ CONST CHAR8 *mExceptionNameStr[] = {
 
 #define EXCEPTION_KNOWN_NAME_NUM  (sizeof (mExceptionNameStr) / sizeof (CHAR8 *))
 
+//
+// Unknown PDB file name
+//
+GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
+
 /**
   Get ASCII format string exception name by exception type.
 
@@ -177,4 +182,57 @@ ReadAndVerifyVectorInfo (
     VectorInfo ++;
   }
   return EFI_SUCCESS;
-}
\ No newline at end of file
+}
+
+/**
+  Get absolute path and file name of PDB file in PE/COFF image.
+
+  @param[in]  ImageBase            Base address of PE/COFF image.
+  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
+  @param[out] PdbFileName          File name of PDB file.
+**/
+VOID
+GetPdbFileName (
+  IN  UINTN    ImageBase,
+  OUT CHAR8    **PdbAbsoluteFilePath,
+  OUT CHAR8    **PdbFileName
+  )
+{
+  VOID   *PdbPointer;
+  CHAR8  *Str;
+
+  //
+  // Get PDB file name from PE/COFF image
+  //
+  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
+  if (PdbPointer == NULL) {
+    //
+    // No PDB file name found. Set it to an unknown file name.
+    //
+    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
+    if (PdbAbsoluteFilePath != NULL) {
+      *PdbAbsoluteFilePath = NULL;
+    }
+  } else {
+    //
+    // Get file name portion out of PDB file in PE/COFF image
+    //
+    Str = (CHAR8 *)((UINTN)PdbPointer +
+                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
+    for (; *Str != '/' && *Str != '\\'; Str--) {
+      ;
+    }
+
+    //
+    // Set PDB file name (also skip trailing path separator: '/' or '\\')
+    //
+    *PdbFileName = Str + 1;
+
+    if (PdbAbsoluteFilePath != NULL) {
+      //
+      // Set absolute file path of PDB file
+      //
+      *PdbAbsoluteFilePath = PdbPointer;
+    }
+  }
+}
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
index 740a58828b..042207025e 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
@@ -288,5 +288,19 @@ CommonExceptionHandlerWorker (
   IN EXCEPTION_HANDLER_DATA      *ExceptionHandlerData
   );
 
+/**
+  Get absolute path and file name of PDB file in PE/COFF image.
+
+  @param[in]  ImageBase            Base address of PE/COFF image.
+  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
+  @param[out] PdbFileName          File name of PDB file.
+**/
+VOID
+GetPdbFileName (
+  IN  UINTN    ImageBase,
+  OUT CHAR8    **PdbAbsoluteFilePath,
+  OUT CHAR8    **PdbFileName
+  );
+
 #endif
 
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
index 11cd7c9e1c..ace835c02c 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
@@ -14,11 +14,6 @@
 
 #include "CpuExceptionCommon.h"
 
-//
-// Unknown PDB file name
-//
-GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
-
 /**
   Return address map of exception handler template so that C code can generate
   exception tables.
@@ -247,60 +242,6 @@ DumpCpuContext (
     );
 }
 
-/**
-  Get absolute path and file name of PDB file in PE/COFF image.
-
-  @param[in]  ImageBase            Base address of PE/COFF image.
-  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
-  @param[out] PdbFileName          File name of PDB file.
-**/
-STATIC
-VOID
-GetPdbFileName (
-  IN  UINTN    ImageBase,
-  OUT CHAR8    **PdbAbsoluteFilePath,
-  OUT CHAR8    **PdbFileName
-  )
-{
-  VOID   *PdbPointer;
-  CHAR8  *Str;
-
-  //
-  // Get PDB file name from PE/COFF image
-  //
-  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
-  if (PdbPointer == NULL) {
-    //
-    // No PDB file name found. Set it to an unknown file name.
-    //
-    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
-    if (PdbAbsoluteFilePath != NULL) {
-      *PdbAbsoluteFilePath = NULL;
-    }
-  } else {
-    //
-    // Get file name portion out of PDB file in PE/COFF image
-    //
-    Str = (CHAR8 *)((UINTN)PdbPointer +
-                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
-    for (; *Str != '/' && *Str != '\\'; Str--) {
-      ;
-    }
-
-    //
-    // Set PDB file name (also skip trailing path separator: '/' or '\\')
-    //
-    *PdbFileName = Str + 1;
-
-    if (PdbAbsoluteFilePath != NULL) {
-      //
-      // Set absolute file path of PDB file
-      //
-      *PdbAbsoluteFilePath = PdbPointer;
-    }
-  }
-}
-
 /**
   Dump stack contents.
 
-- 
2.14.3



^ permalink raw reply related	[flat|nested] 82+ messages in thread

* [RFC v2 3/3] UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support
  2017-11-16  1:18 ` [RFC v2 0/3] " Paulo Alcantara
  2017-11-16  1:18   ` [RFC v2 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support Paulo Alcantara
  2017-11-16  1:18   ` [RFC v2 2/3] UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName() Paulo Alcantara
@ 2017-11-16  1:18   ` Paulo Alcantara
  2017-11-16  1:46   ` [RFC v2 0/3] Stack trace support in X64 exception handling Paulo Alcantara
  2017-11-16 21:56   ` [RFC v3 " Paulo Alcantara
  4 siblings, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2017-11-16  1:18 UTC (permalink / raw)
  To: edk2-devel; +Cc: Paulo Alcantara, Eric Dong, Laszlo Ersek

This patch adds stack trace support during a IA32 CPU exception.

It will dump out back trace, stack contents as well as image module
names that were part of the call stack.

Contributed-under: TianoCore Contribution Agreement 1.1
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c        |  42 ---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h        |  11 -
 UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c | 303 +++++++++++++++++++-
 3 files changed, 301 insertions(+), 55 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
index f62ab8c48c..867c5c01d6 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
@@ -109,48 +109,6 @@ InternalPrintMessage (
   SerialPortWrite ((UINT8 *)Buffer, AsciiStrLen (Buffer));
 }
 
-/**
-  Find and display image base address and return image base and its entry point.
-
-  @param CurrentEip      Current instruction pointer.
-
-**/
-VOID
-DumpModuleImageInfo (
-  IN  UINTN              CurrentEip
-  )
-{
-  EFI_STATUS                           Status;
-  UINTN                                Pe32Data;
-  VOID                                 *PdbPointer;
-  VOID                                 *EntryPoint;
-
-  Pe32Data = PeCoffSearchImageBase (CurrentEip);
-  if (Pe32Data == 0) {
-    InternalPrintMessage ("!!!! Can't find image information. !!!!\n");
-  } else {
-    //
-    // Find Image Base entry point
-    //
-    Status = PeCoffLoaderGetEntryPoint ((VOID *) Pe32Data, &EntryPoint);
-    if (EFI_ERROR (Status)) {
-      EntryPoint = NULL;
-    }
-    InternalPrintMessage ("!!!! Find image ");
-    PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *) Pe32Data);
-    if (PdbPointer != NULL) {
-      InternalPrintMessage ("%a", PdbPointer);
-    } else {
-      InternalPrintMessage ("(No PDB) " );
-    }
-    InternalPrintMessage (
-      " (ImageBase=%016lp, EntryPoint=%016p) !!!!\n",
-      (VOID *) Pe32Data,
-      EntryPoint
-      );
-  }
-}
-
 /**
   Read and save reserved vector information
 
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
index 042207025e..478374d003 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
@@ -119,17 +119,6 @@ InternalPrintMessage (
   ...
   );
 
-/**
-  Find and display image base address and return image base and its entry point.
-
-  @param CurrentEip      Current instruction pointer.
-
-**/
-VOID
-DumpModuleImageInfo (
-  IN  UINTN              CurrentEip
-  );
-
 /**
   Display CPU information.
 
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
index f2c39eb193..169a0b660e 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
@@ -210,6 +210,289 @@ DumpCpuContext (
     );
 }
 
+/**
+  Dump stack trace.
+
+  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
+  @param[out] UnwondStacksCount  Count of unwond stack frames.
+**/
+STATIC
+VOID
+DumpStackTrace (
+  IN  EFI_SYSTEM_CONTEXT   SystemContext,
+  OUT INTN                 *UnwondStacksCount
+  )
+{
+  UINT32  Eip;
+  UINT32  Ebp;
+  UINTN   ImageBase;
+  CHAR8   *PdbFileName;
+
+  //
+  // Set current EIP address
+  //
+  Eip = SystemContext.SystemContextIa32->Eip;
+
+  //
+  // Set current frame pointer address
+  //
+  Ebp = SystemContext.SystemContextIa32->Ebp;
+
+  //
+  // Check for proper frame pointer alignment
+  //
+  if (((UINTN)Ebp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
+    InternalPrintMessage ("!!!! Unaligned frame pointer. !!!!\n");
+    return;
+  }
+
+  //
+  // Get initial PE/COFF image base address from current EIP
+  //
+  ImageBase = PeCoffSearchImageBase (Eip);
+  if (ImageBase == 0) {
+    InternalPrintMessage ("!!!! Could not find backtrace information. !!!!");
+    return;
+  }
+
+  //
+  // Get PDB file name from initial PE/COFF image
+  //
+  GetPdbFileName (ImageBase, NULL, &PdbFileName);
+
+  //
+  // Initialize count of unwond stacks
+  //
+  *UnwondStacksCount = 1;
+
+  //
+  // Print out back trace
+  //
+  InternalPrintMessage ("\nCall trace:\n");
+
+  for (;;) {
+    //
+    // Print stack frame in the following format:
+    //
+    // # <EIP> @ <ImageBase>+<RelOffset> (EBP) in [<ModuleName> | ????]
+    //
+    InternalPrintMessage (
+      "%d 0x%08x @ 0x%08x+0x%x (0x%08x) in %a\n",
+      *UnwondStacksCount - 1,
+      Eip,
+      ImageBase,
+      Eip - ImageBase - 1,
+      Ebp,
+      PdbFileName
+      );
+
+    //
+    // Set EIP with return address from current stack frame
+    //
+    Eip = *(UINT32 *)((UINTN)Ebp + 4);
+
+    //
+    // If EIP is zero, then stop unwinding the stack
+    //
+    if (Eip == 0) {
+      break;
+    }
+
+    //
+    // Check if EIP is within another PE/COFF image base address
+    //
+    if (Eip < ImageBase) {
+      //
+      // Search for the respective PE/COFF image based on EIP
+      //
+      ImageBase = PeCoffSearchImageBase (Eip);
+      if (ImageBase == 0) {
+        //
+        // Stop stack trace
+        //
+        break;
+      }
+
+      //
+      // Get PDB file name
+      //
+      GetPdbFileName (ImageBase, NULL, &PdbFileName);
+    }
+
+    //
+    // Unwind the stack
+    //
+    Ebp = *(UINT32 *)(UINTN)Ebp;
+
+    //
+    // Increment count of unwond stacks
+    //
+    (*UnwondStacksCount)++;
+  }
+}
+
+/**
+  Dump all image module names from call stack.
+
+  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+**/
+STATIC
+VOID
+DumpImageModuleNames (
+  IN EFI_SYSTEM_CONTEXT   SystemContext
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      Eip;
+  UINT32      Ebp;
+  UINTN       ImageBase;
+  VOID        *EntryPoint;
+  CHAR8       *PdbAbsoluteFilePath;
+  CHAR8       *PdbFileName;
+
+  //
+  // Set current EIP address
+  //
+  Eip = SystemContext.SystemContextIa32->Eip;
+
+  //
+  // Set current frame pointer address
+  //
+  Ebp = SystemContext.SystemContextIa32->Ebp;
+
+  //
+  // Get initial PE/COFF image base address from current EIP
+  //
+  ImageBase = PeCoffSearchImageBase (Eip);
+  if (ImageBase == 0) {
+    InternalPrintMessage ("!!!! Could not find image module names. !!!!");
+    return;
+  }
+
+  //
+  // Get initial PE/COFF image's entry point
+  //
+  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
+  if (EFI_ERROR (Status)) {
+    EntryPoint = NULL;
+  }
+
+  //
+  // Get file name and absolute path of initial PDB file
+  //
+  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
+
+  //
+  // Print out initial image module name (if any)
+  //
+  if (PdbAbsoluteFilePath != NULL) {
+    InternalPrintMessage (
+      "\n%a (ImageBase=0x%08x, EntryPoint=0x%08x):\n",
+      PdbFileName,
+      ImageBase,
+      (UINTN)EntryPoint
+      );
+    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
+  }
+
+  //
+  // Walk through call stack and find next module names
+  //
+  for (;;) {
+    //
+    // Set EIP with return address from current stack frame
+    //
+    Eip = *(UINT32 *)((UINTN)Ebp + 4);
+
+    //
+    // Check if EIP is within another PE/COFF image base address
+    //
+    if (Eip < ImageBase) {
+      //
+      // Search for the respective PE/COFF image based on Eip
+      //
+      ImageBase = PeCoffSearchImageBase (Eip);
+      if (ImageBase == 0) {
+        //
+        // Stop stack trace
+        //
+        break;
+      }
+
+      //
+      // Get PE/COFF image's entry point
+      //
+      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
+      if (EFI_ERROR (Status)) {
+        EntryPoint = NULL;
+      }
+
+      //
+      // Get file name and absolute path of PDB file
+      //
+      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
+
+      //
+      // Print out image module name (if any)
+      //
+      if (PdbAbsoluteFilePath != NULL) {
+        InternalPrintMessage (
+          "%a (ImageBase=0x%08x, EntryPoint=0x%08x):\n",
+          PdbFileName,
+          ImageBase,
+          (UINTN)EntryPoint
+          );
+        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
+      }
+    }
+
+    //
+    // Unwind the stack
+    //
+    Ebp = *(UINT32 *)(UINTN)Ebp;
+  }
+}
+
+/**
+  Dump stack contents.
+
+  @param[in]  CurrentEsp         Current stack pointer address.
+  @param[in]  UnwondStacksCount  Count of unwond stack frames.
+**/
+STATIC
+VOID
+DumpStackContents (
+  IN UINT32  CurrentEsp,
+  IN INTN    UnwondStacksCount
+  )
+{
+  //
+  // Check for proper stack alignment
+  //
+  if (((UINTN)CurrentEsp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
+    InternalPrintMessage ("!!!! Unaligned stack pointer. !!!!\n");
+    return;
+  }
+
+  //
+  // Dump out stack contents
+  //
+  InternalPrintMessage ("\nStack dump:\n");
+  while (UnwondStacksCount-- > 0) {
+    InternalPrintMessage (
+      "0x%08x: %08x %08x\n",
+      CurrentEsp,
+      *(UINT32 *)CurrentEsp,
+      *(UINT32 *)((UINTN)CurrentEsp + 4)
+      );
+
+    //
+    // Point to next stack
+    //
+    CurrentEsp += CPU_STACK_ALIGNMENT;
+  }
+}
+
 /**
   Display CPU information.
 
@@ -222,9 +505,25 @@ DumpImageAndCpuContent (
   IN EFI_SYSTEM_CONTEXT   SystemContext
   )
 {
+  INTN UnwondStacksCount;
+
+  //
+  // Dump CPU context
+  //
   DumpCpuContext (ExceptionType, SystemContext);
+
+  //
+  // Dump stack trace
+  //
+  DumpStackTrace (SystemContext, &UnwondStacksCount);
+
+  //
+  // Dump image module names
+  //
+  DumpImageModuleNames (SystemContext);
+
   //
-  // Dump module image base and module entry point by EIP
+  // Dump stack contents
   //
-  DumpModuleImageInfo (SystemContext.SystemContextIa32->Eip);
+  DumpStackContents (SystemContext.SystemContextIa32->Esp, UnwondStacksCount);
 }
-- 
2.14.3



^ permalink raw reply related	[flat|nested] 82+ messages in thread

* Re: [RFC v2 0/3] Stack trace support in X64 exception handling
  2017-11-16  1:18 ` [RFC v2 0/3] " Paulo Alcantara
                     ` (2 preceding siblings ...)
  2017-11-16  1:18   ` [RFC v2 3/3] UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support Paulo Alcantara
@ 2017-11-16  1:46   ` Paulo Alcantara
  2017-11-16  5:01     ` Andrew Fish
  2017-11-16 21:56   ` [RFC v3 " Paulo Alcantara
  4 siblings, 1 reply; 82+ messages in thread
From: Paulo Alcantara @ 2017-11-16  1:46 UTC (permalink / raw)
  To: edk2-devel
  Cc: Rick Bramley, Andrew Fish, Eric Dong, Laszlo Ersek,
	Brian J. Johnson, Jeff Fan

[-- Attachment #1: Type: text/plain, Size: 2507 bytes --]

Hi,

On 11/15/2017 11:18 PM, Paulo Alcantara wrote:
> Hi,
> 
> This series adds stack trace support during a X64 CPU exception.
> 
> Informations like back trace, stack contents and image module names
> (that were part of the call stack) will be dumped out.
> 
> We already have such support in ARM/AArch64 (IIRC) exception handling
> (thanks to Ard), and then I thought we'd also deserve it in X64 and
> IA-32 platforms.
> 
> What do you think guys?
> 
> BTW, I've tested this only with OVMF (X64 only), using:
>    - gcc-6.3.0, GCC5, NOOPT
> 
> Any other tests  would be really appreciable.
> 
> Thanks!
> Paulo
> 
> Repo:   https://github.com/pcacjr/edk2.git
> Branch: stacktrace_v2
> 
> Cc: Rick Bramley <richard.bramley@hp.com>
> Cc: Andrew Fish <afish@apple.com>
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: "Brian J. Johnson" <brian.johnson@hpe.com>
> Cc: Jeff Fan <jeff.fan@intel.com>
> Contributed-under: TianoCore Contribution Agreement 1.1
> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> ---
> 
> v1 -> v2:
>    * Add IA32 arch support (GCC toolchain only)
>    * Replace hard-coded stack alignment value (16) with
>      CPU_STACK_ALIGNMENT.
>    * Check for proper stack and frame pointer alignments.
>    * Fix initialization of UnwoundStacksCount to 1.
>    * Move GetPdbFileName() to common code since it will be used by both
>      IA32 and X64 implementations.

Sorry for the delay in sending v2. It's holiday here :-)

FWIW, I've attached two files which contain stack trace dumps of IA32 
and X64 exceptions.

The new IA32 arch support is still limited to GCC only (that is, relying 
on frame pointers), but I'll start investing in a new solution that 
would work on both MSVC and GCC toolchains -- probably this weekend. If 
I come up with something, I'll let you know.

On IA32, I performed the same test as in X64 to trigger an NMI interrupt 
manually with: asm ("int $0x2") in PartitionDxe driver and watched out 
the call stack. The difference between the two dumps, regardless the CPU 
context, etc. is that we don't see the calls from PeiCore.dll. Then I 
figured out that the EIP gets a value of 0 before jumping to 
PartitionDxe's entry point.

I guess that's related to the "push $0" that Andrew mentioned earlier so 
the debugger knows when to stop unwinding. Although I can't see a "push 
0" equivalent neither in SwitchStack.nasm nor in SwitchStack.asm for X64
-- so we're able to see the calls within PeiCore.dll.

Thanks!
Paulo

[-- Attachment #2: x64_dump.txt --]
[-- Type: text/plain, Size: 4665 bytes --]

!!!! X64 Exception Type - 02(NMI Interrupt)  CPU Apic ID - 00000000 !!!!
RIP  - 000000007EC30266, CS  - 0000000000000038, RFLAGS - 0000000000000202
RAX  - 000000007FE71018, RCX - 000000007F34F498, RDX - 000000007FE71018
RBX  - 0000000000810248, RSP - 000000007FEE4C70, RBP - 000000007FEE4CB0
RSI  - 0000000000000007, RDI - 000000007F34E018
R8   - 000000007EC32DC8, R9  - 000000007F34E298, R10 - 0000000000000036
R11  - 00000000000000D7, R12 - 0000000000000000, R13 - 0000000000000000
R14  - 0000000000000000, R15 - 0000000000000000
DS   - 0000000000000030, ES  - 0000000000000030, FS  - 0000000000000030
GS   - 0000000000000030, SS  - 0000000000000030
CR0  - 0000000080010033, CR2 - 0000000000000000, CR3 - 000000007FE83000
CR4  - 0000000000000668, CR8 - 0000000000000000
DR0  - 0000000000000000, DR1 - 0000000000000000, DR2 - 0000000000000000
DR3  - 0000000000000000, DR6 - 00000000FFFF0FF0, DR7 - 0000000000000400
GDTR - 000000007FE71A98 0000000000000047, LDTR - 0000000000000000
IDTR - 000000007F7AB018 0000000000000FFF,   TR - 0000000000000000
FXSAVE_STATE - 000000007FEE48D0

Call trace:
0 0x000000007EC30266 @ 0x000000007EC28000+0x8265 (0x000000007FEE4CB0) in PartitionDxe.dll
1 0x000000007EC3063D @ 0x000000007EC28000+0x863C (0x000000007FEE4CE0) in PartitionDxe.dll
2 0x000000007EC2B116 @ 0x000000007EC28000+0x3115 (0x000000007FEE4D20) in PartitionDxe.dll
3 0x000000007FEF8A15 @ 0x000000007EC28000+0x12D0A14 (0x000000007FEE4DB0) in PartitionDxe.dll
4 0x000000007FF10F0A @ 0x000000007EC28000+0x12E8F09 (0x000000007FEE4E30) in PartitionDxe.dll
5 0x000000007FF13445 @ 0x000000007EC28000+0x12EB444 (0x000000007FEE4F60) in PartitionDxe.dll
6 0x000000007FF13BC9 @ 0x000000007EC28000+0x12EBBC8 (0x000000007FEE4F90) in PartitionDxe.dll
7 0x000000007FEE9DDE @ 0x000000007EC28000+0x12C1DDD (0x000000007FEE4FC0) in PartitionDxe.dll
8 0x000000007FF5B75F @ 0x000000007EC28000+0x133375E (0x000000007BFDC840) in PartitionDxe.dll
9 0x000000007FF61551 @ 0x000000007EC28000+0x1339550 (0x000000007BFDC8C0) in PartitionDxe.dll
10 0x000000007FF6031D @ 0x000000007EC28000+0x133831C (0x000000007BFDCA30) in PartitionDxe.dll
11 0x000000007FF6CDCB @ 0x000000007EC28000+0x1344DCA (0x000000007BFDCF80) in PartitionDxe.dll
12 0x00000000008286F4 @ 0x0000000000820140+0x85B3 (0x000000007BFDD4D0) in PeiCore.dll
13 0x0000000000830940 @ 0x0000000000820140+0x107FF (0x0000000000817600) in PeiCore.dll
14 0x0000000000831585 @ 0x0000000000820140+0x11444 (0x00000000008176D0) in PeiCore.dll
15 0x0000000000828DAD @ 0x0000000000820140+0x8C6C (0x0000000000817C20) in PeiCore.dll
16 0x0000000000832392 @ 0x0000000000820140+0x12251 (0x0000000000817C50) in PeiCore.dll
17 0x0000000000824313 @ 0x0000000000820140+0x41D2 (0x0000000000817C80) in PeiCore.dll
18 0x00000000FFFD42F1 @ 0x0000000000820140+0xFF7B41B0 (0x0000000000817CE0) in PeiCore.dll
19 0x00000000FFFCF578 @ 0x0000000000820140+0xFF7AF437 (0x0000000000817D10) in PeiCore.dll
20 0x00000000FFFD428C @ 0x0000000000820140+0xFF7B414B (0x0000000000817FD0) in PeiCore.dll
21 0x00000000FFFD44E9 @ 0x0000000000820140+0xFF7B43A8 (0x00000000FFFCC000) in PeiCore.dll

PartitionDxe.dll (ImageBase=0x000000007EC28000, EntryPoint=0x000000007EC2B01F):
/home/pcacjr/src/edk2/Build/OvmfX64/NOOPT_GCC5/X64/MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe/DEBUG/PartitionDxe.dll
PeiCore.dll (ImageBase=0x0000000000820140, EntryPoint=0x00000000008242ED):
/home/pcacjr/src/edk2/Build/OvmfX64/NOOPT_GCC5/X64/MdeModulePkg/Core/Pei/PeiMain/DEBUG/PeiCore.dll

Stack dump:
0x000000007FEE4C70: 000000007EC32B20 0000000000000000
0x000000007FEE4C80: 000000007EC32DC8 0000000000000100
0x000000007FEE4C90: 000000007FEE4CB0 0000000000000000
0x000000007FEE4CA0: 000000007FEE4CE0 000000007EC305B7
0x000000007FEE4CB0: 000000007FEE4CE0 000000007EC3063D
0x000000007FEE4CC0: 000000007F34F498 000000007FE71018
0x000000007FEE4CD0: 000000077FEF88F5 0000000000000000
0x000000007FEE4CE0: 000000007FEE4D20 000000007EC2B116
0x000000007FEE4CF0: 000000007F34F498 000000007FE71018
0x000000007FEE4D00: 0000000000000000 0000000000000000
0x000000007FEE4D10: 0000000000000000 0000000000000000
0x000000007FEE4D20: 000000007FEE4DB0 000000007FEF8A15
0x000000007FEE4D30: 000000007F34F498 000000007FE71018
0x000000007FEE4D40: 0000000000000000 0000000000000004
0x000000007FEE4D50: 000000007FF1C1A8 000000007FF1CF90
0x000000007FEE4D60: 000000007FEE4DB0 0000000000000000
0x000000007FEE4D70: 000000007FF1C180 00000000000000B0
0x000000007FEE4D80: 0000000000000000 000000007F34E018
0x000000007FEE4D90: 000000007F34F498 0000000000000000
0x000000007FEE4DA0: 000000007FF1C1A8 000000007FF1CF90
0x000000007FEE4DB0: 000000007FEE4E30 000000007FF10F0A
0x000000007FEE4DC0: 000000007F34F498 0000000000000000

[-- Attachment #3: ia32_dump.txt --]
[-- Type: text/plain, Size: 1602 bytes --]

!!!! IA32 Exception Type - 02(NMI Interrupt)  CPU Apic ID - 00000000 !!!!
EIP  - 7DBD41BB, CS  - 00000010, EFLAGS - 00000206
EAX  - 00000000, ECX - 7EEC8CFF, EDX - 7ED9C220, EBX - 00000000
ESP  - 7EEC8DE4, EBP - 7EEC8DFC, ESI - 00000004, EDI - 00000000
DS   - 00000008, ES  - 00000008, FS  - 00000008, GS  - 00000008, SS - 00000008
CR0  - 00000033, CR2 - 00000000, CR3 - 00000000, CR4 - 00000640
DR0  - 00000000, DR1 - 00000000, DR2 - 00000000, DR3 - 00000000
DR6  - FFFF0FF0, DR7 - 00000400
GDTR - 7EE97A90 00000047, IDTR - 7E65B010 000007FF
LDTR - 00000000, TR - 00000000
FXSAVE_STATE - 7EEC8B20

Call trace:
0 0x7DBD41BB @ 0x7DBCD000+0x71BA (0x7EEC8DFC) in PartitionDxe.dll
1 0x7DBD4569 @ 0x7DBCD000+0x7568 (0x7EEC8E1C) in PartitionDxe.dll
2 0x7DBCF7F4 @ 0x7DBCD000+0x27F3 (0x7EEC8E4C) in PartitionDxe.dll
3 0x7EED9EA0 @ 0x7DBCD000+0x130CE9F (0x7EEC8E9C) in PartitionDxe.dll
4 0x7EEF1A88 @ 0x7DBCD000+0x1324A87 (0x7EEC8EDC) in PartitionDxe.dll
5 0x7EEF3DCC @ 0x7DBCD000+0x1326DCB (0x7EEC8FAC) in PartitionDxe.dll
6 0x7EEF44A1 @ 0x7DBCD000+0x13274A0 (0x7EEC8FCC) in PartitionDxe.dll
7 0x7EECD272 @ 0x7DBCD000+0x1300271 (0x7EEC8FEC) in PartitionDxe.dll

PartitionDxe.dll (ImageBase=0x7DBCD000, EntryPoint=0x7DBCF71B):
/home/pcacjr/src/edk2/Build/OvmfIa32/NOOPT_GCC5/IA32/MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe/DEBUG/PartitionDxe.dll

Stack dump:
0x7EEC8DE4: 7EAA1690 7EEFC520
0x7EEC8DE8: 7EEFC520 7EEC8E1C
0x7EEC8DEC: 7EEC8E1C 7DBD44E7
0x7EEC8DF0: 7DBD44E7 7E10E010
0x7EEC8DF4: 7E10E010 7EE97010
0x7EEC8DF8: 7EE97010 7EEC8E1C
0x7EEC8DFC: 7EEC8E1C 7DBD4569
0x7EEC8E00: 7DBD4569 7E10E010

^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC v2 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
  2017-11-16  1:18   ` [RFC v2 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support Paulo Alcantara
@ 2017-11-16  1:57     ` Yao, Jiewen
  2017-11-16 22:13       ` Paulo Alcantara
  2017-11-16 15:43     ` Brian J. Johnson
  1 sibling, 1 reply; 82+ messages in thread
From: Yao, Jiewen @ 2017-11-16  1:57 UTC (permalink / raw)
  To: Paulo Alcantara, edk2-devel@lists.01.org; +Cc: Laszlo Ersek, Dong, Eric

Hi Paulo
Thanks to bring this cool feature.

I have same feeling as Jeff Fan. It is great!

I have some questions, hope you can clarify.
1) Would you please let us know which tool change is validated? Such as MSVC, or GCC?

2) Would you please let us know which phase is validated? Such as PEI PreMemory, PEI PostMemory, DXE, SMM?

3) Would you please let us know if there is any special condition validated? Such as code is in an interrupt handler, SMM initialization code, thunk code during mode switch, etc.
I ask this because I have seen lots of discussion on sanity check, to avoid double exception.

4) We supported some security feature to create a hole in normal memory map, such as PageGuard, StackGuard, SMM communication protection, etc.
Accessing those memory directly will cause #PF immediately.
Would you please let us know how we detect that case, to avoid RIP or RSP fail to the non-present page?

5) May I know why we check RIP < ImageBase? Is that legal or illegal if RIP > ImageBase+ImageSize, but RIP in another PE/COFF image?
> +    //
> +    // Check if RIP is within another PE/COFF image base address
> +    //
> +    if (Rip < ImageBase) {
> +      //
> +      // Search for the respective PE/COFF image based on RIP
> +      //

Thank you
Yao Jiewen


> -----Original Message-----
> From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of Paulo
> Alcantara
> Sent: Thursday, November 16, 2017 9:18 AM
> To: edk2-devel@lists.01.org
> Cc: Laszlo Ersek <lersek@redhat.com>; Dong, Eric <eric.dong@intel.com>
> Subject: [edk2] [RFC v2 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack
> trace support
> 
> This patch adds stack trace support during a X64 CPU exception.
> 
> It will dump out back trace, stack contents as well as image module
> names that were part of the call stack.
> 
> Contributed-under: TianoCore Contribution Agreement 1.1
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> ---
>  UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 369
> +++++++++++++++++++-
>  1 file changed, 367 insertions(+), 2 deletions(-)
> 
> diff --git
> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> index 65f0cff680..11cd7c9e1c 100644
> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> @@ -14,6 +14,11 @@
> 
>  #include "CpuExceptionCommon.h"
> 
> +//
> +// Unknown PDB file name
> +//
> +GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8
> *mUnknownPdbFileName = "????";
> +
>  /**
>    Return address map of exception handler template so that C code can
> generate
>    exception tables.
> @@ -242,6 +247,350 @@ DumpCpuContext (
>      );
>  }
> 
> +/**
> +  Get absolute path and file name of PDB file in PE/COFF image.
> +
> +  @param[in]  ImageBase            Base address of PE/COFF image.
> +  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
> +  @param[out] PdbFileName          File name of PDB file.
> +**/
> +STATIC
> +VOID
> +GetPdbFileName (
> +  IN  UINTN    ImageBase,
> +  OUT CHAR8    **PdbAbsoluteFilePath,
> +  OUT CHAR8    **PdbFileName
> +  )
> +{
> +  VOID   *PdbPointer;
> +  CHAR8  *Str;
> +
> +  //
> +  // Get PDB file name from PE/COFF image
> +  //
> +  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
> +  if (PdbPointer == NULL) {
> +    //
> +    // No PDB file name found. Set it to an unknown file name.
> +    //
> +    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
> +    if (PdbAbsoluteFilePath != NULL) {
> +      *PdbAbsoluteFilePath = NULL;
> +    }
> +  } else {
> +    //
> +    // Get file name portion out of PDB file in PE/COFF image
> +    //
> +    Str = (CHAR8 *)((UINTN)PdbPointer +
> +                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
> +    for (; *Str != '/' && *Str != '\\'; Str--) {
> +      ;
> +    }
> +
> +    //
> +    // Set PDB file name (also skip trailing path separator: '/' or '\\')
> +    //
> +    *PdbFileName = Str + 1;
> +
> +    if (PdbAbsoluteFilePath != NULL) {
> +      //
> +      // Set absolute file path of PDB file
> +      //
> +      *PdbAbsoluteFilePath = PdbPointer;
> +    }
> +  }
> +}
> +
> +/**
> +  Dump stack contents.
> +
> +  @param[in]  CurrentRsp         Current stack pointer address.
> +  @param[in]  UnwondStacksCount  Count of unwond stack frames.
> +**/
> +STATIC
> +VOID
> +DumpStackContents (
> +  IN UINT64  CurrentRsp,
> +  IN INTN    UnwondStacksCount
> +  )
> +{
> +  //
> +  // Check for proper stack pointer alignment
> +  //
> +  if (((UINTN)CurrentRsp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
> +    InternalPrintMessage ("!!!! Unaligned stack pointer. !!!!\n");
> +    return;
> +  }
> +
> +  //
> +  // Dump out stack contents
> +  //
> +  InternalPrintMessage ("\nStack dump:\n");
> +  while (UnwondStacksCount-- > 0) {
> +    InternalPrintMessage (
> +      "0x%016lx: %016lx %016lx\n",
> +      CurrentRsp,
> +      *(UINT64 *)CurrentRsp,
> +      *(UINT64 *)((UINTN)CurrentRsp + 8)
> +      );
> +
> +    //
> +    // Point to next stack
> +    //
> +    CurrentRsp += CPU_STACK_ALIGNMENT;
> +  }
> +}
> +
> +/**
> +  Dump all image module names from call stack.
> +
> +  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
> +**/
> +STATIC
> +VOID
> +DumpImageModuleNames (
> +  IN EFI_SYSTEM_CONTEXT   SystemContext
> +  )
> +{
> +  EFI_STATUS  Status;
> +  UINT64      Rip;
> +  UINTN       ImageBase;
> +  VOID        *EntryPoint;
> +  CHAR8       *PdbAbsoluteFilePath;
> +  CHAR8       *PdbFileName;
> +  UINT64      Rbp;
> +
> +  //
> +  // Set current RIP address
> +  //
> +  Rip = SystemContext.SystemContextX64->Rip;
> +
> +  //
> +  // Set current frame pointer address
> +  //
> +  Rbp = SystemContext.SystemContextX64->Rbp;
> +
> +  //
> +  // Check for proper frame pointer alignment
> +  //
> +  if (((UINTN)Rbp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
> +    InternalPrintMessage ("!!!! Unaligned frame pointer. !!!!\n");
> +    return;
> +  }
> +
> +  //
> +  // Get initial PE/COFF image base address from current RIP
> +  //
> +  ImageBase = PeCoffSearchImageBase (Rip);
> +  if (ImageBase == 0) {
> +    InternalPrintMessage ("!!!! Could not find image module names. !!!!");
> +    return;
> +  }
> +
> +  //
> +  // Get initial PE/COFF image's entry point
> +  //
> +  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
> +  if (EFI_ERROR (Status)) {
> +    EntryPoint = NULL;
> +  }
> +
> +  //
> +  // Get file name and absolute path of initial PDB file
> +  //
> +  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
> +
> +  //
> +  // Print out initial image module name (if any)
> +  //
> +  if (PdbAbsoluteFilePath != NULL) {
> +    InternalPrintMessage (
> +      "\n%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
> +      PdbFileName,
> +      ImageBase,
> +      (UINTN)EntryPoint
> +      );
> +    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
> +  }
> +
> +  //
> +  // Walk through call stack and find next module names
> +  //
> +  for (;;) {
> +    //
> +    // Set RIP with return address from current stack frame
> +    //
> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
> +
> +    //
> +    // If RIP is zero, then stop unwinding the stack
> +    //
> +    if (Rip == 0) {
> +      break;
> +    }
> +
> +    //
> +    // Check if RIP is within another PE/COFF image base address
> +    //
> +    if (Rip < ImageBase) {
> +      //
> +      // Search for the respective PE/COFF image based on RIP
> +      //
> +      ImageBase = PeCoffSearchImageBase (Rip);
> +      if (ImageBase == 0) {
> +        //
> +        // Stop stack trace
> +        //
> +        break;
> +      }
> +
> +      //
> +      // Get PE/COFF image's entry point
> +      //
> +      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
> +      if (EFI_ERROR (Status)) {
> +        EntryPoint = NULL;
> +      }
> +
> +      //
> +      // Get file name and absolute path of PDB file
> +      //
> +      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
> +
> +      //
> +      // Print out image module name (if any)
> +      //
> +      if (PdbAbsoluteFilePath != NULL) {
> +        InternalPrintMessage (
> +          "%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
> +          PdbFileName,
> +          ImageBase,
> +          (UINTN)EntryPoint
> +          );
> +        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
> +      }
> +    }
> +
> +    //
> +    // Unwind the stack
> +    //
> +    Rbp = *(UINT64 *)(UINTN)Rbp;
> +  }
> +}
> +
> +/**
> +  Dump stack trace.
> +
> +  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
> +  @param[out] UnwondStacksCount  Count of unwond stack frames.
> +**/
> +STATIC
> +VOID
> +DumpStackTrace (
> +  IN  EFI_SYSTEM_CONTEXT   SystemContext,
> +  OUT INTN                 *UnwondStacksCount
> +  )
> +{
> +  UINT64  Rip;
> +  UINT64  Rbp;
> +  UINTN   ImageBase;
> +  CHAR8   *PdbFileName;
> +
> +  //
> +  // Set current RIP address
> +  //
> +  Rip = SystemContext.SystemContextX64->Rip;
> +
> +  //
> +  // Set current frame pointer address
> +  //
> +  Rbp = SystemContext.SystemContextX64->Rbp;
> +
> +  //
> +  // Get initial PE/COFF image base address from current RIP
> +  //
> +  ImageBase = PeCoffSearchImageBase (Rip);
> +  if (ImageBase == 0) {
> +    InternalPrintMessage ("!!!! Could not find backtrace information. !!!!");
> +    return;
> +  }
> +
> +  //
> +  // Get PDB file name from initial PE/COFF image
> +  //
> +  GetPdbFileName (ImageBase, NULL, &PdbFileName);
> +
> +  //
> +  // Initialize count of unwond stacks
> +  //
> +  *UnwondStacksCount = 1;
> +
> +  //
> +  // Print out back trace
> +  //
> +  InternalPrintMessage ("\nCall trace:\n");
> +
> +  for (;;) {
> +    //
> +    // Print stack frame in the following format:
> +    //
> +    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName> | ????]
> +    //
> +    InternalPrintMessage (
> +      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
> +      *UnwondStacksCount - 1,
> +      Rip,
> +      ImageBase,
> +      Rip - ImageBase - 1,
> +      Rbp,
> +      PdbFileName
> +      );
> +
> +    //
> +    // Set RIP with return address from current stack frame
> +    //
> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
> +
> +    //
> +    // If RIP is zero, then stop unwinding the stack
> +    //
> +    if (Rip == 0) {
> +      break;
> +    }
> +
> +    //
> +    // Check if RIP is within another PE/COFF image base address
> +    //
> +    if (Rip < ImageBase) {
> +      //
> +      // Search for the respective PE/COFF image based on RIP
> +      //
> +      ImageBase = PeCoffSearchImageBase (Rip);
> +      if (ImageBase == 0) {
> +        //
> +        // Stop stack trace
> +        //
> +        break;
> +      }
> +
> +      //
> +      // Get PDB file name
> +      //
> +      GetPdbFileName (ImageBase, NULL, &PdbFileName);
> +    }
> +
> +    //
> +    // Unwind the stack
> +    //
> +    Rbp = *(UINT64 *)(UINTN)Rbp;
> +
> +    //
> +    // Increment count of unwond stacks
> +    //
> +    (*UnwondStacksCount)++;
> +  }
> +}
> +
>  /**
>    Display CPU information.
> 
> @@ -254,9 +603,25 @@ DumpImageAndCpuContent (
>    IN EFI_SYSTEM_CONTEXT   SystemContext
>    )
>  {
> +  INTN UnwondStacksCount;
> +
> +  //
> +  // Dump CPU context
> +  //
>    DumpCpuContext (ExceptionType, SystemContext);
> +
> +  //
> +  // Dump stack trace
> +  //
> +  DumpStackTrace (SystemContext, &UnwondStacksCount);
> +
> +  //
> +  // Dump image module names
> +  //
> +  DumpImageModuleNames (SystemContext);
> +
>    //
> -  // Dump module image base and module entry point by RIP
> +  // Dump stack contents
>    //
> -  DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
> +  DumpStackContents (SystemContext.SystemContextX64->Rsp,
> UnwondStacksCount);
>  }
> --
> 2.14.3
> 
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org
> https://lists.01.org/mailman/listinfo/edk2-devel


^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC v2 0/3] Stack trace support in X64 exception handling
  2017-11-16  1:46   ` [RFC v2 0/3] Stack trace support in X64 exception handling Paulo Alcantara
@ 2017-11-16  5:01     ` Andrew Fish
  2017-11-16 22:02       ` Paulo Alcantara
  0 siblings, 1 reply; 82+ messages in thread
From: Andrew Fish @ 2017-11-16  5:01 UTC (permalink / raw)
  To: Paulo Alcantara
  Cc: edk2-devel, Rick Bramley, Eric Dong, Laszlo Ersek, Jeff Fan

Paulo,

Those attached stack traces don't look right. 

Thanks,

Andrew Fish

> On Nov 15, 2017, at 5:46 PM, Paulo Alcantara <pcacjr@zytor.com> wrote:
> 
> Hi,
> 
> On 11/15/2017 11:18 PM, Paulo Alcantara wrote:
>> Hi,
>> This series adds stack trace support during a X64 CPU exception.
>> Informations like back trace, stack contents and image module names
>> (that were part of the call stack) will be dumped out.
>> We already have such support in ARM/AArch64 (IIRC) exception handling
>> (thanks to Ard), and then I thought we'd also deserve it in X64 and
>> IA-32 platforms.
>> What do you think guys?
>> BTW, I've tested this only with OVMF (X64 only), using:
>>   - gcc-6.3.0, GCC5, NOOPT
>> Any other tests  would be really appreciable.
>> Thanks!
>> Paulo
>> Repo:   https://github.com/pcacjr/edk2.git
>> Branch: stacktrace_v2
>> Cc: Rick Bramley <richard.bramley@hp.com>
>> Cc: Andrew Fish <afish@apple.com>
>> Cc: Eric Dong <eric.dong@intel.com>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Cc: "Brian J. Johnson" <brian.johnson@hpe.com>
>> Cc: Jeff Fan <jeff.fan@intel.com>
>> Contributed-under: TianoCore Contribution Agreement 1.1
>> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
>> ---
>> v1 -> v2:
>>   * Add IA32 arch support (GCC toolchain only)
>>   * Replace hard-coded stack alignment value (16) with
>>     CPU_STACK_ALIGNMENT.
>>   * Check for proper stack and frame pointer alignments.
>>   * Fix initialization of UnwoundStacksCount to 1.
>>   * Move GetPdbFileName() to common code since it will be used by both
>>     IA32 and X64 implementations.
> 
> Sorry for the delay in sending v2. It's holiday here :-)
> 
> FWIW, I've attached two files which contain stack trace dumps of IA32 and X64 exceptions.
> 
> The new IA32 arch support is still limited to GCC only (that is, relying on frame pointers), but I'll start investing in a new solution that would work on both MSVC and GCC toolchains -- probably this weekend. If I come up with something, I'll let you know.
> 
> On IA32, I performed the same test as in X64 to trigger an NMI interrupt manually with: asm ("int $0x2") in PartitionDxe driver and watched out the call stack. The difference between the two dumps, regardless the CPU context, etc. is that we don't see the calls from PeiCore.dll. Then I figured out that the EIP gets a value of 0 before jumping to PartitionDxe's entry point.
> 
> I guess that's related to the "push $0" that Andrew mentioned earlier so the debugger knows when to stop unwinding. Although I can't see a "push 0" equivalent neither in SwitchStack.nasm nor in SwitchStack.asm for X64
> -- so we're able to see the calls within PeiCore.dll.
> 
> Thanks!
> Paulo
> <x64_dump.txt><ia32_dump.txt>_______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org <mailto:edk2-devel@lists.01.org>
> https://lists.01.org/mailman/listinfo/edk2-devel <https://lists.01.org/mailman/listinfo/edk2-devel>


^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC v2 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
  2017-11-16  1:18   ` [RFC v2 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support Paulo Alcantara
  2017-11-16  1:57     ` Yao, Jiewen
@ 2017-11-16 15:43     ` Brian J. Johnson
  2017-11-16 22:19       ` Paulo Alcantara
  1 sibling, 1 reply; 82+ messages in thread
From: Brian J. Johnson @ 2017-11-16 15:43 UTC (permalink / raw)
  To: edk2-devel

On 11/15/2017 07:18 PM, Paulo Alcantara wrote:
> This patch adds stack trace support during a X64 CPU exception.
> 
> It will dump out back trace, stack contents as well as image module
> names that were part of the call stack.
> 
> Contributed-under: TianoCore Contribution Agreement 1.1
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>

(These comments apply to patch 3/3 as well.)

Typo:  UnwondStacksCount should be UnwoundStacksCount

It's good to check the alignment of the stack, as you're doing.  But 
I'll reiterate that you absolutely need some better sanity checking of 
the stack and IP addresses before you dereference them.  Remember that 
they could be absolutely *anything* at the entry to this code. 
Something caused an error, and it may have been one of those registers.

Also, if the code was built with a compiler which isn't using RBP as a 
base pointer, RBP is unlikely to contain a stack address.  That will 
cause issues if you use it for unwinding without a sanity check.

Thanks,
Brian

> ---
>   UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 369 +++++++++++++++++++-
>   1 file changed, 367 insertions(+), 2 deletions(-)
> 
> diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> index 65f0cff680..11cd7c9e1c 100644
> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> @@ -14,6 +14,11 @@
>   
>   #include "CpuExceptionCommon.h"
>   
> +//
> +// Unknown PDB file name
> +//
> +GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
> +
>   /**
>     Return address map of exception handler template so that C code can generate
>     exception tables.
> @@ -242,6 +247,350 @@ DumpCpuContext (
>       );
>   }
>   
> +/**
> +  Get absolute path and file name of PDB file in PE/COFF image.
> +
> +  @param[in]  ImageBase            Base address of PE/COFF image.
> +  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
> +  @param[out] PdbFileName          File name of PDB file.
> +**/
> +STATIC
> +VOID
> +GetPdbFileName (
> +  IN  UINTN    ImageBase,
> +  OUT CHAR8    **PdbAbsoluteFilePath,
> +  OUT CHAR8    **PdbFileName
> +  )
> +{
> +  VOID   *PdbPointer;
> +  CHAR8  *Str;
> +
> +  //
> +  // Get PDB file name from PE/COFF image
> +  //
> +  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
> +  if (PdbPointer == NULL) {
> +    //
> +    // No PDB file name found. Set it to an unknown file name.
> +    //
> +    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
> +    if (PdbAbsoluteFilePath != NULL) {
> +      *PdbAbsoluteFilePath = NULL;
> +    }
> +  } else {
> +    //
> +    // Get file name portion out of PDB file in PE/COFF image
> +    //
> +    Str = (CHAR8 *)((UINTN)PdbPointer +
> +                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
> +    for (; *Str != '/' && *Str != '\\'; Str--) {
> +      ;
> +    }
> +
> +    //
> +    // Set PDB file name (also skip trailing path separator: '/' or '\\')
> +    //
> +    *PdbFileName = Str + 1;
> +
> +    if (PdbAbsoluteFilePath != NULL) {
> +      //
> +      // Set absolute file path of PDB file
> +      //
> +      *PdbAbsoluteFilePath = PdbPointer;
> +    }
> +  }
> +}
> +
> +/**
> +  Dump stack contents.
> +
> +  @param[in]  CurrentRsp         Current stack pointer address.
> +  @param[in]  UnwondStacksCount  Count of unwond stack frames.
> +**/
> +STATIC
> +VOID
> +DumpStackContents (
> +  IN UINT64  CurrentRsp,
> +  IN INTN    UnwondStacksCount
> +  )
> +{
> +  //
> +  // Check for proper stack pointer alignment
> +  //
> +  if (((UINTN)CurrentRsp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
> +    InternalPrintMessage ("!!!! Unaligned stack pointer. !!!!\n");
> +    return;
> +  }
> +
> +  //
> +  // Dump out stack contents
> +  //
> +  InternalPrintMessage ("\nStack dump:\n");
> +  while (UnwondStacksCount-- > 0) {
> +    InternalPrintMessage (
> +      "0x%016lx: %016lx %016lx\n",
> +      CurrentRsp,
> +      *(UINT64 *)CurrentRsp,
> +      *(UINT64 *)((UINTN)CurrentRsp + 8)
> +      );
> +
> +    //
> +    // Point to next stack
> +    //
> +    CurrentRsp += CPU_STACK_ALIGNMENT;
> +  }
> +}
> +
> +/**
> +  Dump all image module names from call stack.
> +
> +  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
> +**/
> +STATIC
> +VOID
> +DumpImageModuleNames (
> +  IN EFI_SYSTEM_CONTEXT   SystemContext
> +  )
> +{
> +  EFI_STATUS  Status;
> +  UINT64      Rip;
> +  UINTN       ImageBase;
> +  VOID        *EntryPoint;
> +  CHAR8       *PdbAbsoluteFilePath;
> +  CHAR8       *PdbFileName;
> +  UINT64      Rbp;
> +
> +  //
> +  // Set current RIP address
> +  //
> +  Rip = SystemContext.SystemContextX64->Rip;
> +
> +  //
> +  // Set current frame pointer address
> +  //
> +  Rbp = SystemContext.SystemContextX64->Rbp;
> +
> +  //
> +  // Check for proper frame pointer alignment
> +  //
> +  if (((UINTN)Rbp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
> +    InternalPrintMessage ("!!!! Unaligned frame pointer. !!!!\n");
> +    return;
> +  }
> +
> +  //
> +  // Get initial PE/COFF image base address from current RIP
> +  //
> +  ImageBase = PeCoffSearchImageBase (Rip);
> +  if (ImageBase == 0) {
> +    InternalPrintMessage ("!!!! Could not find image module names. !!!!");
> +    return;
> +  }
> +
> +  //
> +  // Get initial PE/COFF image's entry point
> +  //
> +  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
> +  if (EFI_ERROR (Status)) {
> +    EntryPoint = NULL;
> +  }
> +
> +  //
> +  // Get file name and absolute path of initial PDB file
> +  //
> +  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
> +
> +  //
> +  // Print out initial image module name (if any)
> +  //
> +  if (PdbAbsoluteFilePath != NULL) {
> +    InternalPrintMessage (
> +      "\n%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
> +      PdbFileName,
> +      ImageBase,
> +      (UINTN)EntryPoint
> +      );
> +    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
> +  }
> +
> +  //
> +  // Walk through call stack and find next module names
> +  //
> +  for (;;) {
> +    //
> +    // Set RIP with return address from current stack frame
> +    //
> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
> +
> +    //
> +    // If RIP is zero, then stop unwinding the stack
> +    //
> +    if (Rip == 0) {
> +      break;
> +    }
> +
> +    //
> +    // Check if RIP is within another PE/COFF image base address
> +    //
> +    if (Rip < ImageBase) {
> +      //
> +      // Search for the respective PE/COFF image based on RIP
> +      //
> +      ImageBase = PeCoffSearchImageBase (Rip);
> +      if (ImageBase == 0) {
> +        //
> +        // Stop stack trace
> +        //
> +        break;
> +      }
> +
> +      //
> +      // Get PE/COFF image's entry point
> +      //
> +      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
> +      if (EFI_ERROR (Status)) {
> +        EntryPoint = NULL;
> +      }
> +
> +      //
> +      // Get file name and absolute path of PDB file
> +      //
> +      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
> +
> +      //
> +      // Print out image module name (if any)
> +      //
> +      if (PdbAbsoluteFilePath != NULL) {
> +        InternalPrintMessage (
> +          "%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
> +          PdbFileName,
> +          ImageBase,
> +          (UINTN)EntryPoint
> +          );
> +        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
> +      }
> +    }
> +
> +    //
> +    // Unwind the stack
> +    //
> +    Rbp = *(UINT64 *)(UINTN)Rbp;
> +  }
> +}
> +
> +/**
> +  Dump stack trace.
> +
> +  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
> +  @param[out] UnwondStacksCount  Count of unwond stack frames.
> +**/
> +STATIC
> +VOID
> +DumpStackTrace (
> +  IN  EFI_SYSTEM_CONTEXT   SystemContext,
> +  OUT INTN                 *UnwondStacksCount
> +  )
> +{
> +  UINT64  Rip;
> +  UINT64  Rbp;
> +  UINTN   ImageBase;
> +  CHAR8   *PdbFileName;
> +
> +  //
> +  // Set current RIP address
> +  //
> +  Rip = SystemContext.SystemContextX64->Rip;
> +
> +  //
> +  // Set current frame pointer address
> +  //
> +  Rbp = SystemContext.SystemContextX64->Rbp;
> +
> +  //
> +  // Get initial PE/COFF image base address from current RIP
> +  //
> +  ImageBase = PeCoffSearchImageBase (Rip);
> +  if (ImageBase == 0) {
> +    InternalPrintMessage ("!!!! Could not find backtrace information. !!!!");
> +    return;
> +  }
> +
> +  //
> +  // Get PDB file name from initial PE/COFF image
> +  //
> +  GetPdbFileName (ImageBase, NULL, &PdbFileName);
> +
> +  //
> +  // Initialize count of unwond stacks
> +  //
> +  *UnwondStacksCount = 1;
> +
> +  //
> +  // Print out back trace
> +  //
> +  InternalPrintMessage ("\nCall trace:\n");
> +
> +  for (;;) {
> +    //
> +    // Print stack frame in the following format:
> +    //
> +    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName> | ????]
> +    //
> +    InternalPrintMessage (
> +      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
> +      *UnwondStacksCount - 1,
> +      Rip,
> +      ImageBase,
> +      Rip - ImageBase - 1,
> +      Rbp,
> +      PdbFileName
> +      );
> +
> +    //
> +    // Set RIP with return address from current stack frame
> +    //
> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
> +
> +    //
> +    // If RIP is zero, then stop unwinding the stack
> +    //
> +    if (Rip == 0) {
> +      break;
> +    }
> +
> +    //
> +    // Check if RIP is within another PE/COFF image base address
> +    //
> +    if (Rip < ImageBase) {
> +      //
> +      // Search for the respective PE/COFF image based on RIP
> +      //
> +      ImageBase = PeCoffSearchImageBase (Rip);
> +      if (ImageBase == 0) {
> +        //
> +        // Stop stack trace
> +        //
> +        break;
> +      }
> +
> +      //
> +      // Get PDB file name
> +      //
> +      GetPdbFileName (ImageBase, NULL, &PdbFileName);
> +    }
> +
> +    //
> +    // Unwind the stack
> +    //
> +    Rbp = *(UINT64 *)(UINTN)Rbp;
> +
> +    //
> +    // Increment count of unwond stacks
> +    //
> +    (*UnwondStacksCount)++;
> +  }
> +}
> +
>   /**
>     Display CPU information.
>   
> @@ -254,9 +603,25 @@ DumpImageAndCpuContent (
>     IN EFI_SYSTEM_CONTEXT   SystemContext
>     )
>   {
> +  INTN UnwondStacksCount;
> +
> +  //
> +  // Dump CPU context
> +  //
>     DumpCpuContext (ExceptionType, SystemContext);
> +
> +  //
> +  // Dump stack trace
> +  //
> +  DumpStackTrace (SystemContext, &UnwondStacksCount);
> +
> +  //
> +  // Dump image module names
> +  //
> +  DumpImageModuleNames (SystemContext);
> +
>     //
> -  // Dump module image base and module entry point by RIP
> +  // Dump stack contents
>     //
> -  DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
> +  DumpStackContents (SystemContext.SystemContextX64->Rsp, UnwondStacksCount);
>   }
> 


-- 
Brian J. Johnson
Enterprise X86 Lab

Hewlett Packard Enterprise

brian.johnson@hpe.com



^ permalink raw reply	[flat|nested] 82+ messages in thread

* [RFC v3 0/3] Stack trace support in X64 exception handling
  2017-11-16  1:18 ` [RFC v2 0/3] " Paulo Alcantara
                     ` (3 preceding siblings ...)
  2017-11-16  1:46   ` [RFC v2 0/3] Stack trace support in X64 exception handling Paulo Alcantara
@ 2017-11-16 21:56   ` Paulo Alcantara
  2017-11-16 21:56     ` [RFC v3 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support Paulo Alcantara
                       ` (2 more replies)
  4 siblings, 3 replies; 82+ messages in thread
From: Paulo Alcantara @ 2017-11-16 21:56 UTC (permalink / raw)
  To: edk2-devel
  Cc: Paulo Alcantara, Rick Bramley, Andrew Fish, Eric Dong,
	Laszlo Ersek, brian.johnson, jiewen.yao, Jeff Fan

Hi,

This series adds stack trace support during a X64 CPU exception.

Informations like back trace, stack contents and image module names
(that were part of the call stack) will be dumped out.

We already have such support in ARM/AArch64 (IIRC) exception handling
(thanks to Ard), and then I thought we'd also deserve it in X64 and
IA-32 platforms.

What do you think guys?

BTW, I've tested this only with OVMF (X64 only), using:
  - gcc-6.3.0, GCC5, NOOPT

Any other tests  would be really appreciable.

Thanks!
Paulo

Repo:   https://github.com/pcacjr/edk2.git
Branch: stacktrace_v2

Cc: Rick Bramley <richard.bramley@hp.com>
Cc: Andrew Fish <afish@apple.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: brian.johnson@hpe.com
Cc: jiewen.yao@intel.com
Cc: Jeff Fan <jeff.fan@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---

v1 -> v2:
  * Add IA32 arch support (GCC toolchain only)
  * Replace hard-coded stack alignment value (16) with
    CPU_STACK_ALIGNMENT.
  * Check for proper stack and frame pointer alignments.
  * Fix initialization of UnwoundStacksCount to 1.
  * Move GetPdbFileName() to common code since it will be used by both
    IA32 and X64 implementations.

v2 -> v3:
  * Fixed wrong assumption about "RIP < ImageBase" to start searching
    for another PE/COFF image. That is, RIP may point to lower and
    higher addresses for any other PE/COFF images. Both IA32 & X64.
    (Thanks Andrew & Jiewen)
  * Fixed typo: unwond -> unwound. Both IA32 & X64. (Thanks Brian)

Brian: I didn't have a chance to investigate on how to validate the RIP
and RSP values yet as you've suggested, sorry. But I will any time soon.

NOTE: This RFC for stack trace in IA32 & X64 supports *only* GCC at the
moment.

Paulo Alcantara (3):
  UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
  UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName()
  UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support

 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c        | 102 ++++---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h        |  25 +-
 UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c | 310 ++++++++++++++++++-
 UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c  | 317 +++++++++++++++++++-
 4 files changed, 696 insertions(+), 58 deletions(-)

-- 
2.14.3



^ permalink raw reply	[flat|nested] 82+ messages in thread

* [RFC v3 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
  2017-11-16 21:56   ` [RFC v3 " Paulo Alcantara
@ 2017-11-16 21:56     ` Paulo Alcantara
  2017-11-17  7:24       ` 答复: " Fan Jeff
  2017-11-16 21:56     ` [RFC v3 2/3] UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName() Paulo Alcantara
  2017-11-16 21:56     ` [RFC v3 3/3] UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support Paulo Alcantara
  2 siblings, 1 reply; 82+ messages in thread
From: Paulo Alcantara @ 2017-11-16 21:56 UTC (permalink / raw)
  To: edk2-devel; +Cc: Paulo Alcantara, Eric Dong, Laszlo Ersek

This patch adds stack trace support during a X64 CPU exception.

It will dump out back trace, stack contents as well as image module
names that were part of the call stack.

Contributed-under: TianoCore Contribution Agreement 1.1
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 376 +++++++++++++++++++-
 1 file changed, 374 insertions(+), 2 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
index 65f0cff680..fe776ccc2d 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
@@ -14,6 +14,11 @@
 
 #include "CpuExceptionCommon.h"
 
+//
+// Unknown PDB file name
+//
+GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
+
 /**
   Return address map of exception handler template so that C code can generate
   exception tables.
@@ -242,6 +247,357 @@ DumpCpuContext (
     );
 }
 
+/**
+  Get absolute path and file name of PDB file in PE/COFF image.
+
+  @param[in]  ImageBase            Base address of PE/COFF image.
+  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
+  @param[out] PdbFileName          File name of PDB file.
+**/
+STATIC
+VOID
+GetPdbFileName (
+  IN  UINTN    ImageBase,
+  OUT CHAR8    **PdbAbsoluteFilePath,
+  OUT CHAR8    **PdbFileName
+  )
+{
+  VOID   *PdbPointer;
+  CHAR8  *Str;
+
+  //
+  // Get PDB file name from PE/COFF image
+  //
+  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
+  if (PdbPointer == NULL) {
+    //
+    // No PDB file name found. Set it to an unknown file name.
+    //
+    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
+    if (PdbAbsoluteFilePath != NULL) {
+      *PdbAbsoluteFilePath = NULL;
+    }
+  } else {
+    //
+    // Get file name portion out of PDB file in PE/COFF image
+    //
+    Str = (CHAR8 *)((UINTN)PdbPointer +
+                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
+    for (; *Str != '/' && *Str != '\\'; Str--) {
+      ;
+    }
+
+    //
+    // Set PDB file name (also skip trailing path separator: '/' or '\\')
+    //
+    *PdbFileName = Str + 1;
+
+    if (PdbAbsoluteFilePath != NULL) {
+      //
+      // Set absolute file path of PDB file
+      //
+      *PdbAbsoluteFilePath = PdbPointer;
+    }
+  }
+}
+
+/**
+  Dump stack contents.
+
+  @param[in]  CurrentRsp         Current stack pointer address.
+  @param[in]  UnwoundStacksCount  Count of unwound stack frames.
+**/
+STATIC
+VOID
+DumpStackContents (
+  IN UINT64  CurrentRsp,
+  IN INTN    UnwoundStacksCount
+  )
+{
+  //
+  // Check for proper stack pointer alignment
+  //
+  if (((UINTN)CurrentRsp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
+    InternalPrintMessage ("!!!! Unaligned stack pointer. !!!!\n");
+    return;
+  }
+
+  //
+  // Dump out stack contents
+  //
+  InternalPrintMessage ("\nStack dump:\n");
+  while (UnwoundStacksCount-- > 0) {
+    InternalPrintMessage (
+      "0x%016lx: %016lx %016lx\n",
+      CurrentRsp,
+      *(UINT64 *)CurrentRsp,
+      *(UINT64 *)((UINTN)CurrentRsp + 8)
+      );
+
+    //
+    // Point to next stack
+    //
+    CurrentRsp += CPU_STACK_ALIGNMENT;
+  }
+}
+
+/**
+  Dump all image module names from call stack.
+
+  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+**/
+STATIC
+VOID
+DumpImageModuleNames (
+  IN EFI_SYSTEM_CONTEXT   SystemContext
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Rip;
+  UINTN       ImageBase;
+  VOID        *EntryPoint;
+  CHAR8       *PdbAbsoluteFilePath;
+  CHAR8       *PdbFileName;
+  UINT64      Rbp;
+  UINTN       LastImageBase;
+
+  //
+  // Set current RIP address
+  //
+  Rip = SystemContext.SystemContextX64->Rip;
+
+  //
+  // Set current frame pointer address
+  //
+  Rbp = SystemContext.SystemContextX64->Rbp;
+
+  //
+  // Check for proper frame pointer alignment
+  //
+  if (((UINTN)Rbp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
+    InternalPrintMessage ("!!!! Unaligned frame pointer. !!!!\n");
+    return;
+  }
+
+  //
+  // Get initial PE/COFF image base address from current RIP
+  //
+  ImageBase = PeCoffSearchImageBase (Rip);
+  if (ImageBase == 0) {
+    InternalPrintMessage ("!!!! Could not find image module names. !!!!");
+    return;
+  }
+
+  //
+  // Set last PE/COFF image base address
+  //
+  LastImageBase = ImageBase;
+
+  //
+  // Get initial PE/COFF image's entry point
+  //
+  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
+  if (EFI_ERROR (Status)) {
+    EntryPoint = NULL;
+  }
+
+  //
+  // Get file name and absolute path of initial PDB file
+  //
+  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
+
+  //
+  // Print out initial image module name (if any)
+  //
+  if (PdbAbsoluteFilePath != NULL) {
+    InternalPrintMessage (
+      "\n%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
+      PdbFileName,
+      ImageBase,
+      (UINTN)EntryPoint
+      );
+    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
+  }
+
+  //
+  // Walk through call stack and find next module names
+  //
+  for (;;) {
+    //
+    // Set RIP with return address from current stack frame
+    //
+    Rip = *(UINT64 *)((UINTN)Rbp + 8);
+
+    //
+    // If RIP is zero, then stop unwinding the stack
+    //
+    if (Rip == 0) {
+      break;
+    }
+
+    //
+    // Search for the respective PE/COFF image based on RIP
+    //
+    ImageBase = PeCoffSearchImageBase (Rip);
+    if (ImageBase == 0) {
+      //
+      // Stop stack trace
+      //
+      break;
+    }
+
+    //
+    // If RIP points to another PE/COFF image, then find its respective PDB file
+    // name.
+    //
+    if (LastImageBase != ImageBase) {
+      //
+      // Get PE/COFF image's entry point
+      //
+      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
+      if (EFI_ERROR (Status)) {
+        EntryPoint = NULL;
+      }
+
+      //
+      // Get file name and absolute path of PDB file
+      //
+      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
+
+      //
+      // Print out image module name (if any)
+      //
+      if (PdbAbsoluteFilePath != NULL) {
+        InternalPrintMessage (
+          "%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
+          PdbFileName,
+          ImageBase,
+          (UINTN)EntryPoint
+          );
+        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
+      }
+
+      //
+      // Save last PE/COFF image base address
+      //
+      LastImageBase = ImageBase;
+    }
+
+    //
+    // Unwind the stack
+    //
+    Rbp = *(UINT64 *)(UINTN)Rbp;
+  }
+}
+
+/**
+  Dump stack trace.
+
+  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
+  @param[out] UnwoundStacksCount  Count of unwound stack frames.
+**/
+STATIC
+VOID
+DumpStackTrace (
+  IN  EFI_SYSTEM_CONTEXT   SystemContext,
+  OUT INTN                 *UnwoundStacksCount
+  )
+{
+  UINT64  Rip;
+  UINT64  Rbp;
+  UINTN   ImageBase;
+  CHAR8   *PdbFileName;
+
+  //
+  // Set current RIP address
+  //
+  Rip = SystemContext.SystemContextX64->Rip;
+
+  //
+  // Set current frame pointer address
+  //
+  Rbp = SystemContext.SystemContextX64->Rbp;
+
+  //
+  // Get initial PE/COFF image base address from current RIP
+  //
+  ImageBase = PeCoffSearchImageBase (Rip);
+  if (ImageBase == 0) {
+    InternalPrintMessage ("!!!! Could not find backtrace information. !!!!");
+    return;
+  }
+
+  //
+  // Get PDB file name from initial PE/COFF image
+  //
+  GetPdbFileName (ImageBase, NULL, &PdbFileName);
+
+  //
+  // Initialize count of unwound stacks
+  //
+  *UnwoundStacksCount = 1;
+
+  //
+  // Print out back trace
+  //
+  InternalPrintMessage ("\nCall trace:\n");
+
+  for (;;) {
+    //
+    // Print stack frame in the following format:
+    //
+    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName> | ????]
+    //
+    InternalPrintMessage (
+      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
+      *UnwoundStacksCount - 1,
+      Rip,
+      ImageBase,
+      Rip - ImageBase - 1,
+      Rbp,
+      PdbFileName
+      );
+
+    //
+    // Set RIP with return address from current stack frame
+    //
+    Rip = *(UINT64 *)((UINTN)Rbp + 8);
+
+    //
+    // If RIP is zero, then stop unwinding the stack
+    //
+    if (Rip == 0) {
+      break;
+    }
+
+    //
+    // Search for the respective PE/COFF image based on RIP
+    //
+    ImageBase = PeCoffSearchImageBase (Rip);
+    if (ImageBase == 0) {
+      //
+      // Stop stack trace
+      //
+      break;
+    }
+
+    //
+    // Get PDB file name
+    //
+    GetPdbFileName (ImageBase, NULL, &PdbFileName);
+
+    //
+    // Unwind the stack
+    //
+    Rbp = *(UINT64 *)(UINTN)Rbp;
+
+    //
+    // Increment count of unwound stacks
+    //
+    (*UnwoundStacksCount)++;
+  }
+}
+
 /**
   Display CPU information.
 
@@ -254,9 +610,25 @@ DumpImageAndCpuContent (
   IN EFI_SYSTEM_CONTEXT   SystemContext
   )
 {
+  INTN UnwoundStacksCount;
+
+  //
+  // Dump CPU context
+  //
   DumpCpuContext (ExceptionType, SystemContext);
+
+  //
+  // Dump stack trace
+  //
+  DumpStackTrace (SystemContext, &UnwoundStacksCount);
+
+  //
+  // Dump image module names
+  //
+  DumpImageModuleNames (SystemContext);
+
   //
-  // Dump module image base and module entry point by RIP
+  // Dump stack contents
   //
-  DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
+  DumpStackContents (SystemContext.SystemContextX64->Rsp, UnwoundStacksCount);
 }
-- 
2.14.3



^ permalink raw reply related	[flat|nested] 82+ messages in thread

* [RFC v3 2/3] UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName()
  2017-11-16 21:56   ` [RFC v3 " Paulo Alcantara
  2017-11-16 21:56     ` [RFC v3 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support Paulo Alcantara
@ 2017-11-16 21:56     ` Paulo Alcantara
  2017-11-16 21:56     ` [RFC v3 3/3] UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support Paulo Alcantara
  2 siblings, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2017-11-16 21:56 UTC (permalink / raw)
  To: edk2-devel; +Cc: Paulo Alcantara, Eric Dong, Laszlo Ersek

This function will be used by both IA32 and X64 exception handling in
order to print out image module names during stack unwinding.

Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c       | 60 +++++++++++++++++++-
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h       | 14 +++++
 UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 59 -------------------
 3 files changed, 73 insertions(+), 60 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
index dbfaae1d30..f62ab8c48c 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
@@ -54,6 +54,11 @@ CONST CHAR8 *mExceptionNameStr[] = {
 
 #define EXCEPTION_KNOWN_NAME_NUM  (sizeof (mExceptionNameStr) / sizeof (CHAR8 *))
 
+//
+// Unknown PDB file name
+//
+GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
+
 /**
   Get ASCII format string exception name by exception type.
 
@@ -177,4 +182,57 @@ ReadAndVerifyVectorInfo (
     VectorInfo ++;
   }
   return EFI_SUCCESS;
-}
\ No newline at end of file
+}
+
+/**
+  Get absolute path and file name of PDB file in PE/COFF image.
+
+  @param[in]  ImageBase            Base address of PE/COFF image.
+  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
+  @param[out] PdbFileName          File name of PDB file.
+**/
+VOID
+GetPdbFileName (
+  IN  UINTN    ImageBase,
+  OUT CHAR8    **PdbAbsoluteFilePath,
+  OUT CHAR8    **PdbFileName
+  )
+{
+  VOID   *PdbPointer;
+  CHAR8  *Str;
+
+  //
+  // Get PDB file name from PE/COFF image
+  //
+  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
+  if (PdbPointer == NULL) {
+    //
+    // No PDB file name found. Set it to an unknown file name.
+    //
+    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
+    if (PdbAbsoluteFilePath != NULL) {
+      *PdbAbsoluteFilePath = NULL;
+    }
+  } else {
+    //
+    // Get file name portion out of PDB file in PE/COFF image
+    //
+    Str = (CHAR8 *)((UINTN)PdbPointer +
+                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
+    for (; *Str != '/' && *Str != '\\'; Str--) {
+      ;
+    }
+
+    //
+    // Set PDB file name (also skip trailing path separator: '/' or '\\')
+    //
+    *PdbFileName = Str + 1;
+
+    if (PdbAbsoluteFilePath != NULL) {
+      //
+      // Set absolute file path of PDB file
+      //
+      *PdbAbsoluteFilePath = PdbPointer;
+    }
+  }
+}
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
index 740a58828b..042207025e 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
@@ -288,5 +288,19 @@ CommonExceptionHandlerWorker (
   IN EXCEPTION_HANDLER_DATA      *ExceptionHandlerData
   );
 
+/**
+  Get absolute path and file name of PDB file in PE/COFF image.
+
+  @param[in]  ImageBase            Base address of PE/COFF image.
+  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
+  @param[out] PdbFileName          File name of PDB file.
+**/
+VOID
+GetPdbFileName (
+  IN  UINTN    ImageBase,
+  OUT CHAR8    **PdbAbsoluteFilePath,
+  OUT CHAR8    **PdbFileName
+  );
+
 #endif
 
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
index fe776ccc2d..7f8ec65e1d 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
@@ -14,11 +14,6 @@
 
 #include "CpuExceptionCommon.h"
 
-//
-// Unknown PDB file name
-//
-GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
-
 /**
   Return address map of exception handler template so that C code can generate
   exception tables.
@@ -247,60 +242,6 @@ DumpCpuContext (
     );
 }
 
-/**
-  Get absolute path and file name of PDB file in PE/COFF image.
-
-  @param[in]  ImageBase            Base address of PE/COFF image.
-  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
-  @param[out] PdbFileName          File name of PDB file.
-**/
-STATIC
-VOID
-GetPdbFileName (
-  IN  UINTN    ImageBase,
-  OUT CHAR8    **PdbAbsoluteFilePath,
-  OUT CHAR8    **PdbFileName
-  )
-{
-  VOID   *PdbPointer;
-  CHAR8  *Str;
-
-  //
-  // Get PDB file name from PE/COFF image
-  //
-  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
-  if (PdbPointer == NULL) {
-    //
-    // No PDB file name found. Set it to an unknown file name.
-    //
-    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
-    if (PdbAbsoluteFilePath != NULL) {
-      *PdbAbsoluteFilePath = NULL;
-    }
-  } else {
-    //
-    // Get file name portion out of PDB file in PE/COFF image
-    //
-    Str = (CHAR8 *)((UINTN)PdbPointer +
-                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
-    for (; *Str != '/' && *Str != '\\'; Str--) {
-      ;
-    }
-
-    //
-    // Set PDB file name (also skip trailing path separator: '/' or '\\')
-    //
-    *PdbFileName = Str + 1;
-
-    if (PdbAbsoluteFilePath != NULL) {
-      //
-      // Set absolute file path of PDB file
-      //
-      *PdbAbsoluteFilePath = PdbPointer;
-    }
-  }
-}
-
 /**
   Dump stack contents.
 
-- 
2.14.3



^ permalink raw reply related	[flat|nested] 82+ messages in thread

* [RFC v3 3/3] UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support
  2017-11-16 21:56   ` [RFC v3 " Paulo Alcantara
  2017-11-16 21:56     ` [RFC v3 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support Paulo Alcantara
  2017-11-16 21:56     ` [RFC v3 2/3] UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName() Paulo Alcantara
@ 2017-11-16 21:56     ` Paulo Alcantara
  2 siblings, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2017-11-16 21:56 UTC (permalink / raw)
  To: edk2-devel; +Cc: Paulo Alcantara, Eric Dong, Laszlo Ersek

This patch adds stack trace support during a IA32 CPU exception.

It will dump out back trace, stack contents as well as image module
names that were part of the call stack.

Contributed-under: TianoCore Contribution Agreement 1.1
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c        |  42 ---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h        |  11 -
 UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c | 310 +++++++++++++++++++-
 3 files changed, 308 insertions(+), 55 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
index f62ab8c48c..867c5c01d6 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
@@ -109,48 +109,6 @@ InternalPrintMessage (
   SerialPortWrite ((UINT8 *)Buffer, AsciiStrLen (Buffer));
 }
 
-/**
-  Find and display image base address and return image base and its entry point.
-
-  @param CurrentEip      Current instruction pointer.
-
-**/
-VOID
-DumpModuleImageInfo (
-  IN  UINTN              CurrentEip
-  )
-{
-  EFI_STATUS                           Status;
-  UINTN                                Pe32Data;
-  VOID                                 *PdbPointer;
-  VOID                                 *EntryPoint;
-
-  Pe32Data = PeCoffSearchImageBase (CurrentEip);
-  if (Pe32Data == 0) {
-    InternalPrintMessage ("!!!! Can't find image information. !!!!\n");
-  } else {
-    //
-    // Find Image Base entry point
-    //
-    Status = PeCoffLoaderGetEntryPoint ((VOID *) Pe32Data, &EntryPoint);
-    if (EFI_ERROR (Status)) {
-      EntryPoint = NULL;
-    }
-    InternalPrintMessage ("!!!! Find image ");
-    PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *) Pe32Data);
-    if (PdbPointer != NULL) {
-      InternalPrintMessage ("%a", PdbPointer);
-    } else {
-      InternalPrintMessage ("(No PDB) " );
-    }
-    InternalPrintMessage (
-      " (ImageBase=%016lp, EntryPoint=%016p) !!!!\n",
-      (VOID *) Pe32Data,
-      EntryPoint
-      );
-  }
-}
-
 /**
   Read and save reserved vector information
 
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
index 042207025e..478374d003 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
@@ -119,17 +119,6 @@ InternalPrintMessage (
   ...
   );
 
-/**
-  Find and display image base address and return image base and its entry point.
-
-  @param CurrentEip      Current instruction pointer.
-
-**/
-VOID
-DumpModuleImageInfo (
-  IN  UINTN              CurrentEip
-  );
-
 /**
   Display CPU information.
 
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
index f2c39eb193..db8013a5af 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
@@ -210,6 +210,296 @@ DumpCpuContext (
     );
 }
 
+/**
+  Dump stack trace.
+
+  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
+  @param[out] UnwoundStacksCount  Count of unwound stack frames.
+**/
+STATIC
+VOID
+DumpStackTrace (
+  IN  EFI_SYSTEM_CONTEXT   SystemContext,
+  OUT INTN                 *UnwoundStacksCount
+  )
+{
+  UINT32  Eip;
+  UINT32  Ebp;
+  UINTN   ImageBase;
+  CHAR8   *PdbFileName;
+
+  //
+  // Set current EIP address
+  //
+  Eip = SystemContext.SystemContextIa32->Eip;
+
+  //
+  // Set current frame pointer address
+  //
+  Ebp = SystemContext.SystemContextIa32->Ebp;
+
+  //
+  // Check for proper frame pointer alignment
+  //
+  if (((UINTN)Ebp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
+    InternalPrintMessage ("!!!! Unaligned frame pointer. !!!!\n");
+    return;
+  }
+
+  //
+  // Get initial PE/COFF image base address from current EIP
+  //
+  ImageBase = PeCoffSearchImageBase (Eip);
+  if (ImageBase == 0) {
+    InternalPrintMessage ("!!!! Could not find backtrace information. !!!!");
+    return;
+  }
+
+  //
+  // Get PDB file name from initial PE/COFF image
+  //
+  GetPdbFileName (ImageBase, NULL, &PdbFileName);
+
+  //
+  // Initialize count of unwound stacks
+  //
+  *UnwoundStacksCount = 1;
+
+  //
+  // Print out back trace
+  //
+  InternalPrintMessage ("\nCall trace:\n");
+
+  for (;;) {
+    //
+    // Print stack frame in the following format:
+    //
+    // # <EIP> @ <ImageBase>+<RelOffset> (EBP) in [<ModuleName> | ????]
+    //
+    InternalPrintMessage (
+      "%d 0x%08x @ 0x%08x+0x%x (0x%08x) in %a\n",
+      *UnwoundStacksCount - 1,
+      Eip,
+      ImageBase,
+      Eip - ImageBase - 1,
+      Ebp,
+      PdbFileName
+      );
+
+    //
+    // Set EIP with return address from current stack frame
+    //
+    Eip = *(UINT32 *)((UINTN)Ebp + 4);
+
+    //
+    // If EIP is zero, then stop unwinding the stack
+    //
+    if (Eip == 0) {
+      break;
+    }
+
+    //
+    // Search for the respective PE/COFF image based on EIP
+    //
+    ImageBase = PeCoffSearchImageBase (Eip);
+    if (ImageBase == 0) {
+      //
+      // Stop stack trace
+      //
+      break;
+    }
+
+    //
+    // Get PDB file name
+    //
+    GetPdbFileName (ImageBase, NULL, &PdbFileName);
+
+    //
+    // Unwind the stack
+    //
+    Ebp = *(UINT32 *)(UINTN)Ebp;
+
+    //
+    // Increment count of unwound stacks
+    //
+    (*UnwoundStacksCount)++;
+  }
+}
+
+/**
+  Dump all image module names from call stack.
+
+  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+**/
+STATIC
+VOID
+DumpImageModuleNames (
+  IN EFI_SYSTEM_CONTEXT   SystemContext
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      Eip;
+  UINT32      Ebp;
+  UINTN       ImageBase;
+  VOID        *EntryPoint;
+  CHAR8       *PdbAbsoluteFilePath;
+  CHAR8       *PdbFileName;
+  UINTN       LastImageBase;
+
+  //
+  // Set current EIP address
+  //
+  Eip = SystemContext.SystemContextIa32->Eip;
+
+  //
+  // Set current frame pointer address
+  //
+  Ebp = SystemContext.SystemContextIa32->Ebp;
+
+  //
+  // Get initial PE/COFF image base address from current EIP
+  //
+  ImageBase = PeCoffSearchImageBase (Eip);
+  if (ImageBase == 0) {
+    InternalPrintMessage ("!!!! Could not find image module names. !!!!");
+    return;
+  }
+
+  //
+  // Set last PE/COFF image base address
+  //
+  LastImageBase = ImageBase;
+
+  //
+  // Get initial PE/COFF image's entry point
+  //
+  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
+  if (EFI_ERROR (Status)) {
+    EntryPoint = NULL;
+  }
+
+  //
+  // Get file name and absolute path of initial PDB file
+  //
+  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
+
+  //
+  // Print out initial image module name (if any)
+  //
+  if (PdbAbsoluteFilePath != NULL) {
+    InternalPrintMessage (
+      "\n%a (ImageBase=0x%08x, EntryPoint=0x%08x):\n",
+      PdbFileName,
+      ImageBase,
+      (UINTN)EntryPoint
+      );
+    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
+  }
+
+  //
+  // Walk through call stack and find next module names
+  //
+  for (;;) {
+    //
+    // Set EIP with return address from current stack frame
+    //
+    Eip = *(UINT32 *)((UINTN)Ebp + 4);
+
+    //
+    // Search for the respective PE/COFF image based on Eip
+    //
+    ImageBase = PeCoffSearchImageBase (Eip);
+    if (ImageBase == 0) {
+      //
+      // Stop stack trace
+      //
+      break;
+    }
+
+    //
+    // If EIP points to another PE/COFF image, then find its respective PDB file
+    // name.
+    //
+    if (LastImageBase != ImageBase) {
+      //
+      // Get PE/COFF image's entry point
+      //
+      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
+      if (EFI_ERROR (Status)) {
+        EntryPoint = NULL;
+      }
+
+      //
+      // Get file name and absolute path of PDB file
+      //
+      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
+
+      //
+      // Print out image module name (if any)
+      //
+      if (PdbAbsoluteFilePath != NULL) {
+        InternalPrintMessage (
+          "%a (ImageBase=0x%08x, EntryPoint=0x%08x):\n",
+          PdbFileName,
+          ImageBase,
+          (UINTN)EntryPoint
+          );
+        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
+      }
+
+      //
+      // Save last PE/COFF image base address
+      //
+      LastImageBase = ImageBase;
+    }
+
+    //
+    // Unwind the stack
+    //
+    Ebp = *(UINT32 *)(UINTN)Ebp;
+  }
+}
+
+/**
+  Dump stack contents.
+
+  @param[in]  CurrentEsp         Current stack pointer address.
+  @param[in]  UnwoundStacksCount  Count of unwound stack frames.
+**/
+STATIC
+VOID
+DumpStackContents (
+  IN UINT32  CurrentEsp,
+  IN INTN    UnwoundStacksCount
+  )
+{
+  //
+  // Check for proper stack alignment
+  //
+  if (((UINTN)CurrentEsp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
+    InternalPrintMessage ("!!!! Unaligned stack pointer. !!!!\n");
+    return;
+  }
+
+  //
+  // Dump out stack contents
+  //
+  InternalPrintMessage ("\nStack dump:\n");
+  while (UnwoundStacksCount-- > 0) {
+    InternalPrintMessage (
+      "0x%08x: %08x %08x\n",
+      CurrentEsp,
+      *(UINT32 *)CurrentEsp,
+      *(UINT32 *)((UINTN)CurrentEsp + 4)
+      );
+
+    //
+    // Point to next stack
+    //
+    CurrentEsp += CPU_STACK_ALIGNMENT;
+  }
+}
+
 /**
   Display CPU information.
 
@@ -222,9 +512,25 @@ DumpImageAndCpuContent (
   IN EFI_SYSTEM_CONTEXT   SystemContext
   )
 {
+  INTN UnwoundStacksCount;
+
+  //
+  // Dump CPU context
+  //
   DumpCpuContext (ExceptionType, SystemContext);
+
+  //
+  // Dump stack trace
+  //
+  DumpStackTrace (SystemContext, &UnwoundStacksCount);
+
+  //
+  // Dump image module names
+  //
+  DumpImageModuleNames (SystemContext);
+
   //
-  // Dump module image base and module entry point by EIP
+  // Dump stack contents
   //
-  DumpModuleImageInfo (SystemContext.SystemContextIa32->Eip);
+  DumpStackContents (SystemContext.SystemContextIa32->Esp, UnwoundStacksCount);
 }
-- 
2.14.3



^ permalink raw reply related	[flat|nested] 82+ messages in thread

* Re: [RFC v2 0/3] Stack trace support in X64 exception handling
  2017-11-16  5:01     ` Andrew Fish
@ 2017-11-16 22:02       ` Paulo Alcantara
  0 siblings, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2017-11-16 22:02 UTC (permalink / raw)
  To: Andrew Fish
  Cc: Paulo Alcantara, edk2-devel, Rick Bramley, Eric Dong,
	Laszlo Ersek, Jeff Fan

[-- Attachment #1: Type: text/plain, Size: 3205 bytes --]

Hi Andrew,

On Thu, November 16, 2017 3:01 am, Andrew Fish wrote:
> Paulo,
>
> Those attached stack traces don't look right.

What about the new ones?

Thanks!
Paulo

>
> Thanks,
>
> Andrew Fish
>
>> On Nov 15, 2017, at 5:46 PM, Paulo Alcantara <pcacjr@zytor.com> wrote:
>>
>> Hi,
>>
>> On 11/15/2017 11:18 PM, Paulo Alcantara wrote:
>>> Hi,
>>> This series adds stack trace support during a X64 CPU exception.
>>> Informations like back trace, stack contents and image module names
>>> (that were part of the call stack) will be dumped out.
>>> We already have such support in ARM/AArch64 (IIRC) exception handling
>>> (thanks to Ard), and then I thought we'd also deserve it in X64 and
>>> IA-32 platforms.
>>> What do you think guys?
>>> BTW, I've tested this only with OVMF (X64 only), using:
>>>   - gcc-6.3.0, GCC5, NOOPT
>>> Any other tests  would be really appreciable.
>>> Thanks!
>>> Paulo
>>> Repo:   https://github.com/pcacjr/edk2.git
>>> Branch: stacktrace_v2
>>> Cc: Rick Bramley <richard.bramley@hp.com>
>>> Cc: Andrew Fish <afish@apple.com>
>>> Cc: Eric Dong <eric.dong@intel.com>
>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>> Cc: "Brian J. Johnson" <brian.johnson@hpe.com>
>>> Cc: Jeff Fan <jeff.fan@intel.com>
>>> Contributed-under: TianoCore Contribution Agreement 1.1
>>> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
>>> ---
>>> v1 -> v2:
>>>   * Add IA32 arch support (GCC toolchain only)
>>>   * Replace hard-coded stack alignment value (16) with
>>>     CPU_STACK_ALIGNMENT.
>>>   * Check for proper stack and frame pointer alignments.
>>>   * Fix initialization of UnwoundStacksCount to 1.
>>>   * Move GetPdbFileName() to common code since it will be used by both
>>>     IA32 and X64 implementations.
>>
>> Sorry for the delay in sending v2. It's holiday here :-)
>>
>> FWIW, I've attached two files which contain stack trace dumps of IA32
>> and X64 exceptions.
>>
>> The new IA32 arch support is still limited to GCC only (that is, relying
>> on frame pointers), but I'll start investing in a new solution that
>> would work on both MSVC and GCC toolchains -- probably this weekend. If
>> I come up with something, I'll let you know.
>>
>> On IA32, I performed the same test as in X64 to trigger an NMI interrupt
>> manually with: asm ("int $0x2") in PartitionDxe driver and watched out
>> the call stack. The difference between the two dumps, regardless the CPU
>> context, etc. is that we don't see the calls from PeiCore.dll. Then I
>> figured out that the EIP gets a value of 0 before jumping to
>> PartitionDxe's entry point.
>>
>> I guess that's related to the "push $0" that Andrew mentioned earlier so
>> the debugger knows when to stop unwinding. Although I can't see a "push
>> 0" equivalent neither in SwitchStack.nasm nor in SwitchStack.asm for X64
>> -- so we're able to see the calls within PeiCore.dll.
>>
>> Thanks!
>> Paulo
>> <x64_dump.txt><ia32_dump.txt>_______________________________________________
>> edk2-devel mailing list
>> edk2-devel@lists.01.org <mailto:edk2-devel@lists.01.org>
>> https://lists.01.org/mailman/listinfo/edk2-devel
>> <https://lists.01.org/mailman/listinfo/edk2-devel>
>


-- 
Paulo Alcantara, HP Inc.
Speaking for myself only.

[-- Attachment #2: ia32_dump.txt --]
[-- Type: text/plain, Size: 1824 bytes --]

!!!! IA32 Exception Type - 03(#BP - Breakpoint)  CPU Apic ID - 00000000 !!!!
EIP  - 7DBCD580, CS  - 00000010, EFLAGS - 00000206
EAX  - 00000000, ECX - 7EEC8CFF, EDX - 7ED9C220, EBX - 00000000
ESP  - 7EEC8DDC, EBP - 7EEC8DDC, ESI - 00000004, EDI - 00000000
DS   - 00000008, ES  - 00000008, FS  - 00000008, GS  - 00000008, SS - 00000008
CR0  - 00000033, CR2 - 00000000, CR3 - 00000000, CR4 - 00000640
DR0  - 00000000, DR1 - 00000000, DR2 - 00000000, DR3 - 00000000
DR6  - FFFF0FF0, DR7 - 00000400
GDTR - 7EE97A90 00000047, IDTR - 7E65B010 000007FF
LDTR - 00000000, TR - 00000000
FXSAVE_STATE - 7EEC8B20

Call trace:
0 0x7DBCD580 @ 0x7DBCD000+0x57F (0x7EEC8DDC) in PartitionDxe.dll
1 0x7DBD41BE @ 0x7DBCD000+0x71BD (0x7EEC8DFC) in PartitionDxe.dll
2 0x7DBD456C @ 0x7DBCD000+0x756B (0x7EEC8E1C) in PartitionDxe.dll
3 0x7DBCF7F4 @ 0x7DBCD000+0x27F3 (0x7EEC8E4C) in PartitionDxe.dll
4 0x7EED9EA4 @ 0x7EEC9000+0x10EA3 (0x7EEC8E9C) in DxeCore.dll
5 0x7EEF1A8C @ 0x7EEC9000+0x28A8B (0x7EEC8EDC) in DxeCore.dll
6 0x7EEF3DD0 @ 0x7EEC9000+0x2ADCF (0x7EEC8FAC) in DxeCore.dll
7 0x7EEF44A5 @ 0x7EEC9000+0x2B4A4 (0x7EEC8FCC) in DxeCore.dll
8 0x7EECD272 @ 0x7EEC9000+0x4271 (0x7EEC8FEC) in DxeCore.dll

PartitionDxe.dll (ImageBase=0x7DBCD000, EntryPoint=0x7DBCF71B):
/home/pcacjr/src/edk2/Build/OvmfIa32/NOOPT_GCC5/IA32/MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe/DEBUG/PartitionDxe.dll
DxeCore.dll (ImageBase=0x7EEC9000, EntryPoint=0x7EECD259):
/home/pcacjr/src/edk2/Build/OvmfIa32/NOOPT_GCC5/IA32/MdeModulePkg/Core/Dxe/DxeMain/DEBUG/DxeCore.dll

Stack dump:
0x7EEC8DDC: 7EEC8DFC 7DBD41BE
0x7EEC8DE0: 7DBD41BE 7EAA1690
0x7EEC8DE4: 7EAA1690 7EEFC520
0x7EEC8DE8: 7EEFC520 7EEC8E1C
0x7EEC8DEC: 7EEC8E1C 7DBD44EA
0x7EEC8DF0: 7DBD44EA 7E10E010
0x7EEC8DF4: 7E10E010 7EE97010
0x7EEC8DF8: 7EE97010 7EEC8E1C
0x7EEC8DFC: 7EEC8E1C 7DBD456C

[-- Attachment #3: x64_dump.txt --]
[-- Type: text/plain, Size: 5414 bytes --]

!!!! X64 Exception Type - 03(#BP - Breakpoint)  CPU Apic ID - 00000000 !!!!
RIP  - 000000007EC28791, CS  - 0000000000000038, RFLAGS - 0000000000000202
RAX  - 000000007FE71018, RCX - 000000007F34F498, RDX - 000000007FE71018
RBX  - 0000000000810248, RSP - 000000007FEE4C60, RBP - 000000007FEE4C60
RSI  - 0000000000000007, RDI - 000000007F34E018
R8   - 000000007EC32DC8, R9  - 000000007F34E298, R10 - 0000000000000036
R11  - 00000000000000D7, R12 - 0000000000000000, R13 - 0000000000000000
R14  - 0000000000000000, R15 - 0000000000000000
DS   - 0000000000000030, ES  - 0000000000000030, FS  - 0000000000000030
GS   - 0000000000000030, SS  - 0000000000000030
CR0  - 0000000080010033, CR2 - 0000000000000000, CR3 - 000000007FE83000
CR4  - 0000000000000668, CR8 - 0000000000000000
DR0  - 0000000000000000, DR1 - 0000000000000000, DR2 - 0000000000000000
DR3  - 0000000000000000, DR6 - 00000000FFFF0FF0, DR7 - 0000000000000400
GDTR - 000000007FE71A98 0000000000000047, LDTR - 0000000000000000
IDTR - 000000007F7AB018 0000000000000FFF,   TR - 0000000000000000
FXSAVE_STATE - 000000007FEE48C0

Call trace:
0 0x000000007EC28791 @ 0x000000007EC28000+0x790 (0x000000007FEE4C60) in PartitionDxe.dll
1 0x000000007EC30269 @ 0x000000007EC28000+0x8268 (0x000000007FEE4CB0) in PartitionDxe.dll
2 0x000000007EC30640 @ 0x000000007EC28000+0x863F (0x000000007FEE4CE0) in PartitionDxe.dll
3 0x000000007EC2B116 @ 0x000000007EC28000+0x3115 (0x000000007FEE4D20) in PartitionDxe.dll
4 0x000000007FEF8A1B @ 0x000000007FEE5000+0x13A1A (0x000000007FEE4DB0) in DxeCore.dll
5 0x000000007FF10F10 @ 0x000000007FEE5000+0x2BF0F (0x000000007FEE4E30) in DxeCore.dll
6 0x000000007FF1344B @ 0x000000007FEE5000+0x2E44A (0x000000007FEE4F60) in DxeCore.dll
7 0x000000007FF13BCF @ 0x000000007FEE5000+0x2EBCE (0x000000007FEE4F90) in DxeCore.dll
8 0x000000007FEE9DDE @ 0x000000007FEE5000+0x4DDD (0x000000007FEE4FC0) in DxeCore.dll
9 0x000000007FF5B75F @ 0x000000007FF5B000+0x75E (0x000000007BFDC840) in DxeIpl.dll
10 0x000000007FF61551 @ 0x000000007FF5B000+0x6550 (0x000000007BFDC8C0) in DxeIpl.dll
11 0x000000007FF6031D @ 0x000000007FF5B000+0x531C (0x000000007BFDCA30) in DxeIpl.dll
12 0x000000007FF6CDCB @ 0x000000007FF64000+0x8DCA (0x000000007BFDCF80) in PeiCore.dll
13 0x00000000008286F4 @ 0x0000000000820140+0x85B3 (0x000000007BFDD4D0) in PeiCore.dll
14 0x0000000000830940 @ 0x0000000000820140+0x107FF (0x0000000000817600) in PeiCore.dll
15 0x0000000000831585 @ 0x0000000000820140+0x11444 (0x00000000008176D0) in PeiCore.dll
16 0x0000000000828DAD @ 0x0000000000820140+0x8C6C (0x0000000000817C20) in PeiCore.dll
17 0x0000000000832392 @ 0x0000000000820140+0x12251 (0x0000000000817C50) in PeiCore.dll
18 0x0000000000824313 @ 0x0000000000820140+0x41D2 (0x0000000000817C80) in PeiCore.dll
19 0x00000000FFFD42F1 @ 0x00000000FFFCC094+0x825C (0x0000000000817CE0) in SecMain.dll
20 0x00000000FFFCF578 @ 0x00000000FFFCC094+0x34E3 (0x0000000000817D10) in SecMain.dll
21 0x00000000FFFD428C @ 0x00000000FFFCC094+0x81F7 (0x0000000000817FD0) in SecMain.dll
22 0x00000000FFFD44E9 @ 0x00000000FFFCC094+0x8454 (0x00000000FFFCC000) in SecMain.dll

PartitionDxe.dll (ImageBase=0x000000007EC28000, EntryPoint=0x000000007EC2B01F):
/home/pcacjr/src/edk2/Build/OvmfX64/NOOPT_GCC5/X64/MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe/DEBUG/PartitionDxe.dll
DxeCore.dll (ImageBase=0x000000007FEE5000, EntryPoint=0x000000007FEE9DBE):
/home/pcacjr/src/edk2/Build/OvmfX64/NOOPT_GCC5/X64/MdeModulePkg/Core/Dxe/DxeMain/DEBUG/DxeCore.dll
DxeIpl.dll (ImageBase=0x000000007FF5B000, EntryPoint=0x000000007FF5F87F):
/home/pcacjr/src/edk2/Build/OvmfX64/NOOPT_GCC5/X64/MdeModulePkg/Core/DxeIplPeim/DxeIpl/DEBUG/DxeIpl.dll
PeiCore.dll (ImageBase=0x000000007FF64000, EntryPoint=0x000000007FF681AD):
/home/pcacjr/src/edk2/Build/OvmfX64/NOOPT_GCC5/X64/MdeModulePkg/Core/Pei/PeiMain/DEBUG/PeiCore.dll
PeiCore.dll (ImageBase=0x0000000000820140, EntryPoint=0x00000000008242ED):
/home/pcacjr/src/edk2/Build/OvmfX64/NOOPT_GCC5/X64/MdeModulePkg/Core/Pei/PeiMain/DEBUG/PeiCore.dll
SecMain.dll (ImageBase=0x00000000FFFCC094, EntryPoint=0x00000000FFFD44D4):
/home/pcacjr/src/edk2/Build/OvmfX64/NOOPT_GCC5/X64/OvmfPkg/Sec/SecMain/DEBUG/SecMain.dll

Stack dump:
0x000000007FEE4C60: 000000007FEE4CB0 000000007EC30269
0x000000007FEE4C70: 000000007EC32B20 0000000000000000
0x000000007FEE4C80: 000000007EC32DC8 0000000000000100
0x000000007FEE4C90: 000000007FEE4CB0 0000000000000000
0x000000007FEE4CA0: 000000007FEE4CE0 000000007EC305BA
0x000000007FEE4CB0: 000000007FEE4CE0 000000007EC30640
0x000000007FEE4CC0: 000000007F34F498 000000007FE71018
0x000000007FEE4CD0: 000000077FEF88FB 0000000000000000
0x000000007FEE4CE0: 000000007FEE4D20 000000007EC2B116
0x000000007FEE4CF0: 000000007F34F498 000000007FE71018
0x000000007FEE4D00: 0000000000000000 0000000000000000
0x000000007FEE4D10: 0000000000000000 0000000000000000
0x000000007FEE4D20: 000000007FEE4DB0 000000007FEF8A1B
0x000000007FEE4D30: 000000007F34F498 000000007FE71018
0x000000007FEE4D40: 0000000000000000 0000000000000004
0x000000007FEE4D50: 000000007FF1C1A8 000000007FF1CF90
0x000000007FEE4D60: 000000007FEE4DB0 0000000000000000
0x000000007FEE4D70: 000000007FF1C180 00000000000000B0
0x000000007FEE4D80: 0000000000000000 000000007F34E018
0x000000007FEE4D90: 000000007F34F498 0000000000000000
0x000000007FEE4DA0: 000000007FF1C1A8 000000007FF1CF90
0x000000007FEE4DB0: 000000007FEE4E30 000000007FF10F10
0x000000007FEE4DC0: 000000007F34F498 0000000000000000

^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC v2 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
  2017-11-16  1:57     ` Yao, Jiewen
@ 2017-11-16 22:13       ` Paulo Alcantara
  2017-11-17  3:43         ` Yao, Jiewen
  0 siblings, 1 reply; 82+ messages in thread
From: Paulo Alcantara @ 2017-11-16 22:13 UTC (permalink / raw)
  To: Yao, Jiewen
  Cc: Paulo Alcantara, edk2-devel@lists.01.org, Laszlo Ersek,
	Dong, Eric

Hi Jiewen,

On Wed, November 15, 2017 11:57 pm, Yao, Jiewen wrote:
> Hi Paulo
> Thanks to bring this cool feature.
>
> I have same feeling as Jeff Fan. It is great!
>
> I have some questions, hope you can clarify.
> 1) Would you please let us know which tool change is validated? Such as
> MSVC, or GCC?

GCC only. This should not work on MSVC because it relies on frame pointers.

> 2) Would you please let us know which phase is validated? Such as PEI
> PreMemory, PEI PostMemory, DXE, SMM?

DXE. I'm currently testing it by placing a random "CpuBreakpoint ()" in
PartitionDxe driver.

> 3) Would you please let us know if there is any special condition
> validated? Such as code is in an interrupt handler, SMM initialization
> code, thunk code during mode switch, etc.
> I ask this because I have seen lots of discussion on sanity check, to
> avoid double exception.

At the moment I'm only ensuring proper aligned RSP and ESP values. But we
still need to validate the RIP values, etc. as Andrew and Brian suggested.

> 4) We supported some security feature to create a hole in normal memory
> map, such as PageGuard, StackGuard, SMM communication protection, etc.
> Accessing those memory directly will cause #PF immediately.
> Would you please let us know how we detect that case, to avoid RIP or RSP
> fail to the non-present page?

Sorry. I have no idea :-( I'd hope to get some help from you guys.

> 5) May I know why we check RIP < ImageBase? Is that legal or illegal if
> RIP > ImageBase+ImageSize, but RIP in another PE/COFF image?

That check was a wrong assumption that I had in the beginning. RIP may
point to either lower or higher addresses where the other PE/COFF images
are located. Fixed it in v3.

Sorry for the delay in the response. I'm only able to work on this in my
free time.

Thanks!
Paulo

>> +    //
>> +    // Check if RIP is within another PE/COFF image base address
>> +    //
>> +    if (Rip < ImageBase) {
>> +      //
>> +      // Search for the respective PE/COFF image based on RIP
>> +      //
>
> Thank you
> Yao Jiewen
>
>
>> -----Original Message-----
>> From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of
>> Paulo
>> Alcantara
>> Sent: Thursday, November 16, 2017 9:18 AM
>> To: edk2-devel@lists.01.org
>> Cc: Laszlo Ersek <lersek@redhat.com>; Dong, Eric <eric.dong@intel.com>
>> Subject: [edk2] [RFC v2 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add
>> stack
>> trace support
>>
>> This patch adds stack trace support during a X64 CPU exception.
>>
>> It will dump out back trace, stack contents as well as image module
>> names that were part of the call stack.
>>
>> Contributed-under: TianoCore Contribution Agreement 1.1
>> Cc: Eric Dong <eric.dong@intel.com>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
>> ---
>>  UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c |
>> 369
>> +++++++++++++++++++-
>>  1 file changed, 367 insertions(+), 2 deletions(-)
>>
>> diff --git
>> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
>> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
>> index 65f0cff680..11cd7c9e1c 100644
>> ---
>> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
>> +++
>> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
>> @@ -14,6 +14,11 @@
>>
>>  #include "CpuExceptionCommon.h"
>>
>> +//
>> +// Unknown PDB file name
>> +//
>> +GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8
>> *mUnknownPdbFileName = "????";
>> +
>>  /**
>>    Return address map of exception handler template so that C code can
>> generate
>>    exception tables.
>> @@ -242,6 +247,350 @@ DumpCpuContext (
>>      );
>>  }
>>
>> +/**
>> +  Get absolute path and file name of PDB file in PE/COFF image.
>> +
>> +  @param[in]  ImageBase            Base address of PE/COFF image.
>> +  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
>> +  @param[out] PdbFileName          File name of PDB file.
>> +**/
>> +STATIC
>> +VOID
>> +GetPdbFileName (
>> +  IN  UINTN    ImageBase,
>> +  OUT CHAR8    **PdbAbsoluteFilePath,
>> +  OUT CHAR8    **PdbFileName
>> +  )
>> +{
>> +  VOID   *PdbPointer;
>> +  CHAR8  *Str;
>> +
>> +  //
>> +  // Get PDB file name from PE/COFF image
>> +  //
>> +  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
>> +  if (PdbPointer == NULL) {
>> +    //
>> +    // No PDB file name found. Set it to an unknown file name.
>> +    //
>> +    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
>> +    if (PdbAbsoluteFilePath != NULL) {
>> +      *PdbAbsoluteFilePath = NULL;
>> +    }
>> +  } else {
>> +    //
>> +    // Get file name portion out of PDB file in PE/COFF image
>> +    //
>> +    Str = (CHAR8 *)((UINTN)PdbPointer +
>> +                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
>> +    for (; *Str != '/' && *Str != '\\'; Str--) {
>> +      ;
>> +    }
>> +
>> +    //
>> +    // Set PDB file name (also skip trailing path separator: '/' or
>> '\\')
>> +    //
>> +    *PdbFileName = Str + 1;
>> +
>> +    if (PdbAbsoluteFilePath != NULL) {
>> +      //
>> +      // Set absolute file path of PDB file
>> +      //
>> +      *PdbAbsoluteFilePath = PdbPointer;
>> +    }
>> +  }
>> +}
>> +
>> +/**
>> +  Dump stack contents.
>> +
>> +  @param[in]  CurrentRsp         Current stack pointer address.
>> +  @param[in]  UnwondStacksCount  Count of unwond stack frames.
>> +**/
>> +STATIC
>> +VOID
>> +DumpStackContents (
>> +  IN UINT64  CurrentRsp,
>> +  IN INTN    UnwondStacksCount
>> +  )
>> +{
>> +  //
>> +  // Check for proper stack pointer alignment
>> +  //
>> +  if (((UINTN)CurrentRsp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
>> +    InternalPrintMessage ("!!!! Unaligned stack pointer. !!!!\n");
>> +    return;
>> +  }
>> +
>> +  //
>> +  // Dump out stack contents
>> +  //
>> +  InternalPrintMessage ("\nStack dump:\n");
>> +  while (UnwondStacksCount-- > 0) {
>> +    InternalPrintMessage (
>> +      "0x%016lx: %016lx %016lx\n",
>> +      CurrentRsp,
>> +      *(UINT64 *)CurrentRsp,
>> +      *(UINT64 *)((UINTN)CurrentRsp + 8)
>> +      );
>> +
>> +    //
>> +    // Point to next stack
>> +    //
>> +    CurrentRsp += CPU_STACK_ALIGNMENT;
>> +  }
>> +}
>> +
>> +/**
>> +  Dump all image module names from call stack.
>> +
>> +  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
>> +**/
>> +STATIC
>> +VOID
>> +DumpImageModuleNames (
>> +  IN EFI_SYSTEM_CONTEXT   SystemContext
>> +  )
>> +{
>> +  EFI_STATUS  Status;
>> +  UINT64      Rip;
>> +  UINTN       ImageBase;
>> +  VOID        *EntryPoint;
>> +  CHAR8       *PdbAbsoluteFilePath;
>> +  CHAR8       *PdbFileName;
>> +  UINT64      Rbp;
>> +
>> +  //
>> +  // Set current RIP address
>> +  //
>> +  Rip = SystemContext.SystemContextX64->Rip;
>> +
>> +  //
>> +  // Set current frame pointer address
>> +  //
>> +  Rbp = SystemContext.SystemContextX64->Rbp;
>> +
>> +  //
>> +  // Check for proper frame pointer alignment
>> +  //
>> +  if (((UINTN)Rbp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
>> +    InternalPrintMessage ("!!!! Unaligned frame pointer. !!!!\n");
>> +    return;
>> +  }
>> +
>> +  //
>> +  // Get initial PE/COFF image base address from current RIP
>> +  //
>> +  ImageBase = PeCoffSearchImageBase (Rip);
>> +  if (ImageBase == 0) {
>> +    InternalPrintMessage ("!!!! Could not find image module names.
>> !!!!");
>> +    return;
>> +  }
>> +
>> +  //
>> +  // Get initial PE/COFF image's entry point
>> +  //
>> +  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
>> +  if (EFI_ERROR (Status)) {
>> +    EntryPoint = NULL;
>> +  }
>> +
>> +  //
>> +  // Get file name and absolute path of initial PDB file
>> +  //
>> +  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
>> +
>> +  //
>> +  // Print out initial image module name (if any)
>> +  //
>> +  if (PdbAbsoluteFilePath != NULL) {
>> +    InternalPrintMessage (
>> +      "\n%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
>> +      PdbFileName,
>> +      ImageBase,
>> +      (UINTN)EntryPoint
>> +      );
>> +    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
>> +  }
>> +
>> +  //
>> +  // Walk through call stack and find next module names
>> +  //
>> +  for (;;) {
>> +    //
>> +    // Set RIP with return address from current stack frame
>> +    //
>> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
>> +
>> +    //
>> +    // If RIP is zero, then stop unwinding the stack
>> +    //
>> +    if (Rip == 0) {
>> +      break;
>> +    }
>> +
>> +    //
>> +    // Check if RIP is within another PE/COFF image base address
>> +    //
>> +    if (Rip < ImageBase) {
>> +      //
>> +      // Search for the respective PE/COFF image based on RIP
>> +      //
>> +      ImageBase = PeCoffSearchImageBase (Rip);
>> +      if (ImageBase == 0) {
>> +        //
>> +        // Stop stack trace
>> +        //
>> +        break;
>> +      }
>> +
>> +      //
>> +      // Get PE/COFF image's entry point
>> +      //
>> +      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase,
>> &EntryPoint);
>> +      if (EFI_ERROR (Status)) {
>> +        EntryPoint = NULL;
>> +      }
>> +
>> +      //
>> +      // Get file name and absolute path of PDB file
>> +      //
>> +      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
>> +
>> +      //
>> +      // Print out image module name (if any)
>> +      //
>> +      if (PdbAbsoluteFilePath != NULL) {
>> +        InternalPrintMessage (
>> +          "%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
>> +          PdbFileName,
>> +          ImageBase,
>> +          (UINTN)EntryPoint
>> +          );
>> +        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
>> +      }
>> +    }
>> +
>> +    //
>> +    // Unwind the stack
>> +    //
>> +    Rbp = *(UINT64 *)(UINTN)Rbp;
>> +  }
>> +}
>> +
>> +/**
>> +  Dump stack trace.
>> +
>> +  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
>> +  @param[out] UnwondStacksCount  Count of unwond stack frames.
>> +**/
>> +STATIC
>> +VOID
>> +DumpStackTrace (
>> +  IN  EFI_SYSTEM_CONTEXT   SystemContext,
>> +  OUT INTN                 *UnwondStacksCount
>> +  )
>> +{
>> +  UINT64  Rip;
>> +  UINT64  Rbp;
>> +  UINTN   ImageBase;
>> +  CHAR8   *PdbFileName;
>> +
>> +  //
>> +  // Set current RIP address
>> +  //
>> +  Rip = SystemContext.SystemContextX64->Rip;
>> +
>> +  //
>> +  // Set current frame pointer address
>> +  //
>> +  Rbp = SystemContext.SystemContextX64->Rbp;
>> +
>> +  //
>> +  // Get initial PE/COFF image base address from current RIP
>> +  //
>> +  ImageBase = PeCoffSearchImageBase (Rip);
>> +  if (ImageBase == 0) {
>> +    InternalPrintMessage ("!!!! Could not find backtrace information.
>> !!!!");
>> +    return;
>> +  }
>> +
>> +  //
>> +  // Get PDB file name from initial PE/COFF image
>> +  //
>> +  GetPdbFileName (ImageBase, NULL, &PdbFileName);
>> +
>> +  //
>> +  // Initialize count of unwond stacks
>> +  //
>> +  *UnwondStacksCount = 1;
>> +
>> +  //
>> +  // Print out back trace
>> +  //
>> +  InternalPrintMessage ("\nCall trace:\n");
>> +
>> +  for (;;) {
>> +    //
>> +    // Print stack frame in the following format:
>> +    //
>> +    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName> | ????]
>> +    //
>> +    InternalPrintMessage (
>> +      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
>> +      *UnwondStacksCount - 1,
>> +      Rip,
>> +      ImageBase,
>> +      Rip - ImageBase - 1,
>> +      Rbp,
>> +      PdbFileName
>> +      );
>> +
>> +    //
>> +    // Set RIP with return address from current stack frame
>> +    //
>> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
>> +
>> +    //
>> +    // If RIP is zero, then stop unwinding the stack
>> +    //
>> +    if (Rip == 0) {
>> +      break;
>> +    }
>> +
>> +    //
>> +    // Check if RIP is within another PE/COFF image base address
>> +    //
>> +    if (Rip < ImageBase) {
>> +      //
>> +      // Search for the respective PE/COFF image based on RIP
>> +      //
>> +      ImageBase = PeCoffSearchImageBase (Rip);
>> +      if (ImageBase == 0) {
>> +        //
>> +        // Stop stack trace
>> +        //
>> +        break;
>> +      }
>> +
>> +      //
>> +      // Get PDB file name
>> +      //
>> +      GetPdbFileName (ImageBase, NULL, &PdbFileName);
>> +    }
>> +
>> +    //
>> +    // Unwind the stack
>> +    //
>> +    Rbp = *(UINT64 *)(UINTN)Rbp;
>> +
>> +    //
>> +    // Increment count of unwond stacks
>> +    //
>> +    (*UnwondStacksCount)++;
>> +  }
>> +}
>> +
>>  /**
>>    Display CPU information.
>>
>> @@ -254,9 +603,25 @@ DumpImageAndCpuContent (
>>    IN EFI_SYSTEM_CONTEXT   SystemContext
>>    )
>>  {
>> +  INTN UnwondStacksCount;
>> +
>> +  //
>> +  // Dump CPU context
>> +  //
>>    DumpCpuContext (ExceptionType, SystemContext);
>> +
>> +  //
>> +  // Dump stack trace
>> +  //
>> +  DumpStackTrace (SystemContext, &UnwondStacksCount);
>> +
>> +  //
>> +  // Dump image module names
>> +  //
>> +  DumpImageModuleNames (SystemContext);
>> +
>>    //
>> -  // Dump module image base and module entry point by RIP
>> +  // Dump stack contents
>>    //
>> -  DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
>> +  DumpStackContents (SystemContext.SystemContextX64->Rsp,
>> UnwondStacksCount);
>>  }
>> --
>> 2.14.3
>>
>> _______________________________________________
>> edk2-devel mailing list
>> edk2-devel@lists.01.org
>> https://lists.01.org/mailman/listinfo/edk2-devel
>
>


-- 
Paulo Alcantara, HP Inc.
Speaking for myself only.


^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC v2 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
  2017-11-16 15:43     ` Brian J. Johnson
@ 2017-11-16 22:19       ` Paulo Alcantara
  0 siblings, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2017-11-16 22:19 UTC (permalink / raw)
  To: Brian J. Johnson; +Cc: edk2-devel

Hi Brijan,

On Thu, November 16, 2017 1:43 pm, Brian J. Johnson
>
> (These comments apply to patch 3/3 as well.)
>
> Typo:  UnwondStacksCount should be UnwoundStacksCount

Fixed in v3. Thanks!

>
> It's good to check the alignment of the stack, as you're doing.  But
> I'll reiterate that you absolutely need some better sanity checking of
> the stack and IP addresses before you dereference them.  Remember that
> they could be absolutely *anything* at the entry to this code.
> Something caused an error, and it may have been one of those registers.

Yes - you're absolutely right. I currently have no idea how to validate
those values as well as didn't have time to do so. Sorry. I hope to get to
it ASAP or someone could help me with that.

>
> Also, if the code was built with a compiler which isn't using RBP as a
> base pointer, RBP is unlikely to contain a stack address.  That will
> cause issues if you use it for unwinding without a sanity check.

Yes. I haven't figured out how to do that yet. It's also in my TODO list.

Thank you very much for the suggestions!

Paulo

>
> Thanks,
> Brian
>
>> ---
>>   UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c |
>> 369 +++++++++++++++++++-
>>   1 file changed, 367 insertions(+), 2 deletions(-)
>>
>> diff --git
>> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
>> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
>> index 65f0cff680..11cd7c9e1c 100644
>> ---
>> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
>> +++
>> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
>> @@ -14,6 +14,11 @@
>>
>>   #include "CpuExceptionCommon.h"
>>
>> +//
>> +// Unknown PDB file name
>> +//
>> +GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName =
>> "????";
>> +
>>   /**
>>     Return address map of exception handler template so that C code can
>> generate
>>     exception tables.
>> @@ -242,6 +247,350 @@ DumpCpuContext (
>>       );
>>   }
>>
>> +/**
>> +  Get absolute path and file name of PDB file in PE/COFF image.
>> +
>> +  @param[in]  ImageBase            Base address of PE/COFF image.
>> +  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
>> +  @param[out] PdbFileName          File name of PDB file.
>> +**/
>> +STATIC
>> +VOID
>> +GetPdbFileName (
>> +  IN  UINTN    ImageBase,
>> +  OUT CHAR8    **PdbAbsoluteFilePath,
>> +  OUT CHAR8    **PdbFileName
>> +  )
>> +{
>> +  VOID   *PdbPointer;
>> +  CHAR8  *Str;
>> +
>> +  //
>> +  // Get PDB file name from PE/COFF image
>> +  //
>> +  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
>> +  if (PdbPointer == NULL) {
>> +    //
>> +    // No PDB file name found. Set it to an unknown file name.
>> +    //
>> +    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
>> +    if (PdbAbsoluteFilePath != NULL) {
>> +      *PdbAbsoluteFilePath = NULL;
>> +    }
>> +  } else {
>> +    //
>> +    // Get file name portion out of PDB file in PE/COFF image
>> +    //
>> +    Str = (CHAR8 *)((UINTN)PdbPointer +
>> +                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
>> +    for (; *Str != '/' && *Str != '\\'; Str--) {
>> +      ;
>> +    }
>> +
>> +    //
>> +    // Set PDB file name (also skip trailing path separator: '/' or
>> '\\')
>> +    //
>> +    *PdbFileName = Str + 1;
>> +
>> +    if (PdbAbsoluteFilePath != NULL) {
>> +      //
>> +      // Set absolute file path of PDB file
>> +      //
>> +      *PdbAbsoluteFilePath = PdbPointer;
>> +    }
>> +  }
>> +}
>> +
>> +/**
>> +  Dump stack contents.
>> +
>> +  @param[in]  CurrentRsp         Current stack pointer address.
>> +  @param[in]  UnwondStacksCount  Count of unwond stack frames.
>> +**/
>> +STATIC
>> +VOID
>> +DumpStackContents (
>> +  IN UINT64  CurrentRsp,
>> +  IN INTN    UnwondStacksCount
>> +  )
>> +{
>> +  //
>> +  // Check for proper stack pointer alignment
>> +  //
>> +  if (((UINTN)CurrentRsp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
>> +    InternalPrintMessage ("!!!! Unaligned stack pointer. !!!!\n");
>> +    return;
>> +  }
>> +
>> +  //
>> +  // Dump out stack contents
>> +  //
>> +  InternalPrintMessage ("\nStack dump:\n");
>> +  while (UnwondStacksCount-- > 0) {
>> +    InternalPrintMessage (
>> +      "0x%016lx: %016lx %016lx\n",
>> +      CurrentRsp,
>> +      *(UINT64 *)CurrentRsp,
>> +      *(UINT64 *)((UINTN)CurrentRsp + 8)
>> +      );
>> +
>> +    //
>> +    // Point to next stack
>> +    //
>> +    CurrentRsp += CPU_STACK_ALIGNMENT;
>> +  }
>> +}
>> +
>> +/**
>> +  Dump all image module names from call stack.
>> +
>> +  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
>> +**/
>> +STATIC
>> +VOID
>> +DumpImageModuleNames (
>> +  IN EFI_SYSTEM_CONTEXT   SystemContext
>> +  )
>> +{
>> +  EFI_STATUS  Status;
>> +  UINT64      Rip;
>> +  UINTN       ImageBase;
>> +  VOID        *EntryPoint;
>> +  CHAR8       *PdbAbsoluteFilePath;
>> +  CHAR8       *PdbFileName;
>> +  UINT64      Rbp;
>> +
>> +  //
>> +  // Set current RIP address
>> +  //
>> +  Rip = SystemContext.SystemContextX64->Rip;
>> +
>> +  //
>> +  // Set current frame pointer address
>> +  //
>> +  Rbp = SystemContext.SystemContextX64->Rbp;
>> +
>> +  //
>> +  // Check for proper frame pointer alignment
>> +  //
>> +  if (((UINTN)Rbp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
>> +    InternalPrintMessage ("!!!! Unaligned frame pointer. !!!!\n");
>> +    return;
>> +  }
>> +
>> +  //
>> +  // Get initial PE/COFF image base address from current RIP
>> +  //
>> +  ImageBase = PeCoffSearchImageBase (Rip);
>> +  if (ImageBase == 0) {
>> +    InternalPrintMessage ("!!!! Could not find image module names.
>> !!!!");
>> +    return;
>> +  }
>> +
>> +  //
>> +  // Get initial PE/COFF image's entry point
>> +  //
>> +  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
>> +  if (EFI_ERROR (Status)) {
>> +    EntryPoint = NULL;
>> +  }
>> +
>> +  //
>> +  // Get file name and absolute path of initial PDB file
>> +  //
>> +  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
>> +
>> +  //
>> +  // Print out initial image module name (if any)
>> +  //
>> +  if (PdbAbsoluteFilePath != NULL) {
>> +    InternalPrintMessage (
>> +      "\n%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
>> +      PdbFileName,
>> +      ImageBase,
>> +      (UINTN)EntryPoint
>> +      );
>> +    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
>> +  }
>> +
>> +  //
>> +  // Walk through call stack and find next module names
>> +  //
>> +  for (;;) {
>> +    //
>> +    // Set RIP with return address from current stack frame
>> +    //
>> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
>> +
>> +    //
>> +    // If RIP is zero, then stop unwinding the stack
>> +    //
>> +    if (Rip == 0) {
>> +      break;
>> +    }
>> +
>> +    //
>> +    // Check if RIP is within another PE/COFF image base address
>> +    //
>> +    if (Rip < ImageBase) {
>> +      //
>> +      // Search for the respective PE/COFF image based on RIP
>> +      //
>> +      ImageBase = PeCoffSearchImageBase (Rip);
>> +      if (ImageBase == 0) {
>> +        //
>> +        // Stop stack trace
>> +        //
>> +        break;
>> +      }
>> +
>> +      //
>> +      // Get PE/COFF image's entry point
>> +      //
>> +      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase,
>> &EntryPoint);
>> +      if (EFI_ERROR (Status)) {
>> +        EntryPoint = NULL;
>> +      }
>> +
>> +      //
>> +      // Get file name and absolute path of PDB file
>> +      //
>> +      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
>> +
>> +      //
>> +      // Print out image module name (if any)
>> +      //
>> +      if (PdbAbsoluteFilePath != NULL) {
>> +        InternalPrintMessage (
>> +          "%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
>> +          PdbFileName,
>> +          ImageBase,
>> +          (UINTN)EntryPoint
>> +          );
>> +        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
>> +      }
>> +    }
>> +
>> +    //
>> +    // Unwind the stack
>> +    //
>> +    Rbp = *(UINT64 *)(UINTN)Rbp;
>> +  }
>> +}
>> +
>> +/**
>> +  Dump stack trace.
>> +
>> +  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
>> +  @param[out] UnwondStacksCount  Count of unwond stack frames.
>> +**/
>> +STATIC
>> +VOID
>> +DumpStackTrace (
>> +  IN  EFI_SYSTEM_CONTEXT   SystemContext,
>> +  OUT INTN                 *UnwondStacksCount
>> +  )
>> +{
>> +  UINT64  Rip;
>> +  UINT64  Rbp;
>> +  UINTN   ImageBase;
>> +  CHAR8   *PdbFileName;
>> +
>> +  //
>> +  // Set current RIP address
>> +  //
>> +  Rip = SystemContext.SystemContextX64->Rip;
>> +
>> +  //
>> +  // Set current frame pointer address
>> +  //
>> +  Rbp = SystemContext.SystemContextX64->Rbp;
>> +
>> +  //
>> +  // Get initial PE/COFF image base address from current RIP
>> +  //
>> +  ImageBase = PeCoffSearchImageBase (Rip);
>> +  if (ImageBase == 0) {
>> +    InternalPrintMessage ("!!!! Could not find backtrace information.
>> !!!!");
>> +    return;
>> +  }
>> +
>> +  //
>> +  // Get PDB file name from initial PE/COFF image
>> +  //
>> +  GetPdbFileName (ImageBase, NULL, &PdbFileName);
>> +
>> +  //
>> +  // Initialize count of unwond stacks
>> +  //
>> +  *UnwondStacksCount = 1;
>> +
>> +  //
>> +  // Print out back trace
>> +  //
>> +  InternalPrintMessage ("\nCall trace:\n");
>> +
>> +  for (;;) {
>> +    //
>> +    // Print stack frame in the following format:
>> +    //
>> +    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName> | ????]
>> +    //
>> +    InternalPrintMessage (
>> +      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
>> +      *UnwondStacksCount - 1,
>> +      Rip,
>> +      ImageBase,
>> +      Rip - ImageBase - 1,
>> +      Rbp,
>> +      PdbFileName
>> +      );
>> +
>> +    //
>> +    // Set RIP with return address from current stack frame
>> +    //
>> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
>> +
>> +    //
>> +    // If RIP is zero, then stop unwinding the stack
>> +    //
>> +    if (Rip == 0) {
>> +      break;
>> +    }
>> +
>> +    //
>> +    // Check if RIP is within another PE/COFF image base address
>> +    //
>> +    if (Rip < ImageBase) {
>> +      //
>> +      // Search for the respective PE/COFF image based on RIP
>> +      //
>> +      ImageBase = PeCoffSearchImageBase (Rip);
>> +      if (ImageBase == 0) {
>> +        //
>> +        // Stop stack trace
>> +        //
>> +        break;
>> +      }
>> +
>> +      //
>> +      // Get PDB file name
>> +      //
>> +      GetPdbFileName (ImageBase, NULL, &PdbFileName);
>> +    }
>> +
>> +    //
>> +    // Unwind the stack
>> +    //
>> +    Rbp = *(UINT64 *)(UINTN)Rbp;
>> +
>> +    //
>> +    // Increment count of unwond stacks
>> +    //
>> +    (*UnwondStacksCount)++;
>> +  }
>> +}
>> +
>>   /**
>>     Display CPU information.
>>
>> @@ -254,9 +603,25 @@ DumpImageAndCpuContent (
>>     IN EFI_SYSTEM_CONTEXT   SystemContext
>>     )
>>   {
>> +  INTN UnwondStacksCount;
>> +
>> +  //
>> +  // Dump CPU context
>> +  //
>>     DumpCpuContext (ExceptionType, SystemContext);
>> +
>> +  //
>> +  // Dump stack trace
>> +  //
>> +  DumpStackTrace (SystemContext, &UnwondStacksCount);
>> +
>> +  //
>> +  // Dump image module names
>> +  //
>> +  DumpImageModuleNames (SystemContext);
>> +
>>     //
>> -  // Dump module image base and module entry point by RIP
>> +  // Dump stack contents
>>     //
>> -  DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
>> +  DumpStackContents (SystemContext.SystemContextX64->Rsp,
>> UnwondStacksCount);
>>   }
>>
>
>
> --
> Brian J. Johnson
> Enterprise X86 Lab
>
> Hewlett Packard Enterprise
>
> brian.johnson@hpe.com
>
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org
> https://lists.01.org/mailman/listinfo/edk2-devel
>


-- 
Paulo Alcantara, HP Inc.
Speaking for myself only.


^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC v2 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
  2017-11-16 22:13       ` Paulo Alcantara
@ 2017-11-17  3:43         ` Yao, Jiewen
  2017-11-20 14:51           ` Paulo Alcantara
  0 siblings, 1 reply; 82+ messages in thread
From: Yao, Jiewen @ 2017-11-17  3:43 UTC (permalink / raw)
  To: Paulo Alcantara; +Cc: edk2-devel@lists.01.org, Laszlo Ersek, Dong, Eric

Thanks for your reply. Comments below:

> -----Original Message-----
> From: Paulo Alcantara [mailto:pcacjr@zytor.com]
> Sent: Friday, November 17, 2017 6:13 AM
> To: Yao, Jiewen <jiewen.yao@intel.com>
> Cc: Paulo Alcantara <pcacjr@zytor.com>; edk2-devel@lists.01.org; Laszlo Ersek
> <lersek@redhat.com>; Dong, Eric <eric.dong@intel.com>
> Subject: RE: [edk2] [RFC v2 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add
> stack trace support
> 
> Hi Jiewen,
> 
> On Wed, November 15, 2017 11:57 pm, Yao, Jiewen wrote:
> > Hi Paulo
> > Thanks to bring this cool feature.
> >
> > I have same feeling as Jeff Fan. It is great!
> >
> > I have some questions, hope you can clarify.
> > 1) Would you please let us know which tool change is validated? Such as
> > MSVC, or GCC?
> 
> GCC only. This should not work on MSVC because it relies on frame pointers.

[Jiewen] Not work is OK.
But my question is more about: Have you tested? Or have you tried?
Do you see basic exception information can be dumped without any problem for MSVC?
Or do you see system crash immediately for MSVC?



> > 2) Would you please let us know which phase is validated? Such as PEI
> > PreMemory, PEI PostMemory, DXE, SMM?
> 
> DXE. I'm currently testing it by placing a random "CpuBreakpoint ()" in
> PartitionDxe driver.
> 

[Jiewen] Ummm.
Based upon the fact that the code you update may be used in all those phases.
I recommend to validate them in the final patch.


> > 3) Would you please let us know if there is any special condition
> > validated? Such as code is in an interrupt handler, SMM initialization
> > code, thunk code during mode switch, etc.
> > I ask this because I have seen lots of discussion on sanity check, to
> > avoid double exception.
> 
> At the moment I'm only ensuring proper aligned RSP and ESP values. But we
> still need to validate the RIP values, etc. as Andrew and Brian suggested.

[Jiewen] OK. I look forward to seeing your patch to validate more.
Same as #2. I suggest you validate all those corner cases in the final patch.

> > 4) We supported some security feature to create a hole in normal memory
> > map, such as PageGuard, StackGuard, SMM communication protection, etc.
> > Accessing those memory directly will cause #PF immediately.
> > Would you please let us know how we detect that case, to avoid RIP or RSP
> > fail to the non-present page?
> 
> Sorry. I have no idea :-( I'd hope to get some help from you guys.


[Jiewen] One possible solution is to scan page table, before access this address, to make sure you are accessing present page.
You can also filer invalid address directly by checking CPUID physical address bit before that. The invalid address will cause #GP instead of #PF.

All I all, I would like to clarify the goal for exception lib: whatever exception condition the BIOS meets, the exception handler can always dump the *basic* exception information and halt.
The missing of advanced symbol info is acceptable. But system crash, when we want to dump the advanced info, is not the best choice.



> > 5) May I know why we check RIP < ImageBase? Is that legal or illegal if
> > RIP > ImageBase+ImageSize, but RIP in another PE/COFF image?
> 
> That check was a wrong assumption that I had in the beginning. RIP may
> point to either lower or higher addresses where the other PE/COFF images
> are located. Fixed it in v3.


[Jiewen] Great. Thanks.


> Sorry for the delay in the response. I'm only able to work on this in my
> free time.
> 
> Thanks!
> Paulo
> 
> >> +    //
> >> +    // Check if RIP is within another PE/COFF image base address
> >> +    //
> >> +    if (Rip < ImageBase) {
> >> +      //
> >> +      // Search for the respective PE/COFF image based on RIP
> >> +      //
> >
> > Thank you
> > Yao Jiewen
> >
> >
> >> -----Original Message-----
> >> From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of
> >> Paulo
> >> Alcantara
> >> Sent: Thursday, November 16, 2017 9:18 AM
> >> To: edk2-devel@lists.01.org
> >> Cc: Laszlo Ersek <lersek@redhat.com>; Dong, Eric <eric.dong@intel.com>
> >> Subject: [edk2] [RFC v2 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add
> >> stack
> >> trace support
> >>
> >> This patch adds stack trace support during a X64 CPU exception.
> >>
> >> It will dump out back trace, stack contents as well as image module
> >> names that were part of the call stack.
> >>
> >> Contributed-under: TianoCore Contribution Agreement 1.1
> >> Cc: Eric Dong <eric.dong@intel.com>
> >> Cc: Laszlo Ersek <lersek@redhat.com>
> >> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> >> ---
> >>  UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c |
> >> 369
> >> +++++++++++++++++++-
> >>  1 file changed, 367 insertions(+), 2 deletions(-)
> >>
> >> diff --git
> >> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> >> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> >> index 65f0cff680..11cd7c9e1c 100644
> >> ---
> >> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> >> +++
> >> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> >> @@ -14,6 +14,11 @@
> >>
> >>  #include "CpuExceptionCommon.h"
> >>
> >> +//
> >> +// Unknown PDB file name
> >> +//
> >> +GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8
> >> *mUnknownPdbFileName = "????";
> >> +
> >>  /**
> >>    Return address map of exception handler template so that C code can
> >> generate
> >>    exception tables.
> >> @@ -242,6 +247,350 @@ DumpCpuContext (
> >>      );
> >>  }
> >>
> >> +/**
> >> +  Get absolute path and file name of PDB file in PE/COFF image.
> >> +
> >> +  @param[in]  ImageBase            Base address of PE/COFF image.
> >> +  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
> >> +  @param[out] PdbFileName          File name of PDB file.
> >> +**/
> >> +STATIC
> >> +VOID
> >> +GetPdbFileName (
> >> +  IN  UINTN    ImageBase,
> >> +  OUT CHAR8    **PdbAbsoluteFilePath,
> >> +  OUT CHAR8    **PdbFileName
> >> +  )
> >> +{
> >> +  VOID   *PdbPointer;
> >> +  CHAR8  *Str;
> >> +
> >> +  //
> >> +  // Get PDB file name from PE/COFF image
> >> +  //
> >> +  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
> >> +  if (PdbPointer == NULL) {
> >> +    //
> >> +    // No PDB file name found. Set it to an unknown file name.
> >> +    //
> >> +    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
> >> +    if (PdbAbsoluteFilePath != NULL) {
> >> +      *PdbAbsoluteFilePath = NULL;
> >> +    }
> >> +  } else {
> >> +    //
> >> +    // Get file name portion out of PDB file in PE/COFF image
> >> +    //
> >> +    Str = (CHAR8 *)((UINTN)PdbPointer +
> >> +                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
> >> +    for (; *Str != '/' && *Str != '\\'; Str--) {
> >> +      ;
> >> +    }
> >> +
> >> +    //
> >> +    // Set PDB file name (also skip trailing path separator: '/' or
> >> '\\')
> >> +    //
> >> +    *PdbFileName = Str + 1;
> >> +
> >> +    if (PdbAbsoluteFilePath != NULL) {
> >> +      //
> >> +      // Set absolute file path of PDB file
> >> +      //
> >> +      *PdbAbsoluteFilePath = PdbPointer;
> >> +    }
> >> +  }
> >> +}
> >> +
> >> +/**
> >> +  Dump stack contents.
> >> +
> >> +  @param[in]  CurrentRsp         Current stack pointer address.
> >> +  @param[in]  UnwondStacksCount  Count of unwond stack frames.
> >> +**/
> >> +STATIC
> >> +VOID
> >> +DumpStackContents (
> >> +  IN UINT64  CurrentRsp,
> >> +  IN INTN    UnwondStacksCount
> >> +  )
> >> +{
> >> +  //
> >> +  // Check for proper stack pointer alignment
> >> +  //
> >> +  if (((UINTN)CurrentRsp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
> >> +    InternalPrintMessage ("!!!! Unaligned stack pointer. !!!!\n");
> >> +    return;
> >> +  }
> >> +
> >> +  //
> >> +  // Dump out stack contents
> >> +  //
> >> +  InternalPrintMessage ("\nStack dump:\n");
> >> +  while (UnwondStacksCount-- > 0) {
> >> +    InternalPrintMessage (
> >> +      "0x%016lx: %016lx %016lx\n",
> >> +      CurrentRsp,
> >> +      *(UINT64 *)CurrentRsp,
> >> +      *(UINT64 *)((UINTN)CurrentRsp + 8)
> >> +      );
> >> +
> >> +    //
> >> +    // Point to next stack
> >> +    //
> >> +    CurrentRsp += CPU_STACK_ALIGNMENT;
> >> +  }
> >> +}
> >> +
> >> +/**
> >> +  Dump all image module names from call stack.
> >> +
> >> +  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
> >> +**/
> >> +STATIC
> >> +VOID
> >> +DumpImageModuleNames (
> >> +  IN EFI_SYSTEM_CONTEXT   SystemContext
> >> +  )
> >> +{
> >> +  EFI_STATUS  Status;
> >> +  UINT64      Rip;
> >> +  UINTN       ImageBase;
> >> +  VOID        *EntryPoint;
> >> +  CHAR8       *PdbAbsoluteFilePath;
> >> +  CHAR8       *PdbFileName;
> >> +  UINT64      Rbp;
> >> +
> >> +  //
> >> +  // Set current RIP address
> >> +  //
> >> +  Rip = SystemContext.SystemContextX64->Rip;
> >> +
> >> +  //
> >> +  // Set current frame pointer address
> >> +  //
> >> +  Rbp = SystemContext.SystemContextX64->Rbp;
> >> +
> >> +  //
> >> +  // Check for proper frame pointer alignment
> >> +  //
> >> +  if (((UINTN)Rbp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
> >> +    InternalPrintMessage ("!!!! Unaligned frame pointer. !!!!\n");
> >> +    return;
> >> +  }
> >> +
> >> +  //
> >> +  // Get initial PE/COFF image base address from current RIP
> >> +  //
> >> +  ImageBase = PeCoffSearchImageBase (Rip);
> >> +  if (ImageBase == 0) {
> >> +    InternalPrintMessage ("!!!! Could not find image module names.
> >> !!!!");
> >> +    return;
> >> +  }
> >> +
> >> +  //
> >> +  // Get initial PE/COFF image's entry point
> >> +  //
> >> +  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
> >> +  if (EFI_ERROR (Status)) {
> >> +    EntryPoint = NULL;
> >> +  }
> >> +
> >> +  //
> >> +  // Get file name and absolute path of initial PDB file
> >> +  //
> >> +  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
> >> +
> >> +  //
> >> +  // Print out initial image module name (if any)
> >> +  //
> >> +  if (PdbAbsoluteFilePath != NULL) {
> >> +    InternalPrintMessage (
> >> +      "\n%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
> >> +      PdbFileName,
> >> +      ImageBase,
> >> +      (UINTN)EntryPoint
> >> +      );
> >> +    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
> >> +  }
> >> +
> >> +  //
> >> +  // Walk through call stack and find next module names
> >> +  //
> >> +  for (;;) {
> >> +    //
> >> +    // Set RIP with return address from current stack frame
> >> +    //
> >> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
> >> +
> >> +    //
> >> +    // If RIP is zero, then stop unwinding the stack
> >> +    //
> >> +    if (Rip == 0) {
> >> +      break;
> >> +    }
> >> +
> >> +    //
> >> +    // Check if RIP is within another PE/COFF image base address
> >> +    //
> >> +    if (Rip < ImageBase) {
> >> +      //
> >> +      // Search for the respective PE/COFF image based on RIP
> >> +      //
> >> +      ImageBase = PeCoffSearchImageBase (Rip);
> >> +      if (ImageBase == 0) {
> >> +        //
> >> +        // Stop stack trace
> >> +        //
> >> +        break;
> >> +      }
> >> +
> >> +      //
> >> +      // Get PE/COFF image's entry point
> >> +      //
> >> +      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase,
> >> &EntryPoint);
> >> +      if (EFI_ERROR (Status)) {
> >> +        EntryPoint = NULL;
> >> +      }
> >> +
> >> +      //
> >> +      // Get file name and absolute path of PDB file
> >> +      //
> >> +      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath,
> &PdbFileName);
> >> +
> >> +      //
> >> +      // Print out image module name (if any)
> >> +      //
> >> +      if (PdbAbsoluteFilePath != NULL) {
> >> +        InternalPrintMessage (
> >> +          "%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
> >> +          PdbFileName,
> >> +          ImageBase,
> >> +          (UINTN)EntryPoint
> >> +          );
> >> +        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
> >> +      }
> >> +    }
> >> +
> >> +    //
> >> +    // Unwind the stack
> >> +    //
> >> +    Rbp = *(UINT64 *)(UINTN)Rbp;
> >> +  }
> >> +}
> >> +
> >> +/**
> >> +  Dump stack trace.
> >> +
> >> +  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
> >> +  @param[out] UnwondStacksCount  Count of unwond stack frames.
> >> +**/
> >> +STATIC
> >> +VOID
> >> +DumpStackTrace (
> >> +  IN  EFI_SYSTEM_CONTEXT   SystemContext,
> >> +  OUT INTN                 *UnwondStacksCount
> >> +  )
> >> +{
> >> +  UINT64  Rip;
> >> +  UINT64  Rbp;
> >> +  UINTN   ImageBase;
> >> +  CHAR8   *PdbFileName;
> >> +
> >> +  //
> >> +  // Set current RIP address
> >> +  //
> >> +  Rip = SystemContext.SystemContextX64->Rip;
> >> +
> >> +  //
> >> +  // Set current frame pointer address
> >> +  //
> >> +  Rbp = SystemContext.SystemContextX64->Rbp;
> >> +
> >> +  //
> >> +  // Get initial PE/COFF image base address from current RIP
> >> +  //
> >> +  ImageBase = PeCoffSearchImageBase (Rip);
> >> +  if (ImageBase == 0) {
> >> +    InternalPrintMessage ("!!!! Could not find backtrace information.
> >> !!!!");
> >> +    return;
> >> +  }
> >> +
> >> +  //
> >> +  // Get PDB file name from initial PE/COFF image
> >> +  //
> >> +  GetPdbFileName (ImageBase, NULL, &PdbFileName);
> >> +
> >> +  //
> >> +  // Initialize count of unwond stacks
> >> +  //
> >> +  *UnwondStacksCount = 1;
> >> +
> >> +  //
> >> +  // Print out back trace
> >> +  //
> >> +  InternalPrintMessage ("\nCall trace:\n");
> >> +
> >> +  for (;;) {
> >> +    //
> >> +    // Print stack frame in the following format:
> >> +    //
> >> +    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName>
> | ????]
> >> +    //
> >> +    InternalPrintMessage (
> >> +      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
> >> +      *UnwondStacksCount - 1,
> >> +      Rip,
> >> +      ImageBase,
> >> +      Rip - ImageBase - 1,
> >> +      Rbp,
> >> +      PdbFileName
> >> +      );
> >> +
> >> +    //
> >> +    // Set RIP with return address from current stack frame
> >> +    //
> >> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
> >> +
> >> +    //
> >> +    // If RIP is zero, then stop unwinding the stack
> >> +    //
> >> +    if (Rip == 0) {
> >> +      break;
> >> +    }
> >> +
> >> +    //
> >> +    // Check if RIP is within another PE/COFF image base address
> >> +    //
> >> +    if (Rip < ImageBase) {
> >> +      //
> >> +      // Search for the respective PE/COFF image based on RIP
> >> +      //
> >> +      ImageBase = PeCoffSearchImageBase (Rip);
> >> +      if (ImageBase == 0) {
> >> +        //
> >> +        // Stop stack trace
> >> +        //
> >> +        break;
> >> +      }
> >> +
> >> +      //
> >> +      // Get PDB file name
> >> +      //
> >> +      GetPdbFileName (ImageBase, NULL, &PdbFileName);
> >> +    }
> >> +
> >> +    //
> >> +    // Unwind the stack
> >> +    //
> >> +    Rbp = *(UINT64 *)(UINTN)Rbp;
> >> +
> >> +    //
> >> +    // Increment count of unwond stacks
> >> +    //
> >> +    (*UnwondStacksCount)++;
> >> +  }
> >> +}
> >> +
> >>  /**
> >>    Display CPU information.
> >>
> >> @@ -254,9 +603,25 @@ DumpImageAndCpuContent (
> >>    IN EFI_SYSTEM_CONTEXT   SystemContext
> >>    )
> >>  {
> >> +  INTN UnwondStacksCount;
> >> +
> >> +  //
> >> +  // Dump CPU context
> >> +  //
> >>    DumpCpuContext (ExceptionType, SystemContext);
> >> +
> >> +  //
> >> +  // Dump stack trace
> >> +  //
> >> +  DumpStackTrace (SystemContext, &UnwondStacksCount);
> >> +
> >> +  //
> >> +  // Dump image module names
> >> +  //
> >> +  DumpImageModuleNames (SystemContext);
> >> +
> >>    //
> >> -  // Dump module image base and module entry point by RIP
> >> +  // Dump stack contents
> >>    //
> >> -  DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
> >> +  DumpStackContents (SystemContext.SystemContextX64->Rsp,
> >> UnwondStacksCount);
> >>  }
> >> --
> >> 2.14.3
> >>
> >> _______________________________________________
> >> edk2-devel mailing list
> >> edk2-devel@lists.01.org
> >> https://lists.01.org/mailman/listinfo/edk2-devel
> >
> >
> 
> 
> --
> Paulo Alcantara, HP Inc.
> Speaking for myself only.


^ permalink raw reply	[flat|nested] 82+ messages in thread

* 答复: [RFC v3 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
  2017-11-16 21:56     ` [RFC v3 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support Paulo Alcantara
@ 2017-11-17  7:24       ` Fan Jeff
  2017-11-20 14:59         ` Paulo Alcantara
  0 siblings, 1 reply; 82+ messages in thread
From: Fan Jeff @ 2017-11-17  7:24 UTC (permalink / raw)
  To: Paulo Alcantara, edk2-devel@lists.01.org; +Cc: Laszlo Ersek, Eric Dong

Paulo,


I don't understand why you - 1 when calculating EIP offset in image, it confused me.


+  for (;;) {
+    //
+    // Print stack frame in the following format:
+    //
+    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName> | ????]
+    //
+    InternalPrintMessage (
+      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
+      *UnwoundStacksCount - 1,
+      Rip,
+      ImageBase,
+      Rip - ImageBase - 1,   // ????
+      Rbp,
+      PdbFileName
+      );
+

Jeff




________________________________
发件人: edk2-devel <edk2-devel-bounces@lists.01.org> 代表 Paulo Alcantara <pcacjr@zytor.com>
发送时间: 2017年11月17日 5:56
收件人: edk2-devel@lists.01.org
抄送: Laszlo Ersek; Eric Dong
主题: [edk2] [RFC v3 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support

This patch adds stack trace support during a X64 CPU exception.

It will dump out back trace, stack contents as well as image module
names that were part of the call stack.

Contributed-under: TianoCore Contribution Agreement 1.1
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 376 +++++++++++++++++++-
 1 file changed, 374 insertions(+), 2 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
index 65f0cff680..fe776ccc2d 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
@@ -14,6 +14,11 @@

 #include "CpuExceptionCommon.h"

+//
+// Unknown PDB file name
+//
+GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
+
 /**
   Return address map of exception handler template so that C code can generate
   exception tables.
@@ -242,6 +247,357 @@ DumpCpuContext (
     );
 }

+/**
+  Get absolute path and file name of PDB file in PE/COFF image.
+
+  @param[in]  ImageBase            Base address of PE/COFF image.
+  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
+  @param[out] PdbFileName          File name of PDB file.
+**/
+STATIC
+VOID
+GetPdbFileName (
+  IN  UINTN    ImageBase,
+  OUT CHAR8    **PdbAbsoluteFilePath,
+  OUT CHAR8    **PdbFileName
+  )
+{
+  VOID   *PdbPointer;
+  CHAR8  *Str;
+
+  //
+  // Get PDB file name from PE/COFF image
+  //
+  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
+  if (PdbPointer == NULL) {
+    //
+    // No PDB file name found. Set it to an unknown file name.
+    //
+    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
+    if (PdbAbsoluteFilePath != NULL) {
+      *PdbAbsoluteFilePath = NULL;
+    }
+  } else {
+    //
+    // Get file name portion out of PDB file in PE/COFF image
+    //
+    Str = (CHAR8 *)((UINTN)PdbPointer +
+                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
+    for (; *Str != '/' && *Str != '\\'; Str--) {
+      ;
+    }
+
+    //
+    // Set PDB file name (also skip trailing path separator: '/' or '\\')
+    //
+    *PdbFileName = Str + 1;
+
+    if (PdbAbsoluteFilePath != NULL) {
+      //
+      // Set absolute file path of PDB file
+      //
+      *PdbAbsoluteFilePath = PdbPointer;
+    }
+  }
+}
+
+/**
+  Dump stack contents.
+
+  @param[in]  CurrentRsp         Current stack pointer address.
+  @param[in]  UnwoundStacksCount  Count of unwound stack frames.
+**/
+STATIC
+VOID
+DumpStackContents (
+  IN UINT64  CurrentRsp,
+  IN INTN    UnwoundStacksCount
+  )
+{
+  //
+  // Check for proper stack pointer alignment
+  //
+  if (((UINTN)CurrentRsp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
+    InternalPrintMessage ("!!!! Unaligned stack pointer. !!!!\n");
+    return;
+  }
+
+  //
+  // Dump out stack contents
+  //
+  InternalPrintMessage ("\nStack dump:\n");
+  while (UnwoundStacksCount-- > 0) {
+    InternalPrintMessage (
+      "0x%016lx: %016lx %016lx\n",
+      CurrentRsp,
+      *(UINT64 *)CurrentRsp,
+      *(UINT64 *)((UINTN)CurrentRsp + 8)
+      );
+
+    //
+    // Point to next stack
+    //
+    CurrentRsp += CPU_STACK_ALIGNMENT;
+  }
+}
+
+/**
+  Dump all image module names from call stack.
+
+  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+**/
+STATIC
+VOID
+DumpImageModuleNames (
+  IN EFI_SYSTEM_CONTEXT   SystemContext
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Rip;
+  UINTN       ImageBase;
+  VOID        *EntryPoint;
+  CHAR8       *PdbAbsoluteFilePath;
+  CHAR8       *PdbFileName;
+  UINT64      Rbp;
+  UINTN       LastImageBase;
+
+  //
+  // Set current RIP address
+  //
+  Rip = SystemContext.SystemContextX64->Rip;
+
+  //
+  // Set current frame pointer address
+  //
+  Rbp = SystemContext.SystemContextX64->Rbp;
+
+  //
+  // Check for proper frame pointer alignment
+  //
+  if (((UINTN)Rbp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
+    InternalPrintMessage ("!!!! Unaligned frame pointer. !!!!\n");
+    return;
+  }
+
+  //
+  // Get initial PE/COFF image base address from current RIP
+  //
+  ImageBase = PeCoffSearchImageBase (Rip);
+  if (ImageBase == 0) {
+    InternalPrintMessage ("!!!! Could not find image module names. !!!!");
+    return;
+  }
+
+  //
+  // Set last PE/COFF image base address
+  //
+  LastImageBase = ImageBase;
+
+  //
+  // Get initial PE/COFF image's entry point
+  //
+  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
+  if (EFI_ERROR (Status)) {
+    EntryPoint = NULL;
+  }
+
+  //
+  // Get file name and absolute path of initial PDB file
+  //
+  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
+
+  //
+  // Print out initial image module name (if any)
+  //
+  if (PdbAbsoluteFilePath != NULL) {
+    InternalPrintMessage (
+      "\n%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
+      PdbFileName,
+      ImageBase,
+      (UINTN)EntryPoint
+      );
+    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
+  }
+
+  //
+  // Walk through call stack and find next module names
+  //
+  for (;;) {
+    //
+    // Set RIP with return address from current stack frame
+    //
+    Rip = *(UINT64 *)((UINTN)Rbp + 8);
+
+    //
+    // If RIP is zero, then stop unwinding the stack
+    //
+    if (Rip == 0) {
+      break;
+    }
+
+    //
+    // Search for the respective PE/COFF image based on RIP
+    //
+    ImageBase = PeCoffSearchImageBase (Rip);
+    if (ImageBase == 0) {
+      //
+      // Stop stack trace
+      //
+      break;
+    }
+
+    //
+    // If RIP points to another PE/COFF image, then find its respective PDB file
+    // name.
+    //
+    if (LastImageBase != ImageBase) {
+      //
+      // Get PE/COFF image's entry point
+      //
+      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
+      if (EFI_ERROR (Status)) {
+        EntryPoint = NULL;
+      }
+
+      //
+      // Get file name and absolute path of PDB file
+      //
+      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
+
+      //
+      // Print out image module name (if any)
+      //
+      if (PdbAbsoluteFilePath != NULL) {
+        InternalPrintMessage (
+          "%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
+          PdbFileName,
+          ImageBase,
+          (UINTN)EntryPoint
+          );
+        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
+      }
+
+      //
+      // Save last PE/COFF image base address
+      //
+      LastImageBase = ImageBase;
+    }
+
+    //
+    // Unwind the stack
+    //
+    Rbp = *(UINT64 *)(UINTN)Rbp;
+  }
+}
+
+/**
+  Dump stack trace.
+
+  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
+  @param[out] UnwoundStacksCount  Count of unwound stack frames.
+**/
+STATIC
+VOID
+DumpStackTrace (
+  IN  EFI_SYSTEM_CONTEXT   SystemContext,
+  OUT INTN                 *UnwoundStacksCount
+  )
+{
+  UINT64  Rip;
+  UINT64  Rbp;
+  UINTN   ImageBase;
+  CHAR8   *PdbFileName;
+
+  //
+  // Set current RIP address
+  //
+  Rip = SystemContext.SystemContextX64->Rip;
+
+  //
+  // Set current frame pointer address
+  //
+  Rbp = SystemContext.SystemContextX64->Rbp;
+
+  //
+  // Get initial PE/COFF image base address from current RIP
+  //
+  ImageBase = PeCoffSearchImageBase (Rip);
+  if (ImageBase == 0) {
+    InternalPrintMessage ("!!!! Could not find backtrace information. !!!!");
+    return;
+  }
+
+  //
+  // Get PDB file name from initial PE/COFF image
+  //
+  GetPdbFileName (ImageBase, NULL, &PdbFileName);
+
+  //
+  // Initialize count of unwound stacks
+  //
+  *UnwoundStacksCount = 1;
+
+  //
+  // Print out back trace
+  //
+  InternalPrintMessage ("\nCall trace:\n");
+
+  for (;;) {
+    //
+    // Print stack frame in the following format:
+    //
+    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName> | ????]
+    //
+    InternalPrintMessage (
+      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
+      *UnwoundStacksCount - 1,
+      Rip,
+      ImageBase,
+      Rip - ImageBase - 1,
+      Rbp,
+      PdbFileName
+      );
+
+    //
+    // Set RIP with return address from current stack frame
+    //
+    Rip = *(UINT64 *)((UINTN)Rbp + 8);
+
+    //
+    // If RIP is zero, then stop unwinding the stack
+    //
+    if (Rip == 0) {
+      break;
+    }
+
+    //
+    // Search for the respective PE/COFF image based on RIP
+    //
+    ImageBase = PeCoffSearchImageBase (Rip);
+    if (ImageBase == 0) {
+      //
+      // Stop stack trace
+      //
+      break;
+    }
+
+    //
+    // Get PDB file name
+    //
+    GetPdbFileName (ImageBase, NULL, &PdbFileName);
+
+    //
+    // Unwind the stack
+    //
+    Rbp = *(UINT64 *)(UINTN)Rbp;
+
+    //
+    // Increment count of unwound stacks
+    //
+    (*UnwoundStacksCount)++;
+  }
+}
+
 /**
   Display CPU information.

@@ -254,9 +610,25 @@ DumpImageAndCpuContent (
   IN EFI_SYSTEM_CONTEXT   SystemContext
   )
 {
+  INTN UnwoundStacksCount;
+
+  //
+  // Dump CPU context
+  //
   DumpCpuContext (ExceptionType, SystemContext);
+
+  //
+  // Dump stack trace
+  //
+  DumpStackTrace (SystemContext, &UnwoundStacksCount);
+
+  //
+  // Dump image module names
+  //
+  DumpImageModuleNames (SystemContext);
+
   //
-  // Dump module image base and module entry point by RIP
+  // Dump stack contents
   //
-  DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
+  DumpStackContents (SystemContext.SystemContextX64->Rsp, UnwoundStacksCount);
 }
--
2.14.3

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel

edk2-devel Info Page - 01.org<https://lists.01.org/mailman/listinfo/edk2-devel>
lists.01.org
Your email address: Your name (optional): You may enter a privacy password below. This provides only mild security, but should prevent others from messing ...




^ permalink raw reply related	[flat|nested] 82+ messages in thread

* Re: [RFC v2 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
  2017-11-17  3:43         ` Yao, Jiewen
@ 2017-11-20 14:51           ` Paulo Alcantara
  0 siblings, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2017-11-20 14:51 UTC (permalink / raw)
  To: Yao, Jiewen; +Cc: edk2-devel@lists.01.org, Laszlo Ersek, Dong, Eric

Hi Jiewen,

(sorry for the late response)

On 11/17/2017 1:43 AM, Yao, Jiewen wrote:
> Thanks for your reply. Comments below:
> 
>> -----Original Message-----
>> From: Paulo Alcantara [mailto:pcacjr@zytor.com]
>> Sent: Friday, November 17, 2017 6:13 AM
>> To: Yao, Jiewen <jiewen.yao@intel.com>
>> Cc: Paulo Alcantara <pcacjr@zytor.com>; edk2-devel@lists.01.org; Laszlo Ersek
>> <lersek@redhat.com>; Dong, Eric <eric.dong@intel.com>
>> Subject: RE: [edk2] [RFC v2 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add
>> stack trace support
>>
>> Hi Jiewen,
>>
>> On Wed, November 15, 2017 11:57 pm, Yao, Jiewen wrote:
>>> Hi Paulo
>>> Thanks to bring this cool feature.
>>>
>>> I have same feeling as Jeff Fan. It is great!
>>>
>>> I have some questions, hope you can clarify.
>>> 1) Would you please let us know which tool change is validated? Such as
>>> MSVC, or GCC?
>>
>> GCC only. This should not work on MSVC because it relies on frame pointers.
> 
> [Jiewen] Not work is OK.
> But my question is more about: Have you tested? Or have you tried?
> Do you see basic exception information can be dumped without any problem for MSVC?
> Or do you see system crash immediately for MSVC?

No, I haven't tried it. I built it with VS2015 and just run 'objdump -x 
-D' in an arbitrary .efi to check whether RBP was being used in 
function's prologues. They weren't and then I didn't even give it try. 
I'll test it though and then let you know its behavior.

> 
> 
> 
>>> 2) Would you please let us know which phase is validated? Such as PEI
>>> PreMemory, PEI PostMemory, DXE, SMM?
>>
>> DXE. I'm currently testing it by placing a random "CpuBreakpoint ()" in
>> PartitionDxe driver.
>>
> 
> [Jiewen] Ummm.
> Based upon the fact that the code you update may be used in all those phases.
> I recommend to validate them in the final patch.

Sure.

> 
> 
>>> 3) Would you please let us know if there is any special condition
>>> validated? Such as code is in an interrupt handler, SMM initialization
>>> code, thunk code during mode switch, etc.
>>> I ask this because I have seen lots of discussion on sanity check, to
>>> avoid double exception.
>>
>> At the moment I'm only ensuring proper aligned RSP and ESP values. But we
>> still need to validate the RIP values, etc. as Andrew and Brian suggested.
> 
> [Jiewen] OK. I look forward to seeing your patch to validate more.
> Same as #2. I suggest you validate all those corner cases in the final patch.

I will for sure.

> 
>>> 4) We supported some security feature to create a hole in normal memory
>>> map, such as PageGuard, StackGuard, SMM communication protection, etc.
>>> Accessing those memory directly will cause #PF immediately.
>>> Would you please let us know how we detect that case, to avoid RIP or RSP
>>> fail to the non-present page?
>>
>> Sorry. I have no idea :-( I'd hope to get some help from you guys.
> 
> 
> [Jiewen] One possible solution is to scan page table, before access this address, to make sure you are accessing present page.
> You can also filer invalid address directly by checking CPUID physical address bit before that. The invalid address will cause #GP instead of #PF.

Oh, looks like a great solution. I'll try them out and let you know if 
it worked. Thank you!!

> 
> All I all, I would like to clarify the goal for exception lib: whatever exception condition the BIOS meets, the exception handler can always dump the *basic* exception information and halt.
> The missing of advanced symbol info is acceptable. But system crash, when we want to dump the advanced info, is not the best choice.

You're right. I'll make sure to validate it as much as possible and 
avoid such issues. Please, give me some more time because currently I've 
got pretty busy with something else.

I really appreciate your help. Thanks!

Paulo

> 
> 
> 
>>> 5) May I know why we check RIP < ImageBase? Is that legal or illegal if
>>> RIP > ImageBase+ImageSize, but RIP in another PE/COFF image?
>>
>> That check was a wrong assumption that I had in the beginning. RIP may
>> point to either lower or higher addresses where the other PE/COFF images
>> are located. Fixed it in v3.
> 
> 
> [Jiewen] Great. Thanks.
> 
> 
>> Sorry for the delay in the response. I'm only able to work on this in my
>> free time.
>>
>> Thanks!
>> Paulo
>>
>>>> +    //
>>>> +    // Check if RIP is within another PE/COFF image base address
>>>> +    //
>>>> +    if (Rip < ImageBase) {
>>>> +      //
>>>> +      // Search for the respective PE/COFF image based on RIP
>>>> +      //
>>>
>>> Thank you
>>> Yao Jiewen
>>>
>>>
>>>> -----Original Message-----
>>>> From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of
>>>> Paulo
>>>> Alcantara
>>>> Sent: Thursday, November 16, 2017 9:18 AM
>>>> To: edk2-devel@lists.01.org
>>>> Cc: Laszlo Ersek <lersek@redhat.com>; Dong, Eric <eric.dong@intel.com>
>>>> Subject: [edk2] [RFC v2 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add
>>>> stack
>>>> trace support
>>>>
>>>> This patch adds stack trace support during a X64 CPU exception.
>>>>
>>>> It will dump out back trace, stack contents as well as image module
>>>> names that were part of the call stack.
>>>>
>>>> Contributed-under: TianoCore Contribution Agreement 1.1
>>>> Cc: Eric Dong <eric.dong@intel.com>
>>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>>> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
>>>> ---
>>>>   UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c |
>>>> 369
>>>> +++++++++++++++++++-
>>>>   1 file changed, 367 insertions(+), 2 deletions(-)
>>>>
>>>> diff --git
>>>> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
>>>> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
>>>> index 65f0cff680..11cd7c9e1c 100644
>>>> ---
>>>> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
>>>> +++
>>>> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
>>>> @@ -14,6 +14,11 @@
>>>>
>>>>   #include "CpuExceptionCommon.h"
>>>>
>>>> +//
>>>> +// Unknown PDB file name
>>>> +//
>>>> +GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8
>>>> *mUnknownPdbFileName = "????";
>>>> +
>>>>   /**
>>>>     Return address map of exception handler template so that C code can
>>>> generate
>>>>     exception tables.
>>>> @@ -242,6 +247,350 @@ DumpCpuContext (
>>>>       );
>>>>   }
>>>>
>>>> +/**
>>>> +  Get absolute path and file name of PDB file in PE/COFF image.
>>>> +
>>>> +  @param[in]  ImageBase            Base address of PE/COFF image.
>>>> +  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
>>>> +  @param[out] PdbFileName          File name of PDB file.
>>>> +**/
>>>> +STATIC
>>>> +VOID
>>>> +GetPdbFileName (
>>>> +  IN  UINTN    ImageBase,
>>>> +  OUT CHAR8    **PdbAbsoluteFilePath,
>>>> +  OUT CHAR8    **PdbFileName
>>>> +  )
>>>> +{
>>>> +  VOID   *PdbPointer;
>>>> +  CHAR8  *Str;
>>>> +
>>>> +  //
>>>> +  // Get PDB file name from PE/COFF image
>>>> +  //
>>>> +  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
>>>> +  if (PdbPointer == NULL) {
>>>> +    //
>>>> +    // No PDB file name found. Set it to an unknown file name.
>>>> +    //
>>>> +    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
>>>> +    if (PdbAbsoluteFilePath != NULL) {
>>>> +      *PdbAbsoluteFilePath = NULL;
>>>> +    }
>>>> +  } else {
>>>> +    //
>>>> +    // Get file name portion out of PDB file in PE/COFF image
>>>> +    //
>>>> +    Str = (CHAR8 *)((UINTN)PdbPointer +
>>>> +                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
>>>> +    for (; *Str != '/' && *Str != '\\'; Str--) {
>>>> +      ;
>>>> +    }
>>>> +
>>>> +    //
>>>> +    // Set PDB file name (also skip trailing path separator: '/' or
>>>> '\\')
>>>> +    //
>>>> +    *PdbFileName = Str + 1;
>>>> +
>>>> +    if (PdbAbsoluteFilePath != NULL) {
>>>> +      //
>>>> +      // Set absolute file path of PDB file
>>>> +      //
>>>> +      *PdbAbsoluteFilePath = PdbPointer;
>>>> +    }
>>>> +  }
>>>> +}
>>>> +
>>>> +/**
>>>> +  Dump stack contents.
>>>> +
>>>> +  @param[in]  CurrentRsp         Current stack pointer address.
>>>> +  @param[in]  UnwondStacksCount  Count of unwond stack frames.
>>>> +**/
>>>> +STATIC
>>>> +VOID
>>>> +DumpStackContents (
>>>> +  IN UINT64  CurrentRsp,
>>>> +  IN INTN    UnwondStacksCount
>>>> +  )
>>>> +{
>>>> +  //
>>>> +  // Check for proper stack pointer alignment
>>>> +  //
>>>> +  if (((UINTN)CurrentRsp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
>>>> +    InternalPrintMessage ("!!!! Unaligned stack pointer. !!!!\n");
>>>> +    return;
>>>> +  }
>>>> +
>>>> +  //
>>>> +  // Dump out stack contents
>>>> +  //
>>>> +  InternalPrintMessage ("\nStack dump:\n");
>>>> +  while (UnwondStacksCount-- > 0) {
>>>> +    InternalPrintMessage (
>>>> +      "0x%016lx: %016lx %016lx\n",
>>>> +      CurrentRsp,
>>>> +      *(UINT64 *)CurrentRsp,
>>>> +      *(UINT64 *)((UINTN)CurrentRsp + 8)
>>>> +      );
>>>> +
>>>> +    //
>>>> +    // Point to next stack
>>>> +    //
>>>> +    CurrentRsp += CPU_STACK_ALIGNMENT;
>>>> +  }
>>>> +}
>>>> +
>>>> +/**
>>>> +  Dump all image module names from call stack.
>>>> +
>>>> +  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
>>>> +**/
>>>> +STATIC
>>>> +VOID
>>>> +DumpImageModuleNames (
>>>> +  IN EFI_SYSTEM_CONTEXT   SystemContext
>>>> +  )
>>>> +{
>>>> +  EFI_STATUS  Status;
>>>> +  UINT64      Rip;
>>>> +  UINTN       ImageBase;
>>>> +  VOID        *EntryPoint;
>>>> +  CHAR8       *PdbAbsoluteFilePath;
>>>> +  CHAR8       *PdbFileName;
>>>> +  UINT64      Rbp;
>>>> +
>>>> +  //
>>>> +  // Set current RIP address
>>>> +  //
>>>> +  Rip = SystemContext.SystemContextX64->Rip;
>>>> +
>>>> +  //
>>>> +  // Set current frame pointer address
>>>> +  //
>>>> +  Rbp = SystemContext.SystemContextX64->Rbp;
>>>> +
>>>> +  //
>>>> +  // Check for proper frame pointer alignment
>>>> +  //
>>>> +  if (((UINTN)Rbp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
>>>> +    InternalPrintMessage ("!!!! Unaligned frame pointer. !!!!\n");
>>>> +    return;
>>>> +  }
>>>> +
>>>> +  //
>>>> +  // Get initial PE/COFF image base address from current RIP
>>>> +  //
>>>> +  ImageBase = PeCoffSearchImageBase (Rip);
>>>> +  if (ImageBase == 0) {
>>>> +    InternalPrintMessage ("!!!! Could not find image module names.
>>>> !!!!");
>>>> +    return;
>>>> +  }
>>>> +
>>>> +  //
>>>> +  // Get initial PE/COFF image's entry point
>>>> +  //
>>>> +  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
>>>> +  if (EFI_ERROR (Status)) {
>>>> +    EntryPoint = NULL;
>>>> +  }
>>>> +
>>>> +  //
>>>> +  // Get file name and absolute path of initial PDB file
>>>> +  //
>>>> +  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
>>>> +
>>>> +  //
>>>> +  // Print out initial image module name (if any)
>>>> +  //
>>>> +  if (PdbAbsoluteFilePath != NULL) {
>>>> +    InternalPrintMessage (
>>>> +      "\n%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
>>>> +      PdbFileName,
>>>> +      ImageBase,
>>>> +      (UINTN)EntryPoint
>>>> +      );
>>>> +    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
>>>> +  }
>>>> +
>>>> +  //
>>>> +  // Walk through call stack and find next module names
>>>> +  //
>>>> +  for (;;) {
>>>> +    //
>>>> +    // Set RIP with return address from current stack frame
>>>> +    //
>>>> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
>>>> +
>>>> +    //
>>>> +    // If RIP is zero, then stop unwinding the stack
>>>> +    //
>>>> +    if (Rip == 0) {
>>>> +      break;
>>>> +    }
>>>> +
>>>> +    //
>>>> +    // Check if RIP is within another PE/COFF image base address
>>>> +    //
>>>> +    if (Rip < ImageBase) {
>>>> +      //
>>>> +      // Search for the respective PE/COFF image based on RIP
>>>> +      //
>>>> +      ImageBase = PeCoffSearchImageBase (Rip);
>>>> +      if (ImageBase == 0) {
>>>> +        //
>>>> +        // Stop stack trace
>>>> +        //
>>>> +        break;
>>>> +      }
>>>> +
>>>> +      //
>>>> +      // Get PE/COFF image's entry point
>>>> +      //
>>>> +      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase,
>>>> &EntryPoint);
>>>> +      if (EFI_ERROR (Status)) {
>>>> +        EntryPoint = NULL;
>>>> +      }
>>>> +
>>>> +      //
>>>> +      // Get file name and absolute path of PDB file
>>>> +      //
>>>> +      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath,
>> &PdbFileName);
>>>> +
>>>> +      //
>>>> +      // Print out image module name (if any)
>>>> +      //
>>>> +      if (PdbAbsoluteFilePath != NULL) {
>>>> +        InternalPrintMessage (
>>>> +          "%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
>>>> +          PdbFileName,
>>>> +          ImageBase,
>>>> +          (UINTN)EntryPoint
>>>> +          );
>>>> +        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
>>>> +      }
>>>> +    }
>>>> +
>>>> +    //
>>>> +    // Unwind the stack
>>>> +    //
>>>> +    Rbp = *(UINT64 *)(UINTN)Rbp;
>>>> +  }
>>>> +}
>>>> +
>>>> +/**
>>>> +  Dump stack trace.
>>>> +
>>>> +  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
>>>> +  @param[out] UnwondStacksCount  Count of unwond stack frames.
>>>> +**/
>>>> +STATIC
>>>> +VOID
>>>> +DumpStackTrace (
>>>> +  IN  EFI_SYSTEM_CONTEXT   SystemContext,
>>>> +  OUT INTN                 *UnwondStacksCount
>>>> +  )
>>>> +{
>>>> +  UINT64  Rip;
>>>> +  UINT64  Rbp;
>>>> +  UINTN   ImageBase;
>>>> +  CHAR8   *PdbFileName;
>>>> +
>>>> +  //
>>>> +  // Set current RIP address
>>>> +  //
>>>> +  Rip = SystemContext.SystemContextX64->Rip;
>>>> +
>>>> +  //
>>>> +  // Set current frame pointer address
>>>> +  //
>>>> +  Rbp = SystemContext.SystemContextX64->Rbp;
>>>> +
>>>> +  //
>>>> +  // Get initial PE/COFF image base address from current RIP
>>>> +  //
>>>> +  ImageBase = PeCoffSearchImageBase (Rip);
>>>> +  if (ImageBase == 0) {
>>>> +    InternalPrintMessage ("!!!! Could not find backtrace information.
>>>> !!!!");
>>>> +    return;
>>>> +  }
>>>> +
>>>> +  //
>>>> +  // Get PDB file name from initial PE/COFF image
>>>> +  //
>>>> +  GetPdbFileName (ImageBase, NULL, &PdbFileName);
>>>> +
>>>> +  //
>>>> +  // Initialize count of unwond stacks
>>>> +  //
>>>> +  *UnwondStacksCount = 1;
>>>> +
>>>> +  //
>>>> +  // Print out back trace
>>>> +  //
>>>> +  InternalPrintMessage ("\nCall trace:\n");
>>>> +
>>>> +  for (;;) {
>>>> +    //
>>>> +    // Print stack frame in the following format:
>>>> +    //
>>>> +    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName>
>> | ????]
>>>> +    //
>>>> +    InternalPrintMessage (
>>>> +      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
>>>> +      *UnwondStacksCount - 1,
>>>> +      Rip,
>>>> +      ImageBase,
>>>> +      Rip - ImageBase - 1,
>>>> +      Rbp,
>>>> +      PdbFileName
>>>> +      );
>>>> +
>>>> +    //
>>>> +    // Set RIP with return address from current stack frame
>>>> +    //
>>>> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
>>>> +
>>>> +    //
>>>> +    // If RIP is zero, then stop unwinding the stack
>>>> +    //
>>>> +    if (Rip == 0) {
>>>> +      break;
>>>> +    }
>>>> +
>>>> +    //
>>>> +    // Check if RIP is within another PE/COFF image base address
>>>> +    //
>>>> +    if (Rip < ImageBase) {
>>>> +      //
>>>> +      // Search for the respective PE/COFF image based on RIP
>>>> +      //
>>>> +      ImageBase = PeCoffSearchImageBase (Rip);
>>>> +      if (ImageBase == 0) {
>>>> +        //
>>>> +        // Stop stack trace
>>>> +        //
>>>> +        break;
>>>> +      }
>>>> +
>>>> +      //
>>>> +      // Get PDB file name
>>>> +      //
>>>> +      GetPdbFileName (ImageBase, NULL, &PdbFileName);
>>>> +    }
>>>> +
>>>> +    //
>>>> +    // Unwind the stack
>>>> +    //
>>>> +    Rbp = *(UINT64 *)(UINTN)Rbp;
>>>> +
>>>> +    //
>>>> +    // Increment count of unwond stacks
>>>> +    //
>>>> +    (*UnwondStacksCount)++;
>>>> +  }
>>>> +}
>>>> +
>>>>   /**
>>>>     Display CPU information.
>>>>
>>>> @@ -254,9 +603,25 @@ DumpImageAndCpuContent (
>>>>     IN EFI_SYSTEM_CONTEXT   SystemContext
>>>>     )
>>>>   {
>>>> +  INTN UnwondStacksCount;
>>>> +
>>>> +  //
>>>> +  // Dump CPU context
>>>> +  //
>>>>     DumpCpuContext (ExceptionType, SystemContext);
>>>> +
>>>> +  //
>>>> +  // Dump stack trace
>>>> +  //
>>>> +  DumpStackTrace (SystemContext, &UnwondStacksCount);
>>>> +
>>>> +  //
>>>> +  // Dump image module names
>>>> +  //
>>>> +  DumpImageModuleNames (SystemContext);
>>>> +
>>>>     //
>>>> -  // Dump module image base and module entry point by RIP
>>>> +  // Dump stack contents
>>>>     //
>>>> -  DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
>>>> +  DumpStackContents (SystemContext.SystemContextX64->Rsp,
>>>> UnwondStacksCount);
>>>>   }
>>>> --
>>>> 2.14.3
>>>>
>>>> _______________________________________________
>>>> edk2-devel mailing list
>>>> edk2-devel@lists.01.org
>>>> https://lists.01.org/mailman/listinfo/edk2-devel
>>>
>>>
>>
>>
>> --
>> Paulo Alcantara, HP Inc.
>> Speaking for myself only.
> 


^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: 答复: [RFC v3 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
  2017-11-17  7:24       ` 答复: " Fan Jeff
@ 2017-11-20 14:59         ` Paulo Alcantara
  2017-11-23 14:27           ` 答复: " Fan Jeff
  0 siblings, 1 reply; 82+ messages in thread
From: Paulo Alcantara @ 2017-11-20 14:59 UTC (permalink / raw)
  To: Fan Jeff, edk2-devel@lists.01.org; +Cc: Laszlo Ersek, Eric Dong

Hi Jeff,

(sorry for the late response)

On 11/17/2017 5:24 AM, Fan Jeff wrote:
> Paulo,
> 
> 
> I don't understand why you - 1 when calculating EIP offset in image, it 
> confused me.

That's an offset relative to the PE/COFF image base: 0 - (ImageBase + 
ImageBaseSize - 1)

Doesn't that look right to you?

Thanks
Paulo

> 
> 
> +  for (;;) {
> +    //
> +    // Print stack frame in the following format:
> +    //
> +    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName> | ????]
> +    //
> +    InternalPrintMessage (
> +      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
> +      *UnwoundStacksCount - 1,
> +      Rip,
> +      ImageBase,
> +      Rip - ImageBase - 1,   // ????
> +      Rbp,
> +      PdbFileName
> +      );
> +
> 
> Jeff
> 
> 
> 
> 
> ------------------------------------------------------------------------
> *发件人:* edk2-devel <edk2-devel-bounces@lists.01.org> 代表 Paulo 
> Alcantara <pcacjr@zytor.com>
> *发送时间:* 2017年11月17日 5:56
> *收件人:* edk2-devel@lists.01.org
> *抄送:* Laszlo Ersek; Eric Dong
> *主题:* [edk2] [RFC v3 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add 
> stack trace support
> This patch adds stack trace support during a X64 CPU exception.
> 
> It will dump out back trace, stack contents as well as image module
> names that were part of the call stack.
> 
> Contributed-under: TianoCore Contribution Agreement 1.1
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> ---
>   UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 
> 376 +++++++++++++++++++-
>   1 file changed, 374 insertions(+), 2 deletions(-)
> 
> diff --git 
> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c 
> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> index 65f0cff680..fe776ccc2d 100644
> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> @@ -14,6 +14,11 @@
> 
>   #include "CpuExceptionCommon.h"
> 
> +//
> +// Unknown PDB file name
> +//
> +GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
> +
>   /**
>     Return address map of exception handler template so that C code can 
> generate
>     exception tables.
> @@ -242,6 +247,357 @@ DumpCpuContext (
>       );
>   }
> 
> +/**
> +  Get absolute path and file name of PDB file in PE/COFF image.
> +
> +  @param[in]  ImageBase            Base address of PE/COFF image.
> +  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
> +  @param[out] PdbFileName          File name of PDB file.
> +**/
> +STATIC
> +VOID
> +GetPdbFileName (
> +  IN  UINTN    ImageBase,
> +  OUT CHAR8    **PdbAbsoluteFilePath,
> +  OUT CHAR8    **PdbFileName
> +  )
> +{
> +  VOID   *PdbPointer;
> +  CHAR8  *Str;
> +
> +  //
> +  // Get PDB file name from PE/COFF image
> +  //
> +  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
> +  if (PdbPointer == NULL) {
> +    //
> +    // No PDB file name found. Set it to an unknown file name.
> +    //
> +    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
> +    if (PdbAbsoluteFilePath != NULL) {
> +      *PdbAbsoluteFilePath = NULL;
> +    }
> +  } else {
> +    //
> +    // Get file name portion out of PDB file in PE/COFF image
> +    //
> +    Str = (CHAR8 *)((UINTN)PdbPointer +
> +                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
> +    for (; *Str != '/' && *Str != '\\'; Str--) {
> +      ;
> +    }
> +
> +    //
> +    // Set PDB file name (also skip trailing path separator: '/' or '\\')
> +    //
> +    *PdbFileName = Str + 1;
> +
> +    if (PdbAbsoluteFilePath != NULL) {
> +      //
> +      // Set absolute file path of PDB file
> +      //
> +      *PdbAbsoluteFilePath = PdbPointer;
> +    }
> +  }
> +}
> +
> +/**
> +  Dump stack contents.
> +
> +  @param[in]  CurrentRsp         Current stack pointer address.
> +  @param[in]  UnwoundStacksCount  Count of unwound stack frames.
> +**/
> +STATIC
> +VOID
> +DumpStackContents (
> +  IN UINT64  CurrentRsp,
> +  IN INTN    UnwoundStacksCount
> +  )
> +{
> +  //
> +  // Check for proper stack pointer alignment
> +  //
> +  if (((UINTN)CurrentRsp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
> +    InternalPrintMessage ("!!!! Unaligned stack pointer. !!!!\n");
> +    return;
> +  }
> +
> +  //
> +  // Dump out stack contents
> +  //
> +  InternalPrintMessage ("\nStack dump:\n");
> +  while (UnwoundStacksCount-- > 0) {
> +    InternalPrintMessage (
> +      "0x%016lx: %016lx %016lx\n",
> +      CurrentRsp,
> +      *(UINT64 *)CurrentRsp,
> +      *(UINT64 *)((UINTN)CurrentRsp + 8)
> +      );
> +
> +    //
> +    // Point to next stack
> +    //
> +    CurrentRsp += CPU_STACK_ALIGNMENT;
> +  }
> +}
> +
> +/**
> +  Dump all image module names from call stack.
> +
> +  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
> +**/
> +STATIC
> +VOID
> +DumpImageModuleNames (
> +  IN EFI_SYSTEM_CONTEXT   SystemContext
> +  )
> +{
> +  EFI_STATUS  Status;
> +  UINT64      Rip;
> +  UINTN       ImageBase;
> +  VOID        *EntryPoint;
> +  CHAR8       *PdbAbsoluteFilePath;
> +  CHAR8       *PdbFileName;
> +  UINT64      Rbp;
> +  UINTN       LastImageBase;
> +
> +  //
> +  // Set current RIP address
> +  //
> +  Rip = SystemContext.SystemContextX64->Rip;
> +
> +  //
> +  // Set current frame pointer address
> +  //
> +  Rbp = SystemContext.SystemContextX64->Rbp;
> +
> +  //
> +  // Check for proper frame pointer alignment
> +  //
> +  if (((UINTN)Rbp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
> +    InternalPrintMessage ("!!!! Unaligned frame pointer. !!!!\n");
> +    return;
> +  }
> +
> +  //
> +  // Get initial PE/COFF image base address from current RIP
> +  //
> +  ImageBase = PeCoffSearchImageBase (Rip);
> +  if (ImageBase == 0) {
> +    InternalPrintMessage ("!!!! Could not find image module names. !!!!");
> +    return;
> +  }
> +
> +  //
> +  // Set last PE/COFF image base address
> +  //
> +  LastImageBase = ImageBase;
> +
> +  //
> +  // Get initial PE/COFF image's entry point
> +  //
> +  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
> +  if (EFI_ERROR (Status)) {
> +    EntryPoint = NULL;
> +  }
> +
> +  //
> +  // Get file name and absolute path of initial PDB file
> +  //
> +  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
> +
> +  //
> +  // Print out initial image module name (if any)
> +  //
> +  if (PdbAbsoluteFilePath != NULL) {
> +    InternalPrintMessage (
> +      "\n%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
> +      PdbFileName,
> +      ImageBase,
> +      (UINTN)EntryPoint
> +      );
> +    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
> +  }
> +
> +  //
> +  // Walk through call stack and find next module names
> +  //
> +  for (;;) {
> +    //
> +    // Set RIP with return address from current stack frame
> +    //
> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
> +
> +    //
> +    // If RIP is zero, then stop unwinding the stack
> +    //
> +    if (Rip == 0) {
> +      break;
> +    }
> +
> +    //
> +    // Search for the respective PE/COFF image based on RIP
> +    //
> +    ImageBase = PeCoffSearchImageBase (Rip);
> +    if (ImageBase == 0) {
> +      //
> +      // Stop stack trace
> +      //
> +      break;
> +    }
> +
> +    //
> +    // If RIP points to another PE/COFF image, then find its respective 
> PDB file
> +    // name.
> +    //
> +    if (LastImageBase != ImageBase) {
> +      //
> +      // Get PE/COFF image's entry point
> +      //
> +      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
> +      if (EFI_ERROR (Status)) {
> +        EntryPoint = NULL;
> +      }
> +
> +      //
> +      // Get file name and absolute path of PDB file
> +      //
> +      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
> +
> +      //
> +      // Print out image module name (if any)
> +      //
> +      if (PdbAbsoluteFilePath != NULL) {
> +        InternalPrintMessage (
> +          "%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
> +          PdbFileName,
> +          ImageBase,
> +          (UINTN)EntryPoint
> +          );
> +        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
> +      }
> +
> +      //
> +      // Save last PE/COFF image base address
> +      //
> +      LastImageBase = ImageBase;
> +    }
> +
> +    //
> +    // Unwind the stack
> +    //
> +    Rbp = *(UINT64 *)(UINTN)Rbp;
> +  }
> +}
> +
> +/**
> +  Dump stack trace.
> +
> +  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
> +  @param[out] UnwoundStacksCount  Count of unwound stack frames.
> +**/
> +STATIC
> +VOID
> +DumpStackTrace (
> +  IN  EFI_SYSTEM_CONTEXT   SystemContext,
> +  OUT INTN                 *UnwoundStacksCount
> +  )
> +{
> +  UINT64  Rip;
> +  UINT64  Rbp;
> +  UINTN   ImageBase;
> +  CHAR8   *PdbFileName;
> +
> +  //
> +  // Set current RIP address
> +  //
> +  Rip = SystemContext.SystemContextX64->Rip;
> +
> +  //
> +  // Set current frame pointer address
> +  //
> +  Rbp = SystemContext.SystemContextX64->Rbp;
> +
> +  //
> +  // Get initial PE/COFF image base address from current RIP
> +  //
> +  ImageBase = PeCoffSearchImageBase (Rip);
> +  if (ImageBase == 0) {
> +    InternalPrintMessage ("!!!! Could not find backtrace information. 
> !!!!");
> +    return;
> +  }
> +
> +  //
> +  // Get PDB file name from initial PE/COFF image
> +  //
> +  GetPdbFileName (ImageBase, NULL, &PdbFileName);
> +
> +  //
> +  // Initialize count of unwound stacks
> +  //
> +  *UnwoundStacksCount = 1;
> +
> +  //
> +  // Print out back trace
> +  //
> +  InternalPrintMessage ("\nCall trace:\n");
> +
> +  for (;;) {
> +    //
> +    // Print stack frame in the following format:
> +    //
> +    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName> | ????]
> +    //
> +    InternalPrintMessage (
> +      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
> +      *UnwoundStacksCount - 1,
> +      Rip,
> +      ImageBase,
> +      Rip - ImageBase - 1,
> +      Rbp,
> +      PdbFileName
> +      );
> +
> +    //
> +    // Set RIP with return address from current stack frame
> +    //
> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
> +
> +    //
> +    // If RIP is zero, then stop unwinding the stack
> +    //
> +    if (Rip == 0) {
> +      break;
> +    }
> +
> +    //
> +    // Search for the respective PE/COFF image based on RIP
> +    //
> +    ImageBase = PeCoffSearchImageBase (Rip);
> +    if (ImageBase == 0) {
> +      //
> +      // Stop stack trace
> +      //
> +      break;
> +    }
> +
> +    //
> +    // Get PDB file name
> +    //
> +    GetPdbFileName (ImageBase, NULL, &PdbFileName);
> +
> +    //
> +    // Unwind the stack
> +    //
> +    Rbp = *(UINT64 *)(UINTN)Rbp;
> +
> +    //
> +    // Increment count of unwound stacks
> +    //
> +    (*UnwoundStacksCount)++;
> +  }
> +}
> +
>   /**
>     Display CPU information.
> 
> @@ -254,9 +610,25 @@ DumpImageAndCpuContent (
>     IN EFI_SYSTEM_CONTEXT   SystemContext
>     )
>   {
> +  INTN UnwoundStacksCount;
> +
> +  //
> +  // Dump CPU context
> +  //
>     DumpCpuContext (ExceptionType, SystemContext);
> +
> +  //
> +  // Dump stack trace
> +  //
> +  DumpStackTrace (SystemContext, &UnwoundStacksCount);
> +
> +  //
> +  // Dump image module names
> +  //
> +  DumpImageModuleNames (SystemContext);
> +
>     //
> -  // Dump module image base and module entry point by RIP
> +  // Dump stack contents
>     //
> -  DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
> +  DumpStackContents (SystemContext.SystemContextX64->Rsp, 
> UnwoundStacksCount);
>   }
> -- 
> 2.14.3
> 
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org
> https://lists.01.org/mailman/listinfo/edk2-devel
> 
> edk2-devel Info Page - 01.org 
> <https://lists.01.org/mailman/listinfo/edk2-devel>
> lists.01.org
> Your email address: Your name (optional): You may enter a privacy 
> password below. This provides only mild security, but should prevent 
> others from messing ...
> 
> 
> 


^ permalink raw reply	[flat|nested] 82+ messages in thread

* 答复: 答复: [RFC v3 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
  2017-11-20 14:59         ` Paulo Alcantara
@ 2017-11-23 14:27           ` Fan Jeff
  2017-11-23 18:34             ` Andrew Fish
  0 siblings, 1 reply; 82+ messages in thread
From: Fan Jeff @ 2017-11-23 14:27 UTC (permalink / raw)
  To: Paulo Alcantara, edk2-devel@lists.01.org; +Cc: Laszlo Ersek, Eric Dong

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="gb2312", Size: 14163 bytes --]

Hi Paulo,



I¡¯d like to clarify my question.

The following call trace information is abstracted from your x64 dump contents.

Call trace:

0 0x7DBCD580 @ 0x7DBCD000+0x57F (0x7EEC8DDC) in PartitionDxe.dll

1 0x7DBD41BE @ 0x7DBCD000+0x71BD (0x7EEC8DFC) in PartitionDxe.dll



I guess you used CpuBreakpoint() to do your validation.

For 0x7DBCD580, it is return address followed by ¡°int 3¡± from your case.

If we dump obj file, we would see the following asm code.

  000000007DBCD57F: CC                 int         3

  000000007DBCD580: C3                 ret

  (On case 0, the code at offset 0x57F is int 3)

  000000007DBD41B9: E8 AF 2A 00 00     call        CpuBreakpoint

  000000007DBD41BE: XX XX XX XX         XXX    XXXXXX

  (On case 1, what¡¯s the code at offset 0x71BD??)



If the upper asm code is not correct, please copy your obj file here.



If the upper asm code is correct, I think we should show the return address as below, since we cannot calculate the calling IP address on most cases. (return address ¨C 1 is not always the calling IP address on IA arch).

0 0x7DBCD580 @ 0x7DBCD000+0x580 (0x7EEC8DDC) in PartitionDxe.dll

1 0x7DBD41BE @ 0x7DBCD000+0x71BE (0x7EEC8DFC) in PartitionDxe.dll



Thanks!

Jeff



________________________________
From: Paulo Alcantara <pcacjr@zytor.com>
Sent: Monday, November 20, 2017 10:59:41 PM
To: Fan Jeff; edk2-devel@lists.01.org
Cc: Laszlo Ersek; Eric Dong
Subject: Re: ´ð¸´: [edk2] [RFC v3 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support

Hi Jeff,

(sorry for the late response)

On 11/17/2017 5:24 AM, Fan Jeff wrote:
> Paulo,
>
>
> I don't understand why you - 1 when calculating EIP offset in image, it
> confused me.

That's an offset relative to the PE/COFF image base: 0 - (ImageBase +
ImageBaseSize - 1)

Doesn't that look right to you?

Thanks
Paulo

>
>
> +  for (;;) {
> +    //
> +    // Print stack frame in the following format:
> +    //
> +    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName> | ????]
> +    //
> +    InternalPrintMessage (
> +      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
> +      *UnwoundStacksCount - 1,
> +      Rip,
> +      ImageBase,
> +      Rip - ImageBase - 1,   // ????
> +      Rbp,
> +      PdbFileName
> +      );
> +
>
> Jeff
>
>
>
>
> ------------------------------------------------------------------------
> *·¢¼þÈË:* edk2-devel <edk2-devel-bounces@lists.01.org> ´ú±í Paulo
> Alcantara <pcacjr@zytor.com>
> *·¢ËÍʱ¼ä:* 2017Äê11ÔÂ17ÈÕ 5:56
> *ÊÕ¼þÈË:* edk2-devel@lists.01.org
> *³­ËÍ:* Laszlo Ersek; Eric Dong
> *Ö÷Ìâ:* [edk2] [RFC v3 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add
> stack trace support
> This patch adds stack trace support during a X64 CPU exception.
>
> It will dump out back trace, stack contents as well as image module
> names that were part of the call stack.
>
> Contributed-under: TianoCore Contribution Agreement 1.1
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com>
> ---
>   UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c |
> 376 +++++++++++++++++++-
>   1 file changed, 374 insertions(+), 2 deletions(-)
>
> diff --git
> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> index 65f0cff680..fe776ccc2d 100644
> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> @@ -14,6 +14,11 @@
>
>   #include "CpuExceptionCommon.h"
>
> +//
> +// Unknown PDB file name
> +//
> +GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
> +
>   /**
>     Return address map of exception handler template so that C code can
> generate
>     exception tables.
> @@ -242,6 +247,357 @@ DumpCpuContext (
>       );
>   }
>
> +/**
> +  Get absolute path and file name of PDB file in PE/COFF image.
> +
> +  @param[in]  ImageBase            Base address of PE/COFF image.
> +  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
> +  @param[out] PdbFileName          File name of PDB file.
> +**/
> +STATIC
> +VOID
> +GetPdbFileName (
> +  IN  UINTN    ImageBase,
> +  OUT CHAR8    **PdbAbsoluteFilePath,
> +  OUT CHAR8    **PdbFileName
> +  )
> +{
> +  VOID   *PdbPointer;
> +  CHAR8  *Str;
> +
> +  //
> +  // Get PDB file name from PE/COFF image
> +  //
> +  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
> +  if (PdbPointer == NULL) {
> +    //
> +    // No PDB file name found. Set it to an unknown file name.
> +    //
> +    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
> +    if (PdbAbsoluteFilePath != NULL) {
> +      *PdbAbsoluteFilePath = NULL;
> +    }
> +  } else {
> +    //
> +    // Get file name portion out of PDB file in PE/COFF image
> +    //
> +    Str = (CHAR8 *)((UINTN)PdbPointer +
> +                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
> +    for (; *Str != '/' && *Str != '\\'; Str--) {
> +      ;
> +    }
> +
> +    //
> +    // Set PDB file name (also skip trailing path separator: '/' or '\\')
> +    //
> +    *PdbFileName = Str + 1;
> +
> +    if (PdbAbsoluteFilePath != NULL) {
> +      //
> +      // Set absolute file path of PDB file
> +      //
> +      *PdbAbsoluteFilePath = PdbPointer;
> +    }
> +  }
> +}
> +
> +/**
> +  Dump stack contents.
> +
> +  @param[in]  CurrentRsp         Current stack pointer address.
> +  @param[in]  UnwoundStacksCount  Count of unwound stack frames.
> +**/
> +STATIC
> +VOID
> +DumpStackContents (
> +  IN UINT64  CurrentRsp,
> +  IN INTN    UnwoundStacksCount
> +  )
> +{
> +  //
> +  // Check for proper stack pointer alignment
> +  //
> +  if (((UINTN)CurrentRsp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
> +    InternalPrintMessage ("!!!! Unaligned stack pointer. !!!!\n");
> +    return;
> +  }
> +
> +  //
> +  // Dump out stack contents
> +  //
> +  InternalPrintMessage ("\nStack dump:\n");
> +  while (UnwoundStacksCount-- > 0) {
> +    InternalPrintMessage (
> +      "0x%016lx: %016lx %016lx\n",
> +      CurrentRsp,
> +      *(UINT64 *)CurrentRsp,
> +      *(UINT64 *)((UINTN)CurrentRsp + 8)
> +      );
> +
> +    //
> +    // Point to next stack
> +    //
> +    CurrentRsp += CPU_STACK_ALIGNMENT;
> +  }
> +}
> +
> +/**
> +  Dump all image module names from call stack.
> +
> +  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
> +**/
> +STATIC
> +VOID
> +DumpImageModuleNames (
> +  IN EFI_SYSTEM_CONTEXT   SystemContext
> +  )
> +{
> +  EFI_STATUS  Status;
> +  UINT64      Rip;
> +  UINTN       ImageBase;
> +  VOID        *EntryPoint;
> +  CHAR8       *PdbAbsoluteFilePath;
> +  CHAR8       *PdbFileName;
> +  UINT64      Rbp;
> +  UINTN       LastImageBase;
> +
> +  //
> +  // Set current RIP address
> +  //
> +  Rip = SystemContext.SystemContextX64->Rip;
> +
> +  //
> +  // Set current frame pointer address
> +  //
> +  Rbp = SystemContext.SystemContextX64->Rbp;
> +
> +  //
> +  // Check for proper frame pointer alignment
> +  //
> +  if (((UINTN)Rbp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
> +    InternalPrintMessage ("!!!! Unaligned frame pointer. !!!!\n");
> +    return;
> +  }
> +
> +  //
> +  // Get initial PE/COFF image base address from current RIP
> +  //
> +  ImageBase = PeCoffSearchImageBase (Rip);
> +  if (ImageBase == 0) {
> +    InternalPrintMessage ("!!!! Could not find image module names. !!!!");
> +    return;
> +  }
> +
> +  //
> +  // Set last PE/COFF image base address
> +  //
> +  LastImageBase = ImageBase;
> +
> +  //
> +  // Get initial PE/COFF image's entry point
> +  //
> +  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
> +  if (EFI_ERROR (Status)) {
> +    EntryPoint = NULL;
> +  }
> +
> +  //
> +  // Get file name and absolute path of initial PDB file
> +  //
> +  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
> +
> +  //
> +  // Print out initial image module name (if any)
> +  //
> +  if (PdbAbsoluteFilePath != NULL) {
> +    InternalPrintMessage (
> +      "\n%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
> +      PdbFileName,
> +      ImageBase,
> +      (UINTN)EntryPoint
> +      );
> +    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
> +  }
> +
> +  //
> +  // Walk through call stack and find next module names
> +  //
> +  for (;;) {
> +    //
> +    // Set RIP with return address from current stack frame
> +    //
> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
> +
> +    //
> +    // If RIP is zero, then stop unwinding the stack
> +    //
> +    if (Rip == 0) {
> +      break;
> +    }
> +
> +    //
> +    // Search for the respective PE/COFF image based on RIP
> +    //
> +    ImageBase = PeCoffSearchImageBase (Rip);
> +    if (ImageBase == 0) {
> +      //
> +      // Stop stack trace
> +      //
> +      break;
> +    }
> +
> +    //
> +    // If RIP points to another PE/COFF image, then find its respective
> PDB file
> +    // name.
> +    //
> +    if (LastImageBase != ImageBase) {
> +      //
> +      // Get PE/COFF image's entry point
> +      //
> +      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
> +      if (EFI_ERROR (Status)) {
> +        EntryPoint = NULL;
> +      }
> +
> +      //
> +      // Get file name and absolute path of PDB file
> +      //
> +      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
> +
> +      //
> +      // Print out image module name (if any)
> +      //
> +      if (PdbAbsoluteFilePath != NULL) {
> +        InternalPrintMessage (
> +          "%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
> +          PdbFileName,
> +          ImageBase,
> +          (UINTN)EntryPoint
> +          );
> +        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
> +      }
> +
> +      //
> +      // Save last PE/COFF image base address
> +      //
> +      LastImageBase = ImageBase;
> +    }
> +
> +    //
> +    // Unwind the stack
> +    //
> +    Rbp = *(UINT64 *)(UINTN)Rbp;
> +  }
> +}
> +
> +/**
> +  Dump stack trace.
> +
> +  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
> +  @param[out] UnwoundStacksCount  Count of unwound stack frames.
> +**/
> +STATIC
> +VOID
> +DumpStackTrace (
> +  IN  EFI_SYSTEM_CONTEXT   SystemContext,
> +  OUT INTN                 *UnwoundStacksCount
> +  )
> +{
> +  UINT64  Rip;
> +  UINT64  Rbp;
> +  UINTN   ImageBase;
> +  CHAR8   *PdbFileName;
> +
> +  //
> +  // Set current RIP address
> +  //
> +  Rip = SystemContext.SystemContextX64->Rip;
> +
> +  //
> +  // Set current frame pointer address
> +  //
> +  Rbp = SystemContext.SystemContextX64->Rbp;
> +
> +  //
> +  // Get initial PE/COFF image base address from current RIP
> +  //
> +  ImageBase = PeCoffSearchImageBase (Rip);
> +  if (ImageBase == 0) {
> +    InternalPrintMessage ("!!!! Could not find backtrace information.
> !!!!");
> +    return;
> +  }
> +
> +  //
> +  // Get PDB file name from initial PE/COFF image
> +  //
> +  GetPdbFileName (ImageBase, NULL, &PdbFileName);
> +
> +  //
> +  // Initialize count of unwound stacks
> +  //
> +  *UnwoundStacksCount = 1;
> +
> +  //
> +  // Print out back trace
> +  //
> +  InternalPrintMessage ("\nCall trace:\n");
> +
> +  for (;;) {
> +    //
> +    // Print stack frame in the following format:
> +    //
> +    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName> | ????]
> +    //
> +    InternalPrintMessage (
> +      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
> +      *UnwoundStacksCount - 1,
> +      Rip,
> +      ImageBase,
> +      Rip - ImageBase - 1,
> +      Rbp,
> +      PdbFileName
> +      );
> +
> +    //
> +    // Set RIP with return address from current stack frame
> +    //
> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
> +
> +    //
> +    // If RIP is zero, then stop unwinding the stack
> +    //
> +    if (Rip == 0) {
> +      break;
> +    }
> +
> +    //
> +    // Search for the respective PE/COFF image based on RIP
> +    //
> +    ImageBase = PeCoffSearchImageBase (Rip);
> +    if (ImageBase == 0) {
> +      //
> +      // Stop stack trace
> +      //
> +      break;
> +    }
> +
> +    //
> +    // Get PDB file name
> +    //
> +    GetPdbFileName (ImageBase, NULL, &PdbFileName);
> +
> +    //
> +    // Unwind the stack
> +    //
> +    Rbp = *(UINT64 *)(UINTN)Rbp;
> +
> +    //
> +    // Increment count of unwound stacks
> +    //
> +    (*UnwoundStacksCount)++;
> +  }
> +}
> +
>   /**
>     Display CPU information.
>
> @@ -254,9 +610,25 @@ DumpImageAndCpuContent (
>     IN EFI_SYSTEM_CONTEXT   SystemContext
>     )
>   {
> +  INTN UnwoundStacksCount;
> +
> +  //
> +  // Dump CPU context
> +  //
>     DumpCpuContext (ExceptionType, SystemContext);
> +
> +  //
> +  // Dump stack trace
> +  //
> +  DumpStackTrace (SystemContext, &UnwoundStacksCount);
> +
> +  //
> +  // Dump image module names
> +  //
> +  DumpImageModuleNames (SystemContext);
> +
>     //
> -  // Dump module image base and module entry point by RIP
> +  // Dump stack contents
>     //
> -  DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
> +  DumpStackContents (SystemContext.SystemContextX64->Rsp,
> UnwoundStacksCount);
>   }
> --
> 2.14.3
>
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org
> https://lists.01.org/mailman/listinfo/edk2-devel
>
> edk2-devel Info Page - 01.org
> <https://lists.01.org/mailman/listinfo/edk2-devel>
> lists.01.org
> Your email address: Your name (optional): You may enter a privacy
> password below. This provides only mild security, but should prevent
> others from messing ...
>
>
>

^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC v3 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
  2017-11-23 14:27           ` 答复: " Fan Jeff
@ 2017-11-23 18:34             ` Andrew Fish
  2017-11-23 19:49               ` Fan Jeff
  0 siblings, 1 reply; 82+ messages in thread
From: Andrew Fish @ 2017-11-23 18:34 UTC (permalink / raw)
  To: Fan Jeff
  Cc: Paulo Alcantara, edk2-devel@lists.01.org, Laszlo Ersek, Eric Dong


> On Nov 23, 2017, at 6:27 AM, Fan Jeff <vanjeff_919@hotmail.com <mailto:vanjeff_919@hotmail.com>> wrote:
> 
> Hi Paulo,
> 
> 
> 
> I’d like to clarify my question.
> 
> The following call trace information is abstracted from your x64 dump contents.
> 
> Call trace:
> 
> 0 0x7DBCD580 @ 0x7DBCD000+0x57F (0x7EEC8DDC) in PartitionDxe.dll
> 
> 1 0x7DBD41BE @ 0x7DBCD000+0x71BD (0x7EEC8DFC) in PartitionDxe.dll
> 
> 
> 
> I guess you used CpuBreakpoint() to do your validation.
> 
> For 0x7DBCD580, it is return address followed by “int 3” from your case.
> 
> If we dump obj file, we would see the following asm code.
> 
>  000000007DBCD57F: CC                 int         3
> 
>  000000007DBCD580: C3                 ret
> 

Jeff,

Can you walk the stack without a frame pointer? I don't think the common nasm code supports that. For x86 GCC and clang use %rbp as the frame pointer. The common nasm code may be why his walk is off?

As you can see when emit frame pointer is on you get:
	pushq	%rbp
	movq	%rsp, %rbp
...
	popq	%rbp
	retq

vs. this without the frame pointer, like your example above. 
...
	retq

Without a frame pointer you need debug symbols as you don't know how deep the return address is on the stack in any given location in a function. 

Simple frame pointer example with clang:
~/work/Compiler>cat breakpoint.c
void
CpuBreakpoint (
  void
  )
{
  __asm__ __volatile__ ("int $3");
}
~/work/Compiler>clang breakpoint.c -S
~/work/Compiler>cat breakpoint.S
	.section	__TEXT,__text,regular,pure_instructions
	.macosx_version_min 10, 12
	.globl	_CpuBreakpoint
	.p2align	4, 0x90
_CpuBreakpoint:                         ## @CpuBreakpoint
	.cfi_startproc
## BB#0:
	pushq	%rbp
Lcfi0:
	.cfi_def_cfa_offset 16
Lcfi1:
	.cfi_offset %rbp, -16
	movq	%rsp, %rbp
Lcfi2:
	.cfi_def_cfa_register %rbp
	## InlineAsm Start
	int3
	## InlineAsm End
	popq	%rbp
	retq
	.cfi_endproc


.subsections_via_symbols
~/work/Compiler>

Thanks,

Andrew Fish

PS some lldb Python that walks an x86 stack frame, assuming you have a stack pointer. Given dereferencing a non-canonical addresses causes a General Protection fault it is good to error check for them if your stack walk code can not tolerate exceptions. EFI_BAD_POINTER, aka 0xAFAFAFAFAFAFAFAF, is the most common thing you hit (Thanks to Vincent Zimmer making sure EFI faults have my initials in them, not to mention the header for TE images is VZ). 

def NotCanonicalAddress(addr, start=0x00007FFFFFFFFFFF, end=0xFF800000000000000):
  return addr > start and addr < end

def print_raw_stacktrace(debugger, fp, pc, address = 0, verbose = False):
  # Do a raw stack trace
  
  stride =4  
  SBTarget  = debugger.GetSelectedTarget()
  if SBTarget:
    Triple = SBTarget.triple
    if Triple.find ("x86_64") != -1:
      stride = 8

  frame_num  = 0
  if address != 0:
    frame_addr = readPointer (debugger, address + 0)
    frame_pc   = readPointer (debugger, address + stride)
  else:
    frame_addr = fp
    frame_pc   = pc
  while frame_num < 50:
    print "0x%x: 0x%x: %s" % (frame_addr, frame_pc, disassembleInstruction (debugger, frame_pc))
    if verbose:
      str = efiSymbolicate (debugger, None, frame_pc, False)
      if str != '':
        print "  %s" % str

    frame_pc   = readPointer (debugger, frame_addr + stride)
    frame_addr = readPointer (debugger, frame_addr + 0)
    frame_num += 1
    if frame_pc == 0:
      break
    if NotCanonicalAddress(frame_pc) or NotCanonicalAddress(frame_addr):
      break
  
  return frame_num



>  (On case 0, the code at offset 0x57F is int 3)
> 
>  000000007DBD41B9: E8 AF 2A 00 00     call        CpuBreakpoint
> 
>  000000007DBD41BE: XX XX XX XX         XXX    XXXXXX
> 
>  (On case 1, what’s the code at offset 0x71BD??)
> 
> 
> 
> If the upper asm code is not correct, please copy your obj file here.
> 
> 
> 
> If the upper asm code is correct, I think we should show the return address as below, since we cannot calculate the calling IP address on most cases. (return address – 1 is not always the calling IP address on IA arch).
> 
> 0 0x7DBCD580 @ 0x7DBCD000+0x580 (0x7EEC8DDC) in PartitionDxe.dll
> 
> 1 0x7DBD41BE @ 0x7DBCD000+0x71BE (0x7EEC8DFC) in PartitionDxe.dll
> 
> 
> 
> Thanks!
> 
> Jeff
> 
> 
> 
> ________________________________
> From: Paulo Alcantara <pcacjr@zytor.com <mailto:pcacjr@zytor.com>>
> Sent: Monday, November 20, 2017 10:59:41 PM
> To: Fan Jeff; edk2-devel@lists.01.org <mailto:edk2-devel@lists.01.org>
> Cc: Laszlo Ersek; Eric Dong
> Subject: Re: 答复: [edk2] [RFC v3 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
> 
> Hi Jeff,
> 
> (sorry for the late response)
> 
> On 11/17/2017 5:24 AM, Fan Jeff wrote:
>> Paulo,
>> 
>> 
>> I don't understand why you - 1 when calculating EIP offset in image, it
>> confused me.
> 
> That's an offset relative to the PE/COFF image base: 0 - (ImageBase +
> ImageBaseSize - 1)
> 
> Doesn't that look right to you?
> 
> Thanks
> Paulo
> 
>> 
>> 
>> +  for (;;) {
>> +    //
>> +    // Print stack frame in the following format:
>> +    //
>> +    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName> | ????]
>> +    //
>> +    InternalPrintMessage (
>> +      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
>> +      *UnwoundStacksCount - 1,
>> +      Rip,
>> +      ImageBase,
>> +      Rip - ImageBase - 1,   // ????
>> +      Rbp,
>> +      PdbFileName
>> +      );
>> +
>> 
>> Jeff
>> 
>> 
>> 
>> 
>> ------------------------------------------------------------------------
>> *发件人:* edk2-devel <edk2-devel-bounces@lists.01.org <mailto:edk2-devel-bounces@lists.01.org>> 代表 Paulo
>> Alcantara <pcacjr@zytor.com <mailto:pcacjr@zytor.com>>
>> *发送时间:* 2017年11月17日 5:56
>> *收件人:* edk2-devel@lists.01.org <mailto:edk2-devel@lists.01.org>
>> *抄送:* Laszlo Ersek; Eric Dong
>> *主题:* [edk2] [RFC v3 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add
>> stack trace support
>> This patch adds stack trace support during a X64 CPU exception.
>> 
>> It will dump out back trace, stack contents as well as image module
>> names that were part of the call stack.
>> 
>> Contributed-under: TianoCore Contribution Agreement 1.1
>> Cc: Eric Dong <eric.dong@intel.com <mailto:eric.dong@intel.com>>
>> Cc: Laszlo Ersek <lersek@redhat.com <mailto:lersek@redhat.com>>
>> Signed-off-by: Paulo Alcantara <pcacjr@zytor.com <mailto:pcacjr@zytor.com>>
>> ---
>>  UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c |
>> 376 +++++++++++++++++++-
>>  1 file changed, 374 insertions(+), 2 deletions(-)
>> 
>> diff --git
>> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
>> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
>> index 65f0cff680..fe776ccc2d 100644
>> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
>> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
>> @@ -14,6 +14,11 @@
>> 
>>  #include "CpuExceptionCommon.h"
>> 
>> +//
>> +// Unknown PDB file name
>> +//
>> +GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
>> +
>>  /**
>>    Return address map of exception handler template so that C code can
>> generate
>>    exception tables.
>> @@ -242,6 +247,357 @@ DumpCpuContext (
>>      );
>>  }
>> 
>> +/**
>> +  Get absolute path and file name of PDB file in PE/COFF image.
>> +
>> +  @param[in]  ImageBase            Base address of PE/COFF image.
>> +  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
>> +  @param[out] PdbFileName          File name of PDB file.
>> +**/
>> +STATIC
>> +VOID
>> +GetPdbFileName (
>> +  IN  UINTN    ImageBase,
>> +  OUT CHAR8    **PdbAbsoluteFilePath,
>> +  OUT CHAR8    **PdbFileName
>> +  )
>> +{
>> +  VOID   *PdbPointer;
>> +  CHAR8  *Str;
>> +
>> +  //
>> +  // Get PDB file name from PE/COFF image
>> +  //
>> +  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
>> +  if (PdbPointer == NULL) {
>> +    //
>> +    // No PDB file name found. Set it to an unknown file name.
>> +    //
>> +    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
>> +    if (PdbAbsoluteFilePath != NULL) {
>> +      *PdbAbsoluteFilePath = NULL;
>> +    }
>> +  } else {
>> +    //
>> +    // Get file name portion out of PDB file in PE/COFF image
>> +    //
>> +    Str = (CHAR8 *)((UINTN)PdbPointer +
>> +                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
>> +    for (; *Str != '/' && *Str != '\\'; Str--) {
>> +      ;
>> +    }
>> +
>> +    //
>> +    // Set PDB file name (also skip trailing path separator: '/' or '\\')
>> +    //
>> +    *PdbFileName = Str + 1;
>> +
>> +    if (PdbAbsoluteFilePath != NULL) {
>> +      //
>> +      // Set absolute file path of PDB file
>> +      //
>> +      *PdbAbsoluteFilePath = PdbPointer;
>> +    }
>> +  }
>> +}
>> +
>> +/**
>> +  Dump stack contents.
>> +
>> +  @param[in]  CurrentRsp         Current stack pointer address.
>> +  @param[in]  UnwoundStacksCount  Count of unwound stack frames.
>> +**/
>> +STATIC
>> +VOID
>> +DumpStackContents (
>> +  IN UINT64  CurrentRsp,
>> +  IN INTN    UnwoundStacksCount
>> +  )
>> +{
>> +  //
>> +  // Check for proper stack pointer alignment
>> +  //
>> +  if (((UINTN)CurrentRsp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
>> +    InternalPrintMessage ("!!!! Unaligned stack pointer. !!!!\n");
>> +    return;
>> +  }
>> +
>> +  //
>> +  // Dump out stack contents
>> +  //
>> +  InternalPrintMessage ("\nStack dump:\n");
>> +  while (UnwoundStacksCount-- > 0) {
>> +    InternalPrintMessage (
>> +      "0x%016lx: %016lx %016lx\n",
>> +      CurrentRsp,
>> +      *(UINT64 *)CurrentRsp,
>> +      *(UINT64 *)((UINTN)CurrentRsp + 8)
>> +      );
>> +
>> +    //
>> +    // Point to next stack
>> +    //
>> +    CurrentRsp += CPU_STACK_ALIGNMENT;
>> +  }
>> +}
>> +
>> +/**
>> +  Dump all image module names from call stack.
>> +
>> +  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
>> +**/
>> +STATIC
>> +VOID
>> +DumpImageModuleNames (
>> +  IN EFI_SYSTEM_CONTEXT   SystemContext
>> +  )
>> +{
>> +  EFI_STATUS  Status;
>> +  UINT64      Rip;
>> +  UINTN       ImageBase;
>> +  VOID        *EntryPoint;
>> +  CHAR8       *PdbAbsoluteFilePath;
>> +  CHAR8       *PdbFileName;
>> +  UINT64      Rbp;
>> +  UINTN       LastImageBase;
>> +
>> +  //
>> +  // Set current RIP address
>> +  //
>> +  Rip = SystemContext.SystemContextX64->Rip;
>> +
>> +  //
>> +  // Set current frame pointer address
>> +  //
>> +  Rbp = SystemContext.SystemContextX64->Rbp;
>> +
>> +  //
>> +  // Check for proper frame pointer alignment
>> +  //
>> +  if (((UINTN)Rbp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
>> +    InternalPrintMessage ("!!!! Unaligned frame pointer. !!!!\n");
>> +    return;
>> +  }
>> +
>> +  //
>> +  // Get initial PE/COFF image base address from current RIP
>> +  //
>> +  ImageBase = PeCoffSearchImageBase (Rip);
>> +  if (ImageBase == 0) {
>> +    InternalPrintMessage ("!!!! Could not find image module names. !!!!");
>> +    return;
>> +  }
>> +
>> +  //
>> +  // Set last PE/COFF image base address
>> +  //
>> +  LastImageBase = ImageBase;
>> +
>> +  //
>> +  // Get initial PE/COFF image's entry point
>> +  //
>> +  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
>> +  if (EFI_ERROR (Status)) {
>> +    EntryPoint = NULL;
>> +  }
>> +
>> +  //
>> +  // Get file name and absolute path of initial PDB file
>> +  //
>> +  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
>> +
>> +  //
>> +  // Print out initial image module name (if any)
>> +  //
>> +  if (PdbAbsoluteFilePath != NULL) {
>> +    InternalPrintMessage (
>> +      "\n%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
>> +      PdbFileName,
>> +      ImageBase,
>> +      (UINTN)EntryPoint
>> +      );
>> +    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
>> +  }
>> +
>> +  //
>> +  // Walk through call stack and find next module names
>> +  //
>> +  for (;;) {
>> +    //
>> +    // Set RIP with return address from current stack frame
>> +    //
>> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
>> +
>> +    //
>> +    // If RIP is zero, then stop unwinding the stack
>> +    //
>> +    if (Rip == 0) {
>> +      break;
>> +    }
>> +
>> +    //
>> +    // Search for the respective PE/COFF image based on RIP
>> +    //
>> +    ImageBase = PeCoffSearchImageBase (Rip);
>> +    if (ImageBase == 0) {
>> +      //
>> +      // Stop stack trace
>> +      //
>> +      break;
>> +    }
>> +
>> +    //
>> +    // If RIP points to another PE/COFF image, then find its respective
>> PDB file
>> +    // name.
>> +    //
>> +    if (LastImageBase != ImageBase) {
>> +      //
>> +      // Get PE/COFF image's entry point
>> +      //
>> +      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
>> +      if (EFI_ERROR (Status)) {
>> +        EntryPoint = NULL;
>> +      }
>> +
>> +      //
>> +      // Get file name and absolute path of PDB file
>> +      //
>> +      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
>> +
>> +      //
>> +      // Print out image module name (if any)
>> +      //
>> +      if (PdbAbsoluteFilePath != NULL) {
>> +        InternalPrintMessage (
>> +          "%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
>> +          PdbFileName,
>> +          ImageBase,
>> +          (UINTN)EntryPoint
>> +          );
>> +        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
>> +      }
>> +
>> +      //
>> +      // Save last PE/COFF image base address
>> +      //
>> +      LastImageBase = ImageBase;
>> +    }
>> +
>> +    //
>> +    // Unwind the stack
>> +    //
>> +    Rbp = *(UINT64 *)(UINTN)Rbp;
>> +  }
>> +}
>> +
>> +/**
>> +  Dump stack trace.
>> +
>> +  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
>> +  @param[out] UnwoundStacksCount  Count of unwound stack frames.
>> +**/
>> +STATIC
>> +VOID
>> +DumpStackTrace (
>> +  IN  EFI_SYSTEM_CONTEXT   SystemContext,
>> +  OUT INTN                 *UnwoundStacksCount
>> +  )
>> +{
>> +  UINT64  Rip;
>> +  UINT64  Rbp;
>> +  UINTN   ImageBase;
>> +  CHAR8   *PdbFileName;
>> +
>> +  //
>> +  // Set current RIP address
>> +  //
>> +  Rip = SystemContext.SystemContextX64->Rip;
>> +
>> +  //
>> +  // Set current frame pointer address
>> +  //
>> +  Rbp = SystemContext.SystemContextX64->Rbp;
>> +
>> +  //
>> +  // Get initial PE/COFF image base address from current RIP
>> +  //
>> +  ImageBase = PeCoffSearchImageBase (Rip);
>> +  if (ImageBase == 0) {
>> +    InternalPrintMessage ("!!!! Could not find backtrace information.
>> !!!!");
>> +    return;
>> +  }
>> +
>> +  //
>> +  // Get PDB file name from initial PE/COFF image
>> +  //
>> +  GetPdbFileName (ImageBase, NULL, &PdbFileName);
>> +
>> +  //
>> +  // Initialize count of unwound stacks
>> +  //
>> +  *UnwoundStacksCount = 1;
>> +
>> +  //
>> +  // Print out back trace
>> +  //
>> +  InternalPrintMessage ("\nCall trace:\n");
>> +
>> +  for (;;) {
>> +    //
>> +    // Print stack frame in the following format:
>> +    //
>> +    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName> | ????]
>> +    //
>> +    InternalPrintMessage (
>> +      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
>> +      *UnwoundStacksCount - 1,
>> +      Rip,
>> +      ImageBase,
>> +      Rip - ImageBase - 1,
>> +      Rbp,
>> +      PdbFileName
>> +      );
>> +
>> +    //
>> +    // Set RIP with return address from current stack frame
>> +    //
>> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
>> +
>> +    //
>> +    // If RIP is zero, then stop unwinding the stack
>> +    //
>> +    if (Rip == 0) {
>> +      break;
>> +    }
>> +
>> +    //
>> +    // Search for the respective PE/COFF image based on RIP
>> +    //
>> +    ImageBase = PeCoffSearchImageBase (Rip);
>> +    if (ImageBase == 0) {
>> +      //
>> +      // Stop stack trace
>> +      //
>> +      break;
>> +    }
>> +
>> +    //
>> +    // Get PDB file name
>> +    //
>> +    GetPdbFileName (ImageBase, NULL, &PdbFileName);
>> +
>> +    //
>> +    // Unwind the stack
>> +    //
>> +    Rbp = *(UINT64 *)(UINTN)Rbp;
>> +
>> +    //
>> +    // Increment count of unwound stacks
>> +    //
>> +    (*UnwoundStacksCount)++;
>> +  }
>> +}
>> +
>>  /**
>>    Display CPU information.
>> 
>> @@ -254,9 +610,25 @@ DumpImageAndCpuContent (
>>    IN EFI_SYSTEM_CONTEXT   SystemContext
>>    )
>>  {
>> +  INTN UnwoundStacksCount;
>> +
>> +  //
>> +  // Dump CPU context
>> +  //
>>    DumpCpuContext (ExceptionType, SystemContext);
>> +
>> +  //
>> +  // Dump stack trace
>> +  //
>> +  DumpStackTrace (SystemContext, &UnwoundStacksCount);
>> +
>> +  //
>> +  // Dump image module names
>> +  //
>> +  DumpImageModuleNames (SystemContext);
>> +
>>    //
>> -  // Dump module image base and module entry point by RIP
>> +  // Dump stack contents
>>    //
>> -  DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
>> +  DumpStackContents (SystemContext.SystemContextX64->Rsp,
>> UnwoundStacksCount);
>>  }
>> --
>> 2.14.3
>> 
>> _______________________________________________
>> edk2-devel mailing list
>> edk2-devel@lists.01.org <mailto:edk2-devel@lists.01.org>
>> https://lists.01.org/mailman/listinfo/edk2-devel
>> 
>> edk2-devel Info Page - 01.org
>> <https://lists.01.org/mailman/listinfo/edk2-devel>
>> lists.01.org
>> Your email address: Your name (optional): You may enter a privacy
>> password below. This provides only mild security, but should prevent
>> others from messing ...
>> 
>> 
>> 
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org <mailto:edk2-devel@lists.01.org>
> https://lists.01.org/mailman/listinfo/edk2-devel



^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC v3 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
  2017-11-23 18:34             ` Andrew Fish
@ 2017-11-23 19:49               ` Fan Jeff
  0 siblings, 0 replies; 82+ messages in thread
From: Fan Jeff @ 2017-11-23 19:49 UTC (permalink / raw)
  To: Andrew Fish
  Cc: Paulo Alcantara, edk2-devel@lists.01.org, Laszlo Ersek, Eric Dong

Andrew,

I agreed msft x64 compiler would not use ebp to save stack frame. In my part, I am trying to make use of exception data section to calculate the stack frame.

I just used Paulo’s gcc x64 case to show my question on return address display issue. It maybe not a good example:)
It’better Paulo could provide his gcc reassembly code.

Thanks!
Jeff



发自我的 iPhone

在 2017年11月24日,上午2:34,Andrew Fish <afish@apple.com<mailto:afish@apple.com>> 写道:


On Nov 23, 2017, at 6:27 AM, Fan Jeff <vanjeff_919@hotmail.com<mailto:vanjeff_919@hotmail.com>> wrote:

Hi Paulo,



I’d like to clarify my question.

The following call trace information is abstracted from your x64 dump contents.

Call trace:

0 0x7DBCD580 @ 0x7DBCD000+0x57F (0x7EEC8DDC) in PartitionDxe.dll

1 0x7DBD41BE @ 0x7DBCD000+0x71BD (0x7EEC8DFC) in PartitionDxe.dll



I guess you used CpuBreakpoint() to do your validation.

For 0x7DBCD580, it is return address followed by “int 3” from your case.

If we dump obj file, we would see the following asm code.

 000000007DBCD57F: CC                 int         3

 000000007DBCD580: C3                 ret


Jeff,

Can you walk the stack without a frame pointer? I don't think the common nasm code supports that. For x86 GCC and clang use %rbp as the frame pointer. The common nasm code may be why his walk is off?

As you can see when emit frame pointer is on you get:
pushq %rbp
movq %rsp, %rbp
...
popq %rbp
retq

vs. this without the frame pointer, like your example above.
...
retq

Without a frame pointer you need debug symbols as you don't know how deep the return address is on the stack in any given location in a function.

Simple frame pointer example with clang:
~/work/Compiler>cat breakpoint.c
void
CpuBreakpoint (
  void
  )
{
  __asm__ __volatile__ ("int $3");
}
~/work/Compiler>clang breakpoint.c -S
~/work/Compiler>cat breakpoint.S
.section __TEXT,__text,regular,pure_instructions
.macosx_version_min 10, 12
.globl _CpuBreakpoint
.p2align 4, 0x90
_CpuBreakpoint:                         ## @CpuBreakpoint
.cfi_startproc
## BB#0:
pushq %rbp
Lcfi0:
.cfi_def_cfa_offset 16
Lcfi1:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Lcfi2:
.cfi_def_cfa_register %rbp
## InlineAsm Start
int3
## InlineAsm End
popq %rbp
retq
.cfi_endproc


.subsections_via_symbols
~/work/Compiler>

Thanks,

Andrew Fish

PS some lldb Python that walks an x86 stack frame, assuming you have a stack pointer. Given dereferencing a non-canonical addresses causes a General Protection fault it is good to error check for them if your stack walk code can not tolerate exceptions. EFI_BAD_POINTER, aka 0xAFAFAFAFAFAFAFAF, is the most common thing you hit (Thanks to Vincent Zimmer making sure EFI faults have my initials in them, not to mention the header for TE images is VZ).

def NotCanonicalAddress(addr, start=0x00007FFFFFFFFFFF, end=0xFF800000000000000):
  return addr > start and addr < end

def print_raw_stacktrace(debugger, fp, pc, address = 0, verbose = False):
  # Do a raw stack trace

  stride =4
  SBTarget  = debugger.GetSelectedTarget()
  if SBTarget:
    Triple = SBTarget.triple
    if Triple.find ("x86_64") != -1:
      stride = 8

  frame_num  = 0
  if address != 0:
    frame_addr = readPointer (debugger, address + 0)
    frame_pc   = readPointer (debugger, address + stride)
  else:
    frame_addr = fp
    frame_pc   = pc
  while frame_num < 50:
    print "0x%x: 0x%x: %s" % (frame_addr, frame_pc, disassembleInstruction (debugger, frame_pc))
    if verbose:
      str = efiSymbolicate (debugger, None, frame_pc, False)
      if str != '':
        print "  %s" % str

    frame_pc   = readPointer (debugger, frame_addr + stride)
    frame_addr = readPointer (debugger, frame_addr + 0)
    frame_num += 1
    if frame_pc == 0:
      break
    if NotCanonicalAddress(frame_pc) or NotCanonicalAddress(frame_addr):
      break

  return frame_num



 (On case 0, the code at offset 0x57F is int 3)

 000000007DBD41B9: E8 AF 2A 00 00     call        CpuBreakpoint

 000000007DBD41BE: XX XX XX XX         XXX    XXXXXX

 (On case 1, what’s the code at offset 0x71BD??)



If the upper asm code is not correct, please copy your obj file here.



If the upper asm code is correct, I think we should show the return address as below, since we cannot calculate the calling IP address on most cases. (return address – 1 is not always the calling IP address on IA arch).

0 0x7DBCD580 @ 0x7DBCD000+0x580 (0x7EEC8DDC) in PartitionDxe.dll

1 0x7DBD41BE @ 0x7DBCD000+0x71BE (0x7EEC8DFC) in PartitionDxe.dll



Thanks!

Jeff



________________________________
From: Paulo Alcantara <pcacjr@zytor.com<mailto:pcacjr@zytor.com>>
Sent: Monday, November 20, 2017 10:59:41 PM
To: Fan Jeff; edk2-devel@lists.01.org<mailto:edk2-devel@lists.01.org>
Cc: Laszlo Ersek; Eric Dong
Subject: Re: 答复: [edk2] [RFC v3 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support

Hi Jeff,

(sorry for the late response)

On 11/17/2017 5:24 AM, Fan Jeff wrote:
Paulo,


I don't understand why you - 1 when calculating EIP offset in image, it
confused me.

That's an offset relative to the PE/COFF image base: 0 - (ImageBase +
ImageBaseSize - 1)

Doesn't that look right to you?

Thanks
Paulo



+  for (;;) {
+    //
+    // Print stack frame in the following format:
+    //
+    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName> | ????]
+    //
+    InternalPrintMessage (
+      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
+      *UnwoundStacksCount - 1,
+      Rip,
+      ImageBase,
+      Rip - ImageBase - 1,   // ????
+      Rbp,
+      PdbFileName
+      );
+

Jeff




------------------------------------------------------------------------
*发件人:* edk2-devel <edk2-devel-bounces@lists.01.org<mailto:edk2-devel-bounces@lists.01.org>> 代表 Paulo
Alcantara <pcacjr@zytor.com<mailto:pcacjr@zytor.com>>
*发送时间:* 2017年11月17日 5:56
*收件人:* edk2-devel@lists.01.org<mailto:edk2-devel@lists.01.org>
*抄送:* Laszlo Ersek; Eric Dong
*主题:* [edk2] [RFC v3 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add
stack trace support
This patch adds stack trace support during a X64 CPU exception.

It will dump out back trace, stack contents as well as image module
names that were part of the call stack.

Contributed-under: TianoCore Contribution Agreement 1.1
Cc: Eric Dong <eric.dong@intel.com<mailto:eric.dong@intel.com>>
Cc: Laszlo Ersek <lersek@redhat.com<mailto:lersek@redhat.com>>
Signed-off-by: Paulo Alcantara <pcacjr@zytor.com<mailto:pcacjr@zytor.com>>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c |
376 +++++++++++++++++++-
 1 file changed, 374 insertions(+), 2 deletions(-)

diff --git
a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
index 65f0cff680..fe776ccc2d 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
@@ -14,6 +14,11 @@

 #include "CpuExceptionCommon.h"

+//
+// Unknown PDB file name
+//
+GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
+
 /**
   Return address map of exception handler template so that C code can
generate
   exception tables.
@@ -242,6 +247,357 @@ DumpCpuContext (
     );
 }

+/**
+  Get absolute path and file name of PDB file in PE/COFF image.
+
+  @param[in]  ImageBase            Base address of PE/COFF image.
+  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
+  @param[out] PdbFileName          File name of PDB file.
+**/
+STATIC
+VOID
+GetPdbFileName (
+  IN  UINTN    ImageBase,
+  OUT CHAR8    **PdbAbsoluteFilePath,
+  OUT CHAR8    **PdbFileName
+  )
+{
+  VOID   *PdbPointer;
+  CHAR8  *Str;
+
+  //
+  // Get PDB file name from PE/COFF image
+  //
+  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
+  if (PdbPointer == NULL) {
+    //
+    // No PDB file name found. Set it to an unknown file name.
+    //
+    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
+    if (PdbAbsoluteFilePath != NULL) {
+      *PdbAbsoluteFilePath = NULL;
+    }
+  } else {
+    //
+    // Get file name portion out of PDB file in PE/COFF image
+    //
+    Str = (CHAR8 *)((UINTN)PdbPointer +
+                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
+    for (; *Str != '/' && *Str != '\\'; Str--) {
+      ;
+    }
+
+    //
+    // Set PDB file name (also skip trailing path separator: '/' or '\\')
+    //
+    *PdbFileName = Str + 1;
+
+    if (PdbAbsoluteFilePath != NULL) {
+      //
+      // Set absolute file path of PDB file
+      //
+      *PdbAbsoluteFilePath = PdbPointer;
+    }
+  }
+}
+
+/**
+  Dump stack contents.
+
+  @param[in]  CurrentRsp         Current stack pointer address.
+  @param[in]  UnwoundStacksCount  Count of unwound stack frames.
+**/
+STATIC
+VOID
+DumpStackContents (
+  IN UINT64  CurrentRsp,
+  IN INTN    UnwoundStacksCount
+  )
+{
+  //
+  // Check for proper stack pointer alignment
+  //
+  if (((UINTN)CurrentRsp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
+    InternalPrintMessage ("!!!! Unaligned stack pointer. !!!!\n");
+    return;
+  }
+
+  //
+  // Dump out stack contents
+  //
+  InternalPrintMessage ("\nStack dump:\n");
+  while (UnwoundStacksCount-- > 0) {
+    InternalPrintMessage (
+      "0x%016lx: %016lx %016lx\n",
+      CurrentRsp,
+      *(UINT64 *)CurrentRsp,
+      *(UINT64 *)((UINTN)CurrentRsp + 8)
+      );
+
+    //
+    // Point to next stack
+    //
+    CurrentRsp += CPU_STACK_ALIGNMENT;
+  }
+}
+
+/**
+  Dump all image module names from call stack.
+
+  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+**/
+STATIC
+VOID
+DumpImageModuleNames (
+  IN EFI_SYSTEM_CONTEXT   SystemContext
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Rip;
+  UINTN       ImageBase;
+  VOID        *EntryPoint;
+  CHAR8       *PdbAbsoluteFilePath;
+  CHAR8       *PdbFileName;
+  UINT64      Rbp;
+  UINTN       LastImageBase;
+
+  //
+  // Set current RIP address
+  //
+  Rip = SystemContext.SystemContextX64->Rip;
+
+  //
+  // Set current frame pointer address
+  //
+  Rbp = SystemContext.SystemContextX64->Rbp;
+
+  //
+  // Check for proper frame pointer alignment
+  //
+  if (((UINTN)Rbp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
+    InternalPrintMessage ("!!!! Unaligned frame pointer. !!!!\n");
+    return;
+  }
+
+  //
+  // Get initial PE/COFF image base address from current RIP
+  //
+  ImageBase = PeCoffSearchImageBase (Rip);
+  if (ImageBase == 0) {
+    InternalPrintMessage ("!!!! Could not find image module names. !!!!");
+    return;
+  }
+
+  //
+  // Set last PE/COFF image base address
+  //
+  LastImageBase = ImageBase;
+
+  //
+  // Get initial PE/COFF image's entry point
+  //
+  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
+  if (EFI_ERROR (Status)) {
+    EntryPoint = NULL;
+  }
+
+  //
+  // Get file name and absolute path of initial PDB file
+  //
+  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
+
+  //
+  // Print out initial image module name (if any)
+  //
+  if (PdbAbsoluteFilePath != NULL) {
+    InternalPrintMessage (
+      "\n%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
+      PdbFileName,
+      ImageBase,
+      (UINTN)EntryPoint
+      );
+    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
+  }
+
+  //
+  // Walk through call stack and find next module names
+  //
+  for (;;) {
+    //
+    // Set RIP with return address from current stack frame
+    //
+    Rip = *(UINT64 *)((UINTN)Rbp + 8);
+
+    //
+    // If RIP is zero, then stop unwinding the stack
+    //
+    if (Rip == 0) {
+      break;
+    }
+
+    //
+    // Search for the respective PE/COFF image based on RIP
+    //
+    ImageBase = PeCoffSearchImageBase (Rip);
+    if (ImageBase == 0) {
+      //
+      // Stop stack trace
+      //
+      break;
+    }
+
+    //
+    // If RIP points to another PE/COFF image, then find its respective
PDB file
+    // name.
+    //
+    if (LastImageBase != ImageBase) {
+      //
+      // Get PE/COFF image's entry point
+      //
+      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
+      if (EFI_ERROR (Status)) {
+        EntryPoint = NULL;
+      }
+
+      //
+      // Get file name and absolute path of PDB file
+      //
+      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
+
+      //
+      // Print out image module name (if any)
+      //
+      if (PdbAbsoluteFilePath != NULL) {
+        InternalPrintMessage (
+          "%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
+          PdbFileName,
+          ImageBase,
+          (UINTN)EntryPoint
+          );
+        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
+      }
+
+      //
+      // Save last PE/COFF image base address
+      //
+      LastImageBase = ImageBase;
+    }
+
+    //
+    // Unwind the stack
+    //
+    Rbp = *(UINT64 *)(UINTN)Rbp;
+  }
+}
+
+/**
+  Dump stack trace.
+
+  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
+  @param[out] UnwoundStacksCount  Count of unwound stack frames.
+**/
+STATIC
+VOID
+DumpStackTrace (
+  IN  EFI_SYSTEM_CONTEXT   SystemContext,
+  OUT INTN                 *UnwoundStacksCount
+  )
+{
+  UINT64  Rip;
+  UINT64  Rbp;
+  UINTN   ImageBase;
+  CHAR8   *PdbFileName;
+
+  //
+  // Set current RIP address
+  //
+  Rip = SystemContext.SystemContextX64->Rip;
+
+  //
+  // Set current frame pointer address
+  //
+  Rbp = SystemContext.SystemContextX64->Rbp;
+
+  //
+  // Get initial PE/COFF image base address from current RIP
+  //
+  ImageBase = PeCoffSearchImageBase (Rip);
+  if (ImageBase == 0) {
+    InternalPrintMessage ("!!!! Could not find backtrace information.
!!!!");
+    return;
+  }
+
+  //
+  // Get PDB file name from initial PE/COFF image
+  //
+  GetPdbFileName (ImageBase, NULL, &PdbFileName);
+
+  //
+  // Initialize count of unwound stacks
+  //
+  *UnwoundStacksCount = 1;
+
+  //
+  // Print out back trace
+  //
+  InternalPrintMessage ("\nCall trace:\n");
+
+  for (;;) {
+    //
+    // Print stack frame in the following format:
+    //
+    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName> | ????]
+    //
+    InternalPrintMessage (
+      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
+      *UnwoundStacksCount - 1,
+      Rip,
+      ImageBase,
+      Rip - ImageBase - 1,
+      Rbp,
+      PdbFileName
+      );
+
+    //
+    // Set RIP with return address from current stack frame
+    //
+    Rip = *(UINT64 *)((UINTN)Rbp + 8);
+
+    //
+    // If RIP is zero, then stop unwinding the stack
+    //
+    if (Rip == 0) {
+      break;
+    }
+
+    //
+    // Search for the respective PE/COFF image based on RIP
+    //
+    ImageBase = PeCoffSearchImageBase (Rip);
+    if (ImageBase == 0) {
+      //
+      // Stop stack trace
+      //
+      break;
+    }
+
+    //
+    // Get PDB file name
+    //
+    GetPdbFileName (ImageBase, NULL, &PdbFileName);
+
+    //
+    // Unwind the stack
+    //
+    Rbp = *(UINT64 *)(UINTN)Rbp;
+
+    //
+    // Increment count of unwound stacks
+    //
+    (*UnwoundStacksCount)++;
+  }
+}
+
 /**
   Display CPU information.

@@ -254,9 +610,25 @@ DumpImageAndCpuContent (
   IN EFI_SYSTEM_CONTEXT   SystemContext
   )
 {
+  INTN UnwoundStacksCount;
+
+  //
+  // Dump CPU context
+  //
   DumpCpuContext (ExceptionType, SystemContext);
+
+  //
+  // Dump stack trace
+  //
+  DumpStackTrace (SystemContext, &UnwoundStacksCount);
+
+  //
+  // Dump image module names
+  //
+  DumpImageModuleNames (SystemContext);
+
   //
-  // Dump module image base and module entry point by RIP
+  // Dump stack contents
   //
-  DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
+  DumpStackContents (SystemContext.SystemContextX64->Rsp,
UnwoundStacksCount);
 }
--
2.14.3

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org<mailto:edk2-devel@lists.01.org>
https://lists.01.org/mailman/listinfo/edk2-devel

edk2-devel Info Page - 01.org<http://01.org>
<https://lists.01.org/mailman/listinfo/edk2-devel>
lists.01.org<http://lists.01.org>
Your email address: Your name (optional): You may enter a privacy
password below. This provides only mild security, but should prevent
others from messing ...



_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org<mailto:edk2-devel@lists.01.org>
https://lists.01.org/mailman/listinfo/edk2-devel


^ permalink raw reply related	[flat|nested] 82+ messages in thread

* [RFC v4 0/6] Stack trace support in X64 exception handling
  2017-11-14 12:47 ` [RFC 1/1] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support Paulo Alcantara
  2017-11-14 14:01   ` Andrew Fish
@ 2017-12-29  3:48   ` Paulo Alcantara
  2017-12-29  4:39     ` [RFC v4 1/6] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support Paulo Alcantara
                       ` (7 more replies)
  1 sibling, 8 replies; 82+ messages in thread
From: Paulo Alcantara @ 2017-12-29  3:48 UTC (permalink / raw)
  To: edk2-devel
  Cc: Paulo Alcantara, Rick Bramley, Andrew Fish, Eric Dong,
	Laszlo Ersek, Brian Johnson, Jeff Fan, Jiewen Yao

Hi,

This series adds stack trace support during IA32 and X64 CPU exceptions.

Informations like back trace, stack contents and image module names
(that were part of the call stack) will be dumped out.

The current limitation is that it relies on available frame pointers
(GCC only) in order to successfully unwind the stack.

(Sorry for the very long delay - I was very busy working on something
 else and then went to vacations)

Jiewen,

I have tested it with VS2015x86 and the stack trace just hanged when
printing out the first EIP (that is, no frame pointer at all).

Thanks!
Paulo

Repo:   https://github.com/pcacjr/edk2.git
Branch: stacktrace_v4

Cc: Rick Bramley <richard.bramley@hp.com>
Cc: Andrew Fish <afish@apple.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Brian Johnson <brian.johnson@hpe.com>
Cc: Jeff Fan <vanjeff_919@hotmail.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
---

v1 -> v2:
  * Add IA32 arch support (GCC toolchain only)
  * Replace hard-coded stack alignment value (16) with
    CPU_STACK_ALIGNMENT.
  * Check for proper stack and frame pointer alignments.
  * Fix initialization of UnwoundStacksCount to 1.
  * Move GetPdbFileName() to common code since it will be used by both
    IA32 and X64 implementations.

v2 -> v3:
  * Fixed wrong assumption about "RIP < ImageBase" to start searching
    for another PE/COFF image. That is, RIP may point to lower and
    higher addresses for any other PE/COFF images. Both IA32 & X64.
    (Thanks Andrew & Jiewen)
  * Fixed typo: unwond -> unwound. Both IA32 & X64. (Thanks Brian)

v3 -> v4:
  * Validate all frame/stack pointer addresses before dereferencing them
    as requested by Brian & Jiewen.
  * Correctly print out IP addresses during the stack traces (by Jeff)

Paulo Alcantara (6):
  UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
  UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName()
  UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support
  UefiCpuPkg/CpuExceptionHandlerLib: Add helper to valid memory
    addresses
  UefiCpuPkg/CpuExceptionHandlerLib: Ensure valid frame/stack pointers
  UefiCpuPkg/CpuExceptionHandlerLib: Correctly print IP addresses

 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c        | 484 ++++++++++++++++++--
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h        |  41 +-
 UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c | 445 +++++++++++++++++-
 UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c  | 384 +++++++++++++++-
 4 files changed, 1296 insertions(+), 58 deletions(-)

-- 
2.14.3



^ permalink raw reply	[flat|nested] 82+ messages in thread

* [RFC v4 1/6] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
  2017-12-29  3:48   ` [RFC v4 0/6] Stack trace support in X64 exception handling Paulo Alcantara
@ 2017-12-29  4:39     ` Paulo Alcantara
  2018-01-03  8:53       ` 答复: " Fan Jeff
  2017-12-29  4:39     ` [RFC v4 2/6] UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName() Paulo Alcantara
                       ` (6 subsequent siblings)
  7 siblings, 1 reply; 82+ messages in thread
From: Paulo Alcantara @ 2017-12-29  4:39 UTC (permalink / raw)
  To: edk2-devel; +Cc: Paulo Alcantara, Eric Dong, Laszlo Ersek

This patch adds stack trace support during a X64 CPU exception.

It will dump out back trace, stack contents as well as image module
names that were part of the call stack.

Contributed-under: TianoCore Contribution Agreement 1.1
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 376 +++++++++++++++++++-
 1 file changed, 374 insertions(+), 2 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
index 1dcf4277de..19bfaa329a 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
@@ -14,6 +14,11 @@
 
 #include "CpuExceptionCommon.h"
 
+//
+// Unknown PDB file name
+//
+GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
+
 /**
   Return address map of exception handler template so that C code can generate
   exception tables.
@@ -398,6 +403,357 @@ DumpCpuContext (
     );
 }
 
+/**
+  Get absolute path and file name of PDB file in PE/COFF image.
+
+  @param[in]  ImageBase            Base address of PE/COFF image.
+  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
+  @param[out] PdbFileName          File name of PDB file.
+**/
+STATIC
+VOID
+GetPdbFileName (
+  IN  UINTN    ImageBase,
+  OUT CHAR8    **PdbAbsoluteFilePath,
+  OUT CHAR8    **PdbFileName
+  )
+{
+  VOID   *PdbPointer;
+  CHAR8  *Str;
+
+  //
+  // Get PDB file name from PE/COFF image
+  //
+  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
+  if (PdbPointer == NULL) {
+    //
+    // No PDB file name found. Set it to an unknown file name.
+    //
+    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
+    if (PdbAbsoluteFilePath != NULL) {
+      *PdbAbsoluteFilePath = NULL;
+    }
+  } else {
+    //
+    // Get file name portion out of PDB file in PE/COFF image
+    //
+    Str = (CHAR8 *)((UINTN)PdbPointer +
+                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
+    for (; *Str != '/' && *Str != '\\'; Str--) {
+      ;
+    }
+
+    //
+    // Set PDB file name (also skip trailing path separator: '/' or '\\')
+    //
+    *PdbFileName = Str + 1;
+
+    if (PdbAbsoluteFilePath != NULL) {
+      //
+      // Set absolute file path of PDB file
+      //
+      *PdbAbsoluteFilePath = PdbPointer;
+    }
+  }
+}
+
+/**
+  Dump stack contents.
+
+  @param[in]  CurrentRsp         Current stack pointer address.
+  @param[in]  UnwoundStacksCount  Count of unwound stack frames.
+**/
+STATIC
+VOID
+DumpStackContents (
+  IN UINT64  CurrentRsp,
+  IN INTN    UnwoundStacksCount
+  )
+{
+  //
+  // Check for proper stack pointer alignment
+  //
+  if (((UINTN)CurrentRsp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
+    InternalPrintMessage ("!!!! Unaligned stack pointer. !!!!\n");
+    return;
+  }
+
+  //
+  // Dump out stack contents
+  //
+  InternalPrintMessage ("\nStack dump:\n");
+  while (UnwoundStacksCount-- > 0) {
+    InternalPrintMessage (
+      "0x%016lx: %016lx %016lx\n",
+      CurrentRsp,
+      *(UINT64 *)CurrentRsp,
+      *(UINT64 *)((UINTN)CurrentRsp + 8)
+      );
+
+    //
+    // Point to next stack
+    //
+    CurrentRsp += CPU_STACK_ALIGNMENT;
+  }
+}
+
+/**
+  Dump all image module names from call stack.
+
+  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+**/
+STATIC
+VOID
+DumpImageModuleNames (
+  IN EFI_SYSTEM_CONTEXT   SystemContext
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Rip;
+  UINTN       ImageBase;
+  VOID        *EntryPoint;
+  CHAR8       *PdbAbsoluteFilePath;
+  CHAR8       *PdbFileName;
+  UINT64      Rbp;
+  UINTN       LastImageBase;
+
+  //
+  // Set current RIP address
+  //
+  Rip = SystemContext.SystemContextX64->Rip;
+
+  //
+  // Set current frame pointer address
+  //
+  Rbp = SystemContext.SystemContextX64->Rbp;
+
+  //
+  // Check for proper frame pointer alignment
+  //
+  if (((UINTN)Rbp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
+    InternalPrintMessage ("!!!! Unaligned frame pointer. !!!!\n");
+    return;
+  }
+
+  //
+  // Get initial PE/COFF image base address from current RIP
+  //
+  ImageBase = PeCoffSearchImageBase (Rip);
+  if (ImageBase == 0) {
+    InternalPrintMessage ("!!!! Could not find image module names. !!!!");
+    return;
+  }
+
+  //
+  // Set last PE/COFF image base address
+  //
+  LastImageBase = ImageBase;
+
+  //
+  // Get initial PE/COFF image's entry point
+  //
+  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
+  if (EFI_ERROR (Status)) {
+    EntryPoint = NULL;
+  }
+
+  //
+  // Get file name and absolute path of initial PDB file
+  //
+  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
+
+  //
+  // Print out initial image module name (if any)
+  //
+  if (PdbAbsoluteFilePath != NULL) {
+    InternalPrintMessage (
+      "\n%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
+      PdbFileName,
+      ImageBase,
+      (UINTN)EntryPoint
+      );
+    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
+  }
+
+  //
+  // Walk through call stack and find next module names
+  //
+  for (;;) {
+    //
+    // Set RIP with return address from current stack frame
+    //
+    Rip = *(UINT64 *)((UINTN)Rbp + 8);
+
+    //
+    // If RIP is zero, then stop unwinding the stack
+    //
+    if (Rip == 0) {
+      break;
+    }
+
+    //
+    // Search for the respective PE/COFF image based on RIP
+    //
+    ImageBase = PeCoffSearchImageBase (Rip);
+    if (ImageBase == 0) {
+      //
+      // Stop stack trace
+      //
+      break;
+    }
+
+    //
+    // If RIP points to another PE/COFF image, then find its respective PDB file
+    // name.
+    //
+    if (LastImageBase != ImageBase) {
+      //
+      // Get PE/COFF image's entry point
+      //
+      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
+      if (EFI_ERROR (Status)) {
+        EntryPoint = NULL;
+      }
+
+      //
+      // Get file name and absolute path of PDB file
+      //
+      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
+
+      //
+      // Print out image module name (if any)
+      //
+      if (PdbAbsoluteFilePath != NULL) {
+        InternalPrintMessage (
+          "%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
+          PdbFileName,
+          ImageBase,
+          (UINTN)EntryPoint
+          );
+        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
+      }
+
+      //
+      // Save last PE/COFF image base address
+      //
+      LastImageBase = ImageBase;
+    }
+
+    //
+    // Unwind the stack
+    //
+    Rbp = *(UINT64 *)(UINTN)Rbp;
+  }
+}
+
+/**
+  Dump stack trace.
+
+  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
+  @param[out] UnwoundStacksCount  Count of unwound stack frames.
+**/
+STATIC
+VOID
+DumpStackTrace (
+  IN  EFI_SYSTEM_CONTEXT   SystemContext,
+  OUT INTN                 *UnwoundStacksCount
+  )
+{
+  UINT64  Rip;
+  UINT64  Rbp;
+  UINTN   ImageBase;
+  CHAR8   *PdbFileName;
+
+  //
+  // Set current RIP address
+  //
+  Rip = SystemContext.SystemContextX64->Rip;
+
+  //
+  // Set current frame pointer address
+  //
+  Rbp = SystemContext.SystemContextX64->Rbp;
+
+  //
+  // Get initial PE/COFF image base address from current RIP
+  //
+  ImageBase = PeCoffSearchImageBase (Rip);
+  if (ImageBase == 0) {
+    InternalPrintMessage ("!!!! Could not find backtrace information. !!!!");
+    return;
+  }
+
+  //
+  // Get PDB file name from initial PE/COFF image
+  //
+  GetPdbFileName (ImageBase, NULL, &PdbFileName);
+
+  //
+  // Initialize count of unwound stacks
+  //
+  *UnwoundStacksCount = 1;
+
+  //
+  // Print out back trace
+  //
+  InternalPrintMessage ("\nCall trace:\n");
+
+  for (;;) {
+    //
+    // Print stack frame in the following format:
+    //
+    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName> | ????]
+    //
+    InternalPrintMessage (
+      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
+      *UnwoundStacksCount - 1,
+      Rip,
+      ImageBase,
+      Rip - ImageBase - 1,
+      Rbp,
+      PdbFileName
+      );
+
+    //
+    // Set RIP with return address from current stack frame
+    //
+    Rip = *(UINT64 *)((UINTN)Rbp + 8);
+
+    //
+    // If RIP is zero, then stop unwinding the stack
+    //
+    if (Rip == 0) {
+      break;
+    }
+
+    //
+    // Search for the respective PE/COFF image based on RIP
+    //
+    ImageBase = PeCoffSearchImageBase (Rip);
+    if (ImageBase == 0) {
+      //
+      // Stop stack trace
+      //
+      break;
+    }
+
+    //
+    // Get PDB file name
+    //
+    GetPdbFileName (ImageBase, NULL, &PdbFileName);
+
+    //
+    // Unwind the stack
+    //
+    Rbp = *(UINT64 *)(UINTN)Rbp;
+
+    //
+    // Increment count of unwound stacks
+    //
+    (*UnwoundStacksCount)++;
+  }
+}
+
 /**
   Display CPU information.
 
@@ -410,9 +766,25 @@ DumpImageAndCpuContent (
   IN EFI_SYSTEM_CONTEXT   SystemContext
   )
 {
+  INTN UnwoundStacksCount;
+
+  //
+  // Dump CPU context
+  //
   DumpCpuContext (ExceptionType, SystemContext);
+
+  //
+  // Dump stack trace
+  //
+  DumpStackTrace (SystemContext, &UnwoundStacksCount);
+
+  //
+  // Dump image module names
+  //
+  DumpImageModuleNames (SystemContext);
+
   //
-  // Dump module image base and module entry point by RIP
+  // Dump stack contents
   //
-  DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
+  DumpStackContents (SystemContext.SystemContextX64->Rsp, UnwoundStacksCount);
 }
-- 
2.14.3



^ permalink raw reply related	[flat|nested] 82+ messages in thread

* [RFC v4 2/6] UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName()
  2017-12-29  3:48   ` [RFC v4 0/6] Stack trace support in X64 exception handling Paulo Alcantara
  2017-12-29  4:39     ` [RFC v4 1/6] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support Paulo Alcantara
@ 2017-12-29  4:39     ` Paulo Alcantara
  2017-12-29  4:39     ` [RFC v4 3/6] UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support Paulo Alcantara
                       ` (5 subsequent siblings)
  7 siblings, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2017-12-29  4:39 UTC (permalink / raw)
  To: edk2-devel; +Cc: Paulo Alcantara, Eric Dong, Laszlo Ersek

This function will be used by both IA32 and X64 exception handling in
order to print out image module names during stack unwinding.

Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c       | 60 +++++++++++++++++++-
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h       | 14 +++++
 UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 59 -------------------
 3 files changed, 73 insertions(+), 60 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
index dbfaae1d30..f62ab8c48c 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
@@ -54,6 +54,11 @@ CONST CHAR8 *mExceptionNameStr[] = {
 
 #define EXCEPTION_KNOWN_NAME_NUM  (sizeof (mExceptionNameStr) / sizeof (CHAR8 *))
 
+//
+// Unknown PDB file name
+//
+GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
+
 /**
   Get ASCII format string exception name by exception type.
 
@@ -177,4 +182,57 @@ ReadAndVerifyVectorInfo (
     VectorInfo ++;
   }
   return EFI_SUCCESS;
-}
\ No newline at end of file
+}
+
+/**
+  Get absolute path and file name of PDB file in PE/COFF image.
+
+  @param[in]  ImageBase            Base address of PE/COFF image.
+  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
+  @param[out] PdbFileName          File name of PDB file.
+**/
+VOID
+GetPdbFileName (
+  IN  UINTN    ImageBase,
+  OUT CHAR8    **PdbAbsoluteFilePath,
+  OUT CHAR8    **PdbFileName
+  )
+{
+  VOID   *PdbPointer;
+  CHAR8  *Str;
+
+  //
+  // Get PDB file name from PE/COFF image
+  //
+  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
+  if (PdbPointer == NULL) {
+    //
+    // No PDB file name found. Set it to an unknown file name.
+    //
+    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
+    if (PdbAbsoluteFilePath != NULL) {
+      *PdbAbsoluteFilePath = NULL;
+    }
+  } else {
+    //
+    // Get file name portion out of PDB file in PE/COFF image
+    //
+    Str = (CHAR8 *)((UINTN)PdbPointer +
+                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
+    for (; *Str != '/' && *Str != '\\'; Str--) {
+      ;
+    }
+
+    //
+    // Set PDB file name (also skip trailing path separator: '/' or '\\')
+    //
+    *PdbFileName = Str + 1;
+
+    if (PdbAbsoluteFilePath != NULL) {
+      //
+      // Set absolute file path of PDB file
+      //
+      *PdbAbsoluteFilePath = PdbPointer;
+    }
+  }
+}
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
index e10d9379d5..64c7094513 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
@@ -327,5 +327,19 @@ AsmGetTssTemplateMap (
   OUT EXCEPTION_HANDLER_TEMPLATE_MAP  *AddressMap
   );
 
+/**
+  Get absolute path and file name of PDB file in PE/COFF image.
+
+  @param[in]  ImageBase            Base address of PE/COFF image.
+  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
+  @param[out] PdbFileName          File name of PDB file.
+**/
+VOID
+GetPdbFileName (
+  IN  UINTN    ImageBase,
+  OUT CHAR8    **PdbAbsoluteFilePath,
+  OUT CHAR8    **PdbFileName
+  );
+
 #endif
 
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
index 19bfaa329a..d3a3878b3d 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
@@ -14,11 +14,6 @@
 
 #include "CpuExceptionCommon.h"
 
-//
-// Unknown PDB file name
-//
-GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
-
 /**
   Return address map of exception handler template so that C code can generate
   exception tables.
@@ -403,60 +398,6 @@ DumpCpuContext (
     );
 }
 
-/**
-  Get absolute path and file name of PDB file in PE/COFF image.
-
-  @param[in]  ImageBase            Base address of PE/COFF image.
-  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
-  @param[out] PdbFileName          File name of PDB file.
-**/
-STATIC
-VOID
-GetPdbFileName (
-  IN  UINTN    ImageBase,
-  OUT CHAR8    **PdbAbsoluteFilePath,
-  OUT CHAR8    **PdbFileName
-  )
-{
-  VOID   *PdbPointer;
-  CHAR8  *Str;
-
-  //
-  // Get PDB file name from PE/COFF image
-  //
-  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
-  if (PdbPointer == NULL) {
-    //
-    // No PDB file name found. Set it to an unknown file name.
-    //
-    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
-    if (PdbAbsoluteFilePath != NULL) {
-      *PdbAbsoluteFilePath = NULL;
-    }
-  } else {
-    //
-    // Get file name portion out of PDB file in PE/COFF image
-    //
-    Str = (CHAR8 *)((UINTN)PdbPointer +
-                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
-    for (; *Str != '/' && *Str != '\\'; Str--) {
-      ;
-    }
-
-    //
-    // Set PDB file name (also skip trailing path separator: '/' or '\\')
-    //
-    *PdbFileName = Str + 1;
-
-    if (PdbAbsoluteFilePath != NULL) {
-      //
-      // Set absolute file path of PDB file
-      //
-      *PdbAbsoluteFilePath = PdbPointer;
-    }
-  }
-}
-
 /**
   Dump stack contents.
 
-- 
2.14.3



^ permalink raw reply related	[flat|nested] 82+ messages in thread

* [RFC v4 3/6] UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support
  2017-12-29  3:48   ` [RFC v4 0/6] Stack trace support in X64 exception handling Paulo Alcantara
  2017-12-29  4:39     ` [RFC v4 1/6] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support Paulo Alcantara
  2017-12-29  4:39     ` [RFC v4 2/6] UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName() Paulo Alcantara
@ 2017-12-29  4:39     ` Paulo Alcantara
  2017-12-29  4:39     ` [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add helper to valid memory addresses Paulo Alcantara
                       ` (4 subsequent siblings)
  7 siblings, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2017-12-29  4:39 UTC (permalink / raw)
  To: edk2-devel; +Cc: Paulo Alcantara, Eric Dong, Laszlo Ersek

This patch adds stack trace support during a IA32 CPU exception.

It will dump out back trace, stack contents as well as image module
names that were part of the call stack.

Contributed-under: TianoCore Contribution Agreement 1.1
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c        |  42 ---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h        |  11 -
 UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c | 310 +++++++++++++++++++-
 3 files changed, 308 insertions(+), 55 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
index f62ab8c48c..867c5c01d6 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
@@ -109,48 +109,6 @@ InternalPrintMessage (
   SerialPortWrite ((UINT8 *)Buffer, AsciiStrLen (Buffer));
 }
 
-/**
-  Find and display image base address and return image base and its entry point.
-
-  @param CurrentEip      Current instruction pointer.
-
-**/
-VOID
-DumpModuleImageInfo (
-  IN  UINTN              CurrentEip
-  )
-{
-  EFI_STATUS                           Status;
-  UINTN                                Pe32Data;
-  VOID                                 *PdbPointer;
-  VOID                                 *EntryPoint;
-
-  Pe32Data = PeCoffSearchImageBase (CurrentEip);
-  if (Pe32Data == 0) {
-    InternalPrintMessage ("!!!! Can't find image information. !!!!\n");
-  } else {
-    //
-    // Find Image Base entry point
-    //
-    Status = PeCoffLoaderGetEntryPoint ((VOID *) Pe32Data, &EntryPoint);
-    if (EFI_ERROR (Status)) {
-      EntryPoint = NULL;
-    }
-    InternalPrintMessage ("!!!! Find image ");
-    PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *) Pe32Data);
-    if (PdbPointer != NULL) {
-      InternalPrintMessage ("%a", PdbPointer);
-    } else {
-      InternalPrintMessage ("(No PDB) " );
-    }
-    InternalPrintMessage (
-      " (ImageBase=%016lp, EntryPoint=%016p) !!!!\n",
-      (VOID *) Pe32Data,
-      EntryPoint
-      );
-  }
-}
-
 /**
   Read and save reserved vector information
 
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
index 64c7094513..ec46c2d9d3 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
@@ -130,17 +130,6 @@ InternalPrintMessage (
   ...
   );
 
-/**
-  Find and display image base address and return image base and its entry point.
-
-  @param CurrentEip      Current instruction pointer.
-
-**/
-VOID
-DumpModuleImageInfo (
-  IN  UINTN              CurrentEip
-  );
-
 /**
   Display CPU information.
 
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
index 6ac8549839..25e02fbbc1 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
@@ -398,6 +398,296 @@ DumpCpuContext (
     );
 }
 
+/**
+  Dump stack trace.
+
+  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
+  @param[out] UnwoundStacksCount  Count of unwound stack frames.
+**/
+STATIC
+VOID
+DumpStackTrace (
+  IN  EFI_SYSTEM_CONTEXT   SystemContext,
+  OUT INTN                 *UnwoundStacksCount
+  )
+{
+  UINT32  Eip;
+  UINT32  Ebp;
+  UINTN   ImageBase;
+  CHAR8   *PdbFileName;
+
+  //
+  // Set current EIP address
+  //
+  Eip = SystemContext.SystemContextIa32->Eip;
+
+  //
+  // Set current frame pointer address
+  //
+  Ebp = SystemContext.SystemContextIa32->Ebp;
+
+  //
+  // Check for proper frame pointer alignment
+  //
+  if (((UINTN)Ebp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
+    InternalPrintMessage ("!!!! Unaligned frame pointer. !!!!\n");
+    return;
+  }
+
+  //
+  // Get initial PE/COFF image base address from current EIP
+  //
+  ImageBase = PeCoffSearchImageBase (Eip);
+  if (ImageBase == 0) {
+    InternalPrintMessage ("!!!! Could not find backtrace information. !!!!");
+    return;
+  }
+
+  //
+  // Get PDB file name from initial PE/COFF image
+  //
+  GetPdbFileName (ImageBase, NULL, &PdbFileName);
+
+  //
+  // Initialize count of unwound stacks
+  //
+  *UnwoundStacksCount = 1;
+
+  //
+  // Print out back trace
+  //
+  InternalPrintMessage ("\nCall trace:\n");
+
+  for (;;) {
+    //
+    // Print stack frame in the following format:
+    //
+    // # <EIP> @ <ImageBase>+<RelOffset> (EBP) in [<ModuleName> | ????]
+    //
+    InternalPrintMessage (
+      "%d 0x%08x @ 0x%08x+0x%x (0x%08x) in %a\n",
+      *UnwoundStacksCount - 1,
+      Eip,
+      ImageBase,
+      Eip - ImageBase - 1,
+      Ebp,
+      PdbFileName
+      );
+
+    //
+    // Set EIP with return address from current stack frame
+    //
+    Eip = *(UINT32 *)((UINTN)Ebp + 4);
+
+    //
+    // If EIP is zero, then stop unwinding the stack
+    //
+    if (Eip == 0) {
+      break;
+    }
+
+    //
+    // Search for the respective PE/COFF image based on EIP
+    //
+    ImageBase = PeCoffSearchImageBase (Eip);
+    if (ImageBase == 0) {
+      //
+      // Stop stack trace
+      //
+      break;
+    }
+
+    //
+    // Get PDB file name
+    //
+    GetPdbFileName (ImageBase, NULL, &PdbFileName);
+
+    //
+    // Unwind the stack
+    //
+    Ebp = *(UINT32 *)(UINTN)Ebp;
+
+    //
+    // Increment count of unwound stacks
+    //
+    (*UnwoundStacksCount)++;
+  }
+}
+
+/**
+  Dump all image module names from call stack.
+
+  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+**/
+STATIC
+VOID
+DumpImageModuleNames (
+  IN EFI_SYSTEM_CONTEXT   SystemContext
+  )
+{
+  EFI_STATUS  Status;
+  UINT32      Eip;
+  UINT32      Ebp;
+  UINTN       ImageBase;
+  VOID        *EntryPoint;
+  CHAR8       *PdbAbsoluteFilePath;
+  CHAR8       *PdbFileName;
+  UINTN       LastImageBase;
+
+  //
+  // Set current EIP address
+  //
+  Eip = SystemContext.SystemContextIa32->Eip;
+
+  //
+  // Set current frame pointer address
+  //
+  Ebp = SystemContext.SystemContextIa32->Ebp;
+
+  //
+  // Get initial PE/COFF image base address from current EIP
+  //
+  ImageBase = PeCoffSearchImageBase (Eip);
+  if (ImageBase == 0) {
+    InternalPrintMessage ("!!!! Could not find image module names. !!!!");
+    return;
+  }
+
+  //
+  // Set last PE/COFF image base address
+  //
+  LastImageBase = ImageBase;
+
+  //
+  // Get initial PE/COFF image's entry point
+  //
+  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
+  if (EFI_ERROR (Status)) {
+    EntryPoint = NULL;
+  }
+
+  //
+  // Get file name and absolute path of initial PDB file
+  //
+  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
+
+  //
+  // Print out initial image module name (if any)
+  //
+  if (PdbAbsoluteFilePath != NULL) {
+    InternalPrintMessage (
+      "\n%a (ImageBase=0x%08x, EntryPoint=0x%08x):\n",
+      PdbFileName,
+      ImageBase,
+      (UINTN)EntryPoint
+      );
+    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
+  }
+
+  //
+  // Walk through call stack and find next module names
+  //
+  for (;;) {
+    //
+    // Set EIP with return address from current stack frame
+    //
+    Eip = *(UINT32 *)((UINTN)Ebp + 4);
+
+    //
+    // Search for the respective PE/COFF image based on Eip
+    //
+    ImageBase = PeCoffSearchImageBase (Eip);
+    if (ImageBase == 0) {
+      //
+      // Stop stack trace
+      //
+      break;
+    }
+
+    //
+    // If EIP points to another PE/COFF image, then find its respective PDB file
+    // name.
+    //
+    if (LastImageBase != ImageBase) {
+      //
+      // Get PE/COFF image's entry point
+      //
+      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
+      if (EFI_ERROR (Status)) {
+        EntryPoint = NULL;
+      }
+
+      //
+      // Get file name and absolute path of PDB file
+      //
+      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
+
+      //
+      // Print out image module name (if any)
+      //
+      if (PdbAbsoluteFilePath != NULL) {
+        InternalPrintMessage (
+          "%a (ImageBase=0x%08x, EntryPoint=0x%08x):\n",
+          PdbFileName,
+          ImageBase,
+          (UINTN)EntryPoint
+          );
+        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
+      }
+
+      //
+      // Save last PE/COFF image base address
+      //
+      LastImageBase = ImageBase;
+    }
+
+    //
+    // Unwind the stack
+    //
+    Ebp = *(UINT32 *)(UINTN)Ebp;
+  }
+}
+
+/**
+  Dump stack contents.
+
+  @param[in]  CurrentEsp         Current stack pointer address.
+  @param[in]  UnwoundStacksCount  Count of unwound stack frames.
+**/
+STATIC
+VOID
+DumpStackContents (
+  IN UINT32  CurrentEsp,
+  IN INTN    UnwoundStacksCount
+  )
+{
+  //
+  // Check for proper stack alignment
+  //
+  if (((UINTN)CurrentEsp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
+    InternalPrintMessage ("!!!! Unaligned stack pointer. !!!!\n");
+    return;
+  }
+
+  //
+  // Dump out stack contents
+  //
+  InternalPrintMessage ("\nStack dump:\n");
+  while (UnwoundStacksCount-- > 0) {
+    InternalPrintMessage (
+      "0x%08x: %08x %08x\n",
+      CurrentEsp,
+      *(UINT32 *)CurrentEsp,
+      *(UINT32 *)((UINTN)CurrentEsp + 4)
+      );
+
+    //
+    // Point to next stack
+    //
+    CurrentEsp += CPU_STACK_ALIGNMENT;
+  }
+}
+
 /**
   Display CPU information.
 
@@ -410,9 +700,25 @@ DumpImageAndCpuContent (
   IN EFI_SYSTEM_CONTEXT   SystemContext
   )
 {
+  INTN UnwoundStacksCount;
+
+  //
+  // Dump CPU context
+  //
   DumpCpuContext (ExceptionType, SystemContext);
+
+  //
+  // Dump stack trace
+  //
+  DumpStackTrace (SystemContext, &UnwoundStacksCount);
+
+  //
+  // Dump image module names
+  //
+  DumpImageModuleNames (SystemContext);
+
   //
-  // Dump module image base and module entry point by EIP
+  // Dump stack contents
   //
-  DumpModuleImageInfo (SystemContext.SystemContextIa32->Eip);
+  DumpStackContents (SystemContext.SystemContextIa32->Esp, UnwoundStacksCount);
 }
-- 
2.14.3



^ permalink raw reply related	[flat|nested] 82+ messages in thread

* [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add helper to valid memory addresses
  2017-12-29  3:48   ` [RFC v4 0/6] Stack trace support in X64 exception handling Paulo Alcantara
                       ` (2 preceding siblings ...)
  2017-12-29  4:39     ` [RFC v4 3/6] UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support Paulo Alcantara
@ 2017-12-29  4:39     ` Paulo Alcantara
  2018-01-03  8:42       ` 答复: " Fan Jeff
                         ` (2 more replies)
  2017-12-29  4:39     ` [RFC v4 5/6] UefiCpuPkg/CpuExceptionHandlerLib: Ensure valid frame/stack pointers Paulo Alcantara
                       ` (3 subsequent siblings)
  7 siblings, 3 replies; 82+ messages in thread
From: Paulo Alcantara @ 2017-12-29  4:39 UTC (permalink / raw)
  To: edk2-devel; +Cc: Paulo Alcantara, Eric Dong, Laszlo Ersek

Introduce IsLinearAddressValid() function that will be used for
validating memory addresses that would get dereferenced during stack
traces in IA32 and X64 CPU exceptions.

Contributed-under: TianoCore Contribution Agreement 1.1
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Requested-by: Brian Johnson <brian.johnson@hpe.com>
Requested-by: Jiewen Yao <jiewen.yao@intel.com>
Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c | 382 ++++++++++++++++++++
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h |  16 +
 2 files changed, 398 insertions(+)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
index 867c5c01d6..52b3eb1463 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
@@ -14,6 +14,9 @@
 
 #include "CpuExceptionCommon.h"
 
+#include <Register/Msr.h>
+#include <Library/DebugLib.h>
+
 //
 // Error code flag indicating whether or not an error code will be
 // pushed on the stack if an exception occurs.
@@ -194,3 +197,382 @@ GetPdbFileName (
     }
   }
 }
+
+/**
+  Check if a linear address is valid by walking the page tables in 4-level
+  paging mode.
+
+  @param[in]  Cr3             CR3 control register.
+  @param[in]  MaxPhyAddrBits  MAXPHYADDRBITS bits.
+  @param[in]  LinearAddress   Linear address to be checked.
+**/
+STATIC
+BOOLEAN
+Do4LevelPagingModeCheck (
+  IN UINTN            Cr3,
+  IN UINT8            MaxPhyAddrBits,
+  IN UINTN            LinearAddress
+  )
+{
+  UINT64 PhysicalAddress;
+  UINT64 *Pml4TableEntry;
+  UINT64 *PageDirPtrTableEntry;
+  UINT64 *PageDirEntry;
+  UINT64 *PageTableEntry;
+
+  //
+  // In 4-level paging mode, linear addresses are 48 bits wide
+  //
+  if ((UINT64)LinearAddress > (1ULL << 48) - 1) {
+    return FALSE;
+  }
+
+  //
+  // Calculate physical address of PML4E
+  //
+  PhysicalAddress = (UINT64)Cr3 & (((1ULL << MaxPhyAddrBits) - 1) << 12);
+  PhysicalAddress |= (((UINT64)LinearAddress >> 39) & 0x1FF) << 3;
+
+  ASSERT ((PhysicalAddress & (sizeof (*Pml4TableEntry) - 1)) == 0);
+
+  Pml4TableEntry = (UINT64 *)(UINTN)PhysicalAddress;
+
+  //
+  // Check if a PDPTE is present
+  //
+  if ((*Pml4TableEntry & BIT0) == 0) {
+    return FALSE;
+  }
+
+  //
+  // Calculate physical address of PDPTE
+  //
+  PhysicalAddress = *Pml4TableEntry & (((1ULL << MaxPhyAddrBits) - 1) << 12);
+  PhysicalAddress |= (((UINT64)LinearAddress >> 30) & 0x1FF) << 3;
+
+  ASSERT ((PhysicalAddress & (sizeof (*PageDirPtrTableEntry) - 1)) == 0);
+
+  PageDirPtrTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
+
+  //
+  // Check whether a PDPTE or 1GiB page entry is present
+  //
+  if ((*PageDirPtrTableEntry & BIT0) == 0) {
+    return FALSE;
+  }
+
+  //
+  // Check if PDPTE maps an 1GiB page
+  //
+  if ((*PageDirPtrTableEntry & BIT7) != 0) {
+    return TRUE;
+  }
+
+  //
+  // Calculate physical address of PDE
+  //
+  PhysicalAddress = *PageDirPtrTableEntry & (((1ULL << MaxPhyAddrBits) - 1) <<
+                                             12);
+  PhysicalAddress |= (((UINT64)LinearAddress >> 21) & 0x1FF) << 3;
+
+  ASSERT ((PhysicalAddress & (sizeof (*PageDirEntry) - 1)) == 0);
+
+  PageDirEntry = (UINT64 *)(UINTN)PhysicalAddress;
+
+  //
+  // Check whether a PDE or a 2MiB page entry is present
+  //
+  if ((*PageDirEntry & BIT0) == 0) {
+    return FALSE;
+  }
+
+  //
+  // Check if PDE maps a 2MiB page
+  //
+  if ((*PageDirEntry & BIT7) != 0) {
+    return TRUE;
+  }
+
+  //
+  // Calculate physical address of PTE
+  //
+  PhysicalAddress = *PageDirEntry & (((1ULL << MaxPhyAddrBits) - 1) << 12);
+  PhysicalAddress |= (((UINT64)LinearAddress >> 12) & 0x1FF) << 3;
+
+  ASSERT ((PhysicalAddress & (sizeof (*PageTableEntry) - 1)) == 0);
+
+  PageTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
+
+  //
+  // Check if PTE maps a 4KiB page
+  //
+  if ((*PageTableEntry & BIT0) == 0) {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/**
+  Check if a linear address is valid by walking the page tables in 32-bit paging
+  mode.
+
+  @param[in]  Cr3             CR3 control register.
+  @param[in]  Cr4             CR4 control register.
+  @param[in]  LinearAddress   Linear address to be checked.
+**/
+STATIC
+BOOLEAN
+Do32BitPagingModeCheck (
+  IN UINTN            Cr3,
+  IN UINTN            Cr4,
+  IN UINTN            LinearAddress
+  )
+{
+  UINT64 PhysicalAddress;
+  UINT32 *PageDirEntry;
+  UINT32 *PageTableEntry;
+
+  if (LinearAddress > MAX_UINT32) {
+    return FALSE;
+  }
+
+  //
+  // Calculate physical address of PDE
+  //
+  PhysicalAddress = (UINT32)Cr3 & (((1ULL << 20) - 1) << 12);
+  PhysicalAddress |= (((UINT32)LinearAddress >> 22) & 0x3FF) << 2;
+
+  ASSERT ((PhysicalAddress & (sizeof (*PageDirEntry) - 1)) == 0);
+
+  PageDirEntry = (UINT32 *)(UINTN)PhysicalAddress;
+
+  //
+  // Check whether a PTE or a 4MiB page is present
+  //
+  if ((*PageDirEntry & BIT0) == 0) {
+    return FALSE;
+  }
+
+  //
+  // Check if PDE maps a 4MiB page
+  //
+  if ((Cr4 & BIT4) != 0 && (*PageDirEntry & BIT7) != 0) {
+    return TRUE;
+  }
+
+  //
+  // Calculate physical address of PTE
+  //
+  PhysicalAddress = *PageDirEntry & (((1ULL << 20) - 1) << 12);
+  PhysicalAddress |= (((UINT32)LinearAddress >> 12) & 0x3FF) << 2;
+
+  ASSERT ((PhysicalAddress & (sizeof (*PageTableEntry) - 1)) == 0);
+
+  PageTableEntry = (UINT32 *)(UINTN)PhysicalAddress;
+
+  //
+  // Check if PTE maps a 4KiB page
+  //
+  if ((*PageTableEntry & BIT0) == 0) {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/**
+  Check if a linear address is valid by walking the page tables in PAE paging
+  mode.
+
+  @param[in]  Cr3             CR3 control register.
+  @param[in]  MaxPhyAddrBits  MAXPHYADDRBITS bits.
+  @param[in]  LinearAddress   Linear address to be checked.
+**/
+STATIC
+BOOLEAN
+DoPAEPagingModeCheck (
+  IN UINTN            Cr3,
+  IN UINT8            MaxPhyAddrBits,
+  IN UINTN            LinearAddress
+  )
+{
+  UINT64 PhysicalAddress;
+  UINT64 *PageDirPtrTableEntry;
+  UINT64 *PageDirEntry;
+  UINT64 *PageTableEntry;
+
+  if (LinearAddress > MAX_UINT32) {
+    return FALSE;
+  }
+
+  //
+  // Calculate physical address of PDPTE
+  //
+  PhysicalAddress = (UINT32)Cr3 >> 5;
+
+  //
+  // Select PDPTE register
+  //
+  PhysicalAddress +=
+    ((UINT32)LinearAddress >> 30) * sizeof (*PageDirPtrTableEntry);
+
+  PageDirPtrTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
+
+  //
+  // Check if PDE is present
+  //
+  if ((*PageDirPtrTableEntry & BIT0) == 0) {
+    return FALSE;
+  }
+
+  PhysicalAddress = *PageDirPtrTableEntry & (((1ULL << MaxPhyAddrBits) - 1) <<
+                                             12);
+  PhysicalAddress |= ((LinearAddress >> 21) & 0x1FF) << 3;
+  ASSERT ((PhysicalAddress & (sizeof (*PageDirEntry) - 1)) == 0);
+
+  PageDirEntry = (UINT64 *)(UINTN)PhysicalAddress;
+
+  //
+  // Check whether a PTE or a 2MiB page is present
+  //
+  if ((*PageDirEntry & BIT0) == 0) {
+    return FALSE;
+  }
+
+  //
+  // Check if PDE maps a 2MiB page
+  //
+  if ((*PageDirEntry & BIT7) != 0) {
+    return TRUE;
+  }
+
+  //
+  // Calculate physical address of PTE
+  //
+  PhysicalAddress = *PageDirEntry & (((1ULL << MaxPhyAddrBits) - 1) << 12);
+  PhysicalAddress |= ((LinearAddress >> 12) & 0x1FF) << 3;
+  ASSERT ((PhysicalAddress & (sizeof (*PageTableEntry) - 1)) == 0);
+
+  PageTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
+
+  //
+  // Check if PTE maps a 4KiB page
+  //
+  if ((*PageTableEntry & BIT0) == 0) {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/**
+  Check if a linear address is valid.
+
+  @param[in]  Cr0            CR0 control register.
+  @param[in]  Cr3            CR3 control register.
+  @param[in]  Cr4            CR4 control register.
+  @param[in]  LinearAddress  Linear address to be checked.
+**/
+BOOLEAN
+IsLinearAddressValid (
+  IN  UINTN              Cr0,
+  IN  UINTN              Cr3,
+  IN  UINTN              Cr4,
+  IN  UINTN              LinearAddress
+  )
+{
+  UINT32                  Eax;
+  UINT32                  Edx;
+  UINT8                   MaxPhyAddrBits;
+  MSR_IA32_EFER_REGISTER  Msr;
+  BOOLEAN                 AddressValid;
+
+  //
+  // Check for valid input parameters
+  //
+  if (Cr0 == 0 || Cr4 == 0 || LinearAddress == 0) {
+    return FALSE;
+  }
+
+  //
+  // Check if paging is disabled
+  //
+  if ((Cr0 & BIT31) == 0) {
+    //
+    // If CR4.PAE bit is set, then the linear (or physical) address supports
+    // only up to 36 bits.
+    //
+    if (((Cr4 & BIT5) != 0 && (UINT64)LinearAddress > 0xFFFFFFFFFULL) ||
+        LinearAddress > 0xFFFFFFFF) {
+      return FALSE;
+    }
+
+    return TRUE;
+  }
+
+  //
+  // Paging can be enabled only if CR0.PE bit is set
+  //
+  if ((Cr0 & BIT0) == 0) {
+    return FALSE;
+  }
+
+  //
+  // CR3 register cannot be zero if paging is enabled
+  //
+  if (Cr3 == 0) {
+    return FALSE;
+  }
+
+  //
+  // Get MAXPHYADDR bits
+  //
+  AsmCpuid (0x80000000, &Eax, NULL, NULL, NULL);
+  if (Eax >= 0x80000008) {
+    AsmCpuid (0x80000008, &Eax, NULL, NULL, NULL);
+    MaxPhyAddrBits = (UINT8)Eax;
+  } else {
+    AsmCpuid (1, NULL, NULL, NULL, &Edx);
+    if ((Edx & BIT6) != 0) {
+      MaxPhyAddrBits = 36;
+    } else {
+      MaxPhyAddrBits = 32;
+    }
+  }
+
+  ASSERT (MaxPhyAddrBits > 0);
+
+  AddressValid = FALSE;
+
+  //
+  // check if CR4.PAE bit is not set
+  //
+  if ((Cr4 & BIT5) == 0) {
+    //
+    // Check if linear address is valid in 32-bit paging mode
+    //
+    AddressValid = Do32BitPagingModeCheck (Cr3, Cr4, LinearAddress);
+  } else {
+    if (MaxPhyAddrBits > 52) {
+      return FALSE;
+    }
+
+    Msr.Uint64 = AsmReadMsr64 (MSR_IA32_EFER);
+
+    if (Msr.Bits.LME == 0) {
+      //
+      // Check if linear address is valid in PAE paging mode
+      //
+      AddressValid = DoPAEPagingModeCheck (Cr3, MaxPhyAddrBits, LinearAddress);
+    } else {
+      //
+      // Check if linear address is valid in 4-level paging mode
+      //
+      AddressValid = Do4LevelPagingModeCheck (Cr3, MaxPhyAddrBits,
+                                              LinearAddress);
+    }
+  }
+
+  return AddressValid;
+}
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
index ec46c2d9d3..1b51034c25 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
@@ -330,5 +330,21 @@ GetPdbFileName (
   OUT CHAR8    **PdbFileName
   );
 
+/**
+  Check if a linear address is valid.
+
+  @param[in]  Cr0            CR0 control register.
+  @param[in]  Cr3            CR3 control register.
+  @param[in]  Cr4            CR4 control register.
+  @param[in]  LinearAddress  Linear address to be checked.
+**/
+BOOLEAN
+IsLinearAddressValid (
+  IN  UINTN              Cr0,
+  IN  UINTN              Cr3,
+  IN  UINTN              Cr4,
+  IN  UINTN              LinearAddress
+  );
+
 #endif
 
-- 
2.14.3



^ permalink raw reply related	[flat|nested] 82+ messages in thread

* [RFC v4 5/6] UefiCpuPkg/CpuExceptionHandlerLib: Ensure valid frame/stack pointers
  2017-12-29  3:48   ` [RFC v4 0/6] Stack trace support in X64 exception handling Paulo Alcantara
                       ` (3 preceding siblings ...)
  2017-12-29  4:39     ` [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add helper to valid memory addresses Paulo Alcantara
@ 2017-12-29  4:39     ` Paulo Alcantara
  2018-01-03  8:45       ` 答复: " Fan Jeff
  2018-01-04  1:07       ` Yao, Jiewen
  2017-12-29  4:39     ` [RFC v4 6/6] UefiCpuPkg/CpuExceptionHandlerLib: Correctly print IP addresses Paulo Alcantara
                       ` (2 subsequent siblings)
  7 siblings, 2 replies; 82+ messages in thread
From: Paulo Alcantara @ 2017-12-29  4:39 UTC (permalink / raw)
  To: edk2-devel; +Cc: Paulo Alcantara, Eric Dong, Laszlo Ersek

Validate all possible memory dereferences during stack traces in IA32
and X64 CPU exceptions.

Contributed-under: TianoCore Contribution Agreement 1.1
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Requested-by: Brian Johnson <brian.johnson@hpe.com>
Requested-by: Jiewen Yao <jiewen.yao@intel.com>
Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c | 143 +++++++++++++++++++-
 UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c  |  75 +++++++++-
 2 files changed, 210 insertions(+), 8 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
index 25e02fbbc1..9b52d4f6d2 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
@@ -398,6 +398,96 @@ DumpCpuContext (
     );
 }
 
+/**
+  Check if a logical address is valid.
+
+  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
+  @param[in]  SegmentSelector    Segment selector.
+  @param[in]  Offset             Offset or logical address.
+**/
+STATIC
+BOOLEAN
+IsLogicalAddressValid (
+  IN  EFI_SYSTEM_CONTEXT   SystemContext,
+  IN  UINT16               SegmentSelector,
+  IN  UINTN                Offset
+  )
+{
+  IA32_SEGMENT_DESCRIPTOR  *SegmentDescriptor;
+  UINT32                   SegDescBase;
+  UINT32                   SegDescLimit;
+  UINTN                    SegDescLimitInBytes;
+
+  //
+  // Check for valid input parameters
+  //
+  if (SegmentSelector == 0 || Offset == 0) {
+    return FALSE;
+  }
+
+  //
+  // Check whether to look for a segment descriptor in GDT or LDT table
+  //
+  if ((SegmentSelector & BIT2) == 0) {
+    //
+    // Get segment descriptor from GDT table
+    //
+    SegmentDescriptor =
+      (IA32_SEGMENT_DESCRIPTOR *)(
+        (UINTN)SystemContext.SystemContextIa32->Gdtr[0] +
+        ((SegmentSelector >> 3) * 8)
+        );
+  } else {
+    //
+    // Get segment descriptor from LDT table
+    //
+    SegmentDescriptor =
+      (IA32_SEGMENT_DESCRIPTOR *)(
+        (UINTN)SystemContext.SystemContextIa32->Ldtr +
+        ((SegmentSelector >> 3) * 8)
+        );
+  }
+
+  //
+  // Get segment descriptor's base address
+  //
+  SegDescBase = SegmentDescriptor->Bits.BaseLow |
+    (SegmentDescriptor->Bits.BaseMid << 16) |
+    (SegmentDescriptor->Bits.BaseHigh << 24);
+
+  //
+  // Get segment descriptor's limit
+  //
+  SegDescLimit = SegmentDescriptor->Bits.LimitLow |
+    (SegmentDescriptor->Bits.LimitHigh << 16);
+
+  //
+  // Calculate segment descriptor's limit in bytes
+  //
+  if (SegmentDescriptor->Bits.G == 1) {
+    SegDescLimitInBytes = (UINTN)SegDescLimit * SIZE_4KB;
+  } else {
+    SegDescLimitInBytes = SegDescLimit;
+  }
+
+  //
+  // Make sure to not access beyond a segment limit boundary
+  //
+  if (Offset + SegDescBase > SegDescLimitInBytes) {
+    return FALSE;
+  }
+
+  //
+  // Check if the translated logical address (or linear address) is valid
+  //
+  return IsLinearAddressValid (
+    SystemContext.SystemContextIa32->Cr0,
+    SystemContext.SystemContextIa32->Cr3,
+    SystemContext.SystemContextIa32->Cr4,
+    Offset + SegDescBase
+    );
+}
+
 /**
   Dump stack trace.
 
@@ -459,6 +549,20 @@ DumpStackTrace (
   InternalPrintMessage ("\nCall trace:\n");
 
   for (;;) {
+    //
+    // Check for valid frame pointer
+    //
+    if (!IsLogicalAddressValid (SystemContext,
+                                SystemContext.SystemContextIa32->Ss,
+                                (UINTN)Ebp + 4) ||
+        !IsLogicalAddressValid (SystemContext,
+                                SystemContext.SystemContextIa32->Ss,
+                                (UINTN)Ebp)) {
+      InternalPrintMessage ("%a: attempted to dereference an invalid frame "
+                            "pointer at 0x%08x\n", __FUNCTION__, Ebp);
+      break;
+    }
+
     //
     // Print stack frame in the following format:
     //
@@ -588,6 +692,16 @@ DumpImageModuleNames (
   // Walk through call stack and find next module names
   //
   for (;;) {
+    if (!IsLogicalAddressValid (SystemContext,
+                                SystemContext.SystemContextIa32->Ss,
+                                (UINTN)Ebp) ||
+        !IsLogicalAddressValid (SystemContext,
+                                SystemContext.SystemContextIa32->Ss,
+                                (UINTN)Ebp + 4)) {
+      InternalPrintMessage ("%a: attempted to dereference an invalid frame "
+                            "pointer at 0x%08x\n", __FUNCTION__, Ebp);
+    }
+
     //
     // Set EIP with return address from current stack frame
     //
@@ -651,16 +765,23 @@ DumpImageModuleNames (
 /**
   Dump stack contents.
 
-  @param[in]  CurrentEsp         Current stack pointer address.
+  @param[in]  SystemContext       Pointer to EFI_SYSTEM_CONTEXT.
   @param[in]  UnwoundStacksCount  Count of unwound stack frames.
 **/
 STATIC
 VOID
 DumpStackContents (
-  IN UINT32  CurrentEsp,
-  IN INTN    UnwoundStacksCount
+  IN  EFI_SYSTEM_CONTEXT  SystemContext,
+  IN  INTN                UnwoundStacksCount
   )
 {
+  UINT32 CurrentEsp;
+
+  //
+  // Get current stack pointer
+  //
+  CurrentEsp = SystemContext.SystemContextIa32->Esp;
+
   //
   // Check for proper stack alignment
   //
@@ -674,6 +795,20 @@ DumpStackContents (
   //
   InternalPrintMessage ("\nStack dump:\n");
   while (UnwoundStacksCount-- > 0) {
+    //
+    // Check for a valid stack pointer address
+    //
+    if (!IsLogicalAddressValid (SystemContext,
+                                SystemContext.SystemContextIa32->Ss,
+                                (UINTN)CurrentEsp) ||
+        !IsLogicalAddressValid (SystemContext,
+                                SystemContext.SystemContextIa32->Ss,
+                                (UINTN)CurrentEsp + 4)) {
+      InternalPrintMessage ("%a: attempted to dereference an invalid stack "
+                            "pointer at 0x%08x\n", __FUNCTION__, CurrentEsp);
+      break;
+    }
+
     InternalPrintMessage (
       "0x%08x: %08x %08x\n",
       CurrentEsp,
@@ -720,5 +855,5 @@ DumpImageAndCpuContent (
   //
   // Dump stack contents
   //
-  DumpStackContents (SystemContext.SystemContextIa32->Esp, UnwoundStacksCount);
+  DumpStackContents (SystemContext, UnwoundStacksCount);
 }
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
index d3a3878b3d..8067c34122 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
@@ -401,16 +401,26 @@ DumpCpuContext (
 /**
   Dump stack contents.
 
-  @param[in]  CurrentRsp         Current stack pointer address.
+  @param[in]  SystemContext       Pointer to EFI_SYSTEM_CONTEXT.
   @param[in]  UnwoundStacksCount  Count of unwound stack frames.
 **/
 STATIC
 VOID
 DumpStackContents (
-  IN UINT64  CurrentRsp,
-  IN INTN    UnwoundStacksCount
+  IN  EFI_SYSTEM_CONTEXT  SystemContext,
+  IN  INTN                UnwoundStacksCount
   )
 {
+  UINT64  CurrentRsp;
+  UINTN   Cr0;
+  UINTN   Cr3;
+  UINTN   Cr4;
+
+  //
+  // Get current stack pointer
+  //
+  CurrentRsp = SystemContext.SystemContextX64->Rsp;
+
   //
   // Check for proper stack pointer alignment
   //
@@ -419,11 +429,28 @@ DumpStackContents (
     return;
   }
 
+  //
+  // Get system control registers
+  //
+  Cr0 = SystemContext.SystemContextX64->Cr0;
+  Cr3 = SystemContext.SystemContextX64->Cr3;
+  Cr4 = SystemContext.SystemContextX64->Cr4;
+
   //
   // Dump out stack contents
   //
   InternalPrintMessage ("\nStack dump:\n");
   while (UnwoundStacksCount-- > 0) {
+    //
+    // Check for a valid stack pointer address
+    //
+    if (!IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)CurrentRsp) ||
+        !IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)CurrentRsp + 8)) {
+      InternalPrintMessage ("%a: attempted to dereference an invalid stack "
+                            "pointer at 0x%016lx\n", __FUNCTION__, CurrentRsp);
+      break;
+    }
+
     InternalPrintMessage (
       "0x%016lx: %016lx %016lx\n",
       CurrentRsp,
@@ -457,6 +484,9 @@ DumpImageModuleNames (
   CHAR8       *PdbFileName;
   UINT64      Rbp;
   UINTN       LastImageBase;
+  UINTN       Cr0;
+  UINTN       Cr3;
+  UINTN       Cr4;
 
   //
   // Set current RIP address
@@ -516,10 +546,27 @@ DumpImageModuleNames (
     InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
   }
 
+  //
+  // Get system control registers
+  //
+  Cr0 = SystemContext.SystemContextX64->Cr0;
+  Cr3 = SystemContext.SystemContextX64->Cr3;
+  Cr4 = SystemContext.SystemContextX64->Cr4;
+
   //
   // Walk through call stack and find next module names
   //
   for (;;) {
+    //
+    // Check for a valid frame pointer
+    //
+    if (!IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)Rbp + 8) ||
+        !IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)Rbp)) {
+      InternalPrintMessage ("%a: attempted to dereference an invalid frame "
+                            "pointer at 0x%016lx\n", __FUNCTION__, Rbp);
+      break;
+    }
+
     //
     // Set RIP with return address from current stack frame
     //
@@ -604,6 +651,9 @@ DumpStackTrace (
   UINT64  Rbp;
   UINTN   ImageBase;
   CHAR8   *PdbFileName;
+  UINTN   Cr0;
+  UINTN   Cr3;
+  UINTN   Cr4;
 
   //
   // Set current RIP address
@@ -634,12 +684,29 @@ DumpStackTrace (
   //
   *UnwoundStacksCount = 1;
 
+  //
+  // Get system control registers
+  //
+  Cr0 = SystemContext.SystemContextX64->Cr0;
+  Cr3 = SystemContext.SystemContextX64->Cr3;
+  Cr4 = SystemContext.SystemContextX64->Cr4;
+
   //
   // Print out back trace
   //
   InternalPrintMessage ("\nCall trace:\n");
 
   for (;;) {
+    //
+    // Check for valid frame pointer
+    //
+    if (!IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)Rbp + 8) ||
+        !IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)Rbp)) {
+      InternalPrintMessage ("%a: attempted to dereference an invalid frame "
+                            "pointer at 0x%016lx\n", __FUNCTION__, Rbp);
+      break;
+    }
+
     //
     // Print stack frame in the following format:
     //
@@ -727,5 +794,5 @@ DumpImageAndCpuContent (
   //
   // Dump stack contents
   //
-  DumpStackContents (SystemContext.SystemContextX64->Rsp, UnwoundStacksCount);
+  DumpStackContents (SystemContext, UnwoundStacksCount);
 }
-- 
2.14.3



^ permalink raw reply related	[flat|nested] 82+ messages in thread

* [RFC v4 6/6] UefiCpuPkg/CpuExceptionHandlerLib: Correctly print IP addresses
  2017-12-29  3:48   ` [RFC v4 0/6] Stack trace support in X64 exception handling Paulo Alcantara
                       ` (4 preceding siblings ...)
  2017-12-29  4:39     ` [RFC v4 5/6] UefiCpuPkg/CpuExceptionHandlerLib: Ensure valid frame/stack pointers Paulo Alcantara
@ 2017-12-29  4:39     ` Paulo Alcantara
  2018-01-03  8:46       ` 答复: " Fan Jeff
  2018-01-04  0:59     ` [RFC v4 0/6] Stack trace support in X64 exception handling Yao, Jiewen
  2018-01-15  0:23     ` [RFC v5 0/8] " Paulo Alcantara
  7 siblings, 1 reply; 82+ messages in thread
From: Paulo Alcantara @ 2017-12-29  4:39 UTC (permalink / raw)
  To: edk2-devel; +Cc: Paulo Alcantara, Eric Dong, Laszlo Ersek

Remove the supurious '- 1' when calculating the IP addresses during the
stack traces.

Contributed-under: TianoCore Contribution Agreement 1.1
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Requested-by: Jeff Fan <vanjeff_919@hotmail.com>
Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c | 2 +-
 UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
index 9b52d4f6d2..e2a3341286 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
@@ -573,7 +573,7 @@ DumpStackTrace (
       *UnwoundStacksCount - 1,
       Eip,
       ImageBase,
-      Eip - ImageBase - 1,
+      Eip - ImageBase,
       Ebp,
       PdbFileName
       );
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
index 8067c34122..5ae3aa4e73 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
@@ -717,7 +717,7 @@ DumpStackTrace (
       *UnwoundStacksCount - 1,
       Rip,
       ImageBase,
-      Rip - ImageBase - 1,
+      Rip - ImageBase,
       Rbp,
       PdbFileName
       );
-- 
2.14.3



^ permalink raw reply related	[flat|nested] 82+ messages in thread

* 答复: [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add helper to valid memory addresses
  2017-12-29  4:39     ` [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add helper to valid memory addresses Paulo Alcantara
@ 2018-01-03  8:42       ` Fan Jeff
  2018-01-03 14:45         ` Paulo Alcantara
  2018-01-03 16:59       ` Brian J. Johnson
  2018-01-04  1:36       ` Yao, Jiewen
  2 siblings, 1 reply; 82+ messages in thread
From: Fan Jeff @ 2018-01-03  8:42 UTC (permalink / raw)
  To: Paulo Alcantara, edk2-devel@lists.01.org; +Cc: Laszlo Ersek, Eric Dong

Paul,

+  //
+  // Calculate physical address of PML4E
+  //
+  PhysicalAddress = (UINT64)Cr3 & (((1ULL << MaxPhyAddrBits) - 1) << 12);
+  PhysicalAddress |= (((UINT64)LinearAddress >> 39) & 0x1FF) << 3;
+

Should not pass VS build, instead you could use LShiftU64/RShiftU64 to do 64bit shift operation as below:

  PhysicalAddress = (UINT64)Cr3 & LShiftU64 (LShiftU64 (1, MaxPhyAddrBits) - 1, 12);
  PhysicalAddress |= LShiftU64 (RShiftU64((UINT64)LinearAddress, 39) & 0x1FF), 3);

Jeff

发件人: Paulo Alcantara<mailto:paulo@paulo.ac>
发送时间: 2017年12月29日 12:41
收件人: edk2-devel@lists.01.org<mailto:edk2-devel@lists.01.org>
抄送: Laszlo Ersek<mailto:lersek@redhat.com>; Eric Dong<mailto:eric.dong@intel.com>
主题: [edk2] [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add helper to valid memory addresses

Introduce IsLinearAddressValid() function that will be used for
validating memory addresses that would get dereferenced during stack
traces in IA32 and X64 CPU exceptions.

Contributed-under: TianoCore Contribution Agreement 1.1
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Requested-by: Brian Johnson <brian.johnson@hpe.com>
Requested-by: Jiewen Yao <jiewen.yao@intel.com>
Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c | 382 ++++++++++++++++++++
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h |  16 +
 2 files changed, 398 insertions(+)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
index 867c5c01d6..52b3eb1463 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
@@ -14,6 +14,9 @@

 #include "CpuExceptionCommon.h"

+#include <Register/Msr.h>
+#include <Library/DebugLib.h>
+
 //
 // Error code flag indicating whether or not an error code will be
 // pushed on the stack if an exception occurs.
@@ -194,3 +197,382 @@ GetPdbFileName (
     }
   }
 }
+
+/**
+  Check if a linear address is valid by walking the page tables in 4-level
+  paging mode.
+
+  @param[in]  Cr3             CR3 control register.
+  @param[in]  MaxPhyAddrBits  MAXPHYADDRBITS bits.
+  @param[in]  LinearAddress   Linear address to be checked.
+**/
+STATIC
+BOOLEAN
+Do4LevelPagingModeCheck (
+  IN UINTN            Cr3,
+  IN UINT8            MaxPhyAddrBits,
+  IN UINTN            LinearAddress
+  )
+{
+  UINT64 PhysicalAddress;
+  UINT64 *Pml4TableEntry;
+  UINT64 *PageDirPtrTableEntry;
+  UINT64 *PageDirEntry;
+  UINT64 *PageTableEntry;
+
+  //
+  // In 4-level paging mode, linear addresses are 48 bits wide
+  //
+  if ((UINT64)LinearAddress > (1ULL << 48) - 1) {
+    return FALSE;
+  }
+
+  //
+  // Calculate physical address of PML4E
+  //
+  PhysicalAddress = (UINT64)Cr3 & (((1ULL << MaxPhyAddrBits) - 1) << 12);
+  PhysicalAddress |= (((UINT64)LinearAddress >> 39) & 0x1FF) << 3;
+
+  ASSERT ((PhysicalAddress & (sizeof (*Pml4TableEntry) - 1)) == 0);
+
+  Pml4TableEntry = (UINT64 *)(UINTN)PhysicalAddress;
+
+  //
+  // Check if a PDPTE is present
+  //
+  if ((*Pml4TableEntry & BIT0) == 0) {
+    return FALSE;
+  }
+
+  //
+  // Calculate physical address of PDPTE
+  //
+  PhysicalAddress = *Pml4TableEntry & (((1ULL << MaxPhyAddrBits) - 1) << 12);
+  PhysicalAddress |= (((UINT64)LinearAddress >> 30) & 0x1FF) << 3;
+
+  ASSERT ((PhysicalAddress & (sizeof (*PageDirPtrTableEntry) - 1)) == 0);
+
+  PageDirPtrTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
+
+  //
+  // Check whether a PDPTE or 1GiB page entry is present
+  //
+  if ((*PageDirPtrTableEntry & BIT0) == 0) {
+    return FALSE;
+  }
+
+  //
+  // Check if PDPTE maps an 1GiB page
+  //
+  if ((*PageDirPtrTableEntry & BIT7) != 0) {
+    return TRUE;
+  }
+
+  //
+  // Calculate physical address of PDE
+  //
+  PhysicalAddress = *PageDirPtrTableEntry & (((1ULL << MaxPhyAddrBits) - 1) <<
+                                             12);
+  PhysicalAddress |= (((UINT64)LinearAddress >> 21) & 0x1FF) << 3;
+
+  ASSERT ((PhysicalAddress & (sizeof (*PageDirEntry) - 1)) == 0);
+
+  PageDirEntry = (UINT64 *)(UINTN)PhysicalAddress;
+
+  //
+  // Check whether a PDE or a 2MiB page entry is present
+  //
+  if ((*PageDirEntry & BIT0) == 0) {
+    return FALSE;
+  }
+
+  //
+  // Check if PDE maps a 2MiB page
+  //
+  if ((*PageDirEntry & BIT7) != 0) {
+    return TRUE;
+  }
+
+  //
+  // Calculate physical address of PTE
+  //
+  PhysicalAddress = *PageDirEntry & (((1ULL << MaxPhyAddrBits) - 1) << 12);
+  PhysicalAddress |= (((UINT64)LinearAddress >> 12) & 0x1FF) << 3;
+
+  ASSERT ((PhysicalAddress & (sizeof (*PageTableEntry) - 1)) == 0);
+
+  PageTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
+
+  //
+  // Check if PTE maps a 4KiB page
+  //
+  if ((*PageTableEntry & BIT0) == 0) {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/**
+  Check if a linear address is valid by walking the page tables in 32-bit paging
+  mode.
+
+  @param[in]  Cr3             CR3 control register.
+  @param[in]  Cr4             CR4 control register.
+  @param[in]  LinearAddress   Linear address to be checked.
+**/
+STATIC
+BOOLEAN
+Do32BitPagingModeCheck (
+  IN UINTN            Cr3,
+  IN UINTN            Cr4,
+  IN UINTN            LinearAddress
+  )
+{
+  UINT64 PhysicalAddress;
+  UINT32 *PageDirEntry;
+  UINT32 *PageTableEntry;
+
+  if (LinearAddress > MAX_UINT32) {
+    return FALSE;
+  }
+
+  //
+  // Calculate physical address of PDE
+  //
+  PhysicalAddress = (UINT32)Cr3 & (((1ULL << 20) - 1) << 12);
+  PhysicalAddress |= (((UINT32)LinearAddress >> 22) & 0x3FF) << 2;
+
+  ASSERT ((PhysicalAddress & (sizeof (*PageDirEntry) - 1)) == 0);
+
+  PageDirEntry = (UINT32 *)(UINTN)PhysicalAddress;
+
+  //
+  // Check whether a PTE or a 4MiB page is present
+  //
+  if ((*PageDirEntry & BIT0) == 0) {
+    return FALSE;
+  }
+
+  //
+  // Check if PDE maps a 4MiB page
+  //
+  if ((Cr4 & BIT4) != 0 && (*PageDirEntry & BIT7) != 0) {
+    return TRUE;
+  }
+
+  //
+  // Calculate physical address of PTE
+  //
+  PhysicalAddress = *PageDirEntry & (((1ULL << 20) - 1) << 12);
+  PhysicalAddress |= (((UINT32)LinearAddress >> 12) & 0x3FF) << 2;
+
+  ASSERT ((PhysicalAddress & (sizeof (*PageTableEntry) - 1)) == 0);
+
+  PageTableEntry = (UINT32 *)(UINTN)PhysicalAddress;
+
+  //
+  // Check if PTE maps a 4KiB page
+  //
+  if ((*PageTableEntry & BIT0) == 0) {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/**
+  Check if a linear address is valid by walking the page tables in PAE paging
+  mode.
+
+  @param[in]  Cr3             CR3 control register.
+  @param[in]  MaxPhyAddrBits  MAXPHYADDRBITS bits.
+  @param[in]  LinearAddress   Linear address to be checked.
+**/
+STATIC
+BOOLEAN
+DoPAEPagingModeCheck (
+  IN UINTN            Cr3,
+  IN UINT8            MaxPhyAddrBits,
+  IN UINTN            LinearAddress
+  )
+{
+  UINT64 PhysicalAddress;
+  UINT64 *PageDirPtrTableEntry;
+  UINT64 *PageDirEntry;
+  UINT64 *PageTableEntry;
+
+  if (LinearAddress > MAX_UINT32) {
+    return FALSE;
+  }
+
+  //
+  // Calculate physical address of PDPTE
+  //
+  PhysicalAddress = (UINT32)Cr3 >> 5;
+
+  //
+  // Select PDPTE register
+  //
+  PhysicalAddress +=
+    ((UINT32)LinearAddress >> 30) * sizeof (*PageDirPtrTableEntry);
+
+  PageDirPtrTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
+
+  //
+  // Check if PDE is present
+  //
+  if ((*PageDirPtrTableEntry & BIT0) == 0) {
+    return FALSE;
+  }
+
+  PhysicalAddress = *PageDirPtrTableEntry & (((1ULL << MaxPhyAddrBits) - 1) <<
+                                             12);
+  PhysicalAddress |= ((LinearAddress >> 21) & 0x1FF) << 3;
+  ASSERT ((PhysicalAddress & (sizeof (*PageDirEntry) - 1)) == 0);
+
+  PageDirEntry = (UINT64 *)(UINTN)PhysicalAddress;
+
+  //
+  // Check whether a PTE or a 2MiB page is present
+  //
+  if ((*PageDirEntry & BIT0) == 0) {
+    return FALSE;
+  }
+
+  //
+  // Check if PDE maps a 2MiB page
+  //
+  if ((*PageDirEntry & BIT7) != 0) {
+    return TRUE;
+  }
+
+  //
+  // Calculate physical address of PTE
+  //
+  PhysicalAddress = *PageDirEntry & (((1ULL << MaxPhyAddrBits) - 1) << 12);
+  PhysicalAddress |= ((LinearAddress >> 12) & 0x1FF) << 3;
+  ASSERT ((PhysicalAddress & (sizeof (*PageTableEntry) - 1)) == 0);
+
+  PageTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
+
+  //
+  // Check if PTE maps a 4KiB page
+  //
+  if ((*PageTableEntry & BIT0) == 0) {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/**
+  Check if a linear address is valid.
+
+  @param[in]  Cr0            CR0 control register.
+  @param[in]  Cr3            CR3 control register.
+  @param[in]  Cr4            CR4 control register.
+  @param[in]  LinearAddress  Linear address to be checked.
+**/
+BOOLEAN
+IsLinearAddressValid (
+  IN  UINTN              Cr0,
+  IN  UINTN              Cr3,
+  IN  UINTN              Cr4,
+  IN  UINTN              LinearAddress
+  )
+{
+  UINT32                  Eax;
+  UINT32                  Edx;
+  UINT8                   MaxPhyAddrBits;
+  MSR_IA32_EFER_REGISTER  Msr;
+  BOOLEAN                 AddressValid;
+
+  //
+  // Check for valid input parameters
+  //
+  if (Cr0 == 0 || Cr4 == 0 || LinearAddress == 0) {
+    return FALSE;
+  }
+
+  //
+  // Check if paging is disabled
+  //
+  if ((Cr0 & BIT31) == 0) {
+    //
+    // If CR4.PAE bit is set, then the linear (or physical) address supports
+    // only up to 36 bits.
+    //
+    if (((Cr4 & BIT5) != 0 && (UINT64)LinearAddress > 0xFFFFFFFFFULL) ||
+        LinearAddress > 0xFFFFFFFF) {
+      return FALSE;
+    }
+
+    return TRUE;
+  }
+
+  //
+  // Paging can be enabled only if CR0.PE bit is set
+  //
+  if ((Cr0 & BIT0) == 0) {
+    return FALSE;
+  }
+
+  //
+  // CR3 register cannot be zero if paging is enabled
+  //
+  if (Cr3 == 0) {
+    return FALSE;
+  }
+
+  //
+  // Get MAXPHYADDR bits
+  //
+  AsmCpuid (0x80000000, &Eax, NULL, NULL, NULL);
+  if (Eax >= 0x80000008) {
+    AsmCpuid (0x80000008, &Eax, NULL, NULL, NULL);
+    MaxPhyAddrBits = (UINT8)Eax;
+  } else {
+    AsmCpuid (1, NULL, NULL, NULL, &Edx);
+    if ((Edx & BIT6) != 0) {
+      MaxPhyAddrBits = 36;
+    } else {
+      MaxPhyAddrBits = 32;
+    }
+  }
+
+  ASSERT (MaxPhyAddrBits > 0);
+
+  AddressValid = FALSE;
+
+  //
+  // check if CR4.PAE bit is not set
+  //
+  if ((Cr4 & BIT5) == 0) {
+    //
+    // Check if linear address is valid in 32-bit paging mode
+    //
+    AddressValid = Do32BitPagingModeCheck (Cr3, Cr4, LinearAddress);
+  } else {
+    if (MaxPhyAddrBits > 52) {
+      return FALSE;
+    }
+
+    Msr.Uint64 = AsmReadMsr64 (MSR_IA32_EFER);
+
+    if (Msr.Bits.LME == 0) {
+      //
+      // Check if linear address is valid in PAE paging mode
+      //
+      AddressValid = DoPAEPagingModeCheck (Cr3, MaxPhyAddrBits, LinearAddress);
+    } else {
+      //
+      // Check if linear address is valid in 4-level paging mode
+      //
+      AddressValid = Do4LevelPagingModeCheck (Cr3, MaxPhyAddrBits,
+                                              LinearAddress);
+    }
+  }
+
+  return AddressValid;
+}
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
index ec46c2d9d3..1b51034c25 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
@@ -330,5 +330,21 @@ GetPdbFileName (
   OUT CHAR8    **PdbFileName
   );

+/**
+  Check if a linear address is valid.
+
+  @param[in]  Cr0            CR0 control register.
+  @param[in]  Cr3            CR3 control register.
+  @param[in]  Cr4            CR4 control register.
+  @param[in]  LinearAddress  Linear address to be checked.
+**/
+BOOLEAN
+IsLinearAddressValid (
+  IN  UINTN              Cr0,
+  IN  UINTN              Cr3,
+  IN  UINTN              Cr4,
+  IN  UINTN              LinearAddress
+  );
+
 #endif

--
2.14.3

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel


^ permalink raw reply related	[flat|nested] 82+ messages in thread

* 答复: [RFC v4 5/6] UefiCpuPkg/CpuExceptionHandlerLib: Ensure valid frame/stack pointers
  2017-12-29  4:39     ` [RFC v4 5/6] UefiCpuPkg/CpuExceptionHandlerLib: Ensure valid frame/stack pointers Paulo Alcantara
@ 2018-01-03  8:45       ` Fan Jeff
  2018-01-03 14:48         ` Paulo Alcantara
  2018-01-04  1:07       ` Yao, Jiewen
  1 sibling, 1 reply; 82+ messages in thread
From: Fan Jeff @ 2018-01-03  8:45 UTC (permalink / raw)
  To: Paulo Alcantara, edk2-devel@lists.01.org; +Cc: Laszlo Ersek, Eric Dong

Paulo,

+    if (!IsLogicalAddressValid (SystemContext,
+                                SystemContext.SystemContextIa32->Ss,
+                                (UINTN)Ebp) ||
+        !IsLogicalAddressValid (SystemContext,
+                                SystemContext.SystemContextIa32->Ss,
+                                (UINTN)Ebp + 4)) {

I don’t understand why you check both ebp and ebp+4, I think it’s enough to only check EBP (saved stack pointer address)

Jeff

发件人: Paulo Alcantara<mailto:paulo@paulo.ac>
发送时间: 2017年12月29日 12:41
收件人: edk2-devel@lists.01.org<mailto:edk2-devel@lists.01.org>
抄送: Laszlo Ersek<mailto:lersek@redhat.com>; Eric Dong<mailto:eric.dong@intel.com>
主题: [edk2] [RFC v4 5/6] UefiCpuPkg/CpuExceptionHandlerLib: Ensure valid frame/stack pointers

Validate all possible memory dereferences during stack traces in IA32
and X64 CPU exceptions.

Contributed-under: TianoCore Contribution Agreement 1.1
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Requested-by: Brian Johnson <brian.johnson@hpe.com>
Requested-by: Jiewen Yao <jiewen.yao@intel.com>
Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c | 143 +++++++++++++++++++-
 UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c  |  75 +++++++++-
 2 files changed, 210 insertions(+), 8 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
index 25e02fbbc1..9b52d4f6d2 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
@@ -398,6 +398,96 @@ DumpCpuContext (
     );
 }

+/**
+  Check if a logical address is valid.
+
+  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
+  @param[in]  SegmentSelector    Segment selector.
+  @param[in]  Offset             Offset or logical address.
+**/
+STATIC
+BOOLEAN
+IsLogicalAddressValid (
+  IN  EFI_SYSTEM_CONTEXT   SystemContext,
+  IN  UINT16               SegmentSelector,
+  IN  UINTN                Offset
+  )
+{
+  IA32_SEGMENT_DESCRIPTOR  *SegmentDescriptor;
+  UINT32                   SegDescBase;
+  UINT32                   SegDescLimit;
+  UINTN                    SegDescLimitInBytes;
+
+  //
+  // Check for valid input parameters
+  //
+  if (SegmentSelector == 0 || Offset == 0) {
+    return FALSE;
+  }
+
+  //
+  // Check whether to look for a segment descriptor in GDT or LDT table
+  //
+  if ((SegmentSelector & BIT2) == 0) {
+    //
+    // Get segment descriptor from GDT table
+    //
+    SegmentDescriptor =
+      (IA32_SEGMENT_DESCRIPTOR *)(
+        (UINTN)SystemContext.SystemContextIa32->Gdtr[0] +
+        ((SegmentSelector >> 3) * 8)
+        );
+  } else {
+    //
+    // Get segment descriptor from LDT table
+    //
+    SegmentDescriptor =
+      (IA32_SEGMENT_DESCRIPTOR *)(
+        (UINTN)SystemContext.SystemContextIa32->Ldtr +
+        ((SegmentSelector >> 3) * 8)
+        );
+  }
+
+  //
+  // Get segment descriptor's base address
+  //
+  SegDescBase = SegmentDescriptor->Bits.BaseLow |
+    (SegmentDescriptor->Bits.BaseMid << 16) |
+    (SegmentDescriptor->Bits.BaseHigh << 24);
+
+  //
+  // Get segment descriptor's limit
+  //
+  SegDescLimit = SegmentDescriptor->Bits.LimitLow |
+    (SegmentDescriptor->Bits.LimitHigh << 16);
+
+  //
+  // Calculate segment descriptor's limit in bytes
+  //
+  if (SegmentDescriptor->Bits.G == 1) {
+    SegDescLimitInBytes = (UINTN)SegDescLimit * SIZE_4KB;
+  } else {
+    SegDescLimitInBytes = SegDescLimit;
+  }
+
+  //
+  // Make sure to not access beyond a segment limit boundary
+  //
+  if (Offset + SegDescBase > SegDescLimitInBytes) {
+    return FALSE;
+  }
+
+  //
+  // Check if the translated logical address (or linear address) is valid
+  //
+  return IsLinearAddressValid (
+    SystemContext.SystemContextIa32->Cr0,
+    SystemContext.SystemContextIa32->Cr3,
+    SystemContext.SystemContextIa32->Cr4,
+    Offset + SegDescBase
+    );
+}
+
 /**
   Dump stack trace.

@@ -459,6 +549,20 @@ DumpStackTrace (
   InternalPrintMessage ("\nCall trace:\n");

   for (;;) {
+    //
+    // Check for valid frame pointer
+    //
+    if (!IsLogicalAddressValid (SystemContext,
+                                SystemContext.SystemContextIa32->Ss,
+                                (UINTN)Ebp + 4) ||
+        !IsLogicalAddressValid (SystemContext,
+                                SystemContext.SystemContextIa32->Ss,
+                                (UINTN)Ebp)) {
+      InternalPrintMessage ("%a: attempted to dereference an invalid frame "
+                            "pointer at 0x%08x\n", __FUNCTION__, Ebp);
+      break;
+    }
+
     //
     // Print stack frame in the following format:
     //
@@ -588,6 +692,16 @@ DumpImageModuleNames (
   // Walk through call stack and find next module names
   //
   for (;;) {
+    if (!IsLogicalAddressValid (SystemContext,
+                                SystemContext.SystemContextIa32->Ss,
+                                (UINTN)Ebp) ||
+        !IsLogicalAddressValid (SystemContext,
+                                SystemContext.SystemContextIa32->Ss,
+                                (UINTN)Ebp + 4)) {
+      InternalPrintMessage ("%a: attempted to dereference an invalid frame "
+                            "pointer at 0x%08x\n", __FUNCTION__, Ebp);
+    }
+
     //
     // Set EIP with return address from current stack frame
     //
@@ -651,16 +765,23 @@ DumpImageModuleNames (
 /**
   Dump stack contents.

-  @param[in]  CurrentEsp         Current stack pointer address.
+  @param[in]  SystemContext       Pointer to EFI_SYSTEM_CONTEXT.
   @param[in]  UnwoundStacksCount  Count of unwound stack frames.
 **/
 STATIC
 VOID
 DumpStackContents (
-  IN UINT32  CurrentEsp,
-  IN INTN    UnwoundStacksCount
+  IN  EFI_SYSTEM_CONTEXT  SystemContext,
+  IN  INTN                UnwoundStacksCount
   )
 {
+  UINT32 CurrentEsp;
+
+  //
+  // Get current stack pointer
+  //
+  CurrentEsp = SystemContext.SystemContextIa32->Esp;
+
   //
   // Check for proper stack alignment
   //
@@ -674,6 +795,20 @@ DumpStackContents (
   //
   InternalPrintMessage ("\nStack dump:\n");
   while (UnwoundStacksCount-- > 0) {
+    //
+    // Check for a valid stack pointer address
+    //
+    if (!IsLogicalAddressValid (SystemContext,
+                                SystemContext.SystemContextIa32->Ss,
+                                (UINTN)CurrentEsp) ||
+        !IsLogicalAddressValid (SystemContext,
+                                SystemContext.SystemContextIa32->Ss,
+                                (UINTN)CurrentEsp + 4)) {
+      InternalPrintMessage ("%a: attempted to dereference an invalid stack "
+                            "pointer at 0x%08x\n", __FUNCTION__, CurrentEsp);
+      break;
+    }
+
     InternalPrintMessage (
       "0x%08x: %08x %08x\n",
       CurrentEsp,
@@ -720,5 +855,5 @@ DumpImageAndCpuContent (
   //
   // Dump stack contents
   //
-  DumpStackContents (SystemContext.SystemContextIa32->Esp, UnwoundStacksCount);
+  DumpStackContents (SystemContext, UnwoundStacksCount);
 }
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
index d3a3878b3d..8067c34122 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
@@ -401,16 +401,26 @@ DumpCpuContext (
 /**
   Dump stack contents.

-  @param[in]  CurrentRsp         Current stack pointer address.
+  @param[in]  SystemContext       Pointer to EFI_SYSTEM_CONTEXT.
   @param[in]  UnwoundStacksCount  Count of unwound stack frames.
 **/
 STATIC
 VOID
 DumpStackContents (
-  IN UINT64  CurrentRsp,
-  IN INTN    UnwoundStacksCount
+  IN  EFI_SYSTEM_CONTEXT  SystemContext,
+  IN  INTN                UnwoundStacksCount
   )
 {
+  UINT64  CurrentRsp;
+  UINTN   Cr0;
+  UINTN   Cr3;
+  UINTN   Cr4;
+
+  //
+  // Get current stack pointer
+  //
+  CurrentRsp = SystemContext.SystemContextX64->Rsp;
+
   //
   // Check for proper stack pointer alignment
   //
@@ -419,11 +429,28 @@ DumpStackContents (
     return;
   }

+  //
+  // Get system control registers
+  //
+  Cr0 = SystemContext.SystemContextX64->Cr0;
+  Cr3 = SystemContext.SystemContextX64->Cr3;
+  Cr4 = SystemContext.SystemContextX64->Cr4;
+
   //
   // Dump out stack contents
   //
   InternalPrintMessage ("\nStack dump:\n");
   while (UnwoundStacksCount-- > 0) {
+    //
+    // Check for a valid stack pointer address
+    //
+    if (!IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)CurrentRsp) ||
+        !IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)CurrentRsp + 8)) {
+      InternalPrintMessage ("%a: attempted to dereference an invalid stack "
+                            "pointer at 0x%016lx\n", __FUNCTION__, CurrentRsp);
+      break;
+    }
+
     InternalPrintMessage (
       "0x%016lx: %016lx %016lx\n",
       CurrentRsp,
@@ -457,6 +484,9 @@ DumpImageModuleNames (
   CHAR8       *PdbFileName;
   UINT64      Rbp;
   UINTN       LastImageBase;
+  UINTN       Cr0;
+  UINTN       Cr3;
+  UINTN       Cr4;

   //
   // Set current RIP address
@@ -516,10 +546,27 @@ DumpImageModuleNames (
     InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
   }

+  //
+  // Get system control registers
+  //
+  Cr0 = SystemContext.SystemContextX64->Cr0;
+  Cr3 = SystemContext.SystemContextX64->Cr3;
+  Cr4 = SystemContext.SystemContextX64->Cr4;
+
   //
   // Walk through call stack and find next module names
   //
   for (;;) {
+    //
+    // Check for a valid frame pointer
+    //
+    if (!IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)Rbp + 8) ||
+        !IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)Rbp)) {
+      InternalPrintMessage ("%a: attempted to dereference an invalid frame "
+                            "pointer at 0x%016lx\n", __FUNCTION__, Rbp);
+      break;
+    }
+
     //
     // Set RIP with return address from current stack frame
     //
@@ -604,6 +651,9 @@ DumpStackTrace (
   UINT64  Rbp;
   UINTN   ImageBase;
   CHAR8   *PdbFileName;
+  UINTN   Cr0;
+  UINTN   Cr3;
+  UINTN   Cr4;

   //
   // Set current RIP address
@@ -634,12 +684,29 @@ DumpStackTrace (
   //
   *UnwoundStacksCount = 1;

+  //
+  // Get system control registers
+  //
+  Cr0 = SystemContext.SystemContextX64->Cr0;
+  Cr3 = SystemContext.SystemContextX64->Cr3;
+  Cr4 = SystemContext.SystemContextX64->Cr4;
+
   //
   // Print out back trace
   //
   InternalPrintMessage ("\nCall trace:\n");

   for (;;) {
+    //
+    // Check for valid frame pointer
+    //
+    if (!IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)Rbp + 8) ||
+        !IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)Rbp)) {
+      InternalPrintMessage ("%a: attempted to dereference an invalid frame "
+                            "pointer at 0x%016lx\n", __FUNCTION__, Rbp);
+      break;
+    }
+
     //
     // Print stack frame in the following format:
     //
@@ -727,5 +794,5 @@ DumpImageAndCpuContent (
   //
   // Dump stack contents
   //
-  DumpStackContents (SystemContext.SystemContextX64->Rsp, UnwoundStacksCount);
+  DumpStackContents (SystemContext, UnwoundStacksCount);
 }
--
2.14.3

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel


^ permalink raw reply related	[flat|nested] 82+ messages in thread

* 答复: [RFC v4 6/6] UefiCpuPkg/CpuExceptionHandlerLib: Correctly print IP addresses
  2017-12-29  4:39     ` [RFC v4 6/6] UefiCpuPkg/CpuExceptionHandlerLib: Correctly print IP addresses Paulo Alcantara
@ 2018-01-03  8:46       ` Fan Jeff
  0 siblings, 0 replies; 82+ messages in thread
From: Fan Jeff @ 2018-01-03  8:46 UTC (permalink / raw)
  To: Paulo Alcantara, edk2-devel@lists.01.org; +Cc: Laszlo Ersek, Eric Dong

Paulo,



Thanks.



Reviewed-by:  Jeff Fan <vanjeff_919@hotmail.com><mailto:vanjeff_919@hotmail.com>



Jeff



________________________________
From: edk2-devel <edk2-devel-bounces@lists.01.org> on behalf of Paulo Alcantara <paulo@paulo.ac>
Sent: Friday, December 29, 2017 12:39:39 PM
To: edk2-devel@lists.01.org
Cc: Laszlo Ersek; Eric Dong
Subject: [edk2] [RFC v4 6/6] UefiCpuPkg/CpuExceptionHandlerLib: Correctly print IP addresses

Remove the supurious '- 1' when calculating the IP addresses during the
stack traces.

Contributed-under: TianoCore Contribution Agreement 1.1
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Requested-by: Jeff Fan <vanjeff_919@hotmail.com>
Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c | 2 +-
 UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
index 9b52d4f6d2..e2a3341286 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
@@ -573,7 +573,7 @@ DumpStackTrace (
       *UnwoundStacksCount - 1,
       Eip,
       ImageBase,
-      Eip - ImageBase - 1,
+      Eip - ImageBase,
       Ebp,
       PdbFileName
       );
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
index 8067c34122..5ae3aa4e73 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
@@ -717,7 +717,7 @@ DumpStackTrace (
       *UnwoundStacksCount - 1,
       Rip,
       ImageBase,
-      Rip - ImageBase - 1,
+      Rip - ImageBase,
       Rbp,
       PdbFileName
       );
--
2.14.3

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel

^ permalink raw reply related	[flat|nested] 82+ messages in thread

* 答复: [RFC v4 1/6] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
  2017-12-29  4:39     ` [RFC v4 1/6] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support Paulo Alcantara
@ 2018-01-03  8:53       ` Fan Jeff
  2018-01-03 14:51         ` Paulo Alcantara
  0 siblings, 1 reply; 82+ messages in thread
From: Fan Jeff @ 2018-01-03  8:53 UTC (permalink / raw)
  To: Paulo Alcantara, edk2-devel@lists.01.org; +Cc: Laszlo Ersek, Eric Dong

Paulo,



I did not receive your #0 patch. So, I reply #1 here.



Your serial of patches should work for IA32/X64 GCC and IA32 MSFT. I suggest you could push this serial of patches as soon as pass UefiCpuPkg owner/reviewers’ review and validation,



And then I could send my x64 MSFT support later.



Thanks!

Jeff

________________________________
From: edk2-devel <edk2-devel-bounces@lists.01.org> on behalf of Paulo Alcantara <paulo@paulo.ac>
Sent: Friday, December 29, 2017 12:39:34 PM
To: edk2-devel@lists.01.org
Cc: Laszlo Ersek; Eric Dong
Subject: [edk2] [RFC v4 1/6] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support

This patch adds stack trace support during a X64 CPU exception.

It will dump out back trace, stack contents as well as image module
names that were part of the call stack.

Contributed-under: TianoCore Contribution Agreement 1.1
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 376 +++++++++++++++++++-
 1 file changed, 374 insertions(+), 2 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
index 1dcf4277de..19bfaa329a 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
@@ -14,6 +14,11 @@

 #include "CpuExceptionCommon.h"

+//
+// Unknown PDB file name
+//
+GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
+
 /**
   Return address map of exception handler template so that C code can generate
   exception tables.
@@ -398,6 +403,357 @@ DumpCpuContext (
     );
 }

+/**
+  Get absolute path and file name of PDB file in PE/COFF image.
+
+  @param[in]  ImageBase            Base address of PE/COFF image.
+  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
+  @param[out] PdbFileName          File name of PDB file.
+**/
+STATIC
+VOID
+GetPdbFileName (
+  IN  UINTN    ImageBase,
+  OUT CHAR8    **PdbAbsoluteFilePath,
+  OUT CHAR8    **PdbFileName
+  )
+{
+  VOID   *PdbPointer;
+  CHAR8  *Str;
+
+  //
+  // Get PDB file name from PE/COFF image
+  //
+  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
+  if (PdbPointer == NULL) {
+    //
+    // No PDB file name found. Set it to an unknown file name.
+    //
+    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
+    if (PdbAbsoluteFilePath != NULL) {
+      *PdbAbsoluteFilePath = NULL;
+    }
+  } else {
+    //
+    // Get file name portion out of PDB file in PE/COFF image
+    //
+    Str = (CHAR8 *)((UINTN)PdbPointer +
+                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
+    for (; *Str != '/' && *Str != '\\'; Str--) {
+      ;
+    }
+
+    //
+    // Set PDB file name (also skip trailing path separator: '/' or '\\')
+    //
+    *PdbFileName = Str + 1;
+
+    if (PdbAbsoluteFilePath != NULL) {
+      //
+      // Set absolute file path of PDB file
+      //
+      *PdbAbsoluteFilePath = PdbPointer;
+    }
+  }
+}
+
+/**
+  Dump stack contents.
+
+  @param[in]  CurrentRsp         Current stack pointer address.
+  @param[in]  UnwoundStacksCount  Count of unwound stack frames.
+**/
+STATIC
+VOID
+DumpStackContents (
+  IN UINT64  CurrentRsp,
+  IN INTN    UnwoundStacksCount
+  )
+{
+  //
+  // Check for proper stack pointer alignment
+  //
+  if (((UINTN)CurrentRsp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
+    InternalPrintMessage ("!!!! Unaligned stack pointer. !!!!\n");
+    return;
+  }
+
+  //
+  // Dump out stack contents
+  //
+  InternalPrintMessage ("\nStack dump:\n");
+  while (UnwoundStacksCount-- > 0) {
+    InternalPrintMessage (
+      "0x%016lx: %016lx %016lx\n",
+      CurrentRsp,
+      *(UINT64 *)CurrentRsp,
+      *(UINT64 *)((UINTN)CurrentRsp + 8)
+      );
+
+    //
+    // Point to next stack
+    //
+    CurrentRsp += CPU_STACK_ALIGNMENT;
+  }
+}
+
+/**
+  Dump all image module names from call stack.
+
+  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+**/
+STATIC
+VOID
+DumpImageModuleNames (
+  IN EFI_SYSTEM_CONTEXT   SystemContext
+  )
+{
+  EFI_STATUS  Status;
+  UINT64      Rip;
+  UINTN       ImageBase;
+  VOID        *EntryPoint;
+  CHAR8       *PdbAbsoluteFilePath;
+  CHAR8       *PdbFileName;
+  UINT64      Rbp;
+  UINTN       LastImageBase;
+
+  //
+  // Set current RIP address
+  //
+  Rip = SystemContext.SystemContextX64->Rip;
+
+  //
+  // Set current frame pointer address
+  //
+  Rbp = SystemContext.SystemContextX64->Rbp;
+
+  //
+  // Check for proper frame pointer alignment
+  //
+  if (((UINTN)Rbp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
+    InternalPrintMessage ("!!!! Unaligned frame pointer. !!!!\n");
+    return;
+  }
+
+  //
+  // Get initial PE/COFF image base address from current RIP
+  //
+  ImageBase = PeCoffSearchImageBase (Rip);
+  if (ImageBase == 0) {
+    InternalPrintMessage ("!!!! Could not find image module names. !!!!");
+    return;
+  }
+
+  //
+  // Set last PE/COFF image base address
+  //
+  LastImageBase = ImageBase;
+
+  //
+  // Get initial PE/COFF image's entry point
+  //
+  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
+  if (EFI_ERROR (Status)) {
+    EntryPoint = NULL;
+  }
+
+  //
+  // Get file name and absolute path of initial PDB file
+  //
+  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
+
+  //
+  // Print out initial image module name (if any)
+  //
+  if (PdbAbsoluteFilePath != NULL) {
+    InternalPrintMessage (
+      "\n%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
+      PdbFileName,
+      ImageBase,
+      (UINTN)EntryPoint
+      );
+    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
+  }
+
+  //
+  // Walk through call stack and find next module names
+  //
+  for (;;) {
+    //
+    // Set RIP with return address from current stack frame
+    //
+    Rip = *(UINT64 *)((UINTN)Rbp + 8);
+
+    //
+    // If RIP is zero, then stop unwinding the stack
+    //
+    if (Rip == 0) {
+      break;
+    }
+
+    //
+    // Search for the respective PE/COFF image based on RIP
+    //
+    ImageBase = PeCoffSearchImageBase (Rip);
+    if (ImageBase == 0) {
+      //
+      // Stop stack trace
+      //
+      break;
+    }
+
+    //
+    // If RIP points to another PE/COFF image, then find its respective PDB file
+    // name.
+    //
+    if (LastImageBase != ImageBase) {
+      //
+      // Get PE/COFF image's entry point
+      //
+      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
+      if (EFI_ERROR (Status)) {
+        EntryPoint = NULL;
+      }
+
+      //
+      // Get file name and absolute path of PDB file
+      //
+      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
+
+      //
+      // Print out image module name (if any)
+      //
+      if (PdbAbsoluteFilePath != NULL) {
+        InternalPrintMessage (
+          "%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
+          PdbFileName,
+          ImageBase,
+          (UINTN)EntryPoint
+          );
+        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
+      }
+
+      //
+      // Save last PE/COFF image base address
+      //
+      LastImageBase = ImageBase;
+    }
+
+    //
+    // Unwind the stack
+    //
+    Rbp = *(UINT64 *)(UINTN)Rbp;
+  }
+}
+
+/**
+  Dump stack trace.
+
+  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
+  @param[out] UnwoundStacksCount  Count of unwound stack frames.
+**/
+STATIC
+VOID
+DumpStackTrace (
+  IN  EFI_SYSTEM_CONTEXT   SystemContext,
+  OUT INTN                 *UnwoundStacksCount
+  )
+{
+  UINT64  Rip;
+  UINT64  Rbp;
+  UINTN   ImageBase;
+  CHAR8   *PdbFileName;
+
+  //
+  // Set current RIP address
+  //
+  Rip = SystemContext.SystemContextX64->Rip;
+
+  //
+  // Set current frame pointer address
+  //
+  Rbp = SystemContext.SystemContextX64->Rbp;
+
+  //
+  // Get initial PE/COFF image base address from current RIP
+  //
+  ImageBase = PeCoffSearchImageBase (Rip);
+  if (ImageBase == 0) {
+    InternalPrintMessage ("!!!! Could not find backtrace information. !!!!");
+    return;
+  }
+
+  //
+  // Get PDB file name from initial PE/COFF image
+  //
+  GetPdbFileName (ImageBase, NULL, &PdbFileName);
+
+  //
+  // Initialize count of unwound stacks
+  //
+  *UnwoundStacksCount = 1;
+
+  //
+  // Print out back trace
+  //
+  InternalPrintMessage ("\nCall trace:\n");
+
+  for (;;) {
+    //
+    // Print stack frame in the following format:
+    //
+    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName> | ????]
+    //
+    InternalPrintMessage (
+      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
+      *UnwoundStacksCount - 1,
+      Rip,
+      ImageBase,
+      Rip - ImageBase - 1,
+      Rbp,
+      PdbFileName
+      );
+
+    //
+    // Set RIP with return address from current stack frame
+    //
+    Rip = *(UINT64 *)((UINTN)Rbp + 8);
+
+    //
+    // If RIP is zero, then stop unwinding the stack
+    //
+    if (Rip == 0) {
+      break;
+    }
+
+    //
+    // Search for the respective PE/COFF image based on RIP
+    //
+    ImageBase = PeCoffSearchImageBase (Rip);
+    if (ImageBase == 0) {
+      //
+      // Stop stack trace
+      //
+      break;
+    }
+
+    //
+    // Get PDB file name
+    //
+    GetPdbFileName (ImageBase, NULL, &PdbFileName);
+
+    //
+    // Unwind the stack
+    //
+    Rbp = *(UINT64 *)(UINTN)Rbp;
+
+    //
+    // Increment count of unwound stacks
+    //
+    (*UnwoundStacksCount)++;
+  }
+}
+
 /**
   Display CPU information.

@@ -410,9 +766,25 @@ DumpImageAndCpuContent (
   IN EFI_SYSTEM_CONTEXT   SystemContext
   )
 {
+  INTN UnwoundStacksCount;
+
+  //
+  // Dump CPU context
+  //
   DumpCpuContext (ExceptionType, SystemContext);
+
+  //
+  // Dump stack trace
+  //
+  DumpStackTrace (SystemContext, &UnwoundStacksCount);
+
+  //
+  // Dump image module names
+  //
+  DumpImageModuleNames (SystemContext);
+
   //
-  // Dump module image base and module entry point by RIP
+  // Dump stack contents
   //
-  DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
+  DumpStackContents (SystemContext.SystemContextX64->Rsp, UnwoundStacksCount);
 }
--
2.14.3

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel

^ permalink raw reply related	[flat|nested] 82+ messages in thread

* Re: 答复: [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add helper to valid memory addresses
  2018-01-03  8:42       ` 答复: " Fan Jeff
@ 2018-01-03 14:45         ` Paulo Alcantara
  0 siblings, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2018-01-03 14:45 UTC (permalink / raw)
  To: Fan Jeff, edk2-devel@lists.01.org; +Cc: Laszlo Ersek, Eric Dong

Hi Jeff,

On 1/3/2018 6:42 AM, Fan Jeff wrote:
> Paul,
> 
> +  //
> +  // Calculate physical address of PML4E
> +  //
> +  PhysicalAddress = (UINT64)Cr3 & (((1ULL << MaxPhyAddrBits) - 1) << 12);
> +  PhysicalAddress |= (((UINT64)LinearAddress >> 39) & 0x1FF) << 3;
> +
> 
> Should not pass VS build, instead you could use LShiftU64/RShiftU64 to 
> do 64bit shift operation as below:
> 
>    PhysicalAddress = (UINT64)Cr3 & LShiftU64 (LShiftU64 (1, 
> MaxPhyAddrBits) - 1, 12);
> 
>    PhysicalAddress |= LShiftU64 (RShiftU64((UINT64)LinearAddress, 39) & 
> 0x1FF), 3);

OK - I'll fix them up and then build-test it with IA32/X64 MSFT toolchain.

Thanks
Paulo

> 
> Jeff
> 
> *发件人: *Paulo Alcantara <mailto:paulo@paulo.ac>
> *发送时间: *2017年12月29日12:41
> *收件人: *edk2-devel@lists.01.org <mailto:edk2-devel@lists.01.org>
> *抄送: *Laszlo Ersek <mailto:lersek@redhat.com>; Eric Dong 
> <mailto:eric.dong@intel.com>
> *主题: *[edk2] [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add 
> helper to valid memory addresses
> 
> Introduce IsLinearAddressValid() function that will be used for
> validating memory addresses that would get dereferenced during stack
> traces in IA32 and X64 CPU exceptions.
> 
> Contributed-under: TianoCore Contribution Agreement 1.1
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Requested-by: Brian Johnson <brian.johnson@hpe.com>
> Requested-by: Jiewen Yao <jiewen.yao@intel.com>
> Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
> ---
>   UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c | 382 
> ++++++++++++++++++++
>   UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h |  16 +
>   2 files changed, 398 insertions(+)
> 
> diff --git 
> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c 
> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
> index 867c5c01d6..52b3eb1463 100644
> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
> @@ -14,6 +14,9 @@
> 
>   #include "CpuExceptionCommon.h"
> 
> +#include <Register/Msr.h>
> +#include <Library/DebugLib.h>
> +
>   //
>   // Error code flag indicating whether or not an error code will be
>   // pushed on the stack if an exception occurs.
> @@ -194,3 +197,382 @@ GetPdbFileName (
>       }
>     }
>   }
> +
> +/**
> +  Check if a linear address is valid by walking the page tables in 4-level
> +  paging mode.
> +
> +  @param[in]  Cr3             CR3 control register.
> +  @param[in]  MaxPhyAddrBits  MAXPHYADDRBITS bits.
> +  @param[in]  LinearAddress   Linear address to be checked.
> +**/
> +STATIC
> +BOOLEAN
> +Do4LevelPagingModeCheck (
> +  IN UINTN            Cr3,
> +  IN UINT8            MaxPhyAddrBits,
> +  IN UINTN            LinearAddress
> +  )
> +{
> +  UINT64 PhysicalAddress;
> +  UINT64 *Pml4TableEntry;
> +  UINT64 *PageDirPtrTableEntry;
> +  UINT64 *PageDirEntry;
> +  UINT64 *PageTableEntry;
> +
> +  //
> +  // In 4-level paging mode, linear addresses are 48 bits wide
> +  //
> +  if ((UINT64)LinearAddress > (1ULL << 48) - 1) {
> +    return FALSE;
> +  }
> +
> +  //
> +  // Calculate physical address of PML4E
> +  //
> +  PhysicalAddress = (UINT64)Cr3 & (((1ULL << MaxPhyAddrBits) - 1) << 12);
> +  PhysicalAddress |= (((UINT64)LinearAddress >> 39) & 0x1FF) << 3;
> +
> +  ASSERT ((PhysicalAddress & (sizeof (*Pml4TableEntry) - 1)) == 0);
> +
> +  Pml4TableEntry = (UINT64 *)(UINTN)PhysicalAddress;
> +
> +  //
> +  // Check if a PDPTE is present
> +  //
> +  if ((*Pml4TableEntry & BIT0) == 0) {
> +    return FALSE;
> +  }
> +
> +  //
> +  // Calculate physical address of PDPTE
> +  //
> +  PhysicalAddress = *Pml4TableEntry & (((1ULL << MaxPhyAddrBits) - 1) 
> << 12);
> +  PhysicalAddress |= (((UINT64)LinearAddress >> 30) & 0x1FF) << 3;
> +
> +  ASSERT ((PhysicalAddress & (sizeof (*PageDirPtrTableEntry) - 1)) == 0);
> +
> +  PageDirPtrTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
> +
> +  //
> +  // Check whether a PDPTE or 1GiB page entry is present
> +  //
> +  if ((*PageDirPtrTableEntry & BIT0) == 0) {
> +    return FALSE;
> +  }
> +
> +  //
> +  // Check if PDPTE maps an 1GiB page
> +  //
> +  if ((*PageDirPtrTableEntry & BIT7) != 0) {
> +    return TRUE;
> +  }
> +
> +  //
> +  // Calculate physical address of PDE
> +  //
> +  PhysicalAddress = *PageDirPtrTableEntry & (((1ULL << MaxPhyAddrBits) 
> - 1) <<
> +                                             12);
> +  PhysicalAddress |= (((UINT64)LinearAddress >> 21) & 0x1FF) << 3;
> +
> +  ASSERT ((PhysicalAddress & (sizeof (*PageDirEntry) - 1)) == 0);
> +
> +  PageDirEntry = (UINT64 *)(UINTN)PhysicalAddress;
> +
> +  //
> +  // Check whether a PDE or a 2MiB page entry is present
> +  //
> +  if ((*PageDirEntry & BIT0) == 0) {
> +    return FALSE;
> +  }
> +
> +  //
> +  // Check if PDE maps a 2MiB page
> +  //
> +  if ((*PageDirEntry & BIT7) != 0) {
> +    return TRUE;
> +  }
> +
> +  //
> +  // Calculate physical address of PTE
> +  //
> +  PhysicalAddress = *PageDirEntry & (((1ULL << MaxPhyAddrBits) - 1) << 12);
> +  PhysicalAddress |= (((UINT64)LinearAddress >> 12) & 0x1FF) << 3;
> +
> +  ASSERT ((PhysicalAddress & (sizeof (*PageTableEntry) - 1)) == 0);
> +
> +  PageTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
> +
> +  //
> +  // Check if PTE maps a 4KiB page
> +  //
> +  if ((*PageTableEntry & BIT0) == 0) {
> +    return FALSE;
> +  }
> +
> +  return TRUE;
> +}
> +
> +/**
> +  Check if a linear address is valid by walking the page tables in 
> 32-bit paging
> +  mode.
> +
> +  @param[in]  Cr3             CR3 control register.
> +  @param[in]  Cr4             CR4 control register.
> +  @param[in]  LinearAddress   Linear address to be checked.
> +**/
> +STATIC
> +BOOLEAN
> +Do32BitPagingModeCheck (
> +  IN UINTN            Cr3,
> +  IN UINTN            Cr4,
> +  IN UINTN            LinearAddress
> +  )
> +{
> +  UINT64 PhysicalAddress;
> +  UINT32 *PageDirEntry;
> +  UINT32 *PageTableEntry;
> +
> +  if (LinearAddress > MAX_UINT32) {
> +    return FALSE;
> +  }
> +
> +  //
> +  // Calculate physical address of PDE
> +  //
> +  PhysicalAddress = (UINT32)Cr3 & (((1ULL << 20) - 1) << 12);
> +  PhysicalAddress |= (((UINT32)LinearAddress >> 22) & 0x3FF) << 2;
> +
> +  ASSERT ((PhysicalAddress & (sizeof (*PageDirEntry) - 1)) == 0);
> +
> +  PageDirEntry = (UINT32 *)(UINTN)PhysicalAddress;
> +
> +  //
> +  // Check whether a PTE or a 4MiB page is present
> +  //
> +  if ((*PageDirEntry & BIT0) == 0) {
> +    return FALSE;
> +  }
> +
> +  //
> +  // Check if PDE maps a 4MiB page
> +  //
> +  if ((Cr4 & BIT4) != 0 && (*PageDirEntry & BIT7) != 0) {
> +    return TRUE;
> +  }
> +
> +  //
> +  // Calculate physical address of PTE
> +  //
> +  PhysicalAddress = *PageDirEntry & (((1ULL << 20) - 1) << 12);
> +  PhysicalAddress |= (((UINT32)LinearAddress >> 12) & 0x3FF) << 2;
> +
> +  ASSERT ((PhysicalAddress & (sizeof (*PageTableEntry) - 1)) == 0);
> +
> +  PageTableEntry = (UINT32 *)(UINTN)PhysicalAddress;
> +
> +  //
> +  // Check if PTE maps a 4KiB page
> +  //
> +  if ((*PageTableEntry & BIT0) == 0) {
> +    return FALSE;
> +  }
> +
> +  return TRUE;
> +}
> +
> +/**
> +  Check if a linear address is valid by walking the page tables in PAE 
> paging
> +  mode.
> +
> +  @param[in]  Cr3             CR3 control register.
> +  @param[in]  MaxPhyAddrBits  MAXPHYADDRBITS bits.
> +  @param[in]  LinearAddress   Linear address to be checked.
> +**/
> +STATIC
> +BOOLEAN
> +DoPAEPagingModeCheck (
> +  IN UINTN            Cr3,
> +  IN UINT8            MaxPhyAddrBits,
> +  IN UINTN            LinearAddress
> +  )
> +{
> +  UINT64 PhysicalAddress;
> +  UINT64 *PageDirPtrTableEntry;
> +  UINT64 *PageDirEntry;
> +  UINT64 *PageTableEntry;
> +
> +  if (LinearAddress > MAX_UINT32) {
> +    return FALSE;
> +  }
> +
> +  //
> +  // Calculate physical address of PDPTE
> +  //
> +  PhysicalAddress = (UINT32)Cr3 >> 5;
> +
> +  //
> +  // Select PDPTE register
> +  //
> +  PhysicalAddress +=
> +    ((UINT32)LinearAddress >> 30) * sizeof (*PageDirPtrTableEntry);
> +
> +  PageDirPtrTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
> +
> +  //
> +  // Check if PDE is present
> +  //
> +  if ((*PageDirPtrTableEntry & BIT0) == 0) {
> +    return FALSE;
> +  }
> +
> +  PhysicalAddress = *PageDirPtrTableEntry & (((1ULL << MaxPhyAddrBits) 
> - 1) <<
> +                                             12);
> +  PhysicalAddress |= ((LinearAddress >> 21) & 0x1FF) << 3;
> +  ASSERT ((PhysicalAddress & (sizeof (*PageDirEntry) - 1)) == 0);
> +
> +  PageDirEntry = (UINT64 *)(UINTN)PhysicalAddress;
> +
> +  //
> +  // Check whether a PTE or a 2MiB page is present
> +  //
> +  if ((*PageDirEntry & BIT0) == 0) {
> +    return FALSE;
> +  }
> +
> +  //
> +  // Check if PDE maps a 2MiB page
> +  //
> +  if ((*PageDirEntry & BIT7) != 0) {
> +    return TRUE;
> +  }
> +
> +  //
> +  // Calculate physical address of PTE
> +  //
> +  PhysicalAddress = *PageDirEntry & (((1ULL << MaxPhyAddrBits) - 1) << 12);
> +  PhysicalAddress |= ((LinearAddress >> 12) & 0x1FF) << 3;
> +  ASSERT ((PhysicalAddress & (sizeof (*PageTableEntry) - 1)) == 0);
> +
> +  PageTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
> +
> +  //
> +  // Check if PTE maps a 4KiB page
> +  //
> +  if ((*PageTableEntry & BIT0) == 0) {
> +    return FALSE;
> +  }
> +
> +  return TRUE;
> +}
> +
> +/**
> +  Check if a linear address is valid.
> +
> +  @param[in]  Cr0            CR0 control register.
> +  @param[in]  Cr3            CR3 control register.
> +  @param[in]  Cr4            CR4 control register.
> +  @param[in]  LinearAddress  Linear address to be checked.
> +**/
> +BOOLEAN
> +IsLinearAddressValid (
> +  IN  UINTN              Cr0,
> +  IN  UINTN              Cr3,
> +  IN  UINTN              Cr4,
> +  IN  UINTN              LinearAddress
> +  )
> +{
> +  UINT32                  Eax;
> +  UINT32                  Edx;
> +  UINT8                   MaxPhyAddrBits;
> +  MSR_IA32_EFER_REGISTER  Msr;
> +  BOOLEAN                 AddressValid;
> +
> +  //
> +  // Check for valid input parameters
> +  //
> +  if (Cr0 == 0 || Cr4 == 0 || LinearAddress == 0) {
> +    return FALSE;
> +  }
> +
> +  //
> +  // Check if paging is disabled
> +  //
> +  if ((Cr0 & BIT31) == 0) {
> +    //
> +    // If CR4.PAE bit is set, then the linear (or physical) address 
> supports
> +    // only up to 36 bits.
> +    //
> +    if (((Cr4 & BIT5) != 0 && (UINT64)LinearAddress > 0xFFFFFFFFFULL) ||
> +        LinearAddress > 0xFFFFFFFF) {
> +      return FALSE;
> +    }
> +
> +    return TRUE;
> +  }
> +
> +  //
> +  // Paging can be enabled only if CR0.PE bit is set
> +  //
> +  if ((Cr0 & BIT0) == 0) {
> +    return FALSE;
> +  }
> +
> +  //
> +  // CR3 register cannot be zero if paging is enabled
> +  //
> +  if (Cr3 == 0) {
> +    return FALSE;
> +  }
> +
> +  //
> +  // Get MAXPHYADDR bits
> +  //
> +  AsmCpuid (0x80000000, &Eax, NULL, NULL, NULL);
> +  if (Eax >= 0x80000008) {
> +    AsmCpuid (0x80000008, &Eax, NULL, NULL, NULL);
> +    MaxPhyAddrBits = (UINT8)Eax;
> +  } else {
> +    AsmCpuid (1, NULL, NULL, NULL, &Edx);
> +    if ((Edx & BIT6) != 0) {
> +      MaxPhyAddrBits = 36;
> +    } else {
> +      MaxPhyAddrBits = 32;
> +    }
> +  }
> +
> +  ASSERT (MaxPhyAddrBits > 0);
> +
> +  AddressValid = FALSE;
> +
> +  //
> +  // check if CR4.PAE bit is not set
> +  //
> +  if ((Cr4 & BIT5) == 0) {
> +    //
> +    // Check if linear address is valid in 32-bit paging mode
> +    //
> +    AddressValid = Do32BitPagingModeCheck (Cr3, Cr4, LinearAddress);
> +  } else {
> +    if (MaxPhyAddrBits > 52) {
> +      return FALSE;
> +    }
> +
> +    Msr.Uint64 = AsmReadMsr64 (MSR_IA32_EFER);
> +
> +    if (Msr.Bits.LME == 0) {
> +      //
> +      // Check if linear address is valid in PAE paging mode
> +      //
> +      AddressValid = DoPAEPagingModeCheck (Cr3, MaxPhyAddrBits, 
> LinearAddress);
> +    } else {
> +      //
> +      // Check if linear address is valid in 4-level paging mode
> +      //
> +      AddressValid = Do4LevelPagingModeCheck (Cr3, MaxPhyAddrBits,
> +                                              LinearAddress);
> +    }
> +  }
> +
> +  return AddressValid;
> +}
> diff --git 
> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h 
> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
> index ec46c2d9d3..1b51034c25 100644
> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
> @@ -330,5 +330,21 @@ GetPdbFileName (
>     OUT CHAR8    **PdbFileName
>     );
> 
> +/**
> +  Check if a linear address is valid.
> +
> +  @param[in]  Cr0            CR0 control register.
> +  @param[in]  Cr3            CR3 control register.
> +  @param[in]  Cr4            CR4 control register.
> +  @param[in]  LinearAddress  Linear address to be checked.
> +**/
> +BOOLEAN
> +IsLinearAddressValid (
> +  IN  UINTN              Cr0,
> +  IN  UINTN              Cr3,
> +  IN  UINTN              Cr4,
> +  IN  UINTN              LinearAddress
> +  );
> +
>   #endif
> 
> -- 
> 2.14.3
> 
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org
> https://lists.01.org/mailman/listinfo/edk2-devel
> 



^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: 答复: [RFC v4 5/6] UefiCpuPkg/CpuExceptionHandlerLib: Ensure valid frame/stack pointers
  2018-01-03  8:45       ` 答复: " Fan Jeff
@ 2018-01-03 14:48         ` Paulo Alcantara
  0 siblings, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2018-01-03 14:48 UTC (permalink / raw)
  To: Fan Jeff, edk2-devel@lists.01.org; +Cc: Laszlo Ersek, Eric Dong

On 1/3/2018 6:45 AM, Fan Jeff wrote:
> Paulo,
> 
> +    if (!IsLogicalAddressValid (SystemContext,
> +                                SystemContext.SystemContextIa32->Ss,
> +                                (UINTN)Ebp) ||
> +        !IsLogicalAddressValid (SystemContext,
> +                                SystemContext.SystemContextIa32->Ss,
> +                                (UINTN)Ebp + 4)) {
> 
> I don’t understand why you check both ebp and ebp+4, I think it’s enough 
> to only check EBP (saved stack pointer address)

Isn't it possible that EBP + 4 might potentially point to another page 
frame? If not, then I will drop it out in v5.

Thanks
Paulo

> 
> Jeff
> 
> *发件人: *Paulo Alcantara <mailto:paulo@paulo.ac>
> *发送时间: *2017年12月29日12:41
> *收件人: *edk2-devel@lists.01.org <mailto:edk2-devel@lists.01.org>
> *抄送: *Laszlo Ersek <mailto:lersek@redhat.com>; Eric Dong 
> <mailto:eric.dong@intel.com>
> *主题: *[edk2] [RFC v4 5/6] UefiCpuPkg/CpuExceptionHandlerLib: Ensure 
> valid frame/stack pointers
> 
> Validate all possible memory dereferences during stack traces in IA32
> and X64 CPU exceptions.
> 
> Contributed-under: TianoCore Contribution Agreement 1.1
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Requested-by: Brian Johnson <brian.johnson@hpe.com>
> Requested-by: Jiewen Yao <jiewen.yao@intel.com>
> Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
> ---
>   UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c 
> | 143 +++++++++++++++++++-
>   UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c  
> |  75 +++++++++-
>   2 files changed, 210 insertions(+), 8 deletions(-)
> 
> diff --git 
> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c 
> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
> index 25e02fbbc1..9b52d4f6d2 100644
> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
> @@ -398,6 +398,96 @@ DumpCpuContext (
>       );
>   }
> 
> +/**
> +  Check if a logical address is valid.
> +
> +  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
> +  @param[in]  SegmentSelector    Segment selector.
> +  @param[in]  Offset             Offset or logical address.
> +**/
> +STATIC
> +BOOLEAN
> +IsLogicalAddressValid (
> +  IN  EFI_SYSTEM_CONTEXT   SystemContext,
> +  IN  UINT16               SegmentSelector,
> +  IN  UINTN                Offset
> +  )
> +{
> +  IA32_SEGMENT_DESCRIPTOR  *SegmentDescriptor;
> +  UINT32                   SegDescBase;
> +  UINT32                   SegDescLimit;
> +  UINTN                    SegDescLimitInBytes;
> +
> +  //
> +  // Check for valid input parameters
> +  //
> +  if (SegmentSelector == 0 || Offset == 0) {
> +    return FALSE;
> +  }
> +
> +  //
> +  // Check whether to look for a segment descriptor in GDT or LDT table
> +  //
> +  if ((SegmentSelector & BIT2) == 0) {
> +    //
> +    // Get segment descriptor from GDT table
> +    //
> +    SegmentDescriptor =
> +      (IA32_SEGMENT_DESCRIPTOR *)(
> +        (UINTN)SystemContext.SystemContextIa32->Gdtr[0] +
> +        ((SegmentSelector >> 3) * 8)
> +        );
> +  } else {
> +    //
> +    // Get segment descriptor from LDT table
> +    //
> +    SegmentDescriptor =
> +      (IA32_SEGMENT_DESCRIPTOR *)(
> +        (UINTN)SystemContext.SystemContextIa32->Ldtr +
> +        ((SegmentSelector >> 3) * 8)
> +        );
> +  }
> +
> +  //
> +  // Get segment descriptor's base address
> +  //
> +  SegDescBase = SegmentDescriptor->Bits.BaseLow |
> +    (SegmentDescriptor->Bits.BaseMid << 16) |
> +    (SegmentDescriptor->Bits.BaseHigh << 24);
> +
> +  //
> +  // Get segment descriptor's limit
> +  //
> +  SegDescLimit = SegmentDescriptor->Bits.LimitLow |
> +    (SegmentDescriptor->Bits.LimitHigh << 16);
> +
> +  //
> +  // Calculate segment descriptor's limit in bytes
> +  //
> +  if (SegmentDescriptor->Bits.G == 1) {
> +    SegDescLimitInBytes = (UINTN)SegDescLimit * SIZE_4KB;
> +  } else {
> +    SegDescLimitInBytes = SegDescLimit;
> +  }
> +
> +  //
> +  // Make sure to not access beyond a segment limit boundary
> +  //
> +  if (Offset + SegDescBase > SegDescLimitInBytes) {
> +    return FALSE;
> +  }
> +
> +  //
> +  // Check if the translated logical address (or linear address) is valid
> +  //
> +  return IsLinearAddressValid (
> +    SystemContext.SystemContextIa32->Cr0,
> +    SystemContext.SystemContextIa32->Cr3,
> +    SystemContext.SystemContextIa32->Cr4,
> +    Offset + SegDescBase
> +    );
> +}
> +
>   /**
>     Dump stack trace.
> 
> @@ -459,6 +549,20 @@ DumpStackTrace (
>     InternalPrintMessage ("\nCall trace:\n");
> 
>     for (;;) {
> +    //
> +    // Check for valid frame pointer
> +    //
> +    if (!IsLogicalAddressValid (SystemContext,
> +                                SystemContext.SystemContextIa32->Ss,
> +                                (UINTN)Ebp + 4) ||
> +        !IsLogicalAddressValid (SystemContext,
> +                                SystemContext.SystemContextIa32->Ss,
> +                                (UINTN)Ebp)) {
> +      InternalPrintMessage ("%a: attempted to dereference an invalid 
> frame "
> +                            "pointer at 0x%08x\n", __FUNCTION__, Ebp);
> +      break;
> +    }
> +
>       //
>       // Print stack frame in the following format:
>       //
> @@ -588,6 +692,16 @@ DumpImageModuleNames (
>     // Walk through call stack and find next module names
>     //
>     for (;;) {
> +    if (!IsLogicalAddressValid (SystemContext,
> +                                SystemContext.SystemContextIa32->Ss,
> +                                (UINTN)Ebp) ||
> +        !IsLogicalAddressValid (SystemContext,
> +                                SystemContext.SystemContextIa32->Ss,
> +                                (UINTN)Ebp + 4)) {
> +      InternalPrintMessage ("%a: attempted to dereference an invalid 
> frame "
> +                            "pointer at 0x%08x\n", __FUNCTION__, Ebp);
> +    }
> +
>       //
>       // Set EIP with return address from current stack frame
>       //
> @@ -651,16 +765,23 @@ DumpImageModuleNames (
>   /**
>     Dump stack contents.
> 
> -  @param[in]  CurrentEsp         Current stack pointer address.
> +  @param[in]  SystemContext       Pointer to EFI_SYSTEM_CONTEXT.
>     @param[in]  UnwoundStacksCount  Count of unwound stack frames.
>   **/
>   STATIC
>   VOID
>   DumpStackContents (
> -  IN UINT32  CurrentEsp,
> -  IN INTN    UnwoundStacksCount
> +  IN  EFI_SYSTEM_CONTEXT  SystemContext,
> +  IN  INTN                UnwoundStacksCount
>     )
>   {
> +  UINT32 CurrentEsp;
> +
> +  //
> +  // Get current stack pointer
> +  //
> +  CurrentEsp = SystemContext.SystemContextIa32->Esp;
> +
>     //
>     // Check for proper stack alignment
>     //
> @@ -674,6 +795,20 @@ DumpStackContents (
>     //
>     InternalPrintMessage ("\nStack dump:\n");
>     while (UnwoundStacksCount-- > 0) {
> +    //
> +    // Check for a valid stack pointer address
> +    //
> +    if (!IsLogicalAddressValid (SystemContext,
> +                                SystemContext.SystemContextIa32->Ss,
> +                                (UINTN)CurrentEsp) ||
> +        !IsLogicalAddressValid (SystemContext,
> +                                SystemContext.SystemContextIa32->Ss,
> +                                (UINTN)CurrentEsp + 4)) {
> +      InternalPrintMessage ("%a: attempted to dereference an invalid 
> stack "
> +                            "pointer at 0x%08x\n", __FUNCTION__, 
> CurrentEsp);
> +      break;
> +    }
> +
>       InternalPrintMessage (
>         "0x%08x: %08x %08x\n",
>         CurrentEsp,
> @@ -720,5 +855,5 @@ DumpImageAndCpuContent (
>     //
>     // Dump stack contents
>     //
> -  DumpStackContents (SystemContext.SystemContextIa32->Esp, 
> UnwoundStacksCount);
> +  DumpStackContents (SystemContext, UnwoundStacksCount);
>   }
> diff --git 
> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c 
> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> index d3a3878b3d..8067c34122 100644
> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> @@ -401,16 +401,26 @@ DumpCpuContext (
>   /**
>     Dump stack contents.
> 
> -  @param[in]  CurrentRsp         Current stack pointer address.
> +  @param[in]  SystemContext       Pointer to EFI_SYSTEM_CONTEXT.
>     @param[in]  UnwoundStacksCount  Count of unwound stack frames.
>   **/
>   STATIC
>   VOID
>   DumpStackContents (
> -  IN UINT64  CurrentRsp,
> -  IN INTN    UnwoundStacksCount
> +  IN  EFI_SYSTEM_CONTEXT  SystemContext,
> +  IN  INTN                UnwoundStacksCount
>     )
>   {
> +  UINT64  CurrentRsp;
> +  UINTN   Cr0;
> +  UINTN   Cr3;
> +  UINTN   Cr4;
> +
> +  //
> +  // Get current stack pointer
> +  //
> +  CurrentRsp = SystemContext.SystemContextX64->Rsp;
> +
>     //
>     // Check for proper stack pointer alignment
>     //
> @@ -419,11 +429,28 @@ DumpStackContents (
>       return;
>     }
> 
> +  //
> +  // Get system control registers
> +  //
> +  Cr0 = SystemContext.SystemContextX64->Cr0;
> +  Cr3 = SystemContext.SystemContextX64->Cr3;
> +  Cr4 = SystemContext.SystemContextX64->Cr4;
> +
>     //
>     // Dump out stack contents
>     //
>     InternalPrintMessage ("\nStack dump:\n");
>     while (UnwoundStacksCount-- > 0) {
> +    //
> +    // Check for a valid stack pointer address
> +    //
> +    if (!IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)CurrentRsp) ||
> +        !IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)CurrentRsp + 8)) {
> +      InternalPrintMessage ("%a: attempted to dereference an invalid 
> stack "
> +                            "pointer at 0x%016lx\n", __FUNCTION__, 
> CurrentRsp);
> +      break;
> +    }
> +
>       InternalPrintMessage (
>         "0x%016lx: %016lx %016lx\n",
>         CurrentRsp,
> @@ -457,6 +484,9 @@ DumpImageModuleNames (
>     CHAR8       *PdbFileName;
>     UINT64      Rbp;
>     UINTN       LastImageBase;
> +  UINTN       Cr0;
> +  UINTN       Cr3;
> +  UINTN       Cr4;
> 
>     //
>     // Set current RIP address
> @@ -516,10 +546,27 @@ DumpImageModuleNames (
>       InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
>     }
> 
> +  //
> +  // Get system control registers
> +  //
> +  Cr0 = SystemContext.SystemContextX64->Cr0;
> +  Cr3 = SystemContext.SystemContextX64->Cr3;
> +  Cr4 = SystemContext.SystemContextX64->Cr4;
> +
>     //
>     // Walk through call stack and find next module names
>     //
>     for (;;) {
> +    //
> +    // Check for a valid frame pointer
> +    //
> +    if (!IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)Rbp + 8) ||
> +        !IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)Rbp)) {
> +      InternalPrintMessage ("%a: attempted to dereference an invalid 
> frame "
> +                            "pointer at 0x%016lx\n", __FUNCTION__, Rbp);
> +      break;
> +    }
> +
>       //
>       // Set RIP with return address from current stack frame
>       //
> @@ -604,6 +651,9 @@ DumpStackTrace (
>     UINT64  Rbp;
>     UINTN   ImageBase;
>     CHAR8   *PdbFileName;
> +  UINTN   Cr0;
> +  UINTN   Cr3;
> +  UINTN   Cr4;
> 
>     //
>     // Set current RIP address
> @@ -634,12 +684,29 @@ DumpStackTrace (
>     //
>     *UnwoundStacksCount = 1;
> 
> +  //
> +  // Get system control registers
> +  //
> +  Cr0 = SystemContext.SystemContextX64->Cr0;
> +  Cr3 = SystemContext.SystemContextX64->Cr3;
> +  Cr4 = SystemContext.SystemContextX64->Cr4;
> +
>     //
>     // Print out back trace
>     //
>     InternalPrintMessage ("\nCall trace:\n");
> 
>     for (;;) {
> +    //
> +    // Check for valid frame pointer
> +    //
> +    if (!IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)Rbp + 8) ||
> +        !IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)Rbp)) {
> +      InternalPrintMessage ("%a: attempted to dereference an invalid 
> frame "
> +                            "pointer at 0x%016lx\n", __FUNCTION__, Rbp);
> +      break;
> +    }
> +
>       //
>       // Print stack frame in the following format:
>       //
> @@ -727,5 +794,5 @@ DumpImageAndCpuContent (
>     //
>     // Dump stack contents
>     //
> -  DumpStackContents (SystemContext.SystemContextX64->Rsp, 
> UnwoundStacksCount);
> +  DumpStackContents (SystemContext, UnwoundStacksCount);
>   }
> -- 
> 2.14.3
> 
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org
> https://lists.01.org/mailman/listinfo/edk2-devel
> 



^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: 答复: [RFC v4 1/6] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
  2018-01-03  8:53       ` 答复: " Fan Jeff
@ 2018-01-03 14:51         ` Paulo Alcantara
  0 siblings, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2018-01-03 14:51 UTC (permalink / raw)
  To: Fan Jeff, edk2-devel@lists.01.org; +Cc: Laszlo Ersek, Eric Dong

Hi Jeff,

On 1/3/2018 6:53 AM, Fan Jeff wrote:
> Paulo,
> 
> I did not receive your #0 patch. So, I reply #1 here.
> 
> Your serial of patches should work for IA32/X64 GCC and IA32 MSFT. I 
> suggest you could push this serial of patches as soon as pass UefiCpuPkg 
> owner/reviewers’ review and validation,

OK - That would be really appreciable if you could test it with IA32 
MSFT toolchain and see if the stacktrace is printed out correctly when 
passing the '/Oy-' compile option.

> 
> And then I could send my x64 MSFT support later.

Cool.

Thanks
Paulo

> ------------------------------------------------------------------------
> *From:* edk2-devel <edk2-devel-bounces@lists.01.org> on behalf of Paulo 
> Alcantara <paulo@paulo.ac>
> *Sent:* Friday, December 29, 2017 12:39:34 PM
> *To:* edk2-devel@lists.01.org
> *Cc:* Laszlo Ersek; Eric Dong
> *Subject:* [edk2] [RFC v4 1/6] UefiCpuPkg/CpuExceptionHandlerLib/X64: 
> Add stack trace support
> This patch adds stack trace support during a X64 CPU exception.
> 
> It will dump out back trace, stack contents as well as image module
> names that were part of the call stack.
> 
> Contributed-under: TianoCore Contribution Agreement 1.1
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
> ---
>   UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 
> 376 +++++++++++++++++++-
>   1 file changed, 374 insertions(+), 2 deletions(-)
> 
> diff --git 
> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c 
> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> index 1dcf4277de..19bfaa329a 100644
> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> @@ -14,6 +14,11 @@
> 
>   #include "CpuExceptionCommon.h"
> 
> +//
> +// Unknown PDB file name
> +//
> +GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
> +
>   /**
>     Return address map of exception handler template so that C code can 
> generate
>     exception tables.
> @@ -398,6 +403,357 @@ DumpCpuContext (
>       );
>   }
> 
> +/**
> +  Get absolute path and file name of PDB file in PE/COFF image.
> +
> +  @param[in]  ImageBase            Base address of PE/COFF image.
> +  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
> +  @param[out] PdbFileName          File name of PDB file.
> +**/
> +STATIC
> +VOID
> +GetPdbFileName (
> +  IN  UINTN    ImageBase,
> +  OUT CHAR8    **PdbAbsoluteFilePath,
> +  OUT CHAR8    **PdbFileName
> +  )
> +{
> +  VOID   *PdbPointer;
> +  CHAR8  *Str;
> +
> +  //
> +  // Get PDB file name from PE/COFF image
> +  //
> +  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
> +  if (PdbPointer == NULL) {
> +    //
> +    // No PDB file name found. Set it to an unknown file name.
> +    //
> +    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
> +    if (PdbAbsoluteFilePath != NULL) {
> +      *PdbAbsoluteFilePath = NULL;
> +    }
> +  } else {
> +    //
> +    // Get file name portion out of PDB file in PE/COFF image
> +    //
> +    Str = (CHAR8 *)((UINTN)PdbPointer +
> +                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
> +    for (; *Str != '/' && *Str != '\\'; Str--) {
> +      ;
> +    }
> +
> +    //
> +    // Set PDB file name (also skip trailing path separator: '/' or '\\')
> +    //
> +    *PdbFileName = Str + 1;
> +
> +    if (PdbAbsoluteFilePath != NULL) {
> +      //
> +      // Set absolute file path of PDB file
> +      //
> +      *PdbAbsoluteFilePath = PdbPointer;
> +    }
> +  }
> +}
> +
> +/**
> +  Dump stack contents.
> +
> +  @param[in]  CurrentRsp         Current stack pointer address.
> +  @param[in]  UnwoundStacksCount  Count of unwound stack frames.
> +**/
> +STATIC
> +VOID
> +DumpStackContents (
> +  IN UINT64  CurrentRsp,
> +  IN INTN    UnwoundStacksCount
> +  )
> +{
> +  //
> +  // Check for proper stack pointer alignment
> +  //
> +  if (((UINTN)CurrentRsp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
> +    InternalPrintMessage ("!!!! Unaligned stack pointer. !!!!\n");
> +    return;
> +  }
> +
> +  //
> +  // Dump out stack contents
> +  //
> +  InternalPrintMessage ("\nStack dump:\n");
> +  while (UnwoundStacksCount-- > 0) {
> +    InternalPrintMessage (
> +      "0x%016lx: %016lx %016lx\n",
> +      CurrentRsp,
> +      *(UINT64 *)CurrentRsp,
> +      *(UINT64 *)((UINTN)CurrentRsp + 8)
> +      );
> +
> +    //
> +    // Point to next stack
> +    //
> +    CurrentRsp += CPU_STACK_ALIGNMENT;
> +  }
> +}
> +
> +/**
> +  Dump all image module names from call stack.
> +
> +  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
> +**/
> +STATIC
> +VOID
> +DumpImageModuleNames (
> +  IN EFI_SYSTEM_CONTEXT   SystemContext
> +  )
> +{
> +  EFI_STATUS  Status;
> +  UINT64      Rip;
> +  UINTN       ImageBase;
> +  VOID        *EntryPoint;
> +  CHAR8       *PdbAbsoluteFilePath;
> +  CHAR8       *PdbFileName;
> +  UINT64      Rbp;
> +  UINTN       LastImageBase;
> +
> +  //
> +  // Set current RIP address
> +  //
> +  Rip = SystemContext.SystemContextX64->Rip;
> +
> +  //
> +  // Set current frame pointer address
> +  //
> +  Rbp = SystemContext.SystemContextX64->Rbp;
> +
> +  //
> +  // Check for proper frame pointer alignment
> +  //
> +  if (((UINTN)Rbp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
> +    InternalPrintMessage ("!!!! Unaligned frame pointer. !!!!\n");
> +    return;
> +  }
> +
> +  //
> +  // Get initial PE/COFF image base address from current RIP
> +  //
> +  ImageBase = PeCoffSearchImageBase (Rip);
> +  if (ImageBase == 0) {
> +    InternalPrintMessage ("!!!! Could not find image module names. !!!!");
> +    return;
> +  }
> +
> +  //
> +  // Set last PE/COFF image base address
> +  //
> +  LastImageBase = ImageBase;
> +
> +  //
> +  // Get initial PE/COFF image's entry point
> +  //
> +  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
> +  if (EFI_ERROR (Status)) {
> +    EntryPoint = NULL;
> +  }
> +
> +  //
> +  // Get file name and absolute path of initial PDB file
> +  //
> +  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
> +
> +  //
> +  // Print out initial image module name (if any)
> +  //
> +  if (PdbAbsoluteFilePath != NULL) {
> +    InternalPrintMessage (
> +      "\n%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
> +      PdbFileName,
> +      ImageBase,
> +      (UINTN)EntryPoint
> +      );
> +    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
> +  }
> +
> +  //
> +  // Walk through call stack and find next module names
> +  //
> +  for (;;) {
> +    //
> +    // Set RIP with return address from current stack frame
> +    //
> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
> +
> +    //
> +    // If RIP is zero, then stop unwinding the stack
> +    //
> +    if (Rip == 0) {
> +      break;
> +    }
> +
> +    //
> +    // Search for the respective PE/COFF image based on RIP
> +    //
> +    ImageBase = PeCoffSearchImageBase (Rip);
> +    if (ImageBase == 0) {
> +      //
> +      // Stop stack trace
> +      //
> +      break;
> +    }
> +
> +    //
> +    // If RIP points to another PE/COFF image, then find its respective 
> PDB file
> +    // name.
> +    //
> +    if (LastImageBase != ImageBase) {
> +      //
> +      // Get PE/COFF image's entry point
> +      //
> +      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
> +      if (EFI_ERROR (Status)) {
> +        EntryPoint = NULL;
> +      }
> +
> +      //
> +      // Get file name and absolute path of PDB file
> +      //
> +      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
> +
> +      //
> +      // Print out image module name (if any)
> +      //
> +      if (PdbAbsoluteFilePath != NULL) {
> +        InternalPrintMessage (
> +          "%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
> +          PdbFileName,
> +          ImageBase,
> +          (UINTN)EntryPoint
> +          );
> +        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
> +      }
> +
> +      //
> +      // Save last PE/COFF image base address
> +      //
> +      LastImageBase = ImageBase;
> +    }
> +
> +    //
> +    // Unwind the stack
> +    //
> +    Rbp = *(UINT64 *)(UINTN)Rbp;
> +  }
> +}
> +
> +/**
> +  Dump stack trace.
> +
> +  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
> +  @param[out] UnwoundStacksCount  Count of unwound stack frames.
> +**/
> +STATIC
> +VOID
> +DumpStackTrace (
> +  IN  EFI_SYSTEM_CONTEXT   SystemContext,
> +  OUT INTN                 *UnwoundStacksCount
> +  )
> +{
> +  UINT64  Rip;
> +  UINT64  Rbp;
> +  UINTN   ImageBase;
> +  CHAR8   *PdbFileName;
> +
> +  //
> +  // Set current RIP address
> +  //
> +  Rip = SystemContext.SystemContextX64->Rip;
> +
> +  //
> +  // Set current frame pointer address
> +  //
> +  Rbp = SystemContext.SystemContextX64->Rbp;
> +
> +  //
> +  // Get initial PE/COFF image base address from current RIP
> +  //
> +  ImageBase = PeCoffSearchImageBase (Rip);
> +  if (ImageBase == 0) {
> +    InternalPrintMessage ("!!!! Could not find backtrace information. 
> !!!!");
> +    return;
> +  }
> +
> +  //
> +  // Get PDB file name from initial PE/COFF image
> +  //
> +  GetPdbFileName (ImageBase, NULL, &PdbFileName);
> +
> +  //
> +  // Initialize count of unwound stacks
> +  //
> +  *UnwoundStacksCount = 1;
> +
> +  //
> +  // Print out back trace
> +  //
> +  InternalPrintMessage ("\nCall trace:\n");
> +
> +  for (;;) {
> +    //
> +    // Print stack frame in the following format:
> +    //
> +    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName> | ????]
> +    //
> +    InternalPrintMessage (
> +      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
> +      *UnwoundStacksCount - 1,
> +      Rip,
> +      ImageBase,
> +      Rip - ImageBase - 1,
> +      Rbp,
> +      PdbFileName
> +      );
> +
> +    //
> +    // Set RIP with return address from current stack frame
> +    //
> +    Rip = *(UINT64 *)((UINTN)Rbp + 8);
> +
> +    //
> +    // If RIP is zero, then stop unwinding the stack
> +    //
> +    if (Rip == 0) {
> +      break;
> +    }
> +
> +    //
> +    // Search for the respective PE/COFF image based on RIP
> +    //
> +    ImageBase = PeCoffSearchImageBase (Rip);
> +    if (ImageBase == 0) {
> +      //
> +      // Stop stack trace
> +      //
> +      break;
> +    }
> +
> +    //
> +    // Get PDB file name
> +    //
> +    GetPdbFileName (ImageBase, NULL, &PdbFileName);
> +
> +    //
> +    // Unwind the stack
> +    //
> +    Rbp = *(UINT64 *)(UINTN)Rbp;
> +
> +    //
> +    // Increment count of unwound stacks
> +    //
> +    (*UnwoundStacksCount)++;
> +  }
> +}
> +
>   /**
>     Display CPU information.
> 
> @@ -410,9 +766,25 @@ DumpImageAndCpuContent (
>     IN EFI_SYSTEM_CONTEXT   SystemContext
>     )
>   {
> +  INTN UnwoundStacksCount;
> +
> +  //
> +  // Dump CPU context
> +  //
>     DumpCpuContext (ExceptionType, SystemContext);
> +
> +  //
> +  // Dump stack trace
> +  //
> +  DumpStackTrace (SystemContext, &UnwoundStacksCount);
> +
> +  //
> +  // Dump image module names
> +  //
> +  DumpImageModuleNames (SystemContext);
> +
>     //
> -  // Dump module image base and module entry point by RIP
> +  // Dump stack contents
>     //
> -  DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
> +  DumpStackContents (SystemContext.SystemContextX64->Rsp, 
> UnwoundStacksCount);
>   }
> -- 
> 2.14.3
> 
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org
> https://lists.01.org/mailman/listinfo/edk2-devel



^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add helper to valid memory addresses
  2017-12-29  4:39     ` [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add helper to valid memory addresses Paulo Alcantara
  2018-01-03  8:42       ` 答复: " Fan Jeff
@ 2018-01-03 16:59       ` Brian J. Johnson
  2018-01-04 13:03         ` Paulo Alcantara
  2018-01-04  1:36       ` Yao, Jiewen
  2 siblings, 1 reply; 82+ messages in thread
From: Brian J. Johnson @ 2018-01-03 16:59 UTC (permalink / raw)
  To: Paulo Alcantara, edk2-devel; +Cc: Laszlo Ersek, Eric Dong

On 12/28/2017 10:39 PM, Paulo Alcantara wrote:
> +  //
> +  // Check if paging is disabled
> +  //
> +  if ((Cr0 & BIT31) == 0) {
> +    //
> +    // If CR4.PAE bit is set, then the linear (or physical) address supports
> +    // only up to 36 bits.
> +    //
> +    if (((Cr4 & BIT5) != 0 && (UINT64)LinearAddress > 0xFFFFFFFFFULL) ||
> +        LinearAddress > 0xFFFFFFFF) {
> +      return FALSE;
> +    }
> +
> +    return TRUE;
> +  }

Paulo,

The logic there doesn't look quite right:  if LinearAddress is between 
2^32 and 2^36-1, this code will always return FALSE, even if CR4.PAE is 
set.  Shouldn't it be:

    if ((UINT64)LinearAddress > 0xFFFFFFFFFULL ||
        ((Cr4 & BIT5) == 0 && LinearAddress > 0xFFFFFFFF)) {
      return FALSE;
    }

(I haven't examined all the code in detail, I just happened to notice 
this issue.)

This bug should get fixed before pushing this series.  I also have some 
more general design questions, which shouldn't hold up pushing the 
series, but I think merit some discussion:

This is great code for validating addresses in general, especially when 
guard pages are in use for NULL pointers, stack overflow, etc.  Thanks 
for adding it!  But for [er]sp and [er]bp validation, don't you really 
just want to know if the address is in the expected stack range?  Maybe 
the code which sets up the stack could communicate the valid range to 
CpuExceptionHandlerLib somehow.  It could use special debug register 
values like 
SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/PeCoffExtraActionLib.c 
does.  Or perhaps it could use dynamic PCDs (although I don't know that 
it's a good idea to go looking up PCDs in an exception handler.)  Or 
maybe there's a more straightforward way....  It would have to take AP 
stacks into account, and probably SetJump/LongJump as well.  That may or 
may not be simpler than the current code....

More generally, I'd like to see some sort of platform-specific callout 
to further validate addresses.  Not all mapped addresses, or addresses 
up to the architectural limit, are safe to access.  For instance, reads 
to SMRAM outside of SMM will cause exceptions.  Also, we wouldn't want 
to go backtracing through MMIO or MMCFG space:  reads there could 
potentially have side effects on the hardware.

The rules can also vary at different points in boot.  For example, 
before memory is initialized, Intel Xeon processors generally execute 
32-bit code in cache-as-RAM mode, where the caches are jury-rigged to 
operate as temporary storage while the memory setup code is running.  In 
CAR mode, only a few address ranges can be accessed without causing 
machine checks:  the cache-as-RAM range containing the stack, heap, and 
HOB list, the architectural firmware range below 4G, and a few specific 
MMCFG and MMIO ranges.

So I'd like to suggest that you define an AddressValidationLib library 
class, which provides a routine which takes an address (or an address 
range?) and an indication of the intended use (memory read, memory 
write, execute/disassemble code, stack dump, IO, ...), and returns a 
value specifying if the access is:
- safe (IsLinearAddressValid() should return TRUE)
- unsafe (IsLinearAddressValid() should return FALSE)
- unknown (IsLinearAddressValid() should perform its other tests)

You can supply a NULL instance which always returns "unknown" for 
platforms which don't want to perform their own validation.

Thanks,
-- 
Brian J. Johnson
Enterprise X86 Lab

Hewlett Packard Enterprise
brian.johnson@hpe.com



^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC v4 0/6] Stack trace support in X64 exception handling
  2017-12-29  3:48   ` [RFC v4 0/6] Stack trace support in X64 exception handling Paulo Alcantara
                       ` (5 preceding siblings ...)
  2017-12-29  4:39     ` [RFC v4 6/6] UefiCpuPkg/CpuExceptionHandlerLib: Correctly print IP addresses Paulo Alcantara
@ 2018-01-04  0:59     ` Yao, Jiewen
  2018-01-04 13:36       ` Paulo Alcantara
  2018-01-15  0:23     ` [RFC v5 0/8] " Paulo Alcantara
  7 siblings, 1 reply; 82+ messages in thread
From: Yao, Jiewen @ 2018-01-04  0:59 UTC (permalink / raw)
  To: Paulo Alcantara, edk2-devel@lists.01.org
  Cc: Rick Bramley, Dong, Eric, Andrew Fish, Laszlo Ersek

Thanks Paulo.

I tried to apply the patch series to latest code, but fail with below message.

Have you rebased to latest code?

===================
git.exe am --3way --ignore-space-change --keep-cr "C:\home\EdkIIGit\edk2\[edk2]-[RFC-v4-1-6]-UefiCpuPkg-CpuExceptionHandlerLib-X64-Add-stack-trace-support.patch"
Applying: UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
Using index info to reconstruct a base tree...
M	UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
Falling back to patching base and 3-way merge...
Auto-merging UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
CONFLICT (content): Merge conflict in UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
Patch failed at 0001 UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
The copy of the patch that failed is found in: .git/rebase-apply/patch
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".

.git/rebase-apply/patch:13: trailing whitespace.
//
.git/rebase-apply/patch:14: trailing whitespace.
// Unknown PDB file name
.git/rebase-apply/patch:15: trailing whitespace.
//
.git/rebase-apply/patch:16: trailing whitespace.
GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
.git/rebase-apply/patch:17: trailing whitespace.

warning: squelched 369 whitespace errors
warning: 374 lines add whitespace errors.
error: Failed to merge in the changes.

Fail
=================================================

> -----Original Message-----
> From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of Paulo
> Alcantara
> Sent: Friday, December 29, 2017 11:49 AM
> To: edk2-devel@lists.01.org
> Cc: Rick Bramley <richard.bramley@hp.com>; Dong, Eric
> <eric.dong@intel.com>; Andrew Fish <afish@apple.com>; Yao, Jiewen
> <jiewen.yao@intel.com>; Laszlo Ersek <lersek@redhat.com>
> Subject: [edk2] [RFC v4 0/6] Stack trace support in X64 exception handling
> 
> Hi,
> 
> This series adds stack trace support during IA32 and X64 CPU exceptions.
> 
> Informations like back trace, stack contents and image module names
> (that were part of the call stack) will be dumped out.
> 
> The current limitation is that it relies on available frame pointers
> (GCC only) in order to successfully unwind the stack.
> 
> (Sorry for the very long delay - I was very busy working on something
>  else and then went to vacations)
> 
> Jiewen,
> 
> I have tested it with VS2015x86 and the stack trace just hanged when
> printing out the first EIP (that is, no frame pointer at all).
> 
> Thanks!
> Paulo
> 
> Repo:   https://github.com/pcacjr/edk2.git
> Branch: stacktrace_v4
> 
> Cc: Rick Bramley <richard.bramley@hp.com>
> Cc: Andrew Fish <afish@apple.com>
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Brian Johnson <brian.johnson@hpe.com>
> Cc: Jeff Fan <vanjeff_919@hotmail.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Contributed-under: TianoCore Contribution Agreement 1.1
> Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
> ---
> 
> v1 -> v2:
>   * Add IA32 arch support (GCC toolchain only)
>   * Replace hard-coded stack alignment value (16) with
>     CPU_STACK_ALIGNMENT.
>   * Check for proper stack and frame pointer alignments.
>   * Fix initialization of UnwoundStacksCount to 1.
>   * Move GetPdbFileName() to common code since it will be used by both
>     IA32 and X64 implementations.
> 
> v2 -> v3:
>   * Fixed wrong assumption about "RIP < ImageBase" to start searching
>     for another PE/COFF image. That is, RIP may point to lower and
>     higher addresses for any other PE/COFF images. Both IA32 & X64.
>     (Thanks Andrew & Jiewen)
>   * Fixed typo: unwond -> unwound. Both IA32 & X64. (Thanks Brian)
> 
> v3 -> v4:
>   * Validate all frame/stack pointer addresses before dereferencing them
>     as requested by Brian & Jiewen.
>   * Correctly print out IP addresses during the stack traces (by Jeff)
> 
> Paulo Alcantara (6):
>   UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
>   UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName()
>   UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support
>   UefiCpuPkg/CpuExceptionHandlerLib: Add helper to valid memory
>     addresses
>   UefiCpuPkg/CpuExceptionHandlerLib: Ensure valid frame/stack pointers
>   UefiCpuPkg/CpuExceptionHandlerLib: Correctly print IP addresses
> 
>  UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
> | 484 ++++++++++++++++++--
>  UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
> |  41 +-
>  UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c |
> 445 +++++++++++++++++-
>  UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c  |
> 384 +++++++++++++++-
>  4 files changed, 1296 insertions(+), 58 deletions(-)
> 
> --
> 2.14.3
> 
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org
> https://lists.01.org/mailman/listinfo/edk2-devel


^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC v4 5/6] UefiCpuPkg/CpuExceptionHandlerLib: Ensure valid frame/stack pointers
  2017-12-29  4:39     ` [RFC v4 5/6] UefiCpuPkg/CpuExceptionHandlerLib: Ensure valid frame/stack pointers Paulo Alcantara
  2018-01-03  8:45       ` 答复: " Fan Jeff
@ 2018-01-04  1:07       ` Yao, Jiewen
  1 sibling, 0 replies; 82+ messages in thread
From: Yao, Jiewen @ 2018-01-04  1:07 UTC (permalink / raw)
  To: Paulo Alcantara, edk2-devel@lists.01.org; +Cc: Laszlo Ersek, Dong, Eric

Some suggestion:

1) Would you please use meaning definition for BIT2?

if ((SegmentSelector & BIT2) == 0) {

2) Can we just use (SegmentSelector & ~0x7) for below?

((SegmentSelector >> 3) * 8)

3) Below calculation seems wrong. Should it be: SegDescLimitInBytes = (UINTN)SegDescLimit * SIZE_4KB + (SIZE_4KB - 1) ?

  if (SegmentDescriptor->Bits.G == 1) {
    SegDescLimitInBytes = (UINTN)SegDescLimit * SIZE_4KB;

Thank you
Yao Jiewen


> -----Original Message-----
> From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of Paulo
> Alcantara
> Sent: Friday, December 29, 2017 12:40 PM
> To: edk2-devel@lists.01.org
> Cc: Laszlo Ersek <lersek@redhat.com>; Dong, Eric <eric.dong@intel.com>
> Subject: [edk2] [RFC v4 5/6] UefiCpuPkg/CpuExceptionHandlerLib: Ensure valid
> frame/stack pointers
> 
> Validate all possible memory dereferences during stack traces in IA32
> and X64 CPU exceptions.
> 
> Contributed-under: TianoCore Contribution Agreement 1.1
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Requested-by: Brian Johnson <brian.johnson@hpe.com>
> Requested-by: Jiewen Yao <jiewen.yao@intel.com>
> Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
> ---
>  UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c |
> 143 +++++++++++++++++++-
>  UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c  |
> 75 +++++++++-
>  2 files changed, 210 insertions(+), 8 deletions(-)
> 
> diff --git
> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
> index 25e02fbbc1..9b52d4f6d2 100644
> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
> +++
> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
> @@ -398,6 +398,96 @@ DumpCpuContext (
>      );
>  }
> 
> +/**
> +  Check if a logical address is valid.
> +
> +  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
> +  @param[in]  SegmentSelector    Segment selector.
> +  @param[in]  Offset             Offset or logical address.
> +**/
> +STATIC
> +BOOLEAN
> +IsLogicalAddressValid (
> +  IN  EFI_SYSTEM_CONTEXT   SystemContext,
> +  IN  UINT16               SegmentSelector,
> +  IN  UINTN                Offset
> +  )
> +{
> +  IA32_SEGMENT_DESCRIPTOR  *SegmentDescriptor;
> +  UINT32                   SegDescBase;
> +  UINT32                   SegDescLimit;
> +  UINTN                    SegDescLimitInBytes;
> +
> +  //
> +  // Check for valid input parameters
> +  //
> +  if (SegmentSelector == 0 || Offset == 0) {
> +    return FALSE;
> +  }
> +
> +  //
> +  // Check whether to look for a segment descriptor in GDT or LDT table
> +  //
> +  if ((SegmentSelector & BIT2) == 0) {
> +    //
> +    // Get segment descriptor from GDT table
> +    //
> +    SegmentDescriptor =
> +      (IA32_SEGMENT_DESCRIPTOR *)(
> +        (UINTN)SystemContext.SystemContextIa32->Gdtr[0] +
> +        ((SegmentSelector >> 3) * 8)
> +        );
> +  } else {
> +    //
> +    // Get segment descriptor from LDT table
> +    //
> +    SegmentDescriptor =
> +      (IA32_SEGMENT_DESCRIPTOR *)(
> +        (UINTN)SystemContext.SystemContextIa32->Ldtr +
> +        ((SegmentSelector >> 3) * 8)
> +        );
> +  }
> +
> +  //
> +  // Get segment descriptor's base address
> +  //
> +  SegDescBase = SegmentDescriptor->Bits.BaseLow |
> +    (SegmentDescriptor->Bits.BaseMid << 16) |
> +    (SegmentDescriptor->Bits.BaseHigh << 24);
> +
> +  //
> +  // Get segment descriptor's limit
> +  //
> +  SegDescLimit = SegmentDescriptor->Bits.LimitLow |
> +    (SegmentDescriptor->Bits.LimitHigh << 16);
> +
> +  //
> +  // Calculate segment descriptor's limit in bytes
> +  //
> +  if (SegmentDescriptor->Bits.G == 1) {
> +    SegDescLimitInBytes = (UINTN)SegDescLimit * SIZE_4KB;
> +  } else {
> +    SegDescLimitInBytes = SegDescLimit;
> +  }
> +
> +  //
> +  // Make sure to not access beyond a segment limit boundary
> +  //
> +  if (Offset + SegDescBase > SegDescLimitInBytes) {
> +    return FALSE;
> +  }
> +
> +  //
> +  // Check if the translated logical address (or linear address) is valid
> +  //
> +  return IsLinearAddressValid (
> +    SystemContext.SystemContextIa32->Cr0,
> +    SystemContext.SystemContextIa32->Cr3,
> +    SystemContext.SystemContextIa32->Cr4,
> +    Offset + SegDescBase
> +    );
> +}
> +
>  /**
>    Dump stack trace.
> 
> @@ -459,6 +549,20 @@ DumpStackTrace (
>    InternalPrintMessage ("\nCall trace:\n");
> 
>    for (;;) {
> +    //
> +    // Check for valid frame pointer
> +    //
> +    if (!IsLogicalAddressValid (SystemContext,
> +                                SystemContext.SystemContextIa32->Ss,
> +                                (UINTN)Ebp + 4) ||
> +        !IsLogicalAddressValid (SystemContext,
> +                                SystemContext.SystemContextIa32->Ss,
> +                                (UINTN)Ebp)) {
> +      InternalPrintMessage ("%a: attempted to dereference an invalid frame "
> +                            "pointer at 0x%08x\n", __FUNCTION__, Ebp);
> +      break;
> +    }
> +
>      //
>      // Print stack frame in the following format:
>      //
> @@ -588,6 +692,16 @@ DumpImageModuleNames (
>    // Walk through call stack and find next module names
>    //
>    for (;;) {
> +    if (!IsLogicalAddressValid (SystemContext,
> +                                SystemContext.SystemContextIa32->Ss,
> +                                (UINTN)Ebp) ||
> +        !IsLogicalAddressValid (SystemContext,
> +                                SystemContext.SystemContextIa32->Ss,
> +                                (UINTN)Ebp + 4)) {
> +      InternalPrintMessage ("%a: attempted to dereference an invalid frame "
> +                            "pointer at 0x%08x\n", __FUNCTION__, Ebp);
> +    }
> +
>      //
>      // Set EIP with return address from current stack frame
>      //
> @@ -651,16 +765,23 @@ DumpImageModuleNames (
>  /**
>    Dump stack contents.
> 
> -  @param[in]  CurrentEsp         Current stack pointer address.
> +  @param[in]  SystemContext       Pointer to EFI_SYSTEM_CONTEXT.
>    @param[in]  UnwoundStacksCount  Count of unwound stack frames.
>  **/
>  STATIC
>  VOID
>  DumpStackContents (
> -  IN UINT32  CurrentEsp,
> -  IN INTN    UnwoundStacksCount
> +  IN  EFI_SYSTEM_CONTEXT  SystemContext,
> +  IN  INTN                UnwoundStacksCount
>    )
>  {
> +  UINT32 CurrentEsp;
> +
> +  //
> +  // Get current stack pointer
> +  //
> +  CurrentEsp = SystemContext.SystemContextIa32->Esp;
> +
>    //
>    // Check for proper stack alignment
>    //
> @@ -674,6 +795,20 @@ DumpStackContents (
>    //
>    InternalPrintMessage ("\nStack dump:\n");
>    while (UnwoundStacksCount-- > 0) {
> +    //
> +    // Check for a valid stack pointer address
> +    //
> +    if (!IsLogicalAddressValid (SystemContext,
> +                                SystemContext.SystemContextIa32->Ss,
> +                                (UINTN)CurrentEsp) ||
> +        !IsLogicalAddressValid (SystemContext,
> +                                SystemContext.SystemContextIa32->Ss,
> +                                (UINTN)CurrentEsp + 4)) {
> +      InternalPrintMessage ("%a: attempted to dereference an invalid stack "
> +                            "pointer at 0x%08x\n", __FUNCTION__,
> CurrentEsp);
> +      break;
> +    }
> +
>      InternalPrintMessage (
>        "0x%08x: %08x %08x\n",
>        CurrentEsp,
> @@ -720,5 +855,5 @@ DumpImageAndCpuContent (
>    //
>    // Dump stack contents
>    //
> -  DumpStackContents (SystemContext.SystemContextIa32->Esp,
> UnwoundStacksCount);
> +  DumpStackContents (SystemContext, UnwoundStacksCount);
>  }
> diff --git
> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> index d3a3878b3d..8067c34122 100644
> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> +++
> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> @@ -401,16 +401,26 @@ DumpCpuContext (
>  /**
>    Dump stack contents.
> 
> -  @param[in]  CurrentRsp         Current stack pointer address.
> +  @param[in]  SystemContext       Pointer to EFI_SYSTEM_CONTEXT.
>    @param[in]  UnwoundStacksCount  Count of unwound stack frames.
>  **/
>  STATIC
>  VOID
>  DumpStackContents (
> -  IN UINT64  CurrentRsp,
> -  IN INTN    UnwoundStacksCount
> +  IN  EFI_SYSTEM_CONTEXT  SystemContext,
> +  IN  INTN                UnwoundStacksCount
>    )
>  {
> +  UINT64  CurrentRsp;
> +  UINTN   Cr0;
> +  UINTN   Cr3;
> +  UINTN   Cr4;
> +
> +  //
> +  // Get current stack pointer
> +  //
> +  CurrentRsp = SystemContext.SystemContextX64->Rsp;
> +
>    //
>    // Check for proper stack pointer alignment
>    //
> @@ -419,11 +429,28 @@ DumpStackContents (
>      return;
>    }
> 
> +  //
> +  // Get system control registers
> +  //
> +  Cr0 = SystemContext.SystemContextX64->Cr0;
> +  Cr3 = SystemContext.SystemContextX64->Cr3;
> +  Cr4 = SystemContext.SystemContextX64->Cr4;
> +
>    //
>    // Dump out stack contents
>    //
>    InternalPrintMessage ("\nStack dump:\n");
>    while (UnwoundStacksCount-- > 0) {
> +    //
> +    // Check for a valid stack pointer address
> +    //
> +    if (!IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)CurrentRsp) ||
> +        !IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)CurrentRsp + 8)) {
> +      InternalPrintMessage ("%a: attempted to dereference an invalid stack "
> +                            "pointer at 0x%016lx\n", __FUNCTION__,
> CurrentRsp);
> +      break;
> +    }
> +
>      InternalPrintMessage (
>        "0x%016lx: %016lx %016lx\n",
>        CurrentRsp,
> @@ -457,6 +484,9 @@ DumpImageModuleNames (
>    CHAR8       *PdbFileName;
>    UINT64      Rbp;
>    UINTN       LastImageBase;
> +  UINTN       Cr0;
> +  UINTN       Cr3;
> +  UINTN       Cr4;
> 
>    //
>    // Set current RIP address
> @@ -516,10 +546,27 @@ DumpImageModuleNames (
>      InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
>    }
> 
> +  //
> +  // Get system control registers
> +  //
> +  Cr0 = SystemContext.SystemContextX64->Cr0;
> +  Cr3 = SystemContext.SystemContextX64->Cr3;
> +  Cr4 = SystemContext.SystemContextX64->Cr4;
> +
>    //
>    // Walk through call stack and find next module names
>    //
>    for (;;) {
> +    //
> +    // Check for a valid frame pointer
> +    //
> +    if (!IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)Rbp + 8) ||
> +        !IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)Rbp)) {
> +      InternalPrintMessage ("%a: attempted to dereference an invalid frame "
> +                            "pointer at 0x%016lx\n", __FUNCTION__,
> Rbp);
> +      break;
> +    }
> +
>      //
>      // Set RIP with return address from current stack frame
>      //
> @@ -604,6 +651,9 @@ DumpStackTrace (
>    UINT64  Rbp;
>    UINTN   ImageBase;
>    CHAR8   *PdbFileName;
> +  UINTN   Cr0;
> +  UINTN   Cr3;
> +  UINTN   Cr4;
> 
>    //
>    // Set current RIP address
> @@ -634,12 +684,29 @@ DumpStackTrace (
>    //
>    *UnwoundStacksCount = 1;
> 
> +  //
> +  // Get system control registers
> +  //
> +  Cr0 = SystemContext.SystemContextX64->Cr0;
> +  Cr3 = SystemContext.SystemContextX64->Cr3;
> +  Cr4 = SystemContext.SystemContextX64->Cr4;
> +
>    //
>    // Print out back trace
>    //
>    InternalPrintMessage ("\nCall trace:\n");
> 
>    for (;;) {
> +    //
> +    // Check for valid frame pointer
> +    //
> +    if (!IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)Rbp + 8) ||
> +        !IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)Rbp)) {
> +      InternalPrintMessage ("%a: attempted to dereference an invalid frame "
> +                            "pointer at 0x%016lx\n", __FUNCTION__,
> Rbp);
> +      break;
> +    }
> +
>      //
>      // Print stack frame in the following format:
>      //
> @@ -727,5 +794,5 @@ DumpImageAndCpuContent (
>    //
>    // Dump stack contents
>    //
> -  DumpStackContents (SystemContext.SystemContextX64->Rsp,
> UnwoundStacksCount);
> +  DumpStackContents (SystemContext, UnwoundStacksCount);
>  }
> --
> 2.14.3
> 
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org
> https://lists.01.org/mailman/listinfo/edk2-devel


^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add helper to valid memory addresses
  2017-12-29  4:39     ` [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add helper to valid memory addresses Paulo Alcantara
  2018-01-03  8:42       ` 答复: " Fan Jeff
  2018-01-03 16:59       ` Brian J. Johnson
@ 2018-01-04  1:36       ` Yao, Jiewen
  2018-01-04  1:58         ` Yao, Jiewen
  2018-01-04 13:18         ` Paulo Alcantara
  2 siblings, 2 replies; 82+ messages in thread
From: Yao, Jiewen @ 2018-01-04  1:36 UTC (permalink / raw)
  To: Paulo Alcantara, edk2-devel@lists.01.org; +Cc: Laszlo Ersek, Dong, Eric

Some suggestion:

1) I am not sure if it is proper to use ASSERT in an exception handler, because we know something is wrong.

  ASSERT ((PhysicalAddress & (sizeof (*Pml4TableEntry) - 1)) == 0);

I suggest we just do the check, and return FALSE, if the prerequisite is not satisfied.

2) Can we use meaningful definition for BIT0, BIT7?

  if ((*Pml4TableEntry & BIT0) == 0) {
  if ((*PageDirPtrTableEntry & BIT7) != 0) {

3) I am not sure if I understand below code.

  PhysicalAddress = (UINT64)Cr3 & (((1ULL << MaxPhyAddrBits) - 1) << 12);
  PhysicalAddress = *Pml4TableEntry & (((1ULL << MaxPhyAddrBits) - 1) << 12);
  PhysicalAddress = *PageDirPtrTableEntry & (((1ULL << MaxPhyAddrBits) - 1) << 12);
  PhysicalAddress = *PageDirEntry & (((1ULL << MaxPhyAddrBits) - 1) << 12);

If MaxPhyAddrBits is 48, you will get "Cr3 & 0x0FFFFFFFFFFFF000". Is that what you want? I think we need "Cr3 & 0x0000FFFFFFFFF000"
Should it be: PhysicalAddress = (UINT64)Cr3 & ((1ULL << MaxPhyAddrBits) - 1) & (~0xFFF);

4) Can we use a more readable way to below? Personally, I do not suggest "<< 3", which is just the index calculation.

  PhysicalAddress = (UINT64)Cr3 & (((1ULL << MaxPhyAddrBits) - 1) << 12);
  PhysicalAddress |= (((UINT64)LinearAddress >> 39) & 0x1FF) << 3;
  Pml4TableEntry = (UINT64 *)(UINTN)PhysicalAddress;
  PhysicalAddress = *Pml4TableEntry & (((1ULL << MaxPhyAddrBits) - 1) << 12);

For example:
  PhysicalAddress = (UINT64)Cr3 & ((1ULL << MaxPhyAddrBits) - 1) & (~0xFFF);
  Pml4TableEntry = (UINT64 *)(UINTN)PhysicalAddress;
  Index= (UINTN)(((UINT64)LinearAddress >> 39) & 0x1FF);
  PhysicalAddress = Pml4TableEntry[Index] & ((1ULL << MaxPhyAddrBits) - 1) & (~0xFFF);



Thank you
Yao Jiewen


> -----Original Message-----
> From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of Paulo
> Alcantara
> Sent: Friday, December 29, 2017 12:40 PM
> To: edk2-devel@lists.01.org
> Cc: Laszlo Ersek <lersek@redhat.com>; Dong, Eric <eric.dong@intel.com>
> Subject: [edk2] [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add helper to
> valid memory addresses
> 
> Introduce IsLinearAddressValid() function that will be used for
> validating memory addresses that would get dereferenced during stack
> traces in IA32 and X64 CPU exceptions.
> 
> Contributed-under: TianoCore Contribution Agreement 1.1
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Requested-by: Brian Johnson <brian.johnson@hpe.com>
> Requested-by: Jiewen Yao <jiewen.yao@intel.com>
> Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
> ---
>  UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c | 382
> ++++++++++++++++++++
>  UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h |  16 +
>  2 files changed, 398 insertions(+)
> 
> diff --git
> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
> index 867c5c01d6..52b3eb1463 100644
> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
> @@ -14,6 +14,9 @@
> 
>  #include "CpuExceptionCommon.h"
> 
> +#include <Register/Msr.h>
> +#include <Library/DebugLib.h>
> +
>  //
>  // Error code flag indicating whether or not an error code will be
>  // pushed on the stack if an exception occurs.
> @@ -194,3 +197,382 @@ GetPdbFileName (
>      }
>    }
>  }
> +
> +/**
> +  Check if a linear address is valid by walking the page tables in 4-level
> +  paging mode.
> +
> +  @param[in]  Cr3             CR3 control register.
> +  @param[in]  MaxPhyAddrBits  MAXPHYADDRBITS bits.
> +  @param[in]  LinearAddress   Linear address to be checked.
> +**/
> +STATIC
> +BOOLEAN
> +Do4LevelPagingModeCheck (
> +  IN UINTN            Cr3,
> +  IN UINT8            MaxPhyAddrBits,
> +  IN UINTN            LinearAddress
> +  )
> +{
> +  UINT64 PhysicalAddress;
> +  UINT64 *Pml4TableEntry;
> +  UINT64 *PageDirPtrTableEntry;
> +  UINT64 *PageDirEntry;
> +  UINT64 *PageTableEntry;
> +
> +  //
> +  // In 4-level paging mode, linear addresses are 48 bits wide
> +  //
> +  if ((UINT64)LinearAddress > (1ULL << 48) - 1) {
> +    return FALSE;
> +  }
> +
> +  //
> +  // Calculate physical address of PML4E
> +  //
> +  PhysicalAddress = (UINT64)Cr3 & (((1ULL << MaxPhyAddrBits) - 1) << 12);
> +  PhysicalAddress |= (((UINT64)LinearAddress >> 39) & 0x1FF) << 3;
> +
> +  ASSERT ((PhysicalAddress & (sizeof (*Pml4TableEntry) - 1)) == 0);
> +
> +  Pml4TableEntry = (UINT64 *)(UINTN)PhysicalAddress;
> +
> +  //
> +  // Check if a PDPTE is present
> +  //
> +  if ((*Pml4TableEntry & BIT0) == 0) {
> +    return FALSE;
> +  }
> +
> +  //
> +  // Calculate physical address of PDPTE
> +  //
> +  PhysicalAddress = *Pml4TableEntry & (((1ULL << MaxPhyAddrBits) - 1) <<
> 12);
> +  PhysicalAddress |= (((UINT64)LinearAddress >> 30) & 0x1FF) << 3;
> +
> +  ASSERT ((PhysicalAddress & (sizeof (*PageDirPtrTableEntry) - 1)) == 0);
> +
> +  PageDirPtrTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
> +
> +  //
> +  // Check whether a PDPTE or 1GiB page entry is present
> +  //
> +  if ((*PageDirPtrTableEntry & BIT0) == 0) {
> +    return FALSE;
> +  }
> +
> +  //
> +  // Check if PDPTE maps an 1GiB page
> +  //
> +  if ((*PageDirPtrTableEntry & BIT7) != 0) {
> +    return TRUE;
> +  }
> +
> +  //
> +  // Calculate physical address of PDE
> +  //
> +  PhysicalAddress = *PageDirPtrTableEntry & (((1ULL << MaxPhyAddrBits) - 1)
> <<
> +                                             12);
> +  PhysicalAddress |= (((UINT64)LinearAddress >> 21) & 0x1FF) << 3;
> +
> +  ASSERT ((PhysicalAddress & (sizeof (*PageDirEntry) - 1)) == 0);
> +
> +  PageDirEntry = (UINT64 *)(UINTN)PhysicalAddress;
> +
> +  //
> +  // Check whether a PDE or a 2MiB page entry is present
> +  //
> +  if ((*PageDirEntry & BIT0) == 0) {
> +    return FALSE;
> +  }
> +
> +  //
> +  // Check if PDE maps a 2MiB page
> +  //
> +  if ((*PageDirEntry & BIT7) != 0) {
> +    return TRUE;
> +  }
> +
> +  //
> +  // Calculate physical address of PTE
> +  //
> +  PhysicalAddress = *PageDirEntry & (((1ULL << MaxPhyAddrBits) - 1) << 12);
> +  PhysicalAddress |= (((UINT64)LinearAddress >> 12) & 0x1FF) << 3;
> +
> +  ASSERT ((PhysicalAddress & (sizeof (*PageTableEntry) - 1)) == 0);
> +
> +  PageTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
> +
> +  //
> +  // Check if PTE maps a 4KiB page
> +  //
> +  if ((*PageTableEntry & BIT0) == 0) {
> +    return FALSE;
> +  }
> +
> +  return TRUE;
> +}
> +
> +/**
> +  Check if a linear address is valid by walking the page tables in 32-bit paging
> +  mode.
> +
> +  @param[in]  Cr3             CR3 control register.
> +  @param[in]  Cr4             CR4 control register.
> +  @param[in]  LinearAddress   Linear address to be checked.
> +**/
> +STATIC
> +BOOLEAN
> +Do32BitPagingModeCheck (
> +  IN UINTN            Cr3,
> +  IN UINTN            Cr4,
> +  IN UINTN            LinearAddress
> +  )
> +{
> +  UINT64 PhysicalAddress;
> +  UINT32 *PageDirEntry;
> +  UINT32 *PageTableEntry;
> +
> +  if (LinearAddress > MAX_UINT32) {
> +    return FALSE;
> +  }
> +
> +  //
> +  // Calculate physical address of PDE
> +  //
> +  PhysicalAddress = (UINT32)Cr3 & (((1ULL << 20) - 1) << 12);
> +  PhysicalAddress |= (((UINT32)LinearAddress >> 22) & 0x3FF) << 2;
> +
> +  ASSERT ((PhysicalAddress & (sizeof (*PageDirEntry) - 1)) == 0);
> +
> +  PageDirEntry = (UINT32 *)(UINTN)PhysicalAddress;
> +
> +  //
> +  // Check whether a PTE or a 4MiB page is present
> +  //
> +  if ((*PageDirEntry & BIT0) == 0) {
> +    return FALSE;
> +  }
> +
> +  //
> +  // Check if PDE maps a 4MiB page
> +  //
> +  if ((Cr4 & BIT4) != 0 && (*PageDirEntry & BIT7) != 0) {
> +    return TRUE;
> +  }
> +
> +  //
> +  // Calculate physical address of PTE
> +  //
> +  PhysicalAddress = *PageDirEntry & (((1ULL << 20) - 1) << 12);
> +  PhysicalAddress |= (((UINT32)LinearAddress >> 12) & 0x3FF) << 2;
> +
> +  ASSERT ((PhysicalAddress & (sizeof (*PageTableEntry) - 1)) == 0);
> +
> +  PageTableEntry = (UINT32 *)(UINTN)PhysicalAddress;
> +
> +  //
> +  // Check if PTE maps a 4KiB page
> +  //
> +  if ((*PageTableEntry & BIT0) == 0) {
> +    return FALSE;
> +  }
> +
> +  return TRUE;
> +}
> +
> +/**
> +  Check if a linear address is valid by walking the page tables in PAE paging
> +  mode.
> +
> +  @param[in]  Cr3             CR3 control register.
> +  @param[in]  MaxPhyAddrBits  MAXPHYADDRBITS bits.
> +  @param[in]  LinearAddress   Linear address to be checked.
> +**/
> +STATIC
> +BOOLEAN
> +DoPAEPagingModeCheck (
> +  IN UINTN            Cr3,
> +  IN UINT8            MaxPhyAddrBits,
> +  IN UINTN            LinearAddress
> +  )
> +{
> +  UINT64 PhysicalAddress;
> +  UINT64 *PageDirPtrTableEntry;
> +  UINT64 *PageDirEntry;
> +  UINT64 *PageTableEntry;
> +
> +  if (LinearAddress > MAX_UINT32) {
> +    return FALSE;
> +  }
> +
> +  //
> +  // Calculate physical address of PDPTE
> +  //
> +  PhysicalAddress = (UINT32)Cr3 >> 5;
> +
> +  //
> +  // Select PDPTE register
> +  //
> +  PhysicalAddress +=
> +    ((UINT32)LinearAddress >> 30) * sizeof (*PageDirPtrTableEntry);
> +
> +  PageDirPtrTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
> +
> +  //
> +  // Check if PDE is present
> +  //
> +  if ((*PageDirPtrTableEntry & BIT0) == 0) {
> +    return FALSE;
> +  }
> +
> +  PhysicalAddress = *PageDirPtrTableEntry & (((1ULL << MaxPhyAddrBits) - 1)
> <<
> +                                             12);
> +  PhysicalAddress |= ((LinearAddress >> 21) & 0x1FF) << 3;
> +  ASSERT ((PhysicalAddress & (sizeof (*PageDirEntry) - 1)) == 0);
> +
> +  PageDirEntry = (UINT64 *)(UINTN)PhysicalAddress;
> +
> +  //
> +  // Check whether a PTE or a 2MiB page is present
> +  //
> +  if ((*PageDirEntry & BIT0) == 0) {
> +    return FALSE;
> +  }
> +
> +  //
> +  // Check if PDE maps a 2MiB page
> +  //
> +  if ((*PageDirEntry & BIT7) != 0) {
> +    return TRUE;
> +  }
> +
> +  //
> +  // Calculate physical address of PTE
> +  //
> +  PhysicalAddress = *PageDirEntry & (((1ULL << MaxPhyAddrBits) - 1) << 12);
> +  PhysicalAddress |= ((LinearAddress >> 12) & 0x1FF) << 3;
> +  ASSERT ((PhysicalAddress & (sizeof (*PageTableEntry) - 1)) == 0);
> +
> +  PageTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
> +
> +  //
> +  // Check if PTE maps a 4KiB page
> +  //
> +  if ((*PageTableEntry & BIT0) == 0) {
> +    return FALSE;
> +  }
> +
> +  return TRUE;
> +}
> +
> +/**
> +  Check if a linear address is valid.
> +
> +  @param[in]  Cr0            CR0 control register.
> +  @param[in]  Cr3            CR3 control register.
> +  @param[in]  Cr4            CR4 control register.
> +  @param[in]  LinearAddress  Linear address to be checked.
> +**/
> +BOOLEAN
> +IsLinearAddressValid (
> +  IN  UINTN              Cr0,
> +  IN  UINTN              Cr3,
> +  IN  UINTN              Cr4,
> +  IN  UINTN              LinearAddress
> +  )
> +{
> +  UINT32                  Eax;
> +  UINT32                  Edx;
> +  UINT8                   MaxPhyAddrBits;
> +  MSR_IA32_EFER_REGISTER  Msr;
> +  BOOLEAN                 AddressValid;
> +
> +  //
> +  // Check for valid input parameters
> +  //
> +  if (Cr0 == 0 || Cr4 == 0 || LinearAddress == 0) {
> +    return FALSE;
> +  }
> +
> +  //
> +  // Check if paging is disabled
> +  //
> +  if ((Cr0 & BIT31) == 0) {
> +    //
> +    // If CR4.PAE bit is set, then the linear (or physical) address supports
> +    // only up to 36 bits.
> +    //
> +    if (((Cr4 & BIT5) != 0 && (UINT64)LinearAddress > 0xFFFFFFFFFULL) ||
> +        LinearAddress > 0xFFFFFFFF) {
> +      return FALSE;
> +    }
> +
> +    return TRUE;
> +  }
> +
> +  //
> +  // Paging can be enabled only if CR0.PE bit is set
> +  //
> +  if ((Cr0 & BIT0) == 0) {
> +    return FALSE;
> +  }
> +
> +  //
> +  // CR3 register cannot be zero if paging is enabled
> +  //
> +  if (Cr3 == 0) {
> +    return FALSE;
> +  }
> +
> +  //
> +  // Get MAXPHYADDR bits
> +  //
> +  AsmCpuid (0x80000000, &Eax, NULL, NULL, NULL);
> +  if (Eax >= 0x80000008) {
> +    AsmCpuid (0x80000008, &Eax, NULL, NULL, NULL);
> +    MaxPhyAddrBits = (UINT8)Eax;
> +  } else {
> +    AsmCpuid (1, NULL, NULL, NULL, &Edx);
> +    if ((Edx & BIT6) != 0) {
> +      MaxPhyAddrBits = 36;
> +    } else {
> +      MaxPhyAddrBits = 32;
> +    }
> +  }
> +
> +  ASSERT (MaxPhyAddrBits > 0);
> +
> +  AddressValid = FALSE;
> +
> +  //
> +  // check if CR4.PAE bit is not set
> +  //
> +  if ((Cr4 & BIT5) == 0) {
> +    //
> +    // Check if linear address is valid in 32-bit paging mode
> +    //
> +    AddressValid = Do32BitPagingModeCheck (Cr3, Cr4, LinearAddress);
> +  } else {
> +    if (MaxPhyAddrBits > 52) {
> +      return FALSE;
> +    }
> +
> +    Msr.Uint64 = AsmReadMsr64 (MSR_IA32_EFER);
> +
> +    if (Msr.Bits.LME == 0) {
> +      //
> +      // Check if linear address is valid in PAE paging mode
> +      //
> +      AddressValid = DoPAEPagingModeCheck (Cr3, MaxPhyAddrBits,
> LinearAddress);
> +    } else {
> +      //
> +      // Check if linear address is valid in 4-level paging mode
> +      //
> +      AddressValid = Do4LevelPagingModeCheck (Cr3, MaxPhyAddrBits,
> +                                              LinearAddress);
> +    }
> +  }
> +
> +  return AddressValid;
> +}
> diff --git
> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
> index ec46c2d9d3..1b51034c25 100644
> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
> @@ -330,5 +330,21 @@ GetPdbFileName (
>    OUT CHAR8    **PdbFileName
>    );
> 
> +/**
> +  Check if a linear address is valid.
> +
> +  @param[in]  Cr0            CR0 control register.
> +  @param[in]  Cr3            CR3 control register.
> +  @param[in]  Cr4            CR4 control register.
> +  @param[in]  LinearAddress  Linear address to be checked.
> +**/
> +BOOLEAN
> +IsLinearAddressValid (
> +  IN  UINTN              Cr0,
> +  IN  UINTN              Cr3,
> +  IN  UINTN              Cr4,
> +  IN  UINTN              LinearAddress
> +  );
> +
>  #endif
> 
> --
> 2.14.3
> 
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org
> https://lists.01.org/mailman/listinfo/edk2-devel


^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add helper to valid memory addresses
  2018-01-04  1:36       ` Yao, Jiewen
@ 2018-01-04  1:58         ` Yao, Jiewen
  2018-01-04 13:29           ` Paulo Alcantara
  2018-01-04 13:18         ` Paulo Alcantara
  1 sibling, 1 reply; 82+ messages in thread
From: Yao, Jiewen @ 2018-01-04  1:58 UTC (permalink / raw)
  To: Yao, Jiewen, Paulo Alcantara, edk2-devel@lists.01.org
  Cc: Laszlo Ersek, Dong, Eric

5) For CR4, please use meaning definition for BIT4/BIT5.
  if ((Cr4 & BIT4) != 0 && (*PageDirEntry & BIT7) != 0) {
    if (((Cr4 & BIT5) != 0 && (UINT64)LinearAddress > 0xFFFFFFFFFULL) ||

6) For IA32 PAE/PSE calculation, same comment for 3 and 4.

7) Last but not least important, would you please share the information on how do you validate the 32bit PAE/PSE/normal 4K page table?

Thank you
Yao Jiewen

> -----Original Message-----
> From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of Yao,
> Jiewen
> Sent: Thursday, January 4, 2018 9:36 AM
> To: Paulo Alcantara <paulo@paulo.ac>; edk2-devel@lists.01.org
> Cc: Laszlo Ersek <lersek@redhat.com>; Dong, Eric <eric.dong@intel.com>
> Subject: Re: [edk2] [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add
> helper to valid memory addresses
> 
> Some suggestion:
> 
> 1) I am not sure if it is proper to use ASSERT in an exception handler, because we
> know something is wrong.
> 
>   ASSERT ((PhysicalAddress & (sizeof (*Pml4TableEntry) - 1)) == 0);
> 
> I suggest we just do the check, and return FALSE, if the prerequisite is not
> satisfied.
> 
> 2) Can we use meaningful definition for BIT0, BIT7?
> 
>   if ((*Pml4TableEntry & BIT0) == 0) {
>   if ((*PageDirPtrTableEntry & BIT7) != 0) {
> 
> 3) I am not sure if I understand below code.
> 
>   PhysicalAddress = (UINT64)Cr3 & (((1ULL << MaxPhyAddrBits) - 1) << 12);
>   PhysicalAddress = *Pml4TableEntry & (((1ULL << MaxPhyAddrBits) - 1) << 12);
>   PhysicalAddress = *PageDirPtrTableEntry & (((1ULL << MaxPhyAddrBits) - 1)
> << 12);
>   PhysicalAddress = *PageDirEntry & (((1ULL << MaxPhyAddrBits) - 1) << 12);
> 
> If MaxPhyAddrBits is 48, you will get "Cr3 & 0x0FFFFFFFFFFFF000". Is that what
> you want? I think we need "Cr3 & 0x0000FFFFFFFFF000"
> Should it be: PhysicalAddress = (UINT64)Cr3 & ((1ULL << MaxPhyAddrBits) - 1) &
> (~0xFFF);
> 
> 4) Can we use a more readable way to below? Personally, I do not suggest "<< 3",
> which is just the index calculation.
> 
>   PhysicalAddress = (UINT64)Cr3 & (((1ULL << MaxPhyAddrBits) - 1) << 12);
>   PhysicalAddress |= (((UINT64)LinearAddress >> 39) & 0x1FF) << 3;
>   Pml4TableEntry = (UINT64 *)(UINTN)PhysicalAddress;
>   PhysicalAddress = *Pml4TableEntry & (((1ULL << MaxPhyAddrBits) - 1) << 12);
> 
> For example:
>   PhysicalAddress = (UINT64)Cr3 & ((1ULL << MaxPhyAddrBits) - 1) & (~0xFFF);
>   Pml4TableEntry = (UINT64 *)(UINTN)PhysicalAddress;
>   Index= (UINTN)(((UINT64)LinearAddress >> 39) & 0x1FF);
>   PhysicalAddress = Pml4TableEntry[Index] & ((1ULL << MaxPhyAddrBits) - 1) &
> (~0xFFF);
> 
> 
> 
> Thank you
> Yao Jiewen
> 
> 
> > -----Original Message-----
> > From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of
> Paulo
> > Alcantara
> > Sent: Friday, December 29, 2017 12:40 PM
> > To: edk2-devel@lists.01.org
> > Cc: Laszlo Ersek <lersek@redhat.com>; Dong, Eric <eric.dong@intel.com>
> > Subject: [edk2] [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add helper
> to
> > valid memory addresses
> >
> > Introduce IsLinearAddressValid() function that will be used for
> > validating memory addresses that would get dereferenced during stack
> > traces in IA32 and X64 CPU exceptions.
> >
> > Contributed-under: TianoCore Contribution Agreement 1.1
> > Cc: Eric Dong <eric.dong@intel.com>
> > Cc: Laszlo Ersek <lersek@redhat.com>
> > Requested-by: Brian Johnson <brian.johnson@hpe.com>
> > Requested-by: Jiewen Yao <jiewen.yao@intel.com>
> > Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
> > ---
> >  UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c | 382
> > ++++++++++++++++++++
> >  UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h |
> 16 +
> >  2 files changed, 398 insertions(+)
> >
> > diff --git
> > a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
> > b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
> > index 867c5c01d6..52b3eb1463 100644
> > --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
> > +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
> > @@ -14,6 +14,9 @@
> >
> >  #include "CpuExceptionCommon.h"
> >
> > +#include <Register/Msr.h>
> > +#include <Library/DebugLib.h>
> > +
> >  //
> >  // Error code flag indicating whether or not an error code will be
> >  // pushed on the stack if an exception occurs.
> > @@ -194,3 +197,382 @@ GetPdbFileName (
> >      }
> >    }
> >  }
> > +
> > +/**
> > +  Check if a linear address is valid by walking the page tables in 4-level
> > +  paging mode.
> > +
> > +  @param[in]  Cr3             CR3 control register.
> > +  @param[in]  MaxPhyAddrBits  MAXPHYADDRBITS bits.
> > +  @param[in]  LinearAddress   Linear address to be checked.
> > +**/
> > +STATIC
> > +BOOLEAN
> > +Do4LevelPagingModeCheck (
> > +  IN UINTN            Cr3,
> > +  IN UINT8            MaxPhyAddrBits,
> > +  IN UINTN            LinearAddress
> > +  )
> > +{
> > +  UINT64 PhysicalAddress;
> > +  UINT64 *Pml4TableEntry;
> > +  UINT64 *PageDirPtrTableEntry;
> > +  UINT64 *PageDirEntry;
> > +  UINT64 *PageTableEntry;
> > +
> > +  //
> > +  // In 4-level paging mode, linear addresses are 48 bits wide
> > +  //
> > +  if ((UINT64)LinearAddress > (1ULL << 48) - 1) {
> > +    return FALSE;
> > +  }
> > +
> > +  //
> > +  // Calculate physical address of PML4E
> > +  //
> > +  PhysicalAddress = (UINT64)Cr3 & (((1ULL << MaxPhyAddrBits) - 1) << 12);
> > +  PhysicalAddress |= (((UINT64)LinearAddress >> 39) & 0x1FF) << 3;
> > +
> > +  ASSERT ((PhysicalAddress & (sizeof (*Pml4TableEntry) - 1)) == 0);
> > +
> > +  Pml4TableEntry = (UINT64 *)(UINTN)PhysicalAddress;
> > +
> > +  //
> > +  // Check if a PDPTE is present
> > +  //
> > +  if ((*Pml4TableEntry & BIT0) == 0) {
> > +    return FALSE;
> > +  }
> > +
> > +  //
> > +  // Calculate physical address of PDPTE
> > +  //
> > +  PhysicalAddress = *Pml4TableEntry & (((1ULL << MaxPhyAddrBits) - 1) <<
> > 12);
> > +  PhysicalAddress |= (((UINT64)LinearAddress >> 30) & 0x1FF) << 3;
> > +
> > +  ASSERT ((PhysicalAddress & (sizeof (*PageDirPtrTableEntry) - 1)) == 0);
> > +
> > +  PageDirPtrTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
> > +
> > +  //
> > +  // Check whether a PDPTE or 1GiB page entry is present
> > +  //
> > +  if ((*PageDirPtrTableEntry & BIT0) == 0) {
> > +    return FALSE;
> > +  }
> > +
> > +  //
> > +  // Check if PDPTE maps an 1GiB page
> > +  //
> > +  if ((*PageDirPtrTableEntry & BIT7) != 0) {
> > +    return TRUE;
> > +  }
> > +
> > +  //
> > +  // Calculate physical address of PDE
> > +  //
> > +  PhysicalAddress = *PageDirPtrTableEntry & (((1ULL << MaxPhyAddrBits) -
> 1)
> > <<
> > +                                             12);
> > +  PhysicalAddress |= (((UINT64)LinearAddress >> 21) & 0x1FF) << 3;
> > +
> > +  ASSERT ((PhysicalAddress & (sizeof (*PageDirEntry) - 1)) == 0);
> > +
> > +  PageDirEntry = (UINT64 *)(UINTN)PhysicalAddress;
> > +
> > +  //
> > +  // Check whether a PDE or a 2MiB page entry is present
> > +  //
> > +  if ((*PageDirEntry & BIT0) == 0) {
> > +    return FALSE;
> > +  }
> > +
> > +  //
> > +  // Check if PDE maps a 2MiB page
> > +  //
> > +  if ((*PageDirEntry & BIT7) != 0) {
> > +    return TRUE;
> > +  }
> > +
> > +  //
> > +  // Calculate physical address of PTE
> > +  //
> > +  PhysicalAddress = *PageDirEntry & (((1ULL << MaxPhyAddrBits) - 1) <<
> 12);
> > +  PhysicalAddress |= (((UINT64)LinearAddress >> 12) & 0x1FF) << 3;
> > +
> > +  ASSERT ((PhysicalAddress & (sizeof (*PageTableEntry) - 1)) == 0);
> > +
> > +  PageTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
> > +
> > +  //
> > +  // Check if PTE maps a 4KiB page
> > +  //
> > +  if ((*PageTableEntry & BIT0) == 0) {
> > +    return FALSE;
> > +  }
> > +
> > +  return TRUE;
> > +}
> > +
> > +/**
> > +  Check if a linear address is valid by walking the page tables in 32-bit paging
> > +  mode.
> > +
> > +  @param[in]  Cr3             CR3 control register.
> > +  @param[in]  Cr4             CR4 control register.
> > +  @param[in]  LinearAddress   Linear address to be checked.
> > +**/
> > +STATIC
> > +BOOLEAN
> > +Do32BitPagingModeCheck (
> > +  IN UINTN            Cr3,
> > +  IN UINTN            Cr4,
> > +  IN UINTN            LinearAddress
> > +  )
> > +{
> > +  UINT64 PhysicalAddress;
> > +  UINT32 *PageDirEntry;
> > +  UINT32 *PageTableEntry;
> > +
> > +  if (LinearAddress > MAX_UINT32) {
> > +    return FALSE;
> > +  }
> > +
> > +  //
> > +  // Calculate physical address of PDE
> > +  //
> > +  PhysicalAddress = (UINT32)Cr3 & (((1ULL << 20) - 1) << 12);
> > +  PhysicalAddress |= (((UINT32)LinearAddress >> 22) & 0x3FF) << 2;
> > +
> > +  ASSERT ((PhysicalAddress & (sizeof (*PageDirEntry) - 1)) == 0);
> > +
> > +  PageDirEntry = (UINT32 *)(UINTN)PhysicalAddress;
> > +
> > +  //
> > +  // Check whether a PTE or a 4MiB page is present
> > +  //
> > +  if ((*PageDirEntry & BIT0) == 0) {
> > +    return FALSE;
> > +  }
> > +
> > +  //
> > +  // Check if PDE maps a 4MiB page
> > +  //
> > +  if ((Cr4 & BIT4) != 0 && (*PageDirEntry & BIT7) != 0) {
> > +    return TRUE;
> > +  }
> > +
> > +  //
> > +  // Calculate physical address of PTE
> > +  //
> > +  PhysicalAddress = *PageDirEntry & (((1ULL << 20) - 1) << 12);
> > +  PhysicalAddress |= (((UINT32)LinearAddress >> 12) & 0x3FF) << 2;
> > +
> > +  ASSERT ((PhysicalAddress & (sizeof (*PageTableEntry) - 1)) == 0);
> > +
> > +  PageTableEntry = (UINT32 *)(UINTN)PhysicalAddress;
> > +
> > +  //
> > +  // Check if PTE maps a 4KiB page
> > +  //
> > +  if ((*PageTableEntry & BIT0) == 0) {
> > +    return FALSE;
> > +  }
> > +
> > +  return TRUE;
> > +}
> > +
> > +/**
> > +  Check if a linear address is valid by walking the page tables in PAE paging
> > +  mode.
> > +
> > +  @param[in]  Cr3             CR3 control register.
> > +  @param[in]  MaxPhyAddrBits  MAXPHYADDRBITS bits.
> > +  @param[in]  LinearAddress   Linear address to be checked.
> > +**/
> > +STATIC
> > +BOOLEAN
> > +DoPAEPagingModeCheck (
> > +  IN UINTN            Cr3,
> > +  IN UINT8            MaxPhyAddrBits,
> > +  IN UINTN            LinearAddress
> > +  )
> > +{
> > +  UINT64 PhysicalAddress;
> > +  UINT64 *PageDirPtrTableEntry;
> > +  UINT64 *PageDirEntry;
> > +  UINT64 *PageTableEntry;
> > +
> > +  if (LinearAddress > MAX_UINT32) {
> > +    return FALSE;
> > +  }
> > +
> > +  //
> > +  // Calculate physical address of PDPTE
> > +  //
> > +  PhysicalAddress = (UINT32)Cr3 >> 5;
> > +
> > +  //
> > +  // Select PDPTE register
> > +  //
> > +  PhysicalAddress +=
> > +    ((UINT32)LinearAddress >> 30) * sizeof (*PageDirPtrTableEntry);
> > +
> > +  PageDirPtrTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
> > +
> > +  //
> > +  // Check if PDE is present
> > +  //
> > +  if ((*PageDirPtrTableEntry & BIT0) == 0) {
> > +    return FALSE;
> > +  }
> > +
> > +  PhysicalAddress = *PageDirPtrTableEntry & (((1ULL << MaxPhyAddrBits) -
> 1)
> > <<
> > +                                             12);
> > +  PhysicalAddress |= ((LinearAddress >> 21) & 0x1FF) << 3;
> > +  ASSERT ((PhysicalAddress & (sizeof (*PageDirEntry) - 1)) == 0);
> > +
> > +  PageDirEntry = (UINT64 *)(UINTN)PhysicalAddress;
> > +
> > +  //
> > +  // Check whether a PTE or a 2MiB page is present
> > +  //
> > +  if ((*PageDirEntry & BIT0) == 0) {
> > +    return FALSE;
> > +  }
> > +
> > +  //
> > +  // Check if PDE maps a 2MiB page
> > +  //
> > +  if ((*PageDirEntry & BIT7) != 0) {
> > +    return TRUE;
> > +  }
> > +
> > +  //
> > +  // Calculate physical address of PTE
> > +  //
> > +  PhysicalAddress = *PageDirEntry & (((1ULL << MaxPhyAddrBits) - 1) <<
> 12);
> > +  PhysicalAddress |= ((LinearAddress >> 12) & 0x1FF) << 3;
> > +  ASSERT ((PhysicalAddress & (sizeof (*PageTableEntry) - 1)) == 0);
> > +
> > +  PageTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
> > +
> > +  //
> > +  // Check if PTE maps a 4KiB page
> > +  //
> > +  if ((*PageTableEntry & BIT0) == 0) {
> > +    return FALSE;
> > +  }
> > +
> > +  return TRUE;
> > +}
> > +
> > +/**
> > +  Check if a linear address is valid.
> > +
> > +  @param[in]  Cr0            CR0 control register.
> > +  @param[in]  Cr3            CR3 control register.
> > +  @param[in]  Cr4            CR4 control register.
> > +  @param[in]  LinearAddress  Linear address to be checked.
> > +**/
> > +BOOLEAN
> > +IsLinearAddressValid (
> > +  IN  UINTN              Cr0,
> > +  IN  UINTN              Cr3,
> > +  IN  UINTN              Cr4,
> > +  IN  UINTN              LinearAddress
> > +  )
> > +{
> > +  UINT32                  Eax;
> > +  UINT32                  Edx;
> > +  UINT8                   MaxPhyAddrBits;
> > +  MSR_IA32_EFER_REGISTER  Msr;
> > +  BOOLEAN                 AddressValid;
> > +
> > +  //
> > +  // Check for valid input parameters
> > +  //
> > +  if (Cr0 == 0 || Cr4 == 0 || LinearAddress == 0) {
> > +    return FALSE;
> > +  }
> > +
> > +  //
> > +  // Check if paging is disabled
> > +  //
> > +  if ((Cr0 & BIT31) == 0) {
> > +    //
> > +    // If CR4.PAE bit is set, then the linear (or physical) address supports
> > +    // only up to 36 bits.
> > +    //
> > +    if (((Cr4 & BIT5) != 0 && (UINT64)LinearAddress > 0xFFFFFFFFFULL) ||
> > +        LinearAddress > 0xFFFFFFFF) {
> > +      return FALSE;
> > +    }
> > +
> > +    return TRUE;
> > +  }
> > +
> > +  //
> > +  // Paging can be enabled only if CR0.PE bit is set
> > +  //
> > +  if ((Cr0 & BIT0) == 0) {
> > +    return FALSE;
> > +  }
> > +
> > +  //
> > +  // CR3 register cannot be zero if paging is enabled
> > +  //
> > +  if (Cr3 == 0) {
> > +    return FALSE;
> > +  }
> > +
> > +  //
> > +  // Get MAXPHYADDR bits
> > +  //
> > +  AsmCpuid (0x80000000, &Eax, NULL, NULL, NULL);
> > +  if (Eax >= 0x80000008) {
> > +    AsmCpuid (0x80000008, &Eax, NULL, NULL, NULL);
> > +    MaxPhyAddrBits = (UINT8)Eax;
> > +  } else {
> > +    AsmCpuid (1, NULL, NULL, NULL, &Edx);
> > +    if ((Edx & BIT6) != 0) {
> > +      MaxPhyAddrBits = 36;
> > +    } else {
> > +      MaxPhyAddrBits = 32;
> > +    }
> > +  }
> > +
> > +  ASSERT (MaxPhyAddrBits > 0);
> > +
> > +  AddressValid = FALSE;
> > +
> > +  //
> > +  // check if CR4.PAE bit is not set
> > +  //
> > +  if ((Cr4 & BIT5) == 0) {
> > +    //
> > +    // Check if linear address is valid in 32-bit paging mode
> > +    //
> > +    AddressValid = Do32BitPagingModeCheck (Cr3, Cr4, LinearAddress);
> > +  } else {
> > +    if (MaxPhyAddrBits > 52) {
> > +      return FALSE;
> > +    }
> > +
> > +    Msr.Uint64 = AsmReadMsr64 (MSR_IA32_EFER);
> > +
> > +    if (Msr.Bits.LME == 0) {
> > +      //
> > +      // Check if linear address is valid in PAE paging mode
> > +      //
> > +      AddressValid = DoPAEPagingModeCheck (Cr3, MaxPhyAddrBits,
> > LinearAddress);
> > +    } else {
> > +      //
> > +      // Check if linear address is valid in 4-level paging mode
> > +      //
> > +      AddressValid = Do4LevelPagingModeCheck (Cr3, MaxPhyAddrBits,
> > +                                              LinearAddress);
> > +    }
> > +  }
> > +
> > +  return AddressValid;
> > +}
> > diff --git
> > a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
> > b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
> > index ec46c2d9d3..1b51034c25 100644
> > --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
> > +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
> > @@ -330,5 +330,21 @@ GetPdbFileName (
> >    OUT CHAR8    **PdbFileName
> >    );
> >
> > +/**
> > +  Check if a linear address is valid.
> > +
> > +  @param[in]  Cr0            CR0 control register.
> > +  @param[in]  Cr3            CR3 control register.
> > +  @param[in]  Cr4            CR4 control register.
> > +  @param[in]  LinearAddress  Linear address to be checked.
> > +**/
> > +BOOLEAN
> > +IsLinearAddressValid (
> > +  IN  UINTN              Cr0,
> > +  IN  UINTN              Cr3,
> > +  IN  UINTN              Cr4,
> > +  IN  UINTN              LinearAddress
> > +  );
> > +
> >  #endif
> >
> > --
> > 2.14.3
> >
> > _______________________________________________
> > edk2-devel mailing list
> > edk2-devel@lists.01.org
> > https://lists.01.org/mailman/listinfo/edk2-devel
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org
> https://lists.01.org/mailman/listinfo/edk2-devel


^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add helper to valid memory addresses
  2018-01-03 16:59       ` Brian J. Johnson
@ 2018-01-04 13:03         ` Paulo Alcantara
  0 siblings, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2018-01-04 13:03 UTC (permalink / raw)
  To: Brian J. Johnson, edk2-devel; +Cc: Laszlo Ersek, Eric Dong

"Brian J. Johnson" <brian.johnson@hpe.com> writes:

Hi Brian,

> On 12/28/2017 10:39 PM, Paulo Alcantara wrote:
>> +  //
>> +  // Check if paging is disabled
>> +  //
>> +  if ((Cr0 & BIT31) == 0) {
>> +    //
>> +    // If CR4.PAE bit is set, then the linear (or physical) address supports
>> +    // only up to 36 bits.
>> +    //
>> +    if (((Cr4 & BIT5) != 0 && (UINT64)LinearAddress > 0xFFFFFFFFFULL) ||
>> +        LinearAddress > 0xFFFFFFFF) {
>> +      return FALSE;
>> +    }
>> +
>> +    return TRUE;
>> +  }
>
> Paulo,
>
> The logic there doesn't look quite right:  if LinearAddress is between 
> 2^32 and 2^36-1, this code will always return FALSE, even if CR4.PAE is 
> set.  Shouldn't it be:
>
>     if ((UINT64)LinearAddress > 0xFFFFFFFFFULL ||
>         ((Cr4 & BIT5) == 0 && LinearAddress > 0xFFFFFFFF)) {
>       return FALSE;
>     }

You're right. The check is bogus and I'll fix it up in the next version.

>
> (I haven't examined all the code in detail, I just happened to notice 
> this issue.)

No problem. Your comments are very appreciable.

> This bug should get fixed before pushing this series.  I also have some 
> more general design questions, which shouldn't hold up pushing the 
> series, but I think merit some discussion:
>
> This is great code for validating addresses in general, especially when 
> guard pages are in use for NULL pointers, stack overflow, etc.  Thanks 
> for adding it!  But for [er]sp and [er]bp validation, don't you really 
> just want to know if the address is in the expected stack range?  Maybe 
> the code which sets up the stack could communicate the valid range to 
> CpuExceptionHandlerLib somehow.  It could use special debug register 
> values like 
> SourceLevelDebugPkg/Library/PeCoffExtraActionLibDebug/PeCoffExtraActionLib.c 
> does.  Or perhaps it could use dynamic PCDs (although I don't know that 
> it's a good idea to go looking up PCDs in an exception handler.)  Or 
> maybe there's a more straightforward way....  It would have to take AP 
> stacks into account, and probably SetJump/LongJump as well.  That may or 
> may not be simpler than the current code....

I'm not quite sure if I understood you correctly when you say that I
should be checking whether the address is in the expected range, but
using the debug registers to save call stack information seems like a
good idea, although it doesn't seem to be that simple as you mentioned.

>
> More generally, I'd like to see some sort of platform-specific callout 
> to further validate addresses.  Not all mapped addresses, or addresses 
> up to the architectural limit, are safe to access.  For instance, reads 
> to SMRAM outside of SMM will cause exceptions.  Also, we wouldn't want 
> to go backtracing through MMIO or MMCFG space:  reads there could 
> potentially have side effects on the hardware.

Yes - we should ensure that those regions are not accessed during the
stacktrace, as well as test this implementation a lot more.

>
> The rules can also vary at different points in boot.  For example, 
> before memory is initialized, Intel Xeon processors generally execute 
> 32-bit code in cache-as-RAM mode, where the caches are jury-rigged to 
> operate as temporary storage while the memory setup code is running.  In 
> CAR mode, only a few address ranges can be accessed without causing 
> machine checks:  the cache-as-RAM range containing the stack, heap, and 
> HOB list, the architectural firmware range below 4G, and a few specific 
> MMCFG and MMIO ranges.

Really great info. Thanks. We should take that into account as well.

>
> So I'd like to suggest that you define an AddressValidationLib library 
> class, which provides a routine which takes an address (or an address 
> range?) and an indication of the intended use (memory read, memory 
> write, execute/disassemble code, stack dump, IO, ...), and returns a 
> value specifying if the access is:
> - safe (IsLinearAddressValid() should return TRUE)
> - unsafe (IsLinearAddressValid() should return FALSE)
> - unknown (IsLinearAddressValid() should perform its other tests)
>
> You can supply a NULL instance which always returns "unknown" for 
> platforms which don't want to perform their own validation.

Great idea! I can do that for sure, but first of all, I'd like to make
sure the memory validation code is working correctly for IA32 and X64
platforms before introducing the AddressValidationLib library.

Thank you very much for the review and comments!

Paulo


^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add helper to valid memory addresses
  2018-01-04  1:36       ` Yao, Jiewen
  2018-01-04  1:58         ` Yao, Jiewen
@ 2018-01-04 13:18         ` Paulo Alcantara
  1 sibling, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2018-01-04 13:18 UTC (permalink / raw)
  To: Yao, Jiewen, edk2-devel@lists.01.org; +Cc: Laszlo Ersek, Dong, Eric

"Yao, Jiewen" <jiewen.yao@intel.com> writes:

Hi Jiewen,

> Some suggestion:
>
> 1) I am not sure if it is proper to use ASSERT in an exception handler, because we know something is wrong.
>
>   ASSERT ((PhysicalAddress & (sizeof (*Pml4TableEntry) - 1)) == 0);
>
> I suggest we just do the check, and return FALSE, if the prerequisite is not satisfied.

OK.

>
> 2) Can we use meaningful definition for BIT0, BIT7?
>
>   if ((*Pml4TableEntry & BIT0) == 0) {
>   if ((*PageDirPtrTableEntry & BIT7) != 0) {

Sure. Sorry for missing that.

>
> 3) I am not sure if I understand below code.
>
>   PhysicalAddress = (UINT64)Cr3 & (((1ULL << MaxPhyAddrBits) - 1) << 12);
>   PhysicalAddress = *Pml4TableEntry & (((1ULL << MaxPhyAddrBits) - 1) << 12);
>   PhysicalAddress = *PageDirPtrTableEntry & (((1ULL << MaxPhyAddrBits) - 1) << 12);
>   PhysicalAddress = *PageDirEntry & (((1ULL << MaxPhyAddrBits) - 1) << 12);
>
> If MaxPhyAddrBits is 48, you will get "Cr3 & 0x0FFFFFFFFFFFF000". Is that what you want? I think we need "Cr3 & 0x0000FFFFFFFFF000"
> Should it be: PhysicalAddress = (UINT64)Cr3 & ((1ULL << MaxPhyAddrBits) - 1) & (~0xFFF);

Yes, it should. I've just re-checked the Intel SDM Vol 3A and your
calculation is correct. Thanks! I'll fix that up in next series.

>
> 4) Can we use a more readable way to below? Personally, I do not suggest "<< 3", which is just the index calculation.
>
>   PhysicalAddress = (UINT64)Cr3 & (((1ULL << MaxPhyAddrBits) - 1) << 12);
>   PhysicalAddress |= (((UINT64)LinearAddress >> 39) & 0x1FF) << 3;
>   Pml4TableEntry = (UINT64 *)(UINTN)PhysicalAddress;
>   PhysicalAddress = *Pml4TableEntry & (((1ULL << MaxPhyAddrBits) - 1) << 12);
>
> For example:
>   PhysicalAddress = (UINT64)Cr3 & ((1ULL << MaxPhyAddrBits) - 1) & (~0xFFF);
>   Pml4TableEntry = (UINT64 *)(UINTN)PhysicalAddress;
>   Index= (UINTN)(((UINT64)LinearAddress >> 39) & 0x1FF);
>   PhysicalAddress = Pml4TableEntry[Index] & ((1ULL << MaxPhyAddrBits) - 1) & (~0xFFF);

Sure. This way is much clearer, indeed.

Thank you very much for the effort on reviewing this!

Paulo

>> -----Original Message-----
>> From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of Paulo
>> Alcantara
>> Sent: Friday, December 29, 2017 12:40 PM
>> To: edk2-devel@lists.01.org
>> Cc: Laszlo Ersek <lersek@redhat.com>; Dong, Eric <eric.dong@intel.com>
>> Subject: [edk2] [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add helper to
>> valid memory addresses
>> 
>> Introduce IsLinearAddressValid() function that will be used for
>> validating memory addresses that would get dereferenced during stack
>> traces in IA32 and X64 CPU exceptions.
>> 
>> Contributed-under: TianoCore Contribution Agreement 1.1
>> Cc: Eric Dong <eric.dong@intel.com>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Requested-by: Brian Johnson <brian.johnson@hpe.com>
>> Requested-by: Jiewen Yao <jiewen.yao@intel.com>
>> Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
>> ---
>>  UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c | 382
>> ++++++++++++++++++++
>>  UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h |  16 +
>>  2 files changed, 398 insertions(+)
>> 
>> diff --git
>> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
>> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
>> index 867c5c01d6..52b3eb1463 100644
>> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
>> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
>> @@ -14,6 +14,9 @@
>> 
>>  #include "CpuExceptionCommon.h"
>> 
>> +#include <Register/Msr.h>
>> +#include <Library/DebugLib.h>
>> +
>>  //
>>  // Error code flag indicating whether or not an error code will be
>>  // pushed on the stack if an exception occurs.
>> @@ -194,3 +197,382 @@ GetPdbFileName (
>>      }
>>    }
>>  }
>> +
>> +/**
>> +  Check if a linear address is valid by walking the page tables in 4-level
>> +  paging mode.
>> +
>> +  @param[in]  Cr3             CR3 control register.
>> +  @param[in]  MaxPhyAddrBits  MAXPHYADDRBITS bits.
>> +  @param[in]  LinearAddress   Linear address to be checked.
>> +**/
>> +STATIC
>> +BOOLEAN
>> +Do4LevelPagingModeCheck (
>> +  IN UINTN            Cr3,
>> +  IN UINT8            MaxPhyAddrBits,
>> +  IN UINTN            LinearAddress
>> +  )
>> +{
>> +  UINT64 PhysicalAddress;
>> +  UINT64 *Pml4TableEntry;
>> +  UINT64 *PageDirPtrTableEntry;
>> +  UINT64 *PageDirEntry;
>> +  UINT64 *PageTableEntry;
>> +
>> +  //
>> +  // In 4-level paging mode, linear addresses are 48 bits wide
>> +  //
>> +  if ((UINT64)LinearAddress > (1ULL << 48) - 1) {
>> +    return FALSE;
>> +  }
>> +
>> +  //
>> +  // Calculate physical address of PML4E
>> +  //
>> +  PhysicalAddress = (UINT64)Cr3 & (((1ULL << MaxPhyAddrBits) - 1) << 12);
>> +  PhysicalAddress |= (((UINT64)LinearAddress >> 39) & 0x1FF) << 3;
>> +
>> +  ASSERT ((PhysicalAddress & (sizeof (*Pml4TableEntry) - 1)) == 0);
>> +
>> +  Pml4TableEntry = (UINT64 *)(UINTN)PhysicalAddress;
>> +
>> +  //
>> +  // Check if a PDPTE is present
>> +  //
>> +  if ((*Pml4TableEntry & BIT0) == 0) {
>> +    return FALSE;
>> +  }
>> +
>> +  //
>> +  // Calculate physical address of PDPTE
>> +  //
>> +  PhysicalAddress = *Pml4TableEntry & (((1ULL << MaxPhyAddrBits) - 1) <<
>> 12);
>> +  PhysicalAddress |= (((UINT64)LinearAddress >> 30) & 0x1FF) << 3;
>> +
>> +  ASSERT ((PhysicalAddress & (sizeof (*PageDirPtrTableEntry) - 1)) == 0);
>> +
>> +  PageDirPtrTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
>> +
>> +  //
>> +  // Check whether a PDPTE or 1GiB page entry is present
>> +  //
>> +  if ((*PageDirPtrTableEntry & BIT0) == 0) {
>> +    return FALSE;
>> +  }
>> +
>> +  //
>> +  // Check if PDPTE maps an 1GiB page
>> +  //
>> +  if ((*PageDirPtrTableEntry & BIT7) != 0) {
>> +    return TRUE;
>> +  }
>> +
>> +  //
>> +  // Calculate physical address of PDE
>> +  //
>> +  PhysicalAddress = *PageDirPtrTableEntry & (((1ULL << MaxPhyAddrBits) - 1)
>> <<
>> +                                             12);
>> +  PhysicalAddress |= (((UINT64)LinearAddress >> 21) & 0x1FF) << 3;
>> +
>> +  ASSERT ((PhysicalAddress & (sizeof (*PageDirEntry) - 1)) == 0);
>> +
>> +  PageDirEntry = (UINT64 *)(UINTN)PhysicalAddress;
>> +
>> +  //
>> +  // Check whether a PDE or a 2MiB page entry is present
>> +  //
>> +  if ((*PageDirEntry & BIT0) == 0) {
>> +    return FALSE;
>> +  }
>> +
>> +  //
>> +  // Check if PDE maps a 2MiB page
>> +  //
>> +  if ((*PageDirEntry & BIT7) != 0) {
>> +    return TRUE;
>> +  }
>> +
>> +  //
>> +  // Calculate physical address of PTE
>> +  //
>> +  PhysicalAddress = *PageDirEntry & (((1ULL << MaxPhyAddrBits) - 1) << 12);
>> +  PhysicalAddress |= (((UINT64)LinearAddress >> 12) & 0x1FF) << 3;
>> +
>> +  ASSERT ((PhysicalAddress & (sizeof (*PageTableEntry) - 1)) == 0);
>> +
>> +  PageTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
>> +
>> +  //
>> +  // Check if PTE maps a 4KiB page
>> +  //
>> +  if ((*PageTableEntry & BIT0) == 0) {
>> +    return FALSE;
>> +  }
>> +
>> +  return TRUE;
>> +}
>> +
>> +/**
>> +  Check if a linear address is valid by walking the page tables in 32-bit paging
>> +  mode.
>> +
>> +  @param[in]  Cr3             CR3 control register.
>> +  @param[in]  Cr4             CR4 control register.
>> +  @param[in]  LinearAddress   Linear address to be checked.
>> +**/
>> +STATIC
>> +BOOLEAN
>> +Do32BitPagingModeCheck (
>> +  IN UINTN            Cr3,
>> +  IN UINTN            Cr4,
>> +  IN UINTN            LinearAddress
>> +  )
>> +{
>> +  UINT64 PhysicalAddress;
>> +  UINT32 *PageDirEntry;
>> +  UINT32 *PageTableEntry;
>> +
>> +  if (LinearAddress > MAX_UINT32) {
>> +    return FALSE;
>> +  }
>> +
>> +  //
>> +  // Calculate physical address of PDE
>> +  //
>> +  PhysicalAddress = (UINT32)Cr3 & (((1ULL << 20) - 1) << 12);
>> +  PhysicalAddress |= (((UINT32)LinearAddress >> 22) & 0x3FF) << 2;
>> +
>> +  ASSERT ((PhysicalAddress & (sizeof (*PageDirEntry) - 1)) == 0);
>> +
>> +  PageDirEntry = (UINT32 *)(UINTN)PhysicalAddress;
>> +
>> +  //
>> +  // Check whether a PTE or a 4MiB page is present
>> +  //
>> +  if ((*PageDirEntry & BIT0) == 0) {
>> +    return FALSE;
>> +  }
>> +
>> +  //
>> +  // Check if PDE maps a 4MiB page
>> +  //
>> +  if ((Cr4 & BIT4) != 0 && (*PageDirEntry & BIT7) != 0) {
>> +    return TRUE;
>> +  }
>> +
>> +  //
>> +  // Calculate physical address of PTE
>> +  //
>> +  PhysicalAddress = *PageDirEntry & (((1ULL << 20) - 1) << 12);
>> +  PhysicalAddress |= (((UINT32)LinearAddress >> 12) & 0x3FF) << 2;
>> +
>> +  ASSERT ((PhysicalAddress & (sizeof (*PageTableEntry) - 1)) == 0);
>> +
>> +  PageTableEntry = (UINT32 *)(UINTN)PhysicalAddress;
>> +
>> +  //
>> +  // Check if PTE maps a 4KiB page
>> +  //
>> +  if ((*PageTableEntry & BIT0) == 0) {
>> +    return FALSE;
>> +  }
>> +
>> +  return TRUE;
>> +}
>> +
>> +/**
>> +  Check if a linear address is valid by walking the page tables in PAE paging
>> +  mode.
>> +
>> +  @param[in]  Cr3             CR3 control register.
>> +  @param[in]  MaxPhyAddrBits  MAXPHYADDRBITS bits.
>> +  @param[in]  LinearAddress   Linear address to be checked.
>> +**/
>> +STATIC
>> +BOOLEAN
>> +DoPAEPagingModeCheck (
>> +  IN UINTN            Cr3,
>> +  IN UINT8            MaxPhyAddrBits,
>> +  IN UINTN            LinearAddress
>> +  )
>> +{
>> +  UINT64 PhysicalAddress;
>> +  UINT64 *PageDirPtrTableEntry;
>> +  UINT64 *PageDirEntry;
>> +  UINT64 *PageTableEntry;
>> +
>> +  if (LinearAddress > MAX_UINT32) {
>> +    return FALSE;
>> +  }
>> +
>> +  //
>> +  // Calculate physical address of PDPTE
>> +  //
>> +  PhysicalAddress = (UINT32)Cr3 >> 5;
>> +
>> +  //
>> +  // Select PDPTE register
>> +  //
>> +  PhysicalAddress +=
>> +    ((UINT32)LinearAddress >> 30) * sizeof (*PageDirPtrTableEntry);
>> +
>> +  PageDirPtrTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
>> +
>> +  //
>> +  // Check if PDE is present
>> +  //
>> +  if ((*PageDirPtrTableEntry & BIT0) == 0) {
>> +    return FALSE;
>> +  }
>> +
>> +  PhysicalAddress = *PageDirPtrTableEntry & (((1ULL << MaxPhyAddrBits) - 1)
>> <<
>> +                                             12);
>> +  PhysicalAddress |= ((LinearAddress >> 21) & 0x1FF) << 3;
>> +  ASSERT ((PhysicalAddress & (sizeof (*PageDirEntry) - 1)) == 0);
>> +
>> +  PageDirEntry = (UINT64 *)(UINTN)PhysicalAddress;
>> +
>> +  //
>> +  // Check whether a PTE or a 2MiB page is present
>> +  //
>> +  if ((*PageDirEntry & BIT0) == 0) {
>> +    return FALSE;
>> +  }
>> +
>> +  //
>> +  // Check if PDE maps a 2MiB page
>> +  //
>> +  if ((*PageDirEntry & BIT7) != 0) {
>> +    return TRUE;
>> +  }
>> +
>> +  //
>> +  // Calculate physical address of PTE
>> +  //
>> +  PhysicalAddress = *PageDirEntry & (((1ULL << MaxPhyAddrBits) - 1) << 12);
>> +  PhysicalAddress |= ((LinearAddress >> 12) & 0x1FF) << 3;
>> +  ASSERT ((PhysicalAddress & (sizeof (*PageTableEntry) - 1)) == 0);
>> +
>> +  PageTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
>> +
>> +  //
>> +  // Check if PTE maps a 4KiB page
>> +  //
>> +  if ((*PageTableEntry & BIT0) == 0) {
>> +    return FALSE;
>> +  }
>> +
>> +  return TRUE;
>> +}
>> +
>> +/**
>> +  Check if a linear address is valid.
>> +
>> +  @param[in]  Cr0            CR0 control register.
>> +  @param[in]  Cr3            CR3 control register.
>> +  @param[in]  Cr4            CR4 control register.
>> +  @param[in]  LinearAddress  Linear address to be checked.
>> +**/
>> +BOOLEAN
>> +IsLinearAddressValid (
>> +  IN  UINTN              Cr0,
>> +  IN  UINTN              Cr3,
>> +  IN  UINTN              Cr4,
>> +  IN  UINTN              LinearAddress
>> +  )
>> +{
>> +  UINT32                  Eax;
>> +  UINT32                  Edx;
>> +  UINT8                   MaxPhyAddrBits;
>> +  MSR_IA32_EFER_REGISTER  Msr;
>> +  BOOLEAN                 AddressValid;
>> +
>> +  //
>> +  // Check for valid input parameters
>> +  //
>> +  if (Cr0 == 0 || Cr4 == 0 || LinearAddress == 0) {
>> +    return FALSE;
>> +  }
>> +
>> +  //
>> +  // Check if paging is disabled
>> +  //
>> +  if ((Cr0 & BIT31) == 0) {
>> +    //
>> +    // If CR4.PAE bit is set, then the linear (or physical) address supports
>> +    // only up to 36 bits.
>> +    //
>> +    if (((Cr4 & BIT5) != 0 && (UINT64)LinearAddress > 0xFFFFFFFFFULL) ||
>> +        LinearAddress > 0xFFFFFFFF) {
>> +      return FALSE;
>> +    }
>> +
>> +    return TRUE;
>> +  }
>> +
>> +  //
>> +  // Paging can be enabled only if CR0.PE bit is set
>> +  //
>> +  if ((Cr0 & BIT0) == 0) {
>> +    return FALSE;
>> +  }
>> +
>> +  //
>> +  // CR3 register cannot be zero if paging is enabled
>> +  //
>> +  if (Cr3 == 0) {
>> +    return FALSE;
>> +  }
>> +
>> +  //
>> +  // Get MAXPHYADDR bits
>> +  //
>> +  AsmCpuid (0x80000000, &Eax, NULL, NULL, NULL);
>> +  if (Eax >= 0x80000008) {
>> +    AsmCpuid (0x80000008, &Eax, NULL, NULL, NULL);
>> +    MaxPhyAddrBits = (UINT8)Eax;
>> +  } else {
>> +    AsmCpuid (1, NULL, NULL, NULL, &Edx);
>> +    if ((Edx & BIT6) != 0) {
>> +      MaxPhyAddrBits = 36;
>> +    } else {
>> +      MaxPhyAddrBits = 32;
>> +    }
>> +  }
>> +
>> +  ASSERT (MaxPhyAddrBits > 0);
>> +
>> +  AddressValid = FALSE;
>> +
>> +  //
>> +  // check if CR4.PAE bit is not set
>> +  //
>> +  if ((Cr4 & BIT5) == 0) {
>> +    //
>> +    // Check if linear address is valid in 32-bit paging mode
>> +    //
>> +    AddressValid = Do32BitPagingModeCheck (Cr3, Cr4, LinearAddress);
>> +  } else {
>> +    if (MaxPhyAddrBits > 52) {
>> +      return FALSE;
>> +    }
>> +
>> +    Msr.Uint64 = AsmReadMsr64 (MSR_IA32_EFER);
>> +
>> +    if (Msr.Bits.LME == 0) {
>> +      //
>> +      // Check if linear address is valid in PAE paging mode
>> +      //
>> +      AddressValid = DoPAEPagingModeCheck (Cr3, MaxPhyAddrBits,
>> LinearAddress);
>> +    } else {
>> +      //
>> +      // Check if linear address is valid in 4-level paging mode
>> +      //
>> +      AddressValid = Do4LevelPagingModeCheck (Cr3, MaxPhyAddrBits,
>> +                                              LinearAddress);
>> +    }
>> +  }
>> +
>> +  return AddressValid;
>> +}
>> diff --git
>> a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
>> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
>> index ec46c2d9d3..1b51034c25 100644
>> --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
>> +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
>> @@ -330,5 +330,21 @@ GetPdbFileName (
>>    OUT CHAR8    **PdbFileName
>>    );
>> 
>> +/**
>> +  Check if a linear address is valid.
>> +
>> +  @param[in]  Cr0            CR0 control register.
>> +  @param[in]  Cr3            CR3 control register.
>> +  @param[in]  Cr4            CR4 control register.
>> +  @param[in]  LinearAddress  Linear address to be checked.
>> +**/
>> +BOOLEAN
>> +IsLinearAddressValid (
>> +  IN  UINTN              Cr0,
>> +  IN  UINTN              Cr3,
>> +  IN  UINTN              Cr4,
>> +  IN  UINTN              LinearAddress
>> +  );
>> +
>>  #endif
>> 
>> --
>> 2.14.3
>> 
>> _______________________________________________
>> edk2-devel mailing list
>> edk2-devel@lists.01.org
>> https://lists.01.org/mailman/listinfo/edk2-devel


^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add helper to valid memory addresses
  2018-01-04  1:58         ` Yao, Jiewen
@ 2018-01-04 13:29           ` Paulo Alcantara
  2018-01-04 14:35             ` Yao, Jiewen
  0 siblings, 1 reply; 82+ messages in thread
From: Paulo Alcantara @ 2018-01-04 13:29 UTC (permalink / raw)
  To: Yao, Jiewen, Yao, Jiewen, edk2-devel@lists.01.org
  Cc: Laszlo Ersek, Dong, Eric

"Yao, Jiewen" <jiewen.yao@intel.com> writes:

> 5) For CR4, please use meaning definition for BIT4/BIT5.
>   if ((Cr4 & BIT4) != 0 && (*PageDirEntry & BIT7) != 0) {
>     if (((Cr4 & BIT5) != 0 && (UINT64)LinearAddress > 0xFFFFFFFFFULL) ||

OK.

>
> 6) For IA32 PAE/PSE calculation, same comment for 3 and 4.

OK.

>
> 7) Last but not least important, would you please share the information on how do you validate the 32bit PAE/PSE/normal 4K page table?

Since on IA32 we use 32-bit protected flat model and paging disabled
(OK?), I wasn't able to validate the paging modes other than 4-level
paging mode in X64. The memory validation code I wrote is heavily based
upon what I read from Intel SDM Vol 3A manual.

If you do have any idea on how to validate it -- whether it's a PoC or
test code -- please let me know, and then I validate it.

Thanks again for your review!

Paulo

>
> Thank you
> Yao Jiewen
>
>> -----Original Message-----
>> From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of Yao,
>> Jiewen
>> Sent: Thursday, January 4, 2018 9:36 AM
>> To: Paulo Alcantara <paulo@paulo.ac>; edk2-devel@lists.01.org
>> Cc: Laszlo Ersek <lersek@redhat.com>; Dong, Eric <eric.dong@intel.com>
>> Subject: Re: [edk2] [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add
>> helper to valid memory addresses
>> 
>> Some suggestion:
>> 
>> 1) I am not sure if it is proper to use ASSERT in an exception handler, because we
>> know something is wrong.
>> 
>>   ASSERT ((PhysicalAddress & (sizeof (*Pml4TableEntry) - 1)) == 0);
>> 
>> I suggest we just do the check, and return FALSE, if the prerequisite is not
>> satisfied.
>> 
>> 2) Can we use meaningful definition for BIT0, BIT7?
>> 
>>   if ((*Pml4TableEntry & BIT0) == 0) {
>>   if ((*PageDirPtrTableEntry & BIT7) != 0) {
>> 
>> 3) I am not sure if I understand below code.
>> 
>>   PhysicalAddress = (UINT64)Cr3 & (((1ULL << MaxPhyAddrBits) - 1) << 12);
>>   PhysicalAddress = *Pml4TableEntry & (((1ULL << MaxPhyAddrBits) - 1) << 12);
>>   PhysicalAddress = *PageDirPtrTableEntry & (((1ULL << MaxPhyAddrBits) - 1)
>> << 12);
>>   PhysicalAddress = *PageDirEntry & (((1ULL << MaxPhyAddrBits) - 1) << 12);
>> 
>> If MaxPhyAddrBits is 48, you will get "Cr3 & 0x0FFFFFFFFFFFF000". Is that what
>> you want? I think we need "Cr3 & 0x0000FFFFFFFFF000"
>> Should it be: PhysicalAddress = (UINT64)Cr3 & ((1ULL << MaxPhyAddrBits) - 1) &
>> (~0xFFF);
>> 
>> 4) Can we use a more readable way to below? Personally, I do not suggest "<< 3",
>> which is just the index calculation.
>> 
>>   PhysicalAddress = (UINT64)Cr3 & (((1ULL << MaxPhyAddrBits) - 1) << 12);
>>   PhysicalAddress |= (((UINT64)LinearAddress >> 39) & 0x1FF) << 3;
>>   Pml4TableEntry = (UINT64 *)(UINTN)PhysicalAddress;
>>   PhysicalAddress = *Pml4TableEntry & (((1ULL << MaxPhyAddrBits) - 1) << 12);
>> 
>> For example:
>>   PhysicalAddress = (UINT64)Cr3 & ((1ULL << MaxPhyAddrBits) - 1) & (~0xFFF);
>>   Pml4TableEntry = (UINT64 *)(UINTN)PhysicalAddress;
>>   Index= (UINTN)(((UINT64)LinearAddress >> 39) & 0x1FF);
>>   PhysicalAddress = Pml4TableEntry[Index] & ((1ULL << MaxPhyAddrBits) - 1) &
>> (~0xFFF);
>> 
>> 
>> 
>> Thank you
>> Yao Jiewen
>> 
>> 
>> > -----Original Message-----
>> > From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of
>> Paulo
>> > Alcantara
>> > Sent: Friday, December 29, 2017 12:40 PM
>> > To: edk2-devel@lists.01.org
>> > Cc: Laszlo Ersek <lersek@redhat.com>; Dong, Eric <eric.dong@intel.com>
>> > Subject: [edk2] [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add helper
>> to
>> > valid memory addresses
>> >
>> > Introduce IsLinearAddressValid() function that will be used for
>> > validating memory addresses that would get dereferenced during stack
>> > traces in IA32 and X64 CPU exceptions.
>> >
>> > Contributed-under: TianoCore Contribution Agreement 1.1
>> > Cc: Eric Dong <eric.dong@intel.com>
>> > Cc: Laszlo Ersek <lersek@redhat.com>
>> > Requested-by: Brian Johnson <brian.johnson@hpe.com>
>> > Requested-by: Jiewen Yao <jiewen.yao@intel.com>
>> > Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
>> > ---
>> >  UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c | 382
>> > ++++++++++++++++++++
>> >  UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h |
>> 16 +
>> >  2 files changed, 398 insertions(+)
>> >
>> > diff --git
>> > a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
>> > b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
>> > index 867c5c01d6..52b3eb1463 100644
>> > --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
>> > +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
>> > @@ -14,6 +14,9 @@
>> >
>> >  #include "CpuExceptionCommon.h"
>> >
>> > +#include <Register/Msr.h>
>> > +#include <Library/DebugLib.h>
>> > +
>> >  //
>> >  // Error code flag indicating whether or not an error code will be
>> >  // pushed on the stack if an exception occurs.
>> > @@ -194,3 +197,382 @@ GetPdbFileName (
>> >      }
>> >    }
>> >  }
>> > +
>> > +/**
>> > +  Check if a linear address is valid by walking the page tables in 4-level
>> > +  paging mode.
>> > +
>> > +  @param[in]  Cr3             CR3 control register.
>> > +  @param[in]  MaxPhyAddrBits  MAXPHYADDRBITS bits.
>> > +  @param[in]  LinearAddress   Linear address to be checked.
>> > +**/
>> > +STATIC
>> > +BOOLEAN
>> > +Do4LevelPagingModeCheck (
>> > +  IN UINTN            Cr3,
>> > +  IN UINT8            MaxPhyAddrBits,
>> > +  IN UINTN            LinearAddress
>> > +  )
>> > +{
>> > +  UINT64 PhysicalAddress;
>> > +  UINT64 *Pml4TableEntry;
>> > +  UINT64 *PageDirPtrTableEntry;
>> > +  UINT64 *PageDirEntry;
>> > +  UINT64 *PageTableEntry;
>> > +
>> > +  //
>> > +  // In 4-level paging mode, linear addresses are 48 bits wide
>> > +  //
>> > +  if ((UINT64)LinearAddress > (1ULL << 48) - 1) {
>> > +    return FALSE;
>> > +  }
>> > +
>> > +  //
>> > +  // Calculate physical address of PML4E
>> > +  //
>> > +  PhysicalAddress = (UINT64)Cr3 & (((1ULL << MaxPhyAddrBits) - 1) << 12);
>> > +  PhysicalAddress |= (((UINT64)LinearAddress >> 39) & 0x1FF) << 3;
>> > +
>> > +  ASSERT ((PhysicalAddress & (sizeof (*Pml4TableEntry) - 1)) == 0);
>> > +
>> > +  Pml4TableEntry = (UINT64 *)(UINTN)PhysicalAddress;
>> > +
>> > +  //
>> > +  // Check if a PDPTE is present
>> > +  //
>> > +  if ((*Pml4TableEntry & BIT0) == 0) {
>> > +    return FALSE;
>> > +  }
>> > +
>> > +  //
>> > +  // Calculate physical address of PDPTE
>> > +  //
>> > +  PhysicalAddress = *Pml4TableEntry & (((1ULL << MaxPhyAddrBits) - 1) <<
>> > 12);
>> > +  PhysicalAddress |= (((UINT64)LinearAddress >> 30) & 0x1FF) << 3;
>> > +
>> > +  ASSERT ((PhysicalAddress & (sizeof (*PageDirPtrTableEntry) - 1)) == 0);
>> > +
>> > +  PageDirPtrTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
>> > +
>> > +  //
>> > +  // Check whether a PDPTE or 1GiB page entry is present
>> > +  //
>> > +  if ((*PageDirPtrTableEntry & BIT0) == 0) {
>> > +    return FALSE;
>> > +  }
>> > +
>> > +  //
>> > +  // Check if PDPTE maps an 1GiB page
>> > +  //
>> > +  if ((*PageDirPtrTableEntry & BIT7) != 0) {
>> > +    return TRUE;
>> > +  }
>> > +
>> > +  //
>> > +  // Calculate physical address of PDE
>> > +  //
>> > +  PhysicalAddress = *PageDirPtrTableEntry & (((1ULL << MaxPhyAddrBits) -
>> 1)
>> > <<
>> > +                                             12);
>> > +  PhysicalAddress |= (((UINT64)LinearAddress >> 21) & 0x1FF) << 3;
>> > +
>> > +  ASSERT ((PhysicalAddress & (sizeof (*PageDirEntry) - 1)) == 0);
>> > +
>> > +  PageDirEntry = (UINT64 *)(UINTN)PhysicalAddress;
>> > +
>> > +  //
>> > +  // Check whether a PDE or a 2MiB page entry is present
>> > +  //
>> > +  if ((*PageDirEntry & BIT0) == 0) {
>> > +    return FALSE;
>> > +  }
>> > +
>> > +  //
>> > +  // Check if PDE maps a 2MiB page
>> > +  //
>> > +  if ((*PageDirEntry & BIT7) != 0) {
>> > +    return TRUE;
>> > +  }
>> > +
>> > +  //
>> > +  // Calculate physical address of PTE
>> > +  //
>> > +  PhysicalAddress = *PageDirEntry & (((1ULL << MaxPhyAddrBits) - 1) <<
>> 12);
>> > +  PhysicalAddress |= (((UINT64)LinearAddress >> 12) & 0x1FF) << 3;
>> > +
>> > +  ASSERT ((PhysicalAddress & (sizeof (*PageTableEntry) - 1)) == 0);
>> > +
>> > +  PageTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
>> > +
>> > +  //
>> > +  // Check if PTE maps a 4KiB page
>> > +  //
>> > +  if ((*PageTableEntry & BIT0) == 0) {
>> > +    return FALSE;
>> > +  }
>> > +
>> > +  return TRUE;
>> > +}
>> > +
>> > +/**
>> > +  Check if a linear address is valid by walking the page tables in 32-bit paging
>> > +  mode.
>> > +
>> > +  @param[in]  Cr3             CR3 control register.
>> > +  @param[in]  Cr4             CR4 control register.
>> > +  @param[in]  LinearAddress   Linear address to be checked.
>> > +**/
>> > +STATIC
>> > +BOOLEAN
>> > +Do32BitPagingModeCheck (
>> > +  IN UINTN            Cr3,
>> > +  IN UINTN            Cr4,
>> > +  IN UINTN            LinearAddress
>> > +  )
>> > +{
>> > +  UINT64 PhysicalAddress;
>> > +  UINT32 *PageDirEntry;
>> > +  UINT32 *PageTableEntry;
>> > +
>> > +  if (LinearAddress > MAX_UINT32) {
>> > +    return FALSE;
>> > +  }
>> > +
>> > +  //
>> > +  // Calculate physical address of PDE
>> > +  //
>> > +  PhysicalAddress = (UINT32)Cr3 & (((1ULL << 20) - 1) << 12);
>> > +  PhysicalAddress |= (((UINT32)LinearAddress >> 22) & 0x3FF) << 2;
>> > +
>> > +  ASSERT ((PhysicalAddress & (sizeof (*PageDirEntry) - 1)) == 0);
>> > +
>> > +  PageDirEntry = (UINT32 *)(UINTN)PhysicalAddress;
>> > +
>> > +  //
>> > +  // Check whether a PTE or a 4MiB page is present
>> > +  //
>> > +  if ((*PageDirEntry & BIT0) == 0) {
>> > +    return FALSE;
>> > +  }
>> > +
>> > +  //
>> > +  // Check if PDE maps a 4MiB page
>> > +  //
>> > +  if ((Cr4 & BIT4) != 0 && (*PageDirEntry & BIT7) != 0) {
>> > +    return TRUE;
>> > +  }
>> > +
>> > +  //
>> > +  // Calculate physical address of PTE
>> > +  //
>> > +  PhysicalAddress = *PageDirEntry & (((1ULL << 20) - 1) << 12);
>> > +  PhysicalAddress |= (((UINT32)LinearAddress >> 12) & 0x3FF) << 2;
>> > +
>> > +  ASSERT ((PhysicalAddress & (sizeof (*PageTableEntry) - 1)) == 0);
>> > +
>> > +  PageTableEntry = (UINT32 *)(UINTN)PhysicalAddress;
>> > +
>> > +  //
>> > +  // Check if PTE maps a 4KiB page
>> > +  //
>> > +  if ((*PageTableEntry & BIT0) == 0) {
>> > +    return FALSE;
>> > +  }
>> > +
>> > +  return TRUE;
>> > +}
>> > +
>> > +/**
>> > +  Check if a linear address is valid by walking the page tables in PAE paging
>> > +  mode.
>> > +
>> > +  @param[in]  Cr3             CR3 control register.
>> > +  @param[in]  MaxPhyAddrBits  MAXPHYADDRBITS bits.
>> > +  @param[in]  LinearAddress   Linear address to be checked.
>> > +**/
>> > +STATIC
>> > +BOOLEAN
>> > +DoPAEPagingModeCheck (
>> > +  IN UINTN            Cr3,
>> > +  IN UINT8            MaxPhyAddrBits,
>> > +  IN UINTN            LinearAddress
>> > +  )
>> > +{
>> > +  UINT64 PhysicalAddress;
>> > +  UINT64 *PageDirPtrTableEntry;
>> > +  UINT64 *PageDirEntry;
>> > +  UINT64 *PageTableEntry;
>> > +
>> > +  if (LinearAddress > MAX_UINT32) {
>> > +    return FALSE;
>> > +  }
>> > +
>> > +  //
>> > +  // Calculate physical address of PDPTE
>> > +  //
>> > +  PhysicalAddress = (UINT32)Cr3 >> 5;
>> > +
>> > +  //
>> > +  // Select PDPTE register
>> > +  //
>> > +  PhysicalAddress +=
>> > +    ((UINT32)LinearAddress >> 30) * sizeof (*PageDirPtrTableEntry);
>> > +
>> > +  PageDirPtrTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
>> > +
>> > +  //
>> > +  // Check if PDE is present
>> > +  //
>> > +  if ((*PageDirPtrTableEntry & BIT0) == 0) {
>> > +    return FALSE;
>> > +  }
>> > +
>> > +  PhysicalAddress = *PageDirPtrTableEntry & (((1ULL << MaxPhyAddrBits) -
>> 1)
>> > <<
>> > +                                             12);
>> > +  PhysicalAddress |= ((LinearAddress >> 21) & 0x1FF) << 3;
>> > +  ASSERT ((PhysicalAddress & (sizeof (*PageDirEntry) - 1)) == 0);
>> > +
>> > +  PageDirEntry = (UINT64 *)(UINTN)PhysicalAddress;
>> > +
>> > +  //
>> > +  // Check whether a PTE or a 2MiB page is present
>> > +  //
>> > +  if ((*PageDirEntry & BIT0) == 0) {
>> > +    return FALSE;
>> > +  }
>> > +
>> > +  //
>> > +  // Check if PDE maps a 2MiB page
>> > +  //
>> > +  if ((*PageDirEntry & BIT7) != 0) {
>> > +    return TRUE;
>> > +  }
>> > +
>> > +  //
>> > +  // Calculate physical address of PTE
>> > +  //
>> > +  PhysicalAddress = *PageDirEntry & (((1ULL << MaxPhyAddrBits) - 1) <<
>> 12);
>> > +  PhysicalAddress |= ((LinearAddress >> 12) & 0x1FF) << 3;
>> > +  ASSERT ((PhysicalAddress & (sizeof (*PageTableEntry) - 1)) == 0);
>> > +
>> > +  PageTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
>> > +
>> > +  //
>> > +  // Check if PTE maps a 4KiB page
>> > +  //
>> > +  if ((*PageTableEntry & BIT0) == 0) {
>> > +    return FALSE;
>> > +  }
>> > +
>> > +  return TRUE;
>> > +}
>> > +
>> > +/**
>> > +  Check if a linear address is valid.
>> > +
>> > +  @param[in]  Cr0            CR0 control register.
>> > +  @param[in]  Cr3            CR3 control register.
>> > +  @param[in]  Cr4            CR4 control register.
>> > +  @param[in]  LinearAddress  Linear address to be checked.
>> > +**/
>> > +BOOLEAN
>> > +IsLinearAddressValid (
>> > +  IN  UINTN              Cr0,
>> > +  IN  UINTN              Cr3,
>> > +  IN  UINTN              Cr4,
>> > +  IN  UINTN              LinearAddress
>> > +  )
>> > +{
>> > +  UINT32                  Eax;
>> > +  UINT32                  Edx;
>> > +  UINT8                   MaxPhyAddrBits;
>> > +  MSR_IA32_EFER_REGISTER  Msr;
>> > +  BOOLEAN                 AddressValid;
>> > +
>> > +  //
>> > +  // Check for valid input parameters
>> > +  //
>> > +  if (Cr0 == 0 || Cr4 == 0 || LinearAddress == 0) {
>> > +    return FALSE;
>> > +  }
>> > +
>> > +  //
>> > +  // Check if paging is disabled
>> > +  //
>> > +  if ((Cr0 & BIT31) == 0) {
>> > +    //
>> > +    // If CR4.PAE bit is set, then the linear (or physical) address supports
>> > +    // only up to 36 bits.
>> > +    //
>> > +    if (((Cr4 & BIT5) != 0 && (UINT64)LinearAddress > 0xFFFFFFFFFULL) ||
>> > +        LinearAddress > 0xFFFFFFFF) {
>> > +      return FALSE;
>> > +    }
>> > +
>> > +    return TRUE;
>> > +  }
>> > +
>> > +  //
>> > +  // Paging can be enabled only if CR0.PE bit is set
>> > +  //
>> > +  if ((Cr0 & BIT0) == 0) {
>> > +    return FALSE;
>> > +  }
>> > +
>> > +  //
>> > +  // CR3 register cannot be zero if paging is enabled
>> > +  //
>> > +  if (Cr3 == 0) {
>> > +    return FALSE;
>> > +  }
>> > +
>> > +  //
>> > +  // Get MAXPHYADDR bits
>> > +  //
>> > +  AsmCpuid (0x80000000, &Eax, NULL, NULL, NULL);
>> > +  if (Eax >= 0x80000008) {
>> > +    AsmCpuid (0x80000008, &Eax, NULL, NULL, NULL);
>> > +    MaxPhyAddrBits = (UINT8)Eax;
>> > +  } else {
>> > +    AsmCpuid (1, NULL, NULL, NULL, &Edx);
>> > +    if ((Edx & BIT6) != 0) {
>> > +      MaxPhyAddrBits = 36;
>> > +    } else {
>> > +      MaxPhyAddrBits = 32;
>> > +    }
>> > +  }
>> > +
>> > +  ASSERT (MaxPhyAddrBits > 0);
>> > +
>> > +  AddressValid = FALSE;
>> > +
>> > +  //
>> > +  // check if CR4.PAE bit is not set
>> > +  //
>> > +  if ((Cr4 & BIT5) == 0) {
>> > +    //
>> > +    // Check if linear address is valid in 32-bit paging mode
>> > +    //
>> > +    AddressValid = Do32BitPagingModeCheck (Cr3, Cr4, LinearAddress);
>> > +  } else {
>> > +    if (MaxPhyAddrBits > 52) {
>> > +      return FALSE;
>> > +    }
>> > +
>> > +    Msr.Uint64 = AsmReadMsr64 (MSR_IA32_EFER);
>> > +
>> > +    if (Msr.Bits.LME == 0) {
>> > +      //
>> > +      // Check if linear address is valid in PAE paging mode
>> > +      //
>> > +      AddressValid = DoPAEPagingModeCheck (Cr3, MaxPhyAddrBits,
>> > LinearAddress);
>> > +    } else {
>> > +      //
>> > +      // Check if linear address is valid in 4-level paging mode
>> > +      //
>> > +      AddressValid = Do4LevelPagingModeCheck (Cr3, MaxPhyAddrBits,
>> > +                                              LinearAddress);
>> > +    }
>> > +  }
>> > +
>> > +  return AddressValid;
>> > +}
>> > diff --git
>> > a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
>> > b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
>> > index ec46c2d9d3..1b51034c25 100644
>> > --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
>> > +++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
>> > @@ -330,5 +330,21 @@ GetPdbFileName (
>> >    OUT CHAR8    **PdbFileName
>> >    );
>> >
>> > +/**
>> > +  Check if a linear address is valid.
>> > +
>> > +  @param[in]  Cr0            CR0 control register.
>> > +  @param[in]  Cr3            CR3 control register.
>> > +  @param[in]  Cr4            CR4 control register.
>> > +  @param[in]  LinearAddress  Linear address to be checked.
>> > +**/
>> > +BOOLEAN
>> > +IsLinearAddressValid (
>> > +  IN  UINTN              Cr0,
>> > +  IN  UINTN              Cr3,
>> > +  IN  UINTN              Cr4,
>> > +  IN  UINTN              LinearAddress
>> > +  );
>> > +
>> >  #endif
>> >
>> > --
>> > 2.14.3
>> >
>> > _______________________________________________
>> > edk2-devel mailing list
>> > edk2-devel@lists.01.org
>> > https://lists.01.org/mailman/listinfo/edk2-devel
>> _______________________________________________
>> edk2-devel mailing list
>> edk2-devel@lists.01.org
>> https://lists.01.org/mailman/listinfo/edk2-devel


^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC v4 0/6] Stack trace support in X64 exception handling
  2018-01-04  0:59     ` [RFC v4 0/6] Stack trace support in X64 exception handling Yao, Jiewen
@ 2018-01-04 13:36       ` Paulo Alcantara
  0 siblings, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2018-01-04 13:36 UTC (permalink / raw)
  To: Yao, Jiewen, edk2-devel@lists.01.org
  Cc: Rick Bramley, Dong, Eric, Andrew Fish, Laszlo Ersek

"Yao, Jiewen" <jiewen.yao@intel.com> writes:

Hi Jiewen,

> I tried to apply the patch series to latest code, but fail with below message.
>
> Have you rebased to latest code?

Hrm - I thought I did before sending out v4. Sorry for that. I'll make
sure to rebase it against latest master before sending out v5.

Thanks
Paulo

>
> ===================
> git.exe am --3way --ignore-space-change --keep-cr "C:\home\EdkIIGit\edk2\[edk2]-[RFC-v4-1-6]-UefiCpuPkg-CpuExceptionHandlerLib-X64-Add-stack-trace-support.patch"
> Applying: UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
> Using index info to reconstruct a base tree...
> M	UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> Falling back to patching base and 3-way merge...
> Auto-merging UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> CONFLICT (content): Merge conflict in UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> Patch failed at 0001 UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
> The copy of the patch that failed is found in: .git/rebase-apply/patch
> When you have resolved this problem, run "git am --continue".
> If you prefer to skip this patch, run "git am --skip" instead.
> To restore the original branch and stop patching, run "git am --abort".
>
> .git/rebase-apply/patch:13: trailing whitespace.
> //
> .git/rebase-apply/patch:14: trailing whitespace.
> // Unknown PDB file name
> .git/rebase-apply/patch:15: trailing whitespace.
> //
> .git/rebase-apply/patch:16: trailing whitespace.
> GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
> .git/rebase-apply/patch:17: trailing whitespace.
>
> warning: squelched 369 whitespace errors
> warning: 374 lines add whitespace errors.
> error: Failed to merge in the changes.
>
> Fail
> =================================================
>
>> -----Original Message-----
>> From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of Paulo
>> Alcantara
>> Sent: Friday, December 29, 2017 11:49 AM
>> To: edk2-devel@lists.01.org
>> Cc: Rick Bramley <richard.bramley@hp.com>; Dong, Eric
>> <eric.dong@intel.com>; Andrew Fish <afish@apple.com>; Yao, Jiewen
>> <jiewen.yao@intel.com>; Laszlo Ersek <lersek@redhat.com>
>> Subject: [edk2] [RFC v4 0/6] Stack trace support in X64 exception handling
>> 
>> Hi,
>> 
>> This series adds stack trace support during IA32 and X64 CPU exceptions.
>> 
>> Informations like back trace, stack contents and image module names
>> (that were part of the call stack) will be dumped out.
>> 
>> The current limitation is that it relies on available frame pointers
>> (GCC only) in order to successfully unwind the stack.
>> 
>> (Sorry for the very long delay - I was very busy working on something
>>  else and then went to vacations)
>> 
>> Jiewen,
>> 
>> I have tested it with VS2015x86 and the stack trace just hanged when
>> printing out the first EIP (that is, no frame pointer at all).
>> 
>> Thanks!
>> Paulo
>> 
>> Repo:   https://github.com/pcacjr/edk2.git
>> Branch: stacktrace_v4
>> 
>> Cc: Rick Bramley <richard.bramley@hp.com>
>> Cc: Andrew Fish <afish@apple.com>
>> Cc: Eric Dong <eric.dong@intel.com>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Cc: Brian Johnson <brian.johnson@hpe.com>
>> Cc: Jeff Fan <vanjeff_919@hotmail.com>
>> Cc: Jiewen Yao <jiewen.yao@intel.com>
>> Contributed-under: TianoCore Contribution Agreement 1.1
>> Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
>> ---
>> 
>> v1 -> v2:
>>   * Add IA32 arch support (GCC toolchain only)
>>   * Replace hard-coded stack alignment value (16) with
>>     CPU_STACK_ALIGNMENT.
>>   * Check for proper stack and frame pointer alignments.
>>   * Fix initialization of UnwoundStacksCount to 1.
>>   * Move GetPdbFileName() to common code since it will be used by both
>>     IA32 and X64 implementations.
>> 
>> v2 -> v3:
>>   * Fixed wrong assumption about "RIP < ImageBase" to start searching
>>     for another PE/COFF image. That is, RIP may point to lower and
>>     higher addresses for any other PE/COFF images. Both IA32 & X64.
>>     (Thanks Andrew & Jiewen)
>>   * Fixed typo: unwond -> unwound. Both IA32 & X64. (Thanks Brian)
>> 
>> v3 -> v4:
>>   * Validate all frame/stack pointer addresses before dereferencing them
>>     as requested by Brian & Jiewen.
>>   * Correctly print out IP addresses during the stack traces (by Jeff)
>> 
>> Paulo Alcantara (6):
>>   UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
>>   UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName()
>>   UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support
>>   UefiCpuPkg/CpuExceptionHandlerLib: Add helper to valid memory
>>     addresses
>>   UefiCpuPkg/CpuExceptionHandlerLib: Ensure valid frame/stack pointers
>>   UefiCpuPkg/CpuExceptionHandlerLib: Correctly print IP addresses
>> 
>>  UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
>> | 484 ++++++++++++++++++--
>>  UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
>> |  41 +-
>>  UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c |
>> 445 +++++++++++++++++-
>>  UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c  |
>> 384 +++++++++++++++-
>>  4 files changed, 1296 insertions(+), 58 deletions(-)
>> 
>> --
>> 2.14.3
>> 
>> _______________________________________________
>> edk2-devel mailing list
>> edk2-devel@lists.01.org
>> https://lists.01.org/mailman/listinfo/edk2-devel


^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add helper to valid memory addresses
  2018-01-04 13:29           ` Paulo Alcantara
@ 2018-01-04 14:35             ` Yao, Jiewen
  2018-01-04 15:15               ` Paulo Alcantara
  0 siblings, 1 reply; 82+ messages in thread
From: Yao, Jiewen @ 2018-01-04 14:35 UTC (permalink / raw)
  To: Paulo Alcantara, edk2-devel@lists.01.org; +Cc: Laszlo Ersek, Dong, Eric

Thank you!

For 7), you are right that we disable IA32 paging by default.
However, we do support IA32 PAE in DxeIpl. Please refer to MdeModulePkg\Core\DxeIplPeim\Ia32\DxeLoadFunc.c
======================
    PageTables = 0;
    BuildPageTablesIa32Pae = (BOOLEAN) (PcdGetBool (PcdSetNxForStack) && IsIa32PaeSupport () && IsExecuteDisableBitAvailable ());
    if (BuildPageTablesIa32Pae) {
      PageTables = Create4GPageTablesIa32Pae (BaseOfStack, STACK_SIZE);
      EnableExecuteDisableBit ();
    }
======================

Please notice that we only support IA32 PAE, we do not support IA32 non-PAE mode so far. (no matter PSE is ON/OFF)

So, I suggest:
7.1) Please validate IA32 PAE mode. (You can enable PcdSetNxForStack)
7.2) If we cannot validate the IA32 non-PAE code, please remove them. You can just print "IA32 non-PAE - UNSUPPORTED" and return invalid address. Then it can save our development time, review time, and validation time. The key is that we only want to check in the validated code.

Thank you
Yao Jiewen


> -----Original Message-----
> From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of Paulo
> Alcantara
> Sent: Thursday, January 4, 2018 9:30 PM
> To: Yao, Jiewen <jiewen.yao@intel.com>; Yao, Jiewen <jiewen.yao@intel.com>;
> edk2-devel@lists.01.org
> Cc: Laszlo Ersek <lersek@redhat.com>; Dong, Eric <eric.dong@intel.com>
> Subject: Re: [edk2] [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add
> helper to valid memory addresses
> 
> "Yao, Jiewen" <jiewen.yao@intel.com> writes:
> 
> > 5) For CR4, please use meaning definition for BIT4/BIT5.
> >   if ((Cr4 & BIT4) != 0 && (*PageDirEntry & BIT7) != 0) {
> >     if (((Cr4 & BIT5) != 0 && (UINT64)LinearAddress > 0xFFFFFFFFFULL) ||
> 
> OK.
> 
> >
> > 6) For IA32 PAE/PSE calculation, same comment for 3 and 4.
> 
> OK.
> 
> >
> > 7) Last but not least important, would you please share the information on
> how do you validate the 32bit PAE/PSE/normal 4K page table?
> 
> Since on IA32 we use 32-bit protected flat model and paging disabled
> (OK?), I wasn't able to validate the paging modes other than 4-level
> paging mode in X64. The memory validation code I wrote is heavily based
> upon what I read from Intel SDM Vol 3A manual.
> 
> If you do have any idea on how to validate it -- whether it's a PoC or
> test code -- please let me know, and then I validate it.
> 
> Thanks again for your review!
> 
> Paulo
> 
> >
> > Thank you
> > Yao Jiewen
> >
> >> -----Original Message-----
> >> From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of
> Yao,
> >> Jiewen
> >> Sent: Thursday, January 4, 2018 9:36 AM
> >> To: Paulo Alcantara <paulo@paulo.ac>; edk2-devel@lists.01.org
> >> Cc: Laszlo Ersek <lersek@redhat.com>; Dong, Eric <eric.dong@intel.com>
> >> Subject: Re: [edk2] [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add
> >> helper to valid memory addresses
> >>
> >> Some suggestion:
> >>
> >> 1) I am not sure if it is proper to use ASSERT in an exception handler, because
> we
> >> know something is wrong.
> >>
> >>   ASSERT ((PhysicalAddress & (sizeof (*Pml4TableEntry) - 1)) == 0);
> >>
> >> I suggest we just do the check, and return FALSE, if the prerequisite is not
> >> satisfied.
> >>
> >> 2) Can we use meaningful definition for BIT0, BIT7?
> >>
> >>   if ((*Pml4TableEntry & BIT0) == 0) {
> >>   if ((*PageDirPtrTableEntry & BIT7) != 0) {
> >>
> >> 3) I am not sure if I understand below code.
> >>
> >>   PhysicalAddress = (UINT64)Cr3 & (((1ULL << MaxPhyAddrBits) - 1) << 12);
> >>   PhysicalAddress = *Pml4TableEntry & (((1ULL << MaxPhyAddrBits) - 1) <<
> 12);
> >>   PhysicalAddress = *PageDirPtrTableEntry & (((1ULL << MaxPhyAddrBits) -
> 1)
> >> << 12);
> >>   PhysicalAddress = *PageDirEntry & (((1ULL << MaxPhyAddrBits) - 1) <<
> 12);
> >>
> >> If MaxPhyAddrBits is 48, you will get "Cr3 & 0x0FFFFFFFFFFFF000". Is that
> what
> >> you want? I think we need "Cr3 & 0x0000FFFFFFFFF000"
> >> Should it be: PhysicalAddress = (UINT64)Cr3 & ((1ULL << MaxPhyAddrBits) - 1)
> &
> >> (~0xFFF);
> >>
> >> 4) Can we use a more readable way to below? Personally, I do not suggest
> "<< 3",
> >> which is just the index calculation.
> >>
> >>   PhysicalAddress = (UINT64)Cr3 & (((1ULL << MaxPhyAddrBits) - 1) << 12);
> >>   PhysicalAddress |= (((UINT64)LinearAddress >> 39) & 0x1FF) << 3;
> >>   Pml4TableEntry = (UINT64 *)(UINTN)PhysicalAddress;
> >>   PhysicalAddress = *Pml4TableEntry & (((1ULL << MaxPhyAddrBits) - 1) <<
> 12);
> >>
> >> For example:
> >>   PhysicalAddress = (UINT64)Cr3 & ((1ULL << MaxPhyAddrBits) - 1) &
> (~0xFFF);
> >>   Pml4TableEntry = (UINT64 *)(UINTN)PhysicalAddress;
> >>   Index= (UINTN)(((UINT64)LinearAddress >> 39) & 0x1FF);
> >>   PhysicalAddress = Pml4TableEntry[Index] & ((1ULL << MaxPhyAddrBits) - 1)
> &
> >> (~0xFFF);
> >>
> >>
> >>
> >> Thank you
> >> Yao Jiewen
> >>
> >>
> >> > -----Original Message-----
> >> > From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of
> >> Paulo
> >> > Alcantara
> >> > Sent: Friday, December 29, 2017 12:40 PM
> >> > To: edk2-devel@lists.01.org
> >> > Cc: Laszlo Ersek <lersek@redhat.com>; Dong, Eric <eric.dong@intel.com>
> >> > Subject: [edk2] [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add
> helper
> >> to
> >> > valid memory addresses
> >> >
> >> > Introduce IsLinearAddressValid() function that will be used for
> >> > validating memory addresses that would get dereferenced during stack
> >> > traces in IA32 and X64 CPU exceptions.
> >> >
> >> > Contributed-under: TianoCore Contribution Agreement 1.1
> >> > Cc: Eric Dong <eric.dong@intel.com>
> >> > Cc: Laszlo Ersek <lersek@redhat.com>
> >> > Requested-by: Brian Johnson <brian.johnson@hpe.com>
> >> > Requested-by: Jiewen Yao <jiewen.yao@intel.com>
> >> > Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
> >> > ---
> >> >  UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c |
> 382
> >> > ++++++++++++++++++++
> >> >  UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h |
> >> 16 +
> >> >  2 files changed, 398 insertions(+)
> >> >
> >> > diff --git
> >> > a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
> >> > b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
> >> > index 867c5c01d6..52b3eb1463 100644
> >> > --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
> >> > +++
> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
> >> > @@ -14,6 +14,9 @@
> >> >
> >> >  #include "CpuExceptionCommon.h"
> >> >
> >> > +#include <Register/Msr.h>
> >> > +#include <Library/DebugLib.h>
> >> > +
> >> >  //
> >> >  // Error code flag indicating whether or not an error code will be
> >> >  // pushed on the stack if an exception occurs.
> >> > @@ -194,3 +197,382 @@ GetPdbFileName (
> >> >      }
> >> >    }
> >> >  }
> >> > +
> >> > +/**
> >> > +  Check if a linear address is valid by walking the page tables in 4-level
> >> > +  paging mode.
> >> > +
> >> > +  @param[in]  Cr3             CR3 control register.
> >> > +  @param[in]  MaxPhyAddrBits  MAXPHYADDRBITS bits.
> >> > +  @param[in]  LinearAddress   Linear address to be checked.
> >> > +**/
> >> > +STATIC
> >> > +BOOLEAN
> >> > +Do4LevelPagingModeCheck (
> >> > +  IN UINTN            Cr3,
> >> > +  IN UINT8            MaxPhyAddrBits,
> >> > +  IN UINTN            LinearAddress
> >> > +  )
> >> > +{
> >> > +  UINT64 PhysicalAddress;
> >> > +  UINT64 *Pml4TableEntry;
> >> > +  UINT64 *PageDirPtrTableEntry;
> >> > +  UINT64 *PageDirEntry;
> >> > +  UINT64 *PageTableEntry;
> >> > +
> >> > +  //
> >> > +  // In 4-level paging mode, linear addresses are 48 bits wide
> >> > +  //
> >> > +  if ((UINT64)LinearAddress > (1ULL << 48) - 1) {
> >> > +    return FALSE;
> >> > +  }
> >> > +
> >> > +  //
> >> > +  // Calculate physical address of PML4E
> >> > +  //
> >> > +  PhysicalAddress = (UINT64)Cr3 & (((1ULL << MaxPhyAddrBits) - 1) <<
> 12);
> >> > +  PhysicalAddress |= (((UINT64)LinearAddress >> 39) & 0x1FF) << 3;
> >> > +
> >> > +  ASSERT ((PhysicalAddress & (sizeof (*Pml4TableEntry) - 1)) == 0);
> >> > +
> >> > +  Pml4TableEntry = (UINT64 *)(UINTN)PhysicalAddress;
> >> > +
> >> > +  //
> >> > +  // Check if a PDPTE is present
> >> > +  //
> >> > +  if ((*Pml4TableEntry & BIT0) == 0) {
> >> > +    return FALSE;
> >> > +  }
> >> > +
> >> > +  //
> >> > +  // Calculate physical address of PDPTE
> >> > +  //
> >> > +  PhysicalAddress = *Pml4TableEntry & (((1ULL << MaxPhyAddrBits) - 1)
> <<
> >> > 12);
> >> > +  PhysicalAddress |= (((UINT64)LinearAddress >> 30) & 0x1FF) << 3;
> >> > +
> >> > +  ASSERT ((PhysicalAddress & (sizeof (*PageDirPtrTableEntry) - 1)) == 0);
> >> > +
> >> > +  PageDirPtrTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
> >> > +
> >> > +  //
> >> > +  // Check whether a PDPTE or 1GiB page entry is present
> >> > +  //
> >> > +  if ((*PageDirPtrTableEntry & BIT0) == 0) {
> >> > +    return FALSE;
> >> > +  }
> >> > +
> >> > +  //
> >> > +  // Check if PDPTE maps an 1GiB page
> >> > +  //
> >> > +  if ((*PageDirPtrTableEntry & BIT7) != 0) {
> >> > +    return TRUE;
> >> > +  }
> >> > +
> >> > +  //
> >> > +  // Calculate physical address of PDE
> >> > +  //
> >> > +  PhysicalAddress = *PageDirPtrTableEntry & (((1ULL << MaxPhyAddrBits)
> -
> >> 1)
> >> > <<
> >> > +                                             12);
> >> > +  PhysicalAddress |= (((UINT64)LinearAddress >> 21) & 0x1FF) << 3;
> >> > +
> >> > +  ASSERT ((PhysicalAddress & (sizeof (*PageDirEntry) - 1)) == 0);
> >> > +
> >> > +  PageDirEntry = (UINT64 *)(UINTN)PhysicalAddress;
> >> > +
> >> > +  //
> >> > +  // Check whether a PDE or a 2MiB page entry is present
> >> > +  //
> >> > +  if ((*PageDirEntry & BIT0) == 0) {
> >> > +    return FALSE;
> >> > +  }
> >> > +
> >> > +  //
> >> > +  // Check if PDE maps a 2MiB page
> >> > +  //
> >> > +  if ((*PageDirEntry & BIT7) != 0) {
> >> > +    return TRUE;
> >> > +  }
> >> > +
> >> > +  //
> >> > +  // Calculate physical address of PTE
> >> > +  //
> >> > +  PhysicalAddress = *PageDirEntry & (((1ULL << MaxPhyAddrBits) - 1) <<
> >> 12);
> >> > +  PhysicalAddress |= (((UINT64)LinearAddress >> 12) & 0x1FF) << 3;
> >> > +
> >> > +  ASSERT ((PhysicalAddress & (sizeof (*PageTableEntry) - 1)) == 0);
> >> > +
> >> > +  PageTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
> >> > +
> >> > +  //
> >> > +  // Check if PTE maps a 4KiB page
> >> > +  //
> >> > +  if ((*PageTableEntry & BIT0) == 0) {
> >> > +    return FALSE;
> >> > +  }
> >> > +
> >> > +  return TRUE;
> >> > +}
> >> > +
> >> > +/**
> >> > +  Check if a linear address is valid by walking the page tables in 32-bit
> paging
> >> > +  mode.
> >> > +
> >> > +  @param[in]  Cr3             CR3 control register.
> >> > +  @param[in]  Cr4             CR4 control register.
> >> > +  @param[in]  LinearAddress   Linear address to be checked.
> >> > +**/
> >> > +STATIC
> >> > +BOOLEAN
> >> > +Do32BitPagingModeCheck (
> >> > +  IN UINTN            Cr3,
> >> > +  IN UINTN            Cr4,
> >> > +  IN UINTN            LinearAddress
> >> > +  )
> >> > +{
> >> > +  UINT64 PhysicalAddress;
> >> > +  UINT32 *PageDirEntry;
> >> > +  UINT32 *PageTableEntry;
> >> > +
> >> > +  if (LinearAddress > MAX_UINT32) {
> >> > +    return FALSE;
> >> > +  }
> >> > +
> >> > +  //
> >> > +  // Calculate physical address of PDE
> >> > +  //
> >> > +  PhysicalAddress = (UINT32)Cr3 & (((1ULL << 20) - 1) << 12);
> >> > +  PhysicalAddress |= (((UINT32)LinearAddress >> 22) & 0x3FF) << 2;
> >> > +
> >> > +  ASSERT ((PhysicalAddress & (sizeof (*PageDirEntry) - 1)) == 0);
> >> > +
> >> > +  PageDirEntry = (UINT32 *)(UINTN)PhysicalAddress;
> >> > +
> >> > +  //
> >> > +  // Check whether a PTE or a 4MiB page is present
> >> > +  //
> >> > +  if ((*PageDirEntry & BIT0) == 0) {
> >> > +    return FALSE;
> >> > +  }
> >> > +
> >> > +  //
> >> > +  // Check if PDE maps a 4MiB page
> >> > +  //
> >> > +  if ((Cr4 & BIT4) != 0 && (*PageDirEntry & BIT7) != 0) {
> >> > +    return TRUE;
> >> > +  }
> >> > +
> >> > +  //
> >> > +  // Calculate physical address of PTE
> >> > +  //
> >> > +  PhysicalAddress = *PageDirEntry & (((1ULL << 20) - 1) << 12);
> >> > +  PhysicalAddress |= (((UINT32)LinearAddress >> 12) & 0x3FF) << 2;
> >> > +
> >> > +  ASSERT ((PhysicalAddress & (sizeof (*PageTableEntry) - 1)) == 0);
> >> > +
> >> > +  PageTableEntry = (UINT32 *)(UINTN)PhysicalAddress;
> >> > +
> >> > +  //
> >> > +  // Check if PTE maps a 4KiB page
> >> > +  //
> >> > +  if ((*PageTableEntry & BIT0) == 0) {
> >> > +    return FALSE;
> >> > +  }
> >> > +
> >> > +  return TRUE;
> >> > +}
> >> > +
> >> > +/**
> >> > +  Check if a linear address is valid by walking the page tables in PAE paging
> >> > +  mode.
> >> > +
> >> > +  @param[in]  Cr3             CR3 control register.
> >> > +  @param[in]  MaxPhyAddrBits  MAXPHYADDRBITS bits.
> >> > +  @param[in]  LinearAddress   Linear address to be checked.
> >> > +**/
> >> > +STATIC
> >> > +BOOLEAN
> >> > +DoPAEPagingModeCheck (
> >> > +  IN UINTN            Cr3,
> >> > +  IN UINT8            MaxPhyAddrBits,
> >> > +  IN UINTN            LinearAddress
> >> > +  )
> >> > +{
> >> > +  UINT64 PhysicalAddress;
> >> > +  UINT64 *PageDirPtrTableEntry;
> >> > +  UINT64 *PageDirEntry;
> >> > +  UINT64 *PageTableEntry;
> >> > +
> >> > +  if (LinearAddress > MAX_UINT32) {
> >> > +    return FALSE;
> >> > +  }
> >> > +
> >> > +  //
> >> > +  // Calculate physical address of PDPTE
> >> > +  //
> >> > +  PhysicalAddress = (UINT32)Cr3 >> 5;
> >> > +
> >> > +  //
> >> > +  // Select PDPTE register
> >> > +  //
> >> > +  PhysicalAddress +=
> >> > +    ((UINT32)LinearAddress >> 30) * sizeof (*PageDirPtrTableEntry);
> >> > +
> >> > +  PageDirPtrTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
> >> > +
> >> > +  //
> >> > +  // Check if PDE is present
> >> > +  //
> >> > +  if ((*PageDirPtrTableEntry & BIT0) == 0) {
> >> > +    return FALSE;
> >> > +  }
> >> > +
> >> > +  PhysicalAddress = *PageDirPtrTableEntry & (((1ULL << MaxPhyAddrBits)
> -
> >> 1)
> >> > <<
> >> > +                                             12);
> >> > +  PhysicalAddress |= ((LinearAddress >> 21) & 0x1FF) << 3;
> >> > +  ASSERT ((PhysicalAddress & (sizeof (*PageDirEntry) - 1)) == 0);
> >> > +
> >> > +  PageDirEntry = (UINT64 *)(UINTN)PhysicalAddress;
> >> > +
> >> > +  //
> >> > +  // Check whether a PTE or a 2MiB page is present
> >> > +  //
> >> > +  if ((*PageDirEntry & BIT0) == 0) {
> >> > +    return FALSE;
> >> > +  }
> >> > +
> >> > +  //
> >> > +  // Check if PDE maps a 2MiB page
> >> > +  //
> >> > +  if ((*PageDirEntry & BIT7) != 0) {
> >> > +    return TRUE;
> >> > +  }
> >> > +
> >> > +  //
> >> > +  // Calculate physical address of PTE
> >> > +  //
> >> > +  PhysicalAddress = *PageDirEntry & (((1ULL << MaxPhyAddrBits) - 1) <<
> >> 12);
> >> > +  PhysicalAddress |= ((LinearAddress >> 12) & 0x1FF) << 3;
> >> > +  ASSERT ((PhysicalAddress & (sizeof (*PageTableEntry) - 1)) == 0);
> >> > +
> >> > +  PageTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
> >> > +
> >> > +  //
> >> > +  // Check if PTE maps a 4KiB page
> >> > +  //
> >> > +  if ((*PageTableEntry & BIT0) == 0) {
> >> > +    return FALSE;
> >> > +  }
> >> > +
> >> > +  return TRUE;
> >> > +}
> >> > +
> >> > +/**
> >> > +  Check if a linear address is valid.
> >> > +
> >> > +  @param[in]  Cr0            CR0 control register.
> >> > +  @param[in]  Cr3            CR3 control register.
> >> > +  @param[in]  Cr4            CR4 control register.
> >> > +  @param[in]  LinearAddress  Linear address to be checked.
> >> > +**/
> >> > +BOOLEAN
> >> > +IsLinearAddressValid (
> >> > +  IN  UINTN              Cr0,
> >> > +  IN  UINTN              Cr3,
> >> > +  IN  UINTN              Cr4,
> >> > +  IN  UINTN              LinearAddress
> >> > +  )
> >> > +{
> >> > +  UINT32                  Eax;
> >> > +  UINT32                  Edx;
> >> > +  UINT8                   MaxPhyAddrBits;
> >> > +  MSR_IA32_EFER_REGISTER  Msr;
> >> > +  BOOLEAN                 AddressValid;
> >> > +
> >> > +  //
> >> > +  // Check for valid input parameters
> >> > +  //
> >> > +  if (Cr0 == 0 || Cr4 == 0 || LinearAddress == 0) {
> >> > +    return FALSE;
> >> > +  }
> >> > +
> >> > +  //
> >> > +  // Check if paging is disabled
> >> > +  //
> >> > +  if ((Cr0 & BIT31) == 0) {
> >> > +    //
> >> > +    // If CR4.PAE bit is set, then the linear (or physical) address supports
> >> > +    // only up to 36 bits.
> >> > +    //
> >> > +    if (((Cr4 & BIT5) != 0 && (UINT64)LinearAddress > 0xFFFFFFFFFULL)
> ||
> >> > +        LinearAddress > 0xFFFFFFFF) {
> >> > +      return FALSE;
> >> > +    }
> >> > +
> >> > +    return TRUE;
> >> > +  }
> >> > +
> >> > +  //
> >> > +  // Paging can be enabled only if CR0.PE bit is set
> >> > +  //
> >> > +  if ((Cr0 & BIT0) == 0) {
> >> > +    return FALSE;
> >> > +  }
> >> > +
> >> > +  //
> >> > +  // CR3 register cannot be zero if paging is enabled
> >> > +  //
> >> > +  if (Cr3 == 0) {
> >> > +    return FALSE;
> >> > +  }
> >> > +
> >> > +  //
> >> > +  // Get MAXPHYADDR bits
> >> > +  //
> >> > +  AsmCpuid (0x80000000, &Eax, NULL, NULL, NULL);
> >> > +  if (Eax >= 0x80000008) {
> >> > +    AsmCpuid (0x80000008, &Eax, NULL, NULL, NULL);
> >> > +    MaxPhyAddrBits = (UINT8)Eax;
> >> > +  } else {
> >> > +    AsmCpuid (1, NULL, NULL, NULL, &Edx);
> >> > +    if ((Edx & BIT6) != 0) {
> >> > +      MaxPhyAddrBits = 36;
> >> > +    } else {
> >> > +      MaxPhyAddrBits = 32;
> >> > +    }
> >> > +  }
> >> > +
> >> > +  ASSERT (MaxPhyAddrBits > 0);
> >> > +
> >> > +  AddressValid = FALSE;
> >> > +
> >> > +  //
> >> > +  // check if CR4.PAE bit is not set
> >> > +  //
> >> > +  if ((Cr4 & BIT5) == 0) {
> >> > +    //
> >> > +    // Check if linear address is valid in 32-bit paging mode
> >> > +    //
> >> > +    AddressValid = Do32BitPagingModeCheck (Cr3, Cr4, LinearAddress);
> >> > +  } else {
> >> > +    if (MaxPhyAddrBits > 52) {
> >> > +      return FALSE;
> >> > +    }
> >> > +
> >> > +    Msr.Uint64 = AsmReadMsr64 (MSR_IA32_EFER);
> >> > +
> >> > +    if (Msr.Bits.LME == 0) {
> >> > +      //
> >> > +      // Check if linear address is valid in PAE paging mode
> >> > +      //
> >> > +      AddressValid = DoPAEPagingModeCheck (Cr3, MaxPhyAddrBits,
> >> > LinearAddress);
> >> > +    } else {
> >> > +      //
> >> > +      // Check if linear address is valid in 4-level paging mode
> >> > +      //
> >> > +      AddressValid = Do4LevelPagingModeCheck (Cr3, MaxPhyAddrBits,
> >> > +                                              LinearAddress);
> >> > +    }
> >> > +  }
> >> > +
> >> > +  return AddressValid;
> >> > +}
> >> > diff --git
> >> > a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
> >> > b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
> >> > index ec46c2d9d3..1b51034c25 100644
> >> > --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
> >> > +++
> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
> >> > @@ -330,5 +330,21 @@ GetPdbFileName (
> >> >    OUT CHAR8    **PdbFileName
> >> >    );
> >> >
> >> > +/**
> >> > +  Check if a linear address is valid.
> >> > +
> >> > +  @param[in]  Cr0            CR0 control register.
> >> > +  @param[in]  Cr3            CR3 control register.
> >> > +  @param[in]  Cr4            CR4 control register.
> >> > +  @param[in]  LinearAddress  Linear address to be checked.
> >> > +**/
> >> > +BOOLEAN
> >> > +IsLinearAddressValid (
> >> > +  IN  UINTN              Cr0,
> >> > +  IN  UINTN              Cr3,
> >> > +  IN  UINTN              Cr4,
> >> > +  IN  UINTN              LinearAddress
> >> > +  );
> >> > +
> >> >  #endif
> >> >
> >> > --
> >> > 2.14.3
> >> >
> >> > _______________________________________________
> >> > edk2-devel mailing list
> >> > edk2-devel@lists.01.org
> >> > https://lists.01.org/mailman/listinfo/edk2-devel
> >> _______________________________________________
> >> edk2-devel mailing list
> >> edk2-devel@lists.01.org
> >> https://lists.01.org/mailman/listinfo/edk2-devel
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org
> https://lists.01.org/mailman/listinfo/edk2-devel


^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add helper to valid memory addresses
  2018-01-04 14:35             ` Yao, Jiewen
@ 2018-01-04 15:15               ` Paulo Alcantara
  0 siblings, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2018-01-04 15:15 UTC (permalink / raw)
  To: Yao, Jiewen, edk2-devel@lists.01.org; +Cc: Laszlo Ersek, Dong, Eric

"Yao, Jiewen" <jiewen.yao@intel.com> writes:

Hi Jiewen,

> For 7), you are right that we disable IA32 paging by default.
> However, we do support IA32 PAE in DxeIpl. Please refer to MdeModulePkg\Core\DxeIplPeim\Ia32\DxeLoadFunc.c
> ======================
>     PageTables = 0;
>     BuildPageTablesIa32Pae = (BOOLEAN) (PcdGetBool (PcdSetNxForStack) && IsIa32PaeSupport () && IsExecuteDisableBitAvailable ());
>     if (BuildPageTablesIa32Pae) {
>       PageTables = Create4GPageTablesIa32Pae (BaseOfStack, STACK_SIZE);
>       EnableExecuteDisableBit ();
>     }
> ======================
>
> Please notice that we only support IA32 PAE, we do not support IA32
> non-PAE mode so far. (no matter PSE is ON/OFF)

Cool! I didn't know about it.

>
> So, I suggest:
> 7.1) Please validate IA32 PAE mode. (You can enable PcdSetNxForStack)

I will. Thanks!

> 7.2) If we cannot validate the IA32 non-PAE code, please remove
> them. You can just print "IA32 non-PAE - UNSUPPORTED" and return
> invalid address. Then it can save our development time, review time,
> and validation time. The key is that we only want to check in the
> validated code.

You're right. I'll keep only the code that we were able to validate its
implementation (e.g. 4-level & 32-bit PAE).

Thanks!
Paulo

>> -----Original Message-----
>> From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of Paulo
>> Alcantara
>> Sent: Thursday, January 4, 2018 9:30 PM
>> To: Yao, Jiewen <jiewen.yao@intel.com>; Yao, Jiewen <jiewen.yao@intel.com>;
>> edk2-devel@lists.01.org
>> Cc: Laszlo Ersek <lersek@redhat.com>; Dong, Eric <eric.dong@intel.com>
>> Subject: Re: [edk2] [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add
>> helper to valid memory addresses
>> 
>> "Yao, Jiewen" <jiewen.yao@intel.com> writes:
>> 
>> > 5) For CR4, please use meaning definition for BIT4/BIT5.
>> >   if ((Cr4 & BIT4) != 0 && (*PageDirEntry & BIT7) != 0) {
>> >     if (((Cr4 & BIT5) != 0 && (UINT64)LinearAddress > 0xFFFFFFFFFULL) ||
>> 
>> OK.
>> 
>> >
>> > 6) For IA32 PAE/PSE calculation, same comment for 3 and 4.
>> 
>> OK.
>> 
>> >
>> > 7) Last but not least important, would you please share the information on
>> how do you validate the 32bit PAE/PSE/normal 4K page table?
>> 
>> Since on IA32 we use 32-bit protected flat model and paging disabled
>> (OK?), I wasn't able to validate the paging modes other than 4-level
>> paging mode in X64. The memory validation code I wrote is heavily based
>> upon what I read from Intel SDM Vol 3A manual.
>> 
>> If you do have any idea on how to validate it -- whether it's a PoC or
>> test code -- please let me know, and then I validate it.
>> 
>> Thanks again for your review!
>> 
>> Paulo
>> 
>> >
>> > Thank you
>> > Yao Jiewen
>> >
>> >> -----Original Message-----
>> >> From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of
>> Yao,
>> >> Jiewen
>> >> Sent: Thursday, January 4, 2018 9:36 AM
>> >> To: Paulo Alcantara <paulo@paulo.ac>; edk2-devel@lists.01.org
>> >> Cc: Laszlo Ersek <lersek@redhat.com>; Dong, Eric <eric.dong@intel.com>
>> >> Subject: Re: [edk2] [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add
>> >> helper to valid memory addresses
>> >>
>> >> Some suggestion:
>> >>
>> >> 1) I am not sure if it is proper to use ASSERT in an exception handler, because
>> we
>> >> know something is wrong.
>> >>
>> >>   ASSERT ((PhysicalAddress & (sizeof (*Pml4TableEntry) - 1)) == 0);
>> >>
>> >> I suggest we just do the check, and return FALSE, if the prerequisite is not
>> >> satisfied.
>> >>
>> >> 2) Can we use meaningful definition for BIT0, BIT7?
>> >>
>> >>   if ((*Pml4TableEntry & BIT0) == 0) {
>> >>   if ((*PageDirPtrTableEntry & BIT7) != 0) {
>> >>
>> >> 3) I am not sure if I understand below code.
>> >>
>> >>   PhysicalAddress = (UINT64)Cr3 & (((1ULL << MaxPhyAddrBits) - 1) << 12);
>> >>   PhysicalAddress = *Pml4TableEntry & (((1ULL << MaxPhyAddrBits) - 1) <<
>> 12);
>> >>   PhysicalAddress = *PageDirPtrTableEntry & (((1ULL << MaxPhyAddrBits) -
>> 1)
>> >> << 12);
>> >>   PhysicalAddress = *PageDirEntry & (((1ULL << MaxPhyAddrBits) - 1) <<
>> 12);
>> >>
>> >> If MaxPhyAddrBits is 48, you will get "Cr3 & 0x0FFFFFFFFFFFF000". Is that
>> what
>> >> you want? I think we need "Cr3 & 0x0000FFFFFFFFF000"
>> >> Should it be: PhysicalAddress = (UINT64)Cr3 & ((1ULL << MaxPhyAddrBits) - 1)
>> &
>> >> (~0xFFF);
>> >>
>> >> 4) Can we use a more readable way to below? Personally, I do not suggest
>> "<< 3",
>> >> which is just the index calculation.
>> >>
>> >>   PhysicalAddress = (UINT64)Cr3 & (((1ULL << MaxPhyAddrBits) - 1) << 12);
>> >>   PhysicalAddress |= (((UINT64)LinearAddress >> 39) & 0x1FF) << 3;
>> >>   Pml4TableEntry = (UINT64 *)(UINTN)PhysicalAddress;
>> >>   PhysicalAddress = *Pml4TableEntry & (((1ULL << MaxPhyAddrBits) - 1) <<
>> 12);
>> >>
>> >> For example:
>> >>   PhysicalAddress = (UINT64)Cr3 & ((1ULL << MaxPhyAddrBits) - 1) &
>> (~0xFFF);
>> >>   Pml4TableEntry = (UINT64 *)(UINTN)PhysicalAddress;
>> >>   Index= (UINTN)(((UINT64)LinearAddress >> 39) & 0x1FF);
>> >>   PhysicalAddress = Pml4TableEntry[Index] & ((1ULL << MaxPhyAddrBits) - 1)
>> &
>> >> (~0xFFF);
>> >>
>> >>
>> >>
>> >> Thank you
>> >> Yao Jiewen
>> >>
>> >>
>> >> > -----Original Message-----
>> >> > From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of
>> >> Paulo
>> >> > Alcantara
>> >> > Sent: Friday, December 29, 2017 12:40 PM
>> >> > To: edk2-devel@lists.01.org
>> >> > Cc: Laszlo Ersek <lersek@redhat.com>; Dong, Eric <eric.dong@intel.com>
>> >> > Subject: [edk2] [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add
>> helper
>> >> to
>> >> > valid memory addresses
>> >> >
>> >> > Introduce IsLinearAddressValid() function that will be used for
>> >> > validating memory addresses that would get dereferenced during stack
>> >> > traces in IA32 and X64 CPU exceptions.
>> >> >
>> >> > Contributed-under: TianoCore Contribution Agreement 1.1
>> >> > Cc: Eric Dong <eric.dong@intel.com>
>> >> > Cc: Laszlo Ersek <lersek@redhat.com>
>> >> > Requested-by: Brian Johnson <brian.johnson@hpe.com>
>> >> > Requested-by: Jiewen Yao <jiewen.yao@intel.com>
>> >> > Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
>> >> > ---
>> >> >  UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c |
>> 382
>> >> > ++++++++++++++++++++
>> >> >  UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h |
>> >> 16 +
>> >> >  2 files changed, 398 insertions(+)
>> >> >
>> >> > diff --git
>> >> > a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
>> >> > b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
>> >> > index 867c5c01d6..52b3eb1463 100644
>> >> > --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
>> >> > +++
>> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
>> >> > @@ -14,6 +14,9 @@
>> >> >
>> >> >  #include "CpuExceptionCommon.h"
>> >> >
>> >> > +#include <Register/Msr.h>
>> >> > +#include <Library/DebugLib.h>
>> >> > +
>> >> >  //
>> >> >  // Error code flag indicating whether or not an error code will be
>> >> >  // pushed on the stack if an exception occurs.
>> >> > @@ -194,3 +197,382 @@ GetPdbFileName (
>> >> >      }
>> >> >    }
>> >> >  }
>> >> > +
>> >> > +/**
>> >> > +  Check if a linear address is valid by walking the page tables in 4-level
>> >> > +  paging mode.
>> >> > +
>> >> > +  @param[in]  Cr3             CR3 control register.
>> >> > +  @param[in]  MaxPhyAddrBits  MAXPHYADDRBITS bits.
>> >> > +  @param[in]  LinearAddress   Linear address to be checked.
>> >> > +**/
>> >> > +STATIC
>> >> > +BOOLEAN
>> >> > +Do4LevelPagingModeCheck (
>> >> > +  IN UINTN            Cr3,
>> >> > +  IN UINT8            MaxPhyAddrBits,
>> >> > +  IN UINTN            LinearAddress
>> >> > +  )
>> >> > +{
>> >> > +  UINT64 PhysicalAddress;
>> >> > +  UINT64 *Pml4TableEntry;
>> >> > +  UINT64 *PageDirPtrTableEntry;
>> >> > +  UINT64 *PageDirEntry;
>> >> > +  UINT64 *PageTableEntry;
>> >> > +
>> >> > +  //
>> >> > +  // In 4-level paging mode, linear addresses are 48 bits wide
>> >> > +  //
>> >> > +  if ((UINT64)LinearAddress > (1ULL << 48) - 1) {
>> >> > +    return FALSE;
>> >> > +  }
>> >> > +
>> >> > +  //
>> >> > +  // Calculate physical address of PML4E
>> >> > +  //
>> >> > +  PhysicalAddress = (UINT64)Cr3 & (((1ULL << MaxPhyAddrBits) - 1) <<
>> 12);
>> >> > +  PhysicalAddress |= (((UINT64)LinearAddress >> 39) & 0x1FF) << 3;
>> >> > +
>> >> > +  ASSERT ((PhysicalAddress & (sizeof (*Pml4TableEntry) - 1)) == 0);
>> >> > +
>> >> > +  Pml4TableEntry = (UINT64 *)(UINTN)PhysicalAddress;
>> >> > +
>> >> > +  //
>> >> > +  // Check if a PDPTE is present
>> >> > +  //
>> >> > +  if ((*Pml4TableEntry & BIT0) == 0) {
>> >> > +    return FALSE;
>> >> > +  }
>> >> > +
>> >> > +  //
>> >> > +  // Calculate physical address of PDPTE
>> >> > +  //
>> >> > +  PhysicalAddress = *Pml4TableEntry & (((1ULL << MaxPhyAddrBits) - 1)
>> <<
>> >> > 12);
>> >> > +  PhysicalAddress |= (((UINT64)LinearAddress >> 30) & 0x1FF) << 3;
>> >> > +
>> >> > +  ASSERT ((PhysicalAddress & (sizeof (*PageDirPtrTableEntry) - 1)) == 0);
>> >> > +
>> >> > +  PageDirPtrTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
>> >> > +
>> >> > +  //
>> >> > +  // Check whether a PDPTE or 1GiB page entry is present
>> >> > +  //
>> >> > +  if ((*PageDirPtrTableEntry & BIT0) == 0) {
>> >> > +    return FALSE;
>> >> > +  }
>> >> > +
>> >> > +  //
>> >> > +  // Check if PDPTE maps an 1GiB page
>> >> > +  //
>> >> > +  if ((*PageDirPtrTableEntry & BIT7) != 0) {
>> >> > +    return TRUE;
>> >> > +  }
>> >> > +
>> >> > +  //
>> >> > +  // Calculate physical address of PDE
>> >> > +  //
>> >> > +  PhysicalAddress = *PageDirPtrTableEntry & (((1ULL << MaxPhyAddrBits)
>> -
>> >> 1)
>> >> > <<
>> >> > +                                             12);
>> >> > +  PhysicalAddress |= (((UINT64)LinearAddress >> 21) & 0x1FF) << 3;
>> >> > +
>> >> > +  ASSERT ((PhysicalAddress & (sizeof (*PageDirEntry) - 1)) == 0);
>> >> > +
>> >> > +  PageDirEntry = (UINT64 *)(UINTN)PhysicalAddress;
>> >> > +
>> >> > +  //
>> >> > +  // Check whether a PDE or a 2MiB page entry is present
>> >> > +  //
>> >> > +  if ((*PageDirEntry & BIT0) == 0) {
>> >> > +    return FALSE;
>> >> > +  }
>> >> > +
>> >> > +  //
>> >> > +  // Check if PDE maps a 2MiB page
>> >> > +  //
>> >> > +  if ((*PageDirEntry & BIT7) != 0) {
>> >> > +    return TRUE;
>> >> > +  }
>> >> > +
>> >> > +  //
>> >> > +  // Calculate physical address of PTE
>> >> > +  //
>> >> > +  PhysicalAddress = *PageDirEntry & (((1ULL << MaxPhyAddrBits) - 1) <<
>> >> 12);
>> >> > +  PhysicalAddress |= (((UINT64)LinearAddress >> 12) & 0x1FF) << 3;
>> >> > +
>> >> > +  ASSERT ((PhysicalAddress & (sizeof (*PageTableEntry) - 1)) == 0);
>> >> > +
>> >> > +  PageTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
>> >> > +
>> >> > +  //
>> >> > +  // Check if PTE maps a 4KiB page
>> >> > +  //
>> >> > +  if ((*PageTableEntry & BIT0) == 0) {
>> >> > +    return FALSE;
>> >> > +  }
>> >> > +
>> >> > +  return TRUE;
>> >> > +}
>> >> > +
>> >> > +/**
>> >> > +  Check if a linear address is valid by walking the page tables in 32-bit
>> paging
>> >> > +  mode.
>> >> > +
>> >> > +  @param[in]  Cr3             CR3 control register.
>> >> > +  @param[in]  Cr4             CR4 control register.
>> >> > +  @param[in]  LinearAddress   Linear address to be checked.
>> >> > +**/
>> >> > +STATIC
>> >> > +BOOLEAN
>> >> > +Do32BitPagingModeCheck (
>> >> > +  IN UINTN            Cr3,
>> >> > +  IN UINTN            Cr4,
>> >> > +  IN UINTN            LinearAddress
>> >> > +  )
>> >> > +{
>> >> > +  UINT64 PhysicalAddress;
>> >> > +  UINT32 *PageDirEntry;
>> >> > +  UINT32 *PageTableEntry;
>> >> > +
>> >> > +  if (LinearAddress > MAX_UINT32) {
>> >> > +    return FALSE;
>> >> > +  }
>> >> > +
>> >> > +  //
>> >> > +  // Calculate physical address of PDE
>> >> > +  //
>> >> > +  PhysicalAddress = (UINT32)Cr3 & (((1ULL << 20) - 1) << 12);
>> >> > +  PhysicalAddress |= (((UINT32)LinearAddress >> 22) & 0x3FF) << 2;
>> >> > +
>> >> > +  ASSERT ((PhysicalAddress & (sizeof (*PageDirEntry) - 1)) == 0);
>> >> > +
>> >> > +  PageDirEntry = (UINT32 *)(UINTN)PhysicalAddress;
>> >> > +
>> >> > +  //
>> >> > +  // Check whether a PTE or a 4MiB page is present
>> >> > +  //
>> >> > +  if ((*PageDirEntry & BIT0) == 0) {
>> >> > +    return FALSE;
>> >> > +  }
>> >> > +
>> >> > +  //
>> >> > +  // Check if PDE maps a 4MiB page
>> >> > +  //
>> >> > +  if ((Cr4 & BIT4) != 0 && (*PageDirEntry & BIT7) != 0) {
>> >> > +    return TRUE;
>> >> > +  }
>> >> > +
>> >> > +  //
>> >> > +  // Calculate physical address of PTE
>> >> > +  //
>> >> > +  PhysicalAddress = *PageDirEntry & (((1ULL << 20) - 1) << 12);
>> >> > +  PhysicalAddress |= (((UINT32)LinearAddress >> 12) & 0x3FF) << 2;
>> >> > +
>> >> > +  ASSERT ((PhysicalAddress & (sizeof (*PageTableEntry) - 1)) == 0);
>> >> > +
>> >> > +  PageTableEntry = (UINT32 *)(UINTN)PhysicalAddress;
>> >> > +
>> >> > +  //
>> >> > +  // Check if PTE maps a 4KiB page
>> >> > +  //
>> >> > +  if ((*PageTableEntry & BIT0) == 0) {
>> >> > +    return FALSE;
>> >> > +  }
>> >> > +
>> >> > +  return TRUE;
>> >> > +}
>> >> > +
>> >> > +/**
>> >> > +  Check if a linear address is valid by walking the page tables in PAE paging
>> >> > +  mode.
>> >> > +
>> >> > +  @param[in]  Cr3             CR3 control register.
>> >> > +  @param[in]  MaxPhyAddrBits  MAXPHYADDRBITS bits.
>> >> > +  @param[in]  LinearAddress   Linear address to be checked.
>> >> > +**/
>> >> > +STATIC
>> >> > +BOOLEAN
>> >> > +DoPAEPagingModeCheck (
>> >> > +  IN UINTN            Cr3,
>> >> > +  IN UINT8            MaxPhyAddrBits,
>> >> > +  IN UINTN            LinearAddress
>> >> > +  )
>> >> > +{
>> >> > +  UINT64 PhysicalAddress;
>> >> > +  UINT64 *PageDirPtrTableEntry;
>> >> > +  UINT64 *PageDirEntry;
>> >> > +  UINT64 *PageTableEntry;
>> >> > +
>> >> > +  if (LinearAddress > MAX_UINT32) {
>> >> > +    return FALSE;
>> >> > +  }
>> >> > +
>> >> > +  //
>> >> > +  // Calculate physical address of PDPTE
>> >> > +  //
>> >> > +  PhysicalAddress = (UINT32)Cr3 >> 5;
>> >> > +
>> >> > +  //
>> >> > +  // Select PDPTE register
>> >> > +  //
>> >> > +  PhysicalAddress +=
>> >> > +    ((UINT32)LinearAddress >> 30) * sizeof (*PageDirPtrTableEntry);
>> >> > +
>> >> > +  PageDirPtrTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
>> >> > +
>> >> > +  //
>> >> > +  // Check if PDE is present
>> >> > +  //
>> >> > +  if ((*PageDirPtrTableEntry & BIT0) == 0) {
>> >> > +    return FALSE;
>> >> > +  }
>> >> > +
>> >> > +  PhysicalAddress = *PageDirPtrTableEntry & (((1ULL << MaxPhyAddrBits)
>> -
>> >> 1)
>> >> > <<
>> >> > +                                             12);
>> >> > +  PhysicalAddress |= ((LinearAddress >> 21) & 0x1FF) << 3;
>> >> > +  ASSERT ((PhysicalAddress & (sizeof (*PageDirEntry) - 1)) == 0);
>> >> > +
>> >> > +  PageDirEntry = (UINT64 *)(UINTN)PhysicalAddress;
>> >> > +
>> >> > +  //
>> >> > +  // Check whether a PTE or a 2MiB page is present
>> >> > +  //
>> >> > +  if ((*PageDirEntry & BIT0) == 0) {
>> >> > +    return FALSE;
>> >> > +  }
>> >> > +
>> >> > +  //
>> >> > +  // Check if PDE maps a 2MiB page
>> >> > +  //
>> >> > +  if ((*PageDirEntry & BIT7) != 0) {
>> >> > +    return TRUE;
>> >> > +  }
>> >> > +
>> >> > +  //
>> >> > +  // Calculate physical address of PTE
>> >> > +  //
>> >> > +  PhysicalAddress = *PageDirEntry & (((1ULL << MaxPhyAddrBits) - 1) <<
>> >> 12);
>> >> > +  PhysicalAddress |= ((LinearAddress >> 12) & 0x1FF) << 3;
>> >> > +  ASSERT ((PhysicalAddress & (sizeof (*PageTableEntry) - 1)) == 0);
>> >> > +
>> >> > +  PageTableEntry = (UINT64 *)(UINTN)PhysicalAddress;
>> >> > +
>> >> > +  //
>> >> > +  // Check if PTE maps a 4KiB page
>> >> > +  //
>> >> > +  if ((*PageTableEntry & BIT0) == 0) {
>> >> > +    return FALSE;
>> >> > +  }
>> >> > +
>> >> > +  return TRUE;
>> >> > +}
>> >> > +
>> >> > +/**
>> >> > +  Check if a linear address is valid.
>> >> > +
>> >> > +  @param[in]  Cr0            CR0 control register.
>> >> > +  @param[in]  Cr3            CR3 control register.
>> >> > +  @param[in]  Cr4            CR4 control register.
>> >> > +  @param[in]  LinearAddress  Linear address to be checked.
>> >> > +**/
>> >> > +BOOLEAN
>> >> > +IsLinearAddressValid (
>> >> > +  IN  UINTN              Cr0,
>> >> > +  IN  UINTN              Cr3,
>> >> > +  IN  UINTN              Cr4,
>> >> > +  IN  UINTN              LinearAddress
>> >> > +  )
>> >> > +{
>> >> > +  UINT32                  Eax;
>> >> > +  UINT32                  Edx;
>> >> > +  UINT8                   MaxPhyAddrBits;
>> >> > +  MSR_IA32_EFER_REGISTER  Msr;
>> >> > +  BOOLEAN                 AddressValid;
>> >> > +
>> >> > +  //
>> >> > +  // Check for valid input parameters
>> >> > +  //
>> >> > +  if (Cr0 == 0 || Cr4 == 0 || LinearAddress == 0) {
>> >> > +    return FALSE;
>> >> > +  }
>> >> > +
>> >> > +  //
>> >> > +  // Check if paging is disabled
>> >> > +  //
>> >> > +  if ((Cr0 & BIT31) == 0) {
>> >> > +    //
>> >> > +    // If CR4.PAE bit is set, then the linear (or physical) address supports
>> >> > +    // only up to 36 bits.
>> >> > +    //
>> >> > +    if (((Cr4 & BIT5) != 0 && (UINT64)LinearAddress > 0xFFFFFFFFFULL)
>> ||
>> >> > +        LinearAddress > 0xFFFFFFFF) {
>> >> > +      return FALSE;
>> >> > +    }
>> >> > +
>> >> > +    return TRUE;
>> >> > +  }
>> >> > +
>> >> > +  //
>> >> > +  // Paging can be enabled only if CR0.PE bit is set
>> >> > +  //
>> >> > +  if ((Cr0 & BIT0) == 0) {
>> >> > +    return FALSE;
>> >> > +  }
>> >> > +
>> >> > +  //
>> >> > +  // CR3 register cannot be zero if paging is enabled
>> >> > +  //
>> >> > +  if (Cr3 == 0) {
>> >> > +    return FALSE;
>> >> > +  }
>> >> > +
>> >> > +  //
>> >> > +  // Get MAXPHYADDR bits
>> >> > +  //
>> >> > +  AsmCpuid (0x80000000, &Eax, NULL, NULL, NULL);
>> >> > +  if (Eax >= 0x80000008) {
>> >> > +    AsmCpuid (0x80000008, &Eax, NULL, NULL, NULL);
>> >> > +    MaxPhyAddrBits = (UINT8)Eax;
>> >> > +  } else {
>> >> > +    AsmCpuid (1, NULL, NULL, NULL, &Edx);
>> >> > +    if ((Edx & BIT6) != 0) {
>> >> > +      MaxPhyAddrBits = 36;
>> >> > +    } else {
>> >> > +      MaxPhyAddrBits = 32;
>> >> > +    }
>> >> > +  }
>> >> > +
>> >> > +  ASSERT (MaxPhyAddrBits > 0);
>> >> > +
>> >> > +  AddressValid = FALSE;
>> >> > +
>> >> > +  //
>> >> > +  // check if CR4.PAE bit is not set
>> >> > +  //
>> >> > +  if ((Cr4 & BIT5) == 0) {
>> >> > +    //
>> >> > +    // Check if linear address is valid in 32-bit paging mode
>> >> > +    //
>> >> > +    AddressValid = Do32BitPagingModeCheck (Cr3, Cr4, LinearAddress);
>> >> > +  } else {
>> >> > +    if (MaxPhyAddrBits > 52) {
>> >> > +      return FALSE;
>> >> > +    }
>> >> > +
>> >> > +    Msr.Uint64 = AsmReadMsr64 (MSR_IA32_EFER);
>> >> > +
>> >> > +    if (Msr.Bits.LME == 0) {
>> >> > +      //
>> >> > +      // Check if linear address is valid in PAE paging mode
>> >> > +      //
>> >> > +      AddressValid = DoPAEPagingModeCheck (Cr3, MaxPhyAddrBits,
>> >> > LinearAddress);
>> >> > +    } else {
>> >> > +      //
>> >> > +      // Check if linear address is valid in 4-level paging mode
>> >> > +      //
>> >> > +      AddressValid = Do4LevelPagingModeCheck (Cr3, MaxPhyAddrBits,
>> >> > +                                              LinearAddress);
>> >> > +    }
>> >> > +  }
>> >> > +
>> >> > +  return AddressValid;
>> >> > +}
>> >> > diff --git
>> >> > a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
>> >> > b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
>> >> > index ec46c2d9d3..1b51034c25 100644
>> >> > --- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
>> >> > +++
>> b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
>> >> > @@ -330,5 +330,21 @@ GetPdbFileName (
>> >> >    OUT CHAR8    **PdbFileName
>> >> >    );
>> >> >
>> >> > +/**
>> >> > +  Check if a linear address is valid.
>> >> > +
>> >> > +  @param[in]  Cr0            CR0 control register.
>> >> > +  @param[in]  Cr3            CR3 control register.
>> >> > +  @param[in]  Cr4            CR4 control register.
>> >> > +  @param[in]  LinearAddress  Linear address to be checked.
>> >> > +**/
>> >> > +BOOLEAN
>> >> > +IsLinearAddressValid (
>> >> > +  IN  UINTN              Cr0,
>> >> > +  IN  UINTN              Cr3,
>> >> > +  IN  UINTN              Cr4,
>> >> > +  IN  UINTN              LinearAddress
>> >> > +  );
>> >> > +
>> >> >  #endif
>> >> >
>> >> > --
>> >> > 2.14.3
>> >> >
>> >> > _______________________________________________
>> >> > edk2-devel mailing list
>> >> > edk2-devel@lists.01.org
>> >> > https://lists.01.org/mailman/listinfo/edk2-devel
>> >> _______________________________________________
>> >> edk2-devel mailing list
>> >> edk2-devel@lists.01.org
>> >> https://lists.01.org/mailman/listinfo/edk2-devel
>> _______________________________________________
>> edk2-devel mailing list
>> edk2-devel@lists.01.org
>> https://lists.01.org/mailman/listinfo/edk2-devel


^ permalink raw reply	[flat|nested] 82+ messages in thread

* [RFC v5 0/8] Stack trace support in X64 exception handling
  2017-12-29  3:48   ` [RFC v4 0/6] Stack trace support in X64 exception handling Paulo Alcantara
                       ` (6 preceding siblings ...)
  2018-01-04  0:59     ` [RFC v4 0/6] Stack trace support in X64 exception handling Yao, Jiewen
@ 2018-01-15  0:23     ` Paulo Alcantara
  2018-01-15  0:23       ` [RFC v5 1/8] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support Paulo Alcantara
                         ` (8 more replies)
  7 siblings, 9 replies; 82+ messages in thread
From: Paulo Alcantara @ 2018-01-15  0:23 UTC (permalink / raw)
  To: edk2-devel
  Cc: Paulo Alcantara, Rick Bramley, Kimon Berlin, Diego Medaglia,
	Andrew Fish, Eric Dong, Laszlo Ersek, Brian Johnson, Jeff Fan,
	Jiewen Yao, Paulo Alcantara

Hi,

This series adds stack trace support during IA32 and X64 CPU exceptions.

Informations like back trace, stack contents and image module names
(that were part of the call stack) will be dumped out.

The current limitation is that it relies on available frame pointers
(GCC only) in order to successfully unwind the stack.

Jiewen,

Thank you very much for your time on this. I've applied the changes you
suggested, as well as tested it on IA32 PAE paging mode - it worked as
expected.

Other than that, I also tested the stack trace in SMM code by manually
calling CpuBreakPoint() and then it broke with another exception
(page fault). I didn't have much time to look into that, but what I've
observed is that the page fault ocurred during the search of PE/COFF
image base address (in PeCoffSearchImageBase). The function attempts to
search for the image base from "Address" through 0, so any of those
dereferenced addresses triggers the page fault.

Do you know how we could fix that issue? Perhaps introducing a
AddressValidationLib (as Brian suggested previously) and use it within
PeCoffSearchImageBase()?

I'd also like to thank Brian & Jeff for all the support!

Thanks
Paulo

Repo:   https://github.com/pcacjr/edk2.git
Branch: stacktrace_v5

Cc: Rick Bramley <richard.bramley@hp.com>
Cc: Kimon Berlin <kimon.berlin@hp.com>
Cc: Diego Medaglia <diego.meaglia@hp.com>
Cc: Andrew Fish <afish@apple.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Brian Johnson <brian.johnson@hpe.com>
Cc: Jeff Fan <vanjeff_919@hotmail.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Paulo Alcantara <paulo@hp.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
---

v1 -> v2:
  * Add IA32 arch support (GCC toolchain only)
  * Replace hard-coded stack alignment value (16) with
    CPU_STACK_ALIGNMENT.
  * Check for proper stack and frame pointer alignments.
  * Fix initialization of UnwoundStacksCount to 1.
  * Move GetPdbFileName() to common code since it will be used by both
    IA32 and X64 implementations.

v2 -> v3:
  * Fixed wrong assumption about "RIP < ImageBase" to start searching
    for another PE/COFF image. That is, RIP may point to lower and
    higher addresses for any other PE/COFF images. Both IA32 & X64.
    (Thanks Andrew & Jiewen)
  * Fixed typo: unwond -> unwound. Both IA32 & X64. (Thanks Brian)

v3 -> v4:
  * Validate all frame/stack pointer addresses before dereferencing them
    as requested by Brian & Jiewen.
  * Correctly print out IP addresses during the stack traces (by Jeff)

v4 -> v5:
  * Fixed address calculations and improved code as suggested by Jiewen.
  * Fixed parameter validation as suggested by Brian.
  * Tested stack stack with IA32 PAE paging mode.

Paulo Alcantara (8):
  UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
  UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName()
  UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support
  UefiCpuPkg/CpuExceptionHandlerLib: Add helper to validate memory
    addresses
  UefiCpuPkg/CpuExceptionHandlerLib: Ensure valid frame/stack pointers
  UefiCpuPkg/CpuExceptionHandlerLib: Correctly print IP addresses
  UefiCpuPkg/CpuExceptionHandlerLib: Validate memory address ranges
  UefiCpuPkg/CpuExceptionHandlerLib: Add early check in
    DumpStackContents

 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c        | 537 ++++++++++++++++++--
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h        |  59 ++-
 UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c | 483 +++++++++++++++++-
 UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c  | 426 +++++++++++++++-
 4 files changed, 1435 insertions(+), 70 deletions(-)

-- 
2.14.3



^ permalink raw reply	[flat|nested] 82+ messages in thread

* [RFC v5 1/8] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
  2018-01-15  0:23     ` [RFC v5 0/8] " Paulo Alcantara
@ 2018-01-15  0:23       ` Paulo Alcantara
  2018-01-15  0:23       ` [RFC v5 2/8] UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName() Paulo Alcantara
                         ` (7 subsequent siblings)
  8 siblings, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2018-01-15  0:23 UTC (permalink / raw)
  To: edk2-devel; +Cc: Paulo Alcantara, Eric Dong, Laszlo Ersek

This patch adds stack trace support during a X64 CPU exception.

It will dump out back trace, stack contents as well as image module
names that were part of the call stack.

Contributed-under: TianoCore Contribution Agreement 1.1
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 401 +++++++++++++++++++-
 1 file changed, 393 insertions(+), 8 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
index 56180f4c17..4db9f6465e 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
@@ -14,6 +14,11 @@
 
 #include "CpuExceptionCommon.h"
 
+//
+// Unknown PDB file name
+//
+GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
+
 /**
   Return address map of exception handler template so that C code can generate
   exception tables.
@@ -399,20 +404,281 @@ DumpCpuContext (
 }
 
 /**
-  Display CPU information.
+  Get absolute path and file name of PDB file in PE/COFF image.
 
-  @param ExceptionType  Exception type.
-  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+  @param[in]  ImageBase            Base address of PE/COFF image.
+  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
+  @param[out] PdbFileName          File name of PDB file.
 **/
+STATIC
 VOID
-DumpImageAndCpuContent (
+GetPdbFileName (
+  IN  UINTN    ImageBase,
+  OUT CHAR8    **PdbAbsoluteFilePath,
+  OUT CHAR8    **PdbFileName
+  )
+{
+  VOID   *PdbPointer;
+  CHAR8  *Str;
+
+  //
+  // Get PDB file name from PE/COFF image
+  //
+  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
+  if (PdbPointer == NULL) {
+    //
+    // No PDB file name found. Set it to an unknown file name.
+    //
+    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
+    if (PdbAbsoluteFilePath != NULL) {
+      *PdbAbsoluteFilePath = NULL;
+    }
+  } else {
+    //
+    // Get file name portion out of PDB file in PE/COFF image
+    //
+    Str = (CHAR8 *)((UINTN)PdbPointer +
+                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
+    for (; *Str != '/' && *Str != '\\'; Str--) {
+      ;
+    }
+
+    //
+    // Set PDB file name (also skip trailing path separator: '/' or '\\')
+    //
+    *PdbFileName = Str + 1;
+
+    if (PdbAbsoluteFilePath != NULL) {
+      //
+      // Set absolute file path of PDB file
+      //
+      *PdbAbsoluteFilePath = PdbPointer;
+    }
+  }
+}
+
+/**
+  Dump stack contents.
+
+  @param[in]  CurrentRsp         Current stack pointer address.
+  @param[in]  UnwoundStacksCount  Count of unwound stack frames.
+**/
+STATIC
+VOID
+DumpStackContents (
+  IN UINT64  CurrentRsp,
+  IN INTN    UnwoundStacksCount
+  )
+{
+  //
+  // Check for proper stack pointer alignment
+  //
+  if (((UINTN)CurrentRsp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
+    InternalPrintMessage ("!!!! Unaligned stack pointer. !!!!\n");
+    return;
+  }
+
+  //
+  // Dump out stack contents
+  //
+  InternalPrintMessage ("\nStack dump:\n");
+  while (UnwoundStacksCount-- > 0) {
+    InternalPrintMessage (
+      "0x%016lx: %016lx %016lx\n",
+      CurrentRsp,
+      *(UINT64 *)CurrentRsp,
+      *(UINT64 *)((UINTN)CurrentRsp + 8)
+      );
+
+    //
+    // Point to next stack
+    //
+    CurrentRsp += CPU_STACK_ALIGNMENT;
+  }
+}
+
+/**
+  Dump all image module names from call stack.
+
+  @param[in]  ExceptionType  Exception type.
+  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+**/
+STATIC
+VOID
+DumpImageModuleNames (
   IN EFI_EXCEPTION_TYPE   ExceptionType,
   IN EFI_SYSTEM_CONTEXT   SystemContext
   )
 {
-  DumpCpuContext (ExceptionType, SystemContext);
+  EFI_STATUS  Status;
+  UINT64      Rip;
+  UINTN       ImageBase;
+  VOID        *EntryPoint;
+  CHAR8       *PdbAbsoluteFilePath;
+  CHAR8       *PdbFileName;
+  UINT64      Rbp;
+  UINTN       LastImageBase;
+
+  //
+  // Set current RIP address
+  //
+  if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) &&
+      ((SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_ID) != 0)) {
+    //
+    // The RIP in SystemContext could not be used
+    // if it is page fault with I/D set.
+    //
+    Rip = *(UINT64 *)(UINTN)SystemContext.SystemContextX64->Rsp;
+  } else {
+    Rip = SystemContext.SystemContextX64->Rip;
+  }
+
+  //
+  // Set current frame pointer address
+  //
+  Rbp = SystemContext.SystemContextX64->Rbp;
+
+  //
+  // Check for proper frame pointer alignment
+  //
+  if (((UINTN)Rbp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
+    InternalPrintMessage ("!!!! Unaligned frame pointer. !!!!\n");
+    return;
+  }
+
+  //
+  // Get initial PE/COFF image base address from current RIP
+  //
+  ImageBase = PeCoffSearchImageBase (Rip);
+  if (ImageBase == 0) {
+    InternalPrintMessage ("!!!! Could not find image module names. !!!!");
+    return;
+  }
+
+  //
+  // Set last PE/COFF image base address
+  //
+  LastImageBase = ImageBase;
+
+  //
+  // Get initial PE/COFF image's entry point
+  //
+  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
+  if (EFI_ERROR (Status)) {
+    EntryPoint = NULL;
+  }
+
+  //
+  // Get file name and absolute path of initial PDB file
+  //
+  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
+
+  //
+  // Print out initial image module name (if any)
+  //
+  if (PdbAbsoluteFilePath != NULL) {
+    InternalPrintMessage (
+      "\n%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
+      PdbFileName,
+      ImageBase,
+      (UINTN)EntryPoint
+      );
+    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
+  }
+
+  //
+  // Walk through call stack and find next module names
+  //
+  for (;;) {
+    //
+    // Set RIP with return address from current stack frame
+    //
+    Rip = *(UINT64 *)((UINTN)Rbp + 8);
+
+    //
+    // If RIP is zero, then stop unwinding the stack
+    //
+    if (Rip == 0) {
+      break;
+    }
+
+    //
+    // Search for the respective PE/COFF image based on RIP
+    //
+    ImageBase = PeCoffSearchImageBase (Rip);
+    if (ImageBase == 0) {
+      //
+      // Stop stack trace
+      //
+      break;
+    }
+
+    //
+    // If RIP points to another PE/COFF image, then find its respective PDB file
+    // name.
+    //
+    if (LastImageBase != ImageBase) {
+      //
+      // Get PE/COFF image's entry point
+      //
+      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
+      if (EFI_ERROR (Status)) {
+        EntryPoint = NULL;
+      }
+
+      //
+      // Get file name and absolute path of PDB file
+      //
+      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
+
+      //
+      // Print out image module name (if any)
+      //
+      if (PdbAbsoluteFilePath != NULL) {
+        InternalPrintMessage (
+          "%a (ImageBase=0x%016lx, EntryPoint=0x%016lx):\n",
+          PdbFileName,
+          ImageBase,
+          (UINTN)EntryPoint
+          );
+        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
+      }
+
+      //
+      // Save last PE/COFF image base address
+      //
+      LastImageBase = ImageBase;
+    }
+
+    //
+    // Unwind the stack
+    //
+    Rbp = *(UINT64 *)(UINTN)Rbp;
+  }
+}
+
+/**
+  Dump stack trace.
+
+  @param[in]  ExceptionType      Exception type.
+  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
+  @param[out] UnwoundStacksCount Count of unwound stack frames.
+**/
+STATIC
+VOID
+DumpStacktrace (
+  IN  EFI_EXCEPTION_TYPE   ExceptionType,
+  IN  EFI_SYSTEM_CONTEXT   SystemContext,
+  OUT INTN                 *UnwoundStacksCount
+  )
+{
+  UINT64  Rip;
+  UINT64  Rbp;
+  UINTN   ImageBase;
+  CHAR8   *PdbFileName;
+
   //
-  // Dump module image base and module entry point by RIP
+  // Set current RIP address
   //
   if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) &&
       ((SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_ID) != 0)) {
@@ -420,8 +686,127 @@ DumpImageAndCpuContent (
     // The RIP in SystemContext could not be used
     // if it is page fault with I/D set.
     //
-    DumpModuleImageInfo ((*(UINTN *)(UINTN)SystemContext.SystemContextX64->Rsp));
+    Rip = *(UINT64 *)(UINTN)SystemContext.SystemContextX64->Rsp;
   } else {
-    DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
+    Rip = SystemContext.SystemContextX64->Rip;
+  }
+
+  //
+  // Set current frame pointer address
+  //
+  Rbp = SystemContext.SystemContextX64->Rbp;
+
+  //
+  // Get initial PE/COFF image base address from current RIP
+  //
+  ImageBase = PeCoffSearchImageBase (Rip);
+  if (ImageBase == 0) {
+    InternalPrintMessage ("!!!! Could not find backtrace information. !!!!");
+    return;
+  }
+
+  //
+  // Get PDB file name from initial PE/COFF image
+  //
+  GetPdbFileName (ImageBase, NULL, &PdbFileName);
+
+  //
+  // Initialize count of unwound stacks
+  //
+  *UnwoundStacksCount = 1;
+
+  //
+  // Print out back trace
+  //
+  InternalPrintMessage ("\nCall trace:\n");
+
+  for (;;) {
+    //
+    // Print stack frame in the following format:
+    //
+    // # <RIP> @ <ImageBase>+<RelOffset> (RBP) in [<ModuleName> | ????]
+    //
+    InternalPrintMessage (
+      "%d 0x%016lx @ 0x%016lx+0x%x (0x%016lx) in %a\n",
+      *UnwoundStacksCount - 1,
+      Rip,
+      ImageBase,
+      Rip - ImageBase - 1,
+      Rbp,
+      PdbFileName
+      );
+
+    //
+    // Set RIP with return address from current stack frame
+    //
+    Rip = *(UINT64 *)((UINTN)Rbp + 8);
+
+    //
+    // If RIP is zero, then stop unwinding the stack
+    //
+    if (Rip == 0) {
+      break;
+    }
+
+    //
+    // Search for the respective PE/COFF image based on RIP
+    //
+    ImageBase = PeCoffSearchImageBase (Rip);
+    if (ImageBase == 0) {
+      //
+      // Stop stack trace
+      //
+      break;
+    }
+
+    //
+    // Get PDB file name
+    //
+    GetPdbFileName (ImageBase, NULL, &PdbFileName);
+
+    //
+    // Unwind the stack
+    //
+    Rbp = *(UINT64 *)(UINTN)Rbp;
+
+    //
+    // Increment count of unwound stacks
+    //
+    (*UnwoundStacksCount)++;
   }
 }
+
+/**
+  Display CPU information.
+
+  @param ExceptionType  Exception type.
+  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+**/
+VOID
+DumpImageAndCpuContent (
+  IN EFI_EXCEPTION_TYPE   ExceptionType,
+  IN EFI_SYSTEM_CONTEXT   SystemContext
+  )
+{
+  INTN UnwoundStacksCount;
+
+  //
+  // Dump CPU context
+  //
+  DumpCpuContext (ExceptionType, SystemContext);
+
+  //
+  // Dump stack trace
+  //
+  DumpStacktrace (ExceptionType, SystemContext, &UnwoundStacksCount);
+
+  //
+  // Dump image module names
+  //
+  DumpImageModuleNames (ExceptionType, SystemContext);
+
+  //
+  // Dump stack contents
+  //
+  DumpStackContents (SystemContext.SystemContextX64->Rsp, UnwoundStacksCount);
+}
-- 
2.14.3



^ permalink raw reply related	[flat|nested] 82+ messages in thread

* [RFC v5 2/8] UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName()
  2018-01-15  0:23     ` [RFC v5 0/8] " Paulo Alcantara
  2018-01-15  0:23       ` [RFC v5 1/8] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support Paulo Alcantara
@ 2018-01-15  0:23       ` Paulo Alcantara
  2018-01-15  0:23       ` [RFC v5 3/8] UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support Paulo Alcantara
                         ` (6 subsequent siblings)
  8 siblings, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2018-01-15  0:23 UTC (permalink / raw)
  To: edk2-devel; +Cc: Paulo Alcantara, Eric Dong, Laszlo Ersek

This function will be used by both IA32 and X64 exception handling in
order to print out image module names during stack unwinding.

Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c       | 60 +++++++++++++++++++-
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h       | 14 +++++
 UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 59 -------------------
 3 files changed, 73 insertions(+), 60 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
index 01b0610364..d9abbd772d 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
@@ -54,6 +54,11 @@ CONST CHAR8 *mExceptionNameStr[] = {
 
 #define EXCEPTION_KNOWN_NAME_NUM  (sizeof (mExceptionNameStr) / sizeof (CHAR8 *))
 
+//
+// Unknown PDB file name
+//
+GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
+
 /**
   Get ASCII format string exception name by exception type.
 
@@ -177,4 +182,57 @@ ReadAndVerifyVectorInfo (
     VectorInfo ++;
   }
   return EFI_SUCCESS;
-}
\ No newline at end of file
+}
+
+/**
+  Get absolute path and file name of PDB file in PE/COFF image.
+
+  @param[in]  ImageBase            Base address of PE/COFF image.
+  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
+  @param[out] PdbFileName          File name of PDB file.
+**/
+VOID
+GetPdbFileName (
+  IN  UINTN    ImageBase,
+  OUT CHAR8    **PdbAbsoluteFilePath,
+  OUT CHAR8    **PdbFileName
+  )
+{
+  VOID   *PdbPointer;
+  CHAR8  *Str;
+
+  //
+  // Get PDB file name from PE/COFF image
+  //
+  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
+  if (PdbPointer == NULL) {
+    //
+    // No PDB file name found. Set it to an unknown file name.
+    //
+    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
+    if (PdbAbsoluteFilePath != NULL) {
+      *PdbAbsoluteFilePath = NULL;
+    }
+  } else {
+    //
+    // Get file name portion out of PDB file in PE/COFF image
+    //
+    Str = (CHAR8 *)((UINTN)PdbPointer +
+                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
+    for (; *Str != '/' && *Str != '\\'; Str--) {
+      ;
+    }
+
+    //
+    // Set PDB file name (also skip trailing path separator: '/' or '\\')
+    //
+    *PdbFileName = Str + 1;
+
+    if (PdbAbsoluteFilePath != NULL) {
+      //
+      // Set absolute file path of PDB file
+      //
+      *PdbAbsoluteFilePath = PdbPointer;
+    }
+  }
+}
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
index e10d9379d5..64c7094513 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
@@ -327,5 +327,19 @@ AsmGetTssTemplateMap (
   OUT EXCEPTION_HANDLER_TEMPLATE_MAP  *AddressMap
   );
 
+/**
+  Get absolute path and file name of PDB file in PE/COFF image.
+
+  @param[in]  ImageBase            Base address of PE/COFF image.
+  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
+  @param[out] PdbFileName          File name of PDB file.
+**/
+VOID
+GetPdbFileName (
+  IN  UINTN    ImageBase,
+  OUT CHAR8    **PdbAbsoluteFilePath,
+  OUT CHAR8    **PdbFileName
+  );
+
 #endif
 
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
index 4db9f6465e..523dce95c9 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
@@ -14,11 +14,6 @@
 
 #include "CpuExceptionCommon.h"
 
-//
-// Unknown PDB file name
-//
-GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
-
 /**
   Return address map of exception handler template so that C code can generate
   exception tables.
@@ -403,60 +398,6 @@ DumpCpuContext (
     );
 }
 
-/**
-  Get absolute path and file name of PDB file in PE/COFF image.
-
-  @param[in]  ImageBase            Base address of PE/COFF image.
-  @param[out] PdbAbsoluteFilePath  Absolute path of PDB file.
-  @param[out] PdbFileName          File name of PDB file.
-**/
-STATIC
-VOID
-GetPdbFileName (
-  IN  UINTN    ImageBase,
-  OUT CHAR8    **PdbAbsoluteFilePath,
-  OUT CHAR8    **PdbFileName
-  )
-{
-  VOID   *PdbPointer;
-  CHAR8  *Str;
-
-  //
-  // Get PDB file name from PE/COFF image
-  //
-  PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)ImageBase);
-  if (PdbPointer == NULL) {
-    //
-    // No PDB file name found. Set it to an unknown file name.
-    //
-    *PdbFileName = (CHAR8 *)mUnknownPdbFileName;
-    if (PdbAbsoluteFilePath != NULL) {
-      *PdbAbsoluteFilePath = NULL;
-    }
-  } else {
-    //
-    // Get file name portion out of PDB file in PE/COFF image
-    //
-    Str = (CHAR8 *)((UINTN)PdbPointer +
-                    AsciiStrLen ((CHAR8 *)PdbPointer) - sizeof *Str);
-    for (; *Str != '/' && *Str != '\\'; Str--) {
-      ;
-    }
-
-    //
-    // Set PDB file name (also skip trailing path separator: '/' or '\\')
-    //
-    *PdbFileName = Str + 1;
-
-    if (PdbAbsoluteFilePath != NULL) {
-      //
-      // Set absolute file path of PDB file
-      //
-      *PdbAbsoluteFilePath = PdbPointer;
-    }
-  }
-}
-
 /**
   Dump stack contents.
 
-- 
2.14.3



^ permalink raw reply related	[flat|nested] 82+ messages in thread

* [RFC v5 3/8] UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support
  2018-01-15  0:23     ` [RFC v5 0/8] " Paulo Alcantara
  2018-01-15  0:23       ` [RFC v5 1/8] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support Paulo Alcantara
  2018-01-15  0:23       ` [RFC v5 2/8] UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName() Paulo Alcantara
@ 2018-01-15  0:23       ` Paulo Alcantara
  2018-01-15  0:23       ` [RFC v5 4/8] UefiCpuPkg/CpuExceptionHandlerLib: Add helper to validate memory addresses Paulo Alcantara
                         ` (5 subsequent siblings)
  8 siblings, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2018-01-15  0:23 UTC (permalink / raw)
  To: edk2-devel; +Cc: Paulo Alcantara, Eric Dong, Laszlo Ersek

This patch adds stack trace support during a IA32 CPU exception.

It will dump out back trace, stack contents as well as image module
names that were part of the call stack.

Contributed-under: TianoCore Contribution Agreement 1.1
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c        |  42 ---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h        |  11 -
 UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c | 335 +++++++++++++++++++-
 3 files changed, 327 insertions(+), 61 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
index d9abbd772d..66892320c8 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
@@ -109,48 +109,6 @@ InternalPrintMessage (
   SerialPortWrite ((UINT8 *)Buffer, AsciiStrLen (Buffer));
 }
 
-/**
-  Find and display image base address and return image base and its entry point.
-
-  @param CurrentEip      Current instruction pointer.
-
-**/
-VOID
-DumpModuleImageInfo (
-  IN  UINTN              CurrentEip
-  )
-{
-  EFI_STATUS                           Status;
-  UINTN                                Pe32Data;
-  VOID                                 *PdbPointer;
-  VOID                                 *EntryPoint;
-
-  Pe32Data = PeCoffSearchImageBase (CurrentEip);
-  if (Pe32Data == 0) {
-    InternalPrintMessage ("!!!! Can't find image information. !!!!\n");
-  } else {
-    //
-    // Find Image Base entry point
-    //
-    Status = PeCoffLoaderGetEntryPoint ((VOID *) Pe32Data, &EntryPoint);
-    if (EFI_ERROR (Status)) {
-      EntryPoint = NULL;
-    }
-    InternalPrintMessage ("!!!! Find image based on IP(0x%x) ", CurrentEip);
-    PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *) Pe32Data);
-    if (PdbPointer != NULL) {
-      InternalPrintMessage ("%a", PdbPointer);
-    } else {
-      InternalPrintMessage ("(No PDB) " );
-    }
-    InternalPrintMessage (
-      " (ImageBase=%016lp, EntryPoint=%016p) !!!!\n",
-      (VOID *) Pe32Data,
-      EntryPoint
-      );
-  }
-}
-
 /**
   Read and save reserved vector information
 
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
index 64c7094513..ec46c2d9d3 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
@@ -130,17 +130,6 @@ InternalPrintMessage (
   ...
   );
 
-/**
-  Find and display image base address and return image base and its entry point.
-
-  @param CurrentEip      Current instruction pointer.
-
-**/
-VOID
-DumpModuleImageInfo (
-  IN  UINTN              CurrentEip
-  );
-
 /**
   Display CPU information.
 
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
index 04f2ab593c..c5d6ea0939 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
@@ -399,20 +399,156 @@ DumpCpuContext (
 }
 
 /**
-  Display CPU information.
+  Dump stack trace.
 
-  @param ExceptionType  Exception type.
-  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+  @param[in]  ExceptionType      Exception type.
+  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
+  @param[out] UnwoundStacksCount Count of unwound stack frames.
 **/
+STATIC
 VOID
-DumpImageAndCpuContent (
+DumpStacktrace (
+  IN  EFI_EXCEPTION_TYPE   ExceptionType,
+  IN  EFI_SYSTEM_CONTEXT   SystemContext,
+  OUT INTN                 *UnwoundStacksCount
+  )
+{
+  UINT32  Eip;
+  UINT32  Ebp;
+  UINTN   ImageBase;
+  CHAR8   *PdbFileName;
+
+  //
+  // Set current EIP address
+  //
+  if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) &&
+      ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0)) {
+    //
+    // The EIP in SystemContext could not be used
+    // if it is page fault with I/D set.
+    //
+    Eip = *(UINT32 *)(UINTN)SystemContext.SystemContextIa32->Esp;
+  } else {
+    Eip = SystemContext.SystemContextIa32->Eip;
+  }
+
+  //
+  // Set current frame pointer address
+  //
+  Ebp = SystemContext.SystemContextIa32->Ebp;
+
+  //
+  // Check for proper frame pointer alignment
+  //
+  if (((UINTN)Ebp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
+    InternalPrintMessage ("!!!! Unaligned frame pointer. !!!!\n");
+    return;
+  }
+
+  //
+  // Get initial PE/COFF image base address from current EIP
+  //
+  ImageBase = PeCoffSearchImageBase (Eip);
+  if (ImageBase == 0) {
+    InternalPrintMessage ("!!!! Could not find backtrace information. !!!!");
+    return;
+  }
+
+  //
+  // Get PDB file name from initial PE/COFF image
+  //
+  GetPdbFileName (ImageBase, NULL, &PdbFileName);
+
+  //
+  // Initialize count of unwound stacks
+  //
+  *UnwoundStacksCount = 1;
+
+  //
+  // Print out back trace
+  //
+  InternalPrintMessage ("\nCall trace:\n");
+
+  for (;;) {
+    //
+    // Print stack frame in the following format:
+    //
+    // # <EIP> @ <ImageBase>+<RelOffset> (EBP) in [<ModuleName> | ????]
+    //
+    InternalPrintMessage (
+      "%d 0x%08x @ 0x%08x+0x%x (0x%08x) in %a\n",
+      *UnwoundStacksCount - 1,
+      Eip,
+      ImageBase,
+      Eip - ImageBase - 1,
+      Ebp,
+      PdbFileName
+      );
+
+    //
+    // Set EIP with return address from current stack frame
+    //
+    Eip = *(UINT32 *)((UINTN)Ebp + 4);
+
+    //
+    // If EIP is zero, then stop unwinding the stack
+    //
+    if (Eip == 0) {
+      break;
+    }
+
+    //
+    // Search for the respective PE/COFF image based on EIP
+    //
+    ImageBase = PeCoffSearchImageBase (Eip);
+    if (ImageBase == 0) {
+      //
+      // Stop stack trace
+      //
+      break;
+    }
+
+    //
+    // Get PDB file name
+    //
+    GetPdbFileName (ImageBase, NULL, &PdbFileName);
+
+    //
+    // Unwind the stack
+    //
+    Ebp = *(UINT32 *)(UINTN)Ebp;
+
+    //
+    // Increment count of unwound stacks
+    //
+    (*UnwoundStacksCount)++;
+  }
+}
+
+/**
+  Dump all image module names from call stack.
+
+  @param[in]  ExceptionType  Exception type.
+  @param[in]  SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+**/
+STATIC
+VOID
+DumpImageModuleNames (
   IN EFI_EXCEPTION_TYPE   ExceptionType,
   IN EFI_SYSTEM_CONTEXT   SystemContext
   )
 {
-  DumpCpuContext (ExceptionType, SystemContext);
+  EFI_STATUS  Status;
+  UINT32      Eip;
+  UINT32      Ebp;
+  UINTN       ImageBase;
+  VOID        *EntryPoint;
+  CHAR8       *PdbAbsoluteFilePath;
+  CHAR8       *PdbFileName;
+  UINTN       LastImageBase;
+
   //
-  // Dump module image base and module entry point by EIP
+  // Set current EIP address
   //
   if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) &&
       ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0)) {
@@ -420,8 +556,191 @@ DumpImageAndCpuContent (
     // The EIP in SystemContext could not be used
     // if it is page fault with I/D set.
     //
-    DumpModuleImageInfo ((*(UINTN *)(UINTN)SystemContext.SystemContextIa32->Esp));
+    Eip = *(UINT32 *)(UINTN)SystemContext.SystemContextIa32->Esp;
   } else {
-    DumpModuleImageInfo (SystemContext.SystemContextIa32->Eip);
+    Eip = SystemContext.SystemContextIa32->Eip;
+  }
+
+  //
+  // Set current frame pointer address
+  //
+  Ebp = SystemContext.SystemContextIa32->Ebp;
+
+  //
+  // Get initial PE/COFF image base address from current EIP
+  //
+  ImageBase = PeCoffSearchImageBase (Eip);
+  if (ImageBase == 0) {
+    InternalPrintMessage ("!!!! Could not find image module names. !!!!");
+    return;
   }
+
+  //
+  // Set last PE/COFF image base address
+  //
+  LastImageBase = ImageBase;
+
+  //
+  // Get initial PE/COFF image's entry point
+  //
+  Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
+  if (EFI_ERROR (Status)) {
+    EntryPoint = NULL;
+  }
+
+  //
+  // Get file name and absolute path of initial PDB file
+  //
+  GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
+
+  //
+  // Print out initial image module name (if any)
+  //
+  if (PdbAbsoluteFilePath != NULL) {
+    InternalPrintMessage (
+      "\n%a (ImageBase=0x%08x, EntryPoint=0x%08x):\n",
+      PdbFileName,
+      ImageBase,
+      (UINTN)EntryPoint
+      );
+    InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
+  }
+
+  //
+  // Walk through call stack and find next module names
+  //
+  for (;;) {
+    //
+    // Set EIP with return address from current stack frame
+    //
+    Eip = *(UINT32 *)((UINTN)Ebp + 4);
+
+    //
+    // Search for the respective PE/COFF image based on Eip
+    //
+    ImageBase = PeCoffSearchImageBase (Eip);
+    if (ImageBase == 0) {
+      //
+      // Stop stack trace
+      //
+      break;
+    }
+
+    //
+    // If EIP points to another PE/COFF image, then find its respective PDB file
+    // name.
+    //
+    if (LastImageBase != ImageBase) {
+      //
+      // Get PE/COFF image's entry point
+      //
+      Status = PeCoffLoaderGetEntryPoint ((VOID *)ImageBase, &EntryPoint);
+      if (EFI_ERROR (Status)) {
+        EntryPoint = NULL;
+      }
+
+      //
+      // Get file name and absolute path of PDB file
+      //
+      GetPdbFileName (ImageBase, &PdbAbsoluteFilePath, &PdbFileName);
+
+      //
+      // Print out image module name (if any)
+      //
+      if (PdbAbsoluteFilePath != NULL) {
+        InternalPrintMessage (
+          "%a (ImageBase=0x%08x, EntryPoint=0x%08x):\n",
+          PdbFileName,
+          ImageBase,
+          (UINTN)EntryPoint
+          );
+        InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
+      }
+
+      //
+      // Save last PE/COFF image base address
+      //
+      LastImageBase = ImageBase;
+    }
+
+    //
+    // Unwind the stack
+    //
+    Ebp = *(UINT32 *)(UINTN)Ebp;
+  }
+}
+
+/**
+  Dump stack contents.
+
+  @param[in]  CurrentEsp         Current stack pointer address.
+  @param[in]  UnwoundStacksCount  Count of unwound stack frames.
+**/
+STATIC
+VOID
+DumpStackContents (
+  IN UINT32  CurrentEsp,
+  IN INTN    UnwoundStacksCount
+  )
+{
+  //
+  // Check for proper stack alignment
+  //
+  if (((UINTN)CurrentEsp & (CPU_STACK_ALIGNMENT - 1)) != 0) {
+    InternalPrintMessage ("!!!! Unaligned stack pointer. !!!!\n");
+    return;
+  }
+
+  //
+  // Dump out stack contents
+  //
+  InternalPrintMessage ("\nStack dump:\n");
+  while (UnwoundStacksCount-- > 0) {
+    InternalPrintMessage (
+      "0x%08x: %08x %08x\n",
+      CurrentEsp,
+      *(UINT32 *)CurrentEsp,
+      *(UINT32 *)((UINTN)CurrentEsp + 4)
+      );
+
+    //
+    // Point to next stack
+    //
+    CurrentEsp += CPU_STACK_ALIGNMENT;
+  }
+}
+
+/**
+  Display CPU information.
+
+  @param ExceptionType  Exception type.
+  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.
+**/
+VOID
+DumpImageAndCpuContent (
+  IN EFI_EXCEPTION_TYPE   ExceptionType,
+  IN EFI_SYSTEM_CONTEXT   SystemContext
+  )
+{
+  INTN UnwoundStacksCount;
+
+  //
+  // Dump CPU context
+  //
+  DumpCpuContext (ExceptionType, SystemContext);
+
+  //
+  // Dump stack trace
+  //
+  DumpStacktrace (ExceptionType, SystemContext, &UnwoundStacksCount);
+
+  //
+  // Dump image module names
+  //
+  DumpImageModuleNames (ExceptionType, SystemContext);
+
+  //
+  // Dump stack contents
+  //
+  DumpStackContents (SystemContext.SystemContextIa32->Esp, UnwoundStacksCount);
 }
-- 
2.14.3



^ permalink raw reply related	[flat|nested] 82+ messages in thread

* [RFC v5 4/8] UefiCpuPkg/CpuExceptionHandlerLib: Add helper to validate memory addresses
  2018-01-15  0:23     ` [RFC v5 0/8] " Paulo Alcantara
                         ` (2 preceding siblings ...)
  2018-01-15  0:23       ` [RFC v5 3/8] UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support Paulo Alcantara
@ 2018-01-15  0:23       ` Paulo Alcantara
  2018-01-15  0:23       ` [RFC v5 5/8] UefiCpuPkg/CpuExceptionHandlerLib: Ensure valid frame/stack pointers Paulo Alcantara
                         ` (4 subsequent siblings)
  8 siblings, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2018-01-15  0:23 UTC (permalink / raw)
  To: edk2-devel; +Cc: Paulo Alcantara, Eric Dong, Laszlo Ersek

Introduce IsLinearAddressValid() function that will be used for
validating memory addresses that would get dereferenced during stack
traces in IA32 and X64 CPU exceptions.

Contributed-under: TianoCore Contribution Agreement 1.1
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Requested-by: Brian Johnson <brian.johnson@hpe.com>
Requested-by: Jiewen Yao <jiewen.yao@intel.com>
Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c | 395 ++++++++++++++++++++
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h |  16 +
 2 files changed, 411 insertions(+)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
index 66892320c8..7ac13640de 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
@@ -14,6 +14,8 @@
 
 #include "CpuExceptionCommon.h"
 
+#include <Register/Msr.h>
+
 //
 // Error code flag indicating whether or not an error code will be
 // pushed on the stack if an exception occurs.
@@ -59,6 +61,24 @@ CONST CHAR8 *mExceptionNameStr[] = {
 //
 GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 *mUnknownPdbFileName = "????";
 
+//
+// IA32 virtual memory bit definitions
+//
+#define IA32_PG_P   BIT0
+#define IA32_PG_PS  BIT7
+
+//
+// IA32 control register bit definitions
+//
+#define IA32_CR0_PG   BIT31
+#define IA32_CR4_PAE  BIT5
+#define IA32_CR0_PE   BIT0
+
+//
+// IA32 CPUID 01h EDX bit definitions
+//
+#define IA32_CPUID1_EDX_PAE BIT6
+
 /**
   Get ASCII format string exception name by exception type.
 
@@ -194,3 +214,378 @@ GetPdbFileName (
     }
   }
 }
+
+/**
+  Check if a linear address is valid by walking the page tables in 4-level
+  paging mode.
+
+  @param[in]  Cr3             CR3 control register.
+  @param[in]  MaxPhyAddrBits  MAXPHYADDR bits.
+  @param[in]  LinearAddress   Linear address to be checked.
+**/
+STATIC
+BOOLEAN
+Do4LevelPagingModeCheck (
+  IN UINTN            Cr3,
+  IN UINT8            MaxPhyAddrBits,
+  IN UINTN            LinearAddress
+  )
+{
+  UINT64  PhysicalAddressMask;
+  UINTN   IndexMask;
+  UINTN   Index;
+  UINT64  *Pml4Table;
+  UINT64  *TableEntry;
+  UINT64  *PageDirPtrTable;
+  UINT64  *PageDirTable;
+  UINT64  *PageTable;
+
+  //
+  // In 4-level paging mode, linear addresses are 48 bits wide
+  //
+  if ((UINT64)LinearAddress > 0xFFFFFFFFFFFFULL) {
+    return FALSE;
+  }
+
+  //
+  // Calculate physical address mask (bits M-1:12)
+  //
+  PhysicalAddressMask = (LShiftU64 (1, MaxPhyAddrBits) - 1) & ~0xFFF;
+  //
+  // 9 bits for masking page table indexes out of linear addresses
+  //
+  IndexMask = 0x1FF;
+
+  //
+  // Calculate physical address of PML4 table and index of PML4E
+  //
+  Pml4Table = (UINT64 *)(UINTN)((UINT64)Cr3 & PhysicalAddressMask);
+  Index = (UINTN)(RShiftU64 ((UINT64)LinearAddress, 39) & IndexMask);
+
+  //
+  // Select PML4E
+  //
+  TableEntry = &Pml4Table[Index];
+
+  //
+  // Check if a PDPTE is present
+  //
+  if ((*TableEntry & IA32_PG_P) == 0) {
+    return FALSE;
+  }
+
+  //
+  // Calculate physical address of page-directory-pointer table and index of
+  // PDPTE.
+  //
+  PageDirPtrTable = (UINT64 *)(UINTN)(*TableEntry & PhysicalAddressMask);
+  Index = (UINTN)(RShiftU64 ((UINT64)LinearAddress, 30) & IndexMask);
+
+  //
+  // Select PDPTE
+  //
+  TableEntry = &PageDirPtrTable[Index];
+
+  //
+  // Check whether a PDPTE or 1GiB page entry is present
+  //
+  if ((*TableEntry & IA32_PG_P) == 0) {
+    return FALSE;
+  }
+
+  //
+  // Check if PDPTE maps an 1GiB page
+  //
+  if ((*TableEntry & IA32_PG_PS) != 0) {
+    return TRUE;
+  }
+
+  //
+  // Calculate physical address of page directory table and index of PDE
+  //
+  PageDirTable = (UINT64 *)(UINTN)(*TableEntry & PhysicalAddressMask);
+  Index = (UINTN)(RShiftU64 ((UINT64)LinearAddress, 21) & IndexMask);
+
+  //
+  // Select PDE
+  //
+  TableEntry = &PageDirTable[Index];
+
+  //
+  // Check whether a PDE or a 2MiB page entry is present
+  //
+  if ((*TableEntry & IA32_PG_P) == 0) {
+    return FALSE;
+  }
+
+  //
+  // Check if PDE maps a 2MiB page
+  //
+  if ((*TableEntry & IA32_PG_PS) != 0) {
+    return TRUE;
+  }
+
+  //
+  // Calculate physical address of page table and index of PTE
+  //
+  PageTable = (UINT64 *)(UINTN)(*TableEntry & PhysicalAddressMask);
+  Index = (UINTN)(RShiftU64 ((UINT64)LinearAddress, 12) & IndexMask);
+
+  //
+  // Select PTE
+  //
+  TableEntry = &PageTable[Index];
+
+  //
+  // Check if PTE maps a 4KiB page
+  //
+  if ((*TableEntry & IA32_PG_P) == 0) {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/**
+  Check if a linear address is valid by walking the page tables in 32-bit paging
+  mode.
+
+  NOTE: Current UEFI implementations do not support IA32 non-PAE paging mode.
+
+  @param[in]  Cr3             CR3 control register.
+  @param[in]  Cr4             CR4 control register.
+  @param[in]  LinearAddress   Linear address to be checked.
+**/
+STATIC
+BOOLEAN
+Do32BitPagingModeCheck (
+  IN UINTN            Cr3,
+  IN UINTN            Cr4,
+  IN UINTN            LinearAddress
+  )
+{
+  InternalPrintMessage ("!!!! Unsupported IA32 non-PAE paging mode !!!!\n");
+  return FALSE;
+}
+
+/**
+  Check if a linear address is valid by walking the page tables in PAE paging
+  mode.
+
+  @param[in]  Cr3             CR3 control register.
+  @param[in]  MaxPhyAddrBits  MAXPHYADDR bits.
+  @param[in]  LinearAddress   Linear address to be checked.
+**/
+STATIC
+BOOLEAN
+DoPAEPagingModeCheck (
+  IN UINTN            Cr3,
+  IN UINT8            MaxPhyAddrBits,
+  IN UINTN            LinearAddress
+  )
+{
+  UINT64  *PageDirPtrTable;
+  UINTN   Index;
+  UINT64  *PageDirTable;
+  UINT64  PhysicalAddressMask;
+  UINTN   IndexMask;
+  UINT64  *TableEntry;
+  UINT64  *PageTable;
+
+  //
+  // In 32-bit PAE paging mode, linear addresses are 32 bits wide
+  //
+  if (LinearAddress > 0xFFFFFFFF) {
+    return FALSE;
+  }
+
+  //
+  // Calculate physical address of page-directory-pointer table and index of
+  // PDPTE register.
+  //
+  PageDirPtrTable = (UINT64 *)(UINTN)(Cr3 & ~0x1F);
+  Index = (UINTN)((UINT32)LinearAddress >> 30);
+
+  //
+  // Select PDPTE register
+  //
+  TableEntry = &PageDirPtrTable[Index];
+
+  //
+  // Check if PDE is present
+  //
+  if ((*TableEntry & IA32_PG_P) == 0) {
+    return FALSE;
+  }
+
+  //
+  // Calculate physical address mask (bits M-1:12)
+  //
+  PhysicalAddressMask = (LShiftU64 (1, MaxPhyAddrBits) - 1) & ~0xFFF;
+  //
+  // 9 bits for masking page table indexes out of linear addresses
+  //
+  IndexMask = 0x1FF;
+
+  //
+  // Calculate physical address of page directory table and index of PDE
+  //
+  PageDirTable = (UINT64 *)(UINTN)(*TableEntry & PhysicalAddressMask);
+  Index = (UINTN)(RShiftU64 ((UINT64)LinearAddress, 21) & IndexMask);
+
+  //
+  // Select PDE
+  //
+  TableEntry = &PageDirTable[Index];
+
+  //
+  // Check whether a PTE or a 2MiB page is present
+  //
+  if ((*TableEntry & IA32_PG_P) == 0) {
+    return FALSE;
+  }
+
+  //
+  // Check if PDE maps a 2MiB page
+  //
+  if ((*TableEntry & IA32_PG_PS) != 0) {
+    return TRUE;
+  }
+
+  //
+  // Calculate physical address of page table and index of PTE
+  //
+  PageTable = (UINT64 *)(UINTN)(*TableEntry & PhysicalAddressMask);
+  Index = (UINTN)(RShiftU64 ((UINT64)LinearAddress, 12) & IndexMask);
+
+  //
+  // Select PTE
+  //
+  TableEntry = &PageTable[Index];
+
+  //
+  // Check if PTE maps a 4KiB page
+  //
+  if ((*TableEntry & IA32_PG_P) == 0) {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/**
+  Check if a linear address is valid.
+
+  @param[in]  Cr0            CR0 control register.
+  @param[in]  Cr3            CR3 control register.
+  @param[in]  Cr4            CR4 control register.
+  @param[in]  LinearAddress  Linear address to be checked.
+**/
+BOOLEAN
+IsLinearAddressValid (
+  IN  UINTN              Cr0,
+  IN  UINTN              Cr3,
+  IN  UINTN              Cr4,
+  IN  UINTN              LinearAddress
+  )
+{
+  UINT32                  Eax;
+  UINT32                  Edx;
+  UINT8                   MaxPhyAddrBits;
+  MSR_IA32_EFER_REGISTER  Msr;
+  BOOLEAN                 AddressValid;
+
+  //
+  // Check for valid input parameters
+  //
+  if (Cr0 == 0 || Cr4 == 0 || LinearAddress == 0) {
+    return FALSE;
+  }
+
+  //
+  // Check if paging is disabled
+  //
+  if ((Cr0 & IA32_CR0_PG) == 0) {
+    //
+    // If CR4.PAE bit is set, then the linear (or physical) address supports
+    // only up to 36 bits.
+    //
+    if ((UINT64)LinearAddress > 0xFFFFFFFFFULL ||
+        ((Cr4 & IA32_CR4_PAE) == 0 && LinearAddress > 0xFFFFFFFF)) {
+      return FALSE;
+    }
+
+    return TRUE;
+  }
+
+  //
+  // Paging can be enabled only if CR0.PE bit is set
+  //
+  if ((Cr0 & IA32_CR0_PE) == 0) {
+    return FALSE;
+  }
+
+  //
+  // CR3 register cannot be zero if paging is enabled
+  //
+  if (Cr3 == 0) {
+    return FALSE;
+  }
+
+  //
+  // Get MAXPHYADDR bits
+  //
+  AsmCpuid (0x80000000, &Eax, NULL, NULL, NULL);
+  if (Eax >= 0x80000008) {
+    AsmCpuid (0x80000008, &Eax, NULL, NULL, NULL);
+    MaxPhyAddrBits = (UINT8)Eax;
+  } else {
+    AsmCpuid (1, NULL, NULL, NULL, &Edx);
+    if ((Edx & IA32_CPUID1_EDX_PAE) != 0) {
+      MaxPhyAddrBits = 36;
+    } else {
+      MaxPhyAddrBits = 32;
+    }
+  }
+
+  //
+  // Check if CR4.PAE bit is not set
+  //
+  if ((Cr4 & IA32_CR4_PAE) == 0) {
+    //
+    // Check if linear address is valid in 32-bit paging mode
+    //
+    AddressValid = Do32BitPagingModeCheck (Cr3, Cr4, LinearAddress);
+  } else {
+    //
+    // In either PAE or 4-level paging mode, physical addresses can hold only
+    // up to 52 bits.
+    //
+    if (MaxPhyAddrBits > 52) {
+      return FALSE;
+    }
+
+    //
+    // Read IA32_EFER MSR register
+    //
+    Msr.Uint64 = AsmReadMsr64 (MSR_IA32_EFER);
+
+    //
+    // Check if IA32_EFER.LME bit is not set (e.g. PAE paging mode)
+    //
+    if (Msr.Bits.LME == 0) {
+      //
+      // Check if linear address is valid in PAE paging mode
+      //
+      AddressValid = DoPAEPagingModeCheck (Cr3, MaxPhyAddrBits, LinearAddress);
+    } else {
+      //
+      // Check if linear address is valid in 4-level paging mode
+      //
+      AddressValid = Do4LevelPagingModeCheck (Cr3, MaxPhyAddrBits,
+                                              LinearAddress);
+    }
+  }
+
+  return AddressValid;
+}
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
index ec46c2d9d3..1b51034c25 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
@@ -330,5 +330,21 @@ GetPdbFileName (
   OUT CHAR8    **PdbFileName
   );
 
+/**
+  Check if a linear address is valid.
+
+  @param[in]  Cr0            CR0 control register.
+  @param[in]  Cr3            CR3 control register.
+  @param[in]  Cr4            CR4 control register.
+  @param[in]  LinearAddress  Linear address to be checked.
+**/
+BOOLEAN
+IsLinearAddressValid (
+  IN  UINTN              Cr0,
+  IN  UINTN              Cr3,
+  IN  UINTN              Cr4,
+  IN  UINTN              LinearAddress
+  );
+
 #endif
 
-- 
2.14.3



^ permalink raw reply related	[flat|nested] 82+ messages in thread

* [RFC v5 5/8] UefiCpuPkg/CpuExceptionHandlerLib: Ensure valid frame/stack pointers
  2018-01-15  0:23     ` [RFC v5 0/8] " Paulo Alcantara
                         ` (3 preceding siblings ...)
  2018-01-15  0:23       ` [RFC v5 4/8] UefiCpuPkg/CpuExceptionHandlerLib: Add helper to validate memory addresses Paulo Alcantara
@ 2018-01-15  0:23       ` Paulo Alcantara
  2018-01-15  0:23       ` [RFC v5 6/8] UefiCpuPkg/CpuExceptionHandlerLib: Correctly print IP addresses Paulo Alcantara
                         ` (3 subsequent siblings)
  8 siblings, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2018-01-15  0:23 UTC (permalink / raw)
  To: edk2-devel; +Cc: Paulo Alcantara, Eric Dong, Laszlo Ersek

Validate all possible memory dereferences during stack traces in IA32
and X64 CPU exceptions.

Contributed-under: TianoCore Contribution Agreement 1.1
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Requested-by: Brian Johnson <brian.johnson@hpe.com>
Requested-by: Jiewen Yao <jiewen.yao@intel.com>
Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c | 149 +++++++++++++++++++-
 UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c  |  75 +++++++++-
 2 files changed, 216 insertions(+), 8 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
index c5d6ea0939..3b92512b92 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
@@ -14,6 +14,11 @@
 
 #include "CpuExceptionCommon.h"
 
+//
+// IA32 Segment Selector bit definitions
+//
+#define IA32_SEGSEL_TI BIT2
+
 /**
   Return address map of exception handler template so that C code can generate
   exception tables.
@@ -398,6 +403,97 @@ DumpCpuContext (
     );
 }
 
+/**
+  Check if a logical address is valid.
+
+  @param[in]  SystemContext      Pointer to EFI_SYSTEM_CONTEXT.
+  @param[in]  SegmentSelector    Segment selector.
+  @param[in]  Offset             Offset or logical address.
+**/
+STATIC
+BOOLEAN
+IsLogicalAddressValid (
+  IN  EFI_SYSTEM_CONTEXT   SystemContext,
+  IN  UINT16               SegmentSelector,
+  IN  UINTN                Offset
+  )
+{
+  IA32_SEGMENT_DESCRIPTOR  *SegmentDescriptor;
+  UINT32                   SegDescBase;
+  UINT32                   SegDescLimit;
+  UINT64                   SegDescLimitInBytes;
+
+  //
+  // Check for valid input parameters
+  //
+  if (SegmentSelector == 0 || Offset == 0) {
+    return FALSE;
+  }
+
+  //
+  // Look for a segment descriptor in a GDT or LDT table depending on TI
+  // (Table Indicator) bit in segment selector.
+  //
+  if ((SegmentSelector & IA32_SEGSEL_TI) == 0) {
+    //
+    // Get segment descriptor from GDT table
+    //
+    SegmentDescriptor =
+      (IA32_SEGMENT_DESCRIPTOR *)(
+        (UINTN)SystemContext.SystemContextIa32->Gdtr[0] +
+        (SegmentSelector & ~7)
+        );
+  } else {
+    //
+    // Get segment descriptor from LDT table
+    //
+    SegmentDescriptor =
+      (IA32_SEGMENT_DESCRIPTOR *)(
+        (UINTN)SystemContext.SystemContextIa32->Ldtr +
+        (SegmentSelector & ~7)
+        );
+  }
+
+  //
+  // Get segment descriptor's base address
+  //
+  SegDescBase = SegmentDescriptor->Bits.BaseLow |
+    (SegmentDescriptor->Bits.BaseMid << 16) |
+    (SegmentDescriptor->Bits.BaseHigh << 24);
+
+  //
+  // Get segment descriptor's limit
+  //
+  SegDescLimit = SegmentDescriptor->Bits.LimitLow |
+    (SegmentDescriptor->Bits.LimitHigh << 16);
+
+  //
+  // Calculate segment descriptor's limit in bytes
+  //
+  if (SegmentDescriptor->Bits.G == 1) {
+    SegDescLimitInBytes = (UINT64)SegDescLimit * SIZE_4KB + (SIZE_4KB - 1);
+  } else {
+    SegDescLimitInBytes = SegDescLimit;
+  }
+
+  //
+  // Make sure to not access beyond a segment limit boundary
+  //
+  if ((UINT64)Offset + SegDescBase > SegDescLimitInBytes) {
+    return FALSE;
+  }
+
+  //
+  // Check if the translated logical address (or linear address) is valid
+  //
+  return IsLinearAddressValid (
+    SystemContext.SystemContextIa32->Cr0,
+    SystemContext.SystemContextIa32->Cr3,
+    SystemContext.SystemContextIa32->Cr4,
+    Offset + SegDescBase
+    );
+}
+
 /**
   Dump stack trace.
 
@@ -470,6 +566,20 @@ DumpStacktrace (
   InternalPrintMessage ("\nCall trace:\n");
 
   for (;;) {
+    //
+    // Check for valid frame pointer
+    //
+    if (!IsLogicalAddressValid (SystemContext,
+                                SystemContext.SystemContextIa32->Ss,
+                                (UINTN)Ebp + 4) ||
+        !IsLogicalAddressValid (SystemContext,
+                                SystemContext.SystemContextIa32->Ss,
+                                (UINTN)Ebp)) {
+      InternalPrintMessage ("%a: attempted to dereference an invalid frame "
+                            "pointer at 0x%08x\n", __FUNCTION__, Ebp);
+      break;
+    }
+
     //
     // Print stack frame in the following format:
     //
@@ -610,6 +720,16 @@ DumpImageModuleNames (
   // Walk through call stack and find next module names
   //
   for (;;) {
+    if (!IsLogicalAddressValid (SystemContext,
+                                SystemContext.SystemContextIa32->Ss,
+                                (UINTN)Ebp) ||
+        !IsLogicalAddressValid (SystemContext,
+                                SystemContext.SystemContextIa32->Ss,
+                                (UINTN)Ebp + 4)) {
+      InternalPrintMessage ("%a: attempted to dereference an invalid frame "
+                            "pointer at 0x%08x\n", __FUNCTION__, Ebp);
+    }
+
     //
     // Set EIP with return address from current stack frame
     //
@@ -673,16 +793,23 @@ DumpImageModuleNames (
 /**
   Dump stack contents.
 
-  @param[in]  CurrentEsp         Current stack pointer address.
+  @param[in]  SystemContext       Pointer to EFI_SYSTEM_CONTEXT.
   @param[in]  UnwoundStacksCount  Count of unwound stack frames.
 **/
 STATIC
 VOID
 DumpStackContents (
-  IN UINT32  CurrentEsp,
-  IN INTN    UnwoundStacksCount
+  IN  EFI_SYSTEM_CONTEXT  SystemContext,
+  IN  INTN                UnwoundStacksCount
   )
 {
+  UINT32 CurrentEsp;
+
+  //
+  // Get current stack pointer
+  //
+  CurrentEsp = SystemContext.SystemContextIa32->Esp;
+
   //
   // Check for proper stack alignment
   //
@@ -696,6 +823,20 @@ DumpStackContents (
   //
   InternalPrintMessage ("\nStack dump:\n");
   while (UnwoundStacksCount-- > 0) {
+    //
+    // Check for a valid stack pointer address
+    //
+    if (!IsLogicalAddressValid (SystemContext,
+                                SystemContext.SystemContextIa32->Ss,
+                                (UINTN)CurrentEsp) ||
+        !IsLogicalAddressValid (SystemContext,
+                                SystemContext.SystemContextIa32->Ss,
+                                (UINTN)CurrentEsp + 4)) {
+      InternalPrintMessage ("%a: attempted to dereference an invalid stack "
+                            "pointer at 0x%08x\n", __FUNCTION__, CurrentEsp);
+      break;
+    }
+
     InternalPrintMessage (
       "0x%08x: %08x %08x\n",
       CurrentEsp,
@@ -742,5 +883,5 @@ DumpImageAndCpuContent (
   //
   // Dump stack contents
   //
-  DumpStackContents (SystemContext.SystemContextIa32->Esp, UnwoundStacksCount);
+  DumpStackContents (SystemContext, UnwoundStacksCount);
 }
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
index 523dce95c9..c81f4c00eb 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
@@ -401,16 +401,26 @@ DumpCpuContext (
 /**
   Dump stack contents.
 
-  @param[in]  CurrentRsp         Current stack pointer address.
+  @param[in]  SystemContext       Pointer to EFI_SYSTEM_CONTEXT.
   @param[in]  UnwoundStacksCount  Count of unwound stack frames.
 **/
 STATIC
 VOID
 DumpStackContents (
-  IN UINT64  CurrentRsp,
-  IN INTN    UnwoundStacksCount
+  IN  EFI_SYSTEM_CONTEXT  SystemContext,
+  IN  INTN                UnwoundStacksCount
   )
 {
+  UINT64  CurrentRsp;
+  UINTN   Cr0;
+  UINTN   Cr3;
+  UINTN   Cr4;
+
+  //
+  // Get current stack pointer
+  //
+  CurrentRsp = SystemContext.SystemContextX64->Rsp;
+
   //
   // Check for proper stack pointer alignment
   //
@@ -419,11 +429,28 @@ DumpStackContents (
     return;
   }
 
+  //
+  // Get system control registers
+  //
+  Cr0 = SystemContext.SystemContextX64->Cr0;
+  Cr3 = SystemContext.SystemContextX64->Cr3;
+  Cr4 = SystemContext.SystemContextX64->Cr4;
+
   //
   // Dump out stack contents
   //
   InternalPrintMessage ("\nStack dump:\n");
   while (UnwoundStacksCount-- > 0) {
+    //
+    // Check for a valid stack pointer address
+    //
+    if (!IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)CurrentRsp) ||
+        !IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)CurrentRsp + 8)) {
+      InternalPrintMessage ("%a: attempted to dereference an invalid stack "
+                            "pointer at 0x%016lx\n", __FUNCTION__, CurrentRsp);
+      break;
+    }
+
     InternalPrintMessage (
       "0x%016lx: %016lx %016lx\n",
       CurrentRsp,
@@ -459,6 +486,9 @@ DumpImageModuleNames (
   CHAR8       *PdbFileName;
   UINT64      Rbp;
   UINTN       LastImageBase;
+  UINTN       Cr0;
+  UINTN       Cr3;
+  UINTN       Cr4;
 
   //
   // Set current RIP address
@@ -527,10 +557,27 @@ DumpImageModuleNames (
     InternalPrintMessage ("%a\n", PdbAbsoluteFilePath);
   }
 
+  //
+  // Get system control registers
+  //
+  Cr0 = SystemContext.SystemContextX64->Cr0;
+  Cr3 = SystemContext.SystemContextX64->Cr3;
+  Cr4 = SystemContext.SystemContextX64->Cr4;
+
   //
   // Walk through call stack and find next module names
   //
   for (;;) {
+    //
+    // Check for a valid frame pointer
+    //
+    if (!IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)Rbp + 8) ||
+        !IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)Rbp)) {
+      InternalPrintMessage ("%a: attempted to dereference an invalid frame "
+                            "pointer at 0x%016lx\n", __FUNCTION__, Rbp);
+      break;
+    }
+
     //
     // Set RIP with return address from current stack frame
     //
@@ -617,6 +664,9 @@ DumpStacktrace (
   UINT64  Rbp;
   UINTN   ImageBase;
   CHAR8   *PdbFileName;
+  UINTN   Cr0;
+  UINTN   Cr3;
+  UINTN   Cr4;
 
   //
   // Set current RIP address
@@ -656,12 +706,29 @@ DumpStacktrace (
   //
   *UnwoundStacksCount = 1;
 
+  //
+  // Get system control registers
+  //
+  Cr0 = SystemContext.SystemContextX64->Cr0;
+  Cr3 = SystemContext.SystemContextX64->Cr3;
+  Cr4 = SystemContext.SystemContextX64->Cr4;
+
   //
   // Print out back trace
   //
   InternalPrintMessage ("\nCall trace:\n");
 
   for (;;) {
+    //
+    // Check for valid frame pointer
+    //
+    if (!IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)Rbp + 8) ||
+        !IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)Rbp)) {
+      InternalPrintMessage ("%a: attempted to dereference an invalid frame "
+                            "pointer at 0x%016lx\n", __FUNCTION__, Rbp);
+      break;
+    }
+
     //
     // Print stack frame in the following format:
     //
@@ -749,5 +816,5 @@ DumpImageAndCpuContent (
   //
   // Dump stack contents
   //
-  DumpStackContents (SystemContext.SystemContextX64->Rsp, UnwoundStacksCount);
+  DumpStackContents (SystemContext, UnwoundStacksCount);
 }
-- 
2.14.3



^ permalink raw reply related	[flat|nested] 82+ messages in thread

* [RFC v5 6/8] UefiCpuPkg/CpuExceptionHandlerLib: Correctly print IP addresses
  2018-01-15  0:23     ` [RFC v5 0/8] " Paulo Alcantara
                         ` (4 preceding siblings ...)
  2018-01-15  0:23       ` [RFC v5 5/8] UefiCpuPkg/CpuExceptionHandlerLib: Ensure valid frame/stack pointers Paulo Alcantara
@ 2018-01-15  0:23       ` Paulo Alcantara
  2018-01-15  0:23       ` [RFC v5 7/8] UefiCpuPkg/CpuExceptionHandlerLib: Validate memory address ranges Paulo Alcantara
                         ` (2 subsequent siblings)
  8 siblings, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2018-01-15  0:23 UTC (permalink / raw)
  To: edk2-devel; +Cc: Paulo Alcantara, Eric Dong, Laszlo Ersek

Remove the supurious '- 1' when calculating the IP addresses during the
stack traces.

Contributed-under: TianoCore Contribution Agreement 1.1
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Requested-by: Jeff Fan <vanjeff_919@hotmail.com>
Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c | 2 +-
 UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
index 3b92512b92..31fbd4a164 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
@@ -590,7 +590,7 @@ DumpStacktrace (
       *UnwoundStacksCount - 1,
       Eip,
       ImageBase,
-      Eip - ImageBase - 1,
+      Eip - ImageBase,
       Ebp,
       PdbFileName
       );
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
index c81f4c00eb..71d2d2f5d4 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
@@ -739,7 +739,7 @@ DumpStacktrace (
       *UnwoundStacksCount - 1,
       Rip,
       ImageBase,
-      Rip - ImageBase - 1,
+      Rip - ImageBase,
       Rbp,
       PdbFileName
       );
-- 
2.14.3



^ permalink raw reply related	[flat|nested] 82+ messages in thread

* [RFC v5 7/8] UefiCpuPkg/CpuExceptionHandlerLib: Validate memory address ranges
  2018-01-15  0:23     ` [RFC v5 0/8] " Paulo Alcantara
                         ` (5 preceding siblings ...)
  2018-01-15  0:23       ` [RFC v5 6/8] UefiCpuPkg/CpuExceptionHandlerLib: Correctly print IP addresses Paulo Alcantara
@ 2018-01-15  0:23       ` Paulo Alcantara
  2018-01-15  0:23       ` [RFC v5 8/8] UefiCpuPkg/CpuExceptionHandlerLib: Add early check in DumpStackContents Paulo Alcantara
  2018-01-17 12:57       ` [RFC v5 0/8] Stack trace support in X64 exception handling Yao, Jiewen
  8 siblings, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2018-01-15  0:23 UTC (permalink / raw)
  To: edk2-devel; +Cc: Paulo Alcantara, Eric Dong, Laszlo Ersek

Introduce a new IsLinearAddressRangeValid() function to validate a given
address range and check whether or not it is valid.

This function is useful for validating ranges of memory addresses during
stack traces in X64.

Contributed-under: TianoCore Contribution Agreement 1.1
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Requested-by: Brian Johnson <brian.johnson@hpe.com>
Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c       | 40 ++++++++++++++++++++
 UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h       | 18 +++++++++
 UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c | 40 ++++++++++++--------
 3 files changed, 83 insertions(+), 15 deletions(-)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
index 7ac13640de..e1dd054259 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
@@ -589,3 +589,43 @@ IsLinearAddressValid (
 
   return AddressValid;
 }
+
+/**
+  Check if a linear address range is valid.
+
+  @param[in]  Cr0                 CR0 control register.
+  @param[in]  Cr3                 CR3 control register.
+  @param[in]  Cr4                 CR4 control register.
+  @param[in]  LinearAddressStart  Linear address start.
+  @param[in]  LinearAddressEnd    Linear address end.
+**/
+BOOLEAN
+IsLinearAddressRangeValid (
+  IN  UINTN              Cr0,
+  IN  UINTN              Cr3,
+  IN  UINTN              Cr4,
+  IN  UINTN              LinearAddressStart,
+  IN  UINTN              LinearAddressEnd
+  )
+{
+  //
+  // Check for valid input parameters
+  //
+  if (LinearAddressStart == 0 || LinearAddressEnd == 0 ||
+      LinearAddressStart > LinearAddressEnd) {
+    return FALSE;
+  }
+
+  //
+  // Validate all linear addresses within the given range
+  //
+  for (LinearAddressStart &= ~(SIZE_4KB - 1);
+       LinearAddressStart <= LinearAddressEnd;
+       LinearAddressStart += SIZE_4KB) {
+    if (!IsLinearAddressValid (Cr0, Cr3, Cr4, LinearAddressStart)) {
+      return FALSE;
+    }
+  }
+
+  return TRUE;
+}
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
index 1b51034c25..075f668290 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
@@ -346,5 +346,23 @@ IsLinearAddressValid (
   IN  UINTN              LinearAddress
   );
 
+/**
+  Check if a linear address range is valid.
+
+  @param[in]  Cr0                 CR0 control register.
+  @param[in]  Cr3                 CR3 control register.
+  @param[in]  Cr4                 CR4 control register.
+  @param[in]  LinearAddressStart  Linear address start.
+  @param[in]  LinearAddressEnd    Linear address end.
+**/
+BOOLEAN
+IsLinearAddressRangeValid (
+  IN  UINTN              Cr0,
+  IN  UINTN              Cr3,
+  IN  UINTN              Cr4,
+  IN  UINTN              LinearAddressStart,
+  IN  UINTN              LinearAddressEnd
+  );
+
 #endif
 
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
index 71d2d2f5d4..4d8c9b0a89 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
@@ -415,6 +415,8 @@ DumpStackContents (
   UINTN   Cr0;
   UINTN   Cr3;
   UINTN   Cr4;
+  UINTN   RspAddressStart;
+  UINTN   RspAddressEnd;
 
   //
   // Get current stack pointer
@@ -436,21 +438,29 @@ DumpStackContents (
   Cr3 = SystemContext.SystemContextX64->Cr3;
   Cr4 = SystemContext.SystemContextX64->Cr4;
 
+  //
+  // Calculate address range of the stack pointers
+  //
+  RspAddressStart = (UINTN)CurrentRsp;
+  RspAddressEnd =
+    RspAddressStart + (UINTN)UnwoundStacksCount * CPU_STACK_ALIGNMENT;
+
+  //
+  // Validate address range of stack pointers
+  //
+  if (!IsLinearAddressRangeValid (Cr0, Cr3, Cr4, RspAddressStart,
+                                  RspAddressEnd)) {
+    InternalPrintMessage ("%a: attempted to dereference an invalid stack "
+                          "pointer at 0x%016lx - 0x%016lx\n", __FUNCTION__,
+                          RspAddressStart, RspAddressEnd);
+    return;
+  }
+
   //
   // Dump out stack contents
   //
   InternalPrintMessage ("\nStack dump:\n");
   while (UnwoundStacksCount-- > 0) {
-    //
-    // Check for a valid stack pointer address
-    //
-    if (!IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)CurrentRsp) ||
-        !IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)CurrentRsp + 8)) {
-      InternalPrintMessage ("%a: attempted to dereference an invalid stack "
-                            "pointer at 0x%016lx\n", __FUNCTION__, CurrentRsp);
-      break;
-    }
-
     InternalPrintMessage (
       "0x%016lx: %016lx %016lx\n",
       CurrentRsp,
@@ -459,7 +469,7 @@ DumpStackContents (
       );
 
     //
-    // Point to next stack
+    // Point to next stack pointer
     //
     CurrentRsp += CPU_STACK_ALIGNMENT;
   }
@@ -571,8 +581,8 @@ DumpImageModuleNames (
     //
     // Check for a valid frame pointer
     //
-    if (!IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)Rbp + 8) ||
-        !IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)Rbp)) {
+    if (!IsLinearAddressRangeValid (Cr0, Cr3, Cr4, (UINTN)Rbp,
+                                    (UINTN)Rbp + 8)) {
       InternalPrintMessage ("%a: attempted to dereference an invalid frame "
                             "pointer at 0x%016lx\n", __FUNCTION__, Rbp);
       break;
@@ -722,8 +732,8 @@ DumpStacktrace (
     //
     // Check for valid frame pointer
     //
-    if (!IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)Rbp + 8) ||
-        !IsLinearAddressValid (Cr0, Cr3, Cr4, (UINTN)Rbp)) {
+    if (!IsLinearAddressRangeValid (Cr0, Cr3, Cr4, (UINTN)Rbp,
+                                    (UINTN)Rbp + 8)) {
       InternalPrintMessage ("%a: attempted to dereference an invalid frame "
                             "pointer at 0x%016lx\n", __FUNCTION__, Rbp);
       break;
-- 
2.14.3



^ permalink raw reply related	[flat|nested] 82+ messages in thread

* [RFC v5 8/8] UefiCpuPkg/CpuExceptionHandlerLib: Add early check in DumpStackContents
  2018-01-15  0:23     ` [RFC v5 0/8] " Paulo Alcantara
                         ` (6 preceding siblings ...)
  2018-01-15  0:23       ` [RFC v5 7/8] UefiCpuPkg/CpuExceptionHandlerLib: Validate memory address ranges Paulo Alcantara
@ 2018-01-15  0:23       ` Paulo Alcantara
  2018-01-17 12:57       ` [RFC v5 0/8] Stack trace support in X64 exception handling Yao, Jiewen
  8 siblings, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2018-01-15  0:23 UTC (permalink / raw)
  To: edk2-devel; +Cc: Paulo Alcantara, Eric Dong, Laszlo Ersek

Add an early check in DumpStackContens() to abort in case of no unwound
stacks.

Contributed-under: TianoCore Contribution Agreement 1.1
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
---
 UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c | 7 +++++++
 UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c  | 7 +++++++
 2 files changed, 14 insertions(+)

diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
index 31fbd4a164..ac3801f704 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c
@@ -805,6 +805,13 @@ DumpStackContents (
 {
   UINT32 CurrentEsp;
 
+  //
+  // Do nothing in case there wasn't any unwound stack.
+  //
+  if (UnwoundStacksCount == 0) {
+    return;
+  }
+
   //
   // Get current stack pointer
   //
diff --git a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
index 4d8c9b0a89..6c3bad01a6 100644
--- a/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
+++ b/UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
@@ -418,6 +418,13 @@ DumpStackContents (
   UINTN   RspAddressStart;
   UINTN   RspAddressEnd;
 
+  //
+  // Do nothing in case there wasn't any unwound stack.
+  //
+  if (UnwoundStacksCount == 0) {
+    return;
+  }
+
   //
   // Get current stack pointer
   //
-- 
2.14.3



^ permalink raw reply related	[flat|nested] 82+ messages in thread

* Re: [RFC v5 0/8] Stack trace support in X64 exception handling
  2018-01-15  0:23     ` [RFC v5 0/8] " Paulo Alcantara
                         ` (7 preceding siblings ...)
  2018-01-15  0:23       ` [RFC v5 8/8] UefiCpuPkg/CpuExceptionHandlerLib: Add early check in DumpStackContents Paulo Alcantara
@ 2018-01-17 12:57       ` Yao, Jiewen
  2018-01-17 22:48         ` Yao, Jiewen
                           ` (2 more replies)
  8 siblings, 3 replies; 82+ messages in thread
From: Yao, Jiewen @ 2018-01-17 12:57 UTC (permalink / raw)
  To: Paulo Alcantara, edk2-devel@lists.01.org
  Cc: Rick Bramley, Dong, Eric, Kimon Berlin, Andrew Fish,
	Diego Medaglia, Laszlo Ersek

Thanks Paulo.

The series looks really good. I give it thumb up. :-)

The 8 patches keep updating 4 files, so I squash them when I review. The comment below is for the final code, not for a specific patch.

1) CpuExceptionCommon.c: IsLinearAddressRangeValid().
  //
  // Check for valid input parameters
  //
  if (LinearAddressStart == 0 || LinearAddressEnd == 0 ||
      LinearAddressStart > LinearAddressEnd) {
    return FALSE;
  }

I think LinearAddressStart is a valid case. In BIOS, we do update IVT in 0 address when CSM is enabled.
I do not think we need exclude it here. If we enabled ZeroPointer protection, it can be excluded in page table parsing.

2) I found below logic appears in 2 functions - DumpImageModuleNames() and DumpStacktrace(). Is that possible to merge them?
We can calculate in DumpImageAndCpuContent() and use Eip as parameter. Just in case there is 3rd function need use EIP, it won't miss the calculation.
(It is a bug fix we did recently.)

  //
  // Set current EIP address
  //
  if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) &&
      ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0)) {
    //
    // The EIP in SystemContext could not be used
    // if it is page fault with I/D set.
    //
    Eip = *(UINT32 *)(UINTN)SystemContext.SystemContextIa32->Esp;
  } else {
    Eip = SystemContext.SystemContextIa32->Eip;
  }

3) I am a little surprised on PeCoffSearchImageBase() issue.

We have 4 PeCoffSearchImageBase() call in each arch. DumpImageModuleNames() calls twice and DumpStacktrace() calls twice.
Do you know which specific one triggers the zero address #PF issue?

  C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\ArchExceptionHandler.c(547):  ImageBase = PeCoffSearchImageBase (Eip);
  C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\ArchExceptionHandler.c(613):    ImageBase = PeCoffSearchImageBase (Eip);
  C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\ArchExceptionHandler.c(682):  ImageBase = PeCoffSearchImageBase (Eip);
  C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\ArchExceptionHandler.c(741):    ImageBase = PeCoffSearchImageBase (Eip);
  C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\ArchExceptionHandler.c(540):  ImageBase = PeCoffSearchImageBase (Rip);
  C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\ArchExceptionHandler.c(613):    ImageBase = PeCoffSearchImageBase (Rip);
  C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\ArchExceptionHandler.c(710):  ImageBase = PeCoffSearchImageBase (Rip);
  C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\ArchExceptionHandler.c(779):    ImageBase = PeCoffSearchImageBase (Rip);

The EIP from SystemContext seems good. I assume there is not a problem here.

  if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) &&
      ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0)) {
    //
    // The EIP in SystemContext could not be used
    // if it is page fault with I/D set.
    //
    Eip = *(UINT32 *)(UINTN)SystemContext.SystemContextIa32->Esp;
  } else {
    Eip = SystemContext.SystemContextIa32->Eip;
  }

  //
  // Get initial PE/COFF image base address from current EIP
  //
  ImageBase = PeCoffSearchImageBase (Eip);

But the EIP from stack frame is unknown and risky. Especially for the code in mode-switch, such as PEI->DXE, UEFI->CSM, AP wakeup->AP C code, SMM entrypoint->SMM C code.
Should we add a check for EIP here?

    //
    // Set EIP with return address from current stack frame
    //
    Eip = *(UINT32 *)((UINTN)Ebp + 4);

    //
    // If EIP is zero, then stop unwinding the stack
    //
    if (Eip == 0) {
      break;
    }

    //
    // Search for the respective PE/COFF image based on EIP
    //
    ImageBase = PeCoffSearchImageBase (Eip);

If you can help us do some more investigation on the root-cause and narrow down the issue, I will appreciate that.

We can decide how to fix once it is root-caused.

Maybe we just do a simple IsLogicalAddressValid () check before we call PeCoffSearchImageBase(). :-)


Thank you
Yao Jiewen

> -----Original Message-----
> From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of Paulo
> Alcantara
> Sent: Monday, January 15, 2018 8:23 AM
> To: edk2-devel@lists.01.org
> Cc: Rick Bramley <richard.bramley@hp.com>; Dong, Eric
> <eric.dong@intel.com>; Kimon Berlin <kimon.berlin@hp.com>; Andrew Fish
> <afish@apple.com>; Yao, Jiewen <jiewen.yao@intel.com>; Diego Medaglia
> <diego.meaglia@hp.com>; Laszlo Ersek <lersek@redhat.com>
> Subject: [edk2] [RFC v5 0/8] Stack trace support in X64 exception handling
> 
> Hi,
> 
> This series adds stack trace support during IA32 and X64 CPU exceptions.
> 
> Informations like back trace, stack contents and image module names
> (that were part of the call stack) will be dumped out.
> 
> The current limitation is that it relies on available frame pointers
> (GCC only) in order to successfully unwind the stack.
> 
> Jiewen,
> 
> Thank you very much for your time on this. I've applied the changes you
> suggested, as well as tested it on IA32 PAE paging mode - it worked as
> expected.
> 
> Other than that, I also tested the stack trace in SMM code by manually
> calling CpuBreakPoint() and then it broke with another exception
> (page fault). I didn't have much time to look into that, but what I've
> observed is that the page fault ocurred during the search of PE/COFF
> image base address (in PeCoffSearchImageBase). The function attempts to
> search for the image base from "Address" through 0, so any of those
> dereferenced addresses triggers the page fault.
> 
> Do you know how we could fix that issue? Perhaps introducing a
> AddressValidationLib (as Brian suggested previously) and use it within
> PeCoffSearchImageBase()?
> 
> I'd also like to thank Brian & Jeff for all the support!
> 
> Thanks
> Paulo
> 
> Repo:   https://github.com/pcacjr/edk2.git
> Branch: stacktrace_v5
> 
> Cc: Rick Bramley <richard.bramley@hp.com>
> Cc: Kimon Berlin <kimon.berlin@hp.com>
> Cc: Diego Medaglia <diego.meaglia@hp.com>
> Cc: Andrew Fish <afish@apple.com>
> Cc: Eric Dong <eric.dong@intel.com>
> Cc: Laszlo Ersek <lersek@redhat.com>
> Cc: Brian Johnson <brian.johnson@hpe.com>
> Cc: Jeff Fan <vanjeff_919@hotmail.com>
> Cc: Jiewen Yao <jiewen.yao@intel.com>
> Cc: Paulo Alcantara <paulo@hp.com>
> Contributed-under: TianoCore Contribution Agreement 1.1
> Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
> ---
> 
> v1 -> v2:
>   * Add IA32 arch support (GCC toolchain only)
>   * Replace hard-coded stack alignment value (16) with
>     CPU_STACK_ALIGNMENT.
>   * Check for proper stack and frame pointer alignments.
>   * Fix initialization of UnwoundStacksCount to 1.
>   * Move GetPdbFileName() to common code since it will be used by both
>     IA32 and X64 implementations.
> 
> v2 -> v3:
>   * Fixed wrong assumption about "RIP < ImageBase" to start searching
>     for another PE/COFF image. That is, RIP may point to lower and
>     higher addresses for any other PE/COFF images. Both IA32 & X64.
>     (Thanks Andrew & Jiewen)
>   * Fixed typo: unwond -> unwound. Both IA32 & X64. (Thanks Brian)
> 
> v3 -> v4:
>   * Validate all frame/stack pointer addresses before dereferencing them
>     as requested by Brian & Jiewen.
>   * Correctly print out IP addresses during the stack traces (by Jeff)
> 
> v4 -> v5:
>   * Fixed address calculations and improved code as suggested by Jiewen.
>   * Fixed parameter validation as suggested by Brian.
>   * Tested stack stack with IA32 PAE paging mode.
> 
> Paulo Alcantara (8):
>   UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
>   UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName()
>   UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support
>   UefiCpuPkg/CpuExceptionHandlerLib: Add helper to validate memory
>     addresses
>   UefiCpuPkg/CpuExceptionHandlerLib: Ensure valid frame/stack pointers
>   UefiCpuPkg/CpuExceptionHandlerLib: Correctly print IP addresses
>   UefiCpuPkg/CpuExceptionHandlerLib: Validate memory address ranges
>   UefiCpuPkg/CpuExceptionHandlerLib: Add early check in
>     DumpStackContents
> 
>  UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
> | 537 ++++++++++++++++++--
>  UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
> |  59 ++-
>  UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c |
> 483 +++++++++++++++++-
>  UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c  |
> 426 +++++++++++++++-
>  4 files changed, 1435 insertions(+), 70 deletions(-)
> 
> --
> 2.14.3
> 
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org
> https://lists.01.org/mailman/listinfo/edk2-devel


^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC v5 0/8] Stack trace support in X64 exception handling
  2018-01-17 12:57       ` [RFC v5 0/8] Stack trace support in X64 exception handling Yao, Jiewen
@ 2018-01-17 22:48         ` Yao, Jiewen
  2018-01-19  0:09           ` Paulo Alcantara
  2018-01-19  0:02         ` Paulo Alcantara
  2018-01-29 13:38         ` Paulo Alcantara
  2 siblings, 1 reply; 82+ messages in thread
From: Yao, Jiewen @ 2018-01-17 22:48 UTC (permalink / raw)
  To: Yao, Jiewen, Paulo Alcantara, edk2-devel@lists.01.org
  Cc: Rick Bramley, Dong, Eric, Kimon Berlin, Andrew Fish,
	Diego Medaglia, Laszlo Ersek

Hi Paulo
I have some more thought on #3 for your consideration.

Given the situation that we are doing heap guard feature, a *valid* EIP address might not be enough to guarantee the address is inside of an *executable* page.

I am thinking if we need check the non-executable bit (BIT63) as well for DumpImageModuleNames(). (No need for DumpStacktrace()).
We can add IsLogicalAddressExecutable() on top of IsLogicalAddressValid().


Also I do not object the idea to add check inside of PeCoffSearchImageBase().
I am thinking in another way - we can let *caller* to input a validation function for the PeCoffLib, instead of let PeCoffLib depend on another library.

For example:
PeCoffSearchImageBaseEx (Address, ValidateAddressFunc)

Thank you
Yao Jiewen


> -----Original Message-----
> From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of Yao,
> Jiewen
> Sent: Wednesday, January 17, 2018 8:57 PM
> To: Paulo Alcantara <paulo@paulo.ac>; edk2-devel@lists.01.org
> Cc: Rick Bramley <richard.bramley@hp.com>; Dong, Eric
> <eric.dong@intel.com>; Kimon Berlin <kimon.berlin@hp.com>; Andrew Fish
> <afish@apple.com>; Diego Medaglia <diego.meaglia@hp.com>; Laszlo Ersek
> <lersek@redhat.com>
> Subject: Re: [edk2] [RFC v5 0/8] Stack trace support in X64 exception handling
> 
> Thanks Paulo.
> 
> The series looks really good. I give it thumb up. :-)
> 
> The 8 patches keep updating 4 files, so I squash them when I review. The
> comment below is for the final code, not for a specific patch.
> 
> 1) CpuExceptionCommon.c: IsLinearAddressRangeValid().
>   //
>   // Check for valid input parameters
>   //
>   if (LinearAddressStart == 0 || LinearAddressEnd == 0 ||
>       LinearAddressStart > LinearAddressEnd) {
>     return FALSE;
>   }
> 
> I think LinearAddressStart is a valid case. In BIOS, we do update IVT in 0 address
> when CSM is enabled.
> I do not think we need exclude it here. If we enabled ZeroPointer protection, it
> can be excluded in page table parsing.
> 
> 2) I found below logic appears in 2 functions - DumpImageModuleNames() and
> DumpStacktrace(). Is that possible to merge them?
> We can calculate in DumpImageAndCpuContent() and use Eip as parameter. Just
> in case there is 3rd function need use EIP, it won't miss the calculation.
> (It is a bug fix we did recently.)
> 
>   //
>   // Set current EIP address
>   //
>   if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) &&
>       ((SystemContext.SystemContextIa32->ExceptionData &
> IA32_PF_EC_ID) != 0)) {
>     //
>     // The EIP in SystemContext could not be used
>     // if it is page fault with I/D set.
>     //
>     Eip = *(UINT32 *)(UINTN)SystemContext.SystemContextIa32->Esp;
>   } else {
>     Eip = SystemContext.SystemContextIa32->Eip;
>   }
> 
> 3) I am a little surprised on PeCoffSearchImageBase() issue.
> 
> We have 4 PeCoffSearchImageBase() call in each arch.
> DumpImageModuleNames() calls twice and DumpStacktrace() calls twice.
> Do you know which specific one triggers the zero address #PF issue?
> 
> 
> C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\Arch
> ExceptionHandler.c(547):  ImageBase = PeCoffSearchImageBase (Eip);
> 
> C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\Arch
> ExceptionHandler.c(613):    ImageBase = PeCoffSearchImageBase (Eip);
> 
> C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\Arch
> ExceptionHandler.c(682):  ImageBase = PeCoffSearchImageBase (Eip);
> 
> C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\Arch
> ExceptionHandler.c(741):    ImageBase = PeCoffSearchImageBase (Eip);
> 
> C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\Arch
> ExceptionHandler.c(540):  ImageBase = PeCoffSearchImageBase (Rip);
> 
> C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\Arch
> ExceptionHandler.c(613):    ImageBase = PeCoffSearchImageBase (Rip);
> 
> C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\Arch
> ExceptionHandler.c(710):  ImageBase = PeCoffSearchImageBase (Rip);
> 
> C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\Arch
> ExceptionHandler.c(779):    ImageBase = PeCoffSearchImageBase (Rip);
> 
> The EIP from SystemContext seems good. I assume there is not a problem here.
> 
>   if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) &&
>       ((SystemContext.SystemContextIa32->ExceptionData &
> IA32_PF_EC_ID) != 0)) {
>     //
>     // The EIP in SystemContext could not be used
>     // if it is page fault with I/D set.
>     //
>     Eip = *(UINT32 *)(UINTN)SystemContext.SystemContextIa32->Esp;
>   } else {
>     Eip = SystemContext.SystemContextIa32->Eip;
>   }
> 
>   //
>   // Get initial PE/COFF image base address from current EIP
>   //
>   ImageBase = PeCoffSearchImageBase (Eip);
> 
> But the EIP from stack frame is unknown and risky. Especially for the code in
> mode-switch, such as PEI->DXE, UEFI->CSM, AP wakeup->AP C code, SMM
> entrypoint->SMM C code.
> Should we add a check for EIP here?
> 
>     //
>     // Set EIP with return address from current stack frame
>     //
>     Eip = *(UINT32 *)((UINTN)Ebp + 4);
> 
>     //
>     // If EIP is zero, then stop unwinding the stack
>     //
>     if (Eip == 0) {
>       break;
>     }
> 
>     //
>     // Search for the respective PE/COFF image based on EIP
>     //
>     ImageBase = PeCoffSearchImageBase (Eip);
> 
> If you can help us do some more investigation on the root-cause and narrow
> down the issue, I will appreciate that.
> 
> We can decide how to fix once it is root-caused.
> 
> Maybe we just do a simple IsLogicalAddressValid () check before we call
> PeCoffSearchImageBase(). :-)
> 
> 
> Thank you
> Yao Jiewen
> 
> > -----Original Message-----
> > From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of
> Paulo
> > Alcantara
> > Sent: Monday, January 15, 2018 8:23 AM
> > To: edk2-devel@lists.01.org
> > Cc: Rick Bramley <richard.bramley@hp.com>; Dong, Eric
> > <eric.dong@intel.com>; Kimon Berlin <kimon.berlin@hp.com>; Andrew Fish
> > <afish@apple.com>; Yao, Jiewen <jiewen.yao@intel.com>; Diego Medaglia
> > <diego.meaglia@hp.com>; Laszlo Ersek <lersek@redhat.com>
> > Subject: [edk2] [RFC v5 0/8] Stack trace support in X64 exception handling
> >
> > Hi,
> >
> > This series adds stack trace support during IA32 and X64 CPU exceptions.
> >
> > Informations like back trace, stack contents and image module names
> > (that were part of the call stack) will be dumped out.
> >
> > The current limitation is that it relies on available frame pointers
> > (GCC only) in order to successfully unwind the stack.
> >
> > Jiewen,
> >
> > Thank you very much for your time on this. I've applied the changes you
> > suggested, as well as tested it on IA32 PAE paging mode - it worked as
> > expected.
> >
> > Other than that, I also tested the stack trace in SMM code by manually
> > calling CpuBreakPoint() and then it broke with another exception
> > (page fault). I didn't have much time to look into that, but what I've
> > observed is that the page fault ocurred during the search of PE/COFF
> > image base address (in PeCoffSearchImageBase). The function attempts to
> > search for the image base from "Address" through 0, so any of those
> > dereferenced addresses triggers the page fault.
> >
> > Do you know how we could fix that issue? Perhaps introducing a
> > AddressValidationLib (as Brian suggested previously) and use it within
> > PeCoffSearchImageBase()?
> >
> > I'd also like to thank Brian & Jeff for all the support!
> >
> > Thanks
> > Paulo
> >
> > Repo:   https://github.com/pcacjr/edk2.git
> > Branch: stacktrace_v5
> >
> > Cc: Rick Bramley <richard.bramley@hp.com>
> > Cc: Kimon Berlin <kimon.berlin@hp.com>
> > Cc: Diego Medaglia <diego.meaglia@hp.com>
> > Cc: Andrew Fish <afish@apple.com>
> > Cc: Eric Dong <eric.dong@intel.com>
> > Cc: Laszlo Ersek <lersek@redhat.com>
> > Cc: Brian Johnson <brian.johnson@hpe.com>
> > Cc: Jeff Fan <vanjeff_919@hotmail.com>
> > Cc: Jiewen Yao <jiewen.yao@intel.com>
> > Cc: Paulo Alcantara <paulo@hp.com>
> > Contributed-under: TianoCore Contribution Agreement 1.1
> > Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
> > ---
> >
> > v1 -> v2:
> >   * Add IA32 arch support (GCC toolchain only)
> >   * Replace hard-coded stack alignment value (16) with
> >     CPU_STACK_ALIGNMENT.
> >   * Check for proper stack and frame pointer alignments.
> >   * Fix initialization of UnwoundStacksCount to 1.
> >   * Move GetPdbFileName() to common code since it will be used by both
> >     IA32 and X64 implementations.
> >
> > v2 -> v3:
> >   * Fixed wrong assumption about "RIP < ImageBase" to start searching
> >     for another PE/COFF image. That is, RIP may point to lower and
> >     higher addresses for any other PE/COFF images. Both IA32 & X64.
> >     (Thanks Andrew & Jiewen)
> >   * Fixed typo: unwond -> unwound. Both IA32 & X64. (Thanks Brian)
> >
> > v3 -> v4:
> >   * Validate all frame/stack pointer addresses before dereferencing them
> >     as requested by Brian & Jiewen.
> >   * Correctly print out IP addresses during the stack traces (by Jeff)
> >
> > v4 -> v5:
> >   * Fixed address calculations and improved code as suggested by Jiewen.
> >   * Fixed parameter validation as suggested by Brian.
> >   * Tested stack stack with IA32 PAE paging mode.
> >
> > Paulo Alcantara (8):
> >   UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
> >   UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName()
> >   UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support
> >   UefiCpuPkg/CpuExceptionHandlerLib: Add helper to validate memory
> >     addresses
> >   UefiCpuPkg/CpuExceptionHandlerLib: Ensure valid frame/stack pointers
> >   UefiCpuPkg/CpuExceptionHandlerLib: Correctly print IP addresses
> >   UefiCpuPkg/CpuExceptionHandlerLib: Validate memory address ranges
> >   UefiCpuPkg/CpuExceptionHandlerLib: Add early check in
> >     DumpStackContents
> >
> >  UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
> > | 537 ++++++++++++++++++--
> >  UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
> > |  59 ++-
> >  UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c |
> > 483 +++++++++++++++++-
> >  UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> |
> > 426 +++++++++++++++-
> >  4 files changed, 1435 insertions(+), 70 deletions(-)
> >
> > --
> > 2.14.3
> >
> > _______________________________________________
> > edk2-devel mailing list
> > edk2-devel@lists.01.org
> > https://lists.01.org/mailman/listinfo/edk2-devel
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org
> https://lists.01.org/mailman/listinfo/edk2-devel


^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC v5 0/8] Stack trace support in X64 exception handling
  2018-01-17 12:57       ` [RFC v5 0/8] Stack trace support in X64 exception handling Yao, Jiewen
  2018-01-17 22:48         ` Yao, Jiewen
@ 2018-01-19  0:02         ` Paulo Alcantara
  2018-01-19  0:15           ` Paulo Alcantara
  2018-01-29 13:38         ` Paulo Alcantara
  2 siblings, 1 reply; 82+ messages in thread
From: Paulo Alcantara @ 2018-01-19  0:02 UTC (permalink / raw)
  To: Yao, Jiewen, edk2-devel@lists.01.org
  Cc: Rick Bramley, Dong, Eric, Kimon Berlin, Andrew Fish,
	Diego Medaglia, Laszlo Ersek

"Yao, Jiewen" <jiewen.yao@intel.com> writes:

Hi Jiewen,

Thank you very much for teh review! My comments below:

> The 8 patches keep updating 4 files, so I squash them when I review. The comment below is for the final code, not for a specific patch.
>
> 1) CpuExceptionCommon.c: IsLinearAddressRangeValid().
>   //
>   // Check for valid input parameters
>   //
>   if (LinearAddressStart == 0 || LinearAddressEnd == 0 ||
>       LinearAddressStart > LinearAddressEnd) {
>     return FALSE;
>   }
>
> I think LinearAddressStart is a valid case. In BIOS, we do update IVT in 0 address when CSM is enabled.
> I do not think we need exclude it here. If we enabled ZeroPointer
> protection, it can be excluded in page table parsing.

OK - didn't know about it. I'll remove that check.

> 2) I found below logic appears in 2 functions - DumpImageModuleNames() and DumpStacktrace(). Is that possible to merge them?
> We can calculate in DumpImageAndCpuContent() and use Eip as parameter. Just in case there is 3rd function need use EIP, it won't miss the calculation.
> (It is a bug fix we did recently.)
>
>   //
>   // Set current EIP address
>   //
>   if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) &&
>       ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0)) {
>     //
>     // The EIP in SystemContext could not be used
>     // if it is page fault with I/D set.
>     //
>     Eip = *(UINT32 *)(UINTN)SystemContext.SystemContextIa32->Esp;
>   } else {
>     Eip = SystemContext.SystemContextIa32->Eip;
>   }

Yes - I think it's possible and a good improvement. I'll do it.

> 3) I am a little surprised on PeCoffSearchImageBase() issue.
>
> We have 4 PeCoffSearchImageBase() call in each arch. DumpImageModuleNames() calls twice and DumpStacktrace() calls twice.
> Do you know which specific one triggers the zero address #PF issue?
>
>   C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\ArchExceptionHandler.c(547):  ImageBase = PeCoffSearchImageBase (Eip);
>   C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\ArchExceptionHandler.c(613):    ImageBase = PeCoffSearchImageBase (Eip);
>   C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\ArchExceptionHandler.c(682):  ImageBase = PeCoffSearchImageBase (Eip);
>   C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\ArchExceptionHandler.c(741):    ImageBase = PeCoffSearchImageBase (Eip);
>   C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\ArchExceptionHandler.c(540):  ImageBase = PeCoffSearchImageBase (Rip);
>   C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\ArchExceptionHandler.c(613):    ImageBase = PeCoffSearchImageBase (Rip);
>   C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\ArchExceptionHandler.c(710):  ImageBase = PeCoffSearchImageBase (Rip);
>   C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\ArchExceptionHandler.c(779):    ImageBase = PeCoffSearchImageBase (Rip);
>

When I saw the #PF when testing stack trace in SMM, I was running out of
time and I just saved the log file with the trace. I'm attaching the
log for you, but I'm still going to look into that issue when time
permits.

I haven't looked on how the SMI entry is set up, but don't you think
that we should do something similiar like in DXE and PEI phases with
"push $0" and help the debugger or tracer know when stop unwinding the
stack -- in DEBUG mode, at least? Of course, if that's really possible.

> The EIP from SystemContext seems good. I assume there is not a problem here.
>
>   if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) &&
>       ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0)) {
>     //
>     // The EIP in SystemContext could not be used
>     // if it is page fault with I/D set.
>     //
>     Eip = *(UINT32 *)(UINTN)SystemContext.SystemContextIa32->Esp;
>   } else {
>     Eip = SystemContext.SystemContextIa32->Eip;
>   }

Possibly. I'll add some debug and see what the Eip looks like at this
point.

>   //
>   // Get initial PE/COFF image base address from current EIP
>   //
>   ImageBase = PeCoffSearchImageBase (Eip);
>
> But the EIP from stack frame is unknown and risky. Especially for the code in mode-switch, such as PEI->DXE, UEFI->CSM, AP wakeup->AP C code, SMM entrypoint->SMM C code.
> Should we add a check for EIP here?
>
>     //
>     // Set EIP with return address from current stack frame
>     //
>     Eip = *(UINT32 *)((UINTN)Ebp + 4);
>
>     //
>     // If EIP is zero, then stop unwinding the stack
>     //
>     if (Eip == 0) {
>       break;
>     }
>
>     //
>     // Search for the respective PE/COFF image based on EIP
>     //
>     ImageBase = PeCoffSearchImageBase (Eip);

I think so. There is no guarantee that the return address (Eip) will be
valid even if we're handling a valid frame pointer. It might get
corrupted at some point. Thanks for pointing it out! I'll validate
them.

> If you can help us do some more investigation on the root-cause and narrow down the issue, I will appreciate that.
>
> We can decide how to fix once it is root-caused.

Sure. I will.

Thanks for the comments! I learnt a lot of with them :-)

Paulo

>
>> -----Original Message-----
>> From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of Paulo
>> Alcantara
>> Sent: Monday, January 15, 2018 8:23 AM
>> To: edk2-devel@lists.01.org
>> Cc: Rick Bramley <richard.bramley@hp.com>; Dong, Eric
>> <eric.dong@intel.com>; Kimon Berlin <kimon.berlin@hp.com>; Andrew Fish
>> <afish@apple.com>; Yao, Jiewen <jiewen.yao@intel.com>; Diego Medaglia
>> <diego.meaglia@hp.com>; Laszlo Ersek <lersek@redhat.com>
>> Subject: [edk2] [RFC v5 0/8] Stack trace support in X64 exception handling
>> 
>> Hi,
>> 
>> This series adds stack trace support during IA32 and X64 CPU exceptions.
>> 
>> Informations like back trace, stack contents and image module names
>> (that were part of the call stack) will be dumped out.
>> 
>> The current limitation is that it relies on available frame pointers
>> (GCC only) in order to successfully unwind the stack.
>> 
>> Jiewen,
>> 
>> Thank you very much for your time on this. I've applied the changes you
>> suggested, as well as tested it on IA32 PAE paging mode - it worked as
>> expected.
>> 
>> Other than that, I also tested the stack trace in SMM code by manually
>> calling CpuBreakPoint() and then it broke with another exception
>> (page fault). I didn't have much time to look into that, but what I've
>> observed is that the page fault ocurred during the search of PE/COFF
>> image base address (in PeCoffSearchImageBase). The function attempts to
>> search for the image base from "Address" through 0, so any of those
>> dereferenced addresses triggers the page fault.
>> 
>> Do you know how we could fix that issue? Perhaps introducing a
>> AddressValidationLib (as Brian suggested previously) and use it within
>> PeCoffSearchImageBase()?
>> 
>> I'd also like to thank Brian & Jeff for all the support!
>> 
>> Thanks
>> Paulo
>> 
>> Repo:   https://github.com/pcacjr/edk2.git
>> Branch: stacktrace_v5
>> 
>> Cc: Rick Bramley <richard.bramley@hp.com>
>> Cc: Kimon Berlin <kimon.berlin@hp.com>
>> Cc: Diego Medaglia <diego.meaglia@hp.com>
>> Cc: Andrew Fish <afish@apple.com>
>> Cc: Eric Dong <eric.dong@intel.com>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Cc: Brian Johnson <brian.johnson@hpe.com>
>> Cc: Jeff Fan <vanjeff_919@hotmail.com>
>> Cc: Jiewen Yao <jiewen.yao@intel.com>
>> Cc: Paulo Alcantara <paulo@hp.com>
>> Contributed-under: TianoCore Contribution Agreement 1.1
>> Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
>> ---
>> 
>> v1 -> v2:
>>   * Add IA32 arch support (GCC toolchain only)
>>   * Replace hard-coded stack alignment value (16) with
>>     CPU_STACK_ALIGNMENT.
>>   * Check for proper stack and frame pointer alignments.
>>   * Fix initialization of UnwoundStacksCount to 1.
>>   * Move GetPdbFileName() to common code since it will be used by both
>>     IA32 and X64 implementations.
>> 
>> v2 -> v3:
>>   * Fixed wrong assumption about "RIP < ImageBase" to start searching
>>     for another PE/COFF image. That is, RIP may point to lower and
>>     higher addresses for any other PE/COFF images. Both IA32 & X64.
>>     (Thanks Andrew & Jiewen)
>>   * Fixed typo: unwond -> unwound. Both IA32 & X64. (Thanks Brian)
>> 
>> v3 -> v4:
>>   * Validate all frame/stack pointer addresses before dereferencing them
>>     as requested by Brian & Jiewen.
>>   * Correctly print out IP addresses during the stack traces (by Jeff)
>> 
>> v4 -> v5:
>>   * Fixed address calculations and improved code as suggested by Jiewen.
>>   * Fixed parameter validation as suggested by Brian.
>>   * Tested stack stack with IA32 PAE paging mode.
>> 
>> Paulo Alcantara (8):
>>   UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
>>   UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName()
>>   UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support
>>   UefiCpuPkg/CpuExceptionHandlerLib: Add helper to validate memory
>>     addresses
>>   UefiCpuPkg/CpuExceptionHandlerLib: Ensure valid frame/stack pointers
>>   UefiCpuPkg/CpuExceptionHandlerLib: Correctly print IP addresses
>>   UefiCpuPkg/CpuExceptionHandlerLib: Validate memory address ranges
>>   UefiCpuPkg/CpuExceptionHandlerLib: Add early check in
>>     DumpStackContents
>> 
>>  UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
>> | 537 ++++++++++++++++++--
>>  UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
>> |  59 ++-
>>  UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c |
>> 483 +++++++++++++++++-
>>  UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c  |
>> 426 +++++++++++++++-
>>  4 files changed, 1435 insertions(+), 70 deletions(-)
>> 
>> --
>> 2.14.3
>> 
>> _______________________________________________
>> edk2-devel mailing list
>> edk2-devel@lists.01.org
>> https://lists.01.org/mailman/listinfo/edk2-devel


^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC v5 0/8] Stack trace support in X64 exception handling
  2018-01-17 22:48         ` Yao, Jiewen
@ 2018-01-19  0:09           ` Paulo Alcantara
  0 siblings, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2018-01-19  0:09 UTC (permalink / raw)
  To: Yao, Jiewen, Yao, Jiewen, edk2-devel@lists.01.org
  Cc: Rick Bramley, Dong, Eric, Kimon Berlin, Andrew Fish,
	Diego Medaglia, Laszlo Ersek

Hi Jiewen,

"Yao, Jiewen" <jiewen.yao@intel.com> writes:

> I have some more thought on #3 for your consideration.
>
> Given the situation that we are doing heap guard feature, a *valid*
> EIP address might not be enough to guarantee the address is inside of
> an *executable* page.

OK.

>
> I am thinking if we need check the non-executable bit (BIT63) as well for DumpImageModuleNames(). (No need for DumpStacktrace()).
> We can add IsLogicalAddressExecutable() on top of
> IsLogicalAddressValid().

OK. I can do that.

> Also I do not object the idea to add check inside of PeCoffSearchImageBase().
> I am thinking in another way - we can let *caller* to input a validation function for the PeCoffLib, instead of let PeCoffLib depend on another library.
>
> For example:
> PeCoffSearchImageBaseEx (Address, ValidateAddressFunc)

Looks good to me! Whether or not we're making it into a external library
in the future, we should at least get it working and well tested for the
stack trace support.

Thanks!
Paulo

>> -----Original Message-----
>> From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of Yao,
>> Jiewen
>> Sent: Wednesday, January 17, 2018 8:57 PM
>> To: Paulo Alcantara <paulo@paulo.ac>; edk2-devel@lists.01.org
>> Cc: Rick Bramley <richard.bramley@hp.com>; Dong, Eric
>> <eric.dong@intel.com>; Kimon Berlin <kimon.berlin@hp.com>; Andrew Fish
>> <afish@apple.com>; Diego Medaglia <diego.meaglia@hp.com>; Laszlo Ersek
>> <lersek@redhat.com>
>> Subject: Re: [edk2] [RFC v5 0/8] Stack trace support in X64 exception handling
>> 
>> Thanks Paulo.
>> 
>> The series looks really good. I give it thumb up. :-)
>> 
>> The 8 patches keep updating 4 files, so I squash them when I review. The
>> comment below is for the final code, not for a specific patch.
>> 
>> 1) CpuExceptionCommon.c: IsLinearAddressRangeValid().
>>   //
>>   // Check for valid input parameters
>>   //
>>   if (LinearAddressStart == 0 || LinearAddressEnd == 0 ||
>>       LinearAddressStart > LinearAddressEnd) {
>>     return FALSE;
>>   }
>> 
>> I think LinearAddressStart is a valid case. In BIOS, we do update IVT in 0 address
>> when CSM is enabled.
>> I do not think we need exclude it here. If we enabled ZeroPointer protection, it
>> can be excluded in page table parsing.
>> 
>> 2) I found below logic appears in 2 functions - DumpImageModuleNames() and
>> DumpStacktrace(). Is that possible to merge them?
>> We can calculate in DumpImageAndCpuContent() and use Eip as parameter. Just
>> in case there is 3rd function need use EIP, it won't miss the calculation.
>> (It is a bug fix we did recently.)
>> 
>>   //
>>   // Set current EIP address
>>   //
>>   if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) &&
>>       ((SystemContext.SystemContextIa32->ExceptionData &
>> IA32_PF_EC_ID) != 0)) {
>>     //
>>     // The EIP in SystemContext could not be used
>>     // if it is page fault with I/D set.
>>     //
>>     Eip = *(UINT32 *)(UINTN)SystemContext.SystemContextIa32->Esp;
>>   } else {
>>     Eip = SystemContext.SystemContextIa32->Eip;
>>   }
>> 
>> 3) I am a little surprised on PeCoffSearchImageBase() issue.
>> 
>> We have 4 PeCoffSearchImageBase() call in each arch.
>> DumpImageModuleNames() calls twice and DumpStacktrace() calls twice.
>> Do you know which specific one triggers the zero address #PF issue?
>> 
>> 
>> C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\Arch
>> ExceptionHandler.c(547):  ImageBase = PeCoffSearchImageBase (Eip);
>> 
>> C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\Arch
>> ExceptionHandler.c(613):    ImageBase = PeCoffSearchImageBase (Eip);
>> 
>> C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\Arch
>> ExceptionHandler.c(682):  ImageBase = PeCoffSearchImageBase (Eip);
>> 
>> C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\Arch
>> ExceptionHandler.c(741):    ImageBase = PeCoffSearchImageBase (Eip);
>> 
>> C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\Arch
>> ExceptionHandler.c(540):  ImageBase = PeCoffSearchImageBase (Rip);
>> 
>> C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\Arch
>> ExceptionHandler.c(613):    ImageBase = PeCoffSearchImageBase (Rip);
>> 
>> C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\Arch
>> ExceptionHandler.c(710):  ImageBase = PeCoffSearchImageBase (Rip);
>> 
>> C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\Arch
>> ExceptionHandler.c(779):    ImageBase = PeCoffSearchImageBase (Rip);
>> 
>> The EIP from SystemContext seems good. I assume there is not a problem here.
>> 
>>   if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) &&
>>       ((SystemContext.SystemContextIa32->ExceptionData &
>> IA32_PF_EC_ID) != 0)) {
>>     //
>>     // The EIP in SystemContext could not be used
>>     // if it is page fault with I/D set.
>>     //
>>     Eip = *(UINT32 *)(UINTN)SystemContext.SystemContextIa32->Esp;
>>   } else {
>>     Eip = SystemContext.SystemContextIa32->Eip;
>>   }
>> 
>>   //
>>   // Get initial PE/COFF image base address from current EIP
>>   //
>>   ImageBase = PeCoffSearchImageBase (Eip);
>> 
>> But the EIP from stack frame is unknown and risky. Especially for the code in
>> mode-switch, such as PEI->DXE, UEFI->CSM, AP wakeup->AP C code, SMM
>> entrypoint->SMM C code.
>> Should we add a check for EIP here?
>> 
>>     //
>>     // Set EIP with return address from current stack frame
>>     //
>>     Eip = *(UINT32 *)((UINTN)Ebp + 4);
>> 
>>     //
>>     // If EIP is zero, then stop unwinding the stack
>>     //
>>     if (Eip == 0) {
>>       break;
>>     }
>> 
>>     //
>>     // Search for the respective PE/COFF image based on EIP
>>     //
>>     ImageBase = PeCoffSearchImageBase (Eip);
>> 
>> If you can help us do some more investigation on the root-cause and narrow
>> down the issue, I will appreciate that.
>> 
>> We can decide how to fix once it is root-caused.
>> 
>> Maybe we just do a simple IsLogicalAddressValid () check before we call
>> PeCoffSearchImageBase(). :-)
>> 
>> 
>> Thank you
>> Yao Jiewen
>> 
>> > -----Original Message-----
>> > From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of
>> Paulo
>> > Alcantara
>> > Sent: Monday, January 15, 2018 8:23 AM
>> > To: edk2-devel@lists.01.org
>> > Cc: Rick Bramley <richard.bramley@hp.com>; Dong, Eric
>> > <eric.dong@intel.com>; Kimon Berlin <kimon.berlin@hp.com>; Andrew Fish
>> > <afish@apple.com>; Yao, Jiewen <jiewen.yao@intel.com>; Diego Medaglia
>> > <diego.meaglia@hp.com>; Laszlo Ersek <lersek@redhat.com>
>> > Subject: [edk2] [RFC v5 0/8] Stack trace support in X64 exception handling
>> >
>> > Hi,
>> >
>> > This series adds stack trace support during IA32 and X64 CPU exceptions.
>> >
>> > Informations like back trace, stack contents and image module names
>> > (that were part of the call stack) will be dumped out.
>> >
>> > The current limitation is that it relies on available frame pointers
>> > (GCC only) in order to successfully unwind the stack.
>> >
>> > Jiewen,
>> >
>> > Thank you very much for your time on this. I've applied the changes you
>> > suggested, as well as tested it on IA32 PAE paging mode - it worked as
>> > expected.
>> >
>> > Other than that, I also tested the stack trace in SMM code by manually
>> > calling CpuBreakPoint() and then it broke with another exception
>> > (page fault). I didn't have much time to look into that, but what I've
>> > observed is that the page fault ocurred during the search of PE/COFF
>> > image base address (in PeCoffSearchImageBase). The function attempts to
>> > search for the image base from "Address" through 0, so any of those
>> > dereferenced addresses triggers the page fault.
>> >
>> > Do you know how we could fix that issue? Perhaps introducing a
>> > AddressValidationLib (as Brian suggested previously) and use it within
>> > PeCoffSearchImageBase()?
>> >
>> > I'd also like to thank Brian & Jeff for all the support!
>> >
>> > Thanks
>> > Paulo
>> >
>> > Repo:   https://github.com/pcacjr/edk2.git
>> > Branch: stacktrace_v5
>> >
>> > Cc: Rick Bramley <richard.bramley@hp.com>
>> > Cc: Kimon Berlin <kimon.berlin@hp.com>
>> > Cc: Diego Medaglia <diego.meaglia@hp.com>
>> > Cc: Andrew Fish <afish@apple.com>
>> > Cc: Eric Dong <eric.dong@intel.com>
>> > Cc: Laszlo Ersek <lersek@redhat.com>
>> > Cc: Brian Johnson <brian.johnson@hpe.com>
>> > Cc: Jeff Fan <vanjeff_919@hotmail.com>
>> > Cc: Jiewen Yao <jiewen.yao@intel.com>
>> > Cc: Paulo Alcantara <paulo@hp.com>
>> > Contributed-under: TianoCore Contribution Agreement 1.1
>> > Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
>> > ---
>> >
>> > v1 -> v2:
>> >   * Add IA32 arch support (GCC toolchain only)
>> >   * Replace hard-coded stack alignment value (16) with
>> >     CPU_STACK_ALIGNMENT.
>> >   * Check for proper stack and frame pointer alignments.
>> >   * Fix initialization of UnwoundStacksCount to 1.
>> >   * Move GetPdbFileName() to common code since it will be used by both
>> >     IA32 and X64 implementations.
>> >
>> > v2 -> v3:
>> >   * Fixed wrong assumption about "RIP < ImageBase" to start searching
>> >     for another PE/COFF image. That is, RIP may point to lower and
>> >     higher addresses for any other PE/COFF images. Both IA32 & X64.
>> >     (Thanks Andrew & Jiewen)
>> >   * Fixed typo: unwond -> unwound. Both IA32 & X64. (Thanks Brian)
>> >
>> > v3 -> v4:
>> >   * Validate all frame/stack pointer addresses before dereferencing them
>> >     as requested by Brian & Jiewen.
>> >   * Correctly print out IP addresses during the stack traces (by Jeff)
>> >
>> > v4 -> v5:
>> >   * Fixed address calculations and improved code as suggested by Jiewen.
>> >   * Fixed parameter validation as suggested by Brian.
>> >   * Tested stack stack with IA32 PAE paging mode.
>> >
>> > Paulo Alcantara (8):
>> >   UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
>> >   UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName()
>> >   UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support
>> >   UefiCpuPkg/CpuExceptionHandlerLib: Add helper to validate memory
>> >     addresses
>> >   UefiCpuPkg/CpuExceptionHandlerLib: Ensure valid frame/stack pointers
>> >   UefiCpuPkg/CpuExceptionHandlerLib: Correctly print IP addresses
>> >   UefiCpuPkg/CpuExceptionHandlerLib: Validate memory address ranges
>> >   UefiCpuPkg/CpuExceptionHandlerLib: Add early check in
>> >     DumpStackContents
>> >
>> >  UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
>> > | 537 ++++++++++++++++++--
>> >  UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
>> > |  59 ++-
>> >  UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c |
>> > 483 +++++++++++++++++-
>> >  UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
>> |
>> > 426 +++++++++++++++-
>> >  4 files changed, 1435 insertions(+), 70 deletions(-)
>> >
>> > --
>> > 2.14.3
>> >
>> > _______________________________________________
>> > edk2-devel mailing list
>> > edk2-devel@lists.01.org
>> > https://lists.01.org/mailman/listinfo/edk2-devel
>> _______________________________________________
>> edk2-devel mailing list
>> edk2-devel@lists.01.org
>> https://lists.01.org/mailman/listinfo/edk2-devel


^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC v5 0/8] Stack trace support in X64 exception handling
  2018-01-19  0:02         ` Paulo Alcantara
@ 2018-01-19  0:15           ` Paulo Alcantara
  0 siblings, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2018-01-19  0:15 UTC (permalink / raw)
  To: Yao, Jiewen, edk2-devel@lists.01.org
  Cc: Rick Bramley, Dong, Eric, Kimon Berlin, Andrew Fish,
	Diego Medaglia, Laszlo Ersek

[-- Attachment #1: Type: text/plain, Size: 6658 bytes --]

Paulo Alcantara <paulo@paulo.ac> writes:

>> 3) I am a little surprised on PeCoffSearchImageBase() issue.
>>
>> We have 4 PeCoffSearchImageBase() call in each arch. DumpImageModuleNames() calls twice and DumpStacktrace() calls twice.
>> Do you know which specific one triggers the zero address #PF issue?
>>
>>   C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\ArchExceptionHandler.c(547):  ImageBase = PeCoffSearchImageBase (Eip);
>>   C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\ArchExceptionHandler.c(613):    ImageBase = PeCoffSearchImageBase (Eip);
>>   C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\ArchExceptionHandler.c(682):  ImageBase = PeCoffSearchImageBase (Eip);
>>   C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\ArchExceptionHandler.c(741):    ImageBase = PeCoffSearchImageBase (Eip);
>>   C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\ArchExceptionHandler.c(540):  ImageBase = PeCoffSearchImageBase (Rip);
>>   C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\ArchExceptionHandler.c(613):    ImageBase = PeCoffSearchImageBase (Rip);
>>   C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\ArchExceptionHandler.c(710):  ImageBase = PeCoffSearchImageBase (Rip);
>>   C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\ArchExceptionHandler.c(779):    ImageBase = PeCoffSearchImageBase (Rip);
>>
>
> When I saw the #PF when testing stack trace in SMM, I was running out of
> time and I just saved the log file with the trace. I'm attaching the
> log for you, but I'm still going to look into that issue when time
> permits.

Forgot to attach the log file. Done. :-)

Paulo

>>> -----Original Message-----
>>> From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of Paulo
>>> Alcantara
>>> Sent: Monday, January 15, 2018 8:23 AM
>>> To: edk2-devel@lists.01.org
>>> Cc: Rick Bramley <richard.bramley@hp.com>; Dong, Eric
>>> <eric.dong@intel.com>; Kimon Berlin <kimon.berlin@hp.com>; Andrew Fish
>>> <afish@apple.com>; Yao, Jiewen <jiewen.yao@intel.com>; Diego Medaglia
>>> <diego.meaglia@hp.com>; Laszlo Ersek <lersek@redhat.com>
>>> Subject: [edk2] [RFC v5 0/8] Stack trace support in X64 exception handling
>>> 
>>> Hi,
>>> 
>>> This series adds stack trace support during IA32 and X64 CPU exceptions.
>>> 
>>> Informations like back trace, stack contents and image module names
>>> (that were part of the call stack) will be dumped out.
>>> 
>>> The current limitation is that it relies on available frame pointers
>>> (GCC only) in order to successfully unwind the stack.
>>> 
>>> Jiewen,
>>> 
>>> Thank you very much for your time on this. I've applied the changes you
>>> suggested, as well as tested it on IA32 PAE paging mode - it worked as
>>> expected.
>>> 
>>> Other than that, I also tested the stack trace in SMM code by manually
>>> calling CpuBreakPoint() and then it broke with another exception
>>> (page fault). I didn't have much time to look into that, but what I've
>>> observed is that the page fault ocurred during the search of PE/COFF
>>> image base address (in PeCoffSearchImageBase). The function attempts to
>>> search for the image base from "Address" through 0, so any of those
>>> dereferenced addresses triggers the page fault.
>>> 
>>> Do you know how we could fix that issue? Perhaps introducing a
>>> AddressValidationLib (as Brian suggested previously) and use it within
>>> PeCoffSearchImageBase()?
>>> 
>>> I'd also like to thank Brian & Jeff for all the support!
>>> 
>>> Thanks
>>> Paulo
>>> 
>>> Repo:   https://github.com/pcacjr/edk2.git
>>> Branch: stacktrace_v5
>>> 
>>> Cc: Rick Bramley <richard.bramley@hp.com>
>>> Cc: Kimon Berlin <kimon.berlin@hp.com>
>>> Cc: Diego Medaglia <diego.meaglia@hp.com>
>>> Cc: Andrew Fish <afish@apple.com>
>>> Cc: Eric Dong <eric.dong@intel.com>
>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>> Cc: Brian Johnson <brian.johnson@hpe.com>
>>> Cc: Jeff Fan <vanjeff_919@hotmail.com>
>>> Cc: Jiewen Yao <jiewen.yao@intel.com>
>>> Cc: Paulo Alcantara <paulo@hp.com>
>>> Contributed-under: TianoCore Contribution Agreement 1.1
>>> Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
>>> ---
>>> 
>>> v1 -> v2:
>>>   * Add IA32 arch support (GCC toolchain only)
>>>   * Replace hard-coded stack alignment value (16) with
>>>     CPU_STACK_ALIGNMENT.
>>>   * Check for proper stack and frame pointer alignments.
>>>   * Fix initialization of UnwoundStacksCount to 1.
>>>   * Move GetPdbFileName() to common code since it will be used by both
>>>     IA32 and X64 implementations.
>>> 
>>> v2 -> v3:
>>>   * Fixed wrong assumption about "RIP < ImageBase" to start searching
>>>     for another PE/COFF image. That is, RIP may point to lower and
>>>     higher addresses for any other PE/COFF images. Both IA32 & X64.
>>>     (Thanks Andrew & Jiewen)
>>>   * Fixed typo: unwond -> unwound. Both IA32 & X64. (Thanks Brian)
>>> 
>>> v3 -> v4:
>>>   * Validate all frame/stack pointer addresses before dereferencing them
>>>     as requested by Brian & Jiewen.
>>>   * Correctly print out IP addresses during the stack traces (by Jeff)
>>> 
>>> v4 -> v5:
>>>   * Fixed address calculations and improved code as suggested by Jiewen.
>>>   * Fixed parameter validation as suggested by Brian.
>>>   * Tested stack stack with IA32 PAE paging mode.
>>> 
>>> Paulo Alcantara (8):
>>>   UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
>>>   UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName()
>>>   UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support
>>>   UefiCpuPkg/CpuExceptionHandlerLib: Add helper to validate memory
>>>     addresses
>>>   UefiCpuPkg/CpuExceptionHandlerLib: Ensure valid frame/stack pointers
>>>   UefiCpuPkg/CpuExceptionHandlerLib: Correctly print IP addresses
>>>   UefiCpuPkg/CpuExceptionHandlerLib: Validate memory address ranges
>>>   UefiCpuPkg/CpuExceptionHandlerLib: Add early check in
>>>     DumpStackContents
>>> 
>>>  UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
>>> | 537 ++++++++++++++++++--
>>>  UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
>>> |  59 ++-
>>>  UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c |
>>> 483 +++++++++++++++++-
>>>  UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c  |
>>> 426 +++++++++++++++-
>>>  4 files changed, 1435 insertions(+), 70 deletions(-)
>>> 
>>> --
>>> 2.14.3
>>> 
>>> _______________________________________________
>>> edk2-devel mailing list
>>> edk2-devel@lists.01.org
>>> https://lists.01.org/mailman/listinfo/edk2-devel

[-- Attachment #2: smm_stacktrace.txt --]
[-- Type: text/plain, Size: 3131 bytes --]

!!!! X64 Exception Type - 03(#BP - Breakpoint)  CPU Apic ID - 00000000 !!!!
RIP  - 000000007FF48151, CS  - 0000000000000038, RFLAGS - 0000000000000046
RAX  - 0000000000000000, RCX - 000000007FEBB020, RDX - 0000000000040000
RBX  - 0000000000000000, RSP - 000000007FF7B760, RBP - 000000007FF7B760
RSI  - 000000007FF44018, RDI - 000000007FEBB018
R8   - 00000000000000FF, R9  - 0000000000048400, R10 - 00000000000000C9
R11  - 0000000000000069, R12 - 0000000000000000, R13 - 0000000000000000
R14  - 0000000000000000, R15 - 0000000000000000
DS   - 0000000000000020, ES  - 0000000000000020, FS  - 0000000000000020
GS   - 0000000000000020, SS  - 0000000000000020
CR0  - 0000000080010033, CR2 - 0000000000000000, CR3 - 000000007FF6C000
CR4  - 0000000000000668, CR8 - 0000000000000000
DR0  - 0000000000000000, DR1 - 0000000000000000, DR2 - 0000000000000000
DR3  - 0000000000000000, DR6 - 00000000FFFF0FF0, DR7 - 0000000000000400
GDTR - 000000007FF6B000 000000000000004F, LDTR - 0000000000000000
IDTR - 000000007FF75000 00000000000001FF,   TR - 0000000000000040
FXSAVE_STATE - 000000007FF7B3C0

Call trace:
0 0x000000007FF48151 @ 0x000000007FF46000+0x2151 (0x000000007FF7B760) in VariableSmm.dll
1 0x000000007FF54E35 @ 0x000000007FF46000+0xEE35 (0x000000007FF7B7D0) in VariableSmm.dll
2 0x000000007FF5659E @ 0x000000007FF46000+0x1059E (0x000000007FF7B800) in VariableSmm.dll
3 0x000000007FF4BCFE @ 0x000000007FF46000+0x5CFE (0x000000007FF7B840) in VariableSmm.dll
4 0x000000007FFE9BFA @ 0x000000007FFDB000+0xEBFA (0x000000007FF7B8A0) in PiSmmCore.dll
5 0x000000007FFEAAC4 @ 0x000000007FFDB000+0xFAC4 (0x000000007FF7B9C0) in PiSmmCore.dll
6 0x000000007FFEADD1 @ 0x000000007FFDB000+0xFDD1 (0x000000007FF7BA20) in PiSmmCore.dll
7 0x000000007FFE43F8 @ 0x000000007FFDB000+0x93F8 (0x000000007FF7BA90) in PiSmmCore.dll
8 0x000000007FFA8B9A @ 0x000000007FF9C000+0xCB9A (0x000000007FF7BD50) in PiSmmCpuDxeSmm.dll
9 0x000000007FFA9E92 @ 0x000000007FF9C000+0xDE92 (0x000000007FF7BDC0) in PiSmmCpuDxeSmm.dll
!!!! X64 Exception Type - 0E(#PF - Page-Fault)  CPU Apic ID - 00000000 !!!!
ExceptionData - 0000000000000000  I:0 R:0 U:0 W:0 P:0 PK:0 S:0
RIP  - 000000007FFA1BBE, CS  - 0000000000000038, RFLAGS - 0000000000010006
RAX  - 000000007FF89FFC, RCX - 000000007FF8E149, RDX - FFFFFFFFFFFFFFFF
RBX  - 0000000000000000, RSP - 000000007FF7B1A0, RBP - 000000007FF7B1E0
RSI  - 000000007FFA9E92, RDI - 000000007FF9C000
R8   - 0000000000000001, R9  - 0000000000000000, R10 - 0000000000000000
R11  - 0000000000000069, R12 - 0000000000000000, R13 - 0000000000000000
R14  - 0000000000000000, R15 - 0000000000000000
DS   - 0000000000000020, ES  - 0000000000000020, FS  - 0000000000000020
GS   - 0000000000000020, SS  - 0000000000000020
CR0  - 0000000080010033, CR2 - 000000007FF89FFC, CR3 - 000000007FF6C000
CR4  - 0000000000000668, CR8 - 0000000000000000
DR0  - 0000000000000000, DR1 - 0000000000000000, DR2 - 0000000000000000
DR3  - 0000000000000000, DR6 - 00000000FFFF0FF0, DR7 - 0000000000000400
GDTR - 000000007FF6B000 000000000000004F, LDTR - 0000000000000000
IDTR - 000000007FF75000 00000000000001FF,   TR - 0000000000000040
FXSAVE_STATE - 000000007FF76C60

^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC v5 0/8] Stack trace support in X64 exception handling
  2018-01-17 12:57       ` [RFC v5 0/8] Stack trace support in X64 exception handling Yao, Jiewen
  2018-01-17 22:48         ` Yao, Jiewen
  2018-01-19  0:02         ` Paulo Alcantara
@ 2018-01-29 13:38         ` Paulo Alcantara
  2018-01-31  5:56           ` Yao, Jiewen
  2 siblings, 1 reply; 82+ messages in thread
From: Paulo Alcantara @ 2018-01-29 13:38 UTC (permalink / raw)
  To: Yao, Jiewen, edk2-devel@lists.01.org
  Cc: Rick Bramley, Dong, Eric, Kimon Berlin, Andrew Fish,
	Diego Medaglia, Laszlo Ersek

Hi Jiewen,

On 1/17/2018 10:57 AM, Yao, Jiewen wrote:
> Thanks Paulo.
> 
> The series looks really good. I give it thumb up. :-)
> 
> The 8 patches keep updating 4 files, so I squash them when I review. The comment below is for the final code, not for a specific patch.
> 
> 1) CpuExceptionCommon.c: IsLinearAddressRangeValid().
>    //
>    // Check for valid input parameters
>    //
>    if (LinearAddressStart == 0 || LinearAddressEnd == 0 ||
>        LinearAddressStart > LinearAddressEnd) {
>      return FALSE;
>    }
> 
> I think LinearAddressStart is a valid case. In BIOS, we do update IVT in 0 address when CSM is enabled.
> I do not think we need exclude it here. If we enabled ZeroPointer protection, it can be excluded in page table parsing.
> 
> 2) I found below logic appears in 2 functions - DumpImageModuleNames() and DumpStacktrace(). Is that possible to merge them?
> We can calculate in DumpImageAndCpuContent() and use Eip as parameter. Just in case there is 3rd function need use EIP, it won't miss the calculation.
> (It is a bug fix we did recently.)
> 
>    //
>    // Set current EIP address
>    //
>    if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) &&
>        ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0)) {
>      //
>      // The EIP in SystemContext could not be used
>      // if it is page fault with I/D set.
>      //
>      Eip = *(UINT32 *)(UINTN)SystemContext.SystemContextIa32->Esp;
>    } else {
>      Eip = SystemContext.SystemContextIa32->Eip;
>    }
> 
> 3) I am a little surprised on PeCoffSearchImageBase() issue.
> 
> We have 4 PeCoffSearchImageBase() call in each arch. DumpImageModuleNames() calls twice and DumpStacktrace() calls twice.
> Do you know which specific one triggers the zero address #PF issue?
> 
>    C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\ArchExceptionHandler.c(547):  ImageBase = PeCoffSearchImageBase (Eip);
>    C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\ArchExceptionHandler.c(613):    ImageBase = PeCoffSearchImageBase (Eip);
>    C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\ArchExceptionHandler.c(682):  ImageBase = PeCoffSearchImageBase (Eip);
>    C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\ArchExceptionHandler.c(741):    ImageBase = PeCoffSearchImageBase (Eip);
>    C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\ArchExceptionHandler.c(540):  ImageBase = PeCoffSearchImageBase (Rip);
>    C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\ArchExceptionHandler.c(613):    ImageBase = PeCoffSearchImageBase (Rip);
>    C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\ArchExceptionHandler.c(710):  ImageBase = PeCoffSearchImageBase (Rip);
>    C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\ArchExceptionHandler.c(779):    ImageBase = PeCoffSearchImageBase (Rip);
> 
> The EIP from SystemContext seems good. I assume there is not a problem here.
> 
>    if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) &&
>        ((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0)) {
>      //
>      // The EIP in SystemContext could not be used
>      // if it is page fault with I/D set.
>      //
>      Eip = *(UINT32 *)(UINTN)SystemContext.SystemContextIa32->Esp;
>    } else {
>      Eip = SystemContext.SystemContextIa32->Eip;
>    }
> 
>    //
>    // Get initial PE/COFF image base address from current EIP
>    //
>    ImageBase = PeCoffSearchImageBase (Eip);
> 
> But the EIP from stack frame is unknown and risky. Especially for the code in mode-switch, such as PEI->DXE, UEFI->CSM, AP wakeup->AP C code, SMM entrypoint->SMM C code.
> Should we add a check for EIP here?
> 
>      //
>      // Set EIP with return address from current stack frame
>      //
>      Eip = *(UINT32 *)((UINTN)Ebp + 4);
> 
>      //
>      // If EIP is zero, then stop unwinding the stack
>      //
>      if (Eip == 0) {
>        break;
>      }
> 
>      //
>      // Search for the respective PE/COFF image based on EIP
>      //
>      ImageBase = PeCoffSearchImageBase (Eip);
> 
> If you can help us do some more investigation on the root-cause and narrow down the issue, I will appreciate that.
> 
> We can decide how to fix once it is root-caused.
> 
> Maybe we just do a simple IsLogicalAddressValid () check before we call PeCoffSearchImageBase(). :-)

I was able to work a little bit on stack trace support yesterday, and 
here's what I came up with by following your suggestions:

1. Centralized the calculation of initial EIP in DumpImageAndCpuContent()

2. LinearAddressStart 0 is now a valid case

3. Validate all return addresses (EIP/RIP) from every frame pointer.

Additionally, I run a few quick tests by generating exceptions from DXE 
and SMM and here's my result:

OVMF X64: DXE & SMM worked.
OVMF X64 IA32: DXE & SMM worked.
OVMF IA32: DXE worked, but SMM not.

SMM was failing in both X64 and IA32 - however when I started validating 
the return addresses from the frame pointers and, when we find an 
invalid return address (e.g. last valid frame), we stop the trace. With 
that, SMM is now working in X64.

Since the page fault is only occurring in IA32 with no paging enabled 
(default case), I suspect that when we don't have paging, we are unable 
to successfully validate the memory address when it's i.e. outside SMRAM 
- that is, we don't know when to stop unwinding the stack.

Any ideas on what might be causing that?

Other than that, I'll try to do some more tests this weekend and come 
back with the results. (That's why I didn't send out the v6 yet)

Repo:   https://git.paulo.ac/pub/edk2.git
Branch: stacktrace_v6

Thanks
Paulo

> 
> 
> Thank you
> Yao Jiewen
> 
>> -----Original Message-----
>> From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of Paulo
>> Alcantara
>> Sent: Monday, January 15, 2018 8:23 AM
>> To: edk2-devel@lists.01.org
>> Cc: Rick Bramley <richard.bramley@hp.com>; Dong, Eric
>> <eric.dong@intel.com>; Kimon Berlin <kimon.berlin@hp.com>; Andrew Fish
>> <afish@apple.com>; Yao, Jiewen <jiewen.yao@intel.com>; Diego Medaglia
>> <diego.meaglia@hp.com>; Laszlo Ersek <lersek@redhat.com>
>> Subject: [edk2] [RFC v5 0/8] Stack trace support in X64 exception handling
>>
>> Hi,
>>
>> This series adds stack trace support during IA32 and X64 CPU exceptions.
>>
>> Informations like back trace, stack contents and image module names
>> (that were part of the call stack) will be dumped out.
>>
>> The current limitation is that it relies on available frame pointers
>> (GCC only) in order to successfully unwind the stack.
>>
>> Jiewen,
>>
>> Thank you very much for your time on this. I've applied the changes you
>> suggested, as well as tested it on IA32 PAE paging mode - it worked as
>> expected.
>>
>> Other than that, I also tested the stack trace in SMM code by manually
>> calling CpuBreakPoint() and then it broke with another exception
>> (page fault). I didn't have much time to look into that, but what I've
>> observed is that the page fault ocurred during the search of PE/COFF
>> image base address (in PeCoffSearchImageBase). The function attempts to
>> search for the image base from "Address" through 0, so any of those
>> dereferenced addresses triggers the page fault.
>>
>> Do you know how we could fix that issue? Perhaps introducing a
>> AddressValidationLib (as Brian suggested previously) and use it within
>> PeCoffSearchImageBase()?
>>
>> I'd also like to thank Brian & Jeff for all the support!
>>
>> Thanks
>> Paulo
>>
>> Repo:   https://github.com/pcacjr/edk2.git
>> Branch: stacktrace_v5
>>
>> Cc: Rick Bramley <richard.bramley@hp.com>
>> Cc: Kimon Berlin <kimon.berlin@hp.com>
>> Cc: Diego Medaglia <diego.meaglia@hp.com>
>> Cc: Andrew Fish <afish@apple.com>
>> Cc: Eric Dong <eric.dong@intel.com>
>> Cc: Laszlo Ersek <lersek@redhat.com>
>> Cc: Brian Johnson <brian.johnson@hpe.com>
>> Cc: Jeff Fan <vanjeff_919@hotmail.com>
>> Cc: Jiewen Yao <jiewen.yao@intel.com>
>> Cc: Paulo Alcantara <paulo@hp.com>
>> Contributed-under: TianoCore Contribution Agreement 1.1
>> Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
>> ---
>>
>> v1 -> v2:
>>    * Add IA32 arch support (GCC toolchain only)
>>    * Replace hard-coded stack alignment value (16) with
>>      CPU_STACK_ALIGNMENT.
>>    * Check for proper stack and frame pointer alignments.
>>    * Fix initialization of UnwoundStacksCount to 1.
>>    * Move GetPdbFileName() to common code since it will be used by both
>>      IA32 and X64 implementations.
>>
>> v2 -> v3:
>>    * Fixed wrong assumption about "RIP < ImageBase" to start searching
>>      for another PE/COFF image. That is, RIP may point to lower and
>>      higher addresses for any other PE/COFF images. Both IA32 & X64.
>>      (Thanks Andrew & Jiewen)
>>    * Fixed typo: unwond -> unwound. Both IA32 & X64. (Thanks Brian)
>>
>> v3 -> v4:
>>    * Validate all frame/stack pointer addresses before dereferencing them
>>      as requested by Brian & Jiewen.
>>    * Correctly print out IP addresses during the stack traces (by Jeff)
>>
>> v4 -> v5:
>>    * Fixed address calculations and improved code as suggested by Jiewen.
>>    * Fixed parameter validation as suggested by Brian.
>>    * Tested stack stack with IA32 PAE paging mode.
>>
>> Paulo Alcantara (8):
>>    UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
>>    UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName()
>>    UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support
>>    UefiCpuPkg/CpuExceptionHandlerLib: Add helper to validate memory
>>      addresses
>>    UefiCpuPkg/CpuExceptionHandlerLib: Ensure valid frame/stack pointers
>>    UefiCpuPkg/CpuExceptionHandlerLib: Correctly print IP addresses
>>    UefiCpuPkg/CpuExceptionHandlerLib: Validate memory address ranges
>>    UefiCpuPkg/CpuExceptionHandlerLib: Add early check in
>>      DumpStackContents
>>
>>   UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
>> | 537 ++++++++++++++++++--
>>   UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
>> |  59 ++-
>>   UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c |
>> 483 +++++++++++++++++-
>>   UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c  |
>> 426 +++++++++++++++-
>>   4 files changed, 1435 insertions(+), 70 deletions(-)
>>
>> --
>> 2.14.3
>>
>> _______________________________________________
>> edk2-devel mailing list
>> edk2-devel@lists.01.org
>> https://lists.01.org/mailman/listinfo/edk2-devel



^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC v5 0/8] Stack trace support in X64 exception handling
  2018-01-29 13:38         ` Paulo Alcantara
@ 2018-01-31  5:56           ` Yao, Jiewen
  2018-01-31 19:05             ` Paulo Alcantara
  0 siblings, 1 reply; 82+ messages in thread
From: Yao, Jiewen @ 2018-01-31  5:56 UTC (permalink / raw)
  To: Paulo Alcantara, edk2-devel@lists.01.org
  Cc: Rick Bramley, Dong, Eric, Kimon Berlin, Andrew Fish,
	Diego Medaglia, Laszlo Ersek

Thanks Paulo.
=======================
OVMF IA32: DXE worked, but SMM not.

Since the page fault is only occurring in IA32 with no paging enabled 
(default case), I suspect that when we don't have paging, we are unable 
to successfully validate the memory address when it's i.e. outside SMRAM 
- that is, we don't know when to stop unwinding the stack.
=======================

For IA32 SMM, I am a little confused.
We unconditionally setup page table for SMM, no matter it is IA32 or X64.

If you find a SMM driver running in a page-disable environment, it means, the SMM CORE load the driver in SMRAM, but the SMM CPU driver has not rebased the CPU yet.
SMM driver is still using the PageTable/GDT/IDT setup by DXE CPU driver, not SMM CPU driver.

Would you please double confirm what you have observed?

You can just check the boot log file to see if PiSmmCpu driver has run or not.



Thank you
Yao Jiewen


> -----Original Message-----
> From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of Paulo
> Alcantara
> Sent: Monday, January 29, 2018 9:38 PM
> To: Yao, Jiewen <jiewen.yao@intel.com>; edk2-devel@lists.01.org
> Cc: Rick Bramley <richard.bramley@hp.com>; Dong, Eric
> <eric.dong@intel.com>; Kimon Berlin <kimon.berlin@hp.com>; Andrew Fish
> <afish@apple.com>; Diego Medaglia <diego.meaglia@hp.com>; Laszlo Ersek
> <lersek@redhat.com>
> Subject: Re: [edk2] [RFC v5 0/8] Stack trace support in X64 exception handling
> 
> Hi Jiewen,
> 
> On 1/17/2018 10:57 AM, Yao, Jiewen wrote:
> > Thanks Paulo.
> >
> > The series looks really good. I give it thumb up. :-)
> >
> > The 8 patches keep updating 4 files, so I squash them when I review. The
> comment below is for the final code, not for a specific patch.
> >
> > 1) CpuExceptionCommon.c: IsLinearAddressRangeValid().
> >    //
> >    // Check for valid input parameters
> >    //
> >    if (LinearAddressStart == 0 || LinearAddressEnd == 0 ||
> >        LinearAddressStart > LinearAddressEnd) {
> >      return FALSE;
> >    }
> >
> > I think LinearAddressStart is a valid case. In BIOS, we do update IVT in 0
> address when CSM is enabled.
> > I do not think we need exclude it here. If we enabled ZeroPointer protection, it
> can be excluded in page table parsing.
> >
> > 2) I found below logic appears in 2 functions - DumpImageModuleNames() and
> DumpStacktrace(). Is that possible to merge them?
> > We can calculate in DumpImageAndCpuContent() and use Eip as parameter.
> Just in case there is 3rd function need use EIP, it won't miss the calculation.
> > (It is a bug fix we did recently.)
> >
> >    //
> >    // Set current EIP address
> >    //
> >    if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) &&
> >        ((SystemContext.SystemContextIa32->ExceptionData &
> IA32_PF_EC_ID) != 0)) {
> >      //
> >      // The EIP in SystemContext could not be used
> >      // if it is page fault with I/D set.
> >      //
> >      Eip = *(UINT32 *)(UINTN)SystemContext.SystemContextIa32->Esp;
> >    } else {
> >      Eip = SystemContext.SystemContextIa32->Eip;
> >    }
> >
> > 3) I am a little surprised on PeCoffSearchImageBase() issue.
> >
> > We have 4 PeCoffSearchImageBase() call in each arch.
> DumpImageModuleNames() calls twice and DumpStacktrace() calls twice.
> > Do you know which specific one triggers the zero address #PF issue?
> >
> >
> C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\Arch
> ExceptionHandler.c(547):  ImageBase = PeCoffSearchImageBase (Eip);
> >
> C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\Arch
> ExceptionHandler.c(613):    ImageBase = PeCoffSearchImageBase (Eip);
> >
> C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\Arch
> ExceptionHandler.c(682):  ImageBase = PeCoffSearchImageBase (Eip);
> >
> C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\Ia32\Arch
> ExceptionHandler.c(741):    ImageBase = PeCoffSearchImageBase (Eip);
> >
> C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\Arch
> ExceptionHandler.c(540):  ImageBase = PeCoffSearchImageBase (Rip);
> >
> C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\Arch
> ExceptionHandler.c(613):    ImageBase = PeCoffSearchImageBase (Rip);
> >
> C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\Arch
> ExceptionHandler.c(710):  ImageBase = PeCoffSearchImageBase (Rip);
> >
> C:\home\EdkIIGit\edk2\UefiCpuPkg\Library\CpuExceptionHandlerLib\X64\Arch
> ExceptionHandler.c(779):    ImageBase = PeCoffSearchImageBase (Rip);
> >
> > The EIP from SystemContext seems good. I assume there is not a problem here.
> >
> >    if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) &&
> >        ((SystemContext.SystemContextIa32->ExceptionData &
> IA32_PF_EC_ID) != 0)) {
> >      //
> >      // The EIP in SystemContext could not be used
> >      // if it is page fault with I/D set.
> >      //
> >      Eip = *(UINT32 *)(UINTN)SystemContext.SystemContextIa32->Esp;
> >    } else {
> >      Eip = SystemContext.SystemContextIa32->Eip;
> >    }
> >
> >    //
> >    // Get initial PE/COFF image base address from current EIP
> >    //
> >    ImageBase = PeCoffSearchImageBase (Eip);
> >
> > But the EIP from stack frame is unknown and risky. Especially for the code in
> mode-switch, such as PEI->DXE, UEFI->CSM, AP wakeup->AP C code, SMM
> entrypoint->SMM C code.
> > Should we add a check for EIP here?
> >
> >      //
> >      // Set EIP with return address from current stack frame
> >      //
> >      Eip = *(UINT32 *)((UINTN)Ebp + 4);
> >
> >      //
> >      // If EIP is zero, then stop unwinding the stack
> >      //
> >      if (Eip == 0) {
> >        break;
> >      }
> >
> >      //
> >      // Search for the respective PE/COFF image based on EIP
> >      //
> >      ImageBase = PeCoffSearchImageBase (Eip);
> >
> > If you can help us do some more investigation on the root-cause and narrow
> down the issue, I will appreciate that.
> >
> > We can decide how to fix once it is root-caused.
> >
> > Maybe we just do a simple IsLogicalAddressValid () check before we call
> PeCoffSearchImageBase(). :-)
> 
> I was able to work a little bit on stack trace support yesterday, and
> here's what I came up with by following your suggestions:
> 
> 1. Centralized the calculation of initial EIP in DumpImageAndCpuContent()
> 
> 2. LinearAddressStart 0 is now a valid case
> 
> 3. Validate all return addresses (EIP/RIP) from every frame pointer.
> 
> Additionally, I run a few quick tests by generating exceptions from DXE
> and SMM and here's my result:
> 
> OVMF X64: DXE & SMM worked.
> OVMF X64 IA32: DXE & SMM worked.
> OVMF IA32: DXE worked, but SMM not.
> 
> SMM was failing in both X64 and IA32 - however when I started validating
> the return addresses from the frame pointers and, when we find an
> invalid return address (e.g. last valid frame), we stop the trace. With
> that, SMM is now working in X64.
> 
> Since the page fault is only occurring in IA32 with no paging enabled
> (default case), I suspect that when we don't have paging, we are unable
> to successfully validate the memory address when it's i.e. outside SMRAM
> - that is, we don't know when to stop unwinding the stack.
> 
> Any ideas on what might be causing that?
> 
> Other than that, I'll try to do some more tests this weekend and come
> back with the results. (That's why I didn't send out the v6 yet)
> 
> Repo:   https://git.paulo.ac/pub/edk2.git
> Branch: stacktrace_v6
> 
> Thanks
> Paulo
> 
> >
> >
> > Thank you
> > Yao Jiewen
> >
> >> -----Original Message-----
> >> From: edk2-devel [mailto:edk2-devel-bounces@lists.01.org] On Behalf Of
> Paulo
> >> Alcantara
> >> Sent: Monday, January 15, 2018 8:23 AM
> >> To: edk2-devel@lists.01.org
> >> Cc: Rick Bramley <richard.bramley@hp.com>; Dong, Eric
> >> <eric.dong@intel.com>; Kimon Berlin <kimon.berlin@hp.com>; Andrew Fish
> >> <afish@apple.com>; Yao, Jiewen <jiewen.yao@intel.com>; Diego Medaglia
> >> <diego.meaglia@hp.com>; Laszlo Ersek <lersek@redhat.com>
> >> Subject: [edk2] [RFC v5 0/8] Stack trace support in X64 exception handling
> >>
> >> Hi,
> >>
> >> This series adds stack trace support during IA32 and X64 CPU exceptions.
> >>
> >> Informations like back trace, stack contents and image module names
> >> (that were part of the call stack) will be dumped out.
> >>
> >> The current limitation is that it relies on available frame pointers
> >> (GCC only) in order to successfully unwind the stack.
> >>
> >> Jiewen,
> >>
> >> Thank you very much for your time on this. I've applied the changes you
> >> suggested, as well as tested it on IA32 PAE paging mode - it worked as
> >> expected.
> >>
> >> Other than that, I also tested the stack trace in SMM code by manually
> >> calling CpuBreakPoint() and then it broke with another exception
> >> (page fault). I didn't have much time to look into that, but what I've
> >> observed is that the page fault ocurred during the search of PE/COFF
> >> image base address (in PeCoffSearchImageBase). The function attempts to
> >> search for the image base from "Address" through 0, so any of those
> >> dereferenced addresses triggers the page fault.
> >>
> >> Do you know how we could fix that issue? Perhaps introducing a
> >> AddressValidationLib (as Brian suggested previously) and use it within
> >> PeCoffSearchImageBase()?
> >>
> >> I'd also like to thank Brian & Jeff for all the support!
> >>
> >> Thanks
> >> Paulo
> >>
> >> Repo:   https://github.com/pcacjr/edk2.git
> >> Branch: stacktrace_v5
> >>
> >> Cc: Rick Bramley <richard.bramley@hp.com>
> >> Cc: Kimon Berlin <kimon.berlin@hp.com>
> >> Cc: Diego Medaglia <diego.meaglia@hp.com>
> >> Cc: Andrew Fish <afish@apple.com>
> >> Cc: Eric Dong <eric.dong@intel.com>
> >> Cc: Laszlo Ersek <lersek@redhat.com>
> >> Cc: Brian Johnson <brian.johnson@hpe.com>
> >> Cc: Jeff Fan <vanjeff_919@hotmail.com>
> >> Cc: Jiewen Yao <jiewen.yao@intel.com>
> >> Cc: Paulo Alcantara <paulo@hp.com>
> >> Contributed-under: TianoCore Contribution Agreement 1.1
> >> Signed-off-by: Paulo Alcantara <paulo@paulo.ac>
> >> ---
> >>
> >> v1 -> v2:
> >>    * Add IA32 arch support (GCC toolchain only)
> >>    * Replace hard-coded stack alignment value (16) with
> >>      CPU_STACK_ALIGNMENT.
> >>    * Check for proper stack and frame pointer alignments.
> >>    * Fix initialization of UnwoundStacksCount to 1.
> >>    * Move GetPdbFileName() to common code since it will be used by both
> >>      IA32 and X64 implementations.
> >>
> >> v2 -> v3:
> >>    * Fixed wrong assumption about "RIP < ImageBase" to start searching
> >>      for another PE/COFF image. That is, RIP may point to lower and
> >>      higher addresses for any other PE/COFF images. Both IA32 & X64.
> >>      (Thanks Andrew & Jiewen)
> >>    * Fixed typo: unwond -> unwound. Both IA32 & X64. (Thanks Brian)
> >>
> >> v3 -> v4:
> >>    * Validate all frame/stack pointer addresses before dereferencing them
> >>      as requested by Brian & Jiewen.
> >>    * Correctly print out IP addresses during the stack traces (by Jeff)
> >>
> >> v4 -> v5:
> >>    * Fixed address calculations and improved code as suggested by Jiewen.
> >>    * Fixed parameter validation as suggested by Brian.
> >>    * Tested stack stack with IA32 PAE paging mode.
> >>
> >> Paulo Alcantara (8):
> >>    UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support
> >>    UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName()
> >>    UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support
> >>    UefiCpuPkg/CpuExceptionHandlerLib: Add helper to validate memory
> >>      addresses
> >>    UefiCpuPkg/CpuExceptionHandlerLib: Ensure valid frame/stack pointers
> >>    UefiCpuPkg/CpuExceptionHandlerLib: Correctly print IP addresses
> >>    UefiCpuPkg/CpuExceptionHandlerLib: Validate memory address ranges
> >>    UefiCpuPkg/CpuExceptionHandlerLib: Add early check in
> >>      DumpStackContents
> >>
> >>   UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.c
> >> | 537 ++++++++++++++++++--
> >>   UefiCpuPkg/Library/CpuExceptionHandlerLib/CpuExceptionCommon.h
> >> |  59 ++-
> >>
> UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c |
> >> 483 +++++++++++++++++-
> >>   UefiCpuPkg/Library/CpuExceptionHandlerLib/X64/ArchExceptionHandler.c
> |
> >> 426 +++++++++++++++-
> >>   4 files changed, 1435 insertions(+), 70 deletions(-)
> >>
> >> --
> >> 2.14.3
> >>
> >> _______________________________________________
> >> edk2-devel mailing list
> >> edk2-devel@lists.01.org
> >> https://lists.01.org/mailman/listinfo/edk2-devel
> 
> _______________________________________________
> edk2-devel mailing list
> edk2-devel@lists.01.org
> https://lists.01.org/mailman/listinfo/edk2-devel


^ permalink raw reply	[flat|nested] 82+ messages in thread

* Re: [RFC v5 0/8] Stack trace support in X64 exception handling
  2018-01-31  5:56           ` Yao, Jiewen
@ 2018-01-31 19:05             ` Paulo Alcantara
  0 siblings, 0 replies; 82+ messages in thread
From: Paulo Alcantara @ 2018-01-31 19:05 UTC (permalink / raw)
  To: Yao, Jiewen, edk2-devel@lists.01.org
  Cc: Rick Bramley, Dong, Eric, Kimon Berlin, Andrew Fish,
	Diego Medaglia, Laszlo Ersek

"Yao, Jiewen" <jiewen.yao@intel.com> writes:

Hi Jiewen,

> =======================
> OVMF IA32: DXE worked, but SMM not.
>
> Since the page fault is only occurring in IA32 with no paging enabled 
> (default case), I suspect that when we don't have paging, we are unable 
> to successfully validate the memory address when it's i.e. outside SMRAM 
> - that is, we don't know when to stop unwinding the stack.
> =======================
>
> For IA32 SMM, I am a little confused.
> We unconditionally setup page table for SMM, no matter it is IA32 or X64.
>
> If you find a SMM driver running in a page-disable environment, it means, the SMM CORE load the driver in SMRAM, but the SMM CPU driver has not rebased the CPU yet.
> SMM driver is still using the PageTable/GDT/IDT setup by DXE CPU
> driver, not SMM CPU driver.

OK - thanks for clarifying that.

> Would you please double confirm what you have observed?
>
> You can just check the boot log file to see if PiSmmCpu driver has run
> or not.

Sure. I will do it and then get back to you once I got the results.

Thanks
Paulo


^ permalink raw reply	[flat|nested] 82+ messages in thread

end of thread, other threads:[~2018-01-31 19:00 UTC | newest]

Thread overview: 82+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-11-14 12:47 [RFC 0/1] Stack trace support in X64 exception handling Paulo Alcantara
2017-11-14 12:47 ` [RFC 1/1] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support Paulo Alcantara
2017-11-14 14:01   ` Andrew Fish
2017-11-14 14:26     ` 答复: " Fan Jeff
2017-11-14 14:38       ` Andrew Fish
2017-11-14 15:30     ` Paulo Alcantara
2017-11-14 16:51       ` Brian J. Johnson
2017-12-29  3:48   ` [RFC v4 0/6] Stack trace support in X64 exception handling Paulo Alcantara
2017-12-29  4:39     ` [RFC v4 1/6] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support Paulo Alcantara
2018-01-03  8:53       ` 答复: " Fan Jeff
2018-01-03 14:51         ` Paulo Alcantara
2017-12-29  4:39     ` [RFC v4 2/6] UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName() Paulo Alcantara
2017-12-29  4:39     ` [RFC v4 3/6] UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support Paulo Alcantara
2017-12-29  4:39     ` [RFC v4 4/6] UefiCpuPkg/CpuExceptionHandlerLib: Add helper to valid memory addresses Paulo Alcantara
2018-01-03  8:42       ` 答复: " Fan Jeff
2018-01-03 14:45         ` Paulo Alcantara
2018-01-03 16:59       ` Brian J. Johnson
2018-01-04 13:03         ` Paulo Alcantara
2018-01-04  1:36       ` Yao, Jiewen
2018-01-04  1:58         ` Yao, Jiewen
2018-01-04 13:29           ` Paulo Alcantara
2018-01-04 14:35             ` Yao, Jiewen
2018-01-04 15:15               ` Paulo Alcantara
2018-01-04 13:18         ` Paulo Alcantara
2017-12-29  4:39     ` [RFC v4 5/6] UefiCpuPkg/CpuExceptionHandlerLib: Ensure valid frame/stack pointers Paulo Alcantara
2018-01-03  8:45       ` 答复: " Fan Jeff
2018-01-03 14:48         ` Paulo Alcantara
2018-01-04  1:07       ` Yao, Jiewen
2017-12-29  4:39     ` [RFC v4 6/6] UefiCpuPkg/CpuExceptionHandlerLib: Correctly print IP addresses Paulo Alcantara
2018-01-03  8:46       ` 答复: " Fan Jeff
2018-01-04  0:59     ` [RFC v4 0/6] Stack trace support in X64 exception handling Yao, Jiewen
2018-01-04 13:36       ` Paulo Alcantara
2018-01-15  0:23     ` [RFC v5 0/8] " Paulo Alcantara
2018-01-15  0:23       ` [RFC v5 1/8] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support Paulo Alcantara
2018-01-15  0:23       ` [RFC v5 2/8] UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName() Paulo Alcantara
2018-01-15  0:23       ` [RFC v5 3/8] UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support Paulo Alcantara
2018-01-15  0:23       ` [RFC v5 4/8] UefiCpuPkg/CpuExceptionHandlerLib: Add helper to validate memory addresses Paulo Alcantara
2018-01-15  0:23       ` [RFC v5 5/8] UefiCpuPkg/CpuExceptionHandlerLib: Ensure valid frame/stack pointers Paulo Alcantara
2018-01-15  0:23       ` [RFC v5 6/8] UefiCpuPkg/CpuExceptionHandlerLib: Correctly print IP addresses Paulo Alcantara
2018-01-15  0:23       ` [RFC v5 7/8] UefiCpuPkg/CpuExceptionHandlerLib: Validate memory address ranges Paulo Alcantara
2018-01-15  0:23       ` [RFC v5 8/8] UefiCpuPkg/CpuExceptionHandlerLib: Add early check in DumpStackContents Paulo Alcantara
2018-01-17 12:57       ` [RFC v5 0/8] Stack trace support in X64 exception handling Yao, Jiewen
2018-01-17 22:48         ` Yao, Jiewen
2018-01-19  0:09           ` Paulo Alcantara
2018-01-19  0:02         ` Paulo Alcantara
2018-01-19  0:15           ` Paulo Alcantara
2018-01-29 13:38         ` Paulo Alcantara
2018-01-31  5:56           ` Yao, Jiewen
2018-01-31 19:05             ` Paulo Alcantara
2017-11-14 13:21 ` [RFC 0/1] " Paulo Alcantara
2017-11-14 14:03   ` 答复: " Fan Jeff
2017-11-14 14:12     ` 答复: " Fan Jeff
2017-11-14 15:37     ` Paulo Alcantara
2017-11-14 16:33       ` Brian J. Johnson
2017-11-14 17:23         ` Andrew Fish
2017-11-14 17:41           ` Brian J. Johnson
2017-11-14 17:56             ` Paulo Alcantara
2017-11-15 13:21       ` 答复: 答复: " Fan Jeff
2017-11-15 14:41         ` Paulo Alcantara
2017-11-15 14:52           ` 答复: " Fan Jeff
2017-11-16  1:18 ` [RFC v2 0/3] " Paulo Alcantara
2017-11-16  1:18   ` [RFC v2 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support Paulo Alcantara
2017-11-16  1:57     ` Yao, Jiewen
2017-11-16 22:13       ` Paulo Alcantara
2017-11-17  3:43         ` Yao, Jiewen
2017-11-20 14:51           ` Paulo Alcantara
2017-11-16 15:43     ` Brian J. Johnson
2017-11-16 22:19       ` Paulo Alcantara
2017-11-16  1:18   ` [RFC v2 2/3] UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName() Paulo Alcantara
2017-11-16  1:18   ` [RFC v2 3/3] UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support Paulo Alcantara
2017-11-16  1:46   ` [RFC v2 0/3] Stack trace support in X64 exception handling Paulo Alcantara
2017-11-16  5:01     ` Andrew Fish
2017-11-16 22:02       ` Paulo Alcantara
2017-11-16 21:56   ` [RFC v3 " Paulo Alcantara
2017-11-16 21:56     ` [RFC v3 1/3] UefiCpuPkg/CpuExceptionHandlerLib/X64: Add stack trace support Paulo Alcantara
2017-11-17  7:24       ` 答复: " Fan Jeff
2017-11-20 14:59         ` Paulo Alcantara
2017-11-23 14:27           ` 答复: " Fan Jeff
2017-11-23 18:34             ` Andrew Fish
2017-11-23 19:49               ` Fan Jeff
2017-11-16 21:56     ` [RFC v3 2/3] UefiCpuPkg/CpuExceptionHandlerLib: Export GetPdbFileName() Paulo Alcantara
2017-11-16 21:56     ` [RFC v3 3/3] UefiCpuPkg/CpuExceptionHandlerLib/Ia32: Add stack trace support Paulo Alcantara

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox