From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-wm0-x241.google.com (mail-wm0-x241.google.com [IPv6:2a00:1450:400c:c09::241]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 2FB5881F05 for ; Tue, 24 Jan 2017 04:30:49 -0800 (PST) Received: by mail-wm0-x241.google.com with SMTP id d140so34440886wmd.2 for ; Tue, 24 Jan 2017 04:30:49 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=akeo-ie.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=Ke+b29SM7EHaQX0a3Mn0iCJWjC+XBQlU8EGXrToyl08=; b=zJLyiney3Skc58g9PP6BoGGWa/3kcSwIoqPmi38QlwaSO6ovtRX2SVXhTtRFSaYhB7 Zl+bIW1lYOm4YgXnvVkXHR+mtzVBPhsNGl8CXBPrtLgRbUZCr3UDvAnJ5Ou/iB2w7eSE HTkWVcqY1x1dLhmG34AcefgOc8FS6Y1qANGk6k38Eez2p9BSaef/onlpTCmslwmZR/Ri 4G7W/6PWHl+esJFthaRP81huMUacGin6pI5pvnV8T0oSqQ4MQ2Wp7rAdGluDHw2gDtC+ f944uSsk87epkONzj9MvdjwDy6dP//18skPuWUXGweiPvfgdP4BK35TpuRycq3FtQLEp PhGQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=Ke+b29SM7EHaQX0a3Mn0iCJWjC+XBQlU8EGXrToyl08=; b=QuJWQD3r6KlZyRjfGWL0ACDPyyw6643mF/o5aLKCYcr/MGKlf++v6bCqblrKFg82mX FJPpdwWCzfqTkT+mRDk5hsfS0+N4fTY1LeKcXXtqxz10ZvSozFSOWJovIdQ16qw7NI8d V8FEVW6Ak68H85sj77NAiSjdNfoyWpEm3p2gRPIVnTsROoRYWQvcskI8HAQ8hr1YCCz7 dLdTcUSpl91aL8u2OgS1d/TeH/9FWXNZdQeofoK3E/Mz7FvHl2Yhu6tUaWlm1Me/Dnz1 WIrmcjrg57fK66papsw+6ibNWYcllMjePYw1muhilhkYSDWhm1k+EBUpUC3rI2tRKRZc mY8g== X-Gm-Message-State: AIkVDXKtp/rE7j8VKauMmEF4f2xzuGIdXLoTXMnRV+kmA9IkpbmE7SddZZR8mIGybU1y3A== X-Received: by 10.28.131.132 with SMTP id f126mr19277465wmd.61.1485261046354; Tue, 24 Jan 2017 04:30:46 -0800 (PST) Received: from localhost.localdomain ([84.203.39.43]) by smtp.gmail.com with ESMTPSA id a13sm26343669wma.0.2017.01.24.04.30.43 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 24 Jan 2017 04:30:45 -0800 (PST) From: Pete Batard To: edk2-devel@lists.01.org Date: Tue, 24 Jan 2017 12:30:24 +0000 Message-Id: <20170124123026.5204-3-pete@akeo.ie> X-Mailer: git-send-email 2.9.3.windows.2 In-Reply-To: <20170124123026.5204-1-pete@akeo.ie> References: <20170124123026.5204-1-pete@akeo.ie> Subject: [PATCH 3/5] MdeModulePkg/EbcDxe: add a stack tracker for ARM EBC->native support X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 24 Jan 2017 12:30:49 -0000 * 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 --- 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.
// Copyright (c) 2016, Linaro, Ltd. All rights reserved.
// Copyright (c) 2015, The Linux Foundation. All rights reserved.
// Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.
@@ -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.
+ +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.
Copyright (c) 2016, Linaro, Ltd. All rights reserved.
Copyright (c) 2015, The Linux Foundation. All rights reserved.
Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
@@ -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.
+ +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