public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [PATCH 1/5] MdeModulePkg/EbcDxe: allow VmReadIndex##() to return a decoded index
@ 2017-01-24 12:30 Pete Batard
  2017-01-24 12:30 ` [PATCH 2/5] MdeModulePkg/EbcDxe: add ARM support Pete Batard
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Pete Batard @ 2017-01-24 12:30 UTC (permalink / raw)
  To: edk2-devel

* The VmReadIndex## function now take an optional pointer to an index
  pair structure which, when not NULL, is filled with the decoded const
  and natural values.
* This feature is needed by the ARM EBC VM.
* For now, the new parameters is set to NULL, so as not to change
  existing behaviour with current EBC VM platforms.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Pete Batard <pete@akeo.ie>
---
 MdeModulePkg/Universal/EbcDxe/EbcExecute.c | 116 +++++++++++++++++++++--------
 MdeModulePkg/Universal/EbcDxe/EbcExecute.h |   8 ++
 2 files changed, 93 insertions(+), 31 deletions(-)

diff --git a/MdeModulePkg/Universal/EbcDxe/EbcExecute.c b/MdeModulePkg/Universal/EbcDxe/EbcExecute.c
index e5d290a2fec6..2d21c3364e0d 100644
--- a/MdeModulePkg/Universal/EbcDxe/EbcExecute.c
+++ b/MdeModulePkg/Universal/EbcDxe/EbcExecute.c
@@ -61,6 +61,8 @@ UINT64
   @param  VmPtr             A pointer to VM context.
   @param  CodeOffset        Offset from IP of the location of the 16-bit index
                             to decode.
+  @param  IndexPtr          An optional pointer where the decoded index pair
+                            values can be written.
 
   @return The decoded offset.
 
@@ -68,7 +70,8 @@ UINT64
 INT16
 VmReadIndex16 (
   IN VM_CONTEXT     *VmPtr,
-  IN UINT32         CodeOffset
+  IN UINT32         CodeOffset,
+  OUT EBC_INDEX     *IndexPtr OPTIONAL
   );
 
 /**
@@ -77,6 +80,8 @@ VmReadIndex16 (
   @param  VmPtr             A pointer to VM context.
   @param  CodeOffset        Offset from IP of the location of the 32-bit index
                             to decode.
+  @param  IndexPtr          An optional pointer where the decoded index pair
+                            values can be written.
 
   @return Converted index per EBC VM specification.
 
@@ -84,7 +89,8 @@ VmReadIndex16 (
 INT32
 VmReadIndex32 (
   IN VM_CONTEXT     *VmPtr,
-  IN UINT32         CodeOffset
+  IN UINT32         CodeOffset,
+  OUT EBC_INDEX     *IndexPtr OPTIONAL
   );
 
 /**
@@ -93,6 +99,8 @@ VmReadIndex32 (
   @param  VmPtr             A pointer to VM context.s
   @param  CodeOffset        Offset from IP of the location of the 64-bit index
                             to decode.
+  @param  IndexPtr          An optional pointer where the decoded index pair
+                            values can be written.
 
   @return Converted index per EBC VM specification
 
@@ -100,7 +108,8 @@ VmReadIndex32 (
 INT64
 VmReadIndex64 (
   IN VM_CONTEXT     *VmPtr,
-  IN UINT32         CodeOffset
+  IN UINT32         CodeOffset,
+  OUT EBC_INDEX     *IndexPtr OPTIONAL
   );
 
 /**
@@ -1600,13 +1609,13 @@ ExecuteMOVxx (
       // Get one or both index values.
       //
       if ((Opcode & OPCODE_M_IMMED_OP1) != 0) {
-        Index16     = VmReadIndex16 (VmPtr, 2);
+        Index16     = VmReadIndex16 (VmPtr, 2, NULL);
         Index64Op1  = (INT64) Index16;
         Size += sizeof (UINT16);
       }
 
       if ((Opcode & OPCODE_M_IMMED_OP2) != 0) {
-        Index16     = VmReadIndex16 (VmPtr, Size);
+        Index16     = VmReadIndex16 (VmPtr, Size, NULL);
         Index64Op2  = (INT64) Index16;
         Size += sizeof (UINT16);
       }
@@ -1615,13 +1624,13 @@ ExecuteMOVxx (
       // MOVBD, MOVWD, MOVDD, MOVQD, and MOVND have 32-bit immediate index
       //
       if ((Opcode & OPCODE_M_IMMED_OP1) != 0) {
-        Index32     = VmReadIndex32 (VmPtr, 2);
+        Index32     = VmReadIndex32 (VmPtr, 2, NULL);
         Index64Op1  = (INT64) Index32;
         Size += sizeof (UINT32);
       }
 
       if ((Opcode & OPCODE_M_IMMED_OP2) != 0) {
-        Index32     = VmReadIndex32 (VmPtr, Size);
+        Index32     = VmReadIndex32 (VmPtr, Size, NULL);
         Index64Op2  = (INT64) Index32;
         Size += sizeof (UINT32);
       }
@@ -1630,12 +1639,12 @@ ExecuteMOVxx (
       // MOVqq -- only form with a 64-bit index
       //
       if ((Opcode & OPCODE_M_IMMED_OP1) != 0) {
-        Index64Op1 = VmReadIndex64 (VmPtr, 2);
+        Index64Op1 = VmReadIndex64 (VmPtr, 2, NULL);
         Size += sizeof (UINT64);
       }
 
       if ((Opcode & OPCODE_M_IMMED_OP2) != 0) {
-        Index64Op2 = VmReadIndex64 (VmPtr, Size);
+        Index64Op2 = VmReadIndex64 (VmPtr, Size, NULL);
         Size += sizeof (UINT64);
       }
     } else {
@@ -2042,7 +2051,7 @@ ExecuteJMP (
   //
   if ((Opcode & OPCODE_M_IMMDATA) != 0) {
     if (OPERAND1_INDIRECT (Operand)) {
-      Index32 = VmReadIndex32 (VmPtr, 2);
+      Index32 = VmReadIndex32 (VmPtr, 2, NULL);
     } else {
       Index32 = VmReadImmed32 (VmPtr, 2);
     }
@@ -2210,7 +2219,7 @@ ExecuteMOVI (
   // Get the index (16-bit) if present
   //
   if ((Operands & MOVI_M_IMMDATA) != 0) {
-    Index16 = VmReadIndex16 (VmPtr, 2);
+    Index16 = VmReadIndex16 (VmPtr, 2, NULL);
     Size    = 4;
   } else {
     Index16 = 0;
@@ -2329,7 +2338,7 @@ ExecuteMOVIn (
   // Get the operand1 index (16-bit) if present
   //
   if ((Operands & MOVI_M_IMMDATA) != 0) {
-    Index16 = VmReadIndex16 (VmPtr, 2);
+    Index16 = VmReadIndex16 (VmPtr, 2, NULL);
     Size    = 4;
   } else {
     Index16 = 0;
@@ -2339,15 +2348,15 @@ ExecuteMOVIn (
   // Extract the immediate data and convert to a 64-bit index.
   //
   if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH16) {
-    ImmedIndex16  = VmReadIndex16 (VmPtr, Size);
+    ImmedIndex16  = VmReadIndex16 (VmPtr, Size, NULL);
     ImmedIndex64  = (INT64) ImmedIndex16;
     Size += 2;
   } else if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH32) {
-    ImmedIndex32  = VmReadIndex32 (VmPtr, Size);
+    ImmedIndex32  = VmReadIndex32 (VmPtr, Size, NULL);
     ImmedIndex64  = (INT64) ImmedIndex32;
     Size += 4;
   } else if ((Opcode & MOVI_M_DATAWIDTH) == MOVI_DATAWIDTH64) {
-    ImmedIndex64 = VmReadIndex64 (VmPtr, Size);
+    ImmedIndex64 = VmReadIndex64 (VmPtr, Size, NULL);
     Size += 8;
   } else {
     //
@@ -2430,7 +2439,7 @@ ExecuteMOVREL (
   // Get the Operand 1 index (16-bit) if present
   //
   if ((Operands & MOVI_M_IMMDATA) != 0) {
-    Index16 = VmReadIndex16 (VmPtr, 2);
+    Index16 = VmReadIndex16 (VmPtr, 2, NULL);
     Size    = 4;
   } else {
     Index16 = 0;
@@ -2539,7 +2548,7 @@ ExecuteMOVsnw (
   Size = 2;
   if ((Opcode & OPCODE_M_IMMED_OP1) !=0) {
     if (OPERAND1_INDIRECT (Operands)) {
-      Op1Index = VmReadIndex16 (VmPtr, 2);
+      Op1Index = VmReadIndex16 (VmPtr, 2, NULL);
     } else {
       //
       // Illegal form operand1 direct with index:  MOVsnw R1 Index16, {@}R2
@@ -2557,7 +2566,7 @@ ExecuteMOVsnw (
 
   if ((Opcode & OPCODE_M_IMMED_OP2) != 0) {
     if (OPERAND2_INDIRECT (Operands)) {
-      Op2Index = VmReadIndex16 (VmPtr, Size);
+      Op2Index = VmReadIndex16 (VmPtr, Size, NULL);
     } else {
       Op2Index = VmReadImmed16 (VmPtr, Size);
     }
@@ -2632,7 +2641,7 @@ ExecuteMOVsnd (
   Size = 2;
   if ((Opcode & OPCODE_M_IMMED_OP1) != 0) {
     if (OPERAND1_INDIRECT (Operands)) {
-      Op1Index = VmReadIndex32 (VmPtr, 2);
+      Op1Index = VmReadIndex32 (VmPtr, 2, NULL);
     } else {
       //
       // Illegal form operand1 direct with index:  MOVsnd R1 Index16,..
@@ -2650,7 +2659,7 @@ ExecuteMOVsnd (
 
   if ((Opcode & OPCODE_M_IMMED_OP2) != 0) {
     if (OPERAND2_INDIRECT (Operands)) {
-      Op2Index = VmReadIndex32 (VmPtr, Size);
+      Op2Index = VmReadIndex32 (VmPtr, Size, NULL);
     } else {
       Op2Index = VmReadImmed32 (VmPtr, Size);
     }
@@ -2712,7 +2721,7 @@ ExecutePUSHn (
   //
   if ((Opcode & PUSHPOP_M_IMMDATA) != 0) {
     if (OPERAND1_INDIRECT (Operands)) {
-      Index16 = VmReadIndex16 (VmPtr, 2);
+      Index16 = VmReadIndex16 (VmPtr, 2, NULL);
     } else {
       Index16 = VmReadImmed16 (VmPtr, 2);
     }
@@ -2771,7 +2780,7 @@ ExecutePUSH (
   //
   if ((Opcode & PUSHPOP_M_IMMDATA) != 0) {
     if (OPERAND1_INDIRECT (Operands)) {
-      Index16 = VmReadIndex16 (VmPtr, 2);
+      Index16 = VmReadIndex16 (VmPtr, 2, NULL);
     } else {
       Index16 = VmReadImmed16 (VmPtr, 2);
     }
@@ -2846,7 +2855,7 @@ ExecutePOPn (
   //
   if ((Opcode & PUSHPOP_M_IMMDATA) != 0) {
     if (OPERAND1_INDIRECT (Operands)) {
-      Index16 = VmReadIndex16 (VmPtr, 2);
+      Index16 = VmReadIndex16 (VmPtr, 2, NULL);
     } else {
       Index16 = VmReadImmed16 (VmPtr, 2);
     }
@@ -2906,7 +2915,7 @@ ExecutePOP (
   //
   if ((Opcode & PUSHPOP_M_IMMDATA) != 0) {
     if (OPERAND1_INDIRECT (Operands)) {
-      Index16 = VmReadIndex16 (VmPtr, 2);
+      Index16 = VmReadIndex16 (VmPtr, 2, NULL);
     } else {
       Index16 = VmReadImmed16 (VmPtr, 2);
     }
@@ -3012,7 +3021,7 @@ ExecuteCALL (
       // If register operand is indirect, then the immediate data is an index
       //
       if (OPERAND1_INDIRECT (Operands)) {
-        Immed32 = VmReadIndex32 (VmPtr, 2);
+        Immed32 = VmReadIndex32 (VmPtr, 2, NULL);
       } else {
         Immed32 = VmReadImmed32 (VmPtr, 2);
       }
@@ -3196,7 +3205,7 @@ ExecuteCMP (
   //
   if ((Opcode & OPCODE_M_IMMDATA) != 0) {
     if (OPERAND2_INDIRECT (Operands)) {
-      Index16 = VmReadIndex16 (VmPtr, 2);
+      Index16 = VmReadIndex16 (VmPtr, 2, NULL);
     } else {
       Index16 = VmReadImmed16 (VmPtr, 2);
     }
@@ -3354,7 +3363,7 @@ ExecuteCMPI (
   //
   Size = 2;
   if ((Operands & OPERAND_M_CMPI_INDEX) != 0) {
-    Index16 = VmReadIndex16 (VmPtr, 2);
+    Index16 = VmReadIndex16 (VmPtr, 2, NULL);
     Size += 2;
   } else {
     Index16 = 0;
@@ -4187,7 +4196,7 @@ ExecuteDataManip (
     // Index16 if Ry is indirect, or Immed16 if Ry direct.
     //
     if (OPERAND2_INDIRECT (Operands)) {
-      Index16 = VmReadIndex16 (VmPtr, 2);
+      Index16 = VmReadIndex16 (VmPtr, 2, NULL);
     } else {
       Index16 = VmReadImmed16 (VmPtr, 2);
     }
@@ -4430,6 +4439,8 @@ ExecuteSTORESP (
   @param  VmPtr             A pointer to VM context.
   @param  CodeOffset        Offset from IP of the location of the 16-bit index
                             to decode.
+  @param  IndexPtr          An optional pointer where the decoded index pair
+                            values can be written.
 
   @return The decoded offset.
 
@@ -4437,7 +4448,8 @@ ExecuteSTORESP (
 INT16
 VmReadIndex16 (
   IN VM_CONTEXT     *VmPtr,
-  IN UINT32         CodeOffset
+  IN UINT32         CodeOffset,
+  OUT EBC_INDEX     *IndexPtr OPTIONAL
   )
 {
   UINT16  Index;
@@ -4491,6 +4503,18 @@ VmReadIndex16 (
     Offset = (INT16) ((INT32) Offset * -1);
   }
 
+  //
+  // Copy the decoded index values if requested
+  //
+  if (IndexPtr != NULL) {
+    IndexPtr->NaturalUnits = (INT64) NaturalUnits;
+    IndexPtr->ConstUnits = (INT64) ConstUnits;
+    if ((Index & 0x8000) != 0) {
+      IndexPtr->NaturalUnits = MultS64x64 (IndexPtr->NaturalUnits, -1);
+      IndexPtr->ConstUnits   = MultS64x64 (IndexPtr->ConstUnits, -1);
+    }
+  }
+
   return Offset;
 }
 
@@ -4501,6 +4525,8 @@ VmReadIndex16 (
   @param  VmPtr             A pointer to VM context.
   @param  CodeOffset        Offset from IP of the location of the 32-bit index
                             to decode.
+  @param  IndexPtr          An optional pointer where the decoded index pair
+                            values can be written.
 
   @return Converted index per EBC VM specification.
 
@@ -4508,7 +4534,8 @@ VmReadIndex16 (
 INT32
 VmReadIndex32 (
   IN VM_CONTEXT     *VmPtr,
-  IN UINT32         CodeOffset
+  IN UINT32         CodeOffset,
+  OUT EBC_INDEX     *IndexPtr OPTIONAL
   )
 {
   UINT32  Index;
@@ -4554,6 +4581,18 @@ VmReadIndex32 (
     Offset = Offset * -1;
   }
 
+  //
+  // Copy the decoded index values if requested
+  //
+  if (IndexPtr != NULL) {
+    IndexPtr->NaturalUnits = (INT64) NaturalUnits;
+    IndexPtr->ConstUnits = (INT64) ConstUnits;
+    if ((Index & 0x80000000) != 0) {
+      IndexPtr->NaturalUnits = MultS64x64 (IndexPtr->NaturalUnits, -1);
+      IndexPtr->ConstUnits   = MultS64x64 (IndexPtr->ConstUnits, -1);
+    }
+  }
+
   return Offset;
 }
 
@@ -4564,6 +4603,8 @@ VmReadIndex32 (
   @param  VmPtr             A pointer to VM context.s
   @param  CodeOffset        Offset from IP of the location of the 64-bit index
                             to decode.
+  @param  IndexPtr          An optional pointer where the decoded index pair
+                            values can be written.
 
   @return Converted index per EBC VM specification
 
@@ -4571,7 +4612,8 @@ VmReadIndex32 (
 INT64
 VmReadIndex64 (
   IN VM_CONTEXT     *VmPtr,
-  IN UINT32         CodeOffset
+  IN UINT32         CodeOffset,
+  OUT EBC_INDEX     *IndexPtr OPTIONAL
   )
 {
   UINT64  Index;
@@ -4617,6 +4659,18 @@ VmReadIndex64 (
     Offset = MultS64x64 (Offset, -1);
   }
 
+  //
+  // Copy the decoded index values if requested
+  //
+  if (IndexPtr != NULL) {
+    IndexPtr->NaturalUnits = (INT64) NaturalUnits;
+    IndexPtr->ConstUnits = (INT64) ConstUnits;
+    if ((Index & 0x8000000000000000ULL) != 0) {
+      IndexPtr->NaturalUnits = MultS64x64 (IndexPtr->NaturalUnits, -1);
+      IndexPtr->ConstUnits   = MultS64x64 (IndexPtr->ConstUnits, -1);
+    }
+  }
+
   return Offset;
 }
 
diff --git a/MdeModulePkg/Universal/EbcDxe/EbcExecute.h b/MdeModulePkg/Universal/EbcDxe/EbcExecute.h
index b7489514b919..301c52666366 100644
--- a/MdeModulePkg/Universal/EbcDxe/EbcExecute.h
+++ b/MdeModulePkg/Universal/EbcDxe/EbcExecute.h
@@ -24,6 +24,14 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 #define IS_ALIGNED(addr, size)      !((UINT32) (addr) & (size - 1))
 
 //
+// EBC index pair
+//
+typedef struct {
+  UINT64 NaturalUnits;
+  UINT64 ConstUnits;
+} EBC_INDEX;
+
+//
 // Debug macro
 //
 #define EBCMSG(s) gST->ConOut->OutputString (gST->ConOut, s)
-- 
2.9.3.windows.2



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

* [PATCH 2/5] MdeModulePkg/EbcDxe: add ARM support
  2017-01-24 12:30 [PATCH 1/5] MdeModulePkg/EbcDxe: allow VmReadIndex##() to return a decoded index Pete Batard
@ 2017-01-24 12:30 ` Pete Batard
  2017-01-24 12:30 ` [PATCH 3/5] MdeModulePkg/EbcDxe: add a stack tracker for ARM EBC->native support Pete Batard
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Pete Batard @ 2017-01-24 12:30 UTC (permalink / raw)
  To: edk2-devel; +Cc: Ard Biesheuvel, Pete Batard

From: Ard Biesheuvel <ard.biesheuvel@linaro.org>

* This is a port of the AARCH64 implementation of the EBC runtime to ARM.
* Note that, on its own, this patch only allows running self contained EBC
  applications, such as ones that don't issue calls into the native
  platform, or that aren't being called from native.
* Support for EBC -> native and native -> EBC calls will be added in
  subsequent patches.
* Because EFI32 may not be defined for ARM compilation, this patch also
  alters the EBC Debugger to use MDE_CPU_### macros for address display.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: Pete Batard <pete@akeo.ie>
---
 ArmVirtPkg/ArmVirt.dsc.inc                         |   6 +-
 ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc               |  10 +-
 ArmVirtPkg/ArmVirtXen.fdf                          |  10 +-
 MdeModulePkg/MdeModulePkg.dsc                      |   4 +-
 MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S    | 147 +++++++
 MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c     | 470 +++++++++++++++++++++
 MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf      |   6 +-
 .../EbcDxe/EbcDebugger/EdbDisasmSupport.h          |   4 +-
 .../Universal/EbcDxe/EbcDebuggerConfig.inf         |   2 +-
 MdeModulePkg/Universal/EbcDxe/EbcDxe.inf           |   6 +-
 10 files changed, 648 insertions(+), 17 deletions(-)
 create mode 100644 MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S
 create mode 100644 MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c

diff --git a/ArmVirtPkg/ArmVirt.dsc.inc b/ArmVirtPkg/ArmVirt.dsc.inc
index dbd6678accde..3fa016b501c5 100644
--- a/ArmVirtPkg/ArmVirt.dsc.inc
+++ b/ArmVirtPkg/ArmVirt.dsc.inc
@@ -402,6 +402,11 @@ [Components.common]
   MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDxe.inf
 
   #
+  # EBC support
+  #
+  MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
+
+  #
   # UEFI application (Shell Embedded Boot Loader)
   #
   ShellPkg/Application/Shell/Shell.inf {
@@ -430,4 +435,3 @@ [Components.AARCH64]
   # ACPI Support
   #
   MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxe.inf
-  MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
diff --git a/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc b/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc
index cc5d12aaefea..5145a86a0f33 100644
--- a/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc
+++ b/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc
@@ -140,6 +140,11 @@ [FV.FvMain]
   INF MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.inf
   INF OvmfPkg/SmbiosPlatformDxe/SmbiosPlatformDxe.inf
 
+  #
+  # EBC support
+  #
+  INF MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
+
 !if $(ARCH) == AARCH64
   #
   # ACPI Support
@@ -147,11 +152,6 @@ [FV.FvMain]
   INF MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxe.inf
   INF MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
   INF OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpiPlatformDxe.inf
-
-  #
-  # EBC support
-  #
-  INF MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
 !endif
 
   #
diff --git a/ArmVirtPkg/ArmVirtXen.fdf b/ArmVirtPkg/ArmVirtXen.fdf
index c997251b12b8..ccbd8baa5f82 100644
--- a/ArmVirtPkg/ArmVirtXen.fdf
+++ b/ArmVirtPkg/ArmVirtXen.fdf
@@ -170,6 +170,11 @@ [FV.FvMain]
   INF ShellPkg/Application/Shell/Shell.inf
 
   #
+  # EBC support
+  #
+  INF MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
+
+  #
   # Bds
   #
   INF MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf
@@ -186,11 +191,6 @@ [FV.FvMain]
 !if $(ARCH) == AARCH64
   INF MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxe.inf
   INF ArmVirtPkg/XenAcpiPlatformDxe/XenAcpiPlatformDxe.inf
-
-  #
-  # EBC support
-  #
-  INF MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
 !endif
 
  #
diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc
index 5996fe50f680..35094bab9e9a 100644
--- a/MdeModulePkg/MdeModulePkg.dsc
+++ b/MdeModulePkg/MdeModulePkg.dsc
@@ -430,8 +430,10 @@ [Components]
   MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.inf
   MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.inf
 
-[Components.IA32, Components.X64, Components.IPF, Components.AARCH64]
+[Components.IA32, Components.X64, Components.IPF]
   MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxeBcDxe.inf
+
+[Components.IA32, Components.X64, Components.IPF, Components.ARM, Components.AARCH64]
   MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxe.inf
   MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
   MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf
diff --git a/MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S b/MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S
new file mode 100644
index 000000000000..cb3c11f71673
--- /dev/null
+++ b/MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S
@@ -0,0 +1,147 @@
+///** @file
+//
+//  This code provides low level routines that support the Virtual Machine
+//  for option ROMs.
+//
+//  Copyright (c) 2016, Linaro, Ltd. All rights reserved.<BR>
+//  Copyright (c) 2015, The Linux Foundation. All rights reserved.<BR>
+//  Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+//  This program and the accompanying materials
+//  are licensed and made available under the terms and conditions of the BSD License
+//  which accompanies this distribution.  The full text of the license may be found at
+//  http://opensource.org/licenses/bsd-license.php
+//
+//  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+//**/
+
+    .thumb
+    .syntax unified
+
+ASM_GLOBAL ASM_PFX(EbcLLCALLEXNative)
+ASM_GLOBAL ASM_PFX(EbcLLEbcInterpret)
+ASM_GLOBAL ASM_PFX(EbcLLExecuteEbcImageEntryPoint)
+
+INTERWORK_FUNC(EbcLLCALLEXNative)
+INTERWORK_FUNC(EbcLLEbcInterpret)
+INTERWORK_FUNC(EbcLLExecuteEbcImageEntryPoint)
+
+ASM_GLOBAL ASM_PFX(mEbcInstructionBufferTemplate)
+
+//****************************************************************************
+// EbcLLCALLEX
+//
+// This function is called to execute an EBC CALLEX instruction.
+// This instruction requires that we thunk out to external native
+// code. For ARM, we copy the VM stack into the main stack and then pop
+// the first 4 arguments off according to the ARM Procedure Call Standard
+// On return, we restore the stack pointer to its original location.
+//
+//****************************************************************************
+// UINTN EbcLLCALLEXNative(UINTN FuncAddr, UINTN NewStackPointer, VOID *FramePtr)
+ASM_PFX(EbcLLCALLEXNative):
+    mov     ip, r0                  // Preserve r0
+
+    //
+    // If the EBC stack frame is smaller than or equal to 16 bytes, we know there
+    // are no stacked arguments #5 and beyond that we need to copy to the native
+    // stack. In this case, we can perform a tail call which is much more
+    // efficient, since there is no need to touch the native stack at all.
+    //
+    sub     r3, r2, r1              // Length = NewStackPointer - FramePtr
+    cmp     r3, #16
+    bgt     0f
+
+    ldrd    r2, r3, [r1, #8]
+    ldrd    r0, r1, [r1]
+
+    bx      ip
+
+    //
+    // More than 16 bytes: we need to build the full native stack frame and copy
+    // the part of the VM stack exceeding 16 bytes (which may contain stacked
+    // arguments) to the native stack
+    //
+0:  push    {r4, lr}
+    mov     r4, sp
+
+    //
+    // Ensure that the stack pointer remains 8 byte aligned,
+    // even if the size of the VM stack frame is not a multiple of 8
+    //
+    add     r1, r1, #16             // Skip over [potential] reg params
+    tst     r3, #7                  // Multiple of 8?
+    beq     1f
+    ldr     r3, [r2, #-4]!          // No? Then push one word
+    str     r3, [sp, #-8]!          // ... but use two slots
+    b       2f
+
+1:  ldrd    r0, r3, [r2, #-8]!
+    strd    r0, r3, [sp, #-8]!
+2:  cmp     r2, r1
+    bgt     1b
+
+    ldrd    r2, r3, [r1, #-8]
+    ldrd    r0, r1, [r1, #-16]
+
+    blx     ip
+
+    mov     sp, r4
+    pop     {r4, pc}
+
+//****************************************************************************
+// EbcLLEbcInterpret
+//
+// This function is called by the thunk code to handle an Native to EBC call
+// This can handle up to 16 arguments (1-4 on in r0-r3, 5-16 are on the stack)
+// ip contains the Entry point that will be the first argument when
+// EBCInterpret is called.
+//
+//****************************************************************************
+ASM_PFX(EbcLLEbcInterpret):
+    stmdb   sp!, {r4, lr}
+
+    // push the entry point and the address of args #5 - #16 onto the stack
+    add     r4, sp, #8
+    str     ip, [sp, #-8]!
+    str     r4, [sp, #4]
+
+    // call C-code
+    bl      ASM_PFX(EbcInterpret)
+
+    add     sp, sp, #8
+    ldmia   sp!, {r4, pc}
+
+//****************************************************************************
+// EbcLLExecuteEbcImageEntryPoint
+//
+// This function is called by the thunk code to handle the image entry point
+// ip contains the Entry point that will be the third argument when
+// ExecuteEbcImageEntryPoint is called.
+//
+//****************************************************************************
+ASM_PFX(EbcLLExecuteEbcImageEntryPoint):
+    ldr     r2, [ip, #12]
+
+    // tail call to C code
+    b       ASM_PFX(ExecuteEbcImageEntryPoint)
+
+//****************************************************************************
+// mEbcInstructionBufferTemplate
+//****************************************************************************
+    .section    ".rodata", "a"
+    .align      2
+    .arm
+ASM_PFX(mEbcInstructionBufferTemplate):
+    adr     ip, .
+    ldr     pc, 0f
+
+    //
+    // Add a magic code here to help the VM recognize the thunk.
+    //
+    udf     #0xEBC
+
+    .long   0                       // EBC_ENTRYPOINT_SIGNATURE
+0:  .long   0                       // EBC_LL_EBC_ENTRYPOINT_SIGNATURE
diff --git a/MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c b/MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c
new file mode 100644
index 000000000000..2a42b6c4d0e7
--- /dev/null
+++ b/MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c
@@ -0,0 +1,470 @@
+/** @file
+  This module contains EBC support routines that are customized based on
+  the target Arm processor.
+
+Copyright (c) 2016, Linaro, Ltd. All rights reserved.<BR>
+Copyright (c) 2015, The Linux Foundation. All rights reserved.<BR>
+Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution.  The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "EbcInt.h"
+#include "EbcExecute.h"
+#include "EbcDebuggerHook.h"
+
+//
+// Amount of space that is not used in the stack
+//
+#define STACK_REMAIN_SIZE (1024 * 4)
+
+#pragma pack(1)
+typedef struct {
+  UINT32    Instr[2];
+  UINT32    Magic;
+  UINT32    EbcEntryPoint;
+  UINT32    EbcLlEntryPoint;
+} EBC_INSTRUCTION_BUFFER;
+#pragma pack()
+
+extern CONST EBC_INSTRUCTION_BUFFER       mEbcInstructionBufferTemplate;
+
+/**
+  Begin executing an EBC image.
+  This is used for Ebc Thunk call.
+
+  @return The value returned by the EBC application we're going to run.
+
+**/
+UINT64
+EFIAPI
+EbcLLEbcInterpret (
+  VOID
+  );
+
+/**
+  Begin executing an EBC image.
+  This is used for Ebc image entrypoint.
+
+  @return The value returned by the EBC application we're going to run.
+
+**/
+UINT64
+EFIAPI
+EbcLLExecuteEbcImageEntryPoint (
+  VOID
+  );
+
+/**
+  Pushes a 32 bit unsigned value to the VM stack.
+
+  @param VmPtr  The pointer to current VM context.
+  @param Arg    The value to be pushed.
+
+**/
+VOID
+PushU32 (
+  IN VM_CONTEXT *VmPtr,
+  IN UINT32     Arg
+  )
+{
+  //
+  // Advance the VM stack down, and then copy the argument to the stack.
+  // Hope it's aligned.
+  //
+  VmPtr->Gpr[0] -= sizeof (UINT32);
+  *(UINT32 *)(UINTN)VmPtr->Gpr[0] = Arg;
+}
+
+
+/**
+  Begin executing an EBC image.
+
+  This is a thunk function.
+
+  @param  Arg1                  The 1st argument.
+  @param  Arg2                  The 2nd argument.
+  @param  Arg3                  The 3rd argument.
+  @param  Arg4                  The 4th argument.
+  @param  Arg8                  The 8th argument.
+  @param  EntryPoint            The entrypoint of EBC code.
+  @param  Args5_16[]            Array containing arguments #5 to #16.
+
+  @return The value returned by the EBC application we're going to run.
+
+**/
+UINT64
+EFIAPI
+EbcInterpret (
+  IN UINTN      Arg1,
+  IN UINTN      Arg2,
+  IN UINTN      Arg3,
+  IN UINTN      Arg4,
+  IN UINTN      EntryPoint,
+  IN UINTN      Args5_16[]
+  )
+{
+  //
+  // Create a new VM context on the stack
+  //
+  VM_CONTEXT  VmContext;
+  UINTN       Addr;
+  EFI_STATUS  Status;
+  UINTN       StackIndex;
+
+  //
+  // Get the EBC entry point
+  //
+  Addr = EntryPoint;
+
+  //
+  // Now clear out our context
+  //
+  ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));
+
+  //
+  // Set the VM instruction pointer to the correct location in memory.
+  //
+  VmContext.Ip = (VMIP) Addr;
+
+  //
+  // Initialize the stack pointer for the EBC. Get the current system stack
+  // pointer and adjust it down by the max needed for the interpreter.
+  //
+
+  //
+  // Adjust the VM's stack pointer down.
+  //
+
+  Status = GetEBCStack((EFI_HANDLE)(UINTN)-1, &VmContext.StackPool, &StackIndex);
+  if (EFI_ERROR(Status)) {
+    return Status;
+  }
+  VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE);
+  VmContext.Gpr[0] = (UINT32) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE);
+  VmContext.HighStackBottom = (UINTN) VmContext.Gpr[0];
+  VmContext.Gpr[0] -= sizeof (UINTN);
+
+  //
+  // Align the stack on a natural boundary.
+  //
+  VmContext.Gpr[0] &= ~(VM_REGISTER)(sizeof (UINTN) - 1);
+
+  //
+  // Put a magic value in the stack gap, then adjust down again.
+  //
+  *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) VM_STACK_KEY_VALUE;
+  VmContext.StackMagicPtr             = (UINTN *) (UINTN) VmContext.Gpr[0];
+
+  //
+  // The stack upper to LowStackTop belongs to the VM.
+  //
+  VmContext.LowStackTop   = (UINTN) VmContext.Gpr[0];
+
+  //
+  // For the worst case, assume there are 4 arguments passed in registers, store
+  // them to VM's stack.
+  //
+  PushU32 (&VmContext, (UINT32) Args5_16[11]);
+  PushU32 (&VmContext, (UINT32) Args5_16[10]);
+  PushU32 (&VmContext, (UINT32) Args5_16[9]);
+  PushU32 (&VmContext, (UINT32) Args5_16[8]);
+  PushU32 (&VmContext, (UINT32) Args5_16[7]);
+  PushU32 (&VmContext, (UINT32) Args5_16[6]);
+  PushU32 (&VmContext, (UINT32) Args5_16[5]);
+  PushU32 (&VmContext, (UINT32) Args5_16[4]);
+  PushU32 (&VmContext, (UINT32) Args5_16[3]);
+  PushU32 (&VmContext, (UINT32) Args5_16[2]);
+  PushU32 (&VmContext, (UINT32) Args5_16[1]);
+  PushU32 (&VmContext, (UINT32) Args5_16[0]);
+  PushU32 (&VmContext, (UINT32) Arg4);
+  PushU32 (&VmContext, (UINT32) Arg3);
+  PushU32 (&VmContext, (UINT32) Arg2);
+  PushU32 (&VmContext, (UINT32) Arg1);
+
+  //
+  // Interpreter assumes 64-bit return address is pushed on the stack.
+  // Arm does not do this so pad the stack accordingly.
+  //
+  PushU32 (&VmContext, 0x0UL);
+  PushU32 (&VmContext, 0x0UL);
+  PushU32 (&VmContext, 0x12345678UL);
+  PushU32 (&VmContext, 0x87654321UL);
+
+  //
+  // For Arm, this is where we say our return address is
+  //
+  VmContext.StackRetAddr  = (UINT64) VmContext.Gpr[0];
+
+  //
+  // We need to keep track of where the EBC stack starts. This way, if the EBC
+  // accesses any stack variables above its initial stack setting, then we know
+  // it's accessing variables passed into it, which means the data is on the
+  // VM's stack.
+  // When we're called, on the stack (high to low) we have the parameters, the
+  // return address, then the saved ebp. Save the pointer to the return address.
+  // EBC code knows that's there, so should look above it for function parameters.
+  // The offset is the size of locals (VMContext + Addr + saved ebp).
+  // Note that the interpreter assumes there is a 16 bytes of return address on
+  // the stack too, so adjust accordingly.
+  //  VmContext.HighStackBottom = (UINTN)(Addr + sizeof (VmContext) + sizeof (Addr));
+  //
+
+  //
+  // Begin executing the EBC code
+  //
+  EbcDebuggerHookEbcInterpret (&VmContext);
+  EbcExecute (&VmContext);
+
+  //
+  // Return the value in Gpr[7] unless there was an error
+  //
+  ReturnEBCStack(StackIndex);
+  return (UINT64) VmContext.Gpr[7];
+}
+
+
+/**
+  Begin executing an EBC image.
+
+  @param  ImageHandle      image handle for the EBC application we're executing
+  @param  SystemTable      standard system table passed into an driver's entry
+                           point
+  @param  EntryPoint       The entrypoint of EBC code.
+
+  @return The value returned by the EBC application we're going to run.
+
+**/
+UINT64
+EFIAPI
+ExecuteEbcImageEntryPoint (
+  IN EFI_HANDLE           ImageHandle,
+  IN EFI_SYSTEM_TABLE     *SystemTable,
+  IN UINTN                EntryPoint
+  )
+{
+  //
+  // Create a new VM context on the stack
+  //
+  VM_CONTEXT  VmContext;
+  UINTN       Addr;
+  EFI_STATUS  Status;
+  UINTN       StackIndex;
+
+  //
+  // Get the EBC entry point
+  //
+  Addr = EntryPoint;
+
+  //
+  // Now clear out our context
+  //
+  ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));
+
+  //
+  // Save the image handle so we can track the thunks created for this image
+  //
+  VmContext.ImageHandle = ImageHandle;
+  VmContext.SystemTable = SystemTable;
+
+  //
+  // Set the VM instruction pointer to the correct location in memory.
+  //
+  VmContext.Ip = (VMIP) Addr;
+
+  //
+  // Initialize the stack pointer for the EBC. Get the current system stack
+  // pointer and adjust it down by the max needed for the interpreter.
+  //
+
+  //
+  // Allocate stack pool
+  //
+  Status = GetEBCStack(ImageHandle, &VmContext.StackPool, &StackIndex);
+  if (EFI_ERROR(Status)) {
+    return Status;
+  }
+  VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE);
+  VmContext.Gpr[0] = (UINT64)(UINTN) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE);
+  VmContext.HighStackBottom = (UINTN)VmContext.Gpr[0];
+  VmContext.Gpr[0] -= sizeof (UINTN);
+
+  //
+  // Put a magic value in the stack gap, then adjust down again
+  //
+  *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) VM_STACK_KEY_VALUE;
+  VmContext.StackMagicPtr             = (UINTN *) (UINTN) VmContext.Gpr[0];
+
+  //
+  // Align the stack on a natural boundary
+  //  VmContext.Gpr[0] &= ~(sizeof(UINTN) - 1);
+  //
+  VmContext.LowStackTop   = (UINTN) VmContext.Gpr[0];
+  VmContext.Gpr[0] -= sizeof (UINTN);
+  *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) SystemTable;
+  VmContext.Gpr[0] -= sizeof (UINTN);
+  *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) ImageHandle;
+
+  VmContext.Gpr[0] -= 16;
+  VmContext.StackRetAddr  = (UINT64) VmContext.Gpr[0];
+  //
+  // VM pushes 16-bytes for return address. Simulate that here.
+  //
+
+  //
+  // Begin executing the EBC code
+  //
+  EbcDebuggerHookExecuteEbcImageEntryPoint (&VmContext);
+  EbcExecute (&VmContext);
+
+  //
+  // Return the value in Gpr[7] unless there was an error
+  //
+  ReturnEBCStack(StackIndex);
+  return (UINT64) VmContext.Gpr[7];
+}
+
+
+/**
+  Create thunks for an EBC image entry point, or an EBC protocol service.
+
+  @param  ImageHandle           Image handle for the EBC image. If not null, then
+                                we're creating a thunk for an image entry point.
+  @param  EbcEntryPoint         Address of the EBC code that the thunk is to call
+  @param  Thunk                 Returned thunk we create here
+  @param  Flags                 Flags indicating options for creating the thunk
+
+  @retval EFI_SUCCESS           The thunk was created successfully.
+  @retval EFI_INVALID_PARAMETER The parameter of EbcEntryPoint is not 16-bit
+                                aligned.
+  @retval EFI_OUT_OF_RESOURCES  There is not enough memory to created the EBC
+                                Thunk.
+  @retval EFI_BUFFER_TOO_SMALL  EBC_THUNK_SIZE is not larger enough.
+
+**/
+EFI_STATUS
+EbcCreateThunks (
+  IN EFI_HANDLE           ImageHandle,
+  IN VOID                 *EbcEntryPoint,
+  OUT VOID                **Thunk,
+  IN  UINT32              Flags
+  )
+{
+  EBC_INSTRUCTION_BUFFER       *InstructionBuffer;
+
+  //
+  // Check alignment of pointer to EBC code
+  //
+  if ((UINT32) (UINTN) EbcEntryPoint & 0x01) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  InstructionBuffer = AllocatePool (sizeof (EBC_INSTRUCTION_BUFFER));
+  if (InstructionBuffer == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  //
+  // Give them the address of our buffer we're going to fix up
+  //
+  *Thunk = InstructionBuffer;
+
+  //
+  // Copy whole thunk instruction buffer template
+  //
+  CopyMem (InstructionBuffer, &mEbcInstructionBufferTemplate,
+    sizeof (EBC_INSTRUCTION_BUFFER));
+
+  //
+  // Patch EbcEntryPoint and EbcLLEbcInterpret
+  //
+  InstructionBuffer->EbcEntryPoint = (UINT32)EbcEntryPoint;
+  if ((Flags & FLAG_THUNK_ENTRY_POINT) != 0) {
+    InstructionBuffer->EbcLlEntryPoint = (UINT32)EbcLLExecuteEbcImageEntryPoint;
+  } else {
+    InstructionBuffer->EbcLlEntryPoint = (UINT32)EbcLLEbcInterpret;
+  }
+
+  //
+  // Add the thunk to the list for this image. Do this last since the add
+  // function flushes the cache for us.
+  //
+  EbcAddImageThunk (ImageHandle, InstructionBuffer,
+    sizeof (EBC_INSTRUCTION_BUFFER));
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  This function is called to execute an EBC CALLEX instruction.
+  The function check the callee's content to see whether it is common native
+  code or a thunk to another piece of EBC code.
+  If the callee is common native code, use EbcLLCAllEXASM to manipulate,
+  otherwise, set the VM->IP to target EBC code directly to avoid another VM
+  be startup which cost time and stack space.
+
+  @param  VmPtr            Pointer to a VM context.
+  @param  FuncAddr         Callee's address
+  @param  NewStackPointer  New stack pointer after the call
+  @param  FramePtr         New frame pointer after the call
+  @param  Size             The size of call instruction
+
+**/
+VOID
+EbcLLCALLEX (
+  IN VM_CONTEXT   *VmPtr,
+  IN UINTN        FuncAddr,
+  IN UINTN        NewStackPointer,
+  IN VOID         *FramePtr,
+  IN UINT8        Size
+  )
+{
+  CONST EBC_INSTRUCTION_BUFFER *InstructionBuffer;
+
+  //
+  // Processor specific code to check whether the callee is a thunk to EBC.
+  //
+  InstructionBuffer = (EBC_INSTRUCTION_BUFFER *)FuncAddr;
+
+  if (CompareMem (InstructionBuffer, &mEbcInstructionBufferTemplate,
+        sizeof(EBC_INSTRUCTION_BUFFER) - 2 * sizeof (UINT32)) == 0) {
+    //
+    // The callee is a thunk to EBC, adjust the stack pointer down 16 bytes and
+    // put our return address and frame pointer on the VM stack.
+    // Then set the VM's IP to new EBC code.
+    //
+    VmPtr->Gpr[0] -= 8;
+    VmWriteMemN (VmPtr, (UINTN) VmPtr->Gpr[0], (UINTN) FramePtr);
+    VmPtr->FramePtr = (VOID *) (UINTN) VmPtr->Gpr[0];
+    VmPtr->Gpr[0] -= 8;
+    VmWriteMem64 (VmPtr, (UINTN) VmPtr->Gpr[0], (UINT64) (UINTN) (VmPtr->Ip + Size));
+
+    VmPtr->Ip = (VMIP) InstructionBuffer->EbcEntryPoint;
+  } else {
+    //
+    // The callee is not a thunk to EBC, call native code,
+    // and get return value.
+    //
+    // Note that we are not able to distinguish which part of the interval
+    // [NewStackPointer, FramePtr] consists of stacked function arguments for
+    // this call, and which part simply consists of locals in the caller's
+    // stack frame. All we know is that there is an 8 byte gap at the top that
+    // we can ignore.
+    //
+    VmPtr->Gpr[7] = EbcLLCALLEXNative (FuncAddr, NewStackPointer, FramePtr - 8);
+
+    //
+    // Advance the IP.
+    //
+    VmPtr->Ip += Size;
+  }
+}
+
diff --git a/MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf b/MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf
index ce413c0b25f6..b55538304605 100644
--- a/MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf
+++ b/MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf
@@ -25,7 +25,7 @@ [Defines]
 #
 # The following information is for reference only and not required by the build tools.
 #
-#  VALID_ARCHITECTURES           = IA32 X64 IPF AARCH64
+#  VALID_ARCHITECTURES           = IA32 X64 IPF ARM AARCH64
 #
 
 [Sources]
@@ -86,6 +86,10 @@ [Sources.AARCH64]
   AArch64/EbcSupport.c
   AArch64/EbcLowLevel.S
 
+[Sources.ARM]
+  Arm/EbcSupport.c
+  Arm/EbcLowLevel.S
+
 [Packages]
   MdePkg/MdePkg.dec
   MdeModulePkg/MdeModulePkg.dec
diff --git a/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbDisasmSupport.h b/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbDisasmSupport.h
index af5a7cab99f9..db3b0a1f454f 100644
--- a/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbDisasmSupport.h
+++ b/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbDisasmSupport.h
@@ -19,10 +19,10 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 
 #define EDB_BYTECODE_NUMBER_IN_LINE     5
 
-#ifdef EFI32
+#if defined (MDE_CPU_IA32) || defined (MDE_CPU_ARM)
 #define EDB_PRINT_ADDRESS_FORMAT    L"%08x: "
 #else
-// To use 012l instead of 016l because space is not enough
+// We use 012l instead of 016l due to space constraints
 #define EDB_PRINT_ADDRESS_FORMAT    L"%012lx: "
 #endif
 
diff --git a/MdeModulePkg/Universal/EbcDxe/EbcDebuggerConfig.inf b/MdeModulePkg/Universal/EbcDxe/EbcDebuggerConfig.inf
index 0d931a46f01a..c08ad17dd1c8 100644
--- a/MdeModulePkg/Universal/EbcDxe/EbcDebuggerConfig.inf
+++ b/MdeModulePkg/Universal/EbcDxe/EbcDebuggerConfig.inf
@@ -25,7 +25,7 @@ [Defines]
 #
 # The following information is for reference only and not required by the build tools.
 #
-#  VALID_ARCHITECTURES           = IA32 X64 IPF AARCH64
+#  VALID_ARCHITECTURES           = IA32 X64 IPF ARM AARCH64
 #
 
 [Sources]
diff --git a/MdeModulePkg/Universal/EbcDxe/EbcDxe.inf b/MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
index d11888e25f92..78e058b6cbc6 100644
--- a/MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
+++ b/MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
@@ -29,7 +29,7 @@ [Defines]
 #
 # The following information is for reference only and not required by the build tools.
 #
-#  VALID_ARCHITECTURES           = IA32 X64 IPF AARCH64
+#  VALID_ARCHITECTURES           = IA32 X64 IPF ARM AARCH64
 #
 
 [Sources]
@@ -57,6 +57,10 @@ [Sources.IPF]
   Ipf/EbcSupport.c
   Ipf/EbcLowLevel.s
 
+[Sources.ARM]
+  Arm/EbcSupport.c
+  Arm/EbcLowLevel.S
+
 [Sources.AARCH64]
   AArch64/EbcSupport.c
   AArch64/EbcLowLevel.S
-- 
2.9.3.windows.2



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

* [PATCH 3/5] MdeModulePkg/EbcDxe: add a stack tracker for ARM EBC->native support
  2017-01-24 12:30 [PATCH 1/5] MdeModulePkg/EbcDxe: allow VmReadIndex##() to return a decoded index Pete Batard
  2017-01-24 12:30 ` [PATCH 2/5] MdeModulePkg/EbcDxe: add ARM support Pete Batard
