* [PATCH 2/5] MdeModulePkg/EbcDxe: add ARM support
2017-01-24 12:30 [PATCH 1/5] MdeModulePkg/EbcDxe: allow VmReadIndex##() to return a decoded index Pete Batard
@ 2017-01-24 12:30 ` Pete Batard
2017-01-24 12:30 ` [PATCH 3/5] MdeModulePkg/EbcDxe: add a stack tracker for ARM EBC->native support Pete Batard
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Pete Batard @ 2017-01-24 12:30 UTC (permalink / raw)
To: edk2-devel; +Cc: Ard Biesheuvel, Pete Batard
From: Ard Biesheuvel <ard.biesheuvel@linaro.org>
* This is a port of the AARCH64 implementation of the EBC runtime to ARM.
* Note that, on its own, this patch only allows running self contained EBC
applications, such as ones that don't issue calls into the native
platform, or that aren't being called from native.
* Support for EBC -> native and native -> EBC calls will be added in
subsequent patches.
* Because EFI32 may not be defined for ARM compilation, this patch also
alters the EBC Debugger to use MDE_CPU_### macros for address display.
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: Pete Batard <pete@akeo.ie>
---
ArmVirtPkg/ArmVirt.dsc.inc | 6 +-
ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc | 10 +-
ArmVirtPkg/ArmVirtXen.fdf | 10 +-
MdeModulePkg/MdeModulePkg.dsc | 4 +-
MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S | 147 +++++++
MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c | 470 +++++++++++++++++++++
MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf | 6 +-
.../EbcDxe/EbcDebugger/EdbDisasmSupport.h | 4 +-
.../Universal/EbcDxe/EbcDebuggerConfig.inf | 2 +-
MdeModulePkg/Universal/EbcDxe/EbcDxe.inf | 6 +-
10 files changed, 648 insertions(+), 17 deletions(-)
create mode 100644 MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S
create mode 100644 MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c
diff --git a/ArmVirtPkg/ArmVirt.dsc.inc b/ArmVirtPkg/ArmVirt.dsc.inc
index dbd6678accde..3fa016b501c5 100644
--- a/ArmVirtPkg/ArmVirt.dsc.inc
+++ b/ArmVirtPkg/ArmVirt.dsc.inc
@@ -402,6 +402,11 @@ [Components.common]
MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDxe.inf
#
+ # EBC support
+ #
+ MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
+
+ #
# UEFI application (Shell Embedded Boot Loader)
#
ShellPkg/Application/Shell/Shell.inf {
@@ -430,4 +435,3 @@ [Components.AARCH64]
# ACPI Support
#
MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxe.inf
- MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
diff --git a/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc b/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc
index cc5d12aaefea..5145a86a0f33 100644
--- a/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc
+++ b/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc
@@ -140,6 +140,11 @@ [FV.FvMain]
INF MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.inf
INF OvmfPkg/SmbiosPlatformDxe/SmbiosPlatformDxe.inf
+ #
+ # EBC support
+ #
+ INF MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
+
!if $(ARCH) == AARCH64
#
# ACPI Support
@@ -147,11 +152,6 @@ [FV.FvMain]
INF MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxe.inf
INF MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
INF OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpiPlatformDxe.inf
-
- #
- # EBC support
- #
- INF MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
!endif
#
diff --git a/ArmVirtPkg/ArmVirtXen.fdf b/ArmVirtPkg/ArmVirtXen.fdf
index c997251b12b8..ccbd8baa5f82 100644
--- a/ArmVirtPkg/ArmVirtXen.fdf
+++ b/ArmVirtPkg/ArmVirtXen.fdf
@@ -170,6 +170,11 @@ [FV.FvMain]
INF ShellPkg/Application/Shell/Shell.inf
#
+ # EBC support
+ #
+ INF MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
+
+ #
# Bds
#
INF MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf
@@ -186,11 +191,6 @@ [FV.FvMain]
!if $(ARCH) == AARCH64
INF MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxe.inf
INF ArmVirtPkg/XenAcpiPlatformDxe/XenAcpiPlatformDxe.inf
-
- #
- # EBC support
- #
- INF MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
!endif
#
diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc
index 5996fe50f680..35094bab9e9a 100644
--- a/MdeModulePkg/MdeModulePkg.dsc
+++ b/MdeModulePkg/MdeModulePkg.dsc
@@ -430,8 +430,10 @@ [Components]
MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.inf
MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.inf
-[Components.IA32, Components.X64, Components.IPF, Components.AARCH64]
+[Components.IA32, Components.X64, Components.IPF]
MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxeBcDxe.inf
+
+[Components.IA32, Components.X64, Components.IPF, Components.ARM, Components.AARCH64]
MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxe.inf
MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf
diff --git a/MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S b/MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S
new file mode 100644
index 000000000000..cb3c11f71673
--- /dev/null
+++ b/MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S
@@ -0,0 +1,147 @@
+///** @file
+//
+// This code provides low level routines that support the Virtual Machine
+// for option ROMs.
+//
+// Copyright (c) 2016, Linaro, Ltd. All rights reserved.<BR>
+// Copyright (c) 2015, The Linux Foundation. All rights reserved.<BR>
+// Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// This program and the accompanying materials
+// are licensed and made available under the terms and conditions of the BSD License
+// which accompanies this distribution. The full text of the license may be found at
+// http://opensource.org/licenses/bsd-license.php
+//
+// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+//
+//**/
+
+ .thumb
+ .syntax unified
+
+ASM_GLOBAL ASM_PFX(EbcLLCALLEXNative)
+ASM_GLOBAL ASM_PFX(EbcLLEbcInterpret)
+ASM_GLOBAL ASM_PFX(EbcLLExecuteEbcImageEntryPoint)
+
+INTERWORK_FUNC(EbcLLCALLEXNative)
+INTERWORK_FUNC(EbcLLEbcInterpret)
+INTERWORK_FUNC(EbcLLExecuteEbcImageEntryPoint)
+
+ASM_GLOBAL ASM_PFX(mEbcInstructionBufferTemplate)
+
+//****************************************************************************
+// EbcLLCALLEX
+//
+// This function is called to execute an EBC CALLEX instruction.
+// This instruction requires that we thunk out to external native
+// code. For ARM, we copy the VM stack into the main stack and then pop
+// the first 4 arguments off according to the ARM Procedure Call Standard
+// On return, we restore the stack pointer to its original location.
+//
+//****************************************************************************
+// UINTN EbcLLCALLEXNative(UINTN FuncAddr, UINTN NewStackPointer, VOID *FramePtr)
+ASM_PFX(EbcLLCALLEXNative):
+ mov ip, r0 // Preserve r0
+
+ //
+ // If the EBC stack frame is smaller than or equal to 16 bytes, we know there
+ // are no stacked arguments #5 and beyond that we need to copy to the native
+ // stack. In this case, we can perform a tail call which is much more
+ // efficient, since there is no need to touch the native stack at all.
+ //
+ sub r3, r2, r1 // Length = NewStackPointer - FramePtr
+ cmp r3, #16
+ bgt 0f
+
+ ldrd r2, r3, [r1, #8]
+ ldrd r0, r1, [r1]
+
+ bx ip
+
+ //
+ // More than 16 bytes: we need to build the full native stack frame and copy
+ // the part of the VM stack exceeding 16 bytes (which may contain stacked
+ // arguments) to the native stack
+ //
+0: push {r4, lr}
+ mov r4, sp
+
+ //
+ // Ensure that the stack pointer remains 8 byte aligned,
+ // even if the size of the VM stack frame is not a multiple of 8
+ //
+ add r1, r1, #16 // Skip over [potential] reg params
+ tst r3, #7 // Multiple of 8?
+ beq 1f
+ ldr r3, [r2, #-4]! // No? Then push one word
+ str r3, [sp, #-8]! // ... but use two slots
+ b 2f
+
+1: ldrd r0, r3, [r2, #-8]!
+ strd r0, r3, [sp, #-8]!
+2: cmp r2, r1
+ bgt 1b
+
+ ldrd r2, r3, [r1, #-8]
+ ldrd r0, r1, [r1, #-16]
+
+ blx ip
+
+ mov sp, r4
+ pop {r4, pc}
+
+//****************************************************************************
+// EbcLLEbcInterpret
+//
+// This function is called by the thunk code to handle an Native to EBC call
+// This can handle up to 16 arguments (1-4 on in r0-r3, 5-16 are on the stack)
+// ip contains the Entry point that will be the first argument when
+// EBCInterpret is called.
+//
+//****************************************************************************
+ASM_PFX(EbcLLEbcInterpret):
+ stmdb sp!, {r4, lr}
+
+ // push the entry point and the address of args #5 - #16 onto the stack
+ add r4, sp, #8
+ str ip, [sp, #-8]!
+ str r4, [sp, #4]
+
+ // call C-code
+ bl ASM_PFX(EbcInterpret)
+
+ add sp, sp, #8
+ ldmia sp!, {r4, pc}
+
+//****************************************************************************
+// EbcLLExecuteEbcImageEntryPoint
+//
+// This function is called by the thunk code to handle the image entry point
+// ip contains the Entry point that will be the third argument when
+// ExecuteEbcImageEntryPoint is called.
+//
+//****************************************************************************
+ASM_PFX(EbcLLExecuteEbcImageEntryPoint):
+ ldr r2, [ip, #12]
+
+ // tail call to C code
+ b ASM_PFX(ExecuteEbcImageEntryPoint)
+
+//****************************************************************************
+// mEbcInstructionBufferTemplate
+//****************************************************************************
+ .section ".rodata", "a"
+ .align 2
+ .arm
+ASM_PFX(mEbcInstructionBufferTemplate):
+ adr ip, .
+ ldr pc, 0f
+
+ //
+ // Add a magic code here to help the VM recognize the thunk.
+ //
+ udf #0xEBC
+
+ .long 0 // EBC_ENTRYPOINT_SIGNATURE
+0: .long 0 // EBC_LL_EBC_ENTRYPOINT_SIGNATURE
diff --git a/MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c b/MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c
new file mode 100644
index 000000000000..2a42b6c4d0e7
--- /dev/null
+++ b/MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c
@@ -0,0 +1,470 @@
+/** @file
+ This module contains EBC support routines that are customized based on
+ the target Arm processor.
+
+Copyright (c) 2016, Linaro, Ltd. All rights reserved.<BR>
+Copyright (c) 2015, The Linux Foundation. All rights reserved.<BR>
+Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
+
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "EbcInt.h"
+#include "EbcExecute.h"
+#include "EbcDebuggerHook.h"
+
+//
+// Amount of space that is not used in the stack
+//
+#define STACK_REMAIN_SIZE (1024 * 4)
+
+#pragma pack(1)
+typedef struct {
+ UINT32 Instr[2];
+ UINT32 Magic;
+ UINT32 EbcEntryPoint;
+ UINT32 EbcLlEntryPoint;
+} EBC_INSTRUCTION_BUFFER;
+#pragma pack()
+
+extern CONST EBC_INSTRUCTION_BUFFER mEbcInstructionBufferTemplate;
+
+/**
+ Begin executing an EBC image.
+ This is used for Ebc Thunk call.
+
+ @return The value returned by the EBC application we're going to run.
+
+**/
+UINT64
+EFIAPI
+EbcLLEbcInterpret (
+ VOID
+ );
+
+/**
+ Begin executing an EBC image.
+ This is used for Ebc image entrypoint.
+
+ @return The value returned by the EBC application we're going to run.
+
+**/
+UINT64
+EFIAPI
+EbcLLExecuteEbcImageEntryPoint (
+ VOID
+ );
+
+/**
+ Pushes a 32 bit unsigned value to the VM stack.
+
+ @param VmPtr The pointer to current VM context.
+ @param Arg The value to be pushed.
+
+**/
+VOID
+PushU32 (
+ IN VM_CONTEXT *VmPtr,
+ IN UINT32 Arg
+ )
+{
+ //
+ // Advance the VM stack down, and then copy the argument to the stack.
+ // Hope it's aligned.
+ //
+ VmPtr->Gpr[0] -= sizeof (UINT32);
+ *(UINT32 *)(UINTN)VmPtr->Gpr[0] = Arg;
+}
+
+
+/**
+ Begin executing an EBC image.
+
+ This is a thunk function.
+
+ @param Arg1 The 1st argument.
+ @param Arg2 The 2nd argument.
+ @param Arg3 The 3rd argument.
+ @param Arg4 The 4th argument.
+ @param Arg8 The 8th argument.
+ @param EntryPoint The entrypoint of EBC code.
+ @param Args5_16[] Array containing arguments #5 to #16.
+
+ @return The value returned by the EBC application we're going to run.
+
+**/
+UINT64
+EFIAPI
+EbcInterpret (
+ IN UINTN Arg1,
+ IN UINTN Arg2,
+ IN UINTN Arg3,
+ IN UINTN Arg4,
+ IN UINTN EntryPoint,
+ IN UINTN Args5_16[]
+ )
+{
+ //
+ // Create a new VM context on the stack
+ //
+ VM_CONTEXT VmContext;
+ UINTN Addr;
+ EFI_STATUS Status;
+ UINTN StackIndex;
+
+ //
+ // Get the EBC entry point
+ //
+ Addr = EntryPoint;
+
+ //
+ // Now clear out our context
+ //
+ ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));
+
+ //
+ // Set the VM instruction pointer to the correct location in memory.
+ //
+ VmContext.Ip = (VMIP) Addr;
+
+ //
+ // Initialize the stack pointer for the EBC. Get the current system stack
+ // pointer and adjust it down by the max needed for the interpreter.
+ //
+
+ //
+ // Adjust the VM's stack pointer down.
+ //
+
+ Status = GetEBCStack((EFI_HANDLE)(UINTN)-1, &VmContext.StackPool, &StackIndex);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+ VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE);
+ VmContext.Gpr[0] = (UINT32) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE);
+ VmContext.HighStackBottom = (UINTN) VmContext.Gpr[0];
+ VmContext.Gpr[0] -= sizeof (UINTN);
+
+ //
+ // Align the stack on a natural boundary.
+ //
+ VmContext.Gpr[0] &= ~(VM_REGISTER)(sizeof (UINTN) - 1);
+
+ //
+ // Put a magic value in the stack gap, then adjust down again.
+ //
+ *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) VM_STACK_KEY_VALUE;
+ VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.Gpr[0];
+
+ //
+ // The stack upper to LowStackTop belongs to the VM.
+ //
+ VmContext.LowStackTop = (UINTN) VmContext.Gpr[0];
+
+ //
+ // For the worst case, assume there are 4 arguments passed in registers, store
+ // them to VM's stack.
+ //
+ PushU32 (&VmContext, (UINT32) Args5_16[11]);
+ PushU32 (&VmContext, (UINT32) Args5_16[10]);
+ PushU32 (&VmContext, (UINT32) Args5_16[9]);
+ PushU32 (&VmContext, (UINT32) Args5_16[8]);
+ PushU32 (&VmContext, (UINT32) Args5_16[7]);
+ PushU32 (&VmContext, (UINT32) Args5_16[6]);
+ PushU32 (&VmContext, (UINT32) Args5_16[5]);
+ PushU32 (&VmContext, (UINT32) Args5_16[4]);
+ PushU32 (&VmContext, (UINT32) Args5_16[3]);
+ PushU32 (&VmContext, (UINT32) Args5_16[2]);
+ PushU32 (&VmContext, (UINT32) Args5_16[1]);
+ PushU32 (&VmContext, (UINT32) Args5_16[0]);
+ PushU32 (&VmContext, (UINT32) Arg4);
+ PushU32 (&VmContext, (UINT32) Arg3);
+ PushU32 (&VmContext, (UINT32) Arg2);
+ PushU32 (&VmContext, (UINT32) Arg1);
+
+ //
+ // Interpreter assumes 64-bit return address is pushed on the stack.
+ // Arm does not do this so pad the stack accordingly.
+ //
+ PushU32 (&VmContext, 0x0UL);
+ PushU32 (&VmContext, 0x0UL);
+ PushU32 (&VmContext, 0x12345678UL);
+ PushU32 (&VmContext, 0x87654321UL);
+
+ //
+ // For Arm, this is where we say our return address is
+ //
+ VmContext.StackRetAddr = (UINT64) VmContext.Gpr[0];
+
+ //
+ // We need to keep track of where the EBC stack starts. This way, if the EBC
+ // accesses any stack variables above its initial stack setting, then we know
+ // it's accessing variables passed into it, which means the data is on the
+ // VM's stack.
+ // When we're called, on the stack (high to low) we have the parameters, the
+ // return address, then the saved ebp. Save the pointer to the return address.
+ // EBC code knows that's there, so should look above it for function parameters.
+ // The offset is the size of locals (VMContext + Addr + saved ebp).
+ // Note that the interpreter assumes there is a 16 bytes of return address on
+ // the stack too, so adjust accordingly.
+ // VmContext.HighStackBottom = (UINTN)(Addr + sizeof (VmContext) + sizeof (Addr));
+ //
+
+ //
+ // Begin executing the EBC code
+ //
+ EbcDebuggerHookEbcInterpret (&VmContext);
+ EbcExecute (&VmContext);
+
+ //
+ // Return the value in Gpr[7] unless there was an error
+ //
+ ReturnEBCStack(StackIndex);
+ return (UINT64) VmContext.Gpr[7];
+}
+
+
+/**
+ Begin executing an EBC image.
+
+ @param ImageHandle image handle for the EBC application we're executing
+ @param SystemTable standard system table passed into an driver's entry
+ point
+ @param EntryPoint The entrypoint of EBC code.
+
+ @return The value returned by the EBC application we're going to run.
+
+**/
+UINT64
+EFIAPI
+ExecuteEbcImageEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable,
+ IN UINTN EntryPoint
+ )
+{
+ //
+ // Create a new VM context on the stack
+ //
+ VM_CONTEXT VmContext;
+ UINTN Addr;
+ EFI_STATUS Status;
+ UINTN StackIndex;
+
+ //
+ // Get the EBC entry point
+ //
+ Addr = EntryPoint;
+
+ //
+ // Now clear out our context
+ //
+ ZeroMem ((VOID *) &VmContext, sizeof (VM_CONTEXT));
+
+ //
+ // Save the image handle so we can track the thunks created for this image
+ //
+ VmContext.ImageHandle = ImageHandle;
+ VmContext.SystemTable = SystemTable;
+
+ //
+ // Set the VM instruction pointer to the correct location in memory.
+ //
+ VmContext.Ip = (VMIP) Addr;
+
+ //
+ // Initialize the stack pointer for the EBC. Get the current system stack
+ // pointer and adjust it down by the max needed for the interpreter.
+ //
+
+ //
+ // Allocate stack pool
+ //
+ Status = GetEBCStack(ImageHandle, &VmContext.StackPool, &StackIndex);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+ VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE);
+ VmContext.Gpr[0] = (UINT64)(UINTN) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE);
+ VmContext.HighStackBottom = (UINTN)VmContext.Gpr[0];
+ VmContext.Gpr[0] -= sizeof (UINTN);
+
+ //
+ // Put a magic value in the stack gap, then adjust down again
+ //
+ *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) VM_STACK_KEY_VALUE;
+ VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.Gpr[0];
+
+ //
+ // Align the stack on a natural boundary
+ // VmContext.Gpr[0] &= ~(sizeof(UINTN) - 1);
+ //
+ VmContext.LowStackTop = (UINTN) VmContext.Gpr[0];
+ VmContext.Gpr[0] -= sizeof (UINTN);
+ *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) SystemTable;
+ VmContext.Gpr[0] -= sizeof (UINTN);
+ *(UINTN *) (UINTN) (VmContext.Gpr[0]) = (UINTN) ImageHandle;
+
+ VmContext.Gpr[0] -= 16;
+ VmContext.StackRetAddr = (UINT64) VmContext.Gpr[0];
+ //
+ // VM pushes 16-bytes for return address. Simulate that here.
+ //
+
+ //
+ // Begin executing the EBC code
+ //
+ EbcDebuggerHookExecuteEbcImageEntryPoint (&VmContext);
+ EbcExecute (&VmContext);
+
+ //
+ // Return the value in Gpr[7] unless there was an error
+ //
+ ReturnEBCStack(StackIndex);
+ return (UINT64) VmContext.Gpr[7];
+}
+
+
+/**
+ Create thunks for an EBC image entry point, or an EBC protocol service.
+
+ @param ImageHandle Image handle for the EBC image. If not null, then
+ we're creating a thunk for an image entry point.
+ @param EbcEntryPoint Address of the EBC code that the thunk is to call
+ @param Thunk Returned thunk we create here
+ @param Flags Flags indicating options for creating the thunk
+
+ @retval EFI_SUCCESS The thunk was created successfully.
+ @retval EFI_INVALID_PARAMETER The parameter of EbcEntryPoint is not 16-bit
+ aligned.
+ @retval EFI_OUT_OF_RESOURCES There is not enough memory to created the EBC
+ Thunk.
+ @retval EFI_BUFFER_TOO_SMALL EBC_THUNK_SIZE is not larger enough.
+
+**/
+EFI_STATUS
+EbcCreateThunks (
+ IN EFI_HANDLE ImageHandle,
+ IN VOID *EbcEntryPoint,
+ OUT VOID **Thunk,
+ IN UINT32 Flags
+ )
+{
+ EBC_INSTRUCTION_BUFFER *InstructionBuffer;
+
+ //
+ // Check alignment of pointer to EBC code
+ //
+ if ((UINT32) (UINTN) EbcEntryPoint & 0x01) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ InstructionBuffer = AllocatePool (sizeof (EBC_INSTRUCTION_BUFFER));
+ if (InstructionBuffer == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Give them the address of our buffer we're going to fix up
+ //
+ *Thunk = InstructionBuffer;
+
+ //
+ // Copy whole thunk instruction buffer template
+ //
+ CopyMem (InstructionBuffer, &mEbcInstructionBufferTemplate,
+ sizeof (EBC_INSTRUCTION_BUFFER));
+
+ //
+ // Patch EbcEntryPoint and EbcLLEbcInterpret
+ //
+ InstructionBuffer->EbcEntryPoint = (UINT32)EbcEntryPoint;
+ if ((Flags & FLAG_THUNK_ENTRY_POINT) != 0) {
+ InstructionBuffer->EbcLlEntryPoint = (UINT32)EbcLLExecuteEbcImageEntryPoint;
+ } else {
+ InstructionBuffer->EbcLlEntryPoint = (UINT32)EbcLLEbcInterpret;
+ }
+
+ //
+ // Add the thunk to the list for this image. Do this last since the add
+ // function flushes the cache for us.
+ //
+ EbcAddImageThunk (ImageHandle, InstructionBuffer,
+ sizeof (EBC_INSTRUCTION_BUFFER));
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This function is called to execute an EBC CALLEX instruction.
+ The function check the callee's content to see whether it is common native
+ code or a thunk to another piece of EBC code.
+ If the callee is common native code, use EbcLLCAllEXASM to manipulate,
+ otherwise, set the VM->IP to target EBC code directly to avoid another VM
+ be startup which cost time and stack space.
+
+ @param VmPtr Pointer to a VM context.
+ @param FuncAddr Callee's address
+ @param NewStackPointer New stack pointer after the call
+ @param FramePtr New frame pointer after the call
+ @param Size The size of call instruction
+
+**/
+VOID
+EbcLLCALLEX (
+ IN VM_CONTEXT *VmPtr,
+ IN UINTN FuncAddr,
+ IN UINTN NewStackPointer,
+ IN VOID *FramePtr,
+ IN UINT8 Size
+ )
+{
+ CONST EBC_INSTRUCTION_BUFFER *InstructionBuffer;
+
+ //
+ // Processor specific code to check whether the callee is a thunk to EBC.
+ //
+ InstructionBuffer = (EBC_INSTRUCTION_BUFFER *)FuncAddr;
+
+ if (CompareMem (InstructionBuffer, &mEbcInstructionBufferTemplate,
+ sizeof(EBC_INSTRUCTION_BUFFER) - 2 * sizeof (UINT32)) == 0) {
+ //
+ // The callee is a thunk to EBC, adjust the stack pointer down 16 bytes and
+ // put our return address and frame pointer on the VM stack.
+ // Then set the VM's IP to new EBC code.
+ //
+ VmPtr->Gpr[0] -= 8;
+ VmWriteMemN (VmPtr, (UINTN) VmPtr->Gpr[0], (UINTN) FramePtr);
+ VmPtr->FramePtr = (VOID *) (UINTN) VmPtr->Gpr[0];
+ VmPtr->Gpr[0] -= 8;
+ VmWriteMem64 (VmPtr, (UINTN) VmPtr->Gpr[0], (UINT64) (UINTN) (VmPtr->Ip + Size));
+
+ VmPtr->Ip = (VMIP) InstructionBuffer->EbcEntryPoint;
+ } else {
+ //
+ // The callee is not a thunk to EBC, call native code,
+ // and get return value.
+ //
+ // Note that we are not able to distinguish which part of the interval
+ // [NewStackPointer, FramePtr] consists of stacked function arguments for
+ // this call, and which part simply consists of locals in the caller's
+ // stack frame. All we know is that there is an 8 byte gap at the top that
+ // we can ignore.
+ //
+ VmPtr->Gpr[7] = EbcLLCALLEXNative (FuncAddr, NewStackPointer, FramePtr - 8);
+
+ //
+ // Advance the IP.
+ //
+ VmPtr->Ip += Size;
+ }
+}
+
diff --git a/MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf b/MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf
index ce413c0b25f6..b55538304605 100644
--- a/MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf
+++ b/MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf
@@ -25,7 +25,7 @@ [Defines]
#
# The following information is for reference only and not required by the build tools.
#
-# VALID_ARCHITECTURES = IA32 X64 IPF AARCH64
+# VALID_ARCHITECTURES = IA32 X64 IPF ARM AARCH64
#
[Sources]
@@ -86,6 +86,10 @@ [Sources.AARCH64]
AArch64/EbcSupport.c
AArch64/EbcLowLevel.S
+[Sources.ARM]
+ Arm/EbcSupport.c
+ Arm/EbcLowLevel.S
+
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
diff --git a/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbDisasmSupport.h b/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbDisasmSupport.h
index af5a7cab99f9..db3b0a1f454f 100644
--- a/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbDisasmSupport.h
+++ b/MdeModulePkg/Universal/EbcDxe/EbcDebugger/EdbDisasmSupport.h
@@ -19,10 +19,10 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#define EDB_BYTECODE_NUMBER_IN_LINE 5
-#ifdef EFI32
+#if defined (MDE_CPU_IA32) || defined (MDE_CPU_ARM)
#define EDB_PRINT_ADDRESS_FORMAT L"%08x: "
#else
-// To use 012l instead of 016l because space is not enough
+// We use 012l instead of 016l due to space constraints
#define EDB_PRINT_ADDRESS_FORMAT L"%012lx: "
#endif
diff --git a/MdeModulePkg/Universal/EbcDxe/EbcDebuggerConfig.inf b/MdeModulePkg/Universal/EbcDxe/EbcDebuggerConfig.inf
index 0d931a46f01a..c08ad17dd1c8 100644
--- a/MdeModulePkg/Universal/EbcDxe/EbcDebuggerConfig.inf
+++ b/MdeModulePkg/Universal/EbcDxe/EbcDebuggerConfig.inf
@@ -25,7 +25,7 @@ [Defines]
#
# The following information is for reference only and not required by the build tools.
#
-# VALID_ARCHITECTURES = IA32 X64 IPF AARCH64
+# VALID_ARCHITECTURES = IA32 X64 IPF ARM AARCH64
#
[Sources]
diff --git a/MdeModulePkg/Universal/EbcDxe/EbcDxe.inf b/MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
index d11888e25f92..78e058b6cbc6 100644
--- a/MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
+++ b/MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
@@ -29,7 +29,7 @@ [Defines]
#
# The following information is for reference only and not required by the build tools.
#
-# VALID_ARCHITECTURES = IA32 X64 IPF AARCH64
+# VALID_ARCHITECTURES = IA32 X64 IPF ARM AARCH64
#
[Sources]
@@ -57,6 +57,10 @@ [Sources.IPF]
Ipf/EbcSupport.c
Ipf/EbcLowLevel.s
+[Sources.ARM]
+ Arm/EbcSupport.c
+ Arm/EbcLowLevel.S
+
[Sources.AARCH64]
AArch64/EbcSupport.c
AArch64/EbcLowLevel.S
--
2.9.3.windows.2
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 3/5] MdeModulePkg/EbcDxe: add a stack tracker for ARM EBC->native support
2017-01-24 12:30 [PATCH 1/5] MdeModulePkg/EbcDxe: allow VmReadIndex##() to return a decoded index Pete Batard
2017-01-24 12:30 ` [PATCH 2/5] MdeModulePkg/EbcDxe: add ARM support Pete Batard
@ 2017-01-24 12:30 ` Pete Batard
2017-01-24 12:30 ` [PATCH 4/5] MdeModulePkg/EbcDxe: add call signatures for ARM native->EBC support Pete Batard
2017-01-24 12:30 ` [PATCH 5/5] BaseTools: add scripts to generate EBC call signatures Pete Batard
3 siblings, 0 replies; 5+ messages in thread
From: Pete Batard @ 2017-01-24 12:30 UTC (permalink / raw)
To: edk2-devel
* This patch fixes the CALLEX to native vs ARM calling convention issue by
tracking whether each potential call argument is a natural or a 64-bit
value.
* This is accomplished by monitoring stack pointer operations, including
arithmetic or single stack buffer reallocation, through a separate stack
tracker. The stack tracker is then used, at calltime to determine 64-bit
arguments that need to be aligned.
* While currently ARM specific, the stack tracking is added in a generic
manner, so that any future platform that requires a similar mechanism may
do so with minimal changes.
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Pete Batard <pete@akeo.ie>
---
MdeModulePkg/Include/Protocol/EbcVmTest.h | 2 +
MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S | 121 ++--
.../Universal/EbcDxe/Arm/EbcStackTracker.c | 634 +++++++++++++++++++++
MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c | 124 +++-
MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf | 4 +
MdeModulePkg/Universal/EbcDxe/EbcDxe.inf | 4 +
MdeModulePkg/Universal/EbcDxe/EbcExecute.c | 155 ++++-
MdeModulePkg/Universal/EbcDxe/EbcStackTracker.c | 65 +++
8 files changed, 1038 insertions(+), 71 deletions(-)
create mode 100644 MdeModulePkg/Universal/EbcDxe/Arm/EbcStackTracker.c
create mode 100644 MdeModulePkg/Universal/EbcDxe/EbcStackTracker.c
diff --git a/MdeModulePkg/Include/Protocol/EbcVmTest.h b/MdeModulePkg/Include/Protocol/EbcVmTest.h
index 9eedca1906a2..12d5e5217059 100644
--- a/MdeModulePkg/Include/Protocol/EbcVmTest.h
+++ b/MdeModulePkg/Include/Protocol/EbcVmTest.h
@@ -111,6 +111,8 @@ typedef struct {
UINTN ImageBase;
VOID *StackPool;
VOID *StackTop;
+ VOID *StackTracker; ///< pointer to an optional, opaque and arch-specific
+ /// structure, which may be used to track stack ops.
} VM_CONTEXT;
/**
diff --git a/MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S b/MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S
index cb3c11f71673..dc9c3946ced2 100644
--- a/MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S
+++ b/MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S
@@ -3,6 +3,7 @@
// This code provides low level routines that support the Virtual Machine
// for option ROMs.
//
+// Copyright (c) 2016, Pete Batard. All rights reserved.<BR>
// Copyright (c) 2016, Linaro, Ltd. All rights reserved.<BR>
// Copyright (c) 2015, The Linux Foundation. All rights reserved.<BR>
// Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>
@@ -20,11 +21,11 @@
.thumb
.syntax unified
-ASM_GLOBAL ASM_PFX(EbcLLCALLEXNative)
+ASM_GLOBAL ASM_PFX(EbcLLCALLEXNativeArm)
ASM_GLOBAL ASM_PFX(EbcLLEbcInterpret)
ASM_GLOBAL ASM_PFX(EbcLLExecuteEbcImageEntryPoint)
-INTERWORK_FUNC(EbcLLCALLEXNative)
+INTERWORK_FUNC(EbcLLCALLEXNativeArm)
INTERWORK_FUNC(EbcLLEbcInterpret)
INTERWORK_FUNC(EbcLLExecuteEbcImageEntryPoint)
@@ -34,62 +35,96 @@ ASM_GLOBAL ASM_PFX(mEbcInstructionBufferTemplate)
// EbcLLCALLEX
//
// This function is called to execute an EBC CALLEX instruction.
-// This instruction requires that we thunk out to external native
-// code. For ARM, we copy the VM stack into the main stack and then pop
-// the first 4 arguments off according to the ARM Procedure Call Standard
-// On return, we restore the stack pointer to its original location.
+// This instruction requires that we thunk out to external native code.
+// Note that due to the ARM Procedure Call Standard, we may have to align
+// 64-bit arguments to an even register or dword aligned stack address,
+// which is what the extra ArgLayout parameter is used for.
+// Also, to optimize for speed, we arbitrarily limit to 16 the maximum
+// number of arguments a native call can have.
//
//****************************************************************************
-// UINTN EbcLLCALLEXNative(UINTN FuncAddr, UINTN NewStackPointer, VOID *FramePtr)
-ASM_PFX(EbcLLCALLEXNative):
- mov ip, r0 // Preserve r0
+// UINTN EbcLLCALLEXNativeArm(UINTN FuncAddr, UINTN NewStackPointer,
+// VOID *FramePtr, UINT16 ArgLayout)
+ASM_PFX(EbcLLCALLEXNativeArm):
+
+ mov ip, r1 // Use ip as our argument walker
+ push {r0, r4-r6}
+ mov r4, r2 // Keep a copy of FramePtr
+ mov r5, r3 // Keep a copy of ArgLayout
+ mov r6, #2 // Arg counter (2 for r0 and r2)
//
- // If the EBC stack frame is smaller than or equal to 16 bytes, we know there
- // are no stacked arguments #5 and beyond that we need to copy to the native
- // stack. In this case, we can perform a tail call which is much more
- // efficient, since there is no need to touch the native stack at all.
+ // Process the register arguments, skipping r1 and r3
+ // as needed, according to the argument layout.
//
- sub r3, r2, r1 // Length = NewStackPointer - FramePtr
- cmp r3, #16
- bgt 0f
-
- ldrd r2, r3, [r1, #8]
- ldrd r0, r1, [r1]
-
+ lsrs r5, r5, #1
+ bcc 0f // Is our next argument 64-bit?
+ ldr r0, [ip], #4 // Yes => fill in r0-r1
+ ldr r1, [ip], #4
+ b 1f
+0: ldr r0, [ip], #4 // No => fill in r0
+ lsrs r5, r5, #1
+ bcs 2f // Is our next argument 64-bit?
+ ldr r1, [ip], #4 // No => fill in r1
+ add r6, r6, #1 // Increment arg counter for r1
+1: lsrs r5, r5, #1
+ bcc 0f // Is our next argument 64-bit?
+2: ldr r2, [ip], #4 // Yes => fill in r2-r3
+ ldr r3, [ip], #4
+ b 1f
+0: ldr r2, [ip], #4 // No => fill in r2
+ tst r5, #1
+ bne 1f // Is our next argument 64-bit?
+ ldr r3, [ip], #4 // No => fill in r3
+ lsr r5, r5, #1
+ add r6, r6, #1 // Increment arg counter for r3
+1: cmp r4, ip
+ bgt 0f // More args?
+ pop {ip} // No => perform a tail call
+ pop {r4-r6}
bx ip
//
- // More than 16 bytes: we need to build the full native stack frame and copy
- // the part of the VM stack exceeding 16 bytes (which may contain stacked
- // arguments) to the native stack
+ // Cannot perform a tail call => We need to properly enqueue (and
+ // align) all EBC stack parameters before we invoke the native call.
//
-0: push {r4, lr}
- mov r4, sp
+0: push {r7-r10, lr}
+ mov r10, sp // Preserve original sp
+ sub r7, r10, #116 // Space for 14 64-bit args (+1 word)
+ and r7, r7, #0xfffffff8 // Start with an aligned stack
+ mov sp, r7
//
- // Ensure that the stack pointer remains 8 byte aligned,
- // even if the size of the VM stack frame is not a multiple of 8
+ // Duplicate EBC data onto the local stack:
+ // ip = EBC arg walker
+ // r4 = top of EBC stack frame
+ // r5 = arg layout
+ // r6 = arg counter
+ // r7 = local stack pointer
//
- add r1, r1, #16 // Skip over [potential] reg params
- tst r3, #7 // Multiple of 8?
- beq 1f
- ldr r3, [r2, #-4]! // No? Then push one word
- str r3, [sp, #-8]! // ... but use two slots
+0: add r6, r6, #1 // Increment the arg counter
+ lsrs r5, r5, #1
+ bcs 1f // Is the current argument 64 bit?
+ ldr r8, [ip], #4 // No? Then just copy it onstack
+ str r8, [r7], #4
b 2f
+1: tst r7, #7 // Yes. Is SP aligned to 8 bytes?
+ beq 1f
+ add r7, r7, #4 // No? Realign.
+1: ldr r8, [ip], #4 // EBC stack may not be aligned for ldrd...
+ ldr r9, [ip], #4
+ strd r8, r9, [r7], #8 // ...but the local stack is.
+2: cmp r6, #16 // More than 16 arguments processed?
+ bge 0f
+ cmp r4, ip // Reached the top of the EBC stack frame?
+ bgt 0b
-1: ldrd r0, r3, [r2, #-8]!
- strd r0, r3, [sp, #-8]!
-2: cmp r2, r1
- bgt 1b
-
- ldrd r2, r3, [r1, #-8]
- ldrd r0, r1, [r1, #-16]
-
+0: ldr ip, [r10, #20] // Set the target address in ip
blx ip
-
- mov sp, r4
- pop {r4, pc}
+ mov sp, r10 // Restore the stack, dequeue and return
+ pop {r7-r10, ip}
+ pop {r3, r4-r6} // Destack with r3, as r0 may contain a return value
+ mov pc, ip
//****************************************************************************
// EbcLLEbcInterpret
diff --git a/MdeModulePkg/Universal/EbcDxe/Arm/EbcStackTracker.c b/MdeModulePkg/Universal/EbcDxe/Arm/EbcStackTracker.c
new file mode 100644
index 000000000000..ab42d8ad4e92
--- /dev/null
+++ b/MdeModulePkg/Universal/EbcDxe/Arm/EbcStackTracker.c
@@ -0,0 +1,634 @@
+/** @file
+ This module contains the stack tracking routines used for parameter
+ processing and alignment on ARM.
+
+Copyright (c) 2016, Pete Batard. All rights reserved.<BR>
+
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "EbcInt.h"
+#include "EbcExecute.h"
+
+//
+// Amount of space to be used by the stack argument tracker
+// In most cases, less than 2 bits should be used for every 32 bits of
+// stack data and we can grow our buffer if needed, so start at 1/64th
+//
+#define STACK_TRACKER_SIZE (STACK_POOL_SIZE / 64)
+#define STACK_TRACKER_SIZE_MAX (STACK_POOL_SIZE / 8)
+
+//
+// Stack tracking data structure, used to compute parameter alignment.
+//
+typedef struct {
+ UINT8 *Data; ///< stack tracker data buffer
+ INTN Size; ///< size of the stack tracker data buffer, in bytes
+ INTN Index; ///< current stack tracker index, in 1/4th bytes
+ INTN OrgIndex; ///< copy of the index, used on stack buffer switch
+ UINTN OrgStackPointer; ///< copy of the stack pointer, used on stack buffer switch
+} EBC_STACK_TRACKER;
+
+/**
+ Allocate a stack tracker structure and initialize it.
+
+ @param VmPtr The pointer to current VM context.
+
+ @retval EFI_OUT_OF_RESOURCES Not enough memory to set the stack tracker.
+ @retval EFI_SUCCESS The stack tracker was initialized successfully.
+
+**/
+EFI_STATUS
+AllocateStackTracker (
+ IN VM_CONTEXT *VmPtr
+ )
+{
+ EBC_STACK_TRACKER *StackTracker;
+
+ StackTracker = AllocateZeroPool(sizeof(EBC_STACK_TRACKER));
+ if (StackTracker == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ StackTracker->Size = STACK_TRACKER_SIZE;
+ StackTracker->Data = AllocatePool(StackTracker->Size);
+ if (StackTracker->Data == NULL) {
+ FreePool(StackTracker);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Add tracking for EfiMain() call just in case
+ //
+ StackTracker->Data[0] = 0x05; // 2 x UINT64, 2 x UINTN
+ StackTracker->Index = 4;
+
+ VmPtr->StackTracker = (VOID*) StackTracker;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Free a stack tracker structure.
+
+ @param VmPtr The pointer to current VM context.
+**/
+VOID
+FreeStackTracker (
+ IN VM_CONTEXT *VmPtr
+ )
+{
+ EBC_STACK_TRACKER *StackTracker;
+
+ StackTracker = (EBC_STACK_TRACKER*) VmPtr->StackTracker;
+
+ FreePool(StackTracker->Data);
+ FreePool(StackTracker);
+ VmPtr->StackTracker = NULL;
+}
+
+/**
+ Return the argument layout for the current function call, according
+ to the current stack tracking data.
+ The first argument is at bit 0, with the 16th parameter at bit 15,
+ with a bit set to 1 if an argument is 64 bit, 0 otherwise.
+
+ @param VmPtr The pointer to current VM context.
+
+ @return A 16 bit argument layout.
+**/
+UINT16
+GetArgLayout (
+ IN VM_CONTEXT *VmPtr
+ )
+{
+ EBC_STACK_TRACKER *StackTracker;
+ UINT16 ArgLayout, Mask;
+ INTN Index;
+
+ StackTracker = (EBC_STACK_TRACKER*) VmPtr->StackTracker;
+
+ ASSERT (StackTracker->Data != NULL);
+ ASSERT (StackTracker->Index >= 0);
+
+ // One major issue we have on Arm is that, if a mix of natural and
+ // 64-bit arguments are stacked as parameters for a native call, we risk
+ // running afoul of the AAPCS (the ARM calling convention) which mandates
+ // that the first 2 to 4 arguments are passed as register, and that any
+ // 64-bit argument *MUST* start either on an even register or at an 8-byte
+ // aligned address.
+ //
+ // So if, for instance, we have a natural parameter (32-bit) in Arg0 and
+ // a 64-bit parameter in Arg1, then, after we copy the data onto r0, we
+ // must skip r1 so that Arg1 starts at register r2. Similarly, we may have
+ // to skip words on stack for 64-bit parameter alignment.
+ //
+ // This is where our stack tracker comes into play, as it tracks EBC stack
+ // manipulations and allows us to find whether each of the (potential)
+ // arguments being passed to a native CALLEX is 64-bit or natural. As
+ // a reminder, and as per the specs (UEFI 2.6, section 21.9.3), 64-bit
+ // or natural are the ONLY types of arguments that are allowed when
+ // performing EBC function calls, inluding native ones (in which case any
+ // argument of 32 bit or less must be stacked as a natural).
+ //
+ // Through the stack tracker, we can retreive the last 16 argument types
+ // (decoded from the 2 bit sequences), which we convert to a 16-bit value
+ // that gives us the argument layout.
+ //
+ ArgLayout = 0;
+ Mask = 1;
+ for (Index = StackTracker->Index - 1; Index >= StackTracker->Index - 16; Index--) {
+ if ((Index / 4) < 0) {
+ break; // Don't underflow the tracker.
+ }
+ //
+ // Actual function parameters are stored as 2 bit sequences in our
+ // tracker, with 00b indicating a 64-bit parameter and 01b a natural.
+ // When converting this to ArgLayout, we set the relevant arg position
+ // bit to 1, for a 64-bit arg, or 0, for a natural.
+ // Also, since there's little point in avoiding to process 4-bit
+ // sequences (for stack values that aren't natural or 64-bit, and thus
+ // can't be used as arguments) we also process them as 2-bit.
+ //
+ if ((StackTracker->Data[Index / 4] & (1 << (2 * (3 - (Index % 4))))) == 0) {
+ ArgLayout |= Mask;
+ }
+ Mask <<= 1;
+ }
+ return ArgLayout;
+}
+
+/**
+ Returns the current stack tracker entry.
+
+ @param StackTracker A pointer to the stack tracker struct.
+
+ @return The decoded stack tracker index [0x00, 0x08].
+
+ The decoding of stack tracker entries operates as follows:
+ 00b -> 0000b
+ 01b -> 1000b
+ 1xb preceded by yzb -> 0xyzb
+ (e.g. 11b preceded by 10b -> 0110b)
+
+**/
+UINT8
+GetStackTrackerEntry (
+ IN EBC_STACK_TRACKER *StackTracker
+ )
+{
+ UINT8 Index, PreviousIndex;
+
+ if (StackTracker->Index < 0) {
+ //
+ // Anything prior to tracking is considered aligned to 64 bits
+ //
+ return 0x00;
+ }
+
+ Index = StackTracker->Data[(StackTracker->Index - 1) / 4];
+ Index >>= 6 - (2 * ((StackTracker->Index - 1) % 4));
+ Index &= 0x03;
+
+ if (Index == 0x01) {
+ Index = 0x08;
+ } else if ((Index & 0x02) != 0) {
+ PreviousIndex = StackTracker->Data[(StackTracker->Index - 2) / 4];
+ PreviousIndex >>= 6 - (2 * ((StackTracker->Index - 2) % 4));
+ Index = ((Index << 2) & 0x04) | (PreviousIndex & 0x03);
+ }
+
+ return Index;
+}
+
+/**
+ Add a single new stack tracker entry.
+
+ @param StackTracker A pointer to the stack tracker struct.
+ @param Value The value to be encoded.
+
+ @retval EFI_OUT_OF_RESOURCES Not enough memory to grow the stack tracker.
+ @retval EFI_SUCCESS The entry was added successfully.
+
+ For reference, each 2-bit index sequence is stored as follows:
+ Stack tracker byte: byte 0 byte 1 byte 3
+ Stack tracker index: [0|1|2|3] [4|5|6|7] [8|9|...]
+
+ Valid values are in [0x00, 0x08] and get encoded as:
+ 0000b -> 00b (single 2-bit sequence)
+ 0001b -> 01b 10b (dual 2-bit sequence)
+ 0010b -> 10b 10b (dual 2-bit sequence)
+ 0011b -> 11b 10b (dual 2-bit sequence)
+ 0100b -> 00b 11b (dual 2-bit sequence)
+ 0101b -> 01b 11b (dual 2-bit sequence)
+ 0110b -> 10b 11b (dual 2-bit sequence)
+ 0111b -> 11b 11b (dual 2-bit sequence)
+ 1000b -> 01b (single 2-bit sequence)
+**/
+EFI_STATUS
+AddStackTrackerEntry (
+ IN EBC_STACK_TRACKER *StackTracker,
+ IN UINT8 Value
+ )
+{
+ UINT8 Pass, Data;
+
+ ASSERT (Value <= 0x08);
+ if (Value == 0x08) {
+ Data = 0x01;
+ } else {
+ Data = Value & 0x03;
+ }
+
+ for (Pass = 0; Pass < 2; Pass++) {
+ if ((StackTracker->Index / 4) >= StackTracker->Size) {
+ //
+ // Grow the stack tracker buffer
+ //
+ StackTracker->Data = ReallocatePool(StackTracker->Size,
+ StackTracker->Size*2, StackTracker->Data);
+ if (StackTracker->Data == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ StackTracker->Size *= 2;
+ ASSERT (StackTracker->Size <= STACK_TRACKER_SIZE_MAX);
+ }
+
+ //
+ // Clear bits and add our value
+ //
+ StackTracker->Data[StackTracker->Index / 4] &=
+ 0xFC << (6 - 2 * (StackTracker->Index % 4));
+ StackTracker->Data[StackTracker->Index / 4] |=
+ Data << (6 - 2 * (StackTracker->Index % 4));
+ StackTracker->Index++;
+ if ((Value >= 0x01) && (Value <= 0x07)) {
+ //
+ // 4-bits needed => Append the extra 2 bit value
+ //
+ Data = (Value >> 2) | 0x02;
+ } else {
+ //
+ // 2-bit encoding was enough
+ //
+ break;
+ }
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ Insert 'Count' number of 'Value' bytes into the stack tracker.
+ This expects the current entry to be aligned to byte boundary.
+
+ @param StackTracker A pointer to the stack tracker struct.
+ @param Value The byte value to be inserted.
+ @param Count The number of times the value should be repeated.
+
+ @retval EFI_OUT_OF_RESOURCES Not enough memory to grow the stack tracker.
+ @retval EFI_SUCCESS The entries were added successfully.
+
+**/
+EFI_STATUS
+AddStackTrackerBytes (
+ IN EBC_STACK_TRACKER *StackTracker,
+ IN UINT8 Value,
+ IN INTN Count
+ )
+{
+ UINTN ByteNum, NewSize;
+ INTN UpdatedIndex;
+
+ //
+ // Byte alignement should have been sorted prior to this call
+ //
+ ASSERT (StackTracker->Index % 4 == 0);
+
+ UpdatedIndex = StackTracker->Index + 4 * Count;
+ if (UpdatedIndex >= StackTracker->Size) {
+ //
+ // Grow the stack tracker buffer
+ //
+ for (NewSize = StackTracker->Size * 2; NewSize <= UpdatedIndex;
+ NewSize *= 2);
+ ASSERT (NewSize <= STACK_TRACKER_SIZE_MAX);
+ StackTracker->Data = ReallocatePool(StackTracker->Size, NewSize,
+ StackTracker->Data);
+ if (StackTracker->Data == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ StackTracker->Size = NewSize;
+ }
+ for (ByteNum = 0; ByteNum < Count; ByteNum++) {
+ StackTracker->Data[(StackTracker->Index / 4) + ByteNum] = Value;
+ }
+ StackTracker->Index = UpdatedIndex;
+ return EFI_SUCCESS;
+}
+
+/**
+ Delete a single stack tracker entry.
+
+ @param StackTracker A pointer to the stack tracker struct.
+
+ @retval EFI_UNSUPPORTED The stack tracker is being underflown due
+ to unbalanced stack operations.
+ @retval EFI_SUCCESS The index was added successfully.
+**/
+EFI_STATUS
+DelStackTrackerEntry (
+ IN EBC_STACK_TRACKER *StackTracker
+ )
+{
+ UINT8 Index;
+
+ ASSERT (StackTracker->Index > 0);
+
+ //
+ // Don't care about clearing the used bits, just update the index
+ //
+ StackTracker->Index--;
+ Index = StackTracker->Data[StackTracker->Index / 4];
+ Index >>= 6 - (2 * (StackTracker->Index % 4));
+
+ if ((Index & 0x02) != 0) {
+ //
+ // 4-bit sequence
+ //
+ StackTracker->Index--;
+ }
+ if (StackTracker->Index < 0) {
+ return EFI_UNSUPPORTED;
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ Update the stack tracker according to the latest natural and constant
+ value stack manipulation operations.
+
+ @param VmPtr The pointer to current VM context.
+ @param NaturalUnits The number of natural values that were pushed (>0) or
+ popped (<0).
+ @param ConstUnits The number of const bytes that were pushed (>0) or
+ popped (<0).
+
+ @retval EFI_OUT_OF_RESOURCES Not enough memory to grow the stack tracker.
+ @retval EFI_UNSUPPORTED The stack tracker is being underflown due to
+ unbalanced stack operations.
+ @retval EFI_SUCCESS The stack tracker was updated successfully.
+
+**/
+EFI_STATUS
+UpdateStackTracker (
+ IN VM_CONTEXT *VmPtr,
+ IN INTN NaturalUnits,
+ IN INTN ConstUnits
+ )
+{
+ EFI_STATUS Status;
+ UINT8 LastEntry;
+ EBC_STACK_TRACKER *StackTracker;
+
+ StackTracker = (EBC_STACK_TRACKER*) VmPtr->StackTracker;
+
+ //
+ // Mismatched signage should already have been filtered out.
+ //
+ ASSERT ( ((NaturalUnits >= 0) && (ConstUnits >= 0)) ||
+ ((NaturalUnits <= 0) && (ConstUnits <= 0)) );
+
+ while (NaturalUnits < 0) {
+ //
+ // Add natural indexes (1000b) into our stack tracker
+ // Note, we don't care if the previous entry was aligned as a non 64-bit
+ // aligned entry cannot be used as a call parameter in valid EBC code.
+ // This also adds the effect of re-aligning our data to 64 bytes, which
+ // will help speed up tracking of local stack variables (arrays, etc.)
+ //
+ if ((StackTracker->Index % 4 == 0) && (NaturalUnits <= -4)) {
+ //
+ // Optimize adding a large number of naturals, such as ones reserved
+ // for local function variables/arrays. 0x55 means 4 naturals.
+ //
+ Status = AddStackTrackerBytes (StackTracker, 0x55, -NaturalUnits / 4);
+ NaturalUnits -= 4 * (NaturalUnits / 4); // Beware of negative modulos
+ } else {
+ Status = AddStackTrackerEntry (StackTracker, 0x08);
+ NaturalUnits++;
+ }
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ if (ConstUnits < 0) {
+ //
+ // Add constant indexes (0000b-0111b) into our stack tracker
+ // For constants, we do care if the previous entry was aligned to 64 bit
+ // since we need to include any existing non aligned indexes into the new
+ // set of (constant) indexes we are adding. Thus, if the last index is
+ // non zero (non 64-bit aligned) we just delete it and add the value to
+ // our constant.
+ //
+ LastEntry = GetStackTrackerEntry (StackTracker);
+ if ((LastEntry != 0x00) && (LastEntry != 0x08)) {
+ DelStackTrackerEntry (StackTracker);
+ ConstUnits -= LastEntry;
+ }
+
+ //
+ // Now, add as many 64-bit indexes as we can (0000b values)
+ //
+ while (ConstUnits <= -8) {
+ if ((ConstUnits <= -32) && (StackTracker->Index % 4 == 0)) {
+ //
+ // Optimize adding a large number of consts, such as ones reserved
+ // for local function variables/arrays. 0x00 means 4 64-bit consts.
+ //
+ Status = AddStackTrackerBytes (StackTracker, 0x00, -ConstUnits / 32);
+ ConstUnits -= 32 * (ConstUnits / 32); // Beware of negative modulos
+ } else {
+ Status = AddStackTrackerEntry (StackTracker, 0x00);
+ ConstUnits += 8;
+ }
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ //
+ // Add any remaining non 64-bit aligned bytes
+ //
+ if ((ConstUnits % 8) != 0) {
+ Status = AddStackTrackerEntry (StackTracker, (-ConstUnits) % 8);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+ }
+
+ while ((NaturalUnits > 0) || (ConstUnits > 0)) {
+ ASSERT(StackTracker->Index > 0);
+
+ //
+ // Delete natural/constant items from the stack tracker
+ //
+ if (StackTracker->Index % 4 == 0) {
+ //
+ // Speedup deletion for blocks of naturals/consts
+ //
+ while ((ConstUnits >= 32) &&
+ (StackTracker->Data[(StackTracker->Index-1) % 4] == 0x00)) {
+ //
+ // Start with consts since that's what we add last
+ //
+ StackTracker->Index -= 4;
+ ConstUnits -= 32;
+ }
+ while ((NaturalUnits >= 4) &&
+ (StackTracker->Data[(StackTracker->Index-1) % 4] == 0x55)) {
+ StackTracker->Index -= 4;
+ NaturalUnits -= 4;
+ }
+ }
+
+ if ((NaturalUnits == 0) && (ConstUnits == 0)) {
+ //
+ // May already have depleted our values through block processing above
+ //
+ break;
+ }
+
+ LastEntry = GetStackTrackerEntry (StackTracker);
+ Status = DelStackTrackerEntry (StackTracker);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (LastEntry == 0x08) {
+ if (NaturalUnits > 0) {
+ //
+ // Remove a natural and move on
+ //
+ NaturalUnits--;
+ continue;
+ }
+ //
+ // Got a natural while expecting const which may be the result of a
+ // "cloaked" stack operation (eg. R1 <- R0, stack ops on R1, R0 <- R1)
+ // which we monitor through the R0 delta converted to const. In this
+ // case just remove 4 const for each natural we find in the tracker.
+ //
+ LastEntry = 0x04;
+ } else if (ConstUnits <= 0) {
+ //
+ // Got a const while expecting a natural which may be the result of a
+ // "cloaked" stack operation => Substract 1 natural unit and add 4 to
+ // const units. Note that "cloaked" stack operations cannot break our
+ // tracking as the enqueuing of natural parameters is not something
+ // that can be concealed if one interprets the EBC specs correctly.
+ //
+ NaturalUnits--;
+ ConstUnits += 4;
+ }
+
+ if (LastEntry == 0x00) {
+ LastEntry = 0x08;
+ }
+ //
+ // Remove a set of const bytes
+ //
+ if (ConstUnits >= LastEntry) {
+ //
+ // Enough const bytes to remove at least one stack tracker entry
+ //
+ ConstUnits -= LastEntry;
+ } else {
+ //
+ // Not enough const bytes - need to add the remainder back
+ //
+ Status = AddStackTrackerEntry (StackTracker, LastEntry - ConstUnits);
+ ConstUnits = 0;
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+ }
+
+ ASSERT(StackTracker->Index >= 0);
+ return EFI_SUCCESS;
+}
+
+/**
+ Update the stack tracker by computing the R0 delta.
+
+ @param VmPtr The pointer to current VM context.
+ @param UpdatedR0 The new R0 value.
+
+ @retval EFI_OUT_OF_RESOURCES Not enough memory to grow the stack tracker.
+ @retval EFI_UNSUPPORTED The stack tracker is being underflown due to
+ unbalanced stack operations.
+ @retval EFI_SUCCESS The stack tracker was updated successfully.
+
+**/
+EFI_STATUS
+UpdateStackTrackerFromDelta (
+ IN VM_CONTEXT *VmPtr,
+ IN UINTN UpdatedR0
+ )
+{
+ INTN StackPointerDelta;
+ EBC_STACK_TRACKER *StackTracker;
+
+ StackTracker = (EBC_STACK_TRACKER*) VmPtr->StackTracker;
+
+ //
+ // Check if the updated R0 is still in our original stack buffer.
+ //
+ if ((StackTracker->OrgIndex == 0) && ((UpdatedR0 < (UINTN) VmPtr->StackPool) ||
+ (UpdatedR0 >= (UINTN) VmPtr->StackPool + STACK_POOL_SIZE))) {
+ //
+ // We are swicthing from the default stack buffer to a newly allocated
+ // one. Keep track of our current stack tracker index in case we come
+ // back to the original stack with unbalanced stack ops (e.g.
+ // SP <- New stack; Enqueue data without dequeuing; SP <- Old SP)
+ // Note that, since we are not moinitoring memory allocations, we can
+ // only ever detect swicthing in and out of the default stack buffer.
+ //
+ StackTracker->OrgIndex = StackTracker->Index;
+ StackTracker->OrgStackPointer = (UINTN) VmPtr->Gpr[0];
+
+ //
+ // Do not track switching. Just realign the index.
+ //
+ StackTracker->Index = 4 * ((StackTracker->Index + 3) / 4);
+ return EFI_SUCCESS;
+ }
+
+ if ((StackTracker->OrgIndex != 0) && ((UpdatedR0 >= (UINTN) VmPtr->StackPool) ||
+ (UpdatedR0 < (UINTN) VmPtr->StackPool + STACK_POOL_SIZE))) {
+ //
+ // Coming back from a newly allocated stack to the original one
+ // As we don't expect stack ops to have been properly balanced we just
+ // restore the old stack tracker index.
+ //
+ StackTracker->Index = StackTracker->OrgIndex;
+ StackTracker->OrgIndex = 0;
+ //
+ // There's also no guarantee that the new R0 is being restored to the
+ // value it held when stwiching stacks, so we use the value R0 held
+ // at the time the switch was performed, to compute the delta.
+ StackPointerDelta = UpdatedR0 - StackTracker->OrgStackPointer;
+ } else {
+ StackPointerDelta = UpdatedR0 - (UINTN) VmPtr->Gpr[0];
+ }
+
+ return UpdateStackTracker(VmPtr, 0, StackPointerDelta);
+}
diff --git a/MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c b/MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c
index 2a42b6c4d0e7..2e308772b477 100644
--- a/MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c
+++ b/MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c
@@ -2,6 +2,7 @@
This module contains EBC support routines that are customized based on
the target Arm processor.
+Copyright (c) 2016, Pete Batard. All rights reserved.<BR>
Copyright (c) 2016, Linaro, Ltd. All rights reserved.<BR>
Copyright (c) 2015, The Linux Foundation. All rights reserved.<BR>
Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
@@ -63,6 +64,69 @@ EbcLLExecuteEbcImageEntryPoint (
);
/**
+ This function is called to execute an EBC CALLEX instruction.
+ This is a special version of EbcLLCALLEXNative for Arm as we
+ need to pad or align arguments depending on whether they are
+ 64-bit or natural.
+
+ @param CallAddr The function address.
+ @param EbcSp The new EBC stack pointer.
+ @param FramePtr The frame pointer.
+ @param ArgLayout The layout for up to 32 arguments. 1 means
+ the argument is 64-bit, 0 means natural.
+
+ @return The unmodified value returned by the native code.
+
+**/
+INT64
+EFIAPI
+EbcLLCALLEXNativeArm (
+ IN UINTN CallAddr,
+ IN UINTN EbcSp,
+ IN VOID *FramePtr,
+ IN UINT16 ArgLayout
+ );
+
+/**
+ Allocate a stack tracker structure and initialize it.
+
+ @param VmPtr The pointer to current VM context.
+
+ @retval EFI_OUT_OF_RESOURCES Not enough memory to set the stack tracker.
+ @retval EFI_SUCCESS The stack tracker was initialized successfully.
+
+**/
+EFI_STATUS
+AllocateStackTracker (
+ IN VM_CONTEXT *VmPtr
+ );
+
+/**
+ Free a stack tracker structure.
+
+ @param VmPtr The pointer to current VM context.
+**/
+VOID
+FreeStackTracker (
+ IN VM_CONTEXT *VmPtr
+ );
+
+/**
+ Return the argument layout for the current function call, according
+ to the current stack tracking data.
+ The first argument is at bit 0, with the 16th parameter at bit 15,
+ with a bit set to 1 if an argument is 64 bit, 0 otherwise.
+
+ @param VmPtr The pointer to current VM context.
+
+ @return A 16 bit argument layout.
+**/
+UINT16
+GetArgLayout (
+ IN VM_CONTEXT *VmPtr
+ );
+
+/**
Pushes a 32 bit unsigned value to the VM stack.
@param VmPtr The pointer to current VM context.
@@ -85,7 +149,7 @@ PushU32 (
/**
- Begin executing an EBC image.
+ Begin executing an EBC function call.
This is a thunk function.
@@ -93,8 +157,7 @@ PushU32 (
@param Arg2 The 2nd argument.
@param Arg3 The 3rd argument.
@param Arg4 The 4th argument.
- @param Arg8 The 8th argument.
- @param EntryPoint The entrypoint of EBC code.
+ @param InstructionBuffer A pointer to the thunk instruction buffer.
@param Args5_16[] Array containing arguments #5 to #16.
@return The value returned by the EBC application we're going to run.
@@ -103,12 +166,12 @@ PushU32 (
UINT64
EFIAPI
EbcInterpret (
- IN UINTN Arg1,
- IN UINTN Arg2,
- IN UINTN Arg3,
- IN UINTN Arg4,
- IN UINTN EntryPoint,
- IN UINTN Args5_16[]
+ IN UINTN Arg1,
+ IN UINTN Arg2,
+ IN UINTN Arg3,
+ IN UINTN Arg4,
+ IN EBC_INSTRUCTION_BUFFER *InstructionBuffer,
+ IN UINTN Args5_16[]
)
{
//
@@ -122,7 +185,7 @@ EbcInterpret (
//
// Get the EBC entry point
//
- Addr = EntryPoint;
+ Addr = InstructionBuffer->EbcEntryPoint;
//
// Now clear out our context
@@ -135,20 +198,28 @@ EbcInterpret (
VmContext.Ip = (VMIP) Addr;
//
+ // Initialize the stack tracker
+ //
+ Status = AllocateStackTracker(&VmContext);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ //
// Initialize the stack pointer for the EBC. Get the current system stack
// pointer and adjust it down by the max needed for the interpreter.
//
-
- //
- // Adjust the VM's stack pointer down.
- //
-
Status = GetEBCStack((EFI_HANDLE)(UINTN)-1, &VmContext.StackPool, &StackIndex);
if (EFI_ERROR(Status)) {
+ FreeStackTracker(&VmContext);
return Status;
}
- VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE);
- VmContext.Gpr[0] = (UINT32) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE);
+
+ //
+ // Adjust the VM's stack pointer down.
+ //
+ VmContext.StackTop = (UINT8*)VmContext.StackPool + STACK_REMAIN_SIZE;
+ VmContext.Gpr[0] = (UINT64)(UINTN) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE);
VmContext.HighStackBottom = (UINTN) VmContext.Gpr[0];
VmContext.Gpr[0] -= sizeof (UINTN);
@@ -227,6 +298,7 @@ EbcInterpret (
// Return the value in Gpr[7] unless there was an error
//
ReturnEBCStack(StackIndex);
+ FreeStackTracker(&VmContext);
return (UINT64) VmContext.Gpr[7];
}
@@ -280,6 +352,14 @@ ExecuteEbcImageEntryPoint (
VmContext.Ip = (VMIP) Addr;
//
+ // Initialize the stack tracker
+ //
+ Status = AllocateStackTracker(&VmContext);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ //
// Initialize the stack pointer for the EBC. Get the current system stack
// pointer and adjust it down by the max needed for the interpreter.
//
@@ -287,11 +367,12 @@ ExecuteEbcImageEntryPoint (
//
// Allocate stack pool
//
- Status = GetEBCStack(ImageHandle, &VmContext.StackPool, &StackIndex);
+ Status = GetEBCStack (ImageHandle, &VmContext.StackPool, &StackIndex);
if (EFI_ERROR(Status)) {
+ FreeStackTracker(&VmContext);
return Status;
}
- VmContext.StackTop = (UINT8*)VmContext.StackPool + (STACK_REMAIN_SIZE);
+ VmContext.StackTop = (UINT8*)VmContext.StackPool + STACK_REMAIN_SIZE;
VmContext.Gpr[0] = (UINT64)(UINTN) ((UINT8*)VmContext.StackPool + STACK_POOL_SIZE);
VmContext.HighStackBottom = (UINTN)VmContext.Gpr[0];
VmContext.Gpr[0] -= sizeof (UINTN);
@@ -328,6 +409,7 @@ ExecuteEbcImageEntryPoint (
// Return the value in Gpr[7] unless there was an error
//
ReturnEBCStack(StackIndex);
+ FreeStackTracker(&VmContext);
return (UINT64) VmContext.Gpr[7];
}
@@ -459,7 +541,8 @@ EbcLLCALLEX (
// stack frame. All we know is that there is an 8 byte gap at the top that
// we can ignore.
//
- VmPtr->Gpr[7] = EbcLLCALLEXNative (FuncAddr, NewStackPointer, FramePtr - 8);
+ VmPtr->Gpr[7] = EbcLLCALLEXNativeArm (FuncAddr, NewStackPointer,
+ &((UINT8*)FramePtr)[-8], GetArgLayout(VmPtr));
//
// Advance the IP.
@@ -467,4 +550,3 @@ EbcLLCALLEX (
VmPtr->Ip += Size;
}
}
-
diff --git a/MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf b/MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf
index b55538304605..f627f66e5492 100644
--- a/MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf
+++ b/MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf
@@ -65,6 +65,9 @@ [Sources]
EbcDebugger/EdbSupportString.c
EbcDebugger/EdbSupportFile.c
+[Sources.Ia32, Sources.X64, Sources.IPF, Sources.AARCH64]
+ EbcStackTracker.c
+
[Sources.Ia32]
Ia32/EbcSupport.c
Ia32/EbcLowLevel.nasm
@@ -89,6 +92,7 @@ [Sources.AARCH64]
[Sources.ARM]
Arm/EbcSupport.c
Arm/EbcLowLevel.S
+ Arm/EbcStackTracker.c
[Packages]
MdePkg/MdePkg.dec
diff --git a/MdeModulePkg/Universal/EbcDxe/EbcDxe.inf b/MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
index 78e058b6cbc6..a82e1c062921 100644
--- a/MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
+++ b/MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
@@ -40,6 +40,9 @@ [Sources]
EbcInt.h
EbcInt.c
+[Sources.Ia32, Sources.X64, Sources.IPF, Sources.AARCH64]
+ EbcStackTracker.c
+
[Sources.Ia32]
Ia32/EbcSupport.c
Ia32/EbcLowLevel.nasm
@@ -60,6 +63,7 @@ [Sources.IPF]
[Sources.ARM]
Arm/EbcSupport.c
Arm/EbcLowLevel.S
+ Arm/EbcStackTracker.c
[Sources.AARCH64]
AArch64/EbcSupport.c
diff --git a/MdeModulePkg/Universal/EbcDxe/EbcExecute.c b/MdeModulePkg/Universal/EbcDxe/EbcExecute.c
index 2d21c3364e0d..b6801815f8f1 100644
--- a/MdeModulePkg/Universal/EbcDxe/EbcExecute.c
+++ b/MdeModulePkg/Universal/EbcDxe/EbcExecute.c
@@ -113,6 +113,47 @@ VmReadIndex64 (
);
/**
+ Update the stack tracker according to the latest natural and constant
+ value stack manipulation operations.
+
+ @param VmPtr The pointer to current VM context.
+ @param NaturalUnits The number of natural values that were pushed (>0) or
+ popped (<0).
+ @param ConstUnits The number of const bytes that were pushed (>0) or
+ popped (<0).
+
+ @retval EFI_OUT_OF_RESOURCES Not enough memory to grow the stack tracker.
+ @retval EFI_UNSUPPORTED The stack tracker is being underflown due to
+ unbalanced stack operations.
+ @retval EFI_SUCCESS The stack tracker was updated successfully.
+
+**/
+EFI_STATUS
+UpdateStackTracker(
+ IN VM_CONTEXT *VmPtr,
+ IN INTN NaturalUnits,
+ IN INTN ConstUnits
+ );
+
+/**
+ Update the stack tracker by computing the R0 delta.
+
+ @param VmPtr The pointer to current VM context.
+ @param NewR0 The new R0 value.
+
+ @retval EFI_OUT_OF_RESOURCES Not enough memory to grow the stack tracker.
+ @retval EFI_UNSUPPORTED The stack tracker is being underflown due to
+ unbalanced stack operations.
+ @retval EFI_SUCCESS The stack tracker was updated successfully.
+
+**/
+EFI_STATUS
+UpdateStackTrackerFromDelta (
+ IN VM_CONTEXT *VmPtr,
+ IN UINTN NewR0
+ );
+
+/**
Reads 8-bit data form the memory address.
@param VmPtr A pointer to VM context.
@@ -1578,9 +1619,13 @@ ExecuteMOVxx (
UINT64 Data64;
UINT64 DataMask;
UINTN Source;
+ EBC_INDEX Index;
+ EBC_INDEX *IndexPtr;
+ EFI_STATUS Status;
Opcode = GETOPCODE (VmPtr);
OpcMasked = (UINT8) (Opcode & OPCODE_M_OPCODE);
+ IndexPtr = (VmPtr->StackTracker != NULL) ? &Index : NULL;
//
// Get the operands byte so we can get R1 and R2
@@ -1615,7 +1660,7 @@ ExecuteMOVxx (
}
if ((Opcode & OPCODE_M_IMMED_OP2) != 0) {
- Index16 = VmReadIndex16 (VmPtr, Size, NULL);
+ Index16 = VmReadIndex16 (VmPtr, Size, IndexPtr);
Index64Op2 = (INT64) Index16;
Size += sizeof (UINT16);
}
@@ -1630,7 +1675,7 @@ ExecuteMOVxx (
}
if ((Opcode & OPCODE_M_IMMED_OP2) != 0) {
- Index32 = VmReadIndex32 (VmPtr, Size, NULL);
+ Index32 = VmReadIndex32 (VmPtr, Size, IndexPtr);
Index64Op2 = (INT64) Index32;
Size += sizeof (UINT32);
}
@@ -1644,7 +1689,7 @@ ExecuteMOVxx (
}
if ((Opcode & OPCODE_M_IMMED_OP2) != 0) {
- Index64Op2 = VmReadIndex64 (VmPtr, Size, NULL);
+ Index64Op2 = VmReadIndex64 (VmPtr, Size, IndexPtr);
Size += sizeof (UINT64);
}
} else {
@@ -1757,6 +1802,34 @@ ExecuteMOVxx (
}
}
}
+
+ if (VmPtr->StackTracker != NULL) {
+ if ((OPERAND1_REGNUM (Operands) == 0) && (!OPERAND1_INDIRECT (Operands))) {
+ //
+ // The stack pointer (R0) is being directly modified - track it
+ //
+ if ((OPERAND2_REGNUM (Operands) == 0) && (!OPERAND2_INDIRECT (Operands))) {
+ //
+ // MOV R0, R0(+/-n,+/-c)
+ //
+ if ((Opcode & OPCODE_M_IMMED_OP2) != 0) {
+ Status = UpdateStackTracker (VmPtr, (INTN) Index.NaturalUnits, (INTN) Index.ConstUnits);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+ } else {
+ //
+ // Track the R0 delta
+ //
+ Status = UpdateStackTrackerFromDelta (VmPtr, (INTN) (Data64 & DataMask));
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+ }
+ }
+
//
// Now write it back
//
@@ -2276,6 +2349,15 @@ ExecuteMOVI (
Mask64 = (UINT64)~0;
}
+ if (VmPtr->StackTracker != NULL) {
+ //
+ // Track direct register operations on R0
+ //
+ if (OPERAND1_REGNUM (Operands) == 0) {
+ UpdateStackTrackerFromDelta (VmPtr, (UINTN) ImmData64 & Mask64);
+ }
+ }
+
VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = ImmData64 & Mask64;
} else {
//
@@ -2386,6 +2468,15 @@ ExecuteMOVIn (
return EFI_UNSUPPORTED;
}
+ if (VmPtr->StackTracker != NULL) {
+ //
+ // Track direct register operations on R0
+ //
+ if (OPERAND1_REGNUM (Operands) == 0) {
+ UpdateStackTrackerFromDelta (VmPtr, (UINTN) ImmedIndex64);
+ }
+ }
+
VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = ImmedIndex64;
} else {
//
@@ -2485,6 +2576,15 @@ ExecuteMOVREL (
return EFI_UNSUPPORTED;
}
+ if (VmPtr->StackTracker != NULL) {
+ //
+ // Track direct register operations on R0
+ //
+ if (OPERAND1_REGNUM (Operands) == 0) {
+ UpdateStackTrackerFromDelta (VmPtr, (UINTN) Op2);
+ }
+ }
+
VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = (VM_REGISTER) Op2;
} else {
//
@@ -2584,6 +2684,14 @@ ExecuteMOVsnw (
// Now write back the result.
//
if (!OPERAND1_INDIRECT (Operands)) {
+ if (VmPtr->StackTracker != NULL) {
+ //
+ // Track direct register operations on R0
+ //
+ if (OPERAND1_REGNUM (Operands) == 0) {
+ UpdateStackTrackerFromDelta (VmPtr, (UINTN) Op2);
+ }
+ }
VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = Op2;
} else {
VmWriteMemN (VmPtr, (UINTN) (VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Op1Index), (UINTN) Op2);
@@ -2677,6 +2785,14 @@ ExecuteMOVsnd (
// Now write back the result.
//
if (!OPERAND1_INDIRECT (Operands)) {
+ if (VmPtr->StackTracker != NULL) {
+ //
+ // Track direct register operations on R0
+ //
+ if (OPERAND1_REGNUM (Operands) == 0) {
+ UpdateStackTrackerFromDelta (VmPtr, (UINTN) Op2);
+ }
+ }
VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = Op2;
} else {
VmWriteMemN (VmPtr, (UINTN) (VmPtr->Gpr[OPERAND1_REGNUM (Operands)] + Op1Index), (UINTN) Op2);
@@ -2744,7 +2860,11 @@ ExecutePUSHn (
//
VmPtr->Gpr[0] -= sizeof (UINTN);
VmWriteMemN (VmPtr, (UINTN) VmPtr->Gpr[0], DataN);
- return EFI_SUCCESS;
+ if (VmPtr->StackTracker != NULL) {
+ return UpdateStackTracker (VmPtr, -1, 0);
+ } else {
+ return EFI_SUCCESS;
+ }
}
@@ -2820,7 +2940,11 @@ ExecutePUSH (
VmWriteMem32 (VmPtr, (UINTN) VmPtr->Gpr[0], Data32);
}
- return EFI_SUCCESS;
+ if (VmPtr->StackTracker != NULL) {
+ return UpdateStackTracker (VmPtr, 0, ((Opcode & PUSHPOP_M_64) != 0) ? -8 : -4);
+ } else {
+ return EFI_SUCCESS;
+ }
}
@@ -2879,7 +3003,11 @@ ExecutePOPn (
VmPtr->Gpr[OPERAND1_REGNUM (Operands)] = (INT64) (UINT64) ((UINTN) DataN + Index16);
}
- return EFI_SUCCESS;
+ if (VmPtr->StackTracker != NULL) {
+ return UpdateStackTracker (VmPtr, 1, 0);
+ } else {
+ return EFI_SUCCESS;
+ }
}
@@ -2958,7 +3086,11 @@ ExecutePOP (
}
}
- return EFI_SUCCESS;
+ if (VmPtr->StackTracker != NULL) {
+ return UpdateStackTracker (VmPtr, 0, ((Opcode & PUSHPOP_M_64) != 0) ? 8 : 4);
+ } else {
+ return EFI_SUCCESS;
+ }
}
@@ -4289,6 +4421,15 @@ ExecuteDataManip (
VmWriteMem32 (VmPtr, (UINTN) Op1, (UINT32) Op2);
}
} else {
+ if (VmPtr->StackTracker != NULL) {
+ //
+ // Track direct register operations on R0
+ //
+ if (OPERAND1_REGNUM (Operands) == 0) {
+ UpdateStackTrackerFromDelta(VmPtr, (UINTN) Op2);
+ }
+ }
+
//
// Storage back to a register. Write back, clearing upper bits (as per
// the specification) if 32-bit operation.
diff --git a/MdeModulePkg/Universal/EbcDxe/EbcStackTracker.c b/MdeModulePkg/Universal/EbcDxe/EbcStackTracker.c
new file mode 100644
index 000000000000..8e78fb2e35f6
--- /dev/null
+++ b/MdeModulePkg/Universal/EbcDxe/EbcStackTracker.c
@@ -0,0 +1,65 @@
+/** @file
+ This module contains dummy function calls, for platforms that do not
+ require stack tracking.
+
+Copyright (c) 2016, Pete Batard. All rights reserved.<BR>
+
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "EbcInt.h"
+#include "EbcExecute.h"
+
+/**
+ Update the stack tracker according to the latest natural and constant
+ value stack manipulation operations.
+
+ @param VmPtr The pointer to current VM context.
+ @param NaturalUnits The number of natural values that were pushed (>0) or
+ popped (<0).
+ @param ConstUnits The number of const bytes that were pushed (>0) or
+ popped (<0).
+
+ @retval EFI_OUT_OF_RESOURCES Not enough memory to grow the stack tracker.
+ @retval EFI_UNSUPPORTED The stack tracker is being underflown due to
+ unbalanced stack operations.
+ @retval EFI_SUCCESS The stack tracker was updated successfully.
+
+**/
+EFI_STATUS
+UpdateStackTracker(
+ IN VM_CONTEXT *VmPtr,
+ IN INTN NaturalUnits,
+ IN INTN ConstUnits
+ )
+{
+ return EFI_SUCCESS;
+}
+
+/**
+ Update the stack tracker by computing the R0 delta.
+
+ @param VmPtr The pointer to current VM context.
+ @param NewR0 The new R0 value.
+
+ @retval EFI_OUT_OF_RESOURCES Not enough memory to grow the stack tracker.
+ @retval EFI_UNSUPPORTED The stack tracker is being underflown due to
+ unbalanced stack operations.
+ @retval EFI_SUCCESS The stack tracker was updated successfully.
+
+**/
+EFI_STATUS
+UpdateStackTrackerFromDelta (
+ IN VM_CONTEXT *VmPtr,
+ IN UINTN NewR0
+ )
+{
+ return EFI_SUCCESS;
+}
--
2.9.3.windows.2
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 4/5] MdeModulePkg/EbcDxe: add call signatures for ARM native->EBC support
2017-01-24 12:30 [PATCH 1/5] MdeModulePkg/EbcDxe: allow VmReadIndex##() to return a decoded index Pete Batard
2017-01-24 12:30 ` [PATCH 2/5] MdeModulePkg/EbcDxe: add ARM support Pete Batard
2017-01-24 12:30 ` [PATCH 3/5] MdeModulePkg/EbcDxe: add a stack tracker for ARM EBC->native support Pete Batard
@ 2017-01-24 12:30 ` Pete Batard
2017-01-24 12:30 ` [PATCH 5/5] BaseTools: add scripts to generate EBC call signatures Pete Batard
3 siblings, 0 replies; 5+ messages in thread
From: Pete Batard @ 2017-01-24 12:30 UTC (permalink / raw)
To: edk2-devel
* This patch fixes the call from native into EBC ARM calling convention
issue by introducing support for call signatures when invoking BREAK 5.
* These call signatures are provided as the high 32 bit word of the
64-bit longword to which R7 points during BREAK 5 invocation (the lowest
32-bit being left untouched, as the "offset from self").
* Within this word the upper 16-bits should be set to an EBC_CALL_SIGNATURE
marker and the lower 16 bits to the signature itself, with each bit
indicating if the corresponding argument is 64-bit (1) or not (0).
* The compiler toolchain is expected to automatically fill these signatures
when producing binaries.
* None of the changes introduced by this patch have any bearing on the
behaviour of existing EBC VMs or EBC applications, except for ARM.
Especially, existing EBC code that is missing signatures still works
exactly as it did on the updated versions of IA32, X64 and AARCH64 VMs,
and EBC code that includes signatures also works just the same on non
signature-aware VMs.
The presence or absence of signature has only an impact on ARM platforms.
* However, because this feature requires a minor specs change and because
an EBC application that provides call signatures may want to detect if
the VM is signature aware, we also bump the global VM version to 1.1.
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Pete Batard <pete@akeo.ie>
---
MdeModulePkg/Include/Protocol/EbcVmTest.h | 2 +-
MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S | 8 ++-
MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c | 85 +++++++++++++++++++------
MdeModulePkg/Universal/EbcDxe/EbcExecute.c | 27 +++++++-
MdeModulePkg/Universal/EbcDxe/EbcInt.h | 7 +-
5 files changed, 102 insertions(+), 27 deletions(-)
diff --git a/MdeModulePkg/Include/Protocol/EbcVmTest.h b/MdeModulePkg/Include/Protocol/EbcVmTest.h
index 12d5e5217059..5b6aee704795 100644
--- a/MdeModulePkg/Include/Protocol/EbcVmTest.h
+++ b/MdeModulePkg/Include/Protocol/EbcVmTest.h
@@ -34,7 +34,7 @@ typedef struct _EFI_EBC_VM_TEST_PROTOCOL EFI_EBC_VM_TEST_PROTOCOL;
// VM major/minor version
//
#define VM_MAJOR_VERSION 1
-#define VM_MINOR_VERSION 0
+#define VM_MINOR_VERSION 1
//
// Bits in the VM->StopFlags field
diff --git a/MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S b/MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S
index dc9c3946ced2..2325dd07a472 100644
--- a/MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S
+++ b/MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S
@@ -129,16 +129,17 @@ ASM_PFX(EbcLLCALLEXNativeArm):
//****************************************************************************
// EbcLLEbcInterpret
//
-// This function is called by the thunk code to handle an Native to EBC call
-// This can handle up to 16 arguments (1-4 on in r0-r3, 5-16 are on the stack)
+// This function is called by the thunk code to handle a Native to EBC call
+// This can handle up to 16 arguments (args 1-2/1-4 in r0-r3, rest onstack)
// ip contains the Entry point that will be the first argument when
// EBCInterpret is called.
//
//****************************************************************************
ASM_PFX(EbcLLEbcInterpret):
+
stmdb sp!, {r4, lr}
- // push the entry point and the address of args #5 - #16 onto the stack
+ // push the entry point and the address of non register args on the stack
add r4, sp, #8
str ip, [sp, #-8]!
str r4, [sp, #4]
@@ -180,3 +181,4 @@ ASM_PFX(mEbcInstructionBufferTemplate):
.long 0 // EBC_ENTRYPOINT_SIGNATURE
0: .long 0 // EBC_LL_EBC_ENTRYPOINT_SIGNATURE
+ .long 0 // EBC_CALL_SIGNATURE
diff --git a/MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c b/MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c
index 2e308772b477..99bed1697748 100644
--- a/MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c
+++ b/MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c
@@ -32,6 +32,7 @@ typedef struct {
UINT32 Magic;
UINT32 EbcEntryPoint;
UINT32 EbcLlEntryPoint;
+ UINT32 EbcCallSignature;
} EBC_INSTRUCTION_BUFFER;
#pragma pack()
@@ -158,7 +159,7 @@ PushU32 (
@param Arg3 The 3rd argument.
@param Arg4 The 4th argument.
@param InstructionBuffer A pointer to the thunk instruction buffer.
- @param Args5_16[] Array containing arguments #5 to #16.
+ @param Args5To32[] Array containing arguments #5 to #16.
@return The value returned by the EBC application we're going to run.
@@ -171,7 +172,7 @@ EbcInterpret (
IN UINTN Arg3,
IN UINTN Arg4,
IN EBC_INSTRUCTION_BUFFER *InstructionBuffer,
- IN UINTN Args5_16[]
+ IN UINTN Args5To32[]
)
{
//
@@ -179,11 +180,23 @@ EbcInterpret (
//
VM_CONTEXT VmContext;
UINTN Addr;
+ UINT32 Mask;
EFI_STATUS Status;
UINTN StackIndex;
+ INTN ArgNumber;
+ BOOLEAN *SkipArg;
//
- // Get the EBC entry point
+ // If the call signature is missing (high 16-bits are not set to
+ // EBC_CALL_SIGNATURE), return an error as we aren't able to
+ // properly reconstruct the EBC VM parameter stack.
+ //
+ if ((InstructionBuffer->EbcCallSignature & 0xFFFF0000) != EBC_CALL_SIGNATURE) {
+ return EFI_INCOMPATIBLE_VERSION;
+ }
+
+ //
+ // Get the EBC entry point and signature
//
Addr = InstructionBuffer->EbcEntryPoint;
@@ -240,25 +253,49 @@ EbcInterpret (
VmContext.LowStackTop = (UINTN) VmContext.Gpr[0];
//
- // For the worst case, assume there are 4 arguments passed in registers, store
- // them to VM's stack.
+ // Find which 32-bit args need to be skipped
//
- PushU32 (&VmContext, (UINT32) Args5_16[11]);
- PushU32 (&VmContext, (UINT32) Args5_16[10]);
- PushU32 (&VmContext, (UINT32) Args5_16[9]);
- PushU32 (&VmContext, (UINT32) Args5_16[8]);
- PushU32 (&VmContext, (UINT32) Args5_16[7]);
- PushU32 (&VmContext, (UINT32) Args5_16[6]);
- PushU32 (&VmContext, (UINT32) Args5_16[5]);
- PushU32 (&VmContext, (UINT32) Args5_16[4]);
- PushU32 (&VmContext, (UINT32) Args5_16[3]);
- PushU32 (&VmContext, (UINT32) Args5_16[2]);
- PushU32 (&VmContext, (UINT32) Args5_16[1]);
- PushU32 (&VmContext, (UINT32) Args5_16[0]);
- PushU32 (&VmContext, (UINT32) Arg4);
+ SkipArg = AllocateZeroPool(16 * sizeof(BOOLEAN));
+ for (ArgNumber = 0, Mask = 1; Mask < 0x10000; Mask <<= 1) {
+ if ((InstructionBuffer->EbcCallSignature & Mask) == Mask) {
+ //
+ // This is a 64 bit arg => check if we are aligned.
+ // If not, then we need to skip one 32-bit arg.
+ //
+ if (ArgNumber % 2 != 0) {
+ SkipArg[ArgNumber / 2] = TRUE;
+ ArgNumber += 1;
+ }
+ ArgNumber += 2;
+ } else {
+ ArgNumber += 1;
+ }
+ }
+ ASSERT(ArgNumber <= 32);
+
+ //
+ // Process the stack arguments. ArgNumber is already set to the max number
+ // of 32 bit values we need to process (including registers) so use that.
+ //
+ for (ArgNumber -= 5; ArgNumber >= 0; ArgNumber--) {
+ if ((ArgNumber % 2 == 0) || (!SkipArg[(ArgNumber + 4) / 2])) {
+ PushU32 (&VmContext, (UINT32) Args5To32[ArgNumber]);
+ }
+ }
+
+ //
+ // For the worst case, assume there are 4 arguments passed in registers,
+ // store them to VM's stack.
+ //
+ if (!SkipArg[1]) {
+ PushU32 (&VmContext, (UINT32) Arg4);
+ }
PushU32 (&VmContext, (UINT32) Arg3);
- PushU32 (&VmContext, (UINT32) Arg2);
+ if (!SkipArg[0]) {
+ PushU32 (&VmContext, (UINT32) Arg2);
+ }
PushU32 (&VmContext, (UINT32) Arg1);
+ FreePool(SkipArg);
//
// Interpreter assumes 64-bit return address is pushed on the stack.
@@ -475,6 +512,16 @@ EbcCreateThunks (
}
//
+ // Add the call signature (high 16-bits of Flags) along with the.
+ // EBC_CALL_SIGNATURE marker. A missing marker helps us fault the
+ // EBC call at runtime, if it doesn't have a signature.
+ //
+ if ((Flags & FLAG_THUNK_SIGNATURE) != 0) {
+ InstructionBuffer->EbcCallSignature =
+ (UINT32)(EBC_CALL_SIGNATURE | (Flags >> 16));
+ }
+
+ //
// Add the thunk to the list for this image. Do this last since the add
// function flushes the cache for us.
//
diff --git a/MdeModulePkg/Universal/EbcDxe/EbcExecute.c b/MdeModulePkg/Universal/EbcDxe/EbcExecute.c
index b6801815f8f1..ebd6257a3c8c 100644
--- a/MdeModulePkg/Universal/EbcDxe/EbcExecute.c
+++ b/MdeModulePkg/Universal/EbcDxe/EbcExecute.c
@@ -1914,6 +1914,8 @@ ExecuteBREAK (
VOID *Thunk;
UINT64 U64EbcEntryPoint;
INT32 Offset;
+ UINT32 Flags;
+ UINT32 CallSignature;
Thunk = NULL;
Operands = GETOPERANDS (VmPtr);
@@ -1960,19 +1962,38 @@ ExecuteBREAK (
break;
//
- // Create a thunk for EBC code. R7 points to a 32-bit (in a 64-bit slot)
- // "offset from self" pointer to the EBC entry point.
+ // Create a thunk for EBC code. R7 points to a 64-bit longword, where the
+ // lower 32 bits contain an "offset from self" pointer to the EBC entry
+ // point, and the higher 32 bits contain the call signature.
// After we're done, *(UINT64 *)R7 will be the address of the new thunk.
//
case 5:
Offset = (INT32) VmReadMem32 (VmPtr, (UINTN) VmPtr->Gpr[7]);
+ CallSignature = VmReadMem32 (VmPtr, (UINTN) VmPtr->Gpr[7] + 4);
U64EbcEntryPoint = (UINT64) (VmPtr->Gpr[7] + Offset + 4);
EbcEntryPoint = (VOID *) (UINTN) U64EbcEntryPoint;
//
+ // A call signature is needed (notably on ARM systems) to properly
+ // reconstruct the EBC call parameter stack. When BREAK 5 is invoked,
+ // this signature is expected to be provided after the "offset from
+ // self", as another 32-bit word.
+ // Within this word, the upper 16-bits are set to EBC_CALL_SIGNATURE
+ // and the lower 16 bits contain the actual call signature (up to 16
+ // arguments, with bits set to 1 for 64-bit args, 0 otherwise).
+ // This 16-bit signature, if present, is then passed to EbcCreateThunks()
+ // as the high 16-bits of the Flags parameter, along with the
+ // FLAG_THUNK_SIGNATURE bit set.
+ //
+ Flags = 0;
+ if ((CallSignature & 0xFFFF0000) == EBC_CALL_SIGNATURE) {
+ Flags = (CallSignature << 16) | FLAG_THUNK_SIGNATURE;
+ }
+
+ //
// Now create a new thunk
//
- Status = EbcCreateThunks (VmPtr->ImageHandle, EbcEntryPoint, &Thunk, 0);
+ Status = EbcCreateThunks (VmPtr->ImageHandle, EbcEntryPoint, &Thunk, Flags);
if (EFI_ERROR (Status)) {
return Status;
}
diff --git a/MdeModulePkg/Universal/EbcDxe/EbcInt.h b/MdeModulePkg/Universal/EbcDxe/EbcInt.h
index 75017a23e75e..2e1c410f7d56 100644
--- a/MdeModulePkg/Universal/EbcDxe/EbcInt.h
+++ b/MdeModulePkg/Universal/EbcDxe/EbcInt.h
@@ -38,11 +38,16 @@ extern VM_CONTEXT *mVmPtr;
//
#define FLAG_THUNK_ENTRY_POINT 0x01 // thunk for an image entry point
#define FLAG_THUNK_PROTOCOL 0x00 // thunk for an EBC protocol service
+#define FLAG_THUNK_SIGNATURE 0x02 // a 16-bit call signature is present
+//
+// 32-bit call signature marker
+//
+#define EBC_CALL_SIGNATURE 0x2EBC0000
//
// Put this value at the bottom of the VM's stack gap so we can check it on
// occasion to make sure the stack has not been corrupted.
//
-#define VM_STACK_KEY_VALUE 0xDEADBEEF
+#define VM_STACK_KEY_VALUE 0xDEADBEEF
/**
Create thunks for an EBC image entry point, or an EBC protocol service.
--
2.9.3.windows.2
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 5/5] BaseTools: add scripts to generate EBC call signatures
2017-01-24 12:30 [PATCH 1/5] MdeModulePkg/EbcDxe: allow VmReadIndex##() to return a decoded index Pete Batard
` (2 preceding siblings ...)
2017-01-24 12:30 ` [PATCH 4/5] MdeModulePkg/EbcDxe: add call signatures for ARM native->EBC support Pete Batard
@ 2017-01-24 12:30 ` Pete Batard
3 siblings, 0 replies; 5+ messages in thread
From: Pete Batard @ 2017-01-24 12:30 UTC (permalink / raw)
To: edk2-devel
* EBC binaries meant to be compatible with the ARM EBC VM
require the insertion of call signatures at the locations
where the BREAK 5 offsets are stored.
* This patch adds 2 new Python applications, as well as the
required modifications for build_rule and tools_def templates:
- GenEbcSignature is a script that parses a preprocessed C
source, along with a binary object file, as generated by the
intel EBC compiler, to create the signature data, which it
stores into a .sig file.
- PatchEbcSignature is a script that processes the .sig files
as well as the .map data and the .efi binary, to insert the
signatures at the required locations.
* Note that the insertion of signatures into EBC binaries has
absolutely no impact for EBC execution on non ARM platforms,
as it applies to data parts that are ignored on these VMs.
* Also note the GenEbcSignature is designed to be very basic
with regards to the detection of call signatures that have
straight 64-bit parameters, as it is meant to be used as a
stopgap solution until the intel EBC compiler (which does
has easy access to the data required to do so, and is best
placed for such processing) is updated to generate .sig files
on its own.
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Pete Batard <pete@akeo.ie>
---
.../BinWrappers/WindowsLike/GenEbcSignature.bat | 3 +
.../BinWrappers/WindowsLike/PatchEbcSignature.bat | 3 +
BaseTools/Conf/build_rule.template | 72 +++--
BaseTools/Conf/tools_def.template | 12 +
.../Python/GenEbcSignature/GenEbcSignature.py | 306 +++++++++++++++++++++
.../Source/Python/GenEbcSignature/__init__.py | 15 +
.../Python/PatchEbcSignature/PatchEbcSignature.py | 226 +++++++++++++++
.../Source/Python/PatchEbcSignature/__init__.py | 15 +
8 files changed, 633 insertions(+), 19 deletions(-)
create mode 100644 BaseTools/BinWrappers/WindowsLike/GenEbcSignature.bat
create mode 100644 BaseTools/BinWrappers/WindowsLike/PatchEbcSignature.bat
create mode 100644 BaseTools/Source/Python/GenEbcSignature/GenEbcSignature.py
create mode 100644 BaseTools/Source/Python/GenEbcSignature/__init__.py
create mode 100644 BaseTools/Source/Python/PatchEbcSignature/PatchEbcSignature.py
create mode 100644 BaseTools/Source/Python/PatchEbcSignature/__init__.py
diff --git a/BaseTools/BinWrappers/WindowsLike/GenEbcSignature.bat b/BaseTools/BinWrappers/WindowsLike/GenEbcSignature.bat
new file mode 100644
index 000000000000..9fbb704a6eb0
--- /dev/null
+++ b/BaseTools/BinWrappers/WindowsLike/GenEbcSignature.bat
@@ -0,0 +1,3 @@
+@setlocal
+@set ToolName=%~n0%
+@%PYTHON_HOME%\python.exe %BASE_TOOLS_PATH%\Source\Python\%ToolName%\%ToolName%.py %*
diff --git a/BaseTools/BinWrappers/WindowsLike/PatchEbcSignature.bat b/BaseTools/BinWrappers/WindowsLike/PatchEbcSignature.bat
new file mode 100644
index 000000000000..9fbb704a6eb0
--- /dev/null
+++ b/BaseTools/BinWrappers/WindowsLike/PatchEbcSignature.bat
@@ -0,0 +1,3 @@
+@setlocal
+@set ToolName=%~n0%
+@%PYTHON_HOME%\python.exe %BASE_TOOLS_PATH%\Source\Python\%ToolName%\%ToolName%.py %*
diff --git a/BaseTools/Conf/build_rule.template b/BaseTools/Conf/build_rule.template
index 1db94b696f89..3408f507e78b 100755
--- a/BaseTools/Conf/build_rule.template
+++ b/BaseTools/Conf/build_rule.template
@@ -161,6 +161,27 @@
"$(CC)" $(CC_FLAGS) -c -o ${dst} $(INC) ${src}
"$(SYMRENAME)" $(SYMRENAME_FLAGS) ${dst}
+[C-Code-File.COMMON.EBC]
+ <InputFile>
+ ?.c
+ ?.C
+ ?.cc
+ ?.CC
+ ?.cpp
+ ?.Cpp
+ ?.CPP
+
+ <ExtraDependency>
+ $(MAKE_FILE)
+
+ <OutputFile>
+ $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.obj
+
+ <Command.MSFT, Command.INTEL>
+ "$(CC)" /Fo${dst} $(CC_FLAGS) $(INC) ${src}
+ # Parse preprocessed sources to generate the EBC call signatures
+ "$(CC)" /E /EP $(CC_FLAGS) $(INC) ${src} | "$(GENEBC)" -v -o ${dst} -s $(OUTPUT_DIR)(+)$(MODULE_NAME).sig
+
[C-Code-File.BASE.AARCH64,C-Code-File.SEC.AARCH64,C-Code-File.PEI_CORE.AARCH64,C-Code-File.PEIM.AARCH64,C-Code-File.BASE.ARM,C-Code-File.SEC.ARM,C-Code-File.PEI_CORE.ARM,C-Code-File.PEIM.ARM]
<InputFile>
?.c
@@ -267,10 +288,10 @@
<Command.GCC, Command.GCCLD>
"$(SLINK)" cr ${dst} $(SLINK_FLAGS) @$(OBJECT_FILES_LIST)
-
+
<Command.RVCT>
"$(SLINK)" $(SLINK_FLAGS) ${dst} --via $(OBJECT_FILES_LIST)
-
+
<Command.RVCTCYGWIN>
# $(OBJECT_FILES_LIST) has wrong paths for cygwin
"$(SLINK)" $(SLINK_FLAGS) ${dst} $(OBJECT_FILES)
@@ -308,8 +329,8 @@
<Command.XCODE>
"$(DLINK)" $(DLINK_FLAGS) -o ${dst} $(DLINK_SPATH) -filelist $(STATIC_LIBRARY_FILES_LIST) $(DLINK2_FLAGS)
-
-
+
+
[Static-Library-File.SEC.AARCH64, Static-Library-File.PEI_CORE.AARCH64, Static-Library-File.PEIM.AARCH64,Static-Library-File.SEC.ARM, Static-Library-File.PEI_CORE.ARM, Static-Library-File.PEIM.ARM]
<InputFile>
*.lib
@@ -353,8 +374,8 @@
<Command.XCODE>
"$(DLINK)" -o ${dst} $(DLINK_FLAGS) $(DLINK_SPATH) -filelist $(STATIC_LIBRARY_FILES_LIST) $(DLINK2_FLAGS)
-
-
+
+
[Dynamic-Library-File]
<InputFile>
?.dll
@@ -367,7 +388,7 @@
$(CP) ${dst} $(OUTPUT_DIR)
$(CP) ${dst} $(BIN_DIR)(+)$(MODULE_NAME_GUID).efi
-$(CP) $(DEBUG_DIR)(+)*.map $(OUTPUT_DIR)
- -$(CP) $(DEBUG_DIR)(+)*.pdb $(OUTPUT_DIR)
+ -$(CP) $(DEBUG_DIR)(+)*.pdb $(OUTPUT_DIR)
<Command.GCC, Command.GCCLD>
$(CP) ${src} $(DEBUG_DIR)(+)$(MODULE_NAME).debug
$(OBJCOPY) --strip-unneeded -R .eh_frame ${src}
@@ -382,7 +403,7 @@
$(CP) ${dst} $(OUTPUT_DIR)
$(CP) ${dst} $(BIN_DIR)(+)$(MODULE_NAME_GUID).efi
-$(CP) $(DEBUG_DIR)(+)*.map $(OUTPUT_DIR)
-
+
<Command.XCODE>
# tool to convert Mach-O to PE/COFF
"$(MTOC)" -subsystem $(MODULE_TYPE) $(MTOC_FLAGS) ${src} $(DEBUG_DIR)(+)$(MODULE_NAME).pecoff
@@ -393,6 +414,21 @@
$(CP) ${dst} $(BIN_DIR)(+)$(MODULE_NAME_GUID).efi
-$(CP) $(DEBUG_DIR)(+)*.map $(OUTPUT_DIR)
+[Dynamic-Library-File.COMMON.EBC]
+ <InputFile>
+ ?.dll
+
+ <OutputFile>
+ $(DEBUG_DIR)(+)$(MODULE_NAME).efi
+
+ <Command.MSFT, Command.INTEL, Command.RVCT>
+ "$(GENFW)" -e $(MODULE_TYPE) -o ${dst} ${src} $(GENFW_FLAGS)
+ "$(PATCHEBC)" -v -m $(DEBUG_DIR)(+)$(MODULE_NAME).map -e ${dst} $(OUTPUT_DIR)(+)$(MODULE_NAME).sig $(STATIC_LIBRARY_FILES)
+ $(CP) ${dst} $(OUTPUT_DIR)
+ $(CP) ${dst} $(BIN_DIR)(+)$(MODULE_NAME_GUID).efi
+ -$(CP) $(DEBUG_DIR)(+)*.map $(OUTPUT_DIR)
+ -$(CP) $(DEBUG_DIR)(+)*.pdb $(OUTPUT_DIR)
+
[Dependency-Expression-File]
<InputFile>
?.dxs, ?.Dxs, ?.DXS
@@ -421,13 +457,13 @@
<Command.MSFT, Command.INTEL>
Trim --asl-file -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.i -i $(INC_LIST) ${src}
"$(ASLPP)" $(ASLPP_FLAGS) $(INC) /I${s_path} $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.i > $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.iii
- Trim --source-code -l -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.iiii $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.iii
+ Trim --source-code -l -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.iiii $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.iii
"$(ASL)" $(ASL_FLAGS) $(ASL_OUTFLAGS)${dst} $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.iiii
<Command.GCC, Command.GCCLD>
Trim --asl-file -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.i -i $(INC_LIST) ${src}
"$(ASLPP)" $(ASLPP_FLAGS) $(INC) -I${s_path} $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.i > $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.iii
- Trim --source-code -l -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.iiii $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.iii
+ Trim --source-code -l -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.iiii $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.iii
"$(ASL)" $(ASL_FLAGS) $(ASL_OUTFLAGS)${dst} $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.iiii
[C-Code-File.AcpiTable]
@@ -469,14 +505,14 @@
"$(ASLCC)" -c -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.obj $(CC_FLAGS) $(ASLCC_FLAGS) $(INC) ${src}
"$(ASLDLINK)" -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.dll $(ASLDLINK_FLAGS) $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.obj
"$(GENFW)" -o ${dst} -c $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.dll $(GENFW_FLAGS)
-
- <Command.XCODE>
+
+ <Command.XCODE>
"$(ASLCC)" -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.obj $(ASLCC_FLAGS) $(INC) ${src}
"$(ASLDLINK)" -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.dll $(ASLDLINK_FLAGS) $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.obj
"$(MTOC)" -subsystem $(MODULE_TYPE) $(MTOC_FLAGS) $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.dll $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.efi
"$(GENFW)" -o ${dst} -c $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.efi $(GENFW_FLAGS)
-
-
+
+
[Masm16-Code-File]
<InputFile>
?.asm16, ?.Asm16, ?.ASM16, ?.s16, ?.S16
@@ -499,14 +535,14 @@
Trim --source-code -o ${d_path}(+)${s_base}.iii ${d_path}(+)${s_base}.i
"$(ASM)" -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.obj $(ASM_FLAGS) $(INC) ${d_path}(+)${s_base}.iii
"$(DLINK)" -o ${dst} $(DLINK_FLAGS) --start-group $(DLINK_SPATH) $(LIBS) $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.obj --end-group
-
+
<Command.XCODE>
"$(PP)" $(PP_FLAGS) $(INC) ${src} > ${d_path}(+)${s_base}.i
Trim --source-code -o ${d_path}(+)${s_base}.iii ${d_path}(+)${s_base}.i
"$(ASM)" -o $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.obj $(ASM_FLAGS) $(INC) ${d_path}(+)${s_base}.iii
"$(SLINK)" $(SLINK_FLAGS) $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.slib $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.obj
otool -t $(OUTPUT_DIR)(+)${s_dir}(+)${s_base}.slib | hex2bin.py ${dst}
-
+
[Nasm-to-Binary-Code-File]
<InputFile>
@@ -642,8 +678,6 @@
<Command.GCC, Command.GCCLD>
"$(GENFW)" -o $(OUTPUT_DIR)(+)$(MODULE_NAME)hii.rc -g $(MODULE_GUID) --hiibinpackage $(HII_BINARY_PACKAGES) $(GENFW_FLAGS)
"$(RC)" $(RC_FLAGS) $(OUTPUT_DIR)(+)$(MODULE_NAME)hii.rc ${dst}
-
+
<Command.XCODE, Command.RVCT>
GenFw -o $(OUTPUT_DIR)(+)$(MODULE_NAME)hii.rc -g $(MODULE_GUID) --hiibinpackage $(HII_BINARY_PACKAGES)
-
-
diff --git a/BaseTools/Conf/tools_def.template b/BaseTools/Conf/tools_def.template
index aaae4fcd2916..fa0e1ac23211 100755
--- a/BaseTools/Conf/tools_def.template
+++ b/BaseTools/Conf/tools_def.template
@@ -7651,6 +7651,18 @@ RELEASE_RVCTCYGWIN_ARM_CC_FLAGS = "$(CCPATH_FLAG)" $(ARCHCC_FLAGS) $(PLATFORM_F
*_*_*_GENFW_FLAGS =
##################
+# GenEbc tool definitions
+##################
+*_*_EBC_GENEBC_PATH = GenEbcSignature
+*_*_EBC_GENEBC_FLAGS =
+
+##################
+# PatchEbc tool definitions
+##################
+*_*_EBC_PATCHEBC_PATH = PatchEbcSignature
+*_*_EBC_PATCHEBC_FLAGS =
+
+##################
# Asl Compiler definitions
##################
*_*_*_ASLCC_FLAGS = /nologo /c /FIAutoGen.h /TC /Dmain=ReferenceAcpiTable
diff --git a/BaseTools/Source/Python/GenEbcSignature/GenEbcSignature.py b/BaseTools/Source/Python/GenEbcSignature/GenEbcSignature.py
new file mode 100644
index 000000000000..0a509d8d971e
--- /dev/null
+++ b/BaseTools/Source/Python/GenEbcSignature/GenEbcSignature.py
@@ -0,0 +1,306 @@
+## @file
+# Generate a list of EBC call signatures
+#
+# Copyright (c) 2008 - 2016, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2016, Pete Batard. All rights reserved.<BR>
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+
+#====================================== External Libraries ========================================
+import optparse
+import Common.LongFilePathOs as os
+import re
+import sys
+import array
+import struct
+import pickle
+
+from Common.BuildToolError import *
+import Common.EdkLogger as EdkLogger
+from Common.Misc import PeImageClass
+from Common.BuildVersion import gBUILD_VERSION
+from Common.LongFilePathSupport import OpenLongFilePath as open
+
+# Version and Copyright
+__version_number__ = ("0.5" + " " + gBUILD_VERSION)
+__version__ = "%prog Version " + __version_number__
+__copyright__ = "Copyright (c) 2016, Intel Corporation. All rights reserved."
+
+#====================================== Internal Libraries ========================================
+
+#============================================== Code ===============================================
+EBC_CALL_SIGNATURE = 0x2EBC0000
+
+functionStartRe = re.compile('^\s*(\w+)\s+\(\s*', re.UNICODE)
+param64BitRe = re.compile('\\bU*INT64\\b(?!\s*\*)\s*\w+\\b', re.UNICODE)
+
+def ParseSource(CallList):
+ """ Parse preprocessed source to generate a function signature.
+
+ Note that this is a very basic parser, that expects all function definitions to follow the
+ UEFI coding standard, i.e. with function name and each parameters on individual lines.
+ It also expects 64 bit parameter to be explicitly passed as INT64 or UINT64.
+
+ @return the processed function call list with their signatures set
+ """
+ global Options
+ Status = 0
+ Signature = 0
+ FuncName = ""
+
+ for Line in sys.stdin:
+ Line = Line.strip()
+
+ if Status == 0:
+ m = functionStartRe.match(Line)
+ if m != None:
+ FuncName, = m.groups(0)
+ if FuncName in CallList:
+ Status = 1
+ Mask = 1
+ Signature = 0
+ if Options.debug > 1:
+ print FuncName + "("
+ continue
+
+ if Status == 1 and Line[0] == ')':
+ if Options.debug > 1:
+ print ")"
+ CallList[FuncName] = Signature
+ Status = 0
+ if Status == 1:
+ if Options.debug > 1:
+ print " " + Line
+ m = param64BitRe.match(Line)
+ if m != None:
+ Signature = Signature | Mask
+ Mask = Mask << 1
+
+ return True
+
+def ParseObject(ObjPath):
+ """ Parse a COFF object file to identify function calls that require signature generation
+ @param ObjPath COFF object absolute path
+
+ @return a list of function call names require signature generation
+ """
+ global Options
+ CallList = {}
+ RelocList = []
+ SymbolList = {}
+
+ with file(ObjPath, 'rb') as f:
+ # Parse the object header
+ Machine = struct.unpack('H', f.read(2))[0]
+ assert Machine == 0xEBC, "Not an EBC object file"
+ NumSections = struct.unpack('H', f.read(2))[0]
+ f.seek(4, 1) # Skip timestamp
+ SymTabAddr = struct.unpack('I', f.read(4))[0]
+ NumSymbols = struct.unpack('I', f.read(4))[0]
+ OptHeaderSize = struct.unpack('H', f.read(2))[0]
+ assert OptHeaderSize == 0, "Unexpected object file header"
+ Characteristics = struct.unpack('H', f.read(2))[0]
+ StrTabAddr = SymTabAddr + NumSymbols * 18
+ # Optional debug output
+ if Options.debug > 0:
+ print "Number of Sections: " + str(NumSections)
+ print "Number of Symbols: " + str(NumSymbols)
+ print "Symbol Table at: " + hex(SymTabAddr)
+ print "String Table at: " + hex(StrTabAddr)
+
+ # Parse the object sections, to identify reloc sections
+ for i in range (0, NumSections):
+ SectionName = struct.unpack('8s', f.read(8))[0]
+ SectionName = SectionName.strip()
+ VirtualSize = struct.unpack('I', f.read(4))[0]
+ assert VirtualSize == 0, "Unexpected Obj file section"
+ # Skip Virtual Address
+ f.seek(4, 1)
+ DataSize = struct.unpack('I', f.read(4))[0]
+ DataAddr = struct.unpack('I', f.read(4))[0]
+ RelocAddr = struct.unpack('I', f.read(4))[0]
+ # Skip Line Numbers
+ f.seek(4, 1)
+ RelocNum = struct.unpack('H', f.read(2))[0]
+ f.seek(6, 1)
+ if RelocNum > 0:
+ RelocList.append([RelocNum, RelocAddr])
+ if Options.debug > 1:
+ print " Section " + str(i) + ": " + SectionName
+ print " Size: " + hex(DataSize)
+ print " Addr: " + hex(DataAddr)
+ if RelocNum > 0:
+ print " Rels: " + str(RelocNum) + " at " + hex(RelocAddr)
+
+ # Parse reloc sections
+ if RelocList != None:
+ # Copy the String Table into something we can access more easily
+ f.seek(StrTabAddr, 0)
+ StrTabSize = struct.unpack('I', f.read(4))[0]
+ StrData = f.read(StrTabSize)
+ if Options.debug > 0:
+ print "String Table Size: " + str(StrTabSize)
+ if Options.debug > 1:
+ StrArray = StrData.split(b'\x00')
+ for String in StrArray:
+ print " " + String
+
+ # Now build a full symbol table, that includes long names
+ f.seek(SymTabAddr, 0)
+ NumAuxSections = 0
+ if Options.debug > 0 and NumSymbols > 0:
+ print "Relocation symbols:"
+ for i in range(0, NumSymbols):
+ if NumAuxSections > 0:
+ f.seek(18, 1)
+ NumAuxSections = NumAuxSections - 1
+ continue
+ SymName = f.read(8)
+ SymValue = struct.unpack('I', f.read(4))[0]
+ SymSecNum = struct.unpack('h', f.read(2))[0]
+ SymType = struct.unpack('H', f.read(2))[0]
+ SymClass = struct.unpack('B', f.read(1))[0]
+ NumAuxSections = struct.unpack('B', f.read(1))[0]
+ if SymName[0] != '\0':
+ SymName = SymName.strip(' \0')
+ else:
+ StrIndex = struct.unpack('I', SymName[4:8])[0]
+ StrEnd = StrData.find('\0', StrIndex-3)
+ SymName = StrData[StrIndex-4:StrEnd]
+ SymbolList[i] = SymName
+ if Options.debug > 1:
+ print " " + SymName + ":"
+ print " Index=" + str(i) + " SecNum=" + str(SymSecNum) + " Type=" + str(SymType) + " Class=" + str(SymClass) + " NbAux=" + str(NumAuxSections)
+
+ # Finally, go through each reloc symbol and identify the "_plabel" suffixed ones
+ for Entry in RelocList:
+ f.seek(Entry[1], 0)
+
+ for i in range(0, Entry[0]):
+ # Skip Virtual Address
+ f.seek(4, 1)
+ SymbolIndex = struct.unpack('I', f.read(4))[0]
+ assert SymbolIndex in SymbolList, "Symbol not found in list"
+ RelocType = struct.unpack('H', f.read(2))[0]
+ m = re.match('(.*)_plabel', SymbolList[SymbolIndex])
+ if m != None:
+ CallName, = m.groups(0)
+ if CallName not in CallList:
+ # Add this call, and set its signature to undefined (-1)
+ CallList[CallName] = -1
+ if Options.debug > 0:
+ print " " + SymbolList[SymbolIndex] + " (Index " + hex(SymbolIndex) + ", Type " + str(RelocType) + ")"
+
+ if len(CallList) == 0:
+ return None
+ return CallList
+
+def UpdateSignatures(CallList, SigFile):
+ """ Create or update a signature file
+ @param CallList A dictionary containing the signatures to save/update
+ @param SigFile A path to the signature file to create/update
+
+ @return True if successfull
+ """
+ SavedList = {}
+
+ if Options.verbose != None:
+ print "EBC signatures:"
+ Width = max(len(CallName) for CallName in CallList) + 3
+ for CallName in CallList:
+ print " " + (CallName + "()").ljust(Width) + "0b" + "{:016b}".format(CallList[CallName])
+
+ try:
+ f = open(SigFile, 'rb')
+ SavedList = pickle.load(f)
+ f.close()
+
+ except:
+ pass
+
+ SavedList.update(CallList)
+ with open(Options.sigfile, 'wb') as f:
+ pickle.dump(SavedList, f, protocol=pickle.HIGHEST_PROTOCOL)
+
+ return True
+
+def ParseOptions():
+ Usage = "%prog [-v] -o <ObjFile> -s <SigFile>"
+ AdditionalNotes = "\nThis application currently only recognizes INT64 or UINT64 types as 64-bit arg."
+ Parser = optparse.OptionParser(usage=Usage, description=__copyright__, version="%prog " + __version_number__)
+ Parser.add_option('-o', '--objfile', action='store', dest='objfile', type="string", help='Path of the object file.')
+ Parser.add_option('-s', '--sigfile', action='store', dest='sigfile', type="string", help='Path to the signature file.')
+ Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed.")
+ Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")
+ Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")
+
+ (options, args) = Parser.parse_args()
+ return options
+
+def main():
+ global Options
+ AppName = os.path.basename(sys.argv[0])
+ Options = ParseOptions()
+ ReturnCode = 0
+
+ EdkLogger.Initialize()
+ try:
+ if Options.verbose != None:
+ EdkLogger.SetLevel(EdkLogger.VERBOSE)
+ if Options.quiet != None:
+ EdkLogger.SetLevel(EdkLogger.QUIET)
+ if Options.debug != None:
+ EdkLogger.SetLevel(Options.debug + 1)
+ else:
+ EdkLogger.SetLevel(EdkLogger.INFO)
+
+ if Options.objfile == None:
+ EdkLogger.error(AppName, OPTION_MISSING, "Object file not defined",
+ ExtraData="Please use '-o' switch to set the input Object file.")
+ if Options.sigfile == None:
+ EdkLogger.error(AppName, OPTION_MISSING, "Signature file not defined",
+ ExtraData="Please use '-s' switch to set the output Signature file.")
+
+ List = ParseObject(Options.objfile)
+ if List != None:
+ ParseSource(List)
+ # Check for missing signatures
+ for CallName in List:
+ assert List[CallName] != -1, "Could not set signature for function " + CallName
+ UpdateSignatures(List, Options.sigfile)
+ else:
+ # Still need to deplete stdin data
+ for Line in sys.stdin:
+ pass
+
+ except FatalError, X:
+ if Options.debug != None:
+ import traceback
+ EdkLogger.quiet(traceback.format_exc())
+ ReturnCode = X.args[0]
+
+ except:
+ import traceback
+ EdkLogger.error(
+ "\nPython",
+ CODE_ERROR,
+ "Tools code failure",
+ ExtraData="Please send email to edk2-devel@lists.01.org for help, attaching following call stack trace!\n",
+ RaiseError=False
+ )
+ EdkLogger.quiet(traceback.format_exc())
+ ReturnCode = CODE_ERROR
+ return ReturnCode
+
+if __name__ == '__main__':
+ r = main()
+ ## 0-127 is a safe return range, and 1 is a standard default error
+ if r < 0 or r > 127: r = 1
+ sys.exit(r)
diff --git a/BaseTools/Source/Python/GenEbcSignature/__init__.py b/BaseTools/Source/Python/GenEbcSignature/__init__.py
new file mode 100644
index 000000000000..58a5b1d630f0
--- /dev/null
+++ b/BaseTools/Source/Python/GenEbcSignature/__init__.py
@@ -0,0 +1,15 @@
+## @file
+# Python 'GenPatchPcdTable' package initialization file.
+#
+# This file is required to make Python interpreter treat the directory
+# as containing package.
+#
+# Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
diff --git a/BaseTools/Source/Python/PatchEbcSignature/PatchEbcSignature.py b/BaseTools/Source/Python/PatchEbcSignature/PatchEbcSignature.py
new file mode 100644
index 000000000000..14f34641fb09
--- /dev/null
+++ b/BaseTools/Source/Python/PatchEbcSignature/PatchEbcSignature.py
@@ -0,0 +1,226 @@
+## @file
+# Insert EBC Call Signature data in an EFI executable
+#
+# Copyright (c) 2008 - 2016, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2016, Pete Batard. All rights reserved.<BR>
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+
+#====================================== External Libraries ========================================
+import optparse
+import Common.LongFilePathOs as os
+import re
+import sys
+import array
+import struct
+import pickle
+
+from Common.BuildToolError import *
+import Common.EdkLogger as EdkLogger
+from Common.Misc import PeImageClass
+from Common.BuildVersion import gBUILD_VERSION
+from Common.LongFilePathSupport import OpenLongFilePath as open
+
+# Version and Copyright
+__version_number__ = ("1.0" + " " + gBUILD_VERSION)
+__version__ = "%prog Version " + __version_number__
+__copyright__ = "Copyright (c) 2016, Intel Corporation. All rights reserved."
+
+#====================================== Internal Libraries ========================================
+
+#============================================== Code ===============================================
+EBC_CALL_SIGNATURE = 0x2EBC0000
+
+CallRegexp = re.compile('^[\da-fA-F]+:[\da-fA-F]+ +([\w@\$]+)_plabel +([\da-fA-F]+) f? +([\w]+):([\w]+)\.obj', re.UNICODE)
+LoadRegexp = re.compile('Preferred load address is +([\da-fA-F]+)', re.UNICODE)
+
+def ParseMap(MapPath):
+ """ Parse a map file and get the list and address of functions that need an EBC call signature
+ @param MapPath Map file absolute path
+
+ @return a list of call names with their address and source location
+ """
+ Status = 0
+ CallList = []
+
+ with file(MapPath, 'r') as f:
+ for Line in f:
+ Line = Line.strip()
+
+ if Status == 0:
+ m = LoadRegexp.match(Line)
+ if m != None:
+ LoadAddr, = m.groups(0)
+ LoadAddr = int(LoadAddr, 16)
+ Status = 1
+
+ if Status == 1:
+ m = CallRegexp.match(Line)
+ if m != None:
+ FuncName, FuncAddr, ModName, SrcName = m.groups(0)
+ FuncAddr = int(FuncAddr, 16)
+ SrcName = SrcName + ".c"
+ CallList.append([FuncName, FuncAddr - LoadAddr, ModName, SrcName])
+
+ assert Status != 0, "Failed to read load address"
+
+ if len(CallList) == 0:
+ return None
+ return CallList
+
+def BuildSignatureList(FileList):
+ """ Parse the list of files passed as arguments, and try to locate and open
+ the matching signature files to build a complete EBC signature list.
+ as the signature file to ensure that the signatures we need are present.
+ @param FileList A list of .sig or .lib files
+
+ @return a list of function calls with their signature data
+ """
+ global Options
+ SigList = {}
+
+ for File in FileList:
+ File = os.path.splitext(File)[0]+'.sig'
+ try:
+ with open(File, 'rb') as f:
+ SavedList = pickle.load(f)
+ SigList.update(SavedList)
+ if Options.verbose != None:
+ print " Loaded " + str(len(SavedList)) + " EBC signature(s) from " + File
+ except:
+ pass
+
+ if len(SigList) == 0:
+ return None
+ return SigList
+
+def CheckSignatures(EfiPath, MapList, SigList):
+ """ Sanity check to ensures that the signatures' data and addresses are valid.
+ @param EfiPath EFI binary absolute path
+ @param MapList Function calls requiring signature, with their address
+ @param SigList Function calls signature dictionary
+
+ @return True if the check passed
+ """
+
+ for Entry in MapList:
+ # Check for missing signatures
+ assert Entry[0] in SigList, Entry[0] + ": missing signature"
+ # Make sure the signature fits in 16 bits
+ assert SigList[Entry[0]] < 0x10000, Entry[0] + ": invalid signature"
+
+ with file(EfiPath, 'rb') as f:
+ for Entry in MapList:
+ f.seek(Entry[1] + 4)
+ Data = struct.unpack('I', f.read(4))[0]
+ # The 32 bit data should either be 0 or have the call signature marker
+ assert Data == 0 or Data & 0xFFFF0000 == EBC_CALL_SIGNATURE, "Unexpected data at address 0x%x" % Entry[1]
+
+ return True;
+
+def InsertSignatures(EfiPath, MapList, SigList):
+ """ Check the EFI binary to ensure we have the right signature addresses
+ @param EfiPath EFI binary absolute path
+ @param Maplist Function calls requiring signature, with their address
+ @param SigList Function calls signature dictionary
+
+ @return True if the signatures were successfully patched
+ """
+ global Options
+
+ with file(EfiPath, 'r+b') as f:
+ Width = max(len(Entry[0]) for Entry in MapList) + 3
+ for Entry in MapList:
+ f.seek(Entry[1] + 4)
+ f.write(struct.pack('I', EBC_CALL_SIGNATURE + SigList[Entry[0]]))
+ if Options.verbose != None:
+ print " Patched " + (Entry[0] + "()").ljust(Width) + "at 0x{:08X}".format(Entry[1]) + " with signature 0x{:04X}".format(SigList[Entry[0]]) + " (" + Entry[2] + ":" + Entry[3] + ")"
+
+ return True;
+
+def ParseOptions():
+ Usage = "%prog [-v] -m <MapFile> -e <EfiFile> -s <SigFile> [file1] [file2] [...]"
+ AdditionalNotes = "\nfile# can be either a .sig or .lib file. If the latter, its path is used to look for a matching .sig."
+ Parser = optparse.OptionParser(usage=Usage, description=__copyright__, version="%prog " + __version_number__)
+ Parser.add_option('-m', '--mapfile', action='store', dest='mapfile', type="string", help='Path of the module map file.')
+ Parser.add_option('-e', '--efifile', action='store', dest='efifile', type="string", help='Path of the EFI binary to patch.')
+ Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed.")
+ Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")
+ Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")
+
+ return Parser.parse_args()
+
+def main():
+ global Options
+ AppName = os.path.basename(sys.argv[0])
+ (Options, ArgList) = ParseOptions()
+ ReturnCode = 0
+
+ EdkLogger.Initialize()
+
+ try:
+ if Options.verbose != None:
+ EdkLogger.SetLevel(EdkLogger.VERBOSE)
+ if Options.quiet != None:
+ EdkLogger.SetLevel(EdkLogger.QUIET)
+ if Options.debug != None:
+ EdkLogger.SetLevel(Options.debug + 1)
+ else:
+ EdkLogger.SetLevel(EdkLogger.INFO)
+
+ if Options.mapfile == None:
+ EdkLogger.error(AppName, OPTION_MISSING, "Map file not defined",
+ ExtraData="Please use '-m' switch to define a map file.")
+ if Options.efifile == None:
+ EdkLogger.error(AppName, OPTION_MISSING, "EFI file not defined",
+ ExtraData="Please use '-e' switch to set the EFI binary to patch.")
+ if ArgList == None:
+ EdkLogger.error(AppName, OPTION_MISSING, "No signature or library file provided",
+ ExtraData="At least one signature or library path must be provided as argument.")
+
+ MapList = ParseMap(Options.mapfile)
+ if MapList != None:
+ SigList = BuildSignatureList(ArgList)
+ assert SigList != None, "No signatures found"
+ CheckSignatures(Options.efifile, MapList, SigList)
+ if Options.debug != None:
+ print "EBC signatures:"
+ Width = max(len(CallName) for CallName in SigList) + 3
+ for CallName in SigList:
+ print " " + (CallName + "()").ljust(Width) + "0b" + "{:016b}".format(SigList[CallName])
+ InsertSignatures(Options.efifile, MapList, SigList)
+ elif Options.verbose:
+ print 'No EBC call signatures patching required'
+
+ except FatalError, X:
+ if Options.debug != None:
+ import traceback
+ EdkLogger.quiet(traceback.format_exc())
+ ReturnCode = X.args[0]
+
+ except:
+ import traceback
+ EdkLogger.error(
+ "\nPython",
+ CODE_ERROR,
+ "Tools code failure",
+ ExtraData="Please send email to edk2-devel@lists.01.org for help, attaching following call stack trace!\n",
+ RaiseError=False
+ )
+ EdkLogger.quiet(traceback.format_exc())
+ ReturnCode = CODE_ERROR
+
+ return ReturnCode
+
+if __name__ == '__main__':
+ r = main()
+ ## 0-127 is a safe return range, and 1 is a standard default error
+ if r < 0 or r > 127: r = 1
+ sys.exit(r)
diff --git a/BaseTools/Source/Python/PatchEbcSignature/__init__.py b/BaseTools/Source/Python/PatchEbcSignature/__init__.py
new file mode 100644
index 000000000000..58a5b1d630f0
--- /dev/null
+++ b/BaseTools/Source/Python/PatchEbcSignature/__init__.py
@@ -0,0 +1,15 @@
+## @file
+# Python 'GenPatchPcdTable' package initialization file.
+#
+# This file is required to make Python interpreter treat the directory
+# as containing package.
+#
+# Copyright (c) 2010, Intel Corporation. All rights reserved.<BR>
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
--
2.9.3.windows.2
^ permalink raw reply related [flat|nested] 5+ messages in thread