From patchwork Tue Aug 9 14:19:07 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ard Biesheuvel X-Patchwork-Id: 73554 Delivered-To: patch@linaro.org Received: by 10.140.29.52 with SMTP id a49csp511194qga; Tue, 9 Aug 2016 07:19:28 -0700 (PDT) X-Received: by 10.66.229.9 with SMTP id sm9mr172182041pac.138.1470752368453; Tue, 09 Aug 2016 07:19:28 -0700 (PDT) Return-Path: Received: from ml01.01.org (ml01.01.org. [2001:19d0:306:5::1]) by mx.google.com with ESMTPS id v187si42968897pfb.258.2016.08.09.07.19.27 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 09 Aug 2016 07:19:28 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of edk2-devel-bounces@lists.01.org designates 2001:19d0:306:5::1 as permitted sender) client-ip=2001:19d0:306:5::1; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org; spf=pass (google.com: best guess record for domain of edk2-devel-bounces@lists.01.org designates 2001:19d0:306:5::1 as permitted sender) smtp.mailfrom=edk2-devel-bounces@lists.01.org; dmarc=fail (p=NONE dis=NONE) header.from=linaro.org Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 96D3A1A1E18; Tue, 9 Aug 2016 07:19:27 -0700 (PDT) X-Original-To: edk2-devel@lists.01.org Delivered-To: edk2-devel@lists.01.org Received: from mail-wm0-x234.google.com (mail-wm0-x234.google.com [IPv6:2a00:1450:400c:c09::234]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id AB86C1A1DFA for ; Tue, 9 Aug 2016 07:19:25 -0700 (PDT) Received: by mail-wm0-x234.google.com with SMTP id q128so36486800wma.1 for ; Tue, 09 Aug 2016 07:19:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id; bh=IQt/lJ2pqlGj32PerpTYEfcuxayMLL8WYBTvHwTp2Gk=; b=LxzrNFGHAo4l88iw8m+CCRv38Btsa2rrO3vAI40mBCYVDJuzkuYREtjNeKH52wXVfT 8fTLaGBn4XNXT4JdiBQbb1s12tBbqSW0GC9hFPOEGctjpy8T4s5TV7rfSKpLkMy/N6SJ CyT5/31KrRSd0ruHVb+DDdhVqE37a1qqabqzA= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=IQt/lJ2pqlGj32PerpTYEfcuxayMLL8WYBTvHwTp2Gk=; b=G5kMtafZ4msj9ex4/egHmd8Wkfco57fko0h2zVm5kEBITHcPIM+xpr08ZXv8jsDiv9 gt1S1NZftqXAU1d82HOqo7L8oFJQBM4pzP1UbeiGJbJqaPCSol96zz7nRCX4cjL3m9OJ 5dFRS4v6wqeM9YsWaefZ5ZUHfuP5GX4t0alnJD0vHJiOHM2FNApi/NS8ro6xj0yd8+Jp ziNUOkk8yiycZymIfdBh2qIc/PlRfMSE0lqCDQ/v4gS/pvJZulVFBzjag7W2V0dcxI7P 7y1Rloj0eLrX7LL+DPCJXvf2OCD/5VRnZJspa9brHjbj1fBa+LVGA0fmvU9J6r0b9sm5 W0Dw== X-Gm-Message-State: AEkoous/fmhUjuT1Dx2nML5OZsozyfQ6WO81Vc/MEMV5ezwl+pPlP5UE5OxHYWLdQgb39FBV X-Received: by 10.194.104.197 with SMTP id gg5mr88695766wjb.6.1470752363328; Tue, 09 Aug 2016 07:19:23 -0700 (PDT) Received: from localhost.localdomain (46.red-81-37-107.dynamicip.rima-tde.net. [81.37.107.46]) by smtp.gmail.com with ESMTPSA id f3sm38314313wjh.2.2016.08.09.07.19.17 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 09 Aug 2016 07:19:22 -0700 (PDT) From: Ard Biesheuvel To: edk2-devel@lists.01.org Date: Tue, 9 Aug 2016 16:19:07 +0200 Message-Id: <1470752347-19268-1-git-send-email-ard.biesheuvel@linaro.org> X-Mailer: git-send-email 2.7.4 Subject: [edk2] [PATCH] MdeModulePkg/EbcDxe: add ARM support X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: leif.lindholm@linaro.org, Ard Biesheuvel MIME-Version: 1.0 Errors-To: edk2-devel-bounces@lists.01.org Sender: "edk2-devel" This is a port of the recently proposed AARCH64 implementation of the EBC runtime to ARM. It is a proof of concept: it runs HelloWorld.efi, but it is likely that there are issues with function argument marshalling that are difficult to solve. In particular, UINT64 register arguments are passed in even/odd pairs, which would require padding on the VM stack. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Ard Biesheuvel --- MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S | 155 +++++++ MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c | 467 ++++++++++++++++++++ 2 files changed, 622 insertions(+) -- 2.7.4 _______________________________________________ edk2-devel mailing list edk2-devel@lists.01.org https://lists.01.org/mailman/listinfo/edk2-devel diff --git a/MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S b/MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S new file mode 100644 index 000000000000..cc71fcf355df --- /dev/null +++ b/MdeModulePkg/Universal/EbcDxe/Arm/EbcLowLevel.S @@ -0,0 +1,155 @@ +///** @file +// +// This code provides low level routines that support the Virtual Machine +// for option ROMs. +// +// Copyright (c) 2016, Linaro, Ltd. All rights reserved.
+// Copyright (c) 2015, The Linux Foundation. All rights reserved.
+// Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.
+// +// 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. +// +//**/ + +ASM_GLOBAL ASM_PFX(EbcLLCALLEXNative); +ASM_GLOBAL ASM_PFX(EbcLLEbcInterpret); +ASM_GLOBAL ASM_PFX(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) + .type EbcLLCALLEXNative, %function +ASM_PFX(EbcLLCALLEXNative): + mov ip, r1 // Preserve r1 + + // + // If the EBC stack frame is smaller than or equal to 32 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, #32 + bgt 1f + + adr r1, 0f + sub r1, r1, r3, lsr #1 + bx r1 + + ldr r3, [ip, #12] + ldr r2, [ip, #8] + ldr r1, [ip, #4] + ldr ip, [ip] + +0: eor r0, r0, ip // Swap r0 and ip + eor ip, r0, ip + eor r0, r0, ip + + bx ip + + // + // More than 32 bytes: we need to build the full native stack frame and copy + // the part of the VM stack exceeding 32 bytes (which may contain stacked + // arguments) to the native stack + // +1: stmdb sp!, {r4-r6, lr} + mov r4, sp + mov r5, r0 + + // + // 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, #32 // Skip over [potential] reg params + tst r1, #7 // Multiple of 8? + beq 2f + ldr r3, [r2, #-4]! // No? Then push one word + str r3, [sp, #-8]! // ... but use two slots + b 3f + +2: ldrd r0, r1, [r2, #-8]! + strd r0, r1, [sp, #-8]! +3: cmp r2, ip + bgt 2b + + ldrd r0, r1, [ip] + ldrd r2, r3, [ip, #8] + + blx r5 + + mov sp, r4 + ldmia sp!, {r4-r6, 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. +// +//**************************************************************************** + .type EbcLLEbcInterpret, %function +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 +// x16 contains the Entry point that will be the third argument when +// ExecuteEbcImageEntryPoint is called. +// +//**************************************************************************** + .thumb + .type EbcLLExecuteEbcImageEntryPoint, %function +ASM_PFX(EbcLLExecuteEbcImageEntryPoint): + mov r2, ip + + // tail call to C code + b ASM_PFX(ExecuteEbcImageEntryPoint) + +//**************************************************************************** +// mEbcInstructionBufferTemplate +//**************************************************************************** + .section ".rodata", "a" + .align 2 + .arm +ASM_PFX(mEbcInstructionBufferTemplate): + ldr ip, 0f + ldr pc, 1f + + // + // Add a magic code here to help the VM recognize the thunk. + // + .long 0xCA112EBC + +0: .long 0 // EBC_ENTRYPOINT_SIGNATURE +1: .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..7ec26f3330e1 --- /dev/null +++ b/MdeModulePkg/Universal/EbcDxe/Arm/EbcSupport.c @@ -0,0 +1,467 @@ +/** @file + This module contains EBC support routines that are customized based on + the target AArch64 processor. + +Copyright (c) 2016, Linaro, Ltd. All rights reserved.
+Copyright (c) 2015, The Linux Foundation. All rights reserved.
+Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.
+ +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 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 is belong 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. + // AArch64 does not do this so pad the stack accordingly. + // + PushU32 (&VmContext, 0x0UL); + PushU32 (&VmContext, 0x0UL); + PushU32 (&VmContext, 0x12345678UL); + PushU32 (&VmContext, 0x87654321UL); + + // + // For AArch64, 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 + // + EbcExecute (&VmContext); + + // + // Return the value in R[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 + // + EbcExecute (&VmContext); + + // + // Return the value in R[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 will not be 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; + } +} +