@ 2017-01-24 12:30 ` Pete Batard
  2017-01-24 12:30 ` [PATCH 4/5] MdeModulePkg/EbcDxe: add call signatures for ARM native->EBC support Pete Batard
  2017-01-24 12:30 ` [PATCH 5/5] BaseTools: add scripts to generate EBC call signatures Pete Batard
  3 siblings, 0 replies; 5+ messages in thread
From: Pete Batard @ 2017-01-24 12:30 UTC (permalink / raw)
  To: edk2-devel

* This patch fixes the CALLEX to native vs ARM calling convention issue by
  tracking whether each potential call argument is a natural or a 64-bit
  value.
* This is accomplished by monitoring stack pointer operations, including
  arithmetic or single stack buffer reallocation, through a separate stack
  tracker. The stack tracker is then used, at calltime to determine 64-bit
  arguments that need to be aligned.
* While currently ARM specific, the stack tracking is added in a generic
  manner, so that any future platform that requires a similar mechanism may
  do so with minimal changes.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Pete Batard <pete@akeo.ie>
---
 MdeModulePkg/Include/Protocol/EbcVmTest.h          |   2 +
 MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S    | 121 ++--
 .../Universal/EbcDxe/Arm/EbcStackTracker.c         | 634 +++++++++++++++++++++
 MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c     | 124 +++-
 MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf      |   4 +
 MdeModulePkg/Universal/EbcDxe/EbcDxe.inf           |   4 +
 MdeModulePkg/Universal/EbcDxe/EbcExecute.c         | 155 ++++-
 MdeModulePkg/Universal/EbcDxe/EbcStackTracker.c    |  65 +++
 8 files changed, 1038 insertions(+), 71 deletions(-)
 create mode 100644 MdeModulePkg/Universal/EbcDxe/Arm/EbcStackTracker.c
 create mode 100644 MdeModulePkg/Universal/EbcDxe/EbcStackTracker.c

diff --git a/MdeModulePkg/Include/Protocol/EbcVmTest.h b/MdeModulePkg/Include/Protocol/EbcVmTest.h
index 9eedca1906a2..12d5e5217059 100644
--- a/MdeModulePkg/Include/Protocol/EbcVmTest.h
+++ b/MdeModulePkg/Include/Protocol/EbcVmTest.h
@@ -111,6 +111,8 @@ typedef struct {
   UINTN             ImageBase;
   VOID              *StackPool;
   VOID              *StackTop;
+  VOID              *StackTracker;          ///< pointer to an optional, opaque and arch-specific
+                                            ///  structure, which may be used to track stack ops.
 } VM_CONTEXT;
 
 /**
diff --git a/MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S b/MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S
index cb3c11f71673..dc9c3946ced2 100644
--- a/MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S
+++ b/MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S
@@ -3,6 +3,7 @@
 //  This code provides low level routines that support the Virtual Machine
 //  for option ROMs.
 //
+//  Copyright (c) 2016, Pete Batard. All rights reserved.<BR>
 //  Copyright (c) 2016, Linaro, Ltd. All rights reserved.<BR>
 //  Copyright (c) 2015, The Linux Foundation. All rights reserved.<BR>
 //  Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>
@@ -20,11 +21,11 @@
     .thumb
     .syntax unified
 
-ASM_GLOBAL ASM_PFX(EbcLLCALLEXNative)
+ASM_GLOBAL ASM_PFX(EbcLLCALLEXNativeArm)
 ASM_GLOBAL ASM_PFX(EbcLLEbcInterpret)
 ASM_GLOBAL ASM_PFX(EbcLLExecuteEbcImageEntryPoint)
 
-INTERWORK_FUNC(EbcLLCALLEXNative)
+INTERWORK_FUNC(EbcLLCALLEXNativeArm)
 INTERWORK_FUNC(EbcLLEbcInterpret)
 INTERWORK_FUNC(EbcLLExecuteEbcImageEntryPoint)
 
@@ -34,62 +35,96 @@ ASM_GLOBAL ASM_PFX(mEbcInstructionBufferTemplate)
 // EbcLLCALLEX
 //
 // This function is called to execute an EBC CALLEX instruction.
-// This instruction requires that we thunk out to external native
-// code. For ARM, we copy the VM stack into the main stack and then pop
-// the first 4 arguments off according to the ARM Procedure Call Standard
-// On return, we restore the stack pointer to its original location.
+// This instruction requires that we thunk out to external native code.
+// Note that due to the ARM Procedure Call Standard, we may have to align
+// 64-bit arguments to an even register or dword aligned stack address,
+// which is what the extra ArgLayout parameter is used for.
+// Also, to optimize for speed, we arbitrarily limit to 16 the maximum
+// number of arguments a native call can have.
 //
 //****************************************************************************
-// UINTN EbcLLCALLEXNative(UINTN FuncAddr, UINTN NewStackPointer, VOID *FramePtr)
-ASM_PFX(EbcLLCALLEXNative):
-    mov     ip, r0                  // Preserve r0
+// UINTN EbcLLCALLEXNativeArm(UINTN FuncAddr, UINTN NewStackPointer,
+//                            VOID *FramePtr, UINT16 ArgLayout)
+ASM_PFX(EbcLLCALLEXNativeArm):
+
+    mov     ip, r1                  // Use ip as our argument walker
+    push    {r0, r4-r6}
+    mov     r4, r2                  // Keep a copy of FramePtr
+    mov     r5, r3                  // Keep a copy of ArgLayout
+    mov     r6, #2                  // Arg counter (2 for r0 and r2)
 
     //
-    // If the EBC stack frame is smaller than or equal to 16 bytes, we know there
-    // are no stacked arguments #5 and beyond that we need to copy to the native
-    // stack. In this case, we can perform a tail call which is much more
-    // efficient, since there is no need to touch the native stack at all.
+    // Process the register arguments, skipping r1 and r3
+    // as needed, according to the argument layout.
     //
-    sub     r3, r2, r1              // Length = NewStackPointer - FramePtr
-    cmp     r3, #16
-    bgt     0f
-
-    ldrd    r2, r3, [r1, #8]
-    ldrd    r0, r1, [r1]
-
+    lsrs    r5, r5, #1
+    bcc     0f                      // Is our next argument 64-bit?
+    ldr     r0, [ip], #4            // Yes => fill in r0-r1
+    ldr     r1, [ip], #4
+    b       1f
+0:  ldr     r0, [ip], #4            // No => fill in r0
+    lsrs    r5, r5, #1
+    bcs     2f                      // Is our next argument 64-bit?
+    ldr     r1, [ip], #4            // No => fill in r1
+    add     r6, r6, #1              // Increment arg counter for r1
+1:  lsrs    r5, r5, #1
+    bcc     0f                      // Is our next argument 64-bit?
+2:  ldr     r2, [ip], #4            // Yes => fill in r2-r3
+    ldr     r3, [ip], #4
+    b       1f
+0:  ldr     r2, [ip], #4            // No => fill in r2
+    tst     r5, #1
+    bne     1f                      // Is our next argument 64-bit?
+    ldr     r3, [ip], #4            // No => fill in r3
+    lsr     r5, r5, #1
+    add     r6, r6, #1              // Increment arg counter for r3
+1:  cmp     r4, ip
+    bgt     0f                      // More args?
+    pop     {ip}                    // No => perform a tail call
+    pop     {r4-r6}
     bx      ip
 
     //
-    // More than 16 bytes: we need to build the full native stack frame and copy
-    // the part of the VM stack exceeding 16 bytes (which may contain stacked
-    // arguments) to the native stack
+    // Cannot perform a tail call => We need to properly enqueue (and
+    // align) all EBC stack parameters before we invoke the native call.
     //
-0:  push    {r4, lr}
-    mov     r4, sp
+0:  push    {r7-r10, lr}
+    mov     r10, sp                 // Preserve original sp
+    sub     r7, r10, #116           // Space for 14 64-bit args (+1 word)
+    and     r7, r7, #0xfffffff8     // Start with an aligned stack
+    mov     sp, r7
 
     //
-    // Ensure that the stack pointer remains 8 byte aligned,
-    // even if the size of the VM stack frame is not a multiple of 8
+    // Duplicate EBC data onto the local stack:
+    // ip = EBC arg walker
+    // r4 = top of EBC stack frame
+    // r5 = arg layout
+    // r6 = arg counter
+    // r7 = local stack pointer
     //
-    add     r1, r1, #16             // Skip over [potential] reg params
-    tst     r3, #7                  // Multiple of 8?
-    beq     1f
-    ldr     r3, [r2, #-4]!          // No? Then push one word
-    str     r3, [sp, #-8]!          // ... but use two slots
+0:  add     r6, r6, #1              // Increment the arg counter
+    lsrs    r5, r5, #1
+    bcs     1f                      // Is the current argument 64 bit?
+    ldr     r8, [ip], #4            // No? Then just copy it onstack
+    str     r8, [r7], #4
     b       2f
+1:  tst     r7, #7                  // Yes. Is SP aligned to 8 bytes?
+    beq     1f
+    add     r7, r7, #4              // No? Realign.
+1:  ldr     r8, [ip], #4            // EBC stack may not be aligned for ldrd...
+    ldr     r9, [ip], #4
+    strd    r8, r9, [r7], #8        // ...but the local stack is.
+2:  cmp     r6, #16                 // More than 16 arguments processed?
+    bge     0f
+    cmp     r4, ip                  // Reached the top of the EBC stack frame?
+    bgt     0b
 
-1:  ldrd    r0, r3, [r2, #-8]!
-    strd    r0, r3, [sp, #-8]!
-2:  cmp     r2, r1
-    bgt     1b
-
-    ldrd    r2, r3, [r1, #-8]
-    ldrd    r0, r1, [r1, #-16]
-
+0:  ldr     ip, [r10, #20]          // Set the target address in ip
     blx     ip
-
-    mov     sp, r4
-    pop     {r4, pc}
+    mov     sp, r10                 // Restore the stack, dequeue and return
+    pop     {r7-r10, ip}
+    pop     {r3, r4-r6}             // Destack with r3, as r0 may contain a return value
+    mov     pc, ip
 
 //****************************************************************************
 // EbcLLEbcInterpret
diff --git a/MdeModulePkg/Universal/EbcDxe/Arm/EbcStackTracker.c b/MdeModulePkg/Universal/EbcDxe/Arm/EbcStackTracker.c
new file mode 100644
index 000000000000..ab42d8ad4e92
--- /dev/null
+++ b/MdeModulePkg/Universal/EbcDxe/Arm/EbcStackTracker.c
@@ -0,0 +1,634 @@
+/** @file
+  This module contains the stack tracking routines used for parameter
+  processing and alignment on ARM.
+
+Copyright (c) 2016, Pete Batard. All rights reserved.<BR>
+
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution.  The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "EbcInt.h"
+#include "EbcExecute.h"
+
+//
+// Amount of space to be used by the stack argument tracker
+// In most cases, less than 2 bits should be used for every 32 bits of
+// stack data and we can grow our buffer if needed, so start at 1/64th
+//
+#define STACK_TRACKER_SIZE      (STACK_POOL_SIZE / 64)
+#define STACK_TRACKER_SIZE_MAX  (STACK_POOL_SIZE / 8)
+
+//
+// Stack tracking data structure, used to compute parameter alignment.
+//
+typedef struct {
+  UINT8     *Data;              ///< stack tracker data buffer
+  INTN      Size;               ///< size of the stack tracker data buffer, in bytes
+  INTN      Index;              ///< current stack tracker index, in 1/4th bytes
+  INTN      OrgIndex;           ///< copy of the index, used on stack buffer switch
+  UINTN     OrgStackPointer;    ///< copy of the stack pointer, used on stack buffer switch
+} EBC_STACK_TRACKER;
+
+/**
+  Allocate a stack tracker structure and initialize it.
+
+  @param VmPtr         The pointer to current VM context.
+
+  @retval EFI_OUT_OF_RESOURCES  Not enough memory to set the stack tracker.
+  @retval EFI_SUCCESS           The stack tracker was initialized successfully.
+
+**/
+EFI_STATUS
+AllocateStackTracker (
+  IN VM_CONTEXT *VmPtr
+  )
+{
+  EBC_STACK_TRACKER *StackTracker;
+
+  StackTracker = AllocateZeroPool(sizeof(EBC_STACK_TRACKER));
+  if (StackTracker == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+  StackTracker->Size = STACK_TRACKER_SIZE;
+  StackTracker->Data = AllocatePool(StackTracker->Size);
+  if (StackTracker->Data == NULL) {
+    FreePool(StackTracker);
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  //
+  // Add tracking for EfiMain() call just in case
+  //
+  StackTracker->Data[0] = 0x05; // 2 x UINT64, 2 x UINTN
+  StackTracker->Index = 4;
+
+  VmPtr->StackTracker = (VOID*) StackTracker;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Free a stack tracker structure.
+
+  @param VmPtr         The pointer to current VM context.
+**/
+VOID
+FreeStackTracker (
+  IN VM_CONTEXT *VmPtr
+  )
+{
+  EBC_STACK_TRACKER *StackTracker;
+
+  StackTracker = (EBC_STACK_TRACKER*) VmPtr->StackTracker;
+
+  FreePool(StackTracker->Data);
+  FreePool(StackTracker);
+  VmPtr->StackTracker = NULL;
+}
+
+/**
+  Return the argument layout for the current function call, according
+  to the current stack tracking data.
+  The first argument is at bit 0, with the 16th parameter at bit 15,
+  with a bit set to 1 if an argument is 64 bit, 0 otherwise.
+
+  @param VmPtr         The pointer to current VM context.
+
+  @return              A 16 bit argument layout.
+**/
+UINT16
+GetArgLayout (
+  IN VM_CONTEXT *VmPtr
+  )
+{
+  EBC_STACK_TRACKER *StackTracker;
+  UINT16 ArgLayout, Mask;
+  INTN Index;
+
+  StackTracker = (EBC_STACK_TRACKER*) VmPtr->StackTracker;
+
+  ASSERT (StackTracker->Data != NULL);
+  ASSERT (StackTracker->Index >= 0);
+
+  // One major issue we have on Arm is that, if a mix of natural and
+  // 64-bit arguments are stacked as parameters for a native call, we risk
+  // running afoul of the AAPCS (the ARM calling convention) which mandates
+  // that the first 2 to 4 arguments are passed as register, and that any
+  // 64-bit argument *MUST* start either on an even register or at an 8-byte
+  // aligned address.
+  //
+  // So if, for instance, we have a natural parameter (32-bit) in Arg0 and
+  // a 64-bit parameter in Arg1, then, after we copy the data onto r0, we
+  // must skip r1 so that Arg1 starts at register r2. Similarly, we may have
+  // to skip words on stack for 64-bit parameter alignment.
+  //
+  // This is where our stack tracker comes into play, as it tracks EBC stack
+  // manipulations and allows us to find whether each of the (potential)
+  // arguments being passed to a native CALLEX is 64-bit or natural. As
+  // a reminder, and as per the specs (UEFI 2.6, section 21.9.3), 64-bit
+  // or natural are the ONLY types of arguments that are allowed when
+  // performing EBC function calls, inluding native ones (in which case any
+  // argument of 32 bit or less must be stacked as a natural).
+  //
+  // Through the stack tracker, we can retreive the last 16 argument types
+  // (decoded from the 2 bit sequences), which we convert to a 16-bit value
+  // that gives us the argument layout.
+  //
+  ArgLayout = 0;
+  Mask = 1;
+  for (Index = StackTracker->Index - 1; Index >= StackTracker->Index - 16; Index--) {
+    if ((Index / 4) < 0) {
+      break; // Don't underflow the tracker.
+    }
+    //
+    // Actual function parameters are stored as 2 bit sequences in our
+    // tracker, with 00b indicating a 64-bit parameter and 01b a natural.
+    // When converting this to ArgLayout, we set the relevant arg position
+    // bit to 1, for a 64-bit arg, or 0, for a natural.
+    // Also, since there's little point in avoiding to process 4-bit
+    // sequences (for stack values that aren't natural or 64-bit, and thus
+    // can't be used as arguments) we also process them as 2-bit.
+    //
+    if ((StackTracker->Data[Index / 4] & (1 << (2 * (3 - (Index % 4))))) == 0) {
+      ArgLayout |= Mask;
+    }
+    Mask <<= 1;
+  }
+  return ArgLayout;
+}
+
+/**
+  Returns the current stack tracker entry.
+
+  @param StackTracker   A pointer to the stack tracker struct.
+
+  @return  The decoded stack tracker index [0x00, 0x08].
+
+  The decoding of stack tracker entries operates as follows:
+    00b                        -> 0000b
+    01b                        -> 1000b
+    1xb preceded by yzb        -> 0xyzb
+    (e.g. 11b preceded by 10b  -> 0110b)
+
+**/
+UINT8
+GetStackTrackerEntry (
+  IN EBC_STACK_TRACKER *StackTracker
+  )
+{
+  UINT8 Index, PreviousIndex;
+
+  if (StackTracker->Index < 0) {
+    //
+    // Anything prior to tracking is considered aligned to 64 bits
+    //
+    return 0x00;
+  }
+
+  Index = StackTracker->Data[(StackTracker->Index - 1) / 4];
+  Index >>= 6 - (2 * ((StackTracker->Index - 1) % 4));
+  Index &= 0x03;
+
+  if (Index == 0x01) {
+    Index = 0x08;
+  } else if ((Index & 0x02) != 0) {
+    PreviousIndex = StackTracker->Data[(StackTracker->Index - 2) / 4];
+    PreviousIndex >>= 6 - (2 * ((StackTracker->Index - 2) % 4));
+    Index = ((Index << 2) & 0x04) | (PreviousIndex & 0x03);
+  }
+
+  return Index;
+}
+
+/**
+  Add a single new stack tracker entry.
+
+  @param StackTracker   A pointer to the stack tracker struct.
+  @param Value          The value to be encoded.
+
+  @retval EFI_OUT_OF_RESOURCES  Not enough memory to grow the stack tracker.
+  @retval EFI_SUCCESS           The entry was added successfully.
+
+  For reference, each 2-bit index sequence is stored as follows:
+    Stack tracker byte:     byte 0   byte 1    byte 3
+    Stack tracker index:  [0|1|2|3] [4|5|6|7] [8|9|...]
+
+  Valid values are in [0x00, 0x08] and get encoded as:
+    0000b -> 00b      (single 2-bit sequence)
+    0001b -> 01b 10b  (dual 2-bit sequence)
+    0010b -> 10b 10b  (dual 2-bit sequence)
+    0011b -> 11b 10b  (dual 2-bit sequence)
+    0100b -> 00b 11b  (dual 2-bit sequence)
+    0101b -> 01b 11b  (dual 2-bit sequence)
+    0110b -> 10b 11b  (dual 2-bit sequence)
+    0111b -> 11b 11b  (dual 2-bit sequence)
+    1000b -> 01b      (single 2-bit sequence)
+**/
+EFI_STATUS
+AddStackTrackerEntry (
+  IN EBC_STACK_TRACKER *StackTracker,
+  IN UINT8             Value
+  )
+{
+  UINT8 Pass, Data;
+
+  ASSERT (Value <= 0x08);
+  if (Value == 0x08) {
+    Data = 0x01;
+  } else {
+    Data = Value & 0x03;
+  }
+
+  for (Pass = 0; Pass < 2; Pass++) {
+    if ((StackTracker->Index / 4) >= StackTracker->Size) {
+      //
+      // Grow the stack tracker buffer
+      //
+      StackTracker->Data = ReallocatePool(StackTracker->Size,
+            StackTracker->Size*2, StackTracker->Data);
+      if (StackTracker->Data == NULL) {
+        return EFI_OUT_OF_RESOURCES;
+      }
+      StackTracker->Size *= 2;
+      ASSERT (StackTracker->Size <= STACK_TRACKER_SIZE_MAX);
+    }
+
+    //
+    // Clear bits and add our value
+    //
+    StackTracker->Data[StackTracker->Index / 4] &=
+          0xFC << (6 - 2 * (StackTracker->Index % 4));
+    StackTracker->Data[StackTracker->Index / 4] |=
+          Data << (6 - 2 * (StackTracker->Index % 4));
+    StackTracker->Index++;
+    if ((Value >= 0x01) && (Value <= 0x07)) {
+      //
+      // 4-bits needed => Append the extra 2 bit value
+      //
+      Data = (Value >> 2) | 0x02;
+    } else {
+      //
+      // 2-bit encoding was enough
+      //
+      break;
+    }
+  }
+  return EFI_SUCCESS;
+}
+
+/**
+  Insert 'Count' number of 'Value' bytes into the stack tracker.
+  This expects the current entry to be aligned to byte boundary.
+
+  @param StackTracker   A pointer to the stack tracker struct.
+  @param Value          The byte value to be inserted.
+  @param Count          The number of times the value should be repeated.
+
+  @retval EFI_OUT_OF_RESOURCES  Not enough memory to grow the stack tracker.
+  @retval EFI_SUCCESS           The entries were added successfully.
+
+**/
+EFI_STATUS
+AddStackTrackerBytes (
+  IN EBC_STACK_TRACKER *StackTracker,
+  IN UINT8             Value,
+  IN INTN              Count
+  )
+{
+  UINTN ByteNum, NewSize;
+  INTN UpdatedIndex;
+
+  //
+  // Byte alignement should have been sorted prior to this call
+  //
+  ASSERT (StackTracker->Index % 4 == 0);
+
+  UpdatedIndex = StackTracker->Index + 4 * Count;
+  if (UpdatedIndex >= StackTracker->Size) {
+    //
+    // Grow the stack tracker buffer
+    //
+    for (NewSize = StackTracker->Size * 2; NewSize <= UpdatedIndex;
+      NewSize *= 2);
+    ASSERT (NewSize <= STACK_TRACKER_SIZE_MAX);
+    StackTracker->Data = ReallocatePool(StackTracker->Size, NewSize,
+          StackTracker->Data);
+    if (StackTracker->Data == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+    StackTracker->Size = NewSize;
+  }
+  for (ByteNum = 0; ByteNum < Count; ByteNum++) {
+    StackTracker->Data[(StackTracker->Index / 4) + ByteNum] = Value;
+  }
+  StackTracker->Index = UpdatedIndex;
+  return EFI_SUCCESS;
+}
+
+/**
+  Delete a single stack tracker entry.
+
+  @param StackTracker   A pointer to the stack tracker struct.
+
+  @retval EFI_UNSUPPORTED       The stack tracker is being underflown due
+                                to unbalanced stack operations.
+  @retval EFI_SUCCESS           The index was added successfully.
+**/
+EFI_STATUS
+DelStackTrackerEntry (
+  IN EBC_STACK_TRACKER *StackTracker
+  )
+{
+  UINT8 Index;
+
+  ASSERT (StackTracker->Index > 0);
+
+  //
+  // Don't care about clearing the used bits, just update the index
+  //
+  StackTracker->Index--;
+  Index = StackTracker->Data[StackTracker->Index / 4];
+  Index >>= 6 - (2 * (StackTracker->Index % 4));
+
+  if ((Index & 0x02) != 0) {
+    //
+    // 4-bit sequence
+    //
+    StackTracker->Index--;
+  }
+  if (StackTracker->Index < 0) {
+    return EFI_UNSUPPORTED;
+  }
+  return EFI_SUCCESS;
+}
+
+/**
+  Update the stack tracker according to the latest natural and constant
+  value stack manipulation operations.
+
+  @param VmPtr         The pointer to current VM context.
+  @param NaturalUnits  The number of natural values that were pushed (>0) or
+                       popped (<0).
+  @param ConstUnits    The number of const bytes that were pushed (>0) or
+                       popped (<0).
+
+  @retval EFI_OUT_OF_RESOURCES  Not enough memory to grow the stack tracker.
+  @retval EFI_UNSUPPORTED       The stack tracker is being underflown due to
+                                unbalanced stack operations.
+  @retval EFI_SUCCESS           The stack tracker was updated successfully.
+
+**/
+EFI_STATUS
+UpdateStackTracker (
+  IN VM_CONTEXT  *VmPtr,
+  IN INTN        NaturalUnits,
+  IN INTN        ConstUnits
+  )
+{
+  EFI_STATUS Status;
+  UINT8 LastEntry;
+  EBC_STACK_TRACKER *StackTracker;
+
+  StackTracker = (EBC_STACK_TRACKER*) VmPtr->StackTracker;
+
+  //
+  // Mismatched signage should already have been filtered out.
+  //
+  ASSERT ( ((NaturalUnits >= 0) && (ConstUnits >= 0)) ||
+           ((NaturalUnits <= 0) && (ConstUnits <= 0)) );
+
+  while (NaturalUnits < 0) {
+    //
+    // Add natural indexes (1000b) into our stack tracker
+    // Note, we don't care if the previous entry was aligned as a non 64-bit
+    // aligned entry cannot be used as a call parameter in valid EBC code.
+    // This also adds the effect of re-aligning our data to 64 bytes, which
+    // will help speed up tracking of local stack variables (arrays, etc.)
+    //
+    if ((StackTracker->Index % 4 == 0) && (NaturalUnits <= -4)) {
+      //
+      // Optimize adding a large number of naturals, such as ones reserved
+      // for local function variables/arrays. 0x55 means 4 naturals.
+      //
+      Status = AddStackTrackerBytes (StackTracker, 0x55, -NaturalUnits / 4);
+      NaturalUnits -= 4 * (NaturalUnits / 4); // Beware of negative modulos
+    } else {
+      Status = AddStackTrackerEntry (StackTracker, 0x08);
+      NaturalUnits++;
+    }
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+  }
+
+  if (ConstUnits < 0) {
+    //
+    // Add constant indexes (0000b-0111b) into our stack tracker
+    // For constants, we do care if the previous entry was aligned to 64 bit
+    // since we need to include any existing non aligned indexes into the new
+    // set of (constant) indexes we are adding. Thus, if the last index is
+    // non zero (non 64-bit aligned) we just delete it and add the value to
+    // our constant.
+    //
+    LastEntry = GetStackTrackerEntry (StackTracker);
+    if ((LastEntry != 0x00) && (LastEntry != 0x08)) {
+      DelStackTrackerEntry (StackTracker);
+      ConstUnits -= LastEntry;
+    }
+
+    //
+    // Now, add as many 64-bit indexes as we can (0000b values)
+    //
+    while (ConstUnits <= -8) {
+      if ((ConstUnits <= -32) && (StackTracker->Index % 4 == 0)) {
+        //
+        // Optimize adding a large number of consts, such as ones reserved
+        // for local function variables/arrays. 0x00 means 4 64-bit consts.
+        //
+        Status = AddStackTrackerBytes (StackTracker, 0x00, -ConstUnits / 32);
+        ConstUnits -= 32 * (ConstUnits / 32); // Beware of negative modulos
+      } else {
+        Status = AddStackTrackerEntry (StackTracker, 0x00);
+        ConstUnits += 8;
+      }
+      if (EFI_ERROR (Status)) {
+        return Status;
+      }
+    }
+
+    //
+    // Add any remaining non 64-bit aligned bytes
+    //
+    if ((ConstUnits % 8) != 0) {
+      Status = AddStackTrackerEntry (StackTracker, (-ConstUnits) % 8);
+      if (EFI_ERROR (Status)) {
+        return Status;
+      }
+    }
+  }
+
+  while ((NaturalUnits > 0) || (ConstUnits > 0)) {
+    ASSERT(StackTracker->Index > 0);
+
+    //
+    // Delete natural/constant items from the stack tracker
+    //
+    if (StackTracker->Index % 4 == 0) {
+      //
+      // Speedup deletion for blocks of naturals/consts
+      //
+      while ((ConstUnits >= 32) &&
+            (StackTracker->Data[(StackTracker->Index-1) % 4] == 0x00)) {
+        //
+        // Start with consts since that's what we add last
+        //
+        StackTracker->Index -= 4;
+        ConstUnits -= 32;
+      }
+      while ((NaturalUnits >= 4) &&
+            (StackTracker->Data[(StackTracker->Index-1) % 4] == 0x55)) {
+        StackTracker->Index -= 4;
+        NaturalUnits -= 4;
+      }
+    }
+
+    if ((NaturalUnits == 0) && (ConstUnits == 0)) {
+      //
+      // May already have depleted our values through block processing above
+      //
+      break;
+    }
+
+    LastEntry = GetStackTrackerEntry (StackTracker);
+    Status = DelStackTrackerEntry (StackTracker);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    if (LastEntry == 0x08) {
+      if (NaturalUnits > 0) {
+        //
+        // Remove a natural and move on
+        //
+        NaturalUnits--;
+        continue;
+      }
+      //
+      // Got a natural while expecting const which may be the result of a
+      // "cloaked" stack operation (eg. R1 <- R0, stack ops on R1, R0 <- R1)
+      // which we monitor through the R0 delta converted to const. In this
+      // case just remove 4 const for each natural we find in the tracker.
+      //
+      LastEntry = 0x04;
+    } else if (ConstUnits <= 0) {
+      //
+      // Got a const while expecting a natural which may be the result of a
+      // "cloaked" stack operation => Substract 1 natural unit and add 4 to
+      // const units. Note that "cloaked" stack operations cannot break our
+      // tracking as the enqueuing of natural parameters is not something
+      // that can be concealed if one interprets the EBC specs correctly.
+      //
+      NaturalUnits--;
+      ConstUnits += 4;
+    }
+
+    if (LastEntry == 0x00) {
+      LastEntry = 0x08;
+    }
+    //
+    // Remove a set of const bytes
+    //
+    if (ConstUnits >= LastEntry) {
+      //
+      // Enough const bytes to remove at least one stack tracker entry
+      //
+      ConstUnits -= LastEntry;
+    } else {
+      //
+      // Not enough const bytes - need to add the remainder back
+      //
+      Status = AddStackTrackerEntry (StackTracker, LastEntry - ConstUnits);
+      ConstUnits = 0;
+      if (EFI_ERROR (Status)) {
+        return Status;
+      }
+    }
+  }
+
+  ASSERT(StackTracker->Index >= 0);
+  return EFI_SUCCESS;
+}
+
+/**
+  Update the stack tracker by computing the R0 delta.
+
+  @param VmPtr         The pointer to current VM context.
+  @param UpdatedR0     The new R0 value.
+
+  @retval EFI_OUT_OF_RESOURCES  Not enough memory to grow the stack tracker.
+  @retval EFI_UNSUPPORTED       The stack tracker is being underflown due to
+                                unbalanced stack operations.
+  @retval EFI_SUCCESS           The stack tracker was updated successfully.
+
+**/
+EFI_STATUS
+UpdateStackTrackerFromDelta (
+  IN VM_CONTEXT *VmPtr,
+  IN UINTN      UpdatedR0
+  )
+{
+  INTN StackPointerDelta;
+  EBC_STACK_TRACKER *StackTracker;
+
+  StackTracker = (EBC_STACK_TRACKER*) VmPtr->StackTracker;
+
+  //
+  // Check if the updated R0 is still in our original stack buffer.
+  //
+  if ((StackTracker->OrgIndex == 0) && ((UpdatedR0 < (UINTN) VmPtr->StackPool) ||
+      (UpdatedR0 >= (UINTN) VmPtr->StackPool + STACK_POOL_SIZE))) {
+    //
+    // We are swicthing from the default stack buffer to a newly allocated
+    // one. Keep track of our current stack tracker index in case we come
+    // back to the original stack with unbalanced stack ops (e.g.
+    // SP <- New stack; Enqueue data without dequeuing; SP <- Old SP)
+    // Note that, since we are not moinitoring memory allocations, we can
+    // only ever detect swicthing in and out of the default stack buffer.
+    //
+    StackTracker->OrgIndex = StackTracker->Index;
+    StackTracker->OrgStackPointer = (UINTN) VmPtr->Gpr[0];
+
+    //
+    // Do not track switching. Just realign the index.
+    //
+    StackTracker->Index = 4 * ((StackTracker->Index + 3) / 4);
+    return EFI_SUCCESS;
+  }
+
+  if ((StackTracker->OrgIndex != 0) && ((UpdatedR0 >= (UINTN) VmPtr->StackPool) ||
+      (UpdatedR0 < (UINTN) VmPtr->StackPool + STACK_POOL_SIZE))) {
+    //
+    // Coming back from a newly allocated stack to the original one
+    // As we don't expect stack ops to have been properly balanced we just
+    // restore the old stack tracker index.
+    //
+    StackTracker->Index = StackTracker->OrgIndex;
+    StackTracker->OrgIndex = 0;
+    //
+    // There's also no guarantee that the new R0 is being restored to the
+    // value it held when stwiching stacks, so we use the value R0 held
+    // at the time the switch was performed, to compute the delta.
+    StackPointerDelta = UpdatedR0 - StackTracker->OrgStackPointer;
+  } else {
+    StackPointerDelta = UpdatedR0 - (UINTN) VmPtr->Gpr[0];
+  }
+
+  return UpdateStackTracker(VmPtr, 0, StackPointerDelta);
+}
diff --git a/MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c b/MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c
index 2a42b6c4d0e7..2e308772b477 100644
--- a/MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c
+++ b/MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c
@@ -2,6 +2,7 @@
   This module contains EBC support routines that are customized based on
   the target Arm processor.
 
+Copyright (c) 2016, Pete Batard. All rights reserved.<BR>
 Copyright (c) 2016, Linaro, Ltd. All rights reserved.<BR>
 Copyright (c) 2015, The Linux Foundation. All rights reserved.<BR>
 Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
@@ -63,6 +64,69 @@ EbcLLExecuteEbcImageEntryPoint (
   );
 
 /**
+  This function is called to execute an EBC CALLEX instruction.
+  This is a special version of EbcLLCALLEXNative for Arm as we
+  need to pad or align arguments depending on whether they are
+  64-bit or natural.
+
+  @param  CallAddr     The function address.
+  @param  EbcSp        The new EBC stack pointer.
+  @param  FramePtr     The frame pointer.
+  @param  ArgLayout    The layout for up to 32 arguments. 1 means
+                       the argument is 64-bit, 0 means natural.
+
+  @return The unmodified value returned by the native code.
+
+**/
+INT64
+EFIAPI
+EbcLLCALLEXNativeArm (
+  IN UINTN        CallAddr,
+  IN UINTN        EbcSp,
+  IN VOID         *FramePtr,
+  IN UINT16       ArgLayout
+  );
+
+/**
+  Allocate a stack tracker structure and initialize it.
+
+  @param VmPtr         The pointer to current VM context.
+
+  @retval EFI_OUT_OF_RESOURCES  Not enough memory to set the stack tracker.
+  @retval EFI_SUCCESS           The stack tracker was initialized successfully.
+
+**/
+EFI_STATUS
+AllocateStackTracker (
+  IN VM_CONTEXT *VmPtr
+  );
+
+/**
+  Free a stack tracker structure.
+
+  @param VmPtr         The pointer to current VM context.
+**/
+VOID
+FreeStackTracker (
+  IN VM_CONTEXT *VmPtr
+  );
+
+/**
+  Return the argument layout for the current function call, according
+  to the current stack tracking data.
+  The first argument is at bit 0, with the 16th parameter at bit 15,
+  with a bit set to 1 if an argument is 64 bit, 0 otherwise.
+
+  @param VmPtr         The pointer to current VM context.
+
+  @return              A 16 bit argument layout.
+**/
+UINT16
+GetArgLayout (
+  IN VM_CONTEXT *VmPtr
+  );
+
+/**
   Pushes a 32 bit unsigned value to the VM stack.
 
   @param VmPtr  The pointer to current VM context.
@@ -85,7 +149,7 @@ PushU32 (
 
 
 /**
-  Begin executing an EBC image.
+  Begin executing an EBC function call.
 
   This is a thunk function.
 
@@ -93,8 +157,7 @@ PushU32 (
   @param  Arg2                  The 2nd argument.
   @param  Arg3                  The 3rd argument.
   @param  Arg4                  The 4th argument.
-  @param  Arg8                  The 8th argument.
-  @param  EntryPoint            The entrypoint of EBC code.
+  @param  InstructionBuffer     A pointer to the thunk instruction buffer.
   @param  Args5_16[]            Array containing arguments #5 to #16.
 
   @return The value returned by the EBC application we're going to run.
@@ -103,12 +166,12 @@ PushU32 (
 UINT64
 EFIAPI
 EbcInterpret (
-  IN UINTN      Arg1,
-  IN UINTN      Arg2,
-  IN UINTN      Arg3,
-  IN UINTN      Arg4,
-  IN UINTN      EntryPoint,
-  IN UINTN      Args5_16[]
+  IN UINTN                  Arg1,
+  IN UINTN                  Arg2,
+  IN UINTN                  Arg3,
+  IN UINTN                  Arg4,
+  IN EBC_INSTRUCTION_BUFFER *InstructionBuffer,
+  IN UINTN                  Args5_16[]
   )
 {
   //
@@ -122,7 +185,7 @@ EbcInterpret (
   //
   // Get the EBC entry point
   //
-  Addr = EntryPoint;
+  Addr = InstructionBuffer->EbcEntryPoint;
 
   //
   // Now clear out our context
@@ -135,20 +198,28 @@ EbcInterpret (
   VmContext.Ip = (VMIP) Addr;
 
   //
+  // Initialize the stack tracker
+  //
+  Status = AllocateStackTracker(&VmContext);
+  if (EFI_ERROR(Status)) {
+    return Status;
+  }
+
+  //
   // Initialize the stack pointer for the EBC. Get the current system stack
   // pointer and adjust it down by the max needed for the interpreter.
   //
-
-  //
-  // Adjust the VM's stack pointer down.
-  //
-
   Status = GetEBCStack((EFI_HANDLE)(UINTN)-1, &VmContext.StackPool, &StackIndex);
   if (EFI_ERROR(Status)) {
+    FreeStackTracker(&VmContext);
     return Status;
   }
-  VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE);
-  VmContext.Gpr[0] = (UINT32) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE);
+
+  //
+  // Adjust the VM's stack pointer down.
+  //
+  VmContext.StackTop = (UINT8*)VmContext.StackPool + STACK_REMAIN_SIZE;
+  VmContext.Gpr[0] = (UINT64)(UINTN) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE);
   VmContext.HighStackBottom = (UINTN) VmContext.Gpr[0];
   VmContext.Gpr[0] -= sizeof (UINTN);
 
@@ -227,6 +298,7 @@ EbcInterpret (
   // Return the value in Gpr[7] unless there was an error
   //
   ReturnEBCStack(StackIndex);
+  FreeStackTracker(&VmContext);
   return (UINT64) VmContext.Gpr[7];
 }
 
@@ -280,6 +352,14 @@ ExecuteEbcImageEntryPoint (
   VmContext.Ip = (VMIP) Addr;
 
   //
+  // Initialize the stack tracker
+  //
+  Status = AllocateStackTracker(&VmContext);
+  if (EFI_ERROR(Status)) {
+    return Status;
+  }
+
+  //
   // Initialize the stack pointer for the EBC. Get the current system stack
   // pointer and adjust it down by the max needed for the interpreter.
   //
@@ -287,11 +367,12 @@ ExecuteEbcImageEntryPoint (
   //
   // Allocate stack pool
   //
-  Status = GetEBCStack(ImageHandle, &VmContext.StackPool, &StackIndex);
+  Status = GetEBCStack (ImageHandle, &VmContext.StackPool, &StackIndex);
   if (EFI_ERROR(Status)) {
+    FreeStackTracker(&VmContext);
     return Status;
   }
-  VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE);
+  VmContext.StackTop = (UINT8*)VmContext.StackPool + STACK_REMAIN_SIZE;
   VmContext.Gpr[0] = (UINT64)(UINTN) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE);
   VmContext.HighStackBottom = (UINTN)VmContext.Gpr[0];
   VmContext.Gpr[0] -= sizeof (UINTN);
@@ -328,6 +409,7 @@ ExecuteEbcImageEntryPoint (
   // Return the value in Gpr[7] unless there was an error
   //
   ReturnEBCStack(StackIndex);
+  FreeStackTracker(&VmContext);
   return (UINT64) VmContext.Gpr[7];
 }
 
@@ -459,7 +541,8 @@ EbcLLCALLEX (
     // stack frame. All we know is that there is an 8 byte gap at the top that
     // we can ignore.
     //
-    VmPtr->Gpr[7] = EbcLLCALLEXNative (FuncAddr, NewStackPointer, FramePtr - 8);
+    VmPtr->Gpr[7] = EbcLLCALLEXNativeArm (FuncAddr, NewStackPointer,
+          &((UINT8*)FramePtr)[-8], GetArgLayout(VmPtr));
 
     //
     // Advance the IP.
@@ -467,4 +550,3 @@ EbcLLCALLEX (
     VmPtr->Ip += Size;
   }
 }
-
diff --git a/MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf b/MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf
index b55538304605..f627f66e5492 100644
--- a/MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf
+++ b/MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf
@@ -65,6 +65,9 @@ [Sources]
   EbcDebugger/EdbSupportString.c
   EbcDebugger/EdbSupportFile.c
 
+[Sources.Ia32, Sources.X64, Sources.IPF, Sources.AARCH64]
+  EbcStackTracker.c
+
 [Sources.Ia32]
   Ia32/EbcSupport.c
   Ia32/EbcLowLevel.nasm
@@ -89,6 +92,7 @@ [Sources.AARCH64]
 [Sources.ARM]
   Arm/EbcSupport.c
   Arm/EbcLowLevel.S
+  Arm/EbcStackTracker.c
 
 [Packages]
   MdePkg/MdePkg.dec
diff --git a/MdeModulePkg/Universal/EbcDxe/EbcDxe.inf b/MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
index 78e058b6cbc6..a82e1c062921 100644
--- a/MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
+++ b/MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
@@ -40,6 +40,9 @@ [Sources]
   EbcInt.h
   EbcInt.c
 
+[Sources.Ia32, Sources.X64, Sources.IPF, Sources.AARCH64]
+  EbcStackTracker.c
+
 [Sources.Ia32]
   Ia32/EbcSupport.c
   Ia32/EbcLowLevel.nasm
@@ -60,6 +63,7 @@ [Sources.IPF]
 [Sources.ARM]
   Arm/EbcSupport.c
   Arm/EbcLowLevel.S
+  Arm/EbcStackTracker.c
 
 [Sources.AARCH64]
   AArch64/EbcSupport.c
diff --git a/MdeModulePkg/Universal/EbcDxe/EbcExecute.c b/MdeModulePkg/Universal/EbcDxe/EbcExecute.c
index 2d21c3364e0d..b6801815f8f1 100644
--- a/MdeModulePkg/Universal/EbcDxe/EbcExecute.c
+++ b/MdeModulePkg/Universal/EbcDxe/EbcExecute.c
@@ -113,6 +113,47 @@ VmReadIndex64 (
   );
 
 /**
+  Update the stack tracker according to the latest natural and constant
+  value stack manipulation operations.
+
+  @param VmPtr         The pointer to current VM context.
+  @param NaturalUnits  The number of natural values that were pushed (>0) or
+                       popped (<0).
+  @param ConstUnits    The number of const bytes that were pushed (>0) or
+                       popped (<0).
+
+  @retval EFI_OUT_OF_RESOURCES  Not enough memory to grow the stack tracker.
+  @retval EFI_UNSUPPORTED       The stack tracker is being underflown due to
+                                unbalanced stack operations.
+  @retval EFI_SUCCESS           The stack tracker was updated successfully.
+
+**/
+EFI_STATUS
+UpdateStackTracker(
+  IN VM_CONTEXT *VmPtr,
+  IN INTN       NaturalUnits,
+  IN INTN       ConstUnits
+  );
+
+/**
+  Update the stack tracker by computing the R0 delta.
+
+  @param VmPtr         The pointer to current VM context.
+  @param NewR0         The new R0 value.
+
+  @retval EFI_OUT_OF_RESOURCES  Not enough memory to grow the stack tracker.
+  @retval EFI_UNSUPPORTED       The stack tracker is being underflown due to
+                                unbalanced stack operations.
+  @retval EFI_SUCCESS           The stack tracker was updated successfully.
+
+**/
+EFI_STATUS
+UpdateStackTrackerFromDelta (
+  IN VM_CONTEXT *VmPtr,
+  IN UINTN      NewR0
+  );
+
+/**
   Reads 8-bit data form the memory address.
 
   @param  VmPtr             A pointer to VM context.
@@ -1578,9 +1619,13 @@ ExecuteMOVxx (
   UINT64  Data64;
   UINT64  DataMask;
   UINTN   Source;
+  EBC_INDEX Index;
+  EBC_INDEX *IndexPtr;
+  EFI_STATUS Status;
 
   Opcode    = GETOPCODE (VmPtr);
   OpcMasked = (UINT8) (Opcode & OPCODE_M_OPCODE);
+  IndexPtr    = (VmPtr->StackTracker != NULL) ? &Index : NULL;
 
   //
   // Get the operands byte so we can get R1 and R2
@@ -1615,7 +1660,7 @@ ExecuteMOVxx (
       }
 
       if ((Opcode & OPCODE_M_IMMED_OP2) != 0) {
-        Index16     = VmReadIndex16 (VmPtr, Size, NULL);
+        Index16     = VmReadIndex16 (VmPtr, Size, IndexPtr);
         Index64Op2  = (INT64) Index16;
         Size += sizeof (UINT16);
       }
@@ -1630,7 +1675,7 @@ ExecuteMOVxx (
       }
 
       if ((Opcode & OPCODE_M_IMMED_OP2) != 0) {
-        Index32     = VmReadIndex32 (VmPtr, Size, NULL);
+        Index32     = VmReadIndex32 (VmPtr, Size, IndexPtr);
         Index64Op2  = (INT64) Index32;
         Size += sizeof (UINT32);
       }
@@ -1644,7 +1689,7 @@ ExecuteMOVxx (
       }
 
       if ((Opcode & OPCODE_M_IMMED_OP2) != 0) {
-        Index64Op2 = VmReadIndex64 (VmPtr, Size, NULL);
+        Index64Op2 = VmReadIndex64 (VmPtr, Size, IndexPtr);
         Size += sizeof (UINT64);
       }
     } else {
@@ -1757,6 +1802,34 @@ ExecuteMOVxx (
       }
     }
   }
+
+  if (VmPtr->StackTracker != NULL) {
+    if ((OPERAND1_REGNUM (Operands) == 0) && (!OPERAND1_INDIRECT (Operands))) {
+      //
+      // The stack pointer (R0) is being directly modified - track it
+      //
+      if ((OPERAND2_REGNUM (Operands) == 0) && (!OPERAND2_INDIRECT (Operands))) {
+        //
+        // MOV R0, R0(+/-n,+/-c)
+        //
+        if ((Opcode & OPCODE_M_IMMED_OP2) != 0) {
+          Status = UpdateStackTracker (VmPtr, (INTN) Index.NaturalUnits, (INTN) Index.ConstUnits);
+          if (EFI_ERROR (Status)) {
+            return Status;
+          }
+        }
+      } else {
+        //
+        // Track the R0 delta
+        //
+        Status = UpdateStackTrackerFromDelta (VmPtr, (INTN) (Data64 & DataMask));
+        if (EFI_ERROR (Status)) {
+          return Status;
+        }
+      }
+    }
+  }
+
   //
   // Now write it back
   //
@@ -2276,6 +2349,15 @@ ExecuteMOVI (
       Mask64 = (UINT64)~0;
     }
 
+    if (VmPtr->StackTracker != NULL) {
+      //
+      // Track direct register operations on R0
+      //
+      if (OPERAND1_REGNUM (Operands) == 0) {
+        UpdateStackTrackerFromDelta (VmPtr, (UINTN) ImmData64 & Mask64);
+      }
+    }
+
     VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = ImmData64 & Mask64;
   } else {
     //
@@ -2386,6 +2468,15 @@ ExecuteMOVIn (
       return EFI_UNSUPPORTED;
     }
 
+    if (VmPtr->StackTracker != NULL) {
+      //
+      // Track direct register operations on R0
+      //
+      if (OPERAND1_REGNUM (Operands) == 0) {
+        UpdateStackTrackerFromDelta (VmPtr, (UINTN) ImmedIndex64);
+      }
+    }
+
     VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = ImmedIndex64;
   } else {
     //
@@ -2485,6 +2576,15 @@ ExecuteMOVREL (
       return EFI_UNSUPPORTED;
     }
 
+    if (VmPtr->StackTracker != NULL) {
+      //
+      // Track direct register operations on R0
+      //
+      if (OPERAND1_REGNUM (Operands) == 0) {
+        UpdateStackTrackerFromDelta (VmPtr, (UINTN) Op2);
+      }
+    }
+
     VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = (VM_REGISTER) Op2;
   } else {
     //
@@ -2584,6 +2684,14 @@ ExecuteMOVsnw (
   // Now write back the result.
   //
   if (!OPERAND1_INDIRECT (Operands)) {
+    if (VmPtr->StackTracker != NULL) {
+      //
+      // Track direct register operations on R0
+      //
+      if (OPERAND1_REGNUM (Operands) == 0) {
+        UpdateStackTrackerFromDelta (VmPtr, (UINTN) Op2);
+      }
+    }
     VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = Op2;
   } else {
     VmWriteMemN (VmPtr, (UINTN) (VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Op1Index), (UINTN) Op2);
@@ -2677,6 +2785,14 @@ ExecuteMOVsnd (
   // Now write back the result.
   //
   if (!OPERAND1_INDIRECT (Operands)) {
+    if (VmPtr->StackTracker != NULL) {
+      //
+      // Track direct register operations on R0
+      //
+      if (OPERAND1_REGNUM (Operands) == 0) {
+        UpdateStackTrackerFromDelta (VmPtr, (UINTN) Op2);
+      }
+    }
     VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = Op2;
   } else {
     VmWriteMemN (VmPtr, (UINTN) (VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Op1Index), (UINTN) Op2);
@@ -2744,7 +2860,11 @@ ExecutePUSHn (
   //
   VmPtr->Gpr[0] -= sizeof (UINTN);
   VmWriteMemN (VmPtr, (UINTN) VmPtr->Gpr[0], DataN);
-  return EFI_SUCCESS;
+  if (VmPtr->StackTracker != NULL) {
+    return UpdateStackTracker (VmPtr, -1, 0);
+  } else {
+    return EFI_SUCCESS;
+  }
 }
 
 
@@ -2820,7 +2940,11 @@ ExecutePUSH (
     VmWriteMem32 (VmPtr, (UINTN) VmPtr->Gpr[0], Data32);
   }
 
-  return EFI_SUCCESS;
+  if (VmPtr->StackTracker != NULL) {
+    return UpdateStackTracker (VmPtr, 0, ((Opcode & PUSHPOP_M_64) != 0) ? -8 : -4);
+  } else {
+    return EFI_SUCCESS;
+  }
 }
 
 
@@ -2879,7 +3003,11 @@ ExecutePOPn (
     VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = (INT64) (UINT64) ((UINTN) DataN + Index16);
   }
 
-  return EFI_SUCCESS;
+  if (VmPtr->StackTracker != NULL) {
+    return UpdateStackTracker (VmPtr, 1, 0);
+  } else {
+    return EFI_SUCCESS;
+  }
 }
 
 
@@ -2958,7 +3086,11 @@ ExecutePOP (
     }
   }
 
-  return EFI_SUCCESS;
+  if (VmPtr->StackTracker != NULL) {
+    return UpdateStackTracker (VmPtr, 0, ((Opcode & PUSHPOP_M_64) != 0) ? 8 : 4);
+  } else {
+    return EFI_SUCCESS;
+  }
 }
 
 
@@ -4289,6 +4421,15 @@ ExecuteDataManip (
       VmWriteMem32 (VmPtr, (UINTN) Op1, (UINT32) Op2);
     }
   } else {
+    if (VmPtr->StackTracker != NULL) {
+      //
+      // Track direct register operations on R0
+      //
+      if (OPERAND1_REGNUM (Operands) == 0) {
+        UpdateStackTrackerFromDelta(VmPtr, (UINTN) Op2);
+      }
+    }
+
     //
     // Storage back to a register. Write back, clearing upper bits (as per
     // the specification) if 32-bit operation.
diff --git a/MdeModulePkg/Universal/EbcDxe/EbcStackTracker.c b/MdeModulePkg/Universal/EbcDxe/EbcStackTracker.c
new file mode 100644
index 000000000000..8e78fb2e35f6
--- /dev/null
+++ b/MdeModulePkg/Universal/EbcDxe/EbcStackTracker.c
@@ -0,0 +1,65 @@
+/** @file
+  This module contains dummy function calls, for platforms that do not
+  require stack tracking.
+
+Copyright (c) 2016, Pete Batard. All rights reserved.<BR>
+
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution.  The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "EbcInt.h"
+#include "EbcExecute.h"
+
+/**
+  Update the stack tracker according to the latest natural and constant
+  value stack manipulation operations.
+
+  @param VmPtr         The pointer to current VM context.
+  @param NaturalUnits  The number of natural values that were pushed (>0) or
+                       popped (<0).
+  @param ConstUnits    The number of const bytes that were pushed (>0) or
+                       popped (<0).
+
+  @retval EFI_OUT_OF_RESOURCES  Not enough memory to grow the stack tracker.
+  @retval EFI_UNSUPPORTED       The stack tracker is being underflown due to
+                                unbalanced stack operations.
+  @retval EFI_SUCCESS           The stack tracker was updated successfully.
+
+**/
+EFI_STATUS
+UpdateStackTracker(
+  IN VM_CONTEXT *VmPtr,
+  IN INTN       NaturalUnits,
+  IN INTN       ConstUnits
+  )
+{
+  return EFI_SUCCESS;
+}
+
+/**
+  Update the stack tracker by computing the R0 delta.
+
+  @param VmPtr         The pointer to current VM context.
+  @param NewR0         The new R0 value.
+
+  @retval EFI_OUT_OF_RESOURCES  Not enough memory to grow the stack tracker.
+  @retval EFI_UNSUPPORTED       The stack tracker is being underflown due to
+                                unbalanced stack operations.
+  @retval EFI_SUCCESS           The stack tracker was updated successfully.
+
+**/
+EFI_STATUS
+UpdateStackTrackerFromDelta (
+  IN VM_CONTEXT *VmPtr,
+  IN UINTN      NewR0
+  )
+{
+  return EFI_SUCCESS;
+}
-- 
2.9.3.windows.2



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

* [PATCH 4/5] MdeModulePkg/EbcDxe: add call signatures for ARM native->EBC support
  2017-01-24 12:30 [PATCH 1/5] MdeModulePkg/EbcDxe: allow VmReadIndex##() to return a decoded index Pete Batard
  2017-01-24 12:30 ` [PATCH 2/5] MdeModulePkg/EbcDxe: add ARM support Pete Batard
  2017-01-24 12:30 ` [PATCH 3/5] MdeModulePkg/EbcDxe: add a stack tracker for ARM EBC->native support Pete Batard
@ 2017-01-24 12:30 ` Pete Batard
  2017-01-24 12:30 ` [PATCH 5/5] BaseTools: add scripts to generate EBC call signatures Pete Batard
  3 siblings, 0 replies; 5+ messages in thread
From: Pete Batard @ 2017-01-24 12:30 UTC (permalink / raw)
  To: edk2-devel

* This patch fixes the call from native into EBC ARM calling convention
  issue by introducing support for call signatures when invoking BREAK 5.
* These call signatures are provided as the high 32 bit word of the
  64-bit longword to which R7 points during BREAK 5 invocation (the lowest
  32-bit being left untouched, as the "offset from self").
* Within this word the upper 16-bits should be set to an EBC_CALL_SIGNATURE
  marker and the lower 16 bits to the signature itself, with each bit
  indicating if the corresponding argument is 64-bit (1) or not (0).
* The compiler toolchain is expected to automatically fill these signatures
  when producing binaries.
* None of the changes introduced by this patch have any bearing on the
  behaviour of existing EBC VMs or EBC applications, except for ARM.
  Especially, existing EBC code that is missing signatures still works
  exactly as it did on the updated versions of IA32, X64 and AARCH64 VMs,
  and EBC code that includes signatures also works just the same on non
  signature-aware VMs.
  The presence or absence of signature has only an impact on ARM platforms.
* However, because this feature requires a minor specs change and because
  an EBC application that provides call signatures may want to detect if
  the VM is signature aware, we also bump the global VM version to 1.1.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Pete Batard <pete@akeo.ie>
---
 MdeModulePkg/Include/Protocol/EbcVmTest.h       |  2 +-
 MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S |  8 ++-
 MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c  | 85 +++++++++++++++++++------
 MdeModulePkg/Universal/EbcDxe/EbcExecute.c      | 27 +++++++-
 MdeModulePkg/Universal/EbcDxe/EbcInt.h          |  7 +-
 5 files changed, 102 insertions(+), 27 deletions(-)

diff --git a/MdeModulePkg/Include/Protocol/EbcVmTest.h b/MdeModulePkg/Include/Protocol/EbcVmTest.h
index 12d5e5217059..5b6aee704795 100644
--- a/MdeModulePkg/Include/Protocol/EbcVmTest.h
+++ b/MdeModulePkg/Include/Protocol/EbcVmTest.h
@@ -34,7 +34,7 @@ typedef struct _EFI_EBC_VM_TEST_PROTOCOL EFI_EBC_VM_TEST_PROTOCOL;
 // VM major/minor version
 //
 #define VM_MAJOR_VERSION  1
-#define VM_MINOR_VERSION  0
+#define VM_MINOR_VERSION  1
 
 //
 // Bits in the VM->StopFlags field
diff --git a/MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S b/MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S
index dc9c3946ced2..2325dd07a472 100644
--- a/MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S
+++ b/MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S
@@ -129,16 +129,17 @@ ASM_PFX(EbcLLCALLEXNativeArm):
 //****************************************************************************
 // EbcLLEbcInterpret
 //
-// This function is called by the thunk code to handle an Native to EBC call
-// This can handle up to 16 arguments (1-4 on in r0-r3, 5-16 are on the stack)
+// This function is called by the thunk code to handle a Native to EBC call
+// This can handle up to 16 arguments (args 1-2/1-4 in r0-r3, rest onstack)
 // ip contains the Entry point that will be the first argument when
 // EBCInterpret is called.
 //
 //****************************************************************************
 ASM_PFX(EbcLLEbcInterpret):
+
     stmdb   sp!, {r4, lr}
 
-    // push the entry point and the address of args #5 - #16 onto the stack
+    // push the entry point and the address of non register args on the stack
     add     r4, sp, #8
     str     ip, [sp, #-8]!
     str     r4, [sp, #4]
@@ -180,3 +181,4 @@ ASM_PFX(mEbcInstructionBufferTemplate):
 
     .long   0                       // EBC_ENTRYPOINT_SIGNATURE
 0:  .long   0                       // EBC_LL_EBC_ENTRYPOINT_SIGNATURE
+    .long   0                       // EBC_CALL_SIGNATURE
diff --git a/MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c b/MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c
index 2e308772b477..99bed1697748 100644
--- a/MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c
+++ b/MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c
@@ -32,6 +32,7 @@ typedef struct {
   UINT32    Magic;
   UINT32    EbcEntryPoint;
   UINT32    EbcLlEntryPoint;
+  UINT32    EbcCallSignature;
 } EBC_INSTRUCTION_BUFFER;
 #pragma pack()
 
@@ -158,7 +159,7 @@ PushU32 (
   @param  Arg3                  The 3rd argument.
   @param  Arg4                  The 4th argument.
   @param  InstructionBuffer     A pointer to the thunk instruction buffer.
-  @param  Args5_16[]            Array containing arguments #5 to #16.
+  @param  Args5To32[]           Array containing arguments #5 to #16.
 
   @return The value returned by the EBC application we're going to run.
 
@@ -171,7 +172,7 @@ EbcInterpret (
   IN UINTN                  Arg3,
   IN UINTN                  Arg4,
   IN EBC_INSTRUCTION_BUFFER *InstructionBuffer,
-  IN UINTN                  Args5_16[]
+  IN UINTN                  Args5To32[]
   )
 {
   //
@@ -179,11 +180,23 @@ EbcInterpret (
   //
   VM_CONTEXT  VmContext;
   UINTN       Addr;
+  UINT32      Mask;
   EFI_STATUS  Status;
   UINTN       StackIndex;
+  INTN        ArgNumber;
+  BOOLEAN     *SkipArg;
 
   //
-  // Get the EBC entry point
+  // If the call signature is missing (high 16-bits are not set to
+  // EBC_CALL_SIGNATURE), return an error as we aren't able to
+  // properly reconstruct the EBC VM parameter stack.
+  //
+  if ((InstructionBuffer->EbcCallSignature & 0xFFFF0000) != EBC_CALL_SIGNATURE) {
+    return EFI_INCOMPATIBLE_VERSION;
+  }
+
+  //
+  // Get the EBC entry point and signature
   //
   Addr = InstructionBuffer->EbcEntryPoint;
 
@@ -240,25 +253,49 @@ EbcInterpret (
   VmContext.LowStackTop   = (UINTN) VmContext.Gpr[0];
 
   //
-  // For the worst case, assume there are 4 arguments passed in registers, store
-  // them to VM's stack.
+  // Find which 32-bit args need to be skipped
   //
-  PushU32 (&VmContext, (UINT32) Args5_16[11]);
-  PushU32 (&VmContext, (UINT32) Args5_16[10]);
-  PushU32 (&VmContext, (UINT32) Args5_16[9]);
-  PushU32 (&VmContext, (UINT32) Args5_16[8]);
-  PushU32 (&VmContext, (UINT32) Args5_16[7]);
-  PushU32 (&VmContext, (UINT32) Args5_16[6]);
-  PushU32 (&VmContext, (UINT32) Args5_16[5]);
-  PushU32 (&VmContext, (UINT32) Args5_16[4]);
-  PushU32 (&VmContext, (UINT32) Args5_16[3]);
-  PushU32 (&VmContext, (UINT32) Args5_16[2]);
-  PushU32 (&VmContext, (UINT32) Args5_16[1]);
-  PushU32 (&VmContext, (UINT32) Args5_16[0]);
-  PushU32 (&VmContext, (UINT32) Arg4);
+  SkipArg = AllocateZeroPool(16 * sizeof(BOOLEAN));
+  for (ArgNumber = 0, Mask = 1; Mask < 0x10000; Mask <<= 1) {
+    if ((InstructionBuffer->EbcCallSignature & Mask) == Mask) {
+      //
+      // This is a 64 bit arg => check if we are aligned.
+      // If not, then we need to skip one 32-bit arg.
+      //
+      if (ArgNumber % 2 != 0) {
+        SkipArg[ArgNumber / 2] = TRUE;
+        ArgNumber += 1;
+      }
+      ArgNumber += 2;
+    } else {
+      ArgNumber += 1;
+    }
+  }
+  ASSERT(ArgNumber <= 32);
+
+  //
+  // Process the stack arguments. ArgNumber is already set to the max number
+  // of 32 bit values we need to process (including registers) so use that.
+  //
+  for (ArgNumber -= 5; ArgNumber >= 0; ArgNumber--) {
+    if ((ArgNumber % 2 == 0) || (!SkipArg[(ArgNumber + 4) / 2])) {
+      PushU32 (&VmContext, (UINT32) Args5To32[ArgNumber]);
+    }
+  }
+
+  //
+  // For the worst case, assume there are 4 arguments passed in registers,
+  // store them to VM's stack.
+  //
+  if (!SkipArg[1]) {
+    PushU32 (&VmContext, (UINT32) Arg4);
+  }
   PushU32 (&VmContext, (UINT32) Arg3);
-  PushU32 (&VmContext, (UINT32) Arg2);
+  if (!SkipArg[0]) {
+    PushU32 (&VmContext, (UINT32) Arg2);
+  }
   PushU32 (&VmContext, (UINT32) Arg1);
+  FreePool(SkipArg);
 
   //
   // Interpreter assumes 64-bit return address is pushed on the stack.
@@ -475,6 +512,16 @@ EbcCreateThunks (
   }
 
   //
+  // Add the call signature (high 16-bits of Flags) along with the.
+  // EBC_CALL_SIGNATURE marker. A missing marker helps us fault the
+  // EBC call at runtime, if it doesn't have a signature.
+  //
+  if ((Flags & FLAG_THUNK_SIGNATURE) != 0) {
+    InstructionBuffer->EbcCallSignature =
+     (UINT32)(EBC_CALL_SIGNATURE | (Flags >> 16));
+  }
+
+  //
   // Add the thunk to the list for this image. Do this last since the add
   // function flushes the cache for us.
   //
diff --git a/MdeModulePkg/Universal/EbcDxe/EbcExecute.c b/MdeModulePkg/Universal/EbcDxe/EbcExecute.c
index b6801815f8f1..ebd6257a3c8c 100644
--- a/MdeModulePkg/Universal/EbcDxe/EbcExecute.c
+++ b/MdeModulePkg/Universal/EbcDxe/EbcExecute.c
@@ -1914,6 +1914,8 @@ ExecuteBREAK (
   VOID        *Thunk;
   UINT64      U64EbcEntryPoint;
   INT32       Offset;
+  UINT32      Flags;
+  UINT32      CallSignature;
 
   Thunk = NULL;
   Operands = GETOPERANDS (VmPtr);
@@ -1960,19 +1962,38 @@ ExecuteBREAK (
     break;
 
   //
-  // Create a thunk for EBC code. R7 points to a 32-bit (in a 64-bit slot)
-  // "offset from self" pointer to the EBC entry point.
+  // Create a thunk for EBC code. R7 points to a 64-bit longword, where the
+  // lower 32 bits contain an "offset from self" pointer to the EBC entry
+  // point, and the higher 32 bits contain the call signature.
   // After we're done, *(UINT64 *)R7 will be the address of the new thunk.
   //
   case 5:
     Offset            = (INT32) VmReadMem32 (VmPtr, (UINTN) VmPtr->Gpr[7]);
+    CallSignature     = VmReadMem32 (VmPtr, (UINTN) VmPtr->Gpr[7] + 4);
     U64EbcEntryPoint  = (UINT64) (VmPtr->Gpr[7] + Offset + 4);
     EbcEntryPoint     = (VOID *) (UINTN) U64EbcEntryPoint;
 
     //
+    // A call signature is needed (notably on ARM systems) to properly
+    // reconstruct the EBC call parameter stack. When BREAK 5 is invoked,
+    // this signature is expected to be provided after the "offset from
+    // self", as another 32-bit word.
+    // Within this word, the upper 16-bits are set to EBC_CALL_SIGNATURE
+    // and the lower 16 bits contain the actual call signature (up to 16
+    // arguments, with bits set to 1 for 64-bit args, 0 otherwise).
+    // This 16-bit signature, if present, is then passed to EbcCreateThunks()
+    // as the high 16-bits of the Flags parameter, along with the
+    // FLAG_THUNK_SIGNATURE bit set.
+    //
+    Flags = 0;
+    if ((CallSignature & 0xFFFF0000) == EBC_CALL_SIGNATURE) {
+      Flags = (CallSignature << 16) | FLAG_THUNK_SIGNATURE;
+    }
+
+    //
     // Now create a new thunk
     //
-    Status = EbcCreateThunks (VmPtr->ImageHandle, EbcEntryPoint, &Thunk, 0);
+    Status = EbcCreateThunks (VmPtr->ImageHandle, EbcEntryPoint, &Thunk, Flags);
     if (EFI_ERROR (Status)) {
       return Status;
     }
diff --git a/MdeModulePkg/Universal/EbcDxe/EbcInt.h b/MdeModulePkg/Universal/EbcDxe/EbcInt.h
index 75017a23e75e..2e1c410f7d56 100644
--- a/MdeModulePkg/Universal/EbcDxe/EbcInt.h
+++ b/MdeModulePkg/Universal/EbcDxe/EbcInt.h
@@ -38,11 +38,16 @@ extern VM_CONTEXT                    *mVmPtr;
 //
 #define FLAG_THUNK_ENTRY_POINT  0x01  // thunk for an image entry point
 #define FLAG_THUNK_PROTOCOL     0x00  // thunk for an EBC protocol service
+#define FLAG_THUNK_SIGNATURE    0x02  // a 16-bit call signature is present
+//
+// 32-bit call signature marker
+//
+#define EBC_CALL_SIGNATURE      0x2EBC0000
 //
 // Put this value at the bottom of the VM's stack gap so we can check it on
 // occasion to make sure the stack has not been corrupted.
 //
-#define VM_STACK_KEY_VALUE  0xDEADBEEF
+#define VM_STACK_KEY_VALUE      0xDEADBEEF
 
 /**
   Create thunks for an EBC image entry point, or an EBC protocol service.
-- 
2.9.3.windows.2



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

* [PATCH 5/5] BaseTools: add scripts to generate EBC call signatures
  2017-01-24 12:30 [PATCH 1/5] MdeModulePkg/EbcDxe: allow VmReadIndex##() to return a decoded index Pete Batard
                   ` (2 preceding siblings ...)
  2017-01-24 12:30 ` [PATCH 4/5] MdeModulePkg/EbcDxe: add call signatures for ARM native->EBC support Pete Batard
@ 2017-01-24 12:30 ` Pete Batard
  3 siblings, 0 replies; 5+ messages in thread
