summaryrefslogtreecommitdiffstats
path: root/MdeModulePkg/Universal/EbcDxe/AArch64/EbcLowLevel.S
blob: a84d5b3305b81e839929430873782306abc4a254 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
///** @file
//
//  This code provides low level routines that support the Virtual Machine
//  for option ROMs.
//
//  Copyright (c) 2016, Linaro, Ltd. All rights reserved.<BR>
//  Copyright (c) 2015, The Linux Foundation. All rights reserved.<BR>
//  Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>
//
//  SPDX-License-Identifier: BSD-2-Clause-Patent
//
//**/

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 AArch64, we copy the VM stack into the main stack and then pop
// the first 8 arguments off according to the AArch64 Procedure Call Standard
// On return, we restore the stack pointer to its original location.
//
//****************************************************************************
// UINTN EbcLLCALLEXNative(UINTN FuncAddr, UINTN NewStackPointer, VOID *FramePtr)
ASM_PFX(EbcLLCALLEXNative):
    mov     x8, x0                 // Preserve x0
    mov     x9, x1                 // Preserve x1

    //
    // If the EBC stack frame is smaller than or equal to 64 bytes, we know there
    // are no stacked arguments #9 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     x3, x2, x1              // Length = NewStackPointer - FramePtr
    cmp     x3, #64
    b.gt    1f

    //
    // While probably harmless in practice, we should not access the VM stack
    // outside of the interval [NewStackPointer, FramePtr), which means we
    // should not blindly fill all 8 argument registers with VM stack data.
    // So instead, calculate how many argument registers we can fill based on
    // the size of the VM stack frame, and skip the remaining ones.
    //
    adr     x0, 0f                  // Take address of 'br' instruction below
    bic     x3, x3, #7              // Ensure correct alignment
    sub     x0, x0, x3, lsr #1      // Subtract 4 bytes for each arg to unstack
    br      x0                      // Skip remaining argument registers

    ldr     x7, [x9, #56]           // Call with 8 arguments
    ldr     x6, [x9, #48]           //  |
    ldr     x5, [x9, #40]           //  |
    ldr     x4, [x9, #32]           //  |
    ldr     x3, [x9, #24]           //  |
    ldr     x2, [x9, #16]           //  |
    ldr     x1, [x9, #8]            //  V
    ldr     x0, [x9]                // Call with 1 argument

0:  br      x8                      // Call with no arguments

    //
    // More than 64 bytes: we need to build the full native stack frame and copy
    // the part of the VM stack exceeding 64 bytes (which may contain stacked
    // arguments) to the native stack
    //
1:  stp     x29, x30, [sp, #-16]!
    mov     x29, sp

    //
    // Ensure that the stack pointer remains 16 byte aligned,
    // even if the size of the VM stack frame is not a multiple of 16
    //
    add     x1, x1, #64             // Skip over [potential] reg params
    tbz     x3, #3, 2f              // Multiple of 16?
    ldr     x4, [x2, #-8]!          // No? Then push one word
    str     x4, [sp, #-16]!         // ... but use two slots
    b       3f

2:  ldp     x4, x5, [x2, #-16]!
    stp     x4, x5, [sp, #-16]!
3:  cmp     x2, x1
    b.gt    2b

    ldp     x0, x1, [x9]
    ldp     x2, x3, [x9, #16]
    ldp     x4, x5, [x9, #32]
    ldp     x6, x7, [x9, #48]

    blr     x8

    mov     sp, x29
    ldp     x29, x30, [sp], #16
    ret

//****************************************************************************
// EbcLLEbcInterpret
//
// This function is called by the thunk code to handle an Native to EBC call
// This can handle up to 16 arguments (1-8 on in x0-x7, 9-16 are on the stack)
// x16 contains the Entry point that will be the first stacked argument when
// EBCInterpret is called.
//
//****************************************************************************
ASM_PFX(EbcLLEbcInterpret):
    stp     x29, x30, [sp, #-16]!
    mov     x29, sp

    // push the entry point and the address of args #9 - #16 onto the stack
    add     x17, sp, #16
    stp     x16, x17, [sp, #-16]!

    // call C-code
    bl      ASM_PFX(EbcInterpret)

    add     sp, sp, #16
    ldp     x29, x30, [sp], #16
    ret

//****************************************************************************
// 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.
//
//****************************************************************************
ASM_PFX(EbcLLExecuteEbcImageEntryPoint):
    mov     x2, x16

    // tail call to C code
    b       ASM_PFX(ExecuteEbcImageEntryPoint)

//****************************************************************************
// mEbcInstructionBufferTemplate
//****************************************************************************
    .section    ".rodata", "a"
    .align      3
ASM_PFX(mEbcInstructionBufferTemplate):
    adr     x17, 0f
    ldp     x16, x17, [x17]
    br      x17

    //
    // Add a magic code here to help the VM recognize the thunk.
    //
    hlt     #0xEBC

0:  .quad   0   // EBC_ENTRYPOINT_SIGNATURE
    .quad   0   // EBC_LL_EBC_ENTRYPOINT_SIGNATURE