1 /* ----------------------------------------------------------------------
2   ffi.c - Copyright (c) 2013 Imagination Technologies
3 
4   Meta Foreign Function Interface
5   Permission is hereby granted, free of charge, to any person obtaining
6   a copy of this software and associated documentation files (the
7   `Software''), to deal in the Software without restriction, including
8   without limitation the rights to use, copy, modify, merge, publish,
9   distribute, sublicense, and/or sell copies of the Software, and to
10   permit persons to whom the Software is furnished to do so, subject to
11   the following conditions:
12 
13   The above copyright notice and this permission notice shall be included
14   in all copies or substantial portions of the Software.
15 
16   THE SOFTWARE IS PROVIDED `AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
17   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19   IN NO EVENT SHALL SIMON POSNJAK BE LIABLE FOR ANY CLAIM, DAMAGES OR
20   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22   OTHER DEALINGS IN THE SOFTWARE.
23 ----------------------------------------------------------------------- */
24 
25 #include <ffi.h>
26 #include <ffi_common.h>
27 
28 #include <stdlib.h>
29 
30 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
31 
32 /*
33  * ffi_prep_args is called by the assembly routine once stack space has been
34  * allocated for the function's arguments
35  */
36 
ffi_prep_args(char * stack,extended_cif * ecif)37 unsigned int ffi_prep_args(char *stack, extended_cif *ecif)
38 {
39 	register unsigned int i;
40 	register void **p_argv;
41 	register char *argp;
42 	register ffi_type **p_arg;
43 
44 	argp = stack;
45 
46 	/* Store return value */
47 	if ( ecif->cif->flags == FFI_TYPE_STRUCT ) {
48 		argp -= 4;
49 		*(void **) argp = ecif->rvalue;
50 	}
51 
52 	p_argv = ecif->avalue;
53 
54 	/* point to next location */
55 	for (i = ecif->cif->nargs, p_arg = ecif->cif->arg_types; (i != 0); i--, p_arg++, p_argv++)
56 	{
57 		size_t z;
58 
59 		/* Move argp to address of argument */
60 		z = (*p_arg)->size;
61 		argp -= z;
62 
63 		/* Align if necessary */
64 		argp = (char *) ALIGN_DOWN(ALIGN_DOWN(argp, (*p_arg)->alignment), 4);
65 
66 		if (z < sizeof(int)) {
67 			z = sizeof(int);
68 			switch ((*p_arg)->type)
69 			{
70 			case FFI_TYPE_SINT8:
71 				*(signed int *) argp = (signed int)*(SINT8 *)(* p_argv);
72 				break;
73 			case FFI_TYPE_UINT8:
74 				*(unsigned int *) argp = (unsigned int)*(UINT8 *)(* p_argv);
75 				break;
76 			case FFI_TYPE_SINT16:
77 				*(signed int *) argp = (signed int)*(SINT16 *)(* p_argv);
78 				break;
79 			case FFI_TYPE_UINT16:
80 				*(unsigned int *) argp = (unsigned int)*(UINT16 *)(* p_argv);
81 			case FFI_TYPE_STRUCT:
82 				memcpy(argp, *p_argv, (*p_arg)->size);
83 				break;
84 			default:
85 				FFI_ASSERT(0);
86 			}
87 		} else if ( z == sizeof(int)) {
88 			*(unsigned int *) argp = (unsigned int)*(UINT32 *)(* p_argv);
89 		} else {
90 			memcpy(argp, *p_argv, z);
91 		}
92 	}
93 
94 	/* return the size of the arguments to be passed in registers,
95 	   padded to an 8 byte boundary to preserve stack alignment */
96 	return ALIGN(MIN(stack - argp, 6*4), 8);
97 }
98 
99 /* Perform machine dependent cif processing */
ffi_prep_cif_machdep(ffi_cif * cif)100 ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
101 {
102 	ffi_type **ptr;
103 	unsigned i, bytes = 0;
104 
105 	for (ptr = cif->arg_types, i = cif->nargs; i > 0; i--, ptr++) {
106 		if ((*ptr)->size == 0)
107 			return FFI_BAD_TYPEDEF;
108 
109 		/* Perform a sanity check on the argument type, do this
110 		   check after the initialization.  */
111 		FFI_ASSERT_VALID_TYPE(*ptr);
112 
113 		/* Add any padding if necessary */
114 		if (((*ptr)->alignment - 1) & bytes)
115 			bytes = ALIGN(bytes, (*ptr)->alignment);
116 
117 		bytes += ALIGN((*ptr)->size, 4);
118 	}
119 
120 	/* Ensure arg space is aligned to an 8-byte boundary */
121 	bytes = ALIGN(bytes, 8);
122 
123 	/* Make space for the return structure pointer */
124 	if (cif->rtype->type == FFI_TYPE_STRUCT) {
125 		bytes += sizeof(void*);
126 
127 		/* Ensure stack is aligned to an 8-byte boundary */
128 		bytes = ALIGN(bytes, 8);
129 	}
130 
131 	cif->bytes = bytes;
132 
133 	/* Set the return type flag */
134 	switch (cif->rtype->type) {
135 	case FFI_TYPE_VOID:
136 	case FFI_TYPE_FLOAT:
137 	case FFI_TYPE_DOUBLE:
138 		cif->flags = (unsigned) cif->rtype->type;
139 		break;
140 	case FFI_TYPE_SINT64:
141 	case FFI_TYPE_UINT64:
142 		cif->flags = (unsigned) FFI_TYPE_SINT64;
143 		break;
144 	case FFI_TYPE_STRUCT:
145 		/* Meta can store return values which are <= 64 bits */
146 		if (cif->rtype->size <= 4)
147 			/* Returned to D0Re0 as 32-bit value */
148 			cif->flags = (unsigned)FFI_TYPE_INT;
149 		else if ((cif->rtype->size > 4) && (cif->rtype->size <= 8))
150 			/* Returned valued is stored to D1Re0|R0Re0 */
151 			cif->flags = (unsigned)FFI_TYPE_DOUBLE;
152 		else
153 			/* value stored in memory */
154 			cif->flags = (unsigned)FFI_TYPE_STRUCT;
155 		break;
156 	default:
157 		cif->flags = (unsigned)FFI_TYPE_INT;
158 		break;
159 	}
160 	return FFI_OK;
161 }
162 
163 extern void ffi_call_SYSV(void (*fn)(void), extended_cif *, unsigned, unsigned, double *);
164 
165 /*
166  * Exported in API. Entry point
167  * cif -> ffi_cif object
168  * fn -> function pointer
169  * rvalue -> pointer to return value
170  * avalue -> vector of void * pointers pointing to memory locations holding the
171  * arguments
172  */
ffi_call(ffi_cif * cif,void (* fn)(void),void * rvalue,void ** avalue)173 void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
174 {
175 	extended_cif ecif;
176 
177 	int small_struct = (((cif->flags == FFI_TYPE_INT) || (cif->flags == FFI_TYPE_DOUBLE)) && (cif->rtype->type == FFI_TYPE_STRUCT));
178 	ecif.cif = cif;
179 	ecif.avalue = avalue;
180 
181 	double temp;
182 
183 	/*
184 	 * If the return value is a struct and we don't have a return value address
185 	 * then we need to make one
186 	 */
187 
188 	if ((rvalue == NULL ) && (cif->flags == FFI_TYPE_STRUCT))
189 		ecif.rvalue = alloca(cif->rtype->size);
190 	else if (small_struct)
191 		ecif.rvalue = &temp;
192 	else
193 		ecif.rvalue = rvalue;
194 
195 	switch (cif->abi) {
196 	case FFI_SYSV:
197 		ffi_call_SYSV(fn, &ecif, cif->bytes, cif->flags, ecif.rvalue);
198 		break;
199 	default:
200 		FFI_ASSERT(0);
201 		break;
202 	}
203 
204 	if (small_struct)
205 		memcpy (rvalue, &temp, cif->rtype->size);
206 }
207 
208 /* private members */
209 
210 static void ffi_prep_incoming_args_SYSV (char *, void **, void **,
211 	ffi_cif*, float *);
212 
213 void ffi_closure_SYSV (ffi_closure *);
214 
215 /* Do NOT change that without changing the FFI_TRAMPOLINE_SIZE */
216 extern unsigned int ffi_metag_trampoline[10]; /* 10 instructions */
217 
218 /* end of private members */
219 
220 /*
221  * __tramp: trampoline memory location
222  * __fun: assembly routine
223  * __ctx: memory location for wrapper
224  *
225  * At this point, tramp[0] == __ctx !
226  */
ffi_init_trampoline(unsigned char * __tramp,unsigned int __fun,unsigned int __ctx)227 void ffi_init_trampoline(unsigned char *__tramp, unsigned int __fun, unsigned int __ctx) {
228 	memcpy (__tramp, ffi_metag_trampoline, sizeof(ffi_metag_trampoline));
229 	*(unsigned int*) &__tramp[40] = __ctx;
230 	*(unsigned int*) &__tramp[44] = __fun;
231 	/* This will flush the instruction cache */
232 	__builtin_meta2_cachewd(&__tramp[0], 1);
233 	__builtin_meta2_cachewd(&__tramp[47], 1);
234 }
235 
236 
237 
238 /* the cif must already be prepared */
239 
240 ffi_status
ffi_prep_closure_loc(ffi_closure * closure,ffi_cif * cif,void (* fun)(ffi_cif *,void *,void **,void *),void * user_data,void * codeloc)241 ffi_prep_closure_loc (ffi_closure *closure,
242 	ffi_cif* cif,
243 	void (*fun)(ffi_cif*,void*,void**,void*),
244 	void *user_data,
245 	void *codeloc)
246 {
247 	void (*closure_func)(ffi_closure*) = NULL;
248 
249 	if (cif->abi == FFI_SYSV)
250 		closure_func = &ffi_closure_SYSV;
251 	else
252 		return FFI_BAD_ABI;
253 
254 	ffi_init_trampoline(
255 		(unsigned char*)&closure->tramp[0],
256 		(unsigned int)closure_func,
257 		(unsigned int)codeloc);
258 
259 	closure->cif = cif;
260 	closure->user_data = user_data;
261 	closure->fun = fun;
262 
263 	return FFI_OK;
264 }
265 
266 
267 /* This function is jumped to by the trampoline */
ffi_closure_SYSV_inner(closure,respp,args,vfp_args)268 unsigned int ffi_closure_SYSV_inner (closure, respp, args, vfp_args)
269 	ffi_closure *closure;
270 	void **respp;
271 	void *args;
272 	void *vfp_args;
273 {
274 	ffi_cif *cif;
275 	void **arg_area;
276 
277 	cif = closure->cif;
278 	arg_area = (void**) alloca (cif->nargs * sizeof (void*));
279 
280 	/*
281 	 * This call will initialize ARG_AREA, such that each
282 	 * element in that array points to the corresponding
283 	 * value on the stack; and if the function returns
284 	 * a structure, it will re-set RESP to point to the
285 	 * structure return address.
286 	 */
287 	ffi_prep_incoming_args_SYSV(args, respp, arg_area, cif, vfp_args);
288 
289 	(closure->fun) ( cif, *respp, arg_area, closure->user_data);
290 
291 	return cif->flags;
292 }
293 
ffi_prep_incoming_args_SYSV(char * stack,void ** rvalue,void ** avalue,ffi_cif * cif,float * vfp_stack)294 static void ffi_prep_incoming_args_SYSV(char *stack, void **rvalue,
295 	void **avalue, ffi_cif *cif,
296 	float *vfp_stack)
297 {
298 	register unsigned int i;
299 	register void **p_argv;
300 	register char *argp;
301 	register ffi_type **p_arg;
302 
303 	/* stack points to original arguments */
304 	argp = stack;
305 
306 	/* Store return value */
307 	if ( cif->flags == FFI_TYPE_STRUCT ) {
308 		argp -= 4;
309 		*rvalue = *(void **) argp;
310 	}
311 
312 	p_argv = avalue;
313 
314 	for (i = cif->nargs, p_arg = cif->arg_types; (i != 0); i--, p_arg++) {
315 		size_t z;
316 		size_t alignment;
317 
318 		alignment = (*p_arg)->alignment;
319 		if (alignment < 4)
320 			alignment = 4;
321 		if ((alignment - 1) & (unsigned)argp)
322 			argp = (char *) ALIGN(argp, alignment);
323 
324 		z = (*p_arg)->size;
325 		*p_argv = (void*) argp;
326 		p_argv++;
327 		argp -= z;
328 	}
329 	return;
330 }
331