From: Pete Batard @ 2017-01-24 12:30 UTC (permalink / raw)
  To: edk2-devel

* EBC binaries meant to be compatible with the ARM EBC VM
  require the insertion of call signatures at the locations
  where the BREAK 5 offsets are stored.
* This patch adds 2 new Python applications, as well as the
  required modifications for build_rule and tools_def templates:
  - GenEbcSignature is a script that parses a preprocessed C
    source, along with a binary object file, as generated by the
    intel EBC compiler, to create the signature data, which it
    stores into a .sig file.
  - PatchEbcSignature is a script that processes the .sig files
    as well as the .map data and the .efi binary, to insert the
    signatures at the required locations.
* Note that the insertion of signatures into EBC binaries has
  absolutely no impact for EBC execution on non ARM platforms,
  as it applies to data parts that are ignored on these VMs.
* Also note the GenEbcSignature is designed to be very basic
  with regards to the detection of call signatures that have
  straight 64-bit parameters, as it is meant to be used as a
  stopgap solution until the intel EBC compiler (which does
  has easy access to the data required to do so, and is best
  placed for such processing) is updated to generate .sig files
  on its own.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Pete Batard <pete@akeo.ie>
---
 .../BinWrappers/WindowsLike/GenEbcSignature.bat    |   3 +
 .../BinWrappers/WindowsLike/PatchEbcSignature.bat  |   3 +
 BaseTools/Conf/build_rule.template                 |  72 +++--
 BaseTools/Conf/tools_def.template                  |  12 +
 .../Python/GenEbcSignature/GenEbcSignature.py      | 306 +++++++++++++++++++++
 .../Source/Python/GenEbcSignature/__init__.py      |  15 +
 .../Python/PatchEbcSignature/PatchEbcSignature.py  | 226 +++++++++++++++
 .../Source/Python/PatchEbcSignature/__init__.py    |  15 +
 8 files changed, 633 insertions(+), 19 deletions(-)
 create mode 100644 BaseTools/BinWrappers/WindowsLike/GenEbcSignature.bat
 create mode 100644 BaseTools/BinWrappers/WindowsLike/PatchEbcSignature.bat
 create mode 100644 BaseTools/Source/Python/GenEbcSignature/GenEbcSignature.py
 create mode 100644 BaseTools/Source/Python/GenEbcSignature/__init__.py
 create mode 100644 BaseTools/Source/Python/PatchEbcSignature/PatchEbcSignature.py
 create mode 100644 BaseTools/Source/Python/PatchEbcSignature/__init__.py

