1/* -----------------------------------------------------------------------
2   ffi.c - Copyright (c) 2012, 2013 Xilinx, Inc
3
4   MicroBlaze Foreign Function Interface
5
6   Permission is hereby granted, free of charge, to any person obtaining
7   a copy of this software and associated documentation files (the
8   ``Software''), to deal in the Software without restriction, including
9   without limitation the rights to use, copy, modify, merge, publish,
10   distribute, sublicense, and/or sell copies of the Software, and to
11   permit persons to whom the Software is furnished to do so, subject to
12   the following conditions:
13
14   The above copyright notice and this permission notice shall be included
15   in all copies or substantial portions of the Software.
16
17   THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
18   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20   NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24   DEALINGS IN THE SOFTWARE.
25   ----------------------------------------------------------------------- */
26
27#include <ffi.h>
28#include <ffi_common.h>
29
30extern void ffi_call_SYSV(void (*)(void*, extended_cif*), extended_cif*,
31		unsigned int, unsigned int, unsigned int*, void (*fn)(void),
32		unsigned int, unsigned int);
33
34extern void ffi_closure_SYSV(void);
35
36#define WORD_SIZE			sizeof(unsigned int)
37#define ARGS_REGISTER_SIZE	(WORD_SIZE * 6)
38#define WORD_ALIGN(x)		ALIGN(x, WORD_SIZE)
39
40/* ffi_prep_args is called by the assembly routine once stack space
41   has been allocated for the function's arguments */
42void ffi_prep_args(void* stack, extended_cif* ecif)
43{
44	unsigned int i;
45	ffi_type** p_arg;
46	void** p_argv;
47	void* stack_args_p = stack;
48
49	p_argv = ecif->avalue;
50
51	if (ecif == NULL || ecif->cif == NULL) {
52		return; /* no description to prepare */
53	}
54
55	if ((ecif->cif->rtype != NULL) &&
56			(ecif->cif->rtype->type == FFI_TYPE_STRUCT))
57	{
58		/* if return type is a struct which is referenced on the stack/reg5,
59		 * by a pointer. Stored the return value pointer in r5.
60		 */
61		char* addr = stack_args_p;
62		memcpy(addr, &(ecif->rvalue), WORD_SIZE);
63		stack_args_p += WORD_SIZE;
64	}
65
66	if (ecif->avalue == NULL) {
67		return; /* no arguments to prepare */
68	}
69
70	for (i = 0, p_arg = ecif->cif->arg_types; i < ecif->cif->nargs;
71			i++, p_arg++)
72	{
73		size_t size = (*p_arg)->size;
74		int type = (*p_arg)->type;
75		void* value = p_argv[i];
76		char* addr = stack_args_p;
77		int aligned_size = WORD_ALIGN(size);
78
79		/* force word alignment on the stack */
80		stack_args_p += aligned_size;
81
82		switch (type)
83		{
84			case FFI_TYPE_UINT8:
85				*(unsigned int *)addr = (unsigned int)*(UINT8*)(value);
86				break;
87			case FFI_TYPE_SINT8:
88				*(signed int *)addr = (signed int)*(SINT8*)(value);
89				break;
90			case FFI_TYPE_UINT16:
91				*(unsigned int *)addr = (unsigned int)*(UINT16*)(value);
92				break;
93			case FFI_TYPE_SINT16:
94				*(signed int *)addr = (signed int)*(SINT16*)(value);
95				break;
96			case FFI_TYPE_STRUCT:
97#if __BIG_ENDIAN__
98				/*
99				 * MicroBlaze toolchain appears to emit:
100				 * bsrli r5, r5, 8 (caller)
101				 * ...
102				 * <branch to callee>
103				 * ...
104				 * bslli r5, r5, 8 (callee)
105				 *
106				 * For structs like "struct a { uint8_t a[3]; };", when passed
107				 * by value.
108				 *
109				 * Structs like "struct b { uint16_t a; };" are also expected
110				 * to be packed strangely in registers.
111				 *
112				 * This appears to be because the microblaze toolchain expects
113				 * "struct b == uint16_t", which is only any issue for big
114				 * endian.
115				 *
116				 * The following is a work around for big-endian only, for the
117				 * above mentioned case, it will re-align the contents of a
118				 * <= 3-byte struct value.
119				 */
120				if (size < WORD_SIZE)
121				{
122				  memcpy (addr + (WORD_SIZE - size), value, size);
123				  break;
124				}
125#endif
126			case FFI_TYPE_SINT32:
127			case FFI_TYPE_UINT32:
128			case FFI_TYPE_FLOAT:
129			case FFI_TYPE_SINT64:
130			case FFI_TYPE_UINT64:
131			case FFI_TYPE_DOUBLE:
132			default:
133				memcpy(addr, value, aligned_size);
134		}
135	}
136}
137
138ffi_status ffi_prep_cif_machdep(ffi_cif* cif)
139{
140	/* check ABI */
141	switch (cif->abi)
142	{
143		case FFI_SYSV:
144			break;
145		default:
146			return FFI_BAD_ABI;
147	}
148	return FFI_OK;
149}
150
151void ffi_call(ffi_cif* cif, void (*fn)(void), void* rvalue, void** avalue)
152{
153	extended_cif ecif;
154	ecif.cif = cif;
155	ecif.avalue = avalue;
156
157	/* If the return value is a struct and we don't have a return */
158	/* value address then we need to make one */
159	if ((rvalue == NULL) && (cif->rtype->type == FFI_TYPE_STRUCT)) {
160		ecif.rvalue = alloca(cif->rtype->size);
161	} else {
162		ecif.rvalue = rvalue;
163	}
164
165	switch (cif->abi)
166	{
167	case FFI_SYSV:
168		ffi_call_SYSV(ffi_prep_args, &ecif, cif->bytes, cif->flags,
169				ecif.rvalue, fn, cif->rtype->type, cif->rtype->size);
170		break;
171	default:
172		FFI_ASSERT(0);
173		break;
174	}
175}
176
177void ffi_closure_call_SYSV(void* register_args, void* stack_args,
178			ffi_closure* closure, void* rvalue,
179			unsigned int* rtype, unsigned int* rsize)
180{
181	/* prepare arguments for closure call */
182	ffi_cif* cif = closure->cif;
183	ffi_type** arg_types = cif->arg_types;
184
185	/* re-allocate data for the args. This needs to be done in order to keep
186	 * multi-word objects (e.g. structs) in contiguous memory. Callers are not
187	 * required to store the value of args in the lower 6 words in the stack
188	 * (although they are allocated in the stack).
189	 */
190	char* stackclone = alloca(cif->bytes);
191	void** avalue = alloca(cif->nargs * sizeof(void*));
192	void* struct_rvalue = NULL;
193	char* ptr = stackclone;
194	int i;
195
196	/* copy registers into stack clone */
197	int registers_used = cif->bytes;
198	if (registers_used > ARGS_REGISTER_SIZE) {
199		registers_used = ARGS_REGISTER_SIZE;
200	}
201	memcpy(stackclone, register_args, registers_used);
202
203	/* copy stack allocated args into stack clone */
204	if (cif->bytes > ARGS_REGISTER_SIZE) {
205		int stack_used = cif->bytes - ARGS_REGISTER_SIZE;
206		memcpy(stackclone + ARGS_REGISTER_SIZE, stack_args, stack_used);
207	}
208
209	/* preserve struct type return pointer passing */
210	if ((cif->rtype != NULL) && (cif->rtype->type == FFI_TYPE_STRUCT)) {
211		struct_rvalue = *((void**)ptr);
212		ptr += WORD_SIZE;
213	}
214
215	/* populate arg pointer list */
216	for (i = 0; i < cif->nargs; i++)
217	{
218		switch (arg_types[i]->type)
219		{
220			case FFI_TYPE_SINT8:
221			case FFI_TYPE_UINT8:
222#ifdef __BIG_ENDIAN__
223				avalue[i] = ptr + 3;
224#else
225				avalue[i] = ptr;
226#endif
227				break;
228			case FFI_TYPE_SINT16:
229			case FFI_TYPE_UINT16:
230#ifdef __BIG_ENDIAN__
231				avalue[i] = ptr + 2;
232#else
233				avalue[i] = ptr;
234#endif
235				break;
236			case FFI_TYPE_STRUCT:
237#if __BIG_ENDIAN__
238				/*
239				 * Work around strange ABI behaviour.
240				 * (see info in ffi_prep_args)
241				 */
242				if (arg_types[i]->size < WORD_SIZE)
243				{
244				  memcpy (ptr, ptr + (WORD_SIZE - arg_types[i]->size), arg_types[i]->size);
245				}
246#endif
247				avalue[i] = (void*)ptr;
248				break;
249			case FFI_TYPE_UINT64:
250			case FFI_TYPE_SINT64:
251			case FFI_TYPE_DOUBLE:
252				avalue[i] = ptr;
253				break;
254			case FFI_TYPE_SINT32:
255			case FFI_TYPE_UINT32:
256			case FFI_TYPE_FLOAT:
257			default:
258				/* default 4-byte argument */
259				avalue[i] = ptr;
260				break;
261		}
262		ptr += WORD_ALIGN(arg_types[i]->size);
263	}
264
265	/* set the return type info passed back to the wrapper */
266	*rsize = cif->rtype->size;
267	*rtype = cif->rtype->type;
268	if (struct_rvalue != NULL) {
269		closure->fun(cif, struct_rvalue, avalue, closure->user_data);
270		/* copy struct return pointer value into function return value */
271		*((void**)rvalue) = struct_rvalue;
272	} else {
273		closure->fun(cif, rvalue, avalue, closure->user_data);
274	}
275}
276
277ffi_status ffi_prep_closure_loc(
278		ffi_closure* closure, ffi_cif* cif,
279		void (*fun)(ffi_cif*, void*, void**, void*),
280		void* user_data, void* codeloc)
281{
282	unsigned long* tramp = (unsigned long*)&(closure->tramp[0]);
283	unsigned long cls = (unsigned long)codeloc;
284	unsigned long fn = 0;
285	unsigned long fn_closure_call_sysv = (unsigned long)ffi_closure_call_SYSV;
286
287	closure->cif = cif;
288	closure->fun = fun;
289	closure->user_data = user_data;
290
291	switch (cif->abi)
292	{
293	case FFI_SYSV:
294		fn = (unsigned long)ffi_closure_SYSV;
295
296		/* load r11 (temp) with fn */
297		/* imm fn(upper) */
298		tramp[0] = 0xb0000000 | ((fn >> 16) & 0xffff);
299		/* addik r11, r0, fn(lower) */
300		tramp[1] = 0x31600000 | (fn & 0xffff);
301
302		/* load r12 (temp) with cls */
303		/* imm cls(upper) */
304		tramp[2] = 0xb0000000 | ((cls >> 16) & 0xffff);
305		/* addik r12, r0, cls(lower) */
306		tramp[3] = 0x31800000 | (cls & 0xffff);
307
308		/* load r3 (temp) with ffi_closure_call_SYSV */
309		/* imm fn_closure_call_sysv(upper) */
310		tramp[4] = 0xb0000000 | ((fn_closure_call_sysv >> 16) & 0xffff);
311		/* addik r3, r0, fn_closure_call_sysv(lower) */
312		tramp[5] = 0x30600000 | (fn_closure_call_sysv & 0xffff);
313		/* branch/jump to address stored in r11 (fn) */
314		tramp[6] = 0x98085800; /* bra r11 */
315
316		break;
317	default:
318		return FFI_BAD_ABI;
319	}
320	return FFI_OK;
321}
322