/* $Id: xptcinvoke_arm64_vbox.cpp $ */ /** @file * XPCOM - Implementation XPTC_InvokeByIndex for arm64. */ /* * Copyright (C) 2021-2023 Oracle and/or its affiliates. * * This file is part of VirtualBox base platform packages, as * available from https://www.virtualbox.org. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, in version 3 of the * License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * SPDX-License-Identifier: GPL-3.0-only */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #include "xptcprivate.h" #include #include #include /********************************************************************************************************************************* * Defined Constants And Macros * *********************************************************************************************************************************/ #define NUM_ARGS_IN_GPRS 8 /**< Number of arguments passed in general purpose registers (starting with x0). */ #define NUM_ARGS_IN_FPRS 8 /**< Number of arguments passed in floating point registers (starting with d0). */ #define MY_MAX_ARGS 64 /**< Limit ourselves to 64 arguments. */ AssertCompileMemberOffset(nsXPTCVariant, val, 0); extern "C" __attribute__((naked)) nsresult arm64AsmInvoker(uintptr_t pfnMethod /*x0*/, uint32_t cParams /*w1*/, nsXPTCVariant *paParams /*x2*/, uint64_t cbStack /*x3*/, uint8_t *acbStackArgs /*x4*/, uint64_t *pauGprArgs /*x5*/, uint64_t *pauFprArgs /*x6*/, uint32_t cFprArgs /*x7*/) { __asm__ __volatile__( /* Prologue - create the frame. */ "\ sub sp, sp, 16 \n\ stp x29, x30, [sp] \n\ mov x29, sp \n\ .cfi_def_cfa x29, 16 \n\ .cfi_rel_offset x30, -8 \n\ .cfi_rel_offset x29, -16 \n\ \n\ " /* Move pfnMethod to x16 and pauGprArgs to x7 free up x0 and x5: */ "\ mov x16, x0 \n\ mov x17, x5 \n\ \n\ " /* Load FPU registers first so we free up x6 & x7 early: */ "\ cmp w7, #0 \n\ b.eq Lno_fprs\n\ ldp d0, d1, [x6] \n\ ldp d2, d3, [x6, #16] \n\ ldp d4, d5, [x6, #32] \n\ ldp d6, d7, [x6, #48] \n\ Lno_fprs:\n\ \n\ " /* Do argument passing by stack (if any). We align the stack to 16 bytes. */ "\ cmp x3, #0 \n\ beq Lno_stack_args \n\ sub x3, sp, x3 \n\ bic x3, x3, #15 \n\ mov sp, x3 \n\ Lnext_parameter: \n\ ldrb w7, [x4] \n\ cmp w7, #0 \n\ beq Ladvance\n\ \n\ cmp w7, #4 \n\ bgt Lstore_64bits\n\ cmp w7, #1 \n\ beq Lstore_8bits\n\ cmp w7, #2 \n\ beq Lstore_16bits\n\ \n\ Lstore_32bits:\n\ ldr w0, [x2] \n\ add x3, x3, #3 \n\ bic x3, x3, #3 \n\ str w0, [x3] \n" #ifdef RT_OS_DARWIN /* macOS compacts stack usage. */ " add x3, x3, #4 \n" #endif " b Ladvance \n\ \n\ Lstore_8bits:\n\ ldrb w0, [x2] \n\ strb w0, [x3] \n" #ifdef RT_OS_DARWIN /* macOS compacts stack usage. */ " add x3, x3, #1 \n" #endif " b Ladvance \n\ \n\ Lstore_16bits:\n\ ldrh w0, [x2] \n\ add x3, x3, #1 \n\ bic x3, x3, #1 \n\ strh w0, [x3] \n" #ifdef RT_OS_DARWIN /* macOS compacts stack usage. */ " add x3, x3, #2 \n" #endif " b Ladvance \n\ \n\ Lstore_64bits_ptr:\n\ ldr x0, [x2, %[offPtrInXPTCVariant]] \n\ b Lstore_64bits_common \n\ Lstore_64bits:\n\ tst w7, #0x80 \n\ bne Lstore_64bits_ptr \n\ ldr x0, [x2] \n\ Lstore_64bits_common:\n\ add x3, x3, #7 \n\ bic x3, x3, #7 \n\ str x0, [x3] \n" #ifdef RT_OS_DARWIN /* macOS compacts stack usage. */ " add x3, x3, #8 \n" #endif "\n\ Ladvance:\n" #ifndef RT_OS_DARWIN /* macOS compacts stack usage. */ " add x3, x3, #8 \n" #endif " add x4, x4, #1 \n\ add x2, x2, %[cbXPTCVariant] \n\ sub w1, w1, #1 \n\ cmp w1, #0 \n\ bne Lnext_parameter \n\ \n\ " /* reserve stack space for the integer and floating point registers and save them: */ "\ Lno_stack_args: \n\ \n\ " /* Load general purpose argument registers: */ "\ ldp x0, x1, [x17] \n\ ldp x2, x3, [x17, #16] \n\ ldp x4, x5, [x17, #32] \n\ ldp x6, x7, [x17, #48] \n\ \n\ " /* Make the call: */ "\ blr x16 \n\ \n\ " /* Epilogue (clang does not emit the .cfi's here, so drop them too?): */ "\ mov sp, x29 \n\ ldp x29, x30, [sp] \n\ add sp, sp, #16 \n\ .cfi_def_cfa sp, 0 \n\ .cfi_restore x29 \n\ .cfi_restore x30 \n\ ret \n\ " : : [cbXPTCVariant] "i" (sizeof(nsXPTCVariant)) , [offPtrInXPTCVariant] "i" (offsetof(nsXPTCVariant, ptr)) :); } XPTC_PUBLIC_API(nsresult) XPTC_InvokeByIndex(nsISupports *pThis, PRUint32 idxMethod, PRUint32 cParams, nsXPTCVariant *paParams) { AssertMsgReturn(cParams <= MY_MAX_ARGS, ("cParams=%#x idxMethod=%#x\n", cParams, idxMethod), NS_ERROR_UNEXPECTED); /* * Prepare */ uint64_t auGprArgs[NUM_ARGS_IN_GPRS] = {0}; uint64_t auFprArgs[NUM_ARGS_IN_GPRS] = {0}; uint8_t acbStackArgs[MY_MAX_ARGS]; /* The number of value bytes to copy onto the stack. Zero if in register. */ uint32_t cbStackArgs = 0; uint32_t cFprArgs = 0; uint32_t cGprArgs = 0; /* First argument is always 'pThis'. The 'pThis' argument is not accounted for in cParams or acbStackArgs. */ auGprArgs[cGprArgs++] = (uintptr_t)pThis; /* Do the other arguments. */ for (PRUint32 i = 0; i < cParams; i++) { if (paParams[i].IsPtrData()) { if (cGprArgs < NUM_ARGS_IN_GPRS) { auGprArgs[cGprArgs++] = (uintptr_t)paParams[i].ptr; acbStackArgs[i] = 0; } else { acbStackArgs[i] = sizeof(paParams[i].ptr) | UINT8_C(0x80); #ifdef RT_OS_DARWIN /* macOS compacts stack usage. */ cbStackArgs = RT_ALIGN_32(cbStackArgs, sizeof(paParams[i].ptr)) + sizeof(paParams[i].ptr); #else cbStackArgs += sizeof(uint64_t); #endif } } else { if ( paParams[i].type != nsXPTType::T_FLOAT && paParams[i].type != nsXPTType::T_DOUBLE) { if (cGprArgs < NUM_ARGS_IN_GPRS) { switch (paParams[i].type) { case nsXPTType::T_I8: auGprArgs[cGprArgs++] = paParams[i].val.i8; break; case nsXPTType::T_I16: auGprArgs[cGprArgs++] = paParams[i].val.i16; break; case nsXPTType::T_I32: auGprArgs[cGprArgs++] = paParams[i].val.i32; break; case nsXPTType::T_I64: auGprArgs[cGprArgs++] = paParams[i].val.i64; break; case nsXPTType::T_U8: auGprArgs[cGprArgs++] = paParams[i].val.u8; break; case nsXPTType::T_U16: auGprArgs[cGprArgs++] = paParams[i].val.u16; break; case nsXPTType::T_U32: auGprArgs[cGprArgs++] = paParams[i].val.u32; break; default: case nsXPTType::T_U64: auGprArgs[cGprArgs++] = paParams[i].val.u64; break; case nsXPTType::T_BOOL: auGprArgs[cGprArgs++] = paParams[i].val.b; break; case nsXPTType::T_CHAR: auGprArgs[cGprArgs++] = paParams[i].val.c; break; case nsXPTType::T_WCHAR: auGprArgs[cGprArgs++] = paParams[i].val.wc; break; } acbStackArgs[i] = 0; } else { uint8_t cbStack; switch (paParams[i].type) { case nsXPTType::T_I8: cbStack = sizeof(paParams[i].val.i8); break; case nsXPTType::T_I16: cbStack = sizeof(paParams[i].val.i16); break; case nsXPTType::T_I32: cbStack = sizeof(paParams[i].val.i32); break; case nsXPTType::T_I64: cbStack = sizeof(paParams[i].val.i64); break; case nsXPTType::T_U8: cbStack = sizeof(paParams[i].val.u8); break; case nsXPTType::T_U16: cbStack = sizeof(paParams[i].val.u16); break; case nsXPTType::T_U32: cbStack = sizeof(paParams[i].val.u32); break; default: case nsXPTType::T_U64: cbStack = sizeof(paParams[i].val.u64); break; case nsXPTType::T_BOOL: cbStack = sizeof(paParams[i].val.b); break; case nsXPTType::T_CHAR: cbStack = sizeof(paParams[i].val.c); break; case nsXPTType::T_WCHAR: cbStack = sizeof(paParams[i].val.wc); break; } acbStackArgs[i] = cbStack; #ifdef RT_OS_DARWIN /* macOS compacts stack usage. */ cbStackArgs = RT_ALIGN_32(cbStackArgs, cbStack) + cbStack; #else cbStackArgs += sizeof(uint64_t); #endif } } else if (cFprArgs < NUM_ARGS_IN_FPRS) { AssertCompile(sizeof(paParams[i].val.f) == 4); AssertCompile(sizeof(paParams[i].val.d) == 8); if (paParams[i].type == nsXPTType::T_FLOAT) auFprArgs[cFprArgs++] = paParams[i].val.u32; else auFprArgs[cFprArgs++] = paParams[i].val.u64; acbStackArgs[i] = 0; } else { uint8_t cbStack; if (paParams[i].type == nsXPTType::T_FLOAT) cbStack = sizeof(paParams[i].val.f); else cbStack = sizeof(paParams[i].val.d); acbStackArgs[i] = cbStack; #ifdef RT_OS_DARWIN /* macOS compacts stack usage. */ cbStackArgs = RT_ALIGN_32(cbStackArgs, cbStack) + cbStack; #else cbStackArgs += sizeof(uint64_t); #endif } } } /* * Pass it on to a naked wrapper function that does the nitty gritty work. */ uintptr_t *pauVtable = *(uintptr_t **)pThis; return arm64AsmInvoker(pauVtable[idxMethod], cParams, paParams, cbStackArgs, acbStackArgs, auGprArgs, auFprArgs, cFprArgs); }