///** @file | |
// | |
// Contains low level routines for the Virtual Machine implementation | |
// on an Itanium-based platform. | |
// | |
// Copyright (c) 2006 - 2008, Intel Corporation. <BR> | |
// 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. | |
// | |
//**/ | |
.file "EbcLowLevel.s" | |
#define PROCEDURE_ENTRY(name) .##text; \ | |
.##type name, @function; \ | |
.##proc name; \ | |
name:: | |
#define PROCEDURE_EXIT(name) .##endp name | |
// Note: use of NESTED_SETUP requires number of locals (l) >= 3 | |
#define NESTED_SETUP(i,l,o,r) \ | |
alloc loc1=ar##.##pfs,i,l,o,r ;\ | |
mov loc0=b0 | |
#define NESTED_RETURN \ | |
mov b0=loc0 ;\ | |
mov ar##.##pfs=loc1 ;;\ | |
br##.##ret##.##dpnt b0;; | |
.type CopyMem, @function; | |
//----------------------------------------------------------------------------- | |
//++ | |
// EbcAsmLLCALLEX | |
// | |
// Implements the low level EBC CALLEX instruction. Sets up the | |
// stack pointer, does the spill of function arguments, and | |
// calls the native function. On return it restores the original | |
// stack pointer and returns to the caller. | |
// | |
// Arguments : | |
// | |
// On Entry : | |
// in0 = Address of native code to call | |
// in1 = New stack pointer | |
// | |
// Return Value: | |
// | |
// As per static calling conventions. | |
// | |
//-- | |
//--------------------------------------------------------------------------- | |
;// void EbcAsmLLCALLEX (UINTN FunctionAddr, UINTN EbcStackPointer) | |
PROCEDURE_ENTRY(EbcAsmLLCALLEX) | |
NESTED_SETUP (2,6,8,0) | |
// NESTED_SETUP uses loc0 and loc1 for context save | |
// | |
// Save a copy of the EBC VM stack pointer | |
// | |
mov r8 = in1;; | |
// | |
// Copy stack arguments from EBC stack into registers. | |
// Assume worst case and copy 8. | |
// | |
ld8 out0 = [r8], 8;; | |
ld8 out1 = [r8], 8;; | |
ld8 out2 = [r8], 8;; | |
ld8 out3 = [r8], 8;; | |
ld8 out4 = [r8], 8;; | |
ld8 out5 = [r8], 8;; | |
ld8 out6 = [r8], 8;; | |
ld8 out7 = [r8], 8;; | |
// | |
// Save the original stack pointer | |
// | |
mov loc2 = r12; | |
// | |
// Save the gp | |
// | |
or loc3 = r1, r0 | |
// | |
// Set the new aligned stack pointer. Reserve space for the required | |
// 16-bytes of scratch area as well. | |
// | |
add r12 = 48, in1 | |
// | |
// Now call the function. Load up the function address from the descriptor | |
// pointed to by in0. Then get the gp from the descriptor at the following | |
// address in the descriptor. | |
// | |
ld8 r31 = [in0], 8;; | |
ld8 r30 = [in0];; | |
mov b1 = r31 | |
mov r1 = r30 | |
(p0) br.call.dptk.many b0 = b1;; | |
// | |
// Restore the original stack pointer and gp | |
// | |
mov r12 = loc2 | |
or r1 = loc3, r0 | |
// | |
// Now return | |
// | |
NESTED_RETURN | |
PROCEDURE_EXIT(EbcAsmLLCALLEX) | |
//----------------------------------------------------------------------------- | |
//++ | |
// EbcLLCALLEXNative | |
// | |
// This function is called to execute an EBC CALLEX instruction. | |
// This instruction requires that we thunk out to external native | |
// code. On return, we restore the stack pointer to its original location. | |
// Destroys no working registers. For IPF, at least 8 register slots | |
// must be allocated on the stack frame to support any number of | |
// arguments beiung passed to the external native function. The | |
// size of the stack frame is FramePtr - EbcSp. If this size is less | |
// than 64-bytes, the amount of stack frame allocated is rounded up | |
// to 64-bytes | |
// | |
// Arguments On Entry : | |
// in0 = CallAddr The function address. | |
// in1 = EbcSp The new EBC stack pointer. | |
// in2 = FramePtr The frame pointer. | |
// | |
// Return Value: | |
// None | |
// | |
// C Function Prototype: | |
// VOID | |
// EFIAPI | |
// EbcLLCALLEXNative ( | |
// IN UINTN CallAddr, | |
// IN UINTN EbcSp, | |
// IN VOID *FramePtr | |
// ); | |
//-- | |
//--------------------------------------------------------------------------- | |
PROCEDURE_ENTRY(EbcLLCALLEXNative) | |
NESTED_SETUP (3,6,3,0) | |
mov loc2 = in2;; // loc2 = in2 = FramePtr | |
mov loc3 = in1;; // loc3 = in1 = EbcSp | |
sub loc2 = loc2, loc3;; // loc2 = loc2 - loc3 = FramePtr - EbcSp | |
mov out2 = loc2;; // out2 = loc2 = FramePtr - EbcSp | |
mov loc4 = 0x40;; // loc4 = 0x40 | |
cmp.leu p6 = out2, loc4;; // IF out2 < loc4 THEN P6=1 ELSE P6=0; IF (FramePtr - EbcSp) < 0x40 THEN P6 = 1 ELSE P6=0 | |
(p6) mov loc2 = loc4;; // IF P6==1 THEN loc2 = loc4 = 0x40 | |
mov loc4 = r12;; // save sp | |
or loc5 = r1, r0 // save gp | |
sub r12 = r12, loc2;; // sp = sp - loc2 = sp - MAX (0x40, FramePtr - EbcSp) | |
and r12 = -0x10, r12 // Round sp down to the nearest 16-byte boundary | |
mov out1 = in1;; // out1 = EbcSp | |
mov out0 = r12;; // out0 = sp | |
adds r12 = -0x8, r12 | |
(p0) br.call.dptk.many b0 = CopyMem;; // CopyMem (sp, EbcSp, (FramePtr - EbcSp)) | |
adds r12 = 0x8, r12 | |
mov out0 = in0;; // out0 = CallAddr | |
mov out1 = r12;; // out1 = sp | |
(p0) br.call.dptk.many b0 = EbcAsmLLCALLEX;; // EbcAsmLLCALLEX (CallAddr, sp) | |
mov r12 = loc4;; // restore sp | |
or r1 = loc5, r0 // restore gp | |
NESTED_RETURN | |
PROCEDURE_EXIT(EbcLLCALLEXNative) | |
// | |
// UINTN EbcLLGetEbcEntryPoint(VOID) | |
// | |
// Description: | |
// Simply return, so that the caller retrieves the return register | |
// contents (R8). That's where the thunk-to-ebc code stuffed the | |
// EBC entry point. | |
// | |
PROCEDURE_ENTRY(EbcLLGetEbcEntryPoint) | |
br.ret.sptk b0 ;; | |
PROCEDURE_EXIT(EbcLLGetEbcEntryPoint) | |
// | |
// INT64 EbcLLGetReturnValue(VOID) | |
// | |
// Description: | |
// This function is called to get the value returned by native code | |
// to EBC. It simply returns because the return value should still | |
// be in the register, so the caller just gets the unmodified value. | |
// | |
PROCEDURE_ENTRY(EbcLLGetReturnValue) | |
br.ret.sptk b0 ;; | |
PROCEDURE_EXIT(EbcLLGetReturnValue) | |
// | |
// UINTN EbcLLGetStackPointer(VOID) | |
// | |
PROCEDURE_ENTRY(EbcLLGetStackPointer) | |
mov r8 = r12 ;; | |
br.ret.sptk b0 ;; | |
br.sptk.few b6 | |
PROCEDURE_EXIT(EbcLLGetStackPointer) | |