]>
Commit | Line | Data |
---|---|---|
b1760f7f RH |
1 | /* |
2 | * Copyright (c) 2013 Miodrag Vallat. <miod@openbsd.org> | |
3 | * | |
4 | * Permission is hereby granted, free of charge, to any person obtaining | |
5 | * a copy of this software and associated documentation files (the | |
6 | * ``Software''), to deal in the Software without restriction, including | |
7 | * without limitation the rights to use, copy, modify, merge, publish, | |
8 | * distribute, sublicense, and/or sell copies of the Software, and to | |
9 | * permit persons to whom the Software is furnished to do so, subject to | |
10 | * the following conditions: | |
11 | * | |
12 | * The above copyright notice and this permission notice shall be included | |
13 | * in all copies or substantial portions of the Software. | |
14 | * | |
15 | * THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, | |
16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | |
19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |
20 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | |
21 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
22 | */ | |
23 | ||
24 | /* | |
25 | * m88k Foreign Function Interface | |
26 | * | |
27 | * This file attempts to provide all the FFI entry points which can reliably | |
28 | * be implemented in C. | |
29 | * | |
30 | * Only OpenBSD/m88k is currently supported; other platforms (such as | |
31 | * Motorola's SysV/m88k) could be supported with the following tweaks: | |
32 | * | |
33 | * - non-OpenBSD systems use an `outgoing parameter area' as part of the | |
34 | * 88BCS calling convention, which is not supported under OpenBSD from | |
35 | * release 3.6 onwards. Supporting it should be as easy as taking it | |
36 | * into account when adjusting the stack, in the assembly code. | |
37 | * | |
38 | * - the logic deciding whether a function argument gets passed through | |
39 | * registers, or on the stack, has changed several times in OpenBSD in | |
40 | * edge cases (especially for structs larger than 32 bytes being passed | |
41 | * by value). The code below attemps to match the logic used by the | |
42 | * system compiler of OpenBSD 5.3, i.e. gcc 3.3.6 with many m88k backend | |
43 | * fixes. | |
44 | */ | |
45 | ||
46 | #include <ffi.h> | |
47 | #include <ffi_common.h> | |
48 | ||
49 | #include <stdlib.h> | |
50 | #include <unistd.h> | |
51 | ||
52 | void ffi_call_OBSD (unsigned int, extended_cif *, unsigned int, void *, | |
53 | void (*fn) ()); | |
54 | void *ffi_prep_args (void *, extended_cif *); | |
55 | void ffi_closure_OBSD (ffi_closure *); | |
56 | void ffi_closure_struct_OBSD (ffi_closure *); | |
57 | unsigned int ffi_closure_OBSD_inner (ffi_closure *, void *, unsigned int *, | |
58 | char *); | |
59 | void ffi_cacheflush_OBSD (unsigned int, unsigned int); | |
60 | ||
61 | #define CIF_FLAGS_INT (1 << 0) | |
62 | #define CIF_FLAGS_DINT (1 << 1) | |
63 | ||
64 | /* | |
65 | * Foreign Function Interface API | |
66 | */ | |
67 | ||
68 | /* ffi_prep_args is called by the assembly routine once stack space has | |
69 | been allocated for the function's arguments. */ | |
70 | ||
71 | void * | |
72 | ffi_prep_args (void *stack, extended_cif *ecif) | |
73 | { | |
74 | unsigned int i; | |
75 | void **p_argv; | |
76 | char *argp, *stackp; | |
77 | unsigned int *regp; | |
78 | unsigned int regused; | |
79 | ffi_type **p_arg; | |
80 | void *struct_value_ptr; | |
81 | ||
82 | regp = (unsigned int *)stack; | |
83 | stackp = (char *)(regp + 8); | |
84 | regused = 0; | |
85 | ||
86 | if (ecif->cif->rtype->type == FFI_TYPE_STRUCT | |
87 | && !ecif->cif->flags) | |
88 | struct_value_ptr = ecif->rvalue; | |
89 | else | |
90 | struct_value_ptr = NULL; | |
91 | ||
92 | p_argv = ecif->avalue; | |
93 | ||
94 | for (i = ecif->cif->nargs, p_arg = ecif->cif->arg_types; i != 0; i--, p_arg++) | |
95 | { | |
96 | size_t z; | |
97 | unsigned short t, a; | |
98 | ||
99 | z = (*p_arg)->size; | |
100 | t = (*p_arg)->type; | |
101 | a = (*p_arg)->alignment; | |
102 | ||
103 | /* | |
104 | * Figure out whether the argument can be passed through registers | |
105 | * or on the stack. | |
106 | * The rule is that registers can only receive simple types not larger | |
107 | * than 64 bits, or structs the exact size of a register and aligned to | |
108 | * the size of a register. | |
109 | */ | |
110 | if (t == FFI_TYPE_STRUCT) | |
111 | { | |
112 | if (z == sizeof (int) && a == sizeof (int) && regused < 8) | |
113 | argp = (char *)regp; | |
114 | else | |
115 | argp = stackp; | |
116 | } | |
117 | else | |
118 | { | |
119 | if (z > sizeof (int) && regused < 8 - 1) | |
120 | { | |
121 | /* align to an even register pair */ | |
122 | if (regused & 1) | |
123 | { | |
124 | regp++; | |
125 | regused++; | |
126 | } | |
127 | } | |
128 | if (regused < 8) | |
129 | argp = (char *)regp; | |
130 | else | |
131 | argp = stackp; | |
132 | } | |
133 | ||
134 | /* Enforce proper stack alignment of 64-bit types */ | |
135 | if (argp == stackp && a > sizeof (int)) | |
136 | { | |
92456a4e | 137 | stackp = (char *) FFI_ALIGN(stackp, a); |
b1760f7f RH |
138 | argp = stackp; |
139 | } | |
140 | ||
141 | switch (t) | |
142 | { | |
143 | case FFI_TYPE_SINT8: | |
144 | *(signed int *) argp = (signed int) *(SINT8 *) *p_argv; | |
145 | break; | |
146 | ||
147 | case FFI_TYPE_UINT8: | |
148 | *(unsigned int *) argp = (unsigned int) *(UINT8 *) *p_argv; | |
149 | break; | |
150 | ||
151 | case FFI_TYPE_SINT16: | |
152 | *(signed int *) argp = (signed int) *(SINT16 *) *p_argv; | |
153 | break; | |
154 | ||
155 | case FFI_TYPE_UINT16: | |
156 | *(unsigned int *) argp = (unsigned int) *(UINT16 *) *p_argv; | |
157 | break; | |
158 | ||
159 | case FFI_TYPE_INT: | |
160 | case FFI_TYPE_FLOAT: | |
161 | case FFI_TYPE_UINT32: | |
162 | case FFI_TYPE_SINT32: | |
163 | case FFI_TYPE_POINTER: | |
164 | *(unsigned int *) argp = *(unsigned int *) *p_argv; | |
165 | break; | |
166 | ||
167 | case FFI_TYPE_DOUBLE: | |
168 | case FFI_TYPE_UINT64: | |
169 | case FFI_TYPE_SINT64: | |
170 | case FFI_TYPE_STRUCT: | |
171 | memcpy (argp, *p_argv, z); | |
172 | break; | |
173 | ||
174 | default: | |
175 | FFI_ASSERT (0); | |
176 | } | |
177 | ||
178 | /* Align if necessary. */ | |
179 | if ((sizeof (int) - 1) & z) | |
92456a4e | 180 | z = FFI_ALIGN(z, sizeof (int)); |
b1760f7f RH |
181 | |
182 | p_argv++; | |
183 | ||
184 | /* Be careful, once all registers are filled, and about to continue | |
185 | on stack, regp == stackp. Therefore the check for regused as well. */ | |
186 | if (argp == (char *)regp && regused < 8) | |
187 | { | |
188 | regp += z / sizeof (int); | |
189 | regused += z / sizeof (int); | |
190 | } | |
191 | else | |
192 | stackp += z; | |
193 | } | |
194 | ||
195 | return struct_value_ptr; | |
196 | } | |
197 | ||
198 | /* Perform machine dependent cif processing */ | |
199 | ffi_status | |
200 | ffi_prep_cif_machdep (ffi_cif *cif) | |
201 | { | |
202 | /* Set the return type flag */ | |
203 | switch (cif->rtype->type) | |
204 | { | |
205 | case FFI_TYPE_VOID: | |
206 | cif->flags = 0; | |
207 | break; | |
208 | ||
209 | case FFI_TYPE_STRUCT: | |
210 | if (cif->rtype->size == sizeof (int) && | |
211 | cif->rtype->alignment == sizeof (int)) | |
212 | cif->flags = CIF_FLAGS_INT; | |
213 | else | |
214 | cif->flags = 0; | |
215 | break; | |
216 | ||
217 | case FFI_TYPE_DOUBLE: | |
218 | case FFI_TYPE_SINT64: | |
219 | case FFI_TYPE_UINT64: | |
220 | cif->flags = CIF_FLAGS_DINT; | |
221 | break; | |
222 | ||
223 | default: | |
224 | cif->flags = CIF_FLAGS_INT; | |
225 | break; | |
226 | } | |
227 | ||
228 | return FFI_OK; | |
229 | } | |
230 | ||
231 | void | |
232 | ffi_call (ffi_cif *cif, void (*fn) (), void *rvalue, void **avalue) | |
233 | { | |
234 | extended_cif ecif; | |
235 | ||
236 | ecif.cif = cif; | |
237 | ecif.avalue = avalue; | |
238 | ||
239 | /* If the return value is a struct and we don't have a return value | |
240 | address then we need to make one. */ | |
241 | ||
242 | if (rvalue == NULL | |
243 | && cif->rtype->type == FFI_TYPE_STRUCT | |
244 | && (cif->rtype->size != sizeof (int) | |
245 | || cif->rtype->alignment != sizeof (int))) | |
246 | ecif.rvalue = alloca (cif->rtype->size); | |
247 | else | |
248 | ecif.rvalue = rvalue; | |
249 | ||
250 | switch (cif->abi) | |
251 | { | |
252 | case FFI_OBSD: | |
253 | ffi_call_OBSD (cif->bytes, &ecif, cif->flags, ecif.rvalue, fn); | |
254 | break; | |
255 | ||
256 | default: | |
257 | FFI_ASSERT (0); | |
258 | break; | |
259 | } | |
260 | } | |
261 | ||
262 | /* | |
263 | * Closure API | |
264 | */ | |
265 | ||
266 | static void | |
267 | ffi_prep_closure_args_OBSD (ffi_cif *cif, void **avalue, unsigned int *regp, | |
268 | char *stackp) | |
269 | { | |
270 | unsigned int i; | |
271 | void **p_argv; | |
272 | char *argp; | |
273 | unsigned int regused; | |
274 | ffi_type **p_arg; | |
275 | ||
276 | regused = 0; | |
277 | ||
278 | p_argv = avalue; | |
279 | ||
280 | for (i = cif->nargs, p_arg = cif->arg_types; i != 0; i--, p_arg++) | |
281 | { | |
282 | size_t z; | |
283 | unsigned short t, a; | |
284 | ||
285 | z = (*p_arg)->size; | |
286 | t = (*p_arg)->type; | |
287 | a = (*p_arg)->alignment; | |
288 | ||
289 | /* | |
290 | * Figure out whether the argument has been passed through registers | |
291 | * or on the stack. | |
292 | * The rule is that registers can only receive simple types not larger | |
293 | * than 64 bits, or structs the exact size of a register and aligned to | |
294 | * the size of a register. | |
295 | */ | |
296 | if (t == FFI_TYPE_STRUCT) | |
297 | { | |
298 | if (z == sizeof (int) && a == sizeof (int) && regused < 8) | |
299 | argp = (char *)regp; | |
300 | else | |
301 | argp = stackp; | |
302 | } | |
303 | else | |
304 | { | |
305 | if (z > sizeof (int) && regused < 8 - 1) | |
306 | { | |
307 | /* align to an even register pair */ | |
308 | if (regused & 1) | |
309 | { | |
310 | regp++; | |
311 | regused++; | |
312 | } | |
313 | } | |
314 | if (regused < 8) | |
315 | argp = (char *)regp; | |
316 | else | |
317 | argp = stackp; | |
318 | } | |
319 | ||
320 | /* Enforce proper stack alignment of 64-bit types */ | |
321 | if (argp == stackp && a > sizeof (int)) | |
322 | { | |
92456a4e | 323 | stackp = (char *) FFI_ALIGN(stackp, a); |
b1760f7f RH |
324 | argp = stackp; |
325 | } | |
326 | ||
327 | if (z < sizeof (int) && t != FFI_TYPE_STRUCT) | |
328 | *p_argv = (void *) (argp + sizeof (int) - z); | |
329 | else | |
330 | *p_argv = (void *) argp; | |
331 | ||
332 | /* Align if necessary */ | |
333 | if ((sizeof (int) - 1) & z) | |
92456a4e | 334 | z = FFI_ALIGN(z, sizeof (int)); |
b1760f7f RH |
335 | |
336 | p_argv++; | |
337 | ||
338 | /* Be careful, once all registers are exhausted, and about to fetch from | |
339 | stack, regp == stackp. Therefore the check for regused as well. */ | |
340 | if (argp == (char *)regp && regused < 8) | |
341 | { | |
342 | regp += z / sizeof (int); | |
343 | regused += z / sizeof (int); | |
344 | } | |
345 | else | |
346 | stackp += z; | |
347 | } | |
348 | } | |
349 | ||
350 | unsigned int | |
351 | ffi_closure_OBSD_inner (ffi_closure *closure, void *resp, unsigned int *regp, | |
352 | char *stackp) | |
353 | { | |
354 | ffi_cif *cif; | |
355 | void **arg_area; | |
356 | ||
357 | cif = closure->cif; | |
358 | arg_area = (void**) alloca (cif->nargs * sizeof (void *)); | |
359 | ||
360 | ffi_prep_closure_args_OBSD(cif, arg_area, regp, stackp); | |
361 | ||
362 | (closure->fun) (cif, resp, arg_area, closure->user_data); | |
363 | ||
364 | return cif->flags; | |
365 | } | |
366 | ||
367 | ffi_status | |
368 | ffi_prep_closure_loc (ffi_closure* closure, ffi_cif* cif, | |
369 | void (*fun)(ffi_cif*,void*,void**,void*), | |
370 | void *user_data, void *codeloc) | |
371 | { | |
372 | unsigned int *tramp = (unsigned int *) codeloc; | |
373 | void *fn; | |
374 | ||
375 | FFI_ASSERT (cif->abi == FFI_OBSD); | |
376 | ||
377 | if (cif->rtype->type == FFI_TYPE_STRUCT && !cif->flags) | |
378 | fn = &ffi_closure_struct_OBSD; | |
379 | else | |
380 | fn = &ffi_closure_OBSD; | |
381 | ||
382 | /* or.u %r10, %r0, %hi16(fn) */ | |
383 | tramp[0] = 0x5d400000 | (((unsigned int)fn) >> 16); | |
384 | /* or.u %r13, %r0, %hi16(closure) */ | |
385 | tramp[1] = 0x5da00000 | ((unsigned int)closure >> 16); | |
386 | /* or %r10, %r10, %lo16(fn) */ | |
387 | tramp[2] = 0x594a0000 | (((unsigned int)fn) & 0xffff); | |
388 | /* jmp.n %r10 */ | |
389 | tramp[3] = 0xf400c40a; | |
390 | /* or %r13, %r13, %lo16(closure) */ | |
391 | tramp[4] = 0x59ad0000 | ((unsigned int)closure & 0xffff); | |
392 | ||
393 | ffi_cacheflush_OBSD((unsigned int)codeloc, FFI_TRAMPOLINE_SIZE); | |
394 | ||
395 | closure->cif = cif; | |
396 | closure->user_data = user_data; | |
397 | closure->fun = fun; | |
398 | ||
399 | return FFI_OK; | |
400 | } |