diff --git a/BaseTools/BinWrappers/WindowsLike/GenEbcSignature.bat b/BaseTools/BinWrappers/WindowsLike/GenEbcSignature.bat
new file mode 100644
index 000000000000..9fbb704a6eb0
--- /dev/null
+++ b/BaseTools/BinWrappers/WindowsLike/GenEbcSignature.bat
@@ -0,0 +1,3 @@
+@setlocal
+@set ToolName=%~n0%
+@%PYTHON_HOME%\python.exe %BASE_TOOLS_PATH%\Source\Python\%ToolName%\%ToolName%.py %*
diff --git a/BaseTools/BinWrappers/WindowsLike/PatchEbcSignature.bat b/BaseTools/BinWrappers/WindowsLike/PatchEbcSignature.bat
new file mode 100644
index 000000000000..9fbb704a6eb0
--- /dev/null
+++ b/BaseTools/BinWrappers/WindowsLike/PatchEbcSignature.bat
@@ -0,0 +1,3 @@
+@setlocal
+@set ToolName=%~n0%
+@%PYTHON_HOME%\python.exe %BASE_TOOLS_PATH%\Source\Python\%ToolName%\%ToolName%.py %*
diff --git a/BaseTools/Conf/build_rule.template b/BaseTools/Conf/build_rule.template
index 1db94b696f89..3408f507e78b 100755
--- a/BaseTools/Conf/build_rule.template
+++ b/BaseTools/Conf/build_rule.template
@@ -161,6 +161,27 @@
         "$(CC)" $(CC_FLAGS) -c -o ${dst} $(INC) ${src}
         "$(SYMRENAME)" $(SYMRENAME_FLAGS) ${dst}
 
