distribution patches
[qemu] / thunk.c
1 /*
2  *  Generic thunking code to convert data between host and target CPU
3  * 
4  *  Copyright (c) 2003 Fabrice Bellard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <stdarg.h>
23
24 #include "qemu.h"
25 #include "thunk.h"
26
27 //#define DEBUG
28
29 #define MAX_STRUCTS 128
30
31 /* XXX: make it dynamic */
32 static StructEntry struct_entries[MAX_STRUCTS];
33
34 static inline int thunk_type_size(const argtype *type_ptr, int is_host)
35 {
36     int type, size;
37     const StructEntry *se;
38
39     type = *type_ptr;
40     switch(type) {
41     case TYPE_CHAR:
42         return 1;
43     case TYPE_SHORT:
44         return 2;
45     case TYPE_INT:
46         return 4;
47     case TYPE_LONGLONG:
48     case TYPE_ULONGLONG:
49         return 8;
50     case TYPE_LONG:
51     case TYPE_ULONG:
52     case TYPE_PTRVOID:
53     case TYPE_PTR:
54         if (is_host) {
55             return HOST_LONG_SIZE;
56         } else {
57             return TARGET_LONG_SIZE;
58         }
59         break;
60     case TYPE_ARRAY:
61         size = type_ptr[1];
62         return size * thunk_type_size(type_ptr + 2, is_host);
63     case TYPE_STRUCT:
64         se = struct_entries + type_ptr[1];
65         return se->size[is_host];
66     default:
67         return -1;
68     }
69 }
70
71 static inline int thunk_type_align(const argtype *type_ptr, int is_host)
72 {
73     int type;
74     const StructEntry *se;
75
76     type = *type_ptr;
77     switch(type) {
78     case TYPE_CHAR:
79         return 1;
80     case TYPE_SHORT:
81         return 2;
82     case TYPE_INT:
83         return 4;
84     case TYPE_LONGLONG:
85     case TYPE_ULONGLONG:
86         return 8;
87     case TYPE_LONG:
88     case TYPE_ULONG:
89     case TYPE_PTRVOID:
90     case TYPE_PTR:
91         if (is_host) {
92             return HOST_LONG_SIZE;
93         } else {
94             return TARGET_LONG_SIZE;
95         }
96         break;
97     case TYPE_ARRAY:
98         return thunk_type_align(type_ptr + 2, is_host);
99     case TYPE_STRUCT:
100         se = struct_entries + type_ptr[1];
101         return se->align[is_host];
102     default:
103         return -1;
104     }
105 }
106
107 static inline const argtype *thunk_type_next(const argtype *type_ptr)
108 {
109     int type;
110
111     type = *type_ptr++;
112     switch(type) {
113     case TYPE_CHAR:
114     case TYPE_SHORT:
115     case TYPE_INT:
116     case TYPE_LONGLONG:
117     case TYPE_ULONGLONG:
118     case TYPE_LONG:
119     case TYPE_ULONG:
120     case TYPE_PTRVOID:
121         return type_ptr;
122     case TYPE_PTR:
123         return thunk_type_next(type_ptr);
124     case TYPE_ARRAY:
125         return thunk_type_next(type_ptr + 1);
126     case TYPE_STRUCT:
127         return type_ptr + 1;
128     default:
129         return NULL;
130     }
131 }
132
133 void thunk_register_struct(int id, const char *name, const argtype *types)
134 {
135     const argtype *type_ptr;
136     StructEntry *se;
137     int nb_fields, offset, max_align, align, size, i, j;
138
139     se = struct_entries + id;
140     
141     /* first we count the number of fields */
142     type_ptr = types;
143     nb_fields = 0;
144     while (*type_ptr != TYPE_NULL) {
145         type_ptr = thunk_type_next(type_ptr);
146         nb_fields++;
147     }
148     se->field_types = types;
149     se->nb_fields = nb_fields;
150     se->name = name;
151 #ifdef DEBUG
152     printf("struct %s: id=%d nb_fields=%d\n", 
153            se->name, id, se->nb_fields);
154 #endif
155     /* now we can alloc the data */
156
157     for(i = 0;i < 2; i++) {
158         offset = 0;
159         max_align = 1;
160         se->field_offsets[i] = malloc(nb_fields * sizeof(int));
161         type_ptr = se->field_types;
162         for(j = 0;j < nb_fields; j++) {
163             size = thunk_type_size(type_ptr, i);
164             align = thunk_type_align(type_ptr, i);
165             offset = (offset + align - 1) & ~(align - 1);
166             se->field_offsets[i][j] = offset;
167             offset += size;
168             if (align > max_align)
169                 max_align = align;
170         }
171         offset = (offset + max_align - 1) & ~(max_align - 1);
172         se->size[i] = offset;
173         se->align[i] = max_align;
174 #ifdef DEBUG
175         printf("%s: size=%d align=%d\n", 
176                i == THUNK_HOST ? "host" : "target", offset, max_align);
177 #endif
178     }
179 }
180
181 void thunk_register_struct_direct(int id, const char *name, StructEntry *se1)
182 {
183     StructEntry *se;
184     se = struct_entries + id;
185     *se = *se1;
186     se->name = name;
187 }
188
189
190 /* now we can define the main conversion functions */
191 const argtype *thunk_convert(void *dst, const void *src, 
192                              const argtype *type_ptr, int to_host)
193 {
194     int type;
195
196     type = *type_ptr++;
197     switch(type) {
198     case TYPE_CHAR:
199         *(uint8_t *)dst = *(uint8_t *)src;
200         break;
201     case TYPE_SHORT:
202         *(uint16_t *)dst = tswap16(*(uint16_t *)src);
203         break;
204     case TYPE_INT:
205         *(uint32_t *)dst = tswap32(*(uint32_t *)src);
206         break;
207     case TYPE_LONGLONG:
208     case TYPE_ULONGLONG:
209         *(uint64_t *)dst = tswap64(*(uint64_t *)src);
210         break;
211 #if HOST_LONG_BITS == 32 && TARGET_LONG_BITS == 32
212     case TYPE_LONG:
213     case TYPE_ULONG:
214     case TYPE_PTRVOID:
215         *(uint32_t *)dst = tswap32(*(uint32_t *)src);
216         break;
217 #elif HOST_LONG_BITS == 64 && TARGET_LONG_BITS == 32
218     case TYPE_LONG:
219     case TYPE_ULONG:
220     case TYPE_PTRVOID:
221         if (target_to_host) {
222             *(uint64_t *)dst = tswap32(*(uint32_t *)src);
223         } else {
224             *(uint32_t *)dst = tswap32(*(uint64_t *)src & 0xffffffff);
225         }
226         break;
227 #else
228 #error unsupported conversion
229 #endif
230     case TYPE_ARRAY:
231         {
232             int array_length, i, dst_size, src_size;
233             const uint8_t *s;
234             uint8_t  *d;
235
236             array_length = *type_ptr++;
237             dst_size = thunk_type_size(type_ptr, to_host);
238             src_size = thunk_type_size(type_ptr, 1 - to_host);
239             d = dst;
240             s = src;
241             for(i = 0;i < array_length; i++) {
242                 thunk_convert(d, s, type_ptr, to_host);
243                 d += dst_size;
244                 s += src_size;
245             }
246             type_ptr = thunk_type_next(type_ptr);
247         }
248         break;
249     case TYPE_STRUCT:
250         {
251             int i;
252             const StructEntry *se;
253             const uint8_t *s;
254             uint8_t  *d;
255             const argtype *field_types;
256             const int *dst_offsets, *src_offsets;
257             
258             se = struct_entries + *type_ptr++;
259             if (se->convert[0] != NULL) {
260                 /* specific conversion is needed */
261                 (*se->convert[to_host])(dst, src);
262             } else {
263                 /* standard struct conversion */
264                 field_types = se->field_types;
265                 dst_offsets = se->field_offsets[to_host];
266                 src_offsets = se->field_offsets[1 - to_host];
267                 d = dst;
268                 s = src;
269                 for(i = 0;i < se->nb_fields; i++) {
270                     field_types = thunk_convert(d + dst_offsets[i], 
271                                                 s + src_offsets[i], 
272                                                 field_types, to_host);
273                 }
274             }
275         }
276         break;
277     default:
278         fprintf(stderr, "Invalid type 0x%x\n", type);
279         break;
280     }
281     return type_ptr;
282 }
283
284 /* from em86 */
285
286 /* Utility function: Table-driven functions to translate bitmasks
287  * between X86 and Alpha formats...
288  */
289 unsigned int target_to_host_bitmask(unsigned int x86_mask, 
290                                     bitmask_transtbl * trans_tbl)
291 {
292     bitmask_transtbl *  btp;
293     unsigned int        alpha_mask = 0;
294
295     for(btp = trans_tbl; btp->x86_mask && btp->alpha_mask; btp++) {
296         if((x86_mask & btp->x86_mask) == btp->x86_bits) {
297             alpha_mask |= btp->alpha_bits;
298         }
299     }
300     return(alpha_mask);
301 }
302
303 unsigned int host_to_target_bitmask(unsigned int alpha_mask, 
304                                     bitmask_transtbl * trans_tbl)
305 {
306     bitmask_transtbl *  btp;
307     unsigned int        x86_mask = 0;
308
309     for(btp = trans_tbl; btp->x86_mask && btp->alpha_mask; btp++) {
310         if((alpha_mask & btp->alpha_mask) == btp->alpha_bits) {
311             x86_mask |= btp->x86_mask;
312         }
313     }
314     return(x86_mask);
315 }