@samitouri / QOSamiQemu / commits / af1520207b

bsd-user: Copy linux-user/thunk.c to bsd-user

bsd-user's blitz branch has used this file verbatim for a while. Copy it verbatim, but update to the new QEMU standards. Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Signed-off-by: Warner Losh <imp@bsdimp.com>

Warner Losh committed Mar 13, 2026 at 13:04 UTC af1520207b7ccbe4e034e77a5a3de138fd68b5c8
2 files changed +471
bsd-user/meson.build
+1
@@ -17,6 +17,7 @@ bsd_user_ss.add(files(
17 'plugin-api.c',
18 'signal.c',
19 'strace.c',
20 + 'thunk.c',
21 'uaccess.c',
22 ))
23
bsd-user/thunk.c new
+470
@@ -0,0 +1,470 @@
1 +/*
2 + * Generic thunking code to convert data between host and target CPU
3 + *
4 + * Copyright (c) 2003 Fabrice Bellard
5 + *
6 + * SPDX-License-Identifier: GPL-2.0-or-later
7 + */
8 +#include "qemu/osdep.h"
9 +#include "qemu/log.h"
10 +
11 +#include "qemu.h"
12 +#include "user/thunk.h"
13 +
14 +//#define DEBUG
15 +
16 +static unsigned int max_struct_entries;
17 +StructEntry *struct_entries;
18 +
19 +static const argtype *thunk_type_next_ptr(const argtype *type_ptr);
20 +
21 +static inline const argtype *thunk_type_next(const argtype *type_ptr)
22 +{
23 + int type;
24 +
25 + type = *type_ptr++;
26 + switch(type) {
27 + case TYPE_CHAR:
28 + case TYPE_SHORT:
29 + case TYPE_INT:
30 + case TYPE_LONGLONG:
31 + case TYPE_ULONGLONG:
32 + case TYPE_LONG:
33 + case TYPE_ULONG:
34 + case TYPE_PTRVOID:
35 + case TYPE_OLDDEVT:
36 + return type_ptr;
37 + case TYPE_PTR:
38 + return thunk_type_next_ptr(type_ptr);
39 + case TYPE_ARRAY:
40 + return thunk_type_next_ptr(type_ptr + 1);
41 + case TYPE_STRUCT:
42 + return type_ptr + 1;
43 + default:
44 + return NULL;
45 + }
46 +}
47 +
48 +static const argtype *thunk_type_next_ptr(const argtype *type_ptr)
49 +{
50 + return thunk_type_next(type_ptr);
51 +}
52 +
53 +void thunk_register_struct(int id, const char *name, const argtype *types)
54 +{
55 + const argtype *type_ptr;
56 + StructEntry *se;
57 + int nb_fields, offset, max_align, align, size, i, j;
58 +
59 + assert(id < max_struct_entries);
60 +
61 + /* first we count the number of fields */
62 + type_ptr = types;
63 + nb_fields = 0;
64 + while (*type_ptr != TYPE_NULL) {
65 + type_ptr = thunk_type_next(type_ptr);
66 + nb_fields++;
67 + }
68 + assert(nb_fields > 0);
69 + se = struct_entries + id;
70 + se->field_types = types;
71 + se->nb_fields = nb_fields;
72 + se->name = name;
73 +#ifdef DEBUG
74 + printf("struct %s: id=%d nb_fields=%d\n",
75 + se->name, id, se->nb_fields);
76 +#endif
77 + /* now we can alloc the data */
78 +
79 + for (i = 0; i < ARRAY_SIZE(se->field_offsets); i++) {
80 + offset = 0;
81 + max_align = 1;
82 + se->field_offsets[i] = g_new(int, nb_fields);
83 + type_ptr = se->field_types;
84 + for(j = 0;j < nb_fields; j++) {
85 + size = thunk_type_size(type_ptr, i);
86 + align = thunk_type_align(type_ptr, i);
87 + offset = (offset + align - 1) & ~(align - 1);
88 + se->field_offsets[i][j] = offset;
89 + offset += size;
90 + if (align > max_align)
91 + max_align = align;
92 + type_ptr = thunk_type_next(type_ptr);
93 + }
94 + offset = (offset + max_align - 1) & ~(max_align - 1);
95 + se->size[i] = offset;
96 + se->align[i] = max_align;
97 +#ifdef DEBUG
98 + printf("%s: size=%d align=%d\n",
99 + i == THUNK_HOST ? "host" : "target", offset, max_align);
100 +#endif
101 + }
102 +}
103 +
104 +void thunk_register_struct_direct(int id, const char *name,
105 + const StructEntry *se1)
106 +{
107 + StructEntry *se;
108 +
109 + assert(id < max_struct_entries);
110 + se = struct_entries + id;
111 + *se = *se1;
112 + se->name = name;
113 +}
114 +
115 +
116 +/* now we can define the main conversion functions */
117 +const argtype *thunk_convert(void *dst, const void *src,
118 + const argtype *type_ptr, int to_host)
119 +{
120 + int type;
121 +
122 + type = *type_ptr++;
123 + switch(type) {
124 + case TYPE_CHAR:
125 + *(uint8_t *)dst = *(uint8_t *)src;
126 + break;
127 + case TYPE_SHORT:
128 + *(uint16_t *)dst = tswap16(*(uint16_t *)src);
129 + break;
130 + case TYPE_INT:
131 + *(uint32_t *)dst = tswap32(*(uint32_t *)src);
132 + break;
133 + case TYPE_LONGLONG:
134 + case TYPE_ULONGLONG:
135 + *(uint64_t *)dst = tswap64(*(uint64_t *)src);
136 + break;
137 +#if HOST_LONG_BITS == 32 && TARGET_ABI_BITS == 32
138 + case TYPE_LONG:
139 + case TYPE_ULONG:
140 + case TYPE_PTRVOID:
141 + *(uint32_t *)dst = tswap32(*(uint32_t *)src);
142 + break;
143 +#elif HOST_LONG_BITS == 64 && TARGET_ABI_BITS == 32
144 + case TYPE_LONG:
145 + case TYPE_ULONG:
146 + case TYPE_PTRVOID:
147 + if (to_host) {
148 + if (type == TYPE_LONG) {
149 + /* sign extension */
150 + *(uint64_t *)dst = (int32_t)tswap32(*(uint32_t *)src);
151 + } else {
152 + *(uint64_t *)dst = tswap32(*(uint32_t *)src);
153 + }
154 + } else {
155 + *(uint32_t *)dst = tswap32(*(uint64_t *)src & 0xffffffff);
156 + }
157 + break;
158 +#elif HOST_LONG_BITS == 64 && TARGET_ABI_BITS == 64
159 + case TYPE_LONG:
160 + case TYPE_ULONG:
161 + case TYPE_PTRVOID:
162 + *(uint64_t *)dst = tswap64(*(uint64_t *)src);
163 + break;
164 +#elif HOST_LONG_BITS == 32 && TARGET_ABI_BITS == 64
165 + case TYPE_LONG:
166 + case TYPE_ULONG:
167 + case TYPE_PTRVOID:
168 + if (to_host) {
169 + *(uint32_t *)dst = tswap64(*(uint64_t *)src);
170 + } else {
171 + if (type == TYPE_LONG) {
172 + /* sign extension */
173 + *(uint64_t *)dst = tswap64(*(int32_t *)src);
174 + } else {
175 + *(uint64_t *)dst = tswap64(*(uint32_t *)src);
176 + }
177 + }
178 + break;
179 +#else
180 +#warning unsupported conversion
181 +#endif
182 + case TYPE_OLDDEVT:
183 + {
184 + uint64_t val = 0;
185 + switch (thunk_type_size(type_ptr - 1, !to_host)) {
186 + case 2:
187 + val = *(uint16_t *)src;
188 + break;
189 + case 4:
190 + val = *(uint32_t *)src;
191 + break;
192 + case 8:
193 + val = *(uint64_t *)src;
194 + break;
195 + }
196 + switch (thunk_type_size(type_ptr - 1, to_host)) {
197 + case 2:
198 + *(uint16_t *)dst = tswap16(val);
199 + break;
200 + case 4:
201 + *(uint32_t *)dst = tswap32(val);
202 + break;
203 + case 8:
204 + *(uint64_t *)dst = tswap64(val);
205 + break;
206 + }
207 + break;
208 + }
209 + case TYPE_ARRAY:
210 + {
211 + int array_length, i, dst_size, src_size;
212 + const uint8_t *s;
213 + uint8_t *d;
214 +
215 + array_length = *type_ptr++;
216 + dst_size = thunk_type_size(type_ptr, to_host);
217 + src_size = thunk_type_size(type_ptr, 1 - to_host);
218 + d = dst;
219 + s = src;
220 + for(i = 0;i < array_length; i++) {
221 + thunk_convert(d, s, type_ptr, to_host);
222 + d += dst_size;
223 + s += src_size;
224 + }
225 + type_ptr = thunk_type_next(type_ptr);
226 + }
227 + break;
228 + case TYPE_STRUCT:
229 + {
230 + int i;
231 + const StructEntry *se;
232 + const uint8_t *s;
233 + uint8_t *d;
234 + const argtype *field_types;
235 + const int *dst_offsets, *src_offsets;
236 +
237 + assert(*type_ptr < max_struct_entries);
238 + se = struct_entries + *type_ptr++;
239 + if (se->convert[0] != NULL) {
240 + /* specific conversion is needed */
241 + (*se->convert[to_host])(dst, src);
242 + } else {
243 + /* standard struct conversion */
244 + field_types = se->field_types;
245 + dst_offsets = se->field_offsets[to_host];
246 + src_offsets = se->field_offsets[1 - to_host];
247 + d = dst;
248 + s = src;
249 + for(i = 0;i < se->nb_fields; i++) {
250 + field_types = thunk_convert(d + dst_offsets[i],
251 + s + src_offsets[i],
252 + field_types, to_host);
253 + }
254 + }
255 + }
256 + break;
257 + default:
258 + fprintf(stderr, "Invalid type 0x%x\n", type);
259 + break;
260 + }
261 + return type_ptr;
262 +}
263 +
264 +const argtype *thunk_print(void *arg, const argtype *type_ptr)
265 +{
266 + int type;
267 +
268 + type = *type_ptr++;
269 +
270 + switch (type) {
271 + case TYPE_CHAR:
272 + qemu_log("%c", *(uint8_t *)arg);
273 + break;
274 + case TYPE_SHORT:
275 + qemu_log("%" PRId16, tswap16(*(uint16_t *)arg));
276 + break;
277 + case TYPE_INT:
278 + qemu_log("%" PRId32, tswap32(*(uint32_t *)arg));
279 + break;
280 + case TYPE_LONGLONG:
281 + qemu_log("%" PRId64, tswap64(*(uint64_t *)arg));
282 + break;
283 + case TYPE_ULONGLONG:
284 + qemu_log("%" PRIu64, tswap64(*(uint64_t *)arg));
285 + break;
286 +#if HOST_LONG_BITS == 32 && TARGET_ABI_BITS == 32
287 + case TYPE_PTRVOID:
288 + qemu_log("0x%" PRIx32, tswap32(*(uint32_t *)arg));
289 + break;
290 + case TYPE_LONG:
291 + qemu_log("%" PRId32, tswap32(*(uint32_t *)arg));
292 + break;
293 + case TYPE_ULONG:
294 + qemu_log("%" PRIu32, tswap32(*(uint32_t *)arg));
295 + break;
296 +#elif HOST_LONG_BITS == 64 && TARGET_ABI_BITS == 32
297 + case TYPE_PTRVOID:
298 + qemu_log("0x%" PRIx32, tswap32(*(uint64_t *)arg & 0xffffffff));
299 + break;
300 + case TYPE_LONG:
301 + qemu_log("%" PRId32, tswap32(*(uint64_t *)arg & 0xffffffff));
302 + break;
303 + case TYPE_ULONG:
304 + qemu_log("%" PRIu32, tswap32(*(uint64_t *)arg & 0xffffffff));
305 + break;
306 +#elif HOST_LONG_BITS == 64 && TARGET_ABI_BITS == 64
307 + case TYPE_PTRVOID:
308 + qemu_log("0x%" PRIx64, tswap64(*(uint64_t *)arg));
309 + break;
310 + case TYPE_LONG:
311 + qemu_log("%" PRId64, tswap64(*(uint64_t *)arg));
312 + break;
313 + case TYPE_ULONG:
314 + qemu_log("%" PRIu64, tswap64(*(uint64_t *)arg));
315 + break;
316 +#else
317 + case TYPE_PTRVOID:
318 + qemu_log("0x%" PRIx64, tswap64(*(uint64_t *)arg));
319 + break;
320 + case TYPE_LONG:
321 + qemu_log("%" PRId64, tswap64(*(uint64_t *)arg));
322 + break;
323 + case TYPE_ULONG:
324 + qemu_log("%" PRIu64, tswap64(*(uint64_t *)arg));
325 + break;
326 +#endif
327 + case TYPE_OLDDEVT:
328 + {
329 + uint64_t val = 0;
330 + switch (thunk_type_size(type_ptr - 1, 1)) {
331 + case 2:
332 + val = *(uint16_t *)arg;
333 + break;
334 + case 4:
335 + val = *(uint32_t *)arg;
336 + break;
337 + case 8:
338 + val = *(uint64_t *)arg;
339 + break;
340 + }
341 + switch (thunk_type_size(type_ptr - 1, 0)) {
342 + case 2:
343 + qemu_log("%" PRIu16, tswap16(val));
344 + break;
345 + case 4:
346 + qemu_log("%" PRIu32, tswap32(val));
347 + break;
348 + case 8:
349 + qemu_log("%" PRIu64, tswap64(val));
350 + break;
351 + }
352 + }
353 + break;
354 + case TYPE_ARRAY:
355 + {
356 + int i, array_length, arg_size;
357 + uint8_t *a;
358 + int is_string = 0;
359 +
360 + array_length = *type_ptr++;
361 + arg_size = thunk_type_size(type_ptr, 0);
362 + a = arg;
363 +
364 + if (*type_ptr == TYPE_CHAR) {
365 + qemu_log("\"");
366 + is_string = 1;
367 + } else {
368 + qemu_log("[");
369 + }
370 +
371 + for (i = 0; i < array_length; i++) {
372 + if (i > 0 && !is_string) {
373 + qemu_log(",");
374 + }
375 + thunk_print(a, type_ptr);
376 + a += arg_size;
377 + }
378 +
379 + if (is_string) {
380 + qemu_log("\"");
381 + } else {
382 + qemu_log("]");
383 + }
384 +
385 + type_ptr = thunk_type_next(type_ptr);
386 + }
387 + break;
388 + case TYPE_STRUCT:
389 + {
390 + int i;
391 + const StructEntry *se;
392 + uint8_t *a;
393 + const argtype *field_types;
394 + const int *arg_offsets;
395 +
396 + se = struct_entries + *type_ptr++;
397 +
398 + if (se->print != NULL) {
399 + se->print(arg);
400 + } else {
401 + a = arg;
402 +
403 + field_types = se->field_types;
404 + arg_offsets = se->field_offsets[0];
405 +
406 + qemu_log("{");
407 + for (i = 0; i < se->nb_fields; i++) {
408 + if (i > 0) {
409 + qemu_log(",");
410 + }
411 + field_types = thunk_print(a + arg_offsets[i], field_types);
412 + }
413 + qemu_log("}");
414 + }
415 + }
416 + break;
417 + default:
418 + g_assert_not_reached();
419 + }
420 + return type_ptr;
421 +}
422 +
423 +/* from em86 */
424 +
425 +/* Utility function: Table-driven functions to translate bitmasks
426 + * between host and target formats
427 + */
428 +unsigned int target_to_host_bitmask_len(unsigned int target_mask,
429 + const bitmask_transtbl *tbl,
430 + size_t len)
431 +{
432 + unsigned int host_mask = 0;
433 +
434 + for (size_t i = 0; i < len; ++i) {
435 + if ((target_mask & tbl[i].target_mask) == tbl[i].target_bits) {
436 + host_mask |= tbl[i].host_bits;
437 + }
438 + }
439 + return host_mask;
440 +}
441 +
442 +unsigned int host_to_target_bitmask_len(unsigned int host_mask,
443 + const bitmask_transtbl *tbl,
444 + size_t len)
445 +{
446 + unsigned int target_mask = 0;
447 +
448 + for (size_t i = 0; i < len; ++i) {
449 + if ((host_mask & tbl[i].host_mask) == tbl[i].host_bits) {
450 + target_mask |= tbl[i].target_bits;
451 + }
452 + }
453 + return target_mask;
454 +}
455 +
456 +int thunk_type_size_array(const argtype *type_ptr, int is_host)
457 +{
458 + return thunk_type_size(type_ptr, is_host);
459 +}
460 +
461 +int thunk_type_align_array(const argtype *type_ptr, int is_host)
462 +{
463 + return thunk_type_align(type_ptr, is_host);
464 +}
465 +
466 +void thunk_init(unsigned int max_structs)
467 +{
468 + max_struct_entries = max_structs;
469 + struct_entries = g_new0(StructEntry, max_structs);
470 +}