+[C-Code-File.COMMON.EBC]
+    <InputFile>
+        ?.c
+        ?.C
+        ?.cc
+        ?.CC
+        ?.cpp
+        ?.Cpp
+        ?.CPP
+
+    <ExtraDependency>
+        $(MAKE_FILE)
+
+    <OutputFile>
+        $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.obj
+
+    <Command.MSFT, Command.INTEL>
+        "$(CC)" /Fo${dst} $(CC_FLAGS) $(INC) ${src}
+        # Parse preprocessed sources to generate the EBC call signatures
+        "$(CC)" /E /EP $(CC_FLAGS) $(INC) ${src} | "$(GENEBC)" -v -o ${dst} -s $(OUTPUT_DIR)(+)$(MODULE_NAME).sig
+
 [C-Code-File.BASE.AARCH64,C-Code-File.SEC.AARCH64,C-Code-File.PEI_CORE.AARCH64,C-Code-File.PEIM.AARCH64,C-Code-File.BASE.ARM,C-Code-File.SEC.ARM,C-Code-File.PEI_CORE.ARM,C-Code-File.PEIM.ARM]
     <InputFile>
         ?.c
@@ -267,10 +288,10 @@
 
     <Command.GCC, Command.GCCLD>
         "$(SLINK)" cr ${dst} $(SLINK_FLAGS) @$(OBJECT_FILES_LIST)
