///** @file // // Contains low level routines for the Virtual Machine implementation // on an Itanium-based platform. // // Copyright (c) 2006 - 2011, 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. // //**/ .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)