| /* Copyright (c) 2009, 2010, 2011, 2012 ARM Ltd. |
| |
| Permission is hereby granted, free of charge, to any person obtaining |
| a copy of this software and associated documentation files (the |
| ``Software''), to deal in the Software without restriction, including |
| without limitation the rights to use, copy, modify, merge, publish, |
| distribute, sublicense, and/or sell copies of the Software, and to |
| permit persons to whom the Software is furnished to do so, subject to |
| the following conditions: |
| |
| The above copyright notice and this permission notice shall be |
| included in all copies or substantial portions of the Software. |
| |
| THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, |
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
| IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
| CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
| TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
| SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
| |
| #define LIBFFI_ASM |
| #include <fficonfig.h> |
| #include <ffi.h> |
| |
| #ifdef HAVE_MACHINE_ASM_H |
| #include <machine/asm.h> |
| #else |
| #ifdef __USER_LABEL_PREFIX__ |
| #define CONCAT1(a, b) CONCAT2(a, b) |
| #define CONCAT2(a, b) a ## b |
| |
| /* Use the right prefix for global labels. */ |
| #define CNAME(x) CONCAT1 (__USER_LABEL_PREFIX__, x) |
| #else |
| #define CNAME(x) x |
| #endif |
| #endif |
| |
| #define cfi_adjust_cfa_offset(off) .cfi_adjust_cfa_offset off |
| #define cfi_rel_offset(reg, off) .cfi_rel_offset reg, off |
| #define cfi_restore(reg) .cfi_restore reg |
| #define cfi_def_cfa_register(reg) .cfi_def_cfa_register reg |
| |
| .text |
| .globl CNAME(ffi_call_SYSV) |
| #ifdef __ELF__ |
| .type CNAME(ffi_call_SYSV), #function |
| #endif |
| #ifdef __APPLE__ |
| .align 2 |
| #endif |
| |
| /* ffi_call_SYSV() |
| |
| Create a stack frame, setup an argument context, call the callee |
| and extract the result. |
| |
| The maximum required argument stack size is provided, |
| ffi_call_SYSV() allocates that stack space then calls the |
| prepare_fn to populate register context and stack. The |
| argument passing registers are loaded from the register |
| context and the callee called, on return the register passing |
| register are saved back to the context. Our caller will |
| extract the return value from the final state of the saved |
| register context. |
| |
| Prototype: |
| |
| extern unsigned |
| ffi_call_SYSV (void (*)(struct call_context *context, unsigned char *, |
| extended_cif *), |
| struct call_context *context, |
| extended_cif *, |
| size_t required_stack_size, |
| void (*fn)(void)); |
| |
| Therefore on entry we have: |
| |
| x0 prepare_fn |
| x1 &context |
| x2 &ecif |
| x3 bytes |
| x4 fn |
| |
| This function uses the following stack frame layout: |
| |
| == |
| saved x30(lr) |
| x29(fp)-> saved x29(fp) |
| saved x24 |
| saved x23 |
| saved x22 |
| sp' -> saved x21 |
| ... |
| sp -> (constructed callee stack arguments) |
| == |
| |
| Voila! */ |
| |
| #define ffi_call_SYSV_FS (8 * 4) |
| |
| .cfi_startproc |
| CNAME(ffi_call_SYSV): |
| stp x29, x30, [sp, #-16]! |
| cfi_adjust_cfa_offset (16) |
| cfi_rel_offset (x29, 0) |
| cfi_rel_offset (x30, 8) |
| |
| mov x29, sp |
| cfi_def_cfa_register (x29) |
| sub sp, sp, #ffi_call_SYSV_FS |
| |
| stp x21, x22, [sp, #0] |
| cfi_rel_offset (x21, 0 - ffi_call_SYSV_FS) |
| cfi_rel_offset (x22, 8 - ffi_call_SYSV_FS) |
| |
| stp x23, x24, [sp, #16] |
| cfi_rel_offset (x23, 16 - ffi_call_SYSV_FS) |
| cfi_rel_offset (x24, 24 - ffi_call_SYSV_FS) |
| |
| mov x21, x1 |
| mov x22, x2 |
| mov x24, x4 |
| |
| /* Allocate the stack space for the actual arguments, many |
| arguments will be passed in registers, but we assume |
| worst case and allocate sufficient stack for ALL of |
| the arguments. */ |
| sub sp, sp, x3 |
| |
| /* unsigned (*prepare_fn) (struct call_context *context, |
| unsigned char *stack, extended_cif *ecif); |
| */ |
| mov x23, x0 |
| mov x0, x1 |
| mov x1, sp |
| /* x2 already in place */ |
| blr x23 |
| |
| /* Preserve the flags returned. */ |
| mov x23, x0 |
| |
| /* Figure out if we should touch the vector registers. */ |
| tbz x23, #AARCH64_FFI_WITH_V_BIT, 1f |
| |
| /* Load the vector argument passing registers. */ |
| ldp q0, q1, [x21, #8*32 + 0] |
| ldp q2, q3, [x21, #8*32 + 32] |
| ldp q4, q5, [x21, #8*32 + 64] |
| ldp q6, q7, [x21, #8*32 + 96] |
| 1: |
| /* Load the core argument passing registers. */ |
| ldp x0, x1, [x21, #0] |
| ldp x2, x3, [x21, #16] |
| ldp x4, x5, [x21, #32] |
| ldp x6, x7, [x21, #48] |
| |
| /* Don't forget x8 which may be holding the address of a return buffer. |
| */ |
| ldr x8, [x21, #8*8] |
| |
| blr x24 |
| |
| /* Save the core argument passing registers. */ |
| stp x0, x1, [x21, #0] |
| stp x2, x3, [x21, #16] |
| stp x4, x5, [x21, #32] |
| stp x6, x7, [x21, #48] |
| |
| /* Note nothing useful ever comes back in x8! */ |
| |
| /* Figure out if we should touch the vector registers. */ |
| tbz x23, #AARCH64_FFI_WITH_V_BIT, 1f |
| |
| /* Save the vector argument passing registers. */ |
| stp q0, q1, [x21, #8*32 + 0] |
| stp q2, q3, [x21, #8*32 + 32] |
| stp q4, q5, [x21, #8*32 + 64] |
| stp q6, q7, [x21, #8*32 + 96] |
| 1: |
| /* All done, unwind our stack frame. */ |
| ldp x21, x22, [x29, # - ffi_call_SYSV_FS] |
| cfi_restore (x21) |
| cfi_restore (x22) |
| |
| ldp x23, x24, [x29, # - ffi_call_SYSV_FS + 16] |
| cfi_restore (x23) |
| cfi_restore (x24) |
| |
| mov sp, x29 |
| cfi_def_cfa_register (sp) |
| |
| ldp x29, x30, [sp], #16 |
| cfi_adjust_cfa_offset (-16) |
| cfi_restore (x29) |
| cfi_restore (x30) |
| |
| ret |
| |
| .cfi_endproc |
| #ifdef __ELF__ |
| .size CNAME(ffi_call_SYSV), .-CNAME(ffi_call_SYSV) |
| #endif |
| |
| #define ffi_closure_SYSV_FS (8 * 2 + AARCH64_CALL_CONTEXT_SIZE) |
| |
| /* ffi_closure_SYSV |
| |
| Closure invocation glue. This is the low level code invoked directly by |
| the closure trampoline to setup and call a closure. |
| |
| On entry x17 points to a struct trampoline_data, x16 has been clobbered |
| all other registers are preserved. |
| |
| We allocate a call context and save the argument passing registers, |
| then invoked the generic C ffi_closure_SYSV_inner() function to do all |
| the real work, on return we load the result passing registers back from |
| the call context. |
| |
| On entry |
| |
| extern void |
| ffi_closure_SYSV (struct trampoline_data *); |
| |
| struct trampoline_data |
| { |
| UINT64 *ffi_closure; |
| UINT64 flags; |
| }; |
| |
| This function uses the following stack frame layout: |
| |
| == |
| saved x30(lr) |
| x29(fp)-> saved x29(fp) |
| saved x22 |
| saved x21 |
| ... |
| sp -> call_context |
| == |
| |
| Voila! */ |
| |
| .text |
| .globl CNAME(ffi_closure_SYSV) |
| #ifdef __APPLE__ |
| .align 2 |
| #endif |
| .cfi_startproc |
| CNAME(ffi_closure_SYSV): |
| stp x29, x30, [sp, #-16]! |
| cfi_adjust_cfa_offset (16) |
| cfi_rel_offset (x29, 0) |
| cfi_rel_offset (x30, 8) |
| |
| mov x29, sp |
| cfi_def_cfa_register (x29) |
| |
| sub sp, sp, #ffi_closure_SYSV_FS |
| |
| stp x21, x22, [x29, #-16] |
| cfi_rel_offset (x21, -16) |
| cfi_rel_offset (x22, -8) |
| |
| /* Load x21 with &call_context. */ |
| mov x21, sp |
| /* Preserve our struct trampoline_data * */ |
| mov x22, x17 |
| |
| /* Save the rest of the argument passing registers. */ |
| stp x0, x1, [x21, #0] |
| stp x2, x3, [x21, #16] |
| stp x4, x5, [x21, #32] |
| stp x6, x7, [x21, #48] |
| /* Don't forget we may have been given a result scratch pad address. |
| */ |
| str x8, [x21, #64] |
| |
| /* Figure out if we should touch the vector registers. */ |
| ldr x0, [x22, #8] |
| tbz x0, #AARCH64_FFI_WITH_V_BIT, 1f |
| |
| /* Save the argument passing vector registers. */ |
| stp q0, q1, [x21, #8*32 + 0] |
| stp q2, q3, [x21, #8*32 + 32] |
| stp q4, q5, [x21, #8*32 + 64] |
| stp q6, q7, [x21, #8*32 + 96] |
| 1: |
| /* Load &ffi_closure.. */ |
| ldr x0, [x22, #0] |
| mov x1, x21 |
| /* Compute the location of the stack at the point that the |
| trampoline was called. */ |
| add x2, x29, #16 |
| |
| bl CNAME(ffi_closure_SYSV_inner) |
| |
| /* Figure out if we should touch the vector registers. */ |
| ldr x0, [x22, #8] |
| tbz x0, #AARCH64_FFI_WITH_V_BIT, 1f |
| |
| /* Load the result passing vector registers. */ |
| ldp q0, q1, [x21, #8*32 + 0] |
| ldp q2, q3, [x21, #8*32 + 32] |
| ldp q4, q5, [x21, #8*32 + 64] |
| ldp q6, q7, [x21, #8*32 + 96] |
| 1: |
| /* Load the result passing core registers. */ |
| ldp x0, x1, [x21, #0] |
| ldp x2, x3, [x21, #16] |
| ldp x4, x5, [x21, #32] |
| ldp x6, x7, [x21, #48] |
| /* Note nothing useful is returned in x8. */ |
| |
| /* We are done, unwind our frame. */ |
| ldp x21, x22, [x29, #-16] |
| cfi_restore (x21) |
| cfi_restore (x22) |
| |
| mov sp, x29 |
| cfi_def_cfa_register (sp) |
| |
| ldp x29, x30, [sp], #16 |
| cfi_adjust_cfa_offset (-16) |
| cfi_restore (x29) |
| cfi_restore (x30) |
| |
| ret |
| .cfi_endproc |
| #ifdef __ELF__ |
| .size CNAME(ffi_closure_SYSV), .-CNAME(ffi_closure_SYSV) |
| #endif |