public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: Pete Batard <pete@akeo.ie>
To: edk2-devel@lists.01.org
Subject: [PATCH 3/5] MdeModulePkg/EbcDxe: add a stack tracker for ARM EBC->native support
Date: Tue, 24 Jan 2017 12:30:24 +0000	[thread overview]
Message-ID: <20170124123026.5204-3-pete@akeo.ie> (raw)
In-Reply-To: <20170124123026.5204-1-pete@akeo.ie>

* 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



  parent reply	other threads:[~2017-01-24 12:30 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20170124123026.5204-3-pete@akeo.ie \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox