mirror of
https://github.com/partic2/libffi
synced 2026-11-10 18:17:19 +00:00
New microblaze support
This commit is contained in:
@@ -0,0 +1,339 @@
|
||||
/* -----------------------------------------------------------------------
|
||||
ffi.c - Copyright (c) 2012, 2013 Xilinx, Inc
|
||||
|
||||
MicroBlaze Foreign Function Interface
|
||||
|
||||
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.
|
||||
----------------------------------------------------------------------- */
|
||||
|
||||
#include <ffi.h>
|
||||
#include <ffi_common.h>
|
||||
|
||||
extern void ffi_call_SYSV(void (*)(void*, extended_cif*), extended_cif*,
|
||||
unsigned int, unsigned int, unsigned int*, void (*fn)(void),
|
||||
unsigned int, unsigned int);
|
||||
|
||||
extern void ffi_closure_SYSV(void);
|
||||
|
||||
#define WORD_SIZE sizeof(unsigned int)
|
||||
#define ARGS_REGISTER_SIZE (WORD_SIZE * 6)
|
||||
#define WORD_ALIGN(x) ALIGN(x, WORD_SIZE)
|
||||
|
||||
/* ffi_prep_args is called by the assembly routine once stack space
|
||||
has been allocated for the function's arguments */
|
||||
void ffi_prep_args(void* stack, extended_cif* ecif)
|
||||
{
|
||||
unsigned int i;
|
||||
ffi_type** p_arg;
|
||||
void** p_argv;
|
||||
void* stack_args_p = stack;
|
||||
|
||||
p_argv = ecif->avalue;
|
||||
|
||||
if (ecif == NULL || ecif->cif == NULL) {
|
||||
return; /* no description to prepare */
|
||||
}
|
||||
|
||||
if ((ecif->cif->rtype != NULL) &&
|
||||
(ecif->cif->rtype->type == FFI_TYPE_STRUCT))
|
||||
{
|
||||
/* if return type is a struct which is referenced on the stack/reg5,
|
||||
* by a pointer. Stored the return value pointer in r5.
|
||||
*/
|
||||
char* addr = stack_args_p;
|
||||
memcpy(addr, &(ecif->rvalue), WORD_SIZE);
|
||||
stack_args_p += WORD_SIZE;
|
||||
}
|
||||
|
||||
if (ecif->avalue == NULL) {
|
||||
return; /* no arguments to prepare */
|
||||
}
|
||||
|
||||
for (i = 0, p_arg = ecif->cif->arg_types; i < ecif->cif->nargs;
|
||||
i++, p_arg++)
|
||||
{
|
||||
size_t size = (*p_arg)->size;
|
||||
int type = (*p_arg)->type;
|
||||
void* value = p_argv[i];
|
||||
char* addr = stack_args_p;
|
||||
int aligned_size = WORD_ALIGN(size);
|
||||
|
||||
/* force word alignment on the stack */
|
||||
stack_args_p += aligned_size;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case FFI_TYPE_UINT8:
|
||||
*(unsigned int *)addr = (unsigned int)*(UINT8*)(value);
|
||||
break;
|
||||
case FFI_TYPE_SINT8:
|
||||
*(signed int *)addr = (signed int)*(SINT8*)(value);
|
||||
break;
|
||||
case FFI_TYPE_UINT16:
|
||||
*(unsigned int *)addr = (unsigned int)*(UINT16*)(value);
|
||||
break;
|
||||
case FFI_TYPE_SINT16:
|
||||
*(signed int *)addr = (signed int)*(SINT16*)(value);
|
||||
break;
|
||||
case FFI_TYPE_STRUCT:
|
||||
#if __BIG_ENDIAN__
|
||||
/*
|
||||
* MicroBlaze toolchain appears to emit:
|
||||
* bsrli r5, r5, 8 (caller)
|
||||
* ...
|
||||
* <branch to callee>
|
||||
* ...
|
||||
* bslli r5, r5, 8 (callee)
|
||||
*
|
||||
* For structs like "struct a { uint8_t a[3]; };", when passed
|
||||
* by value.
|
||||
*
|
||||
* Structs like "struct b { uint16_t a; };" are also expected
|
||||
* to be packed strangely in registers.
|
||||
*
|
||||
* This appears to be because the microblaze toolchain expects
|
||||
* "struct b == uint16_t", which is only any issue for big
|
||||
* endian.
|
||||
*
|
||||
* The following is a work around for big-endian only, for the
|
||||
* above mentioned case, it will re-align the contents of a
|
||||
* <= 3-byte struct value.
|
||||
*/
|
||||
if (size < WORD_SIZE)
|
||||
{
|
||||
if (size == 1) {
|
||||
*(unsigned int *)addr =
|
||||
(unsigned int)*(UINT8*)(value);
|
||||
} else if (size == 2) {
|
||||
*(unsigned int *)addr =
|
||||
(unsigned int)*(UINT16*)(value);
|
||||
} else {
|
||||
*(unsigned int *)addr =
|
||||
((unsigned int)*(UINT32*)(value)) >> 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case FFI_TYPE_SINT32:
|
||||
case FFI_TYPE_UINT32:
|
||||
case FFI_TYPE_FLOAT:
|
||||
case FFI_TYPE_SINT64:
|
||||
case FFI_TYPE_UINT64:
|
||||
case FFI_TYPE_DOUBLE:
|
||||
default:
|
||||
memcpy(addr, value, aligned_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ffi_status ffi_prep_cif_machdep(ffi_cif* cif)
|
||||
{
|
||||
/* check ABI */
|
||||
switch (cif->abi)
|
||||
{
|
||||
case FFI_SYSV:
|
||||
break;
|
||||
default:
|
||||
return FFI_BAD_ABI;
|
||||
}
|
||||
return FFI_OK;
|
||||
}
|
||||
|
||||
void ffi_call(ffi_cif* cif, void (*fn)(void), void* rvalue, void** avalue)
|
||||
{
|
||||
extended_cif ecif;
|
||||
ecif.cif = cif;
|
||||
ecif.avalue = avalue;
|
||||
|
||||
/* If the return value is a struct and we don't have a return */
|
||||
/* value address then we need to make one */
|
||||
if ((rvalue == NULL) && (cif->rtype->type == FFI_TYPE_STRUCT)) {
|
||||
ecif.rvalue = alloca(cif->rtype->size);
|
||||
} else {
|
||||
ecif.rvalue = rvalue;
|
||||
}
|
||||
|
||||
switch (cif->abi)
|
||||
{
|
||||
case FFI_SYSV:
|
||||
ffi_call_SYSV(ffi_prep_args, &ecif, cif->bytes, cif->flags,
|
||||
ecif.rvalue, fn, cif->rtype->type, cif->rtype->size);
|
||||
break;
|
||||
default:
|
||||
FFI_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ffi_closure_call_SYSV(void* register_args, void* stack_args,
|
||||
ffi_closure* closure, void* rvalue,
|
||||
unsigned int* rtype, unsigned int* rsize)
|
||||
{
|
||||
/* prepare arguments for closure call */
|
||||
ffi_cif* cif = closure->cif;
|
||||
ffi_type** arg_types = cif->arg_types;
|
||||
|
||||
/* re-allocate data for the args. This needs to be done in order to keep
|
||||
* multi-word objects (e.g. structs) in contigious memory. Callers are not
|
||||
* required to store the value of args in the lower 6 words in the stack
|
||||
* (although they are allocated in the stack).
|
||||
*/
|
||||
char* stackclone = alloca(cif->bytes);
|
||||
void** avalue = alloca(cif->nargs * sizeof(void*));
|
||||
void* struct_rvalue = NULL;
|
||||
char* ptr = stackclone;
|
||||
int i;
|
||||
|
||||
/* copy registers into stack clone */
|
||||
int registers_used = cif->bytes;
|
||||
if (registers_used > ARGS_REGISTER_SIZE) {
|
||||
registers_used = ARGS_REGISTER_SIZE;
|
||||
}
|
||||
memcpy(stackclone, register_args, registers_used);
|
||||
|
||||
/* copy stack allocated args into stack clone */
|
||||
if (cif->bytes > ARGS_REGISTER_SIZE) {
|
||||
int stack_used = cif->bytes - ARGS_REGISTER_SIZE;
|
||||
memcpy(stackclone + ARGS_REGISTER_SIZE, stack_args, stack_used);
|
||||
}
|
||||
|
||||
/* preserve struct type return pointer passing */
|
||||
if ((cif->rtype != NULL) && (cif->rtype->type == FFI_TYPE_STRUCT)) {
|
||||
struct_rvalue = *((void**)ptr);
|
||||
ptr += WORD_SIZE;
|
||||
}
|
||||
|
||||
/* populate arg pointer list */
|
||||
for (i = 0; i < cif->nargs; i++)
|
||||
{
|
||||
switch (arg_types[i]->type)
|
||||
{
|
||||
case FFI_TYPE_SINT8:
|
||||
case FFI_TYPE_UINT8:
|
||||
#ifdef __BIG_ENDIAN__
|
||||
avalue[i] = ptr + 3;
|
||||
#else
|
||||
avalue[i] = ptr;
|
||||
#endif
|
||||
break;
|
||||
case FFI_TYPE_SINT16:
|
||||
case FFI_TYPE_UINT16:
|
||||
#ifdef __BIG_ENDIAN__
|
||||
avalue[i] = ptr + 2;
|
||||
#else
|
||||
avalue[i] = ptr;
|
||||
#endif
|
||||
break;
|
||||
case FFI_TYPE_STRUCT:
|
||||
#if __BIG_ENDIAN__
|
||||
/*
|
||||
* Work around strange ABI behaviour.
|
||||
* (see info in ffi_prep_args)
|
||||
*/
|
||||
if (arg_types[i]->size < WORD_SIZE)
|
||||
{
|
||||
if (arg_types[i]->size == 1) {
|
||||
*(unsigned int *)ptr =
|
||||
((unsigned int)*(UINT32*)(ptr)) << 24;
|
||||
} else if (arg_types[i]->size == 2) {
|
||||
*(unsigned int *)ptr =
|
||||
((unsigned int)*(UINT32*)(ptr)) << 16;
|
||||
} else {
|
||||
*(unsigned int *)ptr =
|
||||
((unsigned int)*(UINT32*)(ptr)) << 8;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
avalue[i] = (void*)ptr;
|
||||
break;
|
||||
case FFI_TYPE_UINT64:
|
||||
case FFI_TYPE_SINT64:
|
||||
case FFI_TYPE_DOUBLE:
|
||||
avalue[i] = ptr;
|
||||
break;
|
||||
case FFI_TYPE_SINT32:
|
||||
case FFI_TYPE_UINT32:
|
||||
case FFI_TYPE_FLOAT:
|
||||
default:
|
||||
/* default 4-byte argument */
|
||||
avalue[i] = ptr;
|
||||
break;
|
||||
}
|
||||
ptr += WORD_ALIGN(arg_types[i]->size);
|
||||
}
|
||||
|
||||
/* set the return type info passed back to the wrapper */
|
||||
*rsize = cif->rtype->size;
|
||||
*rtype = cif->rtype->type;
|
||||
if (struct_rvalue != NULL) {
|
||||
closure->fun(cif, struct_rvalue, avalue, closure->user_data);
|
||||
/* copy struct return pointer value into function return value */
|
||||
*((void**)rvalue) = struct_rvalue;
|
||||
} else {
|
||||
closure->fun(cif, rvalue, avalue, closure->user_data);
|
||||
}
|
||||
}
|
||||
|
||||
ffi_status ffi_prep_closure_loc(
|
||||
ffi_closure* closure, ffi_cif* cif,
|
||||
void (*fun)(ffi_cif*, void*, void**, void*),
|
||||
void* user_data, void* codeloc)
|
||||
{
|
||||
unsigned long* tramp = (unsigned long*)&(closure->tramp[0]);
|
||||
unsigned long cls = (unsigned long)codeloc;
|
||||
unsigned long fn = 0;
|
||||
unsigned long fn_closure_call_sysv = (unsigned long)ffi_closure_call_SYSV;
|
||||
|
||||
closure->cif = cif;
|
||||
closure->fun = fun;
|
||||
closure->user_data = user_data;
|
||||
|
||||
switch (cif->abi)
|
||||
{
|
||||
case FFI_SYSV:
|
||||
fn = (unsigned long)ffi_closure_SYSV;
|
||||
|
||||
/* load r11 (temp) with fn */
|
||||
/* imm fn(upper) */
|
||||
tramp[0] = 0xb0000000 | ((fn >> 16) & 0xffff);
|
||||
/* addik r11, r0, fn(lower) */
|
||||
tramp[1] = 0x31600000 | (fn & 0xffff);
|
||||
|
||||
/* load r12 (temp) with cls */
|
||||
/* imm cls(upper) */
|
||||
tramp[2] = 0xb0000000 | ((cls >> 16) & 0xffff);
|
||||
/* addik r12, r0, cls(lower) */
|
||||
tramp[3] = 0x31800000 | (cls & 0xffff);
|
||||
|
||||
/* load r3 (temp) with ffi_closure_call_SYSV */
|
||||
/* imm fn_closure_call_sysv(upper) */
|
||||
tramp[4] = 0xb0000000 | ((fn_closure_call_sysv >> 16) & 0xffff);
|
||||
/* addik r3, r0, fn_closure_call_sysv(lower) */
|
||||
tramp[5] = 0x30600000 | (fn_closure_call_sysv & 0xffff);
|
||||
/* branch/jump to address stored in r11 (fn) */
|
||||
tramp[6] = 0x98085800; /* bra r11 */
|
||||
|
||||
break;
|
||||
default:
|
||||
return FFI_BAD_ABI;
|
||||
}
|
||||
return FFI_OK;
|
||||
}
|
||||
Reference in New Issue
Block a user