Initial import.
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@3 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
148
EdkModulePkg/Universal/Ebc/Dxe/Ia32/EbcLowLevel.asm
Normal file
148
EdkModulePkg/Universal/Ebc/Dxe/Ia32/EbcLowLevel.asm
Normal file
@@ -0,0 +1,148 @@
|
||||
page ,132
|
||||
title VM ASSEMBLY LANGUAGE ROUTINES
|
||||
;****************************************************************************
|
||||
;*
|
||||
;* Copyright (c) 2006, 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.
|
||||
;*
|
||||
;****************************************************************************
|
||||
;****************************************************************************
|
||||
; REV 1.0
|
||||
;****************************************************************************
|
||||
;
|
||||
; Rev Date Description
|
||||
; --- -------- ------------------------------------------------------------
|
||||
; 1.0 03/14/01 Initial creation of file.
|
||||
;
|
||||
;****************************************************************************
|
||||
|
||||
;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
; This code provides low level routines that support the Virtual Machine
|
||||
; for option ROMs.
|
||||
;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
|
||||
;---------------------------------------------------------------------------
|
||||
; Equate files needed.
|
||||
;---------------------------------------------------------------------------
|
||||
|
||||
.XLIST
|
||||
|
||||
.LIST
|
||||
|
||||
;---------------------------------------------------------------------------
|
||||
; Assembler options
|
||||
;---------------------------------------------------------------------------
|
||||
|
||||
.686p
|
||||
.model flat
|
||||
.code
|
||||
;---------------------------------------------------------------------------
|
||||
;;GenericPostSegment SEGMENT USE16
|
||||
;---------------------------------------------------------------------------
|
||||
|
||||
;****************************************************************************
|
||||
; EbcLLCALLEXNative
|
||||
;
|
||||
; This function is called to execute an EBC CALLEX instruction
|
||||
; to native code.
|
||||
; This instruction requires that we thunk out to external native
|
||||
; code. For IA32, we simply switch stacks and jump to the
|
||||
; specified function. On return, we restore the stack pointer
|
||||
; to its original location.
|
||||
;
|
||||
; Destroys no working registers.
|
||||
;****************************************************************************
|
||||
; VOID EbcLLCALLEXNative(UINTN FuncAddr, UINTN NewStackPointer, VOID *FramePtr)
|
||||
_EbcLLCALLEXNative PROC NEAR PUBLIC
|
||||
push ebp
|
||||
mov ebp, esp ; standard function prolog
|
||||
|
||||
; Get function address in a register
|
||||
; mov ecx, FuncAddr => mov ecx, dword ptr [FuncAddr]
|
||||
mov ecx, dword ptr [esp]+8
|
||||
|
||||
; Set stack pointer to new value
|
||||
; mov eax, NewStackPointer => mov eax, dword ptr [NewSp]
|
||||
mov eax, dword ptr [esp] + 0Ch
|
||||
mov esp, eax
|
||||
|
||||
; Now call the external routine
|
||||
call ecx
|
||||
|
||||
; ebp is preserved by the callee. In this function it
|
||||
; equals the original esp, so set them equal
|
||||
mov esp, ebp
|
||||
|
||||
; Standard function epilog
|
||||
mov esp, ebp
|
||||
pop ebp
|
||||
ret
|
||||
_EbcLLCALLEXNative ENDP
|
||||
|
||||
|
||||
; UINTN EbcLLGetEbcEntryPoint(VOID);
|
||||
; Routine Description:
|
||||
; The VM thunk code stuffs an EBC entry point into a processor
|
||||
; register. Since we can't use inline assembly to get it from
|
||||
; the interpreter C code, stuff it into the return value
|
||||
; register and return.
|
||||
;
|
||||
; Arguments:
|
||||
; None.
|
||||
;
|
||||
; Returns:
|
||||
; The contents of the register in which the entry point is passed.
|
||||
;
|
||||
_EbcLLGetEbcEntryPoint PROC NEAR PUBLIC
|
||||
ret
|
||||
_EbcLLGetEbcEntryPoint ENDP
|
||||
|
||||
;/*++
|
||||
;
|
||||
;Routine Description:
|
||||
;
|
||||
; Return the caller's value of the stack pointer.
|
||||
;
|
||||
;Arguments:
|
||||
;
|
||||
; None.
|
||||
;
|
||||
;Returns:
|
||||
;
|
||||
; The current value of the stack pointer for the caller. We
|
||||
; adjust it by 4 here because when they called us, the return address
|
||||
; is put on the stack, thereby lowering it by 4 bytes.
|
||||
;
|
||||
;--*/
|
||||
|
||||
; UINTN EbcLLGetStackPointer()
|
||||
_EbcLLGetStackPointer PROC NEAR PUBLIC
|
||||
mov eax, esp ; get current stack pointer
|
||||
add eax, 4 ; stack adjusted by this much when we were called
|
||||
ret
|
||||
_EbcLLGetStackPointer ENDP
|
||||
|
||||
; UINT64 EbcLLGetReturnValue(VOID);
|
||||
; Routine Description:
|
||||
; When EBC calls native, on return the VM has to stuff the return
|
||||
; value into a VM register. It's assumed here that the value is still
|
||||
; in the register, so simply return and the caller should get the
|
||||
; return result properly.
|
||||
;
|
||||
; Arguments:
|
||||
; None.
|
||||
;
|
||||
; Returns:
|
||||
; The unmodified value returned by the native code.
|
||||
;
|
||||
_EbcLLGetReturnValue PROC NEAR PUBLIC
|
||||
ret
|
||||
_EbcLLGetReturnValue ENDP
|
||||
|
||||
END
|
482
EdkModulePkg/Universal/Ebc/Dxe/Ia32/EbcSupport.c
Normal file
482
EdkModulePkg/Universal/Ebc/Dxe/Ia32/EbcSupport.c
Normal file
@@ -0,0 +1,482 @@
|
||||
/*++
|
||||
|
||||
Copyright (c) 2006, 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.
|
||||
|
||||
Module Name:
|
||||
|
||||
EbcSupport.c
|
||||
|
||||
Abstract:
|
||||
|
||||
This module contains EBC support routines that are customized based on
|
||||
the target processor.
|
||||
|
||||
--*/
|
||||
|
||||
#include "EbcInt.h"
|
||||
#include "EbcExecute.h"
|
||||
|
||||
//
|
||||
// NOTE: This is the stack size allocated for the interpreter
|
||||
// when it executes an EBC image. The requirements can change
|
||||
// based on whether or not a debugger is present, and other
|
||||
// platform-specific configurations.
|
||||
//
|
||||
#define VM_STACK_SIZE (1024 * 4)
|
||||
#define EBC_THUNK_SIZE 32
|
||||
|
||||
VOID
|
||||
EbcLLCALLEX (
|
||||
IN VM_CONTEXT *VmPtr,
|
||||
IN UINTN FuncAddr,
|
||||
IN UINTN NewStackPointer,
|
||||
IN VOID *FramePtr,
|
||||
IN UINT8 Size
|
||||
)
|
||||
/*++
|
||||
|
||||
Routine Description:
|
||||
|
||||
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.
|
||||
|
||||
Arguments:
|
||||
|
||||
VmPtr - Pointer to a VM context.
|
||||
FuncAddr - Callee's address
|
||||
NewStackPointer - New stack pointer after the call
|
||||
FramePtr - New frame pointer after the call
|
||||
Size - The size of call instruction
|
||||
|
||||
Returns:
|
||||
|
||||
None.
|
||||
|
||||
--*/
|
||||
{
|
||||
UINTN IsThunk;
|
||||
UINTN TargetEbcAddr;
|
||||
|
||||
IsThunk = 1;
|
||||
TargetEbcAddr = 0;
|
||||
|
||||
//
|
||||
// Processor specific code to check whether the callee is a thunk to EBC.
|
||||
//
|
||||
if (*((UINT8 *)FuncAddr) != 0xB8) {
|
||||
IsThunk = 0;
|
||||
goto Action;
|
||||
}
|
||||
if (*((UINT8 *)FuncAddr + 1) != 0xBC) {
|
||||
IsThunk = 0;
|
||||
goto Action;
|
||||
}
|
||||
if (*((UINT8 *)FuncAddr + 2) != 0x2E) {
|
||||
IsThunk = 0;
|
||||
goto Action;
|
||||
}
|
||||
if (*((UINT8 *)FuncAddr + 3) != 0x11) {
|
||||
IsThunk = 0;
|
||||
goto Action;
|
||||
}
|
||||
if (*((UINT8 *)FuncAddr + 4) != 0xCA) {
|
||||
IsThunk = 0;
|
||||
goto Action;
|
||||
}
|
||||
if (*((UINT8 *)FuncAddr + 5) != 0xB8) {
|
||||
IsThunk = 0;
|
||||
goto Action;
|
||||
}
|
||||
if (*((UINT8 *)FuncAddr + 10) != 0xB9) {
|
||||
IsThunk = 0;
|
||||
goto Action;
|
||||
}
|
||||
if (*((UINT8 *)FuncAddr + 15) != 0xFF) {
|
||||
IsThunk = 0;
|
||||
goto Action;
|
||||
}
|
||||
if (*((UINT8 *)FuncAddr + 16) != 0xE1) {
|
||||
IsThunk = 0;
|
||||
goto Action;
|
||||
}
|
||||
|
||||
TargetEbcAddr = ((UINTN)(*((UINT8 *)FuncAddr + 9)) << 24) + ((UINTN)(*((UINT8 *)FuncAddr + 8)) << 16) +
|
||||
((UINTN)(*((UINT8 *)FuncAddr + 7)) << 8) + ((UINTN)(*((UINT8 *)FuncAddr + 6)));
|
||||
|
||||
Action:
|
||||
if (IsThunk == 1){
|
||||
//
|
||||
// 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->R[0] -= 8;
|
||||
VmWriteMemN (VmPtr, (UINTN) VmPtr->R[0], (UINTN) FramePtr);
|
||||
VmPtr->FramePtr = (VOID *) (UINTN) VmPtr->R[0];
|
||||
VmPtr->R[0] -= 8;
|
||||
VmWriteMem64 (VmPtr, (UINTN) VmPtr->R[0], (UINT64) (UINTN) (VmPtr->Ip + Size));
|
||||
|
||||
VmPtr->Ip = (VMIP) (UINTN) TargetEbcAddr;
|
||||
} else {
|
||||
//
|
||||
// The callee is not a thunk to EBC, call native code.
|
||||
//
|
||||
EbcLLCALLEXNative (FuncAddr, NewStackPointer, FramePtr);
|
||||
|
||||
//
|
||||
// Get return value and advance the IP.
|
||||
//
|
||||
VmPtr->R[7] = EbcLLGetReturnValue ();
|
||||
VmPtr->Ip += Size;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC
|
||||
UINT64
|
||||
EbcInterpret (
|
||||
IN OUT UINTN Arg1,
|
||||
IN OUT UINTN Arg2,
|
||||
IN OUT UINTN Arg3,
|
||||
IN OUT UINTN Arg4,
|
||||
IN OUT UINTN Arg5,
|
||||
IN OUT UINTN Arg6,
|
||||
IN OUT UINTN Arg7,
|
||||
IN OUT UINTN Arg8
|
||||
)
|
||||
/*++
|
||||
|
||||
Routine Description:
|
||||
|
||||
Begin executing an EBC image. The address of the entry point is passed
|
||||
in via a processor register, so we'll need to make a call to get the
|
||||
value.
|
||||
|
||||
Arguments:
|
||||
|
||||
None. Since we're called from a fixed up thunk (which we want to keep
|
||||
small), our only so-called argument is the EBC entry point passed in
|
||||
to us in a processor register.
|
||||
|
||||
Returns:
|
||||
|
||||
The value returned by the EBC application we're going to run.
|
||||
|
||||
--*/
|
||||
{
|
||||
//
|
||||
// Create a new VM context on the stack
|
||||
//
|
||||
VM_CONTEXT VmContext;
|
||||
UINTN Addr;
|
||||
|
||||
//
|
||||
// Get the EBC entry point from the processor register.
|
||||
//
|
||||
Addr = EbcLLGetEbcEntryPoint ();
|
||||
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
Addr = EbcLLGetStackPointer ();
|
||||
|
||||
VmContext.R[0] = (UINT64) Addr;
|
||||
VmContext.R[0] -= VM_STACK_SIZE;
|
||||
|
||||
//
|
||||
// Align the stack on a natural boundary
|
||||
//
|
||||
VmContext.R[0] &= ~(sizeof (UINTN) - 1);
|
||||
|
||||
//
|
||||
// Put a magic value in the stack gap, then adjust down again
|
||||
//
|
||||
*(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) VM_STACK_KEY_VALUE;
|
||||
VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.R[0];
|
||||
VmContext.R[0] -= sizeof (UINTN);
|
||||
|
||||
//
|
||||
// For IA32, this is where we say our return address is
|
||||
//
|
||||
VmContext.StackRetAddr = (UINT64) VmContext.R[0];
|
||||
VmContext.LowStackTop = (UINTN) VmContext.R[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));
|
||||
//
|
||||
VmContext.HighStackBottom = (UINTN) &Arg1 - 16;
|
||||
//
|
||||
// Begin executing the EBC code
|
||||
//
|
||||
EbcExecute (&VmContext);
|
||||
|
||||
//
|
||||
// Return the value in R[7] unless there was an error
|
||||
//
|
||||
return (UINT64) VmContext.R[7];
|
||||
}
|
||||
|
||||
STATIC
|
||||
UINT64
|
||||
ExecuteEbcImageEntryPoint (
|
||||
IN EFI_HANDLE ImageHandle,
|
||||
IN EFI_SYSTEM_TABLE *SystemTable
|
||||
)
|
||||
/*++
|
||||
|
||||
Routine Description:
|
||||
|
||||
Begin executing an EBC image. The address of the entry point is passed
|
||||
in via a processor register, so we'll need to make a call to get the
|
||||
value.
|
||||
|
||||
Arguments:
|
||||
|
||||
ImageHandle - image handle for the EBC application we're executing
|
||||
SystemTable - standard system table passed into an driver's entry point
|
||||
|
||||
Returns:
|
||||
|
||||
The value returned by the EBC application we're going to run.
|
||||
|
||||
--*/
|
||||
{
|
||||
//
|
||||
// Create a new VM context on the stack
|
||||
//
|
||||
VM_CONTEXT VmContext;
|
||||
UINTN Addr;
|
||||
|
||||
//
|
||||
// Get the EBC entry point from the processor register. Make sure you don't
|
||||
// call any functions before this or you could mess up the register the
|
||||
// entry point is passed in.
|
||||
//
|
||||
Addr = EbcLLGetEbcEntryPoint ();
|
||||
|
||||
//
|
||||
// Print(L"*** Thunked into EBC entry point - ImageHandle = 0x%X\n", (UINTN)ImageHandle);
|
||||
// Print(L"EBC entry point is 0x%X\n", (UINT32)(UINTN)Addr);
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
Addr = EbcLLGetStackPointer ();
|
||||
VmContext.R[0] = (UINT64) Addr;
|
||||
VmContext.R[0] -= VM_STACK_SIZE;
|
||||
//
|
||||
// Put a magic value in the stack gap, then adjust down again
|
||||
//
|
||||
*(UINTN *) (UINTN) (VmContext.R[0]) = (UINTN) VM_STACK_KEY_VALUE;
|
||||
VmContext.StackMagicPtr = (UINTN *) (UINTN) VmContext.R[0];
|
||||
VmContext.R[0] -= sizeof (UINTN);
|
||||
|
||||
//
|
||||
// Align the stack on a natural boundary
|
||||
// VmContext.R[0] &= ~(sizeof(UINTN) - 1);
|
||||
//
|
||||
VmContext.StackRetAddr = (UINT64) VmContext.R[0];
|
||||
VmContext.LowStackTop = (UINTN) VmContext.R[0];
|
||||
//
|
||||
// VM pushes 16-bytes for return address. Simulate that here.
|
||||
//
|
||||
VmContext.HighStackBottom = (UINTN) &ImageHandle - 16;
|
||||
|
||||
//
|
||||
// Begin executing the EBC code
|
||||
//
|
||||
EbcExecute (&VmContext);
|
||||
|
||||
//
|
||||
// Return the value in R[7] unless there was an error
|
||||
//
|
||||
return (UINT64) VmContext.R[7];
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
EbcCreateThunks (
|
||||
IN EFI_HANDLE ImageHandle,
|
||||
IN VOID *EbcEntryPoint,
|
||||
OUT VOID **Thunk,
|
||||
IN UINT32 Flags
|
||||
)
|
||||
/*++
|
||||
|
||||
Routine Description:
|
||||
|
||||
Create an IA32 thunk for the given EBC entry point.
|
||||
|
||||
Arguments:
|
||||
|
||||
ImageHandle - Handle of image for which this thunk is being created
|
||||
EbcEntryPoint - Address of the EBC code that the thunk is to call
|
||||
Thunk - Returned thunk we create here
|
||||
|
||||
Returns:
|
||||
|
||||
Standard EFI status.
|
||||
|
||||
--*/
|
||||
{
|
||||
UINT8 *Ptr;
|
||||
UINT8 *ThunkBase;
|
||||
UINT32 I;
|
||||
UINT32 Addr;
|
||||
INT32 Size;
|
||||
INT32 ThunkSize;
|
||||
EFI_STATUS Status;
|
||||
|
||||
//
|
||||
// Check alignment of pointer to EBC code
|
||||
//
|
||||
if ((UINT32) (UINTN) EbcEntryPoint & 0x01) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Size = EBC_THUNK_SIZE;
|
||||
ThunkSize = Size;
|
||||
|
||||
Status = gBS->AllocatePool (
|
||||
EfiBootServicesData,
|
||||
Size,
|
||||
(VOID *) &Ptr
|
||||
);
|
||||
if (Status != EFI_SUCCESS) {
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
//
|
||||
// Print(L"Allocate TH: 0x%X\n", (UINT32)Ptr);
|
||||
//
|
||||
// Save the start address so we can add a pointer to it to a list later.
|
||||
//
|
||||
ThunkBase = Ptr;
|
||||
|
||||
//
|
||||
// Give them the address of our buffer we're going to fix up
|
||||
//
|
||||
*Thunk = (VOID *) Ptr;
|
||||
|
||||
//
|
||||
// Add a magic code here to help the VM recognize the thunk..
|
||||
// mov eax, 0xca112ebc => B8 BC 2E 11 CA
|
||||
//
|
||||
*Ptr = 0xB8;
|
||||
Ptr++;
|
||||
Size--;
|
||||
Addr = (UINT32) 0xCA112EBC;
|
||||
for (I = 0; I < sizeof (Addr); I++) {
|
||||
*Ptr = (UINT8) (UINTN) Addr;
|
||||
Addr >>= 8;
|
||||
Ptr++;
|
||||
Size--;
|
||||
}
|
||||
|
||||
//
|
||||
// Add code bytes to load up a processor register with the EBC entry point.
|
||||
// mov eax, 0xaa55aa55 => B8 55 AA 55 AA
|
||||
// The first 8 bytes of the thunk entry is the address of the EBC
|
||||
// entry point.
|
||||
//
|
||||
*Ptr = 0xB8;
|
||||
Ptr++;
|
||||
Size--;
|
||||
Addr = (UINT32) EbcEntryPoint;
|
||||
for (I = 0; I < sizeof (Addr); I++) {
|
||||
*Ptr = (UINT8) (UINTN) Addr;
|
||||
Addr >>= 8;
|
||||
Ptr++;
|
||||
Size--;
|
||||
}
|
||||
//
|
||||
// Stick in a load of ecx with the address of appropriate VM function.
|
||||
// mov ecx 12345678h => 0xB9 0x78 0x56 0x34 0x12
|
||||
//
|
||||
if (Flags & FLAG_THUNK_ENTRY_POINT) {
|
||||
Addr = (UINT32) (UINTN) ExecuteEbcImageEntryPoint;
|
||||
} else {
|
||||
Addr = (UINT32) (UINTN) EbcInterpret;
|
||||
}
|
||||
|
||||
//
|
||||
// MOV ecx
|
||||
//
|
||||
*Ptr = 0xB9;
|
||||
Ptr++;
|
||||
Size--;
|
||||
for (I = 0; I < sizeof (Addr); I++) {
|
||||
*Ptr = (UINT8) Addr;
|
||||
Addr >>= 8;
|
||||
Ptr++;
|
||||
Size--;
|
||||
}
|
||||
//
|
||||
// Stick in jump opcode bytes for jmp ecx => 0xFF 0xE1
|
||||
//
|
||||
*Ptr = 0xFF;
|
||||
Ptr++;
|
||||
Size--;
|
||||
*Ptr = 0xE1;
|
||||
Size--;
|
||||
|
||||
//
|
||||
// Double check that our defined size is ok (application error)
|
||||
//
|
||||
if (Size < 0) {
|
||||
ASSERT (FALSE);
|
||||
return EFI_BUFFER_TOO_SMALL;
|
||||
}
|
||||
//
|
||||
// Add the thunk to the list for this image. Do this last since the add
|
||||
// function flushes the cache for us.
|
||||
//
|
||||
EbcAddImageThunk (ImageHandle, (VOID *) ThunkBase, ThunkSize);
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
622
EdkModulePkg/Universal/Ebc/Dxe/Ia32/Ia32Math.asm
Normal file
622
EdkModulePkg/Universal/Ebc/Dxe/Ia32/Ia32Math.asm
Normal file
@@ -0,0 +1,622 @@
|
||||
TITLE Ia32math.asm: Generic math routines for EBC interpreter running on IA32 processor
|
||||
|
||||
;------------------------------------------------------------------------------
|
||||
;
|
||||
; Copyright (c) 2006, 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.
|
||||
;
|
||||
; Module Name:
|
||||
;
|
||||
; Ia32math.asm
|
||||
;
|
||||
; Abstract:
|
||||
;
|
||||
; Generic math routines for EBC interpreter running on IA32 processor
|
||||
;
|
||||
;------------------------------------------------------------------------------
|
||||
|
||||
.686P
|
||||
.XMM
|
||||
.MODEL SMALL
|
||||
.CODE
|
||||
|
||||
LeftShiftU64 PROTO C Operand: QWORD, CountIn: QWORD
|
||||
RightShiftU64 PROTO C Operand: QWORD, CountIn: QWORD
|
||||
ARightShift64 PROTO C Operand: QWORD, CountIn: QWORD
|
||||
MulU64x64 PROTO C Value1: QWORD, Value2: QWORD, ResultHigh: DWORD
|
||||
MulS64x64 PROTO C Value1: QWORD, Value2: QWORD, ResultHigh: DWORD
|
||||
DivU64x64 PROTO C Dividend: QWORD, Divisor: QWORD, Remainder: DWORD, Error: DWORD
|
||||
DivS64x64 PROTO C Dividend: QWORD, Divisor: QWORD, Remainder: DWORD, Error: DWORD
|
||||
|
||||
|
||||
LeftShiftU64 PROC C Operand: QWORD, CountIn: QWORD
|
||||
|
||||
;------------------------------------------------------------------------------
|
||||
; UINT64
|
||||
; LeftShiftU64 (
|
||||
; IN UINT64 Operand,
|
||||
; IN UINT64 CountIn
|
||||
; )
|
||||
;
|
||||
; Routine Description:
|
||||
;
|
||||
; Left-shift a 64-bit value.
|
||||
;
|
||||
; Arguments:
|
||||
;
|
||||
; Operand - the value to shift
|
||||
; Count - shift count
|
||||
;
|
||||
; Returns:
|
||||
;
|
||||
; Operand << Count
|
||||
;------------------------------------------------------------------------------
|
||||
|
||||
push ecx
|
||||
;
|
||||
; if (CountIn > 63) return 0;
|
||||
;
|
||||
cmp dword ptr CountIn[4], 0
|
||||
jne _LeftShiftU64_Overflow
|
||||
mov ecx, dword ptr CountIn[0]
|
||||
cmp ecx, 63
|
||||
jbe _LeftShiftU64_Calc
|
||||
|
||||
_LeftShiftU64_Overflow:
|
||||
xor eax, eax
|
||||
xor edx, edx
|
||||
jmp _LeftShiftU64_Done
|
||||
|
||||
_LeftShiftU64_Calc:
|
||||
mov eax, dword ptr Operand[0]
|
||||
mov edx, dword ptr Operand[4]
|
||||
|
||||
shld edx, eax, cl
|
||||
shl eax, cl
|
||||
cmp ecx, 32
|
||||
jc short _LeftShiftU64_Done
|
||||
|
||||
mov edx, eax
|
||||
xor eax, eax
|
||||
|
||||
_LeftShiftU64_Done:
|
||||
pop ecx
|
||||
ret
|
||||
|
||||
LeftShiftU64 ENDP
|
||||
|
||||
|
||||
RightShiftU64 PROC C Operand: QWORD, CountIn: QWORD
|
||||
|
||||
;------------------------------------------------------------------------------
|
||||
; UINT64
|
||||
; RightShiftU64 (
|
||||
; IN UINT64 Operand,
|
||||
; IN UINT64 CountIn
|
||||
; )
|
||||
;
|
||||
; Routine Description:
|
||||
;
|
||||
; Right-shift an unsigned 64-bit value.
|
||||
;
|
||||
; Arguments:
|
||||
;
|
||||
; Operand - the value to shift
|
||||
; Count - shift count
|
||||
;
|
||||
; Returns:
|
||||
;
|
||||
; Operand >> Count
|
||||
;------------------------------------------------------------------------------
|
||||
|
||||
push ecx
|
||||
;
|
||||
; if (CountIn > 63) return 0;
|
||||
;
|
||||
cmp dword ptr CountIn[4], 0
|
||||
jne _RightShiftU64_Overflow
|
||||
mov ecx, dword ptr CountIn[0]
|
||||
cmp ecx, 63
|
||||
jbe _RightShiftU64_Calc
|
||||
|
||||
_RightShiftU64_Overflow:
|
||||
xor eax, eax
|
||||
xor edx, edx
|
||||
jmp _RightShiftU64_Done
|
||||
|
||||
_RightShiftU64_Calc:
|
||||
mov eax, dword ptr Operand[0]
|
||||
mov edx, dword ptr Operand[4]
|
||||
|
||||
shrd edx, eax, cl
|
||||
shr eax, cl
|
||||
cmp ecx, 32
|
||||
jc short _RightShiftU64_Done
|
||||
|
||||
mov eax, edx
|
||||
xor edx, edx
|
||||
|
||||
_RightShiftU64_Done:
|
||||
pop ecx
|
||||
ret
|
||||
|
||||
RightShiftU64 ENDP
|
||||
|
||||
|
||||
ARightShift64 PROC C Operand: QWORD, CountIn: QWORD
|
||||
|
||||
;------------------------------------------------------------------------------
|
||||
; INT64
|
||||
; ARightShift64 (
|
||||
; IN INT64 Operand,
|
||||
; IN UINT64 CountIn
|
||||
; )
|
||||
;
|
||||
; Routine Description:
|
||||
;
|
||||
; Arithmatic shift a 64 bit signed value.
|
||||
;
|
||||
; Arguments:
|
||||
;
|
||||
; Operand - the value to shift
|
||||
; Count - shift count
|
||||
;
|
||||
; Returns:
|
||||
;
|
||||
; Operand >> Count
|
||||
;------------------------------------------------------------------------------
|
||||
|
||||
push ecx
|
||||
;
|
||||
; If they exceeded the max shift count, then return either 0 or all F's
|
||||
; depending on the sign bit.
|
||||
;
|
||||
cmp dword ptr CountIn[4], 0
|
||||
jne _ARightShiftU64_Overflow
|
||||
mov ecx, dword ptr CountIn[0]
|
||||
cmp ecx, 63
|
||||
jbe _ARightShiftU64_Calc
|
||||
|
||||
_ARightShiftU64_Overflow:
|
||||
;
|
||||
; Check the sign bit of Operand
|
||||
;
|
||||
bt dword ptr Operand[4], 31
|
||||
jnc _ARightShiftU64_Return_Zero
|
||||
;
|
||||
; return -1
|
||||
;
|
||||
or eax, 0FFFFFFFFh
|
||||
or edx, 0FFFFFFFFh
|
||||
jmp _ARightShiftU64_Done
|
||||
|
||||
_ARightShiftU64_Return_Zero:
|
||||
xor eax, eax
|
||||
xor edx, edx
|
||||
jmp _ARightShiftU64_Done
|
||||
|
||||
_ARightShiftU64_Calc:
|
||||
mov eax, dword ptr Operand[0]
|
||||
mov edx, dword ptr Operand[4]
|
||||
|
||||
shrd eax, edx, cl
|
||||
sar edx, cl
|
||||
cmp ecx, 32
|
||||
jc short _ARightShiftU64_Done
|
||||
|
||||
;
|
||||
; if ecx >= 32, then eax = edx, and edx = sign bit
|
||||
;
|
||||
mov eax, edx
|
||||
sar edx, 31
|
||||
|
||||
_ARightShiftU64_Done:
|
||||
pop ecx
|
||||
ret
|
||||
|
||||
ARightShift64 ENDP
|
||||
|
||||
|
||||
MulU64x64 PROC C Value1: QWORD, Value2: QWORD, ResultHigh: DWORD
|
||||
|
||||
;------------------------------------------------------------------------------
|
||||
; UINT64
|
||||
; MulU64x64 (
|
||||
; UINT64 Value1,
|
||||
; UINT64 Value2,
|
||||
; UINT64 *ResultHigh
|
||||
; )
|
||||
;
|
||||
; Routine Description:
|
||||
;
|
||||
; Multiply two unsigned 64-bit values.
|
||||
;
|
||||
; Arguments:
|
||||
;
|
||||
; Value1 - first value to multiply
|
||||
; Value2 - value to multiply by Value1
|
||||
; ResultHigh - result to flag overflows
|
||||
;
|
||||
; Returns:
|
||||
;
|
||||
; Value1 * Value2
|
||||
; The 128-bit result is the concatenation of *ResultHigh and the return value
|
||||
;------------------------------------------------------------------------------
|
||||
|
||||
push ebx
|
||||
push ecx
|
||||
mov ebx, ResultHigh ; ebx points to the high 4 words of result
|
||||
;
|
||||
; The result consists of four double-words.
|
||||
; Here we assume their names from low to high: dw0, dw1, dw2, dw3
|
||||
;
|
||||
mov eax, dword ptr Value1[0]
|
||||
mul dword ptr Value2[0]
|
||||
push eax ; eax contains final result of dw0, push it
|
||||
mov ecx, edx ; ecx contains partial result of dw1
|
||||
|
||||
mov eax, dword ptr Value1[4]
|
||||
mul dword ptr Value2[0]
|
||||
add ecx, eax ; add eax to partial result of dw1
|
||||
adc edx, 0
|
||||
mov dword ptr [ebx], edx ; lower double-word of ResultHigh contains partial result of dw2
|
||||
|
||||
mov eax, dword ptr Value1[0]
|
||||
mul dword ptr Value2[4]
|
||||
add ecx, eax ; add eax to partial result of dw1
|
||||
push ecx ; ecx contains final result of dw1, push it
|
||||
adc edx, 0
|
||||
mov ecx, edx ; ecx contains partial result of dw2, together with ResultHigh
|
||||
|
||||
mov eax, dword ptr Value1[4]
|
||||
mul dword ptr Value2[4]
|
||||
add ecx, eax ; add eax to partial result of dw2
|
||||
adc edx, 0
|
||||
add dword ptr [ebx], ecx ; lower double-word of ResultHigh contains final result of dw2
|
||||
adc edx, 0
|
||||
mov dword ptr [ebx + 4], edx ; high double-word of ResultHigh contains final result of dw3
|
||||
|
||||
pop edx ; edx contains the final result of dw1
|
||||
pop eax ; edx contains the final result of dw0
|
||||
pop ecx
|
||||
pop ebx
|
||||
ret
|
||||
|
||||
MulU64x64 ENDP
|
||||
|
||||
|
||||
MulS64x64 PROC C Value1: QWORD, Value2: QWORD, ResultHigh: DWORD
|
||||
|
||||
;------------------------------------------------------------------------------
|
||||
; INT64
|
||||
; MulS64x64 (
|
||||
; INT64 Value1,
|
||||
; INT64 Value2,
|
||||
; INT64 *ResultHigh
|
||||
; )
|
||||
;
|
||||
; Routine Description:
|
||||
;
|
||||
; Multiply two signed 64-bit values.
|
||||
;
|
||||
; Arguments:
|
||||
;
|
||||
; Value1 - first value to multiply
|
||||
; Value2 - value to multiply by Value1
|
||||
; ResultHigh - result to flag overflows
|
||||
;
|
||||
; Returns:
|
||||
;
|
||||
; Value1 * Value2
|
||||
; The 128-bit result is the concatenation of *ResultHigh and the return value
|
||||
;------------------------------------------------------------------------------
|
||||
|
||||
push ebx
|
||||
push ecx
|
||||
mov ebx, ResultHigh ; ebx points to the high 4 words of result
|
||||
xor ecx, ecx ; the lowest bit of ecx flags the sign
|
||||
|
||||
mov edx, dword ptr Value1[4]
|
||||
bt edx, 31
|
||||
jnc short _MulS64x64_A_Positive
|
||||
;
|
||||
; a is negative
|
||||
;
|
||||
mov eax, dword ptr Value1[0]
|
||||
not edx
|
||||
not eax
|
||||
add eax, 1
|
||||
adc edx, 0
|
||||
mov dword ptr Value1[0], eax
|
||||
mov dword ptr Value1[4], edx
|
||||
btc ecx, 0
|
||||
|
||||
_MulS64x64_A_Positive:
|
||||
mov edx, dword ptr Value2[4]
|
||||
bt edx, 31
|
||||
jnc short _MulS64x64_B_Positive
|
||||
;
|
||||
; b is negative
|
||||
;
|
||||
mov eax, dword ptr Value2[0]
|
||||
not edx
|
||||
not eax
|
||||
add eax, 1
|
||||
adc edx, 0
|
||||
mov dword ptr Value2[0], eax
|
||||
mov dword ptr Value2[4], edx
|
||||
btc ecx, 0
|
||||
|
||||
_MulS64x64_B_Positive:
|
||||
invoke MulU64x64, Value1, Value2, ResultHigh
|
||||
bt ecx, 0
|
||||
jnc short _MulS64x64_Done
|
||||
;
|
||||
;negate the result
|
||||
;
|
||||
not eax
|
||||
not edx
|
||||
not dword ptr [ebx]
|
||||
not dword ptr [ebx + 4]
|
||||
add eax, 1
|
||||
adc edx, 0
|
||||
adc dword ptr [ebx], 0
|
||||
adc dword ptr [ebx + 4], 0
|
||||
|
||||
_MulS64x64_Done:
|
||||
pop ecx
|
||||
pop ebx
|
||||
ret
|
||||
|
||||
MulS64x64 ENDP
|
||||
|
||||
|
||||
DivU64x64 PROC C Dividend: QWORD, Divisor: QWORD, Remainder: DWORD, Error: DWORD,
|
||||
|
||||
;------------------------------------------------------------------------------
|
||||
; UINT64
|
||||
; DivU64x64 (
|
||||
; IN UINT64 Dividend,
|
||||
; IN UINT64 Divisor,
|
||||
; OUT UINT64 *Remainder OPTIONAL,
|
||||
; OUT UINT32 *Error
|
||||
; )
|
||||
;
|
||||
; Routine Description:
|
||||
;
|
||||
; This routine allows a 64 bit value to be divided with a 64 bit value returns
|
||||
; 64bit result and the Remainder
|
||||
;
|
||||
; Arguments:
|
||||
;
|
||||
; Dividend - dividend
|
||||
; Divisor - divisor
|
||||
; ResultHigh - result to flag overflows
|
||||
; Error - flag for error
|
||||
;
|
||||
; Returns:
|
||||
;
|
||||
; Dividend / Divisor
|
||||
; Remainder = Dividend mod Divisor
|
||||
;------------------------------------------------------------------------------
|
||||
|
||||
push ecx
|
||||
|
||||
mov eax, Error
|
||||
mov dword ptr [eax], 0
|
||||
|
||||
cmp dword ptr Divisor[0], 0
|
||||
jne _DivU64x64_Valid
|
||||
cmp dword ptr Divisor[4], 0
|
||||
jne _DivU64x64_Valid
|
||||
;
|
||||
; the divisor is zero
|
||||
;
|
||||
mov dword ptr [eax], 1
|
||||
cmp Remainder, 0
|
||||
je _DivU64x64_Invalid_Return
|
||||
;
|
||||
; fill the remainder if the pointer is not null
|
||||
;
|
||||
mov eax, Remainder
|
||||
mov dword ptr [eax], 0
|
||||
mov dword ptr [eax + 4], 80000000h
|
||||
|
||||
_DivU64x64_Invalid_Return:
|
||||
xor eax, eax
|
||||
mov edx, 80000000h
|
||||
jmp _DivU64x64_Done
|
||||
|
||||
_DivU64x64_Valid:
|
||||
;
|
||||
; let edx and eax contain the intermediate result of remainder
|
||||
;
|
||||
xor edx, edx
|
||||
xor eax, eax
|
||||
mov ecx, 64
|
||||
|
||||
_DivU64x64_Wend:
|
||||
;
|
||||
; shift dividend left one
|
||||
;
|
||||
shl dword ptr Dividend[0], 1
|
||||
rcl dword ptr Dividend[4], 1
|
||||
;
|
||||
; rotate intermediate result of remainder left one
|
||||
;
|
||||
rcl eax, 1
|
||||
rcl edx, 1
|
||||
|
||||
cmp edx, dword ptr Divisor[4]
|
||||
ja _DivU64x64_Sub_Divisor
|
||||
jb _DivU64x64_Cont
|
||||
cmp eax, dword ptr Divisor[0]
|
||||
jb _DivU64x64_Cont
|
||||
|
||||
_DivU64x64_Sub_Divisor:
|
||||
;
|
||||
; If intermediate result of remainder is larger than
|
||||
; or equal to divisor, then set the lowest bit of dividend,
|
||||
; and subtract divisor from intermediate remainder
|
||||
;
|
||||
bts dword ptr Dividend[0], 0
|
||||
sub eax, dword ptr Divisor[0]
|
||||
sbb edx, dword ptr Divisor[4]
|
||||
|
||||
_DivU64x64_Cont:
|
||||
loop _DivU64x64_Wend
|
||||
|
||||
cmp Remainder, 0
|
||||
je _DivU64x64_Assign
|
||||
mov ecx, Remainder
|
||||
mov dword ptr [ecx], eax
|
||||
mov dword ptr [ecx + 4], edx
|
||||
|
||||
_DivU64x64_Assign:
|
||||
mov eax, dword ptr Dividend[0]
|
||||
mov edx, dword ptr Dividend[4]
|
||||
|
||||
_DivU64x64_Done:
|
||||
pop ecx
|
||||
ret
|
||||
|
||||
DivU64x64 ENDP
|
||||
|
||||
DivS64x64 PROC C Dividend: QWORD, Divisor: QWORD, Remainder: DWORD, Error: DWORD,
|
||||
|
||||
;------------------------------------------------------------------------------
|
||||
; INT64
|
||||
; DivU64x64 (
|
||||
; IN INT64 Dividend,
|
||||
; IN INT64 Divisor,
|
||||
; OUT UINT64 *Remainder OPTIONAL,
|
||||
; OUT UINT32 *Error
|
||||
; )
|
||||
;
|
||||
; Routine Description:
|
||||
;
|
||||
; This routine allows a 64 bit signed value to be divided with a 64 bit
|
||||
; signed value returns 64bit result and the Remainder.
|
||||
;
|
||||
; Arguments:
|
||||
;
|
||||
; Dividend - dividend
|
||||
; Divisor - divisor
|
||||
; ResultHigh - result to flag overflows
|
||||
; Error - flag for error
|
||||
;
|
||||
; Returns:
|
||||
;
|
||||
; Dividend / Divisor
|
||||
; Remainder = Dividend mod Divisor
|
||||
;------------------------------------------------------------------------------
|
||||
|
||||
push ecx
|
||||
|
||||
mov eax, Error
|
||||
mov dword ptr [eax], 0
|
||||
|
||||
cmp dword ptr Divisor[0], 0
|
||||
jne _DivS64x64_Valid
|
||||
cmp dword ptr Divisor[4], 0
|
||||
jne _DivS64x64_Valid
|
||||
;
|
||||
; the divisor is zero
|
||||
;
|
||||
mov dword ptr [eax], 1
|
||||
cmp Remainder, 0
|
||||
je _DivS64x64_Invalid_Return
|
||||
;
|
||||
; fill the remainder if the pointer is not null
|
||||
;
|
||||
mov eax, Remainder
|
||||
mov dword ptr [eax], 0
|
||||
mov dword ptr [eax + 4], 80000000h
|
||||
|
||||
_DivS64x64_Invalid_Return:
|
||||
xor eax, eax
|
||||
mov edx, 80000000h
|
||||
jmp _DivS64x64_Done
|
||||
|
||||
_DivS64x64_Valid:
|
||||
;
|
||||
; The lowest bit of ecx flags the sign of quotient,
|
||||
; The seconde lowest bit flags the sign of remainder
|
||||
;
|
||||
xor ecx, ecx
|
||||
|
||||
mov edx, dword ptr Dividend[4]
|
||||
bt edx, 31
|
||||
jnc short _DivS64x64_Dividend_Positive
|
||||
;
|
||||
; dividend is negative
|
||||
;
|
||||
mov eax, dword ptr Dividend[0]
|
||||
not edx
|
||||
not eax
|
||||
add eax, 1
|
||||
adc edx, 0
|
||||
mov dword ptr Dividend[0], eax
|
||||
mov dword ptr Dividend[4], edx
|
||||
;
|
||||
; set both the flags for signs of quotient and remainder
|
||||
;
|
||||
btc ecx, 0
|
||||
btc ecx, 1
|
||||
|
||||
_DivS64x64_Dividend_Positive:
|
||||
mov edx, dword ptr Divisor[4]
|
||||
bt edx, 31
|
||||
jnc short _DivS64x64_Divisor_Positive
|
||||
;
|
||||
; divisor is negative
|
||||
;
|
||||
mov eax, dword ptr Divisor[0]
|
||||
not edx
|
||||
not eax
|
||||
add eax, 1
|
||||
adc edx, 0
|
||||
mov dword ptr Divisor[0], eax
|
||||
mov dword ptr Divisor[4], edx
|
||||
;
|
||||
; just complement the flag for sign of quotient
|
||||
;
|
||||
btc ecx, 0
|
||||
|
||||
_DivS64x64_Divisor_Positive:
|
||||
invoke DivU64x64, Dividend, Divisor, Remainder, Error
|
||||
bt ecx, 0
|
||||
jnc short _DivS64x64_Remainder
|
||||
;
|
||||
; negate the quotient
|
||||
;
|
||||
not eax
|
||||
not edx
|
||||
add eax, 1
|
||||
adc edx, 0
|
||||
|
||||
_DivS64x64_Remainder:
|
||||
bt ecx, 1
|
||||
jnc short _DivS64x64_Done
|
||||
;
|
||||
; negate the remainder
|
||||
;
|
||||
mov ecx, remainder
|
||||
not dword ptr [ecx]
|
||||
not dword ptr [ecx + 4]
|
||||
add dword ptr [ecx], 1
|
||||
adc dword ptr [ecx + 4], 0
|
||||
|
||||
_DivS64x64_Done:
|
||||
pop ecx
|
||||
ret
|
||||
|
||||
DivS64x64 ENDP
|
||||
|
||||
END
|
Reference in New Issue
Block a user