• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
30 extern 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 
34 extern void ffi_closure_SYSV(void);
35 
36 #define WORD_SIZE			sizeof(unsigned int)
37 #define ARGS_REGISTER_SIZE	(WORD_SIZE * 6)
38 #define WORD_FFI_ALIGN(x)		FFI_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 */
ffi_prep_args(void * stack,extended_cif * ecif)42 void 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 	if (ecif == NULL || ecif->cif == NULL) {
50 		return; /* no description to prepare */
51 	}
52 
53 	p_argv = ecif->avalue;
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_FFI_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 
ffi_prep_cif_machdep(ffi_cif * cif)138 ffi_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 
ffi_call(ffi_cif * cif,void (* fn)(void),void * rvalue,void ** avalue)151 void 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 
ffi_closure_call_SYSV(void * register_args,void * stack_args,ffi_closure * closure,void * rvalue,unsigned int * rtype,unsigned int * rsize)177 void 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_FFI_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 
ffi_prep_closure_loc(ffi_closure * closure,ffi_cif * cif,void (* fun)(ffi_cif *,void *,void **,void *),void * user_data,void * codeloc)277 ffi_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