-    
+
     <Command.RVCT>
         "$(SLINK)" $(SLINK_FLAGS) ${dst} --via $(OBJECT_FILES_LIST)
-    
+
     <Command.RVCTCYGWIN>
         # $(OBJECT_FILES_LIST) has wrong paths for cygwin
         "$(SLINK)" $(SLINK_FLAGS) ${dst} $(OBJECT_FILES)
@@ -308,8 +329,8 @@
 
     <Command.XCODE>
         "$(DLINK)" $(DLINK_FLAGS) -o ${dst} $(DLINK_SPATH) -filelist $(STATIC_LIBRARY_FILES_LIST)  $(DLINK2_FLAGS)
-    
-    
+
+
 [Static-Library-File.SEC.AARCH64, Static-Library-File.PEI_CORE.AARCH64, Static-Library-File.PEIM.AARCH64,Static-Library-File.SEC.ARM, Static-Library-File.PEI_CORE.ARM, Static-Library-File.PEIM.ARM]
     <InputFile>
         *.lib
@@ -353,8 +374,8 @@
 
     <Command.XCODE>
         "$(DLINK)" -o ${dst} $(DLINK_FLAGS)  $(DLINK_SPATH) -filelist $(STATIC_LIBRARY_FILES_LIST)  $(DLINK2_FLAGS)
