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

* 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



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

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-01-24 12:30 [PATCH 1/5] MdeModulePkg/EbcDxe: allow VmReadIndex##() to return a decoded index Pete Batard
2017-01-24 12:30 ` [PATCH 2/5] MdeModulePkg/EbcDxe: add ARM support Pete Batard
2017-01-24 12:30 ` [PATCH 3/5] MdeModulePkg/EbcDxe: add a stack tracker for ARM EBC->native support Pete Batard
2017-01-24 12:30 ` Pete Batard [this message]
2017-01-24 12:30 ` [PATCH 5/5] BaseTools: add scripts to generate EBC call signatures Pete Batard

Reply instructions:

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

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

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

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

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

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

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