Merge tag 'v3.14.25' into backport/v3.14.24-ltsi-rc1+v3.14.25/snapshot-merge.wip
[platform/adaptation/renesas_rcar/renesas_kernel.git] / drivers / staging / ktap / runtime / ffi / ffi_call.c
1 /*
2  * ffi_call.c - foreign function calling library support for ktap
3  *
4  * This file is part of ktap by Jovi Zhangwei.
5  *
6  * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
7  *
8  * ktap is free software; you can redistribute it and/or modify it
9  * under the terms and conditions of the GNU General Public License,
10  * version 2, as published by the Free Software Foundation.
11  *
12  * ktap is distributed in the hope it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15  * more details.
16  *
17  * You should have received a copy of the GNU General Public License along with
18  * this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include <linux/ctype.h>
23 #include <linux/slab.h>
24 #include "../../include/ktap_types.h"
25 #include "../../include/ktap_ffi.h"
26 #include "../ktap.h"
27 #include "../kp_vm.h"
28 #include "../kp_obj.h"
29
30 static int ffi_type_check(ktap_state *ks, csymbol_func *csf, int idx)
31 {
32         StkId arg;
33         csymbol *cs;
34         ffi_type type;
35
36         if (idx >= csymf_arg_nr(csf))
37                 return 0;
38         arg = kp_arg(ks, idx + 1);
39         cs = csymf_arg(ks, csf, idx);
40         type = csym_type(cs);
41
42         if (type == FFI_FUNC)
43                 goto error;
44
45         switch (ttypenv(arg)) {
46         case KTAP_TLIGHTUSERDATA:
47                 if (type != FFI_PTR) goto error;
48                 break;
49         case KTAP_TBOOLEAN:
50         case KTAP_TNUMBER:
51                 if (type != FFI_UINT8 && type != FFI_INT8
52                 && type != FFI_UINT16 && type != FFI_INT16
53                 && type != FFI_UINT32 && type != FFI_INT32
54                 && type != FFI_UINT64 && type != FFI_INT64)
55                         goto error;
56                 break;
57         case KTAP_TSTRING:
58                 if (type != FFI_PTR && type != FFI_UINT8 && type != FFI_INT8)
59                         goto error;
60                 break;
61         case KTAP_TCDATA:
62                 if (cs != cd_csym(ks, cdvalue(arg)))
63                         goto error;
64                 break;
65         default:
66                 goto error;
67         }
68         return 0;
69
70  error:
71         kp_error(ks, "Error: Cannot convert to csymbol %s for arg %d\n",
72                         csym_name(cs), idx);
73         return -1;
74 }
75
76 static csymbol *ffi_get_arg_csym(ktap_state *ks, csymbol_func *csf, int idx)
77 {
78         StkId arg;
79         csymbol *cs;
80
81         if (idx < csymf_arg_nr(csf))
82                 return csymf_arg(ks, csf, idx);
83
84         arg = kp_arg(ks, idx + 1);
85         cs = id_to_csym(ks, ffi_get_csym_id(ks, "void *"));
86         switch (ttypenv(arg)) {
87         case KTAP_TLIGHTUSERDATA:
88         case KTAP_TBOOLEAN:
89         case KTAP_TNUMBER:
90         case KTAP_TSTRING:
91                 return cs;
92         case KTAP_TCDATA:
93                 return cd_csym(ks, cdvalue(arg));
94         default:
95                 kp_error(ks, "Error: Cannot get type for arg %d\n", idx);
96                 return cs;
97         }
98 }
99
100 static void ffi_unpack(ktap_state *ks, csymbol_func *csf, int idx,
101                 char *dst, int align)
102 {
103         StkId arg = kp_arg(ks, idx + 1);
104         csymbol *cs = ffi_get_arg_csym(ks, csf, idx);
105         ffi_type type = csym_type(cs);
106         size_t size = csym_size(ks, cs);
107         void *p;
108         struct ktap_cdata *cd;
109
110         /* initialize the destination section */
111         memset(dst, 0, ALIGN(size, align));
112
113         switch (ttypenv(arg)) {
114         case KTAP_TBOOLEAN:
115                 memcpy(dst, &bvalue(arg), sizeof(bool));
116                 return;
117         case KTAP_TLIGHTUSERDATA:
118                 memcpy(dst, pvalue(arg), size);
119                 return;
120         case KTAP_TNUMBER:
121                 memcpy(dst, &nvalue(arg), size < sizeof(ktap_number) ?
122                                 size : sizeof(ktap_number));
123                 return;
124         case KTAP_TSTRING:
125                 p = &rawtsvalue(arg)->tsv + 1;
126                 memcpy(dst, &p, size);
127                 return;
128         }
129
130         cd = cdvalue(arg);
131         switch (type) {
132         case FFI_VOID:
133                 kp_error(ks, "Error: Cannot copy data from void type\n");
134                 return;
135         case FFI_UINT8:
136         case FFI_INT8:
137         case FFI_UINT16:
138         case FFI_INT16:
139         case FFI_UINT32:
140         case FFI_INT32:
141         case FFI_UINT64:
142         case FFI_INT64:
143                 memcpy(dst, &cd_int(cd), size);
144                 return;
145         case FFI_PTR:
146                 memcpy(dst, &cd_ptr(cd), size);
147                 return;
148         case FFI_STRUCT:
149                 memcpy(dst, cd_struct(cd), size);
150                 return;
151         case FFI_FUNC:
152         case FFI_UNKNOWN:
153                 kp_error(ks, "Error: internal error for csymbol %s\n",
154                                 csym_name(cs));
155                 return;
156         }
157 }
158
159 #ifdef __x86_64
160
161 enum arg_status {
162         IN_REGISTER,
163         IN_MEMORY,
164         IN_STACK,
165 };
166
167 #define ALIGN_STACK(v, a) ((void *)(ALIGN(((uint64_t)v), a)))
168 #define STACK_ALIGNMENT 8
169 #define REDZONE_SIZE 128
170 #define GPR_SIZE (sizeof(void *))
171 #define MAX_GPR 6
172 #define MAX_GPR_SIZE (MAX_GPR * GPR_SIZE)
173 #define NEWSTACK_SIZE 512
174
175 #define ffi_call(ks, cf, rvalue) ffi_call_x86_64(ks, cf, rvalue)
176
177 extern void ffi_call_assem_x86_64(void *stack, void *temp_stack,
178                                         void *func_addr, void *rvalue, ffi_type rtype);
179
180 static void ffi_call_x86_64(ktap_state *ks, csymbol_func *csf, void *rvalue)
181 {
182         int i;
183         int gpr_nr;
184         int arg_bytes; /* total bytes needed for exceeded args in stack */
185         int mem_bytes; /* total bytes needed for memory storage */
186         char *stack, *stack_p, *gpr_p, *arg_p, *mem_p, *tmp_p;
187         int arg_nr;
188         csymbol *rsym;
189         ffi_type rtype;
190         size_t rsize;
191         bool ret_in_memory;
192         /* New stack to call C function */
193         char space[NEWSTACK_SIZE];
194
195         arg_nr = kp_arg_nr(ks);
196         rsym = csymf_ret(ks, csf);
197         rtype = csym_type(rsym);
198         rsize = csym_size(ks, rsym);
199         ret_in_memory = false;
200         if (rtype == FFI_STRUCT) {
201                 if (rsize > 16) {
202                         rvalue = kp_malloc(ks, rsize);
203                         rtype = FFI_VOID;
204                         ret_in_memory = true;
205                 } else {
206                         /* much easier to always copy 16 bytes from registers */
207                         rvalue = kp_malloc(ks, 16);
208                 }
209         }
210
211         gpr_nr = 0;
212         arg_bytes = mem_bytes = 0;
213         if (ret_in_memory)
214                 gpr_nr++;
215         /* calculate bytes needed for stack */
216         for (i = 0; i < arg_nr; i++) {
217                 csymbol *cs = ffi_get_arg_csym(ks, csf, i);
218                 size_t size = csym_size(ks, cs);
219                 size_t align = csym_align(ks, cs);
220                 enum arg_status st = IN_REGISTER;
221                 int n_gpr_nr = 0;
222                 if (size > 32) {
223                         st = IN_MEMORY;
224                         n_gpr_nr = 1;
225                 } else if (size > 16)
226                         st = IN_STACK;
227                 else
228                         n_gpr_nr = ALIGN(size, GPR_SIZE) / GPR_SIZE;
229
230                 if (gpr_nr + n_gpr_nr > MAX_GPR) {
231                         if (st == IN_MEMORY)
232                                 arg_bytes += GPR_SIZE;
233                         else
234                                 st = IN_STACK;
235                 } else
236                         gpr_nr += n_gpr_nr;
237                 if (st == IN_STACK) {
238                         arg_bytes = ALIGN(arg_bytes, align);
239                         arg_bytes += size;
240                         arg_bytes = ALIGN(arg_bytes, STACK_ALIGNMENT);
241                 }
242                 if (st == IN_MEMORY) {
243                         mem_bytes = ALIGN(mem_bytes, align);
244                         mem_bytes += size;
245                         mem_bytes = ALIGN(mem_bytes, STACK_ALIGNMENT);
246                 }
247         }
248
249         /* apply space to fake stack for C function call */
250         if (16 + REDZONE_SIZE + MAX_GPR_SIZE + arg_bytes +
251                         mem_bytes + 6 * 8 >= NEWSTACK_SIZE) {
252                 kp_error(ks, "Unable to handle that many arguments by now\n");
253                 return;
254         }
255         stack = space;
256         /* 128 bytes below %rsp is red zone */
257         /* stack should be 16-bytes aligned */
258         stack_p = ALIGN_STACK(stack + REDZONE_SIZE, 16);
259         /* save general purpose registers here */
260         gpr_p = stack_p;
261         memset(gpr_p, 0, MAX_GPR_SIZE);
262         /* save arguments in stack here */
263         arg_p = gpr_p + MAX_GPR_SIZE;
264         /* save arguments in memory here */
265         mem_p = arg_p + arg_bytes;
266         /* set additional space as temporary space */
267         tmp_p = mem_p + mem_bytes;
268
269         /* copy arguments here */
270         gpr_nr = 0;
271         if (ret_in_memory) {
272                 memcpy(gpr_p, &rvalue, GPR_SIZE);
273                 gpr_p += GPR_SIZE;
274                 gpr_nr++;
275         }
276         for (i = 0; i < arg_nr; i++) {
277                 csymbol *cs = ffi_get_arg_csym(ks, csf, i);
278                 size_t size = csym_size(ks, cs);
279                 size_t align = csym_align(ks, cs);
280                 enum arg_status st = IN_REGISTER;
281                 int n_gpr_nr = 0;
282                 if (size > 32) {
283                         st = IN_MEMORY;
284                         n_gpr_nr = 1;
285                 } else if (size > 16)
286                         st = IN_STACK;
287                 else
288                         n_gpr_nr = ALIGN(size, GPR_SIZE) / GPR_SIZE;
289
290                 if (st == IN_MEMORY)
291                         mem_p = ALIGN_STACK(mem_p, align);
292                 /* Tricky way about storing it above mem_p. It won't overflow
293                  * because temp region can be temporarily used if necesseary. */
294                 ffi_unpack(ks, csf, i, mem_p, GPR_SIZE);
295                 if (gpr_nr + n_gpr_nr > MAX_GPR) {
296                         if (st == IN_MEMORY) {
297                                 memcpy(arg_p, &mem_p, GPR_SIZE);
298                                 arg_p += GPR_SIZE;
299                         } else
300                                 st = IN_STACK;
301                 } else {
302                         memcpy(gpr_p, mem_p, n_gpr_nr * GPR_SIZE);
303                         gpr_p += n_gpr_nr * GPR_SIZE;
304                         gpr_nr += n_gpr_nr;
305                 }
306                 if (st == IN_STACK) {
307                         arg_p = ALIGN_STACK(arg_p, align);
308                         memcpy(arg_p, mem_p, size);
309                         arg_p += size;
310                         arg_p = ALIGN_STACK(arg_p, STACK_ALIGNMENT);
311                 }
312                 if (st == IN_MEMORY) {
313                         mem_p += size;
314                         mem_p = ALIGN_STACK(mem_p, STACK_ALIGNMENT);
315                 }
316         }
317
318         kp_verbose_printf(ks, "Stack location: %p -redzone- %p -general purpose "
319                         "register used- %p -zero- %p -stack for argument- %p"
320                         " -memory for argument- %p -temp stack-\n",
321                         stack, stack_p, gpr_p, stack_p + MAX_GPR_SIZE,
322                         arg_p, mem_p);
323         kp_verbose_printf(ks, "GPR number: %d; arg in stack: %d; "
324                         "arg in mem: %d\n",
325                         gpr_nr, arg_bytes, mem_bytes);
326         kp_verbose_printf(ks, "Return: address %p type %d\n", rvalue, rtype);
327         kp_verbose_printf(ks, "Number of register used: %d\n", gpr_nr);
328         kp_verbose_printf(ks, "Start FFI call on %p\n", csf->addr);
329         ffi_call_assem_x86_64(stack_p, tmp_p, csf->addr, rvalue, rtype);
330 }
331
332 #else /* non-supported platform */
333
334 #define ffi_call(ks, cf, rvalue) ffi_call_unsupported(ks, cf, rvalue)
335
336 static void ffi_call_unsupported(ktap_state *ks,
337                 csymbol_func *csf, void *rvalue)
338 {
339         kp_error(ks, "unsupported architecture.\n");
340 }
341
342 #endif /* end for platform-specific setting */
343
344
345 static int ffi_set_return(ktap_state *ks, void *rvalue, csymbol_id ret_id)
346 {
347         ktap_cdata *cd;
348         ffi_type type = csym_type(id_to_csym(ks, ret_id));
349
350         /* push return value to ktap stack */
351         switch (type) {
352         case FFI_VOID:
353                 return 0;
354         case FFI_UINT8:
355         case FFI_INT8:
356         case FFI_UINT16:
357         case FFI_INT16:
358         case FFI_UINT32:
359         case FFI_INT32:
360         case FFI_UINT64:
361         case FFI_INT64:
362                 set_number(ks->top, (ktap_number)rvalue);
363                 break;
364         case FFI_PTR:
365                 cd = kp_cdata_new_ptr(ks, rvalue, ret_id);
366                 set_cdata(ks->top, cd);
367                 break;
368         case FFI_STRUCT:
369                 cd = kp_cdata_new_struct(ks, rvalue, ret_id);
370                 set_cdata(ks->top, cd);
371                 break;
372         case FFI_FUNC:
373         case FFI_UNKNOWN:
374                 kp_error(ks, "Error: Have not support ffi_type %s\n",
375                                 ffi_type_name(type));
376                 return 0;
377         }
378         incr_top(ks);
379         return 1;
380 }
381
382 /*
383  * Call C into function
384  * First argument should be function symbol address, argument types
385  * and return type.
386  * Left arguments should be arguments for calling the C function.
387  * Types between Ktap and C are converted automatically.
388  * Only support x86_64 function call by now
389  */
390 int kp_ffi_call(ktap_state *ks, csymbol_func *csf)
391 {
392         int i;
393         int expected_arg_nr, arg_nr;
394         ktap_closure *cl;
395         void *rvalue;
396
397         expected_arg_nr = csymf_arg_nr(csf);
398         arg_nr = kp_arg_nr(ks);
399
400         /* check stack status for C call */
401         if (!csf->has_var_arg && expected_arg_nr != arg_nr) {
402                 kp_error(ks, "wrong argument number %d, which should be %d\n",
403                                 arg_nr, expected_arg_nr);
404                 goto out;
405         }
406         if (csf->has_var_arg && expected_arg_nr > arg_nr) {
407                 kp_error(ks, "argument number %d, which should be bigger than %d\n",
408                                 arg_nr, expected_arg_nr);
409                 goto out;
410         }
411
412         /* maybe useful later, leave it here first */
413         cl = clvalue(kp_arg(ks, arg_nr + 1));
414
415         /* check the argument types */
416         for (i = 0; i < arg_nr; i++) {
417                 if (ffi_type_check(ks, csf, i) < 0)
418                         goto out;
419         }
420
421         /* platform-specific calling workflow */
422         ffi_call(ks, csf, &rvalue);
423         kp_verbose_printf(ks, "Finish FFI call\n");
424
425 out:
426         return ffi_set_return(ks, rvalue, csymf_ret_id(csf));
427 }