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
+}