-      
-      
+
+
 [Dynamic-Library-File]
     <InputFile>
         ?.dll
@@ -367,7 +388,7 @@
         $(CP) ${dst} $(OUTPUT_DIR)
         $(CP) ${dst} $(BIN_DIR)(+)$(MODULE_NAME_GUID).efi
         -$(CP) $(DEBUG_DIR)(+)*.map $(OUTPUT_DIR)
-        -$(CP) $(DEBUG_DIR)(+)*.pdb $(OUTPUT_DIR) 
+        -$(CP) $(DEBUG_DIR)(+)*.pdb $(OUTPUT_DIR)
     <Command.GCC, Command.GCCLD>
         $(CP) ${src} $(DEBUG_DIR)(+)$(MODULE_NAME).debug
         $(OBJCOPY) --strip-unneeded -R .eh_frame ${src}
@@ -382,7 +403,7 @@
         $(CP) ${dst} $(OUTPUT_DIR)
         $(CP) ${dst} $(BIN_DIR)(+)$(MODULE_NAME_GUID).efi
         -$(CP) $(DEBUG_DIR)(+)*.map $(OUTPUT_DIR)
-        
+
     <Command.XCODE>
         # tool to convert Mach-O to PE/COFF
         "$(MTOC)" -subsystem $(MODULE_TYPE)  $(MTOC_FLAGS)  ${src}  $(DEBUG_DIR)(+)$(MODULE_NAME).pecoff
@@ -393,6 +414,21 @@
         $(CP) ${dst} $(BIN_DIR)(+)$(MODULE_NAME_GUID).efi
         -$(CP) $(DEBUG_DIR)(+)*.map $(OUTPUT_DIR)
 
+[Dynamic-Library-File.COMMON.EBC]
+    <InputFile>
+        ?.dll
+
+    <OutputFile>
+        $(DEBUG_DIR)(+)$(MODULE_NAME).efi
+
+    <Command.MSFT, Command.INTEL, Command.RVCT>
+        "$(GENFW)" -e $(MODULE_TYPE) -o ${dst} ${src} $(GENFW_FLAGS)
+        "$(PATCHEBC)" -v -m $(DEBUG_DIR)(+)$(MODULE_NAME).map -e ${dst} $(OUTPUT_DIR)(+)$(MODULE_NAME).sig $(STATIC_LIBRARY_FILES)
+        $(CP) ${dst} $(OUTPUT_DIR)
+        $(CP) ${dst} $(BIN_DIR)(+)$(MODULE_NAME_GUID).efi
+        -$(CP) $(DEBUG_DIR)(+)*.map $(OUTPUT_DIR)
+        -$(CP) $(DEBUG_DIR)(+)*.pdb $(OUTPUT_DIR)
+
 [Dependency-Expression-File]
     <InputFile>
         ?.dxs, ?.Dxs, ?.DXS
@@ -421,13 +457,13 @@
     <Command.MSFT, Command.INTEL>
         Trim --asl-file -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.i -i $(INC_LIST) ${src}
         "$(ASLPP)" $(ASLPP_FLAGS) $(INC) /I${s_path} $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.i > $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.iii
-        Trim --source-code -l -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.iiii $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.iii 
+        Trim --source-code -l -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.iiii $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.iii
         "$(ASL)" $(ASL_FLAGS) $(ASL_OUTFLAGS)${dst} $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.iiii
 
     <Command.GCC, Command.GCCLD>
         Trim --asl-file -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.i -i $(INC_LIST) ${src}
         "$(ASLPP)" $(ASLPP_FLAGS) $(INC) -I${s_path} $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.i > $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.iii
-        Trim --source-code -l -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.iiii $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.iii 
+        Trim --source-code -l -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.iiii $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.iii
         "$(ASL)" $(ASL_FLAGS) $(ASL_OUTFLAGS)${dst} $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.iiii
 
 [C-Code-File.AcpiTable]
@@ -469,14 +505,14 @@
         "$(ASLCC)" -c -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.obj $(CC_FLAGS) $(ASLCC_FLAGS) $(INC) ${src}
         "$(ASLDLINK)" -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.dll $(ASLDLINK_FLAGS) $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.obj
         "$(GENFW)" -o ${dst} -c $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.dll $(GENFW_FLAGS)
-        
-    <Command.XCODE>        
+
+    <Command.XCODE>
         "$(ASLCC)" -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.obj  $(ASLCC_FLAGS) $(INC) ${src}
         "$(ASLDLINK)" -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.dll $(ASLDLINK_FLAGS) $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.obj
         "$(MTOC)" -subsystem $(MODULE_TYPE)  $(MTOC_FLAGS) $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.dll $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.efi
         "$(GENFW)" -o ${dst} -c $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.efi $(GENFW_FLAGS)
-      
-      
+
+
 [Masm16-Code-File]
     <InputFile>
         ?.asm16, ?.Asm16, ?.ASM16, ?.s16, ?.S16
@@ -499,14 +535,14 @@
       Trim --source-code -o ${d_path}(+)${s_base}.iii ${d_path}(+)${s_base}.i
       "$(ASM)" -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.obj $(ASM_FLAGS) $(INC) ${d_path}(+)${s_base}.iii
       "$(DLINK)" -o ${dst} $(DLINK_FLAGS) --start-group $(DLINK_SPATH) $(LIBS) $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.obj --end-group
-     
+
     <Command.XCODE>
       "$(PP)" $(PP_FLAGS) $(INC) ${src} > ${d_path}(+)${s_base}.i
       Trim --source-code -o ${d_path}(+)${s_base}.iii ${d_path}(+)${s_base}.i
       "$(ASM)" -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.obj $(ASM_FLAGS) $(INC) ${d_path}(+)${s_base}.iii
       "$(SLINK)" $(SLINK_FLAGS) $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.slib $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.obj
       otool -t $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.slib | hex2bin.py ${dst}
-      
+
 
 [Nasm-to-Binary-Code-File]
     <InputFile>
@@ -642,8 +678,6 @@
     <Command.GCC, Command.GCCLD>
         "$(GENFW)" -o $(OUTPUT_DIR)(+)$(MODULE_NAME)hii.rc -g $(MODULE_GUID) --hiibinpackage $(HII_BINARY_PACKAGES) $(GENFW_FLAGS)
         "$(RC)" $(RC_FLAGS) $(OUTPUT_DIR)(+)$(MODULE_NAME)hii.rc ${dst}
-        
+
     <Command.XCODE, Command.RVCT>
         GenFw -o $(OUTPUT_DIR)(+)$(MODULE_NAME)hii.rc -g $(MODULE_GUID) --hiibinpackage $(HII_BINARY_PACKAGES)
-        
-        
diff --git a/BaseTools/Conf/tools_def.template b/BaseTools/Conf/tools_def.template
index aaae4fcd2916..fa0e1ac23211 100755
--- a/BaseTools/Conf/tools_def.template
+++ b/BaseTools/Conf/tools_def.template
@@ -7651,6 +7651,18 @@ RELEASE_RVCTCYGWIN_ARM_CC_FLAGS  = "$(CCPATH_FLAG)" $(ARCHCC_FLAGS) $(PLATFORM_F
 *_*_*_GENFW_FLAGS                  =
 
 ##################
+# GenEbc tool definitions
+##################
+*_*_EBC_GENEBC_PATH                = GenEbcSignature
+*_*_EBC_GENEBC_FLAGS               =
+
+##################
+# PatchEbc tool definitions
+##################
+*_*_EBC_PATCHEBC_PATH              = PatchEbcSignature
+*_*_EBC_PATCHEBC_FLAGS             =
+
+##################
 # Asl Compiler definitions
 ##################
 *_*_*_ASLCC_FLAGS                  = /nologo /c /FIAutoGen.h /TC /Dmain=ReferenceAcpiTable
diff --git a/BaseTools/Source/Python/GenEbcSignature/GenEbcSignature.py b/BaseTools/Source/Python/GenEbcSignature/GenEbcSignature.py
new file mode 100644
index 000000000000..0a509d8d971e
--- /dev/null
+++ b/BaseTools/Source/Python/GenEbcSignature/GenEbcSignature.py
@@ -0,0 +1,306 @@
+## @file
+# Generate a list of EBC call signatures
+#
+# Copyright (c) 2008 - 2016, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2016, Pete Batard. All rights reserved.<BR>
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution.  The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+
+#======================================  External Libraries ========================================
+import optparse
+import Common.LongFilePathOs as os
+import re
+import sys
+import array
+import struct
+import pickle
+
+from Common.BuildToolError import *
+import Common.EdkLogger as EdkLogger
+from Common.Misc import PeImageClass
+from Common.BuildVersion import gBUILD_VERSION
+from Common.LongFilePathSupport import OpenLongFilePath as open
+
+# Version and Copyright
+__version_number__ = ("0.5" + " " + gBUILD_VERSION)
+__version__ = "%prog Version " + __version_number__
+__copyright__ = "Copyright (c) 2016, Intel Corporation. All rights reserved."
+
+#======================================  Internal Libraries ========================================
+
+#============================================== Code ===============================================
+EBC_CALL_SIGNATURE = 0x2EBC0000
+
+functionStartRe = re.compile('^\s*(\w+)\s+\(\s*', re.UNICODE)
+param64BitRe = re.compile('\\bU*INT64\\b(?!\s*\*)\s*\w+\\b', re.UNICODE)
+
+def ParseSource(CallList):
+    """ Parse preprocessed source to generate a function signature.
+
+    Note that this is a very basic parser, that expects all function definitions to follow the
+    UEFI coding standard, i.e. with function name and each parameters on individual lines.
+    It also expects 64 bit parameter to be explicitly passed as INT64 or UINT64.
+
+    @return the processed function call list with their signatures set
+    """
+    global Options
+    Status = 0
+    Signature = 0
+    FuncName = ""
+
+    for Line in sys.stdin:
+        Line = Line.strip()
+
+        if Status == 0:
+            m = functionStartRe.match(Line)
+            if m != None:
+                FuncName, = m.groups(0)
+                if FuncName in CallList:
+                    Status = 1
+                    Mask = 1
+                    Signature = 0
+                    if Options.debug > 1:
+                        print FuncName + "("
+                continue
+
+        if Status == 1 and Line[0] == ')':
+            if Options.debug > 1:
+                print ")"
+            CallList[FuncName] = Signature
+            Status = 0
+        if Status == 1:
+            if Options.debug > 1:
+                print "     " + Line
+            m = param64BitRe.match(Line)
+            if m != None:
+                Signature = Signature | Mask
+            Mask = Mask << 1
+
+    return True
+
+def ParseObject(ObjPath):
+    """ Parse a COFF object file to identify function calls that require signature generation
+    @param ObjPath    COFF object absolute path
+
+    @return a list of function call names require signature generation
+    """
+    global Options
+    CallList = {}
+    RelocList = []
+    SymbolList = {}
+
+    with file(ObjPath, 'rb') as f:
+        # Parse the object header
+        Machine = struct.unpack('H', f.read(2))[0]
+        assert Machine == 0xEBC, "Not an EBC object file"
+        NumSections = struct.unpack('H', f.read(2))[0]
+        f.seek(4, 1)    # Skip timestamp
+        SymTabAddr = struct.unpack('I', f.read(4))[0]
+        NumSymbols = struct.unpack('I', f.read(4))[0]
+        OptHeaderSize = struct.unpack('H', f.read(2))[0]
+        assert OptHeaderSize == 0, "Unexpected object file header"
+        Characteristics = struct.unpack('H', f.read(2))[0]
+        StrTabAddr = SymTabAddr + NumSymbols * 18
+        # Optional debug output
+        if Options.debug > 0:
+            print "Number of Sections: " + str(NumSections)
+            print "Number of Symbols:  " + str(NumSymbols)
+            print "Symbol Table at: " + hex(SymTabAddr)
+            print "String Table at: " + hex(StrTabAddr)
+
+        # Parse the object sections, to identify reloc sections
+        for i in range (0, NumSections):
+            SectionName = struct.unpack('8s', f.read(8))[0]
+            SectionName = SectionName.strip()
+            VirtualSize = struct.unpack('I', f.read(4))[0]
+            assert VirtualSize == 0, "Unexpected Obj file section"
+            # Skip Virtual Address
+            f.seek(4, 1)
+            DataSize = struct.unpack('I', f.read(4))[0]
+            DataAddr = struct.unpack('I', f.read(4))[0]
+            RelocAddr = struct.unpack('I', f.read(4))[0]
+            # Skip Line Numbers
+            f.seek(4, 1)
+            RelocNum = struct.unpack('H', f.read(2))[0]
+            f.seek(6, 1)
+            if RelocNum > 0:
+                RelocList.append([RelocNum, RelocAddr])
+            if Options.debug > 1:
+                print "  Section " + str(i) + ": " + SectionName
+                print "    Size: " + hex(DataSize)
+                print "    Addr: " + hex(DataAddr)
+                if RelocNum > 0:
+                    print "    Rels: " + str(RelocNum) + " at " + hex(RelocAddr)
+
+        # Parse reloc sections
+        if RelocList != None:
+            # Copy the String Table into something we can access more easily
+            f.seek(StrTabAddr, 0)
+            StrTabSize = struct.unpack('I', f.read(4))[0]
+            StrData = f.read(StrTabSize)
+            if Options.debug > 0:
+                print "String Table Size: " + str(StrTabSize)
+                if Options.debug > 1:
+                    StrArray = StrData.split(b'\x00')
+                    for String in StrArray:
+                        print "    " + String
+
+            # Now build a full symbol table, that includes long names
+            f.seek(SymTabAddr, 0)
+            NumAuxSections = 0
+            if Options.debug > 0 and NumSymbols > 0:
+                print "Relocation symbols:"
+            for i in range(0, NumSymbols):
+                if NumAuxSections > 0:
+                    f.seek(18, 1)
+                    NumAuxSections = NumAuxSections - 1
+                    continue
+                SymName = f.read(8)
+                SymValue = struct.unpack('I', f.read(4))[0]
+                SymSecNum = struct.unpack('h', f.read(2))[0]
+                SymType = struct.unpack('H', f.read(2))[0]
+                SymClass = struct.unpack('B', f.read(1))[0]
+                NumAuxSections = struct.unpack('B', f.read(1))[0]
+                if SymName[0] != '\0':
+                   SymName = SymName.strip(' \0')
+                else:
+                   StrIndex = struct.unpack('I', SymName[4:8])[0]
+                   StrEnd = StrData.find('\0', StrIndex-3)
+                   SymName = StrData[StrIndex-4:StrEnd]
+                SymbolList[i] = SymName
+                if Options.debug > 1:
+                    print "    " + SymName + ":"
+                    print "      Index=" + str(i) + " SecNum=" + str(SymSecNum) + " Type=" + str(SymType) + " Class=" + str(SymClass) + " NbAux=" + str(NumAuxSections)
+
+            # Finally, go through each reloc symbol and identify the "_plabel" suffixed ones
+            for Entry in RelocList:
+                f.seek(Entry[1], 0)
+
+                for i in range(0, Entry[0]):
+                    # Skip Virtual Address
+                    f.seek(4, 1)
+                    SymbolIndex = struct.unpack('I', f.read(4))[0]
+                    assert SymbolIndex in SymbolList, "Symbol not found in list"
+                    RelocType = struct.unpack('H', f.read(2))[0]
+                    m = re.match('(.*)_plabel', SymbolList[SymbolIndex])
+                    if m != None:
+                        CallName, = m.groups(0)
+                        if CallName not in CallList:
+                            # Add this call, and set its signature to undefined (-1)
+                            CallList[CallName] = -1
+                    if Options.debug > 0:
+                         print "    " + SymbolList[SymbolIndex] + " (Index " + hex(SymbolIndex) + ", Type " + str(RelocType) + ")"
+
+    if len(CallList) == 0:
+        return None
+    return CallList
+
+def UpdateSignatures(CallList, SigFile):
+    """ Create or update a signature file
+    @param CallList   A dictionary containing the signatures to save/update
+    @param SigFile    A path to the signature file to create/update
+
+    @return True if successfull
+    """
+    SavedList = {}
+
+    if Options.verbose != None:
+        print "EBC signatures:"
+        Width = max(len(CallName) for CallName in CallList) + 3
+        for CallName in CallList:
+            print "  " + (CallName + "()").ljust(Width) + "0b" + "{:016b}".format(CallList[CallName])
+
+    try:
+        f = open(SigFile, 'rb')
+        SavedList = pickle.load(f)
+        f.close()
+
+    except:
+        pass
+
+    SavedList.update(CallList)
+    with open(Options.sigfile, 'wb') as f:
+        pickle.dump(SavedList, f, protocol=pickle.HIGHEST_PROTOCOL)
+
+    return True
+
+def ParseOptions():
+    Usage = "%prog [-v] -o <ObjFile> -s <SigFile>"
+    AdditionalNotes = "\nThis application currently only recognizes INT64 or UINT64 types as 64-bit arg."
+    Parser = optparse.OptionParser(usage=Usage, description=__copyright__, version="%prog " + __version_number__)
+    Parser.add_option('-o', '--objfile', action='store', dest='objfile', type="string", help='Path of the object file.')
+    Parser.add_option('-s', '--sigfile',  action='store', dest='sigfile', type="string", help='Path to the signature file.')
+    Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed.")
+    Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")
+    Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")
+
+    (options, args) = Parser.parse_args()
+    return options
+
+def main():
+    global Options
+    AppName = os.path.basename(sys.argv[0])
+    Options = ParseOptions()
+    ReturnCode = 0
+
+    EdkLogger.Initialize()
+    try:
+        if Options.verbose != None:
+            EdkLogger.SetLevel(EdkLogger.VERBOSE)
+        if Options.quiet != None:
+            EdkLogger.SetLevel(EdkLogger.QUIET)
+        if Options.debug != None:
+            EdkLogger.SetLevel(Options.debug + 1)
+        else:
+            EdkLogger.SetLevel(EdkLogger.INFO)
+
+        if Options.objfile == None:
+            EdkLogger.error(AppName, OPTION_MISSING, "Object file not defined",
+                ExtraData="Please use '-o' switch to set the input Object file.")
+        if Options.sigfile == None:
+            EdkLogger.error(AppName, OPTION_MISSING, "Signature file not defined",
+                ExtraData="Please use '-s' switch to set the output Signature file.")
+
+        List = ParseObject(Options.objfile)
+        if List != None:
+            ParseSource(List)
+            # Check for missing signatures
+            for CallName in List:
+                assert List[CallName] != -1, "Could not set signature for function " + CallName
+            UpdateSignatures(List, Options.sigfile)
+        else:
+            # Still need to deplete stdin data
+            for Line in sys.stdin:
+                pass
+
+    except FatalError, X:
+        if Options.debug != None:
+            import traceback
+            EdkLogger.quiet(traceback.format_exc())
+        ReturnCode = X.args[0]
+
+    except:
+        import traceback
+        EdkLogger.error(
+            "\nPython",
+            CODE_ERROR,
+            "Tools code failure",
+            ExtraData="Please send email to edk2-devel@lists.01.org for help, attaching following call stack trace!\n",
+            RaiseError=False
+        )
+        EdkLogger.quiet(traceback.format_exc())
+        ReturnCode = CODE_ERROR
+    return ReturnCode
+
+if __name__ == '__main__':
+    r = main()
+    ## 0-127 is a safe return range, and 1 is a standard default error
+    if r < 0 or r > 127: r = 1
+    sys.exit(r)
diff --git a/BaseTools/Source/Python/GenEbcSignature/__init__.py b/BaseTools/Source/Python/GenEbcSignature/__init__.py
new file mode 100644
index 000000000000..58a5b1d630f0
--- /dev/null
+++ b/BaseTools/Source/Python/GenEbcSignature/__init__.py
@@ -0,0 +1,15 @@
+## @file
+# Python 'GenPatchPcdTable' package initialization file.
+#
+# This file is required to make Python interpreter treat the directory
+# as containing package.
+#
+# Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution.  The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
diff --git a/BaseTools/Source/Python/PatchEbcSignature/PatchEbcSignature.py b/BaseTools/Source/Python/PatchEbcSignature/PatchEbcSignature.py
new file mode 100644
index 000000000000..14f34641fb09
--- /dev/null
+++ b/BaseTools/Source/Python/PatchEbcSignature/PatchEbcSignature.py
@@ -0,0 +1,226 @@
+## @file
+# Insert EBC Call Signature data in an EFI executable
+#
+# Copyright (c) 2008 - 2016, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2016, Pete Batard. All rights reserved.<BR>
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution.  The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+
+#======================================  External Libraries ========================================
+import optparse
+import Common.LongFilePathOs as os
+import re
+import sys
+import array
+import struct
+import pickle
+
+from Common.BuildToolError import *
+import Common.EdkLogger as EdkLogger
+from Common.Misc import PeImageClass
+from Common.BuildVersion import gBUILD_VERSION
+from Common.LongFilePathSupport import OpenLongFilePath as open
+
+# Version and Copyright
+__version_number__ = ("1.0" + " " + gBUILD_VERSION)
+__version__ = "%prog Version " + __version_number__
+__copyright__ = "Copyright (c) 2016, Intel Corporation. All rights reserved."
+
+#======================================  Internal Libraries ========================================
+
+#============================================== Code ===============================================
+EBC_CALL_SIGNATURE = 0x2EBC0000
+
+CallRegexp = re.compile('^[\da-fA-F]+:[\da-fA-F]+ +([\w@\$]+)_plabel +([\da-fA-F]+) f? +([\w]+):([\w]+)\.obj', re.UNICODE)
+LoadRegexp = re.compile('Preferred load address is +([\da-fA-F]+)', re.UNICODE)
+
+def ParseMap(MapPath):
+    """ Parse a map file and get the list and address of functions that need an EBC call signature
+    @param MapPath    Map file absolute path
+
+    @return a list of call names with their address and source location
+    """
+    Status = 0
+    CallList = []
+
+    with file(MapPath, 'r') as f:
+        for Line in f:
+            Line = Line.strip()
+
+            if Status == 0:
+                m = LoadRegexp.match(Line)
+                if m != None:
+                    LoadAddr, = m.groups(0)
+                    LoadAddr = int(LoadAddr, 16)
+                    Status = 1
+
+            if Status == 1:
+                m = CallRegexp.match(Line)
+                if m != None:
+                    FuncName, FuncAddr, ModName, SrcName = m.groups(0)
+                    FuncAddr = int(FuncAddr, 16)
+                    SrcName = SrcName + ".c"
+                    CallList.append([FuncName, FuncAddr - LoadAddr, ModName, SrcName])
+
+    assert Status != 0, "Failed to read load address"
+
+    if len(CallList) == 0:
+        return None
+    return CallList
+
+def BuildSignatureList(FileList):
+    """ Parse the list of files passed as arguments, and try to locate and open
+        the matching signature files to build a complete EBC signature list.
+        as the signature file to ensure that the signatures we need are present.
+    @param FileList     A list of .sig or .lib files
+
+    @return a list of function calls with their signature data
+    """
+    global Options
+    SigList = {}
+
+    for File in FileList:
+        File = os.path.splitext(File)[0]+'.sig'
+        try:
+            with open(File, 'rb') as f:
+                SavedList = pickle.load(f)
+                SigList.update(SavedList)
+                if Options.verbose != None:
+                    print "  Loaded " + str(len(SavedList)) + " EBC signature(s) from " + File
+        except:
+            pass
+
+    if len(SigList) == 0:
+        return None
+    return SigList
+
+def CheckSignatures(EfiPath, MapList, SigList):
+    """ Sanity check to ensures that the signatures' data and addresses are valid.
+    @param EfiPath      EFI binary absolute path
+    @param MapList      Function calls requiring signature, with their address
+    @param SigList      Function calls signature dictionary
+
+    @return True if the check passed
+    """
+
+    for Entry in MapList:
+        # Check for missing signatures
+        assert Entry[0] in SigList, Entry[0] + ": missing signature"
+        # Make sure the signature fits in 16 bits
+        assert SigList[Entry[0]] < 0x10000, Entry[0] + ": invalid signature"
+
+    with file(EfiPath, 'rb') as f:
+        for Entry in MapList:
+            f.seek(Entry[1] + 4)
+            Data = struct.unpack('I', f.read(4))[0]
+            # The 32 bit data should either be 0 or have the call signature marker
+            assert Data == 0 or Data & 0xFFFF0000 == EBC_CALL_SIGNATURE, "Unexpected data at address 0x%x" % Entry[1]
+
+    return True;
+
+def InsertSignatures(EfiPath, MapList, SigList):
+    """ Check the EFI binary to ensure we have the right signature addresses
+    @param EfiPath      EFI binary absolute path
+    @param Maplist      Function calls requiring signature, with their address
+    @param SigList      Function calls signature dictionary
+
+    @return True if the signatures were successfully patched
+    """
+    global Options
+
+    with file(EfiPath, 'r+b') as f:
+        Width = max(len(Entry[0]) for Entry in MapList) + 3
+        for Entry in MapList:
+            f.seek(Entry[1] + 4)
+            f.write(struct.pack('I', EBC_CALL_SIGNATURE + SigList[Entry[0]]))
+            if Options.verbose != None:
+                print "  Patched " + (Entry[0] + "()").ljust(Width) + "at 0x{:08X}".format(Entry[1]) + " with signature 0x{:04X}".format(SigList[Entry[0]]) + " (" + Entry[2] + ":" + Entry[3] + ")"
+
+    return True;
+
+def ParseOptions():
+    Usage = "%prog [-v] -m <MapFile> -e <EfiFile> -s <SigFile> [file1] [file2] [...]"
+    AdditionalNotes = "\nfile# can be either a .sig or .lib file. If the latter, its path is used to look for a matching .sig."
+    Parser = optparse.OptionParser(usage=Usage, description=__copyright__, version="%prog " + __version_number__)
+    Parser.add_option('-m', '--mapfile', action='store', dest='mapfile', type="string", help='Path of the module map file.')
+    Parser.add_option('-e', '--efifile', action='store', dest='efifile', type="string", help='Path of the EFI binary to patch.')
+    Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed.")
+    Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")
+    Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")
+
+    return Parser.parse_args()
+
+def main():
+    global Options
+    AppName = os.path.basename(sys.argv[0])
+    (Options, ArgList) = ParseOptions()
+    ReturnCode = 0
+
+    EdkLogger.Initialize()
+
+    try:
+        if Options.verbose != None:
+            EdkLogger.SetLevel(EdkLogger.VERBOSE)
+        if Options.quiet != None:
+            EdkLogger.SetLevel(EdkLogger.QUIET)
+        if Options.debug != None:
+            EdkLogger.SetLevel(Options.debug + 1)
+        else:
+            EdkLogger.SetLevel(EdkLogger.INFO)
+
+        if Options.mapfile == None:
+            EdkLogger.error(AppName, OPTION_MISSING, "Map file not defined",
+                ExtraData="Please use '-m' switch to define a map file.")
+        if Options.efifile == None:
+            EdkLogger.error(AppName, OPTION_MISSING, "EFI file not defined",
+                ExtraData="Please use '-e' switch to set the EFI binary to patch.")
+        if ArgList == None:
+            EdkLogger.error(AppName, OPTION_MISSING, "No signature or library file provided",
+                ExtraData="At least one signature or library path must be provided as argument.")
+
+        MapList = ParseMap(Options.mapfile)
+        if MapList != None:
+            SigList = BuildSignatureList(ArgList)
+            assert SigList != None, "No signatures found"
+            CheckSignatures(Options.efifile, MapList, SigList)
+            if Options.debug != None:
+                print "EBC signatures:"
+                Width = max(len(CallName) for CallName in SigList) + 3
+                for CallName in SigList:
+                    print "  " + (CallName + "()").ljust(Width) + "0b" + "{:016b}".format(SigList[CallName])
+            InsertSignatures(Options.efifile, MapList, SigList)
+        elif Options.verbose:
+            print 'No EBC call signatures patching required'
+
+    except FatalError, X:
+        if Options.debug != None:
+            import traceback
+            EdkLogger.quiet(traceback.format_exc())
+        ReturnCode = X.args[0]
+
+    except:
+        import traceback
+        EdkLogger.error(
+            "\nPython",
+            CODE_ERROR,
+            "Tools code failure",
+            ExtraData="Please send email to edk2-devel@lists.01.org for help, attaching following call stack trace!\n",
+            RaiseError=False
+        )
+        EdkLogger.quiet(traceback.format_exc())
+        ReturnCode = CODE_ERROR
+
+    return ReturnCode
+
+if __name__ == '__main__':
+    r = main()
+    ## 0-127 is a safe return range, and 1 is a standard default error
+    if r < 0 or r > 127: r = 1
+    sys.exit(r)
diff --git a/BaseTools/Source/Python/PatchEbcSignature/__init__.py b/BaseTools/Source/Python/PatchEbcSignature/__init__.py
new file mode 100644
index 000000000000..58a5b1d630f0
--- /dev/null
+++ b/BaseTools/Source/Python/PatchEbcSignature/__init__.py
@@ -0,0 +1,15 @@
+## @file
+# Python 'GenPatchPcdTable' package initialization file.
+#
+# This file is required to make Python interpreter treat the directory
+# as containing package.
+#
+# Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution.  The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
-- 
2.9.3.windows.2



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

end of thread, other threads:[~2017-01-24 12:30 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-01-24 12:30 [PATCH 1/5] MdeModulePkg/EbcDxe: allow VmReadIndex##() to return a decoded index Pete Batard
2017-01-24 12:30 ` [PATCH 2/5] MdeModulePkg/EbcDxe: add ARM support Pete Batard
2017-01-24 12:30 ` [PATCH 3/5] MdeModulePkg/EbcDxe: add a stack tracker for ARM EBC->native support Pete Batard
2017-01-24 12:30 ` [PATCH 4/5] MdeModulePkg/EbcDxe: add call signatures for ARM native->EBC support Pete Batard
2017-01-24 12:30 ` [PATCH 5/5] BaseTools: add scripts to generate EBC call signatures Pete Batard

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