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